diff --git a/include/limesurvey/admin/scripts/tabpane/css/tab.css b/include/limesurvey/admin/scripts/tabpane/css/tab.css new file mode 100644 index 00000000..8c091a33 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/css/tab.css @@ -0,0 +1,61 @@ +.dynamic-tab-pane-control.tab-pane { + position: relative; + width: 100%; +} + +.dynamic-tab-pane-control .tab-row .tab { + font: Menu; + cursor: Default; + display: inline; + margin: 1px -1px 1px 2px; + float: left; + padding: 2px 5px 3px 5px; + background: ActiveBorder; + border: 0; /* 1px solid; */ + border-color: ThreeDHighlight ThreeDDarkShadow ThreeDDarkShadow + ThreeDHighlight; + border-bottom: 0; + z-index: 1; + white-space: nowrap; + position: relative; + top: 0; +} + +.dynamic-tab-pane-control .tab-row .tab.selected { + background: ThreeDHighlight !important; + border: 1px solid ThreeDDarkShadow; + border-bottom: 0; + z-index: 3; + padding: 2px 6px 4px 6px; + margin: 1px -3px -3px 0px; + top: -2px; +} + +.dynamic-tab-pane-control .tab-row .tab a { + font: Menu; + color: WindowText; + text-decoration: none; + cursor: default; +} + +.dynamic-tab-pane-control .tab-row .tab.hover { + background: Highlight; +} + +.dynamic-tab-pane-control .tab-page { + clear: both; + border: 1px solid ThreeDDarkShadow; + background: ThreeDHighlight; + z-index: 2; + position: relative; + top: -2px; + color: WindowText; + font: Message-Box; + padding: 10px; + height: 200px; +} + +.dynamic-tab-pane-control .tab-row { + z-index: 1; + white-space: nowrap; +} \ No newline at end of file diff --git a/include/limesurvey/admin/scripts/tabpane/css/tab.winclassic.css b/include/limesurvey/admin/scripts/tabpane/css/tab.winclassic.css new file mode 100644 index 00000000..16a23403 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/css/tab.winclassic.css @@ -0,0 +1,59 @@ +.dynamic-tab-pane-control.tab-pane { + position: relative; + width: 100%; +} + +.dynamic-tab-pane-control .tab-row .tab { + font: Menu; + cursor: Default; + display: inline; + margin: 1px -2px 1px 2px; + float: left; + padding: 2px 5px 3px 5px; + background: ThreeDFace; + border: 1px solid; + border-color: ThreeDHighlight ThreeDDarkShadow ThreeDDarkShadow + ThreeDHighlight; + border-bottom: 0; + z-index: 1; + position: relative; + top: 0; +} + +.dynamic-tab-pane-control .tab-row .tab.selected { + border-bottom: 0; + z-index: 3; + padding: 2px 6px 5px 7px; + margin: 1px -3px -2px 0px; + top: -2px; +} + +.dynamic-tab-pane-control .tab-row .tab a { + font: Menu; + color: WindowText; + text-decoration: none; + cursor: default; +} + +.dynamic-tab-pane-control .tab-row .hover a { + color: blue; +} + +.dynamic-tab-pane-control .tab-page { + clear: both; + border: 1px solid; + border-color: ThreeDHighlight ThreeDDarkShadow ThreeDDarkShadow + ThreeDHighlight; + background: ThreeDFace; + z-index: 2; + position: relative; + top: -2px; + color: WindowText; + font: Message-Box; + padding: 10px; +} + +.dynamic-tab-pane-control .tab-row { + z-index: 1; + white-space: nowrap; +} \ No newline at end of file diff --git a/include/limesurvey/admin/scripts/tabpane/demo.html b/include/limesurvey/admin/scripts/tabpane/demo.html new file mode 100644 index 00000000..90573347 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/demo.html @@ -0,0 +1,213 @@ + + + + +Tab Pane Demo (WebFX) + + + + + + + + + + + + + + + + + + +
+
Select Style Sheet
+ +
+ +
+
+ +

 

+ +
+ +
+

General

+ + + +This is text of tab 1. This is text of tab 1. This is text of tab 1. +This is text of tab 1. This is text of tab 1. This is text of tab 1. +This is text of tab 1. This is text of tab 1. This is text of tab 1.
+ +
+

Security

+ + + +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2.
+
+This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2.
+ +
+

Privacy

+ + + +This is text of tab 3. This is text of tab 3. This is text of tab 3. +This is text of tab 3. This is text of tab 3. This is text of tab 3. +This is text of tab 3. This is text of tab 3. This is text of tab 3.
+ +
+

Content

+ + + +
Content This is text of tab 4. +This is text of tab 4. This is text of tab 4. This is text of tab 4. +This is text of tab 4. This is text of tab 4. This is text of tab 4. +This is text of tab 4. This is text of tab 4.
+ +
+ +
+ + +

Another tab below

+ + + +
+ +
+

General

+ +This is text of tab 1. This is text of tab 1. This is text of tab 1. +This is text of tab 1. This is text of tab 1. This is text of tab 1. +This is text of tab 1. This is text of tab 1. This is text of tab 1.
+ +
+

Security

+ +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2.
+
+This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2.
+ +
+

Nested

+ + +
+ +
+

General

+ +This is text of tab 1. This is text of tab 1. This is text of tab 1. +This is text of tab 1. This is text of tab 1. This is text of tab 1. +This is text of tab 1. This is text of tab 1. This is text of tab 1.
+ +
+

