Skip to content
This repository
Browse code

ENHANCEMENT Lazy-loading image and link dialogs for HtmlEditorField, …

…to avoid unnecessary processing overhead on initial CMS load (also means they'll never get loaded if not required, e.g. in ReportAdmin)
  • Loading branch information...
commit b025b95ede67d5178524c88e3909d44fa59b65d6 1 parent c73b800
Ingo Schommer authored April 12, 2012
7  admin/templates/LeftAndMain.ss
@@ -24,12 +24,7 @@
24 24
 
25 25
 	</div>
26 26
 		
27  
-	<div id="cms-editor-dialogs" class="hide">
28  
-		<% control EditorToolbar %>
29  
-			$MediaForm
30  
-			$LinkForm
31  
-		<% end_control %>
32  
-	</div>
  27
+	$EditorToolbar
33 28
 
34 29
 	<!-- <div class="ss-cms-bottom-bar">
35 30
 			<div class="holder">
9  forms/HtmlEditorField.php
@@ -267,6 +267,14 @@ function __construct($controller, $name) {
267 267
 		$this->name = $name;
268 268
 	}
269 269
 
  270
+	public function forTemplate() {
  271
+		return sprintf(
  272
+			'<div id="cms-editor-dialogs" data-url-linkform="%s" data-url-mediaform="%s"></div>',
  273
+			Controller::join_links($this->controller->Link($this->name), 'LinkForm', 'forTemplate'),
  274
+			Controller::join_links($this->controller->Link($this->name), 'MediaForm', 'forTemplate')
  275
+		);
  276
+	}
  277
+
270 278
 	/**
271 279
 	 * Searches the SiteTree for display in the dropdown
272 280
 	 *  
@@ -627,6 +635,7 @@ protected function getAllowedExtensions() {
627 635
 		$this->extend('updateAllowedExtensions', $exts);
628 636
 		return $exts;
629 637
 	}
  638
+
630 639
 }
631 640
 
632 641
 /**
130  javascript/HtmlEditorField.js
@@ -251,12 +251,10 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
251 251
 
252 252
 				this._super();
253 253
 			},
254  
-
255 254
 			isChanged: function() {
256 255
 				var ed = this.getEditor();
257 256
 				return (ed && ed.getInstance() && ed.isDirty());
258 257
 			},
259  
-
260 258
 			resetChanged: function() {
261 259
 				var ed = this.getEditor();
262 260
 				if(typeof tinyMCE == 'undefined') return;
@@ -265,7 +263,35 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
265 263
 				var inst = tinyMCE.getInstanceById(this.attr('id'));
266 264
 				if (inst) inst.startContent = tinymce.trim(inst.getContent({format : 'raw', no_events : 1}));
267 265
 			},
  266
+			openLinkDialog: function() {
  267
+				this.openDialog('link');
  268
+			},
  269
+			openMediaDialog: function() {
  270
+				this.openDialog('media');	
  271
+			},
  272
+			openDialog: function(type) {
  273
+				var capitalize = function(text) {
  274
+    			return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
  275
+				};
  276
+
  277
+				var url = $('#cms-editor-dialogs').data('url' + capitalize(type) + 'form'),
  278
+					dialog = $('.htmleditorfield-' + type + 'dialog');
268 279
 
  280
+				if(dialog.length) {
  281
+					dialog.open();
  282
+				} else {
  283
+					// Show a placeholder for instant feedback. Will be replaced with actual
  284
+					// form dialog once its loaded.
  285
+					dialog = $('<div class="htmleditorfield-dialog htmleditorfield-' + type + 'dialog loading">');
  286
+					$('body').append(dialog);
  287
+					$.ajax({
  288
+						url: url,
  289
+						success: function(html) {
  290
+							dialog.html(html);
  291
+						}
  292
+					});
  293
+				}
  294
+			},
269 295
 			onunmatch: function() {
270 296
 				// TODO Throws exceptions in Firefox, most likely due to the element being removed from the DOM at this point
271 297
 				// var ed = tinyMCE.get(this.attr('id'));
@@ -275,6 +301,35 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
275 301
 			}
276 302
 		});
277 303
 
  304
+		$('.htmleditorfield-dialog').entwine({
  305
+			onmatch: function() {
  306
+				// Create jQuery dialog
  307
+				this.dialog({autoOpen: true, bgiframe: true, modal: true, height: 500, width: '80%', ghost: true});
  308
+
  309
+				this._super();
  310
+			},
  311
+			getForm: function() {
  312
+				return this.find('form');
  313
+			},
  314
+			open: function() {
  315
+				this.dialog('open');
  316
+			},
  317
+			close: function() {
  318
+				this.dialog('close');
  319
+			},
  320
+			toggle: function(bool) {
  321
+				if(this.is(':visible')) this.close();
  322
+				else this.open();
  323
+			},
  324
+			ondialogopen: function(e) {
  325
+				this.getForm().updateFromEditor();
  326
+				this.redraw();
  327
+			},
  328
+			ondialogclose: function(e) {
  329
+				this.getForm().resetFields();
  330
+			}
  331
+		});
  332
+
278 333
 		/**
279 334
 		 * Base form implementation for interactions with an editor instance,
280 335
 		 * mostly geared towards modification and insertion of content.
@@ -286,40 +341,30 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
286 341
 
287 342
 			// TODO Figure out how to keep bookmark reference in entwine, and still be allowed to delete the JS object
288 343
 			// Bookmark: null,
289  
-
290 344
 			onmatch: function() {
291 345
 				// Move title from headline to (jQuery compatible) title attribute
292 346
 				var titleEl = this.find(':header:first');
293  
-				this.attr('title', titleEl.text());
  347
+				this.getDialog().attr('title', titleEl.text());
294 348
 				titleEl.remove();
295 349
 
296  
-				// Create jQuery dialog
297  
-				this.dialog({autoOpen: false, bgiframe: true, modal: true, height: 500, width: '80%', ghost: true});
298  
-
299 350
 				this.setEditor(ss.editorWrappers['default']());
300 351
 			},
301 352
 			redraw: function() {
302 353
 			},
303  
-			toggle: function() {
304  
-				if(this.is(':visible')) this.close();
305  
-				else this.open();
306  
-			},
307  
-			close: function() {
308  
-				this.dialog('close');
309  
-				this.getEditor().onclose();
  354
+			resetFields: function() {
310 355
 				if(typeof window._ss_htmleditorfield_bookmark != 'undefined') window._ss_htmleditorfield_bookmark = null;
  356
+				this.getEditor().onclose();
311 357
 			},
312  
-			open: function() {
313  
-				this.updateFromEditor();
314  
-				this.dialog('open');
315  
-				this.redraw();
316  
-				this.getEditor().onopen();
317  
-				window._ss_htmleditorfield_bookmark = this.getEditor().createBookmark();
  358
+			getDialog: function() {
  359
+				// TODO Refactor to listen to form events to remove two-way coupling
  360
+				return this.closest('.htmleditorfield-dialog');
318 361
 			},
319 362
 			/**
320 363
 			 * Update the view state based on the current editor selection.
321 364
 			 */
322 365
 			updateFromEditor: function() {
  366
+				this.getEditor().onopen();
  367
+				window._ss_htmleditorfield_bookmark = this.getEditor().createBookmark();
323 368
 			}
324 369
 		});
