Ticket #158: 158_2.patch

File 158_2.patch, 19.0 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/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                findCommand.readOnly = 1;
    1926
    2027                editor.ui.addButton( 'Replace',
     
    2330                                command : 'replace'
    2431                        });
    2532                var replaceCommand = editor.addCommand( 'replace', new CKEDITOR.dialogCommand( 'replace' ) );
    26                 replaceCommand.canUndo = false;
     33                replaceCommand = CKEDITOR.tools.extend( replaceCommand, meta, true );
    2734
    2835                CKEDITOR.dialog.add( 'find',    this.path + 'dialogs/find.js' );
    2936                CKEDITOR.dialog.add( 'replace', this.path + 'dialogs/find.js' );
     37
     38                // Save text selection when editor loose focus in source mode.
     39                CKEDITOR.env.ie && editor.on( 'mode', function()
     40                {
     41                        if ( editor.mode == 'source' )
     42                        {
     43                                var target = editor.textarea;
     44                                target.on( 'beforedeactivate', function()
     45                                        {
     46                                                var range = editor.getTextSelection();
     47                                                target.setCustomData( 'saved-range', range );
     48                                        });
     49
     50                                target.on( 'focus', function()
     51                                        {
     52                                                var savedTxtRange = target.removeCustomData( 'saved-range' );
     53                                                savedTxtRange && savedTxtRange.select();;
     54                                        })
     55                        }
     56                });
     57
    3058        },
    3159
    3260        requires : [ 'styles' ]
    3361} );
    3462
    35 /**
     63// Text selection tools required when working in source mode.
     64( function()
     65{
     66        /**
     67         * Represent the text range of a text input/text area element.
     68         * @constructor
     69         * @param {CKEDITOR.dom.element} element
     70         * @param {Number} start
     71         * @param {Number} end
     72         */
     73        CKEDITOR.dom.textRange = function( element, start, end )
     74        {
     75                if( element instanceof CKEDITOR.dom.element
     76                        && ( element.is( 'textarea' )
     77                        || element.is( 'input' ) && element.getAttribute( 'type' ) == 'text' ) )
     78                {
     79                        this.element = element;
     80                        this.startOffset = start || 0;
     81                        this.endOffset = end || 0;
     82                }
     83        };
     84
     85        CKEDITOR.dom.textRange.prototype =
     86        {
     87                /**
     88                 * Sets the text selection of the specified textfield/textarea.
     89                 * @param {HTMLTextArea|HTMLTextInput} element
     90                 * @param {CKEDITOR.dom.textRange} range
     91                 */
     92                select : function()
     93                {
     94                        var startOffset = this.startOffset,
     95                                endOffset = this.endOffset,
     96                                element = this.element.$;
     97
     98                        element.focus();
     99
     100                        // Standard way of DOM 3.0.
     101                        if ( !CKEDITOR.env.ie || CKEDITOR.ie9Compat )
     102                        {
     103                                element.selectionStart = startOffset;
     104                                element.selectionEnd = endOffset;
     105                        }
     106                        // For old IEs.
     107                        else
     108                        {
     109                                this.element.removeCustomData( 'saved-range' );
     110
     111                                // Adjustment due to moveStart/End skips line breaks.
     112                                var normalizedValue = element.value.replace( /\r\n/g, '\n' );
     113                                startOffset -= normalizedValue.slice( 0, startOffset ).split( '\n' ).length - 1;
     114                                endOffset -= normalizedValue.slice( 0, endOffset ).split( '\n' ).length - 1;
     115
     116                                var textRange = element.createTextRange();
     117                                textRange.collapse( true );
     118                                textRange.moveStart( 'character', startOffset );
     119                                textRange.moveEnd( 'character', endOffset - startOffset );
     120                                textRange.select();
     121                        }
     122                }
     123        };
     124
     125        /**
     126         * Retrieve the current text selection range of the source editing block.
     127         * @returns {CKEDITOR.dom.textRange} Text range represent the caret positoins.
     128         * @example
     129         * var textSelection = CKEDITOR.instances.editor1.<b>getTextSelection()</b>;
     130         * alert( textSelection.startOffset );
     131         * alert( textSelection.endOffset );
     132         */
     133        CKEDITOR.editor.prototype.getTextSelection = function()
     134        {
     135                return this.textarea && getTextSelection( this.textarea.$ ) || null;
     136        };
     137
     138        /**
     139         * Cross-browser implementation for text range calculating.
     140         * @param {HTMLTextArea|HTMLTextInput} element
     141         */
     142        function getTextSelection( element )
     143        {
     144                var startOffset, endOffset;
     145
     146                // Standard way of DOM 3.0.
     147                if ( !CKEDITOR.env.ie || CKEDITOR.ie9Compat )
     148                {
     149                        startOffset = element.selectionStart;
     150                        endOffset = element.selectionEnd;
     151                }
     152                // For old IEs.
     153                else
     154                {
     155                        element.focus();
     156
     157                        var range = CKEDITOR.document.$.selection.createRange(),
     158                                textLength = range.text.length;
     159
     160                        // Create a 'measuring' range to help calculate the start offset by
     161                        // stretching it from start to current position.
     162                        var measureRange = range.duplicate();
     163                        measureRange.moveToElementText( element );
     164                        measureRange.setEndPoint( 'EndToEnd', range );
     165
     166                        endOffset = measureRange.text.length;
     167                        startOffset = endOffset - textLength;
     168                }
     169                return new CKEDITOR.dom.textRange(
     170                        new CKEDITOR.dom.element( element ), startOffset, endOffset );
     171        }
     172
     173} )();
     174
     175/**
    36176 * Defines the style to be used to highlight results with the find dialog.
    37177 * @type Object
    38178 * @default { element : 'span', styles : { 'background-color' : '#004', 'color' : '#fff' } }
  • _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
     
    859935                        },
    860936                        onHide : function()
    861937                        {
    862                                 var range;
    863                                 if ( finder.matchRange && finder.matchRange.isMatched() )
    864                                 {
    865                                         finder.matchRange.removeHighlight();
    866                                         editor.focus();
     938                                if ( editor.mode == 'wysiwyg' )
     939                                {
     940                                        var range;
     941                                        if ( finder.matchRange && finder.matchRange.isMatched() )
     942                                        {
     943                                                finder.matchRange.removeHighlight();
     944                                                editor.focus();
    867945
    868                                         range = finder.matchRange.toDomRange();
    869                                         if ( range )
    870                                                 editor.getSelection().selectRanges( [ range ] );
    871                                 }
     946                                                range = finder.matchRange.toDomRange();
     947                                                if ( range )
     948                                                        editor.getSelection().selectRanges( [ range ] );
     949                                        }
     950                                }
    872951
    873952                                // Clear current session before dialog close
    874953                                delete finder.matchRange;
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy