WordPress.org

Make WordPress Core

Ticket #31441: 31441.7.patch

File 31441.7.patch, 11.2 KB (added by iseulde, 5 years ago)
  • src/wp-includes/class-wp-editor.php

     
    368368                                                'wplink',
    369369                                                'wpdialogs',
    370370                                                'wpview',
     371                                                'wptextpattern'
    371372                                        );
    372373
    373374                                        if ( ! self::$has_medialib ) {
  • src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js

     
     1( function( tinymce, setTimeout ) {
     2        tinymce.PluginManager.add( 'wptextpattern', function( editor ) {
     3                var $$ = editor.$,
     4                        patterns = [],
     5                        canUndo = false;
     6
     7                function add( regExp, callback ) {
     8                        patterns.push( {
     9                                regExp: regExp,
     10                                callback: callback
     11                        } );
     12                }
     13
     14                add( /^[*-]\s/, function() {
     15                        this.execCommand( 'InsertUnorderedList' );
     16                } );
     17
     18                add( /^1[.)]\s/, function() {
     19                        this.execCommand( 'InsertOrderedList' );
     20                } );
     21
     22                editor.on( 'selectionchange', function() {
     23                        canUndo = false;
     24                } );
     25
     26                editor.on( 'keydown', function( event ) {
     27                        if ( canUndo && event.keyCode === tinymce.util.VK.BACKSPACE ) {
     28                                editor.undoManager.undo();
     29                                event.preventDefault();
     30                        }
     31                } );
     32
     33                editor.on( 'keyup', function( event ) {
     34                        var rng, node, text, parent, child;
     35
     36                        if ( event.keyCode !== tinymce.util.VK.SPACEBAR ) {
     37                                return;
     38                        }
     39
     40                        rng = editor.selection.getRng();
     41                        node = rng.startContainer;
     42                        text = node.nodeValue;
     43
     44                        if ( node.nodeType !== 3 ) {
     45                                return;
     46                        }
     47
     48                        parent = editor.dom.getParent( node, 'p' );
     49
     50                        if ( ! parent ) {
     51                                return;
     52                        }
     53
     54                        while ( child = parent.firstChild ) {
     55                                if ( child.nodeType !== 3 ) {
     56                                        parent = child;
     57                                } else {
     58                                        break;
     59                                }
     60                        }
     61
     62                        if ( child !== node ) {
     63                                return;
     64                        }
     65
     66                        tinymce.each( patterns, function( pattern ) {
     67                                var replace = text.replace( pattern.regExp, '' );
     68
     69                                if ( text === replace ) {
     70                                        return;
     71                                }
     72
     73                                if ( rng.startOffset !== text.length - replace.length ) {
     74                                        return;
     75                                }
     76
     77                                editor.undoManager.add();
     78
     79                                editor.undoManager.transact( function() {
     80                                        editor.selection.setCursorLocation( node, 0 );
     81
     82                                        if ( replace ) {
     83                                                $$( node ).replaceWith( document.createTextNode( replace ) );
     84                                        } else  {
     85                                                $$( node.parentNode ).empty().append( '<br>' );
     86                                        }
     87
     88                                        pattern.callback.apply( editor );
     89                                } );
     90
     91                                // We need to wait for native events to be triggered.
     92                                setTimeout( function() {
     93                                        canUndo = true;
     94                                } );
     95
     96                                return false;
     97                        } );
     98                } );
     99        } );
     100} )( window.tinymce, window.setTimeout );
  • tests/qunit/editor/js/utils.js

     
    131131
    132132        // TODO: Replace this with the new event logic in 3.5
    133133        function type(chr) {
    134                 var editor = tinymce.activeEditor, keyCode, charCode, evt, startElm, rng;
     134                var editor = tinymce.activeEditor, keyCode, charCode, evt, startElm, rng, startContainer, startOffset, textNode;
     135
     136                function charCodeToKeyCode(charCode) {
     137                        var lookup = {
     138                                '0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57,'a': 65, 'b': 66, 'c': 67,
     139                                'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73, 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81,
     140                                'r': 82, 's': 83, 't': 84, 'u': 85,     'v': 86, 'w': 87, 'x': 88, 'y': 89, ' ': 32, ',': 188, '-': 189, '.': 190, '/': 191, '\\': 220,
     141                                '[': 219, ']': 221, '\'': 222, ';': 186, '=': 187, ')': 41
     142                        };
     143
     144                        return lookup[String.fromCharCode(charCode)];
     145                }
    135146
    136147                function fakeEvent(target, type, evt) {
    137148                        editor.dom.fire(target, type, evt);
     
    139150
    140151                // Numeric keyCode
    141152                if (typeof(chr) == "number") {
    142                         charCode = keyCode = chr;
     153                        charCode = chr;
     154                        keyCode = charCodeToKeyCode(charCode);
    143155                } else if (typeof(chr) == "string") {
    144156                        // String value
    145157                        if (chr == '\b') {
     
    150162                                charCode = chr.charCodeAt(0);
    151163                        } else {
    152164                                charCode = chr.charCodeAt(0);
    153                                 keyCode = charCode;
     165                                keyCode = charCodeToKeyCode(charCode);
    154166                        }
    155167                } else {
    156168                        evt = chr;
     169
     170                        if (evt.charCode) {
     171                                chr = String.fromCharCode(evt.charCode);
     172                        }
     173
     174                        if (evt.keyCode) {
     175                                keyCode = evt.keyCode;
     176                        }
    157177                }
    158178
    159179                evt = evt || {keyCode: keyCode, charCode: charCode};
     
    175195                                        rng.execCommand('Delete', false, null);
    176196                                } else {
    177197                                        rng = editor.selection.getRng();
     198                                        startContainer = rng.startContainer;
    178199
    179                                         if (rng.startContainer.nodeType == 1 && rng.collapsed) {
    180                                                 var nodes = rng.startContainer.childNodes, lastNode = nodes[nodes.length - 1];
     200                                        if (startContainer.nodeType == 1 && rng.collapsed) {
     201                                                var nodes = rng.startContainer.childNodes;
     202                                                startContainer = nodes[nodes.length - 1];
     203                                        }
    181204
    182                                                 // If caret is at <p>abc|</p> and after the abc text node then move it to the end of the text node
    183                                                 // Expand the range to include the last char <p>ab[c]</p> since IE 11 doesn't delete otherwise
    184                                                 if (rng.startOffset >= nodes.length - 1 && lastNode && lastNode.nodeType == 3 && lastNode.data.length > 0) {
    185                                                         rng.setStart(lastNode, lastNode.data.length - 1);
    186                                                         rng.setEnd(lastNode, lastNode.data.length);
    187                                                         editor.selection.setRng(rng);
    188                                                 }
     205                                        // If caret is at <p>abc|</p> and after the abc text node then move it to the end of the text node
     206                                        // Expand the range to include the last char <p>ab[c]</p> since IE 11 doesn't delete otherwise
     207                                        if ( rng.collapsed && startContainer && startContainer.nodeType == 3 && startContainer.data.length > 0) {
     208                                                rng.setStart(startContainer, startContainer.data.length - 1);
     209                                                rng.setEnd(startContainer, startContainer.data.length);
     210                                                editor.selection.setRng(rng);
    189211                                        }
    190212
    191213                                        editor.getDoc().execCommand('Delete', false, null);
     
    194216                                rng = editor.selection.getRng(true);
    195217
    196218                                if (rng.startContainer.nodeType == 3 && rng.collapsed) {
    197                                         rng.startContainer.insertData(rng.startOffset, chr);
    198                                         rng.setStart(rng.startContainer, rng.startOffset + 1);
    199                                         rng.collapse(true);
    200                                         editor.selection.setRng(rng);
     219                                        // `insertData` may alter the range.
     220                                        startContainer = rng.startContainer;
     221                                        startOffset = rng.startOffset;
     222                                        rng.startContainer.insertData( rng.startOffset, chr );
     223                                        rng.setStart( startContainer, startOffset + 1 );
    201224                                } else {
    202                                         rng.insertNode(editor.getDoc().createTextNode(chr));
     225                                        textNode = editor.getDoc().createTextNode(chr);
     226                                        rng.insertNode(textNode);
     227                                        rng.setStart(textNode, 1);
    203228                                }
     229
     230                                rng.collapse(true);
     231                                editor.selection.setRng(rng);
    204232                        }
    205233                }
    206234
  • tests/qunit/index.html

     
    3939                <script src="wp-includes/js/shortcode.js"></script>
    4040                <script src="wp-admin/js/customize-controls.js"></script>
    4141                <script src="wp-admin/js/customize-controls-utils.js"></script>
     42
     43                <!-- TinyMCE -->
     44
     45                <script src="../../src/wp-includes/js/tinymce/tinymce.js"></script>
     46
     47                <script src="editor/js/utils.js"></script>
     48
     49                <script src="wp-includes/js/tinymce/plugins/wptextpattern/plugin.js"></script>
     50
    4251        </body>
    4352</html>
  • tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js

     
     1( function( $, QUnit, tinymce, _type, setTimeout ) {
     2        var editor;
     3
     4        function type() {
     5                var args = arguments;
     6
     7                setTimeout( function() {
     8                        if ( typeof args[0] === 'string' ) {
     9                                args[0] = args[0].split( '' );
     10                        }
     11
     12                        if ( typeof args[0] === 'function' ) {
     13                                args[0]();
     14                        } else {
     15                                _type( args[0].shift() );
     16                        }
     17
     18                        if ( ! args[0].length ) {
     19                                [].shift.call( args );
     20                        }
     21
     22                        if ( args.length ) {
     23                                type.apply( null, args );
     24                        }
     25                } );
     26        }
     27
     28        QUnit.module( 'tinymce.plugins.wptextpattern', {
     29                beforeEach: function( assert ) {
     30                        var done = assert.async();
     31
     32                        $( '#qunit-fixture' ).append( '<textarea id="editor">' );
     33
     34                        tinymce.init( {
     35                                selector: '#editor',
     36                                plugins: 'wptextpattern',
     37                                init_instance_callback: function() {
     38                                        editor = arguments[0];
     39                                        editor.focus();
     40                                        editor.selection.setCursorLocation();
     41                                        setTimeout( done );
     42                                }
     43                        } );
     44                },
     45                afterEach: function() {
     46                        editor.remove();
     47                }
     48        } );
     49
     50        QUnit.test( 'Unordered list.', function( assert ) {
     51                type( '* test', function() {
     52                        assert.equal( editor.getContent(), '<ul>\n<li>test</li>\n</ul>' );
     53                }, assert.async() );
     54        } );
     55
     56        QUnit.test( 'Ordered list.', function( assert ) {
     57                type( '1. test', function() {
     58                        assert.equal( editor.getContent(), '<ol>\n<li>test</li>\n</ol>' );
     59                }, assert.async() );
     60        } );
     61
     62        QUnit.test( 'Ordered list with content.', function( assert ) {
     63                editor.setContent( '<p><strong>test</strong></p>' );
     64                editor.selection.setCursorLocation();
     65
     66                type( '* ', function() {
     67                        assert.equal( editor.getContent(), '<ul>\n<li><strong>test</strong></li>\n</ul>' );
     68                }, assert.async() );
     69        } );
     70
     71        QUnit.test( 'Only transform inside a P tag.', function( assert ) {
     72                editor.setContent( '<h1>test</h1>' );
     73                editor.selection.setCursorLocation();
     74
     75                type( '* ', function() {
     76                        assert.equal( editor.getContent(), '<h1>* test</h1>' );
     77                }, assert.async() );
     78        } );
     79
     80        QUnit.test( 'Only transform at the start of a P tag.', function( assert ) {
     81                editor.setContent( '<p>test <strong>test</strong></p>' );
     82                editor.selection.setCursorLocation( editor.$( 'strong' )[0].firstChild, 0 );
     83
     84                type( '* ', function() {
     85                        assert.equal( editor.getContent(), '<p>test <strong>* test</strong></p>' );
     86                }, assert.async() );
     87        } );
     88
     89        QUnit.test( 'Only transform when at the cursor is at the start.', function( assert ) {
     90                editor.setContent( '<p>* test</p>' );
     91                editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 6 );
     92
     93                type( ' test', function() {
     94                        assert.equal( editor.getContent(), '<p>* test test</p>' );
     95                }, assert.async() );
     96        } );
     97
     98        QUnit.test( 'Backspace should undo the transformation.', function( assert ) {
     99                editor.setContent( '<p>test</p>' );
     100                editor.selection.setCursorLocation();
     101
     102                type( '* \b', function() {
     103                        assert.equal( editor.getContent(), '<p>* test</p>' );
     104                        assert.equal( editor.selection.getRng().startOffset, 2 );
     105                }, assert.async() );
     106        } );
     107
     108        QUnit.test( 'Backspace should undo the transformation only right after it happened.', function( assert ) {
     109                editor.setContent( '<p>test</p>' );
     110                editor.selection.setCursorLocation();
     111
     112                type( '* ', function() {
     113                        editor.selection.setCursorLocation( editor.$( 'li' )[0].firstChild, 4 );
     114                        // Gecko.
     115                        editor.fire( 'click' );
     116                }, '\b', function() {
     117                        assert.equal( editor.getContent(), '<ul>\n<li>tes</li>\n</ul>' );
     118                }, assert.async() );
     119        } );
     120} )( window.jQuery, window.QUnit, window.tinymce, window.Utils.type, window.setTimeout );