From 48681229410fd495e8f549c5ae1058468c6d58ae Mon Sep 17 00:00:00 2001 From: tonho Date: Tue, 29 Mar 2016 09:13:17 -0300 Subject: [PATCH 1/7] moving to mocha --- test/specs/route-basePath.js | 30 +++++++------- test/specs/route-beutifyLocation.js | 32 +++++++-------- test/specs/route-createRules.js | 8 ++-- test/specs/route-hashnavigation.js | 64 ++++++++++++++--------------- test/specs/route-methods.js | 2 +- test/specs/route.js | 8 ++-- 6 files changed, 72 insertions(+), 72 deletions(-) diff --git a/test/specs/route-basePath.js b/test/specs/route-basePath.js index 295e41b..8bf6e73 100644 --- a/test/specs/route-basePath.js +++ b/test/specs/route-basePath.js @@ -35,49 +35,49 @@ describe('PushStateTree basePath should', function() { var pst = new PushStateTree({ basePath: '/test/' }); - expect(pst.basePath).toEqual('/test/'); + expect(pst.basePath).to.equal('/test/'); }); it('normalize basePath folder', function(){ expect((new PushStateTree({ basePath: 'folder/' - })).basePath).toEqual('/folder/'); + })).basePath).to.equal('/folder/'); expect((new PushStateTree({ basePath: 'folder/sub-folder/' - })).basePath).toEqual('/folder/sub-folder/'); + })).basePath).to.equal('/folder/sub-folder/'); }); it('normalize basePath file', function(){ expect((new PushStateTree({ basePath: 'file' - })).basePath).toEqual('/file'); + })).basePath).to.equal('/file'); expect((new PushStateTree({ basePath: '/file' - })).basePath).toEqual('/file'); + })).basePath).to.equal('/file'); expect((new PushStateTree({ basePath: 'folder/file' - })).basePath).toEqual('/folder/file'); + })).basePath).to.equal('/folder/file'); expect((new PushStateTree({ basePath: '/folder/file' - })).basePath).toEqual('/folder/file'); + })).basePath).to.equal('/folder/file'); expect((new PushStateTree({ basePath: 'folder/file.ext' - })).basePath).toEqual('/folder/file.ext'); + })).basePath).to.equal('/folder/file.ext'); expect((new PushStateTree({ basePath: '/folder/file.ext' - })).basePath).toEqual('/folder/file.ext'); + })).basePath).to.equal('/folder/file.ext'); expect((new PushStateTree({ basePath: 'folder/file.ext?param=value' - })).basePath).toEqual('/folder/file.ext?param=value'); + })).basePath).to.equal('/folder/file.ext?param=value'); expect((new PushStateTree({ basePath: '/folder/file.ext?param=value' - })).basePath).toEqual('/folder/file.ext?param=value'); + })).basePath).to.equal('/folder/file.ext?param=value'); }); it('use the non relative root as basePath if not specified', function(){ history.pushState(null, null, '/abc/123/'); var pst = new PushStateTree(); - expect(pst.basePath).toEqual('/'); + expect(pst.basePath).to.equal('/'); }); it('not share the basePath between route instances', function(){ @@ -87,8 +87,8 @@ describe('PushStateTree basePath should', function() { var pst2 = new PushStateTree({ basePath: '2/' }); - expect(pst1.basePath).toEqual('/1/'); - expect(pst2.basePath).toEqual('/2/'); + expect(pst1.basePath).to.equal('/1/'); + expect(pst2.basePath).to.equal('/2/'); }); -}); \ No newline at end of file +}); diff --git a/test/specs/route-beutifyLocation.js b/test/specs/route-beutifyLocation.js index 3ba1992..557a86c 100644 --- a/test/specs/route-beutifyLocation.js +++ b/test/specs/route-beutifyLocation.js @@ -49,7 +49,7 @@ describe('PushStateTree beutifyLocation should', function() { beautifyLocation: false }); location.hash = '#test'; - expect(pst.uri).toEqual('test'); + expect(pst.uri).to.equal('test'); }); it('remove the first slash from the URI in the regular URL', function(){ @@ -57,8 +57,8 @@ describe('PushStateTree beutifyLocation should', function() { beautifyLocation: false }); history.pushState(null, null, '/test'); - expect(location.pathname).toEqual('/test'); - expect(pst.uri).toEqual('test'); + expect(location.pathname).to.equal('/test'); + expect(pst.uri).to.equal('test'); }); it('remove the first slash from the URI in the location.hash', function(){ @@ -66,8 +66,8 @@ describe('PushStateTree beutifyLocation should', function() { beautifyLocation: false }); location.hash = '/test'; - expect(location.hash).toEqual('#/test'); - expect(pst.uri).toEqual('test'); + expect(location.hash).to.equal('#/test'); + expect(pst.uri).to.equal('test'); }); it('redirect from the hash to path when beautifyLocation is enabled', function(){ @@ -78,14 +78,14 @@ describe('PushStateTree beutifyLocation should', function() { // Reset URL var randomURI = Math.random() + ''; history.pushState(null, null, '/' + randomURI); - expect(pst.uri).toEqual(randomURI); - expect(location.pathname).toEqual('/' + randomURI); + expect(pst.uri).to.equal(randomURI); + expect(location.pathname).to.equal('/' + randomURI); location.hash = '/abc'; - expect(pst.uri).toEqual('abc'); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal('abc'); + expect(location.hash).to.equal(''); - expect(location.pathname).toEqual('/abc'); + expect(location.pathname).to.equal('/abc'); }); it('not apply beautifyLocation when the basePath is not fulfilled', function(){ @@ -95,8 +95,8 @@ describe('PushStateTree beutifyLocation should', function() { basePath: '/test/' }); location.hash = '/abc'; - expect(pst.uri).toEqual('abc'); - expect(location.hash).toEqual('#/abc'); + expect(pst.uri).to.equal('abc'); + expect(location.hash).to.equal('#/abc'); }); it('apply beautifyLocation when the basePath is fulfilled', function(){ @@ -106,8 +106,8 @@ describe('PushStateTree beutifyLocation should', function() { basePath: '/test/' }); location.hash = '/abc'; - expect(pst.uri).toEqual('abc'); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal('abc'); + expect(location.hash).to.equal(''); }); it('no change if usePushState is false', function(){ @@ -116,8 +116,8 @@ describe('PushStateTree beutifyLocation should', function() { usePushState: false }); pst.navigate('test2'); - expect(pst.uri).toEqual('test2'); - expect(location.hash).toEqual('#test2'); + expect(pst.uri).to.equal('test2'); + expect(location.hash).to.equal('#test2'); }); }); diff --git a/test/specs/route-createRules.js b/test/specs/route-createRules.js index cdf7d23..74a4b0f 100644 --- a/test/specs/route-createRules.js +++ b/test/specs/route-createRules.js @@ -37,23 +37,23 @@ describe('PushStateTree createRule', function() { }); it('should add the id attribute to the node', function() { - expect(rule.id).toEqual(idRule); + expect(rule.id).to.equal(idRule); }); it('should create a get function which returns the regex rule', function() { - expect(rule.rule).toEqual(regexRule); + expect(rule.rule).to.equal(regexRule); }); describe('when a set rule function is created', function() { it('should change regex value ', function() { rule.rule = /^faq(\/)?(.*)/; - expect(rule.rule).toEqual(/^faq(\/)?(.*)/); + expect(rule.rule).to.equal(/^faq(\/)?(.*)/); }); it('should convert string into regex format', function() { rule.rule = '^faq(\\/)?(.*)'; - expect(rule.rule).toEqual(/^faq(\/)?(.*)/); + expect(rule.rule).to.equal(/^faq(\/)?(.*)/); }); it('should avoid recursive loop', function() { diff --git a/test/specs/route-hashnavigation.js b/test/specs/route-hashnavigation.js index 001735e..6ec4328 100644 --- a/test/specs/route-hashnavigation.js +++ b/test/specs/route-hashnavigation.js @@ -43,7 +43,7 @@ describe('PushStateTree hash-navigation should', function() { usePushState: false }); location.hash = '#test'; - expect(pst.uri).toEqual('test'); + expect(pst.uri).to.equal('test'); }); it('increment the history length when using navigate method', function(){ @@ -52,7 +52,7 @@ describe('PushStateTree hash-navigation should', function() { }); var currentLength = history.length; pst.navigate('test'); - expect(history.length).toEqual(currentLength + 1); + expect(history.length).to.equal(currentLength + 1); }); it('not increment the history length when using replace method', function(){ @@ -61,7 +61,7 @@ describe('PushStateTree hash-navigation should', function() { }); var currentLength = history.length; pst.replace('test'); - expect(history.length).toEqual(currentLength); + expect(history.length).to.equal(currentLength); }); it('change the hash address when pushstate is disabled', function(){ @@ -69,8 +69,8 @@ describe('PushStateTree hash-navigation should', function() { usePushState: false }); pst.navigate('test2'); - expect(pst.uri).toEqual('test2'); - expect(location.hash).toEqual('#test2'); + expect(pst.uri).to.equal('test2'); + expect(location.hash).to.equal('#test2'); }); it('go to the current folder when use ./ in the command', function(){ @@ -79,10 +79,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/file'; - expect(pst.uri).toEqual('folder/file'); + expect(pst.uri).to.equal('folder/file'); pst.navigate('./'); - expect(pst.uri).toEqual('folder/'); - expect(location.hash).toEqual('#folder/'); + expect(pst.uri).to.equal('folder/'); + expect(location.hash).to.equal('#folder/'); }); it('go to the parent folder when use ../ in the command from a file', function(){ @@ -91,10 +91,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/file'; - expect(pst.uri).toEqual('folder/file'); + expect(pst.uri).to.equal('folder/file'); pst.navigate('../'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the parent folder when use ../ in the command from sub-folder', function(){ @@ -103,10 +103,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/sub-folder/'; - expect(pst.uri).toEqual('folder/sub-folder/'); + expect(pst.uri).to.equal('folder/sub-folder/'); pst.navigate('../'); - expect(pst.uri).toEqual('folder/'); - expect(location.hash).toEqual('#folder/'); + expect(pst.uri).to.equal('folder/'); + expect(location.hash).to.equal('#folder/'); }); it('ignore ./ if is already in the root folder', function(){ @@ -115,10 +115,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#/'; - expect(pst.uri).toEqual(''); + expect(pst.uri).to.equal(''); pst.navigate('./'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('ignore ../ if is already in the root folder', function(){ @@ -127,10 +127,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#/'; - expect(pst.uri).toEqual(''); + expect(pst.uri).to.equal(''); pst.navigate('../'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('stop in the root folder when there is more parent folder commands them possible', function(){ @@ -139,10 +139,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/'; - expect(pst.uri).toEqual('folder/'); + expect(pst.uri).to.equal('folder/'); pst.navigate('../../'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the root folder when use / in the command from file', function(){ @@ -151,10 +151,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/file'; - expect(pst.uri).toEqual('folder/file'); + expect(pst.uri).to.equal('folder/file'); pst.navigate('/'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the root folder when use / in the command from a folder', function(){ @@ -163,10 +163,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/'; - expect(pst.uri).toEqual('folder/'); + expect(pst.uri).to.equal('folder/'); pst.navigate('/'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the root folder when use / in the command from a sub-folder', function(){ @@ -175,9 +175,9 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/sub-folder/'; - expect(pst.uri).toEqual('folder/sub-folder/'); + expect(pst.uri).to.equal('folder/sub-folder/'); pst.navigate('/'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); }); diff --git a/test/specs/route-methods.js b/test/specs/route-methods.js index 4259934..88721f7 100644 --- a/test/specs/route-methods.js +++ b/test/specs/route-methods.js @@ -49,7 +49,7 @@ describe('PushStateTree methods should', function() { 'replace', 'navigate' ].forEach(function(method){ - expect(methods).toContain(method); + expect(methods).to.contain(method); }); }); diff --git a/test/specs/route.js b/test/specs/route.js index e8ea7a9..453a434 100644 --- a/test/specs/route.js +++ b/test/specs/route.js @@ -37,12 +37,12 @@ describe('PushStateTree should', function() { it('instances without "new" operator', function() { /* jshint newcap: false*/ - expect(PushStateTree()).toEqual(jasmine.any(HTMLElement)); + expect(PushStateTree()).to.equal(jasmine.any(HTMLElement)); }); it('construct and became a HTMLElement instance', function(){ - expect(new PushStateTree()).toEqual(jasmine.any(HTMLElement)); - expect(new PushStateTree({})).toEqual(jasmine.any(HTMLElement)); + expect(new PushStateTree()).to.equal(jasmine.any(HTMLElement)); + expect(new PushStateTree({})).to.equal(jasmine.any(HTMLElement)); }); it('auto enable push state if browser support it', function(){ @@ -88,7 +88,7 @@ describe('PushStateTree should', function() { it('get the current URI', function(){ var pst = new PushStateTree(); - expect(pst.uri).toEqual(location.pathname.slice(1)); + expect(pst.uri).to.equal(location.pathname.slice(1)); }); }); From 2a8ec0108567f348ea9e6cfdffdc2c85c4d55dd8 Mon Sep 17 00:00:00 2001 From: tonho Date: Tue, 29 Mar 2016 10:35:13 -0300 Subject: [PATCH 2/7] changed to mocha + chai --- karma.conf.js | 2 +- package.json | 11 ++++++++--- test/specs/route-basePath.js | 4 ++-- test/specs/route-beutifyLocation.js | 10 +++++----- test/specs/route-createRules.js | 28 +++++----------------------- test/specs/route-hashnavigation.js | 6 +++--- test/specs/route-methods.js | 4 ++-- test/specs/route.js | 28 ++++++++++++++-------------- 8 files changed, 40 insertions(+), 53 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 5c03d30..917a2bf 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -5,7 +5,7 @@ module.exports = function (config) { basePath: '.', // frameworks to use - frameworks: ['jasmine'], + frameworks: ['mocha', 'chai-spies', 'chai'], // list of files / patterns to load in the browser files: [ diff --git a/package.json b/package.json index 9cc4ab2..be9f52d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ }, "devDependencies": { "assemble": "~0.4.42", + "chai": "^3.5.0", + "chai-spies": "^0.7.1", "codeclimate-test-reporter": "0.3.1", "grunt": "~0.4.5", "grunt-contrib-clean": "~1.0.0", @@ -44,16 +46,18 @@ "grunt-qunit-junit": "^0.3.1", "grunt-template-jasmine-istanbul": "~0.4.0", "grunt-update-json": "~0.2.1", - "jasmine-core": "^2.4.1", "jshint-junit-reporter": "0.2.2", "jshint-stylish": "~2.1.0", "karma": "^0.13.22", + "karma-chai": "^0.1.0", + "karma-chai-spies": "^0.1.4", "karma-chrome-launcher": "^0.2.2", "karma-coverage": "^0.5.5", - "karma-jasmine": "^0.3.7", + "karma-mocha": "^0.2.2", "karma-phantomjs-launcher": "^1.0.0", "karma-script-launcher": "^0.1.0", "load-grunt-tasks": "~3.4.1", + "mocha": "^2.4.5", "node-static": "^0.7.7", "phantomjs-prebuilt": "^2.1.4" }, @@ -82,5 +86,6 @@ "shim", "navigator", "navigation" - ] + ], + "dependencies": {} } diff --git a/test/specs/route-basePath.js b/test/specs/route-basePath.js index 8bf6e73..fed08a6 100644 --- a/test/specs/route-basePath.js +++ b/test/specs/route-basePath.js @@ -1,4 +1,4 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +/*globals PushStateTree, it, expect, beforeEach, before */ describe('PushStateTree basePath should', function() { 'use strict'; @@ -10,7 +10,7 @@ describe('PushStateTree basePath should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); diff --git a/test/specs/route-beutifyLocation.js b/test/specs/route-beutifyLocation.js index 557a86c..7800052 100644 --- a/test/specs/route-beutifyLocation.js +++ b/test/specs/route-beutifyLocation.js @@ -1,4 +1,4 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +/*globals PushStateTree, it, expect, beforeEach, before */ describe('PushStateTree beutifyLocation should', function() { 'use strict'; @@ -10,7 +10,7 @@ describe('PushStateTree beutifyLocation should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); @@ -33,15 +33,15 @@ describe('PushStateTree beutifyLocation should', function() { it('not enable beautifyLocation feature by default', function(){ var pst = new PushStateTree(); - expect(pst.beautifyLocation).toBeFalsy(); + expect(pst.beautifyLocation).to.be.false; }); it('allow to change the beautifyLocation flag after start running', function(){ var pst = new PushStateTree(); pst.beautifyLocation = false; - expect(pst.beautifyLocation).toBeFalsy(); + expect(pst.beautifyLocation).to.be.false; pst.beautifyLocation = true; - expect(pst.beautifyLocation).toBeTruthy(); + expect(pst.beautifyLocation).to.be.true; }); it('prioritise the hash to provide the URI', function(){ diff --git a/test/specs/route-createRules.js b/test/specs/route-createRules.js index 74a4b0f..7c90083 100644 --- a/test/specs/route-createRules.js +++ b/test/specs/route-createRules.js @@ -1,23 +1,5 @@ -var customMatchers = { - toBeInstanceOf: function(util, customEqualityTesters) { - return { - compare: function(actual, expected) { - var result = {}; - result.pass = actual instanceof expected; - if (!result.pass) { - return "Expected " + actual.constructor.name + notText + " is instance of " + expectedInstance.name; - } - return result; - } - }; - } -} - describe('PushStateTree createRule', function() { 'use strict'; - beforeEach(function() { - jasmine.addMatchers(customMatchers); - }); describe('when rule /^servers(\/)?(.*)/', function() { var pst; @@ -33,7 +15,7 @@ describe('PushStateTree createRule', function() { }); it('should create the html node', function() { - expect(rule).toBeInstanceOf(HTMLElement); + expect(rule).to.be.instanceof(HTMLElement); }); it('should add the id attribute to the node', function() { @@ -48,18 +30,18 @@ describe('PushStateTree createRule', function() { it('should change regex value ', function() { rule.rule = /^faq(\/)?(.*)/; - expect(rule.rule).to.equal(/^faq(\/)?(.*)/); + expect(rule.rule.toString()).to.equal(/^faq(\/)?(.*)/.toString()); }); it('should convert string into regex format', function() { rule.rule = '^faq(\\/)?(.*)'; - expect(rule.rule).to.equal(/^faq(\/)?(.*)/); + expect(rule.rule).to.be.instanceof(RegExp); }); it('should avoid recursive loop', function() { - spyOn(rule, 'setAttribute'); + chai.spy.on(rule, 'setAttribute'); rule.rule = '/^servers(\\/)?(.*)/'; - expect(rule.setAttribute).not.toHaveBeenCalled(); + expect(rule.setAttribute).not.to.have.been.called; }); }); diff --git a/test/specs/route-hashnavigation.js b/test/specs/route-hashnavigation.js index 6ec4328..29b88ea 100644 --- a/test/specs/route-hashnavigation.js +++ b/test/specs/route-hashnavigation.js @@ -1,4 +1,4 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +/*globals PushStateTree, it, expect, beforeEach, before */ describe('PushStateTree hash-navigation should', function() { 'use strict'; @@ -10,7 +10,7 @@ describe('PushStateTree hash-navigation should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); @@ -35,7 +35,7 @@ describe('PushStateTree hash-navigation should', function() { var pst = new PushStateTree({ usePushState: false }); - expect(pst.usePushState).toBeFalsy(); + expect(pst.usePushState).to.be.false; }); it('detect the hash address', function(){ diff --git a/test/specs/route-methods.js b/test/specs/route-methods.js index 88721f7..e282631 100644 --- a/test/specs/route-methods.js +++ b/test/specs/route-methods.js @@ -1,4 +1,4 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +/*globals PushStateTree, it, expect, beforeEach, before */ describe('PushStateTree methods should', function() { 'use strict'; @@ -10,7 +10,7 @@ describe('PushStateTree methods should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); diff --git a/test/specs/route.js b/test/specs/route.js index 453a434..3f7fd10 100644 --- a/test/specs/route.js +++ b/test/specs/route.js @@ -1,4 +1,4 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +/*globals PushStateTree, it, expect, beforeEach, before */ describe('PushStateTree should', function() { 'use strict'; @@ -10,7 +10,7 @@ describe('PushStateTree should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); @@ -32,49 +32,49 @@ describe('PushStateTree should', function() { }); it('be available on global scope', function() { - expect(PushStateTree).toBeDefined(); + expect(PushStateTree).to.be.defined; }); it('instances without "new" operator', function() { /* jshint newcap: false*/ - expect(PushStateTree()).to.equal(jasmine.any(HTMLElement)); + expect(PushStateTree()).to.be.instanceof(HTMLElement); }); it('construct and became a HTMLElement instance', function(){ - expect(new PushStateTree()).to.equal(jasmine.any(HTMLElement)); - expect(new PushStateTree({})).to.equal(jasmine.any(HTMLElement)); + expect(new PushStateTree()).to.be.instanceof(HTMLElement); + expect(new PushStateTree({})).to.be.instanceof(HTMLElement); }); it('auto enable push state if browser support it', function(){ var pst = new PushStateTree(); - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('allow to disable push state if it constructor has a false option', function(){ var pst = new PushStateTree({ usePushState: false }); - expect(pst.usePushState).toBeFalsy(); + expect(pst.usePushState).to.be.false; }); it('allow to enable push state if it constructor has a true option', function(){ var pst = new PushStateTree({ usePushState: true }); - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('usePushState be true by default', function(){ var pst = new PushStateTree(); - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('allow to change the usePushState flag after start running', function(){ var pst = new PushStateTree(); pst.usePushState = false; - expect(pst.usePushState).toBeFalsy(); + expect(pst.usePushState).to.be.false; pst.usePushState = true; - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('usePushState change for each instance', function(){ @@ -82,8 +82,8 @@ describe('PushStateTree should', function() { usePushState: false }); var pst2 = new PushStateTree(); - expect(pst1.usePushState).toBeFalsy(); - expect(pst2.usePushState).toBeTruthy(); + expect(pst1.usePushState).to.be.false; + expect(pst2.usePushState).to.be.true; }); it('get the current URI', function(){ From 6994cef92d045196b5fd340f50d08b779900cfe6 Mon Sep 17 00:00:00 2001 From: Gabriel Reitz Giannattasio Date: Tue, 29 Mar 2016 07:02:37 -0700 Subject: [PATCH 3/7] Add webpack and remove grunt --- .codeclimate.yml | 3 +- .gitignore | 2 + .jshintrc | 4 +- .travis.yml | 4 +- Gruntfile.js | 149 --- bower.json | 1 - component.json | 1 - karma.conf.js | 82 +- package.json | 40 +- push-state-tree.js | 1096 +++++++++++++++++++++ push-state-tree.js.map | 1 + push-state-tree.min.js | 6 + server.js | 21 - src/pushStateTree.js | 1 - test/{specs => }/route-basePath.js | 3 +- test/{specs => }/route-beutifyLocation.js | 3 +- test/{specs => }/route-createRules.js | 2 + test/{specs => }/route-hashnavigation.js | 3 +- test/{specs => }/route-methods.js | 3 +- test/{specs => }/route.js | 3 +- test/rule.js | 1 + test/specs/rule.js | 0 webpack.config.js | 74 ++ 23 files changed, 1296 insertions(+), 207 deletions(-) delete mode 100644 Gruntfile.js delete mode 100644 bower.json delete mode 100644 component.json create mode 100644 push-state-tree.js create mode 100644 push-state-tree.js.map create mode 100644 push-state-tree.min.js delete mode 100644 server.js rename test/{specs => }/route-basePath.js (97%) rename test/{specs => }/route-beutifyLocation.js (98%) rename test/{specs => }/route-createRules.js (97%) rename test/{specs => }/route-hashnavigation.js (98%) rename test/{specs => }/route-methods.js (95%) rename test/{specs => }/route.js (97%) create mode 100644 test/rule.js delete mode 100644 test/specs/rule.js create mode 100644 webpack.config.js diff --git a/.codeclimate.yml b/.codeclimate.yml index 5a95ad6..b41d48a 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -5,7 +5,8 @@ languages: Python: false exclude_paths: - "demo/*" - - "server.js" + - "karma.config.js" + - "webpack.config.js" - "test/*" - "push-state-tree.js" - "push-state-tree.min.js" diff --git a/.gitignore b/.gitignore index b6a5f6f..6d242d0 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,5 @@ npm-debug.log .grunt report/ _SpecRunner.html + +build/ diff --git a/.jshintrc b/.jshintrc index 3df1d99..e90d3fd 100644 --- a/.jshintrc +++ b/.jshintrc @@ -25,6 +25,7 @@ "boss": true, "browser": true, "evil": false, + "esnext": true, "globals": { "jQuery": true, "console": false, @@ -36,6 +37,7 @@ "beforeEach": true, "spyOn": true, "jasmine": true, - "PushStateTree": true + "PushStateTree": true, + "VERSION": true } } \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 0212c2e..39d6bfc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,9 +8,9 @@ addons: before_install: npm install -g grunt-cli install: npm install before_script: - - grunt + - npm test script: - - grunt coveralls + - cat ./build/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js after_script: - codeclimate < report/coverage/lcov/lcov.info sudo: false diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 8474604..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,149 +0,0 @@ -/** - * pushStateTree - * https://github.com/gartz/pushStateTree/ - * Copyright (c) 2014 Gabriel Reitz Giannattasio - * Licensed under the MIT license. - */ -/*jslint node: true */ -'use strict'; - -module.exports = function(grunt) { - - grunt.initConfig({ - meta: { - pkg: grunt.file.readJSON('package.json'), - name: '<%= meta.pkg.name %>', - src: { - main: 'src/**/*.js', - test: 'test/**/*.js' - }, - build: 'build', - coverage: '<%= meta.build %>/coverage', - - //TODO: Move dist to build - dist: '<%= meta.pkg.name %>.min.js', - - banner: '//! <%= meta.pkg.title || meta.name %> - v<%= meta.pkg.version %> - ' + - '<%= grunt.template.today("yyyy-mm-dd") %>\n' + - '//<%= meta.pkg.homepage ? "* " + meta.pkg.homepage + "\\n" : "" %>' + - '//* Copyright (c) <%= grunt.template.today("yyyy") %> <%= meta.pkg.author.name %>;' + - ' Licensed <%= meta.pkg.license %>\n\n' + - 'var PushStateTree = {options: {VERSION: \'<%= meta.pkg.version %>\'}};\n' - }, - clean: { - test: [ - '<%= meta.coverage %>' - ], - concat: [ - '<%= concat.dist.dest %>' - ], - uglify: [ - '<%= meta.dist %>.min.js' - ] - }, - concat: { - options: { - banner: '<%= meta.banner %>', - separator: ';' - }, - dist: { - src: '<%= meta.src.main %>', - dest: '<%= meta.name %>.js' - } - }, - uglify: { - options: { - banner: '<%= meta.banner %>' - }, - dist: { - files: { - '<%= meta.dist %>': ['<%= concat.dist.dest %>'] - } - } - }, - coveralls: { - options: { - // don't fail if coveralls fails - force: true - }, - mainTarget: { - src: '<%= meta.coverage %>/lcov.info' - } - }, - 'update_json': { - bower: { - src: 'package.json', - dest: 'bower.json', - fields: [ - 'name', - 'version', - 'description', - 'repository' - ] - }, - component: { - src: 'package.json', - dest: 'component.json', - fields: { - 'name': null, - 'repository': 'repo', - 'description': null, - 'version': null, - 'keywords': null, - 'main': null, - 'dependencies': null, - 'development': 'devDependencies', - 'license': null - } - } - }, - watchTask: { - files: [ - '<%= meta.src.main %>', - '<%= meta.src.test %>', - 'Gruntfile.js' - ], - tasks: [ - 'update_json', - 'karma:unit:run' - ] - }, - karma: { - options: { - configFile: 'karma.conf.js' - }, - unit: { - browsers: ['PhantomJS', 'Chrome', 'Firefox'], - background: true, - singleRun: false - }, - continuous: { - singleRun: true - } - } - }); - - require('load-grunt-tasks')(grunt, { - pattern: ['grunt-*', '!grunt-template-jasmine-istanbul'] - }); - - grunt.task.renameTask('watch', 'watchTask'); - - grunt.registerTask('watch', 'Run a watch that test the code on every change', [ - 'karma:unit:start', - 'watchTask' - ]); - - grunt.registerTask('test', 'Run tests and code-coverage then print the summary.', [ - 'clean:test', - 'karma:continuous' - ]); - - grunt.registerTask('default', [ - 'update_json', - 'clean', - 'test', - 'concat', - 'uglify' - ]); -}; diff --git a/bower.json b/bower.json deleted file mode 100644 index 839414b..0000000 --- a/bower.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"push-state-tree","main":"pushStateTree.js","dependencies":{},"version":"0.14.1","description":"A standalone powerful library to manage browser routing with nested level support, complex match expressions and on-fly rules change (convenient to lazy module loading).","repository":{"type":"git","url":"https://github.com/gartz/pushStateTree.git"}} diff --git a/component.json b/component.json deleted file mode 100644 index fd2b154..0000000 --- a/component.json +++ /dev/null @@ -1 +0,0 @@ -{"name":"push-state-tree","description":"A standalone powerful library to manage browser routing with nested level support, complex match expressions and on-fly rules change (convenient to lazy module loading).","version":"0.14.1","keywords":["route","routes","router","pushstate","hashchange","history","location","navigation","controller","MVC","event","events","dispatch","url","SPA","Single Page Application","Lazy Load","apps","app","routing","URI","shim","navigator","navigation"],"main":"push-state-tree.js","development":{"assemble":"~0.4.42","grunt":"~0.4.5","grunt-contrib-clean":"~1.0.0","grunt-contrib-concat":"~1.0.0","grunt-contrib-connect":"^1.0.1","grunt-contrib-jasmine":"^1.0.0","grunt-contrib-jshint":"~1.0.0","grunt-contrib-nodeunit":"~1.0.0","grunt-contrib-qunit":"~1.1.0","grunt-contrib-rename":"0.0.3","grunt-contrib-uglify":"~1.0.1","grunt-contrib-watch":"~1.0.0","grunt-coveralls":"^1.0.0","grunt-json-generator":"~0.1.0","grunt-merge-json":"~0.9.4","grunt-template-jasmine-istanbul":"~0.4.0","grunt-update-json":"~0.2.1","jshint-junit-reporter":"0.2.2","jshint-stylish":"~2.1.0","load-grunt-tasks":"~3.4.1","node-static":"^0.7.7","codeclimate-test-reporter":"0.3.1","grunt-qunit-junit":"^0.3.1","grunt-karma":"^0.12.1","karma":"^0.13.22","karma-chrome-launcher":"^0.2.2","karma-jasmine":"^0.3.7","karma-phantomjs-launcher":"^1.0.0","karma-script-launcher":"^0.1.0","phantomjs-prebuilt":"^2.1.4","jasmine-core":"^2.4.1","karma-coverage":"^0.5.5"}} diff --git a/karma.conf.js b/karma.conf.js index 5c03d30..053193c 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,3 +1,66 @@ +'use strict'; + +const webpackDevServer = require('webpack-dev-server'); +const webpack = require('webpack'); +const webpackConfig = require('./webpack.config'); +const yargs = require('yargs'); +const open = require('open'); +const path = require('path'); + +yargs.options({ + 'watch': { + type: 'boolean', + describe: 'Start a dev-server on port 8080 and karma server on port 9876' + }, + 'dev-port': { + type: 'numeric', + describe: 'The dev-server port', + default: 8080 + }, + 'open': { + type: 'boolean', + describe: 'Open the default browser' + } +}); + +let argv = yargs.argv; + +if (argv.watch) { + let port = argv['dev-port'] || 8080; + + const webpackDevConfig = Object.assign({ + cache: true, + devtool: 'hidden-source-map' + }, webpackConfig); + + webpackDevConfig.entry['push-state-tree'] = [ + webpackConfig.entry['push-state-tree'], + `webpack-dev-server/client?http://localhost:${port}/` + ]; + + var compiler = webpack(webpackDevConfig); + var server = new webpackDevServer(compiler, { + historyApiFallback: true, + quiet: true, + noInfo: true, + inline: true, + stats: { colors: true }, + headers: { 'X-SourceMap': '/push-state-tree.js.map' } + }); + server.listen(port, err => { + if (err) throw err; + + var uri = `http://localhost:${port}/`; + console.log(uri); + + console.log(`webpack result is served from ${path.resolve('.')}`); + console.log('404s will fallback to /index.html'); + + if (argv.open) + open(uri); + }); +} + module.exports = function (config) { config.set({ @@ -9,8 +72,7 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ - 'src/*.js', - 'test/specs/*.js' + 'test/**/*.js' ], // list of files to exclude @@ -23,7 +85,19 @@ module.exports = function (config) { // source files, that you wanna generate coverage for // do not include tests or libraries // (these files will be instrumented by Istanbul) - 'src/*.js': ['coverage'] + 'src/**/*.js': ['coverage'], + 'test/**/*.js': ['webpack', 'sourcemap'] + }, + + webpack: { + plugins: webpackConfig.plugins, + devtool: 'inline-source-map' + }, + + webpackMiddleware: { + stats: { + colors: true + } }, coverageReporter: { @@ -65,6 +139,6 @@ module.exports = function (config) { // Continuous Integration mode // if true, it capture browsers, run tests and exit - singleRun: true + singleRun: !argv.watch }); }; diff --git a/package.json b/package.json index 9cc4ab2..e4a9949 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "push-state-tree", + "title": "PushStateTree", "description": "A standalone powerful library to manage browser routing with nested level support, complex match expressions and on-fly rules change (convenient to lazy module loading).", - "version": "0.14.1", + "version": "0.15.0", "homepage": "https://github.com/gartz/pushStateTree/", "author": { "name": "Gabriel Reitz Giannattasio ", @@ -25,37 +26,34 @@ "node": ">= 5.5.0" }, "scripts": { - "start": "node ./server.js", - "test": "grunt test" + "start": "karma start --watch", + "prepublish": "karma start && webpack --publish", + "preversion": "karma start && webpack --publish", + "test": "karma start" }, "devDependencies": { - "assemble": "~0.4.42", + "babel-core": "^6.7.4", + "babel-loader": "^6.2.4", "codeclimate-test-reporter": "0.3.1", - "grunt": "~0.4.5", - "grunt-contrib-clean": "~1.0.0", - "grunt-contrib-concat": "~1.0.0", - "grunt-contrib-jshint": "~1.0.0", - "grunt-contrib-uglify": "~1.0.1", - "grunt-contrib-watch": "~1.0.0", - "grunt-coveralls": "^1.0.0", - "grunt-json-generator": "~0.1.0", - "grunt-karma": "^0.12.1", - "grunt-merge-json": "~0.9.4", - "grunt-qunit-junit": "^0.3.1", - "grunt-template-jasmine-istanbul": "~0.4.0", - "grunt-update-json": "~0.2.1", + "coveralls": "^2.11.9", + "eslint": "^2.5.3", + "eslint-loader": "^1.3.0", "jasmine-core": "^2.4.1", - "jshint-junit-reporter": "0.2.2", - "jshint-stylish": "~2.1.0", "karma": "^0.13.22", "karma-chrome-launcher": "^0.2.2", "karma-coverage": "^0.5.5", "karma-jasmine": "^0.3.7", "karma-phantomjs-launcher": "^1.0.0", "karma-script-launcher": "^0.1.0", + "karma-sourcemap-loader": "^0.3.7", + "karma-webpack": "^1.7.0", "load-grunt-tasks": "~3.4.1", - "node-static": "^0.7.7", - "phantomjs-prebuilt": "^2.1.4" + "moment": "^2.12.0", + "open": "0.0.5", + "phantomjs-prebuilt": "^2.1.4", + "webpack": "^1.12.14", + "webpack-dev-server": "^1.14.1", + "yargs": "^4.3.2" }, "keywords": [ "route", diff --git a/push-state-tree.js b/push-state-tree.js new file mode 100644 index 0000000..d945d04 --- /dev/null +++ b/push-state-tree.js @@ -0,0 +1,1096 @@ +/*! + * ! PushStateTree - v0.15.0 - 2016-03-29 + * * https://github.com/gartz/pushStateTree/ + * * Copyright (c) 2016 Gabriel Reitz Giannattasio ; Licensed undefined + */ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + (function (root) { + 'use strict'; + + var document = root.document; + var window = root.window; + var location = root.location; + + var isIE = function () { + var trident = window.navigator.userAgent.indexOf('Trident'); + return trident >= 0; + }(); + + // Shim, to work with older browsers + (function () { + // Opera and IE doesn't implement location.origin + if (!root.location.origin) { + root.location.origin = root.location.protocol + '//' + root.location.host; + } + })(); + + (function () { + /* global HTMLDocument */ + if (Function.prototype.bind) { + return; + } + + Function.prototype.bind = function (oThis) { + if (typeof this !== 'function') { + // closest thing possible to the ECMAScript 5 internal IsCallable function + throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + FNOP = function () {}, + fBound = function () { + var context = oThis; + if (this instanceof FNOP && oThis) { + context = this; + } + return fToBind.apply(context, aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + FNOP.prototype = this.prototype; + fBound.prototype = new FNOP(); + + return fBound; + }; + })(); + + // IE9 shims + var HashChangeEvent = root.HashChangeEvent; + var Event = root.Event; + + (function () { + if (!Element.prototype.addEventListener) { + return; + } + + function CustomEvent(event, params) { + params = params || { + bubbles: false, + cancelable: false, + detail: undefined + }; + var evt = document.createEvent('CustomEvent'); + evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); + return evt; + } + + CustomEvent.prototype = Event.prototype; + + if (!root.CustomEvent || !!isIE) { + root.CustomEvent = CustomEvent; + } + + // Opera before 15 has HashChangeEvent but throw a DOM Implement error + if (!HashChangeEvent || root.opera && root.opera.version() < 15 || !!isIE) { + HashChangeEvent = root.CustomEvent; + } + + if (!!isIE) { + Event = CustomEvent; + } + + // fix for Safari + try { + new HashChangeEvent('hashchange'); + } catch (e) { + HashChangeEvent = CustomEvent; + } + + try { + new Event('popstate'); + } catch (e) { + Event = CustomEvent; + } + })(); + + // IE 8 shims + (function () { + if (Element.prototype.addEventListener || !Object.defineProperty) { + return; + } + + // create an MS event object and get prototype + var proto = document.createEventObject().constructor.prototype; + + Object.defineProperty(proto, 'target', { + get: function () { + return this.srcElement; + } + }); + + // IE8 addEventLister shim + var addEventListenerFunc = function (type, handler) { + if (!this.__bindedFunctions) { + this.__bindedFunctions = []; + } + + var fn = handler; + + if (!('on' + type in this) || type === 'hashchange') { + this.__elemetIEid = this.__elemetIEid || '__ie__' + Math.random(); + var customEventId = type + this.__elemetIEid; + //TODO: Bug??? + //document.documentElement[customEventId]; + var element = this; + + var propHandler = function (event) { + // if the property changed is the custom jqmReady property + if (event.propertyName === customEventId) { + fn.call(element, document.documentElement[customEventId]); + } + }; + + this.__bindedFunctions.push({ + original: fn, + binded: propHandler + }); + + document.documentElement.attachEvent('onpropertychange', propHandler); + + if (type !== 'hashchange') { + return; + } + } + + var bindedFn = fn.bind(this); + + this.__bindedFunctions.push({ + original: fn, + binded: bindedFn + }); + + this.attachEvent('on' + type, bindedFn); + }; + + // setup the DOM and window objects + HTMLDocument.prototype.addEventListener = addEventListenerFunc; + Element.prototype.addEventListener = addEventListenerFunc; + window.addEventListener = addEventListenerFunc; + + // IE8 removeEventLister shim + var removeEventListenerFunc = function (type, handler) { + if (!this.__bindedFunctions) { + this.__bindedFunctions = []; + } + + var fn = handler; + + var bindedFn; + + if (!('on' + type in this) || type === 'hashchange') { + for (var i = 0; i < this.__bindedFunctions.length; i++) { + if (this.__bindedFunctions[i].original === fn) { + bindedFn = this.__bindedFunctions[i].binded; + this.__bindedFunctions = this.__bindedFunctions.splice(i, 1); + i = this.__bindedFunctions.length; + } + } + + if (bindedFn) { + document.documentElement.detachEvent('onpropertychange', bindedFn); + } + + if (type !== 'hashchange') { + return; + } + } + + for (var j = 0; j < this.__bindedFunctions.length; j++) { + if (this.__bindedFunctions[j].original === fn) { + bindedFn = this.__bindedFunctions[j].binded; + this.__bindedFunctions = this.__bindedFunctions.splice(j, 1); + j = this.__bindedFunctions.length; + } + } + if (!bindedFn) { + return; + } + + this.detachEvent('on' + type, bindedFn); + }; + + // setup the DOM and window objects + HTMLDocument.prototype.removeEventListener = removeEventListenerFunc; + Element.prototype.removeEventListener = removeEventListenerFunc; + window.removeEventListener = removeEventListenerFunc; + + Event = function (type, obj) { + + var evt = document.createEventObject(); + + obj = obj || {}; + evt.type = type; + evt.detail = obj.detail; + + if (!('on' + type in root) || type === 'hashchange') { + evt.name = type; + evt.customEvent = true; + } + + return evt; + }; + + /*jshint -W020 */ + CustomEvent = Event; + + HashChangeEvent = CustomEvent; + + var dispatchEventFunc = function (e) { + if (!e.customEvent) { + this.fireEvent(e.type, e); + return; + } + // no event registred + if (!this.__elemetIEid) { + return; + } + var customEventId = e.name + this.__elemetIEid; + document.documentElement[customEventId] = e; + }; + + // setup the Element dispatchEvent used to trigger events on the board + HTMLDocument.prototype.dispatchEvent = dispatchEventFunc; + Element.prototype.dispatchEvent = dispatchEventFunc; + window.dispatchEvent = dispatchEventFunc; + })(); + + (function () { + // modern browser support forEach, probably will be IE8 + var modernBrowser = 'forEach' in Array.prototype; + + // IE8 pollyfills: + // IE8 slice doesn't work with NodeList + if (!modernBrowser) { + var builtinSlice = Array.prototype.slice; + Array.prototype.slice = function () { + var arr = []; + for (var i = 0, n = this.length; i < n; i++) { + if (i in this) { + arr.push(this[i]); + } + } + + return builtinSlice.apply(arr, arguments); + }; + } + if (!('forEach' in Array.prototype)) { + Array.prototype.forEach = function (action, that) { + for (var i = 0; i < this.length; i++) { + if (i in this) { + action.call(that, this[i], i); + } + } + }; + } + if (typeof String.prototype.trim !== 'function') { + String.prototype.trim = function () { + return this.replace(/^\s+|\s+$/g, ''); + }; + } + if (!Array.prototype.filter) { + Array.prototype.filter = function (fun /*, thisArg */) { + if (this === void 0 || this === null) { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + if (typeof fun !== 'function') { + throw new TypeError(); + } + + var res = []; + var thisArg = arguments.length >= 2 ? arguments[1] : void 0; + for (var i = 0; i < len; i++) { + if (i in t) { + var val = t[i]; + + // NOTE: Technically this should Object.defineProperty at + // the next index, as push can be affected by + // properties on Object.prototype and Array.prototype. + // But that method's new, and collisions should be + // rare, so use the more-compatible alternative. + if (fun.call(thisArg, val, i, t)) { + res.push(val); + } + } + } + + return res; + }; + } + })(); + + // Constants for uglifiers + + var USE_PUSH_STATE = 'usePushState'; + var HAS_PUSH_STATE = 'hasPushState'; + var HASHCHANGE = 'hashchange'; + var POPSTATE = 'popstate'; + var LEAVE = 'leave'; + var UPDATE = 'update'; + var ENTER = 'enter'; + var CHANGE = 'change'; + var MATCH = 'match'; + var OLD_MATCH = 'oldMatch'; + + var options = root.PushStateTree && root.PushStateTree.options || {}; + var DEBUG = root.DEBUG || options.DEBUG; + + // Helpers + function isInt(n) { + return !isNaN(parseFloat(n)) && n % 1 === 0 && isFinite(n); + } + + function wrapProperty(scope, prop, target) { + Object.defineProperty(scope, prop, { + get: function () { + return target; + }, + set: function () {} + }); + } + + function isExternal(url) { + // Check if a URL is external + return (/^[a-z0-9]+:\/\//i.test(url) + ); + } + + function isRelative(uri) { + // Check if a URI is relative path, when begin with # or / isn't relative uri + return (/^[^#/]/.test(uri) + ); + } + + function resolveRelativePath(path) { + // Resolve relative paths manually for browsers using hash navigation + + var parts = path.split('/'); + var i = 1; + while (i < parts.length) { + // if current part is `..` and previous part is different, remove both of them + if (parts[i] === '..' && i > 0 && parts[i - 1] !== '..') { + parts.splice(i - 1, 2); + i -= 2; + } + i++; + } + return parts.join('/').replace(/\/\.\/|\.\/|\.\.\//g, '/').replace(/^\/$/, ''); + } + + // Add compatibility with old IE browsers + var elementPrototype = typeof HTMLElement !== 'undefined' ? HTMLElement : Element; + + function PushStateTree(options) { + options = options || {}; + options[USE_PUSH_STATE] = options[USE_PUSH_STATE] !== false; + + // Force the instance to always return a HTMLElement + if (!(this instanceof elementPrototype)) { + return PushStateTree.apply(document.createElement('pushstatetree-route'), arguments); + } + + var rootElement = this; + this.VERSION = ("0.15.0"); + + // Setup options + for (var prop in options) { + if (options.hasOwnProperty(prop)) { + rootElement[prop] = options[prop]; + } + } + + // Allow switch between pushState or hash navigation modes, in browser that doesn't support + // pushState it will always be false. and use hash navigation enforced. + // use backend non permanent redirect when old browsers are detected in the request. + if (!PushStateTree.prototype[HAS_PUSH_STATE]) { + wrapProperty(rootElement, USE_PUSH_STATE, false); + } else { + var usePushState = options[USE_PUSH_STATE]; + Object.defineProperty(rootElement, USE_PUSH_STATE, { + get: function () { + return usePushState; + }, + set: function (val) { + usePushState = val !== false; + } + }); + } + + // When enabled beautifyLocation will auto switch between hash to pushState when enabled + Object.defineProperty(rootElement, 'beautifyLocation', { + get: function () { + return PushStateTree.prototype.beautifyLocation && usePushState; + }, + set: function (value) { + PushStateTree.prototype.beautifyLocation = value === true; + } + }); + rootElement.beautifyLocation = options.beautifyLocation && rootElement.usePushState; + + var basePath; + Object.defineProperty(rootElement, 'basePath', { + get: function () { + return basePath; + }, + set: function (value) { + basePath = value || ''; + if (basePath[0] !== '/') { + basePath = '/' + basePath; + } + } + }); + rootElement.basePath = options.basePath; + + function wrappMethodsAndPropertiesToPrototype(prop) { + if (typeof PushStateTree.prototype[prop] === 'function') { + // function wrapper + rootElement[prop] = function () { + return PushStateTree.prototype[prop].apply(this, arguments); + }; + } else { + if (typeof rootElement[prop] !== 'undefined') return; + // property wrapper + Object.defineProperty(rootElement, prop, { + get: function () { + return PushStateTree.prototype[prop]; + }, + set: function (val) { + PushStateTree.prototype[prop] = val; + } + }); + } + } + + //TODO: emcapsulate this + for (var protoProperty in PushStateTree.prototype) { + if (PushStateTree.prototype.hasOwnProperty(protoProperty)) { + wrappMethodsAndPropertiesToPrototype(protoProperty); + } + } + + wrapProperty(rootElement, 'length', root.history.length); + wrapProperty(rootElement, 'state', root.history.state); + + var cachedUri = { + url: '', + uri: '' + }; + Object.defineProperty(rootElement, 'uri', { + get: function () { + if (cachedUri.url === root.location.href) return cachedUri.uri; + + var uri; + if (root.location.hash.length || root.location.href[location.href.length - 1] === '#') { + // Remove all begin # chars from the location when using hash + uri = root.location.hash.match(/^(#*)?(.*\/?)/)[2]; + + var usePushState = rootElement[USE_PUSH_STATE]; + if (rootElement.beautifyLocation && rootElement.isPathValid && usePushState) { + // when using pushState, replace the browser location to avoid ugly URLs + + rootElement.replaceState(rootElement.state, rootElement.title, uri[0] === '/' ? uri : '/' + uri); + } + } else { + uri = root.location.pathname + root.location.search; + if (this.isPathValid) { + uri = uri.slice(this.basePath.length); + } + } + + // Remove the very first slash, do don't match it as URI + uri = uri.replace(/^[\/]+/, ''); + + if (rootElement.getAttribute('uri') !== uri) { + rootElement.setAttribute('uri', uri); + } + + cachedUri.url = root.location.href; + cachedUri.uri = uri; + return uri; + }, + configurable: true + }); + + Object.defineProperty(rootElement, 'isPathValid', { + get: function () { + var uri = root.location.pathname + root.location.search; + return !this.basePath || uri.indexOf(this.basePath) === 0; + } + }); + + rootElement.eventStack = { + leave: [], + change: [], + enter: [], + match: [] + }; + + root.addEventListener(POPSTATE, function () { + var eventURI = rootElement.uri; + var eventState = rootElement.state; + rootElement.rulesDispatcher(); + + oldURI = eventURI; + oldState = eventState; + + // If there is holding dispatch in the event, do it now + if (holdingDispatch) { + this.dispatch(); + } + }.bind(rootElement)); + + var readOnhashchange = false; + var onhashchange = function () { + // Workaround IE8 + if (readOnhashchange) return; + + // Don't dispatch, because already have dispatched in popstate event + if (oldURI === rootElement.uri) return; + + var eventURI = rootElement.uri; + var eventState = rootElement.state; + rootElement.rulesDispatcher(); + + oldURI = eventURI; + oldState = eventState; + + // If there is holding dispatch in the event, do it now + if (holdingDispatch) { + this.dispatch(); + } + }.bind(rootElement); + + rootElement.avoidHashchangeHandler = function () { + // Avoid triggering hashchange event + root.removeEventListener(HASHCHANGE, onhashchange); + readOnhashchange = true; + }; + + root.addEventListener(HASHCHANGE, onhashchange); + + // Uglify propourses + var dispatchHashChange = function () { + root.dispatchEvent(new HashChangeEvent(HASHCHANGE)); + }; + + // Modern browsers + document.addEventListener('DOMContentLoaded', dispatchHashChange); + // Some IE browsers + root.addEventListener('readystatechange', dispatchHashChange); + // Almost all browsers + root.addEventListener('load', function () { + dispatchHashChange(); + + if (isIE) { + root.setInterval(function () { + if (rootElement.uri !== oldURI) { + dispatchHashChange(); + return; + } + if (readOnhashchange) { + readOnhashchange = false; + oldURI = rootElement.uri; + root.addEventListener(HASHCHANGE, onhashchange); + } + }.bind(rootElement), 50); + } + }.bind(rootElement)); + + return this; + } + + var oldState = null; + var oldURI = null; + var eventsQueue = []; + var holdingDispatch = false; + var holdDispatch = false; + + PushStateTree.prototype = { + // Version ~0.11 beatifyLocation is enabled by default + beautifyLocation: true, + + createRule: function (options) { + // Create a pushstreamtree-rule element from a literal object + + var rule = document.createElement('pushstatetree-rule'); + + var ruleRegex = new RegExp(''); + + // Bind rule property with element attribute + Object.defineProperty(rule, 'rule', { + get: function () { + return ruleRegex; + }, + set: function (val) { + if (val instanceof RegExp) { + ruleRegex = val; + } else { + + // IE8 trigger set from the property when update the attribute, avoid recursive loop + if (val === ruleRegex.toString()) return; + + // Slice the pattern from the attribute + var slicedPattern = (val + '').match(/^\/(.+)\/([gmi]*)|(.*)/); + + ruleRegex = new RegExp(slicedPattern[1] || slicedPattern[3], slicedPattern[2]); + } + + rule.setAttribute('rule', ruleRegex.toString()); + } + }); + + // Bind rule property with element attribute + Object.defineProperty(rule, 'parentGroup', { + get: function () { + var attr = rule.getAttribute('parent-group'); + if (attr && isInt(attr)) { + return +attr; + } + return null; + }, + set: function (val) { + if (isInt(val)) { + rule.setAttribute('parent-group', val); + } else { + rule.removeAttribute('parent-group'); + } + } + }); + + for (var prop in options) if (options.hasOwnProperty(prop)) { + rule[prop] = options[prop]; + } + + // Match is always a array, so you can test for match[n] anytime + var match = []; + Object.defineProperty(rule, MATCH, { + get: function () { + return match; + }, + set: function (val) { + match = val instanceof Array ? val : []; + } + }); + + var oldMatch = []; + Object.defineProperty(rule, OLD_MATCH, { + get: function () { + return oldMatch; + }, + set: function (val) { + oldMatch = val instanceof Array ? val : []; + } + }); + + rule[MATCH] = []; + rule[OLD_MATCH] = []; + + // Replicate the methods from `route` to the rule, by transversing until find and execute + // the router method, not a fast operation, but ensure the right route to be triggered + ['assign', 'navigate', 'replace', 'dispatch', 'pushState', 'replaceState'].forEach(function (methodName) { + rule[methodName] = function () { + this.parentElement[methodName].apply(this.parentElement, arguments); + }; + }); + + return rule; + }, + + add: function (options) { + // Transform any literal object in a pushstatetree-rule and append it + + this.appendChild(this.createRule(options)); + return this; + }, + + remove: function (queryOrElement) { + // Remove a pushstateree-rule, pass a element or it query + + var element = queryOrElement; + if (typeof queryOrElement === 'string') { + element = this.querySelector(queryOrElement); + } + + if (element && element.parentElement) { + element.parentElement.removeChild(element); + return element; + } + }, + + dispatch: function () { + // Deferred trigger the actual browser location + if (holdDispatch) { + holdingDispatch = true; + return this; + } + holdingDispatch = false; + root.dispatchEvent(new Event(POPSTATE)); + return this; + }, + + assign: function (url) { + // Shortcut for pushState and dispatch methods + return this.pushState(null, null, url).dispatch(); + }, + + replace: function (url) { + // Shortcut for pushState and dispatch methods + return this.replaceState(null, null, url).dispatch(); + }, + + navigate: function () { + this.assign.apply(this, arguments); + }, + + rulesDispatcher: function () { + // Will dispatch the right events in each rule + /*jshint validthis:true */ + + // Cache the URI, in case of an event try to change it + var debug = this.debug === true || DEBUG; + + // Abort if the basePath isn't valid for this router + if (!this.isPathValid) return; + + function runner(uri, oldURI) { + Array.prototype.slice.call(this.children || this.childNodes).forEach(recursiveDispatcher.bind(this, uri, oldURI)); + return uri; + } + + eventsQueue.push(runner.bind(this, this.uri)); + + // Is there already a queue been executed, so just add the runner + // and let the main queue resolve it + if (eventsQueue.length > 1) { + return; + } + + // Chain execute the evetsQueue + var last = oldURI; + while (eventsQueue.length > 0) { + last = eventsQueue[0].call(null, last); + eventsQueue.shift(); + } + + // If a dispatch is triggered inside a event callback, it need to hold + holdDispatch = true; + + // A stack of all events to be dispatched, to ensure the priority order + var eventStack = this.eventStack; + + // Order of events stack execution, leave event isn't here because it executes in the + // recursiveDispatcher, for one loop less + [CHANGE, ENTER, MATCH].forEach(function (type) { + // Execute the leave stack of events + while (eventStack[type].length > 0) { + var events = eventStack[type][0].events; + var element = eventStack[type][0].element; + + //TODO: Ignore if there isn't same in the enter stack and remove it + while (events.length > 0) { + element.dispatchEvent(events[0]); + events.shift(); + } + eventStack[type].shift(); + } + }); + + // If there is holding dispatchs in the event, do it now + holdDispatch = false; + + function recursiveDispatcher(uri, oldURI, ruleElement) { + if (!ruleElement.rule) return; + + var useURI = uri; + var useOldURI = oldURI; + var parentElement; + + if (typeof ruleElement.parentGroup === 'number') { + useURI = ''; + parentElement = ruleElement.parentElement; + + if (parentElement[MATCH].length > ruleElement.parentGroup) useURI = parentElement[MATCH][ruleElement.parentGroup] || ''; + + useOldURI = ''; + if (parentElement[OLD_MATCH].length > ruleElement.parentGroup) useOldURI = parentElement[OLD_MATCH][ruleElement.parentGroup] || ''; + } + + ruleElement[MATCH] = useURI[MATCH](ruleElement.rule); + if (typeof useOldURI === 'string') { + ruleElement[OLD_MATCH] = useOldURI[MATCH](ruleElement.rule); + } else { + ruleElement[OLD_MATCH] = []; + } + var match = ruleElement[MATCH]; + var oldMatch = ruleElement[OLD_MATCH]; + var children = Array.prototype.slice.call(ruleElement.children); + + function PushStateTreeEvent(name, params) { + + params = params || {}; + params.detail = params.detail || {}; + params.detail[MATCH] = match || []; + params.detail[OLD_MATCH] = oldMatch || []; + params.cancelable = true; + + if (debug && typeof console === 'object') { + console.log({ + name: name, + ruleElement: ruleElement, + params: params, + useURI: useURI, + useOldURI: useOldURI + }); + if (console.trace) console.trace(); + } + var event = new root.CustomEvent(name, params); + return event; + } + + // Not match or leave? + if (match.length === 0) { + if (oldMatch.length === 0 || ruleElement.routerURI !== oldURI) { + // just not match... + return; + } + ruleElement.uri = null; + ruleElement.removeAttribute('uri'); + + children.forEach(recursiveDispatcher.bind(this, uri, oldURI)); + + // Don't use stack for LEAVE event, dispatch in this loop + ruleElement.dispatchEvent(new PushStateTreeEvent(UPDATE, { + detail: { type: LEAVE } + })); + + ruleElement.dispatchEvent(new PushStateTreeEvent(LEAVE)); + return; + } + + // dispatch the match event + this.eventStack[MATCH].push({ + element: ruleElement, + events: [new PushStateTreeEvent(MATCH)] + }); + + var isNewURI = ruleElement.routerURI !== oldURI; + ruleElement.routerURI = this.uri; + ruleElement.uri = match[0]; + ruleElement.setAttribute('uri', match[0]); + + if (oldMatch.length === 0 || isNewURI) { + // stack dispatch enter event + this.eventStack[ENTER].push({ + element: ruleElement, + events: [new PushStateTreeEvent(UPDATE, { + detail: { type: ENTER } + }), new PushStateTreeEvent(ENTER)] + }); + + children.forEach(recursiveDispatcher.bind(this, uri, oldURI)); + return; + } + + // if has something changed, dispatch the change event + if (match[0] !== oldMatch[0]) { + // stack dispatch enter event + this.eventStack[CHANGE].push({ + element: ruleElement, + events: [new PushStateTreeEvent(UPDATE, { + detail: { type: CHANGE } + }), new PushStateTreeEvent(CHANGE)] + }); + } + + children.forEach(recursiveDispatcher.bind(this, uri, oldURI)); + } + } + }; + + function preProcessUriBeforeExecuteNativeHistoryMethods(method) { + /*jshint validthis:true */ + var scopeMethod = method; + this[method] = function () { + // Wrap method + + // remove the method from arguments + var args = Array.prototype.slice.call(arguments); + + // if has a basePath translate the not relative paths to use the basePath + if (scopeMethod === 'pushState' || scopeMethod === 'replaceState') { + + if (!isExternal(args[2])) { + // When not external link, need to normalize the URI + + if (isRelative(args[2])) { + // Relative to the uri + var basePath = this.uri.match(/^([^?#]*)\//); + basePath = basePath ? basePath[1] + '/' : ''; + args[2] = basePath + args[2]; + } else { + // This isn't relative, will cleanup / and # from the begin and use the remain path + args[2] = args[2].match(/^([#/]*)?(.*)/)[2]; + } + + if (!this[USE_PUSH_STATE]) { + + // Ignore basePath when using location.hash and resolve relative path and keep + // the current location.pathname, some browsers history API might apply the new pathname + // with the hash content if not explicit + args[2] = location.pathname + '#' + resolveRelativePath(args[2]); + } else { + + // Add the basePath to your uri, not allowing to go by pushState outside the basePath + args[2] = this.basePath + args[2]; + } + } + } + + root.history[scopeMethod].apply(root.history, args); + + // Chainnable + return this; + }; + } + + // Wrap history methods + for (var method in root.history) { + if (typeof root.history[method] === 'function') { + preProcessUriBeforeExecuteNativeHistoryMethods.call(PushStateTree.prototype, method); + } + } + + PushStateTree.prototype[HAS_PUSH_STATE] = root.history && !!root.history.pushState; + if (!PushStateTree.prototype[HAS_PUSH_STATE]) { + PushStateTree.prototype[USE_PUSH_STATE] = false; + } + + var lastTitle = null; + + if (!PushStateTree.prototype.pushState) { + PushStateTree.prototype.pushState = function (state, title, uri) { + var t = document.title || ''; + uri = uri || ''; + if (lastTitle !== null) { + document.title = lastTitle; + } + this.avoidHashchangeHandler(); + + // Replace hash url + if (isExternal(uri)) { + // this will redirect the browser, so doesn't matters the rest... + root.location.href = uri; + } + + // Remove the has if is it present + if (uri[0] === '#') { + uri = uri.slice(1); + } + + if (isRelative(uri)) { + uri = root.location.hash.slice(1, root.location.hash.lastIndexOf('/') + 1) + uri; + uri = resolveRelativePath(uri); + } + + root.location.hash = uri; + + document.title = t; + lastTitle = title; + + return this; + }; + } + + if (!PushStateTree.prototype.replaceState) { + PushStateTree.prototype.replaceState = function (state, title, uri) { + var t = document.title || ''; + uri = uri || ''; + if (lastTitle !== null) { + document.title = lastTitle; + } + this.avoidHashchangeHandler(); + + // Replace the url + if (isExternal(uri)) { + throw new Error('Invalid url replace.'); + } + + if (uri[0] === '#') { + uri = uri.slice(1); + } + + if (isRelative(uri)) { + var relativePos = root.location.hash.lastIndexOf('/') + 1; + uri = root.location.hash.slice(1, relativePos) + uri; + uri = resolveRelativePath(uri); + } + + // Always use hash navigation + uri = '#' + uri; + + root.location.replace(uri); + document.title = t; + lastTitle = title; + + return this; + }; + } + + root.PushStateTree = PushStateTree; + + // Node import support + if (true) module.exports = PushStateTree; + })(function () { + /*jshint strict: false */ + return this; + }()); + +/***/ } +/******/ ]); \ No newline at end of file diff --git a/push-state-tree.js.map b/push-state-tree.js.map new file mode 100644 index 0000000..f24e322 --- /dev/null +++ b/push-state-tree.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack://pushstatetree.source/webpack/bootstrap eb7cc4f4d632fa8af911?a0a2","webpack://pushstatetree.source/./src/pushStateTree.js?c559"],"names":[],"mappings":"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","file":"push-state-tree.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap eb7cc4f4d632fa8af911\n **/","(function (root) {\n 'use strict';\n\n var document = root.document;\n var window = root.window;\n var location = root.location;\n\n var isIE = (function(){\n var trident = window.navigator.userAgent.indexOf('Trident');\n return trident >= 0;\n }());\n\n // Shim, to work with older browsers\n (function () {\n // Opera and IE doesn't implement location.origin\n if (!root.location.origin) {\n root.location.origin = root.location.protocol + '//' + root.location.host;\n }\n })();\n\n (function () {\n /* global HTMLDocument */\n if (Function.prototype.bind) { return; }\n\n Function.prototype.bind = function (oThis) {\n if (typeof this !== 'function') {\n // closest thing possible to the ECMAScript 5 internal IsCallable function\n throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');\n }\n\n var aArgs = Array.prototype.slice.call(arguments, 1),\n fToBind = this,\n FNOP = function () {},\n fBound = function () {\n var context = oThis;\n if (this instanceof FNOP && oThis){\n context = this;\n }\n return fToBind.apply(context, aArgs.concat(Array.prototype.slice.call(arguments)));\n };\n\n FNOP.prototype = this.prototype;\n fBound.prototype = new FNOP();\n\n return fBound;\n };\n })();\n\n// IE9 shims\n var HashChangeEvent = root.HashChangeEvent;\n var Event = root.Event;\n\n (function () {\n if (!Element.prototype.addEventListener) { return; }\n\n function CustomEvent(event, params) {\n params = params || {\n bubbles: false,\n cancelable: false,\n detail: undefined\n };\n var evt = document.createEvent('CustomEvent');\n evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n return evt;\n }\n\n CustomEvent.prototype = Event.prototype;\n\n if (!root.CustomEvent || !!isIE) {\n root.CustomEvent = CustomEvent;\n }\n\n // Opera before 15 has HashChangeEvent but throw a DOM Implement error\n if (!HashChangeEvent || (root.opera && root.opera.version() < 15) || !!isIE) {\n HashChangeEvent = root.CustomEvent;\n }\n\n if (!!isIE) {\n Event = CustomEvent;\n }\n\n // fix for Safari\n try {\n new HashChangeEvent('hashchange');\n } catch(e) {\n HashChangeEvent = CustomEvent;\n }\n\n try {\n new Event('popstate');\n } catch (e) {\n Event = CustomEvent;\n }\n })();\n\n// IE 8 shims\n (function () {\n if (Element.prototype.addEventListener || !Object.defineProperty) { return; }\n\n // create an MS event object and get prototype\n var proto = document.createEventObject().constructor.prototype;\n\n Object.defineProperty(proto, 'target', {\n get: function() { return this.srcElement; }\n });\n\n // IE8 addEventLister shim\n var addEventListenerFunc = function(type, handler) {\n if (!this.__bindedFunctions) {\n this.__bindedFunctions = [];\n }\n\n var fn = handler;\n\n if (!('on' + type in this) || type === 'hashchange') {\n this.__elemetIEid = this.__elemetIEid || '__ie__' + Math.random();\n var customEventId = type + this.__elemetIEid;\n //TODO: Bug???\n //document.documentElement[customEventId];\n var element = this;\n\n var propHandler = function (event) {\n // if the property changed is the custom jqmReady property\n if (event.propertyName === customEventId) {\n fn.call(element, document.documentElement[customEventId]);\n }\n };\n\n this.__bindedFunctions.push({\n original: fn,\n binded: propHandler\n });\n\n document.documentElement.attachEvent('onpropertychange', propHandler);\n\n if (type !== 'hashchange') { return; }\n }\n\n var bindedFn = fn.bind(this);\n\n this.__bindedFunctions.push({\n original: fn,\n binded: bindedFn\n });\n\n this.attachEvent('on' + type, bindedFn);\n };\n\n // setup the DOM and window objects\n HTMLDocument.prototype.addEventListener = addEventListenerFunc;\n Element.prototype.addEventListener = addEventListenerFunc;\n window.addEventListener = addEventListenerFunc;\n\n // IE8 removeEventLister shim\n var removeEventListenerFunc = function(type, handler) {\n if (!this.__bindedFunctions) {\n this.__bindedFunctions = [];\n }\n\n var fn = handler;\n\n var bindedFn;\n\n if (!('on' + type in this) || type === 'hashchange') {\n for (var i = 0; i < this.__bindedFunctions.length; i++) {\n if (this.__bindedFunctions[i].original === fn) {\n bindedFn = this.__bindedFunctions[i].binded;\n this.__bindedFunctions = this.__bindedFunctions.splice(i, 1);\n i = this.__bindedFunctions.length;\n }\n }\n\n if (bindedFn) {\n document.documentElement.detachEvent('onpropertychange', bindedFn);\n }\n\n if (type !== 'hashchange') { return; }\n }\n\n for (var j = 0; j < this.__bindedFunctions.length; j++) {\n if (this.__bindedFunctions[j].original === fn) {\n bindedFn = this.__bindedFunctions[j].binded;\n this.__bindedFunctions = this.__bindedFunctions.splice(j, 1);\n j = this.__bindedFunctions.length;\n }\n }\n if (!bindedFn) { return; }\n\n this.detachEvent('on' + type, bindedFn);\n };\n\n // setup the DOM and window objects\n HTMLDocument.prototype.removeEventListener = removeEventListenerFunc;\n Element.prototype.removeEventListener = removeEventListenerFunc;\n window.removeEventListener = removeEventListenerFunc;\n\n Event = function (type, obj) {\n\n var evt = document.createEventObject();\n\n obj = obj || {};\n evt.type = type;\n evt.detail = obj.detail;\n\n if (!('on' + type in root) || type === 'hashchange') {\n evt.name = type;\n evt.customEvent = true;\n }\n\n return evt;\n };\n\n /*jshint -W020 */\n CustomEvent = Event;\n\n HashChangeEvent = CustomEvent;\n\n var dispatchEventFunc = function (e) {\n if (!e.customEvent) {\n this.fireEvent(e.type, e);\n return;\n }\n // no event registred\n if (!this.__elemetIEid) {\n return;\n }\n var customEventId = e.name + this.__elemetIEid;\n document.documentElement[customEventId] = e;\n };\n\n // setup the Element dispatchEvent used to trigger events on the board\n HTMLDocument.prototype.dispatchEvent = dispatchEventFunc;\n Element.prototype.dispatchEvent = dispatchEventFunc;\n window.dispatchEvent = dispatchEventFunc;\n })();\n\n (function () {\n // modern browser support forEach, probably will be IE8\n var modernBrowser = 'forEach' in Array.prototype;\n\n // IE8 pollyfills:\n // IE8 slice doesn't work with NodeList\n if (!modernBrowser) {\n var builtinSlice = Array.prototype.slice;\n Array.prototype.slice = function() {\n var arr = [];\n for (var i = 0, n = this.length; i < n; i++) {\n if (i in this) {\n arr.push(this[i]);\n }\n }\n\n return builtinSlice.apply(arr, arguments);\n };\n }\n if (!('forEach' in Array.prototype)) {\n Array.prototype.forEach = function(action, that) {\n for (var i = 0; i < this.length; i++) {\n if (i in this) {\n action.call(that, this[i], i);\n }\n }\n };\n }\n if (typeof String.prototype.trim !== 'function') {\n String.prototype.trim = function() {\n return this.replace(/^\\s+|\\s+$/g, '');\n };\n }\n if (!Array.prototype.filter) {\n Array.prototype.filter = function(fun /*, thisArg */) {\n if (this === void 0 || this === null) {\n throw new TypeError();\n }\n\n var t = Object(this);\n var len = t.length >>> 0;\n if (typeof fun !== 'function') {\n throw new TypeError();\n }\n\n var res = [];\n var thisArg = arguments.length >= 2 ? arguments[1] : void 0;\n for (var i = 0; i < len; i++) {\n if (i in t) {\n var val = t[i];\n\n // NOTE: Technically this should Object.defineProperty at\n // the next index, as push can be affected by\n // properties on Object.prototype and Array.prototype.\n // But that method's new, and collisions should be\n // rare, so use the more-compatible alternative.\n if (fun.call(thisArg, val, i, t)) { res.push(val); }\n }\n }\n\n return res;\n };\n }\n })();\n\n // Constants for uglifiers\n\n var USE_PUSH_STATE = 'usePushState';\n var HAS_PUSH_STATE = 'hasPushState';\n var HASHCHANGE = 'hashchange';\n var POPSTATE = 'popstate';\n var LEAVE = 'leave';\n var UPDATE = 'update';\n var ENTER = 'enter';\n var CHANGE = 'change';\n var MATCH = 'match';\n var OLD_MATCH = 'oldMatch';\n\n var options = root.PushStateTree && root.PushStateTree.options || {};\n var DEBUG = root.DEBUG || options.DEBUG;\n\n // Helpers\n function isInt(n) {\n return !isNaN(parseFloat(n)) && n % 1 === 0 && isFinite(n);\n }\n\n function wrapProperty(scope, prop, target) {\n Object.defineProperty(scope, prop, {\n get: function () {\n return target;\n },\n set: function () {}\n });\n }\n\n function isExternal(url) {\n // Check if a URL is external\n return (/^[a-z0-9]+:\\/\\//i).test(url);\n }\n\n function isRelative(uri) {\n // Check if a URI is relative path, when begin with # or / isn't relative uri\n return (/^[^#/]/).test(uri);\n }\n\n function resolveRelativePath(path) {\n // Resolve relative paths manually for browsers using hash navigation\n\n var parts = path.split('/');\n var i = 1;\n while (i < parts.length) {\n // if current part is `..` and previous part is different, remove both of them\n if (parts[i] === '..' && i > 0 && parts[i-1] !== '..') {\n parts.splice(i - 1, 2);\n i -= 2;\n }\n i++;\n }\n return parts\n .join('/')\n .replace(/\\/\\.\\/|\\.\\/|\\.\\.\\//g, '/')\n .replace(/^\\/$/, '');\n }\n\n // Add compatibility with old IE browsers\n var elementPrototype = typeof HTMLElement !== 'undefined' ? HTMLElement : Element;\n\n function PushStateTree(options) {\n options = options || {};\n options[USE_PUSH_STATE] = options[USE_PUSH_STATE] !== false;\n\n // Force the instance to always return a HTMLElement\n if (!(this instanceof elementPrototype)) {\n return PushStateTree.apply(document.createElement('pushstatetree-route'), arguments);\n }\n\n var rootElement = this;\n this.VERSION = VERSION;\n\n // Setup options\n for (var prop in options) {\n if (options.hasOwnProperty(prop)) {\n rootElement[prop] = options[prop];\n }\n }\n\n // Allow switch between pushState or hash navigation modes, in browser that doesn't support\n // pushState it will always be false. and use hash navigation enforced.\n // use backend non permanent redirect when old browsers are detected in the request.\n if (!PushStateTree.prototype[HAS_PUSH_STATE]) {\n wrapProperty(rootElement, USE_PUSH_STATE, false);\n } else {\n var usePushState = options[USE_PUSH_STATE];\n Object.defineProperty(rootElement, USE_PUSH_STATE, {\n get: function () {\n return usePushState;\n },\n set: function (val) {\n usePushState = val !== false;\n }\n });\n }\n\n // When enabled beautifyLocation will auto switch between hash to pushState when enabled\n Object.defineProperty(rootElement, 'beautifyLocation', {\n get: function () {\n return PushStateTree.prototype.beautifyLocation && usePushState;\n },\n set: function (value) {\n PushStateTree.prototype.beautifyLocation = value === true;\n }\n });\n rootElement.beautifyLocation = options.beautifyLocation && rootElement.usePushState;\n\n var basePath;\n Object.defineProperty(rootElement, 'basePath', {\n get: function () {\n return basePath;\n },\n set: function (value) {\n basePath = value || '';\n if (basePath[0] !== '/') {\n basePath = '/' + basePath;\n }\n }\n });\n rootElement.basePath = options.basePath;\n\n function wrappMethodsAndPropertiesToPrototype(prop) {\n if (typeof PushStateTree.prototype[prop] === 'function') {\n // function wrapper\n rootElement[prop] = function () {\n return PushStateTree.prototype[prop].apply(this, arguments);\n };\n } else {\n if (typeof rootElement[prop] !== 'undefined') return;\n // property wrapper\n Object.defineProperty(rootElement, prop, {\n get: function () {\n return PushStateTree.prototype[prop];\n },\n set: function (val) {\n PushStateTree.prototype[prop] = val;\n }\n });\n }\n }\n\n //TODO: emcapsulate this\n for (var protoProperty in PushStateTree.prototype) {\n if (PushStateTree.prototype.hasOwnProperty(protoProperty)) {\n wrappMethodsAndPropertiesToPrototype(protoProperty);\n }\n }\n\n wrapProperty(rootElement, 'length', root.history.length);\n wrapProperty(rootElement, 'state', root.history.state);\n\n var cachedUri = {\n url: '',\n uri: ''\n };\n Object.defineProperty(rootElement, 'uri', {\n get: function () {\n if (cachedUri.url === root.location.href) return cachedUri.uri;\n\n var uri;\n if (root.location.hash.length || root.location.href[location.href.length - 1] === '#') {\n // Remove all begin # chars from the location when using hash\n uri = root.location.hash.match(/^(#*)?(.*\\/?)/)[2];\n\n var usePushState = rootElement[USE_PUSH_STATE];\n if (rootElement.beautifyLocation && rootElement.isPathValid && usePushState) {\n // when using pushState, replace the browser location to avoid ugly URLs\n\n rootElement.replaceState(\n rootElement.state,\n rootElement.title,\n uri[0] === '/' ? uri : '/' + uri\n );\n }\n } else {\n uri = root.location.pathname + root.location.search;\n if (this.isPathValid) {\n uri = uri.slice(this.basePath.length);\n }\n }\n\n // Remove the very first slash, do don't match it as URI\n uri = uri.replace(/^[\\/]+/, '');\n\n if (rootElement.getAttribute('uri') !== uri) {\n rootElement.setAttribute('uri', uri);\n }\n\n cachedUri.url = root.location.href;\n cachedUri.uri = uri;\n return uri;\n },\n configurable: true\n });\n\n Object.defineProperty(rootElement, 'isPathValid', {\n get: function () {\n var uri = root.location.pathname + root.location.search;\n return !this.basePath || (uri).indexOf(this.basePath) === 0;\n }\n });\n\n rootElement.eventStack = {\n leave: [],\n change: [],\n enter: [],\n match: []\n };\n\n root.addEventListener(POPSTATE, function () {\n var eventURI = rootElement.uri;\n var eventState = rootElement.state;\n rootElement.rulesDispatcher();\n\n oldURI = eventURI;\n oldState = eventState;\n\n // If there is holding dispatch in the event, do it now\n if (holdingDispatch) {\n this.dispatch();\n }\n }.bind(rootElement));\n\n var readOnhashchange = false;\n var onhashchange = function () {\n // Workaround IE8\n if (readOnhashchange) return;\n\n // Don't dispatch, because already have dispatched in popstate event\n if (oldURI === rootElement.uri) return;\n\n var eventURI = rootElement.uri;\n var eventState = rootElement.state;\n rootElement.rulesDispatcher();\n\n oldURI = eventURI;\n oldState = eventState;\n\n // If there is holding dispatch in the event, do it now\n if (holdingDispatch) {\n this.dispatch();\n }\n }.bind(rootElement);\n\n rootElement.avoidHashchangeHandler = function () {\n // Avoid triggering hashchange event\n root.removeEventListener(HASHCHANGE, onhashchange);\n readOnhashchange = true;\n };\n\n root.addEventListener(HASHCHANGE, onhashchange);\n\n // Uglify propourses\n var dispatchHashChange = function () {\n root.dispatchEvent(new HashChangeEvent(HASHCHANGE));\n };\n\n // Modern browsers\n document.addEventListener('DOMContentLoaded', dispatchHashChange);\n // Some IE browsers\n root.addEventListener('readystatechange', dispatchHashChange);\n // Almost all browsers\n root.addEventListener('load', function () {\n dispatchHashChange();\n\n if (isIE) {\n root.setInterval(function () {\n if (rootElement.uri !== oldURI) {\n dispatchHashChange();\n return;\n }\n if (readOnhashchange) {\n readOnhashchange = false;\n oldURI = rootElement.uri;\n root.addEventListener(HASHCHANGE, onhashchange);\n }\n }.bind(rootElement), 50);\n }\n }.bind(rootElement));\n\n return this;\n }\n\n var oldState = null;\n var oldURI = null;\n var eventsQueue = [];\n var holdingDispatch = false;\n var holdDispatch = false;\n\n PushStateTree.prototype = {\n // Version ~0.11 beatifyLocation is enabled by default\n beautifyLocation: true,\n\n createRule: function (options) {\n // Create a pushstreamtree-rule element from a literal object\n\n var rule = document.createElement('pushstatetree-rule');\n\n var ruleRegex = new RegExp('');\n\n // Bind rule property with element attribute\n Object.defineProperty(rule, 'rule', {\n get: function () {\n return ruleRegex;\n },\n set: function (val) {\n if (val instanceof RegExp){\n ruleRegex = val;\n } else {\n\n // IE8 trigger set from the property when update the attribute, avoid recursive loop\n if (val === ruleRegex.toString()) return;\n\n // Slice the pattern from the attribute\n var slicedPattern = (val + '').match(/^\\/(.+)\\/([gmi]*)|(.*)/);\n\n ruleRegex = new RegExp(slicedPattern[1] || slicedPattern[3], slicedPattern[2]);\n }\n\n rule.setAttribute('rule', ruleRegex.toString());\n }\n });\n\n // Bind rule property with element attribute\n Object.defineProperty(rule, 'parentGroup', {\n get: function () {\n var attr = rule.getAttribute('parent-group');\n if (attr && isInt(attr)) {\n return + attr;\n }\n return null;\n },\n set: function (val) {\n if (isInt(val)) {\n rule.setAttribute('parent-group', val);\n } else {\n rule.removeAttribute('parent-group');\n }\n }\n });\n\n for (var prop in options)\n if (options.hasOwnProperty(prop)) {\n rule[prop] = options[prop];\n }\n\n // Match is always a array, so you can test for match[n] anytime\n var match = [];\n Object.defineProperty(rule, MATCH, {\n get: function () {\n return match;\n },\n set: function (val) {\n match = val instanceof Array ? val : [];\n },\n });\n\n var oldMatch = [];\n Object.defineProperty(rule, OLD_MATCH, {\n get: function () {\n return oldMatch;\n },\n set: function (val) {\n oldMatch = val instanceof Array ? val : [];\n },\n });\n\n rule[MATCH] = [];\n rule[OLD_MATCH] = [];\n\n // Replicate the methods from `route` to the rule, by transversing until find and execute\n // the router method, not a fast operation, but ensure the right route to be triggered\n [\n 'assign',\n 'navigate',\n 'replace',\n 'dispatch',\n 'pushState',\n 'replaceState'\n ].forEach(function(methodName){\n rule[methodName] = function(){\n this.parentElement[methodName].apply(this.parentElement, arguments);\n };\n });\n\n return rule;\n },\n\n add: function (options) {\n // Transform any literal object in a pushstatetree-rule and append it\n\n this.appendChild(this.createRule(options));\n return this;\n },\n\n remove: function (queryOrElement) {\n // Remove a pushstateree-rule, pass a element or it query\n\n var element = queryOrElement;\n if (typeof queryOrElement === 'string') {\n element = this.querySelector(queryOrElement);\n }\n\n if (element && element.parentElement) {\n element.parentElement.removeChild(element);\n return element;\n }\n },\n\n dispatch: function () {\n // Deferred trigger the actual browser location\n if (holdDispatch) {\n holdingDispatch = true;\n return this;\n }\n holdingDispatch = false;\n root.dispatchEvent(new Event(POPSTATE));\n return this;\n },\n\n assign: function (url) {\n // Shortcut for pushState and dispatch methods\n return this.pushState(null, null, url).dispatch();\n },\n\n replace: function (url) {\n // Shortcut for pushState and dispatch methods\n return this.replaceState(null, null, url).dispatch();\n },\n\n navigate: function(){\n this.assign.apply(this, arguments);\n },\n\n rulesDispatcher: function () {\n // Will dispatch the right events in each rule\n /*jshint validthis:true */\n\n // Cache the URI, in case of an event try to change it\n var debug = this.debug === true || DEBUG;\n\n // Abort if the basePath isn't valid for this router\n if (!this.isPathValid) return;\n\n function runner(uri, oldURI) {\n Array.prototype.slice.call(this.children || this.childNodes)\n .forEach(recursiveDispatcher.bind(this, uri, oldURI));\n return uri;\n }\n\n eventsQueue.push(runner.bind(this, this.uri));\n\n // Is there already a queue been executed, so just add the runner\n // and let the main queue resolve it\n if (eventsQueue.length > 1) { return; }\n\n // Chain execute the evetsQueue\n var last = oldURI;\n while (eventsQueue.length > 0) {\n last = eventsQueue[0].call(null, last);\n eventsQueue.shift();\n }\n\n // If a dispatch is triggered inside a event callback, it need to hold\n holdDispatch = true;\n\n // A stack of all events to be dispatched, to ensure the priority order\n var eventStack = this.eventStack;\n\n // Order of events stack execution, leave event isn't here because it executes in the\n // recursiveDispatcher, for one loop less\n [CHANGE, ENTER, MATCH].forEach(function(type){\n // Execute the leave stack of events\n while (eventStack[type].length > 0) {\n var events = eventStack[type][0].events;\n var element = eventStack[type][0].element;\n\n //TODO: Ignore if there isn't same in the enter stack and remove it\n while (events.length > 0){\n element.dispatchEvent(events[0]);\n events.shift();\n }\n eventStack[type].shift();\n }\n });\n\n // If there is holding dispatchs in the event, do it now\n holdDispatch = false;\n\n function recursiveDispatcher(uri, oldURI, ruleElement) {\n if (!ruleElement.rule) return;\n\n var useURI = uri;\n var useOldURI = oldURI;\n var parentElement;\n\n if (typeof ruleElement.parentGroup === 'number') {\n useURI = '';\n parentElement = ruleElement.parentElement;\n\n if (parentElement[MATCH].length > ruleElement.parentGroup)\n useURI = parentElement[MATCH][ruleElement.parentGroup] || '';\n\n useOldURI = '';\n if (parentElement[OLD_MATCH].length > ruleElement.parentGroup)\n useOldURI = parentElement[OLD_MATCH][ruleElement.parentGroup] || '';\n }\n\n ruleElement[MATCH] = useURI[MATCH](ruleElement.rule);\n if (typeof useOldURI === 'string') {\n ruleElement[OLD_MATCH] = useOldURI[MATCH](ruleElement.rule);\n } else {\n ruleElement[OLD_MATCH] = [];\n }\n var match = ruleElement[MATCH];\n var oldMatch = ruleElement[OLD_MATCH];\n var children = Array.prototype.slice.call(ruleElement.children);\n\n function PushStateTreeEvent(name, params) {\n\n params = params || {};\n params.detail = params.detail || {};\n params.detail[MATCH] = match || [];\n params.detail[OLD_MATCH] = oldMatch || [];\n params.cancelable = true;\n\n if (debug && typeof console === 'object'){\n console.log({\n name: name,\n ruleElement: ruleElement,\n params: params,\n useURI: useURI,\n useOldURI: useOldURI\n });\n if (console.trace) console.trace();\n }\n var event = new root.CustomEvent(name, params);\n return event;\n }\n\n // Not match or leave?\n if (match.length === 0) {\n if (oldMatch.length === 0 || ruleElement.routerURI !== oldURI) {\n // just not match...\n return;\n }\n ruleElement.uri = null;\n ruleElement.removeAttribute('uri');\n\n children.forEach(recursiveDispatcher.bind(this, uri, oldURI));\n\n // Don't use stack for LEAVE event, dispatch in this loop\n ruleElement.dispatchEvent(new PushStateTreeEvent(UPDATE, {\n detail: {type: LEAVE}\n }));\n\n ruleElement.dispatchEvent(new PushStateTreeEvent(LEAVE));\n return;\n }\n\n // dispatch the match event\n this.eventStack[MATCH].push({\n element: ruleElement,\n events: [\n new PushStateTreeEvent(MATCH)\n ]\n });\n\n var isNewURI = ruleElement.routerURI !== oldURI;\n ruleElement.routerURI = this.uri;\n ruleElement.uri = match[0];\n ruleElement.setAttribute('uri', match[0]);\n\n if (oldMatch.length === 0 || isNewURI) {\n // stack dispatch enter event\n this.eventStack[ENTER].push({\n element: ruleElement,\n events: [\n new PushStateTreeEvent(UPDATE, {\n detail: {type: ENTER}\n }),\n new PushStateTreeEvent(ENTER)\n ]\n });\n\n children.forEach(recursiveDispatcher.bind(this, uri, oldURI));\n return;\n }\n\n // if has something changed, dispatch the change event\n if (match[0] !== oldMatch[0]) {\n // stack dispatch enter event\n this.eventStack[CHANGE].push({\n element: ruleElement,\n events: [\n new PushStateTreeEvent(UPDATE, {\n detail: {type: CHANGE}\n }),\n new PushStateTreeEvent(CHANGE)\n ]\n });\n }\n\n children.forEach(recursiveDispatcher.bind(this, uri, oldURI));\n }\n }\n };\n\n function preProcessUriBeforeExecuteNativeHistoryMethods(method) {\n /*jshint validthis:true */\n var scopeMethod = method;\n this[method] = function () {\n // Wrap method\n\n // remove the method from arguments\n var args = Array.prototype.slice.call(arguments);\n\n // if has a basePath translate the not relative paths to use the basePath\n if (scopeMethod === 'pushState' || scopeMethod === 'replaceState') {\n\n if (!isExternal(args[2])) {\n // When not external link, need to normalize the URI\n\n if (isRelative(args[2])) {\n // Relative to the uri\n var basePath = this.uri.match(/^([^?#]*)\\//);\n basePath = basePath ? basePath[1] + '/' : '';\n args[2] = basePath + args[2];\n } else {\n // This isn't relative, will cleanup / and # from the begin and use the remain path\n args[2] = args[2].match(/^([#/]*)?(.*)/)[2];\n }\n\n if (!this[USE_PUSH_STATE]) {\n\n // Ignore basePath when using location.hash and resolve relative path and keep\n // the current location.pathname, some browsers history API might apply the new pathname\n // with the hash content if not explicit\n args[2] = location.pathname + '#' + resolveRelativePath(args[2]);\n } else {\n\n // Add the basePath to your uri, not allowing to go by pushState outside the basePath\n args[2] = this.basePath + args[2];\n }\n }\n }\n\n root.history[scopeMethod].apply(root.history, args);\n\n // Chainnable\n return this;\n };\n }\n\n // Wrap history methods\n for (var method in root.history) {\n if (typeof root.history[method] === 'function') {\n preProcessUriBeforeExecuteNativeHistoryMethods.call(PushStateTree.prototype, method);\n }\n }\n\n PushStateTree.prototype[HAS_PUSH_STATE] = root.history && !!root.history.pushState;\n if (!PushStateTree.prototype[HAS_PUSH_STATE]) {\n PushStateTree.prototype[USE_PUSH_STATE] = false;\n }\n\n var lastTitle = null;\n\n if (!PushStateTree.prototype.pushState) {\n PushStateTree.prototype.pushState = function(state, title, uri) {\n var t = document.title || '';\n uri = uri || '';\n if (lastTitle !== null) {\n document.title = lastTitle;\n }\n this.avoidHashchangeHandler();\n\n // Replace hash url\n if (isExternal(uri)) {\n // this will redirect the browser, so doesn't matters the rest...\n root.location.href = uri;\n }\n\n // Remove the has if is it present\n if (uri[0] === '#') {\n uri = uri.slice(1);\n }\n\n if (isRelative(uri)) {\n uri = root.location.hash.slice(1, root.location.hash.lastIndexOf('/') + 1) + uri;\n uri = resolveRelativePath(uri);\n }\n\n root.location.hash = uri;\n\n document.title = t;\n lastTitle = title;\n\n return this;\n };\n }\n\n if (!PushStateTree.prototype.replaceState) {\n PushStateTree.prototype.replaceState = function(state, title, uri) {\n var t = document.title || '';\n uri = uri || '';\n if (lastTitle !== null) {\n document.title = lastTitle;\n }\n this.avoidHashchangeHandler();\n\n // Replace the url\n if (isExternal(uri)) {\n throw new Error('Invalid url replace.');\n }\n\n if (uri[0] === '#') {\n uri = uri.slice(1);\n }\n\n if (isRelative(uri)) {\n var relativePos = root.location.hash.lastIndexOf('/') + 1;\n uri = root.location.hash.slice(1, relativePos) + uri;\n uri = resolveRelativePath(uri);\n }\n\n // Always use hash navigation\n uri = '#' + uri;\n\n root.location.replace(uri);\n document.title = t;\n lastTitle = title;\n\n return this;\n };\n }\n\n root.PushStateTree = PushStateTree;\n\n // Node import support\n if(typeof module !== 'undefined') module.exports = PushStateTree;\n})((function(){\n /*jshint strict: false */\n return this;\n}()));\n\n\n/** WEBPACK FOOTER **\n ** ./src/pushStateTree.js\n **/"],"sourceRoot":""} \ No newline at end of file diff --git a/push-state-tree.min.js b/push-state-tree.min.js new file mode 100644 index 0000000..977931c --- /dev/null +++ b/push-state-tree.min.js @@ -0,0 +1,6 @@ +/*! + * ! PushStateTree - v0.15.0 - 2016-03-29 + * * https://github.com/gartz/pushStateTree/ + * * Copyright (c) 2016 Gabriel Reitz Giannattasio ; Licensed undefined + */ +!function(t){function e(i){if(n[i])return n[i].exports;var r=n[i]={exports:{},id:i,loaded:!1};return t[i].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){!function(e){"use strict";function n(t){return!isNaN(parseFloat(t))&&t%1===0&&isFinite(t)}function i(t,e,n){Object.defineProperty(t,e,{get:function(){return n},set:function(){}})}function r(t){return/^[a-z0-9]+:\/\//i.test(t)}function o(t){return/^[^#\/]/.test(t)}function a(t){for(var e=t.split("/"),n=1;n0&&".."!==e[n-1]&&(e.splice(n-1,2),n-=2),n++;return e.join("/").replace(/\/\.\/|\.\/|\.\.\//g,"/").replace(/^\/$/,"")}function s(t){function n(t){if("function"==typeof s.prototype[t])r[t]=function(){return s.prototype[t].apply(this,arguments)};else{if("undefined"!=typeof r[t])return;Object.defineProperty(r,t,{get:function(){return s.prototype[t]},set:function(e){s.prototype[t]=e}})}}if(t=t||{},t[v]=t[v]!==!1,!(this instanceof A))return s.apply(u.createElement("pushstatetree-route"),arguments);var r=this;this.VERSION="0.15.0";for(var o in t)t.hasOwnProperty(o)&&(r[o]=t[o]);if(s.prototype[y]){var a=t[v];Object.defineProperty(r,v,{get:function(){return a},set:function(t){a=t!==!1}})}else i(r,v,!1);Object.defineProperty(r,"beautifyLocation",{get:function(){return s.prototype.beautifyLocation&&a},set:function(t){s.prototype.beautifyLocation=t===!0}}),r.beautifyLocation=t.beautifyLocation&&r.usePushState;var c;Object.defineProperty(r,"basePath",{get:function(){return c},set:function(t){c=t||"","/"!==c[0]&&(c="/"+c)}}),r.basePath=t.basePath;for(var h in s.prototype)s.prototype.hasOwnProperty(h)&&n(h);i(r,"length",e.history.length),i(r,"state",e.history.state);var d={url:"",uri:""};Object.defineProperty(r,"uri",{get:function(){if(d.url===e.location.href)return d.uri;var t;if(e.location.hash.length||"#"===e.location.href[p.href.length-1]){t=e.location.hash.match(/^(#*)?(.*\/?)/)[2];var n=r[v];r.beautifyLocation&&r.isPathValid&&n&&r.replaceState(r.state,r.title,"/"===t[0]?t:"/"+t)}else t=e.location.pathname+e.location.search,this.isPathValid&&(t=t.slice(this.basePath.length));return t=t.replace(/^[\/]+/,""),r.getAttribute("uri")!==t&&r.setAttribute("uri",t),d.url=e.location.href,d.uri=t,t},configurable:!0}),Object.defineProperty(r,"isPathValid",{get:function(){var t=e.location.pathname+e.location.search;return!this.basePath||0===t.indexOf(this.basePath)}}),r.eventStack={leave:[],change:[],enter:[],match:[]},e.addEventListener(E,function(){var t=r.uri,e=r.state;r.rulesDispatcher(),j=t,F=e,x&&this.dispatch()}.bind(r));var g=!1,m=function(){if(!g&&j!==r.uri){var t=r.uri,e=r.state;r.rulesDispatcher(),j=t,F=e,x&&this.dispatch()}}.bind(r);r.avoidHashchangeHandler=function(){e.removeEventListener(b,m),g=!0},e.addEventListener(b,m);var _=function(){e.dispatchEvent(new f(b))};return u.addEventListener("DOMContentLoaded",_),e.addEventListener("readystatechange",_),e.addEventListener("load",function(){_(),l&&e.setInterval(function(){return r.uri!==j?void _():void(g&&(g=!1,j=r.uri,e.addEventListener(b,m)))}.bind(r),50)}.bind(r)),this}function c(t){var n=t;this[t]=function(){var t=Array.prototype.slice.call(arguments);if(("pushState"===n||"replaceState"===n)&&!r(t[2])){if(o(t[2])){var i=this.uri.match(/^([^?#]*)\//);i=i?i[1]+"/":"",t[2]=i+t[2]}else t[2]=t[2].match(/^([#\/]*)?(.*)/)[2];this[v]?t[2]=this.basePath+t[2]:t[2]=p.pathname+"#"+a(t[2])}return e.history[n].apply(e.history,t),this}}var u=e.document,h=e.window,p=e.location,l=function(){var t=h.navigator.userAgent.indexOf("Trident");return t>=0}();!function(){e.location.origin||(e.location.origin=e.location.protocol+"//"+e.location.host)}(),function(){Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var e=Array.prototype.slice.call(arguments,1),n=this,i=function(){},r=function(){var r=t;return this instanceof i&&t&&(r=this),n.apply(r,e.concat(Array.prototype.slice.call(arguments)))};return i.prototype=this.prototype,r.prototype=new i,r})}();var f=e.HashChangeEvent,d=e.Event;!function(){function t(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var n=u.createEvent("CustomEvent");return n.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n}if(Element.prototype.addEventListener){t.prototype=d.prototype,e.CustomEvent&&!l||(e.CustomEvent=t),(!f||e.opera&&e.opera.version()<15||l)&&(f=e.CustomEvent),l&&(d=t);try{new f("hashchange")}catch(n){f=t}try{new d("popstate")}catch(n){d=t}}}(),function(){if(!Element.prototype.addEventListener&&Object.defineProperty){var t=u.createEventObject().constructor.prototype;Object.defineProperty(t,"target",{get:function(){return this.srcElement}});var n=function(t,e){this.__bindedFunctions||(this.__bindedFunctions=[]);var n=e;if(!("on"+t in this)||"hashchange"===t){this.__elemetIEid=this.__elemetIEid||"__ie__"+Math.random();var i=t+this.__elemetIEid,r=this,o=function(t){t.propertyName===i&&n.call(r,u.documentElement[i])};if(this.__bindedFunctions.push({original:n,binded:o}),u.documentElement.attachEvent("onpropertychange",o),"hashchange"!==t)return}var a=n.bind(this);this.__bindedFunctions.push({original:n,binded:a}),this.attachEvent("on"+t,a)};HTMLDocument.prototype.addEventListener=n,Element.prototype.addEventListener=n,h.addEventListener=n;var i=function(t,e){this.__bindedFunctions||(this.__bindedFunctions=[]);var n,i=e;if(!("on"+t in this)||"hashchange"===t){for(var r=0;rn;n++)n in this&&t.push(this[n]);return e.apply(t,arguments)}}"forEach"in Array.prototype||(Array.prototype.forEach=function(t,e){for(var n=0;n>>0;if("function"!=typeof t)throw new TypeError;for(var i=[],r=arguments.length>=2?arguments[1]:void 0,o=0;n>o;o++)if(o in e){var a=e[o];t.call(r,a,o,e)&&i.push(a)}return i})}();var v="usePushState",y="hasPushState",b="hashchange",E="popstate",g="leave",m="update",_="enter",P="change",w="match",S="oldMatch",L=e.PushStateTree&&e.PushStateTree.options||{},O=e.DEBUG||L.DEBUG,A="undefined"!=typeof HTMLElement?HTMLElement:Element,F=null,j=null,I=[],x=!1,C=!1;s.prototype={beautifyLocation:!0,createRule:function(t){var e=u.createElement("pushstatetree-rule"),i=new RegExp("");Object.defineProperty(e,"rule",{get:function(){return i},set:function(t){if(t instanceof RegExp)i=t;else{if(t===i.toString())return;var n=(t+"").match(/^\/(.+)\/([gmi]*)|(.*)/);i=new RegExp(n[1]||n[3],n[2])}e.setAttribute("rule",i.toString())}}),Object.defineProperty(e,"parentGroup",{get:function(){var t=e.getAttribute("parent-group");return t&&n(t)?+t:null},set:function(t){n(t)?e.setAttribute("parent-group",t):e.removeAttribute("parent-group")}});for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);var o=[];Object.defineProperty(e,w,{get:function(){return o},set:function(t){o=t instanceof Array?t:[]}});var a=[];return Object.defineProperty(e,S,{get:function(){return a},set:function(t){a=t instanceof Array?t:[]}}),e[w]=[],e[S]=[],["assign","navigate","replace","dispatch","pushState","replaceState"].forEach(function(t){e[t]=function(){this.parentElement[t].apply(this.parentElement,arguments)}}),e},add:function(t){return this.appendChild(this.createRule(t)),this},remove:function(t){var e=t;return"string"==typeof t&&(e=this.querySelector(t)),e&&e.parentElement?(e.parentElement.removeChild(e),e):void 0},dispatch:function(){return C?(x=!0,this):(x=!1,e.dispatchEvent(new d(E)),this)},assign:function(t){return this.pushState(null,null,t).dispatch()},replace:function(t){return this.replaceState(null,null,t).dispatch()},navigate:function(){this.assign.apply(this,arguments)},rulesDispatcher:function(){function t(t,e){return Array.prototype.slice.call(this.children||this.childNodes).forEach(n.bind(this,t,e)),t}function n(t,r,o){function a(t,n){n=n||{},n.detail=n.detail||{},n.detail[w]=h||[],n.detail[S]=p||[],n.cancelable=!0,i&&"object"==typeof console&&(console.log({name:t,ruleElement:o,params:n,useURI:c,useOldURI:u}),console.trace&&console.trace());var r=new e.CustomEvent(t,n);return r}if(o.rule){var s,c=t,u=r;"number"==typeof o.parentGroup&&(c="",s=o.parentElement,s[w].length>o.parentGroup&&(c=s[w][o.parentGroup]||""),u="",s[S].length>o.parentGroup&&(u=s[S][o.parentGroup]||"")),o[w]=c[w](o.rule),"string"==typeof u?o[S]=u[w](o.rule):o[S]=[];var h=o[w],p=o[S],l=Array.prototype.slice.call(o.children);if(0===h.length){if(0===p.length||o.routerURI!==r)return;return o.uri=null,o.removeAttribute("uri"),l.forEach(n.bind(this,t,r)),o.dispatchEvent(new a(m,{detail:{type:g}})),void o.dispatchEvent(new a(g))}this.eventStack[w].push({element:o,events:[new a(w)]});var f=o.routerURI!==r;if(o.routerURI=this.uri,o.uri=h[0],o.setAttribute("uri",h[0]),0===p.length||f)return this.eventStack[_].push({element:o,events:[new a(m,{detail:{type:_}}),new a(_)]}),void l.forEach(n.bind(this,t,r));h[0]!==p[0]&&this.eventStack[P].push({element:o,events:[new a(m,{detail:{type:P}}),new a(P)]}),l.forEach(n.bind(this,t,r))}}var i=this.debug===!0||O;if(this.isPathValid&&(I.push(t.bind(this,this.uri)),!(I.length>1))){for(var r=j;I.length>0;)r=I[0].call(null,r),I.shift();C=!0;var o=this.eventStack;[P,_,w].forEach(function(t){for(;o[t].length>0;){for(var e=o[t][0].events,n=o[t][0].element;e.length>0;)n.dispatchEvent(e[0]),e.shift();o[t].shift()}}),C=!1}}};for(var H in e.history)"function"==typeof e.history[H]&&c.call(s.prototype,H);s.prototype[y]=e.history&&!!e.history.pushState,s.prototype[y]||(s.prototype[v]=!1);var T=null;s.prototype.pushState||(s.prototype.pushState=function(t,n,i){var s=u.title||"";return i=i||"",null!==T&&(u.title=T),this.avoidHashchangeHandler(),r(i)&&(e.location.href=i),"#"===i[0]&&(i=i.slice(1)),o(i)&&(i=e.location.hash.slice(1,e.location.hash.lastIndexOf("/")+1)+i,i=a(i)),e.location.hash=i,u.title=s,T=n,this}),s.prototype.replaceState||(s.prototype.replaceState=function(t,n,i){var s=u.title||"";if(i=i||"",null!==T&&(u.title=T),this.avoidHashchangeHandler(),r(i))throw new Error("Invalid url replace.");if("#"===i[0]&&(i=i.slice(1)),o(i)){var c=e.location.hash.lastIndexOf("/")+1;i=e.location.hash.slice(1,c)+i,i=a(i)}return i="#"+i,e.location.replace(i),u.title=s,T=n,this}),e.PushStateTree=s,t.exports=s}(function(){return this}())}]); \ No newline at end of file diff --git a/server.js b/server.js deleted file mode 100644 index 5bb142d..0000000 --- a/server.js +++ /dev/null @@ -1,21 +0,0 @@ -// This is a simple static server for dev -// Just start it to you can test your libs -// in a browser using HTTP protocol on port 3000 - -var static = require('node-static'); - -var fileServer = new static.Server('./'); - -require('http').createServer(function (request, response) { - request.addListener('end', function () { - fileServer.serve(request, response, function (e){ - if (e && (e.status === 404)) { // If the file wasn't found - fileServer.serveFile('/index.html', 200, {}, request, response); - } - }); - }).resume(); -}).listen(3000, function (){ - console.log('Open development server at port 3000'); - console.log('Files not found will load /index.html for pushState works!'); - console.log('http://localhost:3000'); -}); diff --git a/src/pushStateTree.js b/src/pushStateTree.js index 0b4bebf..79d3871 100644 --- a/src/pushStateTree.js +++ b/src/pushStateTree.js @@ -314,7 +314,6 @@ var options = root.PushStateTree && root.PushStateTree.options || {}; var DEBUG = root.DEBUG || options.DEBUG; - var VERSION = options.VERSION || 'development'; // Helpers function isInt(n) { diff --git a/test/specs/route-basePath.js b/test/route-basePath.js similarity index 97% rename from test/specs/route-basePath.js rename to test/route-basePath.js index 295e41b..f267bba 100644 --- a/test/specs/route-basePath.js +++ b/test/route-basePath.js @@ -1,4 +1,5 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +const PushStateTree = require('../src/pushStateTree'); + describe('PushStateTree basePath should', function() { 'use strict'; diff --git a/test/specs/route-beutifyLocation.js b/test/route-beutifyLocation.js similarity index 98% rename from test/specs/route-beutifyLocation.js rename to test/route-beutifyLocation.js index 3ba1992..b6da996 100644 --- a/test/specs/route-beutifyLocation.js +++ b/test/route-beutifyLocation.js @@ -1,4 +1,5 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +const PushStateTree = require('../src/pushStateTree'); + describe('PushStateTree beutifyLocation should', function() { 'use strict'; diff --git a/test/specs/route-createRules.js b/test/route-createRules.js similarity index 97% rename from test/specs/route-createRules.js rename to test/route-createRules.js index cdf7d23..05318d8 100644 --- a/test/specs/route-createRules.js +++ b/test/route-createRules.js @@ -1,3 +1,5 @@ +const PushStateTree = require('../src/pushStateTree'); + var customMatchers = { toBeInstanceOf: function(util, customEqualityTesters) { return { diff --git a/test/specs/route-hashnavigation.js b/test/route-hashnavigation.js similarity index 98% rename from test/specs/route-hashnavigation.js rename to test/route-hashnavigation.js index 001735e..9b59662 100644 --- a/test/specs/route-hashnavigation.js +++ b/test/route-hashnavigation.js @@ -1,4 +1,5 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +const PushStateTree = require('../src/pushStateTree'); + describe('PushStateTree hash-navigation should', function() { 'use strict'; diff --git a/test/specs/route-methods.js b/test/route-methods.js similarity index 95% rename from test/specs/route-methods.js rename to test/route-methods.js index 4259934..49f927f 100644 --- a/test/specs/route-methods.js +++ b/test/route-methods.js @@ -1,4 +1,5 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +const PushStateTree = require('../src/pushStateTree'); + describe('PushStateTree methods should', function() { 'use strict'; diff --git a/test/specs/route.js b/test/route.js similarity index 97% rename from test/specs/route.js rename to test/route.js index e8ea7a9..a38506c 100644 --- a/test/specs/route.js +++ b/test/route.js @@ -1,4 +1,5 @@ -/*globals PushStateTree, it, expect, beforeEach, beforeAll */ +const PushStateTree = require('../src/pushStateTree'); + describe('PushStateTree should', function() { 'use strict'; diff --git a/test/rule.js b/test/rule.js new file mode 100644 index 0000000..c8f9104 --- /dev/null +++ b/test/rule.js @@ -0,0 +1 @@ +const PushStateTree = require('../src/pushStateTree'); \ No newline at end of file diff --git a/test/specs/rule.js b/test/specs/rule.js deleted file mode 100644 index e69de29..0000000 diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..a287190 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,74 @@ +/*eslint no-console: 0*/ +'use strict'; +const path = require('path'); +const webpack = require('webpack'); +const moment = require('moment'); +const pkg = require('./package.json'); + +const yargs = require('yargs'); + +yargs.options({ + 'publish': { + type: 'boolean', + describe: 'Create multiple entry points' + } +}); + +let argv = yargs.argv; + +const BASE_PATH = path.resolve(__dirname); + +const BANNER = `! ${pkg.title} - v${pkg.version} - ${moment().format('YYYY-MM-DD')} +* ${pkg.homepage} +* Copyright (c) ${moment().format('YYYY')} ${pkg.author.name}; Licensed ${pkg.licenses.type}`; + +let config = { + entry: !argv.publish ? { 'push-state-tree': './src/pushStateTree' } : { + 'push-state-tree': './src/pushStateTree', + 'push-state-tree.min': './src/pushStateTree' + }, + output: { + path: BASE_PATH, + filename: '[name].js', + devtoolModuleFilenameTemplate: 'webpack://pushstatetree.source/[resource-path]?[hash]' + }, + + // Records are needed for HMR and it's used by the PHP to change layout file address + recordsOutputPath: path.join(BASE_PATH, 'build/records.json'), + resolve: { + root: path.join(BASE_PATH, 'src') + }, + module: { + preLoaders: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + loader: 'eslint' + } + ], + loaders: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + loader: 'babel', + cacheDirectory: true + // Babel configurations are located in the package.json file + } + ] + }, + plugins: [ + // Allow global definition to setup environment conditional where minification can remove pieces of code + new webpack.DefinePlugin({ + VERSION: JSON.stringify(pkg.version || '') + }), + + new webpack.BannerPlugin(BANNER, {entryOnly: true}), + + new webpack.optimize.UglifyJsPlugin({ + test: /\.min\.js$/, + sourceMap: true + }) + ] +}; + +module.exports = config; \ No newline at end of file From fc3c3b6289ec281ddebd450f02d1db899f4b0fff Mon Sep 17 00:00:00 2001 From: Gabriel Reitz Giannattasio Date: Tue, 29 Mar 2016 07:12:53 -0700 Subject: [PATCH 4/7] Fix code coverage --- .travis.yml | 1 - karma.conf.js | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 39d6bfc..8068932 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ node_js: addons: code_climate: repo_token: 418a1b87b0824002644b73e147820942b4259522011e2d14ff65ebdee6542872 -before_install: npm install -g grunt-cli install: npm install before_script: - npm test diff --git a/karma.conf.js b/karma.conf.js index 053193c..0fd9337 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -72,6 +72,7 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ + 'src/**/*.js', 'test/**/*.js' ], @@ -114,8 +115,8 @@ module.exports = function (config) { { type: 'cobertura', subdir: '.', file: 'cobertura.txt' }, { type: 'lcovonly', subdir: '.', file: 'lcov.info' }, { type: 'teamcity', subdir: '.', file: 'teamcity.txt' }, - { type: 'text', subdir: '.', file: 'text.txt' }, - { type: 'text-summary', subdir: '.', file: 'text-summary.txt' } + { type: 'text' }, + { type: 'text-summary' } ] }, From ccefe06900d6a655ade802d7b91e097fe1544e1c Mon Sep 17 00:00:00 2001 From: Gabriel Reitz Giannattasio Date: Tue, 29 Mar 2016 08:08:17 -0700 Subject: [PATCH 5/7] Fix code coverage --- karma.conf.js | 10 ++++++++-- package.json | 1 + test/route-basePath.js | 2 +- test/route-beutifyLocation.js | 2 +- test/route-createRules.js | 2 +- test/route-hashnavigation.js | 2 +- test/route-methods.js | 2 +- test/route.js | 2 +- test/rule.js | 2 +- 9 files changed, 16 insertions(+), 9 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 0fd9337..f2cb334 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -72,7 +72,6 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ - 'src/**/*.js', 'test/**/*.js' ], @@ -86,11 +85,18 @@ module.exports = function (config) { // source files, that you wanna generate coverage for // do not include tests or libraries // (these files will be instrumented by Istanbul) - 'src/**/*.js': ['coverage'], + 'src/pushStateTree.js': ['coverage', 'webpack', 'sourcemap'], 'test/**/*.js': ['webpack', 'sourcemap'] }, webpack: { + module: Object.assign(webpackConfig.module, { + postLoaders: [{ + test: /\.js/, + exclude: /(test|node_modules|bower_components)/, + loader: 'istanbul-instrumenter' + }] + }), plugins: webpackConfig.plugins, devtool: 'inline-source-map' }, diff --git a/package.json b/package.json index e4a9949..0ebe04b 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "coveralls": "^2.11.9", "eslint": "^2.5.3", "eslint-loader": "^1.3.0", + "istanbul-instrumenter-loader": "^0.2.0", "jasmine-core": "^2.4.1", "karma": "^0.13.22", "karma-chrome-launcher": "^0.2.2", diff --git a/test/route-basePath.js b/test/route-basePath.js index f267bba..0d094f0 100644 --- a/test/route-basePath.js +++ b/test/route-basePath.js @@ -1,4 +1,4 @@ -const PushStateTree = require('../src/pushStateTree'); +var PushStateTree = require('../src/pushStateTree'); describe('PushStateTree basePath should', function() { 'use strict'; diff --git a/test/route-beutifyLocation.js b/test/route-beutifyLocation.js index b6da996..f0e14fb 100644 --- a/test/route-beutifyLocation.js +++ b/test/route-beutifyLocation.js @@ -1,4 +1,4 @@ -const PushStateTree = require('../src/pushStateTree'); +var PushStateTree = require('../src/pushStateTree'); describe('PushStateTree beutifyLocation should', function() { 'use strict'; diff --git a/test/route-createRules.js b/test/route-createRules.js index 05318d8..c6bea03 100644 --- a/test/route-createRules.js +++ b/test/route-createRules.js @@ -1,4 +1,4 @@ -const PushStateTree = require('../src/pushStateTree'); +var PushStateTree = require('../src/pushStateTree'); var customMatchers = { toBeInstanceOf: function(util, customEqualityTesters) { diff --git a/test/route-hashnavigation.js b/test/route-hashnavigation.js index 9b59662..9cea837 100644 --- a/test/route-hashnavigation.js +++ b/test/route-hashnavigation.js @@ -1,4 +1,4 @@ -const PushStateTree = require('../src/pushStateTree'); +var PushStateTree = require('../src/pushStateTree'); describe('PushStateTree hash-navigation should', function() { 'use strict'; diff --git a/test/route-methods.js b/test/route-methods.js index 49f927f..c16a5a3 100644 --- a/test/route-methods.js +++ b/test/route-methods.js @@ -1,4 +1,4 @@ -const PushStateTree = require('../src/pushStateTree'); +var PushStateTree = require('../src/pushStateTree'); describe('PushStateTree methods should', function() { 'use strict'; diff --git a/test/route.js b/test/route.js index a38506c..f3fa1f8 100644 --- a/test/route.js +++ b/test/route.js @@ -1,4 +1,4 @@ -const PushStateTree = require('../src/pushStateTree'); +var PushStateTree = require('../src/pushStateTree'); describe('PushStateTree should', function() { 'use strict'; diff --git a/test/rule.js b/test/rule.js index c8f9104..ed9f851 100644 --- a/test/rule.js +++ b/test/rule.js @@ -1 +1 @@ -const PushStateTree = require('../src/pushStateTree'); \ No newline at end of file +var PushStateTree = require('../src/pushStateTree'); \ No newline at end of file From d12861365dee668dac305e8641e281c515ccea4d Mon Sep 17 00:00:00 2001 From: tonho Date: Tue, 29 Mar 2016 09:13:17 -0300 Subject: [PATCH 6/7] moving to mocha --- test/route-basePath.js | 30 ++++++++-------- test/route-beutifyLocation.js | 32 +++++++++--------- test/route-createRules.js | 8 ++--- test/route-hashnavigation.js | 64 +++++++++++++++++------------------ test/route-methods.js | 2 +- test/route.js | 8 ++--- 6 files changed, 72 insertions(+), 72 deletions(-) diff --git a/test/route-basePath.js b/test/route-basePath.js index 0d094f0..a907efa 100644 --- a/test/route-basePath.js +++ b/test/route-basePath.js @@ -36,49 +36,49 @@ describe('PushStateTree basePath should', function() { var pst = new PushStateTree({ basePath: '/test/' }); - expect(pst.basePath).toEqual('/test/'); + expect(pst.basePath).to.equal('/test/'); }); it('normalize basePath folder', function(){ expect((new PushStateTree({ basePath: 'folder/' - })).basePath).toEqual('/folder/'); + })).basePath).to.equal('/folder/'); expect((new PushStateTree({ basePath: 'folder/sub-folder/' - })).basePath).toEqual('/folder/sub-folder/'); + })).basePath).to.equal('/folder/sub-folder/'); }); it('normalize basePath file', function(){ expect((new PushStateTree({ basePath: 'file' - })).basePath).toEqual('/file'); + })).basePath).to.equal('/file'); expect((new PushStateTree({ basePath: '/file' - })).basePath).toEqual('/file'); + })).basePath).to.equal('/file'); expect((new PushStateTree({ basePath: 'folder/file' - })).basePath).toEqual('/folder/file'); + })).basePath).to.equal('/folder/file'); expect((new PushStateTree({ basePath: '/folder/file' - })).basePath).toEqual('/folder/file'); + })).basePath).to.equal('/folder/file'); expect((new PushStateTree({ basePath: 'folder/file.ext' - })).basePath).toEqual('/folder/file.ext'); + })).basePath).to.equal('/folder/file.ext'); expect((new PushStateTree({ basePath: '/folder/file.ext' - })).basePath).toEqual('/folder/file.ext'); + })).basePath).to.equal('/folder/file.ext'); expect((new PushStateTree({ basePath: 'folder/file.ext?param=value' - })).basePath).toEqual('/folder/file.ext?param=value'); + })).basePath).to.equal('/folder/file.ext?param=value'); expect((new PushStateTree({ basePath: '/folder/file.ext?param=value' - })).basePath).toEqual('/folder/file.ext?param=value'); + })).basePath).to.equal('/folder/file.ext?param=value'); }); it('use the non relative root as basePath if not specified', function(){ history.pushState(null, null, '/abc/123/'); var pst = new PushStateTree(); - expect(pst.basePath).toEqual('/'); + expect(pst.basePath).to.equal('/'); }); it('not share the basePath between route instances', function(){ @@ -88,8 +88,8 @@ describe('PushStateTree basePath should', function() { var pst2 = new PushStateTree({ basePath: '2/' }); - expect(pst1.basePath).toEqual('/1/'); - expect(pst2.basePath).toEqual('/2/'); + expect(pst1.basePath).to.equal('/1/'); + expect(pst2.basePath).to.equal('/2/'); }); -}); \ No newline at end of file +}); diff --git a/test/route-beutifyLocation.js b/test/route-beutifyLocation.js index f0e14fb..f524d59 100644 --- a/test/route-beutifyLocation.js +++ b/test/route-beutifyLocation.js @@ -50,7 +50,7 @@ describe('PushStateTree beutifyLocation should', function() { beautifyLocation: false }); location.hash = '#test'; - expect(pst.uri).toEqual('test'); + expect(pst.uri).to.equal('test'); }); it('remove the first slash from the URI in the regular URL', function(){ @@ -58,8 +58,8 @@ describe('PushStateTree beutifyLocation should', function() { beautifyLocation: false }); history.pushState(null, null, '/test'); - expect(location.pathname).toEqual('/test'); - expect(pst.uri).toEqual('test'); + expect(location.pathname).to.equal('/test'); + expect(pst.uri).to.equal('test'); }); it('remove the first slash from the URI in the location.hash', function(){ @@ -67,8 +67,8 @@ describe('PushStateTree beutifyLocation should', function() { beautifyLocation: false }); location.hash = '/test'; - expect(location.hash).toEqual('#/test'); - expect(pst.uri).toEqual('test'); + expect(location.hash).to.equal('#/test'); + expect(pst.uri).to.equal('test'); }); it('redirect from the hash to path when beautifyLocation is enabled', function(){ @@ -79,14 +79,14 @@ describe('PushStateTree beutifyLocation should', function() { // Reset URL var randomURI = Math.random() + ''; history.pushState(null, null, '/' + randomURI); - expect(pst.uri).toEqual(randomURI); - expect(location.pathname).toEqual('/' + randomURI); + expect(pst.uri).to.equal(randomURI); + expect(location.pathname).to.equal('/' + randomURI); location.hash = '/abc'; - expect(pst.uri).toEqual('abc'); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal('abc'); + expect(location.hash).to.equal(''); - expect(location.pathname).toEqual('/abc'); + expect(location.pathname).to.equal('/abc'); }); it('not apply beautifyLocation when the basePath is not fulfilled', function(){ @@ -96,8 +96,8 @@ describe('PushStateTree beutifyLocation should', function() { basePath: '/test/' }); location.hash = '/abc'; - expect(pst.uri).toEqual('abc'); - expect(location.hash).toEqual('#/abc'); + expect(pst.uri).to.equal('abc'); + expect(location.hash).to.equal('#/abc'); }); it('apply beautifyLocation when the basePath is fulfilled', function(){ @@ -107,8 +107,8 @@ describe('PushStateTree beutifyLocation should', function() { basePath: '/test/' }); location.hash = '/abc'; - expect(pst.uri).toEqual('abc'); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal('abc'); + expect(location.hash).to.equal(''); }); it('no change if usePushState is false', function(){ @@ -117,8 +117,8 @@ describe('PushStateTree beutifyLocation should', function() { usePushState: false }); pst.navigate('test2'); - expect(pst.uri).toEqual('test2'); - expect(location.hash).toEqual('#test2'); + expect(pst.uri).to.equal('test2'); + expect(location.hash).to.equal('#test2'); }); }); diff --git a/test/route-createRules.js b/test/route-createRules.js index c6bea03..ea3b1c0 100644 --- a/test/route-createRules.js +++ b/test/route-createRules.js @@ -39,23 +39,23 @@ describe('PushStateTree createRule', function() { }); it('should add the id attribute to the node', function() { - expect(rule.id).toEqual(idRule); + expect(rule.id).to.equal(idRule); }); it('should create a get function which returns the regex rule', function() { - expect(rule.rule).toEqual(regexRule); + expect(rule.rule).to.equal(regexRule); }); describe('when a set rule function is created', function() { it('should change regex value ', function() { rule.rule = /^faq(\/)?(.*)/; - expect(rule.rule).toEqual(/^faq(\/)?(.*)/); + expect(rule.rule).to.equal(/^faq(\/)?(.*)/); }); it('should convert string into regex format', function() { rule.rule = '^faq(\\/)?(.*)'; - expect(rule.rule).toEqual(/^faq(\/)?(.*)/); + expect(rule.rule).to.equal(/^faq(\/)?(.*)/); }); it('should avoid recursive loop', function() { diff --git a/test/route-hashnavigation.js b/test/route-hashnavigation.js index 9cea837..178c104 100644 --- a/test/route-hashnavigation.js +++ b/test/route-hashnavigation.js @@ -44,7 +44,7 @@ describe('PushStateTree hash-navigation should', function() { usePushState: false }); location.hash = '#test'; - expect(pst.uri).toEqual('test'); + expect(pst.uri).to.equal('test'); }); it('increment the history length when using navigate method', function(){ @@ -53,7 +53,7 @@ describe('PushStateTree hash-navigation should', function() { }); var currentLength = history.length; pst.navigate('test'); - expect(history.length).toEqual(currentLength + 1); + expect(history.length).to.equal(currentLength + 1); }); it('not increment the history length when using replace method', function(){ @@ -62,7 +62,7 @@ describe('PushStateTree hash-navigation should', function() { }); var currentLength = history.length; pst.replace('test'); - expect(history.length).toEqual(currentLength); + expect(history.length).to.equal(currentLength); }); it('change the hash address when pushstate is disabled', function(){ @@ -70,8 +70,8 @@ describe('PushStateTree hash-navigation should', function() { usePushState: false }); pst.navigate('test2'); - expect(pst.uri).toEqual('test2'); - expect(location.hash).toEqual('#test2'); + expect(pst.uri).to.equal('test2'); + expect(location.hash).to.equal('#test2'); }); it('go to the current folder when use ./ in the command', function(){ @@ -80,10 +80,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/file'; - expect(pst.uri).toEqual('folder/file'); + expect(pst.uri).to.equal('folder/file'); pst.navigate('./'); - expect(pst.uri).toEqual('folder/'); - expect(location.hash).toEqual('#folder/'); + expect(pst.uri).to.equal('folder/'); + expect(location.hash).to.equal('#folder/'); }); it('go to the parent folder when use ../ in the command from a file', function(){ @@ -92,10 +92,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/file'; - expect(pst.uri).toEqual('folder/file'); + expect(pst.uri).to.equal('folder/file'); pst.navigate('../'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the parent folder when use ../ in the command from sub-folder', function(){ @@ -104,10 +104,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/sub-folder/'; - expect(pst.uri).toEqual('folder/sub-folder/'); + expect(pst.uri).to.equal('folder/sub-folder/'); pst.navigate('../'); - expect(pst.uri).toEqual('folder/'); - expect(location.hash).toEqual('#folder/'); + expect(pst.uri).to.equal('folder/'); + expect(location.hash).to.equal('#folder/'); }); it('ignore ./ if is already in the root folder', function(){ @@ -116,10 +116,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#/'; - expect(pst.uri).toEqual(''); + expect(pst.uri).to.equal(''); pst.navigate('./'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('ignore ../ if is already in the root folder', function(){ @@ -128,10 +128,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#/'; - expect(pst.uri).toEqual(''); + expect(pst.uri).to.equal(''); pst.navigate('../'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('stop in the root folder when there is more parent folder commands them possible', function(){ @@ -140,10 +140,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/'; - expect(pst.uri).toEqual('folder/'); + expect(pst.uri).to.equal('folder/'); pst.navigate('../../'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the root folder when use / in the command from file', function(){ @@ -152,10 +152,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/file'; - expect(pst.uri).toEqual('folder/file'); + expect(pst.uri).to.equal('folder/file'); pst.navigate('/'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the root folder when use / in the command from a folder', function(){ @@ -164,10 +164,10 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/'; - expect(pst.uri).toEqual('folder/'); + expect(pst.uri).to.equal('folder/'); pst.navigate('/'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); it('go to the root folder when use / in the command from a sub-folder', function(){ @@ -176,9 +176,9 @@ describe('PushStateTree hash-navigation should', function() { }); location.hash = '#folder/sub-folder/'; - expect(pst.uri).toEqual('folder/sub-folder/'); + expect(pst.uri).to.equal('folder/sub-folder/'); pst.navigate('/'); - expect(pst.uri).toEqual(''); - expect(location.hash).toEqual(''); + expect(pst.uri).to.equal(''); + expect(location.hash).to.equal(''); }); }); diff --git a/test/route-methods.js b/test/route-methods.js index c16a5a3..e6ad29b 100644 --- a/test/route-methods.js +++ b/test/route-methods.js @@ -50,7 +50,7 @@ describe('PushStateTree methods should', function() { 'replace', 'navigate' ].forEach(function(method){ - expect(methods).toContain(method); + expect(methods).to.contain(method); }); }); diff --git a/test/route.js b/test/route.js index f3fa1f8..bdb8ca1 100644 --- a/test/route.js +++ b/test/route.js @@ -38,12 +38,12 @@ describe('PushStateTree should', function() { it('instances without "new" operator', function() { /* jshint newcap: false*/ - expect(PushStateTree()).toEqual(jasmine.any(HTMLElement)); + expect(PushStateTree()).to.equal(jasmine.any(HTMLElement)); }); it('construct and became a HTMLElement instance', function(){ - expect(new PushStateTree()).toEqual(jasmine.any(HTMLElement)); - expect(new PushStateTree({})).toEqual(jasmine.any(HTMLElement)); + expect(new PushStateTree()).to.equal(jasmine.any(HTMLElement)); + expect(new PushStateTree({})).to.equal(jasmine.any(HTMLElement)); }); it('auto enable push state if browser support it', function(){ @@ -89,7 +89,7 @@ describe('PushStateTree should', function() { it('get the current URI', function(){ var pst = new PushStateTree(); - expect(pst.uri).toEqual(location.pathname.slice(1)); + expect(pst.uri).to.equal(location.pathname.slice(1)); }); }); From ea6ebe165bcc54432fcd4dd025af69f438e86868 Mon Sep 17 00:00:00 2001 From: tonho Date: Tue, 29 Mar 2016 10:35:13 -0300 Subject: [PATCH 7/7] changed to mocha + chai --- karma.conf.js | 2 +- package.json | 12 +++++++++--- test/route-basePath.js | 2 +- test/route-beutifyLocation.js | 8 ++++---- test/route-createRules.js | 28 +++++----------------------- test/route-hashnavigation.js | 4 ++-- test/route-methods.js | 2 +- test/route.js | 26 +++++++++++++------------- 8 files changed, 36 insertions(+), 48 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index f2cb334..7022fa7 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -68,7 +68,7 @@ module.exports = function (config) { basePath: '.', // frameworks to use - frameworks: ['jasmine'], + frameworks: ['mocha', 'chai-spies', 'chai'], // list of files / patterns to load in the browser files: [ diff --git a/package.json b/package.json index 0ebe04b..9c1db49 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,14 @@ "eslint-loader": "^1.3.0", "istanbul-instrumenter-loader": "^0.2.0", "jasmine-core": "^2.4.1", + "chai": "^3.5.0", + "chai-spies": "^0.7.1", "karma": "^0.13.22", + "karma-chai": "^0.1.0", + "karma-chai-spies": "^0.1.4", "karma-chrome-launcher": "^0.2.2", "karma-coverage": "^0.5.5", - "karma-jasmine": "^0.3.7", + "karma-mocha": "^0.2.2", "karma-phantomjs-launcher": "^1.0.0", "karma-script-launcher": "^0.1.0", "karma-sourcemap-loader": "^0.3.7", @@ -54,7 +58,8 @@ "phantomjs-prebuilt": "^2.1.4", "webpack": "^1.12.14", "webpack-dev-server": "^1.14.1", - "yargs": "^4.3.2" + "yargs": "^4.3.2", + "mocha": "^2.4.5" }, "keywords": [ "route", @@ -81,5 +86,6 @@ "shim", "navigator", "navigation" - ] + ], + "dependencies": {} } diff --git a/test/route-basePath.js b/test/route-basePath.js index a907efa..40335a9 100644 --- a/test/route-basePath.js +++ b/test/route-basePath.js @@ -11,7 +11,7 @@ describe('PushStateTree basePath should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); diff --git a/test/route-beutifyLocation.js b/test/route-beutifyLocation.js index f524d59..5c0e0ca 100644 --- a/test/route-beutifyLocation.js +++ b/test/route-beutifyLocation.js @@ -11,7 +11,7 @@ describe('PushStateTree beutifyLocation should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); @@ -34,15 +34,15 @@ describe('PushStateTree beutifyLocation should', function() { it('not enable beautifyLocation feature by default', function(){ var pst = new PushStateTree(); - expect(pst.beautifyLocation).toBeFalsy(); + expect(pst.beautifyLocation).to.be.false; }); it('allow to change the beautifyLocation flag after start running', function(){ var pst = new PushStateTree(); pst.beautifyLocation = false; - expect(pst.beautifyLocation).toBeFalsy(); + expect(pst.beautifyLocation).to.be.false; pst.beautifyLocation = true; - expect(pst.beautifyLocation).toBeTruthy(); + expect(pst.beautifyLocation).to.be.true; }); it('prioritise the hash to provide the URI', function(){ diff --git a/test/route-createRules.js b/test/route-createRules.js index ea3b1c0..29e0e2a 100644 --- a/test/route-createRules.js +++ b/test/route-createRules.js @@ -1,25 +1,7 @@ var PushStateTree = require('../src/pushStateTree'); -var customMatchers = { - toBeInstanceOf: function(util, customEqualityTesters) { - return { - compare: function(actual, expected) { - var result = {}; - result.pass = actual instanceof expected; - if (!result.pass) { - return "Expected " + actual.constructor.name + notText + " is instance of " + expectedInstance.name; - } - return result; - } - }; - } -} - describe('PushStateTree createRule', function() { 'use strict'; - beforeEach(function() { - jasmine.addMatchers(customMatchers); - }); describe('when rule /^servers(\/)?(.*)/', function() { var pst; @@ -35,7 +17,7 @@ describe('PushStateTree createRule', function() { }); it('should create the html node', function() { - expect(rule).toBeInstanceOf(HTMLElement); + expect(rule).to.be.instanceof(HTMLElement); }); it('should add the id attribute to the node', function() { @@ -50,18 +32,18 @@ describe('PushStateTree createRule', function() { it('should change regex value ', function() { rule.rule = /^faq(\/)?(.*)/; - expect(rule.rule).to.equal(/^faq(\/)?(.*)/); + expect(rule.rule.toString()).to.equal(/^faq(\/)?(.*)/.toString()); }); it('should convert string into regex format', function() { rule.rule = '^faq(\\/)?(.*)'; - expect(rule.rule).to.equal(/^faq(\/)?(.*)/); + expect(rule.rule).to.be.instanceof(RegExp); }); it('should avoid recursive loop', function() { - spyOn(rule, 'setAttribute'); + chai.spy.on(rule, 'setAttribute'); rule.rule = '/^servers(\\/)?(.*)/'; - expect(rule.setAttribute).not.toHaveBeenCalled(); + expect(rule.setAttribute).not.to.have.been.called; }); }); diff --git a/test/route-hashnavigation.js b/test/route-hashnavigation.js index 178c104..b1cdb24 100644 --- a/test/route-hashnavigation.js +++ b/test/route-hashnavigation.js @@ -11,7 +11,7 @@ describe('PushStateTree hash-navigation should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); @@ -36,7 +36,7 @@ describe('PushStateTree hash-navigation should', function() { var pst = new PushStateTree({ usePushState: false }); - expect(pst.usePushState).toBeFalsy(); + expect(pst.usePushState).to.be.false; }); it('detect the hash address', function(){ diff --git a/test/route-methods.js b/test/route-methods.js index e6ad29b..e8b16d2 100644 --- a/test/route-methods.js +++ b/test/route-methods.js @@ -11,7 +11,7 @@ describe('PushStateTree methods should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); diff --git a/test/route.js b/test/route.js index bdb8ca1..ad13b03 100644 --- a/test/route.js +++ b/test/route.js @@ -11,7 +11,7 @@ describe('PushStateTree should', function() { load: [] }; - beforeAll(function(){ + before(function(){ var addEventListener = window.addEventListener; window.addEventListener = function(name, callback){ events[name].push(callback); @@ -33,49 +33,49 @@ describe('PushStateTree should', function() { }); it('be available on global scope', function() { - expect(PushStateTree).toBeDefined(); + expect(PushStateTree).to.be.defined; }); it('instances without "new" operator', function() { /* jshint newcap: false*/ - expect(PushStateTree()).to.equal(jasmine.any(HTMLElement)); + expect(PushStateTree()).to.be.instanceof(HTMLElement); }); it('construct and became a HTMLElement instance', function(){ - expect(new PushStateTree()).to.equal(jasmine.any(HTMLElement)); - expect(new PushStateTree({})).to.equal(jasmine.any(HTMLElement)); + expect(new PushStateTree()).to.be.instanceof(HTMLElement); + expect(new PushStateTree({})).to.be.instanceof(HTMLElement); }); it('auto enable push state if browser support it', function(){ var pst = new PushStateTree(); - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('allow to disable push state if it constructor has a false option', function(){ var pst = new PushStateTree({ usePushState: false }); - expect(pst.usePushState).toBeFalsy(); + expect(pst.usePushState).to.be.false; }); it('allow to enable push state if it constructor has a true option', function(){ var pst = new PushStateTree({ usePushState: true }); - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('usePushState be true by default', function(){ var pst = new PushStateTree(); - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('allow to change the usePushState flag after start running', function(){ var pst = new PushStateTree(); pst.usePushState = false; - expect(pst.usePushState).toBeFalsy(); + expect(pst.usePushState).to.be.false; pst.usePushState = true; - expect(pst.usePushState).toBeTruthy(); + expect(pst.usePushState).to.be.true; }); it('usePushState change for each instance', function(){ @@ -83,8 +83,8 @@ describe('PushStateTree should', function() { usePushState: false }); var pst2 = new PushStateTree(); - expect(pst1.usePushState).toBeFalsy(); - expect(pst2.usePushState).toBeTruthy(); + expect(pst1.usePushState).to.be.false; + expect(pst2.usePushState).to.be.true; }); it('get the current URI', function(){