diff --git a/admin/javascript/LeftAndMain.Layout.js b/admin/javascript/LeftAndMain.Layout.js index a4b8c261c..b35bd4199 100644 --- a/admin/javascript/LeftAndMain.Layout.js +++ b/admin/javascript/LeftAndMain.Layout.js @@ -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; }; diff --git a/admin/javascript/LeftAndMain.Panel.js b/admin/javascript/LeftAndMain.Panel.js index c772d1a02..226a3a37d 100644 --- a/admin/javascript/LeftAndMain.Panel.js +++ b/admin/javascript/LeftAndMain.Panel.js @@ -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) { diff --git a/admin/javascript/LeftAndMain.Preview.js b/admin/javascript/LeftAndMain.Preview.js index a607e0474..7856e9da5 100644 --- a/admin/javascript/LeftAndMain.Preview.js +++ b/admin/javascript/LeftAndMain.Preview.js @@ -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; }, diff --git a/admin/javascript/LeftAndMain.js b/admin/javascript/LeftAndMain.js index 489db3aa4..c513df1af 100644 --- a/admin/javascript/LeftAndMain.js +++ b/admin/javascript/LeftAndMain.js @@ -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'; - } - - // 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); + // Add to result collection + if(newContentEls) newContentEls.add(newContentEl); + else newContentEls = newContentEl; - // 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); + // 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'; + } - // Replace panel completely (we need to override the "layout" attribute, so can't replace the child instead) - contentEl.replaceWith(newContentEl); + // 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);} + ); + } - // 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(); - } - }); + newContentEl + .removeClass(layoutClasses.join(' ')) + .addClass(origLayoutClasses.join(' ')); + if(origStyle) newContentEl.attr('style', origStyle); - // 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'); + // 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; diff --git a/main.php b/main.php index b7f2a9ca5..f60b5d9c9 100644 --- a/main.php +++ b/main.php @@ -138,7 +138,7 @@ $chain // Otherwise, we start up the session if needed, then check for admin if (!$canFlush) { - if(!isset($_SESSION) && (isset($_COOKIE[session_name()]) || isset($_REQUEST[session_name()]))) { + if(!isset($_SESSION) && Session::request_contains_session_id()) { Session::start(); }