diff --git a/admin/javascript/LeftAndMain.AddForm.js b/admin/javascript/LeftAndMain.AddForm.js index c1bd7d698..9afa50801 100644 --- a/admin/javascript/LeftAndMain.AddForm.js +++ b/admin/javascript/LeftAndMain.AddForm.js @@ -89,7 +89,7 @@ data.push({name:button.attr('name'),value:button.val()}); // TODO Should be set by hiddenfield already - jQuery('.cms-content').entwine('ss').submitForm( + jQuery('.cms-content').submitForm( this.attr('action'), null, function() { @@ -161,4 +161,4 @@ } }); }); -}(jQuery)); \ No newline at end of file +}(jQuery)); diff --git a/admin/javascript/LeftAndMain.BatchActions.js b/admin/javascript/LeftAndMain.BatchActions.js index f09f108fb..d8447aa82 100644 --- a/admin/javascript/LeftAndMain.BatchActions.js +++ b/admin/javascript/LeftAndMain.BatchActions.js @@ -282,83 +282,83 @@ } }); - }); - /** - * Class: #Form_BatchActionsForm :select[name=Action] - */ - $('#Form_BatchActionsForm select[name=Action]').entwine({ - - onmatch: function() { - this.trigger('change'); - this._super(); - }, - /** - * Function: onchange - * - * Parameters: - * (Event) e + * Class: #Form_BatchActionsForm :select[name=Action] */ - onchange: function(e) { - var form = $(e.target.form), btn = form.find(':submit'); - if($(e.target).val() == -1) { - btn.attr('disabled', 'disabled').button('refresh'); - } else { - btn.removeAttr('disabled').button('refresh'); - // form.submit(); - } + $('#Form_BatchActionsForm select[name=Action]').entwine({ + + onmatch: function() { + this.trigger('change'); + this._super(); + }, + + /** + * Function: onchange + * + * Parameters: + * (Event) e + */ + onchange: function(e) { + var form = $(e.target.form), btn = form.find(':submit'); + if($(e.target).val() == -1) { + btn.attr('disabled', 'disabled').button('refresh'); + } else { + btn.removeAttr('disabled').button('refresh'); + // form.submit(); + } - // TODO Should work by triggering change() along, but doesn't - entwine event bubbling? - this.trigger("liszt:updated"); + // TODO Should work by triggering change() along, but doesn't - entwine event bubbling? + this.trigger("liszt:updated"); - this._super(e); - } - }); - - $(document).ready(function() { - /** - * Publish selected pages action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/publish', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to publish?" - ); - return (confirmed) ? ids : false; + this._super(e); + } }); - - /** - * Unpublish selected pages action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/unpublish', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to unpublish?" - ); - return (confirmed) ? ids : false; - }); - - /** - * Delete selected pages action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/delete', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to delete?" - ); - return (confirmed) ? ids : false; - }); - - /** - * Delete selected pages from live action - */ - $('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/deletefromlive', function(ids) { - var confirmed = confirm( - "You have " + ids.length + " pages selected.\n\n" - + "Do your really want to delete these pages from live?" - ); - return (confirmed) ? ids : false; + + $(document).ready(function() { + /** + * Publish selected pages action + */ + $('#Form_BatchActionsForm').register('admin/batchactions/publish', function(ids) { + var confirmed = confirm( + "You have " + ids.length + " pages selected.\n\n" + + "Do your really want to publish?" + ); + return (confirmed) ? ids : false; + }); + + /** + * Unpublish selected pages action + */ + $('#Form_BatchActionsForm').register('admin/batchactions/unpublish', function(ids) { + var confirmed = confirm( + "You have " + ids.length + " pages selected.\n\n" + + "Do your really want to unpublish?" + ); + return (confirmed) ? ids : false; + }); + + /** + * Delete selected pages action + */ + $('#Form_BatchActionsForm').register('admin/batchactions/delete', function(ids) { + var confirmed = confirm( + "You have " + ids.length + " pages selected.\n\n" + + "Do your really want to delete?" + ); + return (confirmed) ? ids : false; + }); + + /** + * Delete selected pages from live action + */ + $('#Form_BatchActionsForm').register('admin/batchactions/deletefromlive', function(ids) { + var confirmed = confirm( + "You have " + ids.length + " pages selected.\n\n" + + "Do your really want to delete these pages from live?" + ); + return (confirmed) ? ids : false; + }); }); }); diff --git a/admin/javascript/LeftAndMain.Content.js b/admin/javascript/LeftAndMain.Content.js index 9c3ceb7f7..6a34704ec 100644 --- a/admin/javascript/LeftAndMain.Content.js +++ b/admin/javascript/LeftAndMain.Content.js @@ -185,60 +185,60 @@ this.removeClass('changed'); } }); - }); - /** - * Load edit form for the selected node when its clicked. - */ - $('.cms-content .cms-tree').entwine({ - onmatch: function() { - var self = this; + /** + * Load edit form for the selected node when its clicked. + */ + $('.cms-content .cms-tree').entwine({ + onmatch: function() { + var self = this; - this._super(); + this._super(); - this.bind('select_node.jstree', function(e, data) { - var node = data.rslt.obj, loadedNodeID = self.find(':input[name=ID]').val(), origEvent = data.args[2], container = $('.cms-container'); - - // Don't trigger unless coming from a click event. - // Avoids problems with automated section switches from tree to detail view - // when JSTree auto-selects elements on first load. - if(!origEvent) { - return false; - }else if($(origEvent.target).hasClass('jstree-icon') || $(origEvent.target).hasClass('jstree-pageicon')){ - // in case the click is not on the node title, ie on pageicon or dragicon, - return false; - } - - // Don't allow checking disabled nodes - if($(node).hasClass('disabled')) return false; + this.bind('select_node.jstree', function(e, data) { + var node = data.rslt.obj, loadedNodeID = self.find(':input[name=ID]').val(), origEvent = data.args[2], container = $('.cms-container'); + + // Don't trigger unless coming from a click event. + // Avoids problems with automated section switches from tree to detail view + // when JSTree auto-selects elements on first load. + if(!origEvent) { + return false; + }else if($(origEvent.target).hasClass('jstree-icon') || $(origEvent.target).hasClass('jstree-pageicon')){ + // in case the click is not on the node title, ie on pageicon or dragicon, + return false; + } + + // Don't allow checking disabled nodes + if($(node).hasClass('disabled')) return false; - // Don't allow reloading of currently selected node, - // mainly to avoid doing an ajax request on initial page load - if($(node).data('id') == loadedNodeID) return; + // Don't allow reloading of currently selected node, + // mainly to avoid doing an ajax request on initial page load + if($(node).data('id') == loadedNodeID) return; - var url = $(node).find('a:first').attr('href'); - if(url && url != '#') { + var url = $(node).find('a:first').attr('href'); + if(url && url != '#') { - // Ensure URL is absolute (important for IE) - if($.path.isExternal($(node).find('a:first'))) url = url = $.path.makeUrlAbsolute(url, $('base').attr('href')); - // Retain search parameters - if(document.location.search) url = $.path.addSearchParams(url, document.location.search.replace(/^\?/, '')); - // Load new page - container.entwine('ss').loadPanel(url); - } else { - self.removeForm(); - } - }); - } - }); + // Ensure URL is absolute (important for IE) + if($.path.isExternal($(node).find('a:first'))) url = url = $.path.makeUrlAbsolute(url, $('base').attr('href')); + // Retain search parameters + if(document.location.search) url = $.path.addSearchParams(url, document.location.search.replace(/^\?/, '')); + // Load new page + container.loadPanel(url); + } else { + self.removeForm(); + } + }); + } + }); - $('.cms-content.loading,.cms-edit-form.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({ - onmatch: function() { - this.append('
'); - }, - onunmatch: function() { - this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove(); - } + $('.cms-content.loading,.cms-edit-form.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({ + onmatch: function() { + this.append('
'); + }, + onunmatch: function() { + this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove(); + } + }); }); })(jQuery); diff --git a/admin/javascript/LeftAndMain.js b/admin/javascript/LeftAndMain.js index 9828cebfc..e6090a976 100644 --- a/admin/javascript/LeftAndMain.js +++ b/admin/javascript/LeftAndMain.js @@ -420,36 +420,36 @@ jQuery.noConflict(); * Add styling to all contained buttons, and create buttonsets if required. */ $('.cms .Actions').entwine({ - onmatch: function() { - this.find('.ss-ui-button').click(function() { - var form = this.form; - // forms don't natively store the button they've been triggered with - if(form) { - form.clickedButton = this; - // Reset the clicked button shortly after the onsubmit handlers - // have fired on the form - setTimeout(function() {form.clickedButton = null;}, 10); - } + onmatch: function() { + this.find('.ss-ui-button').click(function() { + var form = this.form; + // forms don't natively store the button they've been triggered with + if(form) { + form.clickedButton = this; + // Reset the clicked button shortly after the onsubmit handlers + // have fired on the form + setTimeout(function() {form.clickedButton = null;}, 10); + } + }); + + this.redraw(); + this._super(); + }, + redraw: function() { + // Remove whitespace to avoid gaps with inline elements + this.contents().filter(function() { + return (this.nodeType == 3 && !/\S/.test(this.nodeValue)); + }).remove(); + + // Init buttons if required + this.find('.ss-ui-button').each(function() { + if(!$(this).data('button')) $(this).button(); }); - - this.redraw(); - this._super(); - }, - redraw: function() { - // Remove whitespace to avoid gaps with inline elements - this.contents().filter(function() { - return (this.nodeType == 3 && !/\S/.test(this.nodeValue)); - }).remove(); - - // Init buttons if required - this.find('.ss-ui-button').each(function() { - if(!$(this).data('button')) $(this).button(); - }); - - // Mark up buttonsets - this.find('.ss-ui-buttonset').buttonset(); - } - }); + + // Mark up buttonsets + this.find('.ss-ui-buttonset').buttonset(); + } + }); /** * Duplicates functionality in DateField.js, but due to using entwine we can match @@ -508,166 +508,166 @@ jQuery.noConflict(); }); } }); - }); - /** - * Overload the default GridField behaviour (open a new URL in the browser) - * with the CMS-specific ajax loading. - */ - $('.cms .ss-gridfield').entwine({ - showDetailView: function(url) { - // Include any GET parameters from the current URL, as the view state might depend on it. - // For example, a list prefiltered through external search criteria might be passed to GridField. - if(window.location.search) url += window.location.search; - $('.cms-container').entwine('ss').loadPanel(url); - } - }); + /** + * Overload the default GridField behaviour (open a new URL in the browser) + * with the CMS-specific ajax loading. + */ + $('.cms .ss-gridfield').entwine({ + showDetailView: function(url) { + // Include any GET parameters from the current URL, as the view state might depend on it. + // For example, a list prefiltered through external search criteria might be passed to GridField. + if(window.location.search) url += window.location.search; + $('.cms-container').loadPanel(url); + } + }); - /** - * Generic search form in the CMS, often hooked up to a GridField results display. - */ - $('.cms-search-form').entwine({ - - onsubmit: function() { - // Remove empty elements and make the URL prettier - var nonEmptyInputs = this.find(':input:not(:submit)').filter(function() { - // Use fieldValue() from jQuery.form plugin rather than jQuery.val(), - // as it handles checkbox values more consistently - var vals = $.grep($(this).fieldValue(), function(val) { return (val);}); - return (vals.length); - }); - var url = this.attr('action'); - if(nonEmptyInputs.length) url = $.path.addSearchParams(url, nonEmptyInputs.serialize()); - - var container = this.closest('.cms-container'); - container.find('.cms-edit-form').tabs('select',0); //always switch to the first tab (list view) when searching - container .entwine('ss').loadPanel(url); - return false; - }, - /** - * Resets are processed on the serverside, so need to trigger a submit. + * Generic search form in the CMS, often hooked up to a GridField results display. + */ + $('.cms-search-form').entwine({ + + onsubmit: function() { + // Remove empty elements and make the URL prettier + var nonEmptyInputs = this.find(':input:not(:submit)').filter(function() { + // Use fieldValue() from jQuery.form plugin rather than jQuery.val(), + // as it handles checkbox values more consistently + var vals = $.grep($(this).fieldValue(), function(val) { return (val);}); + return (vals.length); + }); + var url = this.attr('action'); + if(nonEmptyInputs.length) url = $.path.addSearchParams(url, nonEmptyInputs.serialize()); + + var container = this.closest('.cms-container'); + container.find('.cms-edit-form').tabs('select',0); //always switch to the first tab (list view) when searching + container.loadPanel(url); + return false; + }, + + /** + * Resets are processed on the serverside, so need to trigger a submit. + */ + onreset: function(e) { + this.clearForm(); + this.submit(); + } + + }); + + /** + * Simple toggle link, which points to a DOm element by its ID selector + * in the href attribute (which doubles as an anchor link to that element). */ - onreset: function(e) { - this.clearForm(); - this.submit(); - } + $('.cms .cms-help-toggle').entwine({ + onmatch: function() { + this._super(); - }); + $(this.attr('href')).hide(); + }, + onclick: function(e) { + $(this.attr('href')).toggle(); + e.preventDefault(); + } + }); - /** - * Simple toggle link, which points to a DOm element by its ID selector - * in the href attribute (which doubles as an anchor link to that element). - */ - $('.cms .cms-help-toggle').entwine({ - onmatch: function() { - this._super(); + /** + * Allows to lazy load a panel, by leaving it empty + * and declaring a URL to load its content via a 'url' HTML5 data attribute. + * The loaded HTML is cached, with cache key being the 'url' attribute. + * In order for this to work consistently, we assume that the responses are stateless. + * To avoid caching, add a 'deferred-no-cache' to the node. + */ + window._panelDeferredCache = {}; + $('.cms-panel-deferred').entwine({ + onmatch: function() { + this._super(); + this.redraw(); + }, + onunmatch: function() { + // Save the HTML state at the last possible moment. + // Don't store the DOM to avoid memory leaks. + if(!this.data('deferredNoCache')) window._panelDeferredCache[this.data('url')] = this.html(); + this._super(); + }, + redraw: function() { + var self = this, url = this.data('url'); + if(!url) throw 'Elements of class .cms-panel-deferred need a "data-url" attribute'; - $(this.attr('href')).hide(); - }, - onclick: function(e) { - $(this.attr('href')).toggle(); - e.preventDefault(); - } - }); + this._super(); - /** - * Allows to lazy load a panel, by leaving it empty - * and declaring a URL to load its content via a 'url' HTML5 data attribute. - * The loaded HTML is cached, with cache key being the 'url' attribute. - * In order for this to work consistently, we assume that the responses are stateless. - * To avoid caching, add a 'deferred-no-cache' to the node. - */ - window._panelDeferredCache = {}; - $('.cms-panel-deferred').entwine({ - onmatch: function() { - this._super(); - this.redraw(); - }, - onunmatch: function() { - // Save the HTML state at the last possible moment. - // Don't store the DOM to avoid memory leaks. - if(!this.data('deferredNoCache')) window._panelDeferredCache[this.data('url')] = this.html(); - this._super(); - }, - redraw: function() { - var self = this, url = this.data('url'); - if(!url) throw 'Elements of class .cms-panel-deferred need a "data-url" attribute'; - - this._super(); - - // If the node is empty, try to either load it from cache or via ajax. - if(!this.children().length) { - if(!this.data('deferredNoCache') && typeof window._panelDeferredCache[url] !== 'undefined') { - this.html(window._panelDeferredCache[url]); - } else { - this.addClass('loading'); - $.ajax({ - url: url, - complete: function() { - self.removeClass('loading'); - }, - success: function(data, status, xhr) { - self.html(data); - } - }); + // If the node is empty, try to either load it from cache or via ajax. + if(!this.children().length) { + if(!this.data('deferredNoCache') && typeof window._panelDeferredCache[url] !== 'undefined') { + this.html(window._panelDeferredCache[url]); + } else { + this.addClass('loading'); + $.ajax({ + url: url, + complete: function() { + self.removeClass('loading'); + }, + success: function(data, status, xhr) { + self.html(data); + } + }); + } } } - } - }); + }); - /** - * Lightweight wrapper around jQuery UI tabs. - * Ensures that anchor links are set properly, - * and any nested tabs are scrolled if they have - * their height explicitly set. This is important - * for forms inside the CMS layout. - */ - $('.cms-tabset').entwine({ - onmatch: function() { - // Can't name redraw() as it clashes with other CMS entwine classes - this.redrawTabs(); - this._super(); - }, - - redrawTabs: function() { - this.rewriteHashlinks(); - - var id = this.attr('id'), cookieId = 'ui-tabs-' + id, - selectedTab = this.find('ul:first .ui-state-selected'); - - // Fix for wrong cookie storage of deselected tabs - if($.cookie && id && $.cookie(cookieId) == -1) $.cookie(cookieId, 0); - this.tabs({ - cookie: ($.cookie && id) ? { expires: 30, path: '/', name: cookieId } : false, - ajaxOptions: { - // Overwrite ajax loading to use CMS logic instead - beforeSend: function(xhr, settings) { - var makeAbs = $.path.makeUrlAbsolute, - baseUrl = $('base').attr('href'), - isSame = (makeAbs(settings.url, baseUrl) == makeAbs(document.location.href)); - - if(!isSame) $('.cms-container').entwine('ss').loadPanel(settings.url); - return false; - } - }, - selected: (selectedTab.index() != -1) ? selectedTab.index() : 0 - }); - }, - /** - * Replace prefixes for all hashlinks in tabs. - * SSViewer rewrites them from "#Root_MyTab" to - * e.g. "/admin/#Root_MyTab" which makes them - * unusable for jQuery UI. + * Lightweight wrapper around jQuery UI tabs. + * Ensures that anchor links are set properly, + * and any nested tabs are scrolled if they have + * their height explicitly set. This is important + * for forms inside the CMS layout. */ - rewriteHashlinks: function() { - $(this).find('ul a').each(function() { - var href = $(this).attr('href').replace(/.*(#.*)/, '$1'); - if(href) $(this).attr('href', href); - }); - } + $('.cms-tabset').entwine({ + onmatch: function() { + // Can't name redraw() as it clashes with other CMS entwine classes + this.redrawTabs(); + this._super(); + }, + + redrawTabs: function() { + this.rewriteHashlinks(); + + var id = this.attr('id'), cookieId = 'ui-tabs-' + id, + selectedTab = this.find('ul:first .ui-state-selected'); + + // Fix for wrong cookie storage of deselected tabs + if($.cookie && id && $.cookie(cookieId) == -1) $.cookie(cookieId, 0); + this.tabs({ + cookie: ($.cookie && id) ? { expires: 30, path: '/', name: cookieId } : false, + ajaxOptions: { + // Overwrite ajax loading to use CMS logic instead + beforeSend: function(xhr, settings) { + var makeAbs = $.path.makeUrlAbsolute, + baseUrl = $('base').attr('href'), + isSame = (makeAbs(settings.url, baseUrl) == makeAbs(document.location.href)); + + if(!isSame) $('.cms-container').loadPanel(settings.url); + return false; + } + }, + selected: (selectedTab.index() != -1) ? selectedTab.index() : 0 + }); + }, + + /** + * Replace prefixes for all hashlinks in tabs. + * SSViewer rewrites them from "#Root_MyTab" to + * e.g. "/admin/#Root_MyTab" which makes them + * unusable for jQuery UI. + */ + rewriteHashlinks: function() { + $(this).find('ul a').each(function() { + var href = $(this).attr('href').replace(/.*(#.*)/, '$1'); + if(href) $(this).attr('href', href); + }); + } + }); }); }(jQuery)); diff --git a/admin/javascript/ssui.core.js b/admin/javascript/ssui.core.js index b071f6e74..931b0af29 100644 --- a/admin/javascript/ssui.core.js +++ b/admin/javascript/ssui.core.js @@ -1,34 +1,36 @@ (function($) { - /** - * Creates a jQuery UI tab navigation bar, detached from the container DOM structure. - */ - $('.ss-ui-tabs-nav').entwine({ - onmatch: function() { - this.redraw(); - - this._super(); - }, + $.entwine('ss', function($) { + /** + * Creates a jQuery UI tab navigation bar, detached from the container DOM structure. + */ + $('.ss-ui-tabs-nav').entwine({ + onmatch: function() { + this.redraw(); - redraw: function() { - this.addClass('ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-panel ui-corner-bottom'); - this.find('ul').addClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all'); - this.find('li').addClass('ui-state-default ui-corner-top'); - // TODO Figure out selected tab - var selected = this.find('li.current'); - if(!selected.length) selected = this.find('li:first'); - selected.selectIt(); - } - }); - - $('.ss-ui-tabs-nav li').entwine({ - onclick: function() { - this.selectIt(); - }, - selectIt: function() { - var cls = 'ui-tabs-selected ui-state-active'; - this.addClass(cls).siblings().not(this).removeClass(cls); - } + this._super(); + }, + + redraw: function() { + this.addClass('ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-panel ui-corner-bottom'); + this.find('ul').addClass('ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all'); + this.find('li').addClass('ui-state-default ui-corner-top'); + // TODO Figure out selected tab + var selected = this.find('li.current'); + if(!selected.length) selected = this.find('li:first'); + selected.selectIt(); + } + }); + + $('.ss-ui-tabs-nav li').entwine({ + onclick: function() { + this.selectIt(); + }, + selectIt: function() { + var cls = 'ui-tabs-selected ui-state-active'; + this.addClass(cls).siblings().not(this).removeClass(cls); + } + }); }); /** diff --git a/javascript/GridField.js b/javascript/GridField.js index 482612ade..55f8f02e0 100644 --- a/javascript/GridField.js +++ b/javascript/GridField.js @@ -1,335 +1,338 @@ (function($){ - $('.ss-gridfield').entwine({ - /** - * @param {Object} Additional options for jQuery.ajax() call - * @param {successCallback} callback to call after reloading succeeded. - */ + $.entwine('ss', function($) { + $('.ss-gridfield').entwine({ + /** + * @param {Object} Additional options for jQuery.ajax() call + * @param {successCallback} callback to call after reloading succeeded. + */ - reload: function(ajaxOpts, successCallback) { - var self = this, form = this.closest('form'), - focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh - data = form.find(':input').serializeArray(); + reload: function(ajaxOpts, successCallback) { + var self = this, form = this.closest('form'), + focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh + data = form.find(':input').serializeArray(); - if(!ajaxOpts) ajaxOpts = {}; - if(!ajaxOpts.data) ajaxOpts.data = []; - ajaxOpts.data = ajaxOpts.data.concat(data); + if(!ajaxOpts) ajaxOpts = {}; + if(!ajaxOpts.data) ajaxOpts.data = []; + ajaxOpts.data = ajaxOpts.data.concat(data); - // Include any GET parameters from the current URL, as the view state might depend on it. - // For example, a list prefiltered through external search criteria might be passed to GridField. - if(window.location.search) { - ajaxOpts.data = window.location.search.replace(/^\?/, '') + '&' + $.param(ajaxOpts.data); - } - - form.addClass('loading'); - - $.ajax($.extend({}, { - headers: {"X-Pjax" : 'CurrentField'}, - type: "POST", - url: this.data('url'), - dataType: 'html', - success: function(data) { - // Replace the grid field with response, not the form. - // TODO Only replaces all its children, to avoid replacing the current scope - // of the executing method. Means that it doesn't retrigger the onmatch() on the main container. - self.empty().append($(data).children()); - - // Refocus previously focused element. Useful e.g. for finding+adding - // multiple relationships via keyboard. - if(focusedElName) self.find(':input[name="' + focusedElName + '"]').focus(); - - var content; - if(ajaxOpts.data[0].filter=="show"){ - content = ''; - self.addClass('show-filter').find('.filter-header').show(); - }else{ - content = ''; - self.removeClass('show-filter').find('.filter-header').hide(); - } - - self.find('.sortable-header th:last').html(content); - - form.removeClass('loading'); - if(successCallback) successCallback.apply(this, arguments); - self.trigger('reload', self); - }, - error: function(e) { - alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION')); - form.removeClass('loading'); + // Include any GET parameters from the current URL, as the view state might depend on it. + // For example, a list prefiltered through external search criteria might be passed to GridField. + if(window.location.search) { + ajaxOpts.data = window.location.search.replace(/^\?/, '') + '&' + $.param(ajaxOpts.data); } - }, ajaxOpts)); - }, - showDetailView: function(url) { - window.location.href = url; - }, - getItems: function() { - return this.find('.ss-gridfield-item'); - }, - /** - * @param {String} - * @param {Mixed} - */ - setState: function(k, v) { - var state = this.getState(); - state[k] = v; - this.find(':input[name="' + this.data('name') + '[GridState]"]').val(JSON.stringify(state)); - }, - /** - * @return {Object} - */ - getState: function() { - return JSON.parse(this.find(':input[name="' + this.data('name') + '[GridState]"]').val()); - } - }); - $('.ss-gridfield *').entwine({ - getGridField: function() { - return this.closest('.ss-gridfield'); - } - }); + form.addClass('loading'); + $.ajax($.extend({}, { + headers: {"X-Pjax" : 'CurrentField'}, + type: "POST", + url: this.data('url'), + dataType: 'html', + success: function(data) { + // Replace the grid field with response, not the form. + // TODO Only replaces all its children, to avoid replacing the current scope + // of the executing method. Means that it doesn't retrigger the onmatch() on the main container. + self.empty().append($(data).children()); + // Refocus previously focused element. Useful e.g. for finding+adding + // multiple relationships via keyboard. + if(focusedElName) self.find(':input[name="' + focusedElName + '"]').focus(); - $('.ss-gridfield :button[name=showFilter]').entwine({ - onclick: function(e) { - $('.filter-header') - .show('slow') // animate visibility - .find(':input:first').focus(); // focus first search field - this.closest('.ss-gridfield').addClass('show-filter'); - this.parent().html(''); - e.preventDefault(); - } - }); + var content; + if(ajaxOpts.data[0].filter=="show"){ + content = ''; + self.addClass('show-filter').find('.filter-header').show(); + }else{ + content = ''; + self.removeClass('show-filter').find('.filter-header').hide(); + } + self.find('.sortable-header th:last').html(content); - $('.ss-gridfield .ss-gridfield-item').entwine({ - onclick: function(e) { - if($(e.target).closest('.action').length) { - this._super(e); - return false; + form.removeClass('loading'); + if(successCallback) successCallback.apply(this, arguments); + self.trigger('reload', self); + }, + error: function(e) { + alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION')); + form.removeClass('loading'); + } + }, ajaxOpts)); + }, + showDetailView: function(url) { + window.location.href = url; + }, + getItems: function() { + return this.find('.ss-gridfield-item'); + }, + /** + * @param {String} + * @param {Mixed} + */ + setState: function(k, v) { + var state = this.getState(); + state[k] = v; + this.find(':input[name="' + this.data('name') + '[GridState]"]').val(JSON.stringify(state)); + }, + /** + * @return {Object} + */ + getState: function() { + return JSON.parse(this.find(':input[name="' + this.data('name') + '[GridState]"]').val()); } + }); - var editLink = this.find('.edit-link'); - if(editLink.length) this.getGridField().showDetailView(editLink.prop('href')); - }, - onmouseover: function() { - if(this.find('.edit-link').length) this.css('cursor', 'pointer'); - }, - onmouseout: function() { - this.css('cursor', 'default'); - } - }); - - $('.ss-gridfield .action').entwine({ - onclick: function(e){ - var filterState='show'; //filterstate should equal current state. - - if(this.hasClass('ss-gridfield-button-close') || !(this.closest('.ss-gridfield').hasClass('show-filter'))){ - filterState='hidden'; + $('.ss-gridfield *').entwine({ + getGridField: function() { + return this.closest('.ss-gridfield'); } + }); - this.getGridField().reload({data: [{name: this.attr('name'), value: this.val(), filter: filterState}]}); - e.preventDefault(); - } - }); - $('.ss-gridfield .action.gridfield-button-delete').entwine({ - onclick: function(e){ - if(!confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE'))) { + + $('.ss-gridfield :button[name=showFilter]').entwine({ + onclick: function(e) { + $('.filter-header') + .show('slow') // animate visibility + .find(':input:first').focus(); // focus first search field + this.closest('.ss-gridfield').addClass('show-filter'); + this.parent().html(''); e.preventDefault(); - return false; - } else { - this._super(e); } - } - }); - - $('.ss-gridfield .action.gridfield-button-print').entwine({ - UUID: null, - onmatch: function() { - this._super(); - this.setUUID(new Date().getTime()); - }, - onclick: function(e){ - var btn = this.closest(':button'), grid = this.getGridField(), - form = this.closest('form'), data = form.find(':input').serialize(); + }); - // Add current button - data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); - // Include any GET parameters from the current URL, as the view state might depend on it. - // For example, a list prefiltered through external search criteria might be passed to GridField. - if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data; + $('.ss-gridfield .ss-gridfield-item').entwine({ + onclick: function(e) { + if($(e.target).closest('.action').length) { + this._super(e); + return false; + } - var url = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href')); - var newWindow = window.open(url); - - return false; - } - }); - - $('.ss-gridfield-print-iframe').entwine({ - onmatch: function(){ - this.hide().bind('load', function() - { - this.focus(); - var ifWin = this.contentWindow || this; - ifWin.print(); - });; - } - }); - - /** - * Prevents actions from causing an ajax reload of the field. - * Useful e.g. for actions which rely on HTTP response headers being interpreted nativel - * by the browser, like file download triggers. - */ - $('.ss-gridfield .action.no-ajax').entwine({ - onclick: function(e){ - var self = this, btn = this.closest(':button'), grid = this.getGridField(), - form = this.closest('form'), data = form.find(':input').serialize(); - - // Add current button - data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); - - // Include any GET parameters from the current URL, as the view state might depend on it. - // For example, a list prefiltered through external search criteria might be passed to GridField. - if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data; - - window.location.href = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href')); - return false; - } - }); - - $('.ss-gridfield .action-detail').entwine({ - onclick: function() { - this.getGridField().showDetailView($(this).prop('href')); - return false; - } - }); - - /** - * Allows selection of one or more rows in the grid field. - * Purely clientside at the moment. - */ - $('.ss-gridfield[data-selectable]').entwine({ - /** - * @return {jQuery} Collection - */ - getSelectedItems: function() { - return this.find('.ss-gridfield-item.ui-selected'); - }, - /** - * @return {Array} Of record IDs - */ - getSelectedIDs: function() { - return $.map(this.getSelectedItems(), function(el) {return $(el).data('id');}); - } - }); - $('.ss-gridfield[data-selectable] .ss-gridfield-items').entwine({ - onmatch: function() { - this._super(); - - // TODO Limit to single selection - this.selectable(); - }, - onunmatch: function() { - this._super(); - this.selectable('destroy'); - } - - }); - - /** - * Catch submission event in filter input fields, and submit the correct button - * rather than the whole form. - */ - $('.ss-gridfield .filter-header :input').entwine({ - onmatch: function(){ - var filterbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-filter'), - resetbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-reset'); - - if(this.val()) { - filterbtn.addClass('filtered'); - resetbtn.addClass('filtered'); + var editLink = this.find('.edit-link'); + if(editLink.length) this.getGridField().showDetailView(editLink.prop('href')); + }, + onmouseover: function() { + if(this.find('.edit-link').length) this.css('cursor', 'pointer'); + }, + onmouseout: function() { + this.css('cursor', 'default'); } - }, - onkeydown: function(e) { - // Skip reset button events, they should trigger default submission - if(this.closest('.ss-gridfield-button-reset').length) return; + }); - var filterbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-filter'), - resetbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-reset'); - - if(e.keyCode == '13') { - var btns = this.closest('.filter-header').find('.ss-gridfield-button-filter'); - var filterState='show'; //filterstate should equal current state. - if(this.hasClass('ss-gridfield-button-close')||!(this.closest('.ss-gridfield').hasClass('show-filter'))){ + $('.ss-gridfield .action').entwine({ + onclick: function(e){ + var filterState='show'; //filterstate should equal current state. + + if(this.hasClass('ss-gridfield-button-close') || !(this.closest('.ss-gridfield').hasClass('show-filter'))){ filterState='hidden'; } - - this.getGridField().reload({data: [{name: btns.attr('name'), value: btns.val(), filter: filterState}]}); - return false; - }else{ - filterbtn.addClass('hover-alike'); - resetbtn.addClass('hover-alike'); - } - } - }); - $(".ss-gridfield .relation-search").entwine({ - onfocusin: function (event) { - this.autocomplete({ - source: function(request, response){ - var searchField = $(this.element); - var form = $(this.element).closest("form"); - // Due to some very weird behaviout of jquery.metadata, the url have to be double quoted - var suggestionUrl = $(searchField).attr('data-search-url').substr(1,$(searchField).attr('data-search-url').length-2); - $.ajax({ - headers: { - "X-Pjax" : 'Partial' - }, - type: "GET", - url: suggestionUrl+'/'+request.term, - data: form.serialize()+'&'+escape(searchField.attr('name'))+'='+escape(searchField.val()), - success: function(data) { - response( $.map(JSON.parse(data), function( name, id ) { - return { label: name, value: name, id: id }; - })); - }, - error: function(e) { - alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION', 'An error occured while fetching data from the server\n Please try again later.')); - } - }); - }, - select: function(event, ui) { - $(this).closest(".ss-gridfield").find("#action_gridfield_relationfind").replaceWith( - '' - ); - var addbutton = $(this).closest(".ss-gridfield").find("#action_gridfield_relationadd"); - if(addbutton.data('button')){ - addbutton.button('enable'); - }else{ - addbutton.removeAttr('disabled'); - } + this.getGridField().reload({data: [{name: this.attr('name'), value: this.val(), filter: filterState}]}); + e.preventDefault(); + } + }); + + $('.ss-gridfield .action.gridfield-button-delete').entwine({ + onclick: function(e){ + if(!confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE'))) { + e.preventDefault(); + return false; + } else { + this._super(e); } - }); - } - }); + } + }); + + $('.ss-gridfield .action.gridfield-button-print').entwine({ + UUID: null, + onmatch: function() { + this._super(); + this.setUUID(new Date().getTime()); + }, + onclick: function(e){ + var btn = this.closest(':button'), grid = this.getGridField(), + form = this.closest('form'), data = form.find(':input').serialize(); - $(".ss-gridfield .pagination-page-number input").entwine({ - onkeydown: function(event) { - if(event.keyCode == 13) { - var newpage = parseInt($(this).val(), 10); + // Add current button + data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); - var gridfield = $(this).getGridField(); - gridfield.setState('GridFieldPaginator', {currentPage: newpage}); - gridfield.reload(); + // Include any GET parameters from the current URL, as the view state might depend on it. + // For example, a list prefiltered through external search criteria might be passed to GridField. + if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data; + + var url = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href')); + var newWindow = window.open(url); return false; } - } + }); + + $('.ss-gridfield-print-iframe').entwine({ + onmatch: function(){ + this.hide().bind('load', function() + { + this.focus(); + var ifWin = this.contentWindow || this; + ifWin.print(); + });; + } + }); + + /** + * Prevents actions from causing an ajax reload of the field. + * Useful e.g. for actions which rely on HTTP response headers being interpreted nativel + * by the browser, like file download triggers. + */ + $('.ss-gridfield .action.no-ajax').entwine({ + onclick: function(e){ + var self = this, btn = this.closest(':button'), grid = this.getGridField(), + form = this.closest('form'), data = form.find(':input').serialize(); + + // Add current button + data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); + + // Include any GET parameters from the current URL, as the view state might depend on it. + // For example, a list prefiltered through external search criteria might be passed to GridField. + if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data; + + window.location.href = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href')); + return false; + } + }); + + $('.ss-gridfield .action-detail').entwine({ + onclick: function() { + this.getGridField().showDetailView($(this).prop('href')); + return false; + } + }); + + /** + * Allows selection of one or more rows in the grid field. + * Purely clientside at the moment. + */ + $('.ss-gridfield[data-selectable]').entwine({ + /** + * @return {jQuery} Collection + */ + getSelectedItems: function() { + return this.find('.ss-gridfield-item.ui-selected'); + }, + /** + * @return {Array} Of record IDs + */ + getSelectedIDs: function() { + return $.map(this.getSelectedItems(), function(el) {return $(el).data('id');}); + } + }); + $('.ss-gridfield[data-selectable] .ss-gridfield-items').entwine({ + onmatch: function() { + this._super(); + + // TODO Limit to single selection + this.selectable(); + }, + onunmatch: function() { + this._super(); + this.selectable('destroy'); + } + + }); + + /** + * Catch submission event in filter input fields, and submit the correct button + * rather than the whole form. + */ + $('.ss-gridfield .filter-header :input').entwine({ + onmatch: function(){ + var filterbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-filter'), + resetbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-reset'); + + if(this.val()) { + filterbtn.addClass('filtered'); + resetbtn.addClass('filtered'); + } + }, + onkeydown: function(e) { + // Skip reset button events, they should trigger default submission + if(this.closest('.ss-gridfield-button-reset').length) return; + + var filterbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-filter'), + resetbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-reset'); + + if(e.keyCode == '13') { + var btns = this.closest('.filter-header').find('.ss-gridfield-button-filter'); + var filterState='show'; //filterstate should equal current state. + if(this.hasClass('ss-gridfield-button-close')||!(this.closest('.ss-gridfield').hasClass('show-filter'))){ + filterState='hidden'; + } + + this.getGridField().reload({data: [{name: btns.attr('name'), value: btns.val(), filter: filterState}]}); + return false; + }else{ + filterbtn.addClass('hover-alike'); + resetbtn.addClass('hover-alike'); + } + } + }); + + $(".ss-gridfield .relation-search").entwine({ + onfocusin: function (event) { + this.autocomplete({ + source: function(request, response){ + var searchField = $(this.element); + var form = $(this.element).closest("form"); + // Due to some very weird behaviout of jquery.metadata, the url have to be double quoted + var suggestionUrl = $(searchField).attr('data-search-url').substr(1,$(searchField).attr('data-search-url').length-2); + $.ajax({ + headers: { + "X-Pjax" : 'Partial' + }, + type: "GET", + url: suggestionUrl+'/'+request.term, + data: form.serialize()+'&'+escape(searchField.attr('name'))+'='+escape(searchField.val()), + success: function(data) { + response( $.map(JSON.parse(data), function( name, id ) { + return { label: name, value: name, id: id }; + })); + }, + error: function(e) { + alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION', 'An error occured while fetching data from the server\n Please try again later.')); + } + }); + }, + select: function(event, ui) { + $(this).closest(".ss-gridfield").find("#action_gridfield_relationfind").replaceWith( + '' + ); + var addbutton = $(this).closest(".ss-gridfield").find("#action_gridfield_relationadd"); + if(addbutton.data('button')){ + addbutton.button('enable'); + }else{ + addbutton.removeAttr('disabled'); + } + } + }); + } + }); + + $(".ss-gridfield .pagination-page-number input").entwine({ + onkeydown: function(event) { + if(event.keyCode == 13) { + var newpage = parseInt($(this).val(), 10); + + var gridfield = $(this).getGridField(); + gridfield.setState('GridFieldPaginator', {currentPage: newpage}); + gridfield.reload(); + + return false; + } + } + }); + }); }(jQuery)); diff --git a/javascript/TreeDropdownField.js b/javascript/TreeDropdownField.js index 112d2a840..e4eacc9dc 100644 --- a/javascript/TreeDropdownField.js +++ b/javascript/TreeDropdownField.js @@ -16,7 +16,7 @@ var _clickTestFn = function(e) { // If the click target is not a child of the current field, close the panel automatically. - if(!$(e.target).parents('.TreeDropdownField').length) jQuery('.TreeDropdownField').entwine('ss').closePanel(); + if(!$(e.target).parents('.TreeDropdownField').length) jQuery('.TreeDropdownField').closePanel(); }; /** @@ -374,4 +374,4 @@ } }); }); -}(jQuery)); \ No newline at end of file +}(jQuery));