UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

556 lines (527 loc) 15.9 kB
/** * @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. * @property {Bool} active Whether this page is the active on in tab view. User can also set this value to true to run into this page. * Note: set this property to false will do nothing. */ 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}); this.defineProp('active', {'dataType': DataType.STRING, 'getter': function() { var v = this.getTabView(); return v? (v.getActiveTabPage() === this): this.getPropStoreFieldValue('active'); }, 'setter': function(value) { this.setPropStoreFieldValue('active', value); var v = this.getTabView(); if (v) { if (!!value) v.setActiveTabPage(this); } } }); }, /** @ignore */ doGetWidgetClassName: function(/*$super*/) { return this.tryApplySuper('doGetWidgetClassName') /* $super() */ + ' ' + CNS.TABVIEW_PAGE; }, /** @ignore */ doCreateRootElement: function(doc) { var result = doc.createElement('div'); return result; }, /** * Returns the parent tab view widget. * @return {Kekule.Widget.TabView} */ getTabView: function() { var p = this.getParent(); return (p instanceof Kekule.Widget.TabView)? p: null; } }); /** * 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) { this.tryApplySuper('initialize', [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(); } } }); this.defineProp('activeTabIndex', {'dataType': DataType.INT, 'getter': function() { var buttons = this.getTabButtons(); for (var i = 0, l = buttons.length; i < l; ++i) { if (buttons[i].getChecked()) return i; } return -1; }, 'setter': function(value) { var buttons = this.getTabButtons(); for (var i = 0, l = buttons.length; i < l; ++i) { buttons[i].setChecked(i === value); } } }); }, /** @private */ initPropValues: function(/*$super*/) { this.tryApplySuper('initPropValues') /* $super() */; this.setUseCornerDecoration(false); }, /** @ignore */ doGetWidgetClassName: function(/*$super*/) { return this.tryApplySuper('doGetWidgetClassName') /* $super() */ + ' ' + CNS.TABBUTTONGROUP; }, /** * Returns the array of child tab buttons. * @returns {Array} */ getTabButtons: function() { var result = []; var children = this.getChildWidgets(); for (var i = 0, l = children.length; i < l; ++i) { var child = children[i]; if (child instanceof Kekule.Widget.RadioButton) result.push(child); } return result; }, /** @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; this.tryApplySuper('initialize', [parentOrElementOrDocument]) /* $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(); this.tryApplySuper('doFinalize') /* $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 this.tryApplySuper('doGetWidgetClassName') /* $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'); var pageContainer = this.doCreateContainerElement(doc, null, '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) { this.tryApplySuper('childWidgetAdded', [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 || widget.getActive()) this.setActiveTabPage(widget); } }, /** @private */ childWidgetRemoved: function(/*$super, */widget) { this.tryApplySuper('childWidgetRemoved', [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) { this.tryApplySuper('childWidgetMoved', [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; } }); })();