diff --git a/code/ModelAdmin.php b/code/ModelAdmin.php index bb383006..1a327813 100755 --- a/code/ModelAdmin.php +++ b/code/ModelAdmin.php @@ -149,6 +149,7 @@ abstract class ModelAdmin extends LeftAndMain { Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/ui.tabs.js'); Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery/jquery_improvements.js'); Requirements::javascript(CMS_DIR . '/javascript/ModelAdmin.js'); + Requirements::javascript(CMS_DIR . '/javascript/ModelAdmin.History.js'); } /** diff --git a/css/ModelAdmin.css b/css/ModelAdmin.css index a6b20fa5..6022043a 100644 --- a/css/ModelAdmin.css +++ b/css/ModelAdmin.css @@ -146,3 +146,20 @@ form .message { margin: .3em 0; padding: 0.3em; } + +body.ModelAdmin .historyNav { + background: #aaa; + padding: 0 10px; + overflow: auto; +} + body.ModelAdmin .historyNav a { + display: block; + color: white; + margin: 3px 0; + } + body.ModelAdmin .historyNav a.back { + float: left; + } + body.ModelAdmin .historyNav a.forward { + float: right; + } \ No newline at end of file diff --git a/javascript/ModelAdmin.History.js b/javascript/ModelAdmin.History.js new file mode 100644 index 00000000..a8a1b93d --- /dev/null +++ b/javascript/ModelAdmin.History.js @@ -0,0 +1,156 @@ +(function($) { + + /** + * A simple ajax browser history implementation tailored towards + * navigating through search results and different forms loaded into + * the ModelAdmin right panels. The logic listens to search and form loading + * events, keeps track of the loaded URLs, and will display graphical back/forward + * buttons where appropriate. A search action will cause the history to be reset. + * + * Note: The logic does not replay save operations or hook into any form actions. + * + * Available Events: + * - historyAdd + * - historyStart + * - historyGoFoward + * - historyGoBack + * + * @todo Switch tab state when re-displaying search forms + * @todo Reload search parameters into forms + * + * @name ss.ModelAdmin + */ + $('.ModelAdmin').concrete('ss', function($){ + return/** @lends ss.ModelAdmin */ { + + History: [], + + Future: [], + + onmatch: function() { + var self = this; + + this._super(); + + // generate markup + this.find('#right').prepend( + '
' + + '< ' + ss.i18n._t('ModelAdmin.HISTORYBACK', 'back') + '' + + '' + ss.i18n._t('ModelAdmin.HISTORYFORWARD', 'forward') + ' >' + + '
' + ).find('.back,.forward').hide(); + + this.find('.historyNav .back').live('click', function() { + self.goBack(); + return false; + }); + + this.find('.historyNav .forward').live('click', function() { + self.goForward(); + return false; + }); + }, + + redraw: function() { + this.find('.historyNav .forward').toggle(Boolean(this.Future().length > 0)); + this.find('.historyNav .back').toggle(Boolean(this.History().length > 1)); + }, + + startHistory: function(url, data) { + this.trigger('historyStart', {url: url, data: data}); + + this.setHistory([]); + this.addHistory(url, data); + }, + + /** + * Add an item to the history, to be accessed by goBack and goForward + */ + addHistory: function(url, data) { + this.trigger('historyAdd', {url: url, data: data}); + + // Combine data into URL + if(data) { + if(url.indexOf('?') == -1) url += '?' + $.param(data); + else url += '&' + $.param(data); + } + + // Add to history + this.History().push(url); + + // Reset future + this.setFuture([]); + + this.redraw(); + }, + + goBack: function() { + if(this.History() && this.History().length) { + if(this.Future() == null) this.setFuture([]); + + var currentPage = this.History().pop(); + var previousPage = this.History()[this.History().length-1]; + + this.Future().push(currentPage); + + this.trigger('historyGoBack', {url:previousPage}); + + // load new location + $('#Form_EditForm').concrete('ss').loadForm(previousPage); + + this.redraw(); + } + }, + + goForward: function() { + if(this.Future() && this.Future().length) { + if(this.Future() == null) this.setFuture([]); + + var nextPage = this.Future().pop(); + + this.History().push(nextPage); + + this.trigger('historyGoForward', {url:nextPage}); + + // load new location + $('#Form_EditForm').concrete('ss').loadForm(nextPage); + + this.redraw(); + } + } + }; + }); + + /** + * A search action will cause the history to be reset. + */ + $('#SearchForm_holder form').concrete('ss', function($) { + return{ + onmatch: function() { + var self = this; + this.bind('beforeSubmit', function(e) { + $('.ModelAdmin').concrete('ss').startHistory( + self.attr('action'), + self.serializeArray() + ); + }); + } + }; + }); + + /** + * We have to apply this to the result table buttons instead of the + * more generic form loading. + */ + $('form[name=Form_ResultsForm] tbody td a').concrete('ss', function($) { + return{ + onmatch: function() { + var self = this; + this.bind('click', function(e) { + $('.ModelAdmin').addHistory(self.attr('href')); + }); + } + }; + }); + +})(jQuery); \ No newline at end of file diff --git a/javascript/ModelAdmin.js b/javascript/ModelAdmin.js index 5635ce57..6583bfbd 100644 --- a/javascript/ModelAdmin.js +++ b/javascript/ModelAdmin.js @@ -9,7 +9,7 @@ * @todo alias the $ function instead of literal jQuery */ (function($) { - + ////////////////////////////////////////////////////////////////// // Search form ////////////////////////////////////////////////////////////////// @@ -43,9 +43,12 @@ $('#SearchForm_holder form').concrete({ onmatch: function() { var self = this; + this.bind('submit', function(e) { // Import forms are processed without ajax if(self.is('#Form_ImportForm')) return true; + + self.trigger('beforeSubmit'); $('#Form_EditForm').concrete('ss').loadForm( self.attr('action'), @@ -129,7 +132,7 @@ this.bind('click', function() { var confirmed = confirm(ss.i18n._t('ModelAdmin.REALLYDELETE', 'Really delete?')); if(!confirmed) { - $(this).removeClass('loading') + $(this).removeClass('loading'); return false; } });