PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs/yui/3.1.0pr1/tabview/tabview.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 470 lines | 332 code | 81 blank | 57 comment | 35 complexity | e8de1739f04e58cd67c0d7a4a83635be MD5 | raw file
  1. YUI.add('tabview', function(Y) {
  2. var getClassName = Y.ClassNameManager.getClassName,
  3. TABVIEW = 'tabview',
  4. TAB = 'tab',
  5. CONTENT = 'content',
  6. PANEL = 'panel',
  7. SELECTED = 'selected',
  8. EMPTY_OBJ = {},
  9. DOT = '.',
  10. _classNames = {
  11. tabview: getClassName(TABVIEW),
  12. tabviewPanel: getClassName(TABVIEW, PANEL),
  13. tabviewList: getClassName(TABVIEW, 'list'),
  14. tab: getClassName(TAB),
  15. tabLabel: getClassName(TAB, 'label'),
  16. tabPanel: getClassName(TAB, PANEL),
  17. selectedTab: getClassName(TAB, SELECTED),
  18. selectedPanel: getClassName(TAB, PANEL, SELECTED)
  19. },
  20. _queries = {
  21. tabview: DOT + _classNames.tabview,
  22. tabviewList: '> ul',
  23. tab: '> ul > li',
  24. tabLabel: '> ul > li > a ',
  25. tabviewPanel: '> div',
  26. tabPanel: '> div > div',
  27. selectedTab: '> ul > ' + DOT + _classNames.selectedTab,
  28. selectedPanel: '> div ' + DOT + _classNames.selectedPanel
  29. },
  30. TabviewBase = function(config) {
  31. this.init.apply(this, arguments);
  32. };
  33. TabviewBase.NAME = 'tabviewBase';
  34. TabviewBase._queries = _queries;
  35. TabviewBase._classNames = _classNames;
  36. Y.mix(TabviewBase.prototype, {
  37. init: function(config) {
  38. config = config || EMPTY_OBJ;
  39. this._node = config.host || Y.one(config.node);
  40. this.refresh();
  41. },
  42. initClassNames: function(index) {
  43. Y.Object.each(_queries, function(query, name) {
  44. // this === tabview._node
  45. if (_classNames[name]) {
  46. var result = this.all(query);
  47. if (index !== undefined) {
  48. result = result.item(index);
  49. }
  50. if (result) {
  51. result.addClass(_classNames[name]);
  52. }
  53. }
  54. }, this._node);
  55. this._node.addClass(_classNames.tabview);
  56. },
  57. _select: function(index) {
  58. var node = this._node,
  59. oldItem = node.one(_queries.selectedTab),
  60. oldContent = node.one(_queries.selectedPanel),
  61. newItem = node.all(_queries.tab).item(index),
  62. newContent = node.all(_queries.tabPanel).item(index);
  63. if (oldItem) {
  64. oldItem.removeClass(_classNames.selectedTab);
  65. }
  66. if (oldContent) {
  67. oldContent.removeClass(_classNames.selectedPanel);
  68. }
  69. if (newItem) {
  70. newItem.addClass(_classNames.selectedTab);
  71. }
  72. if (newContent) {
  73. newContent.addClass(_classNames.selectedPanel);
  74. }
  75. },
  76. initState: function() {
  77. var node = this._node,
  78. activeNode = node.one(_queries.selectedTab),
  79. activeIndex = activeNode ?
  80. node.all(_queries.tab).indexOf(activeNode) : 0;
  81. this._select(activeIndex);
  82. },
  83. // collapse extra space between list-items
  84. _scrubTextNodes: function() {
  85. this._node.one(_queries.tabviewList).get('childNodes').each(function(node) {
  86. console.log(node);
  87. if (node.get('nodeType') === 3) { // text node
  88. node.remove();
  89. }
  90. });
  91. },
  92. // base renderer only enlivens existing markup
  93. refresh: function() {
  94. this._scrubTextNodes();
  95. this.initClassNames();
  96. this.initState();
  97. this.initEvents();
  98. },
  99. tabEventName: 'click',
  100. initEvents: function() {
  101. // TODO: detach prefix for delegate?
  102. // this._node.delegate('tabview|' + this.tabEventName),
  103. this._node.delegate(this.tabEventName,
  104. this.onTabEvent,
  105. _queries.tab,
  106. this
  107. );
  108. },
  109. onTabEvent: function(e) {
  110. e.preventDefault();
  111. this._select(this._node.all(_queries.tab).indexOf(e.currentTarget));
  112. },
  113. destroy: function() {
  114. this._node.detach('tabview|*');
  115. }
  116. });
  117. Y.TabviewBase = TabviewBase;
  118. /**
  119. * The TabView module
  120. *
  121. * @module tabview
  122. */
  123. var _queries = Y.TabviewBase._queries,
  124. _classNames = Y.TabviewBase._classNames,
  125. DOT = '.',
  126. _isGeckoIEWin = ((Y.UA.gecko || Y.UA.ie) && navigator.userAgent.indexOf("Windows") > -1),
  127. getClassName = Y.ClassNameManager.getClassName,
  128. /**
  129. * Provides a tabbed widget interface
  130. * @param config {Object} Object literal specifying tabview configuration properties.
  131. *
  132. * @class TabView
  133. * @constructor
  134. * @extends Widget
  135. */
  136. TabView = Y.Base.create('tabView', Y.Widget, [Y.WidgetParent], {
  137. _afterChildRemoved: function(e) { // update the selected tab when removed
  138. var i = e.index,
  139. selection = this.get('selection');
  140. if (!selection) { // select previous item if selection removed
  141. selection = this.item(i - 1) || this.item(0);
  142. if (selection) {
  143. selection.set('selected', 1);
  144. }
  145. }
  146. },
  147. _initAria: function() {
  148. var contentBox = this.get('contentBox'),
  149. tablist = contentBox.one(_queries.tabviewList);
  150. if (tablist) {
  151. tablist.setAttrs({
  152. //'aria-labelledby':
  153. role: tablist
  154. });
  155. }
  156. // Since the anchor's "href" attribute has been removed, the
  157. // element will not fire the click event in Firefox when the
  158. // user presses the enter key. To fix this, dispatch the
  159. // "click" event to the anchor when the user presses the
  160. // enter key.
  161. if (_isGeckoIEWin) {
  162. tabView.delegate('keydown', function (event) {
  163. if (event.charCode === 13) {
  164. this.simulate("click");
  165. }
  166. }, ">ul>li>a");
  167. }
  168. },
  169. bindUI: function() {
  170. // Use the Node Focus Manager to add keyboard support:
  171. // Pressing the left and right arrow keys will move focus
  172. // among each of the tabs.
  173. this.get('contentBox').plug(Y.Plugin.NodeFocusManager, {
  174. descendants: _queries.tabLabel,
  175. keys: { next: 'down:39', // Right arrow
  176. previous: 'down:37' }, // Left arrow
  177. circular: true
  178. });
  179. this.after('removeChild', this._afterChildRemoved);
  180. },
  181. renderUI: function() {
  182. var contentBox = this.get('contentBox');
  183. this._renderListBox(contentBox);
  184. this._renderPanelBox(contentBox);
  185. this._renderTabs(contentBox);
  186. this._setDefSelection(contentBox);
  187. },
  188. _setDefSelection: function() {
  189. // If no tab is selected, select the first tab.
  190. var firstItem = this.item(0);
  191. if (!this.get('selection') && firstItem) {
  192. firstItem.set('selected', 1);
  193. }
  194. },
  195. _renderListBox: function(contentBox) {
  196. var list = contentBox.one(_queries.tabviewList);
  197. if (!list) {
  198. contentBox.append(TabView.LIST_TEMPLATE);
  199. } else {
  200. list.addClass(_classNames.tabviewList);
  201. }
  202. },
  203. _renderPanelBox: function(contentBox) {
  204. var panel = contentBox.one(_queries.tabviewPanel);
  205. if (!panel) {
  206. contentBox.append(TabView.PANEL_TEMPLATE);
  207. } else {
  208. panel.addClass(_classNames.tabviewPanel);
  209. }
  210. },
  211. _renderTabs: function(contentBox) {
  212. var tabs = contentBox.all(_queries.tab),
  213. panels = contentBox.all(_queries.tabPanel),
  214. tabview = this;
  215. if (tabs) { // add classNames and fill in Tab fields from markup when possible
  216. tabs.addClass(_classNames.tab);
  217. contentBox.all(_queries.tabLabel).addClass(_classNames.tabLabel);
  218. contentBox.all(_queries.tabPanel).addClass(_classNames.tabPanel);
  219. tabs.each(function(node, i) {
  220. var panelNode = panels.item(i);
  221. tabview.add({
  222. boundingBox: node,
  223. contentBox: node.one(DOT + _classNames.tabLabel),
  224. label: node.one(DOT + _classNames.tabLabel).get('text'),
  225. content: panelNode ? panelNode.get('innerHTML') : null
  226. });
  227. });
  228. }
  229. }
  230. }, {
  231. LIST_TEMPLATE: '<ul class="' + _classNames.tabviewList + '"></ul>',
  232. PANEL_TEMPLATE: '<div class="' + _classNames.tabviewPanel + '"></div>',
  233. ATTRS: {
  234. defaultChildType: {
  235. value: 'Tab'
  236. },
  237. tabIndex: {
  238. value: null
  239. //validator: '_validTabIndex'
  240. }
  241. }
  242. });
  243. Y.TabView = TabView;
  244. var Lang = Y.Lang,
  245. _queries = Y.TabviewBase._queries,
  246. _classNames = Y.TabviewBase._classNames,
  247. _isGeckoIEWin = ((Y.UA.gecko || Y.UA.ie) && navigator.userAgent.indexOf("Windows") > -1),
  248. getClassName = Y.ClassNameManager.getClassName;
  249. /**
  250. * Provides Tab instances for use with TabView
  251. * @param config {Object} Object literal specifying tabview configuration properties.
  252. *
  253. * @class Tab
  254. * @constructor
  255. * @extends Widget
  256. */
  257. Y.Tab = Y.Base.create('tab', Y.Widget, [Y.WidgetChild], {
  258. BOUNDING_TEMPLATE : '<li class="' + _classNames.tab + '"></li>',
  259. CONTENT_TEMPLATE : '<a class="' + _classNames.tabLabel + '"></a>',
  260. PANEL_TEMPLATE: '<div class="' + _classNames.tabPanel + '"></div>',
  261. _uiSetSelectedPanel: function(selected) {
  262. this.get('panelNode').toggleClass(_classNames.selectedPanel, selected);
  263. },
  264. _afterTabSelectedChange: function(event) {
  265. this._uiSetSelectedPanel(event.newVal);
  266. },
  267. _afterParentChange: function(e) {
  268. if (!e.newVal) {
  269. this._remove();
  270. } else {
  271. this._add();
  272. }
  273. },
  274. _initAria: function() {
  275. var anchor = this.get('contentBox'),
  276. id = anchor.get('id'),
  277. panel = this.get('panelNode');
  278. if (!id) {
  279. id = Y.guid();
  280. anchor.set('id', id);
  281. }
  282. // Apply the ARIA roles, states and properties to each tab
  283. anchor.set('role', 'tab');
  284. anchor.get('parentNode').set('role', 'presentation');
  285. // Remove the "href" attribute from the anchor element to
  286. // prevent JAWS and NVDA from reading the value of the "href"
  287. // attribute when the anchor is focused
  288. if (_isGeckoIEWin) {
  289. anchor.removeAttribute('href');
  290. }
  291. // Apply the ARIA roles, states and properties to each panel
  292. panel.setAttrs({
  293. role: 'tabpanel',
  294. 'aria-labelledby': id
  295. });
  296. },
  297. syncUI: function() {
  298. this._uiSetSelectedPanel(this.get('selected'));
  299. },
  300. bindUI: function() {
  301. this.after('selectedChange', this._afterTabSelectedChange);
  302. this.after('parentChange', this._afterParentChange);
  303. },
  304. renderUI: function() {
  305. var parentContentBox = this.get('parent').get('contentBox'),
  306. contentBox = this.get('contentBox');
  307. this._renderLabel(contentBox, parentContentBox);
  308. this._renderPanel(contentBox, parentContentBox);
  309. this._initAria();
  310. },
  311. _renderLabel: function(contentBox, parentContentBox) {
  312. var label = this.get('label');
  313. contentBox.setContent(label);
  314. parentContentBox.one(_queries.tabviewList).appendChild(this.get('boundingBox'));
  315. },
  316. _renderPanel: function(contentBox, parentContentBox) {
  317. var panel = parentContentBox.all(_queries.tabPanel).item(this.get('index'));
  318. if (!panel) {
  319. panel = Y.Node.create(this.PANEL_TEMPLATE);
  320. panel.setContent(this.get('content'));
  321. parentContentBox.one(_queries.tabviewPanel).appendChild(panel);
  322. }
  323. this._set('panelNode', panel);
  324. },
  325. _add: function() {
  326. var parentNode = this.get('parent').get('contentBox'),
  327. list = parentNode.one(_queries.tabviewList),
  328. tabviewPanel = parentNode.one(_queries.tabviewPanel);
  329. if (list) {
  330. list.appendChild(this.get('boundingBox'));
  331. }
  332. if (tabviewPanel) {
  333. tabviewPanel.appendChild(this.get('panelNode'));
  334. }
  335. },
  336. _remove: function() {
  337. this.get('boundingBox').remove();
  338. this.get('panelNode').remove();
  339. },
  340. _onActivate: function(e) {
  341. if (e.target === this) {
  342. // Prevent the browser from navigating to the URL specified by the
  343. // anchor's href attribute.
  344. e.domEvent.preventDefault();
  345. e.target.set('selected', 1);
  346. }
  347. },
  348. initializer: function() {
  349. this.publish(this.get('triggerEvent'), {
  350. defaultFn: this._onActivate
  351. });
  352. }
  353. }, {
  354. ATTRS: {
  355. /**
  356. * @attribute triggerEvent
  357. * @default "click"
  358. * @type String
  359. */
  360. triggerEvent: {
  361. value: 'click'
  362. },
  363. /**
  364. * @attribute label
  365. * @type String
  366. */
  367. label: {
  368. validator: Lang.isString
  369. },
  370. /**
  371. * @attribute label
  372. * @type String
  373. */
  374. content: {
  375. validator: Lang.isString
  376. },
  377. panelNode: {},
  378. tabIndex: {
  379. value: null,
  380. validator: '_validTabIndex'
  381. }
  382. },
  383. HTML_PARSER: {
  384. selection: function(contentBox) {
  385. return this.get('boundingBox').hasClass(_classNames.selectedTab);
  386. }
  387. }
  388. });
  389. }, '@VERSION@' ,{requires:['substitute', 'node-focusmanager', 'tabview-base', 'widget', 'widget-parent', 'widget-child']});