1
0

list_focus.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // TODO: This file was created by bulk-decaffeinate.
  2. // Sanity-check the conversion and remove this comment.
  3. /*
  4. * decaffeinate suggestions:
  5. * DS102: Remove unnecessary code created because of implicit returns
  6. * DS206: Consider reworking classes to avoid initClass
  7. * DS207: Consider shorter variations of null checks
  8. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
  9. */
  10. app.views.ListFocus = class ListFocus extends app.View {
  11. static initClass() {
  12. this.activeClass = "focus";
  13. this.events = { click: "onClick" };
  14. this.shortcuts = {
  15. up: "onUp",
  16. down: "onDown",
  17. left: "onLeft",
  18. enter: "onEnter",
  19. superEnter: "onSuperEnter",
  20. escape: "blur",
  21. };
  22. }
  23. constructor(el) {
  24. super(el);
  25. this.focusOnNextFrame = $.framify(this.focus, this);
  26. }
  27. focus(el, options) {
  28. if (options == null) {
  29. options = {};
  30. }
  31. if (el && !el.classList.contains(this.constructor.activeClass)) {
  32. this.blur();
  33. el.classList.add(this.constructor.activeClass);
  34. if (options.silent !== true) {
  35. $.trigger(el, "focus");
  36. }
  37. }
  38. }
  39. blur() {
  40. let cursor;
  41. if ((cursor = this.getCursor())) {
  42. cursor.classList.remove(this.constructor.activeClass);
  43. $.trigger(cursor, "blur");
  44. }
  45. }
  46. getCursor() {
  47. return (
  48. this.findByClass(this.constructor.activeClass) ||
  49. this.findByClass(app.views.ListSelect.activeClass)
  50. );
  51. }
  52. findNext(cursor) {
  53. let next;
  54. if ((next = cursor.nextSibling)) {
  55. if (next.tagName === "A") {
  56. return next;
  57. } else if (next.tagName === "SPAN") {
  58. // pagination link
  59. $.click(next);
  60. return this.findNext(cursor);
  61. } else if (next.tagName === "DIV") {
  62. // sub-list
  63. if (cursor.className.indexOf(" open") >= 0) {
  64. return this.findFirst(next) || this.findNext(next);
  65. } else {
  66. return this.findNext(next);
  67. }
  68. } else if (next.tagName === "H6") {
  69. // title
  70. return this.findNext(next);
  71. }
  72. } else if (cursor.parentNode !== this.el) {
  73. return this.findNext(cursor.parentNode);
  74. }
  75. }
  76. findFirst(cursor) {
  77. let first;
  78. if (!(first = cursor.firstChild)) {
  79. return;
  80. }
  81. if (first.tagName === "A") {
  82. return first;
  83. } else if (first.tagName === "SPAN") {
  84. // pagination link
  85. $.click(first);
  86. return this.findFirst(cursor);
  87. }
  88. }
  89. findPrev(cursor) {
  90. let prev;
  91. if ((prev = cursor.previousSibling)) {
  92. if (prev.tagName === "A") {
  93. return prev;
  94. } else if (prev.tagName === "SPAN") {
  95. // pagination link
  96. $.click(prev);
  97. return this.findPrev(cursor);
  98. } else if (prev.tagName === "DIV") {
  99. // sub-list
  100. if (prev.previousSibling.className.indexOf("open") >= 0) {
  101. return this.findLast(prev) || this.findPrev(prev);
  102. } else {
  103. return this.findPrev(prev);
  104. }
  105. } else if (prev.tagName === "H6") {
  106. // title
  107. return this.findPrev(prev);
  108. }
  109. } else if (cursor.parentNode !== this.el) {
  110. return this.findPrev(cursor.parentNode);
  111. }
  112. }
  113. findLast(cursor) {
  114. let last;
  115. if (!(last = cursor.lastChild)) {
  116. return;
  117. }
  118. if (last.tagName === "A") {
  119. return last;
  120. } else if (last.tagName === "SPAN" || last.tagName === "H6") {
  121. // pagination link or title
  122. return this.findPrev(last);
  123. } else if (last.tagName === "DIV") {
  124. // sub-list
  125. return this.findLast(last);
  126. }
  127. }
  128. onDown() {
  129. let cursor;
  130. if ((cursor = this.getCursor())) {
  131. this.focusOnNextFrame(this.findNext(cursor));
  132. } else {
  133. this.focusOnNextFrame(this.findByTag("a"));
  134. }
  135. }
  136. onUp() {
  137. let cursor;
  138. if ((cursor = this.getCursor())) {
  139. this.focusOnNextFrame(this.findPrev(cursor));
  140. } else {
  141. this.focusOnNextFrame(this.findLastByTag("a"));
  142. }
  143. }
  144. onLeft() {
  145. const cursor = this.getCursor();
  146. if (
  147. cursor &&
  148. !cursor.classList.contains(app.views.ListFold.activeClass) &&
  149. cursor.parentNode !== this.el
  150. ) {
  151. const prev = cursor.parentNode.previousSibling;
  152. if (prev && prev.classList.contains(app.views.ListFold.targetClass)) {
  153. this.focusOnNextFrame(cursor.parentNode.previousSibling);
  154. }
  155. }
  156. }
  157. onEnter() {
  158. let cursor;
  159. if ((cursor = this.getCursor())) {
  160. $.click(cursor);
  161. }
  162. }
  163. onSuperEnter() {
  164. let cursor;
  165. if ((cursor = this.getCursor())) {
  166. $.popup(cursor);
  167. }
  168. }
  169. onClick(event) {
  170. if (event.which !== 1 || event.metaKey || event.ctrlKey) {
  171. return;
  172. }
  173. const target = $.eventTarget(event);
  174. if (target.tagName === "A") {
  175. this.focus(target, { silent: true });
  176. }
  177. }
  178. };
  179. app.views.ListFocus.initClass();