├── .github └── FUNDING.yml ├── jrouting.jcomponent.js ├── jrouting.jcomponent.min.js ├── jrouting.js ├── jrouting.min.js ├── license.txt ├── minify.json ├── minify.sh └── readme.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: totaljs 4 | open_collective: totalplatform 5 | ko_fi: totaljs 6 | liberapay: totaljs 7 | buy_me_a_coffee: totaljs 8 | custom: https://www.totaljs.com/support/ 9 | -------------------------------------------------------------------------------- /jrouting.jcomponent.js: -------------------------------------------------------------------------------- 1 | (function(W) { 2 | 3 | var NAME_NAV = 'NAV.'; 4 | 5 | var jR = { 6 | version: 16.3, 7 | hashtags: false, 8 | middlewares: {}, 9 | params: [], 10 | query: {}, 11 | query2: {}, 12 | routes: [], 13 | url: '', 14 | count: 0 15 | }; 16 | 17 | var Internal = { 18 | $prev: [], 19 | $next: [], 20 | isReady: false, 21 | LIMIT_HISTORY: 100, 22 | LIMIT_HISTORY_ERROR: 100 23 | }; 24 | 25 | jR.history = Internal.$prev; 26 | 27 | var LOC = location; 28 | !W.NAV && (W.NAV = jR); 29 | 30 | jR.custom = function(expire) { 31 | jR.$custom = true; 32 | if (expire == null || expire == true || typeof(expire) === 'string') 33 | CACHEPATH('NAV.href', expire == null || expire == true ? '1 month' : expire); 34 | }; 35 | 36 | jR.remember = function(name, value) { 37 | if (value === null) 38 | delete jR.query2[name]; 39 | else 40 | jR.query2[name] = value == undefined ? jR.query[name] : value; 41 | }; 42 | 43 | jR.remove = function(url) { 44 | url = url.env(true).ROOT(true); 45 | var routes = []; 46 | for (var i = 0; i < jR.routes.length; i++) 47 | jR.routes[i].id !== url && routes.push(jR.routes[i]); 48 | jR.routes = routes; 49 | return jR; 50 | }; 51 | 52 | jR.autosave = function() { 53 | jR.$save = 1; 54 | }; 55 | 56 | jR.save = function() { 57 | try { 58 | localStorage.setItem(DEF.localstorage + '.nav', STRINGIFY({ prev: Internal.$prev, next: Internal.$next, ts: new Date() })); 59 | } catch (e) {} 60 | }; 61 | 62 | jR.load = function() { 63 | try { 64 | var tmp = PARSE(localStorage.getItem(DEF.localstorage + '.nav') || 'null'); 65 | if (tmp) { 66 | if (tmp.prev instanceof Array) 67 | Internal.$prev = jR.history = tmp.prev; 68 | if (tmp.next instanceof Array) 69 | Internal.$next = tmp.next; 70 | jR.$pop = true; 71 | } 72 | } catch(e) {} 73 | }; 74 | 75 | W.ROUTE = function(url, fn, middleware, init) { 76 | 77 | var tmp; 78 | 79 | if (fn instanceof Array) { 80 | var tmp = middleware; 81 | middleware = fn; 82 | fn = tmp; 83 | } 84 | 85 | if (typeof(middleware) === 'function') { 86 | tmp = init; 87 | init = middleware; 88 | middleware = tmp; 89 | } 90 | 91 | url = url.env(true).ROOT(true); 92 | 93 | var priority = url.jRcount('/') + (url.indexOf('*') === -1 ? 0 : 10); 94 | var route = jR._route(url.trim()); 95 | var params = []; 96 | 97 | if (typeof(middleware) === 'string') 98 | middleware = middleware.split(',').trim(); 99 | 100 | var mid = []; 101 | var roles = []; 102 | var options = {}; 103 | 104 | (middleware instanceof Array) && middleware.forEach(function(item) { 105 | if (typeof(item) === 'object') 106 | options = item; 107 | else if (item.substring(0, 1) === '@') 108 | roles.push(item.substring(1)); 109 | else 110 | mid.push(item); 111 | }); 112 | 113 | if (url.indexOf('{') !== -1) { 114 | priority -= 100; 115 | for (var i = 0; i < route.length; i++) 116 | route[i].substring(0, 1) === '{' && params.push(i); 117 | priority -= params.length; 118 | } 119 | 120 | jR.remove(url); 121 | jR.routes.push({ id: url, url: route, fn: fn, priority: priority, params: params, middleware: mid.length ? mid : null, init: init, count: 0, pending: false, options: options, roles: roles }); 122 | jR.routes.sort(function(a, b) { 123 | return a.priority > b.priority ? -1 : a.priority < b.priority ? 1 :0; 124 | }); 125 | 126 | jR.is404 && (url === jR.url || url.indexOf('{') !== -1) && W.REDIRECT(jR.href + location.hash); 127 | EMIT('route', url); 128 | return jR; 129 | }; 130 | 131 | W.MIDDLEWARE = function(name, fn) { 132 | 133 | if (name instanceof Array) { 134 | name.wait(function(item, next) { 135 | var mid = jR.middlewares[item]; 136 | if (mid) 137 | mid(next); 138 | else 139 | next(); 140 | }, fn); 141 | return jR; 142 | } 143 | 144 | jR.middlewares[name] = fn; 145 | return jR; 146 | }; 147 | 148 | jR.refresh = function() { 149 | return jR.location(jR.url, true); 150 | }; 151 | 152 | jR.reload = function() { 153 | return jR.refresh(); 154 | }; 155 | 156 | jR._route = function(url) { 157 | 158 | if (url.charCodeAt(0) === 47) 159 | url = url.substring(1); 160 | 161 | if (url.charCodeAt(url.length - 1) === 47) 162 | url = url.substring(0, url.length - 1); 163 | 164 | var arr = url.split('/'); 165 | if (arr.length === 1 && !arr[0]) 166 | arr[0] = '/'; 167 | 168 | return arr; 169 | }; 170 | 171 | jR._route_param = function(routeUrl, route) { 172 | var arr = []; 173 | 174 | if (!route || !routeUrl) 175 | return arr; 176 | 177 | var length = route.params.length; 178 | if (length) { 179 | for (var i = 0; i < length; i++) { 180 | var value = routeUrl[route.params[i]]; 181 | arr.push(value === '/' || (/\{|\}/).test(value) ? '' : value); 182 | } 183 | } 184 | 185 | return arr; 186 | }; 187 | 188 | jR._route_compare = function(url, route) { 189 | 190 | var length = url.length; 191 | var skip = length === 1 && url[0] === '/'; 192 | 193 | if (route.length !== length) 194 | return false; 195 | 196 | for (var i = 0; i < length; i++) { 197 | 198 | var value = route[i]; 199 | if (!value) 200 | return false; 201 | 202 | if (!skip && value.charCodeAt(0) === 123) 203 | continue; 204 | 205 | if (value === '*') 206 | return true; 207 | 208 | if (url[i] !== value) 209 | return false; 210 | } 211 | 212 | return true; 213 | }; 214 | 215 | jR.location = function(url, isRefresh) { 216 | 217 | if (url) 218 | NAV.href = url; 219 | 220 | Internal.isReady = true; 221 | 222 | var index = url.indexOf('?'); 223 | var qs = ''; 224 | 225 | if (index !== -1) { 226 | qs = url.substring(index); 227 | url = url.substring(0, index); 228 | } 229 | 230 | url = prepareUrl(url); 231 | url = jR.path(url); 232 | 233 | var path = jR._route(url); 234 | var routes = []; 235 | var notfound = true; 236 | var raw = []; 237 | 238 | raw.push.apply(raw, path); 239 | 240 | for (var i = 0; i < path.length; i++) 241 | path[i] = path[i].toLowerCase(); 242 | 243 | if (!isRefresh && jR.url.length && jR.prev() !== jR.url) { 244 | if (jR.$pop) 245 | jR.$pop = false; 246 | else { 247 | Internal.$prev.push(jR.url); 248 | Internal.$prev.length > jR.LIMIT_HISTORY && Internal.$prev.shift(); 249 | Internal.$next.length > jR.LIMIT_HISTORY && Internal.$next.shift(); 250 | jR.$save && jR.save(); 251 | } 252 | } 253 | 254 | if (jR.isback !== Internal.$prev.length) 255 | SET(NAME_NAV + 'isback', Internal.$prev.length); 256 | 257 | if (jR.isforward !== Internal.$next.length) 258 | SET(NAME_NAV + 'isforward', Internal.$next.length); 259 | 260 | for (var i = 0; i < jR.routes.length; i++) { 261 | 262 | var route = jR.routes[i]; 263 | if (!jR._route_compare(path, route.url)) 264 | continue; 265 | 266 | if (route.url.indexOf('*') === -1) 267 | notfound = false; 268 | 269 | if (route.once && route.count > 0) 270 | continue; 271 | 272 | route.count++; 273 | routes.push(route); 274 | break; 275 | } 276 | 277 | var isError = false; 278 | var error = []; 279 | 280 | var tmp = jR.url; 281 | jR.url = url; 282 | 283 | if (jR.url !== tmp) 284 | UPD(NAME_NAV + 'url'); 285 | 286 | UPD(NAME_NAV + 'href'); 287 | 288 | jR._params(qs); 289 | jR.params = jR._route_param(raw, route); 290 | UPD(NAME_NAV + 'params'); 291 | 292 | jR.is404 = false; 293 | EMIT('location', url); 294 | 295 | for (var i = 0; i < routes.length; i++) { 296 | var route = routes[i]; 297 | 298 | if (route.pending) 299 | continue; 300 | 301 | if (!route.middleware || !route.middleware.length) { 302 | 303 | if (!route.init) { 304 | route.fn.apply(jR, jR.params); 305 | continue; 306 | } 307 | 308 | route.pending = true; 309 | 310 | (function(route) { 311 | route.init(function() { 312 | route.fn.apply(jR, jR.params); 313 | route.pending = false; 314 | }); 315 | })(route); 316 | 317 | route.init = null; 318 | continue; 319 | } 320 | 321 | (function(route) { 322 | 323 | var l = route.middleware.length; 324 | var middleware = []; 325 | var current = jR.url; 326 | 327 | for (var j = 0; j < l; j++) { 328 | var m = route.middleware[j]; 329 | 330 | // codelist 331 | if (m.charAt(0) === '#') { 332 | (function(m) { 333 | middleware.push(next => CL(m, () => next())); 334 | })(m.substring(1)); 335 | continue; 336 | } 337 | 338 | var fn = jR.middlewares[m]; 339 | fn && (function(route, fn) { 340 | middleware.push(function(next) { 341 | if (jR.url !== current) { 342 | middleware = null; 343 | next = null; 344 | route.pending = false; 345 | } else 346 | fn.call(jR, next, route.options, route.roles, route); 347 | }); 348 | })(route, fn); 349 | } 350 | 351 | if (!route.init) { 352 | route.pending = true; 353 | middleware.jRmiddleware(function(err) { 354 | if (jR.url === current) 355 | !err && route.fn.apply(jR, jR.params); 356 | route.pending = false; 357 | }, route); 358 | return; 359 | } 360 | 361 | route.pending = true; 362 | route.init(function() { 363 | middleware.jRmiddleware(function(err) { 364 | if (jR.url === current) 365 | !err && route.fn.apply(jR, jR.params); 366 | route.pending = false; 367 | }, route); 368 | }); 369 | 370 | route.init = null; 371 | })(route); 372 | } 373 | 374 | isError && jR.status(500, error); 375 | 376 | if (notfound) { 377 | jR.is404 = true; 378 | jR.status(404); 379 | } else 380 | jR.is404 = false; 381 | }; 382 | 383 | jR.prev = function() { 384 | return Internal.$prev[Internal.$prev.length - 1]; 385 | }; 386 | 387 | jR.next = function() { 388 | return Internal.$next[Internal.$next.length - 1]; 389 | }; 390 | 391 | jR.back = function() { 392 | var url = Internal.$prev.pop() || '/'; 393 | if (jR.url && jR.next() !== jR.url) 394 | Internal.$next.push(jR.url); 395 | jR.url = ''; 396 | W.REDIRECT(url); 397 | jR.$save && jR.save(); 398 | return jR; 399 | }; 400 | 401 | jR.forward = function() { 402 | var url = Internal.$next.pop() || '/'; 403 | if (jR.url && jR.prev() !== jR.url) 404 | Internal.$prev.push(jR.url); 405 | jR.url = ''; 406 | W.REDIRECT(url); 407 | jR.$save && jR.save(); 408 | return jR; 409 | }; 410 | 411 | jR.status = function(code, message) { 412 | EMIT(code + '', message); 413 | return jR; 414 | }; 415 | 416 | NAV.redirect = W.REDIRECT = function(url) { 417 | 418 | if (!url) 419 | url = jR.url; 420 | 421 | var nohistory = false; 422 | 423 | url = url.env(true).ROOT(true).replace(/\s(@)?nohistory/i, function() { 424 | nohistory = true; 425 | return ''; 426 | }); 427 | 428 | 429 | if (Object.keys(jR.query2).length) 430 | url = QUERIFY(url, jR.query2); 431 | 432 | url = url.flags(null, url, 'redirect'); 433 | 434 | if (url.indexOf('./') !== -1) { 435 | 436 | var href = NAV.url; 437 | var index = url.indexOf('?'); 438 | 439 | var qs = index === -1 ? '' : url.substring(index); 440 | 441 | if (url === './') { 442 | REDIRECT(NAV.url); 443 | return; 444 | } 445 | 446 | var count = url.match(/\.\.\//g); 447 | var arr = href.split('/').trim(); 448 | href = '/' + arr.splice(0, arr.length - count.length).join('/'); 449 | if (href.charAt(href.length - 1) !== '/') 450 | href += '/'; 451 | href += qs; 452 | REDIRECT(href); 453 | return; 454 | } 455 | 456 | var c = url.charCodeAt(0); 457 | var l = LOC; 458 | if (c === 35) { 459 | l.hash = url; 460 | jR.location(url, false); 461 | } else if (jR.$custom) { 462 | jR.location(url, false); 463 | } else if (history.pushState) { 464 | if (!nohistory) 465 | history.pushState(null, null, url); 466 | jR.location(l.pathname + l.search, false); 467 | } else 468 | l.href = url; 469 | }; 470 | 471 | jR._params = function(qs) { 472 | 473 | var data = {}; 474 | 475 | jR.queryraw = (qs || LOC.search).substring(1); 476 | var params = jR.queryraw.split('&'); 477 | 478 | for (var i = 0; i < params.length; i++) { 479 | 480 | var param = params[i].replace(/\+/g, '%20').split('='); 481 | if (param.length !== 2) 482 | continue; 483 | 484 | var name = decodeURIComponent(param[0]); 485 | var value = decodeURIComponent(param[1]); 486 | var isArray = data[name] instanceof Array; 487 | 488 | if (data[name] && !isArray) 489 | data[name] = [data[name]]; 490 | 491 | if (isArray) 492 | data[name].push(value); 493 | else 494 | data[name] = value; 495 | } 496 | 497 | jR.query = data; 498 | 499 | var p = NAME_NAV + 'query'; 500 | W.M && UPD(p); 501 | return jR; 502 | }; 503 | 504 | jR.path = function (url, d) { 505 | 506 | if (url.substring(0, 1) === '#') 507 | return url; 508 | 509 | if (!d) 510 | d = '/'; 511 | 512 | var index = url.indexOf('?'); 513 | var params = ''; 514 | 515 | if (index !== -1) { 516 | params = url.substring(index); 517 | url = url.substring(0, index); 518 | } 519 | 520 | var l = url.length; 521 | var c = url.substring(l - 1, l); 522 | if (c !== d) 523 | url += d; 524 | 525 | return url + params; 526 | }; 527 | 528 | function prepareUrl(url) { 529 | if (url.substring(0, 1) === '#') 530 | return url; 531 | var index = url.indexOf('#'); 532 | return index !== -1 ? url.substring(0, index) : url; 533 | } 534 | 535 | Array.prototype.jRmiddleware = function(callback, route) { 536 | 537 | var self = this; 538 | var item = self.shift(); 539 | 540 | if (item === undefined) { 541 | callback && callback(); 542 | return self; 543 | } 544 | 545 | item(function(err) { 546 | if (err instanceof Error || err === false) 547 | callback && callback(err === false ? true : err); 548 | else setTimeout(function() { 549 | self.jRmiddleware(callback, route); 550 | }, 1); 551 | }, route.options, route.roles); 552 | 553 | return self; 554 | }; 555 | 556 | String.prototype.jRcount = function(text) { 557 | var index = 0; 558 | var count = 0; 559 | do { 560 | index = this.indexOf(text, index + text.length); 561 | if (index > 0) 562 | count++; 563 | } while (index > 0); 564 | return count; 565 | }; 566 | 567 | jR.clientside = function(selector) { 568 | $(document).on('click', selector, function(e) { 569 | e.preventDefault(); 570 | var el = $(this); 571 | var url = (el.attr('href') || el.attrd('href') || el.attrd('url')); 572 | if (url !== ('javas' + 'cript:vo' + 'id(0)') && url !== '#') 573 | W.REDIRECT(url); 574 | }); 575 | return jR; 576 | }; 577 | 578 | function jRinit() { 579 | 580 | NAV.ua = MAIN.ua; 581 | 582 | $(document).ready(function() { 583 | 584 | if (!jR.$custom) { 585 | if (jR.hashtags) 586 | jR.url = LOC.hash || jR.path(prepareUrl(LOC.pathname)); 587 | else 588 | jR.url = jR.path(prepareUrl(LOC.pathname)); 589 | } 590 | 591 | setTimeout(function() { 592 | if (!Internal.isReady) 593 | jR.location(jR.href || jR.url); 594 | }, 5); 595 | 596 | $(W).on('hashchange', function() { 597 | Internal.isReady && jR.hashtags && jR.location(jR.path(LOC.hash)); 598 | }).on('popstate', function() { 599 | if (Internal.isReady && !jR.hashtags && !jR.$custom) { 600 | var url = jR.path(LOC.pathname); 601 | jR.url !== url && jR.location(url); 602 | } 603 | }); 604 | }); 605 | } 606 | 607 | if (W.jQuery && W.MAIN && W.MAIN.loaded) { 608 | jRinit(); 609 | } else { 610 | jR.init = setInterval(function() { 611 | if (W.jQuery && W.MAIN && W.MAIN.loaded) { 612 | clearInterval(jR.init); 613 | jRinit(); 614 | } 615 | }, 100); 616 | } 617 | 618 | jR._params(); 619 | 620 | })(window); -------------------------------------------------------------------------------- /jrouting.jcomponent.min.js: -------------------------------------------------------------------------------- 1 | (function(p){var v='NAV.';var h={version:16.3,hashtags:false,middlewares:{},params:[],query:{},query2:{},routes:[],url:'',count:0};var d={$prev:[],$next:[],isReady:false,LIMIT_HISTORY:100,LIMIT_HISTORY_ERROR:100};h.history=d.$prev;var f=location;!p.NAV&&(p.NAV=h);h.custom=function(r){h.$custom=true;if(r==null||r==true||typeof r==='string')CACHEPATH('NAV.href',r==null||r==true?'1 month':r)};h.remember=function(r,e){if(e===null)delete h.query2[r];else h.query2[r]=e==undefined?h.query[r]:e};h.remove=function(r){r=r.env(true).ROOT(true);var e=[];for(var t=0;te.priority?-1:r.priorityh.LIMIT_HISTORY&&d.$prev.shift();d.$next.length>h.LIMIT_HISTORY&&d.$next.shift();h.$save&&h.save()}}if(h.isback!==d.$prev.length)SET(v+'isback',d.$prev.length);if(h.isforward!==d.$next.length)SET(v+'isforward',d.$next.length);for(var s=0;s0)continue;f.count++;a.push(f);break}var l=false;var c=[];var p=h.url;h.url=r;if(h.url!==p)UPD(v+'url');UPD(v+'href');h._params(n);h.params=h._route_param(o,f);UPD(v+'params');h.is404=false;EMIT('location',r);for(var s=0;sCL(e,()=>r()))})(a.substring(1));continue}var u=h.middlewares[a];u&&function(e,t){n.push(function(r){if(h.url!==i){n=null;r=null;e.pending=false}else t.call(h,r,e.options,e.roles,e)})}(e,u)}if(!e.init){e.pending=true;n.jRmiddleware(function(r){if(h.url===i)!r&&e.fn.apply(h,h.params);e.pending=false},e);return}e.pending=true;e.init(function(){n.jRmiddleware(function(r){if(h.url===i)!r&&e.fn.apply(h,h.params);e.pending=false},e)});e.init=null})(f)}l&&h.status(500,c);if(u){h.is404=true;h.status(404)}else h.is404=false};h.prev=function(){return d.$prev[d.$prev.length-1]};h.next=function(){return d.$next[d.$next.length-1]};h.back=function(){var r=d.$prev.pop()||'/';if(h.url&&h.next()!==h.url)d.$next.push(h.url);h.url='';p.REDIRECT(r);h.$save&&h.save();return h};h.forward=function(){var r=d.$next.pop()||'/';if(h.url&&h.prev()!==h.url)d.$prev.push(h.url);h.url='';p.REDIRECT(r);h.$save&&h.save();return h};h.status=function(r,e){EMIT(r+'',e);return h};NAV.redirect=p.REDIRECT=function(r){if(!r)r=h.url;var e=false;r=r.env(true).ROOT(true).replace(/\s(@)?nohistory/i,function(){e=true;return''});if(Object.keys(h.query2).length)r=QUERIFY(r,h.query2);r=r.flags(null,r,'redirect');if(r.indexOf('./')!==-1){var t=NAV.url;var n=r.indexOf('?');var i=n===-1?'':r.substring(n);if(r==='./'){REDIRECT(NAV.url);return}var a=r.match(/\.\.\//g);var u=t.split('/').trim();t='/'+u.splice(0,u.length-a.length).join('/');if(t.charAt(t.length-1)!=='/')t+='/';t+=i;REDIRECT(t);return}var o=r.charCodeAt(0);var s=f;if(o===35){s.hash=r;h.location(r,false)}else if(h.$custom){h.location(r,false)}else if(history.pushState){if(!e)history.pushState(null,null,r);h.location(s.pathname+s.search,false)}else s.href=r};h._params=function(r){var e={};h.queryraw=(r||f.search).substring(1);var t=h.queryraw.split('&');for(var n=0;n0)t++}while(e>0);return t};h.clientside=function(r){$(document).on('click',r,function(r){r.preventDefault();var e=$(this);var t=e.attr('href')||e.attrd('href')||e.attrd('url');if(t!=='javas'+'cript:vo'+'id(0)'&&t!=='#')p.REDIRECT(t)});return h};function r(){NAV.ua=MAIN.ua;$(document).ready(function(){if(!h.$custom){if(h.hashtags)h.url=f.hash||h.path(g(f.pathname));else h.url=h.path(g(f.pathname))}setTimeout(function(){if(!d.isReady)h.location(h.href||h.url)},5);$(p).on('hashchange',function(){d.isReady&&h.hashtags&&h.location(h.path(f.hash))}).on('popstate',function(){if(d.isReady&&!h.hashtags&&!h.$custom){var r=h.path(f.pathname);h.url!==r&&h.location(r)}})})}if(p.jQuery&&p.MAIN&&p.MAIN.loaded){r()}else{h.init=setInterval(function(){if(p.jQuery&&p.MAIN&&p.MAIN.loaded){clearInterval(h.init);r()}},100)}h._params()})(window); -------------------------------------------------------------------------------- /jrouting.js: -------------------------------------------------------------------------------- 1 | var JRFU = {}; 2 | var jR = { 3 | LIMIT_HISTORY: 100, 4 | LIMIT_HISTORY_ERROR: 100, 5 | version: 'v3.0.1', 6 | cache: {}, 7 | routes: [], 8 | history: [], 9 | errors: [], 10 | events: {}, 11 | eventsOnce: {}, 12 | global: {}, 13 | query: {}, 14 | params: [], 15 | middlewares: {}, 16 | repository: {}, 17 | url: '', 18 | model: null, 19 | isFirst: true, 20 | isReady: false, 21 | isRefresh: false, 22 | isModernBrowser: history.pushState ? true : false, 23 | hashtags: false, 24 | count: 0 25 | }; 26 | 27 | if (!window.jR) 28 | window.jR = jR; 29 | 30 | if (!window.jRouting) 31 | window.jRouting = jR; 32 | 33 | if (!window.JRFU) 34 | window.JRFU = JRFU; 35 | 36 | jR.remove = function(url) { 37 | var self = this; 38 | var routes = []; 39 | for (var i = 0, length = self.routes.length; i < length; i++) 40 | self.routes[i].id !== url && routes.push(self.routes[i]); 41 | self.routes = routes; 42 | return self; 43 | }; 44 | 45 | jR.on = function(name, fn) { 46 | var self = this; 47 | var e = self.events[name]; 48 | if (e) 49 | e.push(fn); 50 | else 51 | self.events[name] = [fn]; 52 | return self; 53 | }; 54 | 55 | jR.once = function(name, fn) { 56 | var self = this; 57 | var e = self.eventsOnce[name]; 58 | if (e) 59 | e.push(fn); 60 | else 61 | self.eventsOnce[name] = [fn]; 62 | return self; 63 | }; 64 | 65 | jR.emit = function(name) { 66 | 67 | var self = this; 68 | var events = self.events[name] || []; 69 | var eventsOnce = self.eventsOnce[name] || []; 70 | var length = events.length; 71 | var lengthOnce = eventsOnce.length; 72 | 73 | if (!length && !lengthOnce) 74 | return self; 75 | 76 | var params = []; 77 | var tmp = arguments.length; 78 | 79 | for (var i = 1; i < tmp; i++) 80 | params.push(arguments[i]); 81 | 82 | if (length > 0) { 83 | for (var i = 0; i < length; i++) 84 | events[i].apply(self, params); 85 | } 86 | 87 | if (lengthOnce) { 88 | for (var i = 0; i < length; i++) 89 | eventsOnce[i].apply(self, params); 90 | delete self.eventsOnce[name]; 91 | } 92 | 93 | }; 94 | 95 | jR.route = function(url, fn, middleware, init) { 96 | 97 | var tmp; 98 | 99 | if (fn instanceof Array) { 100 | var tmp = middleware; 101 | middleware = fn; 102 | fn = tmp; 103 | } 104 | 105 | if (typeof(middleware) === 'function') { 106 | tmp = init; 107 | init = middleware; 108 | middleware = tmp; 109 | } 110 | 111 | var priority = url.count('/') + (url.indexOf('*') === -1 ? 0 : 10); 112 | var route = jR._route(url.trim()); 113 | var params = []; 114 | 115 | if (typeof(middleware) === 'string') 116 | middleware = middleware.split(','); 117 | 118 | var mid = []; 119 | var roles = []; 120 | var options = {}; 121 | 122 | (middleware instanceof Array) && middleware.forEach(function(item) { 123 | if (typeof(item) === 'object') 124 | options = item; 125 | else if (item.substring(0, 1) === '@') 126 | roles.push(item.substring(1)); 127 | else 128 | mid.push(item); 129 | }); 130 | 131 | if (url.indexOf('{') !== -1) { 132 | priority -= 100; 133 | for (var i = 0; i < route.length; i++) 134 | route[i].substring(0, 1) === '{' && params.push(i); 135 | priority -= params.length; 136 | } 137 | 138 | jR.remove(url); 139 | jR.routes.push({ id: url, url: route, fn: fn, priority: priority, params: params, middleware: mid.length ? mid : null, init: init, count: 0, pending: false, options: options, roles: roles }); 140 | jR.routes.sort(function(a, b) { 141 | return a.priority > b.priority ? -1 : a.priority < b.priority ? 1 :0; 142 | }); 143 | 144 | return jR; 145 | }; 146 | 147 | jR.middleware = function(name, fn) { 148 | var self = this; 149 | self.middlewares[name] = fn; 150 | return self; 151 | }; 152 | 153 | jR.refresh = function() { 154 | var self = this; 155 | return self.location(self.url, true); 156 | }; 157 | 158 | jR.reload = function() { 159 | return jR.refresh(); 160 | }; 161 | 162 | jR.async = function() { 163 | if (!window.jRoute || !window.jRoute.length) 164 | return; 165 | while (true) { 166 | var fn = window.jRoute.shift(); 167 | if (!fn) 168 | break; 169 | fn(); 170 | } 171 | jR.is404 && jR.location(jR.url); 172 | }; 173 | 174 | jR._route = function(url) { 175 | 176 | if (url.charCodeAt(0) === 47) 177 | url = url.substring(1); 178 | 179 | if (url.charCodeAt(url.length - 1) === 47) 180 | url = url.substring(0, url.length - 1); 181 | 182 | var arr = url.split('/'); 183 | if (arr.length === 1 && !arr[0]) 184 | arr[0] = '/'; 185 | 186 | return arr; 187 | }; 188 | 189 | jR._route_param = function(routeUrl, route) { 190 | var arr = []; 191 | 192 | if (!route || !routeUrl) 193 | return arr; 194 | 195 | var length = route.params.length; 196 | if (length) { 197 | for (var i = 0; i < length; i++) { 198 | var value = routeUrl[route.params[i]]; 199 | arr.push(value === '/' ? '' : value); 200 | } 201 | } 202 | 203 | return arr; 204 | }; 205 | 206 | jR._route_compare = function(url, route) { 207 | 208 | var length = url.length; 209 | var skip = length === 1 && url[0] === '/'; 210 | 211 | if (route.length !== length) 212 | return false; 213 | 214 | for (var i = 0; i < length; i++) { 215 | 216 | var value = route[i]; 217 | if (!value) 218 | return false; 219 | 220 | if (!skip && value.charCodeAt(0) === 123) 221 | continue; 222 | 223 | if (value === '*') 224 | return true; 225 | 226 | if (url[i] !== value) 227 | return false; 228 | } 229 | 230 | return true; 231 | }; 232 | 233 | jR.location = function(url, isRefresh) { 234 | 235 | if (!jR.isReady) 236 | return; 237 | 238 | var index = url.indexOf('?'); 239 | if (index !== -1) 240 | url = url.substring(0, index); 241 | 242 | url = JRFU.prepareUrl(url); 243 | url = JRFU.path(url); 244 | 245 | var self = this; 246 | var path = self._route(url); 247 | var routes = []; 248 | var notfound = true; 249 | var raw = []; 250 | 251 | raw.push.apply(raw, path); 252 | 253 | for (var i = 0, length = path.length; i < length; i++) 254 | path[i] = path[i].toLowerCase(); 255 | 256 | self.isRefresh = isRefresh || false; 257 | self.count++; 258 | 259 | if (!isRefresh && self.url.length && self.history[self.history.length - 1] !== self.url) { 260 | self.history.push(self.url); 261 | self.history.length > self.LIMIT_HISTORY && self.history.shift(); 262 | } 263 | 264 | var length = self.routes.length; 265 | for (var i = 0; i < length; i++) { 266 | 267 | var route = self.routes[i]; 268 | if (!self._route_compare(path, route.url)) 269 | continue; 270 | 271 | if (route.url.indexOf('*') === -1) 272 | notfound = false; 273 | 274 | if (route.once && route.count > 0) 275 | continue; 276 | 277 | route.count++; 278 | routes.push(route); 279 | break; 280 | } 281 | 282 | var isError = false; 283 | var error = []; 284 | 285 | // cache old repository 286 | 287 | if (self.url.length) 288 | self.cache[self.url] = self.repository; 289 | 290 | self.url = url; 291 | self.repository = self.cache[url]; 292 | 293 | if (!self.repository) 294 | self.repository = {}; 295 | 296 | self._params(); 297 | self.params = self._route_param(raw, route); 298 | self.is404 = false; 299 | self.emit('location', url); 300 | length = routes.length; 301 | 302 | for (var i = 0; i < length; i++) { 303 | var route = routes[i]; 304 | 305 | if (route.pending) 306 | continue; 307 | 308 | if (!route.middleware || !route.middleware.length) { 309 | if (!route.init) { 310 | route.fn.apply(self, self.params); 311 | continue; 312 | } 313 | 314 | route.pending = true; 315 | 316 | (function(route) { 317 | route.init(function() { 318 | route.fn.apply(self, self.params); 319 | route.pending = false; 320 | }); 321 | })(route); 322 | 323 | route.init = null; 324 | continue; 325 | } 326 | 327 | (function(route) { 328 | 329 | var l = route.middleware.length; 330 | var middleware = []; 331 | 332 | for (var j = 0; j < l; j++) { 333 | var fn = jR.middlewares[route.middleware[j]]; 334 | fn && (function(route, fn) { 335 | middleware.push(function(next) { 336 | fn.call(jR, next, route.options, route.roles, route); 337 | }); 338 | })(route, fn); 339 | } 340 | 341 | if (!route.init) { 342 | route.pending = true; 343 | middleware.middleware(function(err) { 344 | !err && route.fn.apply(jR, jR.params); 345 | route.pending = false; 346 | }, route); 347 | return; 348 | } 349 | 350 | route.pending = true; 351 | route.init(function() { 352 | middleware.middleware(function(err) { 353 | !err && route.fn.apply(jR, jR.params); 354 | route.pending = false; 355 | }, route); 356 | }); 357 | 358 | route.init = null; 359 | })(route); 360 | } 361 | 362 | isError && self.status(500, error); 363 | self.is404 = true; 364 | notfound && self.status(404, new Error('Route not found.')); 365 | }; 366 | 367 | jR.prev = function() { 368 | return this.history[this.history.length - 1]; 369 | }; 370 | 371 | jR.back = function() { 372 | var self = this; 373 | var url = self.history.pop() || '/'; 374 | self.url = ''; 375 | self.redirect(url, true); 376 | return self; 377 | }; 378 | 379 | jR.status = function(code, message) { 380 | var self = this; 381 | self.emit('status', code || 404, message); 382 | return self; 383 | }; 384 | 385 | jR.redirect = function(url, model) { 386 | var self = this; 387 | 388 | if (url.charCodeAt(0) === 35) { 389 | location.hash = url; 390 | self.model = model || null; 391 | self.location(url, false); 392 | return self; 393 | } 394 | 395 | if (!self.isModernBrowser) { 396 | location.href = url; 397 | return false; 398 | } 399 | 400 | history.pushState(null, null, url); 401 | self.model = model || null; 402 | self.location(url, false); 403 | return self; 404 | }; 405 | 406 | jR._params = function() { 407 | 408 | var self = this; 409 | var data = {}; 410 | 411 | var params = location.href.slice(location.href.indexOf('?') + 1).split('&'); 412 | 413 | for (var i = 0; i < params.length; i++) { 414 | 415 | var param = params[i].replace(/\+/g, '%20').split('='); 416 | if (param.length !== 2) 417 | continue; 418 | 419 | var name = decodeURIComponent(param[0]); 420 | var value = decodeURIComponent(param[1]); 421 | var isArray = data[name] instanceof Array; 422 | 423 | if (data[name] && !isArray) 424 | data[name] = [data[name]]; 425 | 426 | if (isArray) 427 | data[name].push(value); 428 | else 429 | data[name] = value; 430 | } 431 | 432 | self.query = data; 433 | return self; 434 | }; 435 | 436 | jR.path = JRFU.path = function (url, d) { 437 | 438 | if (url.substring(0, 1) === '#') 439 | return url; 440 | 441 | if (!d) 442 | d = '/'; 443 | 444 | var index = url.indexOf('?'); 445 | var params = ''; 446 | 447 | if (index !== -1) { 448 | params = url.substring(index); 449 | url = url.substring(0, index); 450 | } 451 | 452 | var l = url.length; 453 | var c = url.substring(l - 1, l); 454 | if (c !== d) 455 | url += d; 456 | 457 | return url + params; 458 | }; 459 | 460 | JRFU.prepareUrl = function(url) { 461 | if (url.substring(0, 1) === '#') 462 | return url; 463 | var index = url.indexOf('#'); 464 | return index !== -1 ? url.substring(0, index) : url; 465 | }; 466 | 467 | if (!Array.prototype.middleware) { 468 | Array.prototype.middleware = function(callback, route) { 469 | 470 | var self = this; 471 | var item = self.shift(); 472 | 473 | if (item === undefined) { 474 | callback && callback(); 475 | return self; 476 | } 477 | 478 | item(function(err) { 479 | if (err instanceof Error || err === false) 480 | callback && callback(err === false ? true : err); 481 | else setTimeout(function() { 482 | self.middleware(callback, route); 483 | }, 1); 484 | }, route.options, route.roles); 485 | 486 | return self; 487 | }; 488 | } 489 | 490 | if (!String.prototype.count) { 491 | String.prototype.count = function(text) { 492 | var index = 0; 493 | var count = 0; 494 | do { 495 | index = this.indexOf(text, index + text.length); 496 | if (index > 0) 497 | count++; 498 | } while (index > 0); 499 | return count; 500 | }; 501 | } 502 | 503 | jR.on('error', function (err, url, name) { 504 | var self = this; 505 | self.errors.push({ error: err, url: url, name: name, date: new Date() }); 506 | self.errors.length > self.LIMIT_HISTORY_ERROR && self.errors.shift(); 507 | }); 508 | 509 | jR.clientside = function(selector) { 510 | $(document).on('click', selector, function(e) { 511 | e.preventDefault(); 512 | var el = $(this); 513 | jR.redirect(el.attr('href') || el.attr('data-jrouting') || el.attr('data-jr')); 514 | }); 515 | return jR; 516 | }; 517 | 518 | function jRinit() { 519 | jR.async(); 520 | $.fn.jRouting = function(g) { 521 | 522 | if (jR.hashtags || !jR.isModernBrowser) 523 | return this; 524 | 525 | var version = +$.fn.jquery.replace(/\./g, ''); 526 | if (version >= 300 && g === true) 527 | throw Error('$(selector).jRouting() doesn\'t work in jQuery +3. Instead of this use jR.clientside(selector).'); 528 | 529 | var handler = function(e) { 530 | e.preventDefault(); 531 | jR.redirect($(this).attr('href')); 532 | }; 533 | 534 | if (g) 535 | $(document).on('click', this.selector, handler); 536 | else 537 | this.filter('a').bind('click', handler); 538 | 539 | return this; 540 | }; 541 | 542 | $(document).ready(function() { 543 | 544 | jR.async(); 545 | 546 | if (jR.hashtags) 547 | jR.url = location.hash || JRFU.path(JRFU.prepareUrl(location.pathname)); 548 | else 549 | jR.url = JRFU.path(JRFU.prepareUrl(location.pathname)); 550 | 551 | if (jR.events.ready) { 552 | jR.emit('ready', jR.url); 553 | jR.emit('load', jR.url); 554 | } else { 555 | setTimeout(function() { 556 | jR.isReady = true; 557 | jR.location(jR.url); 558 | jR.emit('ready', jR.url); 559 | jR.emit('load', jR.url); 560 | }, 5); 561 | } 562 | 563 | $(window).on('hashchange', function() { 564 | if (!jR.isReady || !jR.hashtags) 565 | return; 566 | jR.location(JRFU.path(location.hash)); 567 | }); 568 | 569 | $(window).on('popstate', function() { 570 | if (!jR.isReady || jR.hashtags) 571 | return; 572 | var url = JRFU.path(location.pathname); 573 | jR.url !== url && jR.location(url); 574 | }); 575 | }); 576 | } 577 | 578 | if (window.jQuery) { 579 | jRinit(); 580 | } else { 581 | jR.init = setInterval(function() { 582 | if (window.jQuery) { 583 | clearInterval(jR.init); 584 | jRinit(); 585 | } 586 | }, 100); 587 | } 588 | 589 | setTimeout(jR.async, 500); 590 | setTimeout(jR.async, 1000); 591 | setTimeout(jR.async, 2000); 592 | setTimeout(jR.async, 3000); 593 | setTimeout(jR.async, 5000); 594 | window.ROUTE = function(url, fn, middleware, init) { 595 | return jR.route(url, fn, middleware, init); 596 | }; -------------------------------------------------------------------------------- /jrouting.min.js: -------------------------------------------------------------------------------- 1 | var JRFU={};var jR={LIMIT_HISTORY:100,LIMIT_HISTORY_ERROR:100,version:'v3.0.1',cache:{},routes:[],history:[],errors:[],events:{},eventsOnce:{},global:{},query:{},params:[],middlewares:{},repository:{},url:'',model:null,isFirst:true,isReady:false,isRefresh:false,isModernBrowser:history.pushState?true:false,hashtags:false,count:0};if(!window.jR)window.jR=jR;if(!window.jRouting)window.jRouting=jR;if(!window.JRFU)window.JRFU=JRFU;jR.remove=function(r){var e=this;var t=[];for(var n=0,i=e.routes.length;n0){for(var u=0;ue.priority?-1:r.priorityn.LIMIT_HISTORY&&n.history.shift()}var u=n.routes.length;for(var s=0;s0)continue;f.count++;a.push(f);break}var c=false;var h=[];if(n.url.length)n.cache[n.url]=n.repository;n.url=r;n.repository=n.cache[r];if(!n.repository)n.repository={};n._params();n.params=n._route_param(o,f);n.is404=false;n.emit('location',r);u=a.length;for(var s=0;s0)t++}while(e>0);return t}}jR.on('error',function(r,e,t){var n=this;n.errors.push({error:r,url:e,name:t,date:new Date});n.errors.length>n.LIMIT_HISTORY_ERROR&&n.errors.shift()});jR.clientside=function(r){$(document).on('click',r,function(r){r.preventDefault();var e=$(this);jR.redirect(e.attr('href')||e.attr('data-jrouting')||e.attr('data-jr'))});return jR};function jRinit(){jR.async();$.fn.jRouting=function(r){if(jR.hashtags||!jR.isModernBrowser)return this;var e=+$.fn.jquery.replace(/\./g,'');if(e>=300&&r===true)throw Error('$(selector).jRouting() doesn\'t work in jQuery +3. Instead of this use jR.clientside(selector).');var t=function(r){r.preventDefault();jR.redirect($(this).attr('href'))};if(r)$(document).on('click',this.selector,t);else this.filter('a').bind('click',t);return this};$(document).ready(function(){jR.async();if(jR.hashtags)jR.url=location.hash||JRFU.path(JRFU.prepareUrl(location.pathname));else jR.url=JRFU.path(JRFU.prepareUrl(location.pathname));if(jR.events.ready){jR.emit('ready',jR.url);jR.emit('load',jR.url)}else{setTimeout(function(){jR.isReady=true;jR.location(jR.url);jR.emit('ready',jR.url);jR.emit('load',jR.url)},5)}$(window).on('hashchange',function(){if(!jR.isReady||!jR.hashtags)return;jR.location(JRFU.path(location.hash))});$(window).on('popstate',function(){if(!jR.isReady||jR.hashtags)return;var r=JRFU.path(location.pathname);jR.url!==r&&jR.location(r)})})}if(window.jQuery){jRinit()}else{jR.init=setInterval(function(){if(window.jQuery){clearInterval(jR.init);jRinit()}},100)}setTimeout(jR.async,500);setTimeout(jR.async,1e3);setTimeout(jR.async,2e3);setTimeout(jR.async,3e3);setTimeout(jR.async,5e3);window.ROUTE=function(r,e,t,n){return jR.route(r,e,t,n)}; -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright 2012-2023 (c) Peter Širka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to permit 10 | persons to whom the Software is furnished to do so, subject to the 11 | following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 19 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 21 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 22 | USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /minify.json: -------------------------------------------------------------------------------- 1 | { 2 | "mangle": true, 3 | "output": { "quote_style": 1 } 4 | } -------------------------------------------------------------------------------- /minify.sh: -------------------------------------------------------------------------------- 1 | ECHO "[COMPILING]" 2 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 3 | cd $DIR 4 | uglifyjs jrouting.js --config-file minify.json -o jrouting.min.js 5 | uglifyjs jrouting.jcomponent.js --config-file minify.json -o jrouting.jcomponent.min.js -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![MIT License][license-image]][license-url] 2 | 3 | # jRouting 4 | 5 | [![Made in EU](https://cdn.componentator.com/eu-small.png)](https://european-union.europa.eu/) 6 | 7 | The library supports the HTML 5 History API only. __This plugin is a little big cannon for the web development__. Works only with jQuery. 8 | 9 | - `>= jQuery +1.7` 10 | - works in `IE 9+` 11 | - easy to use 12 | - minified only 3 kB 13 | - great functionality 14 | - no dependencies 15 | - best of use with [www.totaljs.com - web framework for Node.js](http://www.totaljs.com) 16 | - works with `async` 17 | 18 | __YOU MUST SEE:__ 19 | 20 | - [jComponent - A component library for jQuery](https://github.com/petersirka/jComponent) 21 | - [Tangular - A template engine like Angular.js](https://github.com/petersirka/Tangular) 22 | - [jQuery two way bindings](https://github.com/petersirka/jquery.bindings) 23 | 24 | ## Simple example 25 | 26 | ```js 27 | // =========================== 28 | // DEFINE ROUTING 29 | // =========================== 30 | 31 | // jRouting === global variable 32 | jRouting.route('/homepage/', view_homepage, init_homepage); 33 | jRouting.route('/products/{category}/', view_products, ['data']); 34 | 35 | // ROLES + OPTIONS 36 | // v4.0 for jComponent and v3.0 classic version 37 | jRouting.route('/secure/area/', view_products, ['auth', '@rolename1', '@rolename2', { custom: 'options' }]); 38 | 39 | jRouting.middleware('auth', function(next, options, roles) { 40 | console.log(options); 41 | // --> { custom: 'options' } 42 | console.log(roles); 43 | // --> ['rolename1', 'rolename2'] 44 | }); 45 | 46 | 47 | // Supports HASHTAG routes 48 | jRouting.route('#users', view_homepage, init_homepage); 49 | jRouting.route('#products', view_homepage, init_homepage); 50 | // jRouting.redirect('#users'); 51 | 52 | // =========================== 53 | // DEFINE MIDDLEWARE 54 | // =========================== 55 | 56 | jRouting.middleware('data', function(next) { 57 | next(); 58 | // next(new Error('Some error')) 59 | // IMPORTANT: jRouting won't execute any next middleware and a target route 60 | }); 61 | 62 | // =========================== 63 | // DEFINE VIEWS 64 | // =========================== 65 | 66 | function view_homepage() { 67 | var self = this; 68 | // self === jRouting 69 | $('#content').html('HOMEPAGE'); 70 | } 71 | 72 | function init_homepage(next) { 73 | // is executed one time 74 | next(); 75 | } 76 | 77 | function view_products(category) { 78 | // self === jRouting 79 | $('#content').html('PRODUCTS –> ' + category); 80 | } 81 | 82 | // =========================== 83 | // DEFINE EVENTS 84 | // =========================== 85 | 86 | jRouting.on('location', function(url) { 87 | var menu = $('.menu'); 88 | menu.find('.selected').removeClass('selected'); 89 | menu.find('a[href="' + url + '"]').parent().addClass('selected'); 90 | }); 91 | ``` 92 | 93 | ## Properties 94 | 95 | #### jRouting.url; 96 | 97 | > {String} - Current URL address. 98 | 99 | ```js 100 | console.log(jRouting.url); 101 | ``` 102 | 103 | #### jRouting.version; 104 | 105 | > {Number} - Current framework version. 106 | 107 | ```js 108 | console.log(jRouting.version); 109 | ``` 110 | 111 | #### jRouting.history; 112 | 113 | > {String Array} - History list (LIMIT_HISTORY === 100). 114 | 115 | ```js 116 | console.log(jRouting.history); 117 | ``` 118 | 119 | #### jRouting.errors; 120 | 121 | > {String Array} - Error list (LIMIT_HISTORY_ERROR === 100). 122 | 123 | ```js 124 | console.log(jRouting.errors); 125 | ``` 126 | 127 | #### jRouting.global; 128 | 129 | > {Empty object} - Temporary global object for storing a temporary data. 130 | 131 | ```js 132 | jRouting.global.secret = 'AbcaDUIAZ349'; 133 | jRouting.global.name = 'total.js'; 134 | ``` 135 | 136 | #### jRouting.repository; 137 | 138 | > {Empty Object} - A temporary object for the current location. This property remembers last state for the URL. 139 | 140 | ```js 141 | jRouting.repository.title = 'jRouting'; 142 | ``` 143 | 144 | #### jRouting.model; 145 | 146 | > {Object} - model for the current location. 147 | 148 | ```js 149 | jRouting.redirect('/new-url/', { name: 'jRouting '}); 150 | 151 | // --> view 152 | 153 | function view_new_url() { 154 | var self = this; 155 | console.log(self.model); // --> model.name: jRouting 156 | } 157 | ``` 158 | 159 | #### jRouting.query; 160 | 161 | > {Object} - Get the current params from the URL address (url -> query). After redirect or refresh are params re-loaded. 162 | 163 | ```js 164 | // ---> /current-page/?q=jComponent 165 | console.log(jRouting.query.q); 166 | ``` 167 | 168 | ## Methods 169 | 170 | #### jRouting.route(path, fn, [middleware], [init]) 171 | 172 | > Create a route. 173 | 174 | ```js 175 | jRouting.route('/', view_homepage); 176 | jRouting.route('/products/{category}/', view_products, ['middleware']); 177 | jRouting.route('/products/{category}/', view_products, ['middleware'], function(next) { 178 | // initialization function 179 | next(); 180 | }); 181 | 182 | // OR 183 | ROUTE('/', view_homepage); 184 | ``` 185 | 186 | #### jRouting.middleware(name, fn) 187 | 188 | > Create a middleware 189 | 190 | ```js 191 | jRouting.middleware('latest', function(next, options, roles) { 192 | // continue 193 | next(); 194 | }); 195 | ``` 196 | 197 | #### jRouting.redirect(url, [model]) 198 | 199 | > Redirect. 200 | 201 | ```js 202 | jRouting.redirect('/products/shoes/'); 203 | 204 | // or 205 | 206 | jRouting.redirect('/products/shoes/', { from: 'jeans', latest: true, custom: 'model' }); 207 | ``` 208 | 209 | #### jRouting.prev() 210 | 211 | > Returns the previouse URL address. 212 | 213 | ```javascript 214 | console.log(jRouting.prev()); 215 | ``` 216 | 217 | 218 | #### jRouting.back() 219 | 220 | > Goes back to previous URL. 221 | 222 | ```js 223 | jRouting.back(); 224 | ``` 225 | 226 | #### jRouting.refresh() 227 | 228 | > Refresh the current page. 229 | 230 | ```js 231 | jRouting.refresh(); 232 | ``` 233 | 234 | ## Events 235 | 236 | #### jRouting.on('ready') 237 | 238 | > Is the library ready? 239 | 240 | ```js 241 | jRouting.once('ready', function() { 242 | console.log('I\'m ready'); 243 | jRouting.redirect('/homepage/'); 244 | }); 245 | ``` 246 | 247 | #### jRouting.on('location') 248 | 249 | > Captures a new location. 250 | 251 | ```js 252 | jRouting.on('location', function(url) { 253 | console.log('new location --->', url); 254 | }); 255 | ``` 256 | 257 | #### jRouting.on('error') 258 | 259 | > Captures some error. 260 | 261 | ```js 262 | jRouting.on('error', function(error, url, description) { 263 | console.log('ERROR --->', error, url, description); 264 | }); 265 | ``` 266 | 267 | #### jRouting.on('status') 268 | 269 | > Captures a HTTP error. 270 | 271 | ```js 272 | jRouting.on('status', function(code, message) { 273 | switch (code) { 274 | case 404: 275 | console.log('NOT FOUND', message); 276 | break; 277 | case 500: 278 | console.log('INTERNAL ERROR', message); 279 | break; 280 | } 281 | }); 282 | ``` 283 | 284 | ## Assign links to jRouting 285 | 286 | __IMPORTANT__: doesn't work with hashtags. Hashtags doesn't need a prevention for redirecting. 287 | 288 | ```javascript 289 | jR.clientside('a.jrouting'); 290 | 291 | // or 292 | //
CLICK ON ME
293 | jR.clientside('div.jrouting'); 294 | 295 | ``` 296 | 297 | ## Alias: jRouting is too long as word 298 | 299 | ```javascript 300 | // use alias: 301 | // jR === jRouting 302 | jR.route('/', ...); 303 | ``` 304 | 305 | ## +v1.3.0 Async loading 306 | 307 | ```html 308 | 309 | 310 | ``` 311 | 312 | ```javascript 313 | if (!window.jRoute) 314 | window.jRoute = []; 315 | 316 | window.jRoute.push(function() { 317 | jRouting.route('/', function() { 318 | console.log('Classic route'); 319 | }); 320 | 321 | jRouting.route('#hashtag', function() { 322 | console.log('Hashtag'); 323 | }); 324 | }); 325 | ``` 326 | 327 | ## Contact 328 | 329 | Peter Širka - www.petersirka.eu / 330 | 331 | [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat 332 | [license-url]: license.txt --------------------------------------------------------------------------------