diff --git a/modules/backend/formwidgets/DataGrid.php b/modules/backend/formwidgets/DataGrid.php index 030590784..249b7f0e3 100644 --- a/modules/backend/formwidgets/DataGrid.php +++ b/modules/backend/formwidgets/DataGrid.php @@ -65,6 +65,7 @@ class DataGrid extends FormWidgetBase $this->vars['columnWidths'] = $this->getColumnWidths(); $this->vars['size'] = $this->size; $this->vars['toolbarWidget'] = $this->makeToolbarWidget(); + $this->vars['value'] = json_encode($this->model->{$this->columnName}); } protected function makeToolbarWidget() @@ -184,6 +185,6 @@ class DataGrid extends FormWidgetBase */ public function getSaveData($value) { - return $value; + return json_decode($value); } } \ No newline at end of file diff --git a/modules/backend/formwidgets/datagrid/assets/js/datagrid.js b/modules/backend/formwidgets/datagrid/assets/js/datagrid.js index f9d90f716..83dfd2971 100644 --- a/modules/backend/formwidgets/datagrid/assets/js/datagrid.js +++ b/modules/backend/formwidgets/datagrid/assets/js/datagrid.js @@ -48,6 +48,27 @@ if (this.options.autoInsertRows) handsontableOptions.minSpareRows = 1 + if (this.options.dataLocker) { + /* + * Event to update the data locker + */ + this.$dataLocker = $(this.options.dataLocker) + handsontableOptions.afterChange = function(changes, source) { + if (!self.gridInstance) return + self.$dataLocker.val(JSON.stringify(self.getData())) + } + + /* + * Populate existing data + */ + try { + var existingData = JSON.parse(this.$dataLocker.val()) + if (existingData) handsontableOptions.data = existingData + } catch (e) { + delete handsontableOptions.data + } + } + this.$el.handsontable(handsontableOptions) this.gridInstance = this.$el.handsontable('getInstance') @@ -109,6 +130,7 @@ } DataGrid.DEFAULTS = { + dataLocker: null, startRows: 1, minRows: 1, autoInsertRows: false, @@ -127,11 +149,18 @@ } DataGrid.prototype.getData = function() { - var results = [], + var self = this, + results = [], dataArray = this.gridInstance.getData() $.each(dataArray, function(index, object){ - results.push($.extend(true, {}, object)) + var dataObj = {} + + // Prune out unwanted array data + $.each(self.columns, function(index, column){ + dataObj[column.data] = object[column.data] + }) + results.push(dataObj) }) return results } diff --git a/modules/backend/formwidgets/datagrid/assets/vendor/handsontable/jquery.handsontable.js b/modules/backend/formwidgets/datagrid/assets/vendor/handsontable/jquery.handsontable.js index d4c418fd9..8b7ed63cb 100644 --- a/modules/backend/formwidgets/datagrid/assets/vendor/handsontable/jquery.handsontable.js +++ b/modules/backend/formwidgets/datagrid/assets/vendor/handsontable/jquery.handsontable.js @@ -10446,4 +10446,523 @@ if (!Array.prototype.filter) { }; } -})(jQuery, window, Handsontable); \ No newline at end of file +})(jQuery, window, Handsontable); + +// numeral.js +// version : 1.4.7 +// author : Adam Draper +// license : MIT +// http://adamwdraper.github.com/Numeral-js/ + +(function () { + + /************************************ + Constants + ************************************/ + + var numeral, + VERSION = '1.4.7', + // internal storage for language config files + languages = {}, + currentLanguage = 'en', + zeroFormat = null, + // check for nodeJS + hasModule = (typeof module !== 'undefined' && module.exports); + + + /************************************ + Constructors + ************************************/ + + + // Numeral prototype object + function Numeral (number) { + this._n = number; + } + + /** + * Implementation of toFixed() that treats floats more like decimals + * + * Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present + * problems for accounting- and finance-related software. + */ + function toFixed (value, precision, optionals) { + var power = Math.pow(10, precision), + output; + + // Multiply up by precision, round accurately, then divide and use native toFixed(): + output = (Math.round(value * power) / power).toFixed(precision); + + if (optionals) { + var optionalsRegExp = new RegExp('0{1,' + optionals + '}$'); + output = output.replace(optionalsRegExp, ''); + } + + return output; + } + + /************************************ + Formatting + ************************************/ + + // determine what type of formatting we need to do + function formatNumeral (n, format) { + var output; + + // figure out what kind of format we are dealing with + if (format.indexOf('$') > -1) { // currency!!!!! + output = formatCurrency(n, format); + } else if (format.indexOf('%') > -1) { // percentage + output = formatPercentage(n, format); + } else if (format.indexOf(':') > -1) { // time + output = formatTime(n, format); + } else { // plain ol' numbers or bytes + output = formatNumber(n, format); + } + + // return string + return output; + } + + // revert to number + function unformatNumeral (n, string) { + if (string.indexOf(':') > -1) { + n._n = unformatTime(string); + } else { + if (string === zeroFormat) { + n._n = 0; + } else { + var stringOriginal = string; + if (languages[currentLanguage].delimiters.decimal !== '.') { + string = string.replace(/\./g,'').replace(languages[currentLanguage].delimiters.decimal, '.'); + } + + // see if abbreviations are there so that we can multiply to the correct number + var thousandRegExp = new RegExp(languages[currentLanguage].abbreviations.thousand + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'), + millionRegExp = new RegExp(languages[currentLanguage].abbreviations.million + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'), + billionRegExp = new RegExp(languages[currentLanguage].abbreviations.billion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'), + trillionRegExp = new RegExp(languages[currentLanguage].abbreviations.trillion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'); + + // see if bytes are there so that we can multiply to the correct number + var prefixes = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], + bytesMultiplier = false; + + for (var power = 0; power <= prefixes.length; power++) { + bytesMultiplier = (string.indexOf(prefixes[power]) > -1) ? Math.pow(1024, power + 1) : false; + + if (bytesMultiplier) { + break; + } + } + + // do some math to create our number + n._n = ((bytesMultiplier) ? bytesMultiplier : 1) * ((stringOriginal.match(thousandRegExp)) ? Math.pow(10, 3) : 1) * ((stringOriginal.match(millionRegExp)) ? Math.pow(10, 6) : 1) * ((stringOriginal.match(billionRegExp)) ? Math.pow(10, 9) : 1) * ((stringOriginal.match(trillionRegExp)) ? Math.pow(10, 12) : 1) * ((string.indexOf('%') > -1) ? 0.01 : 1) * Number(((string.indexOf('(') > -1) ? '-' : '') + string.replace(/[^0-9\.-]+/g, '')); + + // round if we are talking about bytes + n._n = (bytesMultiplier) ? Math.ceil(n._n) : n._n; + } + } + return n._n; + } + + function formatCurrency (n, format) { + var prependSymbol = (format.indexOf('$') <= 1) ? true : false; + + // remove $ for the moment + var space = ''; + + // check for space before or after currency + if (format.indexOf(' $') > -1) { + space = ' '; + format = format.replace(' $', ''); + } else if (format.indexOf('$ ') > -1) { + space = ' '; + format = format.replace('$ ', ''); + } else { + format = format.replace('$', ''); + } + + // format the number + var output = formatNumeral(n, format); + + // position the symbol + if (prependSymbol) { + if (output.indexOf('(') > -1 || output.indexOf('-') > -1) { + output = output.split(''); + output.splice(1, 0, languages[currentLanguage].currency.symbol + space); + output = output.join(''); + } else { + output = languages[currentLanguage].currency.symbol + space + output; + } + } else { + if (output.indexOf(')') > -1) { + output = output.split(''); + output.splice(-1, 0, space + languages[currentLanguage].currency.symbol); + output = output.join(''); + } else { + output = output + space + languages[currentLanguage].currency.symbol; + } + } + + return output; + } + + function formatPercentage (n, format) { + var space = ''; + // check for space before % + if (format.indexOf(' %') > -1) { + space = ' '; + format = format.replace(' %', ''); + } else { + format = format.replace('%', ''); + } + + n._n = n._n * 100; + var output = formatNumeral(n, format); + if (output.indexOf(')') > -1 ) { + output = output.split(''); + output.splice(-1, 0, space + '%'); + output = output.join(''); + } else { + output = output + space + '%'; + } + return output; + } + + function formatTime (n, format) { + var hours = Math.floor(n._n/60/60), + minutes = Math.floor((n._n - (hours * 60 * 60))/60), + seconds = Math.round(n._n - (hours * 60 * 60) - (minutes * 60)); + return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds); + } + + function unformatTime (string) { + var timeArray = string.split(':'), + seconds = 0; + // turn hours and minutes into seconds and add them all up + if (timeArray.length === 3) { + // hours + seconds = seconds + (Number(timeArray[0]) * 60 * 60); + // minutes + seconds = seconds + (Number(timeArray[1]) * 60); + // seconds + seconds = seconds + Number(timeArray[2]); + } else if (timeArray.lenght === 2) { + // minutes + seconds = seconds + (Number(timeArray[0]) * 60); + // seconds + seconds = seconds + Number(timeArray[1]); + } + return Number(seconds); + } + + function formatNumber (n, format) { + var negP = false, + optDec = false, + abbr = '', + bytes = '', + ord = '', + abs = Math.abs(n._n); + + // check if number is zero and a custom zero format has been set + if (n._n === 0 && zeroFormat !== null) { + return zeroFormat; + } else { + // see if we should use parentheses for negative number + if (format.indexOf('(') > -1) { + negP = true; + format = format.slice(1, -1); + } + + // see if abbreviation is wanted + if (format.indexOf('a') > -1) { + // check for space before abbreviation + if (format.indexOf(' a') > -1) { + abbr = ' '; + format = format.replace(' a', ''); + } else { + format = format.replace('a', ''); + } + + if (abs >= Math.pow(10, 12)) { + // trillion + abbr = abbr + languages[currentLanguage].abbreviations.trillion; + n._n = n._n / Math.pow(10, 12); + } else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9)) { + // billion + abbr = abbr + languages[currentLanguage].abbreviations.billion; + n._n = n._n / Math.pow(10, 9); + } else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6)) { + // million + abbr = abbr + languages[currentLanguage].abbreviations.million; + n._n = n._n / Math.pow(10, 6); + } else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3)) { + // thousand + abbr = abbr + languages[currentLanguage].abbreviations.thousand; + n._n = n._n / Math.pow(10, 3); + } + } + + // see if we are formatting bytes + if (format.indexOf('b') > -1) { + // check for space before + if (format.indexOf(' b') > -1) { + bytes = ' '; + format = format.replace(' b', ''); + } else { + format = format.replace('b', ''); + } + + var prefixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], + min, + max; + + for (var power = 0; power <= prefixes.length; power++) { + min = Math.pow(1024, power); + max = Math.pow(1024, power+1); + + if (n._n >= min && n._n < max) { + bytes = bytes + prefixes[power]; + if (min > 0) { + n._n = n._n / min; + } + break; + } + } + } + + // see if ordinal is wanted + if (format.indexOf('o') > -1) { + // check for space before + if (format.indexOf(' o') > -1) { + ord = ' '; + format = format.replace(' o', ''); + } else { + format = format.replace('o', ''); + } + + ord = ord + languages[currentLanguage].ordinal(n._n); + } + + if (format.indexOf('[.]') > -1) { + optDec = true; + format = format.replace('[.]', '.'); + } + + var w = n._n.toString().split('.')[0], + precision = format.split('.')[1], + thousands = format.indexOf(','), + d = '', + neg = false; + + if (precision) { + if (precision.indexOf('[') > -1) { + precision = precision.replace(']', ''); + precision = precision.split('['); + d = toFixed(n._n, (precision[0].length + precision[1].length), precision[1].length); + } else { + d = toFixed(n._n, precision.length); + } + + w = d.split('.')[0]; + + if (d.split('.')[1].length) { + d = languages[currentLanguage].delimiters.decimal + d.split('.')[1]; + } else { + d = ''; + } + + if (optDec && Number(d) === 0) { + d = ''; + } + } else { + w = toFixed(n._n, null); + } + + // format number + if (w.indexOf('-') > -1) { + w = w.slice(1); + neg = true; + } + + if (thousands > -1) { + w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands); + } + + if (format.indexOf('.') === 0) { + w = ''; + } + + return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : ''); + } + } + + /************************************ + Top Level Functions + ************************************/ + + numeral = function (input) { + if (numeral.isNumeral(input)) { + input = input.value(); + } else if (!Number(input)) { + input = 0; + } + + return new Numeral(Number(input)); + }; + + // version number + numeral.version = VERSION; + + // compare numeral object + numeral.isNumeral = function (obj) { + return obj instanceof Numeral; + }; + + // This function will load languages and then set the global language. If + // no arguments are passed in, it will simply return the current global + // language key. + numeral.language = function (key, values) { + if (!key) { + return currentLanguage; + } + + if (key && !values) { + currentLanguage = key; + } + + if (values || !languages[key]) { + loadLanguage(key, values); + } + + return numeral; + }; + + numeral.language('en', { + delimiters: { + thousands: ',', + decimal: '.' + }, + abbreviations: { + thousand: 'k', + million: 'm', + billion: 'b', + trillion: 't' + }, + ordinal: function (number) { + var b = number % 10; + return (~~ (number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + }, + currency: { + symbol: '$' + } + }); + + numeral.zeroFormat = function (format) { + if (typeof(format) === 'string') { + zeroFormat = format; + } else { + zeroFormat = null; + } + }; + + /************************************ + Helpers + ************************************/ + + function loadLanguage(key, values) { + languages[key] = values; + } + + + /************************************ + Numeral Prototype + ************************************/ + + + numeral.fn = Numeral.prototype = { + + clone : function () { + return numeral(this); + }, + + format : function (inputString) { + return formatNumeral(this, inputString ? inputString : numeral.defaultFormat); + }, + + unformat : function (inputString) { + return unformatNumeral(this, inputString ? inputString : numeral.defaultFormat); + }, + + value : function () { + return this._n; + }, + + valueOf : function () { + return this._n; + }, + + set : function (value) { + this._n = Number(value); + return this; + }, + + add : function (value) { + this._n = this._n + Number(value); + return this; + }, + + subtract : function (value) { + this._n = this._n - Number(value); + return this; + }, + + multiply : function (value) { + this._n = this._n * Number(value); + return this; + }, + + divide : function (value) { + this._n = this._n / Number(value); + return this; + }, + + difference : function (value) { + var difference = this._n - Number(value); + + if (difference < 0) { + difference = -difference; + } + + return difference; + } + + }; + + /************************************ + Exposing Numeral + ************************************/ + + // CommonJS module is defined + if (hasModule) { + module.exports = numeral; + } + + /*global ender:false */ + if (typeof ender === 'undefined') { + // here, `this` means `window` in the browser, or `global` on the server + // add `numeral` as a global object via a string identifier, + // for Closure Compiler 'advanced' mode + this['numeral'] = numeral; + } + + /*global define:false */ + if (typeof define === 'function' && define.amd) { + define([], function () { + return numeral; + }); + } +}).call(this); diff --git a/modules/backend/formwidgets/datagrid/partials/_datagrid.htm b/modules/backend/formwidgets/datagrid/partials/_datagrid.htm index f53c12f86..33d16d8bc 100644 --- a/modules/backend/formwidgets/datagrid/partials/_datagrid.htm +++ b/modules/backend/formwidgets/datagrid/partials/_datagrid.htm @@ -9,8 +9,16 @@ style="width:100%" class="control-datagrid" data-control="datagrid" + data-data-locker="#getId('datalocker') ?>" data-autocomplete-handler="getEventHandler('onAutocomplete') ?>" > + +