author | ymh <ymh.work@gmail.com> |
Fri, 05 Sep 2025 18:40:08 +0200 | |
changeset 21 | 48c4eec2b7e6 |
parent 16 | a86126ab1dd4 |
permissions | -rw-r--r-- |
9 | 1 |
/** |
2 |
* @output wp-includes/js/wp-backbone.js |
|
3 |
*/ |
|
4 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
5 |
/** @namespace wp */ |
0 | 6 |
window.wp = window.wp || {}; |
7 |
||
8 |
(function ($) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
9 |
/** |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
10 |
* Create the WordPress Backbone namespace. |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
11 |
* |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
12 |
* @namespace wp.Backbone |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
13 |
*/ |
0 | 14 |
wp.Backbone = {}; |
15 |
||
9 | 16 |
/** |
17 |
* A backbone subview manager. |
|
18 |
* |
|
19 |
* @since 3.5.0 |
|
20 |
* @since 3.6.0 Moved wp.media.Views to wp.Backbone.Subviews. |
|
21 |
* |
|
22 |
* @memberOf wp.Backbone |
|
23 |
* |
|
24 |
* @class |
|
25 |
* |
|
16 | 26 |
* @param {wp.Backbone.View} view The main view. |
27 |
* @param {Array|Object} views The subviews for the main view. |
|
9 | 28 |
*/ |
0 | 29 |
wp.Backbone.Subviews = function( view, views ) { |
30 |
this.view = view; |
|
31 |
this._views = _.isArray( views ) ? { '': views } : views || {}; |
|
32 |
}; |
|
33 |
||
34 |
wp.Backbone.Subviews.extend = Backbone.Model.extend; |
|
35 |
||
36 |
_.extend( wp.Backbone.Subviews.prototype, { |
|
9 | 37 |
/** |
38 |
* Fetches all of the subviews. |
|
39 |
* |
|
40 |
* @since 3.5.0 |
|
41 |
* |
|
42 |
* @return {Array} All the subviews. |
|
43 |
*/ |
|
0 | 44 |
all: function() { |
9 | 45 |
return _.flatten( _.values( this._views ) ); |
0 | 46 |
}, |
47 |
||
9 | 48 |
/** |
49 |
* Fetches all subviews that match a given `selector`. |
|
50 |
* |
|
51 |
* If no `selector` is provided, it will grab all subviews attached |
|
52 |
* to the view's root. |
|
53 |
* |
|
54 |
* @since 3.5.0 |
|
55 |
* |
|
56 |
* @param {string} selector A jQuery selector. |
|
57 |
* |
|
58 |
* @return {Array} All the subviews that match the selector. |
|
59 |
*/ |
|
0 | 60 |
get: function( selector ) { |
61 |
selector = selector || ''; |
|
62 |
return this._views[ selector ]; |
|
63 |
}, |
|
64 |
||
9 | 65 |
/** |
66 |
* Fetches the first subview that matches a given `selector`. |
|
67 |
* |
|
68 |
* If no `selector` is provided, it will grab the first subview attached to the |
|
69 |
* view's root. |
|
70 |
* |
|
71 |
* Useful when a selector only has one subview at a time. |
|
72 |
* |
|
73 |
* @since 3.5.0 |
|
74 |
* |
|
75 |
* @param {string} selector A jQuery selector. |
|
76 |
* |
|
77 |
* @return {Backbone.View} The view. |
|
78 |
*/ |
|
0 | 79 |
first: function( selector ) { |
80 |
var views = this.get( selector ); |
|
81 |
return views && views.length ? views[0] : null; |
|
82 |
}, |
|
83 |
||
9 | 84 |
/** |
85 |
* Registers subview(s). |
|
86 |
* |
|
87 |
* Registers any number of `views` to a `selector`. |
|
88 |
* |
|
89 |
* When no `selector` is provided, the root selector (the empty string) |
|
90 |
* is used. `views` accepts a `Backbone.View` instance or an array of |
|
91 |
* `Backbone.View` instances. |
|
92 |
* |
|
93 |
* --- |
|
94 |
* |
|
95 |
* Accepts an `options` object, which has a significant effect on the |
|
96 |
* resulting behavior. |
|
97 |
* |
|
98 |
* `options.silent` - *boolean, `false`* |
|
99 |
* If `options.silent` is true, no DOM modifications will be made. |
|
100 |
* |
|
101 |
* `options.add` - *boolean, `false`* |
|
102 |
* Use `Views.add()` as a shortcut for setting `options.add` to true. |
|
103 |
* |
|
104 |
* By default, the provided `views` will replace any existing views |
|
105 |
* associated with the selector. If `options.add` is true, the provided |
|
106 |
* `views` will be added to the existing views. |
|
107 |
* |
|
108 |
* `options.at` - *integer, `undefined`* |
|
109 |
* When adding, to insert `views` at a specific index, use `options.at`. |
|
110 |
* By default, `views` are added to the end of the array. |
|
111 |
* |
|
112 |
* @since 3.5.0 |
|
113 |
* |
|
114 |
* @param {string} selector A jQuery selector. |
|
115 |
* @param {Array|Object} views The subviews for the main view. |
|
116 |
* @param {Object} options Options for call. If `options.silent` is true, |
|
117 |
* no DOM modifications will be made. Use |
|
118 |
* `Views.add()` as a shortcut for setting |
|
119 |
* `options.add` to true. If `options.add` is |
|
120 |
* true, the provided `views` will be added to |
|
121 |
* the existing views. When adding, to insert |
|
122 |
* `views` at a specific index, use `options.at`. |
|
123 |
* |
|
124 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
125 |
*/ |
|
0 | 126 |
set: function( selector, views, options ) { |
127 |
var existing, next; |
|
128 |
||
129 |
if ( ! _.isString( selector ) ) { |
|
130 |
options = views; |
|
131 |
views = selector; |
|
132 |
selector = ''; |
|
133 |
} |
|
134 |
||
135 |
options = options || {}; |
|
136 |
views = _.isArray( views ) ? views : [ views ]; |
|
137 |
existing = this.get( selector ); |
|
138 |
next = views; |
|
139 |
||
140 |
if ( existing ) { |
|
141 |
if ( options.add ) { |
|
142 |
if ( _.isUndefined( options.at ) ) { |
|
143 |
next = existing.concat( views ); |
|
144 |
} else { |
|
145 |
next = existing; |
|
146 |
next.splice.apply( next, [ options.at, 0 ].concat( views ) ); |
|
147 |
} |
|
148 |
} else { |
|
149 |
_.each( next, function( view ) { |
|
150 |
view.__detach = true; |
|
151 |
}); |
|
152 |
||
153 |
_.each( existing, function( view ) { |
|
154 |
if ( view.__detach ) |
|
155 |
view.$el.detach(); |
|
156 |
else |
|
157 |
view.remove(); |
|
158 |
}); |
|
159 |
||
160 |
_.each( next, function( view ) { |
|
161 |
delete view.__detach; |
|
162 |
}); |
|
163 |
} |
|
164 |
} |
|
165 |
||
166 |
this._views[ selector ] = next; |
|
167 |
||
168 |
_.each( views, function( subview ) { |
|
169 |
var constructor = subview.Views || wp.Backbone.Subviews, |
|
170 |
subviews = subview.views = subview.views || new constructor( subview ); |
|
171 |
subviews.parent = this.view; |
|
172 |
subviews.selector = selector; |
|
173 |
}, this ); |
|
174 |
||
175 |
if ( ! options.silent ) |
|
176 |
this._attach( selector, views, _.extend({ ready: this._isReady() }, options ) ); |
|
177 |
||
178 |
return this; |
|
179 |
}, |
|
180 |
||
9 | 181 |
/** |
182 |
* Add subview(s) to existing subviews. |
|
183 |
* |
|
184 |
* An alias to `Views.set()`, which defaults `options.add` to true. |
|
185 |
* |
|
186 |
* Adds any number of `views` to a `selector`. |
|
187 |
* |
|
188 |
* When no `selector` is provided, the root selector (the empty string) |
|
189 |
* is used. `views` accepts a `Backbone.View` instance or an array of |
|
190 |
* `Backbone.View` instances. |
|
191 |
* |
|
192 |
* Uses `Views.set()` when setting `options.add` to `false`. |
|
193 |
* |
|
194 |
* Accepts an `options` object. By default, provided `views` will be |
|
195 |
* inserted at the end of the array of existing views. To insert |
|
196 |
* `views` at a specific index, use `options.at`. If `options.silent` |
|
197 |
* is true, no DOM modifications will be made. |
|
198 |
* |
|
199 |
* For more information on the `options` object, see `Views.set()`. |
|
200 |
* |
|
201 |
* @since 3.5.0 |
|
202 |
* |
|
203 |
* @param {string} selector A jQuery selector. |
|
204 |
* @param {Array|Object} views The subviews for the main view. |
|
205 |
* @param {Object} options Options for call. To insert `views` at a |
|
206 |
* specific index, use `options.at`. If |
|
207 |
* `options.silent` is true, no DOM modifications |
|
208 |
* will be made. |
|
209 |
* |
|
210 |
* @return {wp.Backbone.Subviews} The current subviews instance. |
|
211 |
*/ |
|
0 | 212 |
add: function( selector, views, options ) { |
213 |
if ( ! _.isString( selector ) ) { |
|
214 |
options = views; |
|
215 |
views = selector; |
|
216 |
selector = ''; |
|
217 |
} |
|
218 |
||
219 |
return this.set( selector, views, _.extend({ add: true }, options ) ); |
|
220 |
}, |
|
221 |
||
9 | 222 |
/** |
223 |
* Removes an added subview. |
|
224 |
* |
|
225 |
* Stops tracking `views` registered to a `selector`. If no `views` are |
|
226 |
* set, then all of the `selector`'s subviews will be unregistered and |
|
227 |
* removed. |
|
228 |
* |
|
229 |
* Accepts an `options` object. If `options.silent` is set, `remove` |
|
230 |
* will *not* be triggered on the unregistered views. |
|
231 |
* |
|
232 |
* @since 3.5.0 |
|
233 |
* |
|
234 |
* @param {string} selector A jQuery selector. |
|
235 |
* @param {Array|Object} views The subviews for the main view. |
|
236 |
* @param {Object} options Options for call. If `options.silent` is set, |
|
237 |
* `remove` will *not* be triggered on the |
|
238 |
* unregistered views. |
|
239 |
* |
|
240 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
241 |
*/ |
|
0 | 242 |
unset: function( selector, views, options ) { |
243 |
var existing; |
|
244 |
||
245 |
if ( ! _.isString( selector ) ) { |
|
246 |
options = views; |
|
247 |
views = selector; |
|
248 |
selector = ''; |
|
249 |
} |
|
250 |
||
251 |
views = views || []; |
|
252 |
||
253 |
if ( existing = this.get( selector ) ) { |
|
254 |
views = _.isArray( views ) ? views : [ views ]; |
|
255 |
this._views[ selector ] = views.length ? _.difference( existing, views ) : []; |
|
256 |
} |
|
257 |
||
258 |
if ( ! options || ! options.silent ) |
|
259 |
_.invoke( views, 'remove' ); |
|
260 |
||
261 |
return this; |
|
262 |
}, |
|
263 |
||
9 | 264 |
/** |
265 |
* Detaches all subviews. |
|
266 |
* |
|
267 |
* Helps to preserve all subview events when re-rendering the master |
|
268 |
* view. Used in conjunction with `Views.render()`. |
|
269 |
* |
|
270 |
* @since 3.5.0 |
|
271 |
* |
|
272 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
273 |
*/ |
|
0 | 274 |
detach: function() { |
275 |
$( _.pluck( this.all(), 'el' ) ).detach(); |
|
276 |
return this; |
|
277 |
}, |
|
278 |
||
9 | 279 |
/** |
280 |
* Renders all subviews. |
|
281 |
* |
|
282 |
* Used in conjunction with `Views.detach()`. |
|
283 |
* |
|
284 |
* @since 3.5.0 |
|
285 |
* |
|
286 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
287 |
*/ |
|
0 | 288 |
render: function() { |
289 |
var options = { |
|
290 |
ready: this._isReady() |
|
291 |
}; |
|
292 |
||
293 |
_.each( this._views, function( views, selector ) { |
|
294 |
this._attach( selector, views, options ); |
|
295 |
}, this ); |
|
296 |
||
297 |
this.rendered = true; |
|
298 |
return this; |
|
299 |
}, |
|
300 |
||
9 | 301 |
/** |
302 |
* Removes all subviews. |
|
303 |
* |
|
304 |
* Triggers the `remove()` method on all subviews. Detaches the master |
|
305 |
* view from its parent. Resets the internals of the views manager. |
|
306 |
* |
|
307 |
* Accepts an `options` object. If `options.silent` is set, `unset` |
|
308 |
* will *not* be triggered on the master view's parent. |
|
309 |
* |
|
310 |
* @since 3.6.0 |
|
311 |
* |
|
312 |
* @param {Object} options Options for call. |
|
21
48c4eec2b7e6
Add CLAUDE.md documentation and sync WordPress core files
ymh <ymh.work@gmail.com>
parents:
16
diff
changeset
|
313 |
* @param {boolean} options.silent If true, `unset` will *not* be triggered on |
9 | 314 |
* the master views' parent. |
315 |
* |
|
316 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
317 |
*/ |
|
0 | 318 |
remove: function( options ) { |
319 |
if ( ! options || ! options.silent ) { |
|
320 |
if ( this.parent && this.parent.views ) |
|
321 |
this.parent.views.unset( this.selector, this.view, { silent: true }); |
|
322 |
delete this.parent; |
|
323 |
delete this.selector; |
|
324 |
} |
|
325 |
||
326 |
_.invoke( this.all(), 'remove' ); |
|
327 |
this._views = []; |
|
328 |
return this; |
|
329 |
}, |
|
330 |
||
9 | 331 |
/** |
332 |
* Replaces a selector's subviews |
|
333 |
* |
|
334 |
* By default, sets the `$target` selector's html to the subview `els`. |
|
335 |
* |
|
336 |
* Can be overridden in subclasses. |
|
337 |
* |
|
338 |
* @since 3.5.0 |
|
339 |
* |
|
340 |
* @param {string} $target Selector where to put the elements. |
|
341 |
* @param {*} els HTML or elements to put into the selector's HTML. |
|
342 |
* |
|
343 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
344 |
*/ |
|
0 | 345 |
replace: function( $target, els ) { |
346 |
$target.html( els ); |
|
347 |
return this; |
|
348 |
}, |
|
349 |
||
9 | 350 |
/** |
351 |
* Insert subviews into a selector. |
|
352 |
* |
|
353 |
* By default, appends the subview `els` to the end of the `$target` |
|
354 |
* selector. If `options.at` is set, inserts the subview `els` at the |
|
355 |
* provided index. |
|
356 |
* |
|
357 |
* Can be overridden in subclasses. |
|
358 |
* |
|
359 |
* @since 3.5.0 |
|
360 |
* |
|
361 |
* @param {string} $target Selector where to put the elements. |
|
362 |
* @param {*} els HTML or elements to put at the end of the |
|
363 |
* $target. |
|
364 |
* @param {?Object} options Options for call. |
|
365 |
* @param {?number} options.at At which index to put the elements. |
|
366 |
* |
|
367 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
368 |
*/ |
|
0 | 369 |
insert: function( $target, els, options ) { |
370 |
var at = options && options.at, |
|
371 |
$children; |
|
372 |
||
373 |
if ( _.isNumber( at ) && ($children = $target.children()).length > at ) |
|
374 |
$children.eq( at ).before( els ); |
|
375 |
else |
|
376 |
$target.append( els ); |
|
377 |
||
378 |
return this; |
|
379 |
}, |
|
380 |
||
9 | 381 |
/** |
382 |
* Triggers the ready event. |
|
383 |
* |
|
384 |
* Only use this method if you know what you're doing. For performance reasons, |
|
385 |
* this method does not check if the view is actually attached to the DOM. It's |
|
386 |
* taking your word for it. |
|
387 |
* |
|
388 |
* Fires the ready event on the current view and all attached subviews. |
|
389 |
* |
|
390 |
* @since 3.5.0 |
|
391 |
*/ |
|
0 | 392 |
ready: function() { |
393 |
this.view.trigger('ready'); |
|
394 |
||
395 |
// Find all attached subviews, and call ready on them. |
|
396 |
_.chain( this.all() ).map( function( view ) { |
|
397 |
return view.views; |
|
398 |
}).flatten().where({ attached: true }).invoke('ready'); |
|
399 |
}, |
|
9 | 400 |
/** |
401 |
* Attaches a series of views to a selector. Internal. |
|
402 |
* |
|
403 |
* Checks to see if a matching selector exists, renders the views, |
|
404 |
* performs the proper DOM operation, and then checks if the view is |
|
405 |
* attached to the document. |
|
406 |
* |
|
407 |
* @since 3.5.0 |
|
408 |
* |
|
409 |
* @private |
|
410 |
* |
|
411 |
* @param {string} selector A jQuery selector. |
|
412 |
* @param {Array|Object} views The subviews for the main view. |
|
413 |
* @param {Object} options Options for call. |
|
414 |
* @param {boolean} options.add If true the provided views will be added. |
|
415 |
* |
|
416 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
417 |
*/ |
|
0 | 418 |
_attach: function( selector, views, options ) { |
419 |
var $selector = selector ? this.view.$( selector ) : this.view.$el, |
|
420 |
managers; |
|
421 |
||
422 |
// Check if we found a location to attach the views. |
|
423 |
if ( ! $selector.length ) |
|
424 |
return this; |
|
425 |
||
426 |
managers = _.chain( views ).pluck('views').flatten().value(); |
|
427 |
||
428 |
// Render the views if necessary. |
|
429 |
_.each( managers, function( manager ) { |
|
430 |
if ( manager.rendered ) |
|
431 |
return; |
|
432 |
||
433 |
manager.view.render(); |
|
434 |
manager.rendered = true; |
|
435 |
}, this ); |
|
436 |
||
437 |
// Insert or replace the views. |
|
438 |
this[ options.add ? 'insert' : 'replace' ]( $selector, _.pluck( views, 'el' ), options ); |
|
439 |
||
9 | 440 |
/* |
441 |
* Set attached and trigger ready if the current view is already |
|
442 |
* attached to the DOM. |
|
443 |
*/ |
|
0 | 444 |
_.each( managers, function( manager ) { |
445 |
manager.attached = true; |
|
446 |
||
447 |
if ( options.ready ) |
|
448 |
manager.ready(); |
|
449 |
}, this ); |
|
450 |
||
451 |
return this; |
|
452 |
}, |
|
453 |
||
9 | 454 |
/** |
455 |
* Determines whether or not the current view is in the DOM. |
|
456 |
* |
|
457 |
* @since 3.5.0 |
|
458 |
* |
|
459 |
* @private |
|
460 |
* |
|
461 |
* @return {boolean} Whether or not the current view is in the DOM. |
|
462 |
*/ |
|
0 | 463 |
_isReady: function() { |
464 |
var node = this.view.el; |
|
465 |
while ( node ) { |
|
466 |
if ( node === document.body ) |
|
467 |
return true; |
|
468 |
node = node.parentNode; |
|
469 |
} |
|
470 |
||
471 |
return false; |
|
472 |
} |
|
473 |
}); |
|
474 |
||
9 | 475 |
wp.Backbone.View = Backbone.View.extend({ |
0 | 476 |
|
477 |
// The constructor for the `Views` manager. |
|
478 |
Subviews: wp.Backbone.Subviews, |
|
479 |
||
9 | 480 |
/** |
481 |
* The base view class. |
|
482 |
* |
|
483 |
* This extends the backbone view to have a build-in way to use subviews. This |
|
484 |
* makes it easier to have nested views. |
|
485 |
* |
|
486 |
* @since 3.5.0 |
|
487 |
* @since 3.6.0 Moved wp.media.View to wp.Backbone.View |
|
488 |
* |
|
489 |
* @constructs |
|
490 |
* @augments Backbone.View |
|
491 |
* |
|
492 |
* @memberOf wp.Backbone |
|
493 |
* |
|
494 |
* |
|
495 |
* @param {Object} options The options for this view. |
|
496 |
*/ |
|
5 | 497 |
constructor: function( options ) { |
0 | 498 |
this.views = new this.Subviews( this, this.views ); |
499 |
this.on( 'ready', this.ready, this ); |
|
500 |
||
5 | 501 |
this.options = options || {}; |
502 |
||
0 | 503 |
Backbone.View.apply( this, arguments ); |
504 |
}, |
|
505 |
||
9 | 506 |
/** |
507 |
* Removes this view and all subviews. |
|
508 |
* |
|
509 |
* @since 3.5.0 |
|
510 |
* |
|
511 |
* @return {wp.Backbone.Subviews} The current Subviews instance. |
|
512 |
*/ |
|
0 | 513 |
remove: function() { |
514 |
var result = Backbone.View.prototype.remove.apply( this, arguments ); |
|
515 |
||
516 |
// Recursively remove child views. |
|
517 |
if ( this.views ) |
|
518 |
this.views.remove(); |
|
519 |
||
520 |
return result; |
|
521 |
}, |
|
522 |
||
9 | 523 |
/** |
524 |
* Renders this view and all subviews. |
|
525 |
* |
|
526 |
* @since 3.5.0 |
|
527 |
* |
|
528 |
* @return {wp.Backbone.View} The current instance of the view. |
|
529 |
*/ |
|
0 | 530 |
render: function() { |
531 |
var options; |
|
532 |
||
533 |
if ( this.prepare ) |
|
534 |
options = this.prepare(); |
|
535 |
||
536 |
this.views.detach(); |
|
537 |
||
538 |
if ( this.template ) { |
|
539 |
options = options || {}; |
|
540 |
this.trigger( 'prepare', options ); |
|
541 |
this.$el.html( this.template( options ) ); |
|
542 |
} |
|
543 |
||
544 |
this.views.render(); |
|
545 |
return this; |
|
546 |
}, |
|
547 |
||
9 | 548 |
/** |
549 |
* Returns the options for this view. |
|
550 |
* |
|
551 |
* @since 3.5.0 |
|
552 |
* |
|
553 |
* @return {Object} The options for this view. |
|
554 |
*/ |
|
0 | 555 |
prepare: function() { |
556 |
return this.options; |
|
557 |
}, |
|
558 |
||
9 | 559 |
/** |
560 |
* Method that is called when the ready event is triggered. |
|
561 |
* |
|
562 |
* @since 3.5.0 |
|
563 |
*/ |
|
0 | 564 |
ready: function() {} |
565 |
}); |
|
566 |
}(jQuery)); |