Repository: kimmobrunfeldt/progressbar.js Branch: master Commit: 1f0ebf3afc00 Files: 35 Total size: 170.5 KB Directory structure: gitextract_1t5gj7f7/ ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .jscs.json ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── dist/ │ └── progressbar.js ├── docs/ │ ├── api/ │ │ ├── general.md │ │ ├── parameters.md │ │ ├── path.md │ │ └── shape.md │ ├── contributing.md │ ├── index.md │ └── pip-requirements.txt ├── karma.conf.js ├── local-dev/ │ ├── index.html │ ├── main.css │ └── main.js ├── mkdocs.yml ├── package.json ├── saucelabs-browsers.js ├── src/ │ ├── circle.js │ ├── line.js │ ├── main.js │ ├── path.js │ ├── semicircle.js │ ├── shape.js │ ├── square.js │ └── utils.js └── tools/ ├── lint.sh ├── release.js └── test.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # EditorConfig is awesome: http://EditorConfig.org # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true # Tab indentation (no size specified) [{*.js, *.css, *.scss}] indent_style = space indent_size = 4 # Matches the exact files package.json and .travis.yml [{*.json,.travis.yml,Gruntfile.js}] indent_style = space indent_size = 2 [Makefile] indent_style = tab indent_size = 4 ================================================ FILE: .eslintrc ================================================ { "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 } } ================================================ FILE: .gitignore ================================================ # Logs logs # Node *.log # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directory # Commenting this out is preferred by some people, see # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- node_modules # Users Environment Variables .lock-wscript # SASS .sass-cache *.css.map # OS X .DS_Store # IDEA editor .idea # Secrets as environment variables secrets.sh # Local dev bundle local-dev/bundle.js .env ================================================ FILE: .jscs.json ================================================ { "disallowImplicitTypeConversion": ["string"], "disallowMixedSpacesAndTabs": true, "disallowMultipleLineBreaks": 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 } } ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - '12' before_install: - npm install -g npm before_script: - npm install - npm install -g mocha grunt-cli testem script: - if [[ $TRAVIS_PULL_REQUEST != 'false' ]]; then testem ci -l phantomjs; fi - if [[ $TRAVIS_PULL_REQUEST == 'false' ]]; then grunt sauce; fi notifications: email: - kimmobrunfeldt+progressbar@gmail.com env: global: - secure: I/NAJpZ+wySjPYYLAqsHfMrueU0HbvOZx1FAJgxHtm3Nkz6/mzL+8ZW/FSi9/XaxkqJLaWAmCqvbVZsrw4qAfb2KKq/Ymf6eEhdTHfCJs+hLf32qM2FENS3LGPu7uJ8y1/7WKhV0VotBttB6AhaH/SNuZ4d8yBDGbriZtvnBT5c= - secure: SLhd3YYQXk5jD+ubnDatUAfeTGjSu6H3y30g7n0TryueNx5qBsBLAbeqAJ92JqZx3QEBAJ0vTJyhr66gJu9dG+G7US3O/KPjYXBHcDbVv0PEXibnWhxI8BLK8PIDQvGm2dwf4/6j0gDqNoWCnjBrhdgj481iR2z2cpR07A8Koew= ================================================ FILE: Gruntfile.js ================================================ var _ = require('lodash'); // Split array to smaller arrays containing n elements at max function groupToElements(array, n) { var lists = _.groupBy(array, function(element, index){ return Math.floor(index / n); }); return _.toArray(lists); } // Setup karma configuration dynamically to run browsers sequentially // https://github.com/karma-runner/karma-sauce-launcher/issues/8 var MAXIMUM_CONCURRENT_SAUCE = 3; var sauceBrowsers = require('./saucelabs-browsers'); var browserGroups = groupToElements(Object.keys(sauceBrowsers), MAXIMUM_CONCURRENT_SAUCE); var karmaConfig = { options: { configFile: 'karma.conf.js' } }; _.each(browserGroups, function(group, index) { karmaConfig['sauce' + index] = { browsers: group } }); module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), shell: { stageMinified: { options: { stdout: true, // If minified files do not change on release, // git command fails failOnError: false }, command: 'git add dist/progressbar.js dist/progressbar.min.js dist/progressbar.min.js.map' }, browserifyDevelopment: { options: { stdout: true }, command: 'browserify src/main.js -o dist/progressbar.js --standalone ProgressBar' }, browserifyMinified: { options: { stdout: true }, // The output file is minified by uglifyjs later. command: 'browserify src/main.js -o dist/progressbar.min.js --standalone ProgressBar' }, watchifyLocalDev: { options: { stdout: true }, command: 'watchify local-dev/main.js -o local-dev/bundle.js --debug --verbose' }, release: { options: { stdout: true }, command: function(bump) { bump = bump || 'patch'; return './tools/release.js ' + bump; } }, karma: { options: { stdout: true }, // This will run tests in Sauce Lab command: './node_modules/karma/bin/karma start' }, }, // Uglify must be run after browserify uglify: { progressbar: { options: { sourceMap: true }, files: { 'dist/progressbar.min.js': ['dist/progressbar.min.js'] } } }, // Calculated dynamically above karma: karmaConfig }); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-shell'); grunt.loadNpmTasks('grunt-karma'); grunt.registerTask('watch', function() { grunt.task.run(['shell:watchifyLocalDev']); }); grunt.registerTask('stageMinified', function() { grunt.task.run(['shell:stageMinified']); }); // Build distributables to dist folder grunt.registerTask('build', [ 'shell:browserifyDevelopment', 'shell:browserifyMinified', 'uglify:progressbar' ]); // Run multiple tests serially, but continue if one of them fails. // Adapted from http://stackoverflow.com/questions/16487681/gruntfile-getting-error-codes-from-programs-serially grunt.registerTask('sauce', function() { var done = this.async(); var tasks = {}; _.each(browserGroups, function(group, index) { tasks['karma:sauce' + index] = 0; }); var success = true; grunt.util.async.forEachSeries(Object.keys(tasks), function(task, next) { grunt.util.spawn({ // Use grunt to spawn grunt: true, args: [task], // Print to the same stdout opts: { stdio: 'inherit' } }, function(err, result, code) { tasks[task] = code; if (code !== 0) { success = false; } next(); }); }, function() { done(success); }); }); // Test, build, and release library to public grunt.registerTask('release', function(bump) { bump = bump || 'patch'; grunt.task.run([ 'build', 'stageMinified', 'shell:release:' + bump ]); }); }; ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Kimmo Brunfeldt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # ProgressBar.js
![Demo animation](docs/img/animation.gif)
Responsive and slick progress bars with animated SVG paths. Use built-in shapes or [create your own paths](http://progressbarjs.readthedocs.org/en/latest/api/path/). [Customize](http://progressbarjs.readthedocs.org/en/latest/api/parameters/#custom-animations) the animations as you wish. Documentation is [hosted at readthedocs.org](http://progressbarjs.readthedocs.org/en/latest/). **Shortcuts** - [Get started](http://progressbarjs.readthedocs.org/en/latest/) - [Demo & Examples](https://kimmobrunfeldt.github.io/progressbar.js) - [**Try** in JSFiddle](http://jsfiddle.net/kimmobrunfeldt/8xa87k31/392/) - [API documentation](http://progressbarjs.readthedocs.org/en/latest/api/shape/) - [Migration between versions](http://progressbarjs.readthedocs.org/en/latest/#migrations) - [react-progressbar.js](https://github.com/kimmobrunfeldt/react-progressbar.js) progress bars in React. ## Contributing See [documentation for contributors](http://progressbarjs.readthedocs.org/en/latest/contributing/). ## Thanks This project is a grateful recipient of the [Futurice Open Source sponsorship program](http://futurice.com/blog/sponsoring-free-time-open-source-activities?utm_source=github&utm_medium=spice&utm_campaign=progressbar). ================================================ FILE: bower.json ================================================ { "name": "progressbar.js", "main": "dist/progressbar.js", "homepage": "https://github.com/kimmobrunfeldt/progressbar.js", "authors": [ "Kimmo Brunfeldt " ], "description": "Responsive and slick progress bars with animated SVG paths", "moduleType": [ "amd", "globals" ], "keywords": [ "progress", "bar", "js", "svg", "circular", "circle", "pace", "radial", "line", "loading", "loader" ], "license": "MIT", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ] } ================================================ FILE: dist/progressbar.js ================================================ // ProgressBar.js 1.1.1 // https://kimmobrunfeldt.github.io/progressbar.js // License: MIT (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ProgressBar = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i * Build: `lodash modularize exports="npm" -o ./` * Copyright OpenJS Foundation and other contributors * Released under MIT license * Based on Underscore.js 1.8.3 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ /** Used as the size to enable large array optimizations. */ var LARGE_ARRAY_SIZE = 200; /** Used to stand-in for `undefined` hash values. */ var HASH_UNDEFINED = '__lodash_hash_undefined__'; /** Used to detect hot functions by number of calls within a span of milliseconds. */ var HOT_COUNT = 800, HOT_SPAN = 16; /** Used as references for various `Number` constants. */ var MAX_SAFE_INTEGER = 9007199254740991; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', arrayTag = '[object Array]', asyncTag = '[object AsyncFunction]', boolTag = '[object Boolean]', dateTag = '[object Date]', errorTag = '[object Error]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', mapTag = '[object Map]', numberTag = '[object Number]', nullTag = '[object Null]', objectTag = '[object Object]', proxyTag = '[object Proxy]', regexpTag = '[object RegExp]', setTag = '[object Set]', stringTag = '[object String]', undefinedTag = '[object Undefined]', weakMapTag = '[object WeakMap]'; var arrayBufferTag = '[object ArrayBuffer]', dataViewTag = '[object DataView]', float32Tag = '[object Float32Array]', float64Tag = '[object Float64Array]', int8Tag = '[object Int8Array]', int16Tag = '[object Int16Array]', int32Tag = '[object Int32Array]', uint8Tag = '[object Uint8Array]', uint8ClampedTag = '[object Uint8ClampedArray]', uint16Tag = '[object Uint16Array]', uint32Tag = '[object Uint32Array]'; /** * Used to match `RegExp` * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). */ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; /** Used to detect host constructors (Safari). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** Used to identify `toStringTag` values of typed arrays. */ var typedArrayTags = {}; typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** Detect free variable `exports`. */ var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; /** Detect free variable `module`. */ var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports`. */ var moduleExports = freeModule && freeModule.exports === freeExports; /** Detect free variable `process` from Node.js. */ var freeProcess = moduleExports && freeGlobal.process; /** Used to access faster Node.js helpers. */ var nodeUtil = (function() { try { // Use `util.types` for Node.js 10+. var types = freeModule && freeModule.require && freeModule.require('util').types; if (types) { return types; } // Legacy `process.binding('util')` for Node.js < 10. return freeProcess && freeProcess.binding && freeProcess.binding('util'); } catch (e) {} }()); /* Node.js helper references. */ var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; /** * A faster alternative to `Function#apply`, this function invokes `func` * with the `this` binding of `thisArg` and the arguments of `args`. * * @private * @param {Function} func The function to invoke. * @param {*} thisArg The `this` binding of `func`. * @param {Array} args The arguments to invoke `func` with. * @returns {*} Returns the result of `func`. */ function apply(func, thisArg, args) { switch (args.length) { case 0: return func.call(thisArg); case 1: return func.call(thisArg, args[0]); case 2: return func.call(thisArg, args[0], args[1]); case 3: return func.call(thisArg, args[0], args[1], args[2]); } return func.apply(thisArg, args); } /** * The base implementation of `_.times` without support for iteratee shorthands * or max array length checks. * * @private * @param {number} n The number of times to invoke `iteratee`. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the array of results. */ function baseTimes(n, iteratee) { var index = -1, result = Array(n); while (++index < n) { result[index] = iteratee(index); } return result; } /** * The base implementation of `_.unary` without support for storing metadata. * * @private * @param {Function} func The function to cap arguments for. * @returns {Function} Returns the new capped function. */ function baseUnary(func) { return function(value) { return func(value); }; } /** * Gets the value at `key` of `object`. * * @private * @param {Object} [object] The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function getValue(object, key) { return object == null ? undefined : object[key]; } /** * Creates a unary function that invokes `func` with its argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** Used for built-in method references. */ var arrayProto = Array.prototype, funcProto = Function.prototype, objectProto = Object.prototype; /** Used to detect overreaching core-js shims. */ var coreJsData = root['__core-js_shared__']; /** Used to resolve the decompiled source of functions. */ var funcToString = funcProto.toString; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** Used to detect methods masquerading as native. */ var maskSrcKey = (function() { var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); return uid ? ('Symbol(src)_1.' + uid) : ''; }()); /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var nativeObjectToString = objectProto.toString; /** Used to infer the `Object` constructor. */ var objectCtorString = funcToString.call(Object); /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** Built-in value references. */ var Buffer = moduleExports ? root.Buffer : undefined, Symbol = root.Symbol, Uint8Array = root.Uint8Array, allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, getPrototype = overArg(Object.getPrototypeOf, Object), objectCreate = Object.create, propertyIsEnumerable = objectProto.propertyIsEnumerable, splice = arrayProto.splice, symToStringTag = Symbol ? Symbol.toStringTag : undefined; var defineProperty = (function() { try { var func = getNative(Object, 'defineProperty'); func({}, '', {}); return func; } catch (e) {} }()); /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, nativeMax = Math.max, nativeNow = Date.now; /* Built-in method references that are verified to be native. */ var Map = getNative(root, 'Map'), nativeCreate = getNative(Object, 'create'); /** * The base implementation of `_.create` without support for assigning * properties to the created object. * * @private * @param {Object} proto The object to inherit from. * @returns {Object} Returns the new object. */ var baseCreate = (function() { function object() {} return function(proto) { if (!isObject(proto)) { return {}; } if (objectCreate) { return objectCreate(proto); } object.prototype = proto; var result = new object; object.prototype = undefined; return result; }; }()); /** * Creates a hash object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Hash(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the hash. * * @private * @name clear * @memberOf Hash */ function hashClear() { this.__data__ = nativeCreate ? nativeCreate(null) : {}; this.size = 0; } /** * Removes `key` and its value from the hash. * * @private * @name delete * @memberOf Hash * @param {Object} hash The hash to modify. * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function hashDelete(key) { var result = this.has(key) && delete this.__data__[key]; this.size -= result ? 1 : 0; return result; } /** * Gets the hash value for `key`. * * @private * @name get * @memberOf Hash * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function hashGet(key) { var data = this.__data__; if (nativeCreate) { var result = data[key]; return result === HASH_UNDEFINED ? undefined : result; } return hasOwnProperty.call(data, key) ? data[key] : undefined; } /** * Checks if a hash value for `key` exists. * * @private * @name has * @memberOf Hash * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function hashHas(key) { var data = this.__data__; return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); } /** * Sets the hash `key` to `value`. * * @private * @name set * @memberOf Hash * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the hash instance. */ function hashSet(key, value) { var data = this.__data__; this.size += this.has(key) ? 0 : 1; data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; return this; } // Add methods to `Hash`. Hash.prototype.clear = hashClear; Hash.prototype['delete'] = hashDelete; Hash.prototype.get = hashGet; Hash.prototype.has = hashHas; Hash.prototype.set = hashSet; /** * Creates an list cache object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function ListCache(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the list cache. * * @private * @name clear * @memberOf ListCache */ function listCacheClear() { this.__data__ = []; this.size = 0; } /** * Removes `key` and its value from the list cache. * * @private * @name delete * @memberOf ListCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function listCacheDelete(key) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { return false; } var lastIndex = data.length - 1; if (index == lastIndex) { data.pop(); } else { splice.call(data, index, 1); } --this.size; return true; } /** * Gets the list cache value for `key`. * * @private * @name get * @memberOf ListCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function listCacheGet(key) { var data = this.__data__, index = assocIndexOf(data, key); return index < 0 ? undefined : data[index][1]; } /** * Checks if a list cache value for `key` exists. * * @private * @name has * @memberOf ListCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function listCacheHas(key) { return assocIndexOf(this.__data__, key) > -1; } /** * Sets the list cache `key` to `value`. * * @private * @name set * @memberOf ListCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the list cache instance. */ function listCacheSet(key, value) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { ++this.size; data.push([key, value]); } else { data[index][1] = value; } return this; } // Add methods to `ListCache`. ListCache.prototype.clear = listCacheClear; ListCache.prototype['delete'] = listCacheDelete; ListCache.prototype.get = listCacheGet; ListCache.prototype.has = listCacheHas; ListCache.prototype.set = listCacheSet; /** * Creates a map cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function MapCache(entries) { var index = -1, length = entries == null ? 0 : entries.length; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } /** * Removes all key-value entries from the map. * * @private * @name clear * @memberOf MapCache */ function mapCacheClear() { this.size = 0; this.__data__ = { 'hash': new Hash, 'map': new (Map || ListCache), 'string': new Hash }; } /** * Removes `key` and its value from the map. * * @private * @name delete * @memberOf MapCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function mapCacheDelete(key) { var result = getMapData(this, key)['delete'](key); this.size -= result ? 1 : 0; return result; } /** * Gets the map value for `key`. * * @private * @name get * @memberOf MapCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function mapCacheGet(key) { return getMapData(this, key).get(key); } /** * Checks if a map value for `key` exists. * * @private * @name has * @memberOf MapCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function mapCacheHas(key) { return getMapData(this, key).has(key); } /** * Sets the map `key` to `value`. * * @private * @name set * @memberOf MapCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the map cache instance. */ function mapCacheSet(key, value) { var data = getMapData(this, key), size = data.size; data.set(key, value); this.size += data.size == size ? 0 : 1; return this; } // Add methods to `MapCache`. MapCache.prototype.clear = mapCacheClear; MapCache.prototype['delete'] = mapCacheDelete; MapCache.prototype.get = mapCacheGet; MapCache.prototype.has = mapCacheHas; MapCache.prototype.set = mapCacheSet; /** * Creates a stack cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Stack(entries) { var data = this.__data__ = new ListCache(entries); this.size = data.size; } /** * Removes all key-value entries from the stack. * * @private * @name clear * @memberOf Stack */ function stackClear() { this.__data__ = new ListCache; this.size = 0; } /** * Removes `key` and its value from the stack. * * @private * @name delete * @memberOf Stack * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function stackDelete(key) { var data = this.__data__, result = data['delete'](key); this.size = data.size; return result; } /** * Gets the stack value for `key`. * * @private * @name get * @memberOf Stack * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function stackGet(key) { return this.__data__.get(key); } /** * Checks if a stack value for `key` exists. * * @private * @name has * @memberOf Stack * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function stackHas(key) { return this.__data__.has(key); } /** * Sets the stack `key` to `value`. * * @private * @name set * @memberOf Stack * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the stack cache instance. */ function stackSet(key, value) { var data = this.__data__; if (data instanceof ListCache) { var pairs = data.__data__; if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { pairs.push([key, value]); this.size = ++data.size; return this; } data = this.__data__ = new MapCache(pairs); } data.set(key, value); this.size = data.size; return this; } // Add methods to `Stack`. Stack.prototype.clear = stackClear; Stack.prototype['delete'] = stackDelete; Stack.prototype.get = stackGet; Stack.prototype.has = stackHas; Stack.prototype.set = stackSet; /** * Creates an array of the enumerable property names of the array-like `value`. * * @private * @param {*} value The value to query. * @param {boolean} inherited Specify returning inherited property names. * @returns {Array} Returns the array of property names. */ function arrayLikeKeys(value, inherited) { var isArr = isArray(value), isArg = !isArr && isArguments(value), isBuff = !isArr && !isArg && isBuffer(value), isType = !isArr && !isArg && !isBuff && isTypedArray(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes(value.length, String) : [], length = result.length; for (var key in value) { if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && ( // Safari 9 has enumerable `arguments.length` in strict mode. key == 'length' || // Node.js 0.10 has enumerable non-index properties on buffers. (isBuff && (key == 'offset' || key == 'parent')) || // PhantomJS 2 has enumerable non-index properties on typed arrays. (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || // Skip index properties. isIndex(key, length) ))) { result.push(key); } } return result; } /** * This function is like `assignValue` except that it doesn't assign * `undefined` values. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignMergeValue(object, key, value) { if ((value !== undefined && !eq(object[key], value)) || (value === undefined && !(key in object))) { baseAssignValue(object, key, value); } } /** * Assigns `value` to `key` of `object` if the existing value is not equivalent * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignValue(object, key, value) { var objValue = object[key]; if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { baseAssignValue(object, key, value); } } /** * Gets the index at which the `key` is found in `array` of key-value pairs. * * @private * @param {Array} array The array to inspect. * @param {*} key The key to search for. * @returns {number} Returns the index of the matched value, else `-1`. */ function assocIndexOf(array, key) { var length = array.length; while (length--) { if (eq(array[length][0], key)) { return length; } } return -1; } /** * The base implementation of `assignValue` and `assignMergeValue` without * value checks. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function baseAssignValue(object, key, value) { if (key == '__proto__' && defineProperty) { defineProperty(object, key, { 'configurable': true, 'enumerable': true, 'value': value, 'writable': true }); } else { object[key] = value; } } /** * The base implementation of `baseForOwn` which iterates over `object` * properties returned by `keysFunc` and invokes `iteratee` for each property. * Iteratee functions may exit iteration early by explicitly returning `false`. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ var baseFor = createBaseFor(); /** * The base implementation of `getTag` without fallbacks for buggy environments. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ function baseGetTag(value) { if (value == null) { return value === undefined ? undefinedTag : nullTag; } return (symToStringTag && symToStringTag in Object(value)) ? getRawTag(value) : objectToString(value); } /** * The base implementation of `_.isArguments`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, */ function baseIsArguments(value) { return isObjectLike(value) && baseGetTag(value) == argsTag; } /** * The base implementation of `_.isNative` without bad shim checks. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. */ function baseIsNative(value) { if (!isObject(value) || isMasked(value)) { return false; } var pattern = isFunction(value) ? reIsNative : reIsHostCtor; return pattern.test(toSource(value)); } /** * The base implementation of `_.isTypedArray` without Node.js optimizations. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. */ function baseIsTypedArray(value) { return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; } /** * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function baseKeysIn(object) { if (!isObject(object)) { return nativeKeysIn(object); } var isProto = isPrototype(object), result = []; for (var key in object) { if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { result.push(key); } } return result; } /** * The base implementation of `_.merge` without support for multiple sources. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {number} srcIndex The index of `source`. * @param {Function} [customizer] The function to customize merged values. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. */ function baseMerge(object, source, srcIndex, customizer, stack) { if (object === source) { return; } baseFor(source, function(srcValue, key) { stack || (stack = new Stack); if (isObject(srcValue)) { baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); } else { var newValue = customizer ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack) : undefined; if (newValue === undefined) { newValue = srcValue; } assignMergeValue(object, key, newValue); } }, keysIn); } /** * A specialized version of `baseMerge` for arrays and objects which performs * deep merges and tracks traversed objects enabling objects with circular * references to be merged. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {string} key The key of the value to merge. * @param {number} srcIndex The index of `source`. * @param {Function} mergeFunc The function to merge values. * @param {Function} [customizer] The function to customize assigned values. * @param {Object} [stack] Tracks traversed source values and their merged * counterparts. */ function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { var objValue = safeGet(object, key), srcValue = safeGet(source, key), stacked = stack.get(srcValue); if (stacked) { assignMergeValue(object, key, stacked); return; } var newValue = customizer ? customizer(objValue, srcValue, (key + ''), object, source, stack) : undefined; var isCommon = newValue === undefined; if (isCommon) { var isArr = isArray(srcValue), isBuff = !isArr && isBuffer(srcValue), isTyped = !isArr && !isBuff && isTypedArray(srcValue); newValue = srcValue; if (isArr || isBuff || isTyped) { if (isArray(objValue)) { newValue = objValue; } else if (isArrayLikeObject(objValue)) { newValue = copyArray(objValue); } else if (isBuff) { isCommon = false; newValue = cloneBuffer(srcValue, true); } else if (isTyped) { isCommon = false; newValue = cloneTypedArray(srcValue, true); } else { newValue = []; } } else if (isPlainObject(srcValue) || isArguments(srcValue)) { newValue = objValue; if (isArguments(objValue)) { newValue = toPlainObject(objValue); } else if (!isObject(objValue) || isFunction(objValue)) { newValue = initCloneObject(srcValue); } } else { isCommon = false; } } if (isCommon) { // Recursively merge objects and arrays (susceptible to call stack limits). stack.set(srcValue, newValue); mergeFunc(newValue, srcValue, srcIndex, customizer, stack); stack['delete'](srcValue); } assignMergeValue(object, key, newValue); } /** * The base implementation of `_.rest` which doesn't validate or coerce arguments. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @returns {Function} Returns the new function. */ function baseRest(func, start) { return setToString(overRest(func, start, identity), func + ''); } /** * The base implementation of `setToString` without support for hot loop shorting. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var baseSetToString = !defineProperty ? identity : function(func, string) { return defineProperty(func, 'toString', { 'configurable': true, 'enumerable': false, 'value': constant(string), 'writable': true }); }; /** * Creates a clone of `buffer`. * * @private * @param {Buffer} buffer The buffer to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Buffer} Returns the cloned buffer. */ function cloneBuffer(buffer, isDeep) { if (isDeep) { return buffer.slice(); } var length = buffer.length, result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); buffer.copy(result); return result; } /** * Creates a clone of `arrayBuffer`. * * @private * @param {ArrayBuffer} arrayBuffer The array buffer to clone. * @returns {ArrayBuffer} Returns the cloned array buffer. */ function cloneArrayBuffer(arrayBuffer) { var result = new arrayBuffer.constructor(arrayBuffer.byteLength); new Uint8Array(result).set(new Uint8Array(arrayBuffer)); return result; } /** * Creates a clone of `typedArray`. * * @private * @param {Object} typedArray The typed array to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned typed array. */ function cloneTypedArray(typedArray, isDeep) { var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); } /** * Copies the values of `source` to `array`. * * @private * @param {Array} source The array to copy values from. * @param {Array} [array=[]] The array to copy values to. * @returns {Array} Returns `array`. */ function copyArray(source, array) { var index = -1, length = source.length; array || (array = Array(length)); while (++index < length) { array[index] = source[index]; } return array; } /** * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. * @param {Array} props The property identifiers to copy. * @param {Object} [object={}] The object to copy properties to. * @param {Function} [customizer] The function to customize copied values. * @returns {Object} Returns `object`. */ function copyObject(source, props, object, customizer) { var isNew = !object; object || (object = {}); var index = -1, length = props.length; while (++index < length) { var key = props[index]; var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined; if (newValue === undefined) { newValue = source[key]; } if (isNew) { baseAssignValue(object, key, newValue); } else { assignValue(object, key, newValue); } } return object; } /** * Creates a function like `_.assign`. * * @private * @param {Function} assigner The function to assign values. * @returns {Function} Returns the new assigner function. */ function createAssigner(assigner) { return baseRest(function(object, sources) { var index = -1, length = sources.length, customizer = length > 1 ? sources[length - 1] : undefined, guard = length > 2 ? sources[2] : undefined; customizer = (assigner.length > 3 && typeof customizer == 'function') ? (length--, customizer) : undefined; if (guard && isIterateeCall(sources[0], sources[1], guard)) { customizer = length < 3 ? undefined : customizer; length = 1; } object = Object(object); while (++index < length) { var source = sources[index]; if (source) { assigner(object, source, index, customizer); } } return object; }); } /** * Creates a base function for methods like `_.forIn` and `_.forOwn`. * * @private * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {Function} Returns the new base function. */ function createBaseFor(fromRight) { return function(object, iteratee, keysFunc) { var index = -1, iterable = Object(object), props = keysFunc(object), length = props.length; while (length--) { var key = props[fromRight ? length : ++index]; if (iteratee(iterable[key], key, iterable) === false) { break; } } return object; }; } /** * Gets the data for `map`. * * @private * @param {Object} map The map to query. * @param {string} key The reference key. * @returns {*} Returns the map data. */ function getMapData(map, key) { var data = map.__data__; return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map; } /** * Gets the native function at `key` of `object`. * * @private * @param {Object} object The object to query. * @param {string} key The key of the method to get. * @returns {*} Returns the function if it's native, else `undefined`. */ function getNative(object, key) { var value = getValue(object, key); return baseIsNative(value) ? value : undefined; } /** * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. * * @private * @param {*} value The value to query. * @returns {string} Returns the raw `toStringTag`. */ function getRawTag(value) { var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag]; try { value[symToStringTag] = undefined; var unmasked = true; } catch (e) {} var result = nativeObjectToString.call(value); if (unmasked) { if (isOwn) { value[symToStringTag] = tag; } else { delete value[symToStringTag]; } } return result; } /** * Initializes an object clone. * * @private * @param {Object} object The object to clone. * @returns {Object} Returns the initialized clone. */ function initCloneObject(object) { return (typeof object.constructor == 'function' && !isPrototype(object)) ? baseCreate(getPrototype(object)) : {}; } /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ function isIndex(value, length) { var type = typeof value; length = length == null ? MAX_SAFE_INTEGER : length; return !!length && (type == 'number' || (type != 'symbol' && reIsUint.test(value))) && (value > -1 && value % 1 == 0 && value < length); } /** * Checks if the given arguments are from an iteratee call. * * @private * @param {*} value The potential iteratee value argument. * @param {*} index The potential iteratee index or key argument. * @param {*} object The potential iteratee object argument. * @returns {boolean} Returns `true` if the arguments are from an iteratee call, * else `false`. */ function isIterateeCall(value, index, object) { if (!isObject(object)) { return false; } var type = typeof index; if (type == 'number' ? (isArrayLike(object) && isIndex(index, object.length)) : (type == 'string' && index in object) ) { return eq(object[index], value); } return false; } /** * Checks if `value` is suitable for use as unique object key. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is suitable, else `false`. */ function isKeyable(value) { var type = typeof value; return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') ? (value !== '__proto__') : (value === null); } /** * Checks if `func` has its source masked. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` is masked, else `false`. */ function isMasked(func) { return !!maskSrcKey && (maskSrcKey in func); } /** * Checks if `value` is likely a prototype object. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. */ function isPrototype(value) { var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; return value === proto; } /** * This function is like * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * except that it includes inherited enumerable properties. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function nativeKeysIn(object) { var result = []; if (object != null) { for (var key in Object(object)) { result.push(key); } } return result; } /** * Converts `value` to a string using `Object.prototype.toString`. * * @private * @param {*} value The value to convert. * @returns {string} Returns the converted string. */ function objectToString(value) { return nativeObjectToString.call(value); } /** * A specialized version of `baseRest` which transforms the rest array. * * @private * @param {Function} func The function to apply a rest parameter to. * @param {number} [start=func.length-1] The start position of the rest parameter. * @param {Function} transform The rest array transform. * @returns {Function} Returns the new function. */ function overRest(func, start, transform) { start = nativeMax(start === undefined ? (func.length - 1) : start, 0); return function() { var args = arguments, index = -1, length = nativeMax(args.length - start, 0), array = Array(length); while (++index < length) { array[index] = args[start + index]; } index = -1; var otherArgs = Array(start + 1); while (++index < start) { otherArgs[index] = args[index]; } otherArgs[start] = transform(array); return apply(func, this, otherArgs); }; } /** * Gets the value at `key`, unless `key` is "__proto__" or "constructor". * * @private * @param {Object} object The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function safeGet(object, key) { if (key === 'constructor' && typeof object[key] === 'function') { return; } if (key == '__proto__') { return; } return object[key]; } /** * Sets the `toString` method of `func` to return `string`. * * @private * @param {Function} func The function to modify. * @param {Function} string The `toString` result. * @returns {Function} Returns `func`. */ var setToString = shortOut(baseSetToString); /** * Creates a function that'll short out and invoke `identity` instead * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` * milliseconds. * * @private * @param {Function} func The function to restrict. * @returns {Function} Returns the new shortable function. */ function shortOut(func) { var count = 0, lastCalled = 0; return function() { var stamp = nativeNow(), remaining = HOT_SPAN - (stamp - lastCalled); lastCalled = stamp; if (remaining > 0) { if (++count >= HOT_COUNT) { return arguments[0]; } } else { count = 0; } return func.apply(undefined, arguments); }; } /** * Converts `func` to its source code. * * @private * @param {Function} func The function to convert. * @returns {string} Returns the source code. */ function toSource(func) { if (func != null) { try { return funcToString.call(func); } catch (e) {} try { return (func + ''); } catch (e) {} } return ''; } /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'a': 1 }; * var other = { 'a': 1 }; * * _.eq(object, object); * // => true * * _.eq(object, other); * // => false * * _.eq('a', 'a'); * // => true * * _.eq('a', Object('a')); * // => false * * _.eq(NaN, NaN); * // => true */ function eq(value, other) { return value === other || (value !== value && other !== other); } /** * Checks if `value` is likely an `arguments` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, * else `false`. * @example * * _.isArguments(function() { return arguments; }()); * // => true * * _.isArguments([1, 2, 3]); * // => false */ var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee'); }; /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ var isArray = Array.isArray; /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * _.isArrayLike([1, 2, 3]); * // => true * * _.isArrayLike(document.body.children); * // => true * * _.isArrayLike('abc'); * // => true * * _.isArrayLike(_.noop); * // => false */ function isArrayLike(value) { return value != null && isLength(value.length) && !isFunction(value); } /** * This method is like `_.isArrayLike` except that it also checks if `value` * is an object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array-like object, * else `false`. * @example * * _.isArrayLikeObject([1, 2, 3]); * // => true * * _.isArrayLikeObject(document.body.children); * // => true * * _.isArrayLikeObject('abc'); * // => false * * _.isArrayLikeObject(_.noop); * // => false */ function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value); } /** * Checks if `value` is a buffer. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. * @example * * _.isBuffer(new Buffer(2)); * // => true * * _.isBuffer(new Uint8Array(2)); * // => false */ var isBuffer = nativeIsBuffer || stubFalse; /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { if (!isObject(value)) { return false; } // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 9 which returns 'object' for typed arrays and other constructors. var tag = baseGetTag(value); return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; } /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function'); } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return value != null && typeof value == 'object'; } /** * Checks if `value` is a plain object, that is, an object created by the * `Object` constructor or one with a `[[Prototype]]` of `null`. * * @static * @memberOf _ * @since 0.8.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. * @example * * function Foo() { * this.a = 1; * } * * _.isPlainObject(new Foo); * // => false * * _.isPlainObject([1, 2, 3]); * // => false * * _.isPlainObject({ 'x': 0, 'y': 0 }); * // => true * * _.isPlainObject(Object.create(null)); * // => true */ function isPlainObject(value) { if (!isObjectLike(value) || baseGetTag(value) != objectTag) { return false; } var proto = getPrototype(value); if (proto === null) { return true; } var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString; } /** * Checks if `value` is classified as a typed array. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. * @example * * _.isTypedArray(new Uint8Array); * // => true * * _.isTypedArray([]); * // => false */ var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; /** * Converts `value` to a plain object flattening inherited enumerable string * keyed properties of `value` to own properties of the plain object. * * @static * @memberOf _ * @since 3.0.0 * @category Lang * @param {*} value The value to convert. * @returns {Object} Returns the converted plain object. * @example * * function Foo() { * this.b = 2; * } * * Foo.prototype.c = 3; * * _.assign({ 'a': 1 }, new Foo); * // => { 'a': 1, 'b': 2 } * * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); * // => { 'a': 1, 'b': 2, 'c': 3 } */ function toPlainObject(value) { return copyObject(value, keysIn(value)); } /** * Creates an array of the own and inherited enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @since 3.0.0 * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keysIn(new Foo); * // => ['a', 'b', 'c'] (iteration order is not guaranteed) */ function keysIn(object) { return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); } /** * This method is like `_.assign` except that it recursively merges own and * inherited enumerable string keyed properties of source objects into the * destination object. Source properties that resolve to `undefined` are * skipped if a destination value exists. Array and plain object properties * are merged recursively. Other objects and value types are overridden by * assignment. Source objects are applied from left to right. Subsequent * sources overwrite property assignments of previous sources. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 0.5.0 * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @example * * var object = { * 'a': [{ 'b': 2 }, { 'd': 4 }] * }; * * var other = { * 'a': [{ 'c': 3 }, { 'e': 5 }] * }; * * _.merge(object, other); * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } */ var merge = createAssigner(function(object, source, srcIndex) { baseMerge(object, source, srcIndex); }); /** * Creates a function that returns `value`. * * @static * @memberOf _ * @since 2.4.0 * @category Util * @param {*} value The value to return from the new function. * @returns {Function} Returns the new constant function. * @example * * var objects = _.times(2, _.constant({ 'a': 1 })); * * console.log(objects); * // => [{ 'a': 1 }, { 'a': 1 }] * * console.log(objects[0] === objects[1]); * // => true */ function constant(value) { return function() { return value; }; } /** * This method returns the first argument it receives. * * @static * @since 0.1.0 * @memberOf _ * @category Util * @param {*} value Any value. * @returns {*} Returns `value`. * @example * * var object = { 'a': 1 }; * * console.log(_.identity(object) === object); * // => true */ function identity(value) { return value; } /** * This method returns `false`. * * @static * @memberOf _ * @since 4.13.0 * @category Util * @returns {boolean} Returns `false`. * @example * * _.times(2, _.stubFalse); * // => [false, false] */ function stubFalse() { return false; } module.exports = merge; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],2:[function(require,module,exports){ /*! For license information please see shifty.js.LICENSE.txt */ !function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define("shifty",[],n):"object"==typeof exports?exports.shifty=n():t.shifty=n()}(self,(function(){return function(){"use strict";var t={720:function(t,n,e){e.r(n),e.d(n,{Scene:function(){return sn},Tweenable:function(){return kt},interpolate:function(){return nn},processTweens:function(){return dt},setBezierFunction:function(){return $},shouldScheduleUpdate:function(){return bt},tween:function(){return Pt},unsetBezierFunction:function(){return L}});var r={};e.r(r),e.d(r,{bounce:function(){return D},bouncePast:function(){return q},easeFrom:function(){return B},easeFromTo:function(){return Q},easeInBack:function(){return E},easeInCirc:function(){return j},easeInCubic:function(){return c},easeInExpo:function(){return w},easeInOutBack:function(){return T},easeInOutCirc:function(){return P},easeInOutCubic:function(){return l},easeInOutExpo:function(){return S},easeInOutQuad:function(){return s},easeInOutQuart:function(){return v},easeInOutQuint:function(){return d},easeInOutSine:function(){return b},easeInQuad:function(){return u},easeInQuart:function(){return h},easeInQuint:function(){return y},easeInSine:function(){return g},easeOutBack:function(){return A},easeOutBounce:function(){return M},easeOutCirc:function(){return k},easeOutCubic:function(){return f},easeOutExpo:function(){return O},easeOutQuad:function(){return a},easeOutQuart:function(){return p},easeOutQuint:function(){return _},easeOutSine:function(){return m},easeTo:function(){return N},elastic:function(){return I},linear:function(){return o},swingFrom:function(){return x},swingFromTo:function(){return F},swingTo:function(){return C}});var i={};e.r(i),e.d(i,{afterTween:function(){return Jt},beforeTween:function(){return Ht},doesApply:function(){return Wt},tweenCreated:function(){return Gt}});var o=function(t){return t},u=function(t){return Math.pow(t,2)},a=function(t){return-(Math.pow(t-1,2)-1)},s=function(t){return(t/=.5)<1?.5*Math.pow(t,2):-.5*((t-=2)*t-2)},c=function(t){return Math.pow(t,3)},f=function(t){return Math.pow(t-1,3)+1},l=function(t){return(t/=.5)<1?.5*Math.pow(t,3):.5*(Math.pow(t-2,3)+2)},h=function(t){return Math.pow(t,4)},p=function(t){return-(Math.pow(t-1,4)-1)},v=function(t){return(t/=.5)<1?.5*Math.pow(t,4):-.5*((t-=2)*Math.pow(t,3)-2)},y=function(t){return Math.pow(t,5)},_=function(t){return Math.pow(t-1,5)+1},d=function(t){return(t/=.5)<1?.5*Math.pow(t,5):.5*(Math.pow(t-2,5)+2)},g=function(t){return 1-Math.cos(t*(Math.PI/2))},m=function(t){return Math.sin(t*(Math.PI/2))},b=function(t){return-.5*(Math.cos(Math.PI*t)-1)},w=function(t){return 0===t?0:Math.pow(2,10*(t-1))},O=function(t){return 1===t?1:1-Math.pow(2,-10*t)},S=function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},j=function(t){return-(Math.sqrt(1-t*t)-1)},k=function(t){return Math.sqrt(1-Math.pow(t-1,2))},P=function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},M=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},E=function(t){var n=1.70158;return t*t*((n+1)*t-n)},A=function(t){var n=1.70158;return(t-=1)*t*((n+1)*t+n)+1},T=function(t){var n=1.70158;return(t/=.5)<1?t*t*((1+(n*=1.525))*t-n)*.5:.5*((t-=2)*t*((1+(n*=1.525))*t+n)+2)},I=function(t){return-1*Math.pow(4,-8*t)*Math.sin((6*t-1)*(2*Math.PI)/2)+1},F=function(t){var n=1.70158;return(t/=.5)<1?t*t*((1+(n*=1.525))*t-n)*.5:.5*((t-=2)*t*((1+(n*=1.525))*t+n)+2)},x=function(t){var n=1.70158;return t*t*((n+1)*t-n)},C=function(t){var n=1.70158;return(t-=1)*t*((n+1)*t+n)+1},D=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},q=function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?2-(7.5625*(t-=1.5/2.75)*t+.75):t<2.5/2.75?2-(7.5625*(t-=2.25/2.75)*t+.9375):2-(7.5625*(t-=2.625/2.75)*t+.984375)},Q=function(t){return(t/=.5)<1?.5*Math.pow(t,4):-.5*((t-=2)*Math.pow(t,3)-2)},B=function(t){return Math.pow(t,4)},N=function(t){return Math.pow(t,.25)};function R(t,n,e,r,i,o){var u,a,s,c,f,l=0,h=0,p=0,v=function(t){return((l*t+h)*t+p)*t},y=function(t){return(3*l*t+2*h)*t+p},_=function(t){return t>=0?t:0-t};return l=1-(p=3*n)-(h=3*(r-n)-p),s=1-(f=3*e)-(c=3*(i-e)-f),u=t,a=function(t){return 1/(200*t)}(o),function(t){return((s*t+c)*t+f)*t}(function(t,n){var e,r,i,o,u,a;for(i=t,a=0;a<8;a++){if(o=v(i)-t,_(o)(r=1))return r;for(;eo?e=i:r=i,i=.5*(r-e)+e}return i}(u,a))}var z,U=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:.25,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:.25,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:.75,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:.75;return function(i){return R(i,t,n,e,r,1)}},$=function(t,n,e,r,i){var o=U(n,e,r,i);return o.displayName=t,o.x1=n,o.y1=e,o.x2=r,o.y2=i,kt.formulas[t]=o},L=function(t){return delete kt.formulas[t]};function V(t,n){if(!(t instanceof n))throw new TypeError("Cannot call a class as a function")}function W(t,n){for(var e=0;et.length)&&(n=t.length);for(var e=0,r=new Array(n);ea?a:n;t._hasEnded=s>=a;var c=o-(a-s),f=t._filters.length>0;if(t._hasEnded)return t._render(u,t._data,c),t.stop(!0);f&&t._applyFilter(ut),s1&&void 0!==arguments[1]?arguments[1]:et,e=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(Array.isArray(n)){var r=U.apply(void 0,H(n));return r}var i=G(n);if(vt[n])return vt[n];if(i===ct||i===st)for(var o in t)e[o]=n;else for(var u in t)e[u]=n[u]||et;return e},St=function(t){t===ht?(ht=t._next)?ht._previous=null:pt=null:t===pt?(pt=t._previous)?pt._next=null:ht=null:(tt=t._previous,nt=t._next,tt._next=nt,nt._previous=tt),t._previous=t._next=null},jt="function"==typeof Promise?Promise:null;z=Symbol.toStringTag;var kt=function(){function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;V(this,t),Y(this,z,"Promise"),this._config={},this._data={},this._delay=0,this._filters=[],this._next=null,this._previous=null,this._timestamp=null,this._hasEnded=!1,this._resolve=null,this._reject=null,this._currentState=n||{},this._originalState={},this._targetState={},this._start=lt,this._render=lt,this._promiseCtor=jt,e&&this.setConfig(e)}var n,e;return n=t,e=[{key:"_applyFilter",value:function(t){for(var n=this._filters.length;n>0;n--){var e=this._filters[n-n][t];e&&e(this)}}},{key:"tween",value:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;return this._isPlaying&&this.stop(),!n&&this._config||this.setConfig(n),this._pausedAtTime=null,this._timestamp=t.now(),this._start(this.get(),this._data),this._delay&&this._render(this._currentState,this._data,0),this._resume(this._timestamp)}},{key:"setConfig",value:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=this._config;for(var r in n)e[r]=n[r];var i=e.promise,o=void 0===i?this._promiseCtor:i,u=e.start,a=void 0===u?lt:u,s=e.finish,c=e.render,f=void 0===c?this._config.step||lt:c,l=e.step,h=void 0===l?lt:l;this._data=e.data||e.attachment||this._data,this._isPlaying=!1,this._pausedAtTime=null,this._scheduleId=null,this._delay=n.delay||0,this._start=a,this._render=f||h,this._duration=e.duration||500,this._promiseCtor=o,s&&(this._resolve=s);var p=n.from,v=n.to,y=void 0===v?{}:v,_=this._currentState,d=this._originalState,g=this._targetState;for(var m in p)_[m]=p[m];var b=!1;for(var w in _){var O=_[w];b||G(O)!==ct||(b=!0),d[w]=O,g[w]=y.hasOwnProperty(w)?y[w]:O}if(this._easing=Ot(this._currentState,e.easing,this._easing),this._filters.length=0,b){for(var S in t.filters)t.filters[S].doesApply(this)&&this._filters.push(t.filters[S]);this._applyFilter(at)}return this}},{key:"then",value:function(t,n){var e=this;return this._promise=new this._promiseCtor((function(t,n){e._resolve=t,e._reject=n})),this._promise.then(t,n)}},{key:"catch",value:function(t){return this.then().catch(t)}},{key:"finally",value:function(t){return this.then().finally(t)}},{key:"get",value:function(){return X({},this._currentState)}},{key:"set",value:function(t){this._currentState=t}},{key:"pause",value:function(){if(this._isPlaying)return this._pausedAtTime=t.now(),this._isPlaying=!1,St(this),this}},{key:"resume",value:function(){return this._resume()}},{key:"_resume",value:function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:t.now();return null===this._timestamp?this.tween():this._isPlaying?this._promise:(this._pausedAtTime&&(this._timestamp+=n-this._pausedAtTime,this._pausedAtTime=null),this._isPlaying=!0,null===ht?(ht=this,pt=this):(this._previous=pt,pt._next=this,pt=this),this)}},{key:"seek",value:function(n){n=Math.max(n,0);var e=t.now();return this._timestamp+n===0||(this._timestamp=e-n,_t(this,e)),this}},{key:"stop",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this._isPlaying)return this;this._isPlaying=!1,St(this);var n=this._filters.length>0;return t&&(n&&this._applyFilter(ut),yt(1,this._currentState,this._originalState,this._targetState,1,0,this._easing),n&&(this._applyFilter(it),this._applyFilter(ot))),this._resolve&&this._resolve({data:this._data,state:this._currentState,tweenable:this}),this._resolve=null,this._reject=null,this}},{key:"cancel",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0],n=this._currentState,e=this._data,r=this._isPlaying;return r?(this._reject&&this._reject({data:e,state:n,tweenable:this}),this._resolve=null,this._reject=null,this.stop(t)):this}},{key:"isPlaying",value:function(){return this._isPlaying}},{key:"hasEnded",value:function(){return this._hasEnded}},{key:"setScheduleFunction",value:function(n){t.setScheduleFunction(n)}},{key:"data",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return t&&(this._data=X({},t)),this._data}},{key:"dispose",value:function(){for(var t in this)delete this[t]}}],e&&W(n.prototype,e),t}();function Pt(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=new kt;return n.tween(t),n.tweenable=n,n}Y(kt,"now",(function(){return Z})),Y(kt,"setScheduleFunction",(function(t){return ft=t})),Y(kt,"filters",{}),Y(kt,"formulas",vt),bt(!0);var Mt,Et,At=/(\d|-|\.)/,Tt=/([^\-0-9.]+)/g,It=/[0-9.-]+/g,Ft=(Mt=It.source,Et=/,\s*/.source,new RegExp("rgba?\\(".concat(Mt).concat(Et).concat(Mt).concat(Et).concat(Mt,"(").concat(Et).concat(Mt,")?\\)"),"g")),xt=/^.*\(/,Ct=/#([0-9]|[a-f]){3,6}/gi,Dt="VAL",qt=function(t,n){return t.map((function(t,e){return"_".concat(n,"_").concat(e)}))};function Qt(t){return parseInt(t,16)}var Bt=function(t){return"rgb(".concat((n=t,3===(n=n.replace(/#/,"")).length&&(n=(n=n.split(""))[0]+n[0]+n[1]+n[1]+n[2]+n[2]),[Qt(n.substr(0,2)),Qt(n.substr(2,2)),Qt(n.substr(4,2))]).join(","),")");var n},Nt=function(t,n,e){var r=n.match(t),i=n.replace(t,Dt);return r&&r.forEach((function(t){return i=i.replace(Dt,e(t))})),i},Rt=function(t){for(var n in t){var e=t[n];"string"==typeof e&&e.match(Ct)&&(t[n]=Nt(Ct,e,Bt))}},zt=function(t){var n=t.match(It),e=n.slice(0,3).map(Math.floor),r=t.match(xt)[0];if(3===n.length)return"".concat(r).concat(e.join(","),")");if(4===n.length)return"".concat(r).concat(e.join(","),",").concat(n[3],")");throw new Error("Invalid rgbChunk: ".concat(t))},Ut=function(t){return t.match(It)},$t=function(t,n){var e={};return n.forEach((function(n){e[n]=t[n],delete t[n]})),e},Lt=function(t,n){return n.map((function(n){return t[n]}))},Vt=function(t,n){return n.forEach((function(n){return t=t.replace(Dt,+n.toFixed(4))})),t},Wt=function(t){for(var n in t._currentState)if("string"==typeof t._currentState[n])return!0;return!1};function Gt(t){var n=t._currentState;[n,t._originalState,t._targetState].forEach(Rt),t._tokenData=function(t){var n,e,r={};for(var i in t){var o=t[i];"string"==typeof o&&(r[i]={formatString:(n=o,e=void 0,e=n.match(Tt),e?(1===e.length||n.charAt(0).match(At))&&e.unshift(""):e=["",""],e.join(Dt)),chunkNames:qt(Ut(o),i)})}return r}(n)}function Ht(t){var n=t._currentState,e=t._originalState,r=t._targetState,i=t._easing,o=t._tokenData;!function(t,n){var e=function(e){var r=n[e].chunkNames,i=t[e];if("string"==typeof i){var o=i.split(" "),u=o[o.length-1];r.forEach((function(n,e){return t[n]=o[e]||u}))}else r.forEach((function(n){return t[n]=i}));delete t[e]};for(var r in n)e(r)}(i,o),[n,e,r].forEach((function(t){return function(t,n){var e=function(e){Ut(t[e]).forEach((function(r,i){return t[n[e].chunkNames[i]]=+r})),delete t[e]};for(var r in n)e(r)}(t,o)}))}function Jt(t){var n=t._currentState,e=t._originalState,r=t._targetState,i=t._easing,o=t._tokenData;[n,e,r].forEach((function(t){return function(t,n){for(var e in n){var r=n[e],i=r.chunkNames,o=r.formatString,u=Vt(o,Lt($t(t,i),i));t[e]=Nt(Ft,u,zt)}}(t,o)})),function(t,n){for(var e in n){var r=n[e].chunkNames,i=t[r[0]];t[e]="string"==typeof i?r.map((function(n){var e=t[n];return delete t[n],e})).join(" "):i}}(i,o)}function Kt(t,n){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);n&&(r=r.filter((function(n){return Object.getOwnPropertyDescriptor(t,n).enumerable}))),e.push.apply(e,r)}return e}function Xt(t){for(var n=1;n4&&void 0!==arguments[4]?arguments[4]:0,o=Xt({},t),u=Ot(t,r);for(var a in Zt._filters.length=0,Zt.set({}),Zt._currentState=o,Zt._originalState=t,Zt._targetState=n,Zt._easing=u,tn)tn[a].doesApply(Zt)&&Zt._filters.push(tn[a]);Zt._applyFilter("tweenCreated"),Zt._applyFilter("beforeTween");var s=yt(e,o,t,n,1,i,u);return Zt._applyFilter("afterTween"),s};function en(t,n){(null==n||n>t.length)&&(n=t.length);for(var e=0,r=new Array(n);e opts.strokeWidth) { widthOfWider = opts.trailWidth; } var r = 50 - widthOfWider / 2; return utils.render(this._pathTemplate, { radius: r, '2radius': r * 2 }); }; Circle.prototype._trailString = function _trailString(opts) { return this._pathString(opts); }; module.exports = Circle; },{"./shape":8,"./utils":10}],4:[function(require,module,exports){ // Line shaped progress bar var Shape = require('./shape'); var utils = require('./utils'); var Line = function Line(container, options) { this._pathTemplate = options.vertical ? 'M {center},100 L {center},0' : 'M 0,{center} L 100,{center}'; Shape.apply(this, arguments); }; Line.prototype = new Shape(); Line.prototype.constructor = Line; Line.prototype._initializeSvg = function _initializeSvg(svg, opts) { var viewBoxStr = opts.vertical ? '0 0 ' + opts.strokeWidth + ' 100' : '0 0 100 ' + opts.strokeWidth; svg.setAttribute('viewBox', viewBoxStr); svg.setAttribute('preserveAspectRatio', 'none'); }; Line.prototype._pathString = function _pathString(opts) { return utils.render(this._pathTemplate, { center: opts.strokeWidth / 2 }); }; Line.prototype._trailString = function _trailString(opts) { return this._pathString(opts); }; module.exports = Line; },{"./shape":8,"./utils":10}],5:[function(require,module,exports){ module.exports = { // Higher level API, different shaped progress bars Line: require('./line'), Circle: require('./circle'), SemiCircle: require('./semicircle'), Square: require('./square'), // Lower level API to use any SVG path Path: require('./path'), // Base-class for creating new custom shapes // to be in line with the API of built-in shapes // Undocumented. Shape: require('./shape'), // Internal utils, undocumented. utils: require('./utils') }; },{"./circle":3,"./line":4,"./path":6,"./semicircle":7,"./shape":8,"./square":9,"./utils":10}],6:[function(require,module,exports){ // Lower level API to animate any kind of svg path var shifty = require('shifty'); var utils = require('./utils'); var Tweenable = shifty.Tweenable; var EASING_ALIASES = { easeIn: 'easeInCubic', easeOut: 'easeOutCubic', easeInOut: 'easeInOutCubic' }; var Path = function Path(path, opts) { // Throw a better error if not initialized with `new` keyword if (!(this instanceof Path)) { throw new Error('Constructor was called without new keyword'); } // Default parameters for animation opts = utils.extend({ delay: 0, duration: 800, easing: 'linear', from: {}, to: {}, step: function() {} }, opts); var element; if (utils.isString(path)) { element = document.querySelector(path); } else { element = path; } // Reveal .path as public attribute this.path = element; this._opts = opts; this._tweenable = null; // Set up the starting positions var length = this.path.getTotalLength(); this.path.style.strokeDasharray = length + ' ' + length; this.set(0); }; Path.prototype.value = function value() { var offset = this._getComputedDashOffset(); var length = this.path.getTotalLength(); var progress = 1 - offset / length; // Round number to prevent returning very small number like 1e-30, which // is practically 0 return parseFloat(progress.toFixed(6), 10); }; Path.prototype.set = function set(progress) { this.stop(); this.path.style.strokeDashoffset = this._progressToOffset(progress); var step = this._opts.step; if (utils.isFunction(step)) { var easing = this._easing(this._opts.easing); var values = this._calculateTo(progress, easing); var reference = this._opts.shape || this; step(values, reference, this._opts.attachment); } }; Path.prototype.stop = function stop() { this._stopTween(); this.path.style.strokeDashoffset = this._getComputedDashOffset(); }; // Method introduced here: // http://jakearchibald.com/2013/animated-line-drawing-svg/ Path.prototype.animate = function animate(progress, opts, cb) { opts = opts || {}; if (utils.isFunction(opts)) { cb = opts; opts = {}; } var passedOpts = utils.extend({}, opts); // Copy default opts to new object so defaults are not modified var defaultOpts = utils.extend({}, this._opts); opts = utils.extend(defaultOpts, opts); var shiftyEasing = this._easing(opts.easing); var values = this._resolveFromAndTo(progress, shiftyEasing, passedOpts); this.stop(); // Trigger a layout so styles are calculated & the browser // picks up the starting position before animating this.path.getBoundingClientRect(); var offset = this._getComputedDashOffset(); var newOffset = this._progressToOffset(progress); var self = this; this._tweenable = new Tweenable(); this._tweenable.tween({ from: utils.extend({ offset: offset }, values.from), to: utils.extend({ offset: newOffset }, values.to), duration: opts.duration, delay: opts.delay, easing: shiftyEasing, step: function(state) { self.path.style.strokeDashoffset = state.offset; var reference = opts.shape || self; opts.step(state, reference, opts.attachment); } }).then(function(state) { if (utils.isFunction(cb)) { cb(); } }).catch(function(err) { console.error('Error in tweening:', err); throw err; }); }; Path.prototype._getComputedDashOffset = function _getComputedDashOffset() { var computedStyle = window.getComputedStyle(this.path, null); return parseFloat(computedStyle.getPropertyValue('stroke-dashoffset'), 10); }; Path.prototype._progressToOffset = function _progressToOffset(progress) { var length = this.path.getTotalLength(); return length - progress * length; }; // Resolves from and to values for animation. Path.prototype._resolveFromAndTo = function _resolveFromAndTo(progress, easing, opts) { if (opts.from && opts.to) { return { from: opts.from, to: opts.to }; } return { from: this._calculateFrom(easing), to: this._calculateTo(progress, easing) }; }; // Calculate `from` values from options passed at initialization Path.prototype._calculateFrom = function _calculateFrom(easing) { return shifty.interpolate(this._opts.from, this._opts.to, this.value(), easing); }; // Calculate `to` values from options passed at initialization Path.prototype._calculateTo = function _calculateTo(progress, easing) { return shifty.interpolate(this._opts.from, this._opts.to, progress, easing); }; Path.prototype._stopTween = function _stopTween() { if (this._tweenable !== null) { this._tweenable.stop(true); this._tweenable = null; } }; Path.prototype._easing = function _easing(easing) { if (EASING_ALIASES.hasOwnProperty(easing)) { return EASING_ALIASES[easing]; } return easing; }; module.exports = Path; },{"./utils":10,"shifty":2}],7:[function(require,module,exports){ // Semi-SemiCircle shaped progress bar 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 this._pathTemplate = 'M 50,50 m -{radius},0' + ' a {radius},{radius} 0 1 1 {2radius},0'; this.containerAspectRatio = 2; Shape.apply(this, arguments); }; SemiCircle.prototype = new Shape(); SemiCircle.prototype.constructor = SemiCircle; SemiCircle.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 50'); }; SemiCircle.prototype._initializeTextContainer = function _initializeTextContainer( opts, container, textContainer ) { if (opts.text.style) { // Reset top style textContainer.style.top = 'auto'; textContainer.style.bottom = '0'; if (opts.text.alignToBottom) { utils.setStyle(textContainer, 'transform', 'translate(-50%, 0)'); } else { utils.setStyle(textContainer, 'transform', 'translate(-50%, 50%)'); } } }; // Share functionality with Circle, just have different path SemiCircle.prototype._pathString = Circle.prototype._pathString; SemiCircle.prototype._trailString = Circle.prototype._trailString; module.exports = SemiCircle; },{"./circle":3,"./shape":8,"./utils":10}],8:[function(require,module,exports){ // Base object for different progress bar shapes var Path = require('./path'); 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 if (!(this instanceof Shape)) { throw new Error('Constructor was called without new keyword'); } // Prevent calling constructor without parameters so inheritance // works correctly. To understand, this is how Shape is inherited: // // Line.prototype = new Shape(); // // We just want to set the prototype for Line. if (arguments.length === 0) { return; } // Default parameters for progress bar creation this._opts = utils.extend({ color: '#555', strokeWidth: 1.0, trailColor: null, trailWidth: null, fill: null, text: { style: { color: null, position: 'absolute', left: '50%', top: '50%', padding: 0, margin: 0, transform: { prefix: true, value: 'translate(-50%, -50%)' } }, autoStyleContainer: true, alignToBottom: true, value: null, className: 'progressbar-text' }, svgStyle: { display: 'block', width: '100%' }, warnings: false }, opts, true); // Use recursive extend // If user specifies e.g. svgStyle or text style, the whole object // should replace the defaults to make working with styles easier if (utils.isObject(opts) && opts.svgStyle !== undefined) { this._opts.svgStyle = opts.svgStyle; } if (utils.isObject(opts) && utils.isObject(opts.text) && opts.text.style !== undefined) { this._opts.text.style = opts.text.style; } var svgView = this._createSvgView(this._opts); var element; if (utils.isString(container)) { element = document.querySelector(container); } else { element = container; } if (!element) { throw new Error('Container does not exist: ' + container); } this._container = element; this._container.appendChild(svgView.svg); if (this._opts.warnings) { this._warnContainerAspectRatio(this._container); } if (this._opts.svgStyle) { utils.setStyles(svgView.svg, this._opts.svgStyle); } // Expose public attributes before Path initialization this.svg = svgView.svg; this.path = svgView.path; this.trail = svgView.trail; this.text = null; var newOpts = utils.extend({ attachment: undefined, shape: this }, this._opts); this._progressPath = new Path(svgView.path, newOpts); if (utils.isObject(this._opts.text) && this._opts.text.value !== null) { this.setText(this._opts.text.value); } }; Shape.prototype.animate = function animate(progress, opts, cb) { 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); } // Don't crash if stop is called inside step function if (this._progressPath === undefined) { return; } this._progressPath.stop(); }; Shape.prototype.pause = function pause() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this._progressPath === undefined) { return; } if (!this._progressPath._tweenable) { // It seems that we can't pause this return; } this._progressPath._tweenable.pause(); }; Shape.prototype.resume = function resume() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this._progressPath === undefined) { return; } if (!this._progressPath._tweenable) { // It seems that we can't resume this return; } this._progressPath._tweenable.resume(); }; Shape.prototype.destroy = function destroy() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } this.stop(); this.svg.parentNode.removeChild(this.svg); this.svg = null; this.path = null; this.trail = null; this._progressPath = null; if (this.text !== null) { this.text.parentNode.removeChild(this.text); this.text = null; } }; Shape.prototype.set = function set(progress) { 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; } return this._progressPath.value(); }; Shape.prototype.setText = function setText(newText) { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this.text === null) { // Create new text node this.text = this._createTextContainer(this._opts, this._container); this._container.appendChild(this.text); } // Remove previous text and add new if (utils.isObject(newText)) { utils.removeChildren(this.text); this.text.appendChild(newText); } else { this.text.innerHTML = newText; } }; Shape.prototype._createSvgView = function _createSvgView(opts) { var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this._initializeSvg(svg, opts); var trailPath = null; // Each option listed in the if condition are 'triggers' for creating // the trail path if (opts.trailColor || opts.trailWidth) { trailPath = this._createTrail(opts); svg.appendChild(trailPath); } var path = this._createPath(opts); svg.appendChild(path); return { svg: svg, path: path, trail: trailPath }; }; Shape.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 100'); }; Shape.prototype._createPath = function _createPath(opts) { var pathString = this._pathString(opts); return this._createPathElement(pathString, opts); }; Shape.prototype._createTrail = function _createTrail(opts) { // Create path string with original passed options var pathString = this._trailString(opts); // Prevent modifying original 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; } newOpts.color = newOpts.trailColor; newOpts.strokeWidth = newOpts.trailWidth; // When trail path is set, fill must be set for it instead of the // actual path to prevent trail stroke from clipping newOpts.fill = null; return this._createPathElement(pathString, newOpts); }; Shape.prototype._createPathElement = function _createPathElement(pathString, opts) { var path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('d', pathString); path.setAttribute('stroke', opts.color); path.setAttribute('stroke-width', opts.strokeWidth); if (opts.fill) { path.setAttribute('fill', opts.fill); } else { path.setAttribute('fill-opacity', '0'); } return path; }; Shape.prototype._createTextContainer = function _createTextContainer(opts, container) { var textContainer = document.createElement('div'); textContainer.className = opts.text.className; var textStyle = opts.text.style; if (textStyle) { if (opts.text.autoStyleContainer) { container.style.position = 'relative'; } utils.setStyles(textContainer, textStyle); // Default text color to progress bar's color if (!textStyle.color) { textContainer.style.color = opts.color; } } this._initializeTextContainer(opts, container, textContainer); return textContainer; }; // Give custom shapes possibility to modify text element Shape.prototype._initializeTextContainer = function(opts, container, element) { // By default, no-op // Custom shapes should respect API options, such as text.style }; Shape.prototype._pathString = function _pathString(opts) { throw new Error('Override this function for each progress bar'); }; Shape.prototype._trailString = function _trailString(opts) { throw new Error('Override this function for each progress bar'); }; Shape.prototype._warnContainerAspectRatio = function _warnContainerAspectRatio(container) { if (!this.containerAspectRatio) { return; } var computedStyle = window.getComputedStyle(container, null); var width = parseFloat(computedStyle.getPropertyValue('width'), 10); var height = parseFloat(computedStyle.getPropertyValue('height'), 10); if (!utils.floatEquals(this.containerAspectRatio, width / height)) { console.warn( 'Incorrect aspect ratio of container', '#' + container.id, 'detected:', computedStyle.getPropertyValue('width') + '(width)', '/', computedStyle.getPropertyValue('height') + '(height)', '=', width / height ); console.warn( 'Aspect ratio of should be', this.containerAspectRatio ); } }; module.exports = Shape; },{"./path":6,"./utils":10}],9:[function(require,module,exports){ // Square shaped progress bar // Note: Square is not core part of API anymore. It's left here // for reference. square is not included to the progressbar // build anymore var Shape = require('./shape'); var utils = require('./utils'); var Square = function Square(container, options) { this._pathTemplate = 'M 0,{halfOfStrokeWidth}' + ' L {width},{halfOfStrokeWidth}' + ' L {width},{width}' + ' L {halfOfStrokeWidth},{width}' + ' L {halfOfStrokeWidth},{strokeWidth}'; this._trailTemplate = 'M {startMargin},{halfOfStrokeWidth}' + ' L {width},{halfOfStrokeWidth}' + ' L {width},{width}' + ' L {halfOfStrokeWidth},{width}' + ' L {halfOfStrokeWidth},{halfOfStrokeWidth}'; Shape.apply(this, arguments); }; Square.prototype = new Shape(); Square.prototype.constructor = Square; Square.prototype._pathString = function _pathString(opts) { var w = 100 - opts.strokeWidth / 2; return utils.render(this._pathTemplate, { width: w, strokeWidth: opts.strokeWidth, halfOfStrokeWidth: opts.strokeWidth / 2 }); }; Square.prototype._trailString = function _trailString(opts) { var w = 100 - opts.strokeWidth / 2; return utils.render(this._trailTemplate, { width: w, strokeWidth: opts.strokeWidth, halfOfStrokeWidth: opts.strokeWidth / 2, startMargin: opts.strokeWidth / 2 - opts.trailWidth / 2 }); }; module.exports = Square; },{"./shape":8,"./utils":10}],10:[function(require,module,exports){ // Utility functions var merge = require('lodash.merge'); var PREFIXES = 'Webkit Moz O ms'.split(' '); var FLOAT_COMPARISON_EPSILON = 0.001; // Renders templates with given variables. Variables must be surrounded with // braces without any spaces, e.g. {variable} // All instances of variable placeholders will be replaced with given content // Example: // render('Hello, {message}!', {message: 'world'}) function render(template, vars) { var rendered = template; for (var key in vars) { if (vars.hasOwnProperty(key)) { var val = vars[key]; var regExpString = '\\{' + key + '\\}'; var regExp = new RegExp(regExpString, 'g'); rendered = rendered.replace(regExp, val); } } return rendered; } function setStyle(element, style, value) { var elStyle = element.style; // cache for performance for (var i = 0; i < PREFIXES.length; ++i) { var prefix = PREFIXES[i]; elStyle[prefix + capitalize(style)] = value; } elStyle[style] = value; } function setStyles(element, styles) { forEachObject(styles, function(styleValue, styleName) { // Allow disabling some individual styles by setting them // to null or undefined if (styleValue === null || styleValue === undefined) { return; } // If style's value is {prefix: true, value: '50%'}, // Set also browser prefixed styles if (isObject(styleValue) && styleValue.prefix === true) { setStyle(element, styleName, styleValue.value); } else { element.style[styleName] = styleValue; } }); } function capitalize(text) { return text.charAt(0).toUpperCase() + text.slice(1); } function isString(obj) { return typeof obj === 'string' || obj instanceof String; } function isFunction(obj) { return typeof obj === 'function'; } function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } // 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; } var type = typeof obj; return type === 'object' && !!obj; } function forEachObject(object, callback) { for (var key in object) { if (object.hasOwnProperty(key)) { var val = object[key]; callback(val, key); } } } function floatEquals(a, b) { return Math.abs(a - b) < FLOAT_COMPARISON_EPSILON; } // https://coderwall.com/p/nygghw/don-t-use-innerhtml-to-empty-dom-elements function removeChildren(el) { while (el.firstChild) { el.removeChild(el.firstChild); } } module.exports = { extend: merge, render: render, setStyle: setStyle, setStyles: setStyles, capitalize: capitalize, isString: isString, isFunction: isFunction, isObject: isObject, forEachObject: forEachObject, floatEquals: floatEquals, removeChildren: removeChildren }; },{"lodash.merge":1}]},{},[5])(5) }); ================================================ FILE: docs/api/general.md ================================================ Functions use node-style callback convention. Callback function is always the last given parameter. Shapes have different SVG canvas sizes: Shape | Canvas size -----------|------------------------ Circle | `100x100` SemiCircle | `100x50` Line | `100x{opts.strokeWidth}` All shapes are fitted exactly to their canvases. **Important:** make sure that your container has same aspect ratio as the SVG canvas. For example: if you are using SemiCircle, set e.g. ```css #container { width: 300px; height: 150px; } ``` ## HTML structure As an example, we'll use these options: ```js var circle = new ProgressBar.Circle('#example-percent-container', { color: '#FCB03C', strokeWidth: 3, trailWidth: 1, text: { value: '0' } }); ``` They would produce the following HTML. ```html

0

``` ================================================ FILE: docs/api/parameters.md ================================================ # Easing Easing functions [provided with *shifty* are supported](https://github.com/jeremyckahn/shifty/blob/master/src/shifty.formulas.js). A few basic easing options: * `'linear'` * `'easeIn'` * `'easeOut'` * `'easeInOut'` You can also provide an custom easing function. # {custom-animations} Custom animations See [example in demo page](https://kimmobrunfeldt.github.io/progressbar.js#example-custom-animation). Customizing animations is possible with the help of `from`, `to` and `step` parameters. Tweening engine changes defined values over time and calls step function for each animation's frame. !!! note There's a big difference between passing the `from` and `to` parameters in initialization of progress bar compared to passing in `.animate()` call. Here's example code and illustrations to explain the difference: **Pass in initialization** ```javascript var bar = new ProgressBar.Line('#container', { from: { color: '#000 '}, to: { color: '#888 '}, step: function(state, bar, attachment) { bar.path.setAttribute('stroke', state.color); } }); ``` ![](../img/animate-init.png) **Pass in `.animate()` call** ```javascript var bar = new ProgressBar.Line('#container', { step: function(state, bar, attachment) { bar.path.setAttribute('stroke', state.color); } }); var opts = { from: { color: '#000 '}, to: { color: '#888'} }; bar.animate(0.5, opts); ``` ![](../img/animate-call.png) ## `from` parameter Object containing values which should be tweened. These values represent the starting values of the animation. Default: `{}`. For example ```javascript { // Start from thin gray line width: 0.1, color: '#eee' } ``` Thanks to shifty, you can tween values in formats like `translateX(45px)`, `rgb(0,255,0)` and `#fff`. See all supported string formats from [shifty's documentation](http://jeremyckahn.github.io/shifty/dist/doc/modules/Tweenable.token.html) Easing defined as option for animation applies to all of the specified values. ## `to` parameter Object containing values which should be tweened. These represent the final values after animation is done. Default: `{}`. For example ```javascript { // Finish to thick black line width: 1, color: '#000' } ``` *Signature must match `from`* ## `step` parameter Function called for each animation step. Tweened values, a reference to the path or shape, and an attachment are passed as parameters. Attachment can be reference to any object you need to modify within step function. Default: `function() {}`. !!! warning This function is called multiple times per second. To make sure animations run smoothly, keep it minimal. For example ```javascript function(state, shape, attachment) { shape.path.setAttribute('stroke-width', state.width); shape.path.setAttribute('stroke', state.color); attachment.text.innerHTML = shape.value() * 100; } ``` ================================================ FILE: docs/api/path.md ================================================ # new Path(path, [*options*]) Custom shaped progress bar. You can create arbitrary shaped progress bars by passing a SVG path created with e.g. Adobe Illustrator. It's on caller's responsibility to append SVG to DOM. !!! note Remember to add e.g. `stroke="1"` and `fill-opacity="0"` attributes for the SVG path. They will reveal the true shape of the path. **Example** Assuming there was SVG object with heart shaped path in HTML ```html ``` Initialization would be this easy ```javascript var svgPath = document.getElementById('heart-path'); var path = new ProgressBar.Path(svgPath, { duration: 300 }); ``` **Working with embedded SVG** If the SVG was not inline in the HTML but instead in, say, an `` tag, we'd have to take extra steps to wait until it has loaded and then access it differently since it's in a separate DOM tree. Given e.g.: ```html No SVG support :( ``` we could do ```javascript var heart = document.getElementById('heart'); heart.addEventListener('load', function() { var path = new ProgressBar.Path(heartObject.contentDocument.querySelector('#heart-path'), { duration: 300 }); ``` **Parameters** * `path` [SVG Path](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths) object or plain selector string. For example `$('svg > path:first-child')[0]`. * `options` Animation options. ```javascript { // Duration for animation in milliseconds // Default: 800 duration: 1200, // Delay for animation in milliseconds // Default: 0 delay: 100, // Easing for animation. See #easing section. // Default: 'linear' easing: 'easeIn', // Attachment which can be any object // you need to modify within the step function. // Passed as a parameter to step function. // Default: undefined attachment: document.querySelector('#container > svg'), // See #custom-animations section from: { color: '#eee' }, to: { color: '#000' }, step: function(state, path, attachment) { // Do any modifications to attachment and/or path attributes } } ``` ## .path Reference to [SVG path](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path) which presents the actual progress bar. ## .animate(progress, [*options*], [*cb*]) Animates drawing of path. **Example** ```javascript path.animate(0.3, { duration: 800 }, function() { console.log('Animation has finished'); }); ``` **Parameters** * `progress` progress from 0 to 1. * `options` Animation options. These options override the defaults given in initialization. ```javascript { // Duration for animation in milliseconds // Default: 800 duration: 1200, // Delay for animation in milliseconds // Default: 0 delay: 100, // Easing for animation. See #easing section. // Default: 'linear' easing: 'easeOut', // Attachment which can be any object // you need to modify within the step function. // Passed as a parameter to step function. // Default: undefined attachment: document.querySelector('#container > svg'), // See #custom-animations section from: { color: '#eee' }, to: { color: '#000' }, step: function(state, path, attachment) { // Do any modifications to attachment and/or path attributes } } ``` * `cb` Callback function which is called after transition ends. ## .pause() Suspends animation at its current position. ## .resume() Resumes animation from a previously paused position. ## .set(progress) Set progress instantly without animation. Clears all transitions for path. ## .stop() Stops animation to its current position. ## .value() Returns current shown progress from 0 to 1. This value changes when animation is running. ================================================ FILE: docs/api/shape.md ================================================ !!! note Line, Circle and SemiCircle all point to the same documentation which is named Shape. You should replace it(Shape) with Line, Circle or SemiCircle. **Example:** if documentation states `Shape.animate()`, replace it with `Circle.animate()`, simple. Shape is the base object for all progress bars. # new Shape(container, [*options*]) Line, Circle or SemiCircle shaped progress bar. Appends SVG to container. **Example** ```javascript var progressBar = new ProgressBar.Circle('#container', { strokeWidth: 2 }); ``` With Line shape, you can control the width of the line by specifying e.g. `height: 5px` with CSS. **Parameters** * `container` Element where SVG is added. Query string or element. For example `'#container'` or `document.getElementById('#container')` * `options` Options for path drawing. ```js { // Stroke color. // Default: '#555' color: '#3a3a3a', // Width of the stroke. // Unit is percentage of SVG canvas' size. // Default: 1.0 // NOTE: In Line shape, you should control // the stroke width by setting container's height. // WARNING: IE doesn't support values over 6, see this bug: // https://github.com/kimmobrunfeldt/progressbar.js/issues/79 strokeWidth: 2.1, // If trail options are not defined, trail won't be drawn // Color for lighter trail stroke // underneath the actual progress path. // Default: '#eee' trailColor: '#f4f4f4', // Width of the trail stroke. Trail is always centered relative to // actual progress path. // Default: same as strokeWidth trailWidth: 0.8, // Inline CSS styles for the created SVG element // Set null to disable all default styles. // You can disable individual defaults by setting them to `null` // If you specify anything in this object, none of the default styles // apply svgStyle: { display: 'block', // Important: make sure that your container has same // aspect ratio as the SVG canvas. See SVG canvas sizes above. width: '100%' }, // Text options. Text element is a

element appended to container // You can add CSS rules for the text element with the className // NOTE: When text is set, 'position: relative' will be set to the // container for centering. You can also prevent all default inline // styles with 'text.style: null' // Default: null text: { // Initial value for text. // Default: null value: 'Text', // Class name for text element. // Default: 'progressbar-text' className: 'progressbar__label', // Inline CSS styles for the text element. // If you want to modify all CSS your self, set null to disable // all default styles. // If the style option contains values, container is automatically // set to position: relative. You can disable behavior this with // autoStyleContainer: false // If you specify anything in this object, none of the default styles // apply // Default: object speficied below style: { // Text color. // Default: same as stroke color (options.color) color: '#f00', position: 'absolute', left: '50%', top: '50%', padding: 0, margin: 0, // You can specify styles which will be browser prefixed transform: { prefix: true, value: 'translate(-50%, -50%)' } }, // Only effective if the text.style is not null // By default position: relative is applied to container if text // is set. Setting this to false disables that feature. autoStyleContainer: true, // Only effective if the shape is SemiCircle. // If true, baseline for text is aligned with bottom of // the SVG canvas. If false, bottom line of SVG canvas // is in the center of text. // Default: true alignToBottom: true }, // Fill color for the shape. If null, no fill. // Default: null fill: 'rgba(0, 0, 0, 0.5)', // Duration for animation in milliseconds // Default: 800 duration: 1200, // Delay for animation in milliseconds // Default: 0 delay: 100, // Easing for animation. See #easing section. // Default: 'linear' easing: 'easeOut', // See #custom-animations section // Built-in shape passes reference to itself and a custom attachment // object to step function from: { color: '#eee' }, to: { color: '#000' }, step: function(state, circle, attachment) { circle.path.setAttribute('stroke', state.color); }, // If true, some useful console.warn calls will be done if it seems // that progressbar is used incorrectly // Default: false warnings: false } ``` ## .svg Reference to [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg) element where progress bar is drawn. ## .path Reference to [SVG path](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path) which presents the actual progress bar. ## .trail Reference to [SVG path](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path) which presents the trail of the progress bar. Returns `null` if trail is not defined. ## {text} .text Reference to [p element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p) which presents the text label for progress bar. Returns `null` if text is not defined. ## .animate(progress, [*options*], [*cb*]) Animates drawing of a shape. **Example** ```javascript progressBar.animate(0.3, { duration: 800 }, function() { console.log('Animation has finished'); }); ``` **Parameters** * `progress` progress from 0 to 1. * `options` Animation options. These options override the defaults given in initialization. ```javascript { // Duration for animation in milliseconds // Default: 800 duration: 1200, // Delay for animation in milliseconds // Default: 0 delay: 100, // Easing for animation. See #easing section. // Default: 'linear' easing: 'easeInOut', // See #custom-animations section // Built-in shape passes reference to itself and a custom attachment // object to step function from: { color: '#eee' }, to: { color: '#000' }, step: function(state, circle, attachment) { circle.path.setAttribute('stroke', state.color); } } ``` * `cb` Callback function which is called after animation ends. ## .set(progress) Sets progress instantly without animation. Clears all animations for path. ## .stop() Stops animation to its current position. ## .value() Returns current shown progress from 0 to 1. This value changes when animation is running. ## .setText(text) Sets text to given a string. If you need to dynamically modify the text element, see [.text](#text) attribute. ## .destroy() Removes SVG element from container and removes all references to DOM elements. Destroying is irreversible. ================================================ FILE: docs/contributing.md ================================================ # Contribution documentation Pull requests and contributions are warmly welcome. Please follow existing code style and commit message conventions. Also remember to keep documentation updated. **Pull requests:** You don't need to bump version numbers or modify anything related to releasing. That stuff is fully automated, just write the functionality. ## Get started with documentation Documentation is written in markdown and [mkdocs](https://github.com/mkdocs/mkdocs) is used to build documentation site. [ReadTheDocs](https://readthedocs.org/projects/progressbarjs/) provides hosting for the documentation site and builds the documentation on each commit. To locally develop documentation, you need to install mkdocs and requirements specified in pip-requirements.txt. After that you can run `mkdocs serve`. ## Get started with development * [Install local environment](#install-environment). * `cd local-dev` and serve folder to browser * Open another terminal to project root and run `grunt watch`. 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 but there's no reason it couldn't be replaced with e.g. Makefile *ProgressBar.js* depends on tweening library called [shifty](https://github.com/jeremyckahn/shifty). *Shifty* is bundled inside the scripts in [dist/](https://github.com/kimmobrunfeldt/progressbar.js/blob/master/dist) directory. Dependency is bundled in to ease using the library. #### Versioning Versioning follows [Semantic Versioning 2.0.0](http://semver.org/). The release script makes sure that for each release, there exists only one commit in history where version number in *bower.json* matches the release's version. That commit is tagged as the release, for example `0.4.1`. Commits after that have -dev suffix(*0.4.1-dev*) in the version number to avoid any possible confusion. In other words, if you look into *bower.json*, you can tell if the code base is a released version or not. ## {install-environment} Install environment Install tools needed for development: npm install npm install -g watchify npm install -g browserify For testing: npm install -g mocha npm install -g testem ## Test Tests are written with [Mocha](http://mochajs.org/) + [expect.js](https://github.com/LearnBoost/expect.js/). Sometimes the tests fail even though they actually work on the browser. That might be cause of setTimeouts used in tests. Quickly run tests: grunt test This will use testem to run tests with Chrome. Other options specified below. #### Testem [Testem](https://github.com/airportyh/testem) is used for running tests locally. It is fast and easy to use. List of example commands: * `testem` Serves testing page so that you can connect any browser to it. * `testem ci` Runs tests an all available/detected local browsers. * `testem ci -R dot -l chrome` Runs tests with Chrome using dot reported. #### Karma [Karma](http://karma-runner.github.io/) is used for running tests to be executed in Sauce Labs. Karma was used because integrating it to Sauce Labs is easier than with Testem. You must setup `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY` environment variables. See [karma-sauce-launcher project documentation](https://github.com/karma-runner/karma-sauce-launcher#username). Browsers used to test are specified in [karma.conf.js](karma.conf.js). You can run tests with all browsers: grunt karma Tests are run sequentially in batches of browsers to prevent timeouts in Sauce Labs. You can also run single set of browsers to see test results faster: grunt karma:sauce0 ## Release **Before releasing, make sure there are no uncommitted files, tests pass and jshint gives no errors.** Creating a new release of the package is simple: 1. Commit and push all changes 2. Run local tests and linters with `npm test` 3. Make sure Sauce Labs tests pass 4. Run `grunt release`, which will create new tag and publish code to GitHub Bower detects your new version of git tag. 5. Edit GitHub release notes By default, patch release is done. You can specify the version bump as a parameter: grunt release:major Valid version bump values: `major`, `minor`, `patch`. ## Decision log * Animate SVG paths with CSS3 transitions to make animations smooth. * API must provide built-in shapes and a way to use totally custom SVG. * Document manually. More overhead and risk of out dated information but easier to get started and contribute with pull requests. * Animate paths with JS because IE does not support CSS transitions for SVG properties. This also allows animation customizations and possible even using different easings per animation(in future). * Expose ProgressBar so it can be used with basic module loaders or as a global. * Bundle shifty inside the final distributable instead of requiring users to install both libs. If someone has already included shifty, then a custom build should be made. * Ship distributables to Bower. Fully automate releasing. * Ship distributables also to NPM to ease life of Browserify users. * Delegate shifty dependency handling to NPM instead of keeping it in repository but still bundle it inside the final distributable. * 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. ================================================ FILE: docs/index.md ================================================ # Get started *ProgressBar.js* is lightweight, MIT licensed and supports all major browsers including **IE11+** when using a polyfill. See complete examples in [full examples](#full-examples) section. ### IE 11 compatibility You can include ES6 polyfills by adding to your index.html: ``` ``` See https://github.com/jeremyckahn/shifty#ie-compatibility and https://polyfill.io/v3/url-builder/. #### {install} Installing * Using bower `bower install progressbar.js` * Using npm `npm install progressbar.js` * Including [*dist/progressbar.js*](https://github.com/kimmobrunfeldt/progressbar.js/blob/master/dist/progressbar.js) or [dist/progressbar.min.js](https://github.com/kimmobrunfeldt/progressbar.js/blob/master/dist/progressbar.min.js) from latest tag to your project. #### Loading module CommonJS ```javascript const ProgressBar = require('progressbar.js') const line = new ProgressBar.Line('#container'); ``` AMD ```javascript require.config({ paths: {'progressbar': '../bower_components/progressbar.js/dist/progressbar'} }); define(['progressbar'], function(ProgressBar) { var line = new ProgressBar.Line('#container'); }); ``` Global variable ```javascript // If you aren't using any module loader, progressbar.js exposes // global variable: window.ProgressBar var line = new ProgressBar.Line('#container'); ``` Files in `dist/` folder are UMD modules built with Browserify's `--standalone` switch. Read more about [standalone Browserify builds](http://www.forbeslindesay.co.uk/post/46324645400/standalone-browserify-builds). # How it works Progress bars are just regular SVG paths. Read [Jake Archibald's blog post](http://jakearchibald.com/2013/animated-line-drawing-svg/) to see how the path drawing works under the hood. *ProgressBar.js* uses [shifty](https://jeremyckahn.github.io/shifty/) tweening library to animate path drawing. So in other words, animation is done with JavaScript using [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame). Animating with JS gives more control over the animation and is supported across major browsers. For example IE [does not support](https://connect.microsoft.com/IE/feedbackdetail/view/920928/ie-11-css-transition-property-not-working-for-svg-elements) animating SVG properties with CSS transitions. ## {migrations} Migration guide To upgrade from version x to y: * Go to [releases page](https://github.com/kimmobrunfeldt/progressbar.js/releases) * Find the version x you are using * Browse through release notes from x to y. API breaking releases are marked with `#breaking`. * Fix your current code to use new API *If you find this very tedious, please open a new issue.* ## {full-examples} Full examples * [**Minimal**](http://kimmobrunfeldt.github.io/progressbar.js/examples/minimal/) [*see code*](https://github.com/kimmobrunfeldt/progressbar.js/tree/gh-pages/examples/minimal) * [**File upload**](http://kimmobrunfeldt.github.io/progressbar.js/examples/upload/) [*see code*](https://github.com/kimmobrunfeldt/progressbar.js/tree/gh-pages/examples/upload) * [**Telegram**](http://kimmobrunfeldt.github.io/progressbar.js/examples/telegram/) [*see code*](https://github.com/kimmobrunfeldt/progressbar.js/tree/gh-pages/examples/telegram) * [**Password strength**](http://kimmobrunfeldt.github.io/progressbar.js/examples/password-strength/) [*see code*](https://github.com/kimmobrunfeldt/progressbar.js/tree/gh-pages/examples/password-strength) # Contributing See [documentation for contributors](contributing/). ================================================ FILE: docs/pip-requirements.txt ================================================ backports-abc==0.4 backports.ssl-match-hostname==3.5.0.1 certifi==2015.11.20.1 click==6.2 Jinja2==2.8 livereload==2.4.1 Markdown==2.6.5 MarkupSafe==0.23 mdx-sections==0.1 mkdocs==0.15.3 mkdocs-bootstrap==0.1.1 mkdocs-bootswatch==0.2.0 pymdown-extensions==1.0.1 PyYAML==3.11 singledispatch==3.4.0.3 six==1.10.0 tornado==4.3 wheel==0.24.0 ================================================ FILE: karma.conf.js ================================================ var customLaunchers = require('./saucelabs-browsers'); module.exports = function(config) { config.set({ frameworks: ['polyfill', 'mocha', 'browserify'], polyfill: ['es6'], files: [ 'test/*.js' ], preprocessors: { 'test/*.js': ['browserify'] }, browserify: { debug: true }, port: 9876, colors: false, // Possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, autoWatch: false, singleRun: true, sauceLabs: { startConnect: true, testName: 'ProgressBar.js', build: process.env.TRAVIS_BUILD_NUMBER || 'manual', tunnelIdentifier: process.env.TRAVIS_BUILD_NUMBER, recordVideo: true }, customLaunchers: customLaunchers, browsers: Object.keys(customLaunchers), reporters: ['dots', 'saucelabs'], // Timeouts browserDisconnectTimeout: 30 * 1000, browserDisconnectTolerance: 3, browserNoActivityTimeout: 30 * 1000, captureTimeout: 120 * 1000 }); }; ================================================ FILE: local-dev/index.html ================================================ ProgressBar.js - Local dev

================================================ FILE: local-dev/main.css ================================================ html, body { height: 100%; width: 100%; } svg { display: block; height: 100%; } #progress { width: 300px; height: 300px; } ================================================ FILE: local-dev/main.js ================================================ var ProgressBar = require('../src/main.js'); function onLoad() { var bar = new ProgressBar.Circle('#progress', { duration: 4000, from: { color: '#f00' }, to: { color: '#0f0' }, step: function(state, bar) { bar.path.setAttribute('stroke', state.color); }, }); bar.animate(1); // Expose the bar to global so it is easy to test from console window.bar = bar; } window.onload = onLoad; ================================================ FILE: mkdocs.yml ================================================ site_name: ProgressBar.js repo_url: https://github.com/kimmobrunfeldt/progressbar.js pages: - Home: index.md - API References: - General information: api/general.md - ProgressBar.Line, Circle, SemiCircle: api/shape.md - ProgressBar.Path: api/path.md - Parameters in detail: api/parameters.md - Developers: - Contributing: contributing.md theme: readthedocs strict: true markdown_extensions: - admonition - toc: permalink: True - mdx_sections - pymdownx.superfences ================================================ FILE: package.json ================================================ { "name": "progressbar.js", "version": "1.1.1", "description": "Responsive and slick progress bars with animated SVG paths", "main": "dist/progressbar.js", "dependencies": { "lodash.merge": "^4.6.2", "shifty": "^2.8.3" }, "devDependencies": { "bluebird": "^2.3.6", "browserify": "^13.0.0", "commander": "^2.4.0", "concurrently": "^2.0.0", "eslint": "^1.0.0", "grunt": "^0.4.5", "grunt-cli": "^0.1.13", "grunt-contrib-jshint": "^0.10.0", "grunt-contrib-uglify": "^0.6.0", "grunt-karma": "^2.0.0", "grunt-shell": "^1.1.1", "jscs": "^2.0.0", "karma": "^2.0.2", "karma-browserify": "^5.2.0", "karma-mocha": "^1.3.0", "karma-polyfill": "^1.1.0", "karma-sauce-launcher": "^1.2.0", "lodash": "^2.4.1", "mocha": "^2.0.1", "mustache": "^2.3.0", "node-static": "^0.7.7", "np": "^5.1.2", "semver": "^4.1.0", "shelljs": "^0.3.0", "sinon": "~1.14.1", "string": "^2.2.0", "testem": "^1.6.0", "watchify": "^3.9.0" }, "scripts": { "test": "./tools/test.sh", "start": "npm run dev", "dev": "concurrently '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", "url": "git://github.com/kimmobrunfeldt/progressbar.js.git" }, "author": "Kimmo Brunfeldt (http://kimmobrunfeldt.github.io/)", "license": "MIT", "bugs": { "url": "https://github.com/kimmobrunfeldt/progressbar.js/issues" }, "homepage": "https://kimmobrunfeldt.github.io/progressbar.js/", "keywords": [ "progress", "bar", "js", "svg", "circular", "circle", "pace", "radial", "line", "loading", "loader", "semi-circle", "indicator" ] } ================================================ FILE: saucelabs-browsers.js ================================================ // Browsers on Sauce Labs // Check out https://saucelabs.com/platforms for all browser/platform combos // and https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/ module.exports = { // Chrome sauce_chrome: { base: 'SauceLabs', browserName: 'chrome', platform: 'macOS 10.15', version: 'latest' }, // Safari sauce_safari: { base: 'SauceLabs', browserName: 'safari', platform: 'macOS 10.15', version: 'latest' }, // Firefox sauce_firefox: { base: 'SauceLabs', browserName: 'firefox', platform: 'Windows 10', version: 'latest' }, // Internet explorer sauce_windows_ie_11: { base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 10', version: '11' }, sauce_windows_ie_10: { base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8', version: '10' }, // MS Edge sauce_windows_edge: { base: 'SauceLabs', browserName: 'MicrosoftEdge', platform: 'Windows 10', version: 'latest' }, }; ================================================ FILE: src/circle.js ================================================ // Circle shaped progress bar 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 this._pathTemplate = 'M 50,50 m 0,-{radius}' + ' a {radius},{radius} 0 1 1 0,{2radius}' + ' a {radius},{radius} 0 1 1 0,-{2radius}'; this.containerAspectRatio = 1; Shape.apply(this, arguments); }; Circle.prototype = new Shape(); Circle.prototype.constructor = Circle; Circle.prototype._pathString = function _pathString(opts) { var widthOfWider = opts.strokeWidth; if (opts.trailWidth && opts.trailWidth > opts.strokeWidth) { widthOfWider = opts.trailWidth; } var r = 50 - widthOfWider / 2; return utils.render(this._pathTemplate, { radius: r, '2radius': r * 2 }); }; Circle.prototype._trailString = function _trailString(opts) { return this._pathString(opts); }; module.exports = Circle; ================================================ FILE: src/line.js ================================================ // Line shaped progress bar var Shape = require('./shape'); var utils = require('./utils'); var Line = function Line(container, options) { this._pathTemplate = options.vertical ? 'M {center},100 L {center},0' : 'M 0,{center} L 100,{center}'; Shape.apply(this, arguments); }; Line.prototype = new Shape(); Line.prototype.constructor = Line; Line.prototype._initializeSvg = function _initializeSvg(svg, opts) { var viewBoxStr = opts.vertical ? '0 0 ' + opts.strokeWidth + ' 100' : '0 0 100 ' + opts.strokeWidth; svg.setAttribute('viewBox', viewBoxStr); svg.setAttribute('preserveAspectRatio', 'none'); }; Line.prototype._pathString = function _pathString(opts) { return utils.render(this._pathTemplate, { center: opts.strokeWidth / 2 }); }; Line.prototype._trailString = function _trailString(opts) { return this._pathString(opts); }; module.exports = Line; ================================================ FILE: src/main.js ================================================ module.exports = { // Higher level API, different shaped progress bars Line: require('./line'), Circle: require('./circle'), SemiCircle: require('./semicircle'), Square: require('./square'), // Lower level API to use any SVG path Path: require('./path'), // Base-class for creating new custom shapes // to be in line with the API of built-in shapes // Undocumented. Shape: require('./shape'), // Internal utils, undocumented. utils: require('./utils') }; ================================================ FILE: src/path.js ================================================ // Lower level API to animate any kind of svg path var shifty = require('shifty'); var utils = require('./utils'); var Tweenable = shifty.Tweenable; var EASING_ALIASES = { easeIn: 'easeInCubic', easeOut: 'easeOutCubic', easeInOut: 'easeInOutCubic' }; var Path = function Path(path, opts) { // Throw a better error if not initialized with `new` keyword if (!(this instanceof Path)) { throw new Error('Constructor was called without new keyword'); } // Default parameters for animation opts = utils.extend({ delay: 0, duration: 800, easing: 'linear', from: {}, to: {}, step: function() {} }, opts); var element; if (utils.isString(path)) { element = document.querySelector(path); } else { element = path; } // Reveal .path as public attribute this.path = element; this._opts = opts; this._tweenable = null; // Set up the starting positions var length = this.path.getTotalLength(); this.path.style.strokeDasharray = length + ' ' + length; this.set(0); }; Path.prototype.value = function value() { var offset = this._getComputedDashOffset(); var length = this.path.getTotalLength(); var progress = 1 - offset / length; // Round number to prevent returning very small number like 1e-30, which // is practically 0 return parseFloat(progress.toFixed(6), 10); }; Path.prototype.set = function set(progress) { this.stop(); this.path.style.strokeDashoffset = this._progressToOffset(progress); var step = this._opts.step; if (utils.isFunction(step)) { var easing = this._easing(this._opts.easing); var values = this._calculateTo(progress, easing); var reference = this._opts.shape || this; step(values, reference, this._opts.attachment); } }; Path.prototype.stop = function stop() { this._stopTween(); this.path.style.strokeDashoffset = this._getComputedDashOffset(); }; // Method introduced here: // http://jakearchibald.com/2013/animated-line-drawing-svg/ Path.prototype.animate = function animate(progress, opts, cb) { opts = opts || {}; if (utils.isFunction(opts)) { cb = opts; opts = {}; } var passedOpts = utils.extend({}, opts); // Copy default opts to new object so defaults are not modified var defaultOpts = utils.extend({}, this._opts); opts = utils.extend(defaultOpts, opts); var shiftyEasing = this._easing(opts.easing); var values = this._resolveFromAndTo(progress, shiftyEasing, passedOpts); this.stop(); // Trigger a layout so styles are calculated & the browser // picks up the starting position before animating this.path.getBoundingClientRect(); var offset = this._getComputedDashOffset(); var newOffset = this._progressToOffset(progress); var self = this; this._tweenable = new Tweenable(); this._tweenable.tween({ from: utils.extend({ offset: offset }, values.from), to: utils.extend({ offset: newOffset }, values.to), duration: opts.duration, delay: opts.delay, easing: shiftyEasing, step: function(state) { self.path.style.strokeDashoffset = state.offset; var reference = opts.shape || self; opts.step(state, reference, opts.attachment); } }).then(function(state) { if (utils.isFunction(cb)) { cb(); } }).catch(function(err) { console.error('Error in tweening:', err); throw err; }); }; Path.prototype._getComputedDashOffset = function _getComputedDashOffset() { var computedStyle = window.getComputedStyle(this.path, null); return parseFloat(computedStyle.getPropertyValue('stroke-dashoffset'), 10); }; Path.prototype._progressToOffset = function _progressToOffset(progress) { var length = this.path.getTotalLength(); return length - progress * length; }; // Resolves from and to values for animation. Path.prototype._resolveFromAndTo = function _resolveFromAndTo(progress, easing, opts) { if (opts.from && opts.to) { return { from: opts.from, to: opts.to }; } return { from: this._calculateFrom(easing), to: this._calculateTo(progress, easing) }; }; // Calculate `from` values from options passed at initialization Path.prototype._calculateFrom = function _calculateFrom(easing) { return shifty.interpolate(this._opts.from, this._opts.to, this.value(), easing); }; // Calculate `to` values from options passed at initialization Path.prototype._calculateTo = function _calculateTo(progress, easing) { return shifty.interpolate(this._opts.from, this._opts.to, progress, easing); }; Path.prototype._stopTween = function _stopTween() { if (this._tweenable !== null) { this._tweenable.stop(true); this._tweenable = null; } }; Path.prototype._easing = function _easing(easing) { if (EASING_ALIASES.hasOwnProperty(easing)) { return EASING_ALIASES[easing]; } return easing; }; module.exports = Path; ================================================ FILE: src/semicircle.js ================================================ // Semi-SemiCircle shaped progress bar 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 this._pathTemplate = 'M 50,50 m -{radius},0' + ' a {radius},{radius} 0 1 1 {2radius},0'; this.containerAspectRatio = 2; Shape.apply(this, arguments); }; SemiCircle.prototype = new Shape(); SemiCircle.prototype.constructor = SemiCircle; SemiCircle.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 50'); }; SemiCircle.prototype._initializeTextContainer = function _initializeTextContainer( opts, container, textContainer ) { if (opts.text.style) { // Reset top style textContainer.style.top = 'auto'; textContainer.style.bottom = '0'; if (opts.text.alignToBottom) { utils.setStyle(textContainer, 'transform', 'translate(-50%, 0)'); } else { utils.setStyle(textContainer, 'transform', 'translate(-50%, 50%)'); } } }; // Share functionality with Circle, just have different path SemiCircle.prototype._pathString = Circle.prototype._pathString; SemiCircle.prototype._trailString = Circle.prototype._trailString; module.exports = SemiCircle; ================================================ FILE: src/shape.js ================================================ // Base object for different progress bar shapes var Path = require('./path'); 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 if (!(this instanceof Shape)) { throw new Error('Constructor was called without new keyword'); } // Prevent calling constructor without parameters so inheritance // works correctly. To understand, this is how Shape is inherited: // // Line.prototype = new Shape(); // // We just want to set the prototype for Line. if (arguments.length === 0) { return; } // Default parameters for progress bar creation this._opts = utils.extend({ color: '#555', strokeWidth: 1.0, trailColor: null, trailWidth: null, fill: null, text: { style: { color: null, position: 'absolute', left: '50%', top: '50%', padding: 0, margin: 0, transform: { prefix: true, value: 'translate(-50%, -50%)' } }, autoStyleContainer: true, alignToBottom: true, value: null, className: 'progressbar-text' }, svgStyle: { display: 'block', width: '100%' }, warnings: false }, opts, true); // Use recursive extend // If user specifies e.g. svgStyle or text style, the whole object // should replace the defaults to make working with styles easier if (utils.isObject(opts) && opts.svgStyle !== undefined) { this._opts.svgStyle = opts.svgStyle; } if (utils.isObject(opts) && utils.isObject(opts.text) && opts.text.style !== undefined) { this._opts.text.style = opts.text.style; } var svgView = this._createSvgView(this._opts); var element; if (utils.isString(container)) { element = document.querySelector(container); } else { element = container; } if (!element) { throw new Error('Container does not exist: ' + container); } this._container = element; this._container.appendChild(svgView.svg); if (this._opts.warnings) { this._warnContainerAspectRatio(this._container); } if (this._opts.svgStyle) { utils.setStyles(svgView.svg, this._opts.svgStyle); } // Expose public attributes before Path initialization this.svg = svgView.svg; this.path = svgView.path; this.trail = svgView.trail; this.text = null; var newOpts = utils.extend({ attachment: undefined, shape: this }, this._opts); this._progressPath = new Path(svgView.path, newOpts); if (utils.isObject(this._opts.text) && this._opts.text.value !== null) { this.setText(this._opts.text.value); } }; Shape.prototype.animate = function animate(progress, opts, cb) { 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); } // Don't crash if stop is called inside step function if (this._progressPath === undefined) { return; } this._progressPath.stop(); }; Shape.prototype.pause = function pause() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this._progressPath === undefined) { return; } if (!this._progressPath._tweenable) { // It seems that we can't pause this return; } this._progressPath._tweenable.pause(); }; Shape.prototype.resume = function resume() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this._progressPath === undefined) { return; } if (!this._progressPath._tweenable) { // It seems that we can't resume this return; } this._progressPath._tweenable.resume(); }; Shape.prototype.destroy = function destroy() { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } this.stop(); this.svg.parentNode.removeChild(this.svg); this.svg = null; this.path = null; this.trail = null; this._progressPath = null; if (this.text !== null) { this.text.parentNode.removeChild(this.text); this.text = null; } }; Shape.prototype.set = function set(progress) { 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; } return this._progressPath.value(); }; Shape.prototype.setText = function setText(newText) { if (this._progressPath === null) { throw new Error(DESTROYED_ERROR); } if (this.text === null) { // Create new text node this.text = this._createTextContainer(this._opts, this._container); this._container.appendChild(this.text); } // Remove previous text and add new if (utils.isObject(newText)) { utils.removeChildren(this.text); this.text.appendChild(newText); } else { this.text.innerHTML = newText; } }; Shape.prototype._createSvgView = function _createSvgView(opts) { var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this._initializeSvg(svg, opts); var trailPath = null; // Each option listed in the if condition are 'triggers' for creating // the trail path if (opts.trailColor || opts.trailWidth) { trailPath = this._createTrail(opts); svg.appendChild(trailPath); } var path = this._createPath(opts); svg.appendChild(path); return { svg: svg, path: path, trail: trailPath }; }; Shape.prototype._initializeSvg = function _initializeSvg(svg, opts) { svg.setAttribute('viewBox', '0 0 100 100'); }; Shape.prototype._createPath = function _createPath(opts) { var pathString = this._pathString(opts); return this._createPathElement(pathString, opts); }; Shape.prototype._createTrail = function _createTrail(opts) { // Create path string with original passed options var pathString = this._trailString(opts); // Prevent modifying original 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; } newOpts.color = newOpts.trailColor; newOpts.strokeWidth = newOpts.trailWidth; // When trail path is set, fill must be set for it instead of the // actual path to prevent trail stroke from clipping newOpts.fill = null; return this._createPathElement(pathString, newOpts); }; Shape.prototype._createPathElement = function _createPathElement(pathString, opts) { var path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('d', pathString); path.setAttribute('stroke', opts.color); path.setAttribute('stroke-width', opts.strokeWidth); if (opts.fill) { path.setAttribute('fill', opts.fill); } else { path.setAttribute('fill-opacity', '0'); } return path; }; Shape.prototype._createTextContainer = function _createTextContainer(opts, container) { var textContainer = document.createElement('div'); textContainer.className = opts.text.className; var textStyle = opts.text.style; if (textStyle) { if (opts.text.autoStyleContainer) { container.style.position = 'relative'; } utils.setStyles(textContainer, textStyle); // Default text color to progress bar's color if (!textStyle.color) { textContainer.style.color = opts.color; } } this._initializeTextContainer(opts, container, textContainer); return textContainer; }; // Give custom shapes possibility to modify text element Shape.prototype._initializeTextContainer = function(opts, container, element) { // By default, no-op // Custom shapes should respect API options, such as text.style }; Shape.prototype._pathString = function _pathString(opts) { throw new Error('Override this function for each progress bar'); }; Shape.prototype._trailString = function _trailString(opts) { throw new Error('Override this function for each progress bar'); }; Shape.prototype._warnContainerAspectRatio = function _warnContainerAspectRatio(container) { if (!this.containerAspectRatio) { return; } var computedStyle = window.getComputedStyle(container, null); var width = parseFloat(computedStyle.getPropertyValue('width'), 10); var height = parseFloat(computedStyle.getPropertyValue('height'), 10); if (!utils.floatEquals(this.containerAspectRatio, width / height)) { console.warn( 'Incorrect aspect ratio of container', '#' + container.id, 'detected:', computedStyle.getPropertyValue('width') + '(width)', '/', computedStyle.getPropertyValue('height') + '(height)', '=', width / height ); console.warn( 'Aspect ratio of should be', this.containerAspectRatio ); } }; module.exports = Shape; ================================================ FILE: src/square.js ================================================ // Square shaped progress bar // Note: Square is not core part of API anymore. It's left here // for reference. square is not included to the progressbar // build anymore var Shape = require('./shape'); var utils = require('./utils'); var Square = function Square(container, options) { this._pathTemplate = 'M 0,{halfOfStrokeWidth}' + ' L {width},{halfOfStrokeWidth}' + ' L {width},{width}' + ' L {halfOfStrokeWidth},{width}' + ' L {halfOfStrokeWidth},{strokeWidth}'; this._trailTemplate = 'M {startMargin},{halfOfStrokeWidth}' + ' L {width},{halfOfStrokeWidth}' + ' L {width},{width}' + ' L {halfOfStrokeWidth},{width}' + ' L {halfOfStrokeWidth},{halfOfStrokeWidth}'; Shape.apply(this, arguments); }; Square.prototype = new Shape(); Square.prototype.constructor = Square; Square.prototype._pathString = function _pathString(opts) { var w = 100 - opts.strokeWidth / 2; return utils.render(this._pathTemplate, { width: w, strokeWidth: opts.strokeWidth, halfOfStrokeWidth: opts.strokeWidth / 2 }); }; Square.prototype._trailString = function _trailString(opts) { var w = 100 - opts.strokeWidth / 2; return utils.render(this._trailTemplate, { width: w, strokeWidth: opts.strokeWidth, halfOfStrokeWidth: opts.strokeWidth / 2, startMargin: opts.strokeWidth / 2 - opts.trailWidth / 2 }); }; module.exports = Square; ================================================ FILE: src/utils.js ================================================ // Utility functions var merge = require('lodash.merge'); var PREFIXES = 'Webkit Moz O ms'.split(' '); var FLOAT_COMPARISON_EPSILON = 0.001; // Renders templates with given variables. Variables must be surrounded with // braces without any spaces, e.g. {variable} // All instances of variable placeholders will be replaced with given content // Example: // render('Hello, {message}!', {message: 'world'}) function render(template, vars) { var rendered = template; for (var key in vars) { if (vars.hasOwnProperty(key)) { var val = vars[key]; var regExpString = '\\{' + key + '\\}'; var regExp = new RegExp(regExpString, 'g'); rendered = rendered.replace(regExp, val); } } return rendered; } function setStyle(element, style, value) { var elStyle = element.style; // cache for performance for (var i = 0; i < PREFIXES.length; ++i) { var prefix = PREFIXES[i]; elStyle[prefix + capitalize(style)] = value; } elStyle[style] = value; } function setStyles(element, styles) { forEachObject(styles, function(styleValue, styleName) { // Allow disabling some individual styles by setting them // to null or undefined if (styleValue === null || styleValue === undefined) { return; } // If style's value is {prefix: true, value: '50%'}, // Set also browser prefixed styles if (isObject(styleValue) && styleValue.prefix === true) { setStyle(element, styleName, styleValue.value); } else { element.style[styleName] = styleValue; } }); } function capitalize(text) { return text.charAt(0).toUpperCase() + text.slice(1); } function isString(obj) { return typeof obj === 'string' || obj instanceof String; } function isFunction(obj) { return typeof obj === 'function'; } function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; } // 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; } var type = typeof obj; return type === 'object' && !!obj; } function forEachObject(object, callback) { for (var key in object) { if (object.hasOwnProperty(key)) { var val = object[key]; callback(val, key); } } } function floatEquals(a, b) { return Math.abs(a - b) < FLOAT_COMPARISON_EPSILON; } // https://coderwall.com/p/nygghw/don-t-use-innerhtml-to-empty-dom-elements function removeChildren(el) { while (el.firstChild) { el.removeChild(el.firstChild); } } module.exports = { extend: merge, render: render, setStyle: setStyle, setStyles: setStyles, capitalize: capitalize, isString: isString, isFunction: isFunction, isObject: isObject, forEachObject: forEachObject, floatEquals: floatEquals, removeChildren: removeChildren }; ================================================ FILE: tools/lint.sh ================================================ #!/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 ================================================ FILE: tools/release.js ================================================ #!/usr/bin/env node // Release automation script inspired by // https://github.com/geddski/grunt-release var fs = require('fs'); var path = require('path'); var _ = require('lodash'); var S = require('string'); var shell = require('shelljs'); var Mustache = require('mustache'); var semver = require('semver'); var program = require('commander'); var Promise = require('bluebird'); // Message templates use https://github.com/janl/mustache.js var config = { indentation: 2, releaseMessage: 'Release {{ version }}', backToDevMessage: 'Bump to dev version', bumpType: 'patch', files: ['package.json'], readmeFile: 'README.md', // Banner to insert in beginning of distributables bannerFiles: ['dist/progressbar.js', 'dist/progressbar.min.js'], banner: '// ProgressBar.js {{ version }}\n// https://kimmobrunfeldt.github.io/progressbar.js\n// License: MIT\n\n', // If true, don't execute anything, just tell what would have been done dryRun: false, // If true, don't push commits/tags or release to npm noPush: false, consolePrefix: '->', devSuffix: '-dev' } var projectRoot = path.join(__dirname, '..'); process.chdir(projectRoot); function main() { parseArgs(); config = mergeArgsToDefaults(config); if (config.dryRun) status('Dry run\n'); var newVersion = bumpVersion(config.files, config.bumpType); var banner = Mustache.render(config.banner, { version: newVersion }); insertBanner(config.bannerFiles, banner); _gitBranchName() .then(function(stdout) { if (stdout.trim().toLowerCase() !== 'master') { throw new Error('You should be in master branch before running the script!'); } return gitAdd(config.bannerFiles); }) .then(function() { return gitAdd([config.readmeFile]); }) .then(function() { return gitAdd(config.files); }) .then(function() { var message = Mustache.render(config.releaseMessage, { version: newVersion }); return gitCommit(message); }) .then(function() { return gitTag(newVersion); }) .then(function() { return gitPushTag(newVersion); }) .then(npmPublish) .then(function() { bumpVersion(config.files, 'dev'); return gitAdd(config.files.concat(config.readmeFile)); }) .then(function() { return gitCommit(config.backToDevMessage); }) .then(function() { gitPush(); }) .then(function() { console.log(''); status('Release successfully done!'); }) .catch(function(err) { console.error('\n!! Releasing failed') console.trace(err); process.exit(2); }); } function parseArgs() { program .usage('bump') program.on('--help', function() { console.log(' Example usage:'); console.log(''); console.log(' $ ./release.js minor'); }); program.parse(process.argv); } function mergeArgsToDefaults(config) { if (program.args[0]) { config.bumpType = program.args[0]; if (!_.contains(['major', 'minor', 'patch'], config.bumpType)) { console.error('Error:', config.bumpType, 'is not a valid bump type'); process.exit(1); } } return config; } function status( /* arguments */ ) { var args = Array.prototype.slice.call(arguments); console.log(config.consolePrefix, args.join(' ')); } function run(cmd, msg) { // All calls are actually synchronous but eventually some task // will need async stuff, so keep them promises return new Promise(function(resolve, reject) { if (msg) { status(msg); } if (config.dryRun) { return resolve(); } var exec = shell.exec(cmd); var success = exec.code === 0; if (success) { resolve(exec.output); } else { var errMsg = 'Error executing: `' + cmd + '`\nOutput:\n' + exec.output; var err = new Error(errMsg); reject(err); } }); } // Task functions // All functions should return promise // Bumps version in specified files. // Files are assumed to contain JSON data which has "version" key following // semantic versioning function bumpVersion(files, bumpType) { status('Bump', bumpType, 'version to files:', files.join(' ')); if (config.dryRun) return '[not available in dry run]'; var newVersion; var originalVersion; files.forEach(function(fileName) { var filePath = path.join(projectRoot, fileName); var data = JSON.parse(fs.readFileSync(filePath)); originalVersion = data.version; var currentVersion = data.version; if (!semver.valid(currentVersion)) { var msg = 'Invalid version ' + currentVersion + ' in file ' + fileName;; var err = new Error(msg); throw err; } if (S(currentVersion).endsWith(config.devSuffix)) { currentVersion = S(currentVersion).chompRight(config.devSuffix).s; } if (bumpType === 'dev') { newVersion = currentVersion + config.devSuffix; } else { newVersion = semver.inc(currentVersion, bumpType); } data.version = newVersion; var content = JSON.stringify(data, null, config.indentation); fs.writeFileSync(filePath, content); status('Bump', originalVersion, '->', newVersion, 'in', fileName); }); bumpReadmeVersion(originalVersion, newVersion, bumpType); return newVersion; } function insertBanner(files, banner) { status('Insert banner to', files.length, 'files'); if (config.dryRun) return; _.each(files, function(fileName) { var filePath = path.join(projectRoot, fileName); var content = fs.readFileSync(filePath); var newContent = banner + content; fs.writeFileSync(filePath, newContent); }); } function bumpReadmeVersion(oldVersion, newVersion, bumpType) { if (bumpType === 'dev') { // Don't bump readme version in to dev version return; } var oldReleaseVersion = oldVersion; if (S(oldReleaseVersion).endsWith(config.devSuffix)) { oldReleaseVersion = S(oldReleaseVersion).chompRight(config.devSuffix).s; } status('Replace readme version', oldReleaseVersion, '->', newVersion); if (config.dryRun) return; var filePath = path.join(projectRoot, config.readmeFile); var content = fs.readFileSync(filePath, {encoding: 'utf-8'}); // Update visible version var re = new RegExp('Version: ' + oldReleaseVersion, 'g'); var newContent = content.replace(re, 'Version: ' + newVersion); // Replace link to previous stable re = new RegExp('tree/[0-9]\\.[0-9]\\.[0-9]'); newContent = newContent.replace(re, 'tree/' + oldReleaseVersion); fs.writeFileSync(filePath, newContent); } function gitAdd(files) { var cmd = 'git add ' + files.join(' '); var msg = 'Staged ' + files.length + ' files'; return run(cmd, msg); } function gitCommit(message) { var cmd = 'git commit -m "' + message + '"'; var msg = 'Commit files' return run(cmd, msg); } function gitTag(name) { var cmd = 'git tag ' + name; var msg = 'Created a new git tag: ' + name; return run(cmd, msg); } function gitPush() { if (config.noPush) return; var cmd = 'git push'; var msg = 'Push to remote'; return run(cmd, msg); } function gitPushTag(tagName) { if (config.noPush) return; var cmd = 'git push origin ' + tagName; var msg = 'Push created git tag to remote' return run(cmd, msg); } function gitCheckout(branch) { var cmd = 'git checkout ' + branch; var msg = 'Checkout branch ' + branch; return run(cmd, msg); } function npmPublish() { if (config.noPush) return; var cmd = 'npm publish'; var msg = 'Publish to npm'; return run(cmd, msg); } function _gitBranchName() { var cmd = 'git rev-parse --abbrev-ref HEAD' return run(cmd, false); } main(); ================================================ FILE: tools/test.sh ================================================ #!/bin/bash # NOTE: Run this only from project root! # Run all commands and if one fails, exit non-zero return code EXIT_STATUS=0 echo -e "\n---- Linting code..\n" npm run lint || EXIT_STATUS=$? exit $EXIT_STATUS