blob: cf2de99ea7e35f66590fc1733c9b7fc88a3451b4 [file] [log] [blame]
Marc Kupietz09b75752023-10-07 09:32:19 +02001/**
2 * Makes it possible to jump to a slide by entering its
3 * slide number or id.
4 */
5export default class JumpToSlide {
6
7 constructor( Reveal ) {
8
9 this.Reveal = Reveal;
10
11 this.onInput = this.onInput.bind( this );
12 this.onBlur = this.onBlur.bind( this );
13 this.onKeyDown = this.onKeyDown.bind( this );
14
15 }
16
17 render() {
18
19 this.element = document.createElement( 'div' );
20 this.element.className = 'jump-to-slide';
21
22 this.jumpInput = document.createElement( 'input' );
23 this.jumpInput.type = 'text';
24 this.jumpInput.className = 'jump-to-slide-input';
25 this.jumpInput.placeholder = 'Jump to slide';
26 this.jumpInput.addEventListener( 'input', this.onInput );
27 this.jumpInput.addEventListener( 'keydown', this.onKeyDown );
28 this.jumpInput.addEventListener( 'blur', this.onBlur );
29
30 this.element.appendChild( this.jumpInput );
31
32 }
33
34 show() {
35
36 this.indicesOnShow = this.Reveal.getIndices();
37
38 this.Reveal.getRevealElement().appendChild( this.element );
39 this.jumpInput.focus();
40
41 }
42
43 hide() {
44
45 if( this.isVisible() ) {
46 this.element.remove();
47 this.jumpInput.value = '';
48
49 clearTimeout( this.jumpTimeout );
50 delete this.jumpTimeout;
51 }
52
53 }
54
55 isVisible() {
56
57 return !!this.element.parentNode;
58
59 }
60
61 /**
62 * Parses the current input and jumps to the given slide.
63 */
64 jump() {
65
66 clearTimeout( this.jumpTimeout );
67 delete this.jumpTimeout;
68
69 const query = this.jumpInput.value.trim( '' );
70 let indices = this.Reveal.location.getIndicesFromHash( query, { oneBasedIndex: true } );
71
72 // If no valid index was found and the input query is a
73 // string, fall back on a simple search
74 if( !indices && /\S+/i.test( query ) && query.length > 1 ) {
75 indices = this.search( query );
76 }
77
78 if( indices && query !== '' ) {
79 this.Reveal.slide( indices.h, indices.v, indices.f );
80 return true;
81 }
82 else {
83 this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
84 return false;
85 }
86
87 }
88
89 jumpAfter( delay ) {
90
91 clearTimeout( this.jumpTimeout );
92 this.jumpTimeout = setTimeout( () => this.jump(), delay );
93
94 }
95
96 /**
97 * A lofi search that looks for the given query in all
98 * of our slides and returns the first match.
99 */
100 search( query ) {
101
102 const regex = new RegExp( '\\b' + query.trim() + '\\b', 'i' );
103
104 const slide = this.Reveal.getSlides().find( ( slide ) => {
105 return regex.test( slide.innerText );
106 } );
107
108 if( slide ) {
109 return this.Reveal.getIndices( slide );
110 }
111 else {
112 return null;
113 }
114
115 }
116
117 /**
118 * Reverts back to the slide we were on when jump to slide was
119 * invoked.
120 */
121 cancel() {
122
123 this.Reveal.slide( this.indicesOnShow.h, this.indicesOnShow.v, this.indicesOnShow.f );
124 this.hide();
125
126 }
127
128 confirm() {
129
130 this.jump();
131 this.hide();
132
133 }
134
135 destroy() {
136
137 this.jumpInput.removeEventListener( 'input', this.onInput );
138 this.jumpInput.removeEventListener( 'keydown', this.onKeyDown );
139 this.jumpInput.removeEventListener( 'blur', this.onBlur );
140
141 this.element.remove();
142
143 }
144
145 onKeyDown( event ) {
146
147 if( event.keyCode === 13 ) {
148 this.confirm();
149 }
150 else if( event.keyCode === 27 ) {
151 this.cancel();
152
153 event.stopImmediatePropagation();
154 }
155
156 }
157
158 onInput( event ) {
159
160 this.jumpAfter( 200 );
161
162 }
163
164 onBlur() {
165
166 setTimeout( () => this.hide(), 1 );
167
168 }
169
170}