325 370
 
@@ -331,23 +376,16 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
331 376
 		 */
332 377
 		$('form.htmleditorfield-linkform').entwine({
333 378
 
334  
-			close: function() {
335  
-				this._super();
336  
-
337  
-				this.resetFields();
338  
-			},
339  
-
340 379
 			// TODO Entwine doesn't respect submits triggered by ENTER key
341 380
 			onsubmit: function(e) {
342 381
 				this.insertLink();
343  
-				this.close();
  382
+				this.getDialog().close();
344 383
 				return false;
345 384
 			},
346  
-
347 385
 			resetFields: function() {
  386
+				this._super();
348 387
 				this.find('fieldset :input:not(:radio)').val('').change();
349 388
 			},
350  
-
351 389
 			redraw: function(setDefaults) {
352 390
 				this._super();
353 391
 
@@ -377,7 +415,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
377 415
 					this.find(':input[name=TargetBlank]').attr('checked', (linkType == 'file'));
378 416
 				}
379 417
 			},
380  
-
381 418
 			insertLink: function() {
382 419
 				var href, target = null, anchor = this.find(':input[name=Anchor]').val(), ed = this.getEditor();
383 420
 				
@@ -428,12 +465,10 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
428 465
 				this.trigger('onafterinsert', attributes);
429 466
 				this.updateFromEditor();
430 467
 			},
431  
-
432 468
 			removeLink: function() {
433 469
 				this.getEditor().removeLink();
434 470
 				this.close();
435 471
 			},
436  
-
437 472
 			addAnchorSelector: function() {
438 473
 				// Avoid adding twice
439 474
 				if(this.find(':input[name=AnchorSelector]').length) return;
@@ -466,7 +501,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
466 501
 					self.find(':input[name="Anchor"]').val($(this).val());
467 502
 				});
