Skip to content

Instantly share code, notes, and snippets.

@cimi
Created January 13, 2012 12:43
Show Gist options
  • Save cimi/1605928 to your computer and use it in GitHub Desktop.
Save cimi/1605928 to your computer and use it in GitHub Desktop.
Generic Functions for OOP in JavaScript
define([], function () {
var helpers;
var Expandable = new Class;
Expandable.include({
init : function (element) {
this.el = element;
this.parentList = helpers.getParentList(this.el);
this.menu = helpers.getMenu(this.el);
this.exclusive = true;
if (this.menu && this.menu.el.parentNode != this.el.parentNode) {
this.el.parentNode.appendChild(this.menu.el);
}
this.el.addEventListener("click", this.proxy(this.toggle), false);
this.expand(this.isExpanded());
},
isExpanded : function () {
return expandedClassRE.test(this.el.className);
},
expand : function (expanded) {
helpers.setClassName(this.el, expanded ? "expanded" : "collapsed");
helpers.setClassName(this.menu.el, expanded ? "expanded" : "collapsed");
if (expanded) {
if (this.exclusive) {
if (!this.parentList.supportsMultiple()) {
Expandable.collapseSiblings(this);
}
}
this.putInView();
} else {
if (this.menu.el.offsetHeight > 0) {
this.menu.expandedHeight = this.menu.el.offsetHeight;
};
}
},
toggle : function (event) {
this.expand(!this.isExpanded());
event.preventDefault();
return false;
},
putInView : function () {
// scroll to this expand link
var menuOffset = getOffset(this.menu.el), anchorOffset = getOffset(this.el);
// we will scroll to the expanded element only if it is not fully visible
if ((this.menu.expandedHeight === 0) || (menuOffset.top + this.menu.expandedHeight > window.getHeight() + getScrollOffset().top)) {
// if the link is near to the expanded element scroll to link instead
if (anchorOffset.top + this.offsetHeight - menuOffset.top < 20) {
Common.scrollSmooth(anchorOffset);
} else {
Common.scrollSmooth(menuOffset);
}
}
}
});
Expandable.extend({
expandables : [],
init : function (document) {
var links = document.querySelectorAll("A.expanded, A.collapsed");
for (var i = 0, len = links.length; i < len; i++) {
if (links[i].getAttribute("href")) {
Expandable.expandables.push(new Expandable(links[i]));
}
}
},
find : function (element) {
var collection = Expandable.expandables;
for (var i = 0, len = collection.length; i < len; i++) {
if (collection[i].el == element) {
return collection[i];
}
}
return undefined;
},
collapseSiblings : function (expandable) {
var sibling, siblings = expandable.parentList.getElementsByTagName("A");
for (var i = 0, len = siblings.length; i < len; i++) {
if (expandable.el != siblings[i]) {
sibling = Expandable.find(siblings[i]);
if (sibling) sibling.expand(false);
}
}
}
});
helpers = {
classRE : /\b(expanded|collapsed)\b/,
expandedClassRE = /\b(expanded)\b/,
getMenu : function (el) {
var menuId = el.getAttribute("href");
var idx = menuId.indexOf("#");
if (idx >= 0)
menuId = menuId.substring(idx + 1);
var menu = { el : el.ownerDocument.getElementById(menuId) };
// if the menu doesn't have a classname add one
if (!classRE.test(menu.el.className)) {
menu.expandedHeight = menu.el.offsetHeight;
menu.el.className += expandedClassRE.test(el.className) ? " expanded" : " collapsed";
};
return menu;
},
getParentList : function (el) {
do {
el = el.parentNode;
} while (el != null && el.tagName !== "UL");
return {
el : el,
supportsMultiple : function () {
return expandable.el.getAttribute("data-multiple") !== "true";
},
toggleChildren : function (target) {
var children = expandable.el.getElementsByTagName("A");
for (var i = 0, len = children.length; i < len; i++) {
if (target.el != children[i]) {
var current = Expandable.find(children[i]);
current.expand(false);
}
}
}
}
},
setClassName : function (element, state) {
element.className = element.className.replace(classRE, state);
}
}
return Expandable;
});
var Class = function () {
var klass = function () {
this.init.apply(this, arguments);
};
klass.prototype.init = function () {};
klass.fn = klass.prototype;
klass.extend = function (obj) {
for (var i in obj) {
klass[i] = obj[i];
}
};
klass.include = function (obj) {
for (var i in obj) {
klass.fn[i] = obj[i];
}
};
klass.proxy = function (func) {
var self = this;
return (function () {
return func.apply(self, arguments);
});
};
klass.fn.proxy = klass.proxy;
return klass;
};
@cimi
Copy link
Author

cimi commented Jan 13, 2012

This class function is inspired from the JavaScript Web Applications book (http://shop.oreilly.com/product/0636920018421.do).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment