BUGFIX: put all CMS JS code into 'ss' namespace.

Conflicts:

	admin/javascript/LeftAndMain.js
	javascript/GridField.js
This commit is contained in:
Mateusz Uzdowski 2012-05-11 15:31:12 +12:00
parent 3412a0e58d
commit 3d0876c8f5
7 changed files with 628 additions and 623 deletions

View File

@ -89,7 +89,7 @@
data.push({name:button.attr('name'),value:button.val()}); data.push({name:button.attr('name'),value:button.val()});
// TODO Should be set by hiddenfield already // TODO Should be set by hiddenfield already
jQuery('.cms-content').entwine('ss').submitForm( jQuery('.cms-content').submitForm(
this.attr('action'), this.attr('action'),
null, null,
function() { function() {

View File

@ -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 * Class: #Form_BatchActionsForm :select[name=Action]
*
* Parameters:
* (Event) e
*/ */
onchange: function(e) { $('#Form_BatchActionsForm select[name=Action]').entwine({
var form = $(e.target.form), btn = form.find(':submit');
if($(e.target).val() == -1) { onmatch: function() {
btn.attr('disabled', 'disabled').button('refresh'); this.trigger('change');
} else { this._super();
btn.removeAttr('disabled').button('refresh'); },
// form.submit();
/**
* 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");
this._super(e);
} }
// 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;
}); });
/** $(document).ready(function() {
* Unpublish selected pages action /**
*/ * Publish selected pages action
$('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/unpublish', function(ids) { */
var confirmed = confirm( $('#Form_BatchActionsForm').register('admin/batchactions/publish', function(ids) {
"You have " + ids.length + " pages selected.\n\n" var confirmed = confirm(
+ "Do your really want to unpublish?" "You have " + ids.length + " pages selected.\n\n"
); + "Do your really want to publish?"
return (confirmed) ? ids : false; );
}); return (confirmed) ? ids : false;
});
/** /**
* Delete selected pages action * Unpublish selected pages action
*/ */
$('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/delete', function(ids) { $('#Form_BatchActionsForm').register('admin/batchactions/unpublish', function(ids) {
var confirmed = confirm( var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n" "You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to delete?" + "Do your really want to unpublish?"
); );
return (confirmed) ? ids : false; return (confirmed) ? ids : false;
}); });
/** /**
* Delete selected pages from live action * Delete selected pages action
*/ */
$('#Form_BatchActionsForm').entwine('ss').register('admin/batchactions/deletefromlive', function(ids) { $('#Form_BatchActionsForm').register('admin/batchactions/delete', function(ids) {
var confirmed = confirm( var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n" "You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to delete these pages from live?" + "Do your really want to delete?"
); );
return (confirmed) ? ids : false; 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;
});
}); });
}); });

View File

@ -185,60 +185,60 @@
this.removeClass('changed'); this.removeClass('changed');
} }
}); });
});
/** /**
* Load edit form for the selected node when its clicked. * Load edit form for the selected node when its clicked.
*/ */
$('.cms-content .cms-tree').entwine({ $('.cms-content .cms-tree').entwine({
onmatch: function() { onmatch: function() {
var self = this; var self = this;
this._super(); this._super();
this.bind('select_node.jstree', function(e, data) { 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'); 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. // Don't trigger unless coming from a click event.
// Avoids problems with automated section switches from tree to detail view // Avoids problems with automated section switches from tree to detail view
// when JSTree auto-selects elements on first load. // when JSTree auto-selects elements on first load.
if(!origEvent) { if(!origEvent) {
return false; return false;
}else if($(origEvent.target).hasClass('jstree-icon') || $(origEvent.target).hasClass('jstree-pageicon')){ }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, // in case the click is not on the node title, ie on pageicon or dragicon,
return false; return false;
} }
// Don't allow checking disabled nodes // Don't allow checking disabled nodes
if($(node).hasClass('disabled')) return false; if($(node).hasClass('disabled')) return false;
// Don't allow reloading of currently selected node, // Don't allow reloading of currently selected node,
// mainly to avoid doing an ajax request on initial page load // mainly to avoid doing an ajax request on initial page load
if($(node).data('id') == loadedNodeID) return; if($(node).data('id') == loadedNodeID) return;
var url = $(node).find('a:first').attr('href'); var url = $(node).find('a:first').attr('href');
if(url && url != '#') { if(url && url != '#') {
// Ensure URL is absolute (important for IE) // Ensure URL is absolute (important for IE)
if($.path.isExternal($(node).find('a:first'))) url = url = $.path.makeUrlAbsolute(url, $('base').attr('href')); if($.path.isExternal($(node).find('a:first'))) url = url = $.path.makeUrlAbsolute(url, $('base').attr('href'));
// Retain search parameters // Retain search parameters
if(document.location.search) url = $.path.addSearchParams(url, document.location.search.replace(/^\?/, '')); if(document.location.search) url = $.path.addSearchParams(url, document.location.search.replace(/^\?/, ''));
// Load new page // Load new page
container.entwine('ss').loadPanel(url); container.loadPanel(url);
} else { } else {
self.removeForm(); self.removeForm();
} }
}); });
} }
}); });
$('.cms-content.loading,.cms-edit-form.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({ $('.cms-content.loading,.cms-edit-form.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({
onmatch: function() { onmatch: function() {
this.append('<div class="cms-content-loading-overlay ui-widget-overlay-light"></div><div class="cms-content-loading-spinner"></div>'); this.append('<div class="cms-content-loading-overlay ui-widget-overlay-light"></div><div class="cms-content-loading-spinner"></div>');
}, },
onunmatch: function() { onunmatch: function() {
this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove(); this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove();
} }
});
}); });
})(jQuery); })(jQuery);

View File

@ -420,36 +420,36 @@ jQuery.noConflict();
* Add styling to all contained buttons, and create buttonsets if required. * Add styling to all contained buttons, and create buttonsets if required.
*/ */
$('.cms .Actions').entwine({ $('.cms .Actions').entwine({
onmatch: function() { onmatch: function() {
this.find('.ss-ui-button').click(function() { this.find('.ss-ui-button').click(function() {
var form = this.form; var form = this.form;
// forms don't natively store the button they've been triggered with // forms don't natively store the button they've been triggered with
if(form) { if(form) {
form.clickedButton = this; form.clickedButton = this;
// Reset the clicked button shortly after the onsubmit handlers // Reset the clicked button shortly after the onsubmit handlers
// have fired on the form // have fired on the form
setTimeout(function() {form.clickedButton = null;}, 10); 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(); // Mark up buttonsets
this._super(); this.find('.ss-ui-buttonset').buttonset();
}, }
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();
}
});
/** /**
* Duplicates functionality in DateField.js, but due to using entwine we can match * 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);
}
});
/**
* 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. * Overload the default GridField behaviour (open a new URL in the browser)
* with the CMS-specific ajax loading.
*/ */
onreset: function(e) { $('.cms .ss-gridfield').entwine({
this.clearForm(); showDetailView: function(url) {
this.submit(); // 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);
}
});
});
/** /**
* Simple toggle link, which points to a DOm element by its ID selector * Generic search form in the CMS, often hooked up to a GridField results display.
* in the href attribute (which doubles as an anchor link to that element). */
*/ $('.cms-search-form').entwine({
$('.cms .cms-help-toggle').entwine({
onmatch: function() {
this._super();
$(this.attr('href')).hide(); onsubmit: function() {
}, // Remove empty elements and make the URL prettier
onclick: function(e) { var nonEmptyInputs = this.find(':input:not(:submit)').filter(function() {
$(this.attr('href')).toggle(); // Use fieldValue() from jQuery.form plugin rather than jQuery.val(),
e.preventDefault(); // 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');
* Allows to lazy load a panel, by leaving it empty container.find('.cms-edit-form').tabs('select',0); //always switch to the first tab (list view) when searching
* and declaring a URL to load its content via a 'url' HTML5 data attribute. container.loadPanel(url);
* The loaded HTML is cached, with cache key being the 'url' attribute. return false;
* 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(); /**
* Resets are processed on the serverside, so need to trigger a submit.
*/
onreset: function(e) {
this.clearForm();
this.submit();
}
// 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]); * Simple toggle link, which points to a DOm element by its ID selector
} else { * in the href attribute (which doubles as an anchor link to that element).
this.addClass('loading'); */
$.ajax({ $('.cms .cms-help-toggle').entwine({
url: url, onmatch: function() {
complete: function() { this._super();
self.removeClass('loading');
}, $(this.attr('href')).hide();
success: function(data, status, xhr) { },
self.html(data); onclick: function(e) {
} $(this.attr('href')).toggle();
}); e.preventDefault();
}
});
/**
* 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);
}
});
}
} }
} }
} });
});
/**
* 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. * Lightweight wrapper around jQuery UI tabs.
* SSViewer rewrites them from "#Root_MyTab" to * Ensures that anchor links are set properly,
* e.g. "/admin/#Root_MyTab" which makes them * and any nested tabs are scrolled if they have
* unusable for jQuery UI. * their height explicitly set. This is important
* for forms inside the CMS layout.
*/ */
rewriteHashlinks: function() { $('.cms-tabset').entwine({
$(this).find('ul a').each(function() { onmatch: function() {
var href = $(this).attr('href').replace(/.*(#.*)/, '$1'); // Can't name redraw() as it clashes with other CMS entwine classes
if(href) $(this).attr('href', href); 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)); }(jQuery));

View File

@ -1,34 +1,36 @@
(function($) { (function($) {
/** $.entwine('ss', function($) {
* Creates a jQuery UI tab navigation bar, detached from the container DOM structure. /**
*/ * Creates a jQuery UI tab navigation bar, detached from the container DOM structure.
$('.ss-ui-tabs-nav').entwine({ */
onmatch: function() { $('.ss-ui-tabs-nav').entwine({
this.redraw(); onmatch: function() {
this.redraw();
this._super(); this._super();
}, },
redraw: function() { redraw: function() {
this.addClass('ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-panel ui-corner-bottom'); 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('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'); this.find('li').addClass('ui-state-default ui-corner-top');
// TODO Figure out selected tab // TODO Figure out selected tab
var selected = this.find('li.current'); var selected = this.find('li.current');
if(!selected.length) selected = this.find('li:first'); if(!selected.length) selected = this.find('li:first');
selected.selectIt(); selected.selectIt();
} }
}); });
$('.ss-ui-tabs-nav li').entwine({ $('.ss-ui-tabs-nav li').entwine({
onclick: function() { onclick: function() {
this.selectIt(); this.selectIt();
}, },
selectIt: function() { selectIt: function() {
var cls = 'ui-tabs-selected ui-state-active'; var cls = 'ui-tabs-selected ui-state-active';
this.addClass(cls).siblings().not(this).removeClass(cls); this.addClass(cls).siblings().not(this).removeClass(cls);
} }
});
}); });
/** /**

View File

@ -1,335 +1,338 @@
(function($){ (function($){
$('.ss-gridfield').entwine({ $.entwine('ss', function($) {
/** $('.ss-gridfield').entwine({
* @param {Object} Additional options for jQuery.ajax() call /**
* @param {successCallback} callback to call after reloading succeeded. * @param {Object} Additional options for jQuery.ajax() call
*/ * @param {successCallback} callback to call after reloading succeeded.
*/
reload: function(ajaxOpts, successCallback) { reload: function(ajaxOpts, successCallback) {
var self = this, form = this.closest('form'), var self = this, form = this.closest('form'),
focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh focusedElName = this.find(':input:focus').attr('name'), // Save focused element for restoring after refresh
data = form.find(':input').serializeArray(); data = form.find(':input').serializeArray();
if(!ajaxOpts) ajaxOpts = {}; if(!ajaxOpts) ajaxOpts = {};
if(!ajaxOpts.data) ajaxOpts.data = []; if(!ajaxOpts.data) ajaxOpts.data = [];
ajaxOpts.data = ajaxOpts.data.concat(data); ajaxOpts.data = ajaxOpts.data.concat(data);
// Include any GET parameters from the current URL, as the view state might depend on it. // 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. // For example, a list prefiltered through external search criteria might be passed to GridField.
if(window.location.search) { if(window.location.search) {
ajaxOpts.data = window.location.search.replace(/^\?/, '') + '&' + $.param(ajaxOpts.data); 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 = '<span class="non-sortable"></span>';
self.addClass('show-filter').find('.filter-header').show();
}else{
content = '<button name="showFilter" class="ss-gridfield-button-filter trigger"></button>';
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');
} }
}, 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({ form.addClass('loading');
getGridField: function() {
return this.closest('.ss-gridfield');
}
});
$.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({ var content;
onclick: function(e) { if(ajaxOpts.data[0].filter=="show"){
$('.filter-header') content = '<span class="non-sortable"></span>';
.show('slow') // animate visibility self.addClass('show-filter').find('.filter-header').show();
.find(':input:first').focus(); // focus first search field }else{
this.closest('.ss-gridfield').addClass('show-filter'); content = '<button name="showFilter" class="ss-gridfield-button-filter trigger"></button>';
this.parent().html('<span class="non-sortable"></span>'); self.removeClass('show-filter').find('.filter-header').hide();
e.preventDefault(); }
}
});
self.find('.sortable-header th:last').html(content);
$('.ss-gridfield .ss-gridfield-item').entwine({ form.removeClass('loading');
onclick: function(e) { if(successCallback) successCallback.apply(this, arguments);
if($(e.target).closest('.action').length) { self.trigger('reload', self);
this._super(e); },
return false; 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'); $('.ss-gridfield *').entwine({
if(editLink.length) this.getGridField().showDetailView(editLink.prop('href')); getGridField: function() {
}, return this.closest('.ss-gridfield');
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';
} }
});
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){ $('.ss-gridfield :button[name=showFilter]').entwine({
if(!confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE'))) { 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('<span class="non-sortable"></span>');
e.preventDefault(); 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 $('.ss-gridfield .ss-gridfield-item').entwine({
data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val()); onclick: function(e) {
if($(e.target).closest('.action').length) {
this._super(e);
return false;
}
// Include any GET parameters from the current URL, as the view state might depend on it. var editLink = this.find('.edit-link');
// For example, a list prefiltered through external search criteria might be passed to GridField. if(editLink.length) this.getGridField().showDetailView(editLink.prop('href'));
if(window.location.search) data = window.location.search.replace(/^\?/, '') + '&' + data; },
onmouseover: function() {
var url = $.path.makeUrlAbsolute(grid.data('url') + '?' + data, $('base').attr('href')); if(this.find('.edit-link').length) this.css('cursor', 'pointer');
var newWindow = window.open(url); },
onmouseout: function() {
return false; this.css('cursor', 'default');
}
});
$('.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'), $('.ss-gridfield .action').entwine({
resetbtn = this.closest('.fieldgroup').find('.ss-gridfield-button-reset'); onclick: function(e){
if(e.keyCode == '13') {
var btns = this.closest('.filter-header').find('.ss-gridfield-button-filter');
var filterState='show'; //filterstate should equal current state. var filterState='show'; //filterstate should equal current state.
if(this.hasClass('ss-gridfield-button-close')||!(this.closest('.ss-gridfield').hasClass('show-filter'))){
if(this.hasClass('ss-gridfield-button-close') || !(this.closest('.ss-gridfield').hasClass('show-filter'))){
filterState='hidden'; filterState='hidden';
} }
this.getGridField().reload({data: [{name: btns.attr('name'), value: btns.val(), filter: filterState}]}); this.getGridField().reload({data: [{name: this.attr('name'), value: this.val(), filter: filterState}]});
return false; e.preventDefault();
}else{
filterbtn.addClass('hover-alike');
resetbtn.addClass('hover-alike');
} }
} });
});
$(".ss-gridfield .relation-search").entwine({ $('.ss-gridfield .action.gridfield-button-delete').entwine({
onfocusin: function (event) { onclick: function(e){
this.autocomplete({ if(!confirm(ss.i18n._t('TABLEFIELD.DELETECONFIRMMESSAGE'))) {
source: function(request, response){ e.preventDefault();
var searchField = $(this.element); return false;
var form = $(this.element).closest("form"); } else {
// Due to some very weird behaviout of jquery.metadata, the url have to be double quoted this._super(e);
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(
'<input type="hidden" name="relationID" value="'+ui.item.id+'" id="relationID"/>'
);
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({ $('.ss-gridfield .action.gridfield-button-print').entwine({
onkeydown: function(event) { UUID: null,
if(event.keyCode == 13) { onmatch: function() {
var newpage = parseInt($(this).val(), 10); 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();
var gridfield = $(this).getGridField(); // Add current button
gridfield.setState('GridFieldPaginator', {currentPage: newpage}); data += '&' + encodeURIComponent(btn.attr('name')) + '=' + encodeURIComponent(btn.val());
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; 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(
'<input type="hidden" name="relationID" value="'+ui.item.id+'" id="relationID"/>'
);
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)); }(jQuery));

View File

@ -16,7 +16,7 @@
var _clickTestFn = function(e) { var _clickTestFn = function(e) {
// If the click target is not a child of the current field, close the panel automatically. // 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();
}; };
/** /**