5
|
1 |
/* global tinymce */ |
|
2 |
tinymce.PluginManager.add( 'wpeditimage', function( editor ) { |
|
3 |
var toolbar, serializer, |
|
4 |
each = tinymce.each, |
|
5 |
iOS = tinymce.Env.iOS; |
|
6 |
|
|
7 |
function isPlaceholder( node ) { |
|
8 |
return !! ( editor.dom.getAttrib( node, 'data-mce-placeholder' ) || editor.dom.getAttrib( node, 'data-mce-object' ) ); |
|
9 |
} |
|
10 |
|
|
11 |
editor.addButton( 'wp_img_remove', { |
|
12 |
tooltip: 'Remove', |
|
13 |
icon: 'dashicon dashicons-no', |
|
14 |
onclick: function() { |
|
15 |
removeImage( editor.selection.getNode() ); |
|
16 |
} |
|
17 |
} ); |
|
18 |
|
|
19 |
editor.addButton( 'wp_img_edit', { |
|
20 |
tooltip: 'Edit ', // trailing space is needed, used for context |
|
21 |
icon: 'dashicon dashicons-edit', |
|
22 |
onclick: function() { |
|
23 |
editImage( editor.selection.getNode() ); |
|
24 |
} |
|
25 |
} ); |
|
26 |
|
|
27 |
each( { |
|
28 |
alignleft: 'Align left', |
|
29 |
aligncenter: 'Align center', |
|
30 |
alignright: 'Align right', |
|
31 |
alignnone: 'No alignment' |
|
32 |
}, function( tooltip, name ) { |
|
33 |
var direction = name.slice( 5 ); |
|
34 |
|
|
35 |
editor.addButton( 'wp_img_' + name, { |
|
36 |
tooltip: tooltip, |
|
37 |
icon: 'dashicon dashicons-align-' + direction, |
|
38 |
cmd: 'alignnone' === name ? 'wpAlignNone' : 'Justify' + direction.slice( 0, 1 ).toUpperCase() + direction.slice( 1 ), |
|
39 |
onPostRender: function() { |
|
40 |
var self = this; |
|
41 |
|
|
42 |
editor.on( 'NodeChange', function( event ) { |
|
43 |
var node; |
|
44 |
|
|
45 |
// Don't bother. |
|
46 |
if ( event.element.nodeName !== 'IMG' ) { |
|
47 |
return; |
|
48 |
} |
|
49 |
|
|
50 |
node = editor.dom.getParent( event.element, '.wp-caption' ) || event.element; |
|
51 |
|
|
52 |
if ( 'alignnone' === name ) { |
|
53 |
self.active( ! /\balign(left|center|right)\b/.test( node.className ) ); |
|
54 |
} else { |
|
55 |
self.active( editor.dom.hasClass( node, name ) ); |
|
56 |
} |
|
57 |
} ); |
|
58 |
} |
|
59 |
} ); |
|
60 |
} ); |
|
61 |
|
|
62 |
editor.once( 'preinit', function() { |
|
63 |
toolbar = editor.wp._createToolbar( [ |
|
64 |
'wp_img_alignleft', |
|
65 |
'wp_img_aligncenter', |
|
66 |
'wp_img_alignright', |
|
67 |
'wp_img_alignnone', |
|
68 |
'wp_img_edit', |
|
69 |
'wp_img_remove' |
|
70 |
] ); |
|
71 |
} ); |
|
72 |
|
|
73 |
editor.on( 'wptoolbar', function( event ) { |
|
74 |
if ( event.element.nodeName === 'IMG' && ! isPlaceholder( event.element ) ) { |
|
75 |
event.toolbar = toolbar; |
|
76 |
} |
|
77 |
} ); |
|
78 |
|
|
79 |
// Safari on iOS fails to select image nodes in contentEditoble mode on touch/click. |
|
80 |
// Select them again. |
|
81 |
if ( iOS ) { |
|
82 |
editor.on( 'click', function( event ) { |
|
83 |
if ( event.target.nodeName === 'IMG' ) { |
|
84 |
var node = event.target; |
|
85 |
|
|
86 |
window.setTimeout( function() { |
|
87 |
editor.selection.select( node ); |
|
88 |
editor.nodeChanged(); |
|
89 |
}, 200 ); |
|
90 |
} else { |
|
91 |
toolbar.hide(); |
|
92 |
} |
|
93 |
} ); |
|
94 |
} |
|
95 |
|
|
96 |
function parseShortcode( content ) { |
|
97 |
return content.replace( /(?:<p>)?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g, function( a, b, c ) { |
|
98 |
var id, align, classes, caption, img, width, |
|
99 |
trim = tinymce.trim; |
|
100 |
|
|
101 |
id = b.match( /id=['"]([^'"]*)['"] ?/ ); |
|
102 |
if ( id ) { |
|
103 |
b = b.replace( id[0], '' ); |
|
104 |
} |
|
105 |
|
|
106 |
align = b.match( /align=['"]([^'"]*)['"] ?/ ); |
|
107 |
if ( align ) { |
|
108 |
b = b.replace( align[0], '' ); |
|
109 |
} |
|
110 |
|
|
111 |
classes = b.match( /class=['"]([^'"]*)['"] ?/ ); |
|
112 |
if ( classes ) { |
|
113 |
b = b.replace( classes[0], '' ); |
|
114 |
} |
|
115 |
|
|
116 |
width = b.match( /width=['"]([0-9]*)['"] ?/ ); |
|
117 |
if ( width ) { |
|
118 |
b = b.replace( width[0], '' ); |
|
119 |
} |
|
120 |
|
|
121 |
c = trim( c ); |
|
122 |
img = c.match( /((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)([\s\S]*)/i ); |
|
123 |
|
|
124 |
if ( img && img[2] ) { |
|
125 |
caption = trim( img[2] ); |
|
126 |
img = trim( img[1] ); |
|
127 |
} else { |
|
128 |
// old captions shortcode style |
|
129 |
caption = trim( b ).replace( /caption=['"]/, '' ).replace( /['"]$/, '' ); |
|
130 |
img = c; |
|
131 |
} |
|
132 |
|
|
133 |
id = ( id && id[1] ) ? id[1].replace( /[<>&]+/g, '' ) : ''; |
|
134 |
align = ( align && align[1] ) ? align[1] : 'alignnone'; |
|
135 |
classes = ( classes && classes[1] ) ? ' ' + classes[1].replace( /[<>&]+/g, '' ) : ''; |
|
136 |
|
|
137 |
if ( ! width && img ) { |
|
138 |
width = img.match( /width=['"]([0-9]*)['"]/ ); |
|
139 |
} |
|
140 |
|
|
141 |
if ( width && width[1] ) { |
|
142 |
width = width[1]; |
|
143 |
} |
|
144 |
|
|
145 |
if ( ! width || ! caption ) { |
|
146 |
return c; |
|
147 |
} |
|
148 |
|
|
149 |
width = parseInt( width, 10 ); |
|
150 |
if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { |
|
151 |
width += 10; |
|
152 |
} |
|
153 |
|
|
154 |
return '<div class="mceTemp"><dl id="' + id + '" class="wp-caption ' + align + classes + '" style="width: ' + width + 'px">' + |
|
155 |
'<dt class="wp-caption-dt">'+ img +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl></div>'; |
|
156 |
}); |
|
157 |
} |
|
158 |
|
|
159 |
function getShortcode( content ) { |
|
160 |
return content.replace( /<div (?:id="attachment_|class="mceTemp)[^>]*>([\s\S]+?)<\/div>/g, function( a, b ) { |
|
161 |
var out = ''; |
|
162 |
|
|
163 |
if ( b.indexOf('<img ') === -1 ) { |
|
164 |
// Broken caption. The user managed to drag the image out? |
|
165 |
// Try to return the caption text as a paragraph. |
|
166 |
out = b.match( /<dd [^>]+>([\s\S]+?)<\/dd>/i ); |
|
167 |
|
|
168 |
if ( out && out[1] ) { |
|
169 |
return '<p>' + out[1] + '</p>'; |
|
170 |
} |
|
171 |
|
|
172 |
return ''; |
|
173 |
} |
|
174 |
|
|
175 |
out = b.replace( /\s*<dl ([^>]+)>\s*<dt [^>]+>([\s\S]+?)<\/dt>\s*<dd [^>]+>([\s\S]*?)<\/dd>\s*<\/dl>\s*/gi, function( a, b, c, caption ) { |
|
176 |
var id, classes, align, width; |
|
177 |
|
|
178 |
width = c.match( /width="([0-9]*)"/ ); |
|
179 |
width = ( width && width[1] ) ? width[1] : ''; |
|
180 |
|
|
181 |
classes = b.match( /class="([^"]*)"/ ); |
|
182 |
classes = ( classes && classes[1] ) ? classes[1] : ''; |
|
183 |
align = classes.match( /align[a-z]+/i ) || 'alignnone'; |
|
184 |
|
|
185 |
if ( ! width || ! caption ) { |
|
186 |
if ( 'alignnone' !== align[0] ) { |
|
187 |
c = c.replace( /><img/, ' class="' + align[0] + '"><img' ); |
|
188 |
} |
|
189 |
return c; |
|
190 |
} |
|
191 |
|
|
192 |
id = b.match( /id="([^"]*)"/ ); |
|
193 |
id = ( id && id[1] ) ? id[1] : ''; |
|
194 |
|
|
195 |
classes = classes.replace( /wp-caption ?|align[a-z]+ ?/gi, '' ); |
|
196 |
|
|
197 |
if ( classes ) { |
|
198 |
classes = ' class="' + classes + '"'; |
|
199 |
} |
|
200 |
|
|
201 |
caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) { |
|
202 |
// no line breaks inside HTML tags |
|
203 |
return a.replace( /[\r\n\t]+/, ' ' ); |
|
204 |
}); |
|
205 |
|
|
206 |
// convert remaining line breaks to <br> |
|
207 |
caption = caption.replace( /\s*\n\s*/g, '<br />' ); |
|
208 |
|
|
209 |
return '[caption id="' + id + '" align="' + align + '" width="' + width + '"' + classes + ']' + c + ' ' + caption + '[/caption]'; |
|
210 |
}); |
|
211 |
|
|
212 |
if ( out.indexOf('[caption') === -1 ) { |
|
213 |
// the caption html seems broken, try to find the image that may be wrapped in a link |
|
214 |
// and may be followed by <p> with the caption text. |
|
215 |
out = b.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' ); |
|
216 |
} |
|
217 |
|
|
218 |
return out; |
|
219 |
}); |
|
220 |
} |
|
221 |
|
|
222 |
function extractImageData( imageNode ) { |
|
223 |
var classes, extraClasses, metadata, captionBlock, caption, link, width, height, |
|
224 |
captionClassName = [], |
|
225 |
dom = editor.dom, |
|
226 |
isIntRegExp = /^\d+$/; |
|
227 |
|
|
228 |
// default attributes |
|
229 |
metadata = { |
|
230 |
attachment_id: false, |
|
231 |
size: 'custom', |
|
232 |
caption: '', |
|
233 |
align: 'none', |
|
234 |
extraClasses: '', |
|
235 |
link: false, |
|
236 |
linkUrl: '', |
|
237 |
linkClassName: '', |
|
238 |
linkTargetBlank: false, |
|
239 |
linkRel: '', |
|
240 |
title: '' |
|
241 |
}; |
|
242 |
|
|
243 |
metadata.url = dom.getAttrib( imageNode, 'src' ); |
|
244 |
metadata.alt = dom.getAttrib( imageNode, 'alt' ); |
|
245 |
metadata.title = dom.getAttrib( imageNode, 'title' ); |
|
246 |
|
|
247 |
width = dom.getAttrib( imageNode, 'width' ); |
|
248 |
height = dom.getAttrib( imageNode, 'height' ); |
|
249 |
|
|
250 |
if ( ! isIntRegExp.test( width ) || parseInt( width, 10 ) < 1 ) { |
|
251 |
width = imageNode.naturalWidth || imageNode.width; |
|
252 |
} |
|
253 |
|
|
254 |
if ( ! isIntRegExp.test( height ) || parseInt( height, 10 ) < 1 ) { |
|
255 |
height = imageNode.naturalHeight || imageNode.height; |
|
256 |
} |
|
257 |
|
|
258 |
metadata.customWidth = metadata.width = width; |
|
259 |
metadata.customHeight = metadata.height = height; |
|
260 |
|
|
261 |
classes = tinymce.explode( imageNode.className, ' ' ); |
|
262 |
extraClasses = []; |
|
263 |
|
|
264 |
tinymce.each( classes, function( name ) { |
|
265 |
|
|
266 |
if ( /^wp-image/.test( name ) ) { |
|
267 |
metadata.attachment_id = parseInt( name.replace( 'wp-image-', '' ), 10 ); |
|
268 |
} else if ( /^align/.test( name ) ) { |
|
269 |
metadata.align = name.replace( 'align', '' ); |
|
270 |
} else if ( /^size/.test( name ) ) { |
|
271 |
metadata.size = name.replace( 'size-', '' ); |
|
272 |
} else { |
|
273 |
extraClasses.push( name ); |
|
274 |
} |
|
275 |
|
|
276 |
} ); |
|
277 |
|
|
278 |
metadata.extraClasses = extraClasses.join( ' ' ); |
|
279 |
|
|
280 |
// Extract caption |
|
281 |
captionBlock = dom.getParents( imageNode, '.wp-caption' ); |
|
282 |
|
|
283 |
if ( captionBlock.length ) { |
|
284 |
captionBlock = captionBlock[0]; |
|
285 |
|
|
286 |
classes = captionBlock.className.split( ' ' ); |
|
287 |
tinymce.each( classes, function( name ) { |
|
288 |
if ( /^align/.test( name ) ) { |
|
289 |
metadata.align = name.replace( 'align', '' ); |
|
290 |
} else if ( name && name !== 'wp-caption' ) { |
|
291 |
captionClassName.push( name ); |
|
292 |
} |
|
293 |
} ); |
|
294 |
|
|
295 |
metadata.captionClassName = captionClassName.join( ' ' ); |
|
296 |
|
|
297 |
caption = dom.select( 'dd.wp-caption-dd', captionBlock ); |
|
298 |
if ( caption.length ) { |
|
299 |
caption = caption[0]; |
|
300 |
|
|
301 |
metadata.caption = editor.serializer.serialize( caption ) |
|
302 |
.replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' ); |
|
303 |
} |
|
304 |
} |
|
305 |
|
|
306 |
// Extract linkTo |
|
307 |
if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' ) { |
|
308 |
link = imageNode.parentNode; |
|
309 |
metadata.linkUrl = dom.getAttrib( link, 'href' ); |
|
310 |
metadata.linkTargetBlank = dom.getAttrib( link, 'target' ) === '_blank' ? true : false; |
|
311 |
metadata.linkRel = dom.getAttrib( link, 'rel' ); |
|
312 |
metadata.linkClassName = link.className; |
|
313 |
} |
|
314 |
|
|
315 |
return metadata; |
|
316 |
} |
|
317 |
|
|
318 |
function hasTextContent( node ) { |
|
319 |
return node && !! ( node.textContent || node.innerText ); |
|
320 |
} |
|
321 |
|
|
322 |
// Verify HTML in captions |
|
323 |
function verifyHTML( caption ) { |
|
324 |
if ( ! caption || ( caption.indexOf( '<' ) === -1 && caption.indexOf( '>' ) === -1 ) ) { |
|
325 |
return caption; |
|
326 |
} |
|
327 |
|
|
328 |
if ( ! serializer ) { |
|
329 |
serializer = new tinymce.html.Serializer( {}, editor.schema ); |
|
330 |
} |
|
331 |
|
|
332 |
return serializer.serialize( editor.parser.parse( caption, { forced_root_block: false } ) ); |
|
333 |
} |
|
334 |
|
|
335 |
function updateImage( imageNode, imageData ) { |
|
336 |
var classes, className, node, html, parent, wrap, linkNode, |
|
337 |
captionNode, dd, dl, id, attrs, linkAttrs, width, height, align, |
|
338 |
dom = editor.dom; |
|
339 |
|
|
340 |
classes = tinymce.explode( imageData.extraClasses, ' ' ); |
|
341 |
|
|
342 |
if ( ! classes ) { |
|
343 |
classes = []; |
|
344 |
} |
|
345 |
|
|
346 |
if ( ! imageData.caption ) { |
|
347 |
classes.push( 'align' + imageData.align ); |
|
348 |
} |
|
349 |
|
|
350 |
if ( imageData.attachment_id ) { |
|
351 |
classes.push( 'wp-image-' + imageData.attachment_id ); |
|
352 |
if ( imageData.size && imageData.size !== 'custom' ) { |
|
353 |
classes.push( 'size-' + imageData.size ); |
|
354 |
} |
|
355 |
} |
|
356 |
|
|
357 |
width = imageData.width; |
|
358 |
height = imageData.height; |
|
359 |
|
|
360 |
if ( imageData.size === 'custom' ) { |
|
361 |
width = imageData.customWidth; |
|
362 |
height = imageData.customHeight; |
|
363 |
} |
|
364 |
|
|
365 |
attrs = { |
|
366 |
src: imageData.url, |
|
367 |
width: width || null, |
|
368 |
height: height || null, |
|
369 |
alt: imageData.alt, |
|
370 |
title: imageData.title || null, |
|
371 |
'class': classes.join( ' ' ) || null |
|
372 |
}; |
|
373 |
|
|
374 |
dom.setAttribs( imageNode, attrs ); |
|
375 |
|
|
376 |
linkAttrs = { |
|
377 |
href: imageData.linkUrl, |
|
378 |
rel: imageData.linkRel || null, |
|
379 |
target: imageData.linkTargetBlank ? '_blank': null, |
|
380 |
'class': imageData.linkClassName || null |
|
381 |
}; |
|
382 |
|
|
383 |
if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) { |
|
384 |
// Update or remove an existing link wrapped around the image |
|
385 |
if ( imageData.linkUrl ) { |
|
386 |
dom.setAttribs( imageNode.parentNode, linkAttrs ); |
|
387 |
} else { |
|
388 |
dom.remove( imageNode.parentNode, true ); |
|
389 |
} |
|
390 |
} else if ( imageData.linkUrl ) { |
|
391 |
if ( linkNode = dom.getParent( imageNode, 'a' ) ) { |
|
392 |
// The image is inside a link together with other nodes, |
|
393 |
// or is nested in another node, move it out |
|
394 |
dom.insertAfter( imageNode, linkNode ); |
|
395 |
} |
|
396 |
|
|
397 |
// Add link wrapped around the image |
|
398 |
linkNode = dom.create( 'a', linkAttrs ); |
|
399 |
imageNode.parentNode.insertBefore( linkNode, imageNode ); |
|
400 |
linkNode.appendChild( imageNode ); |
|
401 |
} |
|
402 |
|
|
403 |
captionNode = editor.dom.getParent( imageNode, '.mceTemp' ); |
|
404 |
|
|
405 |
if ( imageNode.parentNode && imageNode.parentNode.nodeName === 'A' && ! hasTextContent( imageNode.parentNode ) ) { |
|
406 |
node = imageNode.parentNode; |
|
407 |
} else { |
|
408 |
node = imageNode; |
|
409 |
} |
|
410 |
|
|
411 |
if ( imageData.caption ) { |
|
412 |
imageData.caption = verifyHTML( imageData.caption ); |
|
413 |
|
|
414 |
id = imageData.attachment_id ? 'attachment_' + imageData.attachment_id : null; |
|
415 |
align = 'align' + ( imageData.align || 'none' ); |
|
416 |
className = 'wp-caption ' + align; |
|
417 |
|
|
418 |
if ( imageData.captionClassName ) { |
|
419 |
className += ' ' + imageData.captionClassName.replace( /[<>&]+/g, '' ); |
|
420 |
} |
|
421 |
|
|
422 |
if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { |
|
423 |
width = parseInt( width, 10 ); |
|
424 |
width += 10; |
|
425 |
} |
|
426 |
|
|
427 |
if ( captionNode ) { |
|
428 |
dl = dom.select( 'dl.wp-caption', captionNode ); |
|
429 |
|
|
430 |
if ( dl.length ) { |
|
431 |
dom.setAttribs( dl, { |
|
432 |
id: id, |
|
433 |
'class': className, |
|
434 |
style: 'width: ' + width + 'px' |
|
435 |
} ); |
|
436 |
} |
|
437 |
|
|
438 |
dd = dom.select( '.wp-caption-dd', captionNode ); |
|
439 |
|
|
440 |
if ( dd.length ) { |
|
441 |
dom.setHTML( dd[0], imageData.caption ); |
|
442 |
} |
|
443 |
|
|
444 |
} else { |
|
445 |
id = id ? 'id="'+ id +'" ' : ''; |
|
446 |
|
|
447 |
// should create a new function for generating the caption markup |
|
448 |
html = '<dl ' + id + 'class="' + className +'" style="width: '+ width +'px">' + |
|
449 |
'<dt class="wp-caption-dt"></dt><dd class="wp-caption-dd">'+ imageData.caption +'</dd></dl>'; |
|
450 |
|
|
451 |
wrap = dom.create( 'div', { 'class': 'mceTemp' }, html ); |
|
452 |
|
|
453 |
if ( parent = dom.getParent( node, 'p' ) ) { |
|
454 |
parent.parentNode.insertBefore( wrap, parent ); |
|
455 |
} else { |
|
456 |
node.parentNode.insertBefore( wrap, node ); |
|
457 |
} |
|
458 |
|
|
459 |
editor.$( wrap ).find( 'dt.wp-caption-dt' ).append( node ); |
|
460 |
|
|
461 |
if ( parent && dom.isEmpty( parent ) ) { |
|
462 |
dom.remove( parent ); |
|
463 |
} |
|
464 |
} |
|
465 |
} else if ( captionNode ) { |
|
466 |
// Remove the caption wrapper and place the image in new paragraph |
|
467 |
parent = dom.create( 'p' ); |
|
468 |
captionNode.parentNode.insertBefore( parent, captionNode ); |
|
469 |
parent.appendChild( node ); |
|
470 |
dom.remove( captionNode ); |
|
471 |
} |
|
472 |
|
|
473 |
if ( wp.media.events ) { |
|
474 |
wp.media.events.trigger( 'editor:image-update', { |
|
475 |
editor: editor, |
|
476 |
metadata: imageData, |
|
477 |
image: imageNode |
|
478 |
} ); |
|
479 |
} |
|
480 |
|
|
481 |
editor.nodeChanged(); |
|
482 |
} |
|
483 |
|
|
484 |
function editImage( img ) { |
|
485 |
var frame, callback, metadata; |
|
486 |
|
|
487 |
if ( typeof wp === 'undefined' || ! wp.media ) { |
|
488 |
editor.execCommand( 'mceImage' ); |
|
489 |
return; |
|
490 |
} |
|
491 |
|
|
492 |
metadata = extractImageData( img ); |
|
493 |
|
|
494 |
// Manipulate the metadata by reference that is fed into the PostImage model used in the media modal |
|
495 |
wp.media.events.trigger( 'editor:image-edit', { |
|
496 |
editor: editor, |
|
497 |
metadata: metadata, |
|
498 |
image: img |
|
499 |
} ); |
|
500 |
|
|
501 |
frame = wp.media({ |
|
502 |
frame: 'image', |
|
503 |
state: 'image-details', |
|
504 |
metadata: metadata |
|
505 |
} ); |
|
506 |
|
|
507 |
wp.media.events.trigger( 'editor:frame-create', { frame: frame } ); |
|
508 |
|
|
509 |
callback = function( imageData ) { |
|
510 |
editor.focus(); |
|
511 |
editor.undoManager.transact( function() { |
|
512 |
updateImage( img, imageData ); |
|
513 |
} ); |
|
514 |
frame.detach(); |
|
515 |
}; |
|
516 |
|
|
517 |
frame.state('image-details').on( 'update', callback ); |
|
518 |
frame.state('replace-image').on( 'replace', callback ); |
|
519 |
frame.on( 'close', function() { |
|
520 |
editor.focus(); |
|
521 |
frame.detach(); |
|
522 |
}); |
|
523 |
|
|
524 |
frame.open(); |
|
525 |
} |
|
526 |
|
|
527 |
function removeImage( node ) { |
|
528 |
var wrap; |
|
529 |
|
|
530 |
if ( node.nodeName === 'DIV' && editor.dom.hasClass( node, 'mceTemp' ) ) { |
|
531 |
wrap = node; |
|
532 |
} else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) { |
|
533 |
wrap = editor.dom.getParent( node, 'div.mceTemp' ); |
|
534 |
} |
|
535 |
|
|
536 |
if ( wrap ) { |
|
537 |
if ( wrap.nextSibling ) { |
|
538 |
editor.selection.select( wrap.nextSibling ); |
|
539 |
} else if ( wrap.previousSibling ) { |
|
540 |
editor.selection.select( wrap.previousSibling ); |
|
541 |
} else { |
|
542 |
editor.selection.select( wrap.parentNode ); |
|
543 |
} |
|
544 |
|
|
545 |
editor.selection.collapse( true ); |
|
546 |
editor.dom.remove( wrap ); |
|
547 |
} else { |
|
548 |
editor.dom.remove( node ); |
|
549 |
} |
|
550 |
|
|
551 |
editor.nodeChanged(); |
|
552 |
editor.undoManager.add(); |
|
553 |
} |
|
554 |
|
|
555 |
editor.on( 'init', function() { |
|
556 |
var dom = editor.dom, |
|
557 |
captionClass = editor.getParam( 'wpeditimage_html5_captions' ) ? 'html5-captions' : 'html4-captions'; |
|
558 |
|
|
559 |
dom.addClass( editor.getBody(), captionClass ); |
|
560 |
|
|
561 |
// Add caption field to the default image dialog |
|
562 |
editor.on( 'wpLoadImageForm', function( event ) { |
|
563 |
if ( editor.getParam( 'wpeditimage_disable_captions' ) ) { |
|
564 |
return; |
|
565 |
} |
|
566 |
|
|
567 |
var captionField = { |
|
568 |
type: 'textbox', |
|
569 |
flex: 1, |
|
570 |
name: 'caption', |
|
571 |
minHeight: 60, |
|
572 |
multiline: true, |
|
573 |
scroll: true, |
|
574 |
label: 'Image caption' |
|
575 |
}; |
|
576 |
|
|
577 |
event.data.splice( event.data.length - 1, 0, captionField ); |
|
578 |
}); |
|
579 |
|
|
580 |
// Fix caption parent width for images added from URL |
|
581 |
editor.on( 'wpNewImageRefresh', function( event ) { |
|
582 |
var parent, captionWidth; |
|
583 |
|
|
584 |
if ( parent = dom.getParent( event.node, 'dl.wp-caption' ) ) { |
|
585 |
if ( ! parent.style.width ) { |
|
586 |
captionWidth = parseInt( event.node.clientWidth, 10 ) + 10; |
|
587 |
captionWidth = captionWidth ? captionWidth + 'px' : '50%'; |
|
588 |
dom.setStyle( parent, 'width', captionWidth ); |
|
589 |
} |
|
590 |
} |
|
591 |
}); |
|
592 |
|
|
593 |
editor.on( 'wpImageFormSubmit', function( event ) { |
|
594 |
var data = event.imgData.data, |
|
595 |
imgNode = event.imgData.node, |
|
596 |
caption = event.imgData.caption, |
|
597 |
captionId = '', |
|
598 |
captionAlign = '', |
|
599 |
captionWidth = '', |
|
600 |
wrap, parent, node, html, imgId; |
|
601 |
|
|
602 |
// Temp image id so we can find the node later |
|
603 |
data.id = '__wp-temp-img-id'; |
|
604 |
// Cancel the original callback |
|
605 |
event.imgData.cancel = true; |
|
606 |
|
|
607 |
if ( ! data.style ) { |
|
608 |
data.style = null; |
|
609 |
} |
|
610 |
|
|
611 |
if ( ! data.src ) { |
|
612 |
// Delete the image and the caption |
|
613 |
if ( imgNode ) { |
|
614 |
if ( wrap = dom.getParent( imgNode, 'div.mceTemp' ) ) { |
|
615 |
dom.remove( wrap ); |
|
616 |
} else if ( imgNode.parentNode.nodeName === 'A' ) { |
|
617 |
dom.remove( imgNode.parentNode ); |
|
618 |
} else { |
|
619 |
dom.remove( imgNode ); |
|
620 |
} |
|
621 |
|
|
622 |
editor.nodeChanged(); |
|
623 |
} |
|
624 |
return; |
|
625 |
} |
|
626 |
|
|
627 |
if ( caption ) { |
|
628 |
caption = caption.replace( /\r\n|\r/g, '\n' ).replace( /<\/?[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) { |
|
629 |
// No line breaks inside HTML tags |
|
630 |
return a.replace( /[\r\n\t]+/, ' ' ); |
|
631 |
}); |
|
632 |
|
|
633 |
// Convert remaining line breaks to <br> |
|
634 |
caption = caption.replace( /(<br[^>]*>)\s*\n\s*/g, '$1' ).replace( /\s*\n\s*/g, '<br />' ); |
|
635 |
caption = verifyHTML( caption ); |
|
636 |
} |
|
637 |
|
|
638 |
if ( ! imgNode ) { |
|
639 |
// New image inserted |
|
640 |
html = dom.createHTML( 'img', data ); |
|
641 |
|
|
642 |
if ( caption ) { |
|
643 |
node = editor.selection.getNode(); |
|
644 |
|
|
645 |
if ( data.width ) { |
|
646 |
captionWidth = parseInt( data.width, 10 ); |
|
647 |
|
|
648 |
if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { |
|
649 |
captionWidth += 10; |
|
650 |
} |
|
651 |
|
|
652 |
captionWidth = ' style="width: ' + captionWidth + 'px"'; |
|
653 |
} |
|
654 |
|
|
655 |
html = '<dl class="wp-caption alignnone"' + captionWidth + '>' + |
|
656 |
'<dt class="wp-caption-dt">'+ html +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl>'; |
|
657 |
|
|
658 |
if ( node.nodeName === 'P' ) { |
|
659 |
parent = node; |
|
660 |
} else { |
|
661 |
parent = dom.getParent( node, 'p' ); |
|
662 |
} |
|
663 |
|
|
664 |
if ( parent && parent.nodeName === 'P' ) { |
|
665 |
wrap = dom.create( 'div', { 'class': 'mceTemp' }, html ); |
|
666 |
parent.parentNode.insertBefore( wrap, parent ); |
|
667 |
editor.selection.select( wrap ); |
|
668 |
editor.nodeChanged(); |
|
669 |
|
|
670 |
if ( dom.isEmpty( parent ) ) { |
|
671 |
dom.remove( parent ); |
|
672 |
} |
|
673 |
} else { |
|
674 |
editor.selection.setContent( '<div class="mceTemp">' + html + '</div>' ); |
|
675 |
} |
|
676 |
} else { |
|
677 |
editor.selection.setContent( html ); |
|
678 |
} |
|
679 |
} else { |
|
680 |
// Edit existing image |
|
681 |
|
|
682 |
// Store the original image id if any |
|
683 |
imgId = imgNode.id || null; |
|
684 |
// Update the image node |
|
685 |
dom.setAttribs( imgNode, data ); |
|
686 |
wrap = dom.getParent( imgNode, 'dl.wp-caption' ); |
|
687 |
|
|
688 |
if ( caption ) { |
|
689 |
if ( wrap ) { |
|
690 |
if ( parent = dom.select( 'dd.wp-caption-dd', wrap )[0] ) { |
|
691 |
parent.innerHTML = caption; |
|
692 |
} |
|
693 |
} else { |
|
694 |
if ( imgNode.className ) { |
|
695 |
captionId = imgNode.className.match( /wp-image-([0-9]+)/ ); |
|
696 |
captionAlign = imgNode.className.match( /align(left|right|center|none)/ ); |
|
697 |
} |
|
698 |
|
|
699 |
if ( captionAlign ) { |
|
700 |
captionAlign = captionAlign[0]; |
|
701 |
imgNode.className = imgNode.className.replace( /align(left|right|center|none)/g, '' ); |
|
702 |
} else { |
|
703 |
captionAlign = 'alignnone'; |
|
704 |
} |
|
705 |
|
|
706 |
captionAlign = ' class="wp-caption ' + captionAlign + '"'; |
|
707 |
|
|
708 |
if ( captionId ) { |
|
709 |
captionId = ' id="attachment_' + captionId[1] + '"'; |
|
710 |
} |
|
711 |
|
|
712 |
captionWidth = data.width || imgNode.clientWidth; |
|
713 |
|
|
714 |
if ( captionWidth ) { |
|
715 |
captionWidth = parseInt( captionWidth, 10 ); |
|
716 |
|
|
717 |
if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { |
|
718 |
captionWidth += 10; |
|
719 |
} |
|
720 |
|
|
721 |
captionWidth = ' style="width: '+ captionWidth +'px"'; |
|
722 |
} |
|
723 |
|
|
724 |
if ( imgNode.parentNode && imgNode.parentNode.nodeName === 'A' ) { |
|
725 |
node = imgNode.parentNode; |
|
726 |
} else { |
|
727 |
node = imgNode; |
|
728 |
} |
|
729 |
|
|
730 |
html = '<dl ' + captionId + captionAlign + captionWidth + '>' + |
|
731 |
'<dt class="wp-caption-dt"></dt><dd class="wp-caption-dd">'+ caption +'</dd></dl>'; |
|
732 |
|
|
733 |
wrap = dom.create( 'div', { 'class': 'mceTemp' }, html ); |
|
734 |
|
|
735 |
if ( parent = dom.getParent( node, 'p' ) ) { |
|
736 |
parent.parentNode.insertBefore( wrap, parent ); |
|
737 |
} else { |
|
738 |
node.parentNode.insertBefore( wrap, node ); |
|
739 |
} |
|
740 |
|
|
741 |
editor.$( wrap ).find( 'dt.wp-caption-dt' ).append( node ); |
|
742 |
|
|
743 |
if ( parent && dom.isEmpty( parent ) ) { |
|
744 |
dom.remove( parent ); |
|
745 |
} |
|
746 |
} |
|
747 |
} else { |
|
748 |
if ( wrap ) { |
|
749 |
// Remove the caption wrapper and place the image in new paragraph |
|
750 |
if ( imgNode.parentNode.nodeName === 'A' ) { |
|
751 |
html = dom.getOuterHTML( imgNode.parentNode ); |
|
752 |
} else { |
|
753 |
html = dom.getOuterHTML( imgNode ); |
|
754 |
} |
|
755 |
|
|
756 |
parent = dom.create( 'p', {}, html ); |
|
757 |
dom.insertAfter( parent, wrap.parentNode ); |
|
758 |
editor.selection.select( parent ); |
|
759 |
editor.nodeChanged(); |
|
760 |
dom.remove( wrap.parentNode ); |
|
761 |
} |
|
762 |
} |
|
763 |
} |
|
764 |
|
|
765 |
imgNode = dom.get('__wp-temp-img-id'); |
|
766 |
dom.setAttrib( imgNode, 'id', imgId ); |
|
767 |
event.imgData.node = imgNode; |
|
768 |
}); |
|
769 |
|
|
770 |
editor.on( 'wpLoadImageData', function( event ) { |
|
771 |
var parent, |
|
772 |
data = event.imgData.data, |
|
773 |
imgNode = event.imgData.node; |
|
774 |
|
|
775 |
if ( parent = dom.getParent( imgNode, 'dl.wp-caption' ) ) { |
|
776 |
parent = dom.select( 'dd.wp-caption-dd', parent )[0]; |
|
777 |
|
|
778 |
if ( parent ) { |
|
779 |
data.caption = editor.serializer.serialize( parent ) |
|
780 |
.replace( /<br[^>]*>/g, '$&\n' ).replace( /^<p>/, '' ).replace( /<\/p>$/, '' ); |
|
781 |
} |
|
782 |
} |
|
783 |
}); |
|
784 |
|
|
785 |
dom.bind( editor.getDoc(), 'dragstart', function( event ) { |
|
786 |
var node = editor.selection.getNode(); |
|
787 |
|
|
788 |
// Prevent dragging images out of the caption elements |
|
789 |
if ( node.nodeName === 'IMG' && dom.getParent( node, '.wp-caption' ) ) { |
|
790 |
event.preventDefault(); |
|
791 |
} |
|
792 |
}); |
|
793 |
|
|
794 |
// Prevent IE11 from making dl.wp-caption resizable |
|
795 |
if ( tinymce.Env.ie && tinymce.Env.ie > 10 ) { |
|
796 |
// The 'mscontrolselect' event is supported only in IE11+ |
|
797 |
dom.bind( editor.getBody(), 'mscontrolselect', function( event ) { |
|
798 |
if ( event.target.nodeName === 'IMG' && dom.getParent( event.target, '.wp-caption' ) ) { |
|
799 |
// Hide the thick border with resize handles around dl.wp-caption |
|
800 |
editor.getBody().focus(); // :( |
|
801 |
} else if ( event.target.nodeName === 'DL' && dom.hasClass( event.target, 'wp-caption' ) ) { |
|
802 |
// Trigger the thick border with resize handles... |
|
803 |
// This will make the caption text editable. |
|
804 |
event.target.focus(); |
|
805 |
} |
|
806 |
}); |
|
807 |
} |
|
808 |
}); |
|
809 |
|
|
810 |
editor.on( 'ObjectResized', function( event ) { |
|
811 |
var node = event.target; |
|
812 |
|
|
813 |
if ( node.nodeName === 'IMG' ) { |
|
814 |
editor.undoManager.transact( function() { |
|
815 |
var parent, width, |
|
816 |
dom = editor.dom; |
|
817 |
|
|
818 |
node.className = node.className.replace( /\bsize-[^ ]+/, '' ); |
|
819 |
|
|
820 |
if ( parent = dom.getParent( node, '.wp-caption' ) ) { |
|
821 |
width = event.width || dom.getAttrib( node, 'width' ); |
|
822 |
|
|
823 |
if ( width ) { |
|
824 |
width = parseInt( width, 10 ); |
|
825 |
|
|
826 |
if ( ! editor.getParam( 'wpeditimage_html5_captions' ) ) { |
|
827 |
width += 10; |
|
828 |
} |
|
829 |
|
|
830 |
dom.setStyle( parent, 'width', width + 'px' ); |
|
831 |
} |
|
832 |
} |
|
833 |
}); |
|
834 |
} |
|
835 |
}); |
|
836 |
|
|
837 |
editor.on( 'BeforeExecCommand', function( event ) { |
|
838 |
var node, p, DL, align, replacement, |
|
839 |
cmd = event.command, |
|
840 |
dom = editor.dom; |
|
841 |
|
|
842 |
if ( cmd === 'mceInsertContent' ) { |
|
843 |
// When inserting content, if the caret is inside a caption create new paragraph under |
|
844 |
// and move the caret there |
|
845 |
if ( node = dom.getParent( editor.selection.getNode(), 'div.mceTemp' ) ) { |
|
846 |
p = dom.create( 'p' ); |
|
847 |
dom.insertAfter( p, node ); |
|
848 |
editor.selection.setCursorLocation( p, 0 ); |
|
849 |
editor.nodeChanged(); |
|
850 |
} |
|
851 |
} else if ( cmd === 'JustifyLeft' || cmd === 'JustifyRight' || cmd === 'JustifyCenter' || cmd === 'wpAlignNone' ) { |
|
852 |
node = editor.selection.getNode(); |
|
853 |
align = 'align' + cmd.slice( 7 ).toLowerCase(); |
|
854 |
DL = editor.dom.getParent( node, '.wp-caption' ); |
|
855 |
|
|
856 |
if ( node.nodeName !== 'IMG' && ! DL ) { |
|
857 |
return; |
|
858 |
} |
|
859 |
|
|
860 |
node = DL || node; |
|
861 |
|
|
862 |
if ( editor.dom.hasClass( node, align ) ) { |
|
863 |
replacement = ' alignnone'; |
|
864 |
} else { |
|
865 |
replacement = ' ' + align; |
|
866 |
} |
|
867 |
|
|
868 |
node.className = node.className.replace( / ?align(left|center|right|none)/g, '' ) + replacement; |
|
869 |
|
|
870 |
editor.nodeChanged(); |
|
871 |
event.preventDefault(); |
|
872 |
|
|
873 |
if ( toolbar ) { |
|
874 |
toolbar.reposition(); |
|
875 |
} |
|
876 |
|
|
877 |
editor.fire( 'ExecCommand', { |
|
878 |
command: cmd, |
|
879 |
ui: event.ui, |
|
880 |
value: event.value |
|
881 |
} ); |
|
882 |
} |
|
883 |
}); |
|
884 |
|
|
885 |
editor.on( 'keydown', function( event ) { |
|
886 |
var node, wrap, P, spacer, |
|
887 |
selection = editor.selection, |
|
888 |
keyCode = event.keyCode, |
|
889 |
dom = editor.dom, |
|
890 |
VK = tinymce.util.VK; |
|
891 |
|
|
892 |
if ( keyCode === VK.ENTER ) { |
|
893 |
// When pressing Enter inside a caption move the caret to a new parapraph under it |
|
894 |
node = selection.getNode(); |
|
895 |
wrap = dom.getParent( node, 'div.mceTemp' ); |
|
896 |
|
|
897 |
if ( wrap ) { |
|
898 |
dom.events.cancel( event ); // Doesn't cancel all :( |
|
899 |
|
|
900 |
// Remove any extra dt and dd cleated on pressing Enter... |
|
901 |
tinymce.each( dom.select( 'dt, dd', wrap ), function( element ) { |
|
902 |
if ( dom.isEmpty( element ) ) { |
|
903 |
dom.remove( element ); |
|
904 |
} |
|
905 |
}); |
|
906 |
|
|
907 |
spacer = tinymce.Env.ie && tinymce.Env.ie < 11 ? '' : '<br data-mce-bogus="1" />'; |
|
908 |
P = dom.create( 'p', null, spacer ); |
|
909 |
|
|
910 |
if ( node.nodeName === 'DD' ) { |
|
911 |
dom.insertAfter( P, wrap ); |
|
912 |
} else { |
|
913 |
wrap.parentNode.insertBefore( P, wrap ); |
|
914 |
} |
|
915 |
|
|
916 |
editor.nodeChanged(); |
|
917 |
selection.setCursorLocation( P, 0 ); |
|
918 |
} |
|
919 |
} else if ( keyCode === VK.DELETE || keyCode === VK.BACKSPACE ) { |
|
920 |
node = selection.getNode(); |
|
921 |
|
|
922 |
if ( node.nodeName === 'DIV' && dom.hasClass( node, 'mceTemp' ) ) { |
|
923 |
wrap = node; |
|
924 |
} else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) { |
|
925 |
wrap = dom.getParent( node, 'div.mceTemp' ); |
|
926 |
} |
|
927 |
|
|
928 |
if ( wrap ) { |
|
929 |
dom.events.cancel( event ); |
|
930 |
removeImage( node ); |
|
931 |
return false; |
|
932 |
} |
|
933 |
} |
|
934 |
}); |
|
935 |
|
|
936 |
// After undo/redo FF seems to set the image height very slowly when it is set to 'auto' in the CSS. |
|
937 |
// This causes image.getBoundingClientRect() to return wrong values and the resize handles are shown in wrong places. |
|
938 |
// Collapse the selection to remove the resize handles. |
|
939 |
if ( tinymce.Env.gecko ) { |
|
940 |
editor.on( 'undo redo', function() { |
|
941 |
if ( editor.selection.getNode().nodeName === 'IMG' ) { |
|
942 |
editor.selection.collapse(); |
|
943 |
} |
|
944 |
}); |
|
945 |
} |
|
946 |
|
|
947 |
editor.wpSetImgCaption = function( content ) { |
|
948 |
return parseShortcode( content ); |
|
949 |
}; |
|
950 |
|
|
951 |
editor.wpGetImgCaption = function( content ) { |
|
952 |
return getShortcode( content ); |
|
953 |
}; |
|
954 |
|
|
955 |
editor.on( 'BeforeSetContent', function( event ) { |
|
956 |
if ( event.format !== 'raw' ) { |
|
957 |
event.content = editor.wpSetImgCaption( event.content ); |
|
958 |
} |
|
959 |
}); |
|
960 |
|
|
961 |
editor.on( 'PostProcess', function( event ) { |
|
962 |
if ( event.get ) { |
|
963 |
event.content = editor.wpGetImgCaption( event.content ); |
|
964 |
} |
|
965 |
}); |
|
966 |
|
|
967 |
// Add to editor.wp |
|
968 |
editor.wp = editor.wp || {}; |
|
969 |
editor.wp.isPlaceholder = isPlaceholder; |
|
970 |
|
|
971 |
// Back-compat. |
|
972 |
return { |
|
973 |
_do_shcode: parseShortcode, |
|
974 |
_get_shcode: getShortcode |
|
975 |
}; |
|
976 |
}); |