diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..99e3a38 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..4be6d02 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,17 @@ +{ + "extends": [ + "eslint:recommended" + ], + "env": { + "node": true, + "browser": true, + "es6": true, + "jquery": true, + "mocha": true, + "amd": true + }, + "rules": { + "quotes": [2, "double"], + "indent": [2, 3] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21bc9b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +bower_components +.tmp +.sass-cache +.svn diff --git a/.ibn-battutarc b/.ibn-battutarc new file mode 100644 index 0000000..3cfd956 --- /dev/null +++ b/.ibn-battutarc @@ -0,0 +1,16 @@ +{ + "src": "src", + "test": "test", + "dist": "dist", + "temp": ".tmp", + "browserSync": { + "testPort": 9998, + "testHost": "localhost" + }, + "optimize": { + "js": { + "concat": true, + "minify": true + } + } +} diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..7d3c1f4 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,273 @@ +/** + * @description The Gruntfile.js - build file. Tragets are: + * - grunt test + * - grunt package // package for production + * - options --concat, --minify, --no-concat, --no-minify + * - grunt // test, package + * + * @author Fahim Farook + * + */ +module.exports = function(grunt) { + + "use strict"; + + // display the elapsed execution time of grunt tasks + require("time-grunt")(grunt); + + // load grunt plugins automatically by looking at task names + // so no need explicit loading. i.e. grunt.loadNpmTasks('grunt-contrib-clean'); + // task -- plugin matching rules + // - -> + // - -> grunt- + // - -> grunt-contrib- + require("jit-grunt")(grunt, { + // for tasks whose name doesn't match the plugin name, load it explicitily. + useminPrepare: "grunt-usemin" + }); + + // build properties + // build properties + var config = (function() { + var build = grunt.file.readJSON(".ibn-battutarc"); + if (!build) { + grunt.fail.fatal([".ibn-battutarc doesnt exists"]); + } + + // optimize + var defaults = { + js: { + minify: true, + concat: true + } + } + if (!build.optimize) { + grunt.log.error([".ibn-battutarc doesnt define optimize. Commnad line options or defaults will be used."]); + build.optimize = defaults; + } else { + for (var prop in defaults) { + if (!build.optimize[prop]) { + build.optimize[prop] = {}; + } + } + } + + // dirs + if (!(build.src && build.test && build.dist && build.temp)) { + grunt.fail.fatal([".ibn-battutarc doesnt define mandatory directories: src, test, dist, temp"]); + } + + // .bowerrc + var bower = grunt.file.readJSON(".bowerrc"); + if (bower) { + build.bower = bower; + } else { + build.bower = { + directory: "bower_components" + }; + } + // bower.json + bower = grunt.file.readJSON("bower.json"); + if (!bower || !bower.main) { + grunt.fail.fatal(["bower.json or main section in bower.json is missing. bower.json=" + bower]); + } + for (prop in bower) { + if (bower.hasOwnProperty(prop)) { + build.bower[prop] = bower[prop]; + } + } + + // routes + var routes = {}; + routes["/" + build.bower.directory] = "./" + build.bower.directory; + build.routes = routes; + + return build; + }()); + + // configuring grunt... + grunt.initConfig({ + + // settings + config: config, + + // delete files/ directories + clean: { + dist: { + files: [{ + dot: true, // .tmp + src: [ + "<%= config.dist %>", + "<%= config.temp %>" + ] + }] + }, + temp: ["<%= config.temp %>"] + }, + + // copy resources to dist or tmp + copy: { + // copy js files + // from: src, to: temp + // to allow minification (without requirejs) + js_src_temp: { + expand: true, + dot: true, + cwd: "<%= config.src %>", + src: "**/*.js", + dest: "<%= config.temp %>" + }, + // copy js files + // from: temp, to: dist + js_temp_dist: { + expand: true, + dot: true, + cwd: "<%= config.temp %>", + src: "**/*.js", + dest: "<%= config.dist %>" + } + }, + + concat: { + dist: { + src: ["<%= config.src %>/**/*.js"], + dest: "<%= config.temp %>/<%= config.bower.main %>" + } + }, + + // minify js files + uglify: { + target: { + options: { + mangle: false + }, + files: [{ + expand: true, + cwd: "<%= config.temp %>", + src: ["**/*.js", "!**/*.min.css"], + dest: "<%= config.dist %>" + }] + } + }, + + // run predefined tasks whenever watched file patterns are added, changed or deleted. + watch: { + // by default, if Gruntfile.js is being watched, then changes to it will trigger the watch task to restart + gruntfile: { + files: ["Gruntfile.js"] + }, + scripts: { + files: ["<%= config.src %>/**/*.js"], + tasks: ["eslint"] + }, + testScripts: { + files: ["<%= config.test %>/**/*.js"], + tasks: ["test"] // registered test task - basically run mocha + } + }, + + // start browserSync web server - for unit testing only + // https://github.com/Browsersync/recipes + // http://www.browsersync.io/docs/options/ + browserSync: { + options: { + notify: false, // don't show any ui notifications in the browser. + background: true + }, + fromTest: { + options: { + port: config.browserSync.testPort, + host: config.browserSync.testHost, + open: false, + server: { + baseDir: [config.temp, config.test, config.src], + routes: config.routes // The key - url to match : value - which folder to serve (relative to current directory) + } + } + } + }, + + // validate javascript + eslint: { + options: { + configFile: ".eslintrc" // where to find eslint configurations + }, + target: [ + "Gruntfile.js", + "<%= config.src %>/**/*.js", + "<%= config.test %>/**/*.js", + "!**/<%= config.bower.directory %>/**/*.js" + ] + }, + + // mocha unit test configurations + mocha: { + task: { + options: { + run: true, // inject mocha run code to html. i.e. mocha.run(); into following url page + // urls to test via browserSync server + urls: ["http://<%= browserSync.fromTest.options.host %>:<%= browserSync.fromTest.options.port %>/index.html"] + }, + dest: "<%= config.temp %>/mocha.out" // test report + } + } + }); + + // usage: grunt test + grunt.registerTask("test", function() { + grunt.task.run([ + "clean:temp", + "browserSync:fromTest", + "mocha" + ]); + }); + + // usage + // grunt package - package for production. Concat and minify based on settings provided in .zahrawirc + // grunt package --concat - Overrides settings provided in .zahrawirc + // grunt package --minify - Overrides settings provided in .zahrawirc + // grunt package --no-concat - Overrides settings provided in .zahrawirc + // grunt package --no-minify - Overrides settings provided in .zahrawirc + grunt.registerTask("package", function() { + // do configs + var options = [ + "concat", + "minify", + "no-concat", + "no-minify" + ]; + + for (var i = 0, len = options.length; i < len; i++) { + if (grunt.option(options[i])) { + var opts = options[i].split("-"); + + var val = opts[0] === "no" ? false : true; + var action = opts[opts.length - 1]; // concat or minify + config.optimize.js[action] = val; + } + } + + var tasks = ["clean"]; // both .tmp and dist + + if (config.optimize.js.concat) { + tasks.push("concat"); + } else { + tasks.push("copy:js_src_temp"); // 2.a + } + + if (config.optimize.js.minify) { // minify + tasks.push("uglify"); + } else { + tasks.push("copy:js_temp_dist"); + } + + grunt.task.run(tasks); + }); + + // usage: grunt + grunt.registerTask("default", [ + "newer:eslint", + "test", + "package" + ]); +}; diff --git a/README.markdown b/README.markdown index 9d00af4..db0735e 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,8 @@ -# PathJS # +# Forked from PathJS # -PathJS is a lightweight, client-side routing library that allows you to create "single page" applications using Hashbangs and/or HTML5 pushState. +PathJS is a lightweight, client-side routing library that allows you to create "single page" applications using Hashbangs and/or HTML5 pushState. + +Even though PathJS available on bower, it does not have proper bower.json with main:{} section. Also the bower.json files are missing on git repository. Ibn-Battuta is the restructured version of Path JS with proper bower configurations. # Features # * Lightweight diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..b9861a9 --- /dev/null +++ b/bower.json @@ -0,0 +1,24 @@ +{ + "name": "ibn-battuta", + "description": "Bower friendly routing libraray forked from PathJS", + "main": "dist/ibn-battuta.min.js", + "authors": [ + "Fahim Farook" + ], + "license": "ISC", + "keywords": [ + "router", + "pathjs" + ], + "moduleType": [ + "globals" + ], + "homepage": "https://github.com/fahimfarookme/ibn-battuta#readme", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/dist/ibn-battuta.min.js b/dist/ibn-battuta.min.js new file mode 100644 index 0000000..b8946b9 --- /dev/null +++ b/dist/ibn-battuta.min.js @@ -0,0 +1 @@ +var Path={version:"0.8.4",map:function(path){return Path.routes.defined.hasOwnProperty(path)?Path.routes.defined[path]:new Path.core.route(path)},root:function(path){Path.routes.root=path},rescue:function(fn){Path.routes.rescue=fn},history:{initial:{},pushState:function(state,title,path){Path.history.supported?Path.dispatch(path)&&history.pushState(state,title,path):Path.history.fallback&&(window.location.hash="#"+path)},popState:function(event){var initialPop=!Path.history.initial.popped&&location.href==Path.history.initial.URL;Path.history.initial.popped=!0,initialPop||Path.dispatch(document.location.pathname)},listen:function(fallback){if(Path.history.supported=!(!window.history||!window.history.pushState),Path.history.fallback=fallback,Path.history.supported)Path.history.initial.popped="state"in window.history,Path.history.initial.URL=location.href,window.onpopstate=Path.history.popState;else if(Path.history.fallback){for(route in Path.routes.defined)"#"!=route.charAt(0)&&(Path.routes.defined["#"+route]=Path.routes.defined[route],Path.routes.defined["#"+route].path="#"+route);Path.listen()}}},match:function(path,parameterize){var possible_routes,slice,i,j,compare,params={},route=null;for(route in Path.routes.defined)if(null!==route&&void 0!==route)for(route=Path.routes.defined[route],possible_routes=route.partition(),j=0;j0)for(i=0;i=8)?window.onhashchange=fn:setInterval(fn,50),""!==location.hash&&Path.dispatch(location.hash)},core:{route:function(path){this.path=path,this.action=null,this.do_enter=[],this.do_exit=null,this.params={},Path.routes.defined[path]=this}},routes:{current:null,root:null,rescue:null,previous:null,defined:{}}};Path.core.route.prototype={to:function(fn){return this.action=fn,this},enter:function(fns){return fns instanceof Array?this.do_enter=this.do_enter.concat(fns):this.do_enter.push(fns),this},exit:function(fn){return this.do_exit=fn,this},partition:function(){for(var text,i,parts=[],options=[],re=/\(([^}]+?)\)/g;text=re.exec(this.path);)parts.push(text[1]);for(options.push(this.path.split("(")[0]),i=0;i0)for(i=0;i 0) { - for (i = 0; i < slice.split("/").length; i++) { - if ((i < compare.split("/").length) && (slice.split("/")[i].charAt(0) === ":")) { - params[slice.split('/')[i].replace(/:/, '')] = compare.split("/")[i]; - compare = compare.replace(compare.split("/")[i], slice.split("/")[i]); - } - } - } - if (slice === compare) { - if (parameterize) { - route.params = params; - } - return route; - } - } - } - } - return null; - }, - 'dispatch': function (passed_route) { - var previous_route, matched_route; - if (Path.routes.current !== passed_route) { - Path.routes.previous = Path.routes.current; - Path.routes.current = passed_route; - matched_route = Path.match(passed_route, true); - - if (Path.routes.previous) { - previous_route = Path.match(Path.routes.previous); - if (previous_route !== null && previous_route.do_exit !== null) { - previous_route.do_exit(); - } - } - - if (matched_route !== null) { - matched_route.run(); - return true; - } else { - if (Path.routes.rescue !== null) { - Path.routes.rescue(); - } - } - } - }, - 'listen': function () { - var fn = function(){ Path.dispatch(location.hash); } - - if (location.hash === "") { - if (Path.routes.root !== null) { - location.hash = Path.routes.root; - } - } - - // The 'document.documentMode' checks below ensure that PathJS fires the right events - // even in IE "Quirks Mode". - if ("onhashchange" in window && (!document.documentMode || document.documentMode >= 8)) { - window.onhashchange = fn; - } else { - setInterval(fn, 50); - } - - if(location.hash !== "") { - Path.dispatch(location.hash); - } - }, - 'core': { - 'route': function (path) { - this.path = path; - this.action = null; - this.do_enter = []; - this.do_exit = null; - this.params = {}; - Path.routes.defined[path] = this; - } - }, - 'routes': { - 'current': null, - 'root': null, - 'rescue': null, - 'previous': null, - 'defined': {} - } -}; -Path.core.route.prototype = { - 'to': function (fn) { - this.action = fn; - return this; - }, - 'enter': function (fns) { - if (fns instanceof Array) { - this.do_enter = this.do_enter.concat(fns); - } else { - this.do_enter.push(fns); - } - return this; - }, - 'exit': function (fn) { - this.do_exit = fn; - return this; - }, - 'partition': function () { - var parts = [], options = [], re = /\(([^}]+?)\)/g, text, i; - while (text = re.exec(this.path)) { - parts.push(text[1]); - } - options.push(this.path.split("(")[0]); - for (i = 0; i < parts.length; i++) { - options.push(options[options.length - 1] + parts[i]); - } - return options; - }, - 'run': function () { - var halt_execution = false, i, result, previous; - - if (Path.routes.defined[this.path].hasOwnProperty("do_enter")) { - if (Path.routes.defined[this.path].do_enter.length > 0) { - for (i = 0; i < Path.routes.defined[this.path].do_enter.length; i++) { - result = Path.routes.defined[this.path].do_enter[i].apply(this, null); - if (result === false) { - halt_execution = true; - break; - } - } - } - } - if (!halt_execution) { - Path.routes.defined[this.path].action(); - } - } -}; \ No newline at end of file diff --git a/path.min.js b/path.min.js deleted file mode 100644 index f44e647..0000000 --- a/path.min.js +++ /dev/null @@ -1 +0,0 @@ -var Path={version:"0.8.4",map:function(a){if(Path.routes.defined.hasOwnProperty(a)){return Path.routes.defined[a]}else{return new Path.core.route(a)}},root:function(a){Path.routes.root=a},rescue:function(a){Path.routes.rescue=a},history:{initial:{},pushState:function(a,b,c){if(Path.history.supported){if(Path.dispatch(c)){history.pushState(a,b,c)}}else{if(Path.history.fallback){window.location.hash="#"+c}}},popState:function(a){var b=!Path.history.initial.popped&&location.href==Path.history.initial.URL;Path.history.initial.popped=true;if(b)return;Path.dispatch(document.location.pathname)},listen:function(a){Path.history.supported=!!(window.history&&window.history.pushState);Path.history.fallback=a;if(Path.history.supported){Path.history.initial.popped="state"in window.history,Path.history.initial.URL=location.href;window.onpopstate=Path.history.popState}else{if(Path.history.fallback){for(route in Path.routes.defined){if(route.charAt(0)!="#"){Path.routes.defined["#"+route]=Path.routes.defined[route];Path.routes.defined["#"+route].path="#"+route}}Path.listen()}}}},match:function(a,b){var c={},d=null,e,f,g,h,i;for(d in Path.routes.defined){if(d!==null&&d!==undefined){d=Path.routes.defined[d];e=d.partition();for(h=0;h0){for(g=0;g=8)){window.onhashchange=a}else{setInterval(a,50)}if(location.hash!==""){Path.dispatch(location.hash)}},core:{route:function(a){this.path=a;this.action=null;this.do_enter=[];this.do_exit=null;this.params={};Path.routes.defined[a]=this}},routes:{current:null,root:null,rescue:null,previous:null,defined:{}}};Path.core.route.prototype={to:function(a){this.action=a;return this},enter:function(a){if(a instanceof Array){this.do_enter=this.do_enter.concat(a)}else{this.do_enter.push(a)}return this},exit:function(a){this.do_exit=a;return this},partition:function(){var a=[],b=[],c=/\(([^}]+?)\)/g,d,e;while(d=c.exec(this.path)){a.push(d[1])}b.push(this.path.split("(")[0]);for(e=0;e0){for(b=0;b 0) { + for (i = 0; i < slice.split("/").length; i++) { + if ((i < compare.split("/").length) && (slice.split("/")[i].charAt(0) === ":")) { + params[slice.split("/")[i].replace(/:/, "")] = compare.split("/")[i]; + compare = compare.replace(compare.split("/")[i], slice.split("/")[i]); + } + } + } + if (slice === compare) { + if (parameterize) { + route.params = params; + } + return route; + } + } + } + } + return null; + }, + "dispatch": function(passed_route) { + var previous_route, matched_route; + if (Path.routes.current !== passed_route) { + Path.routes.previous = Path.routes.current; + Path.routes.current = passed_route; + matched_route = Path.match(passed_route, true); + + if (Path.routes.previous) { + previous_route = Path.match(Path.routes.previous); + if (previous_route !== null && previous_route.do_exit !== null) { + previous_route.do_exit(); + } + } + + if (matched_route !== null) { + matched_route.run(); + return true; + } else { + if (Path.routes.rescue !== null) { + Path.routes.rescue(); + } + } + } + }, + "listen": function() { + var fn = function() { + Path.dispatch(location.hash); + } + + if (location.hash === "") { + if (Path.routes.root !== null) { + location.hash = Path.routes.root; + } + } + + // The "document.documentMode" checks below ensure that PathJS fires the right events + // even in IE "Quirks Mode". + if ("onhashchange" in window && (!document.documentMode || document.documentMode >= 8)) { + window.onhashchange = fn; + } else { + setInterval(fn, 50); + } + + if (location.hash !== "") { + Path.dispatch(location.hash); + } + }, + "core": { + "route": function(path) { + this.path = path; + this.action = null; + this.do_enter = []; + this.do_exit = null; + this.params = {}; + Path.routes.defined[path] = this; + } + }, + "routes": { + "current": null, + "root": null, + "rescue": null, + "previous": null, + "defined": {} + } +}; +Path.core.route.prototype = { + "to": function(fn) { + this.action = fn; + return this; + }, + "enter": function(fns) { + if (fns instanceof Array) { + this.do_enter = this.do_enter.concat(fns); + } else { + this.do_enter.push(fns); + } + return this; + }, + "exit": function(fn) { + this.do_exit = fn; + return this; + }, + "partition": function() { + var parts = [], + options = [], + re = /\(([^}]+?)\)/g, + text, i; + while (text = re.exec(this.path)) { + parts.push(text[1]); + } + options.push(this.path.split("(")[0]); + for (i = 0; i < parts.length; i++) { + options.push(options[options.length - 1] + parts[i]); + } + return options; + }, + "run": function() { + var halt_execution = false, + i, result, previous; + + if (Path.routes.defined[this.path].hasOwnProperty("do_enter")) { + if (Path.routes.defined[this.path].do_enter.length > 0) { + for (i = 0; i < Path.routes.defined[this.path].do_enter.length; i++) { + result = Path.routes.defined[this.path].do_enter[i].apply(this, null); + if (result === false) { + halt_execution = true; + break; + } + } + } + } + if (!halt_execution) { + Path.routes.defined[this.path].action(); + } + } +}; diff --git a/test/.bowerrc b/test/.bowerrc new file mode 100644 index 0000000..d5e4114 --- /dev/null +++ b/test/.bowerrc @@ -0,0 +1,4 @@ +{ + "directory": "bower_components", + "registry": "http://localhost:5678" +} diff --git a/test/bower.json b/test/bower.json new file mode 100644 index 0000000..824e6f3 --- /dev/null +++ b/test/bower.json @@ -0,0 +1,9 @@ +{ + "name": "zahrawi", + "private": true, + "dependencies": { + "chai": "~1.8.0", + "mocha": "~1.14.0" + }, + "devDependencies": {} +} diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..c5390f8 --- /dev/null +++ b/test/index.html @@ -0,0 +1,36 @@ + + + + + + Mocha Spec Runner + + + + +
+ + + + + + + + + + + + + + + + + diff --git a/tests/path.js.test.html b/test/path.js.test.html similarity index 100% rename from tests/path.js.test.html rename to test/path.js.test.html diff --git a/tests/path.min.js.test.html b/test/path.min.js.test.html similarity index 100% rename from tests/path.min.js.test.html rename to test/path.min.js.test.html diff --git a/tests/pushstate/path.js.test.html b/test/pushstate/path.js.test.html similarity index 100% rename from tests/pushstate/path.js.test.html rename to test/pushstate/path.js.test.html diff --git a/tests/pushstate/path.min.js.test.html b/test/pushstate/path.min.js.test.html similarity index 100% rename from tests/pushstate/path.min.js.test.html rename to test/pushstate/path.min.js.test.html diff --git a/test/spec/test.js b/test/spec/test.js new file mode 100644 index 0000000..f148eb5 --- /dev/null +++ b/test/spec/test.js @@ -0,0 +1,13 @@ +/* global describe, it */ + +(function() { + "use strict"; + + describe("Give it some context", function() { + describe("maybe a bit more context here", function() { + it("should run here few assertions", function() { + + }); + }); + }); +})();