2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
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.INVALID_VALUE = {};
25 YAHOO.util.Attribute.prototype = {
27 * The name of the attribute.
34 * The value of the attribute.
41 * The owner of the attribute.
43 * @type YAHOO.util.AttributeProvider
48 * Whether or not the attribute is read only.
55 * Whether or not the attribute can only be written once.
62 * The attribute's initial configuration.
64 * @property _initialConfig
70 * Whether or not the attribute's value has been set.
78 * A function to call when setting the attribute's value.
79 * The method receives the new value as the first arg and the attribute name as the 2nd
86 * The function to use when setting the attribute's value.
87 * The setter receives the new value as the first arg and the attribute name as the 2nd
88 * The return value of the setter replaces the value passed to set().
95 * The function to use when getting the attribute's value.
96 * The getter receives the new value as the first arg and the attribute name as the 2nd
97 * The return value of the getter will be used as the return from get().
104 * The validator to use when setting the attribute's value.
105 * @property validator
112 * Retrieves the current value of the attribute.
114 * @return {any} The current value of the attribute.
116 getValue: function() {
117 var val = this.value;
120 val = this.getter.call(this.owner, this.name, val);
127 * Sets the value of the attribute and fires beforeChange and change events.
129 * @param {Any} value The value to apply to the attribute.
130 * @param {Boolean} silent If true the change events will not be fired.
131 * @return {Boolean} Whether or not the value was set.
133 setValue: function(value, silent) {
137 invalidValue = YAHOO.util.Attribute.INVALID_VALUE,
141 prevValue: this.getValue(),
145 if (this.readOnly || ( this.writeOnce && this._written) ) {
146 return false; // write not allowed
149 if (this.validator && !this.validator.call(owner, value) ) {
150 return false; // invalid value
154 beforeRetVal = owner.fireBeforeChangeEvent(event);
155 if (beforeRetVal === false) {
161 value = this.setter.call(owner, value, this.name);
162 if (value === undefined) {
165 if (value === invalidValue) {
171 if (this.method.call(owner, value, this.name) === invalidValue) {
176 this.value = value; // TODO: set before calling setter/method?
177 this._written = true;
182 this.owner.fireChangeEvent(event);
189 * Allows for configuring the Attribute's properties.
191 * @param {Object} map A key-value map of Attribute properties.
192 * @param {Boolean} init Whether or not this should become the initial config.
194 configure: function(map, init) {
198 this._written = false; // reset writeOnce
201 this._initialConfig = this._initialConfig || {};
203 for (var key in map) {
204 if ( map.hasOwnProperty(key) ) {
205 this[key] = map[key];
207 this._initialConfig[key] = map[key];
214 * Resets the value to the initial config value.
216 * @return {Boolean} Whether or not the value was set.
218 resetValue: function() {
219 return this.setValue(this._initialConfig.value);
223 * Resets the attribute config to the initial config state.
224 * @method resetConfig
226 resetConfig: function() {
227 this.configure(this._initialConfig, true);
231 * Resets the value to the current value.
232 * Useful when values may have gotten out of sync with actual properties.
234 * @return {Boolean} Whether or not the value was set.
236 refresh: function(silent) {
237 this.setValue(this.value, silent);
242 var Lang = YAHOO.util.Lang;
245 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
246 Code licensed under the BSD License:
247 http://developer.yahoo.net/yui/license.txt
251 * Provides and manages YAHOO.util.Attribute instances
252 * @namespace YAHOO.util
253 * @class AttributeProvider
254 * @uses YAHOO.util.EventProvider
256 YAHOO.util.AttributeProvider = function() {};
258 YAHOO.util.AttributeProvider.prototype = {
261 * A key-value map of Attribute configurations
263 * @protected (may be used by subclasses and augmentors)
269 * Returns the current value of the attribute.
271 * @param {String} key The attribute whose value will be returned.
272 * @return {Any} The current value of the attribute.
275 this._configs = this._configs || {};
276 var config = this._configs[key];
278 if (!config || !this._configs.hasOwnProperty(key)) {
282 return config.getValue();
286 * Sets the value of a config.
288 * @param {String} key The name of the attribute
289 * @param {Any} value The value to apply to the attribute
290 * @param {Boolean} silent Whether or not to suppress change events
291 * @return {Boolean} Whether or not the value was set.
293 set: function(key, value, silent){
294 this._configs = this._configs || {};
295 var config = this._configs[key];
301 return config.setValue(value, silent);
305 * Returns an array of attribute names.
306 * @method getAttributeKeys
307 * @return {Array} An array of attribute names.
309 getAttributeKeys: function(){
310 this._configs = this._configs;
313 for (key in this._configs) {
314 if ( Lang.hasOwnProperty(this._configs, key) &&
315 !Lang.isUndefined(this._configs[key]) ) {
316 keys[keys.length] = key;
324 * Sets multiple attribute values.
325 * @method setAttributes
326 * @param {Object} map A key-value map of attributes
327 * @param {Boolean} silent Whether or not to suppress change events
329 setAttributes: function(map, silent){
330 for (var key in map) {
331 if ( Lang.hasOwnProperty(map, key) ) {
332 this.set(key, map[key], silent);
338 * Resets the specified attribute's value to its initial value.
340 * @param {String} key The name of the attribute
341 * @param {Boolean} silent Whether or not to suppress change events
342 * @return {Boolean} Whether or not the value was set
344 resetValue: function(key, silent){
345 this._configs = this._configs || {};
346 if (this._configs[key]) {
347 this.set(key, this._configs[key]._initialConfig.value, silent);
354 * Sets the attribute's value to its current value.
356 * @param {String | Array} key The attribute(s) to refresh
357 * @param {Boolean} silent Whether or not to suppress change events
359 refresh: function(key, silent) {
360 this._configs = this._configs || {};
361 var configs = this._configs;
363 key = ( ( Lang.isString(key) ) ? [key] : key ) ||
364 this.getAttributeKeys();
366 for (var i = 0, len = key.length; i < len; ++i) {
367 if (configs.hasOwnProperty(key[i])) {
368 this._configs[key[i]].refresh(silent);
374 * Adds an Attribute to the AttributeProvider instance.
376 * @param {String} key The attribute's name
377 * @param {Object} map A key-value map containing the
378 * attribute's properties.
379 * @deprecated Use setAttributeConfig
381 register: function(key, map) {
382 this.setAttributeConfig(key, map);
387 * Returns the attribute's properties.
388 * @method getAttributeConfig
389 * @param {String} key The attribute's name
391 * @return {object} A key-value map containing all of the
392 * attribute's properties.
394 getAttributeConfig: function(key) {
395 this._configs = this._configs || {};
396 var config = this._configs[key] || {};
397 var map = {}; // returning a copy to prevent overrides
399 for (key in config) {
400 if ( Lang.hasOwnProperty(config, key) ) {
401 map[key] = config[key];
409 * Sets or updates an Attribute instance's properties.
410 * @method setAttributeConfig
411 * @param {String} key The attribute's name.
412 * @param {Object} map A key-value map of attribute properties
413 * @param {Boolean} init Whether or not this should become the intial config.
415 setAttributeConfig: function(key, map, init) {
416 this._configs = this._configs || {};
418 if (!this._configs[key]) {
420 this._configs[key] = this.createAttribute(map);
422 this._configs[key].configure(map, init);
427 * Sets or updates an Attribute instance's properties.
428 * @method configureAttribute
429 * @param {String} key The attribute's name.
430 * @param {Object} map A key-value map of attribute properties
431 * @param {Boolean} init Whether or not this should become the intial config.
432 * @deprecated Use setAttributeConfig
434 configureAttribute: function(key, map, init) {
435 this.setAttributeConfig(key, map, init);
439 * Resets an attribute to its intial configuration.
440 * @method resetAttributeConfig
441 * @param {String} key The attribute's name.
444 resetAttributeConfig: function(key){
445 this._configs = this._configs || {};
446 this._configs[key].resetConfig();
449 // wrapper for EventProvider.subscribe
450 // to create events on the fly
451 subscribe: function(type, callback) {
452 this._events = this._events || {};
454 if ( !(type in this._events) ) {
455 this._events[type] = this.createEvent(type);
458 YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments);
462 this.subscribe.apply(this, arguments);
465 addListener: function() {
466 this.subscribe.apply(this, arguments);
470 * Fires the attribute's beforeChange event.
471 * @method fireBeforeChangeEvent
472 * @param {String} key The attribute's name.
473 * @param {Obj} e The event object to pass to handlers.
475 fireBeforeChangeEvent: function(e) {
477 type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change';
479 return this.fireEvent(e.type, e);
483 * Fires the attribute's change event.
484 * @method fireChangeEvent
485 * @param {String} key The attribute's name.
486 * @param {Obj} e The event object to pass to the handlers.
488 fireChangeEvent: function(e) {
490 return this.fireEvent(e.type, e);
493 createAttribute: function(map) {
494 return new YAHOO.util.Attribute(map, this);
498 YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider);
502 // internal shorthand
503 var Dom = YAHOO.util.Dom,
504 AttributeProvider = YAHOO.util.AttributeProvider,
511 * Element provides an wrapper object to simplify adding
512 * event listeners, using dom methods, and managing attributes.
514 * @namespace YAHOO.util
515 * @requires yahoo, dom, event
519 * Element provides an wrapper object to simplify adding
520 * event listeners, using dom methods, and managing attributes.
522 * @uses YAHOO.util.AttributeProvider
524 * @param el {HTMLElement | String} The html element that
525 * represents the Element.
526 * @param {Object} map A key-value map of initial config names and values
528 var Element = function(el, map) {
529 this.init.apply(this, arguments);
532 Element.DOM_EVENTS = {
551 Element.prototype = {
553 * Dom events supported by the Element instance.
554 * @property DOM_EVENTS
559 DEFAULT_HTML_SETTER: function(value, key) {
560 var el = this.get('element');
570 DEFAULT_HTML_GETTER: function(key) {
571 var el = this.get('element'),
582 * Wrapper for HTMLElement method.
583 * @method appendChild
584 * @param {YAHOO.util.Element || HTMLElement} child The element to append.
585 * @return {HTMLElement} The appended DOM element.
587 appendChild: function(child) {
588 child = child.get ? child.get('element') : child;
589 return this.get('element').appendChild(child);
593 * Wrapper for HTMLElement method.
594 * @method getElementsByTagName
595 * @param {String} tag The tagName to collect
596 * @return {HTMLCollection} A collection of DOM elements.
598 getElementsByTagName: function(tag) {
599 return this.get('element').getElementsByTagName(tag);
603 * Wrapper for HTMLElement method.
604 * @method hasChildNodes
605 * @return {Boolean} Whether or not the element has childNodes
607 hasChildNodes: function() {
608 return this.get('element').hasChildNodes();
612 * Wrapper for HTMLElement method.
613 * @method insertBefore
614 * @param {HTMLElement} element The HTMLElement to insert
615 * @param {HTMLElement} before The HTMLElement to insert
616 * the element before.
617 * @return {HTMLElement} The inserted DOM element.
619 insertBefore: function(element, before) {
620 element = element.get ? element.get('element') : element;
621 before = (before && before.get) ? before.get('element') : before;
623 return this.get('element').insertBefore(element, before);
627 * Wrapper for HTMLElement method.
628 * @method removeChild
629 * @param {HTMLElement} child The HTMLElement to remove
630 * @return {HTMLElement} The removed DOM element.
632 removeChild: function(child) {
633 child = child.get ? child.get('element') : child;
634 return this.get('element').removeChild(child);
638 * Wrapper for HTMLElement method.
639 * @method replaceChild
640 * @param {HTMLElement} newNode The HTMLElement to insert
641 * @param {HTMLElement} oldNode The HTMLElement to replace
642 * @return {HTMLElement} The replaced DOM element.
644 replaceChild: function(newNode, oldNode) {
645 newNode = newNode.get ? newNode.get('element') : newNode;
646 oldNode = oldNode.get ? oldNode.get('element') : oldNode;
647 return this.get('element').replaceChild(newNode, oldNode);
652 * Registers Element specific attributes.
653 * @method initAttributes
654 * @param {Object} map A key-value map of initial attribute configs
656 initAttributes: function(map) {
660 * Adds a listener for the given event. These may be DOM or
661 * customEvent listeners. Any event that is fired via fireEvent
662 * can be listened for. All handlers receive an event object.
663 * @method addListener
664 * @param {String} type The name of the event to listen for
665 * @param {Function} fn The handler to call when the event fires
666 * @param {Any} obj A variable to pass to the handler
667 * @param {Object} scope The object to use for the scope of the handler
669 addListener: function(type, fn, obj, scope) {
671 scope = scope || this;
673 var Event = YAHOO.util.Event,
674 el = this.get('element') || this.get('id'),
678 if (specialTypes[type] && !Event._createMouseDelegate) {
683 if (!this._events[type]) { // create on the fly
685 if (el && this.DOM_EVENTS[type]) {
686 Event.on(el, type, function(e, matchedEl) {
688 // Supplement IE with target, currentTarget relatedTarget
690 if (e.srcElement && !e.target) {
691 e.target = e.srcElement;
694 if ((e.toElement && !e.relatedTarget) || (e.fromElement && !e.relatedTarget)) {
695 e.relatedTarget = Event.getRelatedTarget(e);
698 if (!e.currentTarget) {
699 e.currentTarget = el;
702 // Note: matchedEl el is passed back for delegated listeners
703 self.fireEvent(type, e, matchedEl);
707 this.createEvent(type, {scope: this});
710 return YAHOO.util.EventProvider.prototype.subscribe.apply(this, arguments); // notify via customEvent
715 * Alias for addListener
717 * @param {String} type The name of the event to listen for
718 * @param {Function} fn The function call when the event fires
719 * @param {Any} obj A variable to pass to the handler
720 * @param {Object} scope The object to use for the scope of the handler
723 return this.addListener.apply(this, arguments);
727 * Alias for addListener
729 * @param {String} type The name of the event to listen for
730 * @param {Function} fn The function call when the event fires
731 * @param {Any} obj A variable to pass to the handler
732 * @param {Object} scope The object to use for the scope of the handler
734 subscribe: function() {
735 return this.addListener.apply(this, arguments);
739 * Remove an event listener
740 * @method removeListener
741 * @param {String} type The name of the event to listen for
742 * @param {Function} fn The function call when the event fires
744 removeListener: function(type, fn) {
745 return this.unsubscribe.apply(this, arguments);
749 * Wrapper for Dom method.
751 * @param {String} className The className to add
753 addClass: function(className) {
754 Dom.addClass(this.get('element'), className);
758 * Wrapper for Dom method.
759 * @method getElementsByClassName
760 * @param {String} className The className to collect
761 * @param {String} tag (optional) The tag to use in
762 * conjunction with class name
763 * @return {Array} Array of HTMLElements
765 getElementsByClassName: function(className, tag) {
766 return Dom.getElementsByClassName(className, tag,
767 this.get('element') );
771 * Wrapper for Dom method.
773 * @param {String} className The className to add
774 * @return {Boolean} Whether or not the element has the class name
776 hasClass: function(className) {
777 return Dom.hasClass(this.get('element'), className);
781 * Wrapper for Dom method.
782 * @method removeClass
783 * @param {String} className The className to remove
785 removeClass: function(className) {
786 return Dom.removeClass(this.get('element'), className);
790 * Wrapper for Dom method.
791 * @method replaceClass
792 * @param {String} oldClassName The className to replace
793 * @param {String} newClassName The className to add
795 replaceClass: function(oldClassName, newClassName) {
796 return Dom.replaceClass(this.get('element'),
797 oldClassName, newClassName);
801 * Wrapper for Dom method.
803 * @param {String} property The style property to set
804 * @param {String} value The value to apply to the style property
806 setStyle: function(property, value) {
807 return Dom.setStyle(this.get('element'), property, value); // TODO: always queuing?
811 * Wrapper for Dom method.
813 * @param {String} property The style property to retrieve
814 * @return {String} The current value of the property
816 getStyle: function(property) {
817 return Dom.getStyle(this.get('element'), property);
821 * Apply any queued set calls.
824 fireQueue: function() {
825 var queue = this._queue;
826 for (var i = 0, len = queue.length; i < len; ++i) {
827 this[queue[i][0]].apply(this, queue[i][1]);
832 * Appends the HTMLElement into either the supplied parentNode.
834 * @param {HTMLElement | Element} parentNode The node to append to
835 * @param {HTMLElement | Element} before An optional node to insert before
836 * @return {HTMLElement} The appended DOM element.
838 appendTo: function(parent, before) {
839 parent = (parent.get) ? parent.get('element') : Dom.get(parent);
841 this.fireEvent('beforeAppendTo', {
842 type: 'beforeAppendTo',
847 before = (before && before.get) ?
848 before.get('element') : Dom.get(before);
849 var element = this.get('element');
859 if (element.parent != parent) {
861 parent.insertBefore(element, before);
863 parent.appendChild(element);
868 this.fireEvent('appendTo', {
877 var configs = this._configs || {},
878 el = configs.element; // avoid loop due to 'element'
880 if (el && !configs[key] && !YAHOO.lang.isUndefined(el.value[key]) ) {
881 this._setHTMLAttrConfig(key);
884 return AttributeProvider.prototype.get.call(this, key);
887 setAttributes: function(map, silent) {
888 // set based on configOrder
890 configOrder = this._configOrder;
892 // set based on configOrder
893 for (var i = 0, len = configOrder.length; i < len; ++i) {
894 if (map[configOrder[i]] !== undefined) {
895 done[configOrder[i]] = true;
896 this.set(configOrder[i], map[configOrder[i]], silent);
900 // unconfigured (e.g. Dom attributes)
901 for (var att in map) {
902 if (map.hasOwnProperty(att) && !done[att]) {
903 this.set(att, map[att], silent);
908 set: function(key, value, silent) {
909 var el = this.get('element');
911 this._queue[this._queue.length] = ['set', arguments];
912 if (this._configs[key]) {
913 this._configs[key].value = value; // so "get" works while queueing
919 // set it on the element if not configured and is an HTML attribute
920 if ( !this._configs[key] && !YAHOO.lang.isUndefined(el[key]) ) {
921 this._setHTMLAttrConfig(key);
924 return AttributeProvider.prototype.set.apply(this, arguments);
927 setAttributeConfig: function(key, map, init) {
928 this._configOrder.push(key);
929 AttributeProvider.prototype.setAttributeConfig.apply(this, arguments);
932 createEvent: function(type, config) {
933 this._events[type] = true;
934 return AttributeProvider.prototype.createEvent.apply(this, arguments);
937 init: function(el, attr) {
938 this._initElement(el, attr);
941 destroy: function() {
942 var el = this.get('element');
943 YAHOO.util.Event.purgeElement(el, true); // purge DOM listeners recursively
944 this.unsubscribeAll(); // unsubscribe all custom events
946 if (el && el.parentNode) {
947 el.parentNode.removeChild(el); // pull from the DOM
950 // revert initial configs
954 this._configOrder = [];
957 _initElement: function(el, attr) {
958 this._queue = this._queue || [];
959 this._events = this._events || {};
960 this._configs = this._configs || {};
961 this._configOrder = [];
963 attr.element = attr.element || el || null;
965 var isReady = false; // to determine when to init HTMLElement and content
967 var DOM_EVENTS = Element.DOM_EVENTS;
968 this.DOM_EVENTS = this.DOM_EVENTS || {};
970 for (var event in DOM_EVENTS) {
971 if (DOM_EVENTS.hasOwnProperty(event)) {
972 this.DOM_EVENTS[event] = DOM_EVENTS[event];
976 if (typeof attr.element === 'string') { // register ID for get() access
977 this._setHTMLAttrConfig('id', { value: attr.element });
980 if (Dom.get(attr.element)) {
982 this._initHTMLElement(attr);
983 this._initContent(attr);
986 YAHOO.util.Event.onAvailable(attr.element, function() {
987 if (!isReady) { // otherwise already done
988 this._initHTMLElement(attr);
991 this.fireEvent('available', { type: 'available', target: Dom.get(attr.element) });
994 YAHOO.util.Event.onContentReady(attr.element, function() {
995 if (!isReady) { // otherwise already done
996 this._initContent(attr);
998 this.fireEvent('contentReady', { type: 'contentReady', target: Dom.get(attr.element) });
1002 _initHTMLElement: function(attr) {
1004 * The HTMLElement the Element instance refers to.
1005 * @attribute element
1008 this.setAttributeConfig('element', {
1009 value: Dom.get(attr.element),
1014 _initContent: function(attr) {
1015 this.initAttributes(attr);
1016 this.setAttributes(attr, true);
1022 * Sets the value of the property and fires beforeChange and change events.
1024 * @method _setHTMLAttrConfig
1025 * @param {YAHOO.util.Element} element The Element instance to
1026 * register the config to.
1027 * @param {String} key The name of the config to register
1028 * @param {Object} map A key-value map of the config's params
1030 _setHTMLAttrConfig: function(key, map) {
1031 var el = this.get('element');
1035 map.setter = map.setter || this.DEFAULT_HTML_SETTER;
1036 map.getter = map.getter || this.DEFAULT_HTML_GETTER;
1038 map.value = map.value || el[key];
1039 this._configs[key] = new YAHOO.util.Attribute(map, this);
1044 * Fires when the Element's HTMLElement can be retrieved by Id.
1045 * <p>See: <a href="#addListener">Element.addListener</a></p>
1046 * <p><strong>Event fields:</strong><br>
1047 * <code><String> type</code> available<br>
1048 * <code><HTMLElement>
1049 * target</code> the HTMLElement bound to this Element instance<br>
1050 * <p><strong>Usage:</strong><br>
1051 * <code>var handler = function(e) {var target = e.target};<br>
1052 * myTabs.addListener('available', handler);</code></p>
1057 * Fires when the Element's HTMLElement subtree is rendered.
1058 * <p>See: <a href="#addListener">Element.addListener</a></p>
1059 * <p><strong>Event fields:</strong><br>
1060 * <code><String> type</code> contentReady<br>
1061 * <code><HTMLElement>
1062 * target</code> the HTMLElement bound to this Element instance<br>
1063 * <p><strong>Usage:</strong><br>
1064 * <code>var handler = function(e) {var target = e.target};<br>
1065 * myTabs.addListener('contentReady', handler);</code></p>
1066 * @event contentReady
1070 * Fires before the Element is appended to another Element.
1071 * <p>See: <a href="#addListener">Element.addListener</a></p>
1072 * <p><strong>Event fields:</strong><br>
1073 * <code><String> type</code> beforeAppendTo<br>
1074 * <code><HTMLElement/Element>
1075 * target</code> the HTMLElement/Element being appended to
1076 * <p><strong>Usage:</strong><br>
1077 * <code>var handler = function(e) {var target = e.target};<br>
1078 * myTabs.addListener('beforeAppendTo', handler);</code></p>
1079 * @event beforeAppendTo
1083 * Fires after the Element is appended to another Element.
1084 * <p>See: <a href="#addListener">Element.addListener</a></p>
1085 * <p><strong>Event fields:</strong><br>
1086 * <code><String> type</code> appendTo<br>
1087 * <code><HTMLElement/Element>
1088 * target</code> the HTMLElement/Element being appended to
1089 * <p><strong>Usage:</strong><br>
1090 * <code>var handler = function(e) {var target = e.target};<br>
1091 * myTabs.addListener('appendTo', handler);</code></p>
1095 YAHOO.augment(Element, AttributeProvider);
1096 YAHOO.util.Element = Element;
1099 YAHOO.register("element", YAHOO.util.Element, {version: "2.9.0", build: "2800"});