- Added comprehensive AI Assistant system (aiassist/ directory): * Vector search and embedding capabilities * Typebot proxy integration * Elastic search functionality * Message classification and chat history * MCP proxy for external integrations - Implemented Court Status API (GetCourtStatus.php): * Real-time court document status checking * Integration with external court systems * Comprehensive error handling and logging - Enhanced S3 integration: * Improved file backup system with metadata * Batch processing capabilities * Enhanced error logging and recovery * Copy operations with URL fixing - Added Telegram contact creation API - Improved error logging across all modules - Enhanced callback system for AI responses - Extensive backup file storage with timestamps - Updated documentation and README files - File storage improvements: * Thousands of backup files with proper metadata * Fix operations for broken file references * Project-specific backup and recovery systems * Comprehensive file integrity checking Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
332 lines
10 KiB
JavaScript
332 lines
10 KiB
JavaScript
/**
|
|
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
|
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
( function() {
|
|
|
|
CKEDITOR.plugins.add( 'textmatch', {} );
|
|
|
|
/**
|
|
* A global namespace for methods exposed by the [Text Match](https://ckeditor.com/cke4/addon/textmatch) plugin.
|
|
*
|
|
* The most important function is {@link #match} which performs a text
|
|
* search in the DOM.
|
|
*
|
|
* @singleton
|
|
* @class
|
|
* @since 4.10.0
|
|
*/
|
|
CKEDITOR.plugins.textMatch = {};
|
|
|
|
/**
|
|
* Allows to search in the DOM for matching text using a callback which operates on strings instead of text nodes.
|
|
* Returns {@link CKEDITOR.dom.range} and the matching text.
|
|
*
|
|
* ```javascript
|
|
* var range = editor.getSelection().getRanges()[ 0 ];
|
|
*
|
|
* CKEDITOR.plugins.textMatch.match( range, function( text, offset ) {
|
|
* // Let's assume that text is 'Special thanks to #jo.' and offset is 21.
|
|
* // The offset "21" means that the caret is between '#jo' and '.'.
|
|
*
|
|
* // Get the text before the caret.
|
|
* var left = text.slice( 0, offset ),
|
|
* // Will look for a literal '#' character and at least two word characters.
|
|
* match = left.match( /#\w{2,}$/ );
|
|
*
|
|
* if ( !match ) {
|
|
* return null;
|
|
* }
|
|
*
|
|
* // The matching fragment is the '#jo', which can
|
|
* // be identified by the following offsets: { start: 18, end: 21 }.
|
|
* return { start: match.index, end: offset };
|
|
* } );
|
|
* ```
|
|
*
|
|
* @member CKEDITOR.plugins.textMatch
|
|
* @param {CKEDITOR.dom.range} range A collapsed range — the position from which the scanning starts.
|
|
* Usually the caret position.
|
|
* @param {Function} testCallback A callback executed to check if the text matches.
|
|
* @param {String} testCallback.text The full text to check.
|
|
* @param {Number} testCallback.rangeOffset An offset of the `range` in the `text` to be checked.
|
|
* @param {Object} [testCallback.return] The position of the matching fragment (`null` if nothing matches).
|
|
* @param {Number} testCallback.return.start The offset of the start of the matching fragment.
|
|
* @param {Number} testCallback.return.end The offset of the end of the matching fragment.
|
|
*
|
|
* @returns {Object/null} An object with information about the matching text or `null`.
|
|
* @returns {String} return.text The matching text.
|
|
* The text does not reflect the range offsets. The range could contain additional,
|
|
* browser-related characters like {@link CKEDITOR.dom.selection#FILLING_CHAR_SEQUENCE}.
|
|
* @returns {CKEDITOR.dom.range} return.range A range in the DOM for the text that matches.
|
|
*/
|
|
CKEDITOR.plugins.textMatch.match = function( range, callback ) {
|
|
var textAndOffset = CKEDITOR.plugins.textMatch.getTextAndOffset( range ),
|
|
fillingCharSequence = CKEDITOR.dom.selection.FILLING_CHAR_SEQUENCE,
|
|
fillingSequenceOffset = 0;
|
|
|
|
if ( !textAndOffset ) {
|
|
return;
|
|
}
|
|
|
|
// Remove filling char sequence for clean query (#2038).
|
|
if ( textAndOffset.text.indexOf( fillingCharSequence ) == 0 ) {
|
|
fillingSequenceOffset = fillingCharSequence.length;
|
|
|
|
textAndOffset.text = textAndOffset.text.replace( fillingCharSequence, '' );
|
|
textAndOffset.offset -= fillingSequenceOffset;
|
|
}
|
|
|
|
var result = callback( textAndOffset.text, textAndOffset.offset );
|
|
|
|
if ( !result ) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
range: CKEDITOR.plugins.textMatch.getRangeInText( range, result.start, result.end + fillingSequenceOffset ),
|
|
text: textAndOffset.text.slice( result.start, result.end )
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Returns a text (as a string) in which the DOM range is located (the function scans for adjacent text nodes)
|
|
* and the offset of the caret in that text.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* * `{}` is the range position in the text node (it means that the text node is **not** split at that position).
|
|
* * `[]` is the range position in the element (it means that the text node is split at that position).
|
|
* * `.` is a separator for text nodes (it means that the text node is split at that position).
|
|
*
|
|
* Examples:
|
|
*
|
|
* ```
|
|
* Input: <p>he[]llo</p>
|
|
* Result: { text: 'hello', offset: 2 }
|
|
*
|
|
* Input: <p>he.llo{}</p>
|
|
* Result: { text: 'hello', offset: 5 }
|
|
*
|
|
* Input: <p>{}he.ll<i>o</i></p>
|
|
* Result: { text: 'hell', offset: 0 }
|
|
*
|
|
* Input: <p>he{}<i>ll</i>o</p>
|
|
* Result: { text: 'he', offset: 2 }
|
|
*
|
|
* Input: <p>he<i>ll</i>o.m{}y.friend</p>
|
|
* Result: { text: 'omyfriend', offset: 2 }
|
|
* ```
|
|
*
|
|
* @member CKEDITOR.plugins.textMatch
|
|
* @param {CKEDITOR.dom.range} range
|
|
* @returns {Object/null}
|
|
* @returns {String} return.text The text in which the DOM range is located.
|
|
* @returns {Number} return.offset An offset of the caret.
|
|
*/
|
|
CKEDITOR.plugins.textMatch.getTextAndOffset = function( range ) {
|
|
if ( !range.collapsed ) {
|
|
return null;
|
|
}
|
|
|
|
var text = '', offset = 0,
|
|
textNodes = CKEDITOR.plugins.textMatch.getAdjacentTextNodes( range ),
|
|
nodeReached = false,
|
|
elementIndex,
|
|
startContainerIsText = ( range.startContainer.type != CKEDITOR.NODE_ELEMENT );
|
|
|
|
if ( startContainerIsText ) {
|
|
// Determining element index in textNodes array.
|
|
elementIndex = indexOf( textNodes, function( current ) {
|
|
return range.startContainer.equals( current );
|
|
} );
|
|
} else {
|
|
// Based on range startOffset decreased by first text node index.
|
|
elementIndex = range.startOffset - ( textNodes[ 0 ] ? textNodes[ 0 ].getIndex() : 0 );
|
|
}
|
|
|
|
var max = textNodes.length;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var currentNode = textNodes[ i ];
|
|
text += currentNode.getText();
|
|
|
|
// We want to increase text offset only when startContainer is not reached.
|
|
if ( !nodeReached ) {
|
|
if ( startContainerIsText ) {
|
|
if ( i == elementIndex ) {
|
|
nodeReached = true;
|
|
offset += range.startOffset;
|
|
} else {
|
|
offset += currentNode.getText().length;
|
|
}
|
|
} else {
|
|
if ( i == elementIndex ) {
|
|
nodeReached = true;
|
|
}
|
|
|
|
// In below example there are three text nodes in p element and four possible offsets ( 0, 1, 2, 3 )
|
|
// We are going to increase offset while iteration:
|
|
// index 0 ==> 0
|
|
// index 1 ==> 3
|
|
// index 2 ==> 3 + 3
|
|
// index 3 ==> 3 + 3 + 2
|
|
|
|
// <p> foo bar ba </p>
|
|
// 0^^^1^^^2^^3
|
|
if ( i > 0 ) {
|
|
offset += textNodes[ i - 1 ].getText().length;
|
|
}
|
|
|
|
// If element index at last element we also want to increase offset.
|
|
if ( max == elementIndex && i + 1 == max ) {
|
|
offset += currentNode.getText().length;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
text: text,
|
|
offset: offset
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Transforms the `start` and `end` offsets in the text generated by the {@link #getTextAndOffset}
|
|
* method into a DOM range.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* * `{}` is the range position in the text node (it means that the text node is **not** split at that position).
|
|
* * `.` is a separator for text nodes (it means that the text node is split at that position).
|
|
*
|
|
* Examples:
|
|
*
|
|
* ```
|
|
* Input: <p>f{}oo.bar</p>, 0, 3
|
|
* Result: <p>{foo}.bar</p>
|
|
*
|
|
* Input: <p>f{}oo.bar</p>, 1, 5
|
|
* Result: <p>f{oo.ba}r</p>
|
|
* ```
|
|
*
|
|
* @member CKEDITOR.plugins.textMatch
|
|
* @param {CKEDITOR.dom.range} range
|
|
* @param {Number} start A start offset.
|
|
* @param {Number} end An end offset.
|
|
* @returns {CKEDITOR.dom.range} Transformed range.
|
|
*/
|
|
CKEDITOR.plugins.textMatch.getRangeInText = function( range, start, end ) {
|
|
var resultRange = new CKEDITOR.dom.range( range.root ),
|
|
elements = CKEDITOR.plugins.textMatch.getAdjacentTextNodes( range ),
|
|
startData = findElementAtOffset( elements, start ),
|
|
endData = findElementAtOffset( elements, end );
|
|
|
|
resultRange.setStart( startData.element, startData.offset );
|
|
resultRange.setEnd( endData.element, endData.offset );
|
|
|
|
return resultRange;
|
|
};
|
|
|
|
/**
|
|
* Creates a collection of adjacent text nodes which are between DOM elements, starting from the given range.
|
|
* This function works only for collapsed ranges.
|
|
*
|
|
* ## Examples
|
|
*
|
|
* * `{}` is the range position in the text node (it means that the text node is **not** split at that position).
|
|
* * `.` is a separator for text nodes (it means that the text node is split at that position).
|
|
*
|
|
* Examples:
|
|
*
|
|
* ```
|
|
* Input: <p>he.llo{}</p>
|
|
* Result: [ 'he', 'llo' ]
|
|
*
|
|
* Input: <p>{}he.ll<i>o</i></p>
|
|
* Result: [ 'he', 'll' ]
|
|
*
|
|
* Input: <p>he{}<i>ll</i>o.</p>
|
|
* Result: [ 'he' ]
|
|
*
|
|
* Input: <p>he<i>ll</i>{}o.my.friend</p>
|
|
* Result: [ 'o', 'my', 'friend' ]
|
|
* ```
|
|
*
|
|
* @member CKEDITOR.plugins.textMatch
|
|
* @param {CKEDITOR.dom.range} range
|
|
* @return {CKEDITOR.dom.text[]} An array of text nodes.
|
|
*/
|
|
CKEDITOR.plugins.textMatch.getAdjacentTextNodes = function( range ) {
|
|
if ( !range.collapsed ) {
|
|
throw new Error( 'Range must be collapsed.' ); // %REMOVE_LINE%
|
|
// Reachable in prod mode.
|
|
return []; // jshint ignore:line
|
|
}
|
|
|
|
var collection = [],
|
|
siblings,
|
|
elementIndex,
|
|
node, i;
|
|
|
|
if ( range.startContainer.type != CKEDITOR.NODE_ELEMENT ) {
|
|
siblings = range.startContainer.getParent().getChildren();
|
|
elementIndex = range.startContainer.getIndex();
|
|
} else {
|
|
siblings = range.startContainer.getChildren();
|
|
elementIndex = range.startOffset;
|
|
}
|
|
|
|
i = elementIndex;
|
|
while ( node = siblings.getItem( --i ) ) {
|
|
if ( node.type == CKEDITOR.NODE_TEXT ) {
|
|
collection.unshift( node );
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
i = elementIndex;
|
|
while ( node = siblings.getItem( i++ ) ) {
|
|
if ( node.type == CKEDITOR.NODE_TEXT ) {
|
|
collection.push( node );
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return collection;
|
|
};
|
|
|
|
function findElementAtOffset( elements, offset ) {
|
|
var max = elements.length,
|
|
currentOffset = 0;
|
|
for ( var i = 0; i < max; i += 1 ) {
|
|
var current = elements[ i ];
|
|
if ( offset >= currentOffset && currentOffset + current.getText().length >= offset ) {
|
|
return {
|
|
element: current,
|
|
offset: offset - currentOffset
|
|
};
|
|
}
|
|
|
|
currentOffset += current.getText().length;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function indexOf( arr, checker ) {
|
|
for ( var i = 0; i < arr.length; i++ ) {
|
|
if ( checker( arr[ i ] ) ) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
} )();
|