Privacy

+ +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2. +This is text of tab 2. This is text of tab 2. This is text of tab 2.
+ +
+ +
+ +
+ + + + + + diff --git a/include/limesurvey/admin/scripts/tabpane/js/tabpane.js b/include/limesurvey/admin/scripts/tabpane/js/tabpane.js new file mode 100644 index 00000000..92bce6ac --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/js/tabpane.js @@ -0,0 +1,327 @@ +/*----------------------------------------------------------------------------\ +| Tab Pane 1.02 | +|-----------------------------------------------------------------------------| +| Created by Erik Arvidsson | +| (http://webfx.eae.net/contact.html#erik) | +| For WebFX (http://webfx.eae.net/) | +|-----------------------------------------------------------------------------| +| Copyright (c) 2002, 2003, 2006 Erik Arvidsson | +|-----------------------------------------------------------------------------| +| Licensed under the Apache License, Version 2.0 (the "License"); you may not | +| use this file except in compliance with the License. You may obtain a copy | +| of the License at http://www.apache.org/licenses/LICENSE-2.0 | +| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | +| Unless required by applicable law or agreed to in writing, software | +| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | +| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | +| License for the specific language governing permissions and limitations | +| under the License. | +|-----------------------------------------------------------------------------| +| 2002-01-?? | First working version | +| 2002-02-17 | Cleaned up for 1.0 public version | +| 2003-02-18 | Changed from javascript uri for anchors to return false | +| 2003-03-03 | Added dispose methods to release IE memory | +| 2006-05-28 | Changed license to Apache Software License 2.0. | +|-----------------------------------------------------------------------------| +| Dependencies: *.css a css file to define the layout | +|-----------------------------------------------------------------------------| +| Created 2002-01-?? | All changes are in the log above. | Updated 2006-05-28 | +\----------------------------------------------------------------------------*/ + +// This function is used to define if the browser supports the needed +// features +function hasSupport() { + + if (typeof hasSupport.support != "undefined") + return hasSupport.support; + + var ie55 = /msie 5\.[56789]/i.test( navigator.userAgent ); + + hasSupport.support = ( typeof document.implementation != "undefined" && + document.implementation.hasFeature( "html", "1.0" ) || ie55 ) + + // IE55 has a serious DOM1 bug... Patch it! + if ( ie55 ) { + document._getElementsByTagName = document.getElementsByTagName; + document.getElementsByTagName = function ( sTagName ) { + if ( sTagName == "*" ) + return document.all; + else + return document._getElementsByTagName( sTagName ); + }; + } + + return hasSupport.support; +} + +/////////////////////////////////////////////////////////////////////////////////// +// The constructor for tab panes +// +// el : HTMLElement The html element used to represent the tab pane +// bUseCookie : Boolean Optional. Default is true. Used to determine whether to us +// persistance using cookies or not +// +function WebFXTabPane( el, bUseCookie ) { + if ( !hasSupport() || el == null ) return; + + this.element = el; + this.element.tabPane = this; + this.pages = []; + this.selectedIndex = null; + this.useCookie = bUseCookie != null ? bUseCookie : true; + + // add class name tag to class name + this.element.className = this.classNameTag + " " + this.element.className; + + // add tab row + this.tabRow = document.createElement( "div" ); + this.tabRow.className = "tab-row"; + el.insertBefore( this.tabRow, el.firstChild ); + + var tabIndex = 0; + if ( this.useCookie ) { + tabIndex = Number( WebFXTabPane.getCookie( "webfxtab_" + this.element.id ) ); + if ( isNaN( tabIndex ) ) + tabIndex = 0; + } + this.selectedIndex = tabIndex; + + // loop through child nodes and add them + var c=this; + var b=$(el).find(".tab-page"); + jQuery.each(b, function() { + c.addTabPage( this ); + } + ); +} + +WebFXTabPane.prototype.classNameTag = "dynamic-tab-pane-control"; + +WebFXTabPane.prototype.setSelectedIndex = function ( n ) { + if (this.selectedIndex != n) { + if (this.selectedIndex != null && this.pages[ this.selectedIndex ] != null ) + this.pages[ this.selectedIndex ].hide(); + this.selectedIndex = n; + this.pages[ this.selectedIndex ].show(); + + if ( this.useCookie ) + WebFXTabPane.setCookie( "webfxtab_" + this.element.id, n ); // session cookie + } +}; + +WebFXTabPane.prototype.getSelectedIndex = function () { + return this.selectedIndex; +}; + +WebFXTabPane.prototype.addTabPage = function ( oElement ) { + if ( !hasSupport() ) return; + + if ( oElement.tabPage == this ) // already added + return oElement.tabPage; + + var n = this.pages.length; + var tp = this.pages[n] = new WebFXTabPage( oElement, this, n ); + tp.tabPane = this; + + // move the tab out of the box + this.tabRow.appendChild( tp.tab ); + + if ( n == this.selectedIndex ) + tp.show(); + else + tp.hide(); + + return tp; +}; + +WebFXTabPane.prototype.dispose = function () { + this.element.tabPane = null; + this.element = null; + this.tabRow = null; + + for (var i = 0; i < this.pages.length; i++) { + this.pages[i].dispose(); + this.pages[i] = null; + } + this.pages = null; +}; + + + +// Cookie handling +WebFXTabPane.setCookie = function ( sName, sValue, nDays ) { + var expires = ""; + if ( nDays ) { + var d = new Date(); + d.setTime( d.getTime() + nDays * 24 * 60 * 60 * 1000 ); + expires = "; expires=" + d.toGMTString(); + } + + document.cookie = sName + "=" + sValue + expires + "; path=/"; +}; + +WebFXTabPane.getCookie = function (sName) { + var re = new RegExp( "(\;|^)[^;]*(" + sName + ")\=([^;]*)(;|$)" ); + var res = re.exec( document.cookie ); + return res != null ? res[3] : null; +}; + +WebFXTabPane.removeCookie = function ( name ) { + setCookie( name, "", -1 ); +}; + + + + + + + + +/////////////////////////////////////////////////////////////////////////////////// +// The constructor for tab pages. This one should not be used. +// Use WebFXTabPage.addTabPage instead +// +// el : HTMLElement The html element used to represent the tab pane +// tabPane : WebFXTabPane The parent tab pane +// nindex : Number The index of the page in the parent pane page array +// +function WebFXTabPage( el, tabPane, nIndex ) { + if ( !hasSupport() || el == null ) return; + + this.element = el; + this.element.tabPage = this; + this.index = nIndex; + + var cs = el.childNodes; + for (var i = 0; i < cs.length; i++) { + if (cs[i].nodeType == 1 && cs[i].className == "tab") { + this.tab = cs[i]; + break; + } + } + + // insert a tag around content to support keyboard navigation + + + var a = document.createElement( "A" ); + this.aElement = a; + a.href = "#"; + a.onclick = function () { return false; }; + while ( this.tab.hasChildNodes() ) + a.appendChild( this.tab.firstChild ); + this.tab.appendChild( a ); + + + // hook up events, using DOM0 + var oThis = this; + this.tab.onclick = function () { oThis.select(); }; + this.tab.onmouseover = function () { WebFXTabPage.tabOver( oThis ); }; + this.tab.onmouseout = function () { WebFXTabPage.tabOut( oThis ); }; +} + +WebFXTabPage.prototype.show = function () { + var el = this.tab; + var s = el.className + " selected"; + s = s.replace(/ +/g, " "); + el.className = s; + + this.element.style.display = "block"; +}; + +WebFXTabPage.prototype.hide = function () { + var el = this.tab; + var s = el.className; + s = s.replace(/ selected/g, ""); + el.className = s; + + this.element.style.display = "none"; +}; + +WebFXTabPage.prototype.select = function () { + this.tabPane.setSelectedIndex( this.index ); +}; + +WebFXTabPage.prototype.dispose = function () { + this.aElement.onclick = null; + this.aElement = null; + this.element.tabPage = null; + this.tab.onclick = null; + this.tab.onmouseover = null; + this.tab.onmouseout = null; + this.tab = null; + this.tabPane = null; + this.element = null; +}; + +WebFXTabPage.tabOver = function ( tabpage ) { + var el = tabpage.tab; + var s = el.className + " hover"; + s = s.replace(/ +/g, " "); + el.className = s; +}; + +WebFXTabPage.tabOut = function ( tabpage ) { + var el = tabpage.tab; + var s = el.className; + s = s.replace(/ hover/g, ""); + el.className = s; +}; + + +// This function initializes all uninitialized tab panes and tab pages +function setupAllTabs() { + if ( !hasSupport() ) return; + + var all = document.getElementsByTagName( "*" ); + var l = all.length; + var tabPaneRe = /tab\-pane/; + var tabPageRe = /tab\-page/; + var cn, el; + var parentTabPane; + + for ( var i = 0; i < l; i++ ) { + el = all[i] + cn = el.className; + + // no className + if ( cn == "" ) continue; + + // uninitiated tab pane + if ( tabPaneRe.test( cn ) && !el.tabPane ) + new WebFXTabPane( el, true ); + + // unitiated tab page wit a valid tab pane parent + else if ( tabPageRe.test( cn ) && !el.tabPage && + tabPaneRe.test( el.parentNode.className ) ) { + el.parentNode.tabPane.addTabPage( el ); + } + } +} + +function disposeAllTabs() { + if ( !hasSupport() ) return; + + var all = document.getElementsByTagName( "*" ); + var l = all.length; + var tabPaneRe = /tab\-pane/; + var cn, el; + var tabPanes = []; + + for ( var i = 0; i < l; i++ ) { + el = all[i] + cn = el.className; + + // no className + if ( cn == "" ) continue; + + // tab pane + if ( tabPaneRe.test( cn ) && el.tabPane ) + tabPanes[tabPanes.length] = el.tabPane; + } + + for (var i = tabPanes.length - 1; i >= 0; i--) { + tabPanes[i].dispose(); + tabPanes[i] = null; + } +} + diff --git a/include/limesurvey/admin/scripts/tabpane/local/helptip.css b/include/limesurvey/admin/scripts/tabpane/local/helptip.css new file mode 100644 index 00000000..60ee7f57 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/local/helptip.css @@ -0,0 +1,37 @@ +/* + Notice that IE has a display problem if the help link is on + the last line of a container with no padding. If this is the + case increase the padding bottom to at least 1px +*/ +a.helpLink { + color: Green; + text-decoration: none; + border-bottom: 1px dashed Green; +} + +a.helpLink:hover { + color: Red; + text-decoration: none; + border-bottom: 1px dashed Red; +} + +.help-tooltip { + position: absolute; + width: 250px; + border: 1px Solid WindowFrame; + background: Infobackground; + color: InfoText; + font: StatusBar; + font: Status-Bar; + padding: 3px; + filter: progid : DXImageTransform.Microsoft.Shadow ( + color = + + "#777777", Direction = 135, Strength = 3 ); + z-index: 10000; +} + +.help-tooltip a,.help-tooltip a:hover { + color: blue !important; + background: none; +} \ No newline at end of file diff --git a/include/limesurvey/admin/scripts/tabpane/local/helptip.js b/include/limesurvey/admin/scripts/tabpane/local/helptip.js new file mode 100644 index 00000000..f0f83cf0 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/local/helptip.js @@ -0,0 +1,91 @@ +/* + * This script was created by Erik Arvidsson (erik(at)eae.net) + * for WebFX (http://webfx.eae.net) + * Copyright 2001 + * + * For usage see license at http://webfx.eae.net/license.html + * + * Version: 1.0 + * Created: 2001-09-27 * Updated: 2001-11-25 Added a resize to the tooltip if the document width is too small + * + * Dependencies: helptip.css (To set up the CSS of the help-tooltip class) + * + * Usage: + * + * + * + * + * Help + * + */ + +function showHelpTip(e, s) { + // find anchor element + var el = e.target ? e.target : e.srcElement; + while (el.tagName != "A") + el = el.parentNode; + + // is there already a tooltip? If so, remove it + if (el._helpTip) { + document.body.removeChild(el._helpTip); + el._helpTip = null; + el.onblur = null; + return; + } + + // create element and insert last into the body + var d = document.createElement("DIV"); + d.className = "help-tooltip"; + document.body.appendChild(d); + d.innerHTML = s; + + // Allow clicks on A elements inside tooltip + d.onmousedown = function (e) { + if (!e) e = event; + var t = e.target ? e.target : e.srcElement; + while (t.tagName != "A" && t != d) + t = t.parentNode; + if (t == d) return; + + el._onblur = el.onblur; + el.onblur = null; + }; + d.onmouseup = function () { + el.onblur = el._onblur; + el.focus(); + }; + + // position tooltip + var dw = document.width ? document.width : document.documentElement.offsetWidth - 25; + if (d.offsetWidth >= dw) + d.style.width = dw - 10 + "px"; else + d.style.width = ""; + var scroll = getScroll(); + if (e.clientX > dw - d.offsetWidth) + d.style.left = dw - d.offsetWidth + scroll.x + "px"; + else + d.style.left = e.clientX - 2 + scroll.x + "px"; + d.style.top = e.clientY + 18 + scroll.y + "px"; + + // add a listener to the blur event. When blurred remove tooltip and restore anchor + el.onblur = function () { + document.body.removeChild(d); + el.onblur = null; + el._helpTip = null; + }; + + // store a reference to the tooltip div + el._helpTip = d; +} + +// returns the scroll left and top for the browser viewport. +function getScroll() { + if (document.all && document.body.scrollTop != undefined) { // IE model + var ieBox = document.compatMode != "CSS1Compat"; + var cont = ieBox ? document.body : document.documentElement; + return {x : cont.scrollLeft, y : cont.scrollTop}; + } + else { + return {x : window.pageXOffset, y : window.pageYOffset}; + } +} \ No newline at end of file diff --git a/include/limesurvey/admin/scripts/tabpane/local/howto.txt b/include/limesurvey/admin/scripts/tabpane/local/howto.txt new file mode 100644 index 00000000..43be1710 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/local/howto.txt @@ -0,0 +1,3 @@ +Creata a sub folder in the zip directory and copy all these files to that dir. +Replace the include statements of webfxlayout.js and webfxapi.js to point at local/webfxlayout.js and +local/webfxapi.js \ No newline at end of file diff --git a/include/limesurvey/admin/scripts/tabpane/local/title-background.png b/include/limesurvey/admin/scripts/tabpane/local/title-background.png new file mode 100644 index 00000000..93b9aada Binary files /dev/null and b/include/limesurvey/admin/scripts/tabpane/local/title-background.png differ diff --git a/include/limesurvey/admin/scripts/tabpane/local/webfxapi.css b/include/limesurvey/admin/scripts/tabpane/local/webfxapi.css new file mode 100644 index 00000000..1d6b4917 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/local/webfxapi.css @@ -0,0 +1 @@ +/* This style sheet is used for WebFX Api pages */ a.helpLink,a.helpLink:hover { color: rgb(0, 66, 174); border-bottom-color: rgb(0, 66, 174); } .help-tooltip { width: auto; } .help-tooltip h4,.help-tooltip table,.help-tooltip p { width: auto; } .methodContainer { display: none; } .methodInfo h4,.methodInfo thead td { font-size: 13px; background: none; border-bottom: 0; } .methodInfo h4,.methodInfo p,.methodInfo table { margin: 5px; padding: 0; } td { vertical-align: top; } td code { white-space: nowrap; } code a:visited,code a:hover { color: rgb(0, 66, 174); background: transparent; } \ No newline at end of file diff --git a/include/limesurvey/admin/scripts/tabpane/local/webfxapi.js b/include/limesurvey/admin/scripts/tabpane/local/webfxapi.js new file mode 100644 index 00000000..26234731 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/local/webfxapi.js @@ -0,0 +1,20 @@ +/* + * This script is used for WebFX Api pages + * + * It defines one funtion and includes helptip.js, helptip.css and webfxapi.css + */ + +document.write( " + + + + +

