+ Create a custom and accessible panel, styled entirely with
+ CSS.
+
+
+
+
+
+
+
An accessible panel:
+
+
+
The buttons are actual HTML buttons. You can therefore
+ use the TAB key to give the focus to the panel's buttons, and the "ENTER"
+ key to activate or trigger the corresponding control.
+
The buttons include text and titles (displayed when a button
+ is hovered).
+
If you remove colors from the page (for example using FireFox's No
+ Color extension) the buttons are still visible, and
+ accessible using the keyboard.
+
+
+
+
By default a panel creates buttons as divs. In this example the
+ createControlMarkup panel function is overridden to create
+ a more accessible markup for the buttons. See the accessible-panel.js
+ source to see how this is done.
+
+
Note: in IE 8, when a button is pressed its content shifts by 1 pixel.
+ This is a known
+ IE8 bug, with known workarounds. No workaround is applied in this
+ example though.
+
+
+
+
+
diff --git a/examples/accessible-panel.js b/examples/accessible-panel.js
new file mode 100644
index 0000000000..f982fc624b
--- /dev/null
+++ b/examples/accessible-panel.js
@@ -0,0 +1,64 @@
+var lon = 5;
+var lat = 40;
+var zoom = 5;
+var map, layer;
+
+function init() {
+ map = new OpenLayers.Map( 'map', { controls: [] } );
+ layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic'} );
+ map.addLayer(layer);
+
+ vlayer = new OpenLayers.Layer.Vector( "Editable" );
+ map.addLayer(vlayer);
+
+ zb = new OpenLayers.Control.ZoomBox({
+ title: "Zoom box: zoom clicking and dragging",
+ text: "Zoom"
+ });
+
+ var panel = new OpenLayers.Control.Panel({
+ defaultControl: zb,
+ createControlMarkup: function(control) {
+ var button = document.createElement('button'),
+ iconSpan = document.createElement('span'),
+ textSpan = document.createElement('span');
+ iconSpan.innerHTML = ' ';
+ button.appendChild(iconSpan);
+ if (control.text) {
+ textSpan.innerHTML = control.text;
+ }
+ button.appendChild(textSpan);
+ return button;
+ }
+ });
+
+ panel.addControls([
+ zb,
+ new OpenLayers.Control.DrawFeature(vlayer, OpenLayers.Handler.Path,
+ {title:'Draw a feature', text: 'Draw'}),
+ new OpenLayers.Control.ZoomToMaxExtent({
+ title:"Zoom to the max extent",
+ text: "World"
+ })
+ ]);
+
+ nav = new OpenLayers.Control.NavigationHistory({
+ previousOptions: {
+ title: "Go to previous map position",
+ text: "Prev"
+ },
+ nextOptions: {
+ title: "Go to next map position",
+ text: "Next"
+ },
+ displayClass: "navHistory"
+ });
+ // parent control must be added to the map
+ map.addControl(nav);
+ panel.addControls([nav.next, nav.previous]);
+
+ map.addControl(panel);
+
+ map.setCenter(new OpenLayers.LonLat(lon, lat), zoom);
+}
diff --git a/lib/OpenLayers/Control/LayerSwitcher.js b/lib/OpenLayers/Control/LayerSwitcher.js
index a0e480e385..b1f2cc49cb 100644
--- a/lib/OpenLayers/Control/LayerSwitcher.js
+++ b/lib/OpenLayers/Control/LayerSwitcher.js
@@ -120,7 +120,7 @@ OpenLayers.Control.LayerSwitcher =
initialize: function(options) {
OpenLayers.Control.prototype.initialize.apply(this, arguments);
this.layerStates = [];
-
+
if(this.roundedCorner) {
OpenLayers.Console.warn('roundedCorner option is deprecated');
}
@@ -143,6 +143,7 @@ OpenLayers.Control.LayerSwitcher =
changebaselayer: this.redraw,
scope: this
});
+ this.events.unregister("buttonclick", this, this.onButtonClick);
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
@@ -157,13 +158,18 @@ OpenLayers.Control.LayerSwitcher =
OpenLayers.Control.prototype.setMap.apply(this, arguments);
this.map.events.on({
- buttonclick: this.onButtonClick,
addlayer: this.redraw,
changelayer: this.redraw,
removelayer: this.redraw,
changebaselayer: this.redraw,
scope: this
});
+ if (this.outsideViewport) {
+ this.events.attachToElement(this.div);
+ this.events.register("buttonclick", this, this.onButtonClick);
+ } else {
+ this.map.events.register("buttonclick", this, this.onButtonClick);
+ }
},
/**
diff --git a/lib/OpenLayers/Control/Panel.js b/lib/OpenLayers/Control/Panel.js
index b04f6c3863..f141671445 100644
--- a/lib/OpenLayers/Control/Panel.js
+++ b/lib/OpenLayers/Control/Panel.js
@@ -245,26 +245,58 @@ OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {
controls = [controls];
}
this.controls = this.controls.concat(controls);
-
- // Give each control a panel_div which will be used later.
- // Access to this div is via the panel_div attribute of the
- // control added to the panel.
- // Also, stop mousedowns and clicks, but don't stop mouseup,
- // since they need to pass through.
+
for (var i=0, len=controls.length; i} The control to create the HTML
+ * markup for.
+ *
+ * Returns:
+ * {DOMElement} The markup.
+ */
+ createControlMarkup: function(control) {
+ return document.createElement("div");
+ },
/**
* Method: addControlsToMap
diff --git a/lib/OpenLayers/Events.js b/lib/OpenLayers/Events.js
index 907ced8109..ea46bf3cba 100644
--- a/lib/OpenLayers/Events.js
+++ b/lib/OpenLayers/Events.js
@@ -20,6 +20,12 @@ OpenLayers.Event = {
* element._eventCacheID
*/
observers: false,
+
+ /**
+ * Constant: KEY_SPACE
+ * {int}
+ */
+ KEY_SPACE: 32,
/**
* Constant: KEY_BACKSPACE
@@ -388,7 +394,8 @@ OpenLayers.Events = OpenLayers.Class({
"mousedown", "mouseup", "mousemove",
"click", "dblclick", "rightclick", "dblrightclick",
"resize", "focus", "blur",
- "touchstart", "touchmove", "touchend"
+ "touchstart", "touchmove", "touchend",
+ "keydown"
],
/**
diff --git a/lib/OpenLayers/Events/buttonclick.js b/lib/OpenLayers/Events/buttonclick.js
index 351248bf0c..6520b83a0d 100644
--- a/lib/OpenLayers/Events/buttonclick.js
+++ b/lib/OpenLayers/Events/buttonclick.js
@@ -39,7 +39,7 @@ OpenLayers.Events.buttonclick = OpenLayers.Class({
*/
events: [
'mousedown', 'mouseup', 'click', 'dblclick',
- 'touchstart', 'touchmove', 'touchend'
+ 'touchstart', 'touchmove', 'touchend', 'keydown'
],
/**
@@ -97,6 +97,31 @@ OpenLayers.Events.buttonclick = OpenLayers.Class({
delete this.target;
},
+ /**
+ * Method: getPressedButton
+ * Get the pressed button, if any. Returns undefined if no button
+ * was pressed.
+ *
+ * Arguments:
+ * element - {DOMElement} The event target.
+ *
+ * Returns:
+ * {DOMElement} The button element, or undefined.
+ */
+ getPressedButton: function(element) {
+ var depth = 3, // limit the search depth
+ button;
+ do {
+ if(OpenLayers.Element.hasClass(element, "olButton")) {
+ // hit!
+ button = element;
+ break;
+ }
+ element = element.parentNode;
+ } while(--depth > 0 && element);
+ return button;
+ },
+
/**
* Method: buttonClick
* Check if a button was clicked, and fire the buttonclick event
@@ -108,15 +133,25 @@ OpenLayers.Events.buttonclick = OpenLayers.Class({
var propagate = true,
element = OpenLayers.Event.element(evt);
if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) {
- if (element.nodeType === 3 || OpenLayers.Element.hasClass(element, "olAlphaImg")) {
- element = element.parentNode;
- }
- if (OpenLayers.Element.hasClass(element, "olButton")) {
- if (this.startEvt) {
- if (this.completeRegEx.test(evt.type)) {
- var pos = OpenLayers.Util.pagePosition(element);
+ // was a button pressed?
+ var button = this.getPressedButton(element);
+ if (button) {
+ if (evt.type === "keydown") {
+ switch (evt.keyCode) {
+ case OpenLayers.Event.KEY_RETURN:
+ case OpenLayers.Event.KEY_SPACE:
this.target.triggerEvent("buttonclick", {
- buttonElement: element,
+ buttonElement: button
+ });
+ OpenLayers.Event.stop(evt);
+ propagate = false;
+ break;
+ }
+ } else if (this.startEvt) {
+ if (this.completeRegEx.test(evt.type)) {
+ var pos = OpenLayers.Util.pagePosition(button);
+ this.target.triggerEvent("buttonclick", {
+ buttonElement: button,
buttonXY: {
x: this.startEvt.clientX - pos[0],
y: this.startEvt.clientY - pos[1]
diff --git a/lib/OpenLayers/Format/SLD/v1.js b/lib/OpenLayers/Format/SLD/v1.js
index f934f80d65..cd32810be9 100644
--- a/lib/OpenLayers/Format/SLD/v1.js
+++ b/lib/OpenLayers/Format/SLD/v1.js
@@ -990,12 +990,14 @@ OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
},
"LabelPlacement": function(symbolizer) {
var node = this.createElementNSPlus("sld:LabelPlacement");
- if (symbolizer.labelAnchorPointX != null ||
+ // PointPlacement and LinePlacement are choices, so don't output both
+ if ((symbolizer.labelAnchorPointX != null ||
symbolizer.labelAnchorPointY != null ||
symbolizer.labelAlign != null ||
symbolizer.labelXOffset != null ||
symbolizer.labelYOffset != null ||
- symbolizer.labelRotation != null) {
+ symbolizer.labelRotation != null) &&
+ symbolizer.labelPerpendicularOffset == null) {
this.writeNode("PointPlacement", symbolizer, node);
}
if (symbolizer.labelPerpendicularOffset != null) {
diff --git a/lib/OpenLayers/Format/SLD/v1_0_0_GeoServer.js b/lib/OpenLayers/Format/SLD/v1_0_0_GeoServer.js
index 49596be1c4..bee661311b 100644
--- a/lib/OpenLayers/Format/SLD/v1_0_0_GeoServer.js
+++ b/lib/OpenLayers/Format/SLD/v1_0_0_GeoServer.js
@@ -58,12 +58,9 @@ OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(
},
"VendorOption": function(node, obj) {
if (!obj.vendorOptions) {
- obj.vendorOptions = [];
+ obj.vendorOptions = {};
}
- obj.vendorOptions.push({
- name: node.getAttribute("name"),
- value: this.getChildValue(node)
- });
+ obj.vendorOptions[node.getAttribute("name")] = this.getChildValue(node);
}
}, OpenLayers.Format.SLD.v1_0_0.prototype.readers["sld"])
}, OpenLayers.Format.SLD.v1_0_0.prototype.readers),
@@ -130,8 +127,11 @@ OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class(
addVendorOptions: function(node, symbolizer) {
var options = symbolizer.vendorOptions;
if (options) {
- for (var i=0, ii=options.length; i
diff --git a/tests/Format/SLD/v1_0_0.html b/tests/Format/SLD/v1_0_0.html
index af58c30c7d..a295a746b8 100644
--- a/tests/Format/SLD/v1_0_0.html
+++ b/tests/Format/SLD/v1_0_0.html
@@ -528,6 +528,7 @@
var format = new OpenLayers.Format.SLD.v1_0_0({
multipleSymbolizers: true
});
+ // labelPerpendicularOffset takes precedence over labelAlign
var style = new OpenLayers.Style2({
rules: [
new OpenLayers.Rule({
@@ -538,7 +539,8 @@
}),
new OpenLayers.Symbolizer.Text({
label: "${FOO}",
- labelPerpendicularOffset: 10
+ labelPerpendicularOffset: 10,
+ labelAlign: "rb"
})
]
})