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