This page reloads as soon as the page is loaded... look at the +memory consuption.

+ +
+ +
+

General

+ + + + + +
This is text of tab 1. This is text of tab 1. This is text of + tab 1. This is text of tab 1. This is text of tab 1. This is text of + tab 1. This is text of tab 1. This is text of tab 1. This is text of + tab 1.
+ +
+ +
+

Security

+ + + + + +
This is text of tab 2. This is text of tab 2. This is text of + tab 2. This is text of tab 2. This is text of tab 2. This is text of + tab 2. This is text of tab 2. This is text of tab 2. This is text of + tab 2.
+
+ This is text of tab 2. This is text of tab 2. This is text of tab 2. + This is text of tab 2. This is text of tab 2. This is text of tab 2. + This is text of tab 2. This is text of tab 2. This is text of tab 2.
+ +
+ +
+

Privacy

+ + + + + +
This is text of tab 3. This is text of tab 3. This is text of + tab 3. This is text of tab 3. This is text of tab 3. This is text of + tab 3. This is text of tab 3. This is text of tab 3. This is text of + tab 3.
+ +
+ +
+

Content

+ + + + + +
This is text of tab 4. This is text of tab 4. This is text of + tab 4. This is text of tab 4. This is text of tab 4. This is text of + tab 4. This is text of tab 4. This is text of tab 4. This is text of + tab 4.
+ +
+ +
+ + + diff --git a/include/limesurvey/admin/scripts/tabpane/tabpane.html b/include/limesurvey/admin/scripts/tabpane/tabpane.html new file mode 100644 index 00000000..37283645 --- /dev/null +++ b/include/limesurvey/admin/scripts/tabpane/tabpane.html @@ -0,0 +1,1208 @@ + + + + +Tab Pane (WebFX) + + + + + + + + + + + + + + + +
+ +

