Repository: incubator-senssoft Updated Branches: refs/heads/asf-site [created] 855d3ab18
http://git-wip-us.apache.org/repos/asf/incubator-senssoft/blob/855d3ab1/semantic/dist/components/tab.js ---------------------------------------------------------------------- diff --git a/semantic/dist/components/tab.js b/semantic/dist/components/tab.js new file mode 100644 index 0000000..9375ebd --- /dev/null +++ b/semantic/dist/components/tab.js @@ -0,0 +1,946 @@ +/*! + * # Semantic UI 2.2.6 - Tab + * http://github.com/semantic-org/semantic-ui/ + * + * + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + */ + +;(function ($, window, document, undefined) { + +"use strict"; + +window = (typeof window != 'undefined' && window.Math == Math) + ? window + : (typeof self != 'undefined' && self.Math == Math) + ? self + : Function('return this')() +; + +$.fn.tab = function(parameters) { + + var + // use window context if none specified + $allModules = $.isFunction(this) + ? $(window) + : $(this), + + moduleSelector = $allModules.selector || '', + time = new Date().getTime(), + performance = [], + + query = arguments[0], + methodInvoked = (typeof query == 'string'), + queryArguments = [].slice.call(arguments, 1), + + initializedHistory = false, + returnedValue + ; + + $allModules + .each(function() { + var + + settings = ( $.isPlainObject(parameters) ) + ? $.extend(true, {}, $.fn.tab.settings, parameters) + : $.extend({}, $.fn.tab.settings), + + className = settings.className, + metadata = settings.metadata, + selector = settings.selector, + error = settings.error, + + eventNamespace = '.' + settings.namespace, + moduleNamespace = 'module-' + settings.namespace, + + $module = $(this), + $context, + $tabs, + + cache = {}, + firstLoad = true, + recursionDepth = 0, + element = this, + instance = $module.data(moduleNamespace), + + activeTabPath, + parameterArray, + module, + + historyEvent + + ; + + module = { + + initialize: function() { + module.debug('Initializing tab menu item', $module); + module.fix.callbacks(); + module.determineTabs(); + + module.debug('Determining tabs', settings.context, $tabs); + // set up automatic routing + if(settings.auto) { + module.set.auto(); + } + module.bind.events(); + + if(settings.history && !initializedHistory) { + module.initializeHistory(); + initializedHistory = true; + } + + module.instantiate(); + }, + + instantiate: function () { + module.verbose('Storing instance of module', module); + instance = module; + $module + .data(moduleNamespace, module) + ; + }, + + destroy: function() { + module.debug('Destroying tabs', $module); + $module + .removeData(moduleNamespace) + .off(eventNamespace) + ; + }, + + bind: { + events: function() { + // if using $.tab don't add events + if( !$.isWindow( element ) ) { + module.debug('Attaching tab activation events to element', $module); + $module + .on('click' + eventNamespace, module.event.click) + ; + } + } + }, + + determineTabs: function() { + var + $reference + ; + + // determine tab context + if(settings.context === 'parent') { + if($module.closest(selector.ui).length > 0) { + $reference = $module.closest(selector.ui); + module.verbose('Using closest UI element as parent', $reference); + } + else { + $reference = $module; + } + $context = $reference.parent(); + module.verbose('Determined parent element for creating context', $context); + } + else if(settings.context) { + $context = $(settings.context); + module.verbose('Using selector for tab context', settings.context, $context); + } + else { + $context = $('body'); + } + // find tabs + if(settings.childrenOnly) { + $tabs = $context.children(selector.tabs); + module.debug('Searching tab context children for tabs', $context, $tabs); + } + else { + $tabs = $context.find(selector.tabs); + module.debug('Searching tab context for tabs', $context, $tabs); + } + }, + + fix: { + callbacks: function() { + if( $.isPlainObject(parameters) && (parameters.onTabLoad || parameters.onTabInit) ) { + if(parameters.onTabLoad) { + parameters.onLoad = parameters.onTabLoad; + delete parameters.onTabLoad; + module.error(error.legacyLoad, parameters.onLoad); + } + if(parameters.onTabInit) { + parameters.onFirstLoad = parameters.onTabInit; + delete parameters.onTabInit; + module.error(error.legacyInit, parameters.onFirstLoad); + } + settings = $.extend(true, {}, $.fn.tab.settings, parameters); + } + } + }, + + initializeHistory: function() { + module.debug('Initializing page state'); + if( $.address === undefined ) { + module.error(error.state); + return false; + } + else { + if(settings.historyType == 'state') { + module.debug('Using HTML5 to manage state'); + if(settings.path !== false) { + $.address + .history(true) + .state(settings.path) + ; + } + else { + module.error(error.path); + return false; + } + } + $.address + .bind('change', module.event.history.change) + ; + } + }, + + event: { + click: function(event) { + var + tabPath = $(this).data(metadata.tab) + ; + if(tabPath !== undefined) { + if(settings.history) { + module.verbose('Updating page state', event); + $.address.value(tabPath); + } + else { + module.verbose('Changing tab', event); + module.changeTab(tabPath); + } + event.preventDefault(); + } + else { + module.debug('No tab specified'); + } + }, + history: { + change: function(event) { + var + tabPath = event.pathNames.join('/') || module.get.initialPath(), + pageTitle = settings.templates.determineTitle(tabPath) || false + ; + module.performance.display(); + module.debug('History change event', tabPath, event); + historyEvent = event; + if(tabPath !== undefined) { + module.changeTab(tabPath); + } + if(pageTitle) { + $.address.title(pageTitle); + } + } + } + }, + + refresh: function() { + if(activeTabPath) { + module.debug('Refreshing tab', activeTabPath); + module.changeTab(activeTabPath); + } + }, + + cache: { + + read: function(cacheKey) { + return (cacheKey !== undefined) + ? cache[cacheKey] + : false + ; + }, + add: function(cacheKey, content) { + cacheKey = cacheKey || activeTabPath; + module.debug('Adding cached content for', cacheKey); + cache[cacheKey] = content; + }, + remove: function(cacheKey) { + cacheKey = cacheKey || activeTabPath; + module.debug('Removing cached content for', cacheKey); + delete cache[cacheKey]; + } + }, + + set: { + auto: function() { + var + url = (typeof settings.path == 'string') + ? settings.path.replace(/\/$/, '') + '/{$tab}' + : '/{$tab}' + ; + module.verbose('Setting up automatic tab retrieval from server', url); + if($.isPlainObject(settings.apiSettings)) { + settings.apiSettings.url = url; + } + else { + settings.apiSettings = { + url: url + }; + } + }, + loading: function(tabPath) { + var + $tab = module.get.tabElement(tabPath), + isLoading = $tab.hasClass(className.loading) + ; + if(!isLoading) { + module.verbose('Setting loading state for', $tab); + $tab + .addClass(className.loading) + .siblings($tabs) + .removeClass(className.active + ' ' + className.loading) + ; + if($tab.length > 0) { + settings.onRequest.call($tab[0], tabPath); + } + } + }, + state: function(state) { + $.address.value(state); + } + }, + + changeTab: function(tabPath) { + var + pushStateAvailable = (window.history && window.history.pushState), + shouldIgnoreLoad = (pushStateAvailable && settings.ignoreFirstLoad && firstLoad), + remoteContent = (settings.auto || $.isPlainObject(settings.apiSettings) ), + // only add default path if not remote content + pathArray = (remoteContent && !shouldIgnoreLoad) + ? module.utilities.pathToArray(tabPath) + : module.get.defaultPathArray(tabPath) + ; + tabPath = module.utilities.arrayToPath(pathArray); + $.each(pathArray, function(index, tab) { + var + currentPathArray = pathArray.slice(0, index + 1), + currentPath = module.utilities.arrayToPath(currentPathArray), + + isTab = module.is.tab(currentPath), + isLastIndex = (index + 1 == pathArray.length), + + $tab = module.get.tabElement(currentPath), + $anchor, + nextPathArray, + nextPath, + isLastTab + ; + module.verbose('Looking for tab', tab); + if(isTab) { + module.verbose('Tab was found', tab); + // scope up + activeTabPath = currentPath; + parameterArray = module.utilities.filterArray(pathArray, currentPathArray); + + if(isLastIndex) { + isLastTab = true; + } + else { + nextPathArray = pathArray.slice(0, index + 2); + nextPath = module.utilities.arrayToPath(nextPathArray); + isLastTab = ( !module.is.tab(nextPath) ); + if(isLastTab) { + module.verbose('Tab parameters found', nextPathArray); + } + } + if(isLastTab && remoteContent) { + if(!shouldIgnoreLoad) { + module.activate.navigation(currentPath); + module.fetch.content(currentPath, tabPath); + } + else { + module.debug('Ignoring remote content on first tab load', currentPath); + firstLoad = false; + module.cache.add(tabPath, $tab.html()); + module.activate.all(currentPath); + settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent); + settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent); + } + return false; + } + else { + module.debug('Opened local tab', currentPath); + module.activate.all(currentPath); + if( !module.cache.read(currentPath) ) { + module.cache.add(currentPath, true); + module.debug('First time tab loaded calling tab init'); + settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent); + } + settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent); + } + + } + else if(tabPath.search('/') == -1 && tabPath !== '') { + // look for in page anchor + $anchor = $('#' + tabPath + ', a[name="' + tabPath + '"]'); + currentPath = $anchor.closest('[data-tab]').data(metadata.tab); + $tab = module.get.tabElement(currentPath); + // if anchor exists use parent tab + if($anchor && $anchor.length > 0 && currentPath) { + module.debug('Anchor link used, opening parent tab', $tab, $anchor); + if( !$tab.hasClass(className.active) ) { + setTimeout(function() { + module.scrollTo($anchor); + }, 0); + } + module.activate.all(currentPath); + if( !module.cache.read(currentPath) ) { + module.cache.add(currentPath, true); + module.debug('First time tab loaded calling tab init'); + settings.onFirstLoad.call($tab[0], currentPath, parameterArray, historyEvent); + } + settings.onLoad.call($tab[0], currentPath, parameterArray, historyEvent); + return false; + } + } + else { + module.error(error.missingTab, $module, $context, currentPath); + return false; + } + }); + }, + + scrollTo: function($element) { + var + scrollOffset = ($element && $element.length > 0) + ? $element.offset().top + : false + ; + if(scrollOffset !== false) { + module.debug('Forcing scroll to an in-page link in a hidden tab', scrollOffset, $element); + $(document).scrollTop(scrollOffset); + } + }, + + update: { + content: function(tabPath, html, evaluateScripts) { + var + $tab = module.get.tabElement(tabPath), + tab = $tab[0] + ; + evaluateScripts = (evaluateScripts !== undefined) + ? evaluateScripts + : settings.evaluateScripts + ; + if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && typeof html !== 'string') { + $tab + .empty() + .append($(html).clone(true)) + ; + } + else { + if(evaluateScripts) { + module.debug('Updating HTML and evaluating inline scripts', tabPath, html); + $tab.html(html); + } + else { + module.debug('Updating HTML', tabPath, html); + tab.innerHTML = html; + } + } + } + }, + + fetch: { + + content: function(tabPath, fullTabPath) { + var + $tab = module.get.tabElement(tabPath), + apiSettings = { + dataType : 'html', + encodeParameters : false, + on : 'now', + cache : settings.alwaysRefresh, + headers : { + 'X-Remote': true + }, + onSuccess : function(response) { + if(settings.cacheType == 'response') { + module.cache.add(fullTabPath, response); + } + module.update.content(tabPath, response); + if(tabPath == activeTabPath) { + module.debug('Content loaded', tabPath); + module.activate.tab(tabPath); + } + else { + module.debug('Content loaded in background', tabPath); + } + settings.onFirstLoad.call($tab[0], tabPath, parameterArray, historyEvent); + settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent); + + if(typeof settings.cacheType == 'string' && settings.cacheType.toLowerCase() == 'dom' && $tab.children().length > 0) { + setTimeout(function() { + var + $clone = $tab.children().clone(true) + ; + $clone = $clone.not('script'); + module.cache.add(fullTabPath, $clone); + }, 0); + } + else { + module.cache.add(fullTabPath, $tab.html()); + } + }, + urlData: { + tab: fullTabPath + } + }, + request = $tab.api('get request') || false, + existingRequest = ( request && request.state() === 'pending' ), + requestSettings, + cachedContent + ; + + fullTabPath = fullTabPath || tabPath; + cachedContent = module.cache.read(fullTabPath); + + + if(settings.cache && cachedContent) { + module.activate.tab(tabPath); + module.debug('Adding cached content', fullTabPath); + if(settings.evaluateScripts == 'once') { + module.update.content(tabPath, cachedContent, false); + } + else { + module.update.content(tabPath, cachedContent); + } + settings.onLoad.call($tab[0], tabPath, parameterArray, historyEvent); + } + else if(existingRequest) { + module.set.loading(tabPath); + module.debug('Content is already loading', fullTabPath); + } + else if($.api !== undefined) { + requestSettings = $.extend(true, {}, settings.apiSettings, apiSettings); + module.debug('Retrieving remote content', fullTabPath, requestSettings); + module.set.loading(tabPath); + $tab.api(requestSettings); + } + else { + module.error(error.api); + } + } + }, + + activate: { + all: function(tabPath) { + module.activate.tab(tabPath); + module.activate.navigation(tabPath); + }, + tab: function(tabPath) { + var + $tab = module.get.tabElement(tabPath), + $deactiveTabs = (settings.deactivate == 'siblings') + ? $tab.siblings($tabs) + : $tabs.not($tab), + isActive = $tab.hasClass(className.active) + ; + module.verbose('Showing tab content for', $tab); + if(!isActive) { + $tab + .addClass(className.active) + ; + $deactiveTabs + .removeClass(className.active + ' ' + className.loading) + ; + if($tab.length > 0) { + settings.onVisible.call($tab[0], tabPath); + } + } + }, + navigation: function(tabPath) { + var + $navigation = module.get.navElement(tabPath), + $deactiveNavigation = (settings.deactivate == 'siblings') + ? $navigation.siblings($allModules) + : $allModules.not($navigation), + isActive = $navigation.hasClass(className.active) + ; + module.verbose('Activating tab navigation for', $navigation, tabPath); + if(!isActive) { + $navigation + .addClass(className.active) + ; + $deactiveNavigation + .removeClass(className.active + ' ' + className.loading) + ; + } + } + }, + + deactivate: { + all: function() { + module.deactivate.navigation(); + module.deactivate.tabs(); + }, + navigation: function() { + $allModules + .removeClass(className.active) + ; + }, + tabs: function() { + $tabs + .removeClass(className.active + ' ' + className.loading) + ; + } + }, + + is: { + tab: function(tabName) { + return (tabName !== undefined) + ? ( module.get.tabElement(tabName).length > 0 ) + : false + ; + } + }, + + get: { + initialPath: function() { + return $allModules.eq(0).data(metadata.tab) || $tabs.eq(0).data(metadata.tab); + }, + path: function() { + return $.address.value(); + }, + // adds default tabs to tab path + defaultPathArray: function(tabPath) { + return module.utilities.pathToArray( module.get.defaultPath(tabPath) ); + }, + defaultPath: function(tabPath) { + var + $defaultNav = $allModules.filter('[data-' + metadata.tab + '^="' + tabPath + '/"]').eq(0), + defaultTab = $defaultNav.data(metadata.tab) || false + ; + if( defaultTab ) { + module.debug('Found default tab', defaultTab); + if(recursionDepth < settings.maxDepth) { + recursionDepth++; + return module.get.defaultPath(defaultTab); + } + module.error(error.recursion); + } + else { + module.debug('No default tabs found for', tabPath, $tabs); + } + recursionDepth = 0; + return tabPath; + }, + navElement: function(tabPath) { + tabPath = tabPath || activeTabPath; + return $allModules.filter('[data-' + metadata.tab + '="' + tabPath + '"]'); + }, + tabElement: function(tabPath) { + var + $fullPathTab, + $simplePathTab, + tabPathArray, + lastTab + ; + tabPath = tabPath || activeTabPath; + tabPathArray = module.utilities.pathToArray(tabPath); + lastTab = module.utilities.last(tabPathArray); + $fullPathTab = $tabs.filter('[data-' + metadata.tab + '="' + tabPath + '"]'); + $simplePathTab = $tabs.filter('[data-' + metadata.tab + '="' + lastTab + '"]'); + return ($fullPathTab.length > 0) + ? $fullPathTab + : $simplePathTab + ; + }, + tab: function() { + return activeTabPath; + } + }, + + utilities: { + filterArray: function(keepArray, removeArray) { + return $.grep(keepArray, function(keepValue) { + return ( $.inArray(keepValue, removeArray) == -1); + }); + }, + last: function(array) { + return $.isArray(array) + ? array[ array.length - 1] + : false + ; + }, + pathToArray: function(pathName) { + if(pathName === undefined) { + pathName = activeTabPath; + } + return typeof pathName == 'string' + ? pathName.split('/') + : [pathName] + ; + }, + arrayToPath: function(pathArray) { + return $.isArray(pathArray) + ? pathArray.join('/') + : false + ; + } + }, + + setting: function(name, value) { + module.debug('Changing setting', name, value); + if( $.isPlainObject(name) ) { + $.extend(true, settings, name); + } + else if(value !== undefined) { + if($.isPlainObject(settings[name])) { + $.extend(true, settings[name], value); + } + else { + settings[name] = value; + } + } + else { + return settings[name]; + } + }, + internal: function(name, value) { + if( $.isPlainObject(name) ) { + $.extend(true, module, name); + } + else if(value !== undefined) { + module[name] = value; + } + else { + return module[name]; + } + }, + debug: function() { + if(!settings.silent && settings.debug) { + if(settings.performance) { + module.performance.log(arguments); + } + else { + module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); + module.debug.apply(console, arguments); + } + } + }, + verbose: function() { + if(!settings.silent && settings.verbose && settings.debug) { + if(settings.performance) { + module.performance.log(arguments); + } + else { + module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); + module.verbose.apply(console, arguments); + } + } + }, + error: function() { + if(!settings.silent) { + module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); + module.error.apply(console, arguments); + } + }, + performance: { + log: function(message) { + var + currentTime, + executionTime, + previousTime + ; + if(settings.performance) { + currentTime = new Date().getTime(); + previousTime = time || currentTime; + executionTime = currentTime - previousTime; + time = currentTime; + performance.push({ + 'Name' : message[0], + 'Arguments' : [].slice.call(message, 1) || '', + 'Element' : element, + 'Execution Time' : executionTime + }); + } + clearTimeout(module.performance.timer); + module.performance.timer = setTimeout(module.performance.display, 500); + }, + display: function() { + var + title = settings.name + ':', + totalTime = 0 + ; + time = false; + clearTimeout(module.performance.timer); + $.each(performance, function(index, data) { + totalTime += data['Execution Time']; + }); + title += ' ' + totalTime + 'ms'; + if(moduleSelector) { + title += ' \'' + moduleSelector + '\''; + } + if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { + console.groupCollapsed(title); + if(console.table) { + console.table(performance); + } + else { + $.each(performance, function(index, data) { + console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); + }); + } + console.groupEnd(); + } + performance = []; + } + }, + invoke: function(query, passedArguments, context) { + var + object = instance, + maxDepth, + found, + response + ; + passedArguments = passedArguments || queryArguments; + context = element || context; + if(typeof query == 'string' && object !== undefined) { + query = query.split(/[\. ]/); + maxDepth = query.length - 1; + $.each(query, function(depth, value) { + var camelCaseValue = (depth != maxDepth) + ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) + : query + ; + if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { + object = object[camelCaseValue]; + } + else if( object[camelCaseValue] !== undefined ) { + found = object[camelCaseValue]; + return false; + } + else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { + object = object[value]; + } + else if( object[value] !== undefined ) { + found = object[value]; + return false; + } + else { + module.error(error.method, query); + return false; + } + }); + } + if ( $.isFunction( found ) ) { + response = found.apply(context, passedArguments); + } + else if(found !== undefined) { + response = found; + } + if($.isArray(returnedValue)) { + returnedValue.push(response); + } + else if(returnedValue !== undefined) { + returnedValue = [returnedValue, response]; + } + else if(response !== undefined) { + returnedValue = response; + } + return found; + } + }; + if(methodInvoked) { + if(instance === undefined) { + module.initialize(); + } + module.invoke(query); + } + else { + if(instance !== undefined) { + instance.invoke('destroy'); + } + module.initialize(); + } + }) + ; + return (returnedValue !== undefined) + ? returnedValue + : this + ; + +}; + +// shortcut for tabbed content with no defined navigation +$.tab = function() { + $(window).tab.apply(this, arguments); +}; + +$.fn.tab.settings = { + + name : 'Tab', + namespace : 'tab', + + silent : false, + debug : false, + verbose : false, + performance : true, + + auto : false, // uses pjax style endpoints fetching content from same url with remote-content headers + history : false, // use browser history + historyType : 'hash', // #/ or html5 state + path : false, // base path of url + + context : false, // specify a context that tabs must appear inside + childrenOnly : false, // use only tabs that are children of context + maxDepth : 25, // max depth a tab can be nested + + deactivate : 'siblings', // whether tabs should deactivate sibling menu elements or all elements initialized together + + alwaysRefresh : false, // load tab content new every tab click + cache : true, // cache the content requests to pull locally + cacheType : 'response', // Whether to cache exact response, or to html cache contents after scripts execute + ignoreFirstLoad : false, // don't load remote content on first load + + apiSettings : false, // settings for api call + evaluateScripts : 'once', // whether inline scripts should be parsed (true/false/once). Once will not re-evaluate on cached content + + onFirstLoad : function(tabPath, parameterArray, historyEvent) {}, // called first time loaded + onLoad : function(tabPath, parameterArray, historyEvent) {}, // called on every load + onVisible : function(tabPath, parameterArray, historyEvent) {}, // called every time tab visible + onRequest : function(tabPath, parameterArray, historyEvent) {}, // called ever time a tab beings loading remote content + + templates : { + determineTitle: function(tabArray) {} // returns page title for path + }, + + error: { + api : 'You attempted to load content without API module', + method : 'The method you called is not defined', + missingTab : 'Activated tab cannot be found. Tabs are case-sensitive.', + noContent : 'The tab you specified is missing a content url.', + path : 'History enabled, but no path was specified', + recursion : 'Max recursive depth reached', + legacyInit : 'onTabInit has been renamed to onFirstLoad in 2.0, please adjust your code.', + legacyLoad : 'onTabLoad has been renamed to onLoad in 2.0. Please adjust your code', + state : 'History requires Asual\'s Address library <https://github.com/asual/jquery-address>' + }, + + metadata : { + tab : 'tab', + loaded : 'loaded', + promise: 'promise' + }, + + className : { + loading : 'loading', + active : 'active' + }, + + selector : { + tabs : '.ui.tab', + ui : '.ui' + } + +}; + +})( jQuery, window, document );