list_focus.js 5.0 KB

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