2002-02-17: Original version posted.
+2003-03-03: 1.02 - Fixes IE memory leakage.
+2006-05-26: Changed license to Apache Software +License 2.0.
+

+ + +
+
+

Introduction

+ + + +

You might remember that we used to have a pretty good tab strip +control here at WebFX a long time ago. This control was removed when we +redesigned the site with the argument that we should fix it to work in +Mozilla. Now, more than three years later we finally got down and +created a tab pane control that we think is a worthy replacement.

+ +

This Tab Pane control is fairly similar to the tab system Tim +Scarfe created for developer-x.com +and the basic idea is the same. That idea is to be able to use a normal +XHTML document structure and if the browser supports DOM +level 1 then the structure of the elements is slightly adjusted and a +the className is changed for the tab pane container so that +the css rules specially defined for the tab are applied.

+ +

The Tab Pane has support for persistence using cookies so that +you can navigate between pages without having to reselect the selected +tab for all your tab panes in your document. The look and feel is +entirely decided by CSS so it is fairly easy to create the look and feel +you need for your web application. As of this writing there are three +different styles available; Luna, Windows Classic and the WebFX look and +feel that this pane is currently using. See the demo +page for the look and feel of the other two.

+ +
+ +
+

Usage

+ + + +

Include the Files

+ +

