diff --git a/scripts/construct-leaflet-map.js b/scripts/construct-leaflet-map.js
index e7fc82a..73d2f43 100644
--- a/scripts/construct-leaflet-map.js
+++ b/scripts/construct-leaflet-map.js
@@ -200,10 +200,29 @@
return output;
};
+ /**
+ * Trim whitespace
+ * @param {string} a
+ * @returns string
+ */
function trim(a) {
return a.trim ? a.trim() : a.replace(/^\s+|\s+$/gm, '');
}
+ /**
+ * Gets string of anchor for Leaflet's consumption
+ * @param {Record<'href' | 'textContent', string>} atts
+ */
+ function makeStringLink(atts) {
+ const a = document.createElement('a');
+ a.href = atts.href;
+ a.textContent = atts.textContent;
+ a.target = '_blank';
+ a.rel = 'noopener noreferrer';
+
+ return a.outerHTML;
+ }
+
function addAttributionToMap(attribution, map) {
if (!attribution) {
return;
@@ -218,6 +237,19 @@
for (var i = 0, len = attributions.length; i < len; i++) {
var att = trim(attributions[i]);
+
+ // add liquid-style attribution: {WP | link: https://wordpress.com}
+ att = liquid(att, function (match, obj) {
+ if (obj.link) {
+ return makeStringLink({
+ href: obj.link,
+ textContent: obj.key,
+ });
+ }
+
+ return match;
+ });
+
attControl.addAttribution(att);
}
}
@@ -228,8 +260,6 @@
return div.innerText || str;
});
- var templateRe = /\{ *(.*?) *\}/g;
-
/**
* It interpolates variables in curly brackets (regex above)
*
@@ -243,8 +273,8 @@
return str;
}
- return str.replace(templateRe, function (match, key) {
- var obj = liquid(key);
+ // @since 2.21.0 allow for a `default` filter for missing properties
+ return liquid(str, function (match, obj) {
var value = parseKey(data, obj.key);
if (value == null) {
return obj.default || match;
@@ -301,32 +331,42 @@
return value;
}
+ var liquidRe = /{+ *(.*?) *}+/g;
+
/**
* parses liquid tags from a string
*
* @param {string} str
*/
- function liquid(str) {
- var tags = str.split(' | ');
- var obj = {};
-
- // removes initial variable from array
- var key = tags.shift();
-
- for (var i = 0, len = tags.length; i < len; i++) {
- var tag = tags[i].split(': ');
- var tagName = tag.shift();
- var tagValue = tag.join(': ') || true;
-
- obj[tagName] = tagValue;
- }
+ function liquid(str, callback) {
+ return str.replace(liquidRe, function (match, group) {
+ /** @type string[] */
+ var tags = group.split(' | ');
+ var obj = {};
+
+ // removes initial variable from array
+ var filter = tags.shift();
+
+ for (var i = 0, len = tags.length; i < len; i++) {
+ var tag = tags[i];
+ var delimiter = tag.indexOf(':');
+ var tagName = trim(delimiter === -1 ? tag : tag.slice(0, delimiter));
+ var tagValue =
+ delimiter === -1 ? true : trim(tag.slice(delimiter + 1));
+
+ obj[tagName] = tagValue;
+ }
- // always preserve the original string
- obj.key = key;
+ // always preserve the original string
+ obj.key = filter;
- return obj;
+ return callback(match, obj);
+ });
}
+ // export for tests
+ this.liquid = liquid;
+
function waitFor(prop, cb) {
if (typeof L !== 'undefined' && typeof L[prop] !== 'undefined') {
cb();
diff --git a/tests/addAttribution.test.js b/tests/addAttribution.test.js
new file mode 100644
index 0000000..cedac32
--- /dev/null
+++ b/tests/addAttribution.test.js
@@ -0,0 +1,75 @@
+require('../scripts/construct-leaflet-map');
+
+const plugin = window.WPLeafletMapPlugin;
+
+// mock leaflet functions (kind of long-winded)
+const addAttribution = jest.fn();
+window.L = {
+ control: {
+ attribution: () => ({
+ addTo: () => ({
+ addAttribution,
+ }),
+ }),
+ },
+ map: jest.fn(),
+};
+
+const originalAttribution =
+ 'Leaflet; © OpenStreetMap contributors';
+
+describe('addAttribution', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('does not call addAttribution if not present in options', () => {
+ plugin.createMap({});
+
+ expect(L.map).toHaveBeenCalledWith(undefined, expect.any(Object));
+ expect(addAttribution).not.toHaveBeenCalled();
+ });
+
+ it('calls addAttribution with original (backwards-compatible) HTML string', () => {
+ plugin.createMap({
+ attribution: originalAttribution,
+ });
+
+ const attributions = originalAttribution.split('; ');
+
+ expect(addAttribution).toHaveBeenCalledWith(attributions[0]);
+ expect(addAttribution).toHaveBeenCalledWith(attributions[1]);
+ });
+
+ it('calls original attribution with markdown string', () => {
+ const attribution =
+ '{Leaflet | link: http://leafletjs.com}; © {OpenStreetMap | link: http://www.openstreetmap.org/copyright} contributors';
+
+ plugin.createMap({
+ attribution,
+ });
+
+ const attributions = originalAttribution.split('; ');
+
+ expect(addAttribution).toHaveBeenCalledWith(
+ 'Leaflet'
+ );
+ expect(addAttribution).toHaveBeenCalledWith(
+ '© OpenStreetMap contributors'
+ );
+ });
+
+ it('works without a space after the filter', () => {
+ const attribution = '{Leaflet | link:http://leafletjs.com}';
+
+ plugin.createMap({
+ attribution,
+ });
+
+ const attributions = originalAttribution.split('; ');
+
+ expect(addAttribution).toHaveBeenCalledWith(
+ 'Leaflet'
+ );
+ });
+});
diff --git a/tests/liquid.test.js b/tests/liquid.test.js
new file mode 100644
index 0000000..aa93b03
--- /dev/null
+++ b/tests/liquid.test.js
@@ -0,0 +1,89 @@
+require('../scripts/construct-leaflet-map');
+
+const plugin = window.WPLeafletMapPlugin;
+
+describe('liquid', () => {
+ const observer = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('parses a boolean filter', () => {
+ const str = '{ test | isBoolean }';
+
+ plugin.liquid(str, observer);
+
+ expect(observer).toHaveBeenCalledWith(
+ str,
+ expect.objectContaining({
+ key: 'test',
+ isBoolean: true,
+ })
+ );
+ });
+
+ it('also parses when liquid tag is doubled', () => {
+ const str = '{{ test }}';
+
+ plugin.liquid(str, observer);
+
+ expect(observer).toHaveBeenCalledWith(
+ str,
+ expect.objectContaining({
+ key: 'test',
+ })
+ );
+ });
+
+ it('does not parse when tag is malformed', () => {
+ const str = '{ test ';
+
+ const output = plugin.liquid(str, observer);
+
+ expect(observer).not.toHaveBeenCalled();
+ expect(output).toEqual(str);
+ });
+
+ it('does not parse when bar is missing whitespace', () => {
+ const str = '{ test|isBoolean }';
+
+ const output = plugin.liquid(str, observer);
+
+ expect(observer).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.not.objectContaining({
+ isBoolean: true,
+ })
+ );
+ });
+
+ it('accepts multiple filters', () => {
+ const str = '{ test | default: yolo | substr: 0,4 | lowercase }';
+
+ plugin.liquid(str, observer);
+
+ expect(observer).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.objectContaining({
+ key: 'test',
+ default: 'yolo',
+ substr: '0,4',
+ lowercase: true,
+ })
+ );
+ });
+
+ it('does not have key as a filter', () => {
+ const str = '{ key | key: not key }';
+
+ plugin.liquid(str, observer);
+
+ expect(observer).toHaveBeenCalledWith(
+ expect.any(String),
+ expect.objectContaining({
+ key: 'key',
+ })
+ );
+ });
+});
diff --git a/tests/types.d.ts b/tests/types.d.ts
deleted file mode 100644
index e1cea4a..0000000
--- a/tests/types.d.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * VSCode sometimes recognizes this file 🤷♀️
- * maybe the tests should just be written in typescript
- */
-interface Options {
- fitBounds: boolean;
- attribution: string;
-}
-
-type LeafletMap = {};
-type LeafletFeatureGroup = {};
-
-// helps with some intellisense
-export declare global {
- interface Window {
- WPLeafletMapPlugin: {
- push(cb: () => void): void;
- unshift(cb: () => void): void;
- init(): void;
- createMap(options: Options): LeafletMap;
- createImageMap(options: Options): LeafletMap;
- getCurrentMap(): LeafletMap;
- getCurrentGroup(): LeafletFeatureGroup;
- getGroup(map: LeafletMap): LeafletFeatureGroup;
- newMarkerGroup(map: LeafletMap): LeafletFeatureGroup;
- propsToTable(props: {}): string;
- template(str: string, data: {}): string;
- waitForSVG(cb: () => void): void;
- waitForAjax(cb: () => void): void;
- createScale(options: {}): void;
- maps: LeafletMap[];
- images: LeafletMap[];
- markergroups: Record;
- markers: {}[];
- lines: {}[];
- polygons: {}[];
- circles: {}[];
- geojson: {}[];
- };
- }
-}