From ceac3a19fcdd08820249497328b6cde5fcf76443 Mon Sep 17 00:00:00 2001 From: progrape Date: Fri, 20 May 2016 13:17:53 +0800 Subject: [PATCH 1/5] test `util.hasChildren` --- package.json | 1 + test/unit/util.js | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/package.json b/package.json index 4fdd71f..12abc69 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "html-webpack-plugin": "^2.9.0", "iswiper": "^1.4.1", "jquery": "^2.2.3", + "jsdom": "^9.1.0", "less": "^2.6.1", "less-loader": "^2.2.2", "mocha": "^2.4.5", diff --git a/test/unit/util.js b/test/unit/util.js index 2ece8d9..edd29d4 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -1,4 +1,5 @@ import {expect} from 'chai'; +import {jsdom} from 'jsdom'; import * as util from '../../src/util'; describe('util.getHash', () => { @@ -80,4 +81,24 @@ describe('util.getRoute', () => { expect(route.params['userId']).to.be.equal(userId); expect(route.params['postId']).to.be.equal(postId); }); +}); + +describe('util.hasChildren', () => { + it('should have no children when body is empty', () => { + const document = jsdom(' '); + const body = document.body; + + const hasChildren = util.hasChildren(body); + + expect(hasChildren).to.be.equal(false); + }); + + it('should hav children when body is not empty', () => { + const document = jsdom('
router
'); + const body = document.body; + + const hasChildren = util.hasChildren(body); + + expect(hasChildren).to.be.equal(true); + }); }); \ No newline at end of file From d377004d4e861d71b9e2bc30e75b28d2fc726348 Mon Sep 17 00:00:00 2001 From: progrape Date: Fri, 20 May 2016 13:19:03 +0800 Subject: [PATCH 2/5] optimize test `util.hasChildren` --- test/unit/util.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/unit/util.js b/test/unit/util.js index edd29d4..2016220 100644 --- a/test/unit/util.js +++ b/test/unit/util.js @@ -93,7 +93,16 @@ describe('util.hasChildren', () => { expect(hasChildren).to.be.equal(false); }); - it('should hav children when body is not empty', () => { + it('should have no children when body is empty', () => { + const document = jsdom('hello'); + const body = document.body; + + const hasChildren = util.hasChildren(body); + + expect(hasChildren).to.be.equal(false); + }); + + it('should have children when body is not empty', () => { const document = jsdom('
router
'); const body = document.body; From 037b3dc8fac620162f9bbef460d51517a7cd408b Mon Sep 17 00:00:00 2001 From: progrape Date: Thu, 9 Jun 2016 13:37:03 +0800 Subject: [PATCH 3/5] check route is existed --- dist/example/example.js | 8 ++++++++ dist/router.min.js | 2 +- src/index.js | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/dist/example/example.js b/dist/example/example.js index 3e43172..4e89f70 100644 --- a/dist/example/example.js +++ b/dist/example/example.js @@ -175,6 +175,14 @@ }, { key: 'push', value: function push(route) { + + var exist = this._routes.filter(function (r) { + return r.url === route.url; + })[0]; + if (exist) { + throw new Error('route ' + route.url + ' is existed'); + } + route = _extends({}, { url: '*', className: '', diff --git a/dist/router.min.js b/dist/router.min.js index 57d3f49..8e1b454 100644 --- a/dist/router.min.js +++ b/dist/router.min.js @@ -3,4 +3,4 @@ * Copyright 2016 * Licensed under the MIT license */ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Router",[],t):"object"==typeof exports?exports.Router=t():e.Router=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";function r(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t["default"]=e,t}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var i=Object.assign||function(e){for(var t=1;t0?setTimeout(function(){e.parentNode.removeChild(e)},t._options.leaveTimeout):e.parentNode.removeChild(e)}()},i=function(o,i){var a=document.createElement("div");r.className&&a.classList.add(r.className),a.innerHTML=i,t._$container.appendChild(a),!n&&t._options.enter&&o&&a.classList.add(t._options.enter),t._options.enterTimeout>0?setTimeout(function(){a.classList.remove(t._options.enter)},t._options.enterTimeout):a.classList.remove(t._options.enter),location.hash="#"+e;try{n?t._index--:t._index++,history.replaceState&&history.replaceState({_index:t._index},"",location.href)}catch(u){}"function"==typeof r.bind&&r.bind.call(a)},a=s.hasChildren(t._$container);o(a);var u=function(e){var t=arguments.length<=1||void 0===arguments[1]?"":arguments[1];if(e)throw e;i(a,t)},l=r.render(u);l&&"function"==typeof l.then?l.then(function(e){u(null,e)},u):0===r.render.length&&u(null,l)}(),this}}]),e}();t["default"]=l,e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return-1!==e.indexOf("#")?e.substring(e.indexOf("#")+1):"/"}function i(e,t){for(var n=0,r=e.length;r>n;n++){var o=e[n],i=[],a=(0,l["default"])(o.url,i),u=a.exec(t);if(u){o.params={};for(var s=0,c=i.length;c>s;s++){var f=i[s],p=f.name;o.params[p]=u[s+1]}return o}}return null}function a(e){var t=e.children;return t.length>0}function u(){}Object.defineProperty(t,"__esModule",{value:!0}),t.getHash=o,t.getRoute=i,t.hasChildren=a,t.noop=u;var s=n(2),l=r(s)},function(e,t,n){function r(e){for(var t,n=[],r=0,o=0,i="";null!=(t=x.exec(e));){var a=t[0],u=t[1],l=t.index;if(i+=e.slice(o,l),o=l+a.length,u)i+=u[1];else{var c=e[o],f=t[2],p=t[3],h=t[4],d=t[5],v=t[6],g=t[7];null!=f&&null!=c&&c!==f&&(i+=f,f=null),i&&(n.push(i),i="");var y="+"===v||"*"===v,m="?"===v||"*"===v,_=t[2]||"/",w=h||d||(g?".*":"[^"+_+"]+?");n.push({name:p||r++,prefix:f||"",delimiter:_,optional:m,repeat:y,pattern:s(w)})}}return o0?setTimeout(function(){e.parentNode.removeChild(e)},t._options.leaveTimeout):e.parentNode.removeChild(e)}()},i=function(o,i){var a=document.createElement("div");r.className&&a.classList.add(r.className),a.innerHTML=i,t._$container.appendChild(a),!n&&t._options.enter&&o&&a.classList.add(t._options.enter),t._options.enterTimeout>0?setTimeout(function(){a.classList.remove(t._options.enter)},t._options.enterTimeout):a.classList.remove(t._options.enter),location.hash="#"+e;try{n?t._index--:t._index++,history.replaceState&&history.replaceState({_index:t._index},"",location.href)}catch(u){}"function"==typeof r.bind&&r.bind.call(a)},a=s.hasChildren(t._$container);o(a);var u=function(e){var t=arguments.length<=1||void 0===arguments[1]?"":arguments[1];if(e)throw e;i(a,t)},l=r.render(u);l&&"function"==typeof l.then?l.then(function(e){u(null,e)},u):0===r.render.length&&u(null,l)}(),this}}]),e}();t["default"]=l,e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return-1!==e.indexOf("#")?e.substring(e.indexOf("#")+1):"/"}function i(e,t){for(var n=0,r=e.length;r>n;n++){var o=e[n],i=[],a=(0,l["default"])(o.url,i),u=a.exec(t);if(u){o.params={};for(var s=0,c=i.length;c>s;s++){var f=i[s],p=f.name;o.params[p]=u[s+1]}return o}}return null}function a(e){var t=e.children;return t.length>0}function u(){}Object.defineProperty(t,"__esModule",{value:!0}),t.getHash=o,t.getRoute=i,t.hasChildren=a,t.noop=u;var s=n(2),l=r(s)},function(e,t,n){function r(e){for(var t,n=[],r=0,o=0,i="";null!=(t=x.exec(e));){var a=t[0],u=t[1],l=t.index;if(i+=e.slice(o,l),o=l+a.length,u)i+=u[1];else{var c=e[o],f=t[2],p=t[3],h=t[4],d=t[5],v=t[6],g=t[7];null!=f&&null!=c&&c!==f&&(i+=f,f=null),i&&(n.push(i),i="");var y="+"===v||"*"===v,_="?"===v||"*"===v,m=t[2]||"/",w=h||d||(g?".*":"[^"+m+"]+?");n.push({name:p||r++,prefix:f||"",delimiter:m,optional:_,repeat:y,pattern:s(w)})}}return o r.url === route.url)[0]; + if (exist) { + throw new Error(`route ${route.url} is existed`); + } + route = Object.assign({}, { url: '*', className: '', From 0da581406cbf98a3ff18515c966ec199e6894964 Mon Sep 17 00:00:00 2001 From: YongSheng Xu Date: Wed, 7 Sep 2016 01:09:15 +0800 Subject: [PATCH 4/5] fix https://github.com/progrape/router/issues/21 --- src/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/index.js b/src/index.js index d019aae..68439b6 100644 --- a/src/index.js +++ b/src/index.js @@ -42,7 +42,10 @@ class Router { // why not `history.pushState`? see https://github.com/weui/weui/issues/26, Router in wechat webview window.addEventListener('hashchange', (event) => { + const old_hash = util.getHash(event.oldURL); const hash = util.getHash(event.newURL); + // fix '/' repeat see https://github.com/progrape/router/issues/21 + if(old_hash === hash) return; const state = history.state || {}; this.go(hash, state._index <= this._index); From 543e6e7718aa95b270d19003033ddeb98823c85d Mon Sep 17 00:00:00 2001 From: YongSheng Xu Date: Thu, 8 Sep 2016 02:20:02 +0800 Subject: [PATCH 5/5] fix https://github.com/progrape/router/issues/21 --- dist/example/example.js | 691 ++++++++++++++++++++-------------------- dist/example/index.html | 2 +- dist/router.min.js | 2 +- 3 files changed, 342 insertions(+), 353 deletions(-) diff --git a/dist/example/example.js b/dist/example/example.js index 4e89f70..fc58c5a 100644 --- a/dist/example/example.js +++ b/dist/example/example.js @@ -95,7 +95,6 @@ /** * a very simple router for the **demo** of [weui](https://github.com/weui/weui) */ - var Router = function () { /** @@ -105,7 +104,6 @@ // array of route config - function Router(options) { _classCallCheck(this, Router); @@ -147,7 +145,10 @@ // why not `history.pushState`? see https://github.com/weui/weui/issues/26, Router in wechat webview window.addEventListener('hashchange', function (event) { + var old_hash = util.getHash(event.oldURL); var hash = util.getHash(event.newURL); + // fix '/' repeat see https://github.com/progrape/router/issues/21 + if (old_hash === hash) return; var state = history.state || {}; _this.go(hash, state._index <= _this._index); @@ -422,7 +423,7 @@ // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] - '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))' + '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))' ].join('|'), 'g') /** @@ -459,18 +460,13 @@ var modifier = res[6] var asterisk = res[7] - // Only use the prefix when followed by another path segment. - if (prefix != null && next != null && next !== prefix) { - path += prefix - prefix = null - } - // Push the current path onto the tokens. if (path) { tokens.push(path) path = '' } + var partial = prefix != null && next != null && next !== prefix var repeat = modifier === '+' || modifier === '*' var optional = modifier === '?' || modifier === '*' var delimiter = res[2] || '/' @@ -482,6 +478,8 @@ delimiter: delimiter, optional: optional, repeat: repeat, + partial: partial, + asterisk: !!asterisk, pattern: escapeGroup(pattern) }) } @@ -510,13 +508,25 @@ } /** - * Encode characters for segment that could cause trouble for parsing. + * Prettier encoding of URI path segments. * * @param {string} * @return {string} */ function encodeURIComponentPretty (str) { - return encodeURI(str).replace(/[/?#'"]/g, function (c) { + return encodeURI(str).replace(/[\/?#]/g, function (c) { + return '%' + c.charCodeAt(0).toString(16).toUpperCase() + }) + } + + /** + * Encode the asterisk parameter. Similar to `pretty`, but allows slashes. + * + * @param {string} + * @return {string} + */ + function encodeAsterisk (str) { + return encodeURI(str).replace(/[?#]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase() }) } @@ -531,7 +541,7 @@ // Compile all the patterns before compilation. for (var i = 0; i < tokens.length; i++) { if (typeof tokens[i] === 'object') { - matches[i] = new RegExp('^' + tokens[i].pattern + '$') + matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$') } } @@ -555,6 +565,11 @@ if (value == null) { if (token.optional) { + // Prepend partial segment prefixes. + if (token.partial) { + path += token.prefix + } + continue } else { throw new TypeError('Expected "' + token.name + '" to be defined') @@ -563,7 +578,7 @@ if (isarray(value)) { if (!token.repeat) { - throw new TypeError('Expected "' + token.name + '" to not repeat, but received "' + value + '"') + throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') } if (value.length === 0) { @@ -578,7 +593,7 @@ segment = encode(value[j]) if (!matches[i].test(segment)) { - throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') + throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`') } path += (j === 0 ? token.prefix : token.delimiter) + segment @@ -587,7 +602,7 @@ continue } - segment = encode(value) + segment = token.asterisk ? encodeAsterisk(value) : encode(value) if (!matches[i].test(segment)) { throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') @@ -607,7 +622,7 @@ * @return {string} */ function escapeString (str) { - return str.replace(/([.+*?=^!:${}()[\]|\/])/g, '\\$1') + return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1') } /** @@ -661,6 +676,8 @@ delimiter: null, optional: false, repeat: false, + partial: false, + asterisk: false, pattern: null }) } @@ -735,17 +752,17 @@ route += escapeString(token) } else { var prefix = escapeString(token.prefix) - var capture = token.pattern + var capture = '(?:' + token.pattern + ')' if (token.repeat) { capture += '(?:' + prefix + capture + ')*' } if (token.optional) { - if (prefix) { + if (!token.partial) { capture = '(?:' + prefix + '(' + capture + '))?' } else { - capture = '(' + capture + ')?' + capture = prefix + '(' + capture + ')?' } } else { capture = prefix + '(' + capture + ')' @@ -861,308 +878,308 @@ /* 7 */ /***/ function(module, exports) { - /* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra - */ - // css base code, injected by the css-loader - module.exports = function() { - var list = []; - - // return the list of modules as css string - list.toString = function toString() { - var result = []; - for(var i = 0; i < this.length; i++) { - var item = this[i]; - if(item[2]) { - result.push("@media " + item[2] + "{" + item[1] + "}"); - } else { - result.push(item[1]); - } - } - return result.join(""); - }; - - // import a list of modules into the list - list.i = function(modules, mediaQuery) { - if(typeof modules === "string") - modules = [[null, modules, ""]]; - var alreadyImportedModules = {}; - for(var i = 0; i < this.length; i++) { - var id = this[i][0]; - if(typeof id === "number") - alreadyImportedModules[id] = true; - } - for(i = 0; i < modules.length; i++) { - var item = modules[i]; - // skip already imported module - // this implementation is not 100% perfect for weird media query combinations - // when a module is imported multiple times with different media queries. - // I hope this will never occur (Hey this way we have smaller bundles) - if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { - if(mediaQuery && !item[2]) { - item[2] = mediaQuery; - } else if(mediaQuery) { - item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; - } - list.push(item); - } - } - }; - return list; - }; + /* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra + */ + // css base code, injected by the css-loader + module.exports = function() { + var list = []; + + // return the list of modules as css string + list.toString = function toString() { + var result = []; + for(var i = 0; i < this.length; i++) { + var item = this[i]; + if(item[2]) { + result.push("@media " + item[2] + "{" + item[1] + "}"); + } else { + result.push(item[1]); + } + } + return result.join(""); + }; + + // import a list of modules into the list + list.i = function(modules, mediaQuery) { + if(typeof modules === "string") + modules = [[null, modules, ""]]; + var alreadyImportedModules = {}; + for(var i = 0; i < this.length; i++) { + var id = this[i][0]; + if(typeof id === "number") + alreadyImportedModules[id] = true; + } + for(i = 0; i < modules.length; i++) { + var item = modules[i]; + // skip already imported module + // this implementation is not 100% perfect for weird media query combinations + // when a module is imported multiple times with different media queries. + // I hope this will never occur (Hey this way we have smaller bundles) + if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) { + if(mediaQuery && !item[2]) { + item[2] = mediaQuery; + } else if(mediaQuery) { + item[2] = "(" + item[2] + ") and (" + mediaQuery + ")"; + } + list.push(item); + } + } + }; + return list; + }; /***/ }, /* 8 */ /***/ function(module, exports, __webpack_require__) { - /* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra - */ - var stylesInDom = {}, - memoize = function(fn) { - var memo; - return function () { - if (typeof memo === "undefined") memo = fn.apply(this, arguments); - return memo; - }; - }, - isOldIE = memoize(function() { - return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase()); - }), - getHeadElement = memoize(function () { - return document.head || document.getElementsByTagName("head")[0]; - }), - singletonElement = null, - singletonCounter = 0, - styleElementsInsertedAtTop = []; - - module.exports = function(list, options) { - if(false) { - if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment"); - } - - options = options || {}; - // Force single-tag solution on IE6-9, which has a hard limit on the # of