You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

952 lines
32 KiB

9 years ago
10 years ago
6 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
8 years ago
8 years ago
10 years ago
10 years ago
9 years ago
9 years ago
8 years ago
10 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 years ago
8 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
  1. /*!
  2. * # Semantic UI 2.4.1 - Tab
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Released under the MIT license
  7. * http://opensource.org/licenses/MIT
  8. *
  9. */
  10. ;(function ($, window, document, undefined) {
  11. 'use strict';
  12. window = (typeof window != 'undefined' && window.Math == Math)
  13. ? window
  14. : (typeof self != 'undefined' && self.Math == Math)
  15. ? self
  16. : Function('return this')()
  17. ;
  18. $.fn.tab = function(parameters) {
  19. var
  20. // use window context if none specified
  21. $allModules = $.isFunction(this)
  22. ? $(window)
  23. : $(this),
  24. moduleSelector = $allModules.selector || '',
  25. time = new Date().getTime(),
  26. performance = [],
  27. query = arguments[0],
  28. methodInvoked = (typeof query == 'string'),
  29. queryArguments = [].slice.call(arguments, 1),
  30. initializedHistory = false,
  31. returnedValue
  32. ;
  33. $allModules
  34. .each(function() {
  35. var
  36. settings = ( $.isPlainObject(parameters) )
  37. ? $.extend(true, {}, $.fn.tab.settings, parameters)
  38. : $.extend({}, $.fn.tab.settings),
  39. className = settings.className,
  40. metadata = settings.metadata,
  41. selector = settings.selector,
  42. error = settings.error,
  43. eventNamespace = '.' + settings.namespace,
  44. moduleNamespace = 'module-' + settings.namespace,
  45. $module = $(this),
  46. $context,
  47. $tabs,
  48. cache = {},
  49. firstLoad = true,
  50. recursionDepth = 0,
  51. element = this,
  52. instance = $module.data(moduleNamespace),
  53. activeTabPath,
  54. parameterArray,
  55. module,
  56. historyEvent
  57. ;
  58. module = {
  59. initialize: function() {
  60. module.debug('Initializing tab menu item', $module);
  61. module.fix.callbacks();
  62. module.determineTabs();
  63. module.debug('Determining tabs', settings.context, $tabs);
  64. // set up automatic routing
  65. if(settings.auto) {
  66. module.set.auto();
  67. }
  68. module.bind.events();
  69. if(settings.history && !initializedHistory) {
  70. module.initializeHistory();
  71. initializedHistory = true;
  72. }
  73. module.instantiate();
  74. },
  75. instantiate: function () {
  76. module.verbose('Storing instance of module', module);
  77. instance = module;
  78. $module
  79. .data(moduleNamespace, module)
  80. ;
  81. },
  82. destroy: function() {
  83. module.debug('Destroying tabs', $module);
  84. $module
  85. .removeData(moduleNamespace)
  86. .off(eventNamespace)
  87. ;
  88. },
  89. bind: {
  90. events: function() {
  91. // if using $.tab don't add events
  92. if( !$.isWindow( element ) ) {
  93. module.debug('Attaching tab activation events to element', $module);
  94. $module
  95. .on('click' + eventNamespace, module.event.click)
  96. ;
  97. }
  98. }
  99. },
  100. determineTabs: function() {
  101. var
  102. $reference
  103. ;
  104. // determine tab context
  105. if(settings.context === 'parent') {
  106. if($module.closest(selector.ui).length > 0) {
  107. $reference = $module.closest(selector.ui);
  108. module.verbose('Using closest UI element as parent', $reference);
  109. }
  110. else {
  111. $reference = $module;
  112. }
  113. $context = $reference.parent();
  114. module.verbose('Determined parent element for creating context', $context);
  115. }
  116. else if(settings.context) {
  117. $context = $(settings.context);
  118. module.verbose('Using selector for tab context', settings.context, $context);
  119. }
  120. else {
  121. $context = $('body');
  122. }
  123. // find tabs
  124. if(settings.childrenOnly) {
  125. $tabs = $context.children(selector.tabs);
  126. module.debug('Searching tab context children for tabs', $context, $tabs);
  127. }
  128. else {
  129. $tabs = $context.find(selector.tabs);
  130. module.debug('Searching tab context for tabs', $context, $tabs);
  131. }
  132. },
  133. fix: {
  134. callbacks: function() {
  135. if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) {
  136. if(parameters.onTabLoad) {
  137. parameters.onLoad = parameters.onTabLoad;
  138. delete parameters.onTabLoad;
  139. module.error(error.legacyLoad, parameters.onLoad);
  140. }
  141. if(parameters.onTabInit) {
  142. parameters.onFirstLoad = parameters.onTabInit;
  143. delete parameters.onTabInit;
  144. module.error(error.legacyInit, parameters.onFirstLoad);
  145. }
  146. settings = $.extend(true, {}, $.fn.tab.settings, parameters);
  147. }
  148. }
  149. },
  150. initializeHistory: function() {
  151. module.debug('Initializing page state');
  152. if( $.address === undefined ) {
  153. module.error(error.state);
  154. return false;
  155. }
  156. else {
  157. if(settings.historyType == 'state') {
  158. module.debug('Using HTML5 to manage state');
  159. if(settings.path !== false) {
  160. $.address
  161. .history(true)
  162. .state(settings.path)
  163. ;
  164. }
  165. else {
  166. module.error(error.path);
  167. return false;
  168. }
  169. }
  170. $.address
  171. .bind('change', module.event.history.change)
  172. ;
  173. }
  174. },
  175. event: {
  176. click: function(event) {
  177. var
  178. tabPath = $(this).data(metadata.tab)
  179. ;
  180. if(tabPath !== undefined) {
  181. if(settings.history) {
  182. module.verbose('Updating page state', event);
  183. $.address.value(tabPath);
  184. }
  185. else {
  186. module.verbose('Changing tab', event);
  187. module.changeTab(tabPath);
  188. }
  189. event.preventDefault();
  190. }
  191. else {
  192. module.debug('No tab specified');
  193. }
  194. },
  195. history: {
  196. change: function(event) {
  197. var
  198. tabPath = event.pathNames.join('/') || module.get.initialPath(),
  199. pageTitle = settings.templates.determineTitle(tabPath) || false
  200. ;
  201. module.performance.display();
  202. module.debug('History change event', tabPath, event);
  203. historyEvent = event;
  204. if(tabPath !== undefined) {
  205. module.changeTab(tabPath);
  206. }
  207. if(pageTitle) {
  208. $.address.title(pageTitle);
  209. }
  210. }
  211. }
  212. },
  213. refresh: function() {
  214. if(activeTabPath) {
  215. module.debug('Refreshing tab', activeTabPath);
  216. module.changeTab(activeTabPath);
  217. }
  218. },
  219. cache: {
  220. read: function(cacheKey) {
  221. return (cacheKey !== undefined)
  222. ? cache[cacheKey]
  223. : false
  224. ;
  225. },
  226. add: function(cacheKey, content) {
  227. cacheKey = cacheKey || activeTabPath;
  228. module.debug('Adding cached content for', cacheKey);
  229. cache[cacheKey] = content;
  230. },
  231. remove: function(cacheKey) {
  232. cacheKey = cacheKey || activeTabPath;
  233. module.debug('Removing cached content for', cacheKey);
  234. delete cache[cacheKey];
  235. }
  236. },
  237. set: {
  238. auto: function() {
  239. var
  240. url = (typeof settings.path == 'string')
  241. ? settings.path.replace(/\/$/, '') + '/{$tab}'
  242. : '/{$tab}'
  243. ;
  244. module.verbose('Setting up automatic tab retrieval from server', url);
  245. if($.isPlainObject(settings.apiSettings)) {
  246. settings.apiSettings.url = url;
  247. }
  248. else {
  249. settings.apiSettings = {
  250. url: url
  251. };
  252. }
  253. },
  254. loading: function(tabPath) {
  255. var
  256. $tab = module.get.tabElement(tabPath),
  257. isLoading = $tab.hasClass(className.loading)
  258. ;
  259. if(!isLoading) {
  260. module.verbose('Setting loading state for', $tab);
  261. $tab
  262. .addClass(className.loading)
  263. .siblings($tabs)
  264. .removeClass(className.active + ' ' + className.loading)
  265. ;
  266. if($tab.length > 0) {
  267. settings.onRequest.call($tab[0], tabPath);
  268. }
  269. }
  270. },
  271. state: function(state) {
  272. $.address.value(state);
  273. }
  274. },
  275. changeTab: function(tabPath) {
  276. var
  277. pushStateAvailable = (window.history && window.history.pushState),
  278. shouldIgnoreLoad = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad),
  279. remoteContent = (settings.auto || $.isPlainObject(settings.apiSettings) ),
  280. // only add default path if not remote content
  281. pathArray = (remoteContent && !shouldIgnoreLoad)
  282. ? module.utilities.pathToArray(tabPath)
  283. : module.get.defaultPathArray(tabPath)
  284. ;
  285. tabPath = module.utilities.arrayToPath(pathArray);
  286. $.each(pathArray, function(index, tab) {
  287. var
  288. currentPathArray = pathArray.slice(0, index + 1),
  289. currentPath = module.utilities.arrayToPath(currentPathArray),
  290. isTab = module.is.tab(currentPath),
  291. isLastIndex = (index + 1 == pathArray.length),
  292. $tab = module.get.tabElement(currentPath),
  293. $anchor,
  294. nextPathArray,
  295. nextPath,
  296. isLastTab
  297. ;
  298. module.verbose('Looking for tab', tab);
  299. if(isTab) {
  300. module.verbose('Tab was found', tab);
  301. // scope up
  302. activeTabPath = currentPath;
  303. parameterArray = module.utilities.filterArray(pathArray, currentPathArray);
  304. if(isLastIndex) {
  305. isLastTab = true;
  306. }
  307. else {
  308. nextPathArray = pathArray.slice(0, index + 2);
  309. nextPath = module.utilities.arrayToPath(nextPathArray);
  310. isLastTab = ( !module.is.tab(nextPath) );
  311. if(isLastTab) {
  312. module.verbose('Tab parameters found', nextPathArray);
  313. }
  314. }
  315. if(isLastTab && remoteContent) {
  316. if(!shouldIgnoreLoad) {
  317. module.activate.navigation(currentPath);
  318. module.fetch.content(currentPath, tabPath);
  319. }
  320. else {
  321. module.debug('Ignoring remote content on first tab load', currentPath);
  322. firstLoad = false;
  323. module.cache.add(tabPath, $tab.html());
  324. module.activate.all(currentPath);
  325. settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  326. settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  327. }
  328. return false;
  329. }
  330. else {
  331. module.debug('Opened local tab', currentPath);
  332. module.activate.all(currentPath);
  333. if( !module.cache.read(currentPath) ) {
  334. module.cache.add(currentPath, true);
  335. module.debug('First time tab loaded calling tab init');
  336. settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  337. }
  338. settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  339. }
  340. }
  341. else if(tabPath.search('/') == -1 && tabPath !== '') {
  342. // look for in page anchor
  343. $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]');
  344. currentPath = $anchor.closest('[data-tab]').data(metadata.tab);
  345. $tab = module.get.tabElement(currentPath);
  346. // if anchor exists use parent tab
  347. if($anchor && $anchor.length > 0 && currentPath) {
  348. module.debug('Anchor link used, opening parent tab', $tab, $anchor);
  349. if( !$tab.hasClass(className.active) ) {
  350. setTimeout(function() {
  351. module.scrollTo($anchor);
  352. }, 0);
  353. }
  354. module.activate.all(currentPath);
  355. if( !module.cache.read(currentPath) ) {
  356. module.cache.add(currentPath, true);
  357. module.debug('First time tab loaded calling tab init');
  358. settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  359. }
  360. settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent);
  361. return false;
  362. }
  363. }
  364. else {
  365. module.error(error.missingTab, $module, $context, currentPath);
  366. return false;
  367. }
  368. });
  369. },
  370. scrollTo: function($element) {
  371. var
  372. scrollOffset = ($element && $element.length > 0)
  373. ? $element.offset().top
  374. : false
  375. ;
  376. if(scrollOffset !== false) {
  377. module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element);
  378. $(document).scrollTop(scrollOffset);
  379. }
  380. },
  381. update: {
  382. content: function(tabPath, html, evaluateScripts) {
  383. var
  384. $tab = module.get.tabElement(tabPath),
  385. tab = $tab[0]
  386. ;
  387. evaluateScripts = (evaluateScripts !== undefined)
  388. ? evaluateScripts
  389. : settings.evaluateScripts
  390. ;
  391. if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') {
  392. $tab
  393. .empty()
  394. .append($(html).clone(true))
  395. ;
  396. }
  397. else {
  398. if(evaluateScripts) {
  399. module.debug('Updating HTML and evaluating inline scripts', tabPath, html);
  400. $tab.html(html);
  401. }
  402. else {
  403. module.debug('Updating HTML', tabPath, html);
  404. tab.innerHTML = html;
  405. }
  406. }
  407. }
  408. },
  409. fetch: {
  410. content: function(tabPath, fullTabPath) {
  411. var
  412. $tab = module.get.tabElement(tabPath),
  413. apiSettings = {
  414. dataType : 'html',
  415. encodeParameters : false,
  416. on : 'now',
  417. cache : settings.alwaysRefresh,
  418. headers : {
  419. 'X-Remote': true
  420. },
  421. onSuccess : function(response) {
  422. if(settings.cacheType == 'response') {
  423. module.cache.add(fullTabPath, response);
  424. }
  425. module.update.content(tabPath, response);
  426. if(tabPath == activeTabPath) {
  427. module.debug('Content loaded', tabPath);
  428. module.activate.tab(tabPath);
  429. }
  430. else {
  431. module.debug('Content loaded in background', tabPath);
  432. }
  433. settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent);
  434. settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
  435. if(settings.loadOnce) {
  436. module.cache.add(fullTabPath, true);
  437. }
  438. else if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) {
  439. setTimeout(function() {
  440. var
  441. $clone = $tab.children().clone(true)
  442. ;
  443. $clone = $clone.not('script');
  444. module.cache.add(fullTabPath, $clone);
  445. }, 0);
  446. }
  447. else {
  448. module.cache.add(fullTabPath, $tab.html());
  449. }
  450. },
  451. urlData: {
  452. tab: fullTabPath
  453. }
  454. },
  455. request = $tab.api('get request') || false,
  456. existingRequest = ( request && request.state() === 'pending' ),
  457. requestSettings,
  458. cachedContent
  459. ;
  460. fullTabPath = fullTabPath || tabPath;
  461. cachedContent = module.cache.read(fullTabPath);
  462. if(settings.cache && cachedContent) {
  463. module.activate.tab(tabPath);
  464. module.debug('Adding cached content', fullTabPath);
  465. if(!settings.loadOnce) {
  466. if(settings.evaluateScripts == 'once') {
  467. module.update.content(tabPath, cachedContent, false);
  468. }
  469. else {
  470. module.update.content(tabPath, cachedContent);
  471. }
  472. }
  473. settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent);
  474. }
  475. else if(existingRequest) {
  476. module.set.loading(tabPath);
  477. module.debug('Content is already loading', fullTabPath);
  478. }
  479. else if($.api !== undefined) {
  480. requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings);
  481. module.debug('Retrieving remote content', fullTabPath, requestSettings);
  482. module.set.loading(tabPath);
  483. $tab.api(requestSettings);
  484. }
  485. else {
  486. module.error(error.api);
  487. }
  488. }
  489. },
  490. activate: {
  491. all: function(tabPath) {
  492. module.activate.tab(tabPath);
  493. module.activate.navigation(tabPath);
  494. },
  495. tab: function(tabPath) {
  496. var
  497. $tab = module.get.tabElement(tabPath),
  498. $deactiveTabs = (settings.deactivate == 'siblings')
  499. ? $tab.siblings($tabs)
  500. : $tabs.not($tab),
  501. isActive = $tab.hasClass(className.active)
  502. ;
  503. module.verbose('Showing tab content for', $tab);
  504. if(!isActive) {
  505. $tab
  506. .addClass(className.active)
  507. ;
  508. $deactiveTabs
  509. .removeClass(className.active + ' ' + className.loading)
  510. ;
  511. if($tab.length > 0) {
  512. settings.onVisible.call($tab[0], tabPath);
  513. }
  514. }
  515. },
  516. navigation: function(tabPath) {
  517. var
  518. $navigation = module.get.navElement(tabPath),
  519. $deactiveNavigation = (settings.deactivate == 'siblings')
  520. ? $navigation.siblings($allModules)
  521. : $allModules.not($navigation),
  522. isActive = $navigation.hasClass(className.active)
  523. ;
  524. module.verbose('Activating tab navigation for', $navigation, tabPath);
  525. if(!isActive) {
  526. $navigation
  527. .addClass(className.active)
  528. ;
  529. $deactiveNavigation
  530. .removeClass(className.active + ' ' + className.loading)
  531. ;
  532. }
  533. }
  534. },
  535. deactivate: {
  536. all: function() {
  537. module.deactivate.navigation();
  538. module.deactivate.tabs();
  539. },
  540. navigation: function() {
  541. $allModules
  542. .removeClass(className.active)
  543. ;
  544. },
  545. tabs: function() {
  546. $tabs
  547. .removeClass(className.active + ' ' + className.loading)
  548. ;
  549. }
  550. },
  551. is: {
  552. tab: function(tabName) {
  553. return (tabName !== undefined)
  554. ? ( module.get.tabElement(tabName).length > 0 )
  555. : false
  556. ;
  557. }
  558. },
  559. get: {
  560. initialPath: function() {
  561. return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab);
  562. },
  563. path: function() {
  564. return $.address.value();
  565. },
  566. // adds default tabs to tab path
  567. defaultPathArray: function(tabPath) {
  568. return module.utilities.pathToArray( module.get.defaultPath(tabPath) );
  569. },
  570. defaultPath: function(tabPath) {
  571. var
  572. $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + tabPath + '/"]').eq(0),
  573. defaultTab = $defaultNav.data(metadata.tab) || false
  574. ;
  575. if( defaultTab ) {
  576. module.debug('Found default tab', defaultTab);
  577. if(recursionDepth < settings.maxDepth) {
  578. recursionDepth++;
  579. return module.get.defaultPath(defaultTab);
  580. }
  581. module.error(error.recursion);
  582. }
  583. else {
  584. module.debug('No default tabs found for', tabPath, $tabs);
  585. }
  586. recursionDepth = 0;
  587. return tabPath;
  588. },
  589. navElement: function(tabPath) {
  590. tabPath = tabPath || activeTabPath;
  591. return $allModules.filter('[data-' + metadata.tab + '="' + tabPath + '"]');
  592. },
  593. tabElement: function(tabPath) {
  594. var
  595. $fullPathTab,
  596. $simplePathTab,
  597. tabPathArray,
  598. lastTab
  599. ;
  600. tabPath = tabPath || activeTabPath;
  601. tabPathArray = module.utilities.pathToArray(tabPath);
  602. lastTab = module.utilities.last(tabPathArray);
  603. $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + tabPath + '"]');
  604. $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + lastTab + '"]');
  605. return ($fullPathTab.length > 0)
  606. ? $fullPathTab
  607. : $simplePathTab
  608. ;
  609. },
  610. tab: function() {
  611. return activeTabPath;
  612. }
  613. },
  614. utilities: {
  615. filterArray: function(keepArray, removeArray) {
  616. return $.grep(keepArray, function(keepValue) {
  617. return ( $.inArray(keepValue, removeArray) == -1);
  618. });
  619. },
  620. last: function(array) {
  621. return $.isArray(array)
  622. ? array[ array.length - 1]
  623. : false
  624. ;
  625. },
  626. pathToArray: function(pathName) {
  627. if(pathName === undefined) {
  628. pathName = activeTabPath;
  629. }
  630. return typeof pathName == 'string'
  631. ? pathName.split('/')
  632. : [pathName]
  633. ;
  634. },
  635. arrayToPath: function(pathArray) {
  636. return $.isArray(pathArray)
  637. ? pathArray.join('/')
  638. : false
  639. ;
  640. }
  641. },
  642. setting: function(name, value) {
  643. module.debug('Changing setting', name, value);
  644. if( $.isPlainObject(name) ) {
  645. $.extend(true, settings, name);
  646. }
  647. else if(value !== undefined) {
  648. if($.isPlainObject(settings[name])) {
  649. $.extend(true, settings[name], value);
  650. }
  651. else {
  652. settings[name] = value;
  653. }
  654. }
  655. else {
  656. return settings[name];
  657. }
  658. },
  659. internal: function(name, value) {
  660. if( $.isPlainObject(name) ) {
  661. $.extend(true, module, name);
  662. }
  663. else if(value !== undefined) {
  664. module[name] = value;
  665. }
  666. else {
  667. return module[name];
  668. }
  669. },
  670. debug: function() {
  671. if(!settings.silent && settings.debug) {
  672. if(settings.performance) {
  673. module.performance.log(arguments);
  674. }
  675. else {
  676. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  677. module.debug.apply(console, arguments);
  678. }
  679. }
  680. },
  681. verbose: function() {
  682. if(!settings.silent && settings.verbose && settings.debug) {
  683. if(settings.performance) {
  684. module.performance.log(arguments);
  685. }
  686. else {
  687. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  688. module.verbose.apply(console, arguments);
  689. }
  690. }
  691. },
  692. error: function() {
  693. if(!settings.silent) {
  694. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  695. module.error.apply(console, arguments);
  696. }
  697. },
  698. performance: {
  699. log: function(message) {
  700. var
  701. currentTime,
  702. executionTime,
  703. previousTime
  704. ;
  705. if(settings.performance) {
  706. currentTime = new Date().getTime();
  707. previousTime = time || currentTime;
  708. executionTime = currentTime - previousTime;
  709. time = currentTime;
  710. performance.push({
  711. 'Name' : message[0],
  712. 'Arguments' : [].slice.call(message, 1) || '',
  713. 'Element' : element,
  714. 'Execution Time' : executionTime
  715. });
  716. }
  717. clearTimeout(module.performance.timer);
  718. module.performance.timer = setTimeout(module.performance.display, 500);
  719. },
  720. display: function() {
  721. var
  722. title = settings.name + ':',
  723. totalTime = 0
  724. ;
  725. time = false;
  726. clearTimeout(module.performance.timer);
  727. $.each(performance, function(index, data) {
  728. totalTime += data['Execution Time'];
  729. });
  730. title += ' ' + totalTime + 'ms';
  731. if(moduleSelector) {
  732. title += ' \'' + moduleSelector + '\'';
  733. }
  734. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  735. console.groupCollapsed(title);
  736. if(console.table) {
  737. console.table(performance);
  738. }
  739. else {
  740. $.each(performance, function(index, data) {
  741. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  742. });
  743. }
  744. console.groupEnd();
  745. }
  746. performance = [];
  747. }
  748. },
  749. invoke: function(query, passedArguments, context) {
  750. var
  751. object = instance,
  752. maxDepth,
  753. found,
  754. response
  755. ;
  756. passedArguments = passedArguments || queryArguments;
  757. context = element || context;
  758. if(typeof query == 'string' && object !== undefined) {
  759. query = query.split(/[\. ]/);
  760. maxDepth = query.length - 1;
  761. $.each(query, function(depth, value) {
  762. var camelCaseValue = (depth != maxDepth)
  763. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  764. : query
  765. ;
  766. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  767. object = object[camelCaseValue];
  768. }
  769. else if( object[camelCaseValue] !== undefined ) {
  770. found = object[camelCaseValue];
  771. return false;
  772. }
  773. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  774. object = object[value];
  775. }
  776. else if( object[value] !== undefined ) {
  777. found = object[value];
  778. return false;
  779. }
  780. else {
  781. module.error(error.method, query);
  782. return false;
  783. }
  784. });
  785. }
  786. if ( $.isFunction( found ) ) {
  787. response = found.apply(context, passedArguments);
  788. }
  789. else if(found !== undefined) {
  790. response = found;
  791. }
  792. if($.isArray(returnedValue)) {
  793. returnedValue.push(response);
  794. }
  795. else if(returnedValue !== undefined) {
  796. returnedValue = [returnedValue, response];
  797. }
  798. else if(response !== undefined) {
  799. returnedValue = response;
  800. }
  801. return found;
  802. }
  803. };
  804. if(methodInvoked) {
  805. if(instance === undefined) {
  806. module.initialize();
  807. }
  808. module.invoke(query);
  809. }
  810. else {
  811. if(instance !== undefined) {
  812. instance.invoke('destroy');
  813. }
  814. module.initialize();
  815. }
  816. })
  817. ;
  818. return (returnedValue !== undefined)
  819. ? returnedValue
  820. : this
  821. ;
  822. };
  823. // shortcut for tabbed content with no defined navigation
  824. $.tab = function() {
  825. $(window).tab.apply(this, arguments);
  826. };
  827. $.fn.tab.settings = {
  828. name : 'Tab',
  829. namespace : 'tab',
  830. silent : false,
  831. debug : false,
  832. verbose : false,
  833. performance : true,
  834. auto : false, // uses pjax style endpoints fetching content from same url with remote-content headers
  835. history : false, // use browser history
  836. historyType : 'hash', // #/ or html5 state
  837. path : false, // base path of url
  838. context : false, // specify a context that tabs must appear inside
  839. childrenOnly : false, // use only tabs that are children of context
  840. maxDepth : 25, // max depth a tab can be nested
  841. deactivate : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together
  842. alwaysRefresh : false, // load tab content new every tab click
  843. cache : true, // cache the content requests to pull locally
  844. loadOnce : false, // Whether tab data should only be loaded once when using remote content
  845. cacheType : 'response', // Whether to cache exact response, or to html cache contents after scripts execute
  846. ignoreFirstLoad : false, // don't load remote content on first load
  847. apiSettings : false, // settings for api call
  848. evaluateScripts : 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content
  849. onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded
  850. onLoad : function(tabPath, parameterArray, historyEvent) {}, // called on every load
  851. onVisible : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible
  852. onRequest : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content
  853. templates : {
  854. determineTitle: function(tabArray) {} // returns page title for path
  855. },
  856. error: {
  857. api : 'You attempted to load content without API module',
  858. method : 'The method you called is not defined',
  859. missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.',
  860. noContent : 'The tab you specified is missing a content url.',
  861. path : 'History enabled, but no path was specified',
  862. recursion : 'Max recursive depth reached',
  863. legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.',
  864. legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code',
  865. state : 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>'
  866. },
  867. metadata : {
  868. tab : 'tab',
  869. loaded : 'loaded',
  870. promise: 'promise'
  871. },
  872. className : {
  873. loading : 'loading',
  874. active : 'active'
  875. },
  876. selector : {
  877. tabs : '.ui.tab',
  878. ui : '.ui'
  879. }
  880. };
  881. })( jQuery, window, document );