diff --git a/src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js b/src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js index e6f5b66cb4..f246918acb 100644 --- a/src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js +++ b/src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js @@ -15,15 +15,27 @@ return; } + /** + * Escapes characters for use in a Regular Expression. + * + * @param {String} string Characters to escape + * + * @return {String} Escaped characters + */ + function escapeRegExp( string ) { + return string.replace( /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&' ); + } + tinymce.PluginManager.add( 'wptextpattern', function( editor ) { var VK = tinymce.util.VK; + var settings = editor.settings.wptextpattern || {}; - var spacePatterns = [ + var spacePatterns = settings.space || [ { regExp: /^[*-]\s/, cmd: 'InsertUnorderedList' }, { regExp: /^1[.)]\s/, cmd: 'InsertOrderedList' } ]; - var enterPatterns = [ + var enterPatterns = settings.enter || [ { start: '##', format: 'h2' }, { start: '###', format: 'h3' }, { start: '####', format: 'h4' }, @@ -33,7 +45,7 @@ { regExp: /^(-){3,}$/, element: 'hr' } ]; - var inlinePatterns = [ + var inlinePatterns = settings.inline || [ { start: '`', end: '`', format: 'code' } ]; @@ -81,40 +93,39 @@ var format; var zero; + // We need a non empty text node with an offset greater than zero. if ( ! node || node.nodeType !== 3 || ! node.data.length || ! offset ) { return; } + // The ending character should exist in the patterns registered. if ( tinymce.inArray( chars, node.data.charAt( offset - 1 ) ) === -1 ) { return; } - function findStart( node ) { - var i = inlinePatterns.length; - var offset; + var string = node.data.slice( 0, offset ); - while ( i-- ) { - pattern = inlinePatterns[ i ]; - offset = node.data.indexOf( pattern.end ); + tinymce.each( inlinePatterns, function( p ) { + var regExp = new RegExp( escapeRegExp( p.start ) + '\\S+' + escapeRegExp( p.end ) + '$' ); + var match = string.match( regExp ); - if ( offset !== -1 ) { - return offset; - } + if ( ! match ) { + return; } - } - startOffset = findStart( node ); - endOffset = node.data.lastIndexOf( pattern.end ); + // 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 ) { + return; + } - if ( startOffset === endOffset || endOffset === -1 ) { - return; - } + startOffset = match.index; + endOffset = offset - p.end.length; + pattern = p; - if ( endOffset - startOffset <= pattern.start.length ) { - return; - } + return false; + } ); - if ( node.data.slice( startOffset + pattern.start.length, endOffset ).indexOf( pattern.start.slice( 0, 1 ) ) !== -1 ) { + if ( ! pattern ) { return; } diff --git a/tests/qunit/index.html b/tests/qunit/index.html index 244d41ec6f..9a17ec23b8 100644 --- a/tests/qunit/index.html +++ b/tests/qunit/index.html @@ -491,6 +491,7 @@ + diff --git a/tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js b/tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js index 73ba48b00d..7eaf86b8e8 100644 --- a/tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js +++ b/tests/qunit/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js @@ -155,6 +155,12 @@ selector: '#editor', skin: false, plugins: 'wptextpattern', + wptextpattern: { + inline: [ + { start: '`', end: '`', format: 'code' }, + { start: '``', end: '``', format: 'bold' } + ] + }, init_instance_callback: function() { editor = arguments[0]; editor.focus(); @@ -299,19 +305,26 @@ }, assert.async() ); } ); - QUnit.test( 'Inline: single.', function( assert ) { + QUnit.test( 'Inline: single character.', function( assert ) { type( '`test`', function() { assert.equal( editor.getContent(), '

test

' ); assert.equal( editor.selection.getRng().startOffset, 1 ); }, assert.async() ); } ); + QUnit.test( 'Inline: two characters.', function( assert ) { + type( '``test``', function() { + assert.equal( editor.getContent(), '

test

' ); + assert.equal( editor.selection.getRng().startOffset, 1 ); + }, assert.async() ); + } ); + QUnit.test( 'Inline: after typing.', function( assert ) { editor.setContent( '

test test test

' ); editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 5 ); type( '`', function() { - editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 11 ); + editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 10 ); }, '`', function() { assert.equal( editor.getContent(), '

test test test

' ); assert.equal( editor.selection.getRng().startOffset, 1 ); @@ -323,4 +336,13 @@ assert.equal( editor.getContent(), '

test `````

' ); }, assert.async() ); } ); + + QUnit.test( 'Convert with previously unconverted pattern', function( assert ) { + editor.setContent( '

`test` test 

' ); + editor.selection.setCursorLocation( editor.$( 'p' )[0].firstChild, 12 ); + + type( '`test`', function() { + assert.equal( editor.getContent(), '

`test` test test

' ); + }, assert.async() ); + } ); } )( window.jQuery, window.QUnit, window.tinymce, window.setTimeout );