router.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /* global deepEqual */// toolbox.js
  2. /* global msg */
  3. 'use strict';
  4. const router = {
  5. buffer: [],
  6. watchers: [],
  7. getSearch(key) {
  8. return new URLSearchParams(location.search).get(key);
  9. },
  10. update(replace) {
  11. const {buffer} = router;
  12. if (!buffer.length) {
  13. buffer.push(location.href);
  14. } else if (buffer[buffer.length - 1] === location.href) {
  15. return;
  16. } else if (replace) {
  17. buffer[buffer.length - 1] = location.href;
  18. } else if (buffer.length > 1 && buffer[buffer.length - 2] === location.href) {
  19. buffer.pop();
  20. } else {
  21. buffer.push(location.href);
  22. }
  23. for (const {options, callback} of router.watchers) {
  24. let state;
  25. if (options.hash) {
  26. state = options.hash === location.hash;
  27. } else if (options.search) {
  28. const search = new URLSearchParams(location.search);
  29. state = options.search.map(key => search.get(key));
  30. }
  31. if (!deepEqual(state, options.currentState)) {
  32. options.currentState = state;
  33. callback(state);
  34. }
  35. }
  36. },
  37. /**
  38. * @param {string} hash - empty string removes the hash
  39. */
  40. updateHash(hash) {
  41. const {buffer} = router;
  42. if (buffer.length > 1) {
  43. if (!hash && !buffer[buffer.length - 2].includes('#') ||
  44. hash && buffer[buffer.length - 2].endsWith(hash)) {
  45. history.back();
  46. return;
  47. }
  48. }
  49. if (!hash) {
  50. hash = ' ';
  51. }
  52. history.pushState(history.state, null, hash);
  53. router.update();
  54. },
  55. updateSearch(key, value) {
  56. const u = new URL(location);
  57. u.searchParams[value ? 'set' : 'delete'](key, value);
  58. history.replaceState(history.state, null, `${u}`);
  59. router.update(true);
  60. },
  61. watch(options, callback) {
  62. /* Watch search params or hash and get notified on change.
  63. options: {search?: Array<key: String>, hash?: String}
  64. callback: (Array<value: String | null> | Boolean) => void
  65. `hash` should always start with '#'.
  66. When watching search params, callback receives a list of values.
  67. When watching hash, callback receives a boolean.
  68. */
  69. router.watchers.push({options, callback});
  70. },
  71. };
  72. document.on('DOMContentLoaded', () => router.update());
  73. window.on('popstate', () => router.update());
  74. window.on('hashchange', () => router.update());
  75. msg.on(e => {
  76. if (e.method === 'pushState' && e.url !== location.href) {
  77. history.pushState(history.state, null, e.url);
  78. router.update();
  79. return true;
  80. }
  81. });