silverstripe-framework/javascript/GridField.js
Sam Minnee 3ee8f505b7 MINORE: Remove training whitespace.
The main benefit of this is so that authors who make use of
.editorconfig don't end up with whitespace changes in their PRs.

Spaces vs. tabs has been left alone, although that could do with a
tidy-up in SS4 after the switch to PSR-1/2.

The command used was this:

for match in '*.ss' '*.css' '*.scss' '*.html' '*.yml' '*.php' '*.js' '*.csv' '*.inc' '*.php5'; do
	find . -path ./thirdparty -not -prune -o -path ./admin/thirdparty -not -prune -o -type f -name "$match" -exec sed -E -i '' 's/[[:space:]]+$//' {} \+
	find . -path ./thirdparty -not -prune -o -path ./admin/thirdparty -not -prune -o -type f -name "$match" | xargs perl -pi -e 's/ +$//'
done
2016-01-07 10:15:54 +13:00

413 lines
13 KiB
JavaScript

(function($){
$.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();
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);
}
// For browsers which do not support history.pushState like IE9, ss framework uses hash to track
// the current location for PJAX, so for them we pass the query string stored in the hash instead
if(!window.history || !window.history.pushState){
if(window.location.hash && window.location.hash.indexOf('?') != -1){
ajaxOpts.data = window.location.hash.substring(window.location.hash.indexOf('?') + 1) + '&' + $.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();
// Update filter
if(self.find('.filter-header').length) {
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 type="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({
getGridField: function() {
return this.closest('.ss-gridfield');
}
});
$('.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('<span class="non-sortable"></span>');
e.preventDefault();
}
});
$('.ss-gridfield .ss-gridfield-item').entwine({
onclick: function(e) {
if($(e.target).closest('.action').length) {
this._super(e);
return false;
}
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 the button is disabled, do nothing.
if (this.button('option', 'disabled')) {
e.preventDefault();
return;
}
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();
}
});
/**
* Don't allow users to submit empty values in grid field auto complete inputs.
*/
$('.ss-gridfield .add-existing-autocompleter').entwine({
onbuttoncreate: function () {
var self = this;
this.toggleDisabled();
this.find('input[type="text"]').on('keyup', function () {
self.toggleDisabled();
});
},
onunmatch: function () {
this.find('input[type="text"]').off('keyup');
},
toggleDisabled: function () {
var $button = this.find('.ss-ui-button'),
$input = this.find('input[type="text"]'),
inputHasValue = $input.val() !== '',
buttonDisabled = $button.is(':disabled');
if ((inputHasValue && buttonDisabled) || (!inputHasValue && !buttonDisabled)) {
$button.button("option", "disabled", !buttonDisabled);
}
}
});
// Covers both tabular delete button, and the button on the detail form
$('.ss-gridfield .col-buttons .action.gridfield-button-delete, .cms-edit-form .Actions button.action.action-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());
},
onunmatch: function() {
this._super();
},
onclick: function(e){
var btn = this.closest(':button'), grid = this.getGridField(),
form = this.closest('form'), data = form.find(':input.gridstate').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;
}
// decide whether we should use ? or & to connect the URL
var connector = grid.data('url').indexOf('?') == -1 ? '?' : '&';
var url = $.path.makeUrlAbsolute(
grid.data('url') + connector + data,
$('base').attr('href')
);
var newWindow = window.open(url);
return false;
}
});
$('.ss-gridfield-print-iframe').entwine({
onmatch: function(){
this._super();
this.hide().bind('load', function() {
this.focus();
var ifWin = this.contentWindow || this;
ifWin.print();
});
},
onunmatch: function() {
this._super();
}
});
/**
* Prevents actions from causing an ajax reload of the field.
*
* Useful e.g. for actions which rely on HTTP response headers being
* interpreted natively 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.gridstate').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 pre-filtered
// through external search criteria might be passed to GridField.
if(window.location.search) {
data = window.location.search.replace(/^\?/, '') + '&' + data;
}
// decide whether we should use ? or & to connect the URL
var connector = grid.data('url').indexOf('?') == -1 ? '?' : '&';
window.location.href = $.path.makeUrlAbsolute(
grid.data('url') + connector + 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({
onadd: function() {
this._super();
// TODO Limit to single selection
this.selectable();
},
onremove: function() {
this._super();
if (this.data('selectable')) 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');
}
this._super();
},
onunmatch: function() {
this._super();
},
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");
$.ajax({
headers: {
"X-Pjax" : 'Partial'
},
type: "GET",
url: $(searchField).data('searchUrl'),
data: encodeURIComponent(searchField.attr('name'))+'='+encodeURIComponent(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));