]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/tinymce/plugins/wptextpattern/plugin.js
WordPress 4.5.1-scripts
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / plugins / wptextpattern / plugin.js
1 /**
2  * Text pattern plugin for TinyMCE
3  *
4  * @since 4.3.0
5  *
6  * This plugin can automatically format text patterns as you type. It includes two patterns:
7  *  - Unordered list (`* ` and `- `).
8  *  - Ordered list (`1. ` and `1) `).
9  *
10  * If the transformation in unwanted, the user can undo the change by pressing backspace,
11  * using the undo shortcut, or the undo button in the toolbar.
12  */
13 ( function( tinymce, setTimeout ) {
14         if ( tinymce.Env.ie && tinymce.Env.ie < 9 ) {
15                 return;
16         }
17
18         tinymce.PluginManager.add( 'wptextpattern', function( editor ) {
19                 var VK = tinymce.util.VK;
20
21                 var spacePatterns = [
22                         { regExp: /^[*-]\s/, cmd: 'InsertUnorderedList' },
23                         { regExp: /^1[.)]\s/, cmd: 'InsertOrderedList' }
24                 ];
25
26                 var enterPatterns = [
27                         { start: '##', format: 'h2' },
28                         { start: '###', format: 'h3' },
29                         { start: '####', format: 'h4' },
30                         { start: '#####', format: 'h5' },
31                         { start: '######', format: 'h6' },
32                         { start: '>', format: 'blockquote' },
33                         { regExp: /^(-){3,}$/, element: 'hr' }
34                 ];
35
36                 var inlinePatterns = [
37                         { start: '`', end: '`', format: 'code' }
38                 ];
39
40                 var canUndo;
41                 var chars = [];
42
43                 tinymce.each( inlinePatterns, function( pattern ) {
44                         tinymce.each( ( pattern.start + pattern.end ).split( '' ), function( c ) {
45                                 if ( tinymce.inArray( chars, c ) === -1 ) {
46                                         chars.push( c );
47                                 }
48                         } );
49                 } );
50
51                 editor.on( 'selectionchange', function() {
52                         canUndo = null;
53                 } );
54
55                 editor.on( 'keydown', function( event ) {
56                         if ( ( canUndo && event.keyCode === 27 /* ESCAPE */ ) || ( canUndo === 'space' && event.keyCode === VK.BACKSPACE ) ) {
57                                 editor.undoManager.undo();
58                                 event.preventDefault();
59                                 event.stopImmediatePropagation();
60                         }
61
62                         if ( event.keyCode === VK.ENTER && ! VK.modifierPressed( event ) ) {
63                                 enter();
64                         }
65                 }, true );
66
67                 editor.on( 'keyup', function( event ) {
68                         if ( event.keyCode === VK.SPACEBAR && ! event.ctrlKey && ! event.metaKey && ! event.altKey ) {
69                                 space();
70                         } else if ( event.keyCode > 47 && ! ( event.keyCode >= 91 && event.keyCode <= 93 ) ) {
71                                 inline();
72                         }
73                 } );
74
75                 function inline() {
76                         var rng = editor.selection.getRng();
77                         var node = rng.startContainer;
78                         var offset = rng.startOffset;
79                         var startOffset;
80                         var endOffset;
81                         var pattern;
82                         var format;
83                         var zero;
84
85                         if ( ! node || node.nodeType !== 3 || ! node.data.length || ! offset ) {
86                                 return;
87                         }
88
89                         if ( tinymce.inArray( chars, node.data.charAt( offset - 1 ) ) === -1 ) {
90                                 return;
91                         }
92
93                         function findStart( node ) {
94                                 var i = inlinePatterns.length;
95                                 var offset;
96
97                                 while ( i-- ) {
98                                         pattern = inlinePatterns[ i ];
99                                         offset = node.data.indexOf( pattern.end );
100
101                                         if ( offset !== -1 ) {
102                                                 return offset;
103                                         }
104                                 }
105                         }
106
107                         startOffset = findStart( node );
108                         endOffset = node.data.lastIndexOf( pattern.end );
109
110                         if ( startOffset === endOffset || endOffset === -1 ) {
111                                 return;
112                         }
113
114                         if ( endOffset - startOffset <= pattern.start.length ) {
115                                 return;
116                         }
117
118                         if ( node.data.slice( startOffset + pattern.start.length, endOffset ).indexOf( pattern.start.slice( 0, 1 ) ) !== -1 ) {
119                                 return;
120                         }
121
122                         format = editor.formatter.get( pattern.format );
123
124                         if ( format && format[0].inline ) {
125                                 editor.undoManager.add();
126
127                                 editor.undoManager.transact( function() {
128                                         node.insertData( offset, '\u200b' );
129
130                                         node = node.splitText( startOffset );
131                                         zero = node.splitText( offset - startOffset );
132
133                                         node.deleteData( 0, pattern.start.length );
134                                         node.deleteData( node.data.length - pattern.end.length, pattern.end.length );
135
136                                         editor.formatter.apply( pattern.format, {}, node );
137
138                                         editor.selection.setCursorLocation( zero, 1 );
139                                 } );
140
141                                 // We need to wait for native events to be triggered.
142                                 setTimeout( function() {
143                                         canUndo = 'space';
144
145                                         editor.once( 'selectionchange', function() {
146                                                 var offset;
147
148                                                 if ( zero ) {
149                                                         offset = zero.data.indexOf( '\u200b' );
150
151                                                         if ( offset !== -1 ) {
152                                                                 zero.deleteData( offset, offset + 1 );
153                                                         }
154                                                 }
155                                         } );
156                                 } );
157                         }
158                 }
159
160                 function firstTextNode( node ) {
161                         var parent = editor.dom.getParent( node, 'p' ),
162                                 child;
163
164                         if ( ! parent ) {
165                                 return;
166                         }
167
168                         while ( child = parent.firstChild ) {
169                                 if ( child.nodeType !== 3 ) {
170                                         parent = child;
171                                 } else {
172                                         break;
173                                 }
174                         }
175
176                         if ( ! child ) {
177                                 return;
178                         }
179
180                         if ( ! child.data ) {
181                                 if ( child.nextSibling && child.nextSibling.nodeType === 3 ) {
182                                         child = child.nextSibling;
183                                 } else {
184                                         child = null;
185                                 }
186                         }
187
188                         return child;
189                 }
190
191                 function space() {
192                         var rng = editor.selection.getRng(),
193                                 node = rng.startContainer,
194                                 parent,
195                                 text;
196
197                         if ( ! node || firstTextNode( node ) !== node ) {
198                                 return;
199                         }
200
201                         parent = node.parentNode;
202                         text = node.data;
203
204                         tinymce.each( spacePatterns, function( pattern ) {
205                                 var match = text.match( pattern.regExp );
206
207                                 if ( ! match || rng.startOffset !== match[0].length ) {
208                                         return;
209                                 }
210
211                                 editor.undoManager.add();
212
213                                 editor.undoManager.transact( function() {
214                                         node.deleteData( 0, match[0].length );
215
216                                         if ( ! parent.innerHTML ) {
217                                                 parent.appendChild( document.createElement( 'br' ) );
218                                         }
219
220                                         editor.selection.setCursorLocation( parent );
221                                         editor.execCommand( pattern.cmd );
222                                 } );
223
224                                 // We need to wait for native events to be triggered.
225                                 setTimeout( function() {
226                                         canUndo = 'space';
227                                 } );
228
229                                 return false;
230                         } );
231                 }
232
233                 function enter() {
234                         var rng = editor.selection.getRng(),
235                                 start = rng.startContainer,
236                                 node = firstTextNode( start ),
237                                 i = enterPatterns.length,
238                                 text, pattern, parent;
239
240                         if ( ! node ) {
241                                 return;
242                         }
243
244                         text = node.data;
245
246                         while ( i-- ) {
247                                 if ( enterPatterns[ i ].start ) {
248                                         if ( text.indexOf( enterPatterns[ i ].start ) === 0 ) {
249                                                 pattern = enterPatterns[ i ];
250                                                 break;
251                                         }
252                                 } else if ( enterPatterns[ i ].regExp ) {
253                                         if ( enterPatterns[ i ].regExp.test( text ) ) {
254                                                 pattern = enterPatterns[ i ];
255                                                 break;
256                                         }
257                                 }
258                         }
259
260                         if ( ! pattern ) {
261                                 return;
262                         }
263
264                         if ( node === start && tinymce.trim( text ) === pattern.start ) {
265                                 return;
266                         }
267
268                         editor.once( 'keyup', function() {
269                                 editor.undoManager.add();
270
271                                 editor.undoManager.transact( function() {
272                                         if ( pattern.format ) {
273                                                 editor.formatter.apply( pattern.format, {}, node );
274                                                 node.replaceData( 0, node.data.length, ltrim( node.data.slice( pattern.start.length ) ) );
275                                         } else if ( pattern.element ) {
276                                                 parent = node.parentNode && node.parentNode.parentNode;
277
278                                                 if ( parent ) {
279                                                         parent.replaceChild( document.createElement( pattern.element ), node.parentNode );
280                                                 }
281                                         }
282                                 } );
283
284                                 // We need to wait for native events to be triggered.
285                                 setTimeout( function() {
286                                         canUndo = 'enter';
287                                 } );
288                         } );
289                 }
290
291                 function ltrim( text ) {
292                         return text ? text.replace( /^\s+/, '' ) : '';
293                 }
294         } );
295 } )( window.tinymce, window.setTimeout );