2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * Provides Attribute configurations.
9 * @namespace YAHOO.util
12 * @param hash {Object} The intial Attribute.
13 * @param {YAHOO.util.AttributeProvider} The owner of the Attribute instance.
16 YAHOO.util.Attribute = function(hash, owner) {
19 this.configure(hash, true);
23 YAHOO.util.Attribute.prototype = {
25 * The name of the attribute.
32 * The value of the attribute.
39 * The owner of the attribute.
41 * @type YAHOO.util.AttributeProvider
46 * Whether or not the attribute is read only.
53 * Whether or not the attribute can only be written once.
60 * The attribute's initial configuration.
62 * @property _initialConfig
68 * Whether or not the attribute's value has been set.
76 * A function to call when setting the attribute's value.
77 * The method receives the new value as the first arg and the attribute name as the 2nd
84 * The function to use when setting the attribute's value.
85 * The setter receives the new value as the first arg and the attribute name as the 2nd
86 * The return value of the setter replaces the value passed to set().
93 * The function to use when getting the attribute's value.
94 * The getter receives the new value as the first arg and the attribute name as the 2nd
95 * The return value of the getter will be used as the return from get().
102 * The validator to use when setting the attribute's value.
103 * @property validator
110 * Retrieves the current value of the attribute.
112 * @return {any} The current value of the attribute.
114 getValue: function() {
115 var val = this.value;
118 val = this.getter.call(this.owner, this.name);
125 * Sets the value of the attribute and fires beforeChange and change events.
127 * @param {Any} value The value to apply to the attribute.
128 * @param {Boolean} silent If true the change events will not be fired.
129 * @return {Boolean} Whether or not the value was set.
131 setValue: function(value, silent) {
138 prevValue: this.getValue(),
142 if (this.readOnly || ( this.writeOnce && this._written) ) {
143 return false; // write not allowed
146 if (this.validator && !this.validator.call(owner, value) ) {
147 return false; // invalid value
151 beforeRetVal = owner.fireBeforeChangeEvent(event);
152 if (beforeRetVal === false) {
158 value = this.setter.call(owner, value, this.name);
159 if (value === undefined) {
164 this.method.call(owner, value, this.name);
167 this.value = value; // TODO: set before calling setter/method?
168 this._written = true;
173 this.owner.fireChangeEvent(event);
180 * Allows for configuring the Attribute's properties.
182 * @param {Object} map A key-value map of Attribute properties.
183 * @param {Boolean} init Whether or not this should become the initial config.
185 configure: function(map, init) {
189 this._written = false; // reset writeOnce
192 this._initialConfig = this._initialConfig || {};
194 for (var key in map) {
195 if ( map.hasOwnProperty(key) ) {
196 this[key] = map[key];
198 this._initialConfig[key] = map[key];
205 * Resets the value to the initial config value.
207 * @return {Boolean} Whether or not the value was set.
209 resetValue: function() {
210 return this.setValue(this._initialConfig.value);
214 * Resets the attribute config to the initial config state.
215 * @method resetConfig
217 resetConfig: function() {
218 this.configure(this._initialConfig, true);
222 * Resets the value to the current value.
223 * Useful when values may have gotten out of sync with actual properties.
225 * @return {Boolean} Whether or not the value was set.
227 refresh: function(silent) {
228 this.setValue(this.value, silent);
233 var Lang = YAHOO.util.Lang;
236 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
237 Code licensed under the BSD License:
238 http://developer.yahoo.net/yui/license.txt
242 * Provides and manages YAHOO.util.Attribute instances
243 * @namespace YAHOO.util
244 * @class AttributeProvider
245 * @uses YAHOO.util.EventProvider
247 YAHOO.util.AttributeProvider = function() {};
249 YAHOO.util.AttributeProvider.prototype = {
252 * A key-value map of Attribute configurations
254 * @protected (may be used by subclasses and augmentors)
260 * Returns the current value of the attribute.
262 * @param {String} key The attribute whose value will be returned.
263 * @return {Any} The current value of the attribute.
266 this._configs = this._configs || {};
267 var config = this._configs[key];
269 if (!config || !this._configs.hasOwnProperty(key)) {
273 return config.getValue();
277 * Sets the value of a config.
279 * @param {String} key The name of the attribute
280 * @param {Any} value The value to apply to the attribute
281 * @param {Boolean} silent Whether or not to suppress change events
282 * @return {Boolean} Whether or not the value was set.
284 set: function(key, value, silent){
285 this._configs = this._configs || {};
286 var config = this._configs[key];
292 return config.setValue(value, silent);
296 * Returns an array of attribute names.
297 * @method getAttributeKeys
298 * @return {Array} An array of attribute names.
300 getAttributeKeys: function(){
301 this._configs = this._configs;
304 for (key in this._configs) {
305 if ( Lang.hasOwnProperty(this._configs, key) &&
306 !Lang.isUndefined(this._configs[key]) ) {
307 keys[keys.length] = key;
315 * Sets multiple attribute values.
316 * @method setAttributes
317 * @param {Object} map A key-value map of attributes
318 * @param {Boolean} silent Whether or not to suppress change events
320 setAttributes: function(map, silent){
321 for (var key in map) {
322 if ( Lang.hasOwnProperty(map, key) ) {
323 this.set(key, map[key], silent);
329 * Resets the specified attribute's value to its initial value.
331 * @param {String} key The name of the attribute
332 * @param {Boolean} silent Whether or not to suppress change events
333 * @return {Boolean} Whether or not the value was set
335 resetValue: function(key, silent){
336 this._configs = this._configs || {};
337 if (this._configs[key]) {
338 this.set(key, this._configs[key]._initialConfig.value, silent);
345 * Sets the attribute's value to its current value.
347 * @param {String | Array} key The attribute(s) to refresh
348 * @param {Boolean} silent Whether or not to suppress change events
350 refresh: function(key, silent) {
351 this._configs = this._configs || {};
352 var configs = this._configs;
354 key = ( ( Lang.isString(key) ) ? [key] : key ) ||
355 this.getAttributeKeys();
357 for (var i = 0, len = key.length; i < len; ++i) {
358 if (configs.hasOwnProperty(key[i])) {
359 this._configs[key[i]].refresh(silent);
365 * Adds an Attribute to the AttributeProvider instance.
367 * @param {String} key The attribute's name
368 * @param {Object} map A key-value map containing the
369 * attribute's properties.
370 * @deprecated Use setAttributeConfig
372 register: function(key, map) {
373 this.setAttributeConfig(key, map);
378 * Returns the attribute's properties.
379 * @method getAttributeConfig
380 * @param {String} key The attribute's name
382 * @return {object} A key-value map containing all of the
383 * attribute's properties.
385 getAttributeConfig: function(key) {
386 this._configs = this._configs || {};
387 var config = this._configs[key] || {};
388 var map = {}; // returning a copy to prevent overrides
390 for (key in config) {
391 if ( Lang.hasOwnProperty(config, key) ) {
392 map[key] = config[key];
400 * Sets or updates an Attribute instance's properties.
401 * @method setAttributeConfig
402 * @param {String} key The attribute's name.
403 * @param {Object} map A key-value map of attribute properties
404 * @param {Boolean} init Whether or not this should become the intial config.
406 setAttributeConfig: function(key, map, init) {
407 this._configs = this._configs || {};
409 if (!this._configs[key]) {
411 this._configs[key] = this.createAttribute(map);
413 this._configs[key].configure(map, init);
418 * Sets or updates an Attribute instance's properties.
419 * @method configureAttribute
420 * @param {String} key The attribute's name.
421 * @param {Object} map A key-value map of attribute properties
422 * @param {Boolean} init Whether or not this should become the intial config.
423 * @deprecated Use setAttributeConfig
425 configureAttribute: function(key, map, init) {
426 this.setAttributeConfig(key, map, init);
430 * Resets an attribute to its intial configuration.
431 * @method resetAttributeConfig
432 * @param {String} key The attribute's name.
435 resetAttributeConfig: function(key){
436 this._configs = this._configs || {};
437 this._configs[key].resetConfig();
440 // wrapper for EventProvider.subscribe
441 // to create events on the fly
442 subscribe: function(type, callback) {
443 this._events = this._events || {};
445 if ( !(type in this._events) ) {
446 this._events[type] = this.createEvent(type);
449 YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments);
453 this.subscribe.apply(this, arguments);
456 addListener: function() {
457 this.subscribe.apply(this, arguments);
461 * Fires the attribute's beforeChange event.
462 * @method fireBeforeChangeEvent
463 * @param {String} key The attribute's name.
464 * @param {Obj} e The event object to pass to handlers.
466 fireBeforeChangeEvent: function(e) {
468 type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change';
470 return this.fireEvent(e.type, e);
474 * Fires the attribute's change event.
475 * @method fireChangeEvent
476 * @param {String} key The attribute's name.
477 * @param {Obj} e The event object to pass to the handlers.
479 fireChangeEvent: function(e) {
481 return this.fireEvent(e.type, e);
484 createAttribute: function(map) {
485 return new YAHOO.util.Attribute(map, this);
489 YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider);
493 // internal shorthand
494 var Dom = YAHOO.util.Dom,
495 AttributeProvider = YAHOO.util.AttributeProvider;
498 * Element provides an wrapper object to simplify adding
499 * event listeners, using dom methods, and managing attributes.
501 * @namespace YAHOO.util
502 * @requires yahoo, dom, event
506 * Element provides an wrapper object to simplify adding
507 * event listeners, using dom methods, and managing attributes.
509 * @uses YAHOO.util.AttributeProvider
511 * @param el {HTMLElement | String} The html element that
512 * represents the Element.
513 * @param {Object} map A key-value map of initial config names and values
515 var Element = function(el, map) {
516 this.init.apply(this, arguments);
519 Element.DOM_EVENTS = {
536 Element.prototype = {
538 * Dom events supported by the Element instance.
539 * @property DOM_EVENTS
544 DEFAULT_HTML_SETTER: function(value, key) {
545 var el = this.get('element');
552 DEFAULT_HTML_GETTER: function(key) {
553 var el = this.get('element'),
564 * Wrapper for HTMLElement method.
565 * @method appendChild
566 * @param {YAHOO.util.Element || HTMLElement} child The element to append.
567 * @return {HTMLElement} The appended DOM element.
569 appendChild: function(child) {
570 child = child.get ? child.get('element') : child;
571 return this.get('element').appendChild(child);
575 * Wrapper for HTMLElement method.
576 * @method getElementsByTagName
577 * @param {String} tag The tagName to collect
578 * @return {HTMLCollection} A collection of DOM elements.
580 getElementsByTagName: function(tag) {
581 return this.get('element').getElementsByTagName(tag);
585 * Wrapper for HTMLElement method.
586 * @method hasChildNodes
587 * @return {Boolean} Whether or not the element has childNodes
589 hasChildNodes: function() {
590 return this.get('element').hasChildNodes();
594 * Wrapper for HTMLElement method.
595 * @method insertBefore
596 * @param {HTMLElement} element The HTMLElement to insert
597 * @param {HTMLElement} before The HTMLElement to insert
598 * the element before.
599 * @return {HTMLElement} The inserted DOM element.
601 insertBefore: function(element, before) {
602 element = element.get ? element.get('element') : element;
603 before = (before && before.get) ? before.get('element') : before;
605 return this.get('element').insertBefore(element, before);
609 * Wrapper for HTMLElement method.
610 * @method removeChild
611 * @param {HTMLElement} child The HTMLElement to remove
612 * @return {HTMLElement} The removed DOM element.
614 removeChild: function(child) {
615 child = child.get ? child.get('element') : child;
616 return this.get('element').removeChild(child);
620 * Wrapper for HTMLElement method.
621 * @method replaceChild
622 * @param {HTMLElement} newNode The HTMLElement to insert
623 * @param {HTMLElement} oldNode The HTMLElement to replace
624 * @return {HTMLElement} The replaced DOM element.
626 replaceChild: function(newNode, oldNode) {
627 newNode = newNode.get ? newNode.get('element') : newNode;
628 oldNode = oldNode.get ? oldNode.get('element') : oldNode;
629 return this.get('element').replaceChild(newNode, oldNode);
634 * Registers Element specific attributes.
635 * @method initAttributes
636 * @param {Object} map A key-value map of initial attribute configs
638 initAttributes: function(map) {
642 * Adds a listener for the given event. These may be DOM or
643 * customEvent listeners. Any event that is fired via fireEvent
644 * can be listened for. All handlers receive an event object.
645 * @method addListener
646 * @param {String} type The name of the event to listen for
647 * @param {Function} fn The handler to call when the event fires
648 * @param {Any} obj A variable to pass to the handler
649 * @param {Object} scope The object to use for the scope of the handler
651 addListener: function(type, fn, obj, scope) {
652 var el = this.get('element') || this.get('id');
653 scope = scope || this;
656 if (!this._events[type]) { // create on the fly
657 if (el && this.DOM_EVENTS[type]) {
658 YAHOO.util.Event.addListener(el, type, function(e) {
659 if (e.srcElement && !e.target) { // supplement IE with target
660 e.target = e.srcElement;
662 self.fireEvent(type, e);
665 this.createEvent(type, this);
668 return YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments); // notify via customEvent
673 * Alias for addListener
675 * @param {String} type The name of the event to listen for
676 * @param {Function} fn The function call when the event fires
677 * @param {Any} obj A variable to pass to the handler
678 * @param {Object} scope The object to use for the scope of the handler
681 return this.addListener.apply(this, arguments);
685 * Alias for addListener
687 * @param {String} type The name of the event to listen for
688 * @param {Function} fn The function call when the event fires
689 * @param {Any} obj A variable to pass to the handler
690 * @param {Object} scope The object to use for the scope of the handler
692 subscribe: function() {
693 return this.addListener.apply(this, arguments);
697 * Remove an event listener
698 * @method removeListener
699 * @param {String} type The name of the event to listen for
700 * @param {Function} fn The function call when the event fires
702 removeListener: function(type, fn) {
703 return this.unsubscribe.apply(this, arguments);
707 * Wrapper for Dom method.
709 * @param {String} className The className to add
711 addClass: function(className) {
712 Dom.addClass(this.get('element'), className);
716 * Wrapper for Dom method.
717 * @method getElementsByClassName
718 * @param {String} className The className to collect
719 * @param {String} tag (optional) The tag to use in
720 * conjunction with class name
721 * @return {Array} Array of HTMLElements
723 getElementsByClassName: function(className, tag) {
724 return Dom.getElementsByClassName(className, tag,
725 this.get('element') );
729 * Wrapper for Dom method.
731 * @param {String} className The className to add
732 * @return {Boolean} Whether or not the element has the class name
734 hasClass: function(className) {
735 return Dom.hasClass(this.get('element'), className);
739 * Wrapper for Dom method.
740 * @method removeClass
741 * @param {String} className The className to remove
743 removeClass: function(className) {
744 return Dom.removeClass(this.get('element'), className);
748 * Wrapper for Dom method.
749 * @method replaceClass
750 * @param {String} oldClassName The className to replace
751 * @param {String} newClassName The className to add
753 replaceClass: function(oldClassName, newClassName) {
754 return Dom.replaceClass(this.get('element'),
755 oldClassName, newClassName);
759 * Wrapper for Dom method.
761 * @param {String} property The style property to set
762 * @param {String} value The value to apply to the style property
764 setStyle: function(property, value) {
765 return Dom.setStyle(this.get('element'), property, value); // TODO: always queuing?
769 * Wrapper for Dom method.
771 * @param {String} property The style property to retrieve
772 * @return {String} The current value of the property
774 getStyle: function(property) {
775 return Dom.getStyle(this.get('element'), property);
779 * Apply any queued set calls.
782 fireQueue: function() {
783 var queue = this._queue;
784 for (var i = 0, len = queue.length; i < len; ++i) {
785 this[queue[i][0]].apply(this, queue[i][1]);
790 * Appends the HTMLElement into either the supplied parentNode.
792 * @param {HTMLElement | Element} parentNode The node to append to
793 * @param {HTMLElement | Element} before An optional node to insert before
794 * @return {HTMLElement} The appended DOM element.
796 appendTo: function(parent, before) {
797 parent = (parent.get) ? parent.get('element') : Dom.get(parent);
799 this.fireEvent('beforeAppendTo', {
800 type: 'beforeAppendTo',
805 before = (before && before.get) ?
806 before.get('element') : Dom.get(before);
807 var element = this.get('element');
817 if (element.parent != parent) {
819 parent.insertBefore(element, before);
821 parent.appendChild(element);
826 this.fireEvent('appendTo', {
835 var configs = this._configs || {},
836 el = configs.element; // avoid loop due to 'element'
838 if (el && !configs[key] && !YAHOO.lang.isUndefined(el.value[key]) ) {
839 this._setHTMLAttrConfig(key);
842 return AttributeProvider.prototype.get.call(this, key);
845 setAttributes: function(map, silent) {
846 // set based on configOrder
848 configOrder = this._configOrder;
850 // set based on configOrder
851 for (var i = 0, len = configOrder.length; i < len; ++i) {
852 if (map[configOrder[i]] !== undefined) {
853 done[configOrder[i]] = true;
854 this.set(configOrder[i], map[configOrder[i]], silent);
858 // unconfigured (e.g. Dom attributes)
859 for (var att in map) {
860 if (map.hasOwnProperty(att) && !done[att]) {
861 this.set(att, map[att], silent);
866 set: function(key, value, silent) {
867 var el = this.get('element');
869 this._queue[this._queue.length] = ['set', arguments];
870 if (this._configs[key]) {
871 this._configs[key].value = value; // so "get" works while queueing
877 // set it on the element if not configured and is an HTML attribute
878 if ( !this._configs[key] && !YAHOO.lang.isUndefined(el[key]) ) {
879 this._setHTMLAttrConfig(key);
882 return AttributeProvider.prototype.set.apply(this, arguments);
885 setAttributeConfig: function(key, map, init) {
886 this._configOrder.push(key);
887 AttributeProvider.prototype.setAttributeConfig.apply(this, arguments);
890 createEvent: function(type, scope) {
891 this._events[type] = true;
892 return AttributeProvider.prototype.createEvent.apply(this, arguments);
895 init: function(el, attr) {
896 this._initElement(el, attr);
899 destroy: function() {
900 var el = this.get('element');
901 YAHOO.util.Event.purgeElement(el, true); // purge DOM listeners recursively
902 this.unsubscribeAll(); // unsubscribe all custom events
904 if (el && el.parentNode) {
905 el.parentNode.removeChild(el); // pull from the DOM
908 // revert initial configs
912 this._configOrder = [];
915 _initElement: function(el, attr) {
916 this._queue = this._queue || [];
917 this._events = this._events || {};
918 this._configs = this._configs || {};
919 this._configOrder = [];
921 attr.element = attr.element || el || null;
923 var isReady = false; // to determine when to init HTMLElement and content
925 var DOM_EVENTS = Element.DOM_EVENTS;
926 this.DOM_EVENTS = this.DOM_EVENTS || {};
928 for (var event in DOM_EVENTS) {
929 if (DOM_EVENTS.hasOwnProperty(event)) {
930 this.DOM_EVENTS[event] = DOM_EVENTS[event];
934 if (typeof attr.element === 'string') { // register ID for get() access
935 this._setHTMLAttrConfig('id', { value: attr.element });
938 if (Dom.get(attr.element)) {
940 this._initHTMLElement(attr);
941 this._initContent(attr);
944 YAHOO.util.Event.onAvailable(attr.element, function() {
945 if (!isReady) { // otherwise already done
946 this._initHTMLElement(attr);
949 this.fireEvent('available', { type: 'available', target: Dom.get(attr.element) });
952 YAHOO.util.Event.onContentReady(attr.element, function() {
953 if (!isReady) { // otherwise already done
954 this._initContent(attr);
956 this.fireEvent('contentReady', { type: 'contentReady', target: Dom.get(attr.element) });
960 _initHTMLElement: function(attr) {
962 * The HTMLElement the Element instance refers to.
966 this.setAttributeConfig('element', {
967 value: Dom.get(attr.element),
972 _initContent: function(attr) {
973 this.initAttributes(attr);
974 this.setAttributes(attr, true);
980 * Sets the value of the property and fires beforeChange and change events.
982 * @method _setHTMLAttrConfig
983 * @param {YAHOO.util.Element} element The Element instance to
984 * register the config to.
985 * @param {String} key The name of the config to register
986 * @param {Object} map A key-value map of the config's params
988 _setHTMLAttrConfig: function(key, map) {
989 var el = this.get('element');
993 map.setter = map.setter || this.DEFAULT_HTML_SETTER;
994 map.getter = map.getter || this.DEFAULT_HTML_GETTER;
996 map.value = map.value || el[key];
997 this._configs[key] = new YAHOO.util.Attribute(map, this);
1002 * Fires when the Element's HTMLElement can be retrieved by Id.
1003 * <p>See: <a href="#addListener">Element.addListener</a></p>
1004 * <p><strong>Event fields:</strong><br>
1005 * <code><String> type</code> available<br>
1006 * <code><HTMLElement>
1007 * target</code> the HTMLElement bound to this Element instance<br>
1008 * <p><strong>Usage:</strong><br>
1009 * <code>var handler = function(e) {var target = e.target};<br>
1010 * myTabs.addListener('available', handler);</code></p>
1015 * Fires when the Element's HTMLElement subtree is rendered.
1016 * <p>See: <a href="#addListener">Element.addListener</a></p>
1017 * <p><strong>Event fields:</strong><br>
1018 * <code><String> type</code> contentReady<br>
1019 * <code><HTMLElement>
1020 * target</code> the HTMLElement bound to this Element instance<br>
1021 * <p><strong>Usage:</strong><br>
1022 * <code>var handler = function(e) {var target = e.target};<br>
1023 * myTabs.addListener('contentReady', handler);</code></p>
1024 * @event contentReady
1028 * Fires before the Element is appended to another Element.
1029 * <p>See: <a href="#addListener">Element.addListener</a></p>
1030 * <p><strong>Event fields:</strong><br>
1031 * <code><String> type</code> beforeAppendTo<br>
1032 * <code><HTMLElement/Element>
1033 * target</code> the HTMLElement/Element being appended to
1034 * <p><strong>Usage:</strong><br>
1035 * <code>var handler = function(e) {var target = e.target};<br>
1036 * myTabs.addListener('beforeAppendTo', handler);</code></p>
1037 * @event beforeAppendTo
1041 * Fires after the Element is appended to another Element.
1042 * <p>See: <a href="#addListener">Element.addListener</a></p>
1043 * <p><strong>Event fields:</strong><br>
1044 * <code><String> type</code> appendTo<br>
1045 * <code><HTMLElement/Element>
1046 * target</code> the HTMLElement/Element being appended to
1047 * <p><strong>Usage:</strong><br>
1048 * <code>var handler = function(e) {var target = e.target};<br>
1049 * myTabs.addListener('appendTo', handler);</code></p>
1053 YAHOO.augment(Element, AttributeProvider);
1054 YAHOO.util.Element = Element;
1057 YAHOO.register("element", YAHOO.util.Element, {version: "2.7.0", build: "1799"});