To use the tab pane you should include two files. One JavaScript +file and on css file.

+ +
+<script type="text/javascript" src="js/tabpane.js"></script>
+<link type="text/css" rel="StyleSheet" href="css/tab.webfx.css" />
+
+ +

The XHTML Structure

+ +

The basic structure for the tab pane consists of an element with +the class name tab-pane that in turn contains elements with +the class names tab-page. The tab page element should +contain one element with the class name tab. +This last one should preferably be a header of some kind in case the +browser does not support DOM1 and therefore will leave the structure +intact. The other two elements can be almost anything but a div +element is good because it usually does not change the rendering.

+ +

Below is the code for a simple tab pane with the tab pages.

+ +
+<div class="tab-pane" id="tab-pane-1">
+
+   <div class="tab-page">
+      <h2 class="tab">General</h2>
+
+      This is text of tab 1. This is text of tab 1.
+      This is text of tab 1. This is text of tab 1.
+
+   </div>
+
+   <div class="tab-page">
+      <h2 class="tab">Privacy</h2>
+
+      This is text of tab 2. This is text of tab 2.
+      This is text of tab 2. This is text of tab 2.
+
+   </div>
+</div>
+
+ +

Notice that the id is not needed unless two or more +tab panes are present in the same document and you are using the +persistence feature.

+ +

Initialization

+ +

The code above is a complete working tab pane. You do not have to +add any more js code but there are a few good reasons why you would want +to do this. If no js code is added all the tab panes in the document are +initialized when the document is loaded. If you have lots of text and/or +images this will take quite some time and the layout of the page will +feel jerky. A better way is to call the function setupAllTabs +after all your XHTML has been defined. This works much better but if you +have a lot of text this is not optional either because the browser might +render some of the text before the entire tab structure is available.

+ +
+      ...
+   </div>
+</div>
+<!-- tab pane closed -->
+
+<script type="text/javascript">
+setupAllTabs();
+</script>
+
+ +

The best way to go is to create as much as possible as soon as +possible. This involves adding calls to js after the tab pane is opened +and as soon as every page is opened.