468 503
 			},
469  
-
470 504
 			// this function collects the anchors in the currently active editor and regenerates the dropdown
471 505
 			refreshAnchors: function() {
472 506
 				var selector = this.find(':input[name=AnchorSelector]'), anchors = [];
@@ -485,7 +519,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
485 519
 					selector.append($('<option value="'+anchors[j]+'">'+anchors[j]+'</option>'));
486 520
 				}
487 521
 			},
488  
-
489 522
 			updateFromEditor: function() {
490 523
 				var htmlTagPattern = /<\S[^><]*>/g, fieldName, data = this.getCurrentLink();
491 524
 				
@@ -502,7 +535,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
502 535
 					}
503 536
 				}
504 537
 			},
505  
-
506 538
 		/**
507 539
 		 * Return information about the currently selected link, suitable for population of the link
508 540
 		 * form.
@@ -623,13 +655,11 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
623 655
 					$(this).insertHTML();
624 656
 				});
625 657
 				ed.repaint();
626  
-				this.close();
  658
+				this.getDialog().close();
627 659
 
628 660
 				return false;
629 661
 			},
630  
-			ondialogopen: function() {
631  
-				this.redraw();
632  
-
  662
+			updateFromEditor: function() {
633 663
 				var self = this, ed = this.getEditor(), node = $(ed.getSelectedNode());
634 664
 				// TODO Depends on managed mime type
635 665
 				if(node.is('img')) {
@@ -645,16 +675,6 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
645 675
 				// don't respect the z-index of the dialog overlay.
646 676
 				// jQuery(ed.getContainer()).hide();
647 677
 			},
648  
-			ondialogclose: function() {
649  
-				var ed = this.getEditor(), node = $(ed.getSelectedNode());
650  
-
651  
-				// HACK: See ondialogopen()
652  
-				// jQuery(ed.getContainer()).show();
653  
-
654  
-				this.find('.ss-htmleditorfield-file').remove(); // Remove any existing views
655  
-				this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items
656  
-				this.redraw();
657  
-			},
658 678
 			redraw: function() {
659 679
 				this._super();
660 680
 
@@ -673,6 +693,18 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
673 693
 				// Hide file selection and step labels when editing an existing file
674 694
 				this.find('#MediaFormInsertImageTabs,.header-edit')[editingSelected ? 'hide' : 'show']();
675 695
 			},
  696
+			resetFields: function() {
  697
+				var ed = this.getEditor(), node = $(ed.getSelectedNode());
  698
+
  699
+				// HACK: See ondialogopen()
  700
+				// jQuery(ed.getContainer()).show();
  701
+
  702
+				this.find('.ss-htmleditorfield-file').remove(); // Remove any existing views
  703
+				this.find('.ss-gridfield-items .ui-selected').removeClass('ui-selected'); // Unselect all items
  704
+				this.redraw();
  705
+
  706
+				this._super();
  707
+			},
676 708
 			getFileView: function(idOrUrl) {
677 709
 				return this.find('.ss-htmleditorfield-file[data-id=' + idOrUrl + ']');
678 710
 			},

0 notes on commit b025b95

Please sign in to comment.
Something went wrong with that request. Please try again.