From 541bb378ddece2eab135a8066a16994e94436dea Mon Sep 17 00:00:00 2001 From: Giulio Cesare Solaroli Date: Mon, 03 Oct 2011 16:04:12 +0000 Subject: Merge pull request #1 from gcsolaroli/master First version of the restructured repository --- (limited to 'frontend/beta/js/YUI-extensions/widgets/TabPanel.js') diff --git a/frontend/beta/js/YUI-extensions/widgets/TabPanel.js b/frontend/beta/js/YUI-extensions/widgets/TabPanel.js new file mode 100644 index 0000000..25fd142 --- a/dev/null +++ b/frontend/beta/js/YUI-extensions/widgets/TabPanel.js @@ -0,0 +1,756 @@ +/** + * @class YAHOO.ext.TabPanel + * @extends YAHOO.ext.util.Observable + * Creates a lightweight TabPanel component using Yahoo! UI. + *

+ * Usage: + *

+    // basic tabs 1, built from existing content
+    var tabs = new YAHOO.ext.TabPanel('tabs1');
+    tabs.addTab('script', "View Script");
+    tabs.addTab('markup', "View Markup");
+    tabs.activate('script');
+    
+    // more advanced tabs, built from javascript
+    var jtabs = new YAHOO.ext.TabPanel('jtabs');
+    jtabs.addTab('jtabs-1', "Normal Tab", "My content was added during construction.");
+    
+    // set up the UpdateManager
+    var tab2 = jtabs.addTab('jtabs-2', "Ajax Tab 1");
+    var updater = tab2.getUpdateManager();
+    updater.setDefaultUrl('ajax1.htm');
+    tab2.onActivate.subscribe(updater.refresh, updater, true);
+    
+    // Use setUrl for Ajax loading
+    var tab3 = jtabs.addTab('jtabs-3', "Ajax Tab 2");
+    tab3.setUrl('ajax2.htm', null, true);
+    
+    // Disabled tab
+    var tab4 = jtabs.addTab('tabs1-5', "Disabled Tab", "Can't see me cause I'm disabled");
+    tab4.disable();
+    
+    jtabs.activate('jtabs-1');
+}
+ * 
+ * @requires YAHOO.ext.Element + * @requires YAHOO.ext.UpdateManager + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @requires YAHOO.util.Connect (optional) + * @constructor + * Create new TabPanel. + * @param {String/HTMLElement/Element} container The id, DOM element or YAHOO.ext.Element container where this TabPanel is to be rendered. + * @param {Boolean} config Config object to set any properties for this TabPanel or true to render the tabs on the bottom. + */ +YAHOO.ext.TabPanel = function(container, config){ + /** + * The container element for this TabPanel. + * @type YAHOO.ext.Element + */ + this.el = getEl(container, true); + /** The position of the tabs. Can be 'top' or 'bottom' @type String */ + this.tabPosition = 'top'; + this.currentTabWidth = 0; + /** The minimum width of a tab (ignored if resizeTabs is not true). @type Number */ + this.minTabWidth = 40; + /** The maximum width of a tab (ignored if resizeTabs is not true). @type Number */ + this.maxTabWidth = 250; + /** The preferred (default) width of a tab (ignored if resizeTabs is not true). @type Number */ + this.preferredTabWidth = 175; + /** Set this to true to enable dynamic tab resizing. @type Boolean */ + this.resizeTabs = false; + /** Set this to true to turn on window resizing monitoring (ignored if resizeTabs is not true). @type Boolean */ + this.monitorResize = true; + + if(config){ + if(typeof config == 'boolean'){ + this.tabPosition = config ? 'bottom' : 'top'; + }else{ + YAHOO.ext.util.Config.apply(this, config); + } + } + if(this.tabPosition == 'bottom'){ + this.bodyEl = getEl(this.createBody(this.el.dom)); + this.el.addClass('ytabs-bottom'); + } + this.stripWrap = getEl(this.createStrip(this.el.dom), true); + this.stripEl = getEl(this.createStripList(this.stripWrap.dom), true); + this.stripBody = getEl(this.stripWrap.dom.firstChild.firstChild, true); + if(YAHOO.ext.util.Browser.isIE){ + YAHOO.util.Dom.setStyle(this.stripWrap.dom.firstChild, 'overflow-x', 'hidden'); + } + if(this.tabPosition != 'bottom'){ + /** The body element that contains TabPaneItem bodies. + * @type YAHOO.ext.Element + */ + this.bodyEl = getEl(this.createBody(this.el.dom)); + this.el.addClass('ytabs-top'); + } + this.items = []; + + this.bodyEl.setStyle('position', 'relative'); + + // add indexOf to array if it isn't present + if(!this.items.indexOf){ + this.items.indexOf = function(o){ + for(var i = 0, len = this.length; i < len; i++){ + if(this[i] == o) return i; + } + return -1; + } + } + this.active = null; + this.onTabChange = new YAHOO.util.CustomEvent('TabItem.onTabChange'); + this.activateDelegate = this.activate.createDelegate(this); + + this.events = { + /** + * @event tabchange + * Fires when the active tab changes + * @param {YAHOO.ext.TabPanel} this + * @param {YAHOO.ext.TabPanelItem} activePanel The new active tab + */ + 'tabchange': this.onTabChange, + /** + * @event beforetabchange + * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change + * @param {YAHOO.ext.TabPanel} this + * @param {Object} e Set cancel to true on this object to cancel the tab change + * @param {YAHOO.ext.TabPanelItem} tab The tab being changed to + */ + 'beforetabchange' : new YAHOO.util.CustomEvent('beforechange') + }; + + YAHOO.ext.EventManager.onWindowResize(this.onResize, this, true); + this.cpad = this.el.getPadding('lr'); + this.hiddenCount = 0; +} + +YAHOO.ext.TabPanel.prototype = { + fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent, + on : YAHOO.ext.util.Observable.prototype.on, + addListener : YAHOO.ext.util.Observable.prototype.addListener, + delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener, + removeListener : YAHOO.ext.util.Observable.prototype.removeListener, + purgeListeners : YAHOO.ext.util.Observable.prototype.purgeListeners, + /** + * Creates a new TabPanelItem by looking for an existing element with the provided id - if it's not found it creates one. + * @param {String} id The id of the div to use or create + * @param {String} text The text for the tab + * @param {String} content (optional) Content to put in the TabPanelItem body + * @param {Boolean} closable (optional) True to create a close icon on the tab + * @return {YAHOO.ext.TabPanelItem} The created TabPanelItem + */ + addTab : function(id, text, content, closable){ + var item = new YAHOO.ext.TabPanelItem(this, id, text, closable); + this.addTabItem(item); + if(content){ + item.setContent(content); + } + return item; + }, + + /** + * Returns the TabPanelItem with the specified id/index + * @param {String/Number} id The id or index of the TabPanelItem to fetch. + * @return {YAHOO.ext.TabPanelItem} + */ + getTab : function(id){ + return this.items[id]; + }, + + /** + * Hides the TabPanelItem with the specified id/index + * @param {String/Number} id The id or index of the TabPanelItem to hide. + */ + hideTab : function(id){ + var t = this.items[id]; + if(!t.isHidden()){ + t.setHidden(true); + this.hiddenCount++; + this.autoSizeTabs(); + } + }, + + /** + * "Unhides" the TabPanelItem with the specified id/index + * @param {String/Number} id The id or index of the TabPanelItem to unhide. + */ + unhideTab : function(id){ + var t = this.items[id]; + if(t.isHidden()){ + t.setHidden(false); + this.hiddenCount--; + this.autoSizeTabs(); + } + }, + + /** + * Add an existing TabPanelItem. + * @param {YAHOO.ext.TabPanelItem} item The TabPanelItem to add + */ + addTabItem : function(item){ + this.items[item.id] = item; + this.items.push(item); + if(this.resizeTabs){ + item.setWidth(this.currentTabWidth || this.preferredTabWidth) + this.autoSizeTabs(); + }else{ + item.autoSize(); + } + }, + + /** + * Remove a TabPanelItem. + * @param {String/Number} id The id or index of the TabPanelItem to remove. + */ + removeTab : function(id){ + var items = this.items; + var tab = items[id]; + if(!tab) return; + var index = items.indexOf(tab); + if(this.active == tab && items.length > 1){ + var newTab = this.getNextAvailable(index); + if(newTab)newTab.activate(); + } + this.stripEl.dom.removeChild(tab.pnode.dom); + if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error + this.bodyEl.dom.removeChild(tab.bodyEl.dom); + } + items.splice(index, 1); + delete this.items[tab.id]; + tab.fireEvent('close', tab); + tab.purgeListeners(); + this.autoSizeTabs(); + }, + + getNextAvailable : function(start){ + var items = this.items; + var index = start; + // look for a next tab that will slide over to + // replace the one being removed + while(index < items.length){ + var item = items[++index]; + if(item && !item.isHidden()){ + return item; + } + } + // if one isn't found select the previous tab (on the left) + var index = start; + while(index >= 0){ + var item = items[--index]; + if(item && !item.isHidden()){ + return item; + } + } + return null; + }, + + /** + * Disable a TabPanelItem. It cannot be the active tab, if it is this call is ignored.. + * @param {String/Number} id The id or index of the TabPanelItem to disable. + */ + disableTab : function(id){ + var tab = this.items[id]; + if(tab && this.active != tab){ + tab.disable(); + } + }, + + /** + * Enable a TabPanelItem that is disabled. + * @param {String/Number} id The id or index of the TabPanelItem to enable. + */ + enableTab : function(id){ + var tab = this.items[id]; + tab.enable(); + }, + + /** + * Activate a TabPanelItem. The currently active will be deactivated. + * @param {String/Number} id The id or index of the TabPanelItem to activate. + */ + activate : function(id){ + var tab = this.items[id]; + if(tab == this.active){ + return tab; + } + var e = {}; + this.fireEvent('beforetabchange', this, e, tab); + if(e.cancel !== true && !tab.disabled){ + if(this.active){ + this.active.hide(); + } + this.active = this.items[id]; + this.active.show(); + this.onTabChange.fireDirect(this, this.active); + } + return tab; + }, + + /** + * Get the active TabPanelItem + * @return {YAHOO.ext.TabPanelItem} The active TabPanelItem or null if none are active. + */ + getActiveTab : function(){ + return this.active; + }, + + /** + * Updates the tab body element to fit the height of the container element + * for overflow scrolling + * @param {Number} targetHeight (optional) Override the starting height from the elements height + */ + syncHeight : function(targetHeight){ + var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth('tb')-this.el.getPadding('tb'); + var bm = this.bodyEl.getMargins(); + var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom); + this.bodyEl.setHeight(newHeight); + return newHeight; + }, + + onResize : function(){ + if(this.monitorResize){ + this.autoSizeTabs(); + } + }, + + /** + * Disables tab resizing while tabs are being added (if resizeTabs is false this does nothing) + */ + beginUpdate : function(){ + this.updating = true; + }, + + /** + * Stops an update and resizes the tabs (if resizeTabs is false this does nothing) + */ + endUpdate : function(){ + this.updating = false; + this.autoSizeTabs(); + }, + + /** + * Manual call to resize the tabs (if resizeTabs is false this does nothing) + */ + autoSizeTabs : function(){ + var count = this.items.length; + var vcount = count - this.hiddenCount; + if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return; + var w = Math.max(this.el.getWidth() - this.cpad, 10); + var availWidth = Math.floor(w / vcount); + var b = this.stripBody; + if(b.getWidth() > w){ + var tabs = this.items; + this.setTabWidth(Math.max(availWidth, this.minTabWidth)); + if(availWidth < this.minTabWidth){ + /*if(!this.sleft){ // incomplete scrolling code + this.createScrollButtons(); + } + this.showScroll(); + this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/ + } + }else{ + if(this.currentTabWidth < this.preferredTabWidth){ + this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)); + } + } + }, + + /** + * Returns the number of tabs + * @return {Number} + */ + getCount : function(){ + return this.items.length; + }, + + /** + * Resizes all the tabs to the passed width + * @param {Number} The new width + */ + setTabWidth : function(width){ + this.currentTabWidth = width; + for(var i = 0, len = this.items.length; i < len; i++) { + if(!this.items[i].isHidden())this.items[i].setWidth(width); + } + }, + + /** + * Destroys this TabPanel + * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well + */ + destroy : function(removeEl){ + YAHOO.ext.EventManager.removeResizeListener(this.onResize, this); + for(var i = 0, len = this.items.length; i < len; i++){ + this.items[i].purgeListeners(); + } + if(removeEl === true){ + this.el.update(''); + this.el.remove(); + } + } +}; + +/** +* @class YAHOO.ext.TabPanelItem +* @extends YAHOO.ext.util.Observable +*/ +YAHOO.ext.TabPanelItem = function(tabPanel, id, text, closable){ + /** + * The TabPanel this TabPanelItem belongs to + * @type YAHOO.ext.TabPanel + */ + this.tabPanel = tabPanel; + /** + * The id for this TabPanelItem + * @type String + */ + this.id = id; + /** @private */ + this.disabled = false; + /** @private */ + this.text = text; + /** @private */ + this.loaded = false; + this.closable = closable; + + /** + * The body element for this TabPanelItem + * @type YAHOO.ext.Element + */ + this.bodyEl = getEl(tabPanel.createItemBody(tabPanel.bodyEl.dom, id)); + this.bodyEl.setVisibilityMode(YAHOO.ext.Element.VISIBILITY); + this.bodyEl.setStyle('display', 'block'); + this.bodyEl.setStyle('zoom', '1'); + this.hideAction(); + + var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable); + /** @private */ + this.el = getEl(els.el, true); + this.inner = getEl(els.inner, true); + this.textEl = getEl(this.el.dom.firstChild.firstChild.firstChild, true); + this.pnode = getEl(els.el.parentNode, true); + this.el.mon('click', this.onTabClick, this, true); + /** @private */ + if(closable){ + var c = getEl(els.close, true); + c.dom.title = this.closeText; + c.addClassOnOver('close-over'); + c.mon('click', this.closeClick, this, true); + } + + // these two are now private and deprecated + this.onActivate = new YAHOO.util.CustomEvent('TabItem.onActivate'); + this.onDeactivate = new YAHOO.util.CustomEvent('TabItem.onDeactivate'); + + this.events = { + /** + * @event activate + * Fires when this tab becomes the active tab + * @param {YAHOO.ext.TabPanel} tabPanel + * @param {YAHOO.ext.TabPanelItem} this + */ + 'activate': this.onActivate, + /** + * @event beforeclose + * Fires before this tab is closed. To cancal the close, set cancel to true on e. (e.cancel = true) + * @param {YAHOO.ext.TabPanelItem} this + * @param {Object} e Set cancel to true on this object to cancel the close. + */ + 'beforeclose': new YAHOO.util.CustomEvent('beforeclose'), + /** + * @event close + * Fires when this tab is closed + * @param {YAHOO.ext.TabPanelItem} this + */ + 'close': new YAHOO.util.CustomEvent('close'), + /** + * @event deactivate + * Fires when this tab is no longer the active tab + * @param {YAHOO.ext.TabPanel} tabPanel + * @param {YAHOO.ext.TabPanelItem} this + */ + 'deactivate' : this.onDeactivate + }; + this.hidden = false; +}; + +YAHOO.ext.TabPanelItem.prototype = { + fireEvent : YAHOO.ext.util.Observable.prototype.fireEvent, + on : YAHOO.ext.util.Observable.prototype.on, + addListener : YAHOO.ext.util.Observable.prototype.addListener, + delayedListener : YAHOO.ext.util.Observable.prototype.delayedListener, + removeListener : YAHOO.ext.util.Observable.prototype.removeListener, + purgeListeners : function(){ + YAHOO.ext.util.Observable.prototype.purgeListeners.call(this); + this.el.removeAllListeners(); + }, + /** + * Show this TabPanelItem - this does not deactivate the currently active TabPanelItem. + */ + show : function(){ + this.pnode.addClass('on'); + this.showAction(); + if(YAHOO.ext.util.Browser.isOpera){ + this.tabPanel.stripWrap.repaint(); + } + this.onActivate.fireDirect(this.tabPanel, this); + }, + + /** + * Returns true if this tab is the active tab + * @return {Boolean} + */ + isActive : function(){ + return this.tabPanel.getActiveTab() == this; + }, + + /** + * Hide this TabPanelItem - if you don't activate another TabPanelItem this could look odd. + */ + hide : function(){ + this.pnode.removeClass('on'); + this.hideAction(); + this.onDeactivate.fireDirect(this.tabPanel, this); + }, + + hideAction : function(){ + this.bodyEl.setStyle('position', 'absolute'); + this.bodyEl.setLeft('-20000px'); + this.bodyEl.setTop('-20000px'); + this.bodyEl.hide(); + }, + + showAction : function(){ + this.bodyEl.setStyle('position', 'relative'); + this.bodyEl.setTop(''); + this.bodyEl.setLeft(''); + this.bodyEl.show(); + this.tabPanel.el.repaint.defer(1); + }, + + /** + * Set the tooltip (title attribute) for the tab + * @param {String} tooltip + */ + setTooltip : function(text){ + this.textEl.dom.title = text; + }, + + onTabClick : function(e){ + e.preventDefault(); + this.tabPanel.activate(this.id); + }, + + getWidth : function(){ + return this.inner.getWidth(); + }, + + setWidth : function(width){ + var iwidth = width - this.pnode.getPadding("lr"); + this.inner.setWidth(iwidth); + this.textEl.setWidth(iwidth-this.inner.getPadding('lr')); + this.pnode.setWidth(width); + }, + + setHidden : function(hidden){ + this.hidden = hidden; + this.pnode.setStyle('display', hidden ? 'none' : ''); + }, + + /** + * Returns true if this tab is "hidden" + * @return {Boolean} + */ + isHidden : function(){ + return this.hidden; + }, + + /** + * Returns the text for this tab + * @return {String} + */ + getText : function(){ + return this.text; + }, + + autoSize : function(){ + this.el.beginMeasure(); + this.textEl.setWidth(1); + this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding('lr')); + this.el.endMeasure(); + }, + + /** + * Sets the text for the tab (Note: this also sets the tooltip) + * @param {String} text + */ + setText : function(text){ + this.text = text; + this.textEl.update(text); + this.textEl.dom.title = text; + if(!this.tabPanel.resizeTabs){ + this.autoSize(); + } + }, + /** + * Activate this TabPanelItem - this does deactivate the currently active TabPanelItem. + */ + activate : function(){ + this.tabPanel.activate(this.id); + }, + + /** + * Disable this TabPanelItem - this call is ignore if this is the active TabPanelItem. + */ + disable : function(){ + if(this.tabPanel.active != this){ + this.disabled = true; + this.pnode.addClass('disabled'); + } + }, + + /** + * Enable this TabPanelItem if it was previously disabled. + */ + enable : function(){ + this.disabled = false; + this.pnode.removeClass('disabled'); + }, + + /** + * Set the content for this TabPanelItem. + * @param {String} content The content + * @param {Boolean} loadScripts true to look for and load scripts + */ + setContent : function(content, loadScripts){ + this.bodyEl.update(content, loadScripts); + }, + + /** + * Get the {@link YAHOO.ext.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates. + * @return {YAHOO.ext.UpdateManager} The UpdateManager + */ + getUpdateManager : function(){ + return this.bodyEl.getUpdateManager(); + }, + + /** + * Set a URL to be used to load the content for this TabPanelItem. + * @param {String/Function} url The url to load the content from or a function to call to get the url + * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link YAHOO.ext.UpdateManager#update} for more details. (Defaults to null) + * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false) + * @return {YAHOO.ext.UpdateManager} The UpdateManager + */ + setUrl : function(url, params, loadOnce){ + if(this.refreshDelegate){ + this.onActivate.unsubscribe(this.refreshDelegate); + } + this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]); + this.onActivate.subscribe(this.refreshDelegate); + return this.bodyEl.getUpdateManager(); + }, + + /** @private */ + _handleRefresh : function(url, params, loadOnce){ + if(!loadOnce || !this.loaded){ + var updater = this.bodyEl.getUpdateManager(); + updater.update(url, params, this._setLoaded.createDelegate(this)); + } + }, + + /** + * Force a content refresh from the URL specified in the setUrl() method. + * Will fail silently if the setUrl method has not been called. + * This does not activate the panel, just updates its content. + */ + refresh : function(){ + if(this.refreshDelegate){ + this.loaded = false; + this.refreshDelegate(); + } + }, + + /** @private */ + _setLoaded : function(){ + this.loaded = true; + }, + + /** @private */ + closeClick : function(e){ + var e = {}; + this.fireEvent('beforeclose', this, e); + if(e.cancel !== true){ + this.tabPanel.removeTab(this.id); + } + }, + /** + * The text displayed in the tooltip for the close icon. + * @type String + */ + closeText : 'Close this tab' +}; + +/** @private */ +YAHOO.ext.TabPanel.prototype.createStrip = function(container){ + var strip = document.createElement('div'); + strip.className = 'ytab-wrap'; + container.appendChild(strip); + return strip; +}; +/** @private */ +YAHOO.ext.TabPanel.prototype.createStripList = function(strip){ + // div wrapper for retard IE + strip.innerHTML = '
'; + return strip.firstChild.firstChild.firstChild.firstChild; +}; +/** @private */ +YAHOO.ext.TabPanel.prototype.createBody = function(container){ + var body = document.createElement('div'); + YAHOO.util.Dom.generateId(body, 'tab-body'); + YAHOO.util.Dom.addClass(body, 'yui-ext-tabbody'); + container.appendChild(body); + return body; +}; +/** @private */ +YAHOO.ext.TabPanel.prototype.createItemBody = function(bodyEl, id){ + var body = YAHOO.util.Dom.get(id); + if(!body){ + body = document.createElement('div'); + body.id = id; + } + YAHOO.util.Dom.addClass(body, 'yui-ext-tabitembody'); + bodyEl.insertBefore(body, bodyEl.firstChild); + return body; +}; +/** @private */ +YAHOO.ext.TabPanel.prototype.createStripElements = function(stripEl, text, closable){ + var td = document.createElement('td'); + stripEl.appendChild(td); + if(closable){ + td.className = "ytab-closable"; + if(!this.closeTpl){ + this.closeTpl = new YAHOO.ext.Template( + '' + + '{text}' + + '
 
' + ); + } + var el = this.closeTpl.overwrite(td, {'text': text}); + var close = el.getElementsByTagName('div')[0]; + var inner = el.getElementsByTagName('em')[0]; + return {'el': el, 'close': close, 'inner': inner}; + } else { + if(!this.tabTpl){ + this.tabTpl = new YAHOO.ext.Template( + '' + + '{text}' + ); + } + var el = this.tabTpl.overwrite(td, {'text': text}); + var inner = el.getElementsByTagName('em')[0]; + return {'el': el, 'inner': inner}; + } +}; -- cgit v0.9.0.2