KeyboardInteractionSupport.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. define("dojox/geo/charting/KeyboardInteractionSupport", ["dojo/_base/lang","dojo/_base/declare","dojo/_base/event","dojo/_base/connect",
  2. "dojo/_base/html","dojo/dom","dojox/lang/functional","dojo/keys"],
  3. function(lang, declare, event, connect, html, dom, functional, keys) {
  4. return declare("dojox.geo.charting.KeyboardInteractionSupport", null, {
  5. // summary:
  6. // class to handle keyboard interactions on a dojox.geo.charting.Map widget
  7. //
  8. // The sections on the leading edge should receive the focus in response to a TAB event.
  9. // Then use cursor keys to the peer sections. The cursor event should go the adjacent section
  10. // in that direction. With the focus, the section zooms in upon SPACE. The map should zoom out
  11. // on ESC. Finally, while it has the focus, the map should lose the focus on TAB.
  12. // tags:
  13. // private
  14. _map: null,
  15. _zoomEnabled: false,
  16. constructor: function(map, options){
  17. // summary:
  18. // Constructs a new _KeyboardInteractionSupport instance
  19. // map: dojox.geo.charting.Map
  20. // the Map widget this class provides touch navigation for.
  21. this._map = map;
  22. if(options){
  23. this._zoomEnabled = options.enableZoom;
  24. }
  25. },
  26. connect: function(){
  27. // summary:
  28. // connects this keyboard support class to the Map component
  29. var container = dom.byId(this._map.container);
  30. // tab accessing enable
  31. html.attr(container, {
  32. tabindex: 0,
  33. role: "presentation",
  34. "aria-label": "map"
  35. });
  36. // install listeners
  37. this._keydownListener = connect.connect(container, "keydown", this, "keydownHandler");
  38. this._onFocusListener = connect.connect(container, "focus", this, "onFocus");
  39. this._onBlurListener = connect.connect(container, "blur", this, "onBlur");
  40. },
  41. disconnect: function(){
  42. // summary:
  43. // disconnects any installed listeners
  44. connect.disconnect(this._keydownListener);
  45. this._keydownListener = null;
  46. connect.disconnect(this._onFocusListener);
  47. this._onFocusListener = null;
  48. connect.disconnect(this._onBlurListener);
  49. this._onBlurListener = null
  50. },
  51. keydownHandler: function(e){
  52. switch(e.keyCode){
  53. case keys.LEFT_ARROW:
  54. this._directTo(-1,-1,1,-1);
  55. break;
  56. case keys.RIGHT_ARROW:
  57. this._directTo(-1,-1,-1,1);
  58. break;
  59. case keys.UP_ARROW:
  60. this._directTo(1,-1,-1,-1);
  61. break;
  62. case keys.DOWN_ARROW:
  63. this._directTo(-1,1,-1,-1);
  64. break;
  65. case keys.SPACE:
  66. if(this._map.selectedFeature && !this._map.selectedFeature._isZoomIn && this._zoomEnabled){
  67. this._map.selectedFeature._zoomIn();
  68. }
  69. break;
  70. case keys.ESCAPE:
  71. if(this._map.selectedFeature && this._map.selectedFeature._isZoomIn && this._zoomEnabled){
  72. this._map.selectedFeature._zoomOut();
  73. }
  74. break;
  75. default:
  76. return;
  77. }
  78. event.stop(e);
  79. },
  80. onFocus: function(e){
  81. // select the leading region at the map center
  82. if(this._map.selectedFeature || this._map.focused){return;}
  83. this._map.focused = true;
  84. var leadingRegion,
  85. needClick = false;
  86. if(this._map.lastSelectedFeature){
  87. leadingRegion = this._map.lastSelectedFeature;
  88. }else{
  89. var mapCenter = this._map.getMapCenter(),
  90. minDistance = Infinity;
  91. // find the region most closing to the map center
  92. functional.forIn(this._map.mapObj.features, function(feature){
  93. var distance = Math.sqrt(Math.pow(feature._center[0] - mapCenter.x, 2) + Math.pow(feature._center[1] - mapCenter.y, 2));
  94. if(distance < minDistance){
  95. minDistance = distance;
  96. leadingRegion = feature;
  97. }
  98. });
  99. needClick = true;
  100. }
  101. if(leadingRegion){
  102. if(needClick) {
  103. leadingRegion._onclickHandler(null);
  104. }else{
  105. }
  106. this._map.mapObj.marker.show(leadingRegion.id);
  107. }
  108. },
  109. onBlur: function(){
  110. this._map.lastSelectedFeature = this._map.selectedFeature;
  111. },
  112. _directTo: function(up,down,left,right){
  113. var currentSelected = this._map.selectedFeature,
  114. centerX = currentSelected._center[0],
  115. centerY = currentSelected._center[1],
  116. minMargin = Infinity,
  117. nextSelected = null;
  118. functional.forIn(this._map.mapObj.features, function(feature){
  119. var paddingX = Math.abs(centerX - feature._center[0]),
  120. paddingY = Math.abs(centerY - feature._center[1]),
  121. paddingSum = paddingX + paddingY;
  122. if((up - down) * (centerY - feature._center[1]) > 0){
  123. if(paddingX < paddingY && minMargin > paddingSum){
  124. minMargin = paddingSum;
  125. nextSelected = feature;
  126. }
  127. }
  128. if((left - right) * (centerX - feature._center[0]) > 0){
  129. if(paddingX > paddingY && minMargin > paddingSum){
  130. minMargin = paddingSum;
  131. nextSelected = feature;
  132. }
  133. }
  134. });
  135. if(nextSelected){
  136. this._map.mapObj.marker.hide();
  137. nextSelected._onclickHandler(null);
  138. this._map.mapObj.marker.show(nextSelected.id);
  139. }
  140. }
  141. });
  142. });