From 1b5fbf862292ed2d26c4e7c0bd6da3df310cb291 Mon Sep 17 00:00:00 2001 From: JT Smith Date: Sun, 23 Feb 2003 16:56:55 +0000 Subject: [PATCH] Adding Tab form support --- www/extras/tabs/cookie.js | 58 ++++ www/extras/tabs/global.js | 126 ++++++++ www/extras/tabs/tabs.css | 42 +++ www/extras/tabs/tabs.js | 556 ++++++++++++++++++++++++++++++++++++ www/extras/tabs/utils.js | 160 +++++++++++ www/extras/tabs/viewport.js | 45 +++ 6 files changed, 987 insertions(+) create mode 100644 www/extras/tabs/cookie.js create mode 100644 www/extras/tabs/global.js create mode 100644 www/extras/tabs/tabs.css create mode 100644 www/extras/tabs/tabs.js create mode 100644 www/extras/tabs/utils.js create mode 100644 www/extras/tabs/viewport.js diff --git a/www/extras/tabs/cookie.js b/www/extras/tabs/cookie.js new file mode 100644 index 000000000..1d622dfbc --- /dev/null +++ b/www/extras/tabs/cookie.js @@ -0,0 +1,58 @@ +/** + * cookie.js + * by Garrett Smith + * Updated 11-29-2002 + * + * getCookie function based upon: + * Cookie API v1.0 + * http://www.dithered.com/javascript/cookies/index.html + * maintained by Chris Nott (chris@NOSPAMdithered.com - remove NOSPAM) + */ + +// Write a cookie value based on the current directory. +function setPageCookie(name, value) { + document.cookie = name + "=" + escape(value) + "; path=" + getPath(); +} + +// Retrieve a named cookie value +function getCookie(name) { + var dc = document.cookie; + + // find beginning of cookie value in document.cookie + var prefix = name + "="; + var begin = dc.lastIndexOf(prefix); + if (begin == -1) return null; + + // find end of cookie value + var end = dc.indexOf(";", begin); + if (end == -1) end = dc.length; + + // return cookie value + return unescape(dc.substring(begin + prefix.length, end)); +} + +function deletePageCookie(name, path) { + var value = getCookie(name); + if (value != null) + document.cookie = + name + "=" + + "; path=" + getPath() + + "; expires=Thu, 01-Jan-70 00:00:01 GMT"; + return value; +} + + +function getFilename(){ + var href = window.location.href; + var file = href.substring(href.lastIndexOf("/") +1); + return file; +} + +function getPath(){ + var href = window.location.href; + var path = href.substring(href.indexOf("//")+2); + path = path.substring(path.indexOf("/")); + path = path.substring(0, path.lastIndexOf("/")+1); + + return path; +} \ No newline at end of file diff --git a/www/extras/tabs/global.js b/www/extras/tabs/global.js new file mode 100644 index 000000000..deaf81540 --- /dev/null +++ b/www/extras/tabs/global.js @@ -0,0 +1,126 @@ +/** + * global.js + * by Garrett Smith + * Provides functionality for extending custom classes. + * + * ElementWrapper is a simple wrapper class. + * + * EventQueue provides Event Listener Functionality. + */ + + +/** Provides functionality for extending classes. + * + */ +Function.prototype.extend = function(souper) { + this.prototype = new souper; + this.prototype.constructor = this; + this.souper = souper; + this.prototype.souper = souper; +}; +/** Generic Element Wrapper + * + */ +ElementWrapper = function ElementWrapper(el){ + if(arguments.length == 0) return; + this.el = el; + this.id = el.id; + if(!ElementWrapper.list[this.id]) + ElementWrapper.list[this.id] = this; +}; + +ElementWrapper.list = new function(){}; + +ElementWrapper.getWrapper = function(id){ + return ElementWrapper.list[id]; +}; + + +/** EventQueue class Provides Event Listener Functionality. + * + * Instance Methods: + * + * addEventListener(etype, pointer) + * + * + */ +EventQueue = function EventQueue(eventObj){ + if(arguments.length == 0) return; + this.souper = EventQueue.souper; + this.souper(eventObj); + + this.addToPool(); +}; + +EventQueue.extend(ElementWrapper); + +EventQueue.prototype.addEventListener = function(etype, pointer){ + var list = this.eventHandlerList(etype); + return list[list.length++] = pointer; +}; + +EventQueue.prototype.eventHandlerList = function(etype){ + + if(!this[etype]) + this[etype] = new EventQueue.EventHandler(this, etype); + + return this[etype]; +}; + +EventQueue.prototype.removeEventListener = function(etype, pointer){ + var list = this[etype]; + var len = list.length; + if(len == 0) return null; + + var newList = new Array(len-1); + var rtn = null; + for(var i = 0; i < len; i++) + if(list[i] != pointer) + newList[i] = list[i]; + else rtn = pointer; + + this[etype] = newList; + return rtn; +}; + + +EventQueue.prototype.handleEvent = function(e){ + + var rtn = true; + for(var i = 0, len = this[e].length; i < len; i++){ + this.tempFunction = this[e][i]; + if(rtn != false) + rtn = this.tempFunction(); + } + return rtn; +}; + +EventQueue.prototype.addToPool = function(){ + if(!EventQueue.list[this.id]) + EventQueue.list[this.id] = this; +}; + + +/** EventQueue.EventHandler is private to EventQueue class. + * Instantiation and method calls are made automatically from + * EventQueue constructor and instance methods. + */ +EventQueue.EventHandler = function EventHandler(wrapper, etype){ + this.etype = etype; + this.length = 0; + this.id = wrapper.id; + wrapper.el[etype] = new Function("return EventQueue.fireEvent('"+wrapper.id+"', '"+etype+"')"); +}; + +EventQueue.fireEvent = function(id, e){ + var wrapper = EventQueue.list[id]; + if(!wrapper) return false; + var r = wrapper.handleEvent(e); + return r; + +}; +EventQueue.EventHandler.prototype.toString = function toString(){ + return this.id +"." +this.etype; +}; + +EventQueue.list = new Object; diff --git a/www/extras/tabs/tabs.css b/www/extras/tabs/tabs.css new file mode 100644 index 000000000..0d93adef1 --- /dev/null +++ b/www/extras/tabs/tabs.css @@ -0,0 +1,42 @@ + .tabBody { + position: relative; + padding: 8px 12px 12px 12px; + z-index: 500; + } + div.tabs { + position: relative; + top: 2px; + left: 5px; + white-space: nowrap; + cursor: default !important; + font-weight: 700 !important; + white-space:nowrap; + z-index: 10000; + /* -Moz-User-Select: none;*/ + } + .tab { + + padding: 2px 9px 1px 9px; + z-index: 100; + border-bottom-width: 0; + } + .tabHover { + z-index: 1200; + border-bottom-width: 0; + } + .tabActive { + padding: 3px 9px 3px 9px; + z-index: 10000; + } + + div.tabsClone .tab, div.tabsClone .tabActive { + border-top-width: 0 !important; + border-bottom-width: 1px !important; + } + + div.tabsClone .tabActive{top:-2px;} + + a.removeTab{ + font-size: smaller; + text-decoration: none; + } diff --git a/www/extras/tabs/tabs.js b/www/extras/tabs/tabs.js new file mode 100644 index 000000000..2fc14a258 --- /dev/null +++ b/www/extras/tabs/tabs.js @@ -0,0 +1,556 @@ +/** + * tabs.js + * by Garrett Smith + * http://dhtmlkitchen.com/ + */ + +if(!window.TabParams) +window.TabParams = { + useClone : false, + alwaysShowClone : false, + eventType : "click", + tabTagName : "*" +}; + +var tabDisplayNone = Browser.id.OP5 ? "" : "none"; +var contentInheritVis = Browser.id.OP5 ? "visible" : "inherit"; + +TabSystem = function TabSystem(el, tabsDiv){ + if(arguments.length == 0) return; + this.souper = TabSystem.souper; + this.souper(el); + + if(typeof tabsDiv.onselectstart != "undefined") + tabsDiv.onselectstart = function(){return false;}; + + this.el.onChange = this.el.onchange = function(){}; + this.el.onBeforeChange = function(){}; + + this.defaultActiveTab = null; + this.activeTab = null; + this.relatedTab = null; + this.nextTab = null; + + this.tabsDiv = tabsDiv; + this.tabParams = this.getTabParams(); + this.tabArray = get_elements_with_class_from_classList(this.tabsDiv, + this.tabParams.tabTagName, + ["tab", "tabActive"]); + this.tabsClone = null; + this.tabs = new Array(0); + + if(!TabSystem.list[this.id]) + TabSystem.list[this.id] = this; +}; + +TabSystem.list = new Object; +TabSystem.extend(EventQueue); + +TabSystem.prototype.parentSystem = function(){ + + var root = TabSystem.list["body"]; + if(root = this) return null; + + var parent = findAncestorWithClass(this.el, "content"); + if(parent != null) + return TabSystem.list[parent.id]; + return root; +}; + +TabSystem.prototype.getTabParams = function(){ + + if(!this.tabParams){ + this.tabParams = new Object; + var parentSystem = this.parentSystem(); + parentTp = (parentSystem == null) ? + TabParams : parentSystem.getTabParams(); + + for(var param in parentTp) + this.tabParams[param] = parentTp[param]; + } + return this.tabParams; +}; + +TabSystem.prototype.setEventType = function(eventType) { + + var params = this.getTabParams(); + if(params.eventType == eventType) return; + + for(var i = 0, len = this.tabArray.length; i < len; i++){ + var tab = Tab.list[this.tabArray[i].id]; + tab.removeEventListener("on"+params.eventType, tab.depressTab); + tab.addEventListener("on"+eventType, tab.depressTab); + } + + params.eventType = eventType; +}; + +function removeTabs(ts){ + + ts.tabsDiv.style.display = "none"; + if(ts.tabsClone) + ts.tabsClone.style.display = "none"; + + var cs = getElementsWithClass(ts.el, "div", "content"); + for(var i = 0; i < cs.length; i++){ + cs[i].style.visibility='visible'; + cs[i].style.display='block'; + } +} + +function undoRemoveTabs(ts){ + ts.tabsDiv.style.display = "block"; + if(ts.tabsClone) + ts.tabsClone.style.display = "block"; + isTabLayout = true; + for(var i = 0; i < ts.tabs.length; i++) + if(ts.tabs[i] != ts.activeTab){ + ts.tabs[i].content.style.display = "none"; + ts.tabs[i].content.style.visibility = "hidden"; + } +} + +TabSystem.prototype.setAlwaysShowClone = function(flag) { + this.getTabParams().alwaysShowClone = flag; + this.showTabsCloneIfNecessary(); +}; + +TabSystem.prototype.addClone = function() { + + if(!this.tabsDiv.cloneNode) return; + + this.getTabParams().useClone = true; + this.tabsClone = this.tabsDiv.cloneNode(true); + if(!this.tabsClone) return; + this.tabsClone.className = "tabs tabsClone"; + this.el.appendChild(this.tabsClone); + + for(var i = 0; i < this.tabArray.length; i++){ + var cont = Tab.list[this.tabArray[i].id]; + var bt = getDescendantById(this.tabsClone, cont.id); + bt.id = "Bottom" + bt.id; + cont.bottomTab = new BottomTab(bt, cont); + + } + this.addEventListener("onchange", updateTabsClonePosition); + if(Browser.id.MAC_IE5) + window.setInterval("updateTabsClonePosition()", 300); + contentPane.addEventListener("onresize", updateTabsClonePosition); + this.showTabsCloneIfNecessary(); + +}; + + +tabInit = function tabInit(){ + + if(!Browser.isSupported()) + return; + + + var tabsDivs = getElementsWithClass(document.body, "div", "tabs"); + + if(tabsDivs.length == 0) {// back compat. + var tabsDiv0 = document.getElementById("tabs"); + if(tabsDiv0) + tabsDivs = [tabsDiv0]; + else return; + } + var tabToDepress; + for(var i = 0; i < tabsDivs.length; i++){ + var cnt = findAncestorWithClass(tabsDivs[i], "content") || document.body; + if(!cnt.id) + cnt.id = "body"; + var ts = new TabSystem(cnt, tabsDivs[i]); + var len = ts.tabArray.length; + for(var j = 0; j < len; new ControllerTab(ts.tabArray[j++], ts)); + } + var activeTabs = getCookie("activeTabs"+escape(getFilename())); + if(activeTabs != null){ + var activeTabArray = activeTabs.split(","); + for(var i = 0, len = activeTabArray.length; i < len; i++){ + var tab = Tab.list[activeTabArray[i]]; + if(tab) + tab.depressTab(); + } + } + + + + if(Browser.id.MAC_IE5){ + fixDocHeight = function(){ + document.documentElement.style.height = + document.body.style.height = + document.body.clientHeight + "px"; + }; + contentPane.addEventListener("onresize", fixDocHeight); + setTimeout("fixDocHeight()", 500); + + } + + // hash overrides cookie. + handleHashNavigation(); + + deletePageCookie("activeTabs"+escape(getFilename())); + for(id in TabSystem.list){ + var ts = TabSystem.list[id]; + if(ts.tabParams.useClone) + ts.addClone(); + if(ts.activeTab == null && ts.defaultActiveTab != null) + ts.defaultActiveTab.depressTab(); + } + if(Browser.id.MOZ) + repaintFix(document.body); +}; +window.id = "window"; +contentPane = new EventQueue(window); +//contentPane.addEventListener("onload", initTabs); + +function handleHashNavigation(){ + var id = window.location.hash; + if(id){ + var el = document.getElementById(id.substring(1)); + if(el) { + var contentEl; + if(hasToken(el.className, "content")) + contentEl = el; + else contentEl = findAncestorWithClass(el, "content"); + if(contentEl) + switchTabs("tab"+contentEl.id.substring("content".length), null, false); + } + } +} + + +/** + * Tab base class + */ +Tab = function Tab(el, ts){ + + if(arguments.length == 0) return; + this.souper = Tab.souper; + this.souper(el); + this.content = null; + this.tabSystem = ts; + this.properties = new Object; + + this.el.onActivate = function(){}; + + this.addEventListener("onmouseover", this.hoverTab); + this.addEventListener("onmouseout", this.hoverOff); + this.addEventListener("on"+ this.tabSystem.getTabParams().eventType, this.depressTab); + + if(Browser.id.IE5_0) + positionTabEl(this); + if(!Tab.list[this.id]) + Tab.list[this.id] = this; +}; +Tab.extend(EventQueue); + +Tab.list = new Object; + +Tab.prototype.setProperty = function(name, value){ + this.properties[name] = value; +}; + + +Tab.prototype.getContent = function(){ + if(this.content == null){ + var id = this.id.substring(3); + this.content = document.getElementById("content"+id); + if(!this.content){ + alert("tab.id = "+this.id +"\n" + + "content"+id+" does not exist!"); + } + } + return this.content; +}; + +Tab.prototype.getTabSystem = function(){ return this.tabSystem; }; + +hoverTab = function hoverTab() { + + var tab = Tab.list[this.id]; + + var activeTab = tab.tabSystem.activeTab; + if(activeTab && activeTab.id == tab.id) return; + + tab.setClassName("tabHover tab"); + + if(tab.hoversrc) + tab.el.src = tab.hoversrc; +}; + +hoverOff = function hoverOff() { + + var tab = Tab.list[this.id]; + + var activeTab = tab.tabSystem.activeTab; + if(activeTab && activeTab.id == tab.id) return; + + tab.setClassName("tab"); + + if(tab.normalsrc) + tab.el.src = tab.normalsrc; +}; + + +Tab.prototype.toString = function(){return this.id;}; + +/** Resets a tab to default state. + */ +function resetTab(tab) { + + tab.setClassName("tab"); + + if(tab.normalsrc) + tab.el.src = tab.normalsrc; + + tab.getContent().style.display = tabDisplayNone; + + tab.getContent().style.visibility = "hidden"; +} + + + +/** + * ControllerTab class + */ +ControllerTab = function ControllerTab(el, ts){ + + if(arguments.length == 0) return; + + this.souper(el, ts); + + if(el.tagName.toLowerCase() == "img"){ + + this.normalsrc = el.src; + this.hoversrc = el.getAttribute("hoversrc"); + this.activesrc = el.getAttribute("activesrc"); + + } + + if(hasToken(el.className, "tabActive")){ + + this.depressTab(); + this.tabSystem.defaultActiveTab = this; + + } + else { + this.getContent().style.display = tabDisplayNone; + this.getContent().style.visibility = "hidden"; + if(Browser.id.OP5)setTimeout('Tab.list.'+this.id+'.content.style.position = "absolute";',50); + } + this.tabSystem.tabs[this.tabSystem.tabs.length] = this; +}; +ControllerTab.extend(Tab); + + +ControllerTab.prototype.setClassName = function(klass){ + + this.el.className = klass; + if(this.bottomTab) + this.bottomTab.el.className = klass; +}; + +/** ControllerTab Event Handler Methods + * + * hoverTab() - called from el onmouseover and invoked by bottomTab. + * hoverOff() - called from el onmouseout and invoked by bottomTab. + * + * + * depressTab(e) - called with "on"+eventType + */ +ControllerTab.prototype.hoverTab = hoverTab; +ControllerTab.prototype.hoverOff = hoverOff; + +ControllerTab.prototype.depressTab = function depressTab(e) { + + var tab = Tab.list[this.id]; + var tabSystem = tab.tabSystem; + + tabSystem.nextTab = tab; + + if(tabSystem.activeTab == tab) return; + + tabSystem.relatedTab = tabSystem.activeTab; + + if(false == tabSystem.el.onBeforeChange()) return; + + tab.el.onActivate(); + + tab.setClassName("tab tabActive"); + + if(tab.activesrc) + tab.el.src = tab.activesrc; + + if(tabSystem.activeTab) + resetTab(tabSystem.activeTab); + + tabSystem.activeTab = tab; + tabSystem.el.onchange(); + + if(tabSystem.relatedTab) + tabSystem.relatedTab.getContent().style.display = "none"; + tab.getContent().style.display = "block"; + + tab.getContent().style.visibility = contentInheritVis; + + tabSystem.nextTab = null; + + if(tabSystem.tabsClone) + tabSystem.showTabsCloneIfNecessary(); + if(Browser.id.MOZ) + updateTabsClonePosition(1); +}; + + +/** + * BottomTab class + */ +BottomTab = function BottomTab(el, controllerTab) { + if(arguments.length == 0) return; + + this.souper(el, controllerTab.tabSystem); + + this.controllerTab = controllerTab; +}; +BottomTab.extend(Tab); + + +/** BottomTab Event Handler Methods + * + * hoverTab() - called from el onmouseover and invokes controllerTab.hoverTab + * hoverOff() - called from el onmouseout and invokes controllerTab.hoverOff. + * + * + * depressTab(e) - called with "on"+eventType + */ +BottomTab.prototype.hoverTab = function(){ this.controllerTab.hoverTab(); }; +BottomTab.prototype.hoverOff = function(){ this.controllerTab.hoverOff(); }; + + +BottomTab.prototype.depressTab = function depressClonedTab(e){ + + var tabSystem = this.tabSystem; + + if(tabSystem.activeTab == this.controllerTab) return; + + this.controllerTab.depressTab(e); + this.controllerTab.setClassName("tab tabActive"); + window.scrollTo(0, (tabSystem.tabsClone.offsetTop + this.el.offsetHeight) - getViewportHeight()); +}; + + + +/** switchTabs(id, e, bReturn) + * + * USAGE: + * [a href='#elmID' onclick='return switchTabs("tab2", event, true);'] + * + * Will switch to tab2 and then scroll elemId into view. + * + * switchTabs(tabId, event, bReturn); + */ +function switchTabs(id, e, bReturn) { + + if(!Browser.isSupported()) + return true; + + try{ + var tab = Tab.list[id]; + tab.depressTab(e); + }catch(ex){ + //alert(ex); + } + if(!bReturn) + window.scrollTo(0,0); + + return bReturn; +} + +/** + * updating and positioning a tabsClone + */ +updateTabsClonePosition = function updateTabsClonePosition(delay){ + for(var id in TabSystem.list) + if(TabSystem.list[id].tabParams.useClone) + setTimeout("TabSystem.list."+id+".setTabsClonePosition();", delay || 500); +}; + + +TabSystem.prototype.setTabsClonePosition = function(){ + + if(!this.activeTab) return; + + var adjustment = 0; + var contentEl = this.activeTab.content; + if(Browser.id.IE5_0 || Browser.id.MAC_IE5) + adjustment = 0; + else + adjustment = 2; + + this.tabsClone.style.top = (contentEl.offsetHeight + contentEl.offsetTop + adjustment)+px; +}; + +TabSystem.prototype.showTabsCloneIfNecessary = function(){ + + if(!this.activeTab) return; + + var contentEl = this.activeTab.content; + + var contentBottom = contentEl.offsetTop + contentEl.offsetHeight; + var visibility = + (contentBottom > getViewportHeight() || this.getTabParams().alwaysShowClone) ? + "inherit" : "hidden"; + this.tabsClone.style.visibility = visibility; + this.setTabsClonePosition(); + + if(Browser.id.MOZ){ + // what's that grey blank on the screen? + window.scrollBy(0, 1); + window.scrollBy(0,-1); + } + +}; + + +function saveTabSystemState(){ + + var activeTabList = getElementsWithClass(document.body, TabParams.tabTagName, "tabActive"); + for(var i = 0; i < activeTabList.length; i++){ + if(!activeTabList[i].id) continue; + activeTabList[i] = activeTabList[i].id; + setPageCookie("activeTabs"+escape(getFilename()), activeTabList); + } +}; + +contentPane.addEventListener("onunload", saveTabSystemState); + + + +function positionTabEl(tab){ +// add the width of previous tab plus padding + + var tabs = tab.el.parentNode; + if(tab.tagName == "IMG" || tab.id.indexOf("Bottomtab") == 0) + return; + + if(!tabs.tabOffset) + tabs.tabOffset = 0; + var tabWidth = Math.round(tab.el.offsetWidth*1.1)+15; + + var sty = tab.el.style; + + sty.left = tabs.tabOffset +px; + + //add 9px padding to l and r sides + sty.width = tabWidth +px; + sty.textAlign = "center"; + sty.display= "block"; + sty.position= "absolute"; + + + // add the width of previous tab plus tab-spacing (4) + tabs.tabOffset += parseInt(tab.el.offsetWidth) + 4; +} +if(Browser.id.OP5)window.document.write(""); \ No newline at end of file diff --git a/www/extras/tabs/utils.js b/www/extras/tabs/utils.js new file mode 100644 index 000000000..a3186fd6b --- /dev/null +++ b/www/extras/tabs/utils.js @@ -0,0 +1,160 @@ +/** + * util.js + * by Garrett Smith + * Provides functionality for working nodeLists. + */ +Browser = { + isSupported : function(){ + return (Boolean(document.getElementsByTagName) + && Boolean(document.getElementById)); + }, + id : new function() { + + var ua= navigator.userAgent; + var OMNI = ua.indexOf("Omni") > 0; + + this.OP5 = ua.indexOf("Opera 5") >= 0 || ua.indexOf("Opera 6") >= 0; + this.OP7 = ua.indexOf("Opera 7") >= 0; + this.MAC = ua.indexOf("Mac") > 0; + + if(!this.OP5 && !OMNI){ + this.IE5 = ua.indexOf("MSIE 5") > 0; + this.IE5_0 = ua.indexOf("MSIE 5.0") > 0; + this.NS6 = ua.indexOf("Gecko") > 0; + this.MOZ = this.NS6 && ua.indexOf("Netscape") == -1; + this.MAC_IE5 = this.MAC && this.IE5; + this.IE6 = ua.indexOf("MSIE 6") > 0; + this.KONQUEROR = ua.indexOf("Konqueror/") > 0; + } + } +}; +var px = "px"; +TokenizedExps = {}; +function getTokenizedExp(token){ + var x = TokenizedExps[token]; + if(!x) + x = TokenizedExps[token] = new RegExp("\\b"+token+"\\b"); + return x; +} + +function hasToken(s, token){ + return getTokenizedExp(token).test(s); +}; + +/** returns an array of all childNodes + * who have a className that matches the className + * parameter. + * + * Nested elements are not returned, only + * direct descendants (i.e. childNodes). + */ +function getChildNodesWithClass(parent, klass){ + + var collection = parent.childNodes; + var returnedCollection = []; + var exp = getTokenizedExp(klass); + + for(var i = 0, counter = 0; i < collection.length; i++) + if(exp.test(collection[i].className)) + returnedCollection[counter++] = collection[i]; + + return returnedCollection; +} + +/** Obtains a NodeList of all descendant elements + * who have a className that matches the className + * parameter. This method differs from getChildNodesWithClass + * because it returns ALL descendants (deep). + */ +function getElementsWithClass(parent, tagName, klass){ + var returnedCollection = []; + + var exp = getTokenizedExp(klass); + var collection = (tagName == "*" && parent.all) ? + parent.all : parent.getElementsByTagName(tagName); + + for(var i = 0, counter = 0; i < collection.length; i++){ + + if(exp.test(collection[i].className)) + returnedCollection[counter++] = collection[i]; + } + return returnedCollection; +} + +/** Returns an Array of all descendant elements + * where each element has a className that matches + * any of the classNames in classList. + * + * This method is like getElementsWithClass except it accepts + * an Array of classes to search for. + */ + +function get_elements_with_class_from_classList(el, tagName, classList){ + + var returnedCollection = new Array(0); + + var collection = (tagName == "*" && el.all) ? + el.all : el.getElementsByTagName(tagName); + var exps = []; + for(var i = 0; i < classList.length; i++) + exps[i] = getTokenizedExp(classList[i]); + for(var j = 0, coLen = collection.length; j < coLen; j++){ + kloop: for(var k = 0; k < classList.length; k++){ + if(exps[k].test(collection[j].className)){ + returnedCollection[returnedCollection.length] = collection[j]; + break kloop; + } + } + } + return returnedCollection; +} + +function findAncestorWithClass(el, klass) { + + if(el == null) + return null; + var exp = getTokenizedExp(klass); + for(var parent = el.parentNode;parent != null;){ + + if( exp.test(parent.className) ) + return parent; + + parent = parent.parentNode; + } + return null; +} + + +function getDescendantById(parent, id){ + var childNodes = parent.all ? parent.all : parent.getElementsByTagName("*"); + for(var i = 0, len = childNodes.length; i < len; i++) + if(childNodes[i].id == id) + return childNodes[i]; + return null; +} + + +function removeClass(el, klass){ + el.className = el.className.replace(getTokenizedExp(klass),"").normalize(); +} + +function repaintFix(el){ + el.style.visibility = 'hidden'; + el.style.visibility = 'visible'; +} +var trimExp = /^\s+|\s+$/g; +String.prototype.trim = function(){ + return this.replace(trimExp, ""); +}; +var wsMultExp = /\s\s+/g; +String.prototype.normalize = function(){ + return this.trim().replace(wsMultExp, " "); +}; +if(!Array.prototype.unshift) + Array.prototype.unshift = function() { + this.reverse(); + for(var i=arguments.length-1; i > -1; i--) + this[this.length] = arguments[i]; + this.reverse(); + return this.length; +}; \ No newline at end of file diff --git a/www/extras/tabs/viewport.js b/www/extras/tabs/viewport.js new file mode 100644 index 000000000..647ebf5f4 --- /dev/null +++ b/www/extras/tabs/viewport.js @@ -0,0 +1,45 @@ +/** + * viewport.js + * by Garrett Smith + */ +function getViewportHeight() { + if(window.innerHeight) + return window.innerHeight; + + if(typeof window.document.documentElement.clientHeight == "number") + return window.document.documentElement.clientHeight; + + return window.document.body.clientHeight; +} + +function getViewportWidth() { + if(window.innerWidth) + return window.innerWidth -16; + + if(typeof window.document.documentElement.clientWidth == "number") + return window.document.documentElement.clientWidth; + + return window.document.body.clientWidth; +} +function getScrollLeft(){ + if(typeof window.pageXOffset == "number") + return window.pageXOffset; + + if(document.documentElement.scrollLeft) + return Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); + + else if(document.body.scrollLeft != null) + return document.body.scrollLeft; + return 0; +} +function getScrollTop(){ + if(typeof window.pageYOffset == "number") + return window.pageYOffset; + + if(document.documentElement.scrollTop) + return Math.max(document.documentElement.scrollTop, document.body.scrollTop); + + else if(document.body.scrollTop != null) + return document.body.scrollTop; + return 0; +} \ No newline at end of file