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