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