-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlocation-element.js
110 lines (100 loc) · 4.02 KB
/
location-element.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
const attr = ( el, attr )=> el.getAttribute( attr );
let originalHistory;
function ensureTrackLocationChange()
{ if( originalHistory )
return;
originalHistory = {};
'back,forward,go,pushState,replaceState'.split(',').forEach( k =>
{
originalHistory[ k ] = history[ k ];
history[ k ] = function(...rest )
{
originalHistory[k].apply( history, rest );
window.dispatchEvent( new CustomEvent('dce-location',{detail:{ k }}) );
}
});
}
const methods =
{ 'location.href' : src => window.location.href = src
, 'location.hash' : src => window.location.hash = src
, 'location.assign' : src => window.location.assign( src )
, 'location.replace' : src => window.location.replace( src )
, 'history.pushState' : src => window.history.pushState( {}, "", src )
, 'history.replaceState' : src => window.history.replaceState( {}, "", src )
};
export class LocationElement extends HTMLElement
{
static observedAttributes=
[ 'value' // populated from url
, 'slice'
, 'href' // url to be parsed. When omitted window.location is used.
, 'type' // `text|json`, defaults to text, other types are compatible with INPUT field
, 'live' // monitors history change, applicable only when href is omitted.
, 'src' // sets the URL
, 'method' // when defined, changes URL by one of predefined methods.
];
constructor()
{
super();
const state = {}
, listener = () => setTimeout( propagateSlice,1 )
, propagateSlice = ()=>
{ const urlStr = attr(this,'href')
if(!urlStr)
ensureTrackLocationChange();
const url = urlStr? new URL(urlStr, window.location) : window.location;
const params= {}
const search = new URLSearchParams(url.search);
for (const key of search.keys())
params[key] = search.getAll(key)
const detail = {params}
for( const k in url )
{ if ('string' === typeof url[k])
detail[k] = url[k]
}
this.value = detail;
this.dispatchEvent( new Event('change') );
};
this.sliceInit = s =>
{
if( this.hasAttribute('method') )
{
const method = this.getAttribute('method');
const src = this.getAttribute('src');
if( method && src )
if( method === 'location.hash' )
{ if( src !== window.location.hash )
methods[ method ]?.( src );
}else if( window.location.href !== new URL(src, window.location).href )
methods[method]?.(src);
}
if( !state.listener && this.hasAttribute('live') )
{ state.listener = 1;
window.navigation?.addEventListener("navigate", listener );
window.addEventListener( 'popstate' , listener );
window.addEventListener( 'hashchange' , listener );
window.addEventListener( 'dce-location' , listener );
}
propagateSlice();
return s || {}
}
this._destroy = ()=>
{
window.removeEventListener('popstate' , listener);
window.removeEventListener('hashchange' , listener);
window.removeEventListener('dce-location', listener);
delete state.listener;
};
}
attributeChangedCallback(name, oldValue, newValue)
{
if('href'!== name && 'method' !== name && 'src' )
if( !['method','src','href'].includes(name) )
return;
this.sliceInit && this.sliceInit();
}
connectedCallback(){ this.sliceInit() }
disconnectedCallback(){ this._destroy() }
}
window.customElements.define( 'location-element', LocationElement );
export default LocationElement;