diff --git a/README.md b/README.md index 202c3e8..b9fc598 100755 --- a/README.md +++ b/README.md @@ -95,6 +95,9 @@ just paste the code to your own `index.html`, and load it up in a browser. open the console to see the output when navigating. +## Project preparation + +Run 'npm install' and 'bower install' to fetch all dependencies. ## Demos diff --git a/bower.json b/bower.json index 4a9d074..686aef4 100755 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "stateman", - "version": "0.2.0", + "version": "0.2.2", "main": "stateman.js", "description": "A tiny foundation that providing nested state-based routing for complex web application.", "keywords": [ @@ -19,5 +19,8 @@ "gulpfile.js", "*.json", "*.md" - ] -} \ No newline at end of file + ], + "dependencies": { + "qs": "https://github.com/ljharb/qs.git#^6.2.0" + } +} diff --git a/component.json b/component.json index 400002b..3116ab9 100755 --- a/component.json +++ b/component.json @@ -1,6 +1,6 @@ { "name": "stateman", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.js", "description": "A tiny foundation that providing nested state-based routing for complex web application.", "keywords": [ @@ -14,6 +14,7 @@ "src/histery.js", "src/state.js", "src/stateman.js", - "src/util.js" + "src/util.js", + "bower_components/qs/lib/index.js" ] } \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 4b59f35..046eca4 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -8,230 +8,234 @@ var mocha = require('gulp-mocha'); var uglify = require('gulp-uglify'); var _ = require("./src/util.js"); var karma = require("karma").server; -var translate = require("./scripts/gulp-trans.js") +var translate = require("./scripts/gulp-trans.js"); - -var pkg = require("./package.json"); +var pkg = require("./package.json"); // release require("./scripts/release.js")(gulp); - var wpConfig = { - output: { - filename: "stateman.js", - library: "StateMan", - libraryTarget: "umd" - } -} + output: { + filename: "stateman.js", + library: "StateMan", + libraryTarget: "umd" + } +}; var testConfig = { - output: { - filename: "dom.bundle.js" - } -} + output: { + filename: "dom.bundle.js" + } +}; var karmaCommonConf = { - browsers: ['Chrome', 'Firefox', 'IE', 'IE9', 'IE8', 'IE7', 'PhantomJS'], - frameworks: ['mocha', 'commonjs'], - files: [ - 'test/runner/vendor/expect.js', - 'src/**/*.js', - 'test/spec/test-*.js', - 'test/spec/dom-*.js' - ], - client: { - mocha: {ui: 'bdd'} - }, - customLaunchers: { - IE9: { - base: 'IE', - 'x-ua-compatible': 'IE=EmulateIE9' + browsers: ['Chrome', 'Firefox', 'IE', 'IE9', 'IE8', 'IE7', 'PhantomJS'], + frameworks: ['mocha', 'commonjs'], + files: [ + 'test/runner/vendor/expect.js', + 'src/**/*.js', + "bower_components/qs/lib/**/*.js", + 'test/spec/test-*.js', + 'test/spec/dom-*.js' + ], + client: { + mocha: {ui: 'bdd'} }, - IE8: { - base: 'IE', - 'x-ua-compatible': 'IE=EmulateIE8' + customLaunchers: { + IE9: { + base: 'IE', + 'x-ua-compatible': 'IE=EmulateIE9' + }, + IE8: { + base: 'IE', + 'x-ua-compatible': 'IE=EmulateIE8' + }, + IE7: { + base: 'IE', + 'x-ua-compatible': 'IE=EmulateIE7' + } }, - IE7: { - base: 'IE', - 'x-ua-compatible': 'IE=EmulateIE7' - } - }, - - preprocessors: { - 'src/**/*.js': ['commonjs', 'coverage'], - 'test/spec/test-*.js': ['commonjs'], - 'test/spec/dom-*.js': ['commonjs'], - 'test/runner/vendor/expect.js': ['commonjs'] - }, - - // coverage reporter generates the coverage - reporters: ['progress', 'coverage'], - - // preprocessors: { - // // source files, that you wanna generate coverage for - // // do not include tests or libraries - // // (these files will be instrumented by Istanbul) - // // 'test/regular.js': ['coverage'] - // }, - - // optionally, configure the reporter - coverageReporter: { - type: 'html' - } - -}; - - + preprocessors: { + 'src/**/*.js': ['commonjs', 'coverage'], + "bower_components/qs/lib/**/*.js": ['commonjs'], + 'test/spec/test-*.js': ['commonjs'], + 'test/spec/dom-*.js': ['commonjs'], + 'test/runner/vendor/expect.js': ['commonjs'] + }, + // coverage reporter generates the coverage + reporters: ['progress', 'coverage'], -gulp.task('jshint', function(){ - // jshint - gulp.src(['src/**/*.js']) - .pipe(jshint()) - .pipe(jshint.reporter('default')) + // preprocessors: { + // // source files, that you wanna generate coverage for + // // do not include tests or libraries + // // (these files will be instrumented by Istanbul) + // // 'test/regular.js': ['coverage'] + // }, -}) + // optionally, configure the reporter + coverageReporter: { + type: 'html' + } +}; - -gulp.task('build', ['jshint'], function() { - gulp.src("src/index.js") - .pipe(webpack(wpConfig)) - .pipe(wrap(signatrue)) - .pipe(gulp.dest('./')) - .pipe(wrap(mini)) - .pipe(uglify()) - .pipe(gulp.dest('./')) - .on("error", function(err){ - throw err - }) +gulp.task('jshint', function () +{ + // jshint + gulp.src(['src/**/*.js', 'bower_components/qs/lib/**/*.js']) + .pipe(jshint()) + .pipe(jshint.reporter('default')) }); -gulp.task('testbundle', function(){ - gulp.src("test/spec/dom.exports.js") - .pipe(webpack(testConfig)) - .pipe(gulp.dest('test/runner')) - .on("error", function(err){ - throw err - }) -}) - -gulp.task('mocha', function(){ - - return gulp.src(['test/spec/test-*.js']) - - .pipe(mocha({reporter: 'spec' }) ) - - .on('error', function(err){ - console.log(err) - console.log('\u0007'); - }) - .on('end', function(){ - // before_mocha.clean(); - }); -}) - - -gulp.task('watch', ["build", 'testbundle'], function(){ - gulp.watch(['src/**/*.js'], ['build']); -gulp.watch(['docs/src/*.md'], ['doc']); - - gulp.watch(['test/spec/*.js', 'src/**/*.js'], ['testbundle']) -}) - - -gulp.task('default', [ 'watch']); - - -gulp.task('mocha', function() { +gulp.task('build', ['jshint'], function () +{ + gulp.src("src/index.js") + .pipe(webpack(wpConfig)) + .pipe(wrap(signatrue)) + .pipe(gulp.dest('./')) + .pipe(wrap(mini)) + .pipe(uglify()) + .pipe(gulp.dest('./')) + .on("error", function (err) + { + throw err + }) +}); - return gulp.src(['test/spec/test-*.js', 'test/spec/node-*.js' ]) - .pipe(mocha({reporter: 'spec' }) ) - .on('error', function(){ - // gutil.log.apply(this, arguments); - console.log('\u0007'); - }) - .on('end', function(){ - global.expect = null; - }); +gulp.task('testbundle', function () +{ + gulp.src("test/spec/dom.exports.js") + .pipe(webpack(testConfig)) + .pipe(gulp.dest('test/runner')) + .on("error", function (err) + { + throw err + }) }); +gulp.task('mocha', function () +{ + return gulp.src(['test/spec/test-*.js']) + + .pipe(mocha({reporter: 'spec'})) + + .on('error', function (err) + { + console.log(err); + console.log('\u0007'); + }) + .on('end', function () + { + // before_mocha.clean(); + }); +}); -gulp.task('karma', function (done) { - var config = _.extend({}, karmaCommonConf); - if(process.argv[3] === '--phantomjs'){ - config.browsers=["PhantomJS"] - config.coverageReporter = {type : 'text-summary'} +gulp.task('watch', ["build", 'testbundle'], function () +{ + gulp.watch(['src/**/*.js'], ['build']); + gulp.watch(['docs/src/*.md'], ['doc']); - karma.start(_.extend(config, {singleRun: true}), done); + gulp.watch(['test/spec/*.js', 'src/**/*.js'], ['testbundle']) +}); - }else if(process.argv[3] === '--browser'){ - config.browsers = null; - karma.start(_.extend(config, {singleRun: true}), done); - }else{ - karma.start(_.extend(config, {singleRun: true}), done); - } +gulp.task('default', ['watch']); + +gulp.task('mocha', function () +{ + return gulp.src(['test/spec/test-*.js', 'test/spec/node-*.js']) + .pipe(mocha({reporter: 'spec'})) + .on('error', function () + { + // gutil.log.apply(this, arguments); + console.log('\u0007'); + }) + .on('end', function () + { + global.expect = null; + }); }); +gulp.task('karma', function (done) +{ + var config = _.extend({}, karmaCommonConf); + if (process.argv[3] === '--phantomjs') + { + config.browsers = ["PhantomJS"]; + config.coverageReporter = {type: 'text-summary'}; + + karma.start(_.extend(config, {singleRun: true}), done); + + } else if (process.argv[3] === '--browser') + { + config.browsers = null; + karma.start(_.extend(config, {singleRun: true}), done); + } else + { + karma.start(_.extend(config, {singleRun: true}), done); + } +}); -gulp.task("test", ["mocha", "karma"]) +gulp.task("test", ["mocha", "karma"]); -gulp.task('doc', function(){ - return gulp.src(["docs/src/API*.md"]) - .pipe(translate({})) - .pipe(gulp.dest("docs/pages/document")) -}) +gulp.task('doc', function () +{ + return gulp.src(["docs/src/API*.md"]) + .pipe(translate({})) + .pipe(gulp.dest("docs/pages/document")) +}); // -gulp.task("release", ["tag"]) - - -gulp.task('travis', ['jshint' ,'build','mocha', 'karma']); +gulp.task("release", ["tag"]); +gulp.task('travis', ['jshint', 'build', 'mocha', 'karma']); gulp.task('server', ['build'], shell.task([ - "./node_modules/puer/bin/puer" -])) - - -gulp.task('example', function(){ - gulp.src("example/*.html") - .pipe( - gulp.dest('docs/pages/example') - ); - gulp.src("./stateman.js") - .pipe( - gulp.dest('docs/pages') - ); -}) -gulp.task('gh-pages', ['example', 'doc'], function () { - gulp.src("docs/pages/**/*.*") - .pipe(deploy({ - remoteUrl: "git@github.com:leeluolee/stateman", - branch: "gh-pages" - })) - .on("error", function(err){ - console.log(err) - }) + "./node_modules/puer/bin/puer" +])); + +gulp.task('example', function () +{ + gulp.src("example/*.html") + .pipe( + gulp.dest('docs/pages/example') + ); + gulp.src("./stateman.js") + .pipe( + gulp.dest('docs/pages') + ); }); +gulp.task('gh-pages', ['example', 'doc'], function () +{ + gulp.src("docs/pages/**/*.*") + .pipe(deploy({ + remoteUrl: "git@github.com:leeluolee/stateman", + branch: "gh-pages" + })) + .on("error", function (err) + { + console.log(err) + }) +}); - - -function wrap(fn){ - return through.obj(fn); +function wrap(fn) +{ + return through.obj(fn); } -function signatrue(file, enc, cb){ - var sign = '/**\n'+ '@author\t'+ pkg.author.name + '\n'+ '@version\t'+ pkg.version + - '\n'+ '@homepage\t'+ pkg.homepage + '\n*/\n'; - file.contents = Buffer.concat([new Buffer(sign), file.contents]); - cb(null, file); +function signatrue(file, enc, cb) +{ + var sign = '/**\n' + '@author\t' + pkg.author.name + '\n' + '@version\t' + pkg.version + + '\n' + '@homepage\t' + pkg.homepage + '\n*/\n'; + file.contents = Buffer.concat([new Buffer(sign), file.contents]); + cb(null, file); } -function mini(file, enc, cb){ - file.path = file.path.replace('.js', '.min.js'); - cb(null, file) +function mini(file, enc, cb) +{ + file.path = file.path.replace('.js', '.min.js'); + cb(null, file) } diff --git a/package.json b/package.json index 606f2d5..b226baf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stateman", - "version": "0.2.0", + "version": "0.2.1", "description": "A tiny foundation that providing nested state-based routing for complex web application.", "keywords": [ "router", diff --git a/src/state.js b/src/state.js index 09ee7b1..049daea 100755 --- a/src/state.js +++ b/src/state.js @@ -1,154 +1,180 @@ var _ = require("./util.js"); - - - -function State(option){ - this._states = {}; - this._pending = false; - this.visited = false; - if(option) this.config(option); +var Qs = require('../bower_components/qs/lib/index.js'); + +function State(option) +{ + this._states = {}; + this._pending = false; + this.visited = false; + if (option) this.config(option); } - //regexp cache State.rCache = {}; -_.extend( _.emitable( State ), { - - state: function(stateName, config){ - if(_.typeOf(stateName) === "object"){ - for(var i in stateName){ - this.state(i, stateName[i]) - } - return this; - } - var current, next, nextName, states = this._states, i=0; - - if( typeof stateName === "string" ) stateName = stateName.split("."); - - var slen = stateName.length, current = this; - var stack = []; - - - do{ - nextName = stateName[i]; - next = states[nextName]; - stack.push(nextName); - if(!next){ - if(!config) return; - next = states[nextName] = new State(); - _.extend(next, { - parent: current, - manager: current.manager || current, - name: stack.join("."), - currentName: nextName - }) - current.hasNext = true; - next.configUrl(); - } - current = next; - states = next._states; - }while((++i) < slen ) - - if(config){ - next.config(config); - return this; - } else { - return current; +_.extend(_.emitable(State), { + + state: function (stateName, config) + { + if (_.typeOf(stateName) === "object") + { + for (var i in stateName) + { + this.state(i, stateName[i]) + } + return this; + } + var current, next, nextName, states = this._states, i = 0; + + if (typeof stateName === "string") stateName = stateName.split("."); + + var slen = stateName.length, current = this; + var stack = []; + + + do { + nextName = stateName[i]; + next = states[nextName]; + stack.push(nextName); + if (!next) + { + if (!config) return; + next = states[nextName] = new State(); + _.extend(next, { + parent: current, + manager: current.manager || current, + name: stack.join("."), + currentName: nextName + }); + current.hasNext = true; + next.configUrl(); + } + current = next; + states = next._states; + } while ((++i) < slen); + + if (config) + { + next.config(config); + return this; + } else + { + return current; + } + }, + + config: function (configure) + { + + configure = this._getConfig(configure); + + for (var i in configure) + { + var prop = configure[i]; + switch (i) + { + case "url": + if (typeof prop === "string") + { + this.url = prop; + this.configUrl(); + } + break; + case "events": + this.on(prop) + break; + default: + this[i] = prop; + } + } + }, + + // children override + _getConfig: function (configure) + { + return typeof configure === "function" ? {enter: configure} : configure; + }, + + //from url + + configUrl: function () + { + var url = "", base = this, currentUrl; + var _watchedParam = []; + + while (base) + { + + url = (typeof base.url === "string" ? base.url : (base.currentName || "")) + "/" + url; + + // means absolute; + if (url.indexOf("^/") === 0) + { + url = url.slice(1); + break; + } + base = base.parent; + } + this.pattern = _.cleanPath("/" + url); + var pathAndQuery = this.pattern.split("?"); + this.pattern = pathAndQuery[0]; + // some Query we need watched + + _.extend(this, _.normalize(this.pattern), true); + }, + encode: function (param) + { + var state = this; + var result = Qs.stringify(param); + return result; + + + // param = param || {}; + // + // var matched = "%"; + // + // var url = state.matches.replace(/\(([\w-]+)\)/g, function (all, capture) + // { + // var sec = param[capture] || ""; + // matched += capture + "%"; + // return sec; + // }) + "?"; + // + // // remained is the query, we need concat them after url as query + // for (var i in param) + // { + // if (matched.indexOf("%" + i + "%") === -1) url += i + "=" + param[i] + "&"; + // } + // return _.cleanPath(url.replace(/(?:\?|&)$/, "")) + }, + decode: function (path) + { + var result = Qs.parse(path); + return result; + + // var matched = this.regexp.exec(path), + // keys = this.keys; + // + // if (matched) + // { + // var param = {}; + // for (var i = 0, len = keys.length; i < len; i++) + // { + // param[keys[i]] = matched[i + 1] + // } + // return param; + // } else + // { + // return false; + // } + }, + // by default, all lifecycle is permitted + + async: function () + { + throw new Error('please use option.async instead') } - }, - - config: function(configure){ - - configure = this._getConfig(configure); - - for(var i in configure){ - var prop = configure[i]; - switch(i){ - case "url": - if(typeof prop === "string"){ - this.url = prop; - this.configUrl(); - } - break; - case "events": - this.on(prop) - break; - default: - this[i] = prop; - } - } - }, - - // children override - _getConfig: function(configure){ - return typeof configure === "function"? {enter: configure} : configure; - }, - - //from url - - configUrl: function(){ - var url = "" , base = this, currentUrl; - var _watchedParam = []; - - while( base ){ - - url = (typeof base.url === "string" ? base.url: (base.currentName || "")) + "/" + url; - - // means absolute; - if(url.indexOf("^/") === 0) { - url = url.slice(1); - break; - } - base = base.parent; - } - this.pattern = _.cleanPath("/" + url); - var pathAndQuery = this.pattern.split("?"); - this.pattern = pathAndQuery[0]; - // some Query we need watched - - _.extend(this, _.normalize(this.pattern), true); - }, - encode: function(param){ - var state = this; - param = param || {}; - - var matched = "%"; - - var url = state.matches.replace(/\(([\w-]+)\)/g, function(all, capture){ - var sec = param[capture] || ""; - matched+= capture + "%"; - return sec; - }) + "?"; - - // remained is the query, we need concat them after url as query - for(var i in param) { - if( matched.indexOf("%"+i+"%") === -1) url += i + "=" + param[i] + "&"; - } - return _.cleanPath( url.replace(/(?:\?|&)$/,"") ) - }, - decode: function( path ){ - var matched = this.regexp.exec(path), - keys = this.keys; - - if(matched){ - - var param = {}; - for(var i =0,len=keys.length;i app.state - var parent = this._findBase(from , to); + // nothing -> app.state + var parent = this._findBase(from, to); - option.basckward = true; - this._transit( from, parent, option, callForPermit , _.bind( function( notRejected ){ + option.basckward = true; + this._transit(from, parent, option, callForPermit, _.bind(function (notRejected) + { - if( notRejected === false ) return callback( notRejected ); + if (notRejected === false) return callback(notRejected); - // only actual transiton need update base state; - if( !callForPermit ) this._checkQueryAndParam(parent, option) + // only actual transiton need update base state; + if (!callForPermit) this._checkQueryAndParam(parent, option) - option.basckward = false; - this._transit( parent, to, option, callForPermit, callback) + option.basckward = false; + this._transit(parent, to, option, callForPermit, callback) - }, this) ) + }, this)) }, - _transit: function(from, to, option, callForPermit, callback){ - // touch the ending - if( from === to ) return callback(); + _transit: function (from, to, option, callForPermit, callback) + { + // touch the ending + if (from === to) return callback(); - var back = from.name.length > to.name.length; - var method = back? 'leave': 'enter'; - var applied; + var back = from.name.length > to.name.length; + var method = back ? 'leave' : 'enter'; + var applied; - // use canEnter to detect permission - if( callForPermit) method = 'can' + method.replace(/^\w/, function(a){ return a.toUpperCase() }); + // use canEnter to detect permission + if (callForPermit) method = 'can' + method.replace(/^\w/, function (a) + { + return a.toUpperCase() + }); - var loop = _.bind(function( notRejected ){ + var loop = _.bind(function (notRejected) + { - // stop transition or touch the end - if( applied === to || notRejected === false ) return callback(notRejected); + // stop transition or touch the end + if (applied === to || notRejected === false) return callback(notRejected); - if( !applied ) { + if (!applied) + { - applied = back? from : this._computeNext(from, to); + applied = back ? from : this._computeNext(from, to); - }else{ + } else + { - applied = this._computeNext(applied, to); - } + applied = this._computeNext(applied, to); + } - if( (back && applied === to) || !applied )return callback( notRejected ) + if ((back && applied === to) || !applied)return callback(notRejected) - this._moveOn( applied, method, option, loop ); + this._moveOn(applied, method, option, loop); - }, this); + }, this); - loop(); + loop(); }, - _moveOn: function( applied, method, option, callback){ + _moveOn: function (applied, method, option, callback) + { - var isDone = false; - var isPending = false; + var isDone = false; + var isPending = false; - option.async = function(){ + option.async = function () + { - isPending = true; + isPending = true; - return done; - } + return done; + } - function done( notRejected ){ - if( isDone ) return; - isPending = false; - isDone = true; - callback( notRejected ); - } + function done(notRejected) + { + if (isDone) return; + isPending = false; + isDone = true; + callback(notRejected); + } - - option.stop = function(){ - done( false ); - } + option.stop = function () + { + done(false); + } - this.active = applied; - var retValue = applied[method]? applied[method]( option ): true; + this.active = applied; + var retValue = applied[method] ? applied[method](option) : true; - if(method === 'enter') applied.visited = true; - // promise - // need breadk , if we call option.stop first; + if (method === 'enter') applied.visited = true; + // promise + // need breadk , if we call option.stop first; - if( _.isPromise(retValue) ){ + if (_.isPromise(retValue)) + { - return this._wrapPromise(retValue, done); + return this._wrapPromise(retValue, done); - } + } - // if haven't call option.async yet - if( !isPending ) done( retValue ) + // if haven't call option.async yet + if (!isPending) done(retValue) }, + _wrapPromise: function (promise, next) + { - _wrapPromise: function( promise, next ){ - - return promise.then( next, function(){next(false)}) ; + return promise.then(next, function () + { + next(false) + }); }, - _computeNext: function( from, to ){ + _computeNext: function (from, to) + { - var fname = from.name; - var tname = to.name; + var fname = from.name; + var tname = to.name; - var tsplit = tname.split('.') - var fsplit = fname.split('.') + var tsplit = tname.split('.') + var fsplit = fname.split('.') - var tlen = tsplit.length; - var flen = fsplit.length; + var tlen = tsplit.length; + var flen = fsplit.length; - if(fname === '') flen = 0; - if(tname === '') tlen = 0; + if (fname === '') flen = 0; + if (tname === '') tlen = 0; - if( flen < tlen ){ - fsplit[flen] = tsplit[flen]; - }else{ - fsplit.pop(); - } + if (flen < tlen) + { + fsplit[flen] = tsplit[flen]; + } else + { + fsplit.pop(); + } - return this.state(fsplit.join('.')) + return this.state(fsplit.join('.')) }, - _findQuery: function(querystr){ - - var queries = querystr && querystr.split("&"), query= {}; - if(queries){ - var len = queries.length; - var query = {}; - for(var i =0; i< len; i++){ - var tmp = queries[i].split("="); - query[tmp[0]] = tmp[1]; + _findQuery: function (querystr) + { + + var queries = querystr && querystr.split("&"), query = {}; + if (queries) + { + var len = queries.length; + var query = {}; + for (var i = 0; i < len; i++) + { + var tmp = queries[i].split("="); + query[tmp[0]] = tmp[1]; + } } - } - return query; + return query; }, - _findState: function(state, path){ - var states = state._states, found, param; - - // leaf-state has the high priority upon branch-state - if(state.hasNext){ - for(var i in states) if(states.hasOwnProperty(i)){ - found = this._findState( states[i], path ); - if( found ) return found; + _findState: function (state, path) + { + var states = state._states, found, param; + + // leaf-state has the high priority upon branch-state + if (state.hasNext) + { + for (var i in states) if (states.hasOwnProperty(i)) + { + found = this._findState(states[i], path); + if (found) return found; + } + } + // in strict mode only leaf can be touched + // if all children is don. will try it self + param = state.regexp && state.decode(path); + if (param) + { + state.param = param; + return state; + } else + { + return false; } - } - // in strict mode only leaf can be touched - // if all children is don. will try it self - param = state.regexp && state.decode(path); - if(param){ - state.param = param; - return state; - }else{ - return false; - } }, // find the same branch; - _findBase: function(now, before){ - - if(!now || !before || now == this || before == this) return this; - var np = now, bp = before, tmp; - while(np && bp){ - tmp = bp; - while(tmp){ - if(np === tmp) return tmp; - tmp = tmp.parent; + _findBase: function (now, before) + { + + if (!now || !before || now == this || before == this) return this; + var np = now, bp = before, tmp; + while (np && bp) + { + tmp = bp; + while (tmp) + { + if (np === tmp) return tmp; + tmp = tmp.parent; + } + np = np.parent; } - np = np.parent; - } }, // check the query and Param - _checkQueryAndParam: function(baseState, options){ - - var from = baseState; - while( from !== this ){ - from.update && from.update(options); - from = from.parent; - } - + _checkQueryAndParam: function (baseState, options) + { + var from = baseState; + while (from !== this) + { + from.update && from.update(options); + from = from.parent; + } } - -}, true) - - +}, true); module.exports = StateMan; diff --git a/stateman.js b/stateman.js index 4d64994..16aafcc 100644 --- a/stateman.js +++ b/stateman.js @@ -1,6 +1,6 @@ /** @author leeluolee -@version 0.2.0 +@version 0.2.1 @homepage https://github.com/leeluolee/stateman */ (function webpackUniversalModuleDefinition(root, factory) { @@ -61,7 +61,7 @@ return /******/ (function(modules) { // webpackBootstrap var StateMan = __webpack_require__(1); - StateMan.Histery = __webpack_require__(4); + StateMan.Histery = __webpack_require__(8); StateMan.util = __webpack_require__(3); StateMan.State = __webpack_require__(2); @@ -73,467 +73,529 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { var State = __webpack_require__(2), - Histery = __webpack_require__(4), - brow = __webpack_require__(5), - _ = __webpack_require__(3), - baseTitle = document.title, - stateFn = State.prototype.state; - - - function StateMan(options){ - - if(this instanceof StateMan === false){ return new StateMan(options)} - options = options || {}; - // if(options.history) this.history = options.history; - - this._states = {}; - this._stashCallback = []; - this.strict = options.strict; - this.current = this.active = this; - this.title = options.title; - this.on("end", function(){ - var cur = this.current,title; - while( cur ){ - title = cur.title; - if(title) break; - cur = cur.parent; + Histery = __webpack_require__(8), + brow = __webpack_require__(9), + _ = __webpack_require__(3), + baseTitle = document.title, + stateFn = State.prototype.state; + + function StateMan(options) + { + if (this instanceof StateMan === false) + { + return new StateMan(options) } - document.title = typeof title === "function"? cur.title(): String( title || baseTitle ) ; - }) - + options = options || {}; + // if(options.history) this.history = options.history; + + this._states = {}; + this._stashCallback = []; + this.strict = options.strict; + this.current = this.active = this; + this.title = options.title; + this.on("end", function () + { + var cur = this.current, title; + while (cur) + { + title = cur.title; + if (title) break; + cur = cur.parent; + } + document.title = typeof title === "function" ? cur.title() : String(title || baseTitle); + }) } - - _.extend( _.emitable( StateMan ), { + _.extend(_.emitable(StateMan), { // keep blank name: '', - state: function(stateName, config){ + state: function (stateName, config) + { - var active = this.active; - if(typeof stateName === "string" && active){ - stateName = stateName.replace("~", active.name) - if(active.parent) stateName = stateName.replace("^", active.parent.name || ""); - } - // ^ represent current.parent - // ~ represent current - // only - return stateFn.apply(this, arguments); + var active = this.active; + if (typeof stateName === "string" && active) + { + stateName = stateName.replace("~", active.name) + if (active.parent) stateName = stateName.replace("^", active.parent.name || ""); + } + // ^ represent current.parent + // ~ represent current + // only + return stateFn.apply(this, arguments); }, - start: function(options){ - - if( !this.history ) this.history = new Histery(options); - if( !this.history.isStart ){ - this.history.on("change", _.bind(this._afterPathChange, this)); - this.history.start(); - } - return this; + start: function (options) + { + + if (!this.history) this.history = new Histery(options); + if (!this.history.isStart) + { + this.history.on("change", _.bind(this._afterPathChange, this)); + this.history.start(); + } + return this; }, - stop: function(){ - this.history.stop(); + stop: function () + { + this.history.stop(); }, // @TODO direct go the point state - go: function(state, option, callback){ - option = option || {}; - if(typeof state === "string") state = this.state(state); + go: function (state, option, callback) + { + option = option || {}; + var statename; + if (typeof state === "string") + { + statename = state; + state = this.state(state); + } - if(!state) return; + if (!state) return this._notfound({state: statename}); - if(typeof option === "function"){ - callback = option; - option = {}; - } + if (typeof option === "function") + { + callback = option; + option = {}; + } - if(option.encode !== false){ - var url = state.encode(option.param) - option.path = url; - this.nav(url, {silent: true, replace: option.replace}); - } + if (option.encode !== false) + { + var url = state.encode(option.param) + option.path = url; + this.nav(url, {silent: true, replace: option.replace}); + } - this._go(state, option, callback); + this._go(state, option, callback); - return this; + return this; }, - nav: function(url, options, callback){ - if(typeof options === "function"){ - callback = options; - options = {}; - } - options = options || {}; + nav: function (url, options, callback) + { + if (typeof options === "function") + { + callback = options; + options = {}; + } + options = options || {}; - options.path = url; + options.path = url; - this.history.nav( url, _.extend({silent: true}, options)); - if(!options.silent) this._afterPathChange( _.cleanPath(url) , options , callback) + this.history.nav(url, _.extend({silent: true}, options)); + if (!options.silent) this._afterPathChange(_.cleanPath(url), options, callback) - return this; + return this; }, - decode: function(path){ - - var pathAndQuery = path.split("?"); - var query = this._findQuery(pathAndQuery[1]); - path = pathAndQuery[0]; - var state = this._findState(this, path); - if(state) _.extend(state.param, query); - return state; + decode: function (path) + { + + var pathAndQuery = path.split("?"); + var query = this._findQuery(pathAndQuery[1]); + path = pathAndQuery[0]; + var state = this._findState(this, path); + if (state) _.extend(state.param, query); + return state; }, - encode: function(stateName, param){ - var state = this.state(stateName); - return state? state.encode(param) : ''; + encode: function (stateName, param) + { + var state = this.state(stateName); + return state ? state.encode(param) : ''; }, // notify specify state // check the active statename whether to match the passed condition (stateName and param) - is: function(stateName, param, isStrict){ - if(!stateName) return false; - var stateName = (stateName.name || stateName); - var current = this.current, currentName = current.name; - var matchPath = isStrict? currentName === stateName : (currentName + ".").indexOf(stateName + ".")===0; - return matchPath && (!param || _.eql(param, this.param)); + is: function (stateName, param, isStrict) + { + if (!stateName) return false; + var stateName = (stateName.name || stateName); + var current = this.current, currentName = current.name; + var matchPath = isStrict ? currentName === stateName : (currentName + ".").indexOf(stateName + ".") === 0; + return matchPath && (!param || _.eql(param, this.param)); }, // after pathchange changed // @TODO: afterPathChange need based on decode - _afterPathChange: function(path, options ,callback){ + _afterPathChange: function (path, options, callback) + { - this.emit("history:change", path); + this.emit("history:change", path); - var found = this.decode(path); + var found = this.decode(path); - options = options || {}; + options = options || {}; - options.path = path; + options.path = path; - if(!found){ - // loc.nav("$default", {silent: true}) - return this._notfound(options); - } + if (!found) + { + // loc.nav("$default", {silent: true}) + return this._notfound(options); + } - options.param = found.param; + options.param = found.param; - this._go( found, options, callback ); + this._go(found, options, callback); }, - _notfound: function(options){ + _notfound: function (options) + { - // var $notfound = this.state("$notfound"); + // var $notfound = this.state("$notfound"); - // if( $notfound ) this._go($notfound, options); + // if( $notfound ) this._go($notfound, options); - return this.emit("notfound", options); + return this.emit("notfound", options); }, // goto the state with some option - _go: function(state, option, callback){ - - var over; - - // if(typeof state === "string") state = this.state(state); - - // if(!state) return _.log("destination is not defined") - - if(state.hasNext && this.strict) return this._notfound({name: state.name}); - - // not touch the end in previous transtion - - // if( this.pending ){ - // var pendingCurrent = this.pending.current; - // this.pending.stop(); - // _.log("naving to [" + pendingCurrent.name + "] will be stoped, trying to ["+state.name+"] now"); - // } - // if(this.active !== this.current){ - // // we need return - // _.log("naving to [" + this.current.name + "] will be stoped, trying to ["+state.name+"] now"); - // this.current = this.active; - // // back to before - // } - option.param = option.param || {}; - - var current = this.current, - baseState = this._findBase(current, state), - prepath = this.path, - self = this; + _go: function (state, option, callback) + { + + var over; + + // if(typeof state === "string") state = this.state(state); + + // if(!state) return _.log("destination is not defined") + + if (state.hasNext && this.strict) return this._notfound({name: state.name}); + + // not touch the end in previous transtion + + // if( this.pending ){ + // var pendingCurrent = this.pending.current; + // this.pending.stop(); + // _.log("naving to [" + pendingCurrent.name + "] will be stoped, trying to ["+state.name+"] now"); + // } + // if(this.active !== this.current){ + // // we need return + // _.log("naving to [" + this.current.name + "] will be stoped, trying to ["+state.name+"] now"); + // this.current = this.active; + // // back to before + // } + option.param = option.param || {}; + + var current = this.current, + baseState = this._findBase(current, state), + prepath = this.path, + self = this; + + + if (typeof callback === "function") this._stashCallback.push(callback); + // if we done the navigating when start + function done(success) + { + over = true; + if (success !== false) self.emit("end"); + self.pending = null; + self._popStash(option); + } + option.previous = current; + option.current = state; - if( typeof callback === "function" ) this._stashCallback.push(callback); - // if we done the navigating when start - function done(success){ - over = true; - if( success !== false ) self.emit("end"); - self.pending = null; - self._popStash(option); - } - - option.previous = current; - option.current = state; + if (current !== state) + { + option.stop = function () + { + done(false); + self.nav(prepath ? prepath : "/", {silent: true}); + } + self.emit("begin", option); - if(current !== state){ - option.stop = function(){ - done(false); - self.nav( prepath? prepath: "/", {silent:true}); } - self.emit("begin", option); + // if we stop it in 'begin' listener + if (over === true) return; - } - // if we stop it in 'begin' listener - if(over === true) return; + if (current !== state) + { + // option as transition object. - if(current !== state){ - // option as transition object. + option.phase = 'permission'; + this._walk(current, state, option, true, _.bind(function (notRejected) + { - option.phase = 'permission'; - this._walk(current, state, option, true , _.bind( function( notRejected ){ + if (notRejected === false) + { + // if reject in callForPermission, we will return to old + prepath && this.nav(prepath, {silent: true}) - if( notRejected===false ){ - // if reject in callForPermission, we will return to old - prepath && this.nav( prepath, {silent: true}) + done(false, 2) - done(false, 2) + return this.emit('abort', option); - return this.emit('abort', option); + } - } + // stop previous pending. + if (this.pending) this.pending.stop() + this.pending = option; + this.path = option.path; + this.current = option.current; + this.param = option.param; + this.previous = option.previous; + option.phase = 'navigation'; + this._walk(current, state, option, false, _.bind(function (notRejected) + { - // stop previous pending. - if(this.pending) this.pending.stop() - this.pending = option; - this.path = option.path; - this.current = option.current; - this.param = option.param; - this.previous = option.previous; - option.phase = 'navigation'; - this._walk(current, state, option, false, _.bind(function( notRejected ){ + if (notRejected === false) + { + this.current = this.active; + done(false) + return this.emit('abort', option); + } - if( notRejected === false ){ - this.current = this.active; - done(false) - return this.emit('abort', option); - } + this.active = option.current; - this.active = option.current; + option.phase = 'completion'; + return done() - option.phase = 'completion'; - return done() + }, this)) - }, this) ) + }, this)) - }, this) ) + } else + { + self._checkQueryAndParam(baseState, option); + this.pending = null; + done(); + } - }else{ - self._checkQueryAndParam(baseState, option); - this.pending = null; - done(); - } - }, - _popStash: function(option){ + _popStash: function (option) + { - var stash = this._stashCallback, len = stash.length; + var stash = this._stashCallback, len = stash.length; - this._stashCallback = []; + this._stashCallback = []; - if(!len) return; + if (!len) return; - for(var i = 0; i < len; i++){ - stash[i].call(this, option) - } + for (var i = 0; i < len; i++) + { + stash[i].call(this, option) + } }, // the transition logic Used in Both canLeave canEnter && leave enter LifeCycle - _walk: function(from, to, option, callForPermit , callback){ + _walk: function (from, to, option, callForPermit, callback) + { - // nothing -> app.state - var parent = this._findBase(from , to); + // nothing -> app.state + var parent = this._findBase(from, to); - option.basckward = true; - this._transit( from, parent, option, callForPermit , _.bind( function( notRejected ){ + option.basckward = true; + this._transit(from, parent, option, callForPermit, _.bind(function (notRejected) + { - if( notRejected === false ) return callback( notRejected ); + if (notRejected === false) return callback(notRejected); - // only actual transiton need update base state; - if( !callForPermit ) this._checkQueryAndParam(parent, option) + // only actual transiton need update base state; + if (!callForPermit) this._checkQueryAndParam(parent, option) - option.basckward = false; - this._transit( parent, to, option, callForPermit, callback) + option.basckward = false; + this._transit(parent, to, option, callForPermit, callback) - }, this) ) + }, this)) }, - _transit: function(from, to, option, callForPermit, callback){ - // touch the ending - if( from === to ) return callback(); + _transit: function (from, to, option, callForPermit, callback) + { + // touch the ending + if (from === to) return callback(); - var back = from.name.length > to.name.length; - var method = back? 'leave': 'enter'; - var applied; + var back = from.name.length > to.name.length; + var method = back ? 'leave' : 'enter'; + var applied; - // use canEnter to detect permission - if( callForPermit) method = 'can' + method.replace(/^\w/, function(a){ return a.toUpperCase() }); + // use canEnter to detect permission + if (callForPermit) method = 'can' + method.replace(/^\w/, function (a) + { + return a.toUpperCase() + }); - var loop = _.bind(function( notRejected ){ + var loop = _.bind(function (notRejected) + { - // stop transition or touch the end - if( applied === to || notRejected === false ) return callback(notRejected); + // stop transition or touch the end + if (applied === to || notRejected === false) return callback(notRejected); - if( !applied ) { + if (!applied) + { - applied = back? from : this._computeNext(from, to); + applied = back ? from : this._computeNext(from, to); - }else{ + } else + { - applied = this._computeNext(applied, to); - } + applied = this._computeNext(applied, to); + } - if( (back && applied === to) || !applied )return callback( notRejected ) + if ((back && applied === to) || !applied)return callback(notRejected) - this._moveOn( applied, method, option, loop ); + this._moveOn(applied, method, option, loop); - }, this); + }, this); - loop(); + loop(); }, - _moveOn: function( applied, method, option, callback){ + _moveOn: function (applied, method, option, callback) + { - var isDone = false; - var isPending = false; + var isDone = false; + var isPending = false; - option.async = function(){ + option.async = function () + { - isPending = true; + isPending = true; - return done; - } + return done; + } - function done( notRejected ){ - if( isDone ) return; - isPending = false; - isDone = true; - callback( notRejected ); - } + function done(notRejected) + { + if (isDone) return; + isPending = false; + isDone = true; + callback(notRejected); + } - - option.stop = function(){ - done( false ); - } + option.stop = function () + { + done(false); + } - this.active = applied; - var retValue = applied[method]? applied[method]( option ): true; + this.active = applied; + var retValue = applied[method] ? applied[method](option) : true; - if(method === 'enter') applied.visited = true; - // promise - // need breadk , if we call option.stop first; + if (method === 'enter') applied.visited = true; + // promise + // need breadk , if we call option.stop first; - if( _.isPromise(retValue) ){ + if (_.isPromise(retValue)) + { - return this._wrapPromise(retValue, done); + return this._wrapPromise(retValue, done); - } + } - // if haven't call option.async yet - if( !isPending ) done( retValue ) + // if haven't call option.async yet + if (!isPending) done(retValue) }, + _wrapPromise: function (promise, next) + { - _wrapPromise: function( promise, next ){ - - return promise.then( next, function(){next(false)}) ; + return promise.then(next, function () + { + next(false) + }); }, - _computeNext: function( from, to ){ + _computeNext: function (from, to) + { - var fname = from.name; - var tname = to.name; + var fname = from.name; + var tname = to.name; - var tsplit = tname.split('.') - var fsplit = fname.split('.') + var tsplit = tname.split('.') + var fsplit = fname.split('.') - var tlen = tsplit.length; - var flen = fsplit.length; + var tlen = tsplit.length; + var flen = fsplit.length; - if(fname === '') flen = 0; - if(tname === '') tlen = 0; + if (fname === '') flen = 0; + if (tname === '') tlen = 0; - if( flen < tlen ){ - fsplit[flen] = tsplit[flen]; - }else{ - fsplit.pop(); - } + if (flen < tlen) + { + fsplit[flen] = tsplit[flen]; + } else + { + fsplit.pop(); + } - return this.state(fsplit.join('.')) + return this.state(fsplit.join('.')) }, - _findQuery: function(querystr){ - - var queries = querystr && querystr.split("&"), query= {}; - if(queries){ - var len = queries.length; - var query = {}; - for(var i =0; i< len; i++){ - var tmp = queries[i].split("="); - query[tmp[0]] = tmp[1]; + _findQuery: function (querystr) + { + + var queries = querystr && querystr.split("&"), query = {}; + if (queries) + { + var len = queries.length; + var query = {}; + for (var i = 0; i < len; i++) + { + var tmp = queries[i].split("="); + query[tmp[0]] = tmp[1]; + } } - } - return query; + return query; }, - _findState: function(state, path){ - var states = state._states, found, param; - - // leaf-state has the high priority upon branch-state - if(state.hasNext){ - for(var i in states) if(states.hasOwnProperty(i)){ - found = this._findState( states[i], path ); - if( found ) return found; + _findState: function (state, path) + { + var states = state._states, found, param; + + // leaf-state has the high priority upon branch-state + if (state.hasNext) + { + for (var i in states) if (states.hasOwnProperty(i)) + { + found = this._findState(states[i], path); + if (found) return found; + } + } + // in strict mode only leaf can be touched + // if all children is don. will try it self + param = state.regexp && state.decode(path); + if (param) + { + state.param = param; + return state; + } else + { + return false; } - } - // in strict mode only leaf can be touched - // if all children is don. will try it self - param = state.regexp && state.decode(path); - if(param){ - state.param = param; - return state; - }else{ - return false; - } }, // find the same branch; - _findBase: function(now, before){ - - if(!now || !before || now == this || before == this) return this; - var np = now, bp = before, tmp; - while(np && bp){ - tmp = bp; - while(tmp){ - if(np === tmp) return tmp; - tmp = tmp.parent; + _findBase: function (now, before) + { + + if (!now || !before || now == this || before == this) return this; + var np = now, bp = before, tmp; + while (np && bp) + { + tmp = bp; + while (tmp) + { + if (np === tmp) return tmp; + tmp = tmp.parent; + } + np = np.parent; } - np = np.parent; - } }, // check the query and Param - _checkQueryAndParam: function(baseState, options){ - - var from = baseState; - while( from !== this ){ - from.update && from.update(options); - from = from.parent; - } - + _checkQueryAndParam: function (baseState, options) + { + var from = baseState; + while (from !== this) + { + from.update && from.update(options); + from = from.parent; + } } - - }, true) - - + }, true); module.exports = StateMan; @@ -544,157 +606,183 @@ return /******/ (function(modules) { // webpackBootstrap /***/ function(module, exports, __webpack_require__) { var _ = __webpack_require__(3); - - - - function State(option){ - this._states = {}; - this._pending = false; - this.visited = false; - if(option) this.config(option); + var Qs = __webpack_require__(4); + + function State(option) + { + this._states = {}; + this._pending = false; + this.visited = false; + if (option) this.config(option); } - //regexp cache State.rCache = {}; - _.extend( _.emitable( State ), { - - state: function(stateName, config){ - if(_.typeOf(stateName) === "object"){ - for(var i in stateName){ - this.state(i, stateName[i]) - } - return this; - } - var current, next, nextName, states = this._states, i=0; - - if( typeof stateName === "string" ) stateName = stateName.split("."); - - var slen = stateName.length, current = this; - var stack = []; - - - do{ - nextName = stateName[i]; - next = states[nextName]; - stack.push(nextName); - if(!next){ - if(!config) return; - next = states[nextName] = new State(); - _.extend(next, { - parent: current, - manager: current.manager || current, - name: stack.join("."), - currentName: nextName - }) - current.hasNext = true; - next.configUrl(); - } - current = next; - states = next._states; - }while((++i) < slen ) - - if(config){ - next.config(config); - return this; - } else { - return current; - } - }, + _.extend(_.emitable(State), { - config: function(configure){ - - configure = this._getConfig(configure); - - for(var i in configure){ - var prop = configure[i]; - switch(i){ - case "url": - if(typeof prop === "string"){ - this.url = prop; - this.configUrl(); - } - break; - case "events": - this.on(prop) - break; - default: - this[i] = prop; - } - } - }, - - // children override - _getConfig: function(configure){ - return typeof configure === "function"? {enter: configure} : configure; - }, - - //from url + state: function (stateName, config) + { + if (_.typeOf(stateName) === "object") + { + for (var i in stateName) + { + this.state(i, stateName[i]) + } + return this; + } + var current, next, nextName, states = this._states, i = 0; + + if (typeof stateName === "string") stateName = stateName.split("."); + + var slen = stateName.length, current = this; + var stack = []; + + + do { + nextName = stateName[i]; + next = states[nextName]; + stack.push(nextName); + if (!next) + { + if (!config) return; + next = states[nextName] = new State(); + _.extend(next, { + parent: current, + manager: current.manager || current, + name: stack.join("."), + currentName: nextName + }); + current.hasNext = true; + next.configUrl(); + } + current = next; + states = next._states; + } while ((++i) < slen); + + if (config) + { + next.config(config); + return this; + } else + { + return current; + } + }, - configUrl: function(){ - var url = "" , base = this, currentUrl; - var _watchedParam = []; + config: function (configure) + { + + configure = this._getConfig(configure); + + for (var i in configure) + { + var prop = configure[i]; + switch (i) + { + case "url": + if (typeof prop === "string") + { + this.url = prop; + this.configUrl(); + } + break; + case "events": + this.on(prop) + break; + default: + this[i] = prop; + } + } + }, - while( base ){ + // children override + _getConfig: function (configure) + { + return typeof configure === "function" ? {enter: configure} : configure; + }, - url = (typeof base.url === "string" ? base.url: (base.currentName || "")) + "/" + url; + //from url - // means absolute; - if(url.indexOf("^/") === 0) { - url = url.slice(1); - break; - } - base = base.parent; - } - this.pattern = _.cleanPath("/" + url); - var pathAndQuery = this.pattern.split("?"); - this.pattern = pathAndQuery[0]; - // some Query we need watched + configUrl: function () + { + var url = "", base = this, currentUrl; + var _watchedParam = []; - _.extend(this, _.normalize(this.pattern), true); - }, - encode: function(param){ - var state = this; - param = param || {}; - - var matched = "%"; + while (base) + { - var url = state.matches.replace(/\(([\w-]+)\)/g, function(all, capture){ - var sec = param[capture] || ""; - matched+= capture + "%"; - return sec; - }) + "?"; + url = (typeof base.url === "string" ? base.url : (base.currentName || "")) + "/" + url; - // remained is the query, we need concat them after url as query - for(var i in param) { - if( matched.indexOf("%"+i+"%") === -1) url += i + "=" + param[i] + "&"; - } - return _.cleanPath( url.replace(/(?:\?|&)$/,"") ) - }, - decode: function( path ){ - var matched = this.regexp.exec(path), - keys = this.keys; + // means absolute; + if (url.indexOf("^/") === 0) + { + url = url.slice(1); + break; + } + base = base.parent; + } + this.pattern = _.cleanPath("/" + url); + var pathAndQuery = this.pattern.split("?"); + this.pattern = pathAndQuery[0]; + // some Query we need watched - if(matched){ + _.extend(this, _.normalize(this.pattern), true); + }, + encode: function (param) + { + var state = this; + var result = Qs.stringify(param); + return result; + + + // param = param || {}; + // + // var matched = "%"; + // + // var url = state.matches.replace(/\(([\w-]+)\)/g, function (all, capture) + // { + // var sec = param[capture] || ""; + // matched += capture + "%"; + // return sec; + // }) + "?"; + // + // // remained is the query, we need concat them after url as query + // for (var i in param) + // { + // if (matched.indexOf("%" + i + "%") === -1) url += i + "=" + param[i] + "&"; + // } + // return _.cleanPath(url.replace(/(?:\?|&)$/, "")) + }, + decode: function (path) + { + var result = Qs.parse(path); + return result; + + // var matched = this.regexp.exec(path), + // keys = this.keys; + // + // if (matched) + // { + // var param = {}; + // for (var i = 0, len = keys.length; i < len; i++) + // { + // param[keys[i]] = matched[i + 1] + // } + // return param; + // } else + // { + // return false; + // } + }, + // by default, all lifecycle is permitted - var param = {}; - for(var i =0,len=keys.length;i= 0x30 && c <= 0x39) || // 0-9 + (c >= 0x41 && c <= 0x5A) || // a-z + (c >= 0x61 && c <= 0x7A) // A-Z + ) { + out += string.charAt(i); + continue; + } + + if (c < 0x80) { + out = out + hexTable[c]; + continue; + } + + if (c < 0x800) { + out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + if (c < 0xD800 || c >= 0xE000) { + out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + i += 1; + c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); + out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]; + } + + return out; + }; + + exports.compact = function (obj, references) { + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + var refs = references || []; + var lookup = refs.indexOf(obj); + if (lookup !== -1) { + return refs[lookup]; + } + + refs.push(obj); + + if (Array.isArray(obj)) { + var compacted = []; + + for (var i = 0; i < obj.length; ++i) { + if (obj[i] && typeof obj[i] === 'object') { + compacted.push(exports.compact(obj[i], refs)); + } else if (typeof obj[i] !== 'undefined') { + compacted.push(obj[i]); + } + } + + return compacted; + } + + var keys = Object.keys(obj); + for (var j = 0; j < keys.length; ++j) { + var key = keys[j]; + obj[key] = exports.compact(obj[key], refs); + } + + return obj; + }; + + exports.isRegExp = function (obj) { + return Object.prototype.toString.call(obj) === '[object RegExp]'; + }; + + exports.isBuffer = function (obj) { + if (obj === null || typeof obj === 'undefined') { + return false; + } + + return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); + }; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Utils = __webpack_require__(6); + + var has = Object.prototype.hasOwnProperty; + + var defaults = { + delimiter: '&', + depth: 5, + arrayLimit: 20, + parameterLimit: 1000, + strictNullHandling: false, + plainObjects: false, + allowPrototypes: false, + allowDots: false, + decoder: Utils.decode + }; + + var parseValues = function parseValues(str, options) { + var obj = {}; + var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit); + + for (var i = 0; i < parts.length; ++i) { + var part = parts[i]; + var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1; + + var key, val; + if (pos === -1) { + key = options.decoder(part); + val = options.strictNullHandling ? null : ''; + } else { + key = options.decoder(part.slice(0, pos)); + val = options.decoder(part.slice(pos + 1)); + } + if (has.call(obj, key)) { + obj[key] = [].concat(obj[key]).concat(val); + } else { + obj[key] = val; + } + } + + return obj; + }; + + var parseObject = function parseObject(chain, val, options) { + if (!chain.length) { + return val; + } + + var root = chain.shift(); + + var obj; + if (root === '[]') { + obj = []; + obj = obj.concat(parseObject(chain, val, options)); + } else { + obj = options.plainObjects ? Object.create(null) : {}; + var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root; + var index = parseInt(cleanRoot, 10); + if ( + !isNaN(index) && + root !== cleanRoot && + String(index) === cleanRoot && + index >= 0 && + (options.parseArrays && index <= options.arrayLimit) + ) { + obj = []; + obj[index] = parseObject(chain, val, options); + } else { + obj[cleanRoot] = parseObject(chain, val, options); + } + } + + return obj; + }; + + var parseKeys = function parseKeys(givenKey, val, options) { + if (!givenKey) { + return; + } + + // Transform dot notation to bracket notation + var key = options.allowDots ? givenKey.replace(/\.([^\.\[]+)/g, '[$1]') : givenKey; + + // The regex chunks + + var parent = /^([^\[\]]*)/; + var child = /(\[[^\[\]]*\])/g; + + // Get the parent + + var segment = parent.exec(key); + + // Stash the parent if it exists + + var keys = []; + if (segment[1]) { + // If we aren't using plain objects, optionally prefix keys + // that would overwrite object prototype properties + if (!options.plainObjects && has.call(Object.prototype, segment[1])) { + if (!options.allowPrototypes) { + return; + } + } + + keys.push(segment[1]); + } + + // Loop through children appending to the array until we hit depth + + var i = 0; + while ((segment = child.exec(key)) !== null && i < options.depth) { + i += 1; + if (!options.plainObjects && has.call(Object.prototype, segment[1].replace(/\[|\]/g, ''))) { + if (!options.allowPrototypes) { + continue; + } + } + keys.push(segment[1]); + } + + // If there's a remainder, just add whatever is left + + if (segment) { + keys.push('[' + key.slice(segment.index) + ']'); + } + + return parseObject(keys, val, options); + }; + + module.exports = function (str, opts) { + var options = opts || {}; + + if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') { + throw new TypeError('Decoder has to be a function.'); + } + + options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter; + options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth; + options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit; + options.parseArrays = options.parseArrays !== false; + options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder; + options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots; + options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects; + options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes; + options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit; + options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; + + if (str === '' || str === null || typeof str === 'undefined') { + return options.plainObjects ? Object.create(null) : {}; + } + + var tempObj = typeof str === 'string' ? parseValues(str, options) : str; + var obj = options.plainObjects ? Object.create(null) : {}; + + // Iterate over the keys and setup the new object + + var keys = Object.keys(tempObj); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var newObj = parseKeys(key, tempObj[key], options); + obj = Utils.merge(obj, newObj, options); + } + + return Utils.compact(obj); + }; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + // MIT // Thx Backbone.js 1.1.2 and https://github.com/cowboy/jquery-hashchange/blob/master/jquery.ba-hashchange.js // for iframe patches in old ie. - var browser = __webpack_require__(5); + var browser = __webpack_require__(9); var _ = __webpack_require__(3); @@ -1040,7 +1628,8 @@ return /******/ (function(modules) { // webpackBootstrap var target = ev.target || ev.srcElement; if( target.tagName.toLowerCase() !== "a" ) return; - var tmp = (browser.getHref(target)||"").match(self.rPrefix); + var tmp = browser.isSameDomain(target.href)&&(browser.getHref(target)||"").match(self.rPrefix); + var hash = tmp && tmp[1]? tmp[1]: ""; if(!hash) return; @@ -1105,8 +1694,9 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = Histery; + /***/ }, -/* 5 */ +/* 9 */ /***/ function(module, exports) { @@ -1117,6 +1707,13 @@ return /******/ (function(modules) { // webpackBootstrap hash: "onhashchange" in win && (!doc.documentMode || doc.documentMode > 7), history: win.history && "onpopstate" in win, location: win.location, + isSameDomain: function(url){ + var matched = url.match(/^.*?:\/\/([^/]*)/); + if(matched){ + return matched[0] == this.location.origin; + } + return true; + }, getHref: function(node){ return "href" in node ? node.getAttribute("href", 2) : node.getAttribute("href"); }, diff --git a/stateman.min.js b/stateman.min.js index 7e4d3c2..9490071 100644 --- a/stateman.min.js +++ b/stateman.min.js @@ -1 +1 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.StateMan=e():t.StateMan=e()}(this,function(){return 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){var i=n(1);i.Histery=n(4),i.util=n(3),i.State=n(2),t.exports=i},function(t,e,n){function i(t){return this instanceof i==!1?new i(t):(t=t||{},this._states={},this._stashCallback=[],this.strict=t.strict,this.current=this.active=this,this.title=t.title,void this.on("end",function(){for(var t,e=this.current;e&&!(t=e.title);)e=e.parent;document.title="function"==typeof t?e.title():String(t||o)}))}var r=n(2),a=n(4),s=(n(5),n(3)),o=document.title,h=r.prototype.state;s.extend(s.emitable(i),{name:"",state:function(t,e){var n=this.active;return"string"==typeof t&&n&&(t=t.replace("~",n.name),n.parent&&(t=t.replace("^",n.parent.name||""))),h.apply(this,arguments)},start:function(t){return this.history||(this.history=new a(t)),this.history.isStart||(this.history.on("change",s.bind(this._afterPathChange,this)),this.history.start()),this},stop:function(){this.history.stop()},go:function(t,e,n){if(e=e||{},"string"==typeof t&&(t=this.state(t)),t){if("function"==typeof e&&(n=e,e={}),e.encode!==!1){var i=t.encode(e.param);e.path=i,this.nav(i,{silent:!0,replace:e.replace})}return this._go(t,e,n),this}},nav:function(t,e,n){return"function"==typeof e&&(n=e,e={}),e=e||{},e.path=t,this.history.nav(t,s.extend({silent:!0},e)),e.silent||this._afterPathChange(s.cleanPath(t),e,n),this},decode:function(t){var e=t.split("?"),n=this._findQuery(e[1]);t=e[0];var i=this._findState(this,t);return i&&s.extend(i.param,n),i},encode:function(t,e){var n=this.state(t);return n?n.encode(e):""},is:function(t,e,n){if(!t)return!1;var t=t.name||t,i=this.current,r=i.name,a=n?r===t:0===(r+".").indexOf(t+".");return a&&(!e||s.eql(e,this.param))},_afterPathChange:function(t,e,n){this.emit("history:change",t);var i=this.decode(t);return e=e||{},e.path=t,i?(e.param=i.param,void this._go(i,e,n)):this._notfound(e)},_notfound:function(t){return this.emit("notfound",t)},_go:function(t,e,n){function i(t){r=!0,t!==!1&&c.emit("end"),c.pending=null,c._popStash(e)}var r;if(t.hasNext&&this.strict)return this._notfound({name:t.name});e.param=e.param||{};var a=this.current,o=this._findBase(a,t),h=this.path,c=this;"function"==typeof n&&this._stashCallback.push(n),e.previous=a,e.current=t,a!==t&&(e.stop=function(){i(!1),c.nav(h?h:"/",{silent:!0})},c.emit("begin",e)),r!==!0&&(a!==t?(e.phase="permission",this._walk(a,t,e,!0,s.bind(function(n){return n===!1?(h&&this.nav(h,{silent:!0}),i(!1,2),this.emit("abort",e)):(this.pending&&this.pending.stop(),this.pending=e,this.path=e.path,this.current=e.current,this.param=e.param,this.previous=e.previous,e.phase="navigation",void this._walk(a,t,e,!1,s.bind(function(t){return t===!1?(this.current=this.active,i(!1),this.emit("abort",e)):(this.active=e.current,e.phase="completion",i())},this)))},this))):(c._checkQueryAndParam(o,e),this.pending=null,i()))},_popStash:function(t){var e=this._stashCallback,n=e.length;if(this._stashCallback=[],n)for(var i=0;n>i;i++)e[i].call(this,t)},_walk:function(t,e,n,i,r){var a=this._findBase(t,e);n.basckward=!0,this._transit(t,a,n,i,s.bind(function(t){return t===!1?r(t):(i||this._checkQueryAndParam(a,n),n.basckward=!1,void this._transit(a,e,n,i,r))},this))},_transit:function(t,e,n,i,r){if(t===e)return r();var a,o=t.name.length>e.name.length,h=o?"leave":"enter";i&&(h="can"+h.replace(/^\w/,function(t){return t.toUpperCase()}));var c=s.bind(function(i){return a===e||i===!1?r(i):(a=a?this._computeNext(a,e):o?t:this._computeNext(t,e),o&&a===e||!a?r(i):void this._moveOn(a,h,n,c))},this);c()},_moveOn:function(t,e,n,i){function r(t){a||(o=!1,a=!0,i(t))}var a=!1,o=!1;n.async=function(){return o=!0,r},n.stop=function(){r(!1)},this.active=t;var h=t[e]?t[e](n):!0;return"enter"===e&&(t.visited=!0),s.isPromise(h)?this._wrapPromise(h,r):void(o||r(h))},_wrapPromise:function(t,e){return t.then(e,function(){e(!1)})},_computeNext:function(t,e){var n=t.name,i=e.name,r=i.split("."),a=n.split("."),s=r.length,o=a.length;return""===n&&(o=0),""===i&&(s=0),s>o?a[o]=r[o]:a.pop(),this.state(a.join("."))},_findQuery:function(t){var e=t&&t.split("&"),n={};if(e)for(var i=e.length,n={},r=0;i>r;r++){var a=e[r].split("=");n[a[0]]=a[1]}return n},_findState:function(t,e){var n,i,r=t._states;if(t.hasNext)for(var a in r)if(r.hasOwnProperty(a)&&(n=this._findState(r[a],e)))return n;return i=t.regexp&&t.decode(e),i?(t.param=i,t):!1},_findBase:function(t,e){if(!t||!e||t==this||e==this)return this;for(var n,i=t,r=e;i&&r;){for(n=r;n;){if(i===n)return n;n=n.parent}i=i.parent}},_checkQueryAndParam:function(t,e){for(var n=t;n!==this;)n.update&&n.update(e),n=n.parent}},!0),t.exports=i},function(t,e,n){function i(t){this._states={},this._pending=!1,this.visited=!1,t&&this.config(t)}var r=n(3);i.rCache={},r.extend(r.emitable(i),{state:function(t,e){if("object"===r.typeOf(t)){for(var n in t)this.state(n,t[n]);return this}var a,s,o,h=this._states,n=0;"string"==typeof t&&(t=t.split("."));var c=t.length,a=this,u=[];do{if(o=t[n],s=h[o],u.push(o),!s){if(!e)return;s=h[o]=new i,r.extend(s,{parent:a,manager:a.manager||a,name:u.join("."),currentName:o}),a.hasNext=!0,s.configUrl()}a=s,h=s._states}while(++nr;r++)i[n[r]]=e[r+1];return i}return!1},async:function(){throw new Error("please use option.async instead")}}),t.exports=i},function(t,e){function n(t){var e=0,n=[],r=0,a="";t=i.cleanPath(t);var s=t.replace(/\:([\w-]+)(?:\(([^\/]+?)\))?|(?:\(([^\/]+)\))|(\*{2,})|(\*(?!\*))/g,function(i,s,o,h,c,u,f){return f>e&&(a+=t.slice(e,f)),e=f+i.length,s?(a+="("+s+")",n.push(s),"("+(o||"[\\w-]+")+")"):(a+="("+r+")",n.push(r++),h?"("+h+")":c?"(.*)":u?"([^\\/]*)":void 0)});return e!==t.length&&(a+=t.slice(e)),{regexp:new RegExp("^"+s+"/?$"),keys:n,matches:a||t}}var i=t.exports={},r=[].slice,a={}.toString;i.extend=function(t,e,n){for(var i in e)(n||void 0===t[i])&&(t[i]=e[i]);return t},i.slice=function(t,e){return r.call(t,e)},i.typeOf=function(t){return null==t?String(t):a.call(t).slice(8,-1).toLowerCase()},i.eql=function(t,e){var n=i.typeOf(t),r=i.typeOf(e);if(n!==r)return!1;if("object"===n){var a=!0;for(var s in t)t[s]!==e[s]&&(a=!1);return a}return t===e},i.emitable=function(){function t(t){var e=(t||"").split(":");return{event:e[0],namespace:e[1]}}var e={once:function(t,e){var n=function(){e.apply(this,arguments),this.off(t,n)};return this.on(t,n)},on:function(e,n){if("object"==typeof e){for(var i in e)this.on(i,e[i]);return this}var r=t(e);if(e=r.event,e&&"function"==typeof n){var a=this._handles||(this._handles={}),s=a[e]||(a[e]=[]);n._ns=r.namespace,s.push(n)}return this},off:function(e,n){var i=t(e);e=i.event,e&&this._handles||(this._handles={});var r,a=this._handles;if(r=a[e])if(n||i.namespace){for(var s=0,o=r.length;o>s;s++)if(!(n&&n!==r[s]||i.namespace&&r[s]._ns!==i.namespace))return r.splice(s,1),this}else a[e]=[];return this},emit:function(e){var n=t(e);e=n.event;var r,a=i.slice(arguments,1),s=this._handles;if(!s||!(r=s[e]))return this;for(var o=0,h=r.length;h>o;o++){var c=r[o];n.namespace&&c._ns!==n.namespace||c.apply(this,a)}return this}};return function(t){return t="function"==typeof t?t.prototype:t,i.extend(t,e)}}(),i.bind=function(t,e){return function(){return t.apply(e,arguments)}};var s=/\/+/g,o=/\/$/;i.cleanPath=function(t){return("/"+t).replace(s,"/").replace(o,"")||"/"},i.log=function(t,e){"undefined"!=typeof console&&console[e||"log"](t)},i.isPromise=function(t){return!!t&&("object"==typeof t||"function"==typeof t)&&"function"==typeof t.then},i.normalize=n},function(t,e,n){function i(t){t=t||{},this.location=t.location||r.location,this.html5=t.html5,this.mode=t.html5&&r.history?h:o,r.hash||(this.mode=s),t.mode&&(this.mode=t.mode),this.prefix="#"+(t.prefix||""),this.rPrefix=new RegExp(this.prefix+"(.*)$"),this.interval=t.interval||66,this.root=t.root||"/",this.rRoot=new RegExp("^"+this.root),this._fixInitState(),this.autolink=t.autolink!==!1,this.curPath=void 0}var r=n(5),a=n(3),s=3,o=1,h=2;a.extend(a.emitable(i),{start:function(){var t=this.getPath();if(this._checkPath=a.bind(this.checkPath,this),!this.isStart){switch(this.isStart=!0,this.mode===s&&this._fixHashProbelm(t),this.mode){case o:r.on(window,"hashchange",this._checkPath);break;case h:r.on(window,"popstate",this._checkPath);break;case s:this._checkLoop()}this.autolink&&this._autolink(),this.curPath=t,this.emit("change",t)}},stop:function(){r.off(window,"hashchange",this._checkPath),r.off(window,"popstate",this._checkPath),clearTimeout(this.tid),this.isStart=!1,this._checkPath=null},checkPath:function(t){var e=this.getPath(),n=this.curPath;e===n&&this.iframe&&(e=this.getPath(this.iframe.location)),e!==n&&(this.iframe&&this.nav(e,{silent:!0}),this.curPath=e,this.emit("change",e))},getPath:function(t){var e,t=t||this.location;return this.mode!==h?(e=t.href.match(this.rPrefix),e&&e[1]?e[1]:""):a.cleanPath((t.pathname+t.search||"").replace(this.rRoot,"/"))},nav:function(t,e){var n=this.iframe;e=e||{},t=a.cleanPath(t),this.curPath!=t&&(this.curPath=t,this.mode!==h?(this._setHash(this.location,t,e.replace),n&&this.getPath(n.location)!==t&&(e.replace||n.document.open().close(),this._setHash(this.iframe.location,t,e.replace))):history[e.replace?"replaceState":"pushState"]({},e.title||"",a.cleanPath(this.root+t)),e.silent||this.emit("change",t))},_autolink:function(){if(this.mode===h){var t=(this.prefix,this);r.on(document.body,"click",function(e){var n=e.target||e.srcElement;if("a"===n.tagName.toLowerCase()){var i=(r.getHref(n)||"").match(t.rPrefix),a=i&&i[1]?i[1]:"";if(a)return e.preventDefault&&e.preventDefault(),t.nav(a),e.returnValue=!1}})}},_setHash:function(t,e,n){var i=t.href.replace(/(javascript:|#).*$/,"");n?t.replace(i+this.prefix+e):t.hash=this.prefix+e},_checkLoop:function(){var t=this;this.tid=setTimeout(function(){t._checkPath(),t._checkLoop()},this.interval)},_fixInitState:function(){var t,e,n=a.cleanPath(this.location.pathname);this.mode!==h&&this.html5?(e=n.replace(this.rRoot,""),e&&this.location.replace(this.root+this.prefix+e)):this.mode===h&&(t=this.location.hash.replace(this.prefix,""),t&&history.replaceState({},document.title,a.cleanPath(this.root+t)))},_fixHashProbelm:function(t){var e=document.createElement("iframe"),n=document.body;e.src="javascript:;",e.style.display="none",e.tabIndex=-1,e.title="",this.iframe=n.insertBefore(e,n.firstChild).contentWindow,this.iframe.document.open().close(),this.iframe.location.hash="#"+t}}),t.exports=i},function(t,e){var n=window,i=document;t.exports={hash:"onhashchange"in n&&(!i.documentMode||i.documentMode>7),history:n.history&&"onpopstate"in n,location:n.location,getHref:function(t){return"href"in t?t.getAttribute("href",2):t.getAttribute("href")},on:"addEventListener"in n?function(t,e,n){return t.addEventListener(e,n)}:function(t,e,n){return t.attachEvent("on"+e,n)},off:"removeEventListener"in n?function(t,e,n){return t.removeEventListener(e,n)}:function(t,e,n){return t.detachEvent("on"+e,n)}}}])}); \ No newline at end of file +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.StateMan=e():t.StateMan=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var i=n[r]={exports:{},id:r,loaded:!1};return t[r].call(i.exports,i,i.exports,e),i.loaded=!0,i.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){var r=n(1);r.Histery=n(8),r.util=n(3),r.State=n(2),t.exports=r},function(t,e,n){function r(t){return this instanceof r==!1?new r(t):(t=t||{},this._states={},this._stashCallback=[],this.strict=t.strict,this.current=this.active=this,this.title=t.title,void this.on("end",function(){for(var t,e=this.current;e&&!(t=e.title);)e=e.parent;document.title="function"==typeof t?e.title():String(t||s)}))}var i=n(2),o=n(8),a=(n(9),n(3)),s=document.title,c=i.prototype.state;a.extend(a.emitable(r),{name:"",state:function(t,e){var n=this.active;return"string"==typeof t&&n&&(t=t.replace("~",n.name),n.parent&&(t=t.replace("^",n.parent.name||""))),c.apply(this,arguments)},start:function(t){return this.history||(this.history=new o(t)),this.history.isStart||(this.history.on("change",a.bind(this._afterPathChange,this)),this.history.start()),this},stop:function(){this.history.stop()},go:function(t,e,n){e=e||{};var r;if("string"==typeof t&&(r=t,t=this.state(t)),!t)return this._notfound({state:r});if("function"==typeof e&&(n=e,e={}),e.encode!==!1){var i=t.encode(e.param);e.path=i,this.nav(i,{silent:!0,replace:e.replace})}return this._go(t,e,n),this},nav:function(t,e,n){return"function"==typeof e&&(n=e,e={}),e=e||{},e.path=t,this.history.nav(t,a.extend({silent:!0},e)),e.silent||this._afterPathChange(a.cleanPath(t),e,n),this},decode:function(t){var e=t.split("?"),n=this._findQuery(e[1]);t=e[0];var r=this._findState(this,t);return r&&a.extend(r.param,n),r},encode:function(t,e){var n=this.state(t);return n?n.encode(e):""},is:function(t,e,n){if(!t)return!1;var t=t.name||t,r=this.current,i=r.name,o=n?i===t:0===(i+".").indexOf(t+".");return o&&(!e||a.eql(e,this.param))},_afterPathChange:function(t,e,n){this.emit("history:change",t);var r=this.decode(t);return e=e||{},e.path=t,r?(e.param=r.param,void this._go(r,e,n)):this._notfound(e)},_notfound:function(t){return this.emit("notfound",t)},_go:function(t,e,n){function r(t){i=!0,t!==!1&&h.emit("end"),h.pending=null,h._popStash(e)}var i;if(t.hasNext&&this.strict)return this._notfound({name:t.name});e.param=e.param||{};var o=this.current,s=this._findBase(o,t),c=this.path,h=this;"function"==typeof n&&this._stashCallback.push(n),e.previous=o,e.current=t,o!==t&&(e.stop=function(){r(!1),h.nav(c?c:"/",{silent:!0})},h.emit("begin",e)),i!==!0&&(o!==t?(e.phase="permission",this._walk(o,t,e,!0,a.bind(function(n){return n===!1?(c&&this.nav(c,{silent:!0}),r(!1,2),this.emit("abort",e)):(this.pending&&this.pending.stop(),this.pending=e,this.path=e.path,this.current=e.current,this.param=e.param,this.previous=e.previous,e.phase="navigation",void this._walk(o,t,e,!1,a.bind(function(t){return t===!1?(this.current=this.active,r(!1),this.emit("abort",e)):(this.active=e.current,e.phase="completion",r())},this)))},this))):(h._checkQueryAndParam(s,e),this.pending=null,r()))},_popStash:function(t){var e=this._stashCallback,n=e.length;if(this._stashCallback=[],n)for(var r=0;n>r;r++)e[r].call(this,t)},_walk:function(t,e,n,r,i){var o=this._findBase(t,e);n.basckward=!0,this._transit(t,o,n,r,a.bind(function(t){return t===!1?i(t):(r||this._checkQueryAndParam(o,n),n.basckward=!1,void this._transit(o,e,n,r,i))},this))},_transit:function(t,e,n,r,i){if(t===e)return i();var o,s=t.name.length>e.name.length,c=s?"leave":"enter";r&&(c="can"+c.replace(/^\w/,function(t){return t.toUpperCase()}));var h=a.bind(function(r){return o===e||r===!1?i(r):(o=o?this._computeNext(o,e):s?t:this._computeNext(t,e),s&&o===e||!o?i(r):void this._moveOn(o,c,n,h))},this);h()},_moveOn:function(t,e,n,r){function i(t){o||(s=!1,o=!0,r(t))}var o=!1,s=!1;n.async=function(){return s=!0,i},n.stop=function(){i(!1)},this.active=t;var c=t[e]?t[e](n):!0;return"enter"===e&&(t.visited=!0),a.isPromise(c)?this._wrapPromise(c,i):void(s||i(c))},_wrapPromise:function(t,e){return t.then(e,function(){e(!1)})},_computeNext:function(t,e){var n=t.name,r=e.name,i=r.split("."),o=n.split("."),a=i.length,s=o.length;return""===n&&(s=0),""===r&&(a=0),a>s?o[s]=i[s]:o.pop(),this.state(o.join("."))},_findQuery:function(t){var e=t&&t.split("&"),n={};if(e)for(var r=e.length,n={},i=0;r>i;i++){var o=e[i].split("=");n[o[0]]=o[1]}return n},_findState:function(t,e){var n,r,i=t._states;if(t.hasNext)for(var o in i)if(i.hasOwnProperty(o)&&(n=this._findState(i[o],e)))return n;return r=t.regexp&&t.decode(e),r?(t.param=r,t):!1},_findBase:function(t,e){if(!t||!e||t==this||e==this)return this;for(var n,r=t,i=e;r&&i;){for(n=i;n;){if(r===n)return n;n=n.parent}r=r.parent}},_checkQueryAndParam:function(t,e){for(var n=t;n!==this;)n.update&&n.update(e),n=n.parent}},!0),t.exports=r},function(t,e,n){function r(t){this._states={},this._pending=!1,this.visited=!1,t&&this.config(t)}var i=n(3),o=n(4);r.rCache={},i.extend(i.emitable(r),{state:function(t,e){if("object"===i.typeOf(t)){for(var n in t)this.state(n,t[n]);return this}var o,a,s,c=this._states,n=0;"string"==typeof t&&(t=t.split("."));var h=t.length,o=this,u=[];do{if(s=t[n],a=c[s],u.push(s),!a){if(!e)return;a=c[s]=new r,i.extend(a,{parent:o,manager:o.manager||o,name:u.join("."),currentName:s}),o.hasNext=!0,a.configUrl()}o=a,c=a._states}while(++ne&&(o+=t.slice(e,f)),e=f+r.length,a?(o+="("+a+")",n.push(a),"("+(s||"[\\w-]+")+")"):(o+="("+i+")",n.push(i++),c?"("+c+")":h?"(.*)":u?"([^\\/]*)":void 0)});return e!==t.length&&(o+=t.slice(e)),{regexp:new RegExp("^"+a+"/?$"),keys:n,matches:o||t}}var r=t.exports={},i=[].slice,o={}.toString;r.extend=function(t,e,n){for(var r in e)(n||void 0===t[r])&&(t[r]=e[r]);return t},r.slice=function(t,e){return i.call(t,e)},r.typeOf=function(t){return null==t?String(t):o.call(t).slice(8,-1).toLowerCase()},r.eql=function(t,e){var n=r.typeOf(t),i=r.typeOf(e);if(n!==i)return!1;if("object"===n){for(var o in t)if(t[o]!==e[o])return!1;return!0}return t===e},r.emitable=function(){function t(t){var e=(t||"").split(":");return{event:e[0],namespace:e[1]}}var e={once:function(t,e){var n=function(){e.apply(this,arguments),this.off(t,n)};return this.on(t,n)},on:function(e,n){if("object"==typeof e){for(var r in e)this.on(r,e[r]);return this}var i=t(e);if(e=i.event,e&&"function"==typeof n){var o=this._handles||(this._handles={}),a=o[e]||(o[e]=[]);n._ns=i.namespace,a.push(n)}return this},off:function(e,n){var r=t(e);e=r.event,e&&this._handles||(this._handles={});var i,o=this._handles;if(i=o[e])if(n||r.namespace){for(var a=0,s=i.length;s>a;a++)if(!(n&&n!==i[a]||r.namespace&&i[a]._ns!==r.namespace))return i.splice(a,1),this}else o[e]=[];return this},emit:function(e){var n=t(e);e=n.event;var i,o=r.slice(arguments,1),a=this._handles;if(!a||!(i=a[e]))return this;for(var s=0,c=i.length;c>s;s++){var h=i[s];n.namespace&&h._ns!==n.namespace||h.apply(this,o)}return this}};return function(t){return t="function"==typeof t?t.prototype:t,r.extend(t,e)}}(),r.bind=function(t,e){return function(){return t.apply(e,arguments)}};var a=/\/+/g,s=/\/$/;r.cleanPath=function(t){return("/"+t).replace(a,"/").replace(s,"")||"/"},r.log=function(t,e){"undefined"!=typeof console&&console[e||"log"](t)},r.isPromise=function(t){return!!t&&("object"==typeof t||"function"==typeof t)&&"function"==typeof t.then},r.normalize=n},function(t,e,n){"use strict";var r=n(5),i=n(7);t.exports={stringify:r,parse:i}},function(t,e,n){"use strict";var r=n(6),i={brackets:function(t){return t+"[]"},indices:function(t,e){return t+"["+e+"]"},repeat:function(t){return t}},o={delimiter:"&",strictNullHandling:!1,skipNulls:!1,encode:!0,encoder:r.encode},a=function s(t,e,n,i,o,a,c,h,u){var f=t;if("function"==typeof c)f=c(e,f);else if(f instanceof Date)f=f.toISOString();else if(null===f){if(i)return a?a(e):e;f=""}if("string"==typeof f||"number"==typeof f||"boolean"==typeof f||r.isBuffer(f))return a?[a(e)+"="+a(f)]:[e+"="+String(f)];var l=[];if("undefined"==typeof f)return l;var p;if(Array.isArray(c))p=c;else{var d=Object.keys(f);p=h?d.sort(h):d}for(var m=0;me;++e)t[e]="%"+((16>e?"0":"")+e.toString(16)).toUpperCase();return t}();e.arrayToObject=function(t,e){for(var n=e.plainObjects?Object.create(null):{},r=0;r=48&&57>=o||o>=65&&90>=o||o>=97&&122>=o?r+=e.charAt(i):128>o?r+=n[o]:2048>o?r+=n[192|o>>6]+n[128|63&o]:55296>o||o>=57344?r+=n[224|o>>12]+n[128|o>>6&63]+n[128|63&o]:(i+=1,o=65536+((1023&o)<<10|1023&e.charCodeAt(i)),r+=n[240|o>>18]+n[128|o>>12&63]+n[128|o>>6&63]+n[128|63&o])}return r},e.compact=function(t,n){if("object"!=typeof t||null===t)return t;var r=n||[],i=r.indexOf(t);if(-1!==i)return r[i];if(r.push(t),Array.isArray(t)){for(var o=[],a=0;a=0&&n.parseArrays&&a<=n.arrayLimit?(r=[],r[a]=h(t,e,n)):r[o]=h(t,e,n)}return r},c=function(t,e,n){if(t){var r=n.allowDots?t.replace(/\.([^\.\[]+)/g,"[$1]"):t,o=/^([^\[\]]*)/,a=/(\[[^\[\]]*\])/g,c=o.exec(r),h=[];if(c[1]){if(!n.plainObjects&&i.call(Object.prototype,c[1])&&!n.allowPrototypes)return;h.push(c[1])}for(var u=0;null!==(c=a.exec(r))&&u7),history:n.history&&"onpopstate"in n,location:n.location,isSameDomain:function(t){var e=t.match(/^.*?:\/\/([^\/]*)/);return e?e[0]==this.location.origin:!0},getHref:function(t){return"href"in t?t.getAttribute("href",2):t.getAttribute("href")},on:"addEventListener"in n?function(t,e,n){return t.addEventListener(e,n)}:function(t,e,n){return t.attachEvent("on"+e,n)},off:"removeEventListener"in n?function(t,e,n){return t.removeEventListener(e,n)}:function(t,e,n){return t.detachEvent("on"+e,n)}}}])}); \ No newline at end of file diff --git a/test/runner/dom.bundle.js b/test/runner/dom.bundle.js index af0cb66..5855360 100644 --- a/test/runner/dom.bundle.js +++ b/test/runner/dom.bundle.js @@ -46,8 +46,8 @@ __webpack_require__(1); - __webpack_require__(10); - __webpack_require__(13); + __webpack_require__(14); + __webpack_require__(17); @@ -58,7 +58,7 @@ /***/ function(module, exports, __webpack_require__) { var State = __webpack_require__(2); - var expect = __webpack_require__(4) + var expect = __webpack_require__(8) @@ -262,157 +262,183 @@ /***/ function(module, exports, __webpack_require__) { var _ = __webpack_require__(3); - - - - function State(option){ - this._states = {}; - this._pending = false; - this.visited = false; - if(option) this.config(option); + var Qs = __webpack_require__(4); + + function State(option) + { + this._states = {}; + this._pending = false; + this.visited = false; + if (option) this.config(option); } - //regexp cache State.rCache = {}; - _.extend( _.emitable( State ), { - - state: function(stateName, config){ - if(_.typeOf(stateName) === "object"){ - for(var i in stateName){ - this.state(i, stateName[i]) - } - return this; - } - var current, next, nextName, states = this._states, i=0; - - if( typeof stateName === "string" ) stateName = stateName.split("."); - - var slen = stateName.length, current = this; - var stack = []; - - - do{ - nextName = stateName[i]; - next = states[nextName]; - stack.push(nextName); - if(!next){ - if(!config) return; - next = states[nextName] = new State(); - _.extend(next, { - parent: current, - manager: current.manager || current, - name: stack.join("."), - currentName: nextName - }) - current.hasNext = true; - next.configUrl(); - } - current = next; - states = next._states; - }while((++i) < slen ) - - if(config){ - next.config(config); - return this; - } else { - return current; - } - }, - - config: function(configure){ - - configure = this._getConfig(configure); - - for(var i in configure){ - var prop = configure[i]; - switch(i){ - case "url": - if(typeof prop === "string"){ - this.url = prop; - this.configUrl(); - } - break; - case "events": - this.on(prop) - break; - default: - this[i] = prop; - } - } - }, + _.extend(_.emitable(State), { - // children override - _getConfig: function(configure){ - return typeof configure === "function"? {enter: configure} : configure; - }, - - //from url + state: function (stateName, config) + { + if (_.typeOf(stateName) === "object") + { + for (var i in stateName) + { + this.state(i, stateName[i]) + } + return this; + } + var current, next, nextName, states = this._states, i = 0; + + if (typeof stateName === "string") stateName = stateName.split("."); + + var slen = stateName.length, current = this; + var stack = []; + + + do { + nextName = stateName[i]; + next = states[nextName]; + stack.push(nextName); + if (!next) + { + if (!config) return; + next = states[nextName] = new State(); + _.extend(next, { + parent: current, + manager: current.manager || current, + name: stack.join("."), + currentName: nextName + }); + current.hasNext = true; + next.configUrl(); + } + current = next; + states = next._states; + } while ((++i) < slen); + + if (config) + { + next.config(config); + return this; + } else + { + return current; + } + }, - configUrl: function(){ - var url = "" , base = this, currentUrl; - var _watchedParam = []; + config: function (configure) + { + + configure = this._getConfig(configure); + + for (var i in configure) + { + var prop = configure[i]; + switch (i) + { + case "url": + if (typeof prop === "string") + { + this.url = prop; + this.configUrl(); + } + break; + case "events": + this.on(prop) + break; + default: + this[i] = prop; + } + } + }, - while( base ){ + // children override + _getConfig: function (configure) + { + return typeof configure === "function" ? {enter: configure} : configure; + }, - url = (typeof base.url === "string" ? base.url: (base.currentName || "")) + "/" + url; + //from url - // means absolute; - if(url.indexOf("^/") === 0) { - url = url.slice(1); - break; - } - base = base.parent; - } - this.pattern = _.cleanPath("/" + url); - var pathAndQuery = this.pattern.split("?"); - this.pattern = pathAndQuery[0]; - // some Query we need watched + configUrl: function () + { + var url = "", base = this, currentUrl; + var _watchedParam = []; - _.extend(this, _.normalize(this.pattern), true); - }, - encode: function(param){ - var state = this; - param = param || {}; - - var matched = "%"; + while (base) + { - var url = state.matches.replace(/\(([\w-]+)\)/g, function(all, capture){ - var sec = param[capture] || ""; - matched+= capture + "%"; - return sec; - }) + "?"; + url = (typeof base.url === "string" ? base.url : (base.currentName || "")) + "/" + url; - // remained is the query, we need concat them after url as query - for(var i in param) { - if( matched.indexOf("%"+i+"%") === -1) url += i + "=" + param[i] + "&"; - } - return _.cleanPath( url.replace(/(?:\?|&)$/,"") ) - }, - decode: function( path ){ - var matched = this.regexp.exec(path), - keys = this.keys; + // means absolute; + if (url.indexOf("^/") === 0) + { + url = url.slice(1); + break; + } + base = base.parent; + } + this.pattern = _.cleanPath("/" + url); + var pathAndQuery = this.pattern.split("?"); + this.pattern = pathAndQuery[0]; + // some Query we need watched - if(matched){ + _.extend(this, _.normalize(this.pattern), true); + }, + encode: function (param) + { + var state = this; + var result = Qs.stringify(param); + return result; + + + // param = param || {}; + // + // var matched = "%"; + // + // var url = state.matches.replace(/\(([\w-]+)\)/g, function (all, capture) + // { + // var sec = param[capture] || ""; + // matched += capture + "%"; + // return sec; + // }) + "?"; + // + // // remained is the query, we need concat them after url as query + // for (var i in param) + // { + // if (matched.indexOf("%" + i + "%") === -1) url += i + "=" + param[i] + "&"; + // } + // return _.cleanPath(url.replace(/(?:\?|&)$/, "")) + }, + decode: function (path) + { + var result = Qs.parse(path); + return result; + + // var matched = this.regexp.exec(path), + // keys = this.keys; + // + // if (matched) + // { + // var param = {}; + // for (var i = 0, len = keys.length; i < len; i++) + // { + // param[keys[i]] = matched[i + 1] + // } + // return param; + // } else + // { + // return false; + // } + }, + // by default, all lifecycle is permitted - var param = {}; - for(var i =0,len=keys.length;i= 0x30 && c <= 0x39) || // 0-9 + (c >= 0x41 && c <= 0x5A) || // a-z + (c >= 0x61 && c <= 0x7A) // A-Z + ) { + out += string.charAt(i); + continue; + } + + if (c < 0x80) { + out = out + hexTable[c]; + continue; + } + + if (c < 0x800) { + out = out + (hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + if (c < 0xD800 || c >= 0xE000) { + out = out + (hexTable[0xE0 | (c >> 12)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]); + continue; + } + + i += 1; + c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF)); + out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]; + } + + return out; + }; + + exports.compact = function (obj, references) { + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + var refs = references || []; + var lookup = refs.indexOf(obj); + if (lookup !== -1) { + return refs[lookup]; + } + + refs.push(obj); + + if (Array.isArray(obj)) { + var compacted = []; + + for (var i = 0; i < obj.length; ++i) { + if (obj[i] && typeof obj[i] === 'object') { + compacted.push(exports.compact(obj[i], refs)); + } else if (typeof obj[i] !== 'undefined') { + compacted.push(obj[i]); + } + } + + return compacted; + } + + var keys = Object.keys(obj); + for (var j = 0; j < keys.length; ++j) { + var key = keys[j]; + obj[key] = exports.compact(obj[key], refs); + } + + return obj; + }; + + exports.isRegExp = function (obj) { + return Object.prototype.toString.call(obj) === '[object RegExp]'; + }; + + exports.isBuffer = function (obj) { + if (obj === null || typeof obj === 'undefined') { + return false; + } + + return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); + }; + + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var Utils = __webpack_require__(6); + + var has = Object.prototype.hasOwnProperty; + + var defaults = { + delimiter: '&', + depth: 5, + arrayLimit: 20, + parameterLimit: 1000, + strictNullHandling: false, + plainObjects: false, + allowPrototypes: false, + allowDots: false, + decoder: Utils.decode + }; + + var parseValues = function parseValues(str, options) { + var obj = {}; + var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit); + + for (var i = 0; i < parts.length; ++i) { + var part = parts[i]; + var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1; + + var key, val; + if (pos === -1) { + key = options.decoder(part); + val = options.strictNullHandling ? null : ''; + } else { + key = options.decoder(part.slice(0, pos)); + val = options.decoder(part.slice(pos + 1)); + } + if (has.call(obj, key)) { + obj[key] = [].concat(obj[key]).concat(val); + } else { + obj[key] = val; + } + } + + return obj; + }; + + var parseObject = function parseObject(chain, val, options) { + if (!chain.length) { + return val; + } + + var root = chain.shift(); + + var obj; + if (root === '[]') { + obj = []; + obj = obj.concat(parseObject(chain, val, options)); + } else { + obj = options.plainObjects ? Object.create(null) : {}; + var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root; + var index = parseInt(cleanRoot, 10); + if ( + !isNaN(index) && + root !== cleanRoot && + String(index) === cleanRoot && + index >= 0 && + (options.parseArrays && index <= options.arrayLimit) + ) { + obj = []; + obj[index] = parseObject(chain, val, options); + } else { + obj[cleanRoot] = parseObject(chain, val, options); + } + } + + return obj; + }; + + var parseKeys = function parseKeys(givenKey, val, options) { + if (!givenKey) { + return; + } + + // Transform dot notation to bracket notation + var key = options.allowDots ? givenKey.replace(/\.([^\.\[]+)/g, '[$1]') : givenKey; + + // The regex chunks + + var parent = /^([^\[\]]*)/; + var child = /(\[[^\[\]]*\])/g; + + // Get the parent + + var segment = parent.exec(key); + + // Stash the parent if it exists + + var keys = []; + if (segment[1]) { + // If we aren't using plain objects, optionally prefix keys + // that would overwrite object prototype properties + if (!options.plainObjects && has.call(Object.prototype, segment[1])) { + if (!options.allowPrototypes) { + return; + } + } + + keys.push(segment[1]); + } + + // Loop through children appending to the array until we hit depth + + var i = 0; + while ((segment = child.exec(key)) !== null && i < options.depth) { + i += 1; + if (!options.plainObjects && has.call(Object.prototype, segment[1].replace(/\[|\]/g, ''))) { + if (!options.allowPrototypes) { + continue; + } + } + keys.push(segment[1]); + } + + // If there's a remainder, just add whatever is left + + if (segment) { + keys.push('[' + key.slice(segment.index) + ']'); + } + + return parseObject(keys, val, options); + }; + + module.exports = function (str, opts) { + var options = opts || {}; + + if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') { + throw new TypeError('Decoder has to be a function.'); + } + + options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter; + options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth; + options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit; + options.parseArrays = options.parseArrays !== false; + options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder; + options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots; + options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects; + options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes; + options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit; + options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling; + + if (str === '' || str === null || typeof str === 'undefined') { + return options.plainObjects ? Object.create(null) : {}; + } + + var tempObj = typeof str === 'string' ? parseValues(str, options) : str; + var obj = options.plainObjects ? Object.create(null) : {}; + + // Iterate over the keys and setup the new object + + var keys = Object.keys(tempObj); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var newObj = parseKeys(key, tempObj[key], options); + obj = Utils.merge(obj, newObj, options); + } + + return Utils.compact(obj); + }; + + +/***/ }, +/* 8 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(module, Buffer) {(function (global, module) { @@ -1892,10 +2418,10 @@ this , true ? module : {exports: {}} ); - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(5)(module), __webpack_require__(6).Buffer)) + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(9)(module), __webpack_require__(10).Buffer)) /***/ }, -/* 5 */ +/* 9 */ /***/ function(module, exports) { module.exports = function(module) { @@ -1911,7 +2437,7 @@ /***/ }, -/* 6 */ +/* 10 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(Buffer, global) {/*! @@ -1922,16 +2448,15 @@ */ /* eslint-disable no-proto */ - var base64 = __webpack_require__(7) - var ieee754 = __webpack_require__(8) - var isArray = __webpack_require__(9) + 'use strict' + + var base64 = __webpack_require__(11) + var ieee754 = __webpack_require__(12) + var isArray = __webpack_require__(13) exports.Buffer = Buffer exports.SlowBuffer = SlowBuffer exports.INSPECT_MAX_BYTES = 50 - Buffer.poolSize = 8192 // not used by this implementation - - var rootParent = {} /** * If `Buffer.TYPED_ARRAY_SUPPORT`: @@ -1949,9 +2474,6 @@ * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. * - * - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property - * on objects. - * * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. * * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of @@ -1962,20 +2484,24 @@ */ Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined ? global.TYPED_ARRAY_SUPPORT - : (function () { - function Bar () {} - try { - var arr = new Uint8Array(1) - arr.foo = function () { return 42 } - arr.constructor = Bar - return arr.foo() === 42 && // typed array instances can be augmented - arr.constructor === Bar && // constructor can be set - typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` - arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` - } catch (e) { - return false - } - })() + : typedArraySupport() + + /* + * Export kMaxLength after typed array support is determined. + */ + exports.kMaxLength = kMaxLength() + + function typedArraySupport () { + try { + var arr = new Uint8Array(1) + arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }} + return arr.foo() === 42 && // typed array instances can be augmented + typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` + arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` + } catch (e) { + return false + } + } function kMaxLength () { return Buffer.TYPED_ARRAY_SUPPORT @@ -1983,178 +2509,252 @@ : 0x3fffffff } + function createBuffer (that, length) { + if (kMaxLength() < length) { + throw new RangeError('Invalid typed array length') + } + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + that = new Uint8Array(length) + that.__proto__ = Buffer.prototype + } else { + // Fallback: Return an object instance of the Buffer class + if (that === null) { + that = new Buffer(length) + } + that.length = length + } + + return that + } + /** - * Class: Buffer - * ============= - * - * The Buffer constructor returns instances of `Uint8Array` that are augmented - * with function properties for all the node `Buffer` API functions. We use - * `Uint8Array` so that square bracket notation works as expected -- it returns - * a single octet. + * The Buffer constructor returns instances of `Uint8Array` that have their + * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of + * `Uint8Array`, so the returned instances will have all the node `Buffer` methods + * and the `Uint8Array` methods. Square bracket notation works as expected -- it + * returns a single octet. * - * By augmenting the instances, we can avoid modifying the `Uint8Array` - * prototype. + * The `Uint8Array` prototype remains unmodified. */ - function Buffer (arg) { - if (!(this instanceof Buffer)) { - // Avoid going through an ArgumentsAdaptorTrampoline in the common case. - if (arguments.length > 1) return new Buffer(arg, arguments[1]) - return new Buffer(arg) - } - this.length = 0 - this.parent = undefined + function Buffer (arg, encodingOrOffset, length) { + if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) { + return new Buffer(arg, encodingOrOffset, length) + } // Common case. if (typeof arg === 'number') { - return fromNumber(this, arg) + if (typeof encodingOrOffset === 'string') { + throw new Error( + 'If encoding is specified then the first argument must be a string' + ) + } + return allocUnsafe(this, arg) } + return from(this, arg, encodingOrOffset, length) + } - // Slightly less common case. - if (typeof arg === 'string') { - return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') - } + Buffer.poolSize = 8192 // not used by this implementation - // Unusual. - return fromObject(this, arg) + // TODO: Legacy, not needed anymore. Remove in next major version. + Buffer._augment = function (arr) { + arr.__proto__ = Buffer.prototype + return arr } - function fromNumber (that, length) { - that = allocate(that, length < 0 ? 0 : checked(length) | 0) - if (!Buffer.TYPED_ARRAY_SUPPORT) { - for (var i = 0; i < length; i++) { - that[i] = 0 - } + function from (that, value, encodingOrOffset, length) { + if (typeof value === 'number') { + throw new TypeError('"value" argument must not be a number') } - return that - } - function fromString (that, string, encoding) { - if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' + if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { + return fromArrayBuffer(that, value, encodingOrOffset, length) + } - // Assumption: byteLength() return value is always < kMaxLength. - var length = byteLength(string, encoding) | 0 - that = allocate(that, length) + if (typeof value === 'string') { + return fromString(that, value, encodingOrOffset) + } - that.write(string, encoding) - return that + return fromObject(that, value) } - function fromObject (that, object) { - if (Buffer.isBuffer(object)) return fromBuffer(that, object) - - if (isArray(object)) return fromArray(that, object) + /** + * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError + * if value is a number. + * Buffer.from(str[, encoding]) + * Buffer.from(array) + * Buffer.from(buffer) + * Buffer.from(arrayBuffer[, byteOffset[, length]]) + **/ + Buffer.from = function (value, encodingOrOffset, length) { + return from(null, value, encodingOrOffset, length) + } - if (object == null) { - throw new TypeError('must start with number, buffer, array or string') + if (Buffer.TYPED_ARRAY_SUPPORT) { + Buffer.prototype.__proto__ = Uint8Array.prototype + Buffer.__proto__ = Uint8Array + if (typeof Symbol !== 'undefined' && Symbol.species && + Buffer[Symbol.species] === Buffer) { + // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 + Object.defineProperty(Buffer, Symbol.species, { + value: null, + configurable: true + }) } + } - if (typeof ArrayBuffer !== 'undefined') { - if (object.buffer instanceof ArrayBuffer) { - return fromTypedArray(that, object) - } - if (object instanceof ArrayBuffer) { - return fromArrayBuffer(that, object) - } + function assertSize (size) { + if (typeof size !== 'number') { + throw new TypeError('"size" argument must be a number') + } else if (size < 0) { + throw new RangeError('"size" argument must not be negative') } + } - if (object.length) return fromArrayLike(that, object) - - return fromJsonObject(that, object) + function alloc (that, size, fill, encoding) { + assertSize(size) + if (size <= 0) { + return createBuffer(that, size) + } + if (fill !== undefined) { + // Only pay attention to encoding if it's a string. This + // prevents accidentally sending in a number that would + // be interpretted as a start offset. + return typeof encoding === 'string' + ? createBuffer(that, size).fill(fill, encoding) + : createBuffer(that, size).fill(fill) + } + return createBuffer(that, size) } - function fromBuffer (that, buffer) { - var length = checked(buffer.length) | 0 - that = allocate(that, length) - buffer.copy(that, 0, 0, length) - return that + /** + * Creates a new filled Buffer instance. + * alloc(size[, fill[, encoding]]) + **/ + Buffer.alloc = function (size, fill, encoding) { + return alloc(null, size, fill, encoding) } - function fromArray (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 + function allocUnsafe (that, size) { + assertSize(size) + that = createBuffer(that, size < 0 ? 0 : checked(size) | 0) + if (!Buffer.TYPED_ARRAY_SUPPORT) { + for (var i = 0; i < size; ++i) { + that[i] = 0 + } } return that } - // Duplicate of fromArray() to keep fromArray() monomorphic. - function fromTypedArray (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) - // Truncating the elements is probably not what people expect from typed - // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior - // of the old Buffer constructor. - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 - } - return that + /** + * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance. + * */ + Buffer.allocUnsafe = function (size) { + return allocUnsafe(null, size) + } + /** + * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance. + */ + Buffer.allocUnsafeSlow = function (size) { + return allocUnsafe(null, size) } - function fromArrayBuffer (that, array) { - if (Buffer.TYPED_ARRAY_SUPPORT) { - // Return an augmented `Uint8Array` instance, for best performance - array.byteLength - that = Buffer._augment(new Uint8Array(array)) - } else { - // Fallback: Return an object instance of the Buffer class - that = fromTypedArray(that, new Uint8Array(array)) + function fromString (that, string, encoding) { + if (typeof encoding !== 'string' || encoding === '') { + encoding = 'utf8' + } + + if (!Buffer.isEncoding(encoding)) { + throw new TypeError('"encoding" must be a valid string encoding') } + + var length = byteLength(string, encoding) | 0 + that = createBuffer(that, length) + + var actual = that.write(string, encoding) + + if (actual !== length) { + // Writing a hex string, for example, that contains invalid characters will + // cause everything after the first invalid character to be ignored. (e.g. + // 'abxxcd' will be treated as 'ab') + that = that.slice(0, actual) + } + return that } function fromArrayLike (that, array) { - var length = checked(array.length) | 0 - that = allocate(that, length) + var length = array.length < 0 ? 0 : checked(array.length) | 0 + that = createBuffer(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } - // Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. - // Returns a zero-length buffer for inputs that don't conform to the spec. - function fromJsonObject (that, object) { - var array - var length = 0 + function fromArrayBuffer (that, array, byteOffset, length) { + array.byteLength // this throws if `array` is not a valid ArrayBuffer - if (object.type === 'Buffer' && isArray(object.data)) { - array = object.data - length = checked(array.length) | 0 + if (byteOffset < 0 || array.byteLength < byteOffset) { + throw new RangeError('\'offset\' is out of bounds') } - that = allocate(that, length) - for (var i = 0; i < length; i += 1) { - that[i] = array[i] & 255 + if (array.byteLength < byteOffset + (length || 0)) { + throw new RangeError('\'length\' is out of bounds') } - return that - } - if (Buffer.TYPED_ARRAY_SUPPORT) { - Buffer.prototype.__proto__ = Uint8Array.prototype - Buffer.__proto__ = Uint8Array - } + if (byteOffset === undefined && length === undefined) { + array = new Uint8Array(array) + } else if (length === undefined) { + array = new Uint8Array(array, byteOffset) + } else { + array = new Uint8Array(array, byteOffset, length) + } - function allocate (that, length) { if (Buffer.TYPED_ARRAY_SUPPORT) { // Return an augmented `Uint8Array` instance, for best performance - that = Buffer._augment(new Uint8Array(length)) + that = array that.__proto__ = Buffer.prototype } else { // Fallback: Return an object instance of the Buffer class - that.length = length - that._isBuffer = true + that = fromArrayLike(that, array) } + return that + } - var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 - if (fromPool) that.parent = rootParent + function fromObject (that, obj) { + if (Buffer.isBuffer(obj)) { + var len = checked(obj.length) | 0 + that = createBuffer(that, len) - return that + if (that.length === 0) { + return that + } + + obj.copy(that, 0, 0, len) + return that + } + + if (obj) { + if ((typeof ArrayBuffer !== 'undefined' && + obj.buffer instanceof ArrayBuffer) || 'length' in obj) { + if (typeof obj.length !== 'number' || isnan(obj.length)) { + return createBuffer(that, 0) + } + return fromArrayLike(that, obj) + } + + if (obj.type === 'Buffer' && isArray(obj.data)) { + return fromArrayLike(that, obj.data) + } + } + + throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.') } function checked (length) { - // Note: cannot use `length < kMaxLength` here because that fails when + // Note: cannot use `length < kMaxLength()` here because that fails when // length is NaN (which is otherwise coerced to zero.) if (length >= kMaxLength()) { throw new RangeError('Attempt to allocate Buffer larger than maximum ' + @@ -2163,12 +2763,11 @@ return length | 0 } - function SlowBuffer (subject, encoding) { - if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) - - var buf = new Buffer(subject, encoding) - delete buf.parent - return buf + function SlowBuffer (length) { + if (+length != length) { // eslint-disable-line eqeqeq + length = 0 + } + return Buffer.alloc(+length) } Buffer.isBuffer = function isBuffer (b) { @@ -2185,17 +2784,12 @@ var x = a.length var y = b.length - var i = 0 - var len = Math.min(x, y) - while (i < len) { - if (a[i] !== b[i]) break - - ++i - } - - if (i !== len) { - x = a[i] - y = b[i] + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i] + y = b[i] + break + } } if (x < y) return -1 @@ -2209,9 +2803,9 @@ case 'utf8': case 'utf-8': case 'ascii': + case 'latin1': case 'binary': case 'base64': - case 'raw': case 'ucs2': case 'ucs-2': case 'utf16le': @@ -2223,32 +2817,46 @@ } Buffer.concat = function concat (list, length) { - if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.') + if (!isArray(list)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } if (list.length === 0) { - return new Buffer(0) + return Buffer.alloc(0) } var i if (length === undefined) { length = 0 - for (i = 0; i < list.length; i++) { + for (i = 0; i < list.length; ++i) { length += list[i].length } } - var buf = new Buffer(length) + var buffer = Buffer.allocUnsafe(length) var pos = 0 - for (i = 0; i < list.length; i++) { - var item = list[i] - item.copy(buf, pos) - pos += item.length + for (i = 0; i < list.length; ++i) { + var buf = list[i] + if (!Buffer.isBuffer(buf)) { + throw new TypeError('"list" argument must be an Array of Buffers') + } + buf.copy(buffer, pos) + pos += buf.length } - return buf + return buffer } function byteLength (string, encoding) { - if (typeof string !== 'string') string = '' + string + if (Buffer.isBuffer(string)) { + return string.length + } + if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' && + (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) { + return string.byteLength + } + if (typeof string !== 'string') { + string = '' + string + } var len = string.length if (len === 0) return 0 @@ -2258,13 +2866,12 @@ for (;;) { switch (encoding) { case 'ascii': + case 'latin1': case 'binary': - // Deprecated - case 'raw': - case 'raws': return len case 'utf8': case 'utf-8': + case undefined: return utf8ToBytes(string).length case 'ucs2': case 'ucs-2': @@ -2284,20 +2891,42 @@ } Buffer.byteLength = byteLength - // pre-set for values that may exist in the future - Buffer.prototype.length = undefined - Buffer.prototype.parent = undefined - function slowToString (encoding, start, end) { var loweredCase = false - start = start | 0 - end = end === undefined || end === Infinity ? this.length : end | 0 + // No need to verify that "this.length <= MAX_UINT32" since it's a read-only + // property of a typed array. + + // This behaves neither like String nor Uint8Array in that we set start/end + // to their upper/lower bounds if the value passed is out of range. + // undefined is handled specially as per ECMA-262 6th Edition, + // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization. + if (start === undefined || start < 0) { + start = 0 + } + // Return early if start > this.length. Done here to prevent potential uint32 + // coercion fail below. + if (start > this.length) { + return '' + } + + if (end === undefined || end > this.length) { + end = this.length + } + + if (end <= 0) { + return '' + } + + // Force coersion to uint32. This will also coerce falsey/NaN values to 0. + end >>>= 0 + start >>>= 0 + + if (end <= start) { + return '' + } if (!encoding) encoding = 'utf8' - if (start < 0) start = 0 - if (end > this.length) end = this.length - if (end <= start) return '' while (true) { switch (encoding) { @@ -2311,8 +2940,9 @@ case 'ascii': return asciiSlice(this, start, end) + case 'latin1': case 'binary': - return binarySlice(this, start, end) + return latin1Slice(this, start, end) case 'base64': return base64Slice(this, start, end) @@ -2331,6 +2961,53 @@ } } + // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect + // Buffer instances. + Buffer.prototype._isBuffer = true + + function swap (b, n, m) { + var i = b[n] + b[n] = b[m] + b[m] = i + } + + Buffer.prototype.swap16 = function swap16 () { + var len = this.length + if (len % 2 !== 0) { + throw new RangeError('Buffer size must be a multiple of 16-bits') + } + for (var i = 0; i < len; i += 2) { + swap(this, i, i + 1) + } + return this + } + + Buffer.prototype.swap32 = function swap32 () { + var len = this.length + if (len % 4 !== 0) { + throw new RangeError('Buffer size must be a multiple of 32-bits') + } + for (var i = 0; i < len; i += 4) { + swap(this, i, i + 3) + swap(this, i + 1, i + 2) + } + return this + } + + Buffer.prototype.swap64 = function swap64 () { + var len = this.length + if (len % 8 !== 0) { + throw new RangeError('Buffer size must be a multiple of 64-bits') + } + for (var i = 0; i < len; i += 8) { + swap(this, i, i + 7) + swap(this, i + 1, i + 6) + swap(this, i + 2, i + 5) + swap(this, i + 3, i + 4) + } + return this + } + Buffer.prototype.toString = function toString () { var length = this.length | 0 if (length === 0) return '' @@ -2354,63 +3031,197 @@ return '' } - Buffer.prototype.compare = function compare (b) { - if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') - if (this === b) return 0 - return Buffer.compare(this, b) - } + Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) { + if (!Buffer.isBuffer(target)) { + throw new TypeError('Argument must be a Buffer') + } + + if (start === undefined) { + start = 0 + } + if (end === undefined) { + end = target ? target.length : 0 + } + if (thisStart === undefined) { + thisStart = 0 + } + if (thisEnd === undefined) { + thisEnd = this.length + } + + if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) { + throw new RangeError('out of range index') + } + + if (thisStart >= thisEnd && start >= end) { + return 0 + } + if (thisStart >= thisEnd) { + return -1 + } + if (start >= end) { + return 1 + } + + start >>>= 0 + end >>>= 0 + thisStart >>>= 0 + thisEnd >>>= 0 + + if (this === target) return 0 - Buffer.prototype.indexOf = function indexOf (val, byteOffset) { - if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff - else if (byteOffset < -0x80000000) byteOffset = -0x80000000 - byteOffset >>= 0 + var x = thisEnd - thisStart + var y = end - start + var len = Math.min(x, y) + + var thisCopy = this.slice(thisStart, thisEnd) + var targetCopy = target.slice(start, end) + + for (var i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i] + y = targetCopy[i] + break + } + } + + if (x < y) return -1 + if (y < x) return 1 + return 0 + } - if (this.length === 0) return -1 - if (byteOffset >= this.length) return -1 + // Finds either the first index of `val` in `buffer` at offset >= `byteOffset`, + // OR the last index of `val` in `buffer` at offset <= `byteOffset`. + // + // Arguments: + // - buffer - a Buffer to search + // - val - a string, Buffer, or number + // - byteOffset - an index into `buffer`; will be clamped to an int32 + // - encoding - an optional encoding, relevant is val is a string + // - dir - true for indexOf, false for lastIndexOf + function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) { + // Empty buffer means no match + if (buffer.length === 0) return -1 + + // Normalize byteOffset + if (typeof byteOffset === 'string') { + encoding = byteOffset + byteOffset = 0 + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000 + } + byteOffset = +byteOffset // Coerce to Number. + if (isNaN(byteOffset)) { + // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer + byteOffset = dir ? 0 : (buffer.length - 1) + } - // Negative offsets start from the end of the buffer - if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) + // Normalize byteOffset: negative offsets start from the end of the buffer + if (byteOffset < 0) byteOffset = buffer.length + byteOffset + if (byteOffset >= buffer.length) { + if (dir) return -1 + else byteOffset = buffer.length - 1 + } else if (byteOffset < 0) { + if (dir) byteOffset = 0 + else return -1 + } + // Normalize val if (typeof val === 'string') { - if (val.length === 0) return -1 // special case: looking for empty string always fails - return String.prototype.indexOf.call(this, val, byteOffset) + val = Buffer.from(val, encoding) } + + // Finally, search either indexOf (if dir is true) or lastIndexOf if (Buffer.isBuffer(val)) { - return arrayIndexOf(this, val, byteOffset) + // Special case: looking for empty string/buffer always fails + if (val.length === 0) { + return -1 + } + return arrayIndexOf(buffer, val, byteOffset, encoding, dir) + } else if (typeof val === 'number') { + val = val & 0xFF // Search for a byte value [0-255] + if (Buffer.TYPED_ARRAY_SUPPORT && + typeof Uint8Array.prototype.indexOf === 'function') { + if (dir) { + return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset) + } else { + return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset) + } + } + return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir) } - if (typeof val === 'number') { - if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { - return Uint8Array.prototype.indexOf.call(this, val, byteOffset) + + throw new TypeError('val must be string, number or Buffer') + } + + function arrayIndexOf (arr, val, byteOffset, encoding, dir) { + var indexSize = 1 + var arrLength = arr.length + var valLength = val.length + + if (encoding !== undefined) { + encoding = String(encoding).toLowerCase() + if (encoding === 'ucs2' || encoding === 'ucs-2' || + encoding === 'utf16le' || encoding === 'utf-16le') { + if (arr.length < 2 || val.length < 2) { + return -1 + } + indexSize = 2 + arrLength /= 2 + valLength /= 2 + byteOffset /= 2 } - return arrayIndexOf(this, [ val ], byteOffset) } - function arrayIndexOf (arr, val, byteOffset) { + function read (buf, i) { + if (indexSize === 1) { + return buf[i] + } else { + return buf.readUInt16BE(i * indexSize) + } + } + + var i + if (dir) { var foundIndex = -1 - for (var i = 0; byteOffset + i < arr.length; i++) { - if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) { + for (i = byteOffset; i < arrLength; i++) { + if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) { if (foundIndex === -1) foundIndex = i - if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex + if (i - foundIndex + 1 === valLength) return foundIndex * indexSize } else { + if (foundIndex !== -1) i -= i - foundIndex foundIndex = -1 } } - return -1 + } else { + if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength + for (i = byteOffset; i >= 0; i--) { + var found = true + for (var j = 0; j < valLength; j++) { + if (read(arr, i + j) !== read(val, j)) { + found = false + break + } + } + if (found) return i + } } - throw new TypeError('val must be string, number or Buffer') + return -1 } - // `get` is deprecated - Buffer.prototype.get = function get (offset) { - console.log('.get() is deprecated. Access using array indexes instead.') - return this.readUInt8(offset) + Buffer.prototype.includes = function includes (val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1 } - // `set` is deprecated - Buffer.prototype.set = function set (v, offset) { - console.log('.set() is deprecated. Access using array indexes instead.') - return this.writeUInt8(v, offset) + Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true) + } + + Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false) } function hexWrite (buf, string, offset, length) { @@ -2427,14 +3238,14 @@ // must be an even number of digits var strLen = string.length - if (strLen % 2 !== 0) throw new Error('Invalid hex string') + if (strLen % 2 !== 0) throw new TypeError('Invalid hex string') if (length > strLen / 2) { length = strLen / 2 } - for (var i = 0; i < length; i++) { + for (var i = 0; i < length; ++i) { var parsed = parseInt(string.substr(i * 2, 2), 16) - if (isNaN(parsed)) throw new Error('Invalid hex string') + if (isNaN(parsed)) return i buf[offset + i] = parsed } return i @@ -2448,7 +3259,7 @@ return blitBuffer(asciiToBytes(string), buf, offset, length) } - function binaryWrite (buf, string, offset, length) { + function latin1Write (buf, string, offset, length) { return asciiWrite(buf, string, offset, length) } @@ -2483,17 +3294,16 @@ } // legacy write(string, encoding, offset, length) - remove in v0.13 } else { - var swap = encoding - encoding = offset - offset = length | 0 - length = swap + throw new Error( + 'Buffer.write(string, encoding, offset[, length]) is no longer supported' + ) } var remaining = this.length - offset if (length === undefined || length > remaining) length = remaining if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { - throw new RangeError('attempt to write outside buffer bounds') + throw new RangeError('Attempt to write outside buffer bounds') } if (!encoding) encoding = 'utf8' @@ -2511,8 +3321,9 @@ case 'ascii': return asciiWrite(this, string, offset, length) + case 'latin1': case 'binary': - return binaryWrite(this, string, offset, length) + return latin1Write(this, string, offset, length) case 'base64': // Warning: maxLength not taken into account in base64Write @@ -2647,17 +3458,17 @@ var ret = '' end = Math.min(buf.length, end) - for (var i = start; i < end; i++) { + for (var i = start; i < end; ++i) { ret += String.fromCharCode(buf[i] & 0x7F) } return ret } - function binarySlice (buf, start, end) { + function latin1Slice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) - for (var i = start; i < end; i++) { + for (var i = start; i < end; ++i) { ret += String.fromCharCode(buf[i]) } return ret @@ -2670,7 +3481,7 @@ if (!end || end < 0 || end > len) end = len var out = '' - for (var i = start; i < end; i++) { + for (var i = start; i < end; ++i) { out += toHex(buf[i]) } return out @@ -2708,17 +3519,16 @@ var newBuf if (Buffer.TYPED_ARRAY_SUPPORT) { - newBuf = Buffer._augment(this.subarray(start, end)) + newBuf = this.subarray(start, end) + newBuf.__proto__ = Buffer.prototype } else { var sliceLen = end - start newBuf = new Buffer(sliceLen, undefined) - for (var i = 0; i < sliceLen; i++) { + for (var i = 0; i < sliceLen; ++i) { newBuf[i] = this[i + start] } } - if (newBuf.length) newBuf.parent = this.parent || this - return newBuf } @@ -2887,16 +3697,19 @@ } function checkInt (buf, value, offset, ext, max, min) { - if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance') - if (value > max || value < min) throw new RangeError('value is out of bounds') - if (offset + ext > buf.length) throw new RangeError('index out of range') + if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance') + if (value > max || value < min) throw new RangeError('"value" argument is out of bounds') + if (offset + ext > buf.length) throw new RangeError('Index out of range') } Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 byteLength = byteLength | 0 - if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } var mul = 1 var i = 0 @@ -2912,7 +3725,10 @@ value = +value offset = offset | 0 byteLength = byteLength | 0 - if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) + if (!noAssert) { + var maxBytes = Math.pow(2, 8 * byteLength) - 1 + checkInt(this, value, offset, byteLength, maxBytes, 0) + } var i = byteLength - 1 var mul = 1 @@ -2929,13 +3745,13 @@ offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) - this[offset] = value + this[offset] = (value & 0xff) return offset + 1 } function objectWriteUInt16 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffff + value + 1 - for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { + for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) { buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> (littleEndian ? i : 1 - i) * 8 } @@ -2946,7 +3762,7 @@ offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = value + this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) @@ -2960,7 +3776,7 @@ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) - this[offset + 1] = value + this[offset + 1] = (value & 0xff) } else { objectWriteUInt16(this, value, offset, false) } @@ -2969,7 +3785,7 @@ function objectWriteUInt32 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffffffff + value + 1 - for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { + for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) { buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff } } @@ -2982,7 +3798,7 @@ this[offset + 3] = (value >>> 24) this[offset + 2] = (value >>> 16) this[offset + 1] = (value >>> 8) - this[offset] = value + this[offset] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, true) } @@ -2997,7 +3813,7 @@ this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) - this[offset + 3] = value + this[offset + 3] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, false) } @@ -3015,9 +3831,12 @@ var i = 0 var mul = 1 - var sub = value < 0 ? 1 : 0 + var sub = 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) { + sub = 1 + } this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } @@ -3035,9 +3854,12 @@ var i = byteLength - 1 var mul = 1 - var sub = value < 0 ? 1 : 0 + var sub = 0 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { + if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) { + sub = 1 + } this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } @@ -3050,7 +3872,7 @@ if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) if (value < 0) value = 0xff + value + 1 - this[offset] = value + this[offset] = (value & 0xff) return offset + 1 } @@ -3059,7 +3881,7 @@ offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = value + this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) @@ -3073,7 +3895,7 @@ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) - this[offset + 1] = value + this[offset + 1] = (value & 0xff) } else { objectWriteUInt16(this, value, offset, false) } @@ -3085,7 +3907,7 @@ offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (Buffer.TYPED_ARRAY_SUPPORT) { - this[offset] = value + this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) this[offset + 2] = (value >>> 16) this[offset + 3] = (value >>> 24) @@ -3104,7 +3926,7 @@ this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) - this[offset + 3] = value + this[offset + 3] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, false) } @@ -3112,9 +3934,8 @@ } function checkIEEE754 (buf, value, offset, ext, max, min) { - if (value > max || value < min) throw new RangeError('value is out of bounds') - if (offset + ext > buf.length) throw new RangeError('index out of range') - if (offset < 0) throw new RangeError('index out of range') + if (offset + ext > buf.length) throw new RangeError('Index out of range') + if (offset < 0) throw new RangeError('Index out of range') } function writeFloat (buf, value, offset, littleEndian, noAssert) { @@ -3179,143 +4000,91 @@ if (this === target && start < targetStart && targetStart < end) { // descending copy from end - for (i = len - 1; i >= 0; i--) { + for (i = len - 1; i >= 0; --i) { target[i + targetStart] = this[i + start] } } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { // ascending copy from start - for (i = 0; i < len; i++) { + for (i = 0; i < len; ++i) { target[i + targetStart] = this[i + start] } } else { - target._set(this.subarray(start, start + len), targetStart) + Uint8Array.prototype.set.call( + target, + this.subarray(start, start + len), + targetStart + ) } return len } - // fill(value, start=0, end=buffer.length) - Buffer.prototype.fill = function fill (value, start, end) { - if (!value) value = 0 - if (!start) start = 0 - if (!end) end = this.length + // Usage: + // buffer.fill(number[, offset[, end]]) + // buffer.fill(buffer[, offset[, end]]) + // buffer.fill(string[, offset[, end]][, encoding]) + Buffer.prototype.fill = function fill (val, start, end, encoding) { + // Handle string cases: + if (typeof val === 'string') { + if (typeof start === 'string') { + encoding = start + start = 0 + end = this.length + } else if (typeof end === 'string') { + encoding = end + end = this.length + } + if (val.length === 1) { + var code = val.charCodeAt(0) + if (code < 256) { + val = code + } + } + if (encoding !== undefined && typeof encoding !== 'string') { + throw new TypeError('encoding must be a string') + } + if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) { + throw new TypeError('Unknown encoding: ' + encoding) + } + } else if (typeof val === 'number') { + val = val & 255 + } - if (end < start) throw new RangeError('end < start') + // Invalid ranges are not set to a default, so can range check early. + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError('Out of range index') + } + + if (end <= start) { + return this + } - // Fill 0 bytes; we're done - if (end === start) return - if (this.length === 0) return + start = start >>> 0 + end = end === undefined ? this.length : end >>> 0 - if (start < 0 || start >= this.length) throw new RangeError('start out of bounds') - if (end < 0 || end > this.length) throw new RangeError('end out of bounds') + if (!val) val = 0 var i - if (typeof value === 'number') { - for (i = start; i < end; i++) { - this[i] = value + if (typeof val === 'number') { + for (i = start; i < end; ++i) { + this[i] = val } } else { - var bytes = utf8ToBytes(value.toString()) + var bytes = Buffer.isBuffer(val) + ? val + : utf8ToBytes(new Buffer(val, encoding).toString()) var len = bytes.length - for (i = start; i < end; i++) { - this[i] = bytes[i % len] + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len] } } return this } - /** - * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. - * Added in Node 0.12. Only available in browsers that support ArrayBuffer. - */ - Buffer.prototype.toArrayBuffer = function toArrayBuffer () { - if (typeof Uint8Array !== 'undefined') { - if (Buffer.TYPED_ARRAY_SUPPORT) { - return (new Buffer(this)).buffer - } else { - var buf = new Uint8Array(this.length) - for (var i = 0, len = buf.length; i < len; i += 1) { - buf[i] = this[i] - } - return buf.buffer - } - } else { - throw new TypeError('Buffer.toArrayBuffer not supported in this browser') - } - } - // HELPER FUNCTIONS // ================ - var BP = Buffer.prototype - - /** - * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods - */ - Buffer._augment = function _augment (arr) { - arr.constructor = Buffer - arr._isBuffer = true - - // save reference to original Uint8Array set method before overwriting - arr._set = arr.set - - // deprecated - arr.get = BP.get - arr.set = BP.set - - arr.write = BP.write - arr.toString = BP.toString - arr.toLocaleString = BP.toString - arr.toJSON = BP.toJSON - arr.equals = BP.equals - arr.compare = BP.compare - arr.indexOf = BP.indexOf - arr.copy = BP.copy - arr.slice = BP.slice - arr.readUIntLE = BP.readUIntLE - arr.readUIntBE = BP.readUIntBE - arr.readUInt8 = BP.readUInt8 - arr.readUInt16LE = BP.readUInt16LE - arr.readUInt16BE = BP.readUInt16BE - arr.readUInt32LE = BP.readUInt32LE - arr.readUInt32BE = BP.readUInt32BE - arr.readIntLE = BP.readIntLE - arr.readIntBE = BP.readIntBE - arr.readInt8 = BP.readInt8 - arr.readInt16LE = BP.readInt16LE - arr.readInt16BE = BP.readInt16BE - arr.readInt32LE = BP.readInt32LE - arr.readInt32BE = BP.readInt32BE - arr.readFloatLE = BP.readFloatLE - arr.readFloatBE = BP.readFloatBE - arr.readDoubleLE = BP.readDoubleLE - arr.readDoubleBE = BP.readDoubleBE - arr.writeUInt8 = BP.writeUInt8 - arr.writeUIntLE = BP.writeUIntLE - arr.writeUIntBE = BP.writeUIntBE - arr.writeUInt16LE = BP.writeUInt16LE - arr.writeUInt16BE = BP.writeUInt16BE - arr.writeUInt32LE = BP.writeUInt32LE - arr.writeUInt32BE = BP.writeUInt32BE - arr.writeIntLE = BP.writeIntLE - arr.writeIntBE = BP.writeIntBE - arr.writeInt8 = BP.writeInt8 - arr.writeInt16LE = BP.writeInt16LE - arr.writeInt16BE = BP.writeInt16BE - arr.writeInt32LE = BP.writeInt32LE - arr.writeInt32BE = BP.writeInt32BE - arr.writeFloatLE = BP.writeFloatLE - arr.writeFloatBE = BP.writeFloatBE - arr.writeDoubleLE = BP.writeDoubleLE - arr.writeDoubleBE = BP.writeDoubleBE - arr.fill = BP.fill - arr.inspect = BP.inspect - arr.toArrayBuffer = BP.toArrayBuffer - - return arr - } - var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g function base64clean (str) { @@ -3347,7 +4116,7 @@ var leadSurrogate = null var bytes = [] - for (var i = 0; i < length; i++) { + for (var i = 0; i < length; ++i) { codePoint = string.charCodeAt(i) // is surrogate component @@ -3379,7 +4148,7 @@ } // valid surrogate pair - codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 + codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 } else if (leadSurrogate) { // valid bmp char, but last char was a lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) @@ -3422,7 +4191,7 @@ function asciiToBytes (str) { var byteArray = [] - for (var i = 0; i < str.length; i++) { + for (var i = 0; i < str.length; ++i) { // Node's code seems to be doing this and not & 0x7F.. byteArray.push(str.charCodeAt(i) & 0xFF) } @@ -3432,7 +4201,7 @@ function utf16leToBytes (str, units) { var c, hi, lo var byteArray = [] - for (var i = 0; i < str.length; i++) { + for (var i = 0; i < str.length; ++i) { if ((units -= 2) < 0) break c = str.charCodeAt(i) @@ -3450,147 +4219,136 @@ } function blitBuffer (src, dst, offset, length) { - for (var i = 0; i < length; i++) { + for (var i = 0; i < length; ++i) { if ((i + offset >= dst.length) || (i >= src.length)) break dst[i + offset] = src[i] } return i } - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(6).Buffer, (function() { return this; }()))) + function isnan (val) { + return val !== val // eslint-disable-line no-self-compare + } + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(10).Buffer, (function() { return this; }()))) /***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { +/* 11 */ +/***/ function(module, exports) { + + 'use strict' + + exports.toByteArray = toByteArray + exports.fromByteArray = fromByteArray + + var lookup = [] + var revLookup = [] + var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array + + function init () { + var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + for (var i = 0, len = code.length; i < len; ++i) { + lookup[i] = code[i] + revLookup[code.charCodeAt(i)] = i + } + + revLookup['-'.charCodeAt(0)] = 62 + revLookup['_'.charCodeAt(0)] = 63 + } + + init() + + function toByteArray (b64) { + var i, j, l, tmp, placeHolders, arr + var len = b64.length + + if (len % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + placeHolders = b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(len * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? len - 4 : len + + var L = 0 + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)] + arr[L++] = (tmp >> 16) & 0xFF + arr[L++] = (tmp >> 8) & 0xFF + arr[L++] = tmp & 0xFF + } + + if (placeHolders === 2) { + tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4) + arr[L++] = tmp & 0xFF + } else if (placeHolders === 1) { + tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2) + arr[L++] = (tmp >> 8) & 0xFF + arr[L++] = tmp & 0xFF + } + + return arr + } + + function tripletToBase64 (num) { + return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F] + } + + function encodeChunk (uint8, start, end) { + var tmp + var output = [] + for (var i = start; i < end; i += 3) { + tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output.push(tripletToBase64(tmp)) + } + return output.join('') + } + + function fromByteArray (uint8) { + var tmp + var len = uint8.length + var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes + var output = '' + var parts = [] + var maxChunkLength = 16383 // must be multiple of 3 + + // go through the array every three bytes, we'll deal with trailing stuff later + for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { + parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) + } - var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - - ;(function (exports) { - 'use strict'; - - var Arr = (typeof Uint8Array !== 'undefined') - ? Uint8Array - : Array - - var PLUS = '+'.charCodeAt(0) - var SLASH = '/'.charCodeAt(0) - var NUMBER = '0'.charCodeAt(0) - var LOWER = 'a'.charCodeAt(0) - var UPPER = 'A'.charCodeAt(0) - var PLUS_URL_SAFE = '-'.charCodeAt(0) - var SLASH_URL_SAFE = '_'.charCodeAt(0) - - function decode (elt) { - var code = elt.charCodeAt(0) - if (code === PLUS || - code === PLUS_URL_SAFE) - return 62 // '+' - if (code === SLASH || - code === SLASH_URL_SAFE) - return 63 // '/' - if (code < NUMBER) - return -1 //no match - if (code < NUMBER + 10) - return code - NUMBER + 26 + 26 - if (code < UPPER + 26) - return code - UPPER - if (code < LOWER + 26) - return code - LOWER + 26 - } - - function b64ToByteArray (b64) { - var i, j, l, tmp, placeHolders, arr - - if (b64.length % 4 > 0) { - throw new Error('Invalid string. Length must be a multiple of 4') - } - - // the number of equal signs (place holders) - // if there are two placeholders, than the two characters before it - // represent one byte - // if there is only one, then the three characters before it represent 2 bytes - // this is just a cheap hack to not do indexOf twice - var len = b64.length - placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 - - // base64 is 4/3 + up to two characters of the original data - arr = new Arr(b64.length * 3 / 4 - placeHolders) - - // if there are placeholders, only get up to the last complete 4 chars - l = placeHolders > 0 ? b64.length - 4 : b64.length - - var L = 0 - - function push (v) { - arr[L++] = v - } - - for (i = 0, j = 0; i < l; i += 4, j += 3) { - tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) - push((tmp & 0xFF0000) >> 16) - push((tmp & 0xFF00) >> 8) - push(tmp & 0xFF) - } - - if (placeHolders === 2) { - tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) - push(tmp & 0xFF) - } else if (placeHolders === 1) { - tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) - push((tmp >> 8) & 0xFF) - push(tmp & 0xFF) - } - - return arr - } - - function uint8ToBase64 (uint8) { - var i, - extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes - output = "", - temp, length - - function encode (num) { - return lookup.charAt(num) - } - - function tripletToBase64 (num) { - return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) - } - - // go through the array every three bytes, we'll deal with trailing stuff later - for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { - temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) - output += tripletToBase64(temp) - } - - // pad the end with zeros, but make sure to not forget the extra bytes - switch (extraBytes) { - case 1: - temp = uint8[uint8.length - 1] - output += encode(temp >> 2) - output += encode((temp << 4) & 0x3F) - output += '==' - break - case 2: - temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) - output += encode(temp >> 10) - output += encode((temp >> 4) & 0x3F) - output += encode((temp << 2) & 0x3F) - output += '=' - break - } - - return output - } - - exports.toByteArray = b64ToByteArray - exports.fromByteArray = uint8ToBase64 - }( false ? (this.base64js = {}) : exports)) + // pad the end with zeros, but make sure to not forget the extra bytes + if (extraBytes === 1) { + tmp = uint8[len - 1] + output += lookup[tmp >> 2] + output += lookup[(tmp << 4) & 0x3F] + output += '==' + } else if (extraBytes === 2) { + tmp = (uint8[len - 2] << 8) + (uint8[len - 1]) + output += lookup[tmp >> 10] + output += lookup[(tmp >> 4) & 0x3F] + output += lookup[(tmp << 2) & 0x3F] + output += '=' + } + + parts.push(output) + + return parts.join('') + } /***/ }, -/* 8 */ +/* 12 */ /***/ function(module, exports) { exports.read = function (buffer, offset, isLE, mLen, nBytes) { @@ -3680,46 +4438,18 @@ /***/ }, -/* 9 */ +/* 13 */ /***/ function(module, exports) { - - /** - * isArray - */ - - var isArray = Array.isArray; - - /** - * toString - */ - - var str = Object.prototype.toString; + var toString = {}.toString; - /** - * Whether or not the given `val` - * is an array. - * - * example: - * - * isArray([]); - * // > true - * isArray(arguments); - * // > false - * isArray(''); - * // > false - * - * @param {mixed} val - * @return {bool} - */ - - module.exports = isArray || function (val) { - return !! val && '[object Array]' == str.call(val); + module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; }; /***/ }, -/* 10 */ +/* 14 */ /***/ function(module, exports, __webpack_require__) { @@ -3734,9 +4464,9 @@ var _ = __webpack_require__(3); - var browser = __webpack_require__(11); - var Histery = __webpack_require__(12); - var expect = __webpack_require__(4) + var browser = __webpack_require__(15); + var Histery = __webpack_require__(16); + var expect = __webpack_require__(8) // Backbone.js Trick for mock the location service @@ -3856,7 +4586,7 @@ /***/ }, -/* 11 */ +/* 15 */ /***/ function(module, exports) { @@ -3867,6 +4597,13 @@ hash: "onhashchange" in win && (!doc.documentMode || doc.documentMode > 7), history: win.history && "onpopstate" in win, location: win.location, + isSameDomain: function(url){ + var matched = url.match(/^.*?:\/\/([^/]*)/); + if(matched){ + return matched[0] == this.location.origin; + } + return true; + }, getHref: function(node){ return "href" in node ? node.getAttribute("href", 2) : node.getAttribute("href"); }, @@ -3882,7 +4619,7 @@ /***/ }, -/* 12 */ +/* 16 */ /***/ function(module, exports, __webpack_require__) { @@ -3890,7 +4627,7 @@ // Thx Backbone.js 1.1.2 and https://github.com/cowboy/jquery-hashchange/blob/master/jquery.ba-hashchange.js // for iframe patches in old ie. - var browser = __webpack_require__(11); + var browser = __webpack_require__(15); var _ = __webpack_require__(3); @@ -4035,7 +4772,8 @@ var target = ev.target || ev.srcElement; if( target.tagName.toLowerCase() !== "a" ) return; - var tmp = (browser.getHref(target)||"").match(self.rPrefix); + var tmp = browser.isSameDomain(target.href)&&(browser.getHref(target)||"").match(self.rPrefix); + var hash = tmp && tmp[1]? tmp[1]: ""; if(!hash) return; @@ -4100,8 +4838,9 @@ module.exports = Histery; + /***/ }, -/* 13 */ +/* 17 */ /***/ function(module, exports, __webpack_require__) { @@ -4114,8 +4853,8 @@ // For all details and documentation: // http://backbonejs.org - var StateMan = __webpack_require__(14); - var expect = __webpack_require__(4) + var StateMan = __webpack_require__(18); + var expect = __webpack_require__(8) var _ = __webpack_require__(3); var doc = typeof document !== "undefined"? document: {}; @@ -5026,471 +5765,533 @@ /***/ }, -/* 14 */ +/* 18 */ /***/ function(module, exports, __webpack_require__) { var State = __webpack_require__(2), - Histery = __webpack_require__(12), - brow = __webpack_require__(11), - _ = __webpack_require__(3), - baseTitle = document.title, - stateFn = State.prototype.state; - - - function StateMan(options){ - - if(this instanceof StateMan === false){ return new StateMan(options)} - options = options || {}; - // if(options.history) this.history = options.history; - - this._states = {}; - this._stashCallback = []; - this.strict = options.strict; - this.current = this.active = this; - this.title = options.title; - this.on("end", function(){ - var cur = this.current,title; - while( cur ){ - title = cur.title; - if(title) break; - cur = cur.parent; - } - document.title = typeof title === "function"? cur.title(): String( title || baseTitle ) ; - }) - + Histery = __webpack_require__(16), + brow = __webpack_require__(15), + _ = __webpack_require__(3), + baseTitle = document.title, + stateFn = State.prototype.state; + + function StateMan(options) + { + if (this instanceof StateMan === false) + { + return new StateMan(options) + } + options = options || {}; + // if(options.history) this.history = options.history; + + this._states = {}; + this._stashCallback = []; + this.strict = options.strict; + this.current = this.active = this; + this.title = options.title; + this.on("end", function () + { + var cur = this.current, title; + while (cur) + { + title = cur.title; + if (title) break; + cur = cur.parent; + } + document.title = typeof title === "function" ? cur.title() : String(title || baseTitle); + }) } - - _.extend( _.emitable( StateMan ), { + _.extend(_.emitable(StateMan), { // keep blank name: '', - state: function(stateName, config){ + state: function (stateName, config) + { - var active = this.active; - if(typeof stateName === "string" && active){ - stateName = stateName.replace("~", active.name) - if(active.parent) stateName = stateName.replace("^", active.parent.name || ""); - } - // ^ represent current.parent - // ~ represent current - // only - return stateFn.apply(this, arguments); + var active = this.active; + if (typeof stateName === "string" && active) + { + stateName = stateName.replace("~", active.name) + if (active.parent) stateName = stateName.replace("^", active.parent.name || ""); + } + // ^ represent current.parent + // ~ represent current + // only + return stateFn.apply(this, arguments); }, - start: function(options){ - - if( !this.history ) this.history = new Histery(options); - if( !this.history.isStart ){ - this.history.on("change", _.bind(this._afterPathChange, this)); - this.history.start(); - } - return this; + start: function (options) + { + + if (!this.history) this.history = new Histery(options); + if (!this.history.isStart) + { + this.history.on("change", _.bind(this._afterPathChange, this)); + this.history.start(); + } + return this; }, - stop: function(){ - this.history.stop(); + stop: function () + { + this.history.stop(); }, // @TODO direct go the point state - go: function(state, option, callback){ - option = option || {}; - if(typeof state === "string") state = this.state(state); + go: function (state, option, callback) + { + option = option || {}; + var statename; + if (typeof state === "string") + { + statename = state; + state = this.state(state); + } - if(!state) return; + if (!state) return this._notfound({state: statename}); - if(typeof option === "function"){ - callback = option; - option = {}; - } + if (typeof option === "function") + { + callback = option; + option = {}; + } - if(option.encode !== false){ - var url = state.encode(option.param) - option.path = url; - this.nav(url, {silent: true, replace: option.replace}); - } + if (option.encode !== false) + { + var url = state.encode(option.param) + option.path = url; + this.nav(url, {silent: true, replace: option.replace}); + } - this._go(state, option, callback); + this._go(state, option, callback); - return this; + return this; }, - nav: function(url, options, callback){ - if(typeof options === "function"){ - callback = options; - options = {}; - } - options = options || {}; + nav: function (url, options, callback) + { + if (typeof options === "function") + { + callback = options; + options = {}; + } + options = options || {}; - options.path = url; + options.path = url; - this.history.nav( url, _.extend({silent: true}, options)); - if(!options.silent) this._afterPathChange( _.cleanPath(url) , options , callback) + this.history.nav(url, _.extend({silent: true}, options)); + if (!options.silent) this._afterPathChange(_.cleanPath(url), options, callback) - return this; + return this; }, - decode: function(path){ - - var pathAndQuery = path.split("?"); - var query = this._findQuery(pathAndQuery[1]); - path = pathAndQuery[0]; - var state = this._findState(this, path); - if(state) _.extend(state.param, query); - return state; + decode: function (path) + { + + var pathAndQuery = path.split("?"); + var query = this._findQuery(pathAndQuery[1]); + path = pathAndQuery[0]; + var state = this._findState(this, path); + if (state) _.extend(state.param, query); + return state; }, - encode: function(stateName, param){ - var state = this.state(stateName); - return state? state.encode(param) : ''; + encode: function (stateName, param) + { + var state = this.state(stateName); + return state ? state.encode(param) : ''; }, // notify specify state // check the active statename whether to match the passed condition (stateName and param) - is: function(stateName, param, isStrict){ - if(!stateName) return false; - var stateName = (stateName.name || stateName); - var current = this.current, currentName = current.name; - var matchPath = isStrict? currentName === stateName : (currentName + ".").indexOf(stateName + ".")===0; - return matchPath && (!param || _.eql(param, this.param)); + is: function (stateName, param, isStrict) + { + if (!stateName) return false; + var stateName = (stateName.name || stateName); + var current = this.current, currentName = current.name; + var matchPath = isStrict ? currentName === stateName : (currentName + ".").indexOf(stateName + ".") === 0; + return matchPath && (!param || _.eql(param, this.param)); }, // after pathchange changed // @TODO: afterPathChange need based on decode - _afterPathChange: function(path, options ,callback){ + _afterPathChange: function (path, options, callback) + { - this.emit("history:change", path); + this.emit("history:change", path); - var found = this.decode(path); + var found = this.decode(path); - options = options || {}; + options = options || {}; - options.path = path; + options.path = path; - if(!found){ - // loc.nav("$default", {silent: true}) - return this._notfound(options); - } + if (!found) + { + // loc.nav("$default", {silent: true}) + return this._notfound(options); + } - options.param = found.param; + options.param = found.param; - this._go( found, options, callback ); + this._go(found, options, callback); }, - _notfound: function(options){ + _notfound: function (options) + { - // var $notfound = this.state("$notfound"); + // var $notfound = this.state("$notfound"); - // if( $notfound ) this._go($notfound, options); + // if( $notfound ) this._go($notfound, options); - return this.emit("notfound", options); + return this.emit("notfound", options); }, // goto the state with some option - _go: function(state, option, callback){ - - var over; - - // if(typeof state === "string") state = this.state(state); - - // if(!state) return _.log("destination is not defined") - - if(state.hasNext && this.strict) return this._notfound({name: state.name}); - - // not touch the end in previous transtion - - // if( this.pending ){ - // var pendingCurrent = this.pending.current; - // this.pending.stop(); - // _.log("naving to [" + pendingCurrent.name + "] will be stoped, trying to ["+state.name+"] now"); - // } - // if(this.active !== this.current){ - // // we need return - // _.log("naving to [" + this.current.name + "] will be stoped, trying to ["+state.name+"] now"); - // this.current = this.active; - // // back to before - // } - option.param = option.param || {}; - - var current = this.current, - baseState = this._findBase(current, state), - prepath = this.path, - self = this; + _go: function (state, option, callback) + { + + var over; + + // if(typeof state === "string") state = this.state(state); + + // if(!state) return _.log("destination is not defined") + + if (state.hasNext && this.strict) return this._notfound({name: state.name}); + + // not touch the end in previous transtion + + // if( this.pending ){ + // var pendingCurrent = this.pending.current; + // this.pending.stop(); + // _.log("naving to [" + pendingCurrent.name + "] will be stoped, trying to ["+state.name+"] now"); + // } + // if(this.active !== this.current){ + // // we need return + // _.log("naving to [" + this.current.name + "] will be stoped, trying to ["+state.name+"] now"); + // this.current = this.active; + // // back to before + // } + option.param = option.param || {}; + + var current = this.current, + baseState = this._findBase(current, state), + prepath = this.path, + self = this; + + + if (typeof callback === "function") this._stashCallback.push(callback); + // if we done the navigating when start + function done(success) + { + over = true; + if (success !== false) self.emit("end"); + self.pending = null; + self._popStash(option); + } + option.previous = current; + option.current = state; - if( typeof callback === "function" ) this._stashCallback.push(callback); - // if we done the navigating when start - function done(success){ - over = true; - if( success !== false ) self.emit("end"); - self.pending = null; - self._popStash(option); - } - - option.previous = current; - option.current = state; + if (current !== state) + { + option.stop = function () + { + done(false); + self.nav(prepath ? prepath : "/", {silent: true}); + } + self.emit("begin", option); - if(current !== state){ - option.stop = function(){ - done(false); - self.nav( prepath? prepath: "/", {silent:true}); } - self.emit("begin", option); - - } - // if we stop it in 'begin' listener - if(over === true) return; + // if we stop it in 'begin' listener + if (over === true) return; - if(current !== state){ - // option as transition object. + if (current !== state) + { + // option as transition object. - option.phase = 'permission'; - this._walk(current, state, option, true , _.bind( function( notRejected ){ + option.phase = 'permission'; + this._walk(current, state, option, true, _.bind(function (notRejected) + { - if( notRejected===false ){ - // if reject in callForPermission, we will return to old - prepath && this.nav( prepath, {silent: true}) + if (notRejected === false) + { + // if reject in callForPermission, we will return to old + prepath && this.nav(prepath, {silent: true}) - done(false, 2) + done(false, 2) - return this.emit('abort', option); + return this.emit('abort', option); - } + } - // stop previous pending. - if(this.pending) this.pending.stop() - this.pending = option; - this.path = option.path; - this.current = option.current; - this.param = option.param; - this.previous = option.previous; - option.phase = 'navigation'; - this._walk(current, state, option, false, _.bind(function( notRejected ){ + // stop previous pending. + if (this.pending) this.pending.stop() + this.pending = option; + this.path = option.path; + this.current = option.current; + this.param = option.param; + this.previous = option.previous; + option.phase = 'navigation'; + this._walk(current, state, option, false, _.bind(function (notRejected) + { + + if (notRejected === false) + { + this.current = this.active; + done(false) + return this.emit('abort', option); + } - if( notRejected === false ){ - this.current = this.active; - done(false) - return this.emit('abort', option); - } + this.active = option.current; - this.active = option.current; + option.phase = 'completion'; + return done() - option.phase = 'completion'; - return done() + }, this)) - }, this) ) + }, this)) - }, this) ) + } else + { + self._checkQueryAndParam(baseState, option); + this.pending = null; + done(); + } - }else{ - self._checkQueryAndParam(baseState, option); - this.pending = null; - done(); - } - }, - _popStash: function(option){ + _popStash: function (option) + { - var stash = this._stashCallback, len = stash.length; + var stash = this._stashCallback, len = stash.length; - this._stashCallback = []; + this._stashCallback = []; - if(!len) return; + if (!len) return; - for(var i = 0; i < len; i++){ - stash[i].call(this, option) - } + for (var i = 0; i < len; i++) + { + stash[i].call(this, option) + } }, // the transition logic Used in Both canLeave canEnter && leave enter LifeCycle - _walk: function(from, to, option, callForPermit , callback){ + _walk: function (from, to, option, callForPermit, callback) + { - // nothing -> app.state - var parent = this._findBase(from , to); + // nothing -> app.state + var parent = this._findBase(from, to); - option.basckward = true; - this._transit( from, parent, option, callForPermit , _.bind( function( notRejected ){ + option.basckward = true; + this._transit(from, parent, option, callForPermit, _.bind(function (notRejected) + { - if( notRejected === false ) return callback( notRejected ); + if (notRejected === false) return callback(notRejected); - // only actual transiton need update base state; - if( !callForPermit ) this._checkQueryAndParam(parent, option) + // only actual transiton need update base state; + if (!callForPermit) this._checkQueryAndParam(parent, option) - option.basckward = false; - this._transit( parent, to, option, callForPermit, callback) + option.basckward = false; + this._transit(parent, to, option, callForPermit, callback) - }, this) ) + }, this)) }, - _transit: function(from, to, option, callForPermit, callback){ - // touch the ending - if( from === to ) return callback(); + _transit: function (from, to, option, callForPermit, callback) + { + // touch the ending + if (from === to) return callback(); - var back = from.name.length > to.name.length; - var method = back? 'leave': 'enter'; - var applied; + var back = from.name.length > to.name.length; + var method = back ? 'leave' : 'enter'; + var applied; - // use canEnter to detect permission - if( callForPermit) method = 'can' + method.replace(/^\w/, function(a){ return a.toUpperCase() }); + // use canEnter to detect permission + if (callForPermit) method = 'can' + method.replace(/^\w/, function (a) + { + return a.toUpperCase() + }); - var loop = _.bind(function( notRejected ){ + var loop = _.bind(function (notRejected) + { - // stop transition or touch the end - if( applied === to || notRejected === false ) return callback(notRejected); + // stop transition or touch the end + if (applied === to || notRejected === false) return callback(notRejected); - if( !applied ) { + if (!applied) + { - applied = back? from : this._computeNext(from, to); + applied = back ? from : this._computeNext(from, to); - }else{ + } else + { - applied = this._computeNext(applied, to); - } + applied = this._computeNext(applied, to); + } - if( (back && applied === to) || !applied )return callback( notRejected ) + if ((back && applied === to) || !applied)return callback(notRejected) - this._moveOn( applied, method, option, loop ); + this._moveOn(applied, method, option, loop); - }, this); + }, this); - loop(); + loop(); }, - _moveOn: function( applied, method, option, callback){ + _moveOn: function (applied, method, option, callback) + { - var isDone = false; - var isPending = false; + var isDone = false; + var isPending = false; - option.async = function(){ + option.async = function () + { - isPending = true; + isPending = true; - return done; - } + return done; + } - function done( notRejected ){ - if( isDone ) return; - isPending = false; - isDone = true; - callback( notRejected ); - } + function done(notRejected) + { + if (isDone) return; + isPending = false; + isDone = true; + callback(notRejected); + } - - option.stop = function(){ - done( false ); - } + option.stop = function () + { + done(false); + } - this.active = applied; - var retValue = applied[method]? applied[method]( option ): true; + this.active = applied; + var retValue = applied[method] ? applied[method](option) : true; - if(method === 'enter') applied.visited = true; - // promise - // need breadk , if we call option.stop first; + if (method === 'enter') applied.visited = true; + // promise + // need breadk , if we call option.stop first; - if( _.isPromise(retValue) ){ + if (_.isPromise(retValue)) + { - return this._wrapPromise(retValue, done); + return this._wrapPromise(retValue, done); - } + } - // if haven't call option.async yet - if( !isPending ) done( retValue ) + // if haven't call option.async yet + if (!isPending) done(retValue) }, + _wrapPromise: function (promise, next) + { - _wrapPromise: function( promise, next ){ - - return promise.then( next, function(){next(false)}) ; + return promise.then(next, function () + { + next(false) + }); }, - _computeNext: function( from, to ){ + _computeNext: function (from, to) + { - var fname = from.name; - var tname = to.name; + var fname = from.name; + var tname = to.name; - var tsplit = tname.split('.') - var fsplit = fname.split('.') + var tsplit = tname.split('.') + var fsplit = fname.split('.') - var tlen = tsplit.length; - var flen = fsplit.length; + var tlen = tsplit.length; + var flen = fsplit.length; - if(fname === '') flen = 0; - if(tname === '') tlen = 0; + if (fname === '') flen = 0; + if (tname === '') tlen = 0; - if( flen < tlen ){ - fsplit[flen] = tsplit[flen]; - }else{ - fsplit.pop(); - } + if (flen < tlen) + { + fsplit[flen] = tsplit[flen]; + } else + { + fsplit.pop(); + } - return this.state(fsplit.join('.')) + return this.state(fsplit.join('.')) }, - _findQuery: function(querystr){ - - var queries = querystr && querystr.split("&"), query= {}; - if(queries){ - var len = queries.length; - var query = {}; - for(var i =0; i< len; i++){ - var tmp = queries[i].split("="); - query[tmp[0]] = tmp[1]; + _findQuery: function (querystr) + { + + var queries = querystr && querystr.split("&"), query = {}; + if (queries) + { + var len = queries.length; + var query = {}; + for (var i = 0; i < len; i++) + { + var tmp = queries[i].split("="); + query[tmp[0]] = tmp[1]; + } } - } - return query; + return query; }, - _findState: function(state, path){ - var states = state._states, found, param; - - // leaf-state has the high priority upon branch-state - if(state.hasNext){ - for(var i in states) if(states.hasOwnProperty(i)){ - found = this._findState( states[i], path ); - if( found ) return found; + _findState: function (state, path) + { + var states = state._states, found, param; + + // leaf-state has the high priority upon branch-state + if (state.hasNext) + { + for (var i in states) if (states.hasOwnProperty(i)) + { + found = this._findState(states[i], path); + if (found) return found; + } + } + // in strict mode only leaf can be touched + // if all children is don. will try it self + param = state.regexp && state.decode(path); + if (param) + { + state.param = param; + return state; + } else + { + return false; } - } - // in strict mode only leaf can be touched - // if all children is don. will try it self - param = state.regexp && state.decode(path); - if(param){ - state.param = param; - return state; - }else{ - return false; - } }, // find the same branch; - _findBase: function(now, before){ - - if(!now || !before || now == this || before == this) return this; - var np = now, bp = before, tmp; - while(np && bp){ - tmp = bp; - while(tmp){ - if(np === tmp) return tmp; - tmp = tmp.parent; + _findBase: function (now, before) + { + + if (!now || !before || now == this || before == this) return this; + var np = now, bp = before, tmp; + while (np && bp) + { + tmp = bp; + while (tmp) + { + if (np === tmp) return tmp; + tmp = tmp.parent; + } + np = np.parent; } - np = np.parent; - } }, // check the query and Param - _checkQueryAndParam: function(baseState, options){ - - var from = baseState; - while( from !== this ){ - from.update && from.update(options); - from = from.parent; - } - + _checkQueryAndParam: function (baseState, options) + { + var from = baseState; + while (from !== this) + { + from.update && from.update(options); + from = from.parent; + } } - - }, true) - - + }, true); module.exports = StateMan; diff --git a/typings/stateman.d.ts b/typings/stateman.d.ts new file mode 100644 index 0000000..ca0429f --- /dev/null +++ b/typings/stateman.d.ts @@ -0,0 +1,350 @@ +/** + * Created by slanska on 2016-09-16. + */ + +declare module stateman +{ + /* + Document title. String or function that returns a string. + */ + export type StringResult = string | (()=>string); + + /* + Parameter for Stateman constructor + */ + export interface StatemanCtorOptions + { + /* + Default: false. whether only the leaf state can be visited + */ + strict?:boolean; + + /* + document.title + */ + title?:StringResult; + } + + export interface Promise + { + then:()=>Promise; + "catch":()=>Promise; + "finally":()=>Promise; + } + + /* + Parameter for callbacks (enter, leave, canEnter etc.) + */ + export interface RouteInfo + { + /* + represent which phase the navigation is + */ + phase:'permission'|'navigation'|'completion'; + + /* + captured param + */ + param:Object; + + /* + previous state + */ + previous:State; + + /* + target state + */ + current:State; + + /* + fallback for async navigating. + If you must run application in the runtime that doesn’t support Promise (old IE without Promise polyfill), + you can use option.async for asynchronous navigation. + Returns function which accepts boolean as a parameter. + Example: + var f = async(); + ... + f(false); // to reject + */ + async:()=>(result?:boolean)=>void; + + /* + function used to stop the navigating + */ + stop:Function; + + /* + TODO Typo: should be backward + */ + basckward:boolean; + } + + export type StateOptionHandler = (option:RouteInfo)=> (void | boolean | Promise); + + export interface StateConfigObject + { + enter?:StateOptionHandler; + leave?:StateOptionHandler; + update?:StateOptionHandler; + canEnter?:StateOptionHandler; + canLeave?:StateOptionHandler; + url?:string; + + /* + if title is a Function, document.title will use its returnValue + */ + title?:StringResult; + } + + export type StateConfig = StateConfigObject | StateOptionHandler | string; + + export interface StatemanStartOptions + { + /* + (default false) whether to open the html5 history support. + If html5=true (need html5 pushState support), but browser doesn’t support this feature. + stateman will fallback to hash-based routing. + */ + html5?:boolean; + + /* + (default ‘/’) the root of the url , only required when html5 is actived. default is '/' + */ + root?:string; + + /* + for the hash prefix , default is ‘’ (you can pass ! to make the hash like + #!/contact/100), works in hash mode. + */ + prefix?:string; + + /* + (default true) whether to delegate all link(a[href])’s navigating, + only needed when html5 is supported, default is true. + */ + autolink?:boolean; + } + + /* + Options for navigation (Stateman.nav and Stateman.go) + */ + export interface StatemanNavOptions + { + /* + if silent is true, only the location is change in browser, + but will not trigger the stateman’s navigating process + */ + silent?:boolean; + + /* + if replace is true. the previous path in history will be replace by url + ( means you can’t back to or go to the previous path) + */ + replace?:boolean; + + /* + default is true. if encode is false, url will not change at location, only state is change (means will trigger + the stateman’s navigating process). stateman use the encode method to compute the real url. + */ + encode?:boolean; + + /* + the big difference between nav and go is param: + 'go' may need param to compute the real url, and place it in location. + */ + param?:Object; + } + + /* + Individual state of routing + */ + export class State + { + constructor(options?); + + visited:boolean; + + state(stateName:string, config:StateConfigObject):State; + state(stateName:string):State; + state(stateName:string, enter:StateOptionHandler):State; + + /* + Adds child state + + Url rules: + Absolute url - if you dont need the url that defined in parents, use a prefix ^ to make it absolute . all children of the state will also be affect + Empty url - if you pass url:"", the captured_url will be the same as its parent. (but it have higher priority than parent) + */ + state(stateName:string, url:string):State; + + config(configure); + + decode(path:string):{name:string, param:Object}; + + encode(stateName:string, param?:Object):string; + + manager:Stateman; + parent:State | Stateman; + name:string; + currentName:string; + } + + /* + Main class. Manager for all states and routing. + Typical usage: + var StateMan = require("stateman"); + + var stateman = new StateMan({ + title: "Application", + strict: true + }); + // or... + var stateman = StateMan(); + */ + class Stateman // extends State + { + static State:{ + new (options?):State; + }; + + static util:{ + bind(fn, context); + cleanPath(path); + emitable(obj):boolean; + eql(o1, o2):boolean; + extend(o1, o2, override:boolean); + isPromise(obj):boolean; + log(msg, type); + normalize(path); + slice(arr, index); + typeOf(obj); + }; + + /* + TODO Typo: Should be History + */ + static Histery:{ + + }; + + /* + Creates a new instance of stateman + */ + constructor(options ?:StatemanCtorOptions); + + /* + to add/update a state or get particular state(if param config is undefined). + Example: + stateman + .state("app", { + enter: function(){ + console.log("enter the state: app") + }, + leave: function(){ + console.log("leave the state: app") + } + }) + // is equals to {enter: config} + .state("app.contact", function(){ + console.log("enter the app.contact state") + }) + + // pass in a Object for multiple operation + stateman.state({ + "demo.detail": function(){ + console.log("enter the demo.detail state") + }, + "demo.list": { + enter: function(){} + leave: function(){} + } + }) + */ + state(states:{ + [stateName:string + ]:StateConfig + }):Stateman; + state(stateName:string, config:StateConfig):Stateman; + state(stateName:string):State; + + decode(path:string):{ + name:string, param:Object + } + ; + + encode(stateName:string, param ?:Object):string; + + async(); + + start(options ?:StatemanStartOptions):Stateman; + + stop(); + + /* + Recommended (vs 'nav') + */ + go(stateName:string, option ?:StatemanNavOptions, callback ?):Stateman; + + nav(url:string, options ?:StatemanNavOptions, callback ?) + ; + + /* + determine if the current state is equal to or is the child of the state. + If any params are passed then they will be tested for a match as well. + not all the parameters need to be passed, just the ones you’d like to test for equality. + */ + is(stateName:string, + param ?:Object, + /* + Whether the target state need strict equals to current state. + */ + isStrict ?:boolean):boolean; + + /* + Register event handler + Events can be standard or custom. + Standard events include: + 'begin' - Emitted when a navigating is start. every listener got a special param : evt. + 'end' - Emitted when a navigating is end. + 'notfound' - Emitted when target state is not founded. + you can register a 'notfound' listener to redirect the page to default state + (stateman.go) + */ + on(event:string, handle:(state:State)=>void):Stateman; + + off(event:string, handle:(state:State)=>void):Stateman; + + emit(event:string, param):Stateman; + + /* + The active state, represent the state that still in pending. + */ + active:State; + + /* + The previous state. the same as option.previous + */ + previous:State; + + /* + The target state. the same as option.current + */ + current:State; + + /* + The current param captured from url + or be passed to the method stateman.go or stateman.nav + */ + param:Object; + } + + class St extends Stateman.State + { + } +} + +declare module "stateman" +{ + export = stateman.Stateman; +}