/**
* @fileoverview
* A tab widget.
* @author Partridge Jiang
*/
(function(){
"use strict";
var EU = Kekule.HtmlElementUtils;
var CNS = Kekule.Widget.HtmlClassNames;
var AU = Kekule.ArrayUtils;
/** @ignore */
Kekule.Widget.HtmlClassNames = Object.extend(Kekule.Widget.HtmlClassNames, {
TABVIEW: 'K-TabView',
TABVIEW_TABBUTTON: 'K-TabView-TabButton',
TABVIEW_CLIENT: 'K-TabView-Client',
TABVIEW_PAGE: 'K-TabView-Page',
TABVIEW_ACTIVE_PAGE: 'K-TabView-Active-Page',
TABVIEW_TABBUTTON_CONTAINER: 'K-TabView-TabButton-Container',
TABVIEW_PAGE_CONTAINER: 'K-TabView-Page-Container',
TABBUTTONGROUP: 'K-TabButtonGroup',
TAB_AT_LEFT: 'K-TabAtLeft',
TAB_AT_RIGHT: 'K-TabAtRight',
TAB_AT_TOP: 'K-TabAtTop',
TAB_AT_BOTTOM: 'K-TabAtBottom'
});
/**
* Tab page inside tab view widget. Do not use this class alone.
* @class
* @augments Kekule.Widget.BaseWidget
*
* @property {String} text Caption text of tab page. It will be shown in tab button of tab view.
*/
Kekule.Widget.TabPage = Class.create(Kekule.Widget.BaseWidget,
/** @lends Kekule.Widget.TabPage# */
{
/** @private */
CLASS_NAME: 'Kekule.Widget.TabPage',
/** @private */
initProperties: function()
{
this.defineProp('text', {'dataType': DataType.STRING});
},
/** @ignore */
doGetWidgetClassName: function($super)
{
return $super() + ' ' + CNS.TABVIEW_PAGE;
},
/** @ignore */
doCreateRootElement: function(doc)
{
var result = doc.createElement('div');
return result;
}
});
/**
* Tab button set inside tab view widget.
* @class
* @augments Kekule.Widget.ButtonGroup
*/
/**
* Invoked when a new tab button is switched to.
* event param of it has fields: {button, index}
* @name Kekule.Widget.TabButtonGroup#switch
* @event
*/
Kekule.Widget.TabButtonGroup = Class.create(Kekule.Widget.ButtonGroup,
/** @lends Kekule.Widget.TabButtonGroup# */
{
/** @private */
CLASS_NAME: 'Kekule.Widget.TabButtonGroup',
/** @constructs */
initialize: function($super, parentOrElementOrDocument)
{
$super(parentOrElementOrDocument);
this.addEventListener('execute'/*'check'*/, function(e)
{
var target = e.target;
if ((target instanceof Kekule.Widget.RadioButton)/* && (target.getChecked())*/) // switch may fail and the tab button be not be checked
{
this.invokeEvent('switch', {'button': target});
}
}, this);
this.tabButtonPosChanged();
},
/** @private */
initProperties: function()
{
this.defineProp('tabButtonPosition', {'dataType': DataType.INT,
'enumSource': Kekule.Widget.Position,
'setter': function(value)
{
if (this.getTabButtonPosition() !== value)
{
this.setPropStoreFieldValue('tabButtonPosition', value);
this.tabButtonPosChanged();
}
}
});
},
/** @private */
initPropValues: function($super)
{
$super();
this.setUseCornerDecoration(false);
},
/** @ignore */
doGetWidgetClassName: function($super)
{
return $super() + ' ' + CNS.TABBUTTONGROUP;
},
/** @private */
tabButtonPosChanged: function()
{
var WP = Kekule.Widget.Position;
var pos = this.getTabButtonPosition();
var isVertical = (pos & WP.LEFT) || (pos & WP.RIGHT)
var tabPosClassName = (pos & WP.RIGHT)? CNS.TAB_AT_RIGHT:
(pos & WP.BOTTOM)? CNS.TAB_AT_BOTTOM:
(pos & WP.LEFT)? CNS.TAB_AT_LEFT:
CNS.TAB_AT_TOP; // default
if (this._lastTabBtnPosClassName)
{
this.removeClassName(this._lastTabBtnPosClassName);
}
this._lastTabBtnPosClassName = tabPosClassName;
this.addClassName(this._lastTabBtnPosClassName);
this.setLayout(isVertical? Kekule.Widget.Layout.VERTICAL: Kekule.Widget.Layout.HORIZONTAL);
}
});
/**
* Tab view widget.
* @class
* @augments Kekule.Widget.Container
*
* @property {Array} tagPages All pages inside view.
* @property {Kekule.Widget.TabPage} activeTabPage Currently selected tab page.
* @property {Int} activeTabIndex Index of current active page.
* @property {Int} tabButtonPosition Position of tab button, value from {@link Kekule.Widget.Position}.
*/
/**
* Invoked when a new tab is switched to.
* event param of it has fields: {tabPage}
* @name Kekule.Widget.TabView#switchTab
* @event
*/
Kekule.Widget.TabView = Class.create(Kekule.Widget.Container,
/** @lends Kekule.Widget.TabView# */
{
/** @private */
CLASS_NAME: 'Kekule.Widget.TabView',
/** @private */
TAB_BTN_FIELD: '__$tabButton__',
/** @constructs */
initialize: function($super, parentOrElementOrDocument)
{
this._tabBtnContainer = null;
this._pageContainer = null;
$super(parentOrElementOrDocument);
if (Kekule.ObjUtils.isUnset(this.getUseCornerDecoration()))
this.setUseCornerDecoration(true);
this.tabButtonPosChanged(); // update tab button pos
this.addEventListener('change', this.reactTabPagePropChange, this); // actually listen children's change event
},
/** @ignore */
doFinalize: function($super)
{
if (this.getTabGroup())
this.getTabGroup().finalize();
$super();
},
/** @private */
initProperties: function()
{
this.defineProp('tabPages', {
'dataType': DataType.ARRAY, 'serializable': false,
'getter': function () {
var result = this.getPropStoreFieldValue('tabPages');
if (!result) {
result = [];
this.setPropStoreFieldValue('tabPages', result);
}
return result;
},
setter: null
});
this.defineProp('activeTabPage', {'dataType': 'Kekule.Widget.TabPage', 'serializable': false,
'setter': function(value)
{
var old = this.getActiveTabPage();
if (old !== value)
{
if (this.hasChild(value))
{
this.setPropStoreFieldValue('activeTabPage', value);
if (old)
{
old.removeClassName(CNS.TABVIEW_ACTIVE_PAGE);
this._getPageTabButton(old).setChecked(false);
}
if (value)
{
value.addClassName(CNS.TABVIEW_ACTIVE_PAGE);
this._getPageTabButton(value).setChecked(true);
this.invokeEvent('switchTab', {'tabPage': value});
}
}
}
}
});
this.defineProp('activeTabIndex', {'dataType': DataType.INT, 'serializable': false,
'getter': function()
{
return this.getTabPages().indexOf(this.getActiveTabPage());
},
'setter': function(value)
{
var page = this.getTabPages()[value];
if (page)
this.setActiveTabPage(page);
}
});
this.defineProp('tabButtonPosition', {'dataType': DataType.INT,
'enumSource': Kekule.Widget.Position,
'setter': function(value)
{
if (this.getTabButtonPosition() !== value)
{
this.setPropStoreFieldValue('tabButtonPosition', value);
this.tabButtonPosChanged();
}
}
});
this.defineProp('showTabButtons', {'dataType': DataType.BOOL,
'getter': function()
{
var t = this.getTabGroup();
return t && t.getDisplayed();
},
'setter': function(value)
{
var t = this.getTabGroup();
if (t)
t.setDisplayed(value);
}
});
// private
this.defineProp('tabGroup', {'dataType': 'Kekule.Widget.ButtonGroup', 'serializable': false, 'setter': null, 'scope': Class.PropertyScope.PRIVATE});
},
/** @ignore */
doGetWidgetClassName: function($super)
{
return $super() + ' ' + CNS.TABVIEW;
},
/** @ignore */
doCreateRootElement: function(doc)
{
var result = doc.createElement('div');
return result;
},
/** @ignore */
doCreateSubElements: function(doc, docFragment)
{
var tabBtnContainer = doc.createElement('div');
tabBtnContainer.className = CNS.TABVIEW_TABBUTTON_CONTAINER;
var pageContainer = doc.createElement('div');
pageContainer.className = CNS.TABVIEW_PAGE_CONTAINER;
this._tabBtnContainer = tabBtnContainer;
this._pageContainer = pageContainer;
docFragment.appendChild(tabBtnContainer);
docFragment.appendChild(pageContainer);
this.setPropStoreFieldValue('tabGroup', this.doCreateTabbar(doc, tabBtnContainer));
var result = [tabBtnContainer, pageContainer];
return result;
},
/** @private */
doCreateTabbar: function(doc, parentElem)
{
var result = new Kekule.Widget.TabButtonGroup(doc);
result.setParent(this);
result.appendToElem(parentElem);
result.addEventListener('switch', function(e){
var btn = e.button;
//if (btn.getChecked()) // when switching begins, the btn may not be checked yet
{
var page = this._getPageOfTabButton(btn);
if (page)
{
this.setActiveTabPage(page);
}
}
}, this);
return result;
},
/** @ignore */
getChildrenHolderElement: function()
{
return this._pageContainer;
},
getContainerElement: function()
{
return this._pageContainer;
},
/** @private */
_getPageTabButton: function(tabPage)
{
return tabPage[this.TAB_BTN_FIELD];
},
/** @private */
_setPageTabButton: function(tabPage, button)
{
tabPage[this.TAB_BTN_FIELD] = button;
},
/** @private */
_getPageOfTabButton: function(button)
{
var pages = this.getTabPages();
for (var i = 0, l = pages.length; i < l; ++i)
{
var p = pages[i];
if (this._getPageTabButton(p) === button)
return p;
}
return null;
},
/** @ignore */
childWidgetAdded: function($super, widget)
{
$super(widget);
if (widget instanceof Kekule.Widget.TabPage) // a new page is added, update tab buttons
{
var isFirstPage = !this.getTabPages().length;
// ensure add to page container
widget.appendToElem(this._pageContainer);
this.getTabPages().push(widget);
this._insertTabButtonBefore(widget);
if (isFirstPage)
this.setActiveTabPage(widget);
}
},
/** @private */
childWidgetRemoved: function($super, widget)
{
$super(widget);
if (widget instanceof Kekule.Widget.TabPage) // a old page is removed, update tab buttons
{
AU.remove(this.getTabPages(), widget);
this._removeTabButton(widget);
}
},
/** @private */
childWidgetMoved: function($super, widget, newIndex)
{
$super(widget, newIndex);
if (widget instanceof Kekule.Widget.TabPage) // page index changed, update tab buttons
{
this._changeTabIndex(widget, newIndex);
}
},
/** @private */
_insertTabButtonBefore: function(tabPage, refButton)
{
var btnGroup = this.getTabGroup();
var newButton = new Kekule.Widget.RadioButton(this.getDocument());
this._updateTabButton(tabPage, newButton);
this._setPageTabButton(tabPage, newButton);
if (refButton)
newButton.insertToWidget(btnGroup, refButton);
else
newButton.appendToWidget(btnGroup);
},
/** @private */
_updateTabButton: function(tabPage, button)
{
if (!button)
button = this._getPageTabButton(tabPage);
button.setText(tabPage.getText() || '');
if (tabPage.getHint())
button.setHint(tabPage.getHint());
},
/** @private */
_removeTabButton: function(tabPage)
{
var btn = this._getPageTabButton(tabPage);
if (btn)
btn.finalize();
},
/** @private */
_changeTabIndex: function(tabPage, newIndex)
{
var btn = this._getPageTabButton(tabPage);
this.getTabGroup()._moveChild(btn, newIndex);
var pages = this.getTabPages();
var oldIndex = pages.indexOf(tabPage);
if (oldIndex >= 0 && oldIndex !== newIndex)
{
AU.changeItemIndex(pages, tabPage, newIndex);
}
},
/** @private */
reactTabPagePropChange: function(e)
{
var target = e.target;
if (target instanceof Kekule.Widget.TabPage)
{
var btn = this._getPageTabButton(target);
if (btn)
this._updateTabButton(target, btn);
}
},
/** @private */
tabButtonPosChanged: function()
{
var WP = Kekule.Widget.Position;
var pos = this.getTabButtonPosition();
var isAtTailing = (pos & WP.BOTTOM) || (pos & WP.RIGHT);
var isVertical = (pos & WP.LEFT) || (pos & WP.RIGHT)
var tabPosClassName = (pos & WP.RIGHT)? CNS.TAB_AT_RIGHT:
(pos & WP.BOTTOM)? CNS.TAB_AT_BOTTOM:
(pos & WP.LEFT)? CNS.TAB_AT_LEFT:
CNS.TAB_AT_TOP; // default
if (this._lastTabBtnPosClassName)
{
this.removeClassName(this._lastTabBtnPosClassName);
//this.getTabGroup().removeClassName(this._lastTabBtnPosClassName);
}
this._lastTabBtnPosClassName = tabPosClassName;
this.addClassName(this._lastTabBtnPosClassName);
//this.getTabGroup().addClassName(this._lastTabBtnPosClassName);
//this.getTabGroup().setLayout(isVertical? Kekule.Widget.Layout.VERTICAL: Kekule.Widget.Layout.HORIZONTAL);
this.getTabGroup().setTabButtonPosition(pos);
if (isAtTailing)
this.getElement().appendChild(this._tabBtnContainer);
else
this.getElement().insertBefore(this._tabBtnContainer, this._pageContainer);
},
/**
* Create a new tab page in tab view.
* @param {String} title Tab title
* @param {String} hint
* @param {Kekule.Widget.TabPage} refPage If this value is set, new page will be inserted before it.
* @return {Kekule.Widget.TabPage}
*/
createNewTabPage: function(title, hint, refPage)
{
var doc = this.getDocument();
var result = new Kekule.Widget.TabPage(doc);
//var result = new Kekule.Widget.TabPage(this);
result.setText(title);
if (hint)
result.setHint(hint);
//result.setParent(this);
result.appendToWidget(this);
// adjust index
if (refPage)
{
var refIndex = this.getChildWidgets().indexOf(refPage);
this._moveChild(result, refIndex);
}
return result;
}
});
})();