Skip to content

Commit fa6f944

Browse files
prushforthYushan Mu
and
Yushan Mu
committed
Add media attribute to map-layer, map-link, map-style
Remove prefers-map-content hardcoded demo hack Add implementation of _changeHandler that invokes _onAdd, _onRemove. Add media query matches to map-layer._validateDisabled Re-organize _onRemove so that stuff isn't deleted until it's no longer needed, Make call to registerMediaQuery(mq) conditional on there being a mq, make it the path through which a layer gets initialized only when that is true, so as to not perform _onAdd twice. Move detection of media query matches check out of core if (map) block in map-layer._validateDisabled() Remove vestige of old prefers-map-content demo implementation from both map-link.js and layer.js Make execution of map-extent's map-projectionchange handler conditional on the there being a parentLayer._layer property value. This specifically covers the case when this handler is invoked by the projection change event happening, in which the parent layer may be disabled due to a media condition and therefore there is no LayerGroup to add the extentLayer to. add map-layer media attribute as an observed attribute Make map-link._registerMediaQuery async, wait on mapml-viewer to be ready before trying to register a media query (which may depend on mapml-viewer.extent). Remove map-link media attribute from those attributes that are copied onto the 'rendered' <link> element, because the link element actually supports the media attribute, but not the media features we are designing (hence it always forces the element to be disabled). Update map-layer._registerMediaQuery so that when the observed media attribute is removed or set to the empty string, the map-layer goes through the initialization life cycle, as though it was newly connected to the DOM (it may go from disabled to enabled due to the removal). Clean up map-layer _registerMediaQuery Add tests for <map-layer media="..."> attribute Add tests for <map-link rel="stylesheet" media="..."> attributes Add test for <map-link rel="features" media="..."> attribute cycling Update linux image for map-link-media test Initial implementation of map-style media attribute Refactor enable/disableLink methods to _enable/_disableLink Update / fix test title for map-link (should not be 'map-layer') Add test for map-style media attribute. Update implementation of map-link and map-style rendering to take media attribute into account. Move renderStyles to its own file, import it and export as member func Co-authored-by: Yushan Mu <yushan.mu@nrcan-rncan.gc.ca>
1 parent 345de98 commit fa6f944

33 files changed

+699
-210
lines changed

src/layer.js

+81-77
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { createLayerControlHTML } from './mapml/elementSupport/layers/createLaye
66

77
export class BaseLayerElement extends HTMLElement {
88
static get observedAttributes() {
9-
return ['src', 'label', 'checked', 'hidden', 'opacity'];
9+
return ['src', 'label', 'checked', 'hidden', 'opacity', 'media'];
1010
}
1111
/* jshint ignore:start */
1212
#hasConnected;
@@ -53,6 +53,13 @@ export class BaseLayerElement extends HTMLElement {
5353
}
5454
}
5555

56+
get media() {
57+
return this.getAttribute('media');
58+
}
59+
set media(val) {
60+
this.setAttribute('media', val);
61+
}
62+
5663
get opacity() {
5764
// use ?? since 0 is falsy, || would return rhs in that case
5865
return +(this._opacity ?? this.getAttribute('opacity'));
@@ -114,24 +121,73 @@ export class BaseLayerElement extends HTMLElement {
114121
this._onAdd();
115122
}
116123
}
124+
break;
125+
case 'media':
126+
if (oldValue !== newValue) {
127+
this._registerMediaQuery(newValue);
128+
}
129+
break;
117130
}
118131
}
119132
}
133+
_registerMediaQuery(mq) {
134+
if (!this._changeHandler) {
135+
this._changeHandler = () => {
136+
this._onRemove();
137+
if (this._mql.matches) {
138+
this._onAdd();
139+
}
140+
// set the disabled 'read-only' attribute indirectly, via _validateDisabled
141+
this._validateDisabled();
142+
};
143+
}
144+
145+
if (mq) {
146+
// a new media query is being established
147+
let map = this.getMapEl();
148+
if (!map) return;
120149

150+
// Remove listener from the old media query (if it exists)
151+
if (this._mql) {
152+
this._mql.removeEventListener('change', this._changeHandler);
153+
}
154+
155+
this._mql = map.matchMedia(mq);
156+
this._changeHandler();
157+
this._mql.addEventListener('change', this._changeHandler);
158+
} else if (this._mql) {
159+
// the media attribute removed or query set to ''
160+
this._mql.removeEventListener('change', this._changeHandler);
161+
delete this._mql;
162+
// effectively, no / empty media attribute matches, do what changeHandler does
163+
this._onRemove();
164+
this._onAdd();
165+
this._validateDisabled();
166+
}
167+
}
168+
getMapEl() {
169+
return Util.getClosest(this, 'mapml-viewer,map[is=web-map]');
170+
}
121171
constructor() {
122172
// Always call super first in constructor
123173
super();
124174
// this._opacity is used to record the current opacity value (with or without updates),
125175
// the initial value of this._opacity should be set as opacity attribute value, if exists, or the default value 1.0
126176
this._opacity = this.opacity || 1.0;
127-
this._renderingMapContent = M.options.contentPreference;
128177
this.attachShadow({ mode: 'open' });
129178
}
130179
disconnectedCallback() {
131180
// if the map-layer node is removed from the dom, the layer should be
132181
// removed from the map and the layer control
133182
if (this.hasAttribute('data-moving')) return;
134183
this._onRemove();
184+
185+
if (this._mql) {
186+
if (this._changeHandler) {
187+
this._mql.removeEventListener('change', this._changeHandler);
188+
}
189+
delete this._mql;
190+
}
135191
}
136192

