diff --git a/src/wp-includes/class-wp-editor.php b/src/wp-includes/class-wp-editor.php
index 7bccc2f0f1..acd789532c 100644
--- a/src/wp-includes/class-wp-editor.php
+++ b/src/wp-includes/class-wp-editor.php
@@ -368,7 +368,8 @@ final class _WP_Editors {
'wpgallery',
'wplink',
'wpdialogs',
- 'wpview',
+ 'wptextpattern',
+ 'wpview'
);
if ( ! self::$has_medialib ) {
diff --git a/src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js b/src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js
new file mode 100644
index 0000000000..1b7df4fb4a
--- /dev/null
+++ b/src/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js
@@ -0,0 +1,100 @@
+( function( tinymce, setTimeout ) {
+ tinymce.PluginManager.add( 'wptextpattern', function( editor ) {
+ var $$ = editor.$,
+ patterns = [],
+ canUndo = false;
+
+ function add( regExp, callback ) {
+ patterns.push( {
+ regExp: regExp,
+ callback: callback
+ } );
+ }
+
+ add( /^[*-]\s/, function() {
+ this.execCommand( 'InsertUnorderedList' );
+ } );
+
+ add( /^1[.)]\s/, function() {
+ this.execCommand( 'InsertOrderedList' );
+ } );
+
+ editor.on( 'selectionchange', function() {
+ canUndo = false;
+ } );
+
+ editor.on( 'keydown', function( event ) {
+ if ( canUndo && event.keyCode === tinymce.util.VK.BACKSPACE ) {
+ editor.undoManager.undo();
+ event.preventDefault();
+ }
+ } );
+
+ editor.on( 'keyup', function( event ) {
+ var rng, node, text, parent, child;
+
+ if ( event.keyCode !== tinymce.util.VK.SPACEBAR ) {
+ return;
+ }
+
+ rng = editor.selection.getRng();
+ node = rng.startContainer;
+ text = node.nodeValue;
+
+ if ( node.nodeType !== 3 ) {
+ return;
+ }
+
+ parent = editor.dom.getParent( node, 'p' );
+
+ if ( ! parent ) {
+ return;
+ }
+
+ while ( child = parent.firstChild ) {
+ if ( child.nodeType !== 3 ) {
+ parent = child;
+ } else {
+ break;
+ }
+ }
+
+ if ( child !== node ) {
+ return;
+ }
+
+ tinymce.each( patterns, function( pattern ) {
+ var replace = text.replace( pattern.regExp, '' );
+
+ if ( text === replace ) {
+ return;
+ }
+
+ if ( rng.startOffset !== text.length - replace.length ) {
+ return;
+ }
+
+ editor.undoManager.add();
+
+ editor.undoManager.transact( function() {
+ if ( replace ) {
+ $$( node ).replaceWith( document.createTextNode( replace ) );
+ } else {
+ $$( node.parentNode ).empty().append( '
' );
+ }
+
+ editor.selection.setCursorLocation( parent );
+
+ pattern.callback.apply( editor );
+ } );
+
+ // We need to wait for native events to be triggered.
+ setTimeout( function() {
+ canUndo = true;
+ } );
+
+ return false;
+ } );
+ } );
+ } );
+} )( window.tinymce, window.setTimeout );
diff --git a/tests/qunit/editor/js/utils.js b/tests/qunit/editor/js/utils.js
index 17e2644436..570dafe9ac 100644
--- a/tests/qunit/editor/js/utils.js
+++ b/tests/qunit/editor/js/utils.js
@@ -131,7 +131,18 @@
// TODO: Replace this with the new event logic in 3.5
function type(chr) {
- var editor = tinymce.activeEditor, keyCode, charCode, evt, startElm, rng;
+ var editor = tinymce.activeEditor, keyCode, charCode, evt, startElm, rng, startContainer, startOffset, textNode;
+
+ function charCodeToKeyCode(charCode) {
+ var lookup = {
+ '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,
+ '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,
+ 'r': 82, 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, ' ': 32, ',': 188, '-': 189, '.': 190, '/': 191, '\\': 220,
+ '[': 219, ']': 221, '\'': 222, ';': 186, '=': 187, ')': 41
+ };
+
+ return lookup[String.fromCharCode(charCode)];
+ }
function fakeEvent(target, type, evt) {
editor.dom.fire(target, type, evt);
@@ -139,7 +150,8 @@
// Numeric keyCode
if (typeof(chr) == "number") {
- charCode = keyCode = chr;
+ charCode = chr;
+ keyCode = charCodeToKeyCode(charCode);
} else if (typeof(chr) == "string") {
// String value
if (chr == '\b') {
@@ -150,10 +162,18 @@
charCode = chr.charCodeAt(0);
} else {
charCode = chr.charCodeAt(0);
- keyCode = charCode;
+ keyCode = charCodeToKeyCode(charCode);
}
} else {
evt = chr;
+
+ if (evt.charCode) {
+ chr = String.fromCharCode(evt.charCode);
+ }
+
+ if (evt.keyCode) {
+ keyCode = evt.keyCode;
+ }
}
evt = evt || {keyCode: keyCode, charCode: charCode};
@@ -175,17 +195,19 @@
rng.execCommand('Delete', false, null);
} else {
rng = editor.selection.getRng();
+ startContainer = rng.startContainer;
- if (rng.startContainer.nodeType == 1 && rng.collapsed) {
- var nodes = rng.startContainer.childNodes, lastNode = nodes[nodes.length - 1];
+ if (startContainer.nodeType == 1 && rng.collapsed) {
+ var nodes = rng.startContainer.childNodes;
+ startContainer = nodes[nodes.length - 1];
+ }
- // If caret is at
abc|
and after the abc text node then move it to the end of the text node - // Expand the range to include the last charab[c]
since IE 11 doesn't delete otherwise - if (rng.startOffset >= nodes.length - 1 && lastNode && lastNode.nodeType == 3 && lastNode.data.length > 0) { - rng.setStart(lastNode, lastNode.data.length - 1); - rng.setEnd(lastNode, lastNode.data.length); - editor.selection.setRng(rng); - } + // If caret is atabc|
and after the abc text node then move it to the end of the text node + // Expand the range to include the last charab[c]
since IE 11 doesn't delete otherwise + if ( rng.collapsed && startContainer && startContainer.nodeType == 3 && startContainer.data.length > 0) { + rng.setStart(startContainer, startContainer.data.length - 1); + rng.setEnd(startContainer, startContainer.data.length); + editor.selection.setRng(rng); } editor.getDoc().execCommand('Delete', false, null); @@ -194,13 +216,19 @@ rng = editor.selection.getRng(true); if (rng.startContainer.nodeType == 3 && rng.collapsed) { - rng.startContainer.insertData(rng.startOffset, chr); - rng.setStart(rng.startContainer, rng.startOffset + 1); - rng.collapse(true); - editor.selection.setRng(rng); + // `insertData` may alter the range. + startContainer = rng.startContainer; + startOffset = rng.startOffset; + rng.startContainer.insertData( rng.startOffset, chr ); + rng.setStart( startContainer, startOffset + 1 ); } else { - rng.insertNode(editor.getDoc().createTextNode(chr)); + textNode = editor.getDoc().createTextNode(chr); + rng.insertNode(textNode); + rng.setStart(textNode, 1); } + + rng.collapse(true); + editor.selection.setRng(rng); } } diff --git a/tests/qunit/index.html b/tests/qunit/index.html index 2adf20bbb2..6f11295bb3 100644 --- a/tests/qunit/index.html +++ b/tests/qunit/index.html @@ -108,5 +108,11 @@ <# } #> + + + + + +