/** * This {@link Ext.grid.Grid grid} plugin adds clipboard support to a grid. * * This class supports the following `{@link Ext.plugin.AbstractClipboard#formats formats}` * for grid data: * * * `cell` - Complete field data that can be matched to other grids using the same * {@link Ext.data.Model model} regardless of column order. * * `text` - Cell content stripped of HTML tags. * * `html` - Complete cell content, including any rendered HTML tags. * * `raw` - Underlying field values based on `dataIndex`. * * The `cell` format is not valid for the `{@link Ext.plugin.AbstractClipboard#system system}` * clipboard format. */Ext.define('Ext.grid.plugin.Clipboard', { extend: 'Ext.plugin.AbstractClipboard', alias: 'plugin.clipboard', requires: [ 'Ext.util.Format', 'Ext.util.TSV' ], formats: { cell: { get: 'getCells' }, html: { get: 'getCellData' }, raw: { get: 'getCellData', put: 'putCellData' } }, gridListeners: { initialize: 'onCmpReady' }, getCellData: function(format, erase) { var cmp = this.getCmp(), selectable = cmp.getSelectable(), selection = selectable && selectable.getSelection(), ret = [], isRaw = format === 'raw', isText = format === 'text', data, dataIndex, lastRecord, column, record, row; if (selection) { selection.eachCell(function(location, colIdx, rowIdx) { column = location.column; record = location.record; // Do not copy data from ignored columns if (column.getIgnoreExport()) { return; } if (lastRecord !== record) { lastRecord = record; ret.push(row = []); } dataIndex = column.getDataIndex(); data = record.data[dataIndex]; if (!isRaw) { // printValue takes care of not yet rendered cells data = column.printValue(data); if (isText) { data = Ext.util.Format.stripTags(data); } } row.push(data); if (erase && dataIndex) { record.set(dataIndex, null); } }); } // See decode() comment below return Ext.util.TSV.encode(ret); }, getCells: function(format, erase) { var cmp = this.getCmp(), selectable = cmp.getSelectable(), selection = selectable && selectable.getSelection(), ret = [], dataIndex, lastRecord, record, row; if (selection) { selection.eachCell(function(location) { record = location.record; if (lastRecord !== record) { lastRecord = record; ret.push(row = { model: record.self, fields: [] }); } dataIndex = location.column.getDataIndex(); row.fields.push({ name: dataIndex, value: record.data[dataIndex] }); if (erase && dataIndex) { record.set(dataIndex, null); } }); } return ret; }, getTextData: function(format, erase) { return this.getCellData(format, erase); }, putCellData: function(data, format) { var cmp = this.getCmp(), // We pass null as field quote here to override default TSV decoding behavior // that will try to unquote fields and break if double quote character is // encountered in the data. TSV format does not support any kind of field quoting // but Ext.util.TSV mistakenly assumed otherwise pre-6.5.3 values = Ext.util.TSV.decode(data, undefined, null), recCount = values.length, colCount = recCount ? values[0].length : 0, columns = cmp.getHeaderContainer().getVisibleColumns(), store = cmp.getStore(), maxRowIdx = store ? store.getCount() - 1 : 0, maxColIdx = columns.length - 1, selectable = cmp.getSelectable(), selection = selectable && selectable.getSelection(), row, sourceRowIdx, sourceColIdx, column, record, columnIndex, recordIndex, dataObject, destination, dataIndex, startColumnIndex, startRecordIndex; if (maxRowIdx <= 0 || maxColIdx <= 0) { return; } if (selection) { selection.eachCell(function(c) { destination = c; return false; }); } startColumnIndex = destination ? destination.columnIndex : 0; startRecordIndex = destination ? destination.recordIndex : 0; for (sourceRowIdx = 0; sourceRowIdx < recCount; sourceRowIdx++) { row = values[sourceRowIdx]; recordIndex = startRecordIndex + sourceRowIdx; // If we are at the end of the destination store, break the row loop. if (recordIndex > maxRowIdx) { break; } record = store.getAt(recordIndex); dataObject = {}; columnIndex = startColumnIndex; sourceColIdx = 0; // Collect new values in dataObject while (sourceColIdx < colCount && columnIndex <= maxColIdx) { column = columns[columnIndex]; dataIndex = column.getDataIndex(); // we skip ignored columns if (!column.getIgnoreExport()) { // paste the content if the column allows us to do that, otherwise we ignore it if (dataIndex && (format === 'raw' || format === 'text')) { dataObject[dataIndex] = row[sourceColIdx]; } sourceColIdx++; } columnIndex++; } // Update the record in one go. record.set(dataObject); } }, putTextData: function(data, format) { this.putCellData(data, format); }, getTarget: function(comp) { return comp.element; }, privates: { validateAction: function(event) { var cmp = this.getCmp(), viewLocation = cmp.getNavigationModel().getLocation(), selectable = cmp.getSelectable(), checkColumn = selectable && selectable.getCheckbox(); // if current location's column is not the checkbox selection column then allow copying if (viewLocation && viewLocation.actionable && checkColumn !== viewLocation.column) { return false; } } }});