Ticket #158: 158.patch

File 158.patch, 18.8 KB (added by Garry Yao, 9 years ago)
  • _source/core/tools.js

     
    346346                },
    347347
    348348                /**
     349                 * Escape all regular expression preserved characters from input text.
     350                 * @param {String} text
     351                 */
     352                escapeRegExp : function( text )
     353                {
     354                        return text.replace( /[.*+?|()\[\]{}\\]/g, '\\$&' );
     355                },
     356
     357                /**
    349358                 * Gets a unique number for this CKEDITOR execution session. It returns
    350359                 * progressive numbers starting at 1.
    351360                 * @function
  • _source/plugins/selection/plugin.js

     
    457457                                        }
    458458                                });
    459459
     460                        // Save text selection when editor loose focus in source mode.
     461                        CKEDITOR.env.ie && editor.on( 'mode', function()
     462                        {
     463                                if ( editor.mode == 'source' )
     464                                {
     465                                        var target = editor.textarea;
     466                                        target.on( 'beforedeactivate', function()
     467                                                {
     468                                                        target.data( 'cke-saved-txtrange', editor.getTextSelection() );
     469                                                });
     470
     471                                        target.on( 'focus', function()
     472                                                {
     473                                                        var savedTxtRange = target.data( 'cke-saved-txtrange', false );
     474                                                        savedTxtRange && savedTxtRange.select();
     475                                                })
     476                                }
     477                        });
     478
    460479                        // Clear the cached range path before unload. (#7174)
    461480                        editor.on( 'contentDomUnload', editor.forceNextSelectionCheck, editor );
    462481
     
    14281447                        start.scrollIntoView();
    14291448                }
    14301449        };
     1450
     1451        /**
     1452         * Represent the text range of a text input/text area element.
     1453         * @constructor
     1454         * @param {CKEDITOR.dom.element} element
     1455         * @param {Number} start
     1456         * @param {Number} end
     1457         */
     1458        CKEDITOR.dom.textRange = function( element, start, end )
     1459        {
     1460                if( element instanceof CKEDITOR.dom.element
     1461                        && ( element.is( 'textarea' )
     1462                        || element.is( 'input' ) && element.getAttribute( 'type' ) == 'text' ) )
     1463                {
     1464                        this.element = element;
     1465                        this.startOffset = start || 0;
     1466                        this.endOffset = end || 0;
     1467                }
     1468        };
     1469
     1470        CKEDITOR.dom.textRange.prototype =
     1471        {
     1472                /**
     1473                 * Sets the text selection of the specified textfield/textarea.
     1474                 * @param {HTMLTextArea|HTMLTextInput} element
     1475                 * @param {CKEDITOR.dom.textRange} range
     1476                 */
     1477                select : function()
     1478                {
     1479                        var startOffset = this.startOffset,
     1480                                endOffset = this.endOffset,
     1481                                element = this.element.$;
     1482
     1483                        element.focus();
     1484
     1485                        if ( !CKEDITOR.env.ie || CKEDITOR.ie9Compat )
     1486                        {
     1487                                element.selectionStart = startOffset;
     1488                                element.selectionEnd = endOffset;
     1489                        }
     1490                        else
     1491                        {
     1492                                this.element.data( 'cke-saved-txtrange', false );
     1493                                // Create empty selection this
     1494                                var textRange = element.createTextRange();
     1495                                textRange.collapse();
     1496                                textRange.moveStart( 'character', startOffset );
     1497                                textRange.moveEnd( 'character', endOffset - startOffset );
     1498                                textRange.select();
     1499                        }
     1500                }
     1501        };
     1502
     1503        /**
     1504         * Retrieve the current text selection range of the source editing block.
     1505         * @returns {CKEDITOR.dom.textRange} Text range represent the caret positoins.
     1506         * @example
     1507         * var textSelection = CKEDITOR.instances.editor1.<b>getTextSelection()</b>;
     1508         * alert( textSelection.startOffset );
     1509         * alert( textSelection.endOffset );
     1510         */
     1511        CKEDITOR.editor.prototype.getTextSelection = function()
     1512        {
     1513                return this.textarea && getTextSelection( this.textarea.$ ) || null;
     1514        };
     1515
     1516        /**
     1517         * Cross-browser implementation for text range calculating.
     1518         * @param {HTMLTextArea|HTMLTextInput} element
     1519         */
     1520        function getTextSelection( element )
     1521        {
     1522                var startOffset, endOffset;
     1523
     1524                if ( !CKEDITOR.env.ie || CKEDITOR.ie9Compat )
     1525                {
     1526                        startOffset = element.selectionStart;
     1527                        endOffset = element.selectionEnd;
     1528                }
     1529                else
     1530                {
     1531                        element.focus();
     1532
     1533                        // The current selection
     1534                        var range = CKEDITOR.document.$.selection.createRange(),
     1535                                textLength = range.text.length;
     1536
     1537                        // Create a 'measuring' range to help calculate the start offset by
     1538                        // stretching it from start to current position.
     1539                        var measureRange = range.duplicate();
     1540                        measureRange.moveToElementText( element );
     1541                        measureRange.setEndPoint( 'EndToEnd', range );
     1542
     1543                        endOffset = measureRange.text.length;
     1544                        startOffset = endOffset - textLength;
     1545                }
     1546                return new CKEDITOR.dom.textRange(
     1547                        new CKEDITOR.dom.element( element ), startOffset, endOffset );
     1548        }
     1549
     1550
    14311551})();
    14321552
    14331553( function()
  • _source/plugins/find/plugin.js

     
    1313                                label : editor.lang.findAndReplace.find,
    1414                                command : 'find'
    1515                        });
     16
     17                var meta =
     18                {
     19                        canUndo : false,
     20                        modes : { 'wysiwyg': 1, 'source': 1 }
     21                };
     22
    1623                var findCommand = editor.addCommand( 'find', new CKEDITOR.dialogCommand( 'find' ) );
    17                 findCommand.canUndo = false;
     24                findCommand = CKEDITOR.tools.extend( findCommand, meta, true );
    1825
    1926                editor.ui.addButton( 'Replace',
    2027                        {
     
    2229                                command : 'replace'
    2330                        });
    2431                var replaceCommand = editor.addCommand( 'replace', new CKEDITOR.dialogCommand( 'replace' ) );
    25                 replaceCommand.canUndo = false;
     32                replaceCommand = CKEDITOR.tools.extend( replaceCommand, meta, true );
    2633
    2734                CKEDITOR.dialog.add( 'find',    this.path + 'dialogs/find.js' );
    2835                CKEDITOR.dialog.add( 'replace', this.path + 'dialogs/find.js' );
  • _source/plugins/find/dialogs/find.js

     
    454454                                || wordSeparatorRegex.test( c );
    455455                };
    456456
    457                 var finder = {
    458                         searchRange : null,
    459                         matchRange : null,
    460                         find : function( pattern, matchCase, matchWord, matchCyclic, highlightMatched, cyclicRerun )
    461                         {
    462                                 if ( !this.matchRange )
    463                                         this.matchRange =
    464                                                 new characterRange(
    465                                                         new characterWalker( this.searchRange ),
    466                                                         pattern.length );
    467                                 else
    468                                 {
    469                                         this.matchRange.removeHighlight();
    470                                         this.matchRange = this.matchRange.getNextCharacterRange( pattern.length );
    471                                 }
     457                var finder;
    472458
    473                                 var matcher = new kmpMatcher( pattern, !matchCase ),
    474                                         matchState = KMP_NOMATCH,
    475                                         character = '%';
    476 
    477                                 while ( character !== null )
    478                                 {
    479                                         this.matchRange.moveNext();
    480                                         while ( ( character = this.matchRange.getEndCharacter() ) )
    481                                         {
    482                                                 matchState = matcher.feedCharacter( character );
    483                                                 if ( matchState == KMP_MATCHED )
    484                                                         break;
    485                                                 if ( this.matchRange.moveNext().hitMatchBoundary )
    486                                                         matcher.reset();
    487                                         }
    488 
    489                                         if ( matchState == KMP_MATCHED )
    490                                         {
    491                                                 if ( matchWord )
    492                                                 {
    493                                                         var cursors = this.matchRange.getCursors(),
    494                                                                 tail = cursors[ cursors.length - 1 ],
    495                                                                 head = cursors[ 0 ];
    496 
    497                                                         var headWalker = new characterWalker( getRangeBeforeCursor( head ), true ),
    498                                                                 tailWalker = new characterWalker( getRangeAfterCursor( tail ), true );
    499 
    500                                                         if ( ! ( isWordSeparator( headWalker.back().character )
    501                                                                                 && isWordSeparator( tailWalker.next().character ) ) )
    502                                                                 continue;
    503                                                 }
    504                                                 this.matchRange.setMatched();
    505                                                 if ( highlightMatched !== false )
    506                                                         this.matchRange.highlight();
    507                                                 return true;
    508                                         }
    509                                 }
    510 
    511                                 this.matchRange.clearMatched();
    512                                 this.matchRange.removeHighlight();
    513                                 // Clear current session and restart with the default search
    514                                 // range.
    515                                 // Re-run the finding once for cyclic.(#3517)
    516                                 if ( matchCyclic && !cyclicRerun )
    517                                 {
    518                                         this.searchRange = getSearchRange( 1 );
    519                                         this.matchRange = null;
    520                                         return arguments.callee.apply( this,
    521                                                 Array.prototype.slice.call( arguments ).concat( [ true ] ) );
    522                                 }
    523 
    524                                 return false;
    525                         },
    526 
    527                         /**
    528                          * Record how much replacement occurred toward one replacing.
    529                          */
    530                         replaceCounter : 0,
    531 
    532                         replace : function( dialog, pattern, newString, matchCase, matchWord,
    533                                 matchCyclic , isReplaceAll )
    534                         {
    535                                 isReplace = 1;
    536 
    537                                 // Successiveness of current replace/find.
    538                                 var result = 0;
    539 
    540                                 // 1. Perform the replace when there's already a match here.
    541                                 // 2. Otherwise perform the find but don't replace it immediately.
    542                                 if ( this.matchRange && this.matchRange.isMatched()
    543                                                 && !this.matchRange._.isReplaced && !this.matchRange.isReadOnly() )
    544                                 {
    545                                         // Turn off highlight for a while when saving snapshots.
    546                                         this.matchRange.removeHighlight();
    547                                         var domRange = this.matchRange.toDomRange();
    548                                         var text = editor.document.createText( newString );
    549                                         if ( !isReplaceAll )
    550                                         {
    551                                                 // Save undo snaps before and after the replacement.
    552                                                 var selection = editor.getSelection();
    553                                                 selection.selectRanges( [ domRange ] );
    554                                                 editor.fire( 'saveSnapshot' );
    555                                         }
    556                                         domRange.deleteContents();
    557                                         domRange.insertNode( text );
    558                                         if ( !isReplaceAll )
    559                                         {
    560                                                 selection.selectRanges( [ domRange ] );
    561                                                 editor.fire( 'saveSnapshot' );
    562                                         }
    563                                         this.matchRange.updateFromDomRange( domRange );
    564                                         if ( !isReplaceAll )
    565                                                 this.matchRange.highlight();
    566                                         this.matchRange._.isReplaced = true;
    567                                         this.replaceCounter++;
    568                                         result = 1;
    569                                 }
    570                                 else
    571                                         result = this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll );
    572 
    573                                 isReplace = 0;
    574 
    575                                 return result;
    576                         }
    577                 };
    578 
    579                 /**
     459                /**
    580460                 * The range in which find/replace happened, receive from user
    581461                 * selection prior.
    582462                 */
    583463                function getSearchRange( isDefault )
    584464                {
    585                         var searchRange,
    586                                 sel = editor.getSelection(),
    587                                 body = editor.document.getBody();
    588                         if ( sel && !isDefault )
    589                         {
    590                                 searchRange = sel.getRanges()[ 0 ].clone();
    591                                 searchRange.collapse( true );
    592                         }
    593                         else
    594                         {
    595                                 searchRange = new CKEDITOR.dom.range();
    596                                 searchRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START );
    597                         }
    598                         searchRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );
    599                         return searchRange;
    600                 }
     465                        if ( editor.mode == 'wysiwyg' )
     466                        {
     467                                var searchRange,
     468                                                sel = editor.getSelection(),
     469                                                body = editor.document.getBody();
     470                                if ( sel && !isDefault )
     471                                {
     472                                        searchRange = sel.getRanges()[ 0 ].clone();
     473                                        searchRange.collapse( true );
     474                                }
     475                                else
     476                                {
     477                                        searchRange = new CKEDITOR.dom.range();
     478                                        searchRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START );
     479                                }
     480                                searchRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );
     481                                return searchRange;
     482                        }
     483                        else
     484                        {
     485                                var start = editor.getTextSelection().startOffset,
     486                                        length = editor.textarea.$.value.length;
     487                                return isDefault || ( start == length ? 0 : start );
     488                        }
     489                }
    601490
    602491                var lang = editor.lang.findAndReplace;
    603492                return {
     
    689578                                                                        id : 'txtFindReplace',
    690579                                                                        label : lang.findWhat,
    691580                                                                        isChanged : false,
     581                                                                        onChange : function()
     582                                                                        {
     583                                                                                // When text to find changes, obsolete current match range.
     584                                                                                delete finder.matchRange;
     585                                                                        },
    692586                                                                        labelLayout : 'horizontal',
    693587                                                                        accessKey : 'F'
    694588                                                                },
     
    736630                                                                        onClick : function()
    737631                                                                        {
    738632                                                                                var dialog = this.getDialog();
    739                                                                                 var replaceNums;
    740633
    741634                                                                                finder.replaceCounter = 0;
    742635
     
    744637                                                                                finder.searchRange = getSearchRange( 1 );
    745638                                                                                if ( finder.matchRange )
    746639                                                                                {
    747                                                                                         finder.matchRange.removeHighlight();
    748                                                                                         finder.matchRange = null;
     640                                                                                        editor.mode == 'wysiwyg' && finder.matchRange.removeHighlight();
     641                                                                                        delete finder.matchRange;
    749642                                                                                }
    750643                                                                                editor.fire( 'saveSnapshot' );
    751644                                                                                while ( finder.replace( dialog,
     
    850743                        },
    851744                        onShow : function()
    852745                        {
     746                                finder = editor.mode == 'wysiwyg' ?
     747                                {
     748                                        searchRange : null,
     749                                        matchRange : null,
     750                                        find : function( pattern, matchCase, matchWord, matchCyclic, highlightMatched, cyclicRerun )
     751                                        {
     752                                                if ( !this.matchRange )
     753                                                        this.matchRange =
     754                                                                new characterRange(
     755                                                                        new characterWalker( this.searchRange ),
     756                                                                        pattern.length );
     757                                                else
     758                                                {
     759                                                        this.matchRange.removeHighlight();
     760                                                        this.matchRange = this.matchRange.getNextCharacterRange( pattern.length );
     761                                                }
     762
     763                                                var matcher = new kmpMatcher( pattern, !matchCase ),
     764                                                        matchState = KMP_NOMATCH,
     765                                                        character = '%';
     766
     767                                                while ( character !== null )
     768                                                {
     769                                                        this.matchRange.moveNext();
     770                                                        while ( ( character = this.matchRange.getEndCharacter() ) )
     771                                                        {
     772                                                                matchState = matcher.feedCharacter( character );
     773                                                                if ( matchState == KMP_MATCHED )
     774                                                                        break;
     775                                                                if ( this.matchRange.moveNext().hitMatchBoundary )
     776                                                                        matcher.reset();
     777                                                        }
     778
     779                                                        if ( matchState == KMP_MATCHED )
     780                                                        {
     781                                                                if ( matchWord )
     782                                                                {
     783                                                                        var cursors = this.matchRange.getCursors(),
     784                                                                                tail = cursors[ cursors.length - 1 ],
     785                                                                                head = cursors[ 0 ];
     786
     787                                                                        var headWalker = new characterWalker( getRangeBeforeCursor( head ), true ),
     788                                                                                tailWalker = new characterWalker( getRangeAfterCursor( tail ), true );
     789
     790                                                                        if ( ! ( isWordSeparator( headWalker.back().character )
     791                                                                                                && isWordSeparator( tailWalker.next().character ) ) )
     792                                                                                continue;
     793                                                                }
     794                                                                this.matchRange.setMatched();
     795                                                                if ( highlightMatched !== false )
     796                                                                        this.matchRange.highlight();
     797                                                                return true;
     798                                                        }
     799                                                }
     800
     801                                                this.matchRange.clearMatched();
     802                                                this.matchRange.removeHighlight();
     803                                                // Clear current session and restart with the default search
     804                                                // range.
     805                                                // Re-run the finding once for cyclic.(#3517)
     806                                                if ( matchCyclic && !cyclicRerun )
     807                                                {
     808                                                        this.searchRange = getSearchRange( 1 );
     809                                                        this.matchRange = null;
     810                                                        return arguments.callee.apply( this,
     811                                                                Array.prototype.slice.call( arguments ).concat( [ true ] ) );
     812                                                }
     813
     814                                                return false;
     815                                        },
     816
     817                                        /**
     818                                         * Record how much replacement occurred toward one replacing.
     819                                         */
     820                                        replaceCounter : 0,
     821
     822                                        replace : function( dialog, pattern, newString, matchCase, matchWord,
     823                                                matchCyclic , isReplaceAll )
     824                                        {
     825                                                isReplace = 1;
     826
     827                                                // Successiveness of current replace/find.
     828                                                var result = 0;
     829
     830                                                // 1. Perform the replace when there's already a match here.
     831                                                // 2. Otherwise perform the find but don't replace it immediately.
     832                                                if ( this.matchRange && this.matchRange.isMatched()
     833                                                                && !this.matchRange._.isReplaced && !this.matchRange.isReadOnly() )
     834                                                {
     835                                                        // Turn off highlight for a while when saving snapshots.
     836                                                        this.matchRange.removeHighlight();
     837                                                        var domRange = this.matchRange.toDomRange();
     838                                                        var text = editor.document.createText( newString );
     839                                                        if ( !isReplaceAll )
     840                                                        {
     841                                                                // Save undo snaps before and after the replacement.
     842                                                                var selection = editor.getSelection();
     843                                                                selection.selectRanges( [ domRange ] );
     844                                                                editor.fire( 'saveSnapshot' );
     845                                                        }
     846                                                        domRange.deleteContents();
     847                                                        domRange.insertNode( text );
     848                                                        if ( !isReplaceAll )
     849                                                        {
     850                                                                selection.selectRanges( [ domRange ] );
     851                                                                editor.fire( 'saveSnapshot' );
     852                                                        }
     853                                                        this.matchRange.updateFromDomRange( domRange );
     854                                                        if ( !isReplaceAll )
     855                                                                this.matchRange.highlight();
     856                                                        this.matchRange._.isReplaced = true;
     857                                                        this.replaceCounter++;
     858                                                        result = 1;
     859                                                }
     860                                                else
     861                                                        result = this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll );
     862
     863                                                isReplace = 0;
     864
     865                                                return result;
     866                                        }
     867                                }
     868                                :
     869                                {
     870                                        find : function( pattern, matchCase, matchWord, matchCyclic, highlightMatched, cyclicRerun )
     871                                        {
     872                                                var reg = CKEDITOR.tools.escapeRegExp( pattern );
     873                                                if ( matchWord )
     874                                                        reg = '\\b' + reg + '\\b';
     875
     876                                                reg = new RegExp( reg, 'm'+( matchCase ? '' : 'i' ) );
     877                                                var fullText = editor.textarea.$.value;
     878                                                var searchStart = this.searchRange;
     879                                                var start = fullText.substr( searchStart ).search( reg ), end;
     880                                                if ( start != -1 )
     881                                                {
     882                                                        start += searchStart;
     883                                                        end = start + pattern.length;
     884                                                        var highlightRange = new CKEDITOR.dom.textRange( editor.textarea, start, end );
     885                                                        highlightRange.select();
     886                                                        this.matchRange = [ start, end ];
     887                                                        this.searchRange = end;
     888                                                        return true;
     889                                                }
     890                                                else if ( matchCyclic && !cyclicRerun )
     891                                                {
     892                                                        this.searchRange = getSearchRange( 1 );
     893                                                        return arguments.callee.apply( this,
     894                                                                Array.prototype.slice.call( arguments ).concat( [ 1 ] ) );
     895                                                }
     896
     897                                                return false;
     898                                        },
     899
     900                                        /**
     901                                         * Record how much replacement occurred toward one replacing.
     902                                         */
     903                                        replaceCounter : 0,
     904
     905                                        replace : function( dialog, pattern, newString, matchCase, matchWord,
     906                                                matchCyclic , isReplaceAll )
     907                                        {
     908                                                if ( this.matchRange )
     909                                                {
     910                                                        var fullText = editor.textarea.$.value;
     911                                                        var newText = fullText.substr(0, this.matchRange[ 0 ] ) + newString + fullText.substr( this.matchRange[ 1 ] );
     912                                                        var start = this.matchRange[ 0 ];
     913                                                        var end = start + newString.length;
     914                                                        editor.textarea.$.value = newText;
     915                                                        this.replaceCounter++;
     916                                                        if ( !isReplaceAll )
     917                                                        {
     918                                                                var highlightRange = new CKEDITOR.dom.textRange( editor.textarea, start, end );
     919                                                                highlightRange.select();
     920                                                        }
     921                                                        delete this.matchRange;
     922                                                        return true;
     923                                                }
     924                                                else
     925                                                        return this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll );
     926                                        }
     927                                };
     928
    853929                                // Establish initial searching start position.
    854930                                finder.searchRange = getSearchRange();
    855931
     
    857933                        },
    858934                        onHide : function()
    859935                        {
    860                                 var range;
    861                                 if ( finder.matchRange && finder.matchRange.isMatched() )
    862                                 {
    863                                         finder.matchRange.removeHighlight();
    864                                         editor.focus();
     936                                if ( editor.mode == 'wysiwyg' )
     937                                {
     938                                        var range;
     939                                        if ( finder.matchRange && finder.matchRange.isMatched() )
     940                                        {
     941                                                finder.matchRange.removeHighlight();
     942                                                editor.focus();
    865943
    866                                         range = finder.matchRange.toDomRange();
    867                                         if ( range )
    868                                                 editor.getSelection().selectRanges( [ range ] );
    869                                 }
     944                                                range = finder.matchRange.toDomRange();
     945                                                if ( range )
     946                                                        editor.getSelection().selectRanges( [ range ] );
     947                                        }
     948                                }
    870949
    871950                                // Clear current session before dialog close
    872951                                delete finder.matchRange;
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy