├── .gitignore ├── .eslintignore ├── test-app ├── .meteor │ ├── .gitignore │ ├── release │ ├── platforms │ ├── .id │ ├── .finished-upgraders │ ├── packages │ └── versions ├── packages │ └── tests-proxy │ │ ├── tests │ │ └── package.js ├── routes.coffee ├── tests │ └── jasmine │ │ └── client │ │ └── integration │ │ ├── matchers.coffee │ │ ├── _wait-for-router-helper.js │ │ └── spec.coffee └── test-app.html ├── circle.yml ├── .eslintrc ├── package.js ├── LICENSE ├── .versions ├── body-class-test.coffee ├── body-class.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test-app/**/* 2 | -------------------------------------------------------------------------------- /test-app/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /test-app/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.2.1 2 | -------------------------------------------------------------------------------- /test-app/packages/tests-proxy/tests: -------------------------------------------------------------------------------- 1 | ../../tests -------------------------------------------------------------------------------- /test-app/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | override: 3 | - curl https://install.meteor.com | /bin/sh 4 | - npm install spacejam 5 | 6 | test: 7 | override: 8 | - ./node_modules/spacejam/bin/spacejam test-packages ./ 9 | - meteor --test: 10 | pwd: 11 | "test-app" 12 | -------------------------------------------------------------------------------- /test-app/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | x8ofqk11czbqye9e3qr 8 | -------------------------------------------------------------------------------- /test-app/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | 1.2.0-standard-minifiers-package 10 | 1.2.0-meteor-platform-split 11 | 1.2.0-cordova-changes 12 | 1.2.0-breaking-changes 13 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 4 | "strict": 0, 5 | "quotes": [2, "single"], 6 | "no-underscore-dangle": 0, 7 | "curly": 0, 8 | "new-cap": 0 9 | }, 10 | 11 | "env": { 12 | "browser": true, 13 | "node": true, 14 | "jquery": true, 15 | "meteor": true, 16 | "es6": true 17 | }, 18 | 19 | "globals": { 20 | "Iron": false, 21 | "BodyClass": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test-app/routes.coffee: -------------------------------------------------------------------------------- 1 | Router.route 'home', 2 | path: '/' 3 | 4 | Router.route 'sub', 5 | path: '/sub' 6 | 7 | Router.route 'layout', 8 | path: '/layout' 9 | layoutTemplate: 'DefaultLayout' 10 | 11 | Router.route 'sec-layout', 12 | name: 'sec-layout' 13 | layoutTemplate: 'SecondaryLayout' 14 | 15 | Router.route 'changeLayout', 16 | path: 'change' 17 | template: 'sub' 18 | layoutTemplate: 'DefaultLayout' 19 | 20 | action: -> 21 | this.layout 'SecondaryLayout' 22 | this.render() 23 | -------------------------------------------------------------------------------- /test-app/packages/tests-proxy/package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: "velocity:test-proxy", 3 | summary: "Dynamically created package to expose test files to mirrors", 4 | version: "0.0.4", 5 | debugOnly: true 6 | }); 7 | 8 | Package.onUse(function (api) { 9 | api.use("coffeescript", ["client", "server"]); 10 | api.add_files("tests/jasmine/client/integration/_wait-for-router-helper.js",["client"]); 11 | api.add_files("tests/jasmine/client/integration/matchers.coffee",["client"]); 12 | api.add_files("tests/jasmine/client/integration/spec.coffee",["client"]); 13 | }); -------------------------------------------------------------------------------- /test-app/tests/jasmine/client/integration/matchers.coffee: -------------------------------------------------------------------------------- 1 | beforeEach -> 2 | jasmine.addMatchers 3 | toHaveClass: -> 4 | compare: (actual, expected) -> 5 | $el = actual 6 | 7 | expected = '' if expected is undefined 8 | 9 | result = 10 | pass: $el.hasClass(expected) is true 11 | 12 | if result.pass 13 | result.message = "Expected element to not include #{$el.attr 'class'}" 14 | else 15 | result.message = "Expected element to have class(es) #{expected}, got #{$el.attr 'class'}." 16 | 17 | return result 18 | -------------------------------------------------------------------------------- /test-app/test-app.html: -------------------------------------------------------------------------------- 1 | 2 | test-app 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 20 | 21 | 22 | 29 | 30 | 37 | -------------------------------------------------------------------------------- /test-app/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # Check this file (and the other files in this directory) into your repository. 3 | # 4 | # 'meteor add' and 'meteor remove' will edit this file for you, 5 | # but you can also edit it by hand. 6 | 7 | velocity:html-reporter 8 | velocity:console-reporter 9 | sanjo:jasmine 10 | lookback:body-class 11 | iron:router 12 | coffeescript 13 | standard-minifiers 14 | meteor-base 15 | mobile-experience 16 | mongo 17 | blaze-html-templates 18 | session 19 | jquery 20 | tracker 21 | logging 22 | reload 23 | random 24 | ejson 25 | spacebars 26 | check 27 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'lookback:body-class', 3 | version: '0.4.1', 4 | git: 'https://github.com/lookback/meteor-bodyclass', 5 | summary: 'Automatically add classes for router templates and layouts for scoping with CSS.' 6 | }); 7 | 8 | var where = 'client'; 9 | 10 | Package.onUse(function(api) { 11 | api.versionsFrom('1.2.0.2'); 12 | 13 | api.use([ 14 | 'ecmascript', 15 | 'check', 16 | 'underscore', 17 | 'jquery', 18 | 'tracker' 19 | ], where); 20 | 21 | api.use([ 22 | 'iron:router@1.0.0', 23 | 'kadira:flow-router@2.8.0' 24 | ], where, {weak: true}); 25 | 26 | api.addFiles('body-class.js', where); 27 | api.export('BodyClass', where); 28 | }); 29 | 30 | Package.onTest(function(api) { 31 | api.use([ 32 | 'coffeescript', 33 | 'practicalmeteor:munit', 34 | 'lookback:body-class' 35 | ], where); 36 | 37 | api.addFiles('body-class-test.coffee', where); 38 | }); 39 | -------------------------------------------------------------------------------- /test-app/tests/jasmine/client/integration/_wait-for-router-helper.js: -------------------------------------------------------------------------------- 1 | // https://github.com/Sanjo/meteor-jasmine/wiki/Integration-Tests-for-Iron-Router 2 | 3 | (function (Meteor, Tracker, Router) { 4 | var isRouterReady = false; 5 | var callbacks = []; 6 | 7 | window.waitForRouter = function (callback) { 8 | if (isRouterReady) { 9 | callback(); 10 | } else { 11 | callbacks.push(callback); 12 | } 13 | }; 14 | 15 | Router.onAfterAction(function () { 16 | if (!isRouterReady && this.ready()) { 17 | Tracker.afterFlush(function () { 18 | isRouterReady = true; 19 | callbacks.forEach(function (callback) { 20 | callback(); 21 | }); 22 | callbacks = [] 23 | }) 24 | } 25 | }); 26 | 27 | Router.onRerun(function () { 28 | isRouterReady = false; 29 | this.next(); 30 | }); 31 | 32 | Router.onStop(function () { 33 | isRouterReady = false; 34 | if (this.next) { 35 | this.next(); 36 | } 37 | }); 38 | })(Meteor, Tracker, Router); 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Lookback Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.versions: -------------------------------------------------------------------------------- 1 | babel-compiler@5.8.24_1 2 | babel-runtime@0.1.4 3 | base64@1.0.4 4 | binary-heap@1.0.4 5 | blaze@2.1.3 6 | blaze-tools@1.0.4 7 | boilerplate-generator@1.0.4 8 | caching-compiler@1.0.0 9 | callback-hook@1.0.4 10 | check@1.1.0 11 | coffeescript@1.0.11 12 | ddp@1.2.2 13 | ddp-client@1.2.1 14 | ddp-common@1.2.2 15 | ddp-server@1.2.2 16 | deps@1.0.9 17 | diff-sequence@1.0.1 18 | ecmascript@0.1.6 19 | ecmascript-runtime@0.2.6 20 | ejson@1.0.7 21 | geojson-utils@1.0.4 22 | html-tools@1.0.5 23 | htmljs@1.0.5 24 | id-map@1.0.4 25 | jquery@1.11.4 26 | local-test:lookback:body-class@0.4.0 27 | logging@1.0.8 28 | lookback:body-class@0.4.0 29 | meteor@1.1.10 30 | minimongo@1.0.10 31 | mongo@1.1.3 32 | mongo-id@1.0.1 33 | npm-mongo@1.4.39_1 34 | observe-sequence@1.0.7 35 | ordered-dict@1.0.4 36 | practicalmeteor:chai@1.9.2_3 37 | practicalmeteor:loglevel@1.2.0_1 38 | practicalmeteor:munit@2.1.3 39 | practicalmeteor:sinon@1.10.3_2 40 | promise@0.5.1 41 | random@1.0.5 42 | reactive-var@1.0.6 43 | retry@1.0.4 44 | routepolicy@1.0.6 45 | spacebars@1.0.7 46 | spacebars-compiler@1.0.7 47 | test-helpers@1.0.5 48 | tinytest@1.0.6 49 | tracker@1.0.9 50 | ui@1.0.8 51 | underscore@1.0.4 52 | webapp@1.2.3 53 | webapp-hashing@1.0.5 54 | -------------------------------------------------------------------------------- /body-class-test.coffee: -------------------------------------------------------------------------------- 1 | Should = should() 2 | 3 | describe 'BodyClass', -> 4 | 5 | beforeAll -> 6 | # Simple stubs 7 | window.Router = {} 8 | Router.current = -> 9 | lookupTemplate: -> 10 | 'template' 11 | lookupOption: -> 12 | 'layout' 13 | _layout: 14 | _template: 'layout' 15 | 16 | afterEach -> 17 | $('body, html').removeClass() 18 | 19 | it 'should be available', -> 20 | BodyClass.should.be.defined 21 | 22 | describe 'config', -> 23 | 24 | it 'should be able to use an arbitrary element', -> 25 | BodyClass.config(element: 'html').add 'foo' 26 | 27 | $('html').hasClass('foo').should.be.true 28 | # Restore 29 | BodyClass.settings.element = 'body' 30 | 31 | describe 'run', -> 32 | 33 | it 'should add a classes of the current template and layout', -> 34 | BodyClass.run() 35 | 36 | $('body').hasClass('layout template').should.be.true 37 | 38 | it 'should take additional classes as an array', -> 39 | BodyClass.run(classes: ['foo', 'bar']) 40 | $('body').hasClass('layout template foo bar').should.be.true 41 | 42 | describe 'cleanup', -> 43 | 44 | it 'should clean up router classes', -> 45 | BodyClass.cleanup() 46 | 47 | $('body').hasClass('layout template').should.be.false 48 | 49 | describe 'add', -> 50 | 51 | it 'should be able to add a class', -> 52 | BodyClass.add 'foo' 53 | $('body').hasClass('foo').should.be.true 54 | 55 | it 'should be able to add an array of classes', -> 56 | BodyClass.add ['foo', 'bar'] 57 | $('body').hasClass('foo bar').should.be.true 58 | 59 | it 'should be able to add a function', -> 60 | BodyClass.add -> 'foo' 61 | $('body').hasClass('foo').should.be.true 62 | 63 | it 'should be able run function reactively', -> 64 | Session.set 'foo', 'bar' 65 | 66 | BodyClass.add -> 67 | Session.get 'foo' 68 | 69 | $('body').hasClass('bar').should.be.true 70 | 71 | Session.set 'foo', 'baz' 72 | 73 | Tracker.flush() 74 | $('body').hasClass('baz').should.be.true 75 | $('body').hasClass('bar').should.be.false 76 | -------------------------------------------------------------------------------- /test-app/.meteor/versions: -------------------------------------------------------------------------------- 1 | amplify@1.0.0 2 | autoupdate@1.2.4 3 | babel-compiler@5.8.24_1 4 | babel-runtime@0.1.4 5 | base64@1.0.4 6 | binary-heap@1.0.4 7 | blaze@2.1.3 8 | blaze-html-templates@1.0.1 9 | blaze-tools@1.0.4 10 | boilerplate-generator@1.0.4 11 | caching-compiler@1.0.0 12 | caching-html-compiler@1.0.2 13 | callback-hook@1.0.4 14 | check@1.1.0 15 | coffeescript@1.0.11 16 | ddp@1.2.2 17 | ddp-client@1.2.1 18 | ddp-common@1.2.2 19 | ddp-server@1.2.2 20 | deps@1.0.9 21 | diff-sequence@1.0.1 22 | ecmascript@0.1.6 23 | ecmascript-runtime@0.2.6 24 | ejson@1.0.7 25 | fastclick@1.0.7 26 | geojson-utils@1.0.4 27 | hot-code-push@1.0.0 28 | html-tools@1.0.5 29 | htmljs@1.0.5 30 | http@1.1.1 31 | id-map@1.0.4 32 | iron:controller@1.0.12 33 | iron:core@1.0.11 34 | iron:dynamic-template@1.0.12 35 | iron:layout@1.0.12 36 | iron:location@1.0.11 37 | iron:middleware-stack@1.0.11 38 | iron:router@1.0.12 39 | iron:url@1.0.11 40 | jquery@1.11.4 41 | launch-screen@1.0.4 42 | less@2.5.1 43 | livedata@1.0.15 44 | logging@1.0.8 45 | lookback:body-class@0.3.0 46 | meteor@1.1.10 47 | meteor-base@1.0.1 48 | minifiers@1.1.7 49 | minimongo@1.0.10 50 | mobile-experience@1.0.1 51 | mobile-status-bar@1.0.6 52 | mongo@1.1.3 53 | mongo-id@1.0.1 54 | npm-mongo@1.4.39_1 55 | observe-sequence@1.0.7 56 | ordered-dict@1.0.4 57 | package-version-parser@3.0.4 58 | practicalmeteor:chai@2.1.0_1 59 | practicalmeteor:loglevel@1.2.0_2 60 | promise@0.5.1 61 | random@1.0.5 62 | reactive-dict@1.1.3 63 | reactive-var@1.0.6 64 | reload@1.1.4 65 | retry@1.0.4 66 | routepolicy@1.0.6 67 | sanjo:jasmine@0.20.2 68 | sanjo:karma@3.0.2 69 | sanjo:long-running-child-process@1.1.3 70 | sanjo:meteor-files-helpers@1.2.0_1 71 | sanjo:meteor-version@1.0.0 72 | session@1.1.1 73 | spacebars@1.0.7 74 | spacebars-compiler@1.0.7 75 | standard-minifiers@1.0.2 76 | templating@1.1.5 77 | templating-tools@1.0.0 78 | tracker@1.0.9 79 | ui@1.0.8 80 | underscore@1.0.4 81 | url@1.0.5 82 | velocity:chokidar@1.2.0_1 83 | velocity:console-reporter@0.1.4 84 | velocity:core@0.10.8 85 | velocity:html-reporter@0.9.1 86 | velocity:meteor-internals@1.1.0_7 87 | velocity:meteor-stubs@1.1.1 88 | velocity:shim@0.1.0 89 | velocity:source-map-support@0.3.2_1 90 | webapp@1.2.3 91 | webapp-hashing@1.0.5 92 | -------------------------------------------------------------------------------- /test-app/tests/jasmine/client/integration/spec.coffee: -------------------------------------------------------------------------------- 1 | describe 'BodyClass', -> 2 | 3 | beforeEach waitForRouter 4 | 5 | describe 'Iron Router plugin', -> 6 | 7 | beforeEach -> 8 | Router.plugin 'bodyClasses' 9 | 10 | beforeEach (done) -> 11 | Router.go 'home' 12 | Tracker.afterFlush -> 13 | done() 14 | 15 | it 'adds the current template name as a class', (done) -> 16 | expect($('body')).toHaveClass 'home' 17 | 18 | Router.go 'sub' 19 | Tracker.afterFlush -> 20 | expect($('body')).toHaveClass 'sub' 21 | expect($('body')).not.toHaveClass 'home' 22 | done() 23 | 24 | it 'adds the current template and layout name as a class', (done) -> 25 | expect($('body')).toHaveClass 'home' 26 | 27 | Router.go 'layout' 28 | Tracker.afterFlush -> 29 | expect($('body')).toHaveClass 'defaultlayout layout' 30 | expect($('body')).not.toHaveClass 'home' 31 | done() 32 | 33 | describe 'run', -> 34 | 35 | beforeEach -> 36 | $('body').removeClass() 37 | 38 | Router.onAfterAction -> 39 | BodyClass.run() 40 | 41 | beforeEach (done) -> 42 | Router.go 'home' 43 | Tracker.afterFlush -> 44 | done() 45 | 46 | it 'adds the current template name as a class', (done) -> 47 | expect($('body')).toHaveClass 'home' 48 | 49 | Router.go 'sub' 50 | Tracker.afterFlush -> 51 | expect($('body')).toHaveClass 'sub' 52 | done() 53 | 54 | it 'adds the current layout name as a class', (done) -> 55 | 56 | Router.go 'layout' 57 | Tracker.afterFlush -> 58 | expect($('body')).toHaveClass 'defaultlayout' 59 | done() 60 | 61 | it 'adds the both current layout and template name as a class', (done) -> 62 | Router.go 'layout' 63 | Tracker.afterFlush -> 64 | expect($('body')).toHaveClass 'defaultlayout layout' 65 | done() 66 | 67 | describe 'cleanup', -> 68 | 69 | beforeEach (done) -> 70 | Router.go 'layout' 71 | Tracker.afterFlush -> 72 | done() 73 | 74 | it 'should clear old route classes on navigation', (done) -> 75 | expect($('body')).toHaveClass 'defaultlayout layout' 76 | 77 | Router.go 'home' 78 | 79 | Tracker.afterFlush -> 80 | expect($('body')).not.toHaveClass 'defaultlayout layout' 81 | done() 82 | -------------------------------------------------------------------------------- /body-class.js: -------------------------------------------------------------------------------- 1 | const FlowRouter = Package['kadira:flow-router'] ? Package['kadira:flow-router'].FlowRouter : false; 2 | 3 | const routeClasses = () => { 4 | let classes = []; 5 | 6 | if(Package['iron:router']){ 7 | let ctrl = Router.current(); 8 | classes = [ctrl._layout._template, ctrl.lookupTemplate()]; 9 | } 10 | 11 | if(Package['kadira:flow-router']) { 12 | // Gets a route's layout OR its group's layout OR its group's parent's layout 13 | // OR default layout in that order 14 | function getRouteLayout(route) { 15 | return route.options.layout 16 | || route.group && route.group.options.layout 17 | || route.group && route.group.parent && route.group.parent.options.layout 18 | || FlowRouter.defaultLayout; 19 | } 20 | 21 | let route = FlowRouter.current().route; 22 | classes = [route.name, getRouteLayout(route)]; 23 | } 24 | 25 | return _.chain(classes) 26 | .compact() 27 | .invoke('toLowerCase') 28 | .value(); 29 | }; 30 | 31 | BodyClass = { 32 | 33 | settings: { 34 | element: 'body', 35 | classes: [] 36 | }, 37 | 38 | config(opts) { 39 | _.extend(this.settings, opts); 40 | return this; 41 | }, 42 | 43 | add(fn) { 44 | if(!Match.test(fn, Match.OneOf(Array, String, Function))) { 45 | return console.warn('BodyClass: Argument to "add" must be an array, string or function!'); 46 | } 47 | 48 | if(Match.test(fn, Array)) { 49 | return fn.forEach(_.bind(BodyClass.add, BodyClass)); 50 | } 51 | 52 | if(!Match.test(fn, Function)) { 53 | return Meteor.startup(() => $(this.settings.element).addClass(fn)); 54 | } 55 | 56 | if(Match.test(fn, Function)) { 57 | Meteor.startup(() => { 58 | Tracker.autorun(() => { 59 | $(this.settings.element) 60 | .removeClass(fn._prev) 61 | .addClass(fn._prev = fn()); 62 | }); 63 | }); 64 | } 65 | }, 66 | 67 | run(opts = {}) { 68 | let classes = this.settings.classes.concat(routeClasses()); 69 | 70 | if(opts.classes) { 71 | classes = classes.concat(opts.classes); 72 | } 73 | 74 | $(this.settings.element).addClass(classes.join(' ')); 75 | }, 76 | 77 | remove(className) { 78 | check(className, String); 79 | $(this.settings.element).removeClass(className); 80 | }, 81 | 82 | cleanup() { 83 | BodyClass.remove(this.settings.classes.concat(routeClasses()).join(' ')); 84 | } 85 | }; 86 | 87 | if(Package['iron:router']) { 88 | 89 | Iron.Router.plugins.bodyClasses = (router, options) => { 90 | router.onAfterAction(() => { 91 | BodyClass.run(options); 92 | }, options); 93 | 94 | router.onStop(() => { 95 | BodyClass.cleanup(); 96 | }, options); 97 | }; 98 | } 99 | 100 | if(FlowRouter) { 101 | FlowRouter.triggers.enter([() => BodyClass.run()]); 102 | FlowRouter.triggers.exit([() => BodyClass.cleanup()]); 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lookback Body Classes for Meteor 2 | 3 | [![Circle CI](https://circleci.com/gh/lookback/meteor-bodyclass/tree/master.svg?style=svg)](https://circleci.com/gh/lookback/meteor-bodyclass/tree/master) 4 | 5 | This package automatically adds classes on the `body` element (default) for the names of the current template and layout used. It also reactively updates a class name returned from a function. 6 | 7 | Having classes for templates and layouts are great (if not vital) for scoping with CSS and similar. 8 | 9 | ## Install 10 | 11 | `lookback:body-class` is available on [Atmosphere](https://atmospherejs.com/lookback/body-class): 12 | 13 | meteor add lookback:body-class 14 | 15 | ## Usage 16 | 17 | The package exports the `BodyClass` namespace on the client. 18 | 19 | The `add` method adds a class to the body: 20 | ```js 21 | // Adds a string, string array or result from a function to the body element. 22 | 23 | BodyClass.add('some-class') 24 | // => body.some-class 25 | ``` 26 | 27 | ```js 28 | BodyClass.add(['some-class', 'other-class']) 29 | // => body.some-class.other-class 30 | ``` 31 | ```js 32 | Session.setDefault('state', 'foo'); 33 | 34 | BodyClass.add(function() { 35 | return Session.get('state'); 36 | }); 37 | // => body.foo 38 | 39 | Session.set('state', 'bar'); 40 | // => body.bar 41 | ``` 42 | 43 | ## Iron Router 44 | 45 | The `run` method adds the names of the current template and/or layout to the body, and `cleanup` removes them. Goes well with Iron Router's `onBeforeAction` and `onStop` hooks: 46 | 47 | ```js 48 | Router.route('home', { 49 | path: '/home' 50 | }); 51 | 52 | Router.route('foo', { 53 | path: '/foo', 54 | layoutTemplate: 'DefaultTemplate' 55 | }); 56 | 57 | Router.onBeforeAction(function() { 58 | BodyClass.run(); 59 | }); 60 | 61 | Router.onStop(function() { 62 | BodyClass.cleanup(); 63 | }); 64 | 65 | Router.go('home'); 66 | // => body.home 67 | 68 | Router.go('foo'); 69 | // => body.defaulttemplate.foo 70 | ``` 71 | 72 | This behavior is available as a nifty one-line-init Iron Router plugin: 73 | 74 | ```js 75 | Router.plugin('bodyClasses'); 76 | ``` 77 | 78 | ## Flow Router 79 | 80 | Triggers are automatically added for you, with: 81 | 82 | ```js 83 | FlowRouter.triggers.enter([() => BodyClass.run()]); 84 | FlowRouter.triggers.exit([() => BodyClass.cleanup()]); 85 | ``` 86 | 87 | For Flow Router, we use the *route name* and potentially the layout name for a template. Like this: 88 | 89 | ```js 90 | FlowRouter.route({ 91 | name: 'something', 92 | layout: 'MyLayout' 93 | }); 94 | ``` 95 | 96 | As of Flow Router 2.8.0, route groups also can specify options, and thus you can use the `layout` property there as well. 97 | 98 | ```js 99 | FlowRouter.group({ 100 | name: 'webapp', 101 | layout: 'WebAppLayout' 102 | }); 103 | ``` 104 | 105 | *Note that template and layout names are added in lower case.* 106 | 107 | ### Configure 108 | 109 | It's possible to configure the package with the following options: 110 | 111 | ```js 112 | BodyClass.config({ 113 | element: 'body' // What element to manipulate, defaults to 'body' 114 | classes: [] // What classes that *always* should be added 115 | }); 116 | ``` 117 | 118 | ## Tests 119 | 120 | This package is tested with the [`sanjo:jasmine`](https://github.com/Sanjo/meteor-jasmine/) package, bundled in a separate Meteor app in the `test-app` dir. To run tests: 121 | 122 | ```bash 123 | cd test-app 124 | meteor --test 125 | ``` 126 | 127 | ## Version history 128 | 129 | - `0.4.1` 130 | - If current route has no layout, get group's, then try parent's, then default layout 131 | - `0.4.0` 132 | - Add support for Flow Router. 133 | - Needs Meteor 1.2.0.2. 134 | - `0.1.0` - Initial publish. 135 | 136 | ## Contributions 137 | 138 | Contributions are welcome. Please open issues and/or file Pull Requests. 139 | 140 | ## License 141 | 142 | MIT. 143 | 144 | *** 145 | 146 | Made by [Lookback](http://lookback.io). 147 | --------------------------------------------------------------------------------