diff --git a/dist/switcheroo.amd.js b/dist/switcheroo.amd.js new file mode 100644 index 0000000..ed51a0e --- /dev/null +++ b/dist/switcheroo.amd.js @@ -0,0 +1,284 @@ +define('switcheroo', ['react'], function (React) { 'use strict'; + +var React__default = 'default' in React ? React['default'] : React; + +function currentPath(location) { + var path = decodeURI(window.location[location].slice(1).split('?')[0]); + if (path.charAt(0) !== '/') { + return '/' + path; + } else { + return path; + } +} + +function removeTrailingSlash(path) { + if (path === '/') { + return path; + } else { + return path.slice(-1) !== '/' ? path : path.slice(0, -1); + } +} + +function replaceDynamicSegments(path) { + return path.replace(/\/:[^\/]+/g, '/([^/]+)'); +} + +function getDynamicSegmentNames(path) { + var dynamicSegementNames = path.match(/:[^\/]+/g) || []; + return dynamicSegementNames.map(function (name) { + return name.substr(1); + }); +} + +function formatPathRegex(basePath, path) { + return replaceDynamicSegments(removeTrailingSlash(basePath + path) + '/?'); +} + +function createRegexFromPaths(paths) { + return new RegExp('^(' + paths.join('|') + ')$'); +} + +function getSwitch(path, _ref) { + var children = _ref.children; + var basePath = _ref.basePath; + + var consistentPath = removeTrailingSlash(path); + var switches = [].concat(children || []); + return switches.filter(function (child) { + var childPaths = [].concat(child.props.path).map(function (childPath) { + return formatPathRegex(basePath, childPath); + }); + var regex = createRegexFromPaths(childPaths); + return regex.test(consistentPath); + })[0] || null; +} + +function getDynamicSegments(path, basePath, swtch) { + var dynamicValues = {}; + var consistentPath = removeTrailingSlash(path); + if (swtch) { + [].concat(swtch.props.path).forEach(function (childPath) { + var dynamicSegments = getDynamicSegmentNames(basePath + childPath); + var regexStr = formatPathRegex(basePath, childPath); + var matches = consistentPath.match(new RegExp('^' + regexStr + '$')); + if (matches) { + matches.shift(); + dynamicSegments.forEach(function (segment, index) { + dynamicValues[segment] = matches[index]; + }); + } + }); + } + return dynamicValues; +} + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; +} : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + +var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; +}; + +var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; +}; + +var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; +}; + +var Switcher = function (_Component) { + inherits(Switcher, _Component); + + function Switcher(props) { + classCallCheck(this, Switcher); + + var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Switcher).call(this, props)); + + _initialiseProps.call(_this); + + var currPath = currentPath(props.location); + var switchElement = getSwitch(currPath, props); + var dynamicValues = getDynamicSegments(currPath, props.basePath, switchElement); + _this.state = { + visibleSwitch: switchElement, + dynamicValues: dynamicValues + }; + return _this; + } + + createClass(Switcher, [{ + key: 'componentDidMount', + value: function componentDidMount() { + if (this.props.load) { + window.addEventListener('load', this.handleRouteChange); + } + if (this.props.pushState) { + window.addEventListener('popstate', this.handleRouteChange); + } + if (this.props.hashChange) { + window.addEventListener('hashchange', this.handleRouteChange); + } + } + }, { + key: 'componentWillReceiveProps', + value: function componentWillReceiveProps(nextProps) { + this.handleSwitchChange(nextProps); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps) { + return !nextProps.preventUpdate(); + } + }, { + key: 'componentWillUnmount', + value: function componentWillUnmount() { + if (this.props.load) { + window.removeEventListener('load', this.handleRouteChange); + } + if (this.props.pushState) { + window.removeEventListener('popstate', this.handleRouteChange); + } + if (this.props.hashChange) { + window.removeEventListener('hashchange', this.handleRouteChange); + } + } + }, { + key: 'render', + value: function render() { + var _this2 = this; + + var _ref = this.state.visibleSwitch || {}; + + var props = _ref.props; + + var visibleSwitch = this.state.visibleSwitch && React__default.cloneElement(this.state.visibleSwitch, _extends({}, props, this.props.mapDynamicSegments(this.state.dynamicValues))); + + if (this.props.renderSwitch) { + return this.props.renderSwitch(visibleSwitch, this.state.dynamicValues); + } + + if (this.props.wrapper) { + var _ret = function () { + var passedProps = _extends({}, _this2.props); + Object.keys(Switcher.propTypes).forEach(function (k) { + return delete passedProps[k]; + }); + return { + v: React__default.createElement(_this2.props.wrapper, passedProps, visibleSwitch) + }; + }(); + + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + } else { + return visibleSwitch; + } + } + }]); + return Switcher; +}(React.Component); + +Switcher.displayName = 'Switcher'; +Switcher.propTypes = { + children: React.PropTypes.oneOfType([React.PropTypes.arrayOf(React.PropTypes.node), React.PropTypes.node]), + pushState: React.PropTypes.bool, + hashChange: React.PropTypes.bool, + load: React.PropTypes.bool, + onChange: React.PropTypes.func, + wrapper: React.PropTypes.any, + location: React.PropTypes.string, + basePath: React.PropTypes.string, + preventUpdate: React.PropTypes.func, + mapDynamicSegments: React.PropTypes.func, + renderSwitch: React.PropTypes.func +}; +Switcher.defaultProps = { + pushState: false, + hashChange: true, + load: true, + location: 'hash', + basePath: '', + preventUpdate: function preventUpdate() { + return false; + }, + mapDynamicSegments: function mapDynamicSegments(values) { + return values; + } +}; + +var _initialiseProps = function _initialiseProps() { + var _this3 = this; + + this.handleSwitchChange = function (props) { + var currPath = currentPath(props.location); + var visibleSwitch = getSwitch(currPath, props); + var dynamicValues = getDynamicSegments(currPath, props.basePath, visibleSwitch); + + if (typeof props.onChange === 'function') { + props.onChange(!!visibleSwitch, currPath, dynamicValues); + } + + _this3.setState({ visibleSwitch: visibleSwitch, dynamicValues: dynamicValues }); + }; + + this.handleRouteChange = function (ev) { + _this3.handleSwitchChange(_this3.props); + }; +}; + +return Switcher; + +}); \ No newline at end of file diff --git a/dist/switcheroo.browser.js b/dist/switcheroo.browser.js new file mode 100644 index 0000000..aedbcb6 --- /dev/null +++ b/dist/switcheroo.browser.js @@ -0,0 +1,285 @@ +var switcheroo = (function (React) { +'use strict'; + +var React__default = 'default' in React ? React['default'] : React; + +function currentPath(location) { + var path = decodeURI(window.location[location].slice(1).split('?')[0]); + if (path.charAt(0) !== '/') { + return '/' + path; + } else { + return path; + } +} + +function removeTrailingSlash(path) { + if (path === '/') { + return path; + } else { + return path.slice(-1) !== '/' ? path : path.slice(0, -1); + } +} + +function replaceDynamicSegments(path) { + return path.replace(/\/:[^\/]+/g, '/([^/]+)'); +} + +function getDynamicSegmentNames(path) { + var dynamicSegementNames = path.match(/:[^\/]+/g) || []; + return dynamicSegementNames.map(function (name) { + return name.substr(1); + }); +} + +function formatPathRegex(basePath, path) { + return replaceDynamicSegments(removeTrailingSlash(basePath + path) + '/?'); +} + +function createRegexFromPaths(paths) { + return new RegExp('^(' + paths.join('|') + ')$'); +} + +function getSwitch(path, _ref) { + var children = _ref.children; + var basePath = _ref.basePath; + + var consistentPath = removeTrailingSlash(path); + var switches = [].concat(children || []); + return switches.filter(function (child) { + var childPaths = [].concat(child.props.path).map(function (childPath) { + return formatPathRegex(basePath, childPath); + }); + var regex = createRegexFromPaths(childPaths); + return regex.test(consistentPath); + })[0] || null; +} + +function getDynamicSegments(path, basePath, swtch) { + var dynamicValues = {}; + var consistentPath = removeTrailingSlash(path); + if (swtch) { + [].concat(swtch.props.path).forEach(function (childPath) { + var dynamicSegments = getDynamicSegmentNames(basePath + childPath); + var regexStr = formatPathRegex(basePath, childPath); + var matches = consistentPath.match(new RegExp('^' + regexStr + '$')); + if (matches) { + matches.shift(); + dynamicSegments.forEach(function (segment, index) { + dynamicValues[segment] = matches[index]; + }); + } + }); + } + return dynamicValues; +} + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; +} : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; +}; + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + +var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; +}; + +var inherits = function (subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; +}; + +var possibleConstructorReturn = function (self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (typeof call === "object" || typeof call === "function") ? call : self; +}; + +var Switcher = function (_Component) { + inherits(Switcher, _Component); + + function Switcher(props) { + classCallCheck(this, Switcher); + + var _this = possibleConstructorReturn(this, Object.getPrototypeOf(Switcher).call(this, props)); + + _initialiseProps.call(_this); + + var currPath = currentPath(props.location); + var switchElement = getSwitch(currPath, props); + var dynamicValues = getDynamicSegments(currPath, props.basePath, switchElement); + _this.state = { + visibleSwitch: switchElement, + dynamicValues: dynamicValues + }; + return _this; + } + + createClass(Switcher, [{ + key: 'componentDidMount', + value: function componentDidMount() { + if (this.props.load) { + window.addEventListener('load', this.handleRouteChange); + } + if (this.props.pushState) { + window.addEventListener('popstate', this.handleRouteChange); + } + if (this.props.hashChange) { + window.addEventListener('hashchange', this.handleRouteChange); + } + } + }, { + key: 'componentWillReceiveProps', + value: function componentWillReceiveProps(nextProps) { + this.handleSwitchChange(nextProps); + } + }, { + key: 'shouldComponentUpdate', + value: function shouldComponentUpdate(nextProps) { + return !nextProps.preventUpdate(); + } + }, { + key: 'componentWillUnmount', + value: function componentWillUnmount() { + if (this.props.load) { + window.removeEventListener('load', this.handleRouteChange); + } + if (this.props.pushState) { + window.removeEventListener('popstate', this.handleRouteChange); + } + if (this.props.hashChange) { + window.removeEventListener('hashchange', this.handleRouteChange); + } + } + }, { + key: 'render', + value: function render() { + var _this2 = this; + + var _ref = this.state.visibleSwitch || {}; + + var props = _ref.props; + + var visibleSwitch = this.state.visibleSwitch && React__default.cloneElement(this.state.visibleSwitch, _extends({}, props, this.props.mapDynamicSegments(this.state.dynamicValues))); + + if (this.props.renderSwitch) { + return this.props.renderSwitch(visibleSwitch, this.state.dynamicValues); + } + + if (this.props.wrapper) { + var _ret = function () { + var passedProps = _extends({}, _this2.props); + Object.keys(Switcher.propTypes).forEach(function (k) { + return delete passedProps[k]; + }); + return { + v: React__default.createElement(_this2.props.wrapper, passedProps, visibleSwitch) + }; + }(); + + if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + } else { + return visibleSwitch; + } + } + }]); + return Switcher; +}(React.Component); + +Switcher.displayName = 'Switcher'; +Switcher.propTypes = { + children: React.PropTypes.oneOfType([React.PropTypes.arrayOf(React.PropTypes.node), React.PropTypes.node]), + pushState: React.PropTypes.bool, + hashChange: React.PropTypes.bool, + load: React.PropTypes.bool, + onChange: React.PropTypes.func, + wrapper: React.PropTypes.any, + location: React.PropTypes.string, + basePath: React.PropTypes.string, + preventUpdate: React.PropTypes.func, + mapDynamicSegments: React.PropTypes.func, + renderSwitch: React.PropTypes.func +}; +Switcher.defaultProps = { + pushState: false, + hashChange: true, + load: true, + location: 'hash', + basePath: '', + preventUpdate: function preventUpdate() { + return false; + }, + mapDynamicSegments: function mapDynamicSegments(values) { + return values; + } +}; + +var _initialiseProps = function _initialiseProps() { + var _this3 = this; + + this.handleSwitchChange = function (props) { + var currPath = currentPath(props.location); + var visibleSwitch = getSwitch(currPath, props); + var dynamicValues = getDynamicSegments(currPath, props.basePath, visibleSwitch); + + if (typeof props.onChange === 'function') { + props.onChange(!!visibleSwitch, currPath, dynamicValues); + } + + _this3.setState({ visibleSwitch: visibleSwitch, dynamicValues: dynamicValues }); + }; + + this.handleRouteChange = function (ev) { + _this3.handleSwitchChange(_this3.props); + }; +}; + +return Switcher; + +}(React)); \ No newline at end of file diff --git a/dist/switcheroo.browser.min.js b/dist/switcheroo.browser.min.js new file mode 100644 index 0000000..59c354c --- /dev/null +++ b/dist/switcheroo.browser.min.js @@ -0,0 +1 @@ +var switcheroo=function(e){"use strict";function t(e){var t=decodeURI(window.location[e].slice(1).split("?")[0]);return"/"!==t.charAt(0)?"/"+t:t}function n(e){return"/"===e?e:"/"!==e.slice(-1)?e:e.slice(0,-1)}function o(e){return e.replace(/\/:[^\/]+/g,"/([^/]+)")}function r(e){var t=e.match(/:[^\/]+/g)||[];return t.map(function(e){return e.substr(1)})}function a(e,t){return o(n(e+t)+"/?")}function i(e){return new RegExp("^("+e.join("|")+")$")}function s(e,t){var o=t.children,r=t.basePath,s=n(e),p=[].concat(o||[]);return p.filter(function(e){var t=[].concat(e.props.path).map(function(e){return a(r,e)}),n=i(t);return n.test(s)})[0]||null}function p(e,t,o){var i={},s=n(e);return o&&[].concat(o.props.path).forEach(function(e){var n=r(t+e),o=a(t,e),p=s.match(new RegExp("^"+o+"$"));p&&(p.shift(),n.forEach(function(e,t){i[e]=p[t]}))}),i}var c="default"in e?e.default:e,u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},h=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},l=function(){function e(e,t){for(var n=0;n dist/switcheroo.browser.min.js", + "minify-umd": "$(npm bin)/uglifyjs dist/switcheroo.umd.js -cm > dist/switcheroo.umd.min.js", "lint:src": "$(npm bin)/eslint ./src", "lint:test": "$(npm bin)/eslint ./test", "lint": "npm run lint:src && npm run lint:test", @@ -39,6 +43,7 @@ "babel-loader": "6.2.4", "babel-plugin-transform-runtime": "6.7.5", "babel-preset-es2015": "6.6.0", + "babel-preset-es2015-rollup": "1.2.0", "babel-preset-react": "6.5.0", "babel-preset-stage-0": "6.5.0", "babel-runtime": "6.6.1", @@ -50,8 +55,11 @@ "mocha": "2.4.5", "react": "15.0.0", "react-dom": "15.0.0", + "rollup": "0.34.10", + "rollup-plugin-babel": "2.6.1", + "rollup-plugin-replace": "1.1.1", "sinon": "1.17.3", - "webpack": "1.12.14" + "uglify-js": "2.7.3" }, "peerDependencies": { "react": "^0.14.0 || ^15.0.0" diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..1f29bc8 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,28 @@ +import babel from 'rollup-plugin-babel'; +import replace from 'rollup-plugin-replace'; + +export default { + entry: 'src/index.js', + plugins: [ + babel({ + babelrc: false, + presets: ['es2015-rollup', 'stage-0'] + }), + replace({ + 'process.env.NODE_DEBUG': false, + 'process.env.NODE_ENV': 'production' + }) + ], + globals: { + react: 'React' + }, + moduleName: 'switcheroo', + moduleId: 'switcheroo', + targets: [ + {format: 'umd', dest: 'dist/switcheroo.umd.js'}, + {format: 'iife', dest: 'dist/switcheroo.browser.js'}, + {format: 'amd', dest: 'dist/switcheroo.amd.js'}, + {format: 'cjs', dest: 'dist/switcheroo.cjs.js'}, + {format: 'es', dest: 'dist/switcheroo.es-modules.js'} + ] +}; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 1c6d569..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,40 +0,0 @@ -var webpack = require('webpack'); - -module.exports = { - entry: './src/index', - module: { - loaders: [ - { test: /\.js$/, loader: 'babel', exclude: /node_modules/ } - ] - }, - output: { - filename: 'dist/switcheroo.min.js', - libraryTarget: 'umd', - library: 'switcheroo' - }, - resolve: { - extensions: ['', '.js'], - modulesDirectories: ['node_modules', 'src'], - fallback: __dirname - }, - externals: [ - { - "react": { - root: "React", - commonjs2: "react", - commonjs: "react", - amd: "react" - } - } - ], - plugins: [ - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), - new webpack.optimize.DedupePlugin(), - new webpack.optimize.UglifyJsPlugin() - ] -};