Skip to content

Commit 58c3fbb

Browse files
authored
Merge pull request #160 from mapnik/collapsible-menus
Collapsible menus + Datasources + Layer
2 parents dc4457e + d549adb commit 58c3fbb

3 files changed

Lines changed: 186 additions & 94 deletions

File tree

3.0.22/reference.json

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -121,82 +121,95 @@
121121
}
122122
},
123123
"layer" : {
124-
"name": {
125-
"default-value": "",
126-
"type":"string",
127-
"required" : true,
128-
"default-meaning": "No layer name has been provided",
129-
"doc": "The name of a layer. Can be anything you wish and is not strictly validated, but ideally unique in the map."
130-
},
131-
"srs": {
124+
"name": {
125+
"css": "name",
126+
"default-value": "",
127+
"type":"string",
128+
"required" : true,
129+
"default-meaning": "No layer name has been provided",
130+
"doc": "The name of a layer. Can be anything you wish and is not strictly validated, but ideally unique in the map."
131+
},
132+
"srs": {
133+
"css": "srs",
132134
"default-value": "",
133135
"type":"string",
134136
"default-meaning": "No srs value is provided and the value will be inherited from the Map's srs",
135137
"doc": "The spatial reference system definition for the layer, aka the projection. Can either be a proj4 literal string like '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs' or, if the proper proj4 epsg/nad/etc identifier files are installed, a string that uses an id like: '+init=epsg:4326'."
136138
},
137-
"status": {
139+
"status": {
140+
"css": "status",
138141
"default-value": true,
139142
"type":"boolean",
140143
"default-meaning": "This layer will be marked as active and available for processing",
141144
"doc": "A property that can be set to false to disable this layer from being processed."
142145
},
143-
"minzoom": {
146+
"minzoom": {
147+
"css": "minzoom",
144148
"default-value": 0,
145149
"type":"float",
146150
"default-meaning": "The layer will be visible at the minimum possible scale",
147151
"doc": "The minimum scale denominator that this layer will be visible at. A layer's visibility is determined by whether its status is true and if the Map scale >= minzoom - 1e-6 and scale < maxzoom + 1e-6. This option has been deprecated in favor of `minimum-scale-denominator`.",
148152
"status": "deprecated"
149153
},
150-
"maxzoom": {
154+
"maxzoom": {
155+
"css": "minzoom",
151156
"default-value": "1.79769e+308",
152157
"type":"float",
153158
"default-meaning": "The layer will be visible at the maximum possible scale",
154159
"doc": "The maximum scale denominator that this layer will be visible at. The default is the numeric limit of the C++ double type, which may vary slightly by system, but is likely a massive number like 1.79769e+308 and ensures that this layer will always be visible unless the value is reduced. A layer's visibility is determined by whether its status is true and if the Map scale >= minzoom - 1e-6 and scale < maxzoom + 1e-6. This option has been deprecated in favor of `maximum-scale-denominator`.",
155160
"status": "deprecated"
156161
},
157-
"minimum-scale-denominator": {
162+
"minimum-scale-denominator": {
163+
"css": "minimum-scale-denomitator",
158164
"default-value": 0,
159165
"type":"float",
160166
"default-meaning": "The layer will be visible at the minimum possible scale denominator",
161167
"doc": "The minimum scale denominator that this layer will be visible at. A layer's visibility is determined by whether its status is true and if the Map scale denominator >= `minimum-scale-denominator` - 1e-6 and scale denominator < 'maximum-scale-denominator' + 1e-6."
162168
},
163-
"maximum-scale-denominator": {
169+
"maximum-scale-denominator": {
170+
"css": "maximum-scale-denomitator",
164171
"default-value": "1.79769e+308",
165172
"type":"float",
166173
"default-meaning": "The layer will be visible at the maximum possible scale denominator",
167174
"doc": "The maximum scale denominator that this layer will be visible at. The default is the numeric limit of the C++ double type, which may vary slightly by system, but is likely a massive number like 1.79769e+308 and ensures that this layer will always be visible unless the value is reduced. A layer's visibility is determined by whether its status is true and if the Map scale denominator >= `minimum-scale-denominator` - 1e-6 and scale denominator < `maximum-scale-denominator` + 1e-6."
168175
},
169-
"queryable": {
176+
"queryable": {
177+
"css": "queryable",
170178
"default-value": false,
171179
"type":"boolean",
172180
"default-meaning": "The layer will not be available for the direct querying of data values",
173181
"doc": "This property was added for GetFeatureInfo/WMS compatibility and is rarely used. It is off by default meaning that in a WMS context the layer will not be able to be queried unless the property is explicitly set to true."
174182
},
175-
"clear-label-cache": {
183+
"clear-label-cache": {
184+
"css": "clear-label-cache",
176185
"default-value": false,
177186
"type":"boolean",
178187
"default-meaning": "The renderer's collision detector cache (used for avoiding duplicate labels and overlapping markers) will not be cleared immediately before processing this layer",
179188
"doc": "This property, by default off, can be enabled to allow a user to clear the collision detector cache before a given layer is processed. This may be desirable to ensure that a given layers data shows up on the map even if it normally would not because of collisions with previously rendered labels or markers."
180189
},
181-
"group-by": {
190+
"group-by": {
191+
"css": "group-by",
182192
"default-value": "",
183193
"type":"string",
184194
"default-meaning": "No special layer grouping will be used during rendering",
185195
"doc": "https://github.com/mapnik/mapnik/wiki/Grouped-rendering"
186196
},
187-
"buffer-size": {
197+
"buffer-size": {
198+
"css": "buffer-size",
188199
"default-value": 0,
189200
"type":"float",
190201
"default-meaning": "No custom buffer will be used for the layer and rather the Map buffer-size will be used",
191202
"doc": "Extra tolerance around the Layer extent (in pixels) used when querying the layer data during rendering. If set this will override the Map buffer-size."
192203
},
193-
"maximum-extent": {
204+
"maximum-extent": {
205+
"css": "maximum-extent",
194206
"default-value": "none",
195207
"type":"bbox",
196208
"default-meaning": "No clipping extent will be used",
197209
"doc": "An extent to be used to limit the bounds used to query this specific layer data during rendering. Should be minx, miny, maxx, maxy in the coordinates of the Layer."
198210
},
199-
"cache-features": {
211+
"cache-features": {
212+
"css": "cache-features",
200213
"type":"boolean",
201214
"default-value": "off",
202215
"default-meaning": "Features are not cached between rendering multiple styles. The datasource is queried for each style.",

site/main.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,22 @@ a.current {
9494
.headerBlock a {
9595
display: block;
9696
}
97+
.collapsible {
98+
cursor: pointer;
99+
border: none;
100+
text-align: left;
101+
outline: none;
102+
width:100%;
103+
padding:0;
104+
}
105+
106+
.active, .collapsible:hover {
107+
background-color: #d9eaec;
108+
}
109+
110+
.content {
111+
padding-left: 1em;
112+
overflow: hidden;
113+
transition: max-height 0.2s ease-out;
114+
background-color: #f1f1f1;
115+
}

site/main.js

Lines changed: 135 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use strict';
22
(function (window) {
33
var UI = function (version) {
4-
this.menu = document.querySelector('#menu .main');
5-
this.container = document.querySelector('#symbolizers');
6-
this.versionLabel = document.querySelector('#version');
7-
this.fetchFromHash() || this.fetch(version);
8-
var self = this;
9-
window.addEventListener('hashchange', function () {self.fetchFromHash();});
4+
this.menu = document.querySelector('#menu .main');
5+
this.container = document.querySelector('#symbolizers');
6+
this.versionLabel = document.querySelector('#version');
7+
this.fetchFromHash() || this.fetch(version);
8+
var self = this;
9+
window.addEventListener('hashchange', function () {self.fetchFromHash();});
1010
};
1111
UI.versions = ['3.0.22', '3.0.20', '3.0.6', '3.0.3', '3.0.0', '2.3.0', '2.2.0', '2.1.0', '2.0.0'];
1212
UI.prototype = {
@@ -28,21 +28,32 @@
2828
return el;
2929
},
3030

31-
fetch: function (version) {
32-
var self = this;
33-
this.version = version || window.UI.versions[0];
34-
nanoajax.ajax('./' + this.version + '/reference.json', function (code, content) {
35-
self.build(JSON.parse(content));
31+
fetch: function (version) {
32+
var self = this;
33+
this.version = version || window.UI.versions[0];
34+
nanoajax.ajax('./' + this.version + '/reference.json', function (code, content) {
35+
var reference = JSON.parse(content);
36+
nanoajax.ajax('./' + version + '/datasources.json', function (code, content) {
37+
console.log(code);
38+
if (code == 404) {
39+
self.build(reference);
40+
}
41+
else {
42+
var datasources = JSON.parse(content);
43+
reference = Object.assign(reference, datasources);
44+
self.build(reference);
45+
}
3646
});
37-
return true;
47+
});
48+
return true;
3849
},
3950

40-
fetchFromHash: function () {
41-
var newVersion = window.location.hash.split('/')[0].replace('#', '');
42-
if (newVersion !== this.version && this.inArray(UI.versions, newVersion)) return this.fetch(newVersion);
43-
},
51+
fetchFromHash: function () {
52+
var newVersion = window.location.hash.split('/')[0].replace('#', '');
53+
if (newVersion !== this.version && this.inArray(UI.versions, newVersion)) return this.fetch(newVersion);
54+
},
4455

45-
build: function (ref) {
56+
build: function (ref) {
4657
this.container.innerHTML = '';
4758
this.menu.innerHTML = '';
4859
this.versionLabel.innerHTML = this.version;
@@ -71,65 +82,114 @@
7182
var layerMenu = this.node('h5', {className: 'headerBlock'}, this.menu);
7283
this.node('a', {href: '#' + this.version + '/layer'}, layerMenu, 'Layer');
7384
}
74-
var symbolizerMenu = this.node('h5', {className: 'headerBlock'}, this.menu);
75-
this.node('a', {href: '#' + this.version + '/symbolizers'}, symbolizerMenu, 'Symbolizers');
76-
77-
if (hasStyleCss) {
78-
var styleHeading = this.node('h2', {}, this.container);
79-
this.node('a', {id: this.version + '/style', href: '#' + this.version + '/style'}, styleHeading, 'Style');
80-
var styleContainer = this.node('div', {className: 'symbolizer'}, this.container);
81-
for (var id in ref.style) {
82-
if (ref.style[id].hasOwnProperty('css')) {
83-
this.addRule(id, ref.style[id], styleContainer);
84-
}
85-
}
86-
}
87-
88-
if (hasLayerCss) {
89-
var layerHeading = this.node('h2', {}, this.container);
90-
this.node('a', {id: this.version + '/layer', href: '#' + this.version + '/layer'}, layerHeading, 'Layer');
91-
var layerContainer = this.node('div', {className: 'symbolizer'}, this.container);
92-
for (var id in ref.layer) {
93-
if (ref.layer[id].hasOwnProperty('css')) {
94-
this.addRule(id, ref.layer[id], layerContainer);
95-
}
96-
}
97-
}
9885

99-
var symbolizerHeading = this.node('h2', {}, this.container);
100-
this.node('a', {id: this.version + '/symbolizers', href: '#' + this.version + '/symbolizers'}, symbolizerHeading, 'Symbolizers');
101-
for (var id in ref.symbolizers) this.addSymbolizer(id, ref.symbolizers[id]);
10286

103-
this.addVersions();
104-
if (window.location.hash) window.location = window.location; // we have rebuild the DOM, help the browser find the North again.
105-
},
106-
107-
addVersions: function () {
108-
var container = this.node('div', {className: 'versions'}, this.menu);
109-
this.node('h5', {}, container, 'Versions');
110-
for (var i = 0; i < UI.versions.length; i++) this.addVersionLink(UI.versions[i], container);
111-
},
11287

113-
addVersionLink: function (version, parent) {
114-
var current = version === this.version ? ' current' : '';
115-
this.node('a', {className: 'block' + current, href: '#' + version + '/'}, parent, version);
116-
},
117-
118-
anchor: function (id) {
119-
return this.version + '/' + id;
120-
},
88+
if (hasStyleCss) {
89+
var styleHeading = this.node('h2', {}, this.container);
90+
this.node('a', {id: this.version + '/style', href: '#' + this.version + '/style'}, styleHeading, 'Style');
91+
var styleContainer = this.node('div', {className: 'symbolizer'}, this.container);
92+
for (var id in ref.style) {
93+
if (ref.style[id].hasOwnProperty('css')) {
94+
this.addRule(id, ref.style[id], styleContainer);
95+
}
96+
}
97+
}
98+
99+
if (hasLayerCss) {
100+
var layerHeading = this.node('h2', {}, this.container);
101+
this.node('a', {id: this.version + '/layer', href: '#' + this.version + '/layer'}, layerHeading, 'Layer');
102+
var layerContainer = this.node('div', {className: 'symbolizer'}, this.container);
103+
for (var id in ref.layer) {
104+
if (ref.layer[id].hasOwnProperty('css')) {
105+
this.addRule(id, ref.layer[id], layerContainer);
106+
}
107+
}
108+
}
121109

122-
addSymbolizer: function (id, rules) {
123-
this.node('a', {className: 'block', href: '#' + this.anchor(id)}, this.menu, id);
124-
this.addSymbolizerBlock(id, rules);
125-
},
110+
var collapsibleMenu = this.node('h5', {className: 'headerBlock'}, this.menu);
111+
// Symbolizers
112+
var button = this.node('button', {className:'collapsible'}, collapsibleMenu, '');
113+
this.node('a', {href: '#' + this.version + '/symbolizers'}, button, 'Symbolizers');
114+
var symbolizerHeading = this.node('h2', {}, this.container);
115+
this.node('a', {id: this.version + '/symbolizers', href: '#' + this.version + '/symbolizers'}, symbolizerHeading, 'Symbolizers');
116+
var content = this.node('div', {className: 'content', style: 'display:none'}, collapsibleMenu, '');
117+
for (var id in ref.symbolizers) {
118+
this.addSymbolizer(id, ref.symbolizers[id], content);
119+
}
120+
// Datasources
121+
var button = this.node('button', {className:'collapsible'}, collapsibleMenu, '');
122+
this.node('a', {href: '#' + this.version + '/datasources'}, button, 'Datasources');
123+
var datasourcesHeading = this.node('h2', {}, this.container);
124+
this.node('a', {id: this.version + '/datasources', href: '#' + this.version + '/datasources'}, datasourcesHeading, 'Datasources');
125+
var content = this.node('div', {className: 'content', style: 'display:none'}, collapsibleMenu, '');
126+
for (var id in ref.datasources) {
127+
this.addDatasource(id, ref.datasources[id], content);
128+
}
126129

127-
addSymbolizerBlock: function (id, rules) {
128-
var container = this.node('div', {className: 'symbolizer'}, this.container);
129-
var section = this.node('h2', {}, container);
130-
this.node('a', {href: '#' + this.anchor(id), id: this.anchor(id)}, section, id);
131-
for (var ruleId in rules) this.addRule(ruleId, rules[ruleId], container);
132-
},
130+
this.addVersions();
131+
this.addScript();
132+
if (window.location.hash) window.location = window.location; // we have rebuild the DOM, help the browser find the North again.
133+
},
134+
135+
addScript: function () {
136+
var body = document.body;
137+
var script = document.createElement('script');
138+
script.type = 'text/javascript';
139+
script.innerHTML = `var coll = document.getElementsByClassName('collapsible');
140+
for (var i = 0; i < coll.length; i++) {
141+
coll[i].addEventListener('click', function() {
142+
this.classList.toggle('active');
143+
var content = this.nextElementSibling;
144+
console.log(content);
145+
if (content.style.display === 'block') {
146+
content.style.display = 'none';
147+
} else {
148+
content.style.display = 'block';
149+
}
150+
});
151+
}`;
152+
document.body.appendChild(script);
153+
},
154+
addVersions: function () {
155+
var container = this.node('div', {className: 'versions'}, this.menu);
156+
this.node('h5', {}, container, 'Versions');
157+
for (var i = 0; i < UI.versions.length; i++) this.addVersionLink(UI.versions[i], container);
158+
},
159+
160+
addVersionLink: function (version, parent) {
161+
var current = version === this.version ? ' current' : '';
162+
this.node('a', {className: 'block' + current, href: '#' + version + '/'}, parent, version);
163+
},
164+
165+
166+
anchor: function (id) {
167+
return this.version + '/' + id;
168+
},
169+
170+
addSymbolizer: function (id, rules, parent) {
171+
this.node('a', {className: 'block', href: '#' + this.anchor(id)}, parent, id);
172+
this.addSymbolizerBlock(id, rules);
173+
},
174+
175+
addSymbolizerBlock: function (id, rules) {
176+
var container = this.node('div', {className: 'symbolizer'}, this.container);
177+
var section = this.node('h2', {}, container);
178+
this.node('a', {href: '#' + this.anchor(id), id: this.anchor(id)}, section, id);
179+
for (var ruleId in rules) this.addRule(ruleId, rules[ruleId], container);
180+
},
181+
182+
addDatasource: function (id, rules, parent) {
183+
this.node('a', {className: 'block', href: '#' + this.anchor(id)}, parent, id);
184+
this.addDatasourceBlock(id, rules);
185+
},
186+
187+
addDatasourceBlock: function (id, rules) {
188+
var container = this.node('div', {className: 'symbolizer'}, this.container);
189+
var section = this.node('h2', {}, container);
190+
this.node('a', {href: '#' + this.anchor(id), id: this.anchor(id)}, section, id);
191+
for (var ruleId in rules) this.addRule(ruleId, rules[ruleId], container);
192+
},
133193

134194
addRule: function (id, props, parent) {
135195
var title = this.node('h3', {}, parent);
@@ -148,10 +208,10 @@
148208

149209
};
150210
UI.init = function (version) {
151-
// if (!version && window.location.hash && window.location.hash.indexOf('/') !== -1) {
152-
// version = window.location.hash.split('/')[0];
153-
// }
154-
return new UI(version);
211+
// if (!version && window.location.hash && window.location.hash.indexOf('/') !== -1) {
212+
// version = window.location.hash.split('/')[0];
213+
// }
214+
return new UI(version);
155215
};
156216
window.UI = UI;
157217
})(window);

0 commit comments

Comments
 (0)