Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable hash #8596 #8603

Merged
merged 7 commits into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions src/ui/hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import type Map from './map';
class Hash {
_map: Map;
_updateHash: () => ?TimeoutID;
_hashName: ?string;

constructor() {
constructor(hashName: ?string) {
this._hashName = hashName && encodeURIComponent(hashName);
bindAll([
'_getCurrentHash',
'_onHashChange',
'_updateHash'
], this);
Expand Down Expand Up @@ -67,19 +70,50 @@ class Hash {
if (mapFeedback) {
// new map feedback site has some constraints that don't allow
// us to use the same hash format as we do for the Map hash option.
hash += `#/${lng}/${lat}/${zoom}`;
hash += `/${lng}/${lat}/${zoom}`;
} else {
hash += `#${zoom}/${lat}/${lng}`;
hash += `${zoom}/${lat}/${lng}`;
}

if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`);
if (pitch) hash += (`/${Math.round(pitch)}`);
return hash;

if (this._hashName) {
const hashName = this._hashName;
let found = false;
const parts = window.location.hash.slice(1).split('&').map(part => {
const key = part.split('=')[0];
if (key === hashName) {
found = true;
return `${key}=${hash}`;
}
return part;
}).filter(a => a);
if (!found) {
parts.push(`${hashName}=${hash}`);
}
return `#${parts.join('&')}`;
}

return `#${hash}`;
}

_getCurrentHash() {
// Get the current hash from location, stripped from its number sign
const hash = window.location.hash.replace('#', '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment on this just to make it a little easier to grasp at a quick glance?

if (this._hashName) {
// Split the parameter-styled hash into parts and find the value we need
const keyval = hash.split('&').map(
part => part.split('=')
).find(part => part[0] === this._hashName);
return (keyval ? keyval[1] || '' : '').split('/');
ryanhamley marked this conversation as resolved.
Show resolved Hide resolved
}
return hash.split('/');
}

_onHashChange() {
const loc = window.location.hash.replace('#', '').split('/');
if (loc.length >= 3) {
const loc = this._getCurrentHash();
if (loc.length >= 3 && !loc.some(v => isNaN(v))) {
this._map.jumpTo({
center: [+loc[2], +loc[1]],
zoom: +loc[0],
Expand Down
10 changes: 7 additions & 3 deletions src/ui/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type IControl = {
/* eslint-enable no-use-before-define */

type MapOptions = {
hash?: boolean,
hash?: boolean | string,
interactive?: boolean,
container: HTMLElement | string,
bearingSnap?: number,
Expand Down Expand Up @@ -171,8 +171,11 @@ const defaultOptions = {
* Tilesets hosted with Mapbox can be style-optimized if you append `?optimize=true` to the end of your style URL, like `mapbox://styles/mapbox/streets-v9?optimize=true`.
* Learn more about style-optimized vector tiles in our [API documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-tiles).
*
* @param {boolean} [options.hash=false] If `true`, the map's position (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page's URL.
* @param {(boolean|string)} [options.hash=false] If `true`, the map's position (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page's URL.
* For example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`.
* An additional string may optionally be provided to indicate a parameter-styled hash,
* e.g. http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where foo
* is a custom parameter and bar is an arbitrary hash distinct from the map hash.
* @param {boolean} [options.interactive=true] If `false`, no mouse, touch, or keyboard listeners will be attached to the map, so it will not respond to interaction.
* @param {number} [options.bearingSnap=7] The threshold, measured in degrees, that determines when the map's
* bearing will snap to north. For example, with a `bearingSnap` of 7, if the user rotates
Expand Down Expand Up @@ -378,7 +381,8 @@ class Map extends Camera {

bindHandlers(this, options);

this._hash = options.hash && (new Hash()).addTo(this);
const hashName = (typeof options.hash === 'string' && options.hash) || undefined;
this._hash = options.hash && (new Hash(hashName)).addTo(this);
// don't set position from options if set through hash
if (!this._hash || !this._hash._onHashChange()) {
this.jumpTo({
Expand Down
129 changes: 127 additions & 2 deletions test/unit/ui/hash.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import window from '../../../src/util/window';
import { createMap as globalCreateMap } from '../../util';

test('hash', (t) => {
function createHash() {
const hash = new Hash();
function createHash(name) {
SebCorbin marked this conversation as resolved.
Show resolved Hide resolved
const hash = new Hash(name);
hash._updateHash = hash._updateHashUnthrottled.bind(hash);
return hash;
}
Expand Down Expand Up @@ -67,6 +67,14 @@ test('hash', (t) => {
t.equal(map.getBearing(), 30);
t.equal(map.getPitch(), 60);

window.location.hash = '#4/wrongly/formed/hash';

t.false(hash._onHashChange());

window.location.hash = '#map=10/3.00/-1.00&foo=bar';

t.false(hash._onHashChange());

window.location.hash = '';

t.end();
Expand Down Expand Up @@ -100,6 +108,82 @@ test('hash', (t) => {
t.end();
});

t.test('#_onHashChange named', (t) => {
const map = createMap(t);
const hash = createHash('map')
.addTo(map);

window.location.hash = '#map=10/3.00/-1.00&foo=bar';

hash._onHashChange();

t.equal(map.getCenter().lng, -1);
t.equal(map.getCenter().lat, 3);
t.equal(map.getZoom(), 10);
t.equal(map.getBearing(), 0);
t.equal(map.getPitch(), 0);

window.location.hash = '#map&foo=bar';

t.false(hash._onHashChange());

window.location.hash = '#map=4/5/baz&foo=bar';

t.false(hash._onHashChange());

window.location.hash = '#5/1.00/0.50/30/60';

t.false(hash._onHashChange());

window.location.hash = '';

t.end();
});

t.test('#_getCurrentHash', (t) => {
const map = createMap(t);
const hash = createHash()
.addTo(map);

window.location.hash = '#10/3.00/-1.00';

const currentHash = hash._getCurrentHash();

t.equal(currentHash[0], '10');
t.equal(currentHash[1], '3.00');
t.equal(currentHash[2], '-1.00');

window.location.hash = '';

t.end();
});

t.test('#_getCurrentHash named', (t) => {
const map = createMap(t);
const hash = createHash('map')
.addTo(map);

window.location.hash = '#map=10/3.00/-1.00&foo=bar';

let currentHash = hash._getCurrentHash();

t.equal(currentHash[0], '10');
t.equal(currentHash[1], '3.00');
t.equal(currentHash[2], '-1.00');

window.location.hash = '#baz&map=10/3.00/-1.00';

currentHash = hash._getCurrentHash();

t.equal(currentHash[0], '10');
t.equal(currentHash[1], '3.00');
t.equal(currentHash[2], '-1.00');

window.location.hash = '';

t.end();
});

t.test('#_updateHash', (t) => {
function getHash() {
return window.location.hash.split('/');
Expand Down Expand Up @@ -145,6 +229,47 @@ test('hash', (t) => {
t.equal(newHash[3], '135');
t.equal(newHash[4], '60');

window.location.hash = '';

t.end();
});

t.test('#_updateHash named', (t) => {
const map = createMap(t);
createHash('map')
.addTo(map);

t.notok(window.location.hash);

map.setZoom(3);
map.setCenter([1.0, 2.0]);

t.ok(window.location.hash);

t.equal(window.location.hash, '#map=3/2/1');

map.setPitch(60);

t.equal(window.location.hash, '#map=3/2/1/0/60');

map.setBearing(135);

t.equal(window.location.hash, '#map=3/2/1/135/60');

window.location.hash += '&foo=bar';

map.setZoom(7);

t.equal(window.location.hash, '#map=7/2/1/135/60&foo=bar');

window.location.hash = '#baz&map=7/2/1/135/60&foo=bar';

map.setCenter([2.0, 1.0]);

t.equal(window.location.hash, '#baz&map=7/1/2/135/60&foo=bar');

window.location.hash = '';

t.end();
});

Expand Down