Permalink
Browse files

Merge pull request #2345 from hafriedlander/fix/performance

Couple of performance fixes - eliminate un-necessary redraws and preview updating
  • Loading branch information...
2 parents a592c36 + 0ca4969 commit afd3e3f0d0569825f35968166df0a9eb2c04eddf @mateusz mateusz committed Aug 20, 2013
@@ -104,9 +104,21 @@
}
}
+ // Calculate what columns are already hidden pre-layout
+ var prehidden = {
+ content: spec.content.hasClass('column-hidden'),
+ preview: spec.preview.hasClass('column-hidden')
+ };
+
+ // Calculate what columns will be hidden (zero width) post-layout
+ var posthidden = {
+ content: contentWidth === 0,
+ preview: previewWidth === 0
+ };
+
// Apply classes for elements that might not be visible at all.
- spec.content.toggleClass('column-hidden', contentWidth===0);
- spec.preview.toggleClass('column-hidden', previewWidth===0);
+ spec.content.toggleClass('column-hidden', posthidden.content);
+ spec.preview.toggleClass('column-hidden', posthidden.preview);
// Apply the widths to columns, and call subordinate layouts to arrange the children.
menu.bounds({'x': left, 'y': top, 'height': bottom - top, 'width': menuWidth});
@@ -115,12 +127,15 @@
left += menuWidth;
content.bounds({'x': left, 'y': top, 'height': bottom - top, 'width': contentWidth});
- content.doLayout();
+ if (!posthidden.content) content.doLayout();
left += contentWidth;
preview.bounds({'x': left, 'y': top, 'height': bottom - top, 'width': previewWidth});
- preview.doLayout();
+ if (!posthidden.preview) preview.doLayout();
+
+ if (posthidden.content !== prehidden.content) spec.content.trigger('columnvisibilitychanged');
+ if (posthidden.preview !== prehidden.preview) spec.preview.trigger('columnvisibilitychanged');
return container;
};
@@ -30,7 +30,7 @@
WidthCollapsed: null,
- onmatch: function() {
+ onadd: function() {
if(!this.find('.cms-panel-content').length) throw new Exception('Content panel for ".cms-panel" not found');
// Create default controls unless they already exist.
@@ -62,9 +62,6 @@
this._super();
},
- onunmatch: function() {
- this._super();
- },
/**
* @param {Boolean} TRUE to expand, FALSE to collapse.
* @param {Boolean} TRUE means that events won't be fired, which is useful for the component initialization phase.
@@ -89,7 +86,7 @@
// Save collapsed state in cookie
if($.cookie && this.attr('id')) $.cookie('cms-panel-collapsed-' + this.attr('id'), !bool, {path: '/', expires: 31});
-
+
// TODO Fix redraw order (inner to outer), and re-enable silent flag
// to avoid multiple expensive redraws on a single load.
// if(!silent) {
@@ -198,6 +198,7 @@
* Caveat: the preview will be automatically enabled when ".cms-previewable" class is detected.
*/
disablePreview: function() {
+ this.setPendingURL(null);
this._loadUrl('about:blank');
this._block();
this.changeMode('content', false);
@@ -304,6 +305,18 @@
}
},
+ /** @var string A URL that should be displayed in this preview panel once it becomes visible */
+ PendingURL: null,
+
+ oncolumnvisibilitychanged: function() {
+ var url = this.getPendingURL();
+ if (url && !this.is('.column-hidden')) {
+ this.setPendingURL(null);
+ this._loadUrl(url);
+ this._unblock();
+ }
+ },
+
/**
* Update preview whenever a form is submitted.
* This is an alternative to the LeftAndmMain::loadPanel functionality which we already
@@ -369,20 +382,37 @@
});
}
+ var url = null;
+
if (currentState[0]) {
// State is available on the newly loaded content. Get it.
- this._loadUrl(currentState[0].url);
- this._unblock();
+ url = currentState[0].url;
} else if (states.length) {
// Fall back to the first available content state.
this.setCurrentStateName(states[0].name);
- this._loadUrl(states[0].url);
- this._unblock();
+ url = states[0].url;
} else {
// No state available at all.
this.setCurrentStateName(null);
+ }
+
+ // If this preview panel isn't visible at the moment, delay loading the URL until it (maybe) is later
+ if (this.is('.column-hidden')) {
+ this.setPendingURL(url);
+ this._loadUrl('about:blank');
this._block();
}
+ else {
+ this.setPendingURL(null);
+
+ if (url) {
+ this._loadUrl(url);
+ this._unblock();
+ }
+ else {
+ this._block();
+ }
+ }
return this;
},
@@ -104,8 +104,7 @@ jQuery.noConflict();
statusMessage(decodeURIComponent(msg), msgType);
}
});
-
-
+
/**
* Main LeftAndMain interface with some control panel and an edit form.
*
@@ -190,13 +189,22 @@ jQuery.noConflict();
},
/**
- * Change the options of the threeColumnCompressor layout, and trigger layouting. You can provide any or
- * all options. The remaining options will not be changed.
+ * Change the options of the threeColumnCompressor layout, and trigger layouting if needed.
+ * You can provide any or all options. The remaining options will not be changed.
*/
updateLayoutOptions: function(newSpec) {
var spec = this.getLayoutOptions();
- $.extend(spec, newSpec);
- this.redraw();
+
+ var dirty = false;
+
+ for (var k in newSpec) {
+ if (spec[k] !== newSpec[k]) {
+ spec[k] = newSpec[k];
+ dirty = true;
+ }
+ }
+
+ if (dirty) this.redraw();
},
/**
@@ -206,7 +214,6 @@ jQuery.noConflict();
this.updateLayoutOptions({
mode: 'split'
});
- this.redraw();
},
/**
@@ -216,7 +223,6 @@ jQuery.noConflict();
this.updateLayoutOptions({
mode: 'content'
});
- this.redraw();
},
/**
@@ -226,10 +232,13 @@ jQuery.noConflict();
this.updateLayoutOptions({
mode: 'preview'
});
- this.redraw();
},
+ RedrawSuppression: false,
+
redraw: function() {
+ if (this.getRedrawSuppression()) return;
+
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
// Reset the algorithm.
@@ -247,9 +256,9 @@ jQuery.noConflict();
this.layout();
// Redraw on all the children that need it
- this.find('.cms-panel-layout').redraw();
- this.find('.cms-content-fields[data-layout-type]').redraw();
- this.find('.cms-edit-form[data-layout-type]').redraw();
+ this.find('.cms-panel-layout').redraw();
+ this.find('.cms-content-fields[data-layout-type]').redraw();
+ this.find('.cms-edit-form[data-layout-type]').redraw();
this.find('.cms-preview').redraw();
this.find('.cms-content').redraw();
},
@@ -504,62 +513,67 @@ jQuery.noConflict();
newFragments[guessFragment] = $data;
}
- // Replace each fragment individually
- $.each(newFragments, function(newFragment, html) {
- var contentEl = $('[data-pjax-fragment]').filter(function() {
- return $.inArray(newFragment, $(this).data('pjaxFragment').split(' ')) != -1;
- }), newContentEl = $(html);
+ this.setRedrawSuppression(true);
+ try {
+ // Replace each fragment individually
+ $.each(newFragments, function(newFragment, html) {
+ var contentEl = $('[data-pjax-fragment]').filter(function() {
+ return $.inArray(newFragment, $(this).data('pjaxFragment').split(' ')) != -1;
+ }), newContentEl = $(html);
+
+ // Add to result collection
+ if(newContentEls) newContentEls.add(newContentEl);
+ else newContentEls = newContentEl;
+
+ // Update panels
+ if(newContentEl.find('.cms-container').length) {
+ throw 'Content loaded via ajax is not allowed to contain tags matching the ".cms-container" selector to avoid infinite loops';
+ }
- // Add to result collection
- if(newContentEls) newContentEls.add(newContentEl);
- else newContentEls = newContentEl;
-
- // Update panels
- if(newContentEl.find('.cms-container').length) {
- throw 'Content loaded via ajax is not allowed to contain tags matching the ".cms-container" selector to avoid infinite loops';
- }
-
- // Set loading state and store element state
- var origStyle = contentEl.attr('style');
- var origParent = contentEl.parent();
- var origParentLayoutApplied = (typeof origParent.data('jlayout')!=='undefined');
- var layoutClasses = ['east', 'west', 'center', 'north', 'south', 'column-hidden'];
- var elemClasses = contentEl.attr('class');
- var origLayoutClasses = [];
- if(elemClasses) {
- origLayoutClasses = $.grep(
- elemClasses.split(' '),
- function(val) { return ($.inArray(val, layoutClasses) >= 0);}
- );
- }
-
- newContentEl
- .removeClass(layoutClasses.join(' '))
- .addClass(origLayoutClasses.join(' '));
- if(origStyle) newContentEl.attr('style', origStyle);
-
- // Allow injection of inline styles, as they're not allowed in the document body.
- // Not handling this through jQuery.ondemand to avoid parsing the DOM twice.
- var styles = newContentEl.find('style').detach();
- if(styles.length) $(document).find('head').append(styles);
-
- // Replace panel completely (we need to override the "layout" attribute, so can't replace the child instead)
- contentEl.replaceWith(newContentEl);
-
- // Force jlayout to rebuild internal hierarchy to point to the new elements.
- // This is only necessary for elements that are at least 3 levels deep. 2nd level elements will
- // be taken care of when we lay out the top level element (.cms-container).
- if (!origParent.is('.cms-container') && origParentLayoutApplied) {
- origParent.layout();
- }
- });
+ // Set loading state and store element state
+ var origStyle = contentEl.attr('style');
+ var origParent = contentEl.parent();
+ var origParentLayoutApplied = (typeof origParent.data('jlayout')!=='undefined');
+ var layoutClasses = ['east', 'west', 'center', 'north', 'south', 'column-hidden'];
+ var elemClasses = contentEl.attr('class');
+ var origLayoutClasses = [];
+ if(elemClasses) {
+ origLayoutClasses = $.grep(
+ elemClasses.split(' '),
+ function(val) { return ($.inArray(val, layoutClasses) >= 0);}
+ );
+ }
- // Re-init tabs (in case the form tag itself is a tabset)
- var newForm = newContentEls.filter('form');
- if(newForm.hasClass('cms-tabset')) newForm.removeClass('cms-tabset').addClass('cms-tabset');
+ newContentEl
+ .removeClass(layoutClasses.join(' '))
+ .addClass(origLayoutClasses.join(' '));
+ if(origStyle) newContentEl.attr('style', origStyle);
- this.redraw();
+ // Allow injection of inline styles, as they're not allowed in the document body.
+ // Not handling this through jQuery.ondemand to avoid parsing the DOM twice.
+ var styles = newContentEl.find('style').detach();
+ if(styles.length) $(document).find('head').append(styles);
+ // Replace panel completely (we need to override the "layout" attribute, so can't replace the child instead)
+ contentEl.replaceWith(newContentEl);
+
+ // Force jlayout to rebuild internal hierarchy to point to the new elements.
+ // This is only necessary for elements that are at least 3 levels deep. 2nd level elements will
+ // be taken care of when we lay out the top level element (.cms-container).
+ if (!origParent.is('.cms-container') && origParentLayoutApplied) {
+ origParent.layout();
+ }
+ });
+
+ // Re-init tabs (in case the form tag itself is a tabset)
+ var newForm = newContentEls.filter('form');
+ if(newForm.hasClass('cms-tabset')) newForm.removeClass('cms-tabset').addClass('cms-tabset');
+ }
+ finally {
+ this.setRedrawSuppression(false);
+ }
+
+ this.redraw();
this.restoreTabState((state && typeof state.data.tabState !== 'undefined') ? state.data.tabState : null);
return newContentEls;

0 comments on commit afd3e3f

Please sign in to comment.