137193
_onRemove() {
@@ -141,13 +197,6 @@ export class BaseLayerElement extends HTMLElement {
141197
let l = this._layer,
142198
lc = this._layerControl,
143199
lchtml = this._layerControlHTML;
144-
// remove properties of layer involved in whenReady() logic
145-
delete this._layer;
146-
delete this._layerControl;
147-
delete this._layerControlHTML;
148-
delete this._fetchError;
149-
this.shadowRoot.innerHTML = '';
150-
if (this.src) this.innerHTML = '';
151200

152201
if (l) {
153202
l.off();
@@ -158,8 +207,16 @@ export class BaseLayerElement extends HTMLElement {
158207
}
159208

160209
if (lc && !this.hidden) {
210+
// lc.removeLayer depends on this._layerControlHTML, can't delete it until after
161211
lc.removeLayer(l);
162212
}
213+
// remove properties of layer involved in whenReady() logic
214+
delete this._layer;
215+
delete this._layerControl;
216+
delete this._layerControlHTML;
217+
delete this._fetchError;
218+
this.shadowRoot.innerHTML = '';
219+
if (this.src) this.innerHTML = '';
163220
}
164221

165222
connectedCallback() {
@@ -170,11 +227,17 @@ export class BaseLayerElement extends HTMLElement {
170227
this._createLayerControlHTML = createLayerControlHTML.bind(this);
171228
const doConnected = this._onAdd.bind(this);
172229
const doRemove = this._onRemove.bind(this);
230+
const registerMediaQuery = this._registerMediaQuery.bind(this);
231+
let mq = this.media;
173232
this.parentElement
174233
.whenReady()
175234
.then(() => {
176235
doRemove();
177-
doConnected();
236+
if (mq) {
237+
registerMediaQuery(mq);
238+
} else {
239+
doConnected();
240+
}
178241
})
179242
.catch((error) => {
180243
throw new Error('Map never became ready: ' + error);
@@ -189,20 +252,11 @@ export class BaseLayerElement extends HTMLElement {
189252
e.stopPropagation();
190253
// if user changes the style in layer control
191254
if (e.detail) {
192-
this._renderingMapContent = e.detail._renderingMapContent;
193255
this.src = e.detail.src;
194256
}
195257
},
196258
{ once: true }
197259
);
198-
this.addEventListener(
199-
'zoomchangesrc',
200-
function (e) {
201-
e.stopPropagation();
202-
this.src = e.detail.href;
203-
},
204-
{ once: true }
205-
);
206260
let base = this.baseURI ? this.baseURI : document.baseURI;
207261
const headers = new Headers();
208262
headers.append('Accept', 'text/mapml');
@@ -240,7 +294,6 @@ export class BaseLayerElement extends HTMLElement {
240294
.then(() => {
241295
// may throw:
242296
this.selectAlternateOrChangeProjection();
243-
this.checkForPreferredContent();
244297
})
245298
.then(() => {
246299
this._layer = mapMLLayer(new URL(this.src, base).href, this, {
@@ -278,7 +331,6 @@ export class BaseLayerElement extends HTMLElement {
278331
.then(() => {
279332
// may throw:
280333
this.selectAlternateOrChangeProjection();
281-
this.checkForPreferredContent();
282334
})
283335
.then(() => {
284336
this._layer = mapMLLayer(null, this, {
@@ -317,13 +369,6 @@ export class BaseLayerElement extends HTMLElement {
317369
);
318370
this.parentElement.projection = e.cause.mapprojection;
319371
}
320-
} else if (e.message === 'findmatchingpreferredcontent') {
321-
if (e.cause.href) {
322-
console.log(
323-
'Changing layer to matching preferred content at: ' + e.cause.href
324-
);
325-
this.src = e.cause.href;
326-
}
327372
} else if (e.message === 'Failed to fetch') {
328373
// cut short whenReady with the _fetchError property
329374
this._fetchError = true;
@@ -372,23 +417,6 @@ export class BaseLayerElement extends HTMLElement {
372417
}
373418
}
374419

375-
checkForPreferredContent() {
376-
let mapml = this.src ? this.shadowRoot : this;
377-
let availablePreferMapContents = mapml.querySelector(
378-
`map-link[rel="style"][media="prefers-map-content=${this._renderingMapContent}"][href]`
379-
);
380-
if (availablePreferMapContents) {
381-
// resolve href
382-
let url = new URL(
383-
availablePreferMapContents.getAttribute('href'),
384-
availablePreferMapContents.getBase()
385-
).href;
386-
throw new Error('findmatchingpreferredcontent', {
387-
cause: { href: url }
388-
});
389-
}
390-
}
391-
392420
copyRemoteContentToShadowRoot(mapml) {
393421
let shadowRoot = this.shadowRoot;
394422
// get the map-meta[name=projection/cs/extent/zoom] from map-head of remote mapml, attach them to the shadowroot
@@ -454,12 +482,12 @@ export class BaseLayerElement extends HTMLElement {
454482
};
455483
const _addStylesheetLink = (mapLink) => {
456484
this.whenReady().then(() => {
457-
this._layer.appendStyleLink(mapLink);
485+
this._layer.renderStyles(mapLink);
458486
});
459487
};
460488
const _addStyleElement = (mapStyle) => {
461489
this.whenReady().then(() => {
462-
this._layer.appendStyleElement(mapStyle);
490+
this._layer.renderStyles(mapStyle);
463491
});
464492
};
465493
const _addExtentElement = (mapExtent) => {
@@ -610,8 +638,13 @@ export class BaseLayerElement extends HTMLElement {
610638
setTimeout(() => {
611639
let layer = this._layer,
612640
map = layer?._map;
641+
// if there's a media query in play, check it early
642+
if (this._mql && !this._mql.matches) {
643+
this.setAttribute('disabled', '');
644+
this.disabled = true;
645+
return;
646+
}
613647
if (map) {
614-
this._validateLayerZoom({ zoom: map.getZoom() });
615648
// prerequisite: no inline and remote mapml elements exists at the same time
616649
const mapExtents = this.src
617650
? this.shadowRoot.querySelectorAll('map-extent')
@@ -664,35 +697,6 @@ export class BaseLayerElement extends HTMLElement {
664697
}
665698
}, 0);
666699
}
667-
_validateLayerZoom(e) {
668-
// get the min and max zooms from all extents
669-
let toZoom = e.zoom;
670-
let min = this.extent.zoom.minZoom;
671-
let max = this.extent.zoom.maxZoom;
672-
let inLink = this.src
673-
? this.shadowRoot.querySelector('map-link[rel=zoomin]')
674-
: this.querySelector('map-link[rel=zoomin]'),
675-
outLink = this.src
676-
? this.shadowRoot.querySelector('map-link[rel=zoomout]')
677-
: this.querySelector('map-link[rel=zoomout]');
678-
let targetURL;
679-
if (!(min <= toZoom && toZoom <= max)) {
680-
if (inLink && toZoom > max) {
681-
targetURL = inLink.href;
682-
} else if (outLink && toZoom < min) {
683-
targetURL = outLink.href;
684-
}
685-
if (targetURL) {
686-
this.dispatchEvent(
687-
new CustomEvent('zoomchangesrc', {
688-
detail: {
689-
href: targetURL
690-
}
691-
})
692-
);
693-
}
694-
}
695-
}
696700
// disable/italicize layer control elements based on the map-layer.disabled property
697701
toggleLayerControlDisabled() {
698702
let input = this._layerControlCheckbox,

src/map-extent.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -311,12 +311,12 @@ export class HTMLExtentElement extends HTMLElement {
311311
};
312312
const _addStylesheetLink = (mapLink) => {
313313
this.whenReady().then(() => {
314-
this._extentLayer.appendStyleLink(mapLink);
314+
this._extentLayer.renderStyles(mapLink);
315315
});
316316
};
317317
const _addStyleElement = (mapStyle) => {
318318
this.whenReady().then(() => {
319-
this._extentLayer.appendStyleElement(mapStyle);
319+
this._extentLayer.renderStyles(mapStyle);
320320
});
321321
};
322322
for (let i = 0; i < elementsGroup.length; ++i) {
@@ -431,7 +431,7 @@ export class HTMLExtentElement extends HTMLElement {
431431

432432
_handleChange() {
433433
// add _extentLayer to map if map-extent is checked, otherwise remove it
434-
if (this.checked && !this.disabled) {
434+
if (this.checked && !this.disabled && this.parentLayer._layer) {
435435
// can be added to mapmllayer layerGroup no matter map-layer is checked or not
436436
this._extentLayer.addTo(this.parentLayer._layer);
437437
this._extentLayer.setZIndex(

0 commit comments

Comments
 (0)