├── .gitattributes ├── .gitignore ├── test ├── support │ ├── jasmine.json │ └── karma.conf.js ├── fixture.html └── tabbedcontent.spec.js ├── .editorconfig ├── composer.json ├── .codeclimate.yml ├── src ├── demos │ ├── analytics.js │ ├── bootstrap.css │ ├── demo.css │ ├── demo.html │ ├── bootstrap.html │ ├── bootstrap_and_zeptojs.html │ └── bootstrap_multiple.html └── tabbedcontent.js ├── .travis.yml ├── bower.json ├── LICENSE ├── gulpfile.js ├── package.json ├── dist └── tabbedcontent.min.js └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | src/demos/* linguist-documentation 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /dist/demos/ 3 | yarn.lock 4 | 5 | #remove project settings of jetbrains products 6 | .idea 7 | -------------------------------------------------------------------------------- /test/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "test", 3 | "spec_files": [ 4 | "**/*[sS]pec.js" 5 | ], 6 | "helpers": [ 7 | "helpers/**/*.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": false 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Unix-style newlines with a newline ending every file 2 | [*] 3 | end_of_line = lf 4 | insert_final_newline = true 5 | indent_style = space 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | indent_size = 2 9 | 10 | [*.{md,mdown,html}] 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elboletaire/tabbedcontent", 3 | "description": "Lightweight JS tabs plugin with HTML 5 history API for jQuery or Zepto", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Òscar Casajuana", 8 | "email": "elboletaire@underave.net" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | eslint: 3 | enabled: true 4 | csslint: 5 | enabled: true 6 | duplication: 7 | enabled: true 8 | config: 9 | languages: 10 | - javascript 11 | fixme: 12 | enabled: true 13 | ratings: 14 | paths: 15 | - src/tabbedcontent.js 16 | exclude_paths: 17 | - "dist/**" 18 | - "test/**" 19 | - "src/demos/**" 20 | -------------------------------------------------------------------------------- /src/demos/analytics.js: -------------------------------------------------------------------------------- 1 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 2 | ga('create', 'UA-70920203-2', 'auto'); 3 | ga('send', 'pageview'); 4 | -------------------------------------------------------------------------------- /src/demos/bootstrap.css: -------------------------------------------------------------------------------- 1 | /* some styles for the tabs contents */ 2 | .tabscontent > div { 3 | padding: 20px; 4 | border-left: 1px solid #ddd; 5 | border-right: 1px solid #ddd; 6 | border-bottom: 1px solid #ddd; 7 | } 8 | .nav-tabs li.active a { 9 | pointer-events: none; 10 | } 11 | body > div.container { 12 | margin-top: 5vw; 13 | } 14 | blockquote { 15 | margin-top: 20px; 16 | } 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | 5 | git: 6 | depth: 1 7 | 8 | branches: 9 | except: 10 | - gh-pages 11 | 12 | addons: 13 | sauce_connect: true 14 | 15 | cache: 16 | yarn: true 17 | 18 | install: 19 | - npm install -g yarn 20 | 21 | before_script: 22 | - yarn install 23 | - yarn add codecov -g 24 | 25 | script: 26 | - yarn test 27 | 28 | after_success: 29 | - cat coverage/*/lcov.info > coverage/lcov.info 30 | - codecov 31 | - if [ $TRAVIS_BRANCH = 'master' ]; then sh build.sh; fi 32 | -------------------------------------------------------------------------------- /test/fixture.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 |
12 |
13 | First tab content 14 |
15 |
16 | Second tab content 17 |
18 |
19 | Third tab content 20 |
21 |
22 | Fourth tab content 23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tabbedContent", 3 | "version": "1.6.0", 4 | "homepage": "https://github.com/elboletaire/tabbedcontent", 5 | "authors": [ 6 | "Òscar Casajuana " 7 | ], 8 | "description": "jQuery TabbedContent is a lightweight tabs plugin with all the essential, like history navigation and a simple API that allow you to change between tabs. If you are using a modern browser it will use the new HTML5 history API to prevent moving the browser viewport when it changes the hash in the URL. It only deppends on jQuery; there's no need to use any other library like jQuery UI.", 9 | "main": "tabbedcontent.js", 10 | "dependencies": { 11 | "jquery": ">=1.4.0" 12 | }, 13 | "keywords": [ 14 | "jquery", 15 | "plugin", 16 | "addon", 17 | "tabs", 18 | "ui" 19 | ], 20 | "license": "Apache-2.0", 21 | "ignore": [ 22 | "**/.*", 23 | "node_modules", 24 | "bower_components", 25 | "test", 26 | "tests" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2015 Òscar Casajuana 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /src/demos/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: normal 13px/14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | } 4 | blockquote { 5 | border-left: 4px solid #ddd; 6 | margin-left: 0; 7 | margin-right: 20px; 8 | padding: 0 20px; 9 | } 10 | div.wrapper { 11 | width: 600px; 12 | margin: 30px auto; 13 | } 14 | body div > ul { 15 | margin: 0; 16 | padding: 0; 17 | border-bottom: 3px solid #666; 18 | overflow: hidden; 19 | } 20 | ul li { 21 | list-style: none; 22 | float: left; 23 | margin-right: 4px; 24 | } 25 | ul li.controls { 26 | float: right; 27 | } 28 | ul li a { 29 | font-weight: bold; 30 | display: inline-block; 31 | padding: 6px 12px; 32 | color: #888; 33 | outline: 0; 34 | text-decoration: none; 35 | background: #f3f3f3; 36 | background: -webkit-gradient(linear,0 0,0 bottom,from(#eee),to(#e4e4e4)); 37 | background: -moz-linear-gradient(#eee,#e4e4e4); 38 | background: linear-gradient(#eee,#e4e4e4); 39 | -pie-background: linear-gradient(#eee,#e4e4e4); 40 | } 41 | 42 | ul li.active a { 43 | pointer-events: none; 44 | color: white; 45 | background: #666; 46 | background: -webkit-gradient(linear,0 0,0 bottom,from(#888),to(#666)); 47 | background: -moz-linear-gradient(#888,#666); 48 | background: linear-gradient(#888,#666); 49 | -pie-background: linear-gradient(#888,#666); 50 | } 51 | div.tabscontent > div { 52 | padding: 0 15px; 53 | } 54 | div.controls { 55 | float: right; 56 | } 57 | div.controls a { 58 | margin-left: 5px; 59 | } 60 | .lorem { 61 | font-style: italic; 62 | } 63 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'), 2 | uglify = require('gulp-uglify'), 3 | rename = require('gulp-rename'), 4 | cleanCss = require('gulp-clean-css'), 5 | chmod = require('gulp-chmod'), 6 | clean = require('gulp-clean'), 7 | replace = require('gulp-replace') 8 | 9 | gulp.task('demos:css', () => { 10 | return gulp.src('src/demos/*.css') 11 | .pipe(cleanCss()) 12 | .pipe(rename({ 13 | extname: '.min.css' 14 | })) 15 | .pipe(chmod(0o664)) 16 | .pipe(gulp.dest('dist/demos')) 17 | }) 18 | 19 | gulp.task('demos:assets', () => { 20 | return gulp.src([ 21 | './node_modules/jquery/dist/jquery.js', 22 | './node_modules/zepto/src/zepto.js', 23 | './node_modules/zepto/src/event.js', 24 | './node_modules/zepto/src/data.js', 25 | './src/demos/analytics.js', 26 | ]) 27 | .pipe(uglify()) 28 | .pipe(rename({ 29 | extname: '.min.js', 30 | })) 31 | .pipe(chmod(0o664)) 32 | .pipe(gulp.dest('dist/demos/assets')) 33 | }) 34 | 35 | gulp.task('demos:html', () => { 36 | return gulp.src('src/demos/*.html') 37 | .pipe(replace(/\.\.\/\.\.\/node_modules\/(?:jquery|zepto)\/(?:dist|src)\/(\w+)\.js/g, './assets/$1.min.js')) 38 | .pipe(replace('../tabbedcontent.', './tabbedcontent.min.')) 39 | .pipe(replace('./analytics.', './assets/analytics.min.')) 40 | .pipe(replace('./demo.', './demo.min.')) 41 | .pipe(replace('./bootstrap.', './bootstrap.min.')) 42 | .pipe(gulp.dest('dist/demos')) 43 | }) 44 | 45 | gulp.task('assets:js', () => { 46 | return gulp.src('src/tabbedcontent.js') 47 | .pipe(uglify({ 48 | preserveComments: 'license' 49 | })) 50 | .pipe(rename({ 51 | extname: '.min.js' 52 | })) 53 | .pipe(chmod(0o664)) 54 | .pipe(gulp.dest('dist/demos')) 55 | .pipe(gulp.dest('dist')) 56 | }) 57 | 58 | gulp.task('clean', () => { 59 | return gulp 60 | .src(['dist/*'], { read: false }) 61 | .pipe(clean()) 62 | }) 63 | 64 | gulp.task('assets', ['assets:js'], () => { 65 | return gulp.src('src/index.html') 66 | .pipe(replace('./tabbedcontent', './tabbedcontent.min')) 67 | .pipe(chmod(0o664)) 68 | .pipe(gulp.dest('dist')) 69 | }) 70 | 71 | gulp.task('demos', ['demos:css', 'demos:html', 'demos:assets']) 72 | gulp.task('default', ['assets', 'demos']) 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tabbedcontent", 3 | "version": "1.7.0", 4 | "description": "TabbedContent is a lightweight tabs plugin for jQuery and Zepto with all the essential, like history navigation and a simple API that allow you to change between tabs. If you are using a modern browser it will use the new HTML5 history API to prevent moving the browser viewport when it changes the hash in the URL. It only deppends on jQuery/Zepto; there's no need to use any other library like jQuery UI.", 5 | "main": "tabbedcontent.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/elboletaire/tabbedcontent.git" 9 | }, 10 | "keywords": [ 11 | "jquery-plugin", 12 | "zepto-plugin", 13 | "ecosystem:jquery", 14 | "ecosystem:zepto", 15 | "ui", 16 | "tabs", 17 | "browser-history" 18 | ], 19 | "author": { 20 | "name": "Òscar Casajuana", 21 | "url": "http://racotecnic.com" 22 | }, 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/elboletaire/tabbedcontent/issues" 26 | }, 27 | "homepage": "https://github.com/elboletaire/tabbedcontent", 28 | "peerDependencies": { 29 | "jquery": ">=1.3.0", 30 | "zepto": ">=1.2.0" 31 | }, 32 | "devDependencies": { 33 | "brfs": "^1.4.3", 34 | "browserify": "^14.3.0", 35 | "browserify-istanbul": "^2.0.0", 36 | "browserify-shim": "^3.8.14", 37 | "es6-arrow-function": "^0.6.6", 38 | "gulp": "^3.9.0", 39 | "gulp-chmod": "^2.0.0", 40 | "gulp-clean": "^0.3.2", 41 | "gulp-clean-css": "^3.3.1", 42 | "gulp-rename": "^1.2.2", 43 | "gulp-replace": "^0.5.4", 44 | "gulp-uglify": "^1.5.1", 45 | "istanbul": "^0.4.5", 46 | "jasmine": "^2.6.0", 47 | "jquery": "^3.2.1", 48 | "karma": "^1.7.0", 49 | "karma-browserify": "^5.1.1", 50 | "karma-chrome-launcher": "^2.1.1", 51 | "karma-coverage": "^1.1.1", 52 | "karma-firefox-launcher": "^1.0.1", 53 | "karma-jasmine": "^1.1.0", 54 | "karma-jasmine-jquery": "^0.1.1", 55 | "karma-phantomjs-launcher": "^1.0.4", 56 | "karma-sauce-launcher": "^1.1.0", 57 | "watchify": "^3.9.0", 58 | "zepto": "^1.2.0" 59 | }, 60 | "dependencies": {}, 61 | "scripts": { 62 | "test": "karma start test/support/karma.conf.js" 63 | }, 64 | "browserify-shim": {} 65 | } 66 | -------------------------------------------------------------------------------- /dist/tabbedcontent.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tabs plugin for jQuery created by Òscar Casajuana < elboletaire at underave dot net > 3 | * 4 | * @copyright Copyright 2013-2016 Òscar Casajuana 5 | * @license MIT 6 | * @author Òscar Casajuana Alonso 7 | */ 8 | !function(t,n,e,r){"use strict";var i=function(i,a){function o(t){return Boolean(I.filter(t).length)}function s(){return 0===q}function l(t){return t%1===0}function c(){return q===I.length-1}function h(n){return t(this).attr("href").match(new RegExp(n+"$"))}function u(n){return n instanceof t?{tab:n,link:a.links.eq(n.index())}:l(n)?{tab:I.eq(n),link:a.links.eq(n)}:I.filter(n).length?{tab:I.filter(n),link:a.links.filter(function(){return h.apply(this,[n])})}:{tab:I.filter("#"+n),link:a.links.filter(function(){return h.apply(this,["#"+n])})}}function f(){return a.links.parent().filter("."+a.currentClass).index()}function d(t){return++q,t===r&&(t=a.loop),q=I.length)&&k(0,!0)}function p(t){return--q,t===r&&(t=a.loop),q>=0?k(q,!0):!!(t&&q<0)&&k(I.length-1,!0)}function g(t){a.history&&a.historyOnInit&&C&&m!==r&&"pushState"in m&&(C=!1,e.setTimeout(function(){m.replaceState(null,"",t)},100)),q=f(),a.onSwitch&&"function"==typeof a.onSwitch&&a.onSwitch(t,v()),i.trigger("tabcontent.switch",[t,v()])}function k(t,n){return t.toString().match(/^#/)||(t="#"+u(t).tab.attr("id")),!!o(t)&&(a.links.attr("aria-selected","false").parent().removeClass(a.currentClass),a.links.filter(function(){return h.apply(this,[t])}).attr("aria-selected","true").parent().addClass(a.currentClass),I.hide(),a.history&&n&&(m!==r&&"pushState"in m?m.pushState(null,"",t):e.location.hash=t),I.attr("aria-hidden","true").filter(t).show(a.speed,function(){a.speed&&g(t)}).attr("aria-hidden","false"),a.speed||g(t),!0)}function w(t){return k(t,!0)}function b(t){k(x.hash)}function S(){if(o(x.hash)?k(x.hash):a.links.parent().filter("."+a.currentClass).length?k(a.links.parent().filter("."+a.currentClass).index()):a.errorSelector&&I.find(a.errorSelector).length?I.each(function(){if(t(this).find(a.errorSelector).length)return k("#"+t(this).attr("id")),!1}):k("#"+I.filter(":first-child").attr("id")),a.errorSelector&&I.find(a.errorSelector).each(function(){var n=u(t(this).parent());n.link.parent().addClass(a.tabErrorClass)}),"onhashchange"in e)t(e).bind("hashchange",b);else{var n=x.href;e.setInterval(function(){n!==x.href&&(b.call(e.event),n=x.href)},100)}t(a.links).on("click",function(n){k(t(this).attr("href").replace(/^[^#]+/,""),a.history),n.preventDefault()}),a.onInit&&"function"==typeof a.onInit&&a.onInit(v()),i.trigger("tabcontent.init",[v()])}function v(){return{"switch":w,switchTab:w,getCurrent:f,getTab:u,next:d,prev:p,isFirst:s,isLast:c}}var y={links:i.prev().find("a").length?i.prev().find("a"):".tabs a",errorSelector:".error-message",speed:!1,onSwitch:!1,onInit:!1,currentClass:"active",tabErrorClass:"has-errors",history:!0,historyOnInit:!0,loop:!1},C=!1,I=i.children(),m=e.history,x=n.location,q=null;return a=t.extend(y,a),a.links instanceof t||(a.links=t(a.links)),S(),v()};t.fn.tabbedContent=function(n){return this.each(function(){var e=new i(t(this),n);t(this).data("api",e)})}}(window.jQuery||window.Zepto||window.$,document,window); -------------------------------------------------------------------------------- /test/tabbedcontent.spec.js: -------------------------------------------------------------------------------- 1 | var $ = global.jQuery = require('jquery') 2 | 3 | require('../src/tabbedcontent.js') 4 | 5 | describe('$.fn.tabbedContent', () => { 6 | beforeEach(() => { 7 | $.fx.off = true 8 | jasmine.getFixtures().fixturesPath = './base/test/' 9 | loadFixtures('fixture.html') 10 | }) 11 | 12 | describe('init', () => { 13 | it('sets the first tab as active', (done) => { 14 | $('.tabscontent').tabbedContent() 15 | 16 | expect($('ul > li:first-of-type')).toBeMatchedBy('.active') 17 | done() 18 | }) 19 | 20 | it('hides all contents except for the first tab', (done) => { 21 | $('.tabscontent').tabbedContent() 22 | 23 | expect($('#tab-1')).toBeVisible() 24 | expect($('#tab-2')).not.toBeVisible() 25 | expect($('#tab-3')).not.toBeVisible() 26 | expect($('#tab-n')).not.toBeVisible() 27 | 28 | done() 29 | }) 30 | }) 31 | 32 | describe('events', () => { 33 | it('properly changes the tab when clicking', (done) => { 34 | $('.tabscontent').tabbedContent() 35 | 36 | $("a[href='#tab-2']").trigger('click') 37 | 38 | expect($('#tab-1')).not.toBeVisible() 39 | expect($('#tab-2')).toBeVisible() 40 | expect($('#tab-3')).not.toBeVisible() 41 | expect($('#tab-n')).not.toBeVisible() 42 | 43 | done() 44 | }) 45 | 46 | it('actually calls the events', (done) => { 47 | var event = { 48 | type: 'tabcontent.switch', 49 | } 50 | spyOnEvent('.tabscontent', 'tabcontent.init') 51 | $('.tabscontent').tabbedContent({ history: false }) 52 | expect($('.tabscontent')).toHandle('tabcontent.init') 53 | 54 | spyOnEvent('.tabscontent', 'tabcontent.switch') 55 | $("a[href='#tab-n']").trigger('click') 56 | expect($('.tabscontent')).toHandle('tabcontent.switch') 57 | 58 | expect($('#tab-1')).not.toBeVisible() 59 | expect($('#tab-2')).not.toBeVisible() 60 | expect($('#tab-3')).not.toBeVisible() 61 | expect($('#tab-n')).toBeVisible() 62 | 63 | done() 64 | }) 65 | }) 66 | 67 | describe('API', () => { 68 | it('properly shows if it\'s the first tab', (done) => { 69 | var api = $('.tabscontent').tabbedContent().data('api') 70 | api.switch(2) 71 | expect(api.isFirst()).toBeFalsy() 72 | api.switch(0) 73 | expect(api.isFirst()).toBeTruthy() 74 | 75 | done() 76 | }) 77 | 78 | it('properly shows if it\'s the last tab', (done) => { 79 | var api = $('.tabscontent').tabbedContent().data('api') 80 | api.switch(0) 81 | expect(api.isLast()).toBeFalsy() 82 | api.switch(3) 83 | expect(api.isLast()).toBeTruthy() 84 | 85 | done() 86 | }) 87 | 88 | it('properly passes to the next tab using the api', (done) => { 89 | var api = $('.tabscontent').tabbedContent({loop: true}).data('api') 90 | api.switch(0) 91 | expect(api.getCurrent()).toEqual(0) 92 | api.next() 93 | expect(api.getCurrent()).toEqual(1) 94 | api.switch(3) 95 | api.next() 96 | expect(api.getCurrent()).toEqual(0) 97 | 98 | done() 99 | }) 100 | 101 | it('properly passes to the previous tab using the api', (done) => { 102 | var api = $('.tabscontent').tabbedContent({loop: true}).data('api') 103 | api.switch(3) 104 | expect(api.getCurrent()).toEqual(3) 105 | api.prev() 106 | expect(api.getCurrent()).toEqual(2) 107 | api.switch(0) 108 | api.prev() 109 | expect(api.getCurrent()).toEqual(3) 110 | 111 | done() 112 | }) 113 | }) 114 | }); 115 | -------------------------------------------------------------------------------- /test/support/karma.conf.js: -------------------------------------------------------------------------------- 1 | const brfs = require('brfs'), 2 | istanbul = require('browserify-istanbul') 3 | 4 | module.exports = function(config) { 5 | const launchers = { 6 | firefox_first: { 7 | base: 'SauceLabs', 8 | browserName: 'firefox', 9 | platform: 'Linux', 10 | version: '6.0', 11 | }, 12 | firefox_latest: { 13 | base: 'SauceLabs', 14 | browserName: 'firefox', 15 | platform: 'Windows 10', 16 | version: 'latest', 17 | }, 18 | chrome_first: { 19 | base: 'SauceLabs', 20 | browserName: 'chrome', 21 | platform: 'Linux', 22 | version: '26.0', 23 | }, 24 | chrome_latest: { 25 | base: 'SauceLabs', 26 | browserName: 'chrome', 27 | platform: 'Windows 10', 28 | version: 'latest', 29 | }, 30 | ie_win_7: { 31 | base: 'SauceLabs', 32 | browserName: 'internet explorer', 33 | platform: 'Windows 7', 34 | version: '9.0', 35 | }, 36 | ie_win_8: { 37 | base: 'SauceLabs', 38 | browserName: 'internet explorer', 39 | platform: 'Windows 8', 40 | version: '10.0', 41 | }, 42 | ie_win_81: { 43 | base: 'SauceLabs', 44 | browserName: 'internet explorer', 45 | platform: 'Windows 8.1', 46 | version: '11.0', 47 | }, 48 | ie_win_10: { 49 | base: 'SauceLabs', 50 | browserName: 'internet explorer', 51 | platform: 'Windows 10', 52 | version: '11.103', 53 | }, 54 | edge_win_10: { 55 | base: 'SauceLabs', 56 | browserName: 'MicrosoftEdge', 57 | platform: 'Windows 10', 58 | version: '14.14393', 59 | }, 60 | safari_first: { 61 | base: 'SauceLabs', 62 | browserName: 'safari', 63 | platform: 'OS X 10.8', 64 | version: '6.0', 65 | }, 66 | safari_latest: { 67 | base: 'SauceLabs', 68 | browserName: 'safari', 69 | platform: 'macOS 10.12', 70 | version: 'latest', 71 | }, 72 | android_first: { 73 | base: 'SauceLabs', 74 | browserName: 'Browser', 75 | appiumVersion: '1.6.4', 76 | deviceName: 'Android Emulator', 77 | deviceOrientation: 'portrait', 78 | platformVersion: '4.4', 79 | platformName: 'Android', 80 | }, 81 | android_latest: { 82 | base: 'SauceLabs', 83 | browserName: 'Chrome', 84 | appiumVersion: '1.6.4', 85 | deviceName: 'Android Emulator', 86 | deviceOrientation: 'portrait', 87 | platformVersion: '6.0', 88 | platformName: 'Android', 89 | } 90 | } 91 | 92 | const mainLaunchers = Object.keys(launchers) 93 | 94 | const localLaunchers = { 95 | phantom: { base: 'PhantomJS' }, 96 | firefox: { base: 'Firefox' }, 97 | chrome: { base: 'Chrome' }, 98 | } 99 | 100 | let browsers = Object.keys(localLaunchers) 101 | if (process.env.CI) { 102 | browsers = mainLaunchers 103 | } 104 | 105 | const saucelabs = { 106 | testName: 'tabbedcontent', 107 | recordScreenshots: true, 108 | public: 'public', 109 | tags: ['tabbedcontent', 'jquery', 'plugin'], 110 | } 111 | 112 | if (process.env.TRAVIS) { 113 | // https://github.com/karma-runner/karma-sauce-launcher/issues/73 114 | saucelabs.startConnect = false 115 | saucelabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER 116 | } 117 | 118 | config.set({ 119 | client: { 120 | jasmine: { 121 | config: './test/support/jasmine.json', 122 | } 123 | }, 124 | // base path that will be used to resolve all patterns (eg. files, exclude) 125 | basePath: '../../', 126 | 127 | // frameworks to use 128 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 129 | frameworks: ['browserify', 'jasmine-jquery', 'jasmine'], 130 | // list of files / patterns to load in the browser 131 | files: [ 132 | 'src/*.js', 133 | 'test/*.js', 134 | 'test/*.html', 135 | ], 136 | // list of files to exclude 137 | exclude: [ 138 | ], 139 | // preprocess matching files before serving them to the browser 140 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 141 | preprocessors: { 142 | './test/*.spec.js': ['browserify'], 143 | './src/*.js': ['coverage'], 144 | }, 145 | browserify: { 146 | debug: true, 147 | transform: [brfs, 'browserify-shim', 'es6-arrow-function', istanbul({ 148 | ignore: ['**/node_modules/**', '**/test/**'], 149 | })], 150 | }, 151 | coverageReporter: { 152 | type: 'lcov', 153 | }, 154 | sauceLabs: saucelabs, 155 | 156 | // test results reporter to use 157 | // possible values: 'dots', 'progress' 158 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 159 | reporters: ['progress', 'coverage', 'saucelabs'], 160 | 161 | // web server port 162 | port: 9876, 163 | 164 | // enable / disable colors in the output (reporters and logs) 165 | colors: true, 166 | 167 | // level of logging 168 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 169 | logLevel: config.LOG_INFO, 170 | 171 | // enable / disable watching file and executing tests whenever any file changes 172 | autoWatch: true, 173 | 174 | // start these browsers 175 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 176 | customLaunchers: Object.assign({}, launchers, localLaunchers), 177 | browsers: browsers, 178 | 179 | // Continuous Integration mode 180 | // if true, Karma captures browsers, runs the tests and exits 181 | singleRun: true, 182 | 183 | // Concurrency level 184 | // how many browser should be started simultaneous 185 | concurrency: Infinity 186 | }) 187 | } 188 | -------------------------------------------------------------------------------- /src/demos/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TabbedContent - Lightweight tabs plugin jQuery demo 7 | 8 | 9 | 10 | 11 | 12 | 15 | 37 | 38 | 39 |
40 | 52 |
53 |
54 |

55 | Navigate using the tabs and then try out going backward and forward using the browser's history arrows. 56 |

57 |

58 | Click here to switch to tab using the API 59 |

60 |
61 |

62 | TabbedContent is a lightweight tabs plugin 63 | that uses the HTML5 history API to add your tab navigation 64 | to your browser's history. 65 |

66 |

67 | It is compatible with both jQuery and Zepto.js libraries. 68 |

69 |

70 | It also has an API that will let you switch between tabs externally. 71 |

72 |

73 | Check it out at https://github.com/elboletaire/tabbedcontent. 74 |

75 |
76 |
77 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sequi ea similique nulla iure eum illo quis nisi velit incidunt veritatis beatae dolorem labore laboriosam optio odio mollitia adipisci fuga culpa.

78 |

Vel perspiciatis voluptatum libero iure natus dicta illum accusamus nobis quas quidem voluptatem magnam. Et amet rem tempore laborum provident sequi nostrum! Nobis tenetur tempore delectus sunt at atque temporibus.

79 |

Tempora voluptas odit illo aliquam dicta commodi repellat modi laborum quis repellendus cupiditate ex ratione fuga expedita rerum ipsum amet optio iste accusamus officia! Recusandae expedita repellat voluptates ullam quis.

80 |
81 |
82 |
83 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor sit tempora animi ad facere ea inventore repellat cupiditate dicta ducimus veniam sint mollitia facilis odit doloremque eos consectetur magni temporibus!

84 |

Cum temporibus enim debitis sapiente optio illo culpa ratione similique magnam eligendi assumenda asperiores molestias vel! Laudantium nostrum incidunt excepturi. Nisi quisquam tempora laudantium accusamus expedita provident sapiente consequuntur magnam?

85 |

Quisquam totam iste eius reprehenderit earum perferendis minus sit est omnis tempore modi delectus ut beatae rerum ad. Alias laborum quas unde quia et illum reiciendis nihil ab voluptate dolorem.

86 |

Beatae incidunt cumque tempore sapiente porro nulla natus perferendis repellendus dolorem at quisquam sequi officia! Quae ipsum eius rerum deserunt aspernatur molestiae quos incidunt dolor illo ea laudantium ipsam tempora?

87 |

Saepe facere debitis nam molestiae distinctio temporibus libero autem dignissimos qui repudiandae consequuntur ducimus atque. Sunt ea sit magni voluptatum a ab debitis molestiae dolor saepe quae fugit voluptas quis?

88 |
89 |
90 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero odit dolore impedit dolor reprehenderit dignissimos magni praesentium temporibus voluptatum nisi. In possimus commodi similique modi expedita est. Recusandae voluptate dolor?

91 |

Voluptatum in sapiente aut quis nihil fugit quia consequuntur quo doloremque aspernatur! Eius error sequi rerum est earum repellat nemo debitis molestias molestiae fugit dolor suscipit atque. Sequi ad quod.

92 |

Optio consectetur quam numquam architecto a hic suscipit! Vel officia dolore atque minima facilis explicabo quia ullam magnam quis inventore dolor nulla soluta reprehenderit repellendus quibusdam nisi id iusto quam.

93 |
94 |
95 |

96 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores maxime aperiam natus minima dolor expedita voluptas magnam! Molestiae similique aliquam atque quas error soluta eligendi quaerat eveniet quibusdam minus! Eos. 97 |

98 |

99 | Nobis molestiae odio facere magnam harum vitae est? Necessitatibus illum distinctio amet earum sequi ducimus perspiciatis neque velit culpa rerum aspernatur ipsam ab molestiae expedita quo doloribus atque eos cupiditate. 100 |

101 |
102 |
103 |
104 | 105 | 106 | -------------------------------------------------------------------------------- /src/demos/bootstrap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TabbedContent - Lightweight tabs plugin Bootstrap demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 38 | 39 | 40 |
41 | 53 |
54 |
55 |

56 | Navigate using the tabs and then try out going backward and forward using the browser's history arrows. 57 |

58 |

59 | Click here to switch to tab using the API 60 |

61 |
62 |

63 | TabbedContent is a lightweight tabs plugin 64 | that uses the HTML5 history API to add your tab navigation 65 | to your browser's history. 66 |

67 |

68 | It is compatible with both jQuery and Zepto.js libraries. 69 |

70 |

71 | It also has an API that will let you switch between tabs externally. 72 |

73 |

74 | Check it out at https://github.com/elboletaire/tabbedcontent. 75 |

76 |
77 |
78 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sequi ea similique nulla iure eum illo quis nisi velit incidunt veritatis beatae dolorem labore laboriosam optio odio mollitia adipisci fuga culpa.

79 |

Vel perspiciatis voluptatum libero iure natus dicta illum accusamus nobis quas quidem voluptatem magnam. Et amet rem tempore laborum provident sequi nostrum! Nobis tenetur tempore delectus sunt at atque temporibus.

80 |

Tempora voluptas odit illo aliquam dicta commodi repellat modi laborum quis repellendus cupiditate ex ratione fuga expedita rerum ipsum amet optio iste accusamus officia! Recusandae expedita repellat voluptates ullam quis.

81 |
82 |
83 |
84 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor sit tempora animi ad facere ea inventore repellat cupiditate dicta ducimus veniam sint mollitia facilis odit doloremque eos consectetur magni temporibus!

85 |

Cum temporibus enim debitis sapiente optio illo culpa ratione similique magnam eligendi assumenda asperiores molestias vel! Laudantium nostrum incidunt excepturi. Nisi quisquam tempora laudantium accusamus expedita provident sapiente consequuntur magnam?

86 |

Quisquam totam iste eius reprehenderit earum perferendis minus sit est omnis tempore modi delectus ut beatae rerum ad. Alias laborum quas unde quia et illum reiciendis nihil ab voluptate dolorem.

87 |

Beatae incidunt cumque tempore sapiente porro nulla natus perferendis repellendus dolorem at quisquam sequi officia! Quae ipsum eius rerum deserunt aspernatur molestiae quos incidunt dolor illo ea laudantium ipsam tempora?

88 |

Saepe facere debitis nam molestiae distinctio temporibus libero autem dignissimos qui repudiandae consequuntur ducimus atque. Sunt ea sit magni voluptatum a ab debitis molestiae dolor saepe quae fugit voluptas quis?

89 |
90 |
91 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero odit dolore impedit dolor reprehenderit dignissimos magni praesentium temporibus voluptatum nisi. In possimus commodi similique modi expedita est. Recusandae voluptate dolor?

92 |

Voluptatum in sapiente aut quis nihil fugit quia consequuntur quo doloremque aspernatur! Eius error sequi rerum est earum repellat nemo debitis molestias molestiae fugit dolor suscipit atque. Sequi ad quod.

93 |

Optio consectetur quam numquam architecto a hic suscipit! Vel officia dolore atque minima facilis explicabo quia ullam magnam quis inventore dolor nulla soluta reprehenderit repellendus quibusdam nisi id iusto quam.

94 |
95 |
96 |

97 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores maxime aperiam natus minima dolor expedita voluptas magnam! Molestiae similique aliquam atque quas error soluta eligendi quaerat eveniet quibusdam minus! Eos. 98 |

99 |

100 | Nobis molestiae odio facere magnam harum vitae est? Necessitatibus illum distinctio amet earum sequi ducimus perspiciatis neque velit culpa rerum aspernatur ipsam ab molestiae expedita quo doloribus atque eos cupiditate. 101 |

102 |
103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /src/demos/bootstrap_and_zeptojs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TabbedContent - Lightweight tabs plugin Bootstrap & Zepto.js demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 41 | 42 | 43 |
44 | 56 |
57 |
58 |

59 | Navigate using the tabs and then try out going backward and forward using the browser's history arrows. 60 |

61 |

62 | Click here to switch to tab using the API 63 |

64 |
65 |

66 | TabbedContent is a lightweight tabs plugin 67 | that uses the HTML5 history API to add your tab navigation 68 | to your browser's history. 69 |

70 |

71 | It is compatible with both jQuery and Zepto.js libraries. 72 |

73 |

74 | It also has an API that will let you switch between tabs externally. 75 |

76 |

77 | Check it out at https://github.com/elboletaire/tabbedcontent. 78 |

79 |
80 |
81 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sequi ea similique nulla iure eum illo quis nisi velit incidunt veritatis beatae dolorem labore laboriosam optio odio mollitia adipisci fuga culpa.

82 |

Vel perspiciatis voluptatum libero iure natus dicta illum accusamus nobis quas quidem voluptatem magnam. Et amet rem tempore laborum provident sequi nostrum! Nobis tenetur tempore delectus sunt at atque temporibus.

83 |

Tempora voluptas odit illo aliquam dicta commodi repellat modi laborum quis repellendus cupiditate ex ratione fuga expedita rerum ipsum amet optio iste accusamus officia! Recusandae expedita repellat voluptates ullam quis.

84 |
85 |
86 |
87 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor sit tempora animi ad facere ea inventore repellat cupiditate dicta ducimus veniam sint mollitia facilis odit doloremque eos consectetur magni temporibus!

88 |

Cum temporibus enim debitis sapiente optio illo culpa ratione similique magnam eligendi assumenda asperiores molestias vel! Laudantium nostrum incidunt excepturi. Nisi quisquam tempora laudantium accusamus expedita provident sapiente consequuntur magnam?

89 |

Quisquam totam iste eius reprehenderit earum perferendis minus sit est omnis tempore modi delectus ut beatae rerum ad. Alias laborum quas unde quia et illum reiciendis nihil ab voluptate dolorem.

90 |

Beatae incidunt cumque tempore sapiente porro nulla natus perferendis repellendus dolorem at quisquam sequi officia! Quae ipsum eius rerum deserunt aspernatur molestiae quos incidunt dolor illo ea laudantium ipsam tempora?

91 |

Saepe facere debitis nam molestiae distinctio temporibus libero autem dignissimos qui repudiandae consequuntur ducimus atque. Sunt ea sit magni voluptatum a ab debitis molestiae dolor saepe quae fugit voluptas quis?

92 |
93 |
94 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero odit dolore impedit dolor reprehenderit dignissimos magni praesentium temporibus voluptatum nisi. In possimus commodi similique modi expedita est. Recusandae voluptate dolor?

95 |

Voluptatum in sapiente aut quis nihil fugit quia consequuntur quo doloremque aspernatur! Eius error sequi rerum est earum repellat nemo debitis molestias molestiae fugit dolor suscipit atque. Sequi ad quod.

96 |

Optio consectetur quam numquam architecto a hic suscipit! Vel officia dolore atque minima facilis explicabo quia ullam magnam quis inventore dolor nulla soluta reprehenderit repellendus quibusdam nisi id iusto quam.

97 |
98 |
99 |

100 | Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores maxime aperiam natus minima dolor expedita voluptas magnam! Molestiae similique aliquam atque quas error soluta eligendi quaerat eveniet quibusdam minus! Eos. 101 |

102 |

103 | Nobis molestiae odio facere magnam harum vitae est? Necessitatibus illum distinctio amet earum sequi ducimus perspiciatis neque velit culpa rerum aspernatur ipsam ab molestiae expedita quo doloribus atque eos cupiditate. 104 |

105 |
106 |
107 |
108 | 109 | 110 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Easy to use tabs plugin for jQuery & Zepto.js 2 | ============================================= 3 | 4 | [![Build status][build svg]][build status] 5 | [![Code coverage][coverage svg]][coverage] 6 | [![License][license svg]][license] 7 | [![Latest stable version][releases svg]][releases] 8 | [![Total downloads][downloads svg]][downloads] 9 | [![Code climate][climate svg]][climate] 10 | 11 | [![Sauce labs][sauce svg]][sauce] 12 | 13 | TabbedContent is a lightweight* tabs plugin that uses the HTML5 history API to 14 | add your tab navigation to your browser's history. 15 | 16 | > \* 3 KB minified, 1.25 KB gzipped/deflated 17 | 18 | It is compatible with both jQuery and Zepto.js libraries. 19 | 20 | It also has an API that will let you switch between tabs externally. 21 | 22 | 23 | Online Demos 24 | ------------ 25 | 26 | - [Basic demo](http://elboletaire.github.io/tabbedcontent/demos/demo.html) 27 | - [Bootstrap demo](http://elboletaire.github.io/tabbedcontent/demos/bootstrap.html) 28 | - [Bootstrap demo with multiple tabbed contents](http://elboletaire.github.io/tabbedcontent/demos/bootstrap_multiple.html) 29 | - [Bootstrap + Zepto.js demo](http://elboletaire.github.io/tabbedcontent/demos/bootstrap_and_zeptojs.html) 30 | 31 | Installation 32 | ------------ 33 | 34 | ### Using bower 35 | 36 | ```bash 37 | bower install --save tabbedcontent 38 | ``` 39 | 40 | ### Using npm 41 | 42 | ```bash 43 | npm install tabbedcontent 44 | ``` 45 | 46 | Usage 47 | ----- 48 | 49 | Basic layout: 50 | 51 | ```html 52 | 58 |
59 |
60 | 61 |
62 |
63 | 64 |
65 |
66 | 67 |
68 |
69 | 70 |
71 |
72 | ``` 73 | 74 | The links of the tabs should point to each tab id. 75 | 76 | Basic javascript initialization: 77 | 78 | ```javascript 79 | $('.tabscontent').tabbedContent(); 80 | ``` 81 | 82 | By default the plugin will take the links inside the previous wrapper related to the tabs layer; but you can specify your links selector, so you can put your links everywhere: 83 | 84 | ```html 85 |