blob: d7797764775527dcf5b2885dbb6a3cf27ae8c9a3 [file] [log] [blame]
Nils Diewald0e6992a2015-04-14 20:13:52 +00001// Input field for queries
Akron308db382016-05-30 22:34:07 +02002/*
Akronda5bd3a2020-10-16 17:37:49 +02003 * TODO: Support alert for query problems.
Akron308db382016-05-30 22:34:07 +02004 */
5
Akronda5bd3a2020-10-16 17:37:49 +02006"use strict";
Nils Diewald7148c6f2015-05-04 15:07:53 +00007
Akronda5bd3a2020-10-16 17:37:49 +02008define(['util'], function () {
9
10 return {
11
12 /**
13 * Create a new input field.
14 */
15 create : function (element) {
16 return Object.create(this)._init(element);
17 },
Nils Diewald0e6992a2015-04-14 20:13:52 +000018
Nils Diewald0e6992a2015-04-14 20:13:52 +000019
Akronda5bd3a2020-10-16 17:37:49 +020020 // Initialize new input field
21 _init : function (element) {
22 this._element = element;
23
24 this._container = document.createElement("div");
25 this._container.setAttribute('id', 'hint');
26
27 // Create mirror for searchField
28 // This is important for positioning
29 // if ((this._mirror = document.getElementById("searchMirror")) === null) {
30 this._mirror = document.createElement("div");
31 const m = this._mirror;
32 m.classList.add('hint', 'mirror');
33 m.addE("span");
34 m.appendChild(this._container);
35 m.style.height = "0px";
36 document.getElementsByTagName("body")[0].appendChild(m);
37
38 // Update position of the mirror
39 const re = this.reposition.bind(this);
40 window.addEventListener('resize', re);
41 this._element.addEventListener('onfocus', re);
42 this.reposition();
43 return this;
44 },
Nils Diewald0e6992a2015-04-14 20:13:52 +000045
Nils Diewald7148c6f2015-05-04 15:07:53 +000046
Akronda5bd3a2020-10-16 17:37:49 +020047 /**
48 * Get the mirrored input field.
49 */
50 mirror : function () {
51 return this._mirror;
52 },
Nils Diewald0e6992a2015-04-14 20:13:52 +000053
Nils Diewald7148c6f2015-05-04 15:07:53 +000054
Akronda5bd3a2020-10-16 17:37:49 +020055 /**
56 * Get the container element.
57 * This contains the hint helper / menus
58 * and probably an
59 * error message.
60 */
61 container : function () {
62 return this._container;
63 },
Nils Diewald0e6992a2015-04-14 20:13:52 +000064
Akron308db382016-05-30 22:34:07 +020065
Akronda5bd3a2020-10-16 17:37:49 +020066 /**
67 * Get the input element the
68 * hint helper is attached to.
69 */
70 element : function () {
71 return this._element;
72 },
Nils Diewald0e6992a2015-04-14 20:13:52 +000073
Akronc14cbfc2018-08-31 13:15:55 +020074
Akronda5bd3a2020-10-16 17:37:49 +020075 /**
76 * Get the value of the input field
77 * the hint helper is attached to.
78 */
79 value : function () {
80 return this._element.value;
81 },
Akron00cd4d12016-05-31 21:01:11 +020082
Nils Diewald7148c6f2015-05-04 15:07:53 +000083
Akronda5bd3a2020-10-16 17:37:49 +020084 /**
85 * Get the value of the input field mirror.
86 */
87 mirrorValue : function () {
88 return this._mirror.firstChild.textContent;
89 },
Akron95abaf42018-04-26 15:33:22 +020090
91
Akronda5bd3a2020-10-16 17:37:49 +020092 /**
93 * Reset the input value
94 */
95 reset : function () {
96 this._element.value = "";
97 },
Nils Diewald0e6992a2015-04-14 20:13:52 +000098
Akron308db382016-05-30 22:34:07 +020099
Akronda5bd3a2020-10-16 17:37:49 +0200100 /**
101 * Update the mirror content.
102 */
103 update : function () {
104 this._mirror.firstChild.textContent = this._split()[0];
105 this._container.style.left = this._rightPos() + 'px';
106 return this;
107 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000108
Akron308db382016-05-30 22:34:07 +0200109
Akronda5bd3a2020-10-16 17:37:49 +0200110 /**
111 * Insert text into the mirror.
112 * This is a prefix of the input field's
113 * value.
114 */
115 insert : function (text) {
116 const splittedText = this._split();
117 const s = this._element;
118 s.value = splittedText[0] + text + splittedText[1];
119 s.selectionStart = (splittedText[0] + text).length;
120 s.selectionEnd = s.selectionStart;
121
122 // Maybe update?
123 this._mirror.firstChild.textContent = splittedText[0] + text;
124 return this;
125 },
Nils Diewald7148c6f2015-05-04 15:07:53 +0000126
Akronc14cbfc2018-08-31 13:15:55 +0200127
Akronda5bd3a2020-10-16 17:37:49 +0200128 /**
129 * Move hinthelper to given character position
130 */
131 moveto : function (charpos) {
132 const e = this._element;
133 e.selectionStart = charpos;
134 e.selectionEnd = charpos;
135 e.focus();
136 return this.update();
137 },
Akron308db382016-05-30 22:34:07 +0200138
Akronc14cbfc2018-08-31 13:15:55 +0200139
Akronda5bd3a2020-10-16 17:37:49 +0200140 /**
141 * Reposition the input mirror directly
142 * below the input box.
143 */
144 reposition : function () {
145 const inputClientRect = this._element.getBoundingClientRect();
146 const inputStyle = window.getComputedStyle(this._element, null);
147
148 const bodyClientRect =
149 document.getElementsByTagName('body')[0].getBoundingClientRect();
150
151 // Reset position
152 const mirrorStyle = this._mirror.style;
153 mirrorStyle.left = parseInt(inputClientRect.left) + "px";
154 mirrorStyle.top = parseInt(inputClientRect.bottom - bodyClientRect.top) + "px";
155 mirrorStyle.width = inputStyle.getPropertyValue("width");
156
157 // These may be relevant in case of media depending css
158 mirrorStyle.paddingLeft = inputStyle.getPropertyValue("padding-left");
159 mirrorStyle.marginLeft = inputStyle.getPropertyValue("margin-left");
160 mirrorStyle.borderLeftWidth = inputStyle.getPropertyValue("border-left-width");
161 mirrorStyle.borderLeftStyle = inputStyle.getPropertyValue("border-left-style");
162 mirrorStyle.fontSize = inputStyle.getPropertyValue("font-size");
163 mirrorStyle.fontFamily = inputStyle.getPropertyValue("font-family");
164 },
Akron308db382016-05-30 22:34:07 +0200165
166
Akronda5bd3a2020-10-16 17:37:49 +0200167 /**
168 * Get the context, which is the input
169 * field's value bounded to the
170 * cursor position.
171 */
172 context : function () {
173 return this._split()[0];
174 },
Akron308db382016-05-30 22:34:07 +0200175
Akron308db382016-05-30 22:34:07 +0200176
Akronda5bd3a2020-10-16 17:37:49 +0200177 // Get the right position
178 _rightPos : function () {
179 const box = this._mirror.firstChild.getBoundingClientRect();
180 return box.right - box.left;
181 },
182
183
184 /*
185 * Return two substrings,
186 * splitted at a given position
187 * or at the current cursor position.
188 */
189 _split : function (start) {
190 const s = this._element;
191 const value = s.value;
192
193 // Get start from cursor position
194 if (arguments.length === 0)
195 start = s.selectionStart;
196
197 return new Array(
198 value.substring(0, start),
199 value.substring(start, value.length)
200 );
201 }
Nils Diewald0e6992a2015-04-14 20:13:52 +0000202 }
203});