OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008 Apple Inc. All rights reserved. |
3 * Copyright (C) 2011 Google Inc. All rights reserved. | 3 * Copyright (C) 2011 Google Inc. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * | 8 * |
9 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 WebInspector.setCurrentFocusElement(WebInspector.previousFocusElemen
t()); | 130 WebInspector.setCurrentFocusElement(WebInspector.previousFocusElemen
t()); |
131 }, | 131 }, |
132 | 132 |
133 get text() | 133 get text() |
134 { | 134 { |
135 return this._element.textContent; | 135 return this._element.textContent; |
136 }, | 136 }, |
137 | 137 |
138 set text(x) | 138 set text(x) |
139 { | 139 { |
140 this._removeSuggestionAids(); | 140 this.clearAutoComplete(true); |
141 if (!x) { | 141 if (!x) { |
142 // Append a break element instead of setting textContent to make sur
e the selection is inside the prompt. | 142 // Append a break element instead of setting textContent to make sur
e the selection is inside the prompt. |
143 this._element.removeChildren(); | 143 this._element.removeChildren(); |
144 this._element.appendChild(document.createElement("br")); | 144 this._element.appendChild(document.createElement("br")); |
145 } else | 145 } else |
146 this._element.textContent = x; | 146 this._element.textContent = x; |
147 | 147 |
148 this.moveCaretToEndOfPrompt(); | 148 this.moveCaretToEndOfPrompt(); |
149 this._element.scrollIntoView(); | 149 this._element.scrollIntoView(); |
150 }, | 150 }, |
(...skipping 28 matching lines...) Expand all Loading... |
179 _stopEditing: function() | 179 _stopEditing: function() |
180 { | 180 { |
181 this._element.tabIndex = this._oldTabIndex; | 181 this._element.tabIndex = this._oldTabIndex; |
182 if (this._blurListener) | 182 if (this._blurListener) |
183 this._element.removeEventListener("blur", this._blurListener, false)
; | 183 this._element.removeEventListener("blur", this._blurListener, false)
; |
184 this._element.removeStyleClass("editing"); | 184 this._element.removeStyleClass("editing"); |
185 delete this._isEditing; | 185 delete this._isEditing; |
186 WebInspector.markBeingEdited(this._element, false); | 186 WebInspector.markBeingEdited(this._element, false); |
187 }, | 187 }, |
188 | 188 |
189 _removeSuggestionAids: function() | |
190 { | |
191 this.clearAutoComplete(); | |
192 this.hideSuggestBox(); | |
193 }, | |
194 | |
195 _selectStart: function(event) | 189 _selectStart: function(event) |
196 { | 190 { |
197 if (this._selectionTimeout) | 191 if (this._selectionTimeout) |
198 clearTimeout(this._selectionTimeout); | 192 clearTimeout(this._selectionTimeout); |
199 | 193 |
200 this._removeSuggestionAids(); | 194 this.clearAutoComplete(); |
201 | 195 |
202 function moveBackIfOutside() | 196 function moveBackIfOutside() |
203 { | 197 { |
204 delete this._selectionTimeout; | 198 delete this._selectionTimeout; |
205 if (!this.isCaretInsidePrompt() && window.getSelection().isCollapsed
) { | 199 if (!this.isCaretInsidePrompt() && window.getSelection().isCollapsed
) |
206 this.moveCaretToEndOfPrompt(); | 200 this.moveCaretToEndOfPrompt(); |
207 this.autoCompleteSoon(); | 201 this.autoCompleteSoon(); |
208 } | |
209 } | 202 } |
210 | 203 |
211 this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100); | 204 this._selectionTimeout = setTimeout(moveBackIfOutside.bind(this), 100); |
212 }, | 205 }, |
213 | 206 |
214 /** | 207 /** |
215 * @param {boolean=} force | 208 * @param {boolean=} force |
216 */ | 209 */ |
217 defaultKeyHandler: function(event, force) | 210 defaultKeyHandler: function(event, force) |
218 { | 211 { |
219 this.clearAutoComplete(); | 212 this.clearAutoComplete(); |
220 this.autoCompleteSoon(force); | 213 this.autoCompleteSoon(force); |
221 return false; | 214 return false; |
222 }, | 215 }, |
223 | 216 |
224 onKeyDown: function(event) | 217 onKeyDown: function(event) |
225 { | 218 { |
226 var handled = false; | 219 var handled = false; |
227 var invokeDefault = true; | 220 var invokeDefault = true; |
228 | 221 |
229 switch (event.keyIdentifier) { | 222 switch (event.keyIdentifier) { |
230 case "Up": | 223 case "Up": |
231 handled = this.upKeyPressed(event); | 224 handled = this.upKeyPressed(event); |
232 break; | 225 break; |
233 case "Down": | 226 case "Down": |
234 handled = this.downKeyPressed(event); | 227 handled = this.downKeyPressed(event); |
235 break; | 228 break; |
236 case "PageUp": | |
237 handled = this.pageUpKeyPressed(event); | |
238 break; | |
239 case "PageDown": | |
240 handled = this.pageDownKeyPressed(event); | |
241 break; | |
242 case "U+0009": // Tab | 229 case "U+0009": // Tab |
243 handled = this.tabKeyPressed(event); | 230 handled = this.tabKeyPressed(event); |
244 break; | 231 break; |
245 case "Enter": | 232 case "Enter": |
246 handled = this.enterKeyPressed(event); | 233 handled = this.enterKeyPressed(event); |
247 break; | 234 break; |
248 case "Left": | |
249 case "Home": | |
250 this._removeSuggestionAids(); | |
251 invokeDefault = false; | |
252 break; | |
253 case "Right": | 235 case "Right": |
254 case "End": | 236 case "End": |
255 if (this.isCaretAtEndOfPrompt()) | 237 if (this.isSuggestBoxVisible() && this.isCaretAtEndOfPrompt()) |
| 238 handled = this._suggestBox.tabKeyPressed(event); |
| 239 else { |
256 handled = this.acceptAutoComplete(); | 240 handled = this.acceptAutoComplete(); |
257 else | 241 if (!handled) |
258 this._removeSuggestionAids(); | 242 this.autoCompleteSoon(); |
259 invokeDefault = false; | 243 } |
260 break; | 244 break; |
261 case "U+001B": // Esc | 245 case "U+001B": // Esc |
262 if (this.isSuggestBoxVisible()) { | 246 if (this.isSuggestBoxVisible()) { |
263 this._suggestBox.hide(); | 247 this._suggestBox.hide(); |
264 handled = true; | 248 handled = true; |
| 249 break; |
265 } | 250 } |
266 break; | |
267 case "U+0020": // Space | 251 case "U+0020": // Space |
268 if (this._suggestForceable && event.ctrlKey && !event.metaKey && !ev
ent.altKey && !event.shiftKey) { | 252 if (this._suggestForceable && event.ctrlKey && !event.metaKey && !ev
ent.altKey && !event.shiftKey) { |
269 this.defaultKeyHandler(event, true); | 253 this.defaultKeyHandler(event, true); |
270 handled = true; | 254 handled = true; |
271 } | 255 } |
272 break; | 256 break; |
273 case "Alt": | 257 case "Alt": |
274 case "Meta": | 258 case "Meta": |
275 case "Shift": | 259 case "Shift": |
276 case "Control": | 260 case "Control": |
277 invokeDefault = false; | 261 invokeDefault = false; |
278 break; | 262 break; |
279 } | 263 } |
280 | 264 |
281 if (!handled && invokeDefault) | 265 if (!handled && invokeDefault) |
282 handled = this.defaultKeyHandler(event); | 266 handled = this.defaultKeyHandler(event); |
283 | 267 |
284 if (handled) { | 268 if (handled) { |
285 event.preventDefault(); | 269 event.preventDefault(); |
286 event.stopPropagation(); | 270 event.stopPropagation(); |
287 } | 271 } |
288 | 272 |
289 return handled; | 273 return handled; |
290 }, | 274 }, |
291 | 275 |
292 acceptAutoComplete: function() | 276 acceptAutoComplete: function() |
293 { | 277 { |
294 var result = false; | |
295 if (this.isSuggestBoxVisible()) | 278 if (this.isSuggestBoxVisible()) |
296 result = this._suggestBox.acceptSuggestion(); | 279 return this._suggestBox.acceptSuggestion(); |
297 if (!result) | 280 return this.acceptSuggestion(); |
298 result = this.acceptSuggestion(); | |
299 | |
300 return result; | |
301 }, | 281 }, |
302 | 282 |
303 /** | 283 /** |
304 * @param {boolean=} includeTimeout | 284 * @param {boolean=} includeTimeout |
305 */ | 285 */ |
306 clearAutoComplete: function(includeTimeout) | 286 clearAutoComplete: function(includeTimeout) |
307 { | 287 { |
308 if (includeTimeout && this._completeTimeout) { | 288 if (includeTimeout && this._completeTimeout) { |
309 clearTimeout(this._completeTimeout); | 289 clearTimeout(this._completeTimeout); |
310 delete this._completeTimeout; | 290 delete this._completeTimeout; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
361 var selectionRange = selection.getRangeAt(0); | 341 var selectionRange = selection.getRangeAt(0); |
362 var isEmptyInput = selectionRange.commonAncestorContainer === this._elem
ent; // this._element has no child Text nodes. | 342 var isEmptyInput = selectionRange.commonAncestorContainer === this._elem
ent; // this._element has no child Text nodes. |
363 | 343 |
364 var shouldExit; | 344 var shouldExit; |
365 | 345 |
366 // Do not attempt to auto-complete an empty input in the auto mode (only
on demand). | 346 // Do not attempt to auto-complete an empty input in the auto mode (only
on demand). |
367 if (auto && isEmptyInput && !force) | 347 if (auto && isEmptyInput && !force) |
368 shouldExit = true; | 348 shouldExit = true; |
369 else if (!auto && !isEmptyInput && !selectionRange.commonAncestorContain
er.isDescendant(this._element)) | 349 else if (!auto && !isEmptyInput && !selectionRange.commonAncestorContain
er.isDescendant(this._element)) |
370 shouldExit = true; | 350 shouldExit = true; |
371 else if (auto && !force && !this.isCaretAtEndOfPrompt() && !this.isSugge
stBoxVisible()) | 351 else if (auto && !this._suggestBox && !force && !this.isCaretAtEndOfProm
pt()) |
372 shouldExit = true; | 352 shouldExit = true; |
373 else if (!selection.isCollapsed) | 353 else if (!selection.isCollapsed) |
374 shouldExit = true; | 354 shouldExit = true; |
375 else if (!force) { | 355 else if (!force) { |
376 // BUG72018: Do not show suggest box if caret is followed by a non-s
top character. | 356 // BUG72018: Do not show suggest box if caret is followed by a non-s
top character. |
377 var wordSuffixRange = selectionRange.startContainer.rangeOfWord(sele
ctionRange.endOffset, this._completionStopCharacters, this._element, "forward"); | 357 var wordSuffixRange = selectionRange.startContainer.rangeOfWord(sele
ctionRange.endOffset, this._completionStopCharacters, this._element, "forward"); |
378 if (wordSuffixRange.toString().length) | 358 if (wordSuffixRange.toString().length) |
379 shouldExit = true; | 359 shouldExit = true; |
380 } | 360 } |
381 if (shouldExit) { | 361 if (shouldExit) { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 fullWordRange.setStart(originalWordPrefixRange.startContainer, originalW
ordPrefixRange.startOffset); | 398 fullWordRange.setStart(originalWordPrefixRange.startContainer, originalW
ordPrefixRange.startOffset); |
419 fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffs
et); | 399 fullWordRange.setEnd(selectionRange.endContainer, selectionRange.endOffs
et); |
420 | 400 |
421 if (originalWordPrefixRange.toString() + selectionRange.toString() != fu
llWordRange.toString()) | 401 if (originalWordPrefixRange.toString() + selectionRange.toString() != fu
llWordRange.toString()) |
422 return; | 402 return; |
423 | 403 |
424 this._userEnteredRange = fullWordRange; | 404 this._userEnteredRange = fullWordRange; |
425 this._userEnteredText = fullWordRange.toString(); | 405 this._userEnteredText = fullWordRange.toString(); |
426 | 406 |
427 if (this._suggestBox) | 407 if (this._suggestBox) |
428 this._suggestBox.updateSuggestions(this._boxForAnchorAtStart(selecti
on, fullWordRange), completions, !this.isCaretAtEndOfPrompt()); | 408 this._suggestBox.updateSuggestions(this._boxForAnchorAtStart(selecti
on, fullWordRange), completions); |
429 | 409 |
430 var wordPrefixLength = originalWordPrefixRange.toString().length; | 410 var wordPrefixLength = originalWordPrefixRange.toString().length; |
431 | 411 |
432 if (auto) | 412 if (auto) |
433 var completionText = completions[0]; | 413 var completionText = completions[0]; |
434 else { | 414 else { |
435 if (completions.length === 1) { | 415 if (completions.length === 1) { |
436 var completionText = completions[0]; | 416 var completionText = completions[0]; |
437 wordPrefixLength = completionText.length; | 417 wordPrefixLength = completionText.length; |
438 } else { | 418 } else { |
(...skipping 26 matching lines...) Expand all Loading... |
465 var completionText = completions[0]; | 445 var completionText = completions[0]; |
466 else if (nextIndex < 0) | 446 else if (nextIndex < 0) |
467 var completionText = completions[completions.length - 1]
; | 447 var completionText = completions[completions.length - 1]
; |
468 else | 448 else |
469 var completionText = completions[nextIndex]; | 449 var completionText = completions[nextIndex]; |
470 } | 450 } |
471 } | 451 } |
472 } | 452 } |
473 | 453 |
474 if (auto) { | 454 if (auto) { |
475 if (this.isCaretAtEndOfPrompt()) { | 455 this._userEnteredRange.deleteContents(); |
476 this._userEnteredRange.deleteContents(); | 456 this._element.pruneEmptyTextNodes(); |
477 this._element.pruneEmptyTextNodes(); | 457 var finalSelectionRange = document.createRange(); |
478 var finalSelectionRange = document.createRange(); | 458 var prefixText = completionText.substring(0, wordPrefixLength); |
479 var prefixText = completionText.substring(0, wordPrefixLength); | 459 var suffixText = completionText.substring(wordPrefixLength); |
480 var suffixText = completionText.substring(wordPrefixLength); | |
481 | 460 |
482 var prefixTextNode = document.createTextNode(prefixText); | 461 var prefixTextNode = document.createTextNode(prefixText); |
483 fullWordRange.insertNode(prefixTextNode); | 462 fullWordRange.insertNode(prefixTextNode); |
484 | 463 |
485 this.autoCompleteElement = document.createElement("span"); | 464 this.autoCompleteElement = document.createElement("span"); |
486 this.autoCompleteElement.className = "auto-complete-text"; | 465 this.autoCompleteElement.className = "auto-complete-text"; |
487 this.autoCompleteElement.textContent = suffixText; | 466 this.autoCompleteElement.textContent = suffixText; |
488 | 467 |
489 prefixTextNode.parentNode.insertBefore(this.autoCompleteElement,
prefixTextNode.nextSibling); | 468 prefixTextNode.parentNode.insertBefore(this.autoCompleteElement, pre
fixTextNode.nextSibling); |
490 | 469 |
491 finalSelectionRange.setStart(prefixTextNode, wordPrefixLength); | 470 finalSelectionRange.setStart(prefixTextNode, wordPrefixLength); |
492 finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength); | 471 finalSelectionRange.setEnd(prefixTextNode, wordPrefixLength); |
493 selection.removeAllRanges(); | 472 selection.removeAllRanges(); |
494 selection.addRange(finalSelectionRange); | 473 selection.addRange(finalSelectionRange); |
495 } | |
496 } else | 474 } else |
497 this.applySuggestion(completionText, completions.length > 1, origina
lWordPrefixRange); | 475 this.applySuggestion(completionText, completions.length > 1, origina
lWordPrefixRange); |
498 }, | 476 }, |
499 | 477 |
500 /** | 478 /** |
501 * @param {Range=} originalPrefixRange | 479 * @param {Range=} originalPrefixRange |
502 */ | 480 */ |
503 applySuggestion: function(completionText, isIntermediateSuggestion, original
PrefixRange) | 481 applySuggestion: function(completionText, isIntermediateSuggestion, original
PrefixRange) |
504 { | 482 { |
505 var wordPrefixLength; | 483 var wordPrefixLength; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
580 var node = selectionRange.startContainer; | 558 var node = selectionRange.startContainer; |
581 if (node !== this._element && !node.isDescendant(this._element)) | 559 if (node !== this._element && !node.isDescendant(this._element)) |
582 return false; | 560 return false; |
583 | 561 |
584 if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < nod
e.nodeValue.length) | 562 if (node.nodeType === Node.TEXT_NODE && selectionRange.startOffset < nod
e.nodeValue.length) |
585 return false; | 563 return false; |
586 | 564 |
587 var foundNextText = false; | 565 var foundNextText = false; |
588 while (node) { | 566 while (node) { |
589 if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) { | 567 if (node.nodeType === Node.TEXT_NODE && node.nodeValue.length) { |
590 if (foundNextText && (!this.autoCompleteElement || !this.autoCom
pleteElement.isAncestor(node))) | 568 if (foundNextText) |
591 return false; | 569 return false; |
592 foundNextText = true; | 570 foundNextText = true; |
593 } | 571 } |
594 | 572 |
595 node = node.traverseNextNode(this._element); | 573 node = node.traverseNextNode(this._element); |
596 } | 574 } |
597 | 575 |
598 return true; | 576 return true; |
599 }, | 577 }, |
600 | 578 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
650 var offset = this._element.childNodes.length; | 628 var offset = this._element.childNodes.length; |
651 selectionRange.setStart(this._element, offset); | 629 selectionRange.setStart(this._element, offset); |
652 selectionRange.setEnd(this._element, offset); | 630 selectionRange.setEnd(this._element, offset); |
653 | 631 |
654 selection.removeAllRanges(); | 632 selection.removeAllRanges(); |
655 selection.addRange(selectionRange); | 633 selection.addRange(selectionRange); |
656 }, | 634 }, |
657 | 635 |
658 tabKeyPressed: function(event) | 636 tabKeyPressed: function(event) |
659 { | 637 { |
660 // Just consume the key. | 638 if (this.isSuggestBoxVisible()) |
| 639 return this._suggestBox.tabKeyPressed(event); |
| 640 |
| 641 this.complete(false, false, event.shiftKey); |
661 return true; | 642 return true; |
662 }, | 643 }, |
663 | 644 |
664 enterKeyPressed: function(event) | 645 enterKeyPressed: function(event) |
665 { | 646 { |
666 if (this.isSuggestBoxVisible()) | 647 if (this.isSuggestBoxVisible()) |
667 return this._suggestBox.enterKeyPressed(event); | 648 return this._suggestBox.enterKeyPressed(event); |
668 | 649 |
669 return false; | 650 return false; |
670 }, | 651 }, |
671 | 652 |
672 upKeyPressed: function(event) | 653 upKeyPressed: function(event) |
673 { | 654 { |
674 if (this.isSuggestBoxVisible()) | 655 if (this.isSuggestBoxVisible()) |
675 return this._suggestBox.upKeyPressed(event); | 656 return this._suggestBox.upKeyPressed(event); |
676 | 657 |
677 return false; | 658 return false; |
678 }, | 659 }, |
679 | 660 |
680 downKeyPressed: function(event) | 661 downKeyPressed: function(event) |
681 { | 662 { |
682 if (this.isSuggestBoxVisible()) | 663 if (this.isSuggestBoxVisible()) |
683 return this._suggestBox.downKeyPressed(event); | 664 return this._suggestBox.downKeyPressed(event); |
684 | 665 |
685 return false; | 666 return false; |
686 }, | 667 } |
687 | |
688 pageUpKeyPressed: function(event) | |
689 { | |
690 if (this.isSuggestBoxVisible()) | |
691 return this._suggestBox.pageUpKeyPressed(event); | |
692 | |
693 return false; | |
694 }, | |
695 | |
696 pageDownKeyPressed: function(event) | |
697 { | |
698 if (this.isSuggestBoxVisible()) | |
699 return this._suggestBox.pageDownKeyPressed(event); | |
700 | |
701 return false; | |
702 }, | |
703 } | 668 } |
704 | 669 |
705 WebInspector.TextPrompt.prototype.__proto__ = WebInspector.Object.prototype; | 670 WebInspector.TextPrompt.prototype.__proto__ = WebInspector.Object.prototype; |
706 | 671 |
707 /** | 672 /** |
708 * @constructor | 673 * @constructor |
709 * @extends {WebInspector.TextPrompt} | 674 * @extends {WebInspector.TextPrompt} |
710 * @param {function(Range, boolean, function(*))} completions | 675 * @param {function(Range, boolean, function(*))} completions |
711 * @param {string} stopCharacters | 676 * @param {string} stopCharacters |
712 */ | 677 */ |
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1015 var result = this._applySuggestion(text, false); | 980 var result = this._applySuggestion(text, false); |
1016 this.hide(); | 981 this.hide(); |
1017 if (!result) | 982 if (!result) |
1018 return false; | 983 return false; |
1019 | 984 |
1020 this._textPrompt.acceptSuggestion(); | 985 this._textPrompt.acceptSuggestion(); |
1021 | 986 |
1022 return true; | 987 return true; |
1023 }, | 988 }, |
1024 | 989 |
1025 _onNextItem: function(event, isPageScroll) | 990 _onNextItem: function(event) |
1026 { | 991 { |
1027 var children = this.contentElement.childNodes; | 992 var children = this.contentElement.childNodes; |
1028 if (!children.length) | 993 if (!children.length) |
1029 return false; | 994 return false; |
1030 | 995 |
1031 if (!this._selectedElement) | 996 if (this._selectedElement) |
| 997 this._selectedElement = this._selectedElement.nextSibling || this.co
ntentElement.firstChild; |
| 998 else |
1032 this._selectedElement = this.contentElement.firstChild; | 999 this._selectedElement = this.contentElement.firstChild; |
1033 else { | |
1034 if (!isPageScroll) | |
1035 this._selectedElement = this._selectedElement.nextSibling || thi
s.contentElement.firstChild; | |
1036 else { | |
1037 var candidate = this._selectedElement; | |
1038 | |
1039 for (var itemsLeft = this._rowCountPerViewport; itemsLeft; --ite
msLeft) { | |
1040 if (candidate.nextSibling) | |
1041 candidate = candidate.nextSibling; | |
1042 else | |
1043 break; | |
1044 } | |
1045 | |
1046 this._selectedElement = candidate; | |
1047 } | |
1048 } | |
1049 this._updateSelection(); | 1000 this._updateSelection(); |
1050 this._applySuggestion(undefined, true); | 1001 this._applySuggestion(undefined, true); |
1051 return true; | 1002 return true; |
1052 }, | 1003 }, |
1053 | 1004 |
1054 _onPreviousItem: function(event, isPageScroll) | 1005 _onPreviousItem: function(event) |
1055 { | 1006 { |
1056 var children = this.contentElement.childNodes; | 1007 var children = this.contentElement.childNodes; |
1057 if (!children.length) | 1008 if (!children.length) |
1058 return false; | 1009 return false; |
1059 | 1010 |
1060 if (!this._selectedElement) | 1011 if (this._selectedElement) |
| 1012 this._selectedElement = this._selectedElement.previousSibling || thi
s.contentElement.lastChild; |
| 1013 else |
1061 this._selectedElement = this.contentElement.lastChild; | 1014 this._selectedElement = this.contentElement.lastChild; |
1062 else { | |
1063 if (!isPageScroll) | |
1064 this._selectedElement = this._selectedElement.previousSibling ||
this.contentElement.lastChild; | |
1065 else { | |
1066 var candidate = this._selectedElement; | |
1067 | |
1068 for (var itemsLeft = this._rowCountPerViewport; itemsLeft; --ite
msLeft) { | |
1069 if (candidate.previousSibling) | |
1070 candidate = candidate.previousSibling; | |
1071 else | |
1072 break; | |
1073 } | |
1074 | |
1075 this._selectedElement = candidate; | |
1076 } | |
1077 } | |
1078 this._updateSelection(); | 1015 this._updateSelection(); |
1079 this._applySuggestion(undefined, true); | 1016 this._applySuggestion(undefined, true); |
1080 return true; | 1017 return true; |
1081 }, | 1018 }, |
1082 | 1019 |
1083 /** | 1020 /** |
1084 * @param {AnchorBox} anchorBox | 1021 * @param {AnchorBox} anchorBox |
1085 * @param {Array.<string>=} completions | 1022 * @param {Array.<string>=} completions |
1086 * @param {boolean=} canShowForSingleItem | |
1087 */ | 1023 */ |
1088 updateSuggestions: function(anchorBox, completions, canShowForSingleItem) | 1024 updateSuggestions: function(anchorBox, completions) |
1089 { | 1025 { |
1090 if (this._suggestTimeout) { | 1026 if (this._suggestTimeout) { |
1091 clearTimeout(this._suggestTimeout); | 1027 clearTimeout(this._suggestTimeout); |
1092 delete this._suggestTimeout; | 1028 delete this._suggestTimeout; |
1093 } | 1029 } |
1094 this._completionsReady(anchorBox, completions, canShowForSingleItem); | 1030 this._completionsReady(anchorBox, completions); |
1095 }, | 1031 }, |
1096 | 1032 |
1097 _onItemMouseDown: function(text, event) | 1033 _onItemMouseDown: function(text, event) |
1098 { | 1034 { |
1099 this.acceptSuggestion(text); | 1035 this.acceptSuggestion(text); |
1100 event.stopPropagation(); | 1036 event.stopPropagation(); |
1101 event.preventDefault(); | 1037 event.preventDefault(); |
1102 }, | 1038 }, |
1103 | 1039 |
1104 _createItemElement: function(prefix, text) | 1040 _createItemElement: function(prefix, text) |
1105 { | 1041 { |
1106 var element = document.createElement("div"); | 1042 var element = document.createElement("div"); |
1107 element.className = "suggest-box-content-item source-code"; | 1043 element.className = "suggest-box-content-item source-code"; |
1108 element.tabIndex = -1; | 1044 element.tabIndex = -1; |
1109 if (prefix && prefix.length && !text.indexOf(prefix)) { | 1045 if (prefix && prefix.length && !text.indexOf(prefix)) { |
1110 var prefixElement = element.createChild("span", "prefix"); | 1046 var prefixElement = element.createChild("span", "prefix"); |
1111 prefixElement.textContent = prefix; | 1047 prefixElement.textContent = prefix; |
1112 var suffixElement = element.createChild("span", "suffix"); | 1048 var suffixElement = element.createChild("span", "suffix"); |
1113 suffixElement.textContent = text.substring(prefix.length); | 1049 suffixElement.textContent = text.substring(prefix.length); |
1114 } else { | 1050 } else { |
1115 var suffixElement = element.createChild("span", "suffix"); | 1051 var suffixElement = element.createChild("span", "suffix"); |
1116 suffixElement.textContent = text; | 1052 suffixElement.textContent = text; |
1117 } | 1053 } |
1118 element.addEventListener("mousedown", this._onItemMouseDown.bind(this, t
ext), false); | 1054 element.addEventListener("mousedown", this._onItemMouseDown.bind(this, t
ext), false); |
1119 return element; | 1055 return element; |
1120 }, | 1056 }, |
1121 | 1057 |
1122 /** | 1058 _updateItems: function(items) |
1123 * @param {boolean=} canShowForSingleItem | |
1124 */ | |
1125 _updateItems: function(items, canShowForSingleItem) | |
1126 { | 1059 { |
| 1060 var children = this.contentElement.children; |
| 1061 this._selectedIndex = Math.min(children.length - 1, this._selectedIndex)
; |
| 1062 var selectedItemText = this._selectedIndex >= 0 ? children[this._selecte
dIndex].textContent : null; |
| 1063 var itemIndex = 0; |
| 1064 var child = this.contentElement.firstChild; |
| 1065 var childText = child ? child.textContent : null; |
1127 this.contentElement.removeChildren(); | 1066 this.contentElement.removeChildren(); |
1128 | 1067 |
1129 var userEnteredText = this._textPrompt._userEnteredText; | 1068 var userEnteredText = this._textPrompt._userEnteredText; |
1130 for (var i = 0; i < items.length; ++i) { | 1069 for (var i = 0; i < items.length; ++i) { |
1131 var item = items[i]; | 1070 var item = items[i]; |
1132 var currentItemElement = this._createItemElement(userEnteredText, it
em); | 1071 var currentItemElement = this._createItemElement(userEnteredText, it
em); |
1133 this.contentElement.appendChild(currentItemElement); | 1072 this.contentElement.appendChild(currentItemElement); |
1134 } | 1073 } |
1135 | 1074 |
1136 this._selectedElement = canShowForSingleItem ? this.contentElement.first
Child : null; | 1075 this._selectedElement = this.contentElement.firstChild; |
1137 this._updateSelection(); | 1076 this._updateSelection(); |
1138 }, | 1077 }, |
1139 | 1078 |
1140 _updateSelection: function() | 1079 _updateSelection: function() |
1141 { | 1080 { |
1142 // FIXME: might want some optimization if becomes a bottleneck. | 1081 // FIXME: might want some optimization if becomes a bottleneck. |
1143 for (var child = this.contentElement.firstChild; child; child = child.ne
xtSibling) { | 1082 for (var child = this.contentElement.firstChild; child; child = child.ne
xtSibling) { |
1144 if (child !== this._selectedElement) | 1083 if (child !== this._selectedElement) |
1145 child.removeStyleClass("selected"); | 1084 child.removeStyleClass("selected"); |
1146 } | 1085 } |
1147 if (this._selectedElement) { | 1086 if (this._selectedElement) { |
1148 this._selectedElement.addStyleClass("selected"); | 1087 this._selectedElement.addStyleClass("selected"); |
1149 this._selectedElement.scrollIntoViewIfNeeded(false); | 1088 this._selectedElement.scrollIntoViewIfNeeded(false); |
1150 } | 1089 } |
1151 }, | 1090 }, |
1152 | 1091 |
1153 /** | 1092 /** |
1154 * @param {Array.<string>=} completions | |
1155 * @param {boolean=} canShowForSingleItem | |
1156 */ | |
1157 _canShowBox: function(completions, canShowForSingleItem) | |
1158 { | |
1159 if (!completions || !completions.length) | |
1160 return false; | |
1161 | |
1162 if (completions.length > 1) | |
1163 return true; | |
1164 | |
1165 // Do not show a single suggestion if it is the same as user-entered pre
fix, even if allowed to show single-item suggest boxes. | |
1166 return canShowForSingleItem && completions[0] !== this._textPrompt._user
EnteredText; | |
1167 }, | |
1168 | |
1169 _rememberRowCountPerViewport: function() | |
1170 { | |
1171 if (!this.contentElement.firstChild) | |
1172 return; | |
1173 | |
1174 this._rowCountPerViewport = Math.floor(this.containerElement.offsetHeigh
t / this.contentElement.firstChild.offsetHeight); | |
1175 }, | |
1176 | |
1177 /** | |
1178 * @param {AnchorBox} anchorBox | 1093 * @param {AnchorBox} anchorBox |
1179 * @param {Array.<string>=} completions | 1094 * @param {Array.<string>=} completions |
1180 * @param {boolean=} canShowForSingleItem | |
1181 */ | 1095 */ |
1182 _completionsReady: function(anchorBox, completions, canShowForSingleItem) | 1096 _completionsReady: function(anchorBox, completions) |
1183 { | 1097 { |
1184 if (this._canShowBox(completions, canShowForSingleItem)) { | 1098 if (!completions || !completions.length) { |
1185 this._updateItems(completions, canShowForSingleItem); | 1099 this.hide() |
1186 this._updateBoxPosition(anchorBox); | 1100 return; |
| 1101 } |
| 1102 |
| 1103 this._updateItems(completions); |
| 1104 this._updateBoxPosition(anchorBox); |
| 1105 if (this.contentElement.children.length && this.contentElement.children.
length > 1) { |
| 1106 // Will not be shown if a sole suggestion is equal to the user input
. |
1187 this._element.addStyleClass("visible"); | 1107 this._element.addStyleClass("visible"); |
1188 this._rememberRowCountPerViewport(); | |
1189 } else | 1108 } else |
1190 this.hide(); | 1109 this.hide(); |
1191 }, | 1110 }, |
1192 | 1111 |
1193 upKeyPressed: function(event) | 1112 upKeyPressed: function(event) |
1194 { | 1113 { |
1195 return this._onPreviousItem(event); | 1114 return this._onPreviousItem(event); |
1196 }, | 1115 }, |
1197 | 1116 |
1198 downKeyPressed: function(event) | 1117 downKeyPressed: function(event) |
1199 { | 1118 { |
1200 return this._onNextItem(event); | 1119 return this._onNextItem(event); |
1201 }, | 1120 }, |
1202 | 1121 |
1203 pageUpKeyPressed: function(event) | |
1204 { | |
1205 return this._onPreviousItem(event, true); | |
1206 }, | |
1207 | |
1208 pageDownKeyPressed: function(event) | |
1209 { | |
1210 return this._onNextItem(event, true); | |
1211 }, | |
1212 | |
1213 enterKeyPressed: function(event) | 1122 enterKeyPressed: function(event) |
1214 { | 1123 { |
1215 var hasSelectedItem = !!this._selectedElement; | |
1216 this.acceptSuggestion(); | 1124 this.acceptSuggestion(); |
1217 | 1125 return true; |
1218 // Report the event as non-handled if there is no selected item, | |
1219 // to commit the input or handle it otherwise. | |
1220 return hasSelectedItem; | |
1221 }, | 1126 }, |
1222 | 1127 |
1223 tabKeyPressed: function(event) | 1128 tabKeyPressed: function(event) |
1224 { | 1129 { |
1225 return this.enterKeyPressed(event); | 1130 return this.enterKeyPressed(event); |
1226 } | 1131 } |
1227 } | 1132 } |
OLD | NEW |