author | ymh <ymh.work@gmail.com> |
Fri, 05 Sep 2025 18:52:52 +0200 | |
changeset 22 | 8c2e4d02f4ef |
parent 21 | 48c4eec2b7e6 |
permissions | -rw-r--r-- |
9 | 1 |
/** |
2 |
* @output wp-includes/js/mce-view.js |
|
3 |
*/ |
|
4 |
||
5 | 5 |
/* global tinymce */ |
6 |
||
7 |
/* |
|
8 |
* The TinyMCE view API. |
|
9 |
* |
|
10 |
* Note: this API is "experimental" meaning that it will probably change |
|
11 |
* in the next few releases based on feedback from 3.9.0. |
|
12 |
* If you decide to use it, please follow the development closely. |
|
13 |
* |
|
14 |
* Diagram |
|
15 |
* |
|
16 |
* |- registered view constructor (type) |
|
17 |
* | |- view instance (unique text) |
|
18 |
* | | |- editor 1 |
|
19 |
* | | | |- view node |
|
20 |
* | | | |- view node |
|
21 |
* | | | |- ... |
|
22 |
* | | |- editor 2 |
|
23 |
* | | | |- ... |
|
24 |
* | |- view instance |
|
25 |
* | | |- ... |
|
26 |
* |- registered view |
|
27 |
* | |- ... |
|
28 |
*/ |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
29 |
( function( window, wp, shortcode, $ ) { |
5 | 30 |
'use strict'; |
31 |
||
0 | 32 |
var views = {}, |
33 |
instances = {}; |
|
34 |
||
35 |
wp.mce = wp.mce || {}; |
|
36 |
||
5 | 37 |
/** |
38 |
* wp.mce.views |
|
39 |
* |
|
40 |
* A set of utilities that simplifies adding custom UI within a TinyMCE editor. |
|
41 |
* At its core, it serves as a series of converters, transforming text to a |
|
42 |
* custom UI, and back again. |
|
43 |
*/ |
|
44 |
wp.mce.views = { |
|
0 | 45 |
|
5 | 46 |
/** |
47 |
* Registers a new view type. |
|
48 |
* |
|
16 | 49 |
* @param {string} type The view type. |
5 | 50 |
* @param {Object} extend An object to extend wp.mce.View.prototype with. |
51 |
*/ |
|
52 |
register: function( type, extend ) { |
|
53 |
views[ type ] = wp.mce.View.extend( _.extend( extend, { type: type } ) ); |
|
54 |
}, |
|
0 | 55 |
|
5 | 56 |
/** |
57 |
* Unregisters a view type. |
|
58 |
* |
|
16 | 59 |
* @param {string} type The view type. |
5 | 60 |
*/ |
61 |
unregister: function( type ) { |
|
62 |
delete views[ type ]; |
|
0 | 63 |
}, |
64 |
||
5 | 65 |
/** |
66 |
* Returns the settings of a view type. |
|
67 |
* |
|
16 | 68 |
* @param {string} type The view type. |
5 | 69 |
* |
70 |
* @return {Function} The view constructor. |
|
71 |
*/ |
|
72 |
get: function( type ) { |
|
73 |
return views[ type ]; |
|
0 | 74 |
}, |
75 |
||
5 | 76 |
/** |
77 |
* Unbinds all view nodes. |
|
78 |
* Runs before removing all view nodes from the DOM. |
|
79 |
*/ |
|
80 |
unbind: function() { |
|
81 |
_.each( instances, function( instance ) { |
|
82 |
instance.unbind(); |
|
83 |
} ); |
|
0 | 84 |
}, |
85 |
||
5 | 86 |
/** |
87 |
* Scans a given string for each view's pattern, |
|
88 |
* replacing any matches with markers, |
|
89 |
* and creates a new instance for every match. |
|
90 |
* |
|
16 | 91 |
* @param {string} content The string to scan. |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
92 |
* @param {tinymce.Editor} editor The editor. |
5 | 93 |
* |
16 | 94 |
* @return {string} The string with markers. |
5 | 95 |
*/ |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
96 |
setMarkers: function( content, editor ) { |
0 | 97 |
var pieces = [ { content: content } ], |
5 | 98 |
self = this, |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
99 |
instance, current; |
0 | 100 |
|
5 | 101 |
_.each( views, function( view, type ) { |
0 | 102 |
current = pieces.slice(); |
103 |
pieces = []; |
|
104 |
||
105 |
_.each( current, function( piece ) { |
|
106 |
var remaining = piece.content, |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
107 |
result, text; |
0 | 108 |
|
109 |
// Ignore processed pieces, but retain their location. |
|
110 |
if ( piece.processed ) { |
|
111 |
pieces.push( piece ); |
|
112 |
return; |
|
113 |
} |
|
114 |
||
115 |
// Iterate through the string progressively matching views |
|
116 |
// and slicing the string as we go. |
|
5 | 117 |
while ( remaining && ( result = view.prototype.match( remaining ) ) ) { |
0 | 118 |
// Any text before the match becomes an unprocessed piece. |
5 | 119 |
if ( result.index ) { |
120 |
pieces.push( { content: remaining.substring( 0, result.index ) } ); |
|
121 |
} |
|
122 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
123 |
result.options.editor = editor; |
5 | 124 |
instance = self.createInstance( type, result.content, result.options ); |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
125 |
text = instance.loader ? '.' : instance.text; |
0 | 126 |
|
127 |
// Add the processed piece for the match. |
|
5 | 128 |
pieces.push( { |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
129 |
content: instance.ignore ? text : '<p data-wpview-marker="' + instance.encodedText + '">' + text + '</p>', |
0 | 130 |
processed: true |
5 | 131 |
} ); |
0 | 132 |
|
133 |
// Update the remaining content. |
|
134 |
remaining = remaining.slice( result.index + result.content.length ); |
|
135 |
} |
|
136 |
||
5 | 137 |
// There are no additional matches. |
138 |
// If any content remains, add it as an unprocessed piece. |
|
139 |
if ( remaining ) { |
|
140 |
pieces.push( { content: remaining } ); |
|
141 |
} |
|
142 |
} ); |
|
143 |
} ); |
|
0 | 144 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
145 |
content = _.pluck( pieces, 'content' ).join( '' ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
146 |
return content.replace( /<p>\s*<p data-wpview-marker=/g, '<p data-wpview-marker=' ).replace( /<\/p>\s*<\/p>/g, '</p>' ); |
0 | 147 |
}, |
148 |
||
5 | 149 |
/** |
150 |
* Create a view instance. |
|
151 |
* |
|
16 | 152 |
* @param {string} type The view type. |
153 |
* @param {string} text The textual representation of the view. |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
154 |
* @param {Object} options Options. |
16 | 155 |
* @param {boolean} force Recreate the instance. Optional. |
5 | 156 |
* |
157 |
* @return {wp.mce.View} The view instance. |
|
158 |
*/ |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
159 |
createInstance: function( type, text, options, force ) { |
5 | 160 |
var View = this.get( type ), |
161 |
encodedText, |
|
162 |
instance; |
|
0 | 163 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
164 |
if ( text.indexOf( '[' ) !== -1 && text.indexOf( ']' ) !== -1 ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
165 |
// Looks like a shortcode? Remove any line breaks from inside of shortcodes |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
166 |
// or autop will replace them with <p> and <br> later and the string won't match. |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
167 |
text = text.replace( /\[[^\]]+\]/g, function( match ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
168 |
return match.replace( /[\r\n]/g, '' ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
169 |
}); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
170 |
} |
0 | 171 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
172 |
if ( ! force ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
173 |
instance = this.getInstance( text ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
174 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
175 |
if ( instance ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
176 |
return instance; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
177 |
} |
5 | 178 |
} |
0 | 179 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
180 |
encodedText = encodeURIComponent( text ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
181 |
|
5 | 182 |
options = _.extend( options || {}, { |
183 |
text: text, |
|
184 |
encodedText: encodedText |
|
185 |
} ); |
|
0 | 186 |
|
5 | 187 |
return instances[ encodedText ] = new View( options ); |
188 |
}, |
|
0 | 189 |
|
5 | 190 |
/** |
191 |
* Get a view instance. |
|
192 |
* |
|
16 | 193 |
* @param {(string|HTMLElement)} object The textual representation of the view or the view node. |
5 | 194 |
* |
195 |
* @return {wp.mce.View} The view instance or undefined. |
|
196 |
*/ |
|
197 |
getInstance: function( object ) { |
|
198 |
if ( typeof object === 'string' ) { |
|
199 |
return instances[ encodeURIComponent( object ) ]; |
|
200 |
} |
|
0 | 201 |
|
5 | 202 |
return instances[ $( object ).attr( 'data-wpview-text' ) ]; |
0 | 203 |
}, |
204 |
||
5 | 205 |
/** |
206 |
* Given a view node, get the view's text. |
|
207 |
* |
|
208 |
* @param {HTMLElement} node The view node. |
|
209 |
* |
|
16 | 210 |
* @return {string} The textual representation of the view. |
5 | 211 |
*/ |
212 |
getText: function( node ) { |
|
213 |
return decodeURIComponent( $( node ).attr( 'data-wpview-text' ) || '' ); |
|
0 | 214 |
}, |
215 |
||
5 | 216 |
/** |
217 |
* Renders all view nodes that are not yet rendered. |
|
218 |
* |
|
16 | 219 |
* @param {boolean} force Rerender all view nodes. |
5 | 220 |
*/ |
221 |
render: function( force ) { |
|
222 |
_.each( instances, function( instance ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
223 |
instance.render( null, force ); |
5 | 224 |
} ); |
0 | 225 |
}, |
226 |
||
5 | 227 |
/** |
228 |
* Update the text of a given view node. |
|
229 |
* |
|
16 | 230 |
* @param {string} text The new text. |
5 | 231 |
* @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in. |
232 |
* @param {HTMLElement} node The view node to update. |
|
16 | 233 |
* @param {boolean} force Recreate the instance. Optional. |
5 | 234 |
*/ |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
235 |
update: function( text, editor, node, force ) { |
5 | 236 |
var instance = this.getInstance( node ); |
0 | 237 |
|
5 | 238 |
if ( instance ) { |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
239 |
instance.update( text, editor, node, force ); |
5 | 240 |
} |
0 | 241 |
}, |
242 |
||
5 | 243 |
/** |
244 |
* Renders any editing interface based on the view type. |
|
245 |
* |
|
246 |
* @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in. |
|
247 |
* @param {HTMLElement} node The view node to edit. |
|
248 |
*/ |
|
249 |
edit: function( editor, node ) { |
|
250 |
var instance = this.getInstance( node ); |
|
0 | 251 |
|
5 | 252 |
if ( instance && instance.edit ) { |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
253 |
instance.edit( instance.text, function( text, force ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
254 |
instance.update( text, editor, node, force ); |
5 | 255 |
} ); |
256 |
} |
|
0 | 257 |
}, |
258 |
||
5 | 259 |
/** |
260 |
* Remove a given view node from the DOM. |
|
261 |
* |
|
262 |
* @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in. |
|
263 |
* @param {HTMLElement} node The view node to remove. |
|
264 |
*/ |
|
265 |
remove: function( editor, node ) { |
|
266 |
var instance = this.getInstance( node ); |
|
0 | 267 |
|
5 | 268 |
if ( instance ) { |
269 |
instance.remove( editor, node ); |
|
270 |
} |
|
0 | 271 |
} |
272 |
}; |
|
273 |
||
5 | 274 |
/** |
275 |
* A Backbone-like View constructor intended for use when rendering a TinyMCE View. |
|
276 |
* The main difference is that the TinyMCE View is not tied to a particular DOM node. |
|
277 |
* |
|
278 |
* @param {Object} options Options. |
|
279 |
*/ |
|
280 |
wp.mce.View = function( options ) { |
|
281 |
_.extend( this, options ); |
|
282 |
this.initialize(); |
|
283 |
}; |
|
284 |
||
285 |
wp.mce.View.extend = Backbone.View.extend; |
|
286 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
287 |
_.extend( wp.mce.View.prototype, /** @lends wp.mce.View.prototype */{ |
5 | 288 |
|
289 |
/** |
|
290 |
* The content. |
|
291 |
* |
|
292 |
* @type {*} |
|
293 |
*/ |
|
294 |
content: null, |
|
295 |
||
296 |
/** |
|
297 |
* Whether or not to display a loader. |
|
298 |
* |
|
299 |
* @type {Boolean} |
|
300 |
*/ |
|
301 |
loader: true, |
|
302 |
||
303 |
/** |
|
304 |
* Runs after the view instance is created. |
|
305 |
*/ |
|
306 |
initialize: function() {}, |
|
307 |
||
308 |
/** |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
309 |
* Returns the content to render in the view node. |
5 | 310 |
* |
311 |
* @return {*} |
|
312 |
*/ |
|
313 |
getContent: function() { |
|
314 |
return this.content; |
|
315 |
}, |
|
316 |
||
317 |
/** |
|
318 |
* Renders all view nodes tied to this view instance that are not yet rendered. |
|
319 |
* |
|
16 | 320 |
* @param {string} content The content to render. Optional. |
321 |
* @param {boolean} force Rerender all view nodes tied to this view instance. Optional. |
|
5 | 322 |
*/ |
323 |
render: function( content, force ) { |
|
324 |
if ( content != null ) { |
|
325 |
this.content = content; |
|
326 |
} |
|
327 |
||
328 |
content = this.getContent(); |
|
329 |
||
330 |
// If there's nothing to render an no loader needs to be shown, stop. |
|
331 |
if ( ! this.loader && ! content ) { |
|
332 |
return; |
|
333 |
} |
|
334 |
||
335 |
// We're about to rerender all views of this instance, so unbind rendered views. |
|
336 |
force && this.unbind(); |
|
337 |
||
338 |
// Replace any left over markers. |
|
339 |
this.replaceMarkers(); |
|
340 |
||
341 |
if ( content ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
342 |
this.setContent( content, function( editor, node ) { |
5 | 343 |
$( node ).data( 'rendered', true ); |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
344 |
this.bindNode.call( this, editor, node ); |
5 | 345 |
}, force ? null : false ); |
346 |
} else { |
|
347 |
this.setLoader(); |
|
348 |
} |
|
349 |
}, |
|
350 |
||
351 |
/** |
|
352 |
* Binds a given node after its content is added to the DOM. |
|
353 |
*/ |
|
354 |
bindNode: function() {}, |
|
355 |
||
356 |
/** |
|
357 |
* Unbinds a given node before its content is removed from the DOM. |
|
358 |
*/ |
|
359 |
unbindNode: function() {}, |
|
360 |
||
361 |
/** |
|
362 |
* Unbinds all view nodes tied to this view instance. |
|
363 |
* Runs before their content is removed from the DOM. |
|
364 |
*/ |
|
365 |
unbind: function() { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
366 |
this.getNodes( function( editor, node ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
367 |
this.unbindNode.call( this, editor, node ); |
5 | 368 |
}, true ); |
369 |
}, |
|
370 |
||
371 |
/** |
|
372 |
* Gets all the TinyMCE editor instances that support views. |
|
373 |
* |
|
374 |
* @param {Function} callback A callback. |
|
375 |
*/ |
|
376 |
getEditors: function( callback ) { |
|
377 |
_.each( tinymce.editors, function( editor ) { |
|
378 |
if ( editor.plugins.wpview ) { |
|
379 |
callback.call( this, editor ); |
|
380 |
} |
|
381 |
}, this ); |
|
382 |
}, |
|
383 |
||
384 |
/** |
|
385 |
* Gets all view nodes tied to this view instance. |
|
386 |
* |
|
387 |
* @param {Function} callback A callback. |
|
16 | 388 |
* @param {boolean} rendered Get (un)rendered view nodes. Optional. |
5 | 389 |
*/ |
390 |
getNodes: function( callback, rendered ) { |
|
391 |
this.getEditors( function( editor ) { |
|
392 |
var self = this; |
|
393 |
||
394 |
$( editor.getBody() ) |
|
395 |
.find( '[data-wpview-text="' + self.encodedText + '"]' ) |
|
396 |
.filter( function() { |
|
397 |
var data; |
|
398 |
||
399 |
if ( rendered == null ) { |
|
400 |
return true; |
|
401 |
} |
|
402 |
||
403 |
data = $( this ).data( 'rendered' ) === true; |
|
404 |
||
405 |
return rendered ? data : ! data; |
|
406 |
} ) |
|
407 |
.each( function() { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
408 |
callback.call( self, editor, this, this /* back compat */ ); |
5 | 409 |
} ); |
410 |
} ); |
|
411 |
}, |
|
412 |
||
413 |
/** |
|
414 |
* Gets all marker nodes tied to this view instance. |
|
415 |
* |
|
416 |
* @param {Function} callback A callback. |
|
417 |
*/ |
|
418 |
getMarkers: function( callback ) { |
|
419 |
this.getEditors( function( editor ) { |
|
420 |
var self = this; |
|
421 |
||
422 |
$( editor.getBody() ) |
|
423 |
.find( '[data-wpview-marker="' + this.encodedText + '"]' ) |
|
424 |
.each( function() { |
|
425 |
callback.call( self, editor, this ); |
|
426 |
} ); |
|
427 |
} ); |
|
428 |
}, |
|
429 |
||
430 |
/** |
|
431 |
* Replaces all marker nodes tied to this view instance. |
|
432 |
*/ |
|
433 |
replaceMarkers: function() { |
|
434 |
this.getMarkers( function( editor, node ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
435 |
var selected = node === editor.selection.getNode(); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
436 |
var $viewNode; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
437 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
438 |
if ( ! this.loader && $( node ).text() !== tinymce.DOM.decode( this.text ) ) { |
5 | 439 |
editor.dom.setAttrib( node, 'data-wpview-marker', null ); |
440 |
return; |
|
441 |
} |
|
442 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
443 |
$viewNode = editor.$( |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
444 |
'<div class="wpview wpview-wrap" data-wpview-text="' + this.encodedText + '" data-wpview-type="' + this.type + '" contenteditable="false"></div>' |
5 | 445 |
); |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
446 |
|
16 | 447 |
editor.undoManager.ignore( function() { |
448 |
editor.$( node ).replaceWith( $viewNode ); |
|
449 |
} ); |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
450 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
451 |
if ( selected ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
452 |
setTimeout( function() { |
16 | 453 |
editor.undoManager.ignore( function() { |
454 |
editor.selection.select( $viewNode[0] ); |
|
455 |
editor.selection.collapse(); |
|
456 |
} ); |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
457 |
} ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
458 |
} |
5 | 459 |
} ); |
460 |
}, |
|
461 |
||
462 |
/** |
|
463 |
* Removes all marker nodes tied to this view instance. |
|
464 |
*/ |
|
465 |
removeMarkers: function() { |
|
466 |
this.getMarkers( function( editor, node ) { |
|
467 |
editor.dom.setAttrib( node, 'data-wpview-marker', null ); |
|
468 |
} ); |
|
469 |
}, |
|
470 |
||
471 |
/** |
|
472 |
* Sets the content for all view nodes tied to this view instance. |
|
473 |
* |
|
474 |
* @param {*} content The content to set. |
|
475 |
* @param {Function} callback A callback. Optional. |
|
16 | 476 |
* @param {boolean} rendered Only set for (un)rendered nodes. Optional. |
5 | 477 |
*/ |
478 |
setContent: function( content, callback, rendered ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
479 |
if ( _.isObject( content ) && ( content.sandbox || content.head || content.body.indexOf( '<script' ) !== -1 ) ) { |
5 | 480 |
this.setIframes( content.head || '', content.body, callback, rendered ); |
481 |
} else if ( _.isString( content ) && content.indexOf( '<script' ) !== -1 ) { |
|
482 |
this.setIframes( '', content, callback, rendered ); |
|
483 |
} else { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
484 |
this.getNodes( function( editor, node ) { |
5 | 485 |
content = content.body || content; |
486 |
||
487 |
if ( content.indexOf( '<iframe' ) !== -1 ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
488 |
content += '<span class="mce-shim"></span>'; |
5 | 489 |
} |
490 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
491 |
editor.undoManager.transact( function() { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
492 |
node.innerHTML = ''; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
493 |
node.appendChild( _.isString( content ) ? editor.dom.createFragment( content ) : content ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
494 |
editor.dom.add( node, 'span', { 'class': 'wpview-end' } ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
495 |
} ); |
5 | 496 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
497 |
callback && callback.call( this, editor, node ); |
5 | 498 |
}, rendered ); |
499 |
} |
|
500 |
}, |
|
501 |
||
502 |
/** |
|
503 |
* Sets the content in an iframe for all view nodes tied to this view instance. |
|
504 |
* |
|
16 | 505 |
* @param {string} head HTML string to be added to the head of the document. |
506 |
* @param {string} body HTML string to be added to the body of the document. |
|
5 | 507 |
* @param {Function} callback A callback. Optional. |
16 | 508 |
* @param {boolean} rendered Only set for (un)rendered nodes. Optional. |
5 | 509 |
*/ |
510 |
setIframes: function( head, body, callback, rendered ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
511 |
var self = this; |
5 | 512 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
513 |
if ( body.indexOf( '[' ) !== -1 && body.indexOf( ']' ) !== -1 ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
514 |
var shortcodesRegExp = new RegExp( '\\[\\/?(?:' + window.mceViewL10n.shortcodes.join( '|' ) + ')[^\\]]*?\\]', 'g' ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
515 |
// Escape tags inside shortcode previews. |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
516 |
body = body.replace( shortcodesRegExp, function( match ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
517 |
return match.replace( /</g, '<' ).replace( />/g, '>' ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
518 |
} ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
519 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
520 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
521 |
this.getNodes( function( editor, node ) { |
5 | 522 |
var dom = editor.dom, |
523 |
styles = '', |
|
524 |
bodyClasses = editor.getBody().className || '', |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
525 |
editorHead = editor.getDoc().getElementsByTagName( 'head' )[0], |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
526 |
iframe, iframeWin, iframeDoc, MutationObserver, observer, i, block; |
5 | 527 |
|
528 |
tinymce.each( dom.$( 'link[rel="stylesheet"]', editorHead ), function( link ) { |
|
529 |
if ( link.href && link.href.indexOf( 'skins/lightgray/content.min.css' ) === -1 && |
|
530 |
link.href.indexOf( 'skins/wordpress/wp-content.css' ) === -1 ) { |
|
531 |
||
532 |
styles += dom.getOuterHTML( link ); |
|
533 |
} |
|
534 |
} ); |
|
535 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
536 |
if ( self.iframeHeight ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
537 |
dom.add( node, 'span', { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
538 |
'data-mce-bogus': 1, |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
539 |
style: { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
540 |
display: 'block', |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
541 |
width: '100%', |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
542 |
height: self.iframeHeight |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
543 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
544 |
}, '\u200B' ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
545 |
} |
5 | 546 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
547 |
editor.undoManager.transact( function() { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
548 |
node.innerHTML = ''; |
5 | 549 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
550 |
iframe = dom.add( node, 'iframe', { |
5 | 551 |
/* jshint scripturl: true */ |
552 |
src: tinymce.Env.ie ? 'javascript:""' : '', |
|
553 |
frameBorder: '0', |
|
554 |
allowTransparency: 'true', |
|
555 |
scrolling: 'no', |
|
556 |
'class': 'wpview-sandbox', |
|
557 |
style: { |
|
558 |
width: '100%', |
|
559 |
display: 'block' |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
560 |
}, |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
561 |
height: self.iframeHeight |
5 | 562 |
} ); |
563 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
564 |
dom.add( node, 'span', { 'class': 'mce-shim' } ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
565 |
dom.add( node, 'span', { 'class': 'wpview-end' } ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
566 |
} ); |
5 | 567 |
|
16 | 568 |
/* |
569 |
* Bail if the iframe node is not attached to the DOM. |
|
570 |
* Happens when the view is dragged in the editor. |
|
571 |
* There is a browser restriction when iframes are moved in the DOM. They get emptied. |
|
572 |
* The iframe will be rerendered after dropping the view node at the new location. |
|
573 |
*/ |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
574 |
if ( ! iframe.contentWindow ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
575 |
return; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
576 |
} |
5 | 577 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
578 |
iframeWin = iframe.contentWindow; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
579 |
iframeDoc = iframeWin.document; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
580 |
iframeDoc.open(); |
5 | 581 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
582 |
iframeDoc.write( |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
583 |
'<!DOCTYPE html>' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
584 |
'<html>' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
585 |
'<head>' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
586 |
'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
587 |
head + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
588 |
styles + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
589 |
'<style>' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
590 |
'html {' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
591 |
'background: transparent;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
592 |
'padding: 0;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
593 |
'margin: 0;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
594 |
'}' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
595 |
'body#wpview-iframe-sandbox {' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
596 |
'background: transparent;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
597 |
'padding: 1px 0 !important;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
598 |
'margin: -1px 0 0 !important;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
599 |
'}' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
600 |
'body#wpview-iframe-sandbox:before,' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
601 |
'body#wpview-iframe-sandbox:after {' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
602 |
'display: none;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
603 |
'content: "";' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
604 |
'}' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
605 |
'iframe {' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
606 |
'max-width: 100%;' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
607 |
'}' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
608 |
'</style>' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
609 |
'</head>' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
610 |
'<body id="wpview-iframe-sandbox" class="' + bodyClasses + '">' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
611 |
body + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
612 |
'</body>' + |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
613 |
'</html>' |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
614 |
); |
5 | 615 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
616 |
iframeDoc.close(); |
5 | 617 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
618 |
function resize() { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
619 |
var $iframe; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
620 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
621 |
if ( block ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
622 |
return; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
623 |
} |
5 | 624 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
625 |
// Make sure the iframe still exists. |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
626 |
if ( iframe.contentWindow ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
627 |
$iframe = $( iframe ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
628 |
self.iframeHeight = $( iframeDoc.body ).height(); |
5 | 629 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
630 |
if ( $iframe.height() !== self.iframeHeight ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
631 |
$iframe.height( self.iframeHeight ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
632 |
editor.nodeChanged(); |
5 | 633 |
} |
634 |
} |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
635 |
} |
5 | 636 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
637 |
if ( self.iframeHeight ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
638 |
block = true; |
5 | 639 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
640 |
setTimeout( function() { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
641 |
block = false; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
642 |
resize(); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
643 |
}, 3000 ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
644 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
645 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
646 |
function addObserver() { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
647 |
observer = new MutationObserver( _.debounce( resize, 100 ) ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
648 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
649 |
observer.observe( iframeDoc.body, { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
650 |
attributes: true, |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
651 |
childList: true, |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
652 |
subtree: true |
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 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
655 |
|
21
48c4eec2b7e6
Add CLAUDE.md documentation and sync WordPress core files
ymh <ymh.work@gmail.com>
parents:
16
diff
changeset
|
656 |
$( iframeWin ).on( 'load', resize ); |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
657 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
658 |
MutationObserver = iframeWin.MutationObserver || iframeWin.WebKitMutationObserver || iframeWin.MozMutationObserver; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
659 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
660 |
if ( MutationObserver ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
661 |
if ( ! iframeDoc.body ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
662 |
iframeDoc.addEventListener( 'DOMContentLoaded', addObserver, false ); |
5 | 663 |
} else { |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
664 |
addObserver(); |
5 | 665 |
} |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
666 |
} else { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
667 |
for ( i = 1; i < 6; i++ ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
668 |
setTimeout( resize, i * 700 ); |
5 | 669 |
} |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
670 |
} |
5 | 671 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
672 |
callback && callback.call( self, editor, node ); |
5 | 673 |
}, rendered ); |
674 |
}, |
|
675 |
||
676 |
/** |
|
677 |
* Sets a loader for all view nodes tied to this view instance. |
|
678 |
*/ |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
679 |
setLoader: function( dashicon ) { |
5 | 680 |
this.setContent( |
681 |
'<div class="loading-placeholder">' + |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
682 |
'<div class="dashicons dashicons-' + ( dashicon || 'admin-media' ) + '"></div>' + |
5 | 683 |
'<div class="wpview-loading"><ins></ins></div>' + |
684 |
'</div>' |
|
685 |
); |
|
686 |
}, |
|
687 |
||
688 |
/** |
|
689 |
* Sets an error for all view nodes tied to this view instance. |
|
690 |
* |
|
16 | 691 |
* @param {string} message The error message to set. |
692 |
* @param {string} dashicon A dashicon ID. Optional. {@link https://developer.wordpress.org/resource/dashicons/} |
|
5 | 693 |
*/ |
694 |
setError: function( message, dashicon ) { |
|
695 |
this.setContent( |
|
696 |
'<div class="wpview-error">' + |
|
697 |
'<div class="dashicons dashicons-' + ( dashicon || 'no' ) + '"></div>' + |
|
698 |
'<p>' + message + '</p>' + |
|
699 |
'</div>' |
|
700 |
); |
|
701 |
}, |
|
702 |
||
703 |
/** |
|
704 |
* Tries to find a text match in a given string. |
|
705 |
* |
|
16 | 706 |
* @param {string} content The string to scan. |
5 | 707 |
* |
708 |
* @return {Object} |
|
709 |
*/ |
|
710 |
match: function( content ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
711 |
var match = shortcode.next( this.type, content ); |
5 | 712 |
|
713 |
if ( match ) { |
|
714 |
return { |
|
715 |
index: match.index, |
|
716 |
content: match.content, |
|
717 |
options: { |
|
718 |
shortcode: match.shortcode |
|
719 |
} |
|
720 |
}; |
|
721 |
} |
|
722 |
}, |
|
723 |
||
724 |
/** |
|
725 |
* Update the text of a given view node. |
|
726 |
* |
|
16 | 727 |
* @param {string} text The new text. |
5 | 728 |
* @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in. |
729 |
* @param {HTMLElement} node The view node to update. |
|
16 | 730 |
* @param {boolean} force Recreate the instance. Optional. |
5 | 731 |
*/ |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
732 |
update: function( text, editor, node, force ) { |
5 | 733 |
_.find( views, function( view, type ) { |
734 |
var match = view.prototype.match( text ); |
|
735 |
||
736 |
if ( match ) { |
|
737 |
$( node ).data( 'rendered', false ); |
|
738 |
editor.dom.setAttrib( node, 'data-wpview-text', encodeURIComponent( text ) ); |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
739 |
wp.mce.views.createInstance( type, text, match.options, force ).render(); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
740 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
741 |
editor.selection.select( node ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
742 |
editor.nodeChanged(); |
5 | 743 |
editor.focus(); |
744 |
||
745 |
return true; |
|
746 |
} |
|
747 |
} ); |
|
748 |
}, |
|
749 |
||
750 |
/** |
|
751 |
* Remove a given view node from the DOM. |
|
752 |
* |
|
753 |
* @param {tinymce.Editor} editor The TinyMCE editor instance the view node is in. |
|
754 |
* @param {HTMLElement} node The view node to remove. |
|
755 |
*/ |
|
756 |
remove: function( editor, node ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
757 |
this.unbindNode.call( this, editor, node ); |
5 | 758 |
editor.dom.remove( node ); |
759 |
editor.focus(); |
|
760 |
} |
|
761 |
} ); |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
762 |
} )( window, window.wp, window.wp.shortcode, window.jQuery ); |
5 | 763 |
|
764 |
/* |
|
765 |
* The WordPress core TinyMCE views. |
|
766 |
* Views for the gallery, audio, video, playlist and embed shortcodes, |
|
767 |
* and a view for embeddable URLs. |
|
768 |
*/ |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
769 |
( function( window, views, media, $ ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
770 |
var base, gallery, av, embed, |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
771 |
schema, parser, serializer; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
772 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
773 |
function verifyHTML( string ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
774 |
var settings = {}; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
775 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
776 |
if ( ! window.tinymce ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
777 |
return string.replace( /<[^>]+>/g, '' ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
778 |
} |
5 | 779 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
780 |
if ( ! string || ( string.indexOf( '<' ) === -1 && string.indexOf( '>' ) === -1 ) ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
781 |
return string; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
782 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
783 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
784 |
schema = schema || new window.tinymce.html.Schema( settings ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
785 |
parser = parser || new window.tinymce.html.DomParser( settings, schema ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
786 |
serializer = serializer || new window.tinymce.html.Serializer( settings, schema ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
787 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
788 |
return serializer.serialize( parser.parse( string, { forced_root_block: false } ) ); |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
789 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
790 |
|
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
791 |
base = { |
5 | 792 |
state: [], |
793 |
||
794 |
edit: function( text, update ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
795 |
var type = this.type, |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
796 |
frame = media[ type ].edit( text ); |
5 | 797 |
|
798 |
this.pausePlayers && this.pausePlayers(); |
|
799 |
||
800 |
_.each( this.state, function( state ) { |
|
801 |
frame.state( state ).on( 'update', function( selection ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
802 |
update( media[ type ].shortcode( selection ).string(), type === 'gallery' ); |
5 | 803 |
} ); |
804 |
} ); |
|
805 |
||
806 |
frame.on( 'close', function() { |
|
807 |
frame.detach(); |
|
808 |
} ); |
|
809 |
||
810 |
frame.open(); |
|
811 |
} |
|
812 |
}; |
|
813 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
814 |
gallery = _.extend( {}, base, { |
5 | 815 |
state: [ 'gallery-edit' ], |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
816 |
template: media.template( 'editor-gallery' ), |
5 | 817 |
|
818 |
initialize: function() { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
819 |
var attachments = media.gallery.attachments( this.shortcode, media.view.settings.post.id ), |
5 | 820 |
attrs = this.shortcode.attrs.named, |
821 |
self = this; |
|
822 |
||
823 |
attachments.more() |
|
824 |
.done( function() { |
|
825 |
attachments = attachments.toJSON(); |
|
826 |
||
827 |
_.each( attachments, function( attachment ) { |
|
828 |
if ( attachment.sizes ) { |
|
829 |
if ( attrs.size && attachment.sizes[ attrs.size ] ) { |
|
830 |
attachment.thumbnail = attachment.sizes[ attrs.size ]; |
|
831 |
} else if ( attachment.sizes.thumbnail ) { |
|
832 |
attachment.thumbnail = attachment.sizes.thumbnail; |
|
833 |
} else if ( attachment.sizes.full ) { |
|
834 |
attachment.thumbnail = attachment.sizes.full; |
|
835 |
} |
|
836 |
} |
|
837 |
} ); |
|
838 |
||
839 |
self.render( self.template( { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
840 |
verifyHTML: verifyHTML, |
5 | 841 |
attachments: attachments, |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
842 |
columns: attrs.columns ? parseInt( attrs.columns, 10 ) : media.galleryDefaults.columns |
5 | 843 |
} ) ); |
844 |
} ) |
|
845 |
.fail( function( jqXHR, textStatus ) { |
|
846 |
self.setError( textStatus ); |
|
847 |
} ); |
|
848 |
} |
|
849 |
} ); |
|
850 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
851 |
av = _.extend( {}, base, { |
5 | 852 |
action: 'parse-media-shortcode', |
853 |
||
854 |
initialize: function() { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
855 |
var self = this, maxwidth = null; |
5 | 856 |
|
857 |
if ( this.url ) { |
|
858 |
this.loader = false; |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
859 |
this.shortcode = media.embed.shortcode( { |
5 | 860 |
url: this.text |
861 |
} ); |
|
862 |
} |
|
863 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
864 |
// Obtain the target width for the embed. |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
865 |
if ( self.editor ) { |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
866 |
maxwidth = self.editor.getBody().clientWidth; |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
867 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
868 |
|
5 | 869 |
wp.ajax.post( this.action, { |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
870 |
post_ID: media.view.settings.post.id, |
5 | 871 |
type: this.shortcode.tag, |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
872 |
shortcode: this.shortcode.string(), |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
873 |
maxwidth: maxwidth |
5 | 874 |
} ) |
875 |
.done( function( response ) { |
|
876 |
self.render( response ); |
|
877 |
} ) |
|
878 |
.fail( function( response ) { |
|
879 |
if ( self.url ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
880 |
self.ignore = true; |
5 | 881 |
self.removeMarkers(); |
882 |
} else { |
|
883 |
self.setError( response.message || response.statusText, 'admin-media' ); |
|
884 |
} |
|
885 |
} ); |
|
886 |
||
887 |
this.getEditors( function( editor ) { |
|
888 |
editor.on( 'wpview-selected', function() { |
|
889 |
self.pausePlayers(); |
|
890 |
} ); |
|
891 |
} ); |
|
892 |
}, |
|
893 |
||
894 |
pausePlayers: function() { |
|
895 |
this.getNodes( function( editor, node, content ) { |
|
896 |
var win = $( 'iframe.wpview-sandbox', content ).get( 0 ); |
|
897 |
||
898 |
if ( win && ( win = win.contentWindow ) && win.mejs ) { |
|
899 |
_.each( win.mejs.players, function( player ) { |
|
900 |
try { |
|
901 |
player.pause(); |
|
902 |
} catch ( e ) {} |
|
903 |
} ); |
|
904 |
} |
|
905 |
} ); |
|
906 |
} |
|
907 |
} ); |
|
908 |
||
909 |
embed = _.extend( {}, av, { |
|
910 |
action: 'parse-embed', |
|
911 |
||
912 |
edit: function( text, update ) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
913 |
var frame = media.embed.edit( text, this.url ), |
5 | 914 |
self = this; |
915 |
||
916 |
this.pausePlayers(); |
|
917 |
||
918 |
frame.state( 'embed' ).props.on( 'change:url', function( model, url ) { |
|
919 |
if ( url && model.get( 'url' ) ) { |
|
920 |
frame.state( 'embed' ).metadata = model.toJSON(); |
|
921 |
} |
|
922 |
} ); |
|
923 |
||
924 |
frame.state( 'embed' ).on( 'select', function() { |
|
925 |
var data = frame.state( 'embed' ).metadata; |
|
926 |
||
927 |
if ( self.url ) { |
|
928 |
update( data.url ); |
|
929 |
} else { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
930 |
update( media.embed.shortcode( data ).string() ); |
5 | 931 |
} |
932 |
} ); |
|
933 |
||
934 |
frame.on( 'close', function() { |
|
935 |
frame.detach(); |
|
936 |
} ); |
|
937 |
||
938 |
frame.open(); |
|
939 |
} |
|
940 |
} ); |
|
941 |
||
942 |
views.register( 'gallery', _.extend( {}, gallery ) ); |
|
943 |
||
944 |
views.register( 'audio', _.extend( {}, av, { |
|
945 |
state: [ 'audio-details' ] |
|
946 |
} ) ); |
|
947 |
||
948 |
views.register( 'video', _.extend( {}, av, { |
|
949 |
state: [ 'video-details' ] |
|
950 |
} ) ); |
|
951 |
||
952 |
views.register( 'playlist', _.extend( {}, av, { |
|
953 |
state: [ 'playlist-edit', 'video-playlist-edit' ] |
|
954 |
} ) ); |
|
955 |
||
956 |
views.register( 'embed', _.extend( {}, embed ) ); |
|
957 |
||
958 |
views.register( 'embedURL', _.extend( {}, embed, { |
|
959 |
match: function( content ) { |
|
16 | 960 |
// There may be a "bookmark" node next to the URL... |
961 |
var re = /(^|<p>(?:<span data-mce-type="bookmark"[^>]+>\s*<\/span>)?)(https?:\/\/[^\s"]+?)((?:<span data-mce-type="bookmark"[^>]+>\s*<\/span>)?<\/p>\s*|$)/gi; |
|
962 |
var match = re.exec( content ); |
|
5 | 963 |
|
964 |
if ( match ) { |
|
965 |
return { |
|
966 |
index: match.index + match[1].length, |
|
967 |
content: match[2], |
|
968 |
options: { |
|
969 |
url: true |
|
970 |
} |
|
971 |
}; |
|
972 |
} |
|
973 |
} |
|
974 |
} ) ); |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
975 |
} )( window, window.wp.mce.views, window.wp.media, window.jQuery ); |