├── .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 |
10 | Home route
11 |
12 |
13 |
14 | Another route
15 |
16 |
17 |
18 | A layouted route
19 |
20 |
21 |
22 |
23 |
24 | Default layout
25 |
26 | {{ > yield }}
27 |
28 |
29 |
30 |
31 |
32 | Secondary layout
33 |
34 | {{ > yield }}
35 |
36 |
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 | [](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 |
--------------------------------------------------------------------------------