TinyMCE: wptextpattern: Fix inline patterns.
* Allow spaces inside inline pattern text, unless the delimiter stands alone. * Add more unit tests. * Add more inline docs. Part props azaozz. Fixes #37693. git-svn-id: https://develop.svn.wordpress.org/trunk@39150 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
7f069a69dc
commit
c78f002ae6
|
@ -3,12 +3,46 @@
|
|||
*
|
||||
* @since 4.3.0
|
||||
*
|
||||
* This plugin can automatically format text patterns as you type. It includes two patterns:
|
||||
* This plugin can automatically format text patterns as you type. It includes several groups of patterns.
|
||||
*
|
||||
* Start of line patterns:
|
||||
* As-you-type:
|
||||
* - Unordered list (`* ` and `- `).
|
||||
* - Ordered list (`1. ` and `1) `).
|
||||
*
|
||||
* On enter:
|
||||
* - h2 (## ).
|
||||
* - h3 (### ).
|
||||
* - h4 (#### ).
|
||||
* - h5 (##### ).
|
||||
* - h6 (###### ).
|
||||
* - blockquote (> ).
|
||||
* - hr (---).
|
||||
*
|
||||
* Inline patterns:
|
||||
* - <code> (`) (backtick).
|
||||
*
|
||||
* If the transformation in unwanted, the user can undo the change by pressing backspace,
|
||||
* using the undo shortcut, or the undo button in the toolbar.
|
||||
*
|
||||
* Setting for the patterns can be overridden by plugins by using the `tiny_mce_before_init` PHP filter.
|
||||
* The setting name is `wptextpattern` and the value is an object containing override arrays for each
|
||||
* patterns group. There are three groups: "space", "enter", and "inline". Example (PHP):
|
||||
*
|
||||
* add_filter( 'tiny_mce_before_init', 'my_mce_init_wptextpattern' );
|
||||
* function my_mce_init_wptextpattern( $init ) {
|
||||
* $init['wptextpattern'] = wp_json_encode( array(
|
||||
* 'inline' => array(
|
||||
* array( 'delimiter' => '**', 'format' => 'bold' ),
|
||||
* array( 'delimiter' => '__', 'format' => 'italic' ),
|
||||
* ),
|
||||
* ) );
|
||||
*
|
||||
* return $init;
|
||||
* }
|
||||
*
|
||||
* Note that setting this will override the default text patterns. You will need to include them
|
||||
* in your settings array if you want to keep them working.
|
||||
*/
|
||||
( function( tinymce, setTimeout ) {
|
||||
if ( tinymce.Env.ie && tinymce.Env.ie < 9 ) {
|
||||
|
@ -46,19 +80,10 @@
|
|||
];
|
||||
|
||||
var inlinePatterns = settings.inline || [
|
||||
{ start: '`', end: '`', format: 'code' }
|
||||
{ delimiter: '`', format: 'code' }
|
||||
];
|
||||
|
||||
var canUndo;
|
||||
var chars = [];
|
||||
|
||||
tinymce.each( inlinePatterns, function( pattern ) {
|
||||
tinymce.each( ( pattern.start + pattern.end ).split( '' ), function( c ) {
|
||||
if ( tinymce.inArray( chars, c ) === -1 ) {
|
||||
chars.push( c );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
editor.on( 'selectionchange', function() {
|
||||
canUndo = null;
|
||||
|
@ -100,28 +125,44 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// The ending character should exist in the patterns registered.
|
||||
if ( tinymce.inArray( chars, node.data.charAt( offset - 1 ) ) === -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var string = node.data.slice( 0, offset );
|
||||
var lastChar = node.data.charAt( offset - 1 );
|
||||
|
||||
tinymce.each( inlinePatterns, function( p ) {
|
||||
var regExp = new RegExp( escapeRegExp( p.start ) + '\\S+' + escapeRegExp( p.end ) + '$' );
|
||||
// Character before selection should be delimiter.
|
||||
if ( lastChar !== p.delimiter.slice( -1 ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var escDelimiter = escapeRegExp( p.delimiter );
|
||||
var delimiterFirstChar = p.delimiter.charAt( 0 );
|
||||
var regExp = new RegExp( '(.*)' + escDelimiter + '.+' + escDelimiter + '$' );
|
||||
var match = string.match( regExp );
|
||||
|
||||
if ( ! match ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't allow pattern characters in the text.
|
||||
if ( node.data.slice( match.index + p.start.length, offset - p.end.length ).indexOf( p.start.slice( 0, 1 ) ) !== -1 ) {
|
||||
startOffset = match[1].length;
|
||||
endOffset = offset - p.delimiter.length;
|
||||
|
||||
var before = string.charAt( startOffset - 1 );
|
||||
var after = string.charAt( startOffset + p.delimiter.length );
|
||||
|
||||
// test*test* => format applied
|
||||
// test *test* => applied
|
||||
// test* test* => not applied
|
||||
if ( startOffset && /\S/.test( before ) ) {
|
||||
if ( /\s/.test( after ) || before === delimiterFirstChar ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not replace when only whitespace and delimiter characters.
|
||||
if ( ( new RegExp( '^[\\s' + escapeRegExp( delimiterFirstChar ) + ']+$' ) ).test( string.slice( startOffset, endOffset ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
startOffset = match.index;
|
||||
endOffset = offset - p.end.length;
|
||||
pattern = p;
|
||||
|
||||
return false;
|
||||
|
@ -142,8 +183,8 @@
|
|||
node = node.splitText( startOffset );
|
||||
zero = node.splitText( offset - startOffset );
|
||||
|
||||
node.deleteData( 0, pattern.start.length );
|
||||
node.deleteData( node.data.length - pattern.end.length, pattern.end.length );
|
||||
node.deleteData( 0, pattern.delimiter.length );
|
||||
node.deleteData( node.data.length - pattern.delimiter.length, pattern.delimiter.length );
|
||||
|
||||
editor.formatter.apply( pattern.format, {}, node );
|
||||
|
||||
|
|
|
@ -157,8 +157,9 @@
|
|||
plugins: 'wptextpattern',
|
||||
wptextpattern: {
|
||||
inline: [
|
||||
{ start: '`', end: '`', format: 'code' },
|
||||
{ start: '``', end: '``', format: 'bold' }
|
||||
{ delimiter: '`', format: 'code' },
|
||||
{ delimiter: '``', format: 'bold' },
|
||||
{ delimiter: '```', format: 'italic' }
|
||||
]
|
||||
},
|
||||
init_instance_callback: function() {
|
||||
|
@ -319,6 +320,34 @@
|
|||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: allow spaces within text.', function( assert ) {
|
||||
type( '`a a`', function() {
|
||||
assert.equal( editor.getContent(), '<p><code>a a</code></p>' );
|
||||
assert.equal( editor.selection.getRng().startOffset, 1 );
|
||||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: disallow \\S-delimiter-\\s.', function( assert ) {
|
||||
type( 'a` a`', function() {
|
||||
assert.equal( editor.getContent(), '<p>a` a`</p>' );
|
||||
assert.equal( editor.selection.getRng().startOffset, 5 );
|
||||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: allow \\s-delimiter-\\s.', function( assert ) {
|
||||
type( 'a ` a`', function() {
|
||||
assert.equal( editor.getContent(), '<p>a <code> a</code></p>' );
|
||||
assert.equal( editor.selection.getRng().startOffset, 1 );
|
||||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: allow \\S-delimiter-\\S.', function( assert ) {
|
||||
type( 'a`a`', function() {
|
||||
assert.equal( editor.getContent(), '<p>a<code>a</code></p>' );
|
||||
assert.equal( editor.selection.getRng().startOffset, 1 );
|
||||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: after typing.', function( assert ) {
|
||||
editor.setContent( '<p>test test test</p>' );
|
||||
editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 5 );
|
||||
|
@ -331,13 +360,13 @@
|
|||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: no change.', function( assert ) {
|
||||
type( 'test `````', function() {
|
||||
assert.equal( editor.getContent(), '<p>test `````</p>' );
|
||||
QUnit.test( 'Inline: no change without content.', function( assert ) {
|
||||
type( 'test `` ``` ````', function() {
|
||||
assert.equal( editor.getContent(), '<p>test `` ``` ````</p>' );
|
||||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Convert with previously unconverted pattern', function( assert ) {
|
||||
QUnit.test( 'Inline: convert with previously unconverted pattern.', function( assert ) {
|
||||
editor.setContent( '<p>`test` test </p>' );
|
||||
editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 12 );
|
||||
|
||||
|
@ -345,4 +374,22 @@
|
|||
assert.equal( editor.getContent(), '<p>`test` test <code>test</code></p>' );
|
||||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: convert with previous pattern characters.', function( assert ) {
|
||||
editor.setContent( '<p>test``` 123</p>' );
|
||||
editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 11 );
|
||||
|
||||
type( '``456``', function() {
|
||||
assert.equal( editor.getContent(), '<p>test``` 123<strong>456</strong></p>' );
|
||||
}, assert.async() );
|
||||
} );
|
||||
|
||||
QUnit.test( 'Inline: disallow after previous pattern characters and leading space.', function( assert ) {
|
||||
editor.setContent( '<p>test``` 123</p>' );
|
||||
editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 11 );
|
||||
|
||||
type( '``` 456```', function() {
|
||||
assert.equal( editor.getContent(), '<p>test``` 123``` 456```</p>' );
|
||||
}, assert.async() );
|
||||
} );
|
||||
} )( window.jQuery, window.QUnit, window.tinymce, window.setTimeout );
|
||||
|
|
Loading…
Reference in New Issue