|
382 | 382 | } |
383 | 383 |
|
384 | 384 |
|
| 385 | + /** |
| 386 | + * Gets the word at the clicked position along with additional information |
| 387 | + * @param {Element} element - The element that was clicked |
| 388 | + * @param {MouseEvent} event - The click event |
| 389 | + * @return {Object|null} - Object containing the word and additional info, or null if not found |
| 390 | + */ |
| 391 | + function getClickedWord(element, event) { |
| 392 | + |
| 393 | + // Try to find the clicked position within the element |
| 394 | + const range = document.caretRangeFromPoint(event.clientX, event.clientY); |
| 395 | + if (!range) { |
| 396 | + return null; |
| 397 | + } |
| 398 | + |
| 399 | + const textNode = range.startContainer; |
| 400 | + const offset = range.startOffset; |
| 401 | + |
| 402 | + // Check if we have a text node |
| 403 | + if (textNode.nodeType !== Node.TEXT_NODE) { |
| 404 | + |
| 405 | + // If the element itself contains text, try to extract a word from it |
| 406 | + if (element.textContent && element.textContent.trim()) { |
| 407 | + const text = element.textContent.trim(); |
| 408 | + |
| 409 | + // Simple word extraction - get the first word |
| 410 | + const match = text.match(/\b(\w+)\b/); |
| 411 | + if (match) { |
| 412 | + const word = match[1]; |
| 413 | + |
| 414 | + // Since we're just getting the first word, it's the first occurrence |
| 415 | + return { |
| 416 | + word: word, |
| 417 | + occurrenceIndex: 0, |
| 418 | + context: text.substring(0, Math.min(40, text.length)) |
| 419 | + }; |
| 420 | + } |
| 421 | + } |
| 422 | + |
| 423 | + return null; |
| 424 | + } |
| 425 | + |
| 426 | + const nodeText = textNode.textContent; |
| 427 | + |
| 428 | + // Function to extract a word and its occurrence index |
| 429 | + function extractWordAndOccurrence(text, wordStart, wordEnd) { |
| 430 | + const word = text.substring(wordStart, wordEnd); |
| 431 | + |
| 432 | + // Calculate which occurrence of this word it is |
| 433 | + const textBeforeWord = text.substring(0, wordStart); |
| 434 | + const regex = new RegExp("\\b" + word + "\\b", "g"); |
| 435 | + let occurrenceIndex = 0; |
| 436 | + let match; |
| 437 | + |
| 438 | + while ((match = regex.exec(textBeforeWord)) !== null) { |
| 439 | + occurrenceIndex++; |
| 440 | + } |
| 441 | + |
| 442 | + |
| 443 | + // Get context around the word (up to 20 chars before and after) |
| 444 | + const contextStart = Math.max(0, wordStart - 20); |
| 445 | + const contextEnd = Math.min(text.length, wordEnd + 20); |
| 446 | + const context = text.substring(contextStart, contextEnd); |
| 447 | + |
| 448 | + return { |
| 449 | + word: word, |
| 450 | + occurrenceIndex: occurrenceIndex, |
| 451 | + context: context |
| 452 | + }; |
| 453 | + } |
| 454 | + |
| 455 | + // If we're at a space or the text is empty, try to find a nearby word |
| 456 | + if (nodeText.length === 0 || (offset < nodeText.length && /\s/.test(nodeText[offset]))) { |
| 457 | + |
| 458 | + // Look for the nearest word |
| 459 | + let leftPos = offset - 1; |
| 460 | + let rightPos = offset; |
| 461 | + |
| 462 | + // Check to the left |
| 463 | + while (leftPos >= 0 && /\s/.test(nodeText[leftPos])) { |
| 464 | + leftPos--; |
| 465 | + } |
| 466 | + |
| 467 | + // Check to the right |
| 468 | + while (rightPos < nodeText.length && /\s/.test(nodeText[rightPos])) { |
| 469 | + rightPos++; |
| 470 | + } |
| 471 | + |
| 472 | + // If we found a non-space character to the left, extract that word |
| 473 | + if (leftPos >= 0) { |
| 474 | + let wordStart = leftPos; |
| 475 | + while (wordStart > 0 && /\w/.test(nodeText[wordStart - 1])) { |
| 476 | + wordStart--; |
| 477 | + } |
| 478 | + |
| 479 | + return extractWordAndOccurrence(nodeText, wordStart, leftPos + 1); |
| 480 | + } |
| 481 | + |
| 482 | + // If we found a non-space character to the right, extract that word |
| 483 | + if (rightPos < nodeText.length) { |
| 484 | + let wordEnd = rightPos; |
| 485 | + while (wordEnd < nodeText.length && /\w/.test(nodeText[wordEnd])) { |
| 486 | + wordEnd++; |
| 487 | + } |
| 488 | + |
| 489 | + return extractWordAndOccurrence(nodeText, rightPos, wordEnd); |
| 490 | + } |
| 491 | + |
| 492 | + return null; |
| 493 | + } |
| 494 | + |
| 495 | + // Find word boundaries |
| 496 | + let startPos = offset; |
| 497 | + let endPos = offset; |
| 498 | + |
| 499 | + // Move start position to the beginning of the word |
| 500 | + while (startPos > 0 && /\w/.test(nodeText[startPos - 1])) { |
| 501 | + startPos--; |
| 502 | + } |
| 503 | + |
| 504 | + // Move end position to the end of the word |
| 505 | + while (endPos < nodeText.length && /\w/.test(nodeText[endPos])) { |
| 506 | + endPos++; |
| 507 | + } |
| 508 | + |
| 509 | + |
| 510 | + // Extract the word and its occurrence index |
| 511 | + if (endPos > startPos) { |
| 512 | + return extractWordAndOccurrence(nodeText, startPos, endPos); |
| 513 | + } |
| 514 | + |
| 515 | + return null; |
| 516 | + } |
| 517 | + |
385 | 518 | /** |
386 | 519 | * Sends the message containing tagID which is being clicked |
387 | 520 | * to the editor in order to change the cursor position to |
388 | 521 | * the HTML tag corresponding to the clicked element. |
389 | 522 | */ |
390 | 523 | function onDocumentClick(event) { |
| 524 | + |
391 | 525 | // Get the user's current selection |
392 | 526 | const selection = window.getSelection(); |
393 | 527 |
|
|
399 | 533 | return; |
400 | 534 | } |
401 | 535 | var element = event.target; |
| 536 | + |
402 | 537 | if (element && element.hasAttribute('data-brackets-id')) { |
403 | | - MessageBroker.send({ |
| 538 | + |
| 539 | + // Get the clicked word and its information |
| 540 | + const clickedWordInfo = getClickedWord(element, event); |
| 541 | + |
| 542 | + // Prepare the message with the clicked word information |
| 543 | + const message = { |
404 | 544 | "tagId": element.getAttribute('data-brackets-id'), |
405 | 545 | "nodeID": element.id, |
406 | 546 | "nodeClassList": element.classList, |
407 | 547 | "nodeName": element.nodeName, |
408 | 548 | "allSelectors": _getAllInheritedSelectorsInOrder(element), |
409 | 549 | "contentEditable": element.contentEditable === 'true', |
410 | 550 | "clicked": true |
411 | | - }); |
| 551 | + }; |
| 552 | + |
| 553 | + // Add word information if available |
| 554 | + if (clickedWordInfo) { |
| 555 | + message.clickedWord = clickedWordInfo.word; |
| 556 | + message.wordContext = clickedWordInfo.context; |
| 557 | + message.wordOccurrenceIndex = clickedWordInfo.occurrenceIndex; |
| 558 | + } |
| 559 | + |
| 560 | + MessageBroker.send(message); |
| 561 | + } else { |
412 | 562 | } |
413 | 563 | } |
414 | 564 | window.document.addEventListener("click", onDocumentClick); |
|
0 commit comments