Skip to content

Instantly share code, notes, and snippets.

@caridy
Created May 13, 2010 19:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caridy/400260 to your computer and use it in GitHub Desktop.
Save caridy/400260 to your computer and use it in GitHub Desktop.
Y.namespace('Plugin').NodeAccordion = Y.Base.create("NodeAccordion", Y.Plugin.Base, [], {
// Prototype Properties for NodeAccordion
/**
* @property _root
* @description Node instance representing the root node in the accordion.
* @default null
* @protected
* @type Node
*/
_root: null,
_eventHandler: null,
// Public methods
initializer: function (config) {
var _root = this._root = this.get(HOST);
if (_root) {
// close all items and open the actived ones
this.get(ATTR_ITEMS).each(function(item) {
if (item.hasClass(CLASS_ACTIVE)) {
this.expandItem(item);
} else {
this.collapseItem(item);
}
}, this);
// Wire up all event handlers
this._eventHandler = _root.delegate('click', function(e) {
Y.log ('Accordion Trigger: ' + e);
this.toggleItem(e.currentTarget); // probably is better to pass the ancestor for the item
e.target.blur();
e.halt();
}, ITEM_TRIGGER_QUERY, this);
// removing this class if exists, in case the accordion is hidden by default.
_root.removeClass(CLASS_ACCORDION_HIDDEN);
}
},
destructor: function () {
if (this._eventHandler) {
this._eventHandler.detach();
}
this._eventHandler = null;
},
// Protected methods
/**
* @method _getItem
* @description Searching for an item based on a node reference or an index order.
* @protected
* @param {Node|Number} node Node reference or Node index.
* @return {Node} The matching DOM node or null if none found.
*/
_getItem: function(node) {
if (Y.Lang.isNumber(node)) {
node = this.get(ATTR_ITEMS).item(node);
}
var fn = function(n) {
return n.hasClass(CLASS_ACCORDION_ITEM);
};
if (node && !node.hasClass(CLASS_ACCORDION_ITEM)) {
return node.ancestor( fn );
}
return node;
},
/**
* @method _animate
* @description Using Y.Anim to expand or collapse an item.
* @protected
* @param {String} id Global Unique ID for the animation.
* @param {Object} conf Configuration object for the animation.
* @param {Function} fn callback function that should be executed after the end of the anim.
* @return {Object} Animation handler.
*/
_animate: function(id, conf, fn) {
var anim = anims[id];
Y.log ('Anim Conf: ' + conf);
// if the animation is underway: we need to stop it...
if ((anim) && (anim.get ('running'))) {
anim.stop();
}
if (Y.Lang.isFunction(this.get(ATTR_ANIM))) {
conf.easing = this.get(ATTR_ANIM);
}
anim = new Y.Anim(conf);
anim.on('end', fn, this);
anim.run();
anims[id] = anim;
return anim;
},
/**
* @method _openItem
* @description Open an item.
* @protected
* @param {Node} item Node instance representing an item.
*/
_openItem: function (item) {
var bd,
id,
fn,
fs,
i,
list = this.get(ATTR_ITEMS),
o = this.get (ATTR_ORIENTATION),
conf = {
duration: this.get(ATTR_SPEED),
to: {
scroll: []
}
},
mirror;
// if the item is not already opened
if (item && list.size() && !item.hasClass(CLASS_ACTIVE) && (bd = item.one(SELECTOR_ACCORDION_ITEM_BD)) && (id = Y.stamp(bd))) {
// closing all the selected items if neccesary
if (!this.get(ATTR_MULTIPLE)) {
// close all items and open the actived ones
mirror = this._root.one(FC+CLASS_ACTIVE);
}
// opening the selected element, based on the orientation, timer and anim attributes...
conf.to[o] = (o==WIDTH?bd.get(SCROLL_WIDTH):bd.get(SCROLL_HEIGHT));
conf.node = bd;
item.addClass(CLASS_SLIDING);
fn = function() {
item.removeClass(CLASS_SLIDING);
item.addClass(CLASS_ACTIVE);
// broadcasting the corresponding event (close)...
// $B.fire ('accordionOpenItem', item);
};
if (!this.get(ATTR_ANIM)) {
// animation manually
// getting the desired dimension from the current item
fs = bd.get(o);
// override the desired dimension from the mirror if exists
if (Y.Lang.isObject(mirror)) {
fs = mirror.get(o);
mirror.addClass(CLASS_SLIDING);
}
for (i=1;i<=fs;i++){
if (Y.Lang.isObject(mirror)) {
mirror.setStyle (o, (fs-i)+PX);
}
bd.setStyle (o, i+PX);
}
if (Y.Lang.isObject(mirror)) {
mirror.removeClass(CLASS_SLIDING);
mirror.removeClass(CLASS_ACTIVE);
}
fn();
} else {
// scrolling effect
conf.to.scroll = [0,0];
// appliying fadeIn
if (this.get(ATTR_FADE)) {
conf.to.opacity = 1;
}
if (Y.Lang.isObject(mirror)) {
this._closeItem(mirror);
}
this._animate(id, conf, fn);
}
}
},
/**
* @method _closeItem
* @description Closes the specified item.
* @protected
* @param {Node} item Node instance representing an item.
*/
_closeItem: function (item) {
var bd,
id,
fn,
fs,
i,
list = this.get(ATTR_ITEMS),
o = this.get (ATTR_ORIENTATION),
conf = {
duration: this.get(ATTR_SPEED),
to: {
scroll: []
}
};
if (item && list.size() && (bd = item.one(SELECTOR_ACCORDION_ITEM_BD)) && (id = Y.stamp(bd))) {
// closing the item, based on the orientation, timer and anim attributes...
conf.to[o] = (((o==HEIGHT) && UA.ie && (UA.ie<7))?1:0); // hack for vertical accordion issue on Safari and Opera
conf.node = bd;
item.addClass(CLASS_SLIDING);
fn = function() {
item.removeClass(CLASS_SLIDING);
item.removeClass(CLASS_ACTIVE);
// broadcasting the corresponding event (close)...
// $B.fire ('accordionCloseItem', item);
};
if (!this.get(ATTR_ANIM)) {
// animation manually
fs = bd.get(o);
for (i=fs;i>=conf.to[o].to;i--){
bd.setStyle (o, i+PX);
}
fn();
} else {
// scrolling effect
conf.to.scroll = (o==WIDTH?[bd.get(SCROLL_WIDTH),0]:[0,bd.get(SCROLL_HEIGHT)]);
// appliying fadeIn
if (this.get(ATTR_FADE)) {
conf.to.opacity = 0;
}
this._animate(id, conf, fn);
}
}
},
// Generic DOM Event handlers
/**
* @method expandAllItems
* @description Expanding all items.
* @public
* @return {object} Plugin reference for chaining
*/
expandAllItems: function () {
Y.log(("Expanding all items (only if attr multiple=true): " + this._root), "info", "nodeAccordion");
if (this.get(ATTR_MULTIPLE)) {
this.get(ATTR_ITEMS).each(function (node) {
this.expandItem(node);
}, this);
}
return this;
},
/**
* @method collapseAllItems
* @description Collapsing all items.
* @public
* @return {object} Plugin reference for chaining
*/
collapseAllItems: function () {
Y.log(("Collapsing all items (only if attr multiple=true or attr persistent=false): " + this._root), "info", "nodeAccordion");
if (this.get(ATTR_MULTIPLE) || !this.get(ATTR_PERSISTENT)) {
this.get(ATTR_ITEMS).each(function (node) {
this.collapseItem(node);
}, this);
}
return this;
},
/**
* @method expandItem
* @description Expand a certain item.
* @public
* @param {Node} node Node reference
* @return {object} Plugin reference for chaining
*/
expandItem: function ( node ) {
var item = this._getItem(node);
if (item) {
Y.log(("Expanding an item: " + item), "info", "nodeAccordion");
this._openItem (item);
}
return this;
},
/**
* @method collapseItem
* @description Collapse a certain item.
* @public
* @param {Node} node Node reference
* @return {object} Plugin reference for chaining
*/
collapseItem: function ( node ) {
var item = this._getItem(node);
if (item && item.hasClass(CLASS_ACTIVE) && (this.get(ATTR_MULTIPLE) || !this.get(ATTR_PERSISTENT))) {
Y.log(("Collapse an item: " + item), "info", "nodeAccordion");
this._closeItem(item);
}
return this;
},
/**
* @method toggleItem
* @description toggle a certain item.
* @public
* @param {object} node Node reference
* @return {object} Plugin reference for chaining
*/
toggleItem: function ( node ) {
var item = this._getItem(node);
Y.log ('Looking for accordion item: ' + SELECTOR_ACCORDION_ITEM);
if (item) {
// if the item is already opened, and is multiple and not persistent
Y.log(("Toggling an item: " + item), "info", "nodeAccordion");
((item.hasClass(CLASS_ACTIVE) && (this.get(ATTR_MULTIPLE) || !this.get(ATTR_PERSISTENT)))?this._closeItem (item):this._openItem (item));
}
return this;
}
}, {
// Static Properties for DynamicForm
NS: ACCORDION,
/**
* @property DynamicForm.ATTRS
* @type Object
* @static
*/
ATTRS : {
/**
* Nodes representing the list of active items.
*
* @attribute activeItems
* @readOnly
* @type Y.NodeList
*/
activeItems: {
readOnly: true,
getter: function (value) {
return this._root.all(FC+CLASS_ACTIVE);
}
},
/**
* Nodes representing the list of items.
*
* @attribute items
* @readOnly
* @type Y.NodeList
*/
items: {
readOnly: true,
getter: function (value) {
return this._root.all(ITEM_QUERY);
}
},
/**
* orientation defines if the accordion will use width or height to expand and collapse items.
*
* @attribute orientation
* @writeOnce
* @default height
* @type string
*/
orientation: {
value: HEIGHT,
writeOnce: true
},
/**
* Boolean indicating that animation should include opacity to fade in/out the content of the item.
*
* @attribute fade
* @default false
* @type boolean
*/
fade: {
value: false
},
/**
* Boolean indicating that Y.Anim should be used to expand and collapse items.
* It also supports a function with an specific effect.
* <p>
* <code>
* &#60;script type="text/javascript"&#62; <br>
* <br>
* // Call the "use" method, passing in "anim" and "gallery-node-accordion". <br>
* <br>
* YUI().use("anim", "gallery-node-accordion", function(Y) { <br>
* <br>
* Y.one("#myaccordion").plug(Y.Plugin.NodeAccordion, {<br>
* anim: Y.Easing.backIn<br>
* }); <br>
* <br>
* &#60;/script&#62; <br>
* </code>
* </p>
*
* @attribute anim
* @default false
* @type {boolean|function}
*/
anim: {
value: false,
validator : function (v) {
return !Y.Lang.isUndefined(Y.Anim);
}
},
/**
* Boolean indicating that more than one item can be opened at the same time.
*
* @attribute multiple
* @default true
* @type boolean
*/
multiple: {
value: true
},
/**
* Boolean indicating that one of the items should be open at any given time.
*
* @attribute persistent
* @default false
* @type boolean
*/
persistent: {
value: false
},
/**
* Numeric value indicating the speed in mili-seconds for the animation process.
* Also support three predefined strings in lowercase:
* <ol>
* <li>fast = 0.1</li>
* <li>normal = 0.4</li>
* <li>slow = 0.6</li>
* </ol>
*
* @attribute speed
* @default 0.4
* @type numeric
*/
speed: {
value: 0.4,
validator : function (v) {
return (Y.Lang.isNumber(v) || (Y.Lang.isString(v) && WHEELS.hasOwnProperty(v)));
},
setter : function (v) {
return (WHEELS.hasOwnProperty(v)?WHEELS[v]:v);
}
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment