7 * |
11 * |
8 * @namespace wp.Backbone |
12 * @namespace wp.Backbone |
9 */ |
13 */ |
10 wp.Backbone = {}; |
14 wp.Backbone = {}; |
11 |
15 |
12 |
16 /** |
13 // wp.Backbone.Subviews |
17 * A backbone subview manager. |
14 // -------------------- |
18 * |
15 // |
19 * @since 3.5.0 |
16 // A subview manager. |
20 * @since 3.6.0 Moved wp.media.Views to wp.Backbone.Subviews. |
|
21 * |
|
22 * @memberOf wp.Backbone |
|
23 * |
|
24 * @class |
|
25 * |
|
26 * @param {wp.Backbone.View} view The main view. |
|
27 * @param {Array|Object} views The subviews for the main view. |
|
28 */ |
17 wp.Backbone.Subviews = function( view, views ) { |
29 wp.Backbone.Subviews = function( view, views ) { |
18 this.view = view; |
30 this.view = view; |
19 this._views = _.isArray( views ) ? { '': views } : views || {}; |
31 this._views = _.isArray( views ) ? { '': views } : views || {}; |
20 }; |
32 }; |
21 |
33 |
22 wp.Backbone.Subviews.extend = Backbone.Model.extend; |
34 wp.Backbone.Subviews.extend = Backbone.Model.extend; |
23 |
35 |
24 _.extend( wp.Backbone.Subviews.prototype, { |
36 _.extend( wp.Backbone.Subviews.prototype, { |
25 // ### Fetch all of the subviews |
37 /** |
26 // |
38 * Fetches all of the subviews. |
27 // Returns an array of all subviews. |
39 * |
|
40 * @since 3.5.0 |
|
41 * |
|
42 * @return {Array} All the subviews. |
|
43 */ |
28 all: function() { |
44 all: function() { |
29 return _.flatten( _.values( this._views ) ); |
45 return _.flatten( _.values( this._views ) ); |
30 }, |
46 }, |
31 |
47 |
32 // ### Get a selector's subviews |
48 /** |
33 // |
49 * Fetches all subviews that match a given `selector`. |
34 // Fetches all subviews that match a given `selector`. |
50 * |
35 // |
51 * If no `selector` is provided, it will grab all subviews attached |
36 // If no `selector` is provided, it will grab all subviews attached |
52 * to the view's root. |
37 // 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 */ |
38 get: function( selector ) { |
60 get: function( selector ) { |
39 selector = selector || ''; |
61 selector = selector || ''; |
40 return this._views[ selector ]; |
62 return this._views[ selector ]; |
41 }, |
63 }, |
42 |
64 |
43 // ### Get a selector's first subview |
65 /** |
44 // |
66 * Fetches the first subview that matches a given `selector`. |
45 // Fetches the first subview that matches a given `selector`. |
67 * |
46 // |
68 * If no `selector` is provided, it will grab the first subview attached to the |
47 // If no `selector` is provided, it will grab the first subview |
69 * view's root. |
48 // attached to the view's root. |
70 * |
49 // |
71 * Useful when a selector only has one subview at a time. |
50 // 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 */ |
51 first: function( selector ) { |
79 first: function( selector ) { |
52 var views = this.get( selector ); |
80 var views = this.get( selector ); |
53 return views && views.length ? views[0] : null; |
81 return views && views.length ? views[0] : null; |
54 }, |
82 }, |
55 |
83 |
56 // ### Register subview(s) |
84 /** |
57 // |
85 * Registers subview(s). |
58 // Registers any number of `views` to a `selector`. |
86 * |
59 // |
87 * Registers any number of `views` to a `selector`. |
60 // When no `selector` is provided, the root selector (the empty string) |
88 * |
61 // is used. `views` accepts a `Backbone.View` instance or an array of |
89 * When no `selector` is provided, the root selector (the empty string) |
62 // `Backbone.View` instances. |
90 * is used. `views` accepts a `Backbone.View` instance or an array of |
63 // |
91 * `Backbone.View` instances. |
64 // --- |
92 * |
65 // |
93 * --- |
66 // Accepts an `options` object, which has a significant effect on the |
94 * |
67 // resulting behavior. |
95 * Accepts an `options` object, which has a significant effect on the |
68 // |
96 * resulting behavior. |
69 // `options.silent` – *boolean, `false`* |
97 * |
70 // > If `options.silent` is true, no DOM modifications will be made. |
98 * `options.silent` - *boolean, `false`* |
71 // |
99 * If `options.silent` is true, no DOM modifications will be made. |
72 // `options.add` – *boolean, `false`* |
100 * |
73 // > Use `Views.add()` as a shortcut for setting `options.add` to true. |
101 * `options.add` - *boolean, `false`* |
74 // |
102 * Use `Views.add()` as a shortcut for setting `options.add` to true. |
75 // > By default, the provided `views` will replace |
103 * |
76 // any existing views associated with the selector. If `options.add` |
104 * By default, the provided `views` will replace any existing views |
77 // is true, the provided `views` will be added to the existing views. |
105 * associated with the selector. If `options.add` is true, the provided |
78 // |
106 * `views` will be added to the existing views. |
79 // `options.at` – *integer, `undefined`* |
107 * |
80 // > When adding, to insert `views` at a specific index, use |
108 * `options.at` - *integer, `undefined`* |
81 // `options.at`. By default, `views` are added to the end of the array. |
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 */ |
82 set: function( selector, views, options ) { |
126 set: function( selector, views, options ) { |
83 var existing, next; |
127 var existing, next; |
84 |
128 |
85 if ( ! _.isString( selector ) ) { |
129 if ( ! _.isString( selector ) ) { |
86 options = views; |
130 options = views; |
132 this._attach( selector, views, _.extend({ ready: this._isReady() }, options ) ); |
176 this._attach( selector, views, _.extend({ ready: this._isReady() }, options ) ); |
133 |
177 |
134 return this; |
178 return this; |
135 }, |
179 }, |
136 |
180 |
137 // ### Add subview(s) to existing subviews |
181 /** |
138 // |
182 * Add subview(s) to existing subviews. |
139 // An alias to `Views.set()`, which defaults `options.add` to true. |
183 * |
140 // |
184 * An alias to `Views.set()`, which defaults `options.add` to true. |
141 // Adds any number of `views` to a `selector`. |
185 * |
142 // |
186 * Adds any number of `views` to a `selector`. |
143 // When no `selector` is provided, the root selector (the empty string) |
187 * |
144 // is used. `views` accepts a `Backbone.View` instance or an array of |
188 * When no `selector` is provided, the root selector (the empty string) |
145 // `Backbone.View` instances. |
189 * is used. `views` accepts a `Backbone.View` instance or an array of |
146 // |
190 * `Backbone.View` instances. |
147 // Use `Views.set()` when setting `options.add` to `false`. |
191 * |
148 // |
192 * Uses `Views.set()` when setting `options.add` to `false`. |
149 // Accepts an `options` object. By default, provided `views` will be |
193 * |
150 // inserted at the end of the array of existing views. To insert |
194 * Accepts an `options` object. By default, provided `views` will be |
151 // `views` at a specific index, use `options.at`. If `options.silent` |
195 * inserted at the end of the array of existing views. To insert |
152 // is true, no DOM modifications will be made. |
196 * `views` at a specific index, use `options.at`. If `options.silent` |
153 // |
197 * is true, no DOM modifications will be made. |
154 // For more information on the `options` object, see `Views.set()`. |
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 */ |
155 add: function( selector, views, options ) { |
212 add: function( selector, views, options ) { |
156 if ( ! _.isString( selector ) ) { |
213 if ( ! _.isString( selector ) ) { |
157 options = views; |
214 options = views; |
158 views = selector; |
215 views = selector; |
159 selector = ''; |
216 selector = ''; |
160 } |
217 } |
161 |
218 |
162 return this.set( selector, views, _.extend({ add: true }, options ) ); |
219 return this.set( selector, views, _.extend({ add: true }, options ) ); |
163 }, |
220 }, |
164 |
221 |
165 // ### Stop tracking subviews |
222 /** |
166 // |
223 * Removes an added subview. |
167 // Stops tracking `views` registered to a `selector`. If no `views` are |
224 * |
168 // set, then all of the `selector`'s subviews will be unregistered and |
225 * Stops tracking `views` registered to a `selector`. If no `views` are |
169 // removed. |
226 * set, then all of the `selector`'s subviews will be unregistered and |
170 // |
227 * removed. |
171 // Accepts an `options` object. If `options.silent` is set, `remove` |
228 * |
172 // will *not* be triggered on the unregistered views. |
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 */ |
173 unset: function( selector, views, options ) { |
242 unset: function( selector, views, options ) { |
174 var existing; |
243 var existing; |
175 |
244 |
176 if ( ! _.isString( selector ) ) { |
245 if ( ! _.isString( selector ) ) { |
177 options = views; |
246 options = views; |
217 |
296 |
218 this.rendered = true; |
297 this.rendered = true; |
219 return this; |
298 return this; |
220 }, |
299 }, |
221 |
300 |
222 // ### Remove all subviews |
301 /** |
223 // |
302 * Removes all subviews. |
224 // Triggers the `remove()` method on all subviews. Detaches the master |
303 * |
225 // view from its parent. Resets the internals of the views manager. |
304 * Triggers the `remove()` method on all subviews. Detaches the master |
226 // |
305 * view from its parent. Resets the internals of the views manager. |
227 // Accepts an `options` object. If `options.silent` is set, `unset` |
306 * |
228 // will *not* be triggered on the master view's parent. |
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. |
|
313 * @param {boolean} options.silent If true, `unset` wil *not* be triggered on |
|
314 * the master views' parent. |
|
315 * |
|
316 * @return {wp.Backbone.Subviews} The current Subviews instance. |
|
317 */ |
229 remove: function( options ) { |
318 remove: function( options ) { |
230 if ( ! options || ! options.silent ) { |
319 if ( ! options || ! options.silent ) { |
231 if ( this.parent && this.parent.views ) |
320 if ( this.parent && this.parent.views ) |
232 this.parent.views.unset( this.selector, this.view, { silent: true }); |
321 this.parent.views.unset( this.selector, this.view, { silent: true }); |
233 delete this.parent; |
322 delete this.parent; |
237 _.invoke( this.all(), 'remove' ); |
326 _.invoke( this.all(), 'remove' ); |
238 this._views = []; |
327 this._views = []; |
239 return this; |
328 return this; |
240 }, |
329 }, |
241 |
330 |
242 // ### Replace a selector's subviews |
331 /** |
243 // |
332 * Replaces a selector's subviews |
244 // By default, sets the `$target` selector's html to the subview `els`. |
333 * |
245 // |
334 * By default, sets the `$target` selector's html to the subview `els`. |
246 // Can be overridden in subclasses. |
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 */ |
247 replace: function( $target, els ) { |
345 replace: function( $target, els ) { |
248 $target.html( els ); |
346 $target.html( els ); |
249 return this; |
347 return this; |
250 }, |
348 }, |
251 |
349 |
252 // ### Insert subviews into a selector |
350 /** |
253 // |
351 * Insert subviews into a selector. |
254 // By default, appends the subview `els` to the end of the `$target` |
352 * |
255 // selector. If `options.at` is set, inserts the subview `els` at the |
353 * By default, appends the subview `els` to the end of the `$target` |
256 // provided index. |
354 * selector. If `options.at` is set, inserts the subview `els` at the |
257 // |
355 * provided index. |
258 // Can be overridden in subclasses. |
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 */ |
259 insert: function( $target, els, options ) { |
369 insert: function( $target, els, options ) { |
260 var at = options && options.at, |
370 var at = options && options.at, |
261 $children; |
371 $children; |
262 |
372 |
263 if ( _.isNumber( at ) && ($children = $target.children()).length > at ) |
373 if ( _.isNumber( at ) && ($children = $target.children()).length > at ) |
266 $target.append( els ); |
376 $target.append( els ); |
267 |
377 |
268 return this; |
378 return this; |
269 }, |
379 }, |
270 |
380 |
271 // ### Trigger the ready event |
381 /** |
272 // |
382 * Triggers the ready event. |
273 // **Only use this method if you know what you're doing.** |
383 * |
274 // For performance reasons, this method does not check if the view is |
384 * Only use this method if you know what you're doing. For performance reasons, |
275 // actually attached to the DOM. It's taking your word for it. |
385 * this method does not check if the view is actually attached to the DOM. It's |
276 // |
386 * taking your word for it. |
277 // Fires the ready event on the current view and all attached subviews. |
387 * |
|
388 * Fires the ready event on the current view and all attached subviews. |
|
389 * |
|
390 * @since 3.5.0 |
|
391 */ |
278 ready: function() { |
392 ready: function() { |
279 this.view.trigger('ready'); |
393 this.view.trigger('ready'); |
280 |
394 |
281 // Find all attached subviews, and call ready on them. |
395 // Find all attached subviews, and call ready on them. |
282 _.chain( this.all() ).map( function( view ) { |
396 _.chain( this.all() ).map( function( view ) { |
283 return view.views; |
397 return view.views; |
284 }).flatten().where({ attached: true }).invoke('ready'); |
398 }).flatten().where({ attached: true }).invoke('ready'); |
285 }, |
399 }, |
286 |
400 /** |
287 // #### Internal. Attaches a series of views to a selector. |
401 * Attaches a series of views to a selector. Internal. |
288 // |
402 * |
289 // Checks to see if a matching selector exists, renders the views, |
403 * Checks to see if a matching selector exists, renders the views, |
290 // performs the proper DOM operation, and then checks if the view is |
404 * performs the proper DOM operation, and then checks if the view is |
291 // attached to the document. |
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 */ |
292 _attach: function( selector, views, options ) { |
418 _attach: function( selector, views, options ) { |
293 var $selector = selector ? this.view.$( selector ) : this.view.$el, |
419 var $selector = selector ? this.view.$( selector ) : this.view.$el, |
294 managers; |
420 managers; |
295 |
421 |
296 // Check if we found a location to attach the views. |
422 // Check if we found a location to attach the views. |
334 |
470 |
335 return false; |
471 return false; |
336 } |
472 } |
337 }); |
473 }); |
338 |
474 |
339 |
|
340 // wp.Backbone.View |
|
341 // ---------------- |
|
342 // |
|
343 // The base view class. |
|
344 wp.Backbone.View = Backbone.View.extend({ |
475 wp.Backbone.View = Backbone.View.extend({ |
|
476 |
345 // The constructor for the `Views` manager. |
477 // The constructor for the `Views` manager. |
346 Subviews: wp.Backbone.Subviews, |
478 Subviews: wp.Backbone.Subviews, |
347 |
479 |
|
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 */ |
348 constructor: function( options ) { |
497 constructor: function( options ) { |
349 this.views = new this.Subviews( this, this.views ); |
498 this.views = new this.Subviews( this, this.views ); |
350 this.on( 'ready', this.ready, this ); |
499 this.on( 'ready', this.ready, this ); |
351 |
500 |
352 this.options = options || {}; |
501 this.options = options || {}; |
353 |
502 |
354 Backbone.View.apply( this, arguments ); |
503 Backbone.View.apply( this, arguments ); |
355 }, |
504 }, |
356 |
505 |
|
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 */ |
357 remove: function() { |
513 remove: function() { |
358 var result = Backbone.View.prototype.remove.apply( this, arguments ); |
514 var result = Backbone.View.prototype.remove.apply( this, arguments ); |
359 |
515 |
360 // Recursively remove child views. |
516 // Recursively remove child views. |
361 if ( this.views ) |
517 if ( this.views ) |
362 this.views.remove(); |
518 this.views.remove(); |
363 |
519 |
364 return result; |
520 return result; |
365 }, |
521 }, |
366 |
522 |
|
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 */ |
367 render: function() { |
530 render: function() { |
368 var options; |
531 var options; |
369 |
532 |
370 if ( this.prepare ) |
533 if ( this.prepare ) |
371 options = this.prepare(); |
534 options = this.prepare(); |