diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..6c07347 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,59 @@ +{ + "env": { + "browser": true, + "amd": true, + "node": true + }, + + "globals": { + "define": false, + "require": true, + + "alert": true, + "console": true, + "module": true, + + "describe": true, + "it": true, + "before": true, + "beforeEach": true, + "after": true, + "afterEach": true, + "expect": true, + "should": true, + + "Promise": true, // allow possibly overwriting Promise + }, + + "rules": { + "dot-notation": 0, // allow obj['somePropertyName'] + "new-cap": 0, // do not require 'new' keyword, allows i.e. "var promise = $.Deferred()" + "no-alert": 0, // disencourage alert() and confirm() + "no-console": 0, + "no-mixed-spaces-and-tabs": 1, + "semi-spacing": [2, {"before": false, "after": true}], + "no-spaced-func": 1, + "no-undef": 1, + "no-shadow": 1, + "no-trailing-spaces": 1, + "no-extra-parens": 1, + "no-underscore-dangle": 0, // allow _variableName + "no-new": 0, + "no-nested-ternary": 1, + "no-process-exit": 0, + + // requires local variable names to be used, but allows unused arguments + "no-unused-vars": [2, { "vars": "all", "args": "none" }], + "no-use-before-define": 0, + + // disable requirements for strict spacing rules in object properties or assignments + "key-spacing": 0, + "no-multi-spaces": 0, + + "quotes": 0, // allow both single and double quotes + "space-after-keywords": 1, + "space-infix-ops": 1, + "space-return-throw-case": 1, + "strict": 0 + } +} diff --git a/.jscs.json b/.jscs.json new file mode 100644 index 0000000..67a1ed3 --- /dev/null +++ b/.jscs.json @@ -0,0 +1,74 @@ +{ + "disallowImplicitTypeConversion": ["string"], + "disallowMixedSpacesAndTabs": true, + "disallowMultipleLineBreaks": true, + "disallowMultipleVarDecl": true, + "disallowNewlineBeforeBlockStatements": true, + "disallowMultipleLineStrings": true, + "disallowMultipleVarDecl": null, + "disallowSpaceAfterPrefixUnaryOperators": true, + "disallowSpacesInAnonymousFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInsideObjectBrackets": null, + "disallowSpacesInsideArrayBrackets": "all", + "disallowSpacesInsideParentheses": true, + "disallowTrailingWhitespace": true, + + "maximumLineLength": { + "value": 100, + "allowComments": true, + "allowRegex": true + }, + + "fileExtensions": [".js", ".jsx"], + + "requireCamelCaseOrUpperCaseIdentifiers": true, + "requireCommaBeforeLineBreak": true, + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireSpaceAfterBinaryOperators": true, + "requireSpaceAfterKeywords": [ + "do", + "for", + "if", + "else", + "switch", + "case", + "try", + "catch", + "void", + "while", + "with", + "return", + "typeof" + ], + "requireSpaceBeforeBinaryOperators": [ + "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", + "&=", "|=", "^=", "+=", + + "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", + "|", "^", "&&", "||", "===", "==", ">=", + "<=", "<", ">", "!=", "!==" + ], + "requireSpacesInConditionalExpression": true, + "requireSpaceBeforeBlockStatements": true, + "requireSpacesInForStatement": true, + "requireLineFeedAtFileEnd": true, + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + + "validateLineBreaks": "LF", + "validateIndentation": 4, + "validateQuoteMarks": { + "mark": "'", "escape": true + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eb09c34..56edd8d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,6 +15,8 @@ updated. Then you can edit `src/progressbar.js` and changes can be tested in browser. Edit `local-dev/main.js` to your testing needs. +Shorter way to do local development is running: ```npm run dev```. + ## General project stuff This package uses npm/node tools just in the developer environment. Grunt is used as a task runner @@ -122,3 +124,4 @@ animation customizations and possible even using different easings per animation * Automate tests so that testing is locally fast and CI runs tests with more browsers in Sauce Labs * Because of introducing text attribute, the library must modify CSS also. Provide user an option to make CSS them selves. * Keep **master** branch as a release branch so that new users can see the documentation for latest release instead of development version. +* Add some tasks to package.json aside with grunt tasks. I would want to move from Grunt to npm scripts totally but Sauce Labs tests are run with grunt. diff --git a/package.json b/package.json index 7e49508..e0a1136 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "progressbar.js", "version": "0.8.1-dev", - "description": "Beautiful and responsive progress bars with animated SVG paths", + "description": "Responsive and slick progress bars with animated SVG paths", "main": "src/main.js", "dependencies": { "shifty": "1.2.2" @@ -12,11 +12,14 @@ "chai": "^1.10.0", "chai-stats": "kimmobrunfeldt/chai-stats", "commander": "^2.4.0", + "concurrently": "^0.1.1", + "eslint": "^1.0.0", "grunt": "^0.4.5", "grunt-contrib-jshint": "^0.10.0", "grunt-contrib-uglify": "^0.6.0", "grunt-karma": "^0.9.0", "grunt-shell": "^1.1.1", + "jscs": "^2.0.0", "karma": "^0.12.24", "karma-bro": "^0.8.0", "karma-mocha": "^0.1.9", @@ -24,15 +27,21 @@ "lodash": "^2.4.1", "mocha": "^2.0.1", "mustache": "^0.8.2", + "node-static": "^0.7.7", "semver": "^4.1.0", "shelljs": "^0.3.0", + "sinon": "~1.14.1", "string": "^2.2.0", "testem": "^0.6.22", - "watchify": "^2.1.1", - "sinon": "~1.14.1" + "watchify": "^2.1.1" }, "scripts": { - "test": "grunt test" + "test": "grunt test", + "dev": "concurrent 'npm run serve' 'grunt watch' 'open http://localhost:8080'", + "serve": "static ./local-dev -c 0", + "lint": "./tools/lint.sh", + "jscs": "jscs ./src ./test", + "eslint": "eslint --ext .js ./src ./test" }, "repository": { "type": "git", @@ -43,7 +52,7 @@ "bugs": { "url": "https://github.com/kimmobrunfeldt/progressbar.js/issues" }, - "homepage": "https://github.com/kimmobrunfeldt/progressbar.js", + "homepage": "https://kimmobrunfeldt.github.io/progressbar.js/", "keywords": [ "progress", "bar", @@ -55,6 +64,8 @@ "radial", "line", "loading", - "loader" + "loader", + "semi-circle", + "indicator" ] -} \ No newline at end of file +} diff --git a/src/circle.js b/src/circle.js index 4e8c2bd..4b1b0fb 100644 --- a/src/circle.js +++ b/src/circle.js @@ -3,7 +3,6 @@ var Shape = require('./shape'); var utils = require('./utils'); - var Circle = function Circle(container, options) { // Use two arcs to form a circle // See this answer http://stackoverflow.com/a/10477334/1446092 diff --git a/src/line.js b/src/line.js index b738848..e4b248d 100644 --- a/src/line.js +++ b/src/line.js @@ -3,7 +3,6 @@ var Shape = require('./shape'); var utils = require('./utils'); - var Line = function Line(container, options) { this._pathTemplate = 'M 0,{center} L 100,{center}'; Shape.apply(this, arguments); diff --git a/src/path.js b/src/path.js index 6e5ffcb..17d5cd8 100644 --- a/src/path.js +++ b/src/path.js @@ -9,7 +9,6 @@ var EASING_ALIASES = { easeInOut: 'easeInOutCubic' }; - var Path = function Path(path, opts) { // Default parameters for animation opts = utils.extend({ @@ -57,7 +56,8 @@ Path.prototype.set = function set(progress) { if (utils.isFunction(step)) { var easing = this._easing(this._opts.easing); var values = this._calculateTo(progress, easing); - step(values, this._opts.shape||this, this._opts.attachment); + var reference = this._opts.shape || this; + step(values, reference, this._opts.attachment); } }; @@ -103,7 +103,8 @@ Path.prototype.animate = function animate(progress, opts, cb) { easing: shiftyEasing, step: function(state) { self.path.style.strokeDashoffset = state.offset; - opts.step(state, opts.shape||self, opts.attachment); + var reference = opts.shape || self; + opts.step(state, reference, opts.attachment); }, finish: function(state) { if (utils.isFunction(cb)) { diff --git a/src/semicircle.js b/src/semicircle.js index 41b379e..6b2bb9a 100644 --- a/src/semicircle.js +++ b/src/semicircle.js @@ -4,7 +4,6 @@ var Shape = require('./shape'); var Circle = require('./circle'); var utils = require('./utils'); - var SemiCircle = function SemiCircle(container, options) { // Use one arc to form a SemiCircle // See this answer http://stackoverflow.com/a/10477334/1446092 @@ -22,8 +21,12 @@ SemiCircle.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 50'); }; -SemiCircle.prototype._initializeTextElement = function _initializeTextElement(opts, container, element) { - if (opts.text.autoStyle) { +SemiCircle.prototype._initializeTextElement = function _initializeTextElement( + opts, + container, + element +) { + if (opts.text.style) { // Reset top style element.style.top = 'auto'; @@ -36,7 +39,6 @@ SemiCircle.prototype._initializeTextElement = function _initializeTextElement(op } }; - // Share functionality with Circle, just have different path SemiCircle.prototype._pathString = Circle.prototype._pathString; SemiCircle.prototype._trailString = Circle.prototype._trailString; diff --git a/src/shape.js b/src/shape.js index 823823b..ed0ca32 100644 --- a/src/shape.js +++ b/src/shape.js @@ -5,7 +5,6 @@ var utils = require('./utils'); var DESTROYED_ERROR = 'Object is destroyed'; - var Shape = function Shape(container, opts) { // Throw a better error if progress bars are not initialized with `new` // keyword @@ -19,7 +18,9 @@ var Shape = function Shape(container, opts) { // Line.prototype = new Shape(); // // We just want to set the prototype for Line. - if (arguments.length === 0) return; + if (arguments.length === 0) { + return; + } // Default parameters for progress bar creation this._opts = utils.extend({ @@ -73,20 +74,30 @@ var Shape = function Shape(container, opts) { }; Shape.prototype.animate = function animate(progress, opts, cb) { - if (this._progressPath === null) throw new Error(DESTROYED_ERROR); + if (this._progressPath === null) { + throw new Error(DESTROYED_ERROR) + }; + this._progressPath.animate(progress, opts, cb); }; Shape.prototype.stop = function stop() { - if (this._progressPath === null) throw new Error(DESTROYED_ERROR); + if (this._progressPath === null) { + throw new Error(DESTROYED_ERROR); + } + // Don't crash if stop is called inside step function - if (this._progressPath === undefined) return; + if (this._progressPath === undefined) { + return; + } this._progressPath.stop(); }; Shape.prototype.destroy = function destroy() { - if (this._progressPath === null) throw new Error(DESTROYED_ERROR); + if (this._progressPath === null) { + throw new Error(DESTROYED_ERROR); + } this.stop(); this.svg.parentNode.removeChild(this.svg); @@ -102,19 +113,29 @@ Shape.prototype.destroy = function destroy() { }; Shape.prototype.set = function set(progress) { - if (this._progressPath === null) throw new Error(DESTROYED_ERROR); + if (this._progressPath === null) { + throw new Error(DESTROYED_ERROR); + } + this._progressPath.set(progress); }; Shape.prototype.value = function value() { - if (this._progressPath === null) throw new Error(DESTROYED_ERROR); - if (this._progressPath === undefined) return 0; + if (this._progressPath === null) { + throw new Error(DESTROYED_ERROR); + } + + if (this._progressPath === undefined) { + return 0; + } return this._progressPath.value(); }; Shape.prototype.setText = function setText(text) { - if (this._progressPath === null) throw new Error(DESTROYED_ERROR); + if (this._progressPath === null) { + throw new Error(DESTROYED_ERROR); + } if (this.text === null) { // Create new text node @@ -166,8 +187,12 @@ Shape.prototype._createTrail = function _createTrail(opts) { var newOpts = utils.extend({}, opts); // Defaults for parameters which modify trail path - if (!newOpts.trailColor) newOpts.trailColor = '#eee'; - if (!newOpts.trailWidth) newOpts.trailWidth = newOpts.strokeWidth; + if (!newOpts.trailColor) { + newOpts.trailColor = '#eee'; + } + if (!newOpts.trailWidth) { + newOpts.trailWidth = newOpts.strokeWidth; + } newOpts.color = newOpts.trailColor; newOpts.strokeWidth = newOpts.trailWidth; diff --git a/src/square.js b/src/square.js index 3b3ba77..4e97eb4 100644 --- a/src/square.js +++ b/src/square.js @@ -6,7 +6,6 @@ var Shape = require('./shape'); var utils = require('./utils'); - var Square = function Square(container, options) { this._pathTemplate = 'M 0,{halfOfStrokeWidth}' + @@ -45,7 +44,7 @@ Square.prototype._trailString = function _trailString(opts) { width: w, strokeWidth: opts.strokeWidth, halfOfStrokeWidth: opts.strokeWidth / 2, - startMargin: (opts.strokeWidth / 2) - (opts.trailWidth / 2) + startMargin: opts.strokeWidth / 2 - opts.trailWidth / 2 }); }; diff --git a/src/utils.js b/src/utils.js index 4d450d2..0332394 100644 --- a/src/utils.js +++ b/src/utils.js @@ -73,7 +73,9 @@ function isArray(obj) { // Returns true if `obj` is object as in {a: 1, b: 2}, not if it's function or // array function isObject(obj) { - if (isArray(obj)) return false; + if (isArray(obj)) { + return false; + } var type = typeof obj; return type === 'object' && !!obj; diff --git a/test/path-behaviour.js b/test/path-behaviour.js index c303be7..1765400 100644 --- a/test/path-behaviour.js +++ b/test/path-behaviour.js @@ -3,11 +3,7 @@ var chaiStats = require('chai-stats'); chai.use(chaiStats); var expect = chai.expect; -var testUtils = require('./test-utils'); -var sinon = require('sinon'); - var PRECISION = 2; - var ANIM_PROP = { 'styleName': 'stroke-offset', 'scriptName': 'strokeOffset' @@ -17,12 +13,12 @@ var barOpts = { duration: 800, from: {strokeOffset: 0}, to: { strokeOffset: 0 }, - step: function (state, self, attachment) { + step: function(state, self, attachment) { attachment.setAttribute(ANIM_PROP.scriptName, state[ANIM_PROP.scriptName]); } }; -var createPath = function () { +function createPath() { var container = document.querySelector('body'), svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); @@ -35,12 +31,13 @@ var createPath = function () { svg.setAttribute('enable-background', '0 0 197 165.39555'); var attrs = { - "id": "progress-path", - "fill": "none", - "stroke": "#ccc", - "stroke-width":"15", - "stroke-miterlimit": "10", - "d": "m 31.7,160.3 c -15,-16.2 -24.2,-38 -24.2,-61.8 0,-50.3 40.7,-91 91,-91 50.3,0 91,40.7 91,91 0,23.9 -9.2,45.6 -24.2,61.8" + 'id': 'progress-path', + 'fill': 'none', + 'stroke': '#ccc', + 'stroke-width':'15', + 'stroke-miterlimit': '10', + 'd': 'm 31.7,160.3 c -15,-16.2 -24.2,-38 -24.2,-61.8 0,-50.3' + + ' 40.7,-91 91,-91 50.3,0 91,40.7 91,91 0,23.9 -9.2,45.6 -24.2,61.8' }; for (var i in attrs) { @@ -52,21 +49,21 @@ var createPath = function () { container.appendChild(svg); return { - "path": path, - "svg": svg + path: path, + svg: svg }; }; -var pathTests = function pathTests () { +function pathTests() { - it('step function should recieve a reference to ProgressBar as argument #2', function () { + it('step function should recieve a reference to ProgressBar as argument #2', function() { this.bar.animate(1, {duration: 500}); // we only care about the second arg, for each call so we need to manually // inspect them since we dont know what state would look like var ok = true; - for (var i =0; i < this.step.args.length; i++) { + for (var i = 0; i < this.step.args.length; i++) { if (this.step.args[i][1] !== this.bar) { ok = false; } @@ -75,14 +72,14 @@ var pathTests = function pathTests () { expect(ok).to.be.true(); }); - it('step function should recieve a reference to ProgressBar as argument #3', function () { + it('step function should recieve a reference to ProgressBar as argument #3', function() { this.bar.animate(1, {duration: 500}); // we only care about the third arg, for each call so we need to manually // inspect them since we dont know what state would look like var ok = true; - for (var i =0; i < this.step.args.length; i++) { + for (var i = 0; i < this.step.args.length; i++) { if (this.step.args[i][2] !== this.attachment) { ok = false; } @@ -124,7 +121,6 @@ var pathTests = function pathTests () { }); it('stop() should stop animation', function(done) { - var offset = testUtils.getComputedStyle(this.path, ANIM_PROP.scriptName); this.bar.animate(1, {duration: 1000}); var self = this; diff --git a/test/shape-behaviour.js b/test/shape-behaviour.js index 5493944..e204123 100644 --- a/test/shape-behaviour.js +++ b/test/shape-behaviour.js @@ -1,13 +1,10 @@ // Tests which test shared behaviour of all progress bar shapes - var chai = require('chai'); var chaiStats = require('chai-stats'); chai.use(chaiStats); var expect = chai.expect; -var testUtils = require('./test-utils'); - var PRECISION = 2; var TEXT_CLASS_NAME = '.progressbar-text'; @@ -65,11 +62,11 @@ var sharedTests = function sharedTests() { }, 1200); }); - it('step function should recieve a reference to ProgressBar as argument #2', function () { + it('step function should recieve a reference to ProgressBar as argument #2', function() { this.bar.animate(1, {duration: 600}); var allCallsHaveBar = true; - for (var i =0; i < this.step.args.length; i++) { + for (var i = 0; i < this.step.args.length; i++) { if (this.step.args[i][1] !== this.bar) { allCallsHaveBar = false; } @@ -78,11 +75,11 @@ var sharedTests = function sharedTests() { expect(allCallsHaveBar).to.be.true(); }); - it('step function should recieve a reference to attachment as argument #3', function () { + it('step function should recieve a reference to attachment as argument #3', function() { this.bar.animate(1, {duration: 600}); var allCallsHaveAttachment = true; - for (var i =0; i < this.step.args.length; i++) { + for (var i = 0; i < this.step.args.length; i++) { if (this.step.args[i][2] !== this.attachment) { allCallsHaveAttachment = false; } @@ -92,7 +89,6 @@ var sharedTests = function sharedTests() { }); it('stop() should stop animation', function(done) { - var offset = testUtils.getComputedStyle(this.bar.path, 'stroke-dashoffset'); this.bar.animate(1, {duration: 1000}); var self = this; diff --git a/test/test-all.js b/test/test-all.js index 39e5b9f..6463234 100644 --- a/test/test-all.js +++ b/test/test-all.js @@ -12,10 +12,9 @@ var expect = chai.expect; // https://github.com/mochajs/mocha/wiki/Shared-Behaviours var shapeTests = require('./shape-behaviour'); var pathTests = require('./path-behaviour'); -var ProgressBar = require("../src/main"); +var ProgressBar = require('../src/main'); var utils = require('../src/utils'); - var afterEachCase = function() { try { this.bar.destroy(); @@ -28,14 +27,14 @@ var afterEachCase = function() { var barOpts = { text: { value: 'Test' }, trailWidth: 1, - attachment: {"foo": "bar"} + attachment: {foo: 'bar'} }; describe('Line', function() { beforeEach(function() { // Append progress bar to body since adding a custom HTML and div // with Karma was not that trivial compared to Testem - barOpts.step = function (state, bar, attachment) {}; + barOpts.step = function(state, bar, attachment) {}; this.bar = new ProgressBar.Line('body', barOpts); this.attachment = this.bar._opts.attachment; this.step = sinon.spy(this.bar._opts, 'step'); @@ -46,10 +45,9 @@ describe('Line', function() { shapeTests(); }); - describe('Circle', function() { beforeEach(function() { - barOpts.step = function (state, bar, attachment) {}; + barOpts.step = function(state, bar, attachment) {}; this.bar = new ProgressBar.Circle('body', barOpts); this.attachment = this.bar._opts.attachment; this.step = sinon.spy(this.bar._opts, 'step'); @@ -62,7 +60,7 @@ describe('Circle', function() { describe('SemiCircle', function() { beforeEach(function() { - barOpts.step = function (state, bar, attachment) {}; + barOpts.step = function(state, bar, attachment) {}; this.bar = new ProgressBar.SemiCircle('body', barOpts); this.attachment = this.bar._opts.attachment; this.step = sinon.spy(this.bar._opts, 'step'); @@ -72,9 +70,9 @@ describe('SemiCircle', function() { shapeTests(); }); -describe('Path', function () { +describe('Path', function() { - beforeEach(function () { + beforeEach(function() { var svgView = pathTests.createPath(); this.svg = svgView.svg; this.path = svgView.path; @@ -86,7 +84,7 @@ describe('Path', function () { this.step = sinon.spy(this.bar._opts, 'step'); }); - afterEach(function () { + afterEach(function() { var container = this.svg.parentNode; container.removeChild(this.svg); this.svg = null; @@ -115,7 +113,6 @@ describe('utils', function() { }; utils.extend(first, second); - // These should normally override a's attributes expect(first.a.content).to.equal(undefined); expect(first.b).to.equal(second.b); diff --git a/tools/lint.sh b/tools/lint.sh new file mode 100755 index 0000000..a2137dc --- /dev/null +++ b/tools/lint.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# NOTE: Run this only from project root! + +# Run all lint commands and if one fails, exit non-zero return code + +EXIT_STATUS=0 +npm run jscs || EXIT_STATUS=$? +npm run eslint || EXIT_STATUS=$? + +exit $EXIT_STATUS diff --git a/scripts/release.js b/tools/release.js similarity index 100% rename from scripts/release.js rename to tools/release.js