nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

hashState.js (3305B)


      1 function arrayItemsEqual(l1, l2) {
      2     if (l1.length !== l2.length) {
      3         return false;
      4     }
      5     for (var i = 0; i < l1.length; i++) {
      6         if (l1[i] !== l2[i]) {
      7             return false;
      8         }
      9     }
     10     return true;
     11 }
     12 
     13 function parseHashParams(s) {
     14     const args = {},
     15         i = s.indexOf('#');
     16     if (i >= 0) {
     17         s = s.substr(i + 1).trim();
     18         let m, key, value;
     19         for (let pair of s.split('&')) {
     20             m = /^([^=]+?)(?:=(.*))?$/u.exec(pair);
     21             if (m) {
     22                 [, key, value] = m;
     23                 if (value) {
     24                     value = value.split('/');
     25                 } else {
     26                     value = [];
     27                 }
     28                 args[key] = value;
     29             }
     30         }
     31     }
     32     return args;
     33 }
     34 
     35 const hashState = {
     36     _listeners: [],
     37     _state: {},
     38 
     39     addEventListener: function(key, callback) {
     40         for (let [k, c] of this._listeners) {
     41             if (k === key && c === callback) {
     42                 return;
     43             }
     44         }
     45         this._listeners.push([key, callback]);
     46     },
     47 
     48     updateState: function(key, values) {
     49         if (values) {
     50             this._state[key] = values;
     51         } else {
     52             delete this._state[key];
     53         }
     54         this._saveStateToHash();
     55     },
     56 
     57     getState: function(key) {
     58         return this._state[key];
     59     },
     60 
     61     hasKey: function(key) {
     62         return this._state.hasOwnProperty(key);
     63     },
     64 
     65     _saveStateToHash: function() {
     66         var stateItems = [];
     67         for (let key of Object.keys(this._state)) {
     68             if (this._state[key]) {
     69                 if (this._state[key].length) {
     70                     var values = this._state[key].join('/');
     71                     stateItems.push(key + '=' + values);
     72                 } else {
     73                     stateItems.push(key);
     74                 }
     75             }
     76         }
     77         const hash = stateItems.join('&');
     78         const href = `${location.origin}${location.pathname}${location.search}#${hash}`;
     79         this._ignoreChanges = true;
     80         if (href !== location.href) {
     81             location.replace(href);
     82         }
     83         this._ignoreChanges = false;
     84     },
     85 
     86     onHashChanged: function() {
     87         if (this._ignoreChanges) {
     88             return;
     89         }
     90         const newState = parseHashParams(location.hash);
     91         const changedKeys = {};
     92         for (let key of Object.keys(newState)) {
     93             if (!(key in this._state) || !arrayItemsEqual(newState[key], this._state[key])) {
     94                 changedKeys[key] = 1;
     95             }
     96         }
     97 
     98         for (let key of Object.keys(this._state)) {
     99             if (!(key in newState)) {
    100                 changedKeys[key] = 1;
    101             }
    102         }
    103 
    104         for (let [key, callback] of this._listeners) {
    105             if (key in changedKeys) {
    106                 callback(newState[key]);
    107             }
    108         }
    109         this._state = newState;
    110     }
    111 };
    112 
    113 function bindHashStateReadOnly(key, target) {
    114     function onChange() {
    115         target(hashState.getState(key));
    116         hashState.updateState(key, null);
    117     }
    118     hashState.addEventListener(key, onChange);
    119     onChange();
    120 }
    121 
    122 window.addEventListener('hashchange', hashState.onHashChanged.bind(hashState));
    123 hashState.onHashChanged();
    124 
    125 export {hashState, bindHashStateReadOnly, parseHashParams};
    126