mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
ENHANCEMENT HTML5 History.pushState support in CMS
This commit is contained in:
parent
ff54044206
commit
2f2096cd3a
@ -69,6 +69,7 @@ class LeftAndMain extends Controller {
|
|||||||
*/
|
*/
|
||||||
static $allowed_actions = array(
|
static $allowed_actions = array(
|
||||||
'index',
|
'index',
|
||||||
|
'save',
|
||||||
'savetreenode',
|
'savetreenode',
|
||||||
'getitem',
|
'getitem',
|
||||||
'getsubtree',
|
'getsubtree',
|
||||||
@ -241,6 +242,9 @@ class LeftAndMain extends Controller {
|
|||||||
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jsizes/lib/jquery.sizes.js');
|
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jsizes/lib/jquery.sizes.js');
|
||||||
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jlayout.border.js');
|
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jlayout.border.js');
|
||||||
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jquery.jlayout.js');
|
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jquery.jlayout.js');
|
||||||
|
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/history-js/scripts/uncompressed/history.js');
|
||||||
|
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/history-js/scripts/uncompressed/history.html4.js');
|
||||||
|
Requirements::javascript(SAPPHIRE_ADMIN_DIR . '/thirdparty/history-js/scripts/uncompressed/history.adapter.jquery.js');
|
||||||
|
|
||||||
Requirements::javascript(THIRDPARTY_DIR . '/behaviour/behaviour.js');
|
Requirements::javascript(THIRDPARTY_DIR . '/behaviour/behaviour.js');
|
||||||
Requirements::javascript(THIRDPARTY_DIR . '/jquery-cookie/jquery.cookie.js');
|
Requirements::javascript(THIRDPARTY_DIR . '/jquery-cookie/jquery.cookie.js');
|
||||||
@ -300,6 +304,9 @@ class LeftAndMain extends Controller {
|
|||||||
SAPPHIRE_ADMIN_DIR . '/thirdparty/jsizes/lib/jquery.sizes.js',
|
SAPPHIRE_ADMIN_DIR . '/thirdparty/jsizes/lib/jquery.sizes.js',
|
||||||
SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jlayout.border.js',
|
SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jlayout.border.js',
|
||||||
SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jquery.jlayout.js',
|
SAPPHIRE_ADMIN_DIR . '/thirdparty/jlayout/lib/jquery.jlayout.js',
|
||||||
|
SAPPHIRE_ADMIN_DIR . '/thirdparty/history-js/scripts/uncompressed/history.js',
|
||||||
|
SAPPHIRE_ADMIN_DIR . '/thirdparty/history-js/scripts/uncompressed/history.adapter.jquery.js',
|
||||||
|
SAPPHIRE_ADMIN_DIR . '/thirdparty/history-js/scripts/uncompressed/history.html4.js',
|
||||||
THIRDPARTY_DIR . '/jstree/jquery.jstree.js',
|
THIRDPARTY_DIR . '/jstree/jquery.jstree.js',
|
||||||
SAPPHIRE_ADMIN_DIR . '/javascript/jquery-changetracker/lib/jquery.changetracker.js',
|
SAPPHIRE_ADMIN_DIR . '/javascript/jquery-changetracker/lib/jquery.changetracker.js',
|
||||||
SAPPHIRE_DIR . '/javascript/TreeDropdownField.js',
|
SAPPHIRE_DIR . '/javascript/TreeDropdownField.js',
|
||||||
@ -333,6 +340,16 @@ class LeftAndMain extends Controller {
|
|||||||
// TableListField.ss or Form.ss.
|
// TableListField.ss or Form.ss.
|
||||||
SSViewer::set_theme(null);
|
SSViewer::set_theme(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleRequest($request) {
|
||||||
|
$response = parent::handleRequest($request);
|
||||||
|
$response->addHeader('X-Controller', $this->class);
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function index($request) {
|
||||||
|
return ($this->isAjax()) ? $this->show($request) : $this->getViewer('index')->process($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,11 +20,7 @@
|
|||||||
|
|
||||||
var url = $(node).find('a:first').attr('href');
|
var url = $(node).find('a:first').attr('href');
|
||||||
if(url && url != '#') {
|
if(url && url != '#') {
|
||||||
var xmlhttp = self.loadForm(
|
window.History.pushState({}, '', url);
|
||||||
url,
|
|
||||||
null,
|
|
||||||
function(response) {}
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
self.removeForm();
|
self.removeForm();
|
||||||
}
|
}
|
||||||
@ -33,11 +29,20 @@
|
|||||||
this._super();
|
this._super();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onunmatch: function() {
|
||||||
|
this._super();
|
||||||
|
},
|
||||||
|
|
||||||
beforeLoad: function(url) {
|
beforeLoad: function(url) {
|
||||||
this.addClass('loading');
|
this.addClass('loading');
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
afterLoad: function(data, status, xhr) {
|
||||||
|
this.removeClass('loading');
|
||||||
|
this.replaceWith(data);
|
||||||
|
},
|
||||||
|
|
||||||
cleanup: function() {
|
cleanup: function() {
|
||||||
this.empty();
|
this.empty();
|
||||||
},
|
},
|
||||||
|
@ -79,6 +79,13 @@
|
|||||||
$(this).remove();
|
$(this).remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Remove all TinyMCE instances
|
||||||
|
if((typeof tinymce != 'undefined') && tinymce.editors) {
|
||||||
|
$(tinymce.editors).each(function() {
|
||||||
|
if(typeof(this.remove) == 'function') this.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this._super();
|
this._super();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -150,20 +157,6 @@
|
|||||||
this.trigger('validate', {isValid: isValid});
|
this.trigger('validate', {isValid: isValid});
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: cleanup
|
|
||||||
*
|
|
||||||
* Remove all the currently active TinyMCE editors.
|
|
||||||
* Note: Everything that calls this externally has an inappropriate coupling to TinyMCE.
|
|
||||||
*/
|
|
||||||
cleanup: function() {
|
|
||||||
if((typeof tinymce != 'undefined') && tinymce.editors) {
|
|
||||||
$(tinymce.editors).each(function() {
|
|
||||||
if(typeof(this.remove) == 'function') this.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -25,11 +25,18 @@
|
|||||||
*/
|
*/
|
||||||
$('.cms-menu-list').entwine({
|
$('.cms-menu-list').entwine({
|
||||||
onmatch: function() {
|
onmatch: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
// TODO Fix icon etc.
|
// TODO Fix icon etc.
|
||||||
// this.children('li').each(function() {
|
// this.children('li').each(function() {
|
||||||
// $(this).find('a:first').append('<span class="toggle">o</span>');
|
// $(this).find('a:first').append('<span class="toggle">o</span>');
|
||||||
// });
|
// });
|
||||||
|
|
||||||
|
$('.LeftAndMain').bind('afterstatechange', function(e, data) {
|
||||||
|
var controller = data.xhr.getResponseHeader('X-Controller');
|
||||||
|
if(controller) self.find('li#Menu-' + controller).select();
|
||||||
|
});
|
||||||
|
|
||||||
// Sync collapsed state with parent panel
|
// Sync collapsed state with parent panel
|
||||||
this.parents('.cms-panel:first').bind('toggle', function(e) {
|
this.parents('.cms-panel:first').bind('toggle', function(e) {
|
||||||
self.toggleClass('collapsed', $(this).hasClass('collapsed'));
|
self.toggleClass('collapsed', $(this).hasClass('collapsed'));
|
||||||
@ -92,6 +99,8 @@
|
|||||||
if(children.length) {
|
if(children.length) {
|
||||||
children.first().find('a').click();
|
children.first().find('a').click();
|
||||||
} else {
|
} else {
|
||||||
|
// Active menu item is set based on X-Controller ajax header,
|
||||||
|
// which matches one class on the menu
|
||||||
window.History.pushState({}, '', this.attr('href'));
|
window.History.pushState({}, '', this.attr('href'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,12 @@
|
|||||||
var url = $(this).find(':input[name=StageURLSegment]').val();
|
var url = $(this).find(':input[name=StageURLSegment]').val();
|
||||||
if(url) self.loadUrl(url + '&cms-preview-disabled=1');
|
if(url) self.loadUrl(url + '&cms-preview-disabled=1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.cms-container').bind('afterstatechange', function(e) {
|
||||||
|
// var url = ui.xmlhttp.getResponseHeader('x-frontend-url');
|
||||||
|
var url = $('.cms-edit-form').find(':input[name=StageURLSegment]').val();
|
||||||
|
if(url) self.loadUrl(url + '&cms-preview-disabled=1');
|
||||||
|
});
|
||||||
|
|
||||||
if(this.hasClass('is-expanded')) this.expand();
|
if(this.hasClass('is-expanded')) this.expand();
|
||||||
else this.collapse();
|
else this.collapse();
|
||||||
@ -51,12 +57,14 @@
|
|||||||
var doc = this.find('iframe')[0].contentDocument, container = this.getLayoutContainer();
|
var doc = this.find('iframe')[0].contentDocument, container = this.getLayoutContainer();
|
||||||
|
|
||||||
// Only load if we're in the "edit page" view
|
// Only load if we're in the "edit page" view
|
||||||
if(!container.hasClass('CMSMain')) return;
|
if(!container.hasClass('CMSMain') || container.hasClass('CMSPagesController')) return;
|
||||||
|
|
||||||
// Load this page in the admin interface if appropriate
|
// Load this page in the admin interface if appropriate
|
||||||
var id = $(doc).find('meta[name=x-page-id]').attr('content'), contentPanel = $('.cms-content');
|
var id = $(doc).find('meta[name=x-page-id]').attr('content'), contentPanel = $('.cms-content');
|
||||||
// TODO Remove hardcoding
|
// TODO Remove hardcoding
|
||||||
if(id && contentPanel.find(':input[name=ID]').val() != id) contentPanel.loadPanel('admin/page/edit/show/' + id);
|
if(id && contentPanel.find(':input[name=ID]').val() != id) {
|
||||||
|
window.History.pushState({}, '', 'admin/page/edit/show/' + id);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_fixIframeLinks: function() {
|
_fixIframeLinks: function() {
|
||||||
@ -69,7 +77,7 @@
|
|||||||
if (href && href.match(/^http:\/\//)) {
|
if (href && href.match(/^http:\/\//)) {
|
||||||
links[i].setAttribute('href', 'javascript:false');
|
links[i].setAttribute('href', 'javascript:false');
|
||||||
} else {
|
} else {
|
||||||
links[i].setAttribute('href', href + '?cms-preview=1');
|
links[i].setAttribute('href', href + '?cms-preview-disabled=1');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -41,13 +41,15 @@
|
|||||||
* loadnewpage - ...
|
* loadnewpage - ...
|
||||||
*/
|
*/
|
||||||
$('.LeftAndMain').entwine({
|
$('.LeftAndMain').entwine({
|
||||||
|
|
||||||
|
CurrentXHR: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor: onmatch
|
* Constructor: onmatch
|
||||||
*/
|
*/
|
||||||
onmatch: function() {
|
onmatch: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Browser detection
|
// Browser detection
|
||||||
if($.browser.msie && parseInt($.browser.version, 10) < 7) {
|
if($.browser.msie && parseInt($.browser.version, 10) < 7) {
|
||||||
$('.ss-loading-screen').append(
|
$('.ss-loading-screen').append(
|
||||||
@ -68,6 +70,10 @@
|
|||||||
$(window).unbind('resize', positionLoadingSpinner);
|
$(window).unbind('resize', positionLoadingSpinner);
|
||||||
|
|
||||||
$('.cms-edit-form').live('loadnewpage', function() {self.redraw()});
|
$('.cms-edit-form').live('loadnewpage', function() {self.redraw()});
|
||||||
|
|
||||||
|
History.Adapter.bind(window,'statechange',function(){
|
||||||
|
self.handleStateChange();
|
||||||
|
});
|
||||||
|
|
||||||
this._super();
|
this._super();
|
||||||
},
|
},
|
||||||
@ -77,6 +83,53 @@
|
|||||||
var editForm = this.find('.cms-edit-form[data-layout]').layout();
|
var editForm = this.find('.cms-edit-form[data-layout]').layout();
|
||||||
this.find('.cms-content').layout();
|
this.find('.cms-content').layout();
|
||||||
this.find('.cms-container').layout({resize: false})
|
this.find('.cms-container').layout({resize: false})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles ajax loading of new panels through the window.History object.
|
||||||
|
* To trigger loading, pass a new URL to window.History.pushState().
|
||||||
|
*
|
||||||
|
* Due to the nature of history management, no callbacks are allowed.
|
||||||
|
* Use the 'beforestatechange' and 'afterstatechange' events instead,
|
||||||
|
* or overwrite the beforeLoad() and afterLoad() methods on the
|
||||||
|
* DOM element you're loading the new content into.
|
||||||
|
* Although you can pass data into pushState(), it shouldn't contain
|
||||||
|
* DOM elements or callback closures.
|
||||||
|
*
|
||||||
|
* The passed URL should allow reconstructing important interface state
|
||||||
|
* without additional parameters, in the following use cases:
|
||||||
|
* - Explicit loading through History.pushState()
|
||||||
|
* - Implicit loading through browser navigation event triggered by the user (forward or back)
|
||||||
|
* - Full window refresh without ajax
|
||||||
|
* For example, a ModelAdmin search event should contain the search terms
|
||||||
|
* as URL parameters, and the result display should automatically appear
|
||||||
|
* if the URL is loaded without ajax.
|
||||||
|
*
|
||||||
|
* Alternatively, you can load new content via $('.cms-content').loadForm(<url>).
|
||||||
|
* In this case, the action won't be recorded in the browser history.
|
||||||
|
*/
|
||||||
|
handleStateChange: function() {
|
||||||
|
var self = this, h = window.History, state = h.getState();
|
||||||
|
|
||||||
|
// Don't allow parallel loading to avoid edge cases
|
||||||
|
if(this.getCurrentXHR()) this.getCurrentXHR().abort();
|
||||||
|
|
||||||
|
// TODO Support loading into multiple panels
|
||||||
|
var contentEl = $(state.data.selector || '.LeftAndMain .cms-content');
|
||||||
|
this.trigger('beforestatechange', {state: state});
|
||||||
|
contentEl.beforeLoad(state.url);
|
||||||
|
|
||||||
|
var xhr = $.ajax({
|
||||||
|
url: state.url,
|
||||||
|
success: function(data, status, xhr) {
|
||||||
|
// Update panels
|
||||||
|
contentEl.afterLoad(data, status, xhr);
|
||||||
|
self.redraw();
|
||||||
|
|
||||||
|
self.trigger('afterstatechange', {data: data, status: status, xhr: xhr});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setCurrentXHR(xhr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@
|
|||||||
redraw: function() {
|
redraw: function() {
|
||||||
this.find('.historyNav .forward').toggle(Boolean(this.getFuture().length > 0));
|
this.find('.historyNav .forward').toggle(Boolean(this.getFuture().length > 0));
|
||||||
this.find('.historyNav .back').toggle(Boolean(this.getHistory().length > 1));
|
this.find('.historyNav .back').toggle(Boolean(this.getHistory().length > 1));
|
||||||
|
|
||||||
|
this._super();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +118,7 @@
|
|||||||
this.trigger('historyGoBack', {url:previousPage});
|
this.trigger('historyGoBack', {url:previousPage});
|
||||||
|
|
||||||
// load new location
|
// load new location
|
||||||
$('.cms-edit-form').loadForm(previousPage);
|
$('.cms-content').loadForm(previousPage);
|
||||||
|
|
||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
@ -136,7 +138,7 @@
|
|||||||
this.trigger('historyGoForward', {url:nextPage});
|
this.trigger('historyGoForward', {url:nextPage});
|
||||||
|
|
||||||
// load new location
|
// load new location
|
||||||
$('.cms-edit-form').loadForm(nextPage);
|
$('.cms-content').loadForm(nextPage);
|
||||||
|
|
||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user