+ +
+<div class="tab-pane" id="tab-pane-1">
+
+<script type="text/javascript">
+var tabPane1 = new WebFXTabPane( document.getElementById( "tab-pane-1" ) );
+</script>
+
+   <div class="tab-page" id="tab-page-1">
+      <h2 class="tab">General</h2>
+
+      <script type="text/javascript">
+      tabPane1.addTabPage( document.getElementById( "tab-page-1" ) );
+      </script>
+
+      This is text of tab 1. This is text of tab 1.
+      This is text of tab 1. This is text of tab 1.
+
+   </div>
+
+   <div class="tab-page" id="tab-page-2">
+      <h2 class="tab">Privacy</h2>
+
+      <script type="text/javascript">
+      tabPane1.addTabPage( document.getElementById( "tab-page-2" ) );
+      </script>
+
+      This is text of tab 2. This is text of tab 2.
+      This is text of tab 2. This is text of tab 2.
+
+   </div>
+</div>
+
+ +

The code for this is, as you can see, not half as nice and you +should decide from time to time if you really need this. In most web +applications (especially intranet apps) this is not needed because the +amount of data inside each tab page is limited (or added later).

+ +

One thing to note about this last method is that some browser +have trouble changing the content model during the page load (noticeably +Konqueror).

+ +
+ +
+

API

+ + +

WebFXTabPane

+

This is the class representing a tab pane.

+

Syntax

+

new WebFXTabPane(oElement [, +bUseCookie])

+

Parameters

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescripton
oElementHTMLElementThe html element that represents the tab pane
bUseCookieBooleanOptional. If this is set to + true then the selected tab is persisted. The default value is true. +
+

Static Methods

+ + + + + + + + + + + + + + + + + + + + + +
NameDescription
setCookie +
+
+

Syntax

+

object.setCookie(sName, sValue [, + nDays])

+

Arguments

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescripton
sNameStringThe name of the cookie
sValueStringThe value of the cookie
nDaysNumberOptional. The number of + days to store the cookie
+

Return Type

+

void

+
+
+
Sets a cookie
getCookie +
+
+

Syntax

+

object.getCookie(sName)

+

Arguments

+ + + + + + + + + + + + + + + +
NameTypeDescripton
sNameStringThe name of the cookie
+

Return Type

+

String

+
+
+
Retrieves a cookie by name
removeCookie +
+
+

Syntax

+

object.removeCookie(sName)

+

Arguments

+ + + + + + + + + + + + + + + +
NameTypeDescripton
sNameStringThe name of the cookie to remove
+

Return Type

+

void

+
+
+
Removes a cookie by name
+

Static Fields

+ + + + + + + + + + + + + +
NameTypeDescripton
None.
+

Methods

+ + + + + + + + + + + + + + + + + + + + + +
NameDescription
addTabPage +
+
+

Syntax

+

object.addTabPage(oElement)

+

Arguments

+ + + + + + + + + + + + + + + +
NameTypeDescripton
oElementHTMLElementThe html element that represents the tab page
+

Return Type

+

WebFXTabPage

+
+
+
Adds a tab page by passing an html element
getSelectedIndex +
+
+

Syntax

+

object.getSelectedIndex()

+

Arguments

+

No Arguments.

+

Return Type

+

Number

+
+
+
The index of the selected tab page
setSelectedIndex +
+
+

Syntax

+

object.setSelectedIndex(n)

+

Arguments

+ + + + + + + + + + + + + + + +
NameTypeDescripton
nNumberThe index of the tab page to select
+

Return Type

+

void

+
+
+
Sets the selected tab page by index
+

Fields

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescripton
classNameTagStringThis string is added to the class name to tag the tab pane + as beeing created
elementHTMLElementRead only.The html element + being that represents the tab pane
pagesWebFXTabPages[]Read only.An array containing + the tab pages
selectedIndexNumberRead only.The index of the + selected tab page
tabRowHTMLElementRead only.The html element + that encloses all tabs
useCookieBooleanIs used to decide if the selected tab page index should be + persisted using a cookie.
+

Remarks

+

None.

+

WebFXTabPage

+

This is the class representing a tab page.

+

Syntax

+

new WebFXTabPage(oElement, +oTabPane, nIndex)

+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescripton
oElementHTMLElementThe html element that represents the tab page
oTabPaneWebFXTabPaneThe tab pane to add the page to
nIndexNumberThe index of the tab page
+

Static Methods

+ + + + + + + + + + + + +
NameDescription
None.
+

Static Fields

+ + + + + + + + + + + + + +
NameTypeDescripton
None.
+

Methods

+ + + + + + + + + + + + + + + + + + + + + +
NameDescription
hide +
+
+

Syntax

+

object.hide()

+

Arguments

+

No Arguments.

+

Return Type

+

void

+
+
+
Hides the tab page
select +
+
+

Syntax

+

object.select()

+

Arguments

+

No Arguments.

+

Return Type

+

void

+
+
+
Selects the tab page
show +
+
+

Syntax

+

object.show()

+

Arguments

+

No Arguments.

+

Return Type

+

void

+
+
+
Makes the tab page visible
+

Fields

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescripton
elementHTMLElementRead only.The html element + being used as the page
indexNumberRead only. The index of the + tab page in the tab pane pages array.
tabHTMLElementRead only.The html element + being used as the tab.
+

Remarks

+

Do not use this constructor manually. Use addTabPage of the +WebFXTabPane class instead.

+

Globals

+

Functions

+ + + + + + + + + + + + + + + + + +
NameDescription
hasSupport +
+
+

Syntax

+

hasSupport()

+

Arguments

+

No Arguments.

+

Return Type

+

Boolean

+
+
+
Returns whether the browser is supported or not
setupAllTabs +
+
+

Syntax

+

setupAllTabs()

+

Arguments

+

No Arguments.

+

Return Type

+

void

+
+
+
Initializes all tab panes and tab pages that have not been + initialized already.
+

