Merge pull request #223 from camptocamp/access_panelbutton
make it possible to create accessible panel buttons
This commit is contained in:
130
examples/accessible-panel.html
Normal file
130
examples/accessible-panel.html
Normal file
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>Custom and accessible panel</title>
|
||||
<link rel="stylesheet" href="../theme/default/style.css" type="text/css">
|
||||
<link rel="stylesheet" href="style.css" type="text/css">
|
||||
<style type="text/css">
|
||||
|
||||
.olControlPanel button {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin: 2px;
|
||||
border: 1px solid;
|
||||
padding: 0 5px;
|
||||
border-radius: 4px;
|
||||
height: 35px;
|
||||
background-color: white;
|
||||
float: left;
|
||||
overflow: visible; /* needed to remove padding from buttons in IE */
|
||||
}
|
||||
.olControlPanel button span {
|
||||
padding-left: 25px;
|
||||
}
|
||||
.olControlPanel button span:first-child {
|
||||
padding-left: 0;
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
}
|
||||
.olControlPanel .olControlDrawFeatureItemActive span:first-child {
|
||||
background-image: url("../theme/default/img/draw_line_on.png");
|
||||
height: 22px;
|
||||
width: 24px;
|
||||
top: 5px;
|
||||
}
|
||||
.olControlPanel .olControlDrawFeatureItemInactive span:first-child {
|
||||
background-image: url("../theme/default/img/draw_line_off.png");
|
||||
height: 22px;
|
||||
width: 24px;
|
||||
top: 5px;
|
||||
}
|
||||
.olControlPanel .olControlZoomBoxItemInactive span:first-child {
|
||||
background-image: url("../img/drag-rectangle-off.png");
|
||||
height: 29px;
|
||||
width: 29px;
|
||||
top: 2px;
|
||||
}
|
||||
.olControlPanel .olControlZoomBoxItemActive span:first-child {
|
||||
background-image: url("../img/drag-rectangle-on.png");
|
||||
height: 29px;
|
||||
width: 29px;
|
||||
top: 2px;
|
||||
}
|
||||
.olControlPanel .olControlZoomToMaxExtentItemInactive span:first-child {
|
||||
background-image: url("../img/zoom-world-mini.png");
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
top: 8px;
|
||||
}
|
||||
.olControlPanel .navHistory span:first-child {
|
||||
background-image: url("../theme/default/img/navigation_history.png");
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
top: 4px;
|
||||
}
|
||||
.olControlPanel .navHistoryPreviousItemActive span:first-child {
|
||||
background-position: 0 0;
|
||||
}
|
||||
.olControlPanel .navHistoryPreviousItemInactive span:first-child {
|
||||
background-position: 0 -24px;
|
||||
}
|
||||
.olControlPanel .navHistoryNextItemActive span:first-child {
|
||||
background-position: -24px 0;
|
||||
}
|
||||
.olControlPanel .navHistoryNextItemInactive span:first-child {
|
||||
background-position: -24px -24px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="../lib/OpenLayers.js"></script>
|
||||
<script src="accessible-panel.js"></script>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
<h1 id="title">Custom and accessible panel</h1>
|
||||
<div id="tags">
|
||||
panels, CSS, style, accessibility, button
|
||||
</div>
|
||||
<p id="shortdesc">
|
||||
Create a custom and accessible panel, styled entirely with
|
||||
CSS.
|
||||
</p>
|
||||
<div id="panel"></div>
|
||||
<div id="map" class="smallmap"></div>
|
||||
|
||||
<div id="docs">
|
||||
|
||||
<p>An accessible panel:
|
||||
|
||||
<ul>
|
||||
<li>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.</li>
|
||||
<li>The buttons include text and titles (displayed when a button
|
||||
is hovered).</li>
|
||||
<li>If you remove colors from the page (for example using FireFox's <a
|
||||
href="https://addons.mozilla.org/en-US/firefox/addon/no-color/">No
|
||||
Color extension</a>) the buttons are still visible, and
|
||||
accessible using the keyboard.</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>By default a panel creates buttons as divs. In this example the
|
||||
<code>createControlMarkup</code> panel function is overridden to create
|
||||
a more accessible markup for the buttons. See the <a
|
||||
href="accessible-panel.js" target="_blank"> accessible-panel.js
|
||||
source</a> to see how this is done.</p>
|
||||
|
||||
<p>Note: in IE 8, when a button is pressed its content shifts by 1 pixel.
|
||||
This is a <a
|
||||
href="http://labs.findsubstance.com/2009/05/21/ie8-form-button-with-background-image-on-click-css-bug/">known
|
||||
IE8 bug</a>, with known workarounds. No workaround is applied in this
|
||||
example though.</p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
64
examples/accessible-panel.js
Normal file
64
examples/accessible-panel.js
Normal file
@@ -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);
|
||||
}
|
||||
@@ -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<len; i++) {
|
||||
var element = document.createElement("div");
|
||||
element.className = controls[i].displayClass + "ItemInactive olButton";
|
||||
controls[i].panel_div = element;
|
||||
if (controls[i].title != "") {
|
||||
controls[i].panel_div.title = controls[i].title;
|
||||
var control = controls[i],
|
||||
element = this.createControlMarkup(control);
|
||||
OpenLayers.Element.addClass(element,
|
||||
control.displayClass + "ItemInactive");
|
||||
OpenLayers.Element.addClass(element, "olButton");
|
||||
if (control.title != "" && !element.title) {
|
||||
element.title = control.title;
|
||||
}
|
||||
}
|
||||
control.panel_div = element;
|
||||
}
|
||||
|
||||
if (this.map) { // map.addControl() has already been called on the panel
|
||||
this.addControlsToMap(controls);
|
||||
this.redraw();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: createControlMarkup
|
||||
* This function just creates a div for the control. If specific HTML
|
||||
* markup is needed this function can be overridden in specific classes,
|
||||
* or at panel instantiation time:
|
||||
*
|
||||
* Example:
|
||||
* (code)
|
||||
* var panel = new OpenLayers.Control.Panel({
|
||||
* defaultControl: control,
|
||||
* // ovverride createControlMarkup to create actual buttons
|
||||
* // including texts wrapped into span elements.
|
||||
* createControlMarkup: function(control) {
|
||||
* var button = document.createElement('button'),
|
||||
* span = document.createElement('span');
|
||||
* if (control.text) {
|
||||
* span.innerHTML = control.text;
|
||||
* }
|
||||
* return button;
|
||||
* }
|
||||
* });
|
||||
* (end)
|
||||
*
|
||||
* Parameters:
|
||||
* control - {<OpenLayers.Control>} The control to create the HTML
|
||||
* markup for.
|
||||
*
|
||||
* Returns:
|
||||
* {DOMElement} The markup.
|
||||
*/
|
||||
createControlMarkup: function(control) {
|
||||
return document.createElement("div");
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: addControlsToMap
|
||||
|
||||
@@ -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"
|
||||
],
|
||||
|
||||
/**
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -27,9 +27,44 @@
|
||||
buttonClick.destroy();
|
||||
events.destroy();
|
||||
}
|
||||
|
||||
function test_getPressedButton(t) {
|
||||
t.plan(4);
|
||||
|
||||
// set up
|
||||
|
||||
events = new OpenLayers.Events({}, element);
|
||||
buttonClick = new OpenLayers.Events.buttonclick(events);
|
||||
|
||||
var button = document.createElement('button'),
|
||||
span1 = document.createElement('span'),
|
||||
span2 = document.createElement('span'),
|
||||
span3 = document.createElement('span');
|
||||
button.className = 'olButton';
|
||||
button.appendChild(span1);
|
||||
span1.appendChild(span2);
|
||||
span2.appendChild(span3);
|
||||
|
||||
t.ok(buttonClick.getPressedButton(button) === button,
|
||||
'getPressedButton returns button when element is button');
|
||||
t.ok(buttonClick.getPressedButton(span1) === button,
|
||||
'getPressedButton returns button when element is button descendant level 1');
|
||||
t.ok(buttonClick.getPressedButton(span2) === button,
|
||||
'getPressedButton returns button when element is button descendant level 2');
|
||||
t.eq(buttonClick.getPressedButton(span3), undefined,
|
||||
'getPressedButton returns undefined when element is button descendant level 3');
|
||||
|
||||
// test
|
||||
|
||||
|
||||
// tear down
|
||||
|
||||
buttonClick.destroy();
|
||||
events.destroy();
|
||||
}
|
||||
|
||||
function test_ButtonClick_buttonClick(t) {
|
||||
t.plan(23);
|
||||
t.plan(27);
|
||||
events = new OpenLayers.Events({}, element);
|
||||
events.on({
|
||||
"buttonclick": logEvent,
|
||||
@@ -38,7 +73,8 @@
|
||||
"click": logEvent,
|
||||
"dblclick": logEvent,
|
||||
"touchstart": logEvent,
|
||||
"touchend": logEvent
|
||||
"touchend": logEvent,
|
||||
"keydown": logEvent
|
||||
});
|
||||
buttonClick = events.extensions["buttonclick"];
|
||||
|
||||
@@ -111,12 +147,26 @@
|
||||
t.eq(log[1].type, "buttonclick", "buttonclick for 2nd click IE");
|
||||
|
||||
// rightclick
|
||||
log = []
|
||||
log = [];
|
||||
trigger({type: "mousedown", button: 2});
|
||||
trigger({type: "mouseup", button: 2});
|
||||
t.eq(log.length, 2, "two events fired for rightclick");
|
||||
t.eq(log[0].type, "mousedown", "mousedown from rightclick goes through");
|
||||
t.eq(log[1].type, "mouseup", "mouseup from rightclick goes through");
|
||||
|
||||
// keydown RETURN
|
||||
log = [];
|
||||
trigger({type: "keydown", keyCode: OpenLayers.Event.KEY_RETURN});
|
||||
trigger({type: "click"});
|
||||
t.eq(log.length, 1, "one event fired for RETURN keydown");
|
||||
t.eq(log[0].type, "buttonclick", "buttonclick for RETURN keydown");
|
||||
|
||||
// keydown SPACE
|
||||
log = [];
|
||||
trigger({type: "keydown", keyCode: OpenLayers.Event.KEY_SPACE});
|
||||
trigger({type: "click"});
|
||||
t.eq(log.length, 1, "one event fired for SPACE keydown");
|
||||
t.eq(log[0].type, "buttonclick", "buttonclick for SPACE keydown");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user