UNPKG

mobx-location

Version:

minimal observable wrapper around browser location utilizing popstate event of HTML5 history api

129 lines (106 loc) 4.27 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var mobx = require('mobx'); var queryString = _interopDefault(require('query-string')); require('history-events'); var propsToMirror = ['hash', 'host', 'hostname', 'href', 'origin', 'pathname', 'port', 'protocol', 'search']; var _window = window, location = _window.location; var mobxLocation = (function (_ref) { var hashHistory = _ref.hashHistory, _ref$arrayFormat = _ref.arrayFormat, arrayFormat = _ref$arrayFormat === void 0 ? 'bracket' : _ref$arrayFormat; var createSnapshot = function createSnapshot(previousQuery) { var snapshot = propsToMirror.reduce(function (snapshot, prop) { snapshot[prop] = location[prop]; return snapshot; }, {}); var q; if (hashHistory) { q = queryString.parse(snapshot.hash.split('?')[1], { arrayFormat: arrayFormat }); } else { q = queryString.parse(snapshot.search, { arrayFormat: arrayFormat }); } snapshot.query = q || {}; return snapshot; }; var firstSnapshot = createSnapshot(); var locationObservable = mobx.observable(firstSnapshot); /** * executes each time a mobxLocation.query is mutated */ var propagateQueryToLocationSearch = function propagateQueryToLocationSearch() { var queryInObservable = queryString.stringify(mobx.toJS(locationObservable.query), { encode: false, arrayFormat: arrayFormat }); // console.log('currentlyInObservable: ', currentlyInObservable) var search = location.search, protocol = location.protocol, host = location.host, pathname = location.pathname, hash = location.hash; var qs = search; var hashParts = hash.split('?'); if (hashHistory && hash.includes('?')) { qs = hashParts[1]; } if (!qs && !queryInObservable) { return; } if (decodeURI(qs) === queryInObservable) { return; } if (qs !== queryInObservable) { var newUrl = protocol + '//' + host + pathname; if (hashHistory) { var newHash = hashParts[0] + '?' + queryInObservable; locationObservable.hash = newHash; newUrl += newHash; } else { var newSearch = '?' + queryInObservable + hash; locationObservable.search = newSearch; newUrl += newSearch; } locationObservable.href = newUrl; window.removeEventListener('changestate', snapshotAndSet); // console.log('newUrl: ', newUrl) newUrl = encodeURI(newUrl); history.replaceState(null, '', newUrl); window.addEventListener('changestate', snapshotAndSet); } }; var unsubscribe = mobx.autorun(propagateQueryToLocationSearch); var snapshotAndSet = mobx.action('changestateHandler', function (ev) { var snapshot = createSnapshot(mobx.toJS(locationObservable.query)); var currentlyInObservable = mobx.toJS(locationObservable); //unfortunately we need to check that the new snapshot is different-for example when integrating with angularjs it happens that angular router is setting a URL again after we changed it via interacting with observable if (snapshot.href !== currentlyInObservable && decodeURI(snapshot.href) !== locationObservable.href) { mobx.set(locationObservable, snapshot); } }); mobx.observe(locationObservable, function (change) { var name = change.name; if (name !== 'href') { return; // we ignore these } if (decodeURI(location.href) === change.newValue) { return; } if (location.href !== change.newValue) { var search = locationObservable.search, protocol = locationObservable.protocol, host = locationObservable.host, pathname = locationObservable.pathname, hash = locationObservable.hash; var newUrl = encodeURI(protocol + '//' + host + pathname + search + hash); window.removeEventListener('changestate', snapshotAndSet); history.replaceState(null, '', newUrl); window.addEventListener('changestate', snapshotAndSet); } }); window.addEventListener('changestate', snapshotAndSet); return locationObservable; }); module.exports = mobxLocation;