Objects

+ + + + + + + + + + + + + +
NameTypeDescripton
None.
+
+ +
+

Implementation

+ + + +

Check for support

+ +

The way to check the browser whether it support a certain feature +in the DOM is to use the method document.implementation.hasFeature. +However since IE5.5 supports all the features that this script needs but +it does not support this way of checking for support we have to add a +separate check for IE55.

+ +
+function hasSupport() {
+
+   if (typeof hasSupport.support != "undefined")
+      return hasSupport.support;
+
+   var ie55 = /msie 5\.[56789]/i.test( navigator.userAgent );
+
+   hasSupport.support = ( typeof document.implementation != "undefined" &&
+         document.implementation.hasFeature( "html", "1.0" ) || ie55 )
+
+   // IE55 has a serious DOM1 bug... Patch it!
+   if ( ie55 ) {
+      document._getElementsByTagName = document.getElementsByTagName;
+      document.getElementsByTagName = function ( sTagName ) {
+         if ( sTagName == "*" )
+            return document.all;
+         else
+            return document._getElementsByTagName( sTagName );
+      };
+   }
+
+   return hasSupport.support;
+}
+
+ +

As you can see in the code above IE55 has a bug an therefore we +also patch that. Too many people are still using IE55 to just ignore it.

+ +

WebFXTabPane

+ +

The constructor for the tab pane creates the tabRow +div that is used to place all the actual tabs in. It also checks the +cookie state so that the selected tab can be persisted. Besides from +this it sets up some properties needed to keep track of the states. Last +but not least it checks the childNodes of the element and +adds the found tab pages.

+ +
+function WebFXTabPane( el, bUseCookie ) {
+   if ( !hasSupport() || el == null ) return;
+
+   this.element = el;
+   this.element.tabPane = this;
+   this.pages = [];
+   this.selectedIndex = null;
+   this.useCookie = bUseCookie != null ? bUseCookie : true;
+
+   // add class name tag to class name
+   this.element.className = this.classNameTag + " " + this.element.className;
+
+   // add tab row
+   this.tabRow = document.createElement( "div" );
+   this.tabRow.className = "tab-row";
+   el.insertBefore( this.tabRow, el.firstChild );
+
+   var tabIndex = 0;
+   if ( this.useCookie ) {
+      tabIndex = Number( WebFXTabPane.getCookie( "webfxtab_" + this.element.id ) );
+      if ( isNaN( tabIndex ) )
+         tabIndex = 0;
+   }
+   this.selectedIndex = tabIndex;
+
+   // loop through child nodes and add them
+   var cs = el.childNodes;
+   var n;
+   for (var i = 0; i < cs.length; i++) {
+      if (cs[i].nodeType == 1 && cs[i].className == "tab-page") {
+         this.addTabPage( cs[i] );
+      }
+   }
+}
+
+ +

There are a few methods added to the WebFXTabPane +class and one of the more important ones is the method addTabPage. +This method takes the element that represents the tab page and uses that +to create a WebFXTabPage object that is added to the pages +array. Once the tab page has been added it also checks if this page is +the selected one and if it is it shows it.

+ +
+WebFXTabPane.prototype = {
+
+   ...
+
+   addTabPage:   function ( oElement ) {
+      if ( !hasSupport() ) return;
+
+      if ( oElement.tabPage == this )   // already added
+         return oElement.tabPage;
+
+      var n = this.pages.length;
+      var tp = this.pages[n] = new WebFXTabPage( oElement, this, n );
+      tp.tabPane = this;
+
+      // move the tab out of the box
+      this.tabRow.appendChild( tp.tab );
+
+      if ( n == this.selectedIndex )
+         tp.show();
+      else
+         tp.hide();
+
+      return tp;
+   }
+};
+
+ +

WebFXTabPage

+ +

This class is used to keep track of the actual tab page. Once +created it moves the tab element to the tabRow of the tab +pane. It also adds an anchor around the text so that the user can use +the keyboard to activate the tabs.

+ +
+function WebFXTabPage( el, tabPane, nIndex ) {
+   if ( !hasSupport() || el == null ) return;
+
+   this.element = el;
+   this.element.tabPage = this;
+   this.index = nIndex;
+
+   var cs = el.childNodes;
+   for (var i = 0; i < cs.length; i++) {
+      if (cs[i].nodeType == 1 && cs[i].className == "tab") {
+         this.tab = cs[i];
+         break;
+      }
+   }
+
+   // insert a tag around content to support keyboard navigation
+   var a = document.createElement( "A" );
+   a.href = "javascript:void 0;";
+   while ( this.tab.hasChildNodes() )
+      a.appendChild( this.tab.firstChild );
+   this.tab.appendChild( a );
+
+   // hook up events, using DOM0
+   var oThis = this;
+   this.tab.onclick = function () { oThis.select(); };
+   this.tab.onmouseover = function () { WebFXTabPage.tabOver( oThis ); };
+   this.tab.onmouseout = function () { WebFXTabPage.tabOut( oThis ); };
+}
+
+ +

Initialization

+ +

The initialization uses the global function setupAllTabs +that goes through all elements and checks their class names and if the +class names match the classes used by the tab pane controls it checks +whether this element belongs to an uninitialized control and in that +case it initializes it now.

+ +
+function setupAllTabs() {
+   if ( !hasSupport() ) return;
+
+   var all = document.getElementsByTagName( "*" );
+   var l = all.length;
+   var tabPaneRe = /tab\-pane/;
+   var tabPageRe = /tab\-page/;
+   var cn, el;
+   var parentTabPane;
+
+   for ( var i = 0; i < l; i++ ) {
+      el = all[i]
+      cn = el.className;
+
+      // no className
+      if ( cn == "" ) continue;
+
+      // uninitiated tab pane
+      if ( tabPaneRe.test( cn ) && !el.tabPane )
+         new WebFXTabPane( el );
+
+      // unitiated tab page wit a valid tab pane parent
+      else if ( tabPageRe.test( cn ) && !el.tabPage &&
+               tabPaneRe.test( el.parentNode.className ) ) {
+         el.parentNode.tabPane.addTabPage( el );
+      }
+   }
+}
+
+ +

This function can be called manually at any time but the script +makes hooks to the load event for the window. This is done +using DOM level 2 events if available. If not we test if it supports the +IE5 way of attaching events and last we fall back on classic way of +setting events.

+ +
+// DOM2
+if ( typeof window.addEventListener != "undefined" )
+   window.addEventListener( "load", setupAllTabs, false );
+
+// IE
+else if ( typeof window.attachEvent != "undefined" )
+   window.attachEvent( "onload", setupAllTabs );
+
+else {
+   if ( window.onload != null ) {
+      var oldOnload = window.onload;
+      window.onload = function ( e ) {
+         oldOnload( e );
+         setupAllTabs();
+      };
+   }
+   else
+      window.onload = setupAllTabs;
+}
+
+ +
+

Look & Feel

+ + + + +

The structure

+ +

To be able to change the look and feel one needs to understand +the structure of the tab pane. When the original XHTML source tree is +transformed into the tab pane the class name of the element representing +the tab pane is tagged with the property classNameTag. The +default tag is dynamic-tab-pane-control and therefore all +your css rules should take this into account. If you want different look +on different tab panes in the same document this tag can be changed to +make the css rules easier to set up.

+ +
+<div class="dynamic-tab-pane-control tab-pane" id="tab-pane-1">
+   <div class="tab-row">
+      <h2 class="tab selected"><a ... >General</a></h2>
+      <h2 class="tab hover"><a ... >Privacy</a></h2>
+   </div>
+   <div class="tab-page">
+
+      This is text of tab 1. This is text of tab 1.
+      This is text of tab 1. This is text of tab 1.
+
+   </div>
+
+   <div class="tab-page">
+
+      This is text of tab 2. This is text of tab 2.
+      This is text of tab 2. This is text of tab 2.
+
+   </div>
+</div>
+
+ +

The selected tab will have the class name tab +selected and the tab that the mouse hovers over will have the class name +tab hover. If the selected tab is hovered it will have the +class name tab selected hover. These rules allow you to +differentiate the look of tabs between the different states.

+ +

The CSS Rules

+ +

Here we will walk through the Windows +Classic css file. First we set the width and position of the tab pane to +prevent a few rendering bugs in IE6.

+ +
+.dynamic-tab-pane-control.tab-pane {
+   position:        relative;
+   width:           100%;
+}
+
+.dynamic-tab-pane-control .tab-row {
+   z-index:         1;
+   white-space:     nowrap;
+}
+
+ +

Then we setup the css for the tab. Notice how the position is set +to relative to allow the top position to be slightly changed and to +allow the z-index property to be changed to position the tabs below the +tab pages.

+ +
+.dynamic-tab-pane-control .tab-row .tab {
+   font:            Menu;
+   cursor:          Default;
+   display:         inline;
+   margin:          1px -2px 1px 2px;
+   float:           left;
+   padding:         2px 5px 3px 5px;
+   background:      ThreeDFace;
+   border:          1px solid;
+   border-color:    ThreeDHighlight ThreeDDarkShadow
+                    ThreeDDarkShadow ThreeDHighlight;
+   border-bottom:   0;
+   z-index:         1;
+   position:        relative;
+   top:             0;
+}
+
+ +

For the selected tab we set the z-index to 3 to put it above the +tab pages. We also move it a little and change some other properties to +make it look more like the classic window tab control.

+ +
+.dynamic-tab-pane-control .tab-row .tab.selected {
+   border-bottom:   0;
+   z-index:         3;
+   padding:         2px 6px 5px 7px;
+   margin:          1px -3px -2px 0px;
+   top:             -2px;
+}
+
+ +

Then we override the text properties on the tabs as well as for +the .hover rule.

+ +
+.dynamic-tab-pane-control .tab-row .tab a {
+   font:            Menu;
+   color:           WindowText;
+   text-decoration: none;
+   cursor:          default;
+}
+
+.dynamic-tab-pane-control .tab-row .hover a {
+   color:           blue;
+}
+
+ +

Then we set the z-index for the tab pages to 2 so that it will be +shown above tabs but below the selected tab. We also set the borders and +and a few other properties.

+ +
+.dynamic-tab-pane-control .tab-page {
+   clear:           both;
+   border:          1px solid;
+   border-color:    ThreeDHighlight ThreeDDarkShadow
+                    ThreeDDarkShadow ThreeDHighlight;
+   background:      ThreeDFace;
+   z-index:         2;
+   position:        relative;
+   top:             -2px;
+   color:           WindowText;
+   font:            MessageBox;
+   font:            Message-Box;
+   padding:         10px;
+}
+
+
+ + + +

Tab Pane
+Usage
+API
+Implementation
+Look & Feel
+Demo
+Download

+ +

Author: Erik Arvidsson

+ +
+ + +