@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,388 lines (1,118 loc) • 77 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ({
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(1729);
/***/ }),
/***/ 3:
/***/ (function(module, exports) {
module.exports = function() { throw new Error("define cannot be used indirect"); };
/***/ }),
/***/ 20:
/***/ (function(module, exports) {
module.exports = require("../kendo.core");
/***/ }),
/***/ 957:
/***/ (function(module, exports) {
module.exports = require("../kendo.color");
/***/ }),
/***/ 1684:
/***/ (function(module, exports) {
module.exports = require("./runtime");
/***/ }),
/***/ 1712:
/***/ (function(module, exports) {
module.exports = require("./references");
/***/ }),
/***/ 1729:
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function(f, define){
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [ __webpack_require__(20), __webpack_require__(957), __webpack_require__(1684), __webpack_require__(1730), __webpack_require__(1712) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (f), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
})(function(){
(function(kendo) {
/* jshint laxbreak:true, eqnull:true */
var RangeRef = kendo.spreadsheet.RangeRef;
var UnionRef = kendo.spreadsheet.UnionRef;
var CellRef = kendo.spreadsheet.CellRef;
var Range = kendo.spreadsheet.Range;
// This is a “dynamic variable” (see Greenspun's 10th rule). It's
// bound to an array via sheet._saveModifiedFormulas (which see)
// while the callback runs. The goal is to enable external code
// to get a list of formulas or validations that have been
// adjusted as an effect of an insert/delete row/column operation,
// to be able to undo it.
//
// The reason why simply saving the state via sheet.getState() or
// range.getState() won't suffice is that an insert or delete
// operation can have far-reaching effects, like adjusting
// formulas from another sheet.
var MODIFIED_FORMULAS;
var Selection = kendo.Class.extend({
init: function(sheet) {
this._sheet = sheet;
this.selection = kendo.spreadsheet.FIRSTREF.toRangeRef();
this.originalSelection = kendo.spreadsheet.FIRSTREF.toRangeRef();
this._activeCell = kendo.spreadsheet.FIRSTREF.toRangeRef();
this.originalActiveCell = kendo.spreadsheet.FIRSTREF;
},
currentSelectionRange: function() {
return this.selection.rangeAt(this.selectionRangeIndex).toRangeRef();
},
currentOriginalNavigationRange: function() {
return this.originalSelection.rangeAt(this.selectionRangeIndex).toRangeRef();
},
currentNavigationRange: function() {
if (this.singleCellSelection()) {
return this._sheet._sheetRef;
} else {
return this.selection.rangeAt(this.selectionRangeIndex).toRangeRef();
}
},
nextNavigationRange: function() {
if (!this.singleCellSelection()) {
this.selectionRangeIndex = this.selection.nextRangeIndex(this.selectionRangeIndex);
}
return this.currentNavigationRange();
},
previousNavigationRange: function() {
if (!this.singleCellSelection()) {
this.selectionRangeIndex = this.selection.previousRangeIndex(this.selectionRangeIndex);
}
return this.currentNavigationRange();
},
activeCell: function(ref) {
if (ref) {
this.originalActiveCell = ref.first();
this._activeCell = this._sheet.unionWithMerged(ref.toRangeRef());
this._sheet.focus(ref);
this._sheet.triggerChange({ activeCell: true, selection: true });
}
return this._activeCell;
},
select: function(ref, expanded, changeActiveCell, view) {
if (ref) {
if (ref.eq(this.originalSelection)) {
return;
}
this._sheet.triggerSelect(new Range(ref, this._sheet));
this.originalSelection = ref;
this.selection = expanded;
if (changeActiveCell !== false) {
if (ref.isCell()) {
// when selecting part of a merged cell, i.e. A1:B2 is merged and select(B2)
// is requested, mark A1 as the actually active cell.
// https://github.com/telerik/kendo/issues/7698
this._sheet.forEachMergedCell(ref, function(merged){
ref = merged.topLeft;
});
this.activeCell(ref);
} else {
ref = this.selection.lastRange();
if (view && view._sheet === this._sheet && view.panes[0]) {
// we should intersect this ref with the range that's currently on
// screen, so that the view doesn't scroll (that is, keep the activeCell
// visible) - https://github.com/telerik/kendo-ui-core/issues/5182 —
// XXX: fiddling with View internals here is probably bad practice, but
// this whole thing is a mess already, so it can be forgiven. :-/
var panes = view.panes;
var i, pane, rows, cols, visible, newRef;
for (i = 0; i < panes.length; i++) {
pane = panes[i];
if (!pane._currentView) {
continue;
}
rows = pane._currentView.rows.values;
cols = pane._currentView.columns.values;
visible = new RangeRef(
new CellRef(rows.start, cols.start),
new CellRef(rows.end, cols.end)
);
newRef = ref.intersect(visible);
if (newRef !== kendo.spreadsheet.NULLREF) {
break;
}
}
if (newRef && newRef !== kendo.spreadsheet.NULLREF) {
ref = newRef;
}
}
this.activeCell(ref.first());
}
this.selectionRangeIndex = this.selection.size() - 1;
} else {
this._sheet.triggerChange({ selection: true });
}
}
return this.selection;
},
singleCellSelection: function() {
return this._activeCell.eq(this.selection);
}
});
var Sheet = kendo.Observable.extend({
init: function() {
kendo.Observable.prototype.init.call(this);
this._reinit.apply(this, arguments);
},
events: [
"changing",
"commandRequest",
"afterInsertRow",
"afterDeleteRow",
"insertRow",
"insertColumn",
"deleteRow",
"deleteColumn",
"hideRow",
"hideColumn",
"unhideRow",
"unhideColumn",
"select",
"dataBinding",
"dataBound",
"progress"
],
_reinit: function(rowCount, columnCount, rowHeight, columnWidth, headerHeight, headerWidth, defaultCellStyle) {
defaultCellStyle = defaultCellStyle || {};
this._defaultCellStyle = {
background: defaultCellStyle.background,
color: defaultCellStyle.color,
fontFamily: defaultCellStyle.fontFamily,
fontSize: defaultCellStyle.fontSize,
italic: defaultCellStyle.italic,
bold: defaultCellStyle.bold,
underline: defaultCellStyle.underline,
wrap: defaultCellStyle.wrap,
verticalAlign: defaultCellStyle.verticalAlign,
textAlign: defaultCellStyle.textAlign
};
this._rows = new kendo.spreadsheet.Axis(rowCount, rowHeight);
this._columns = new kendo.spreadsheet.Axis(columnCount, columnWidth);
this._filteredRows = new kendo.spreadsheet.RangeList(0, rowCount - 1, false);
this._mergedCells = [];
this._frozenRows = 0;
this._frozenColumns = 0;
this._suspendChanges = false;
this._filter = null;
this._showGridLines = true;
this._gridLinesColor = null;
this._grid = new kendo.spreadsheet.Grid(this._rows, this._columns, rowCount, columnCount, headerHeight, headerWidth);
this._sheetRef = this._grid.normalize(kendo.spreadsheet.SHEETREF);
this._properties = new kendo.spreadsheet.PropertyBag(rowCount, columnCount, this._defaultCellStyle);
this._sorter = new kendo.spreadsheet.Sorter(this._grid, this._properties.sortable());
this._viewSelection = new Selection(this);
this._editSelection = new Selection(this);
this._formulaSelections = [];
this._drawings = [];
},
resize: function(newRows, newCols) {
newRows = Math.max(newRows, 1);
newCols = Math.max(newCols, 1);
var oldRows = this._rows._count;
var oldCols = this._columns._count;
if (newRows < oldRows) {
this.range(newRows, 0, oldRows - newRows, oldCols).clear();
}
if (newCols < oldCols) {
this.range(0, newCols, oldRows, oldCols - newCols).clear();
}
this._rows._resize(newRows);
this._columns._resize(newCols);
this._grid._resize(newRows, newCols);
this._properties._resize(newRows, newCols);
this._sheetRef = this._grid.normalize(kendo.spreadsheet.SHEETREF);
if (newRows > oldRows) {
this.range(oldRows, 0, newRows - oldRows, newCols).clear();
this._filteredRows.value(oldRows, newRows - 1, false);
}
if (newCols > oldCols) {
this.range(0, oldCols, newRows, newCols - oldCols).clear();
}
this.triggerChange({ layout: true });
},
_resizeAddRow: function(count) {
this.resize(this._rows._count + (count || 1), this._columns._count);
},
_resizeAddColumn: function(count) {
this.resize(this._rows._count, this._columns._count + (count || 1));
},
_resizeDeleteRow: function(count) {
this._resizeAddRow(-(count || 1));
},
_resizeDeleteColumn: function(count) {
this._resizeAddColumn(-(count || 1));
},
_resizeForJSON: function(rows) {
var rowCount = rows.length, colCount = 0;
for (var ri = 0; ri < rows.length; ++ri) {
var row = rows[ri];
if (row.index != null) {
rowCount = Math.max(rowCount, row.index + 1);
}
if (row.cells) {
colCount = Math.max(colCount, row.cells.length);
for (var ci = 0; ci < row.cells.length; ++ci) {
var cell = row.cells[ci];
if (cell.index != null) {
colCount = Math.max(colCount, cell.index + 1);
}
}
}
}
this.resize(Math.max(rowCount, this._rows._count),
Math.max(colCount, this._columns._count));
},
_selectionState: function() {
return this._inEdit ? this._editSelection : this._viewSelection;
},
navigator: function() {
if(!this._navigator) {
this._navigator = new kendo.spreadsheet.SheetNavigator(this);
}
return this._navigator;
},
axisManager: function() {
if(!this._axisManager) {
this._axisManager = new kendo.spreadsheet.AxisManager(this);
}
return this._axisManager;
},
_name: function(value) {
if (!value) {
return this._sheetName;
}
this._sheetName = value;
return this;
},
name: function() {
return this._name();
},
_property: function(accessor, value, reason) {
if (value === undefined) {
return accessor();
} else {
accessor(value);
return this.triggerChange(reason);
}
},
_field: function(name, value, reason) {
if (value === undefined) {
return this[name];
} else {
this[name] = value;
return this.triggerChange(reason);
}
},
suspendChanges: function(value) {
if (value === undefined) {
return this._suspendChanges;
}
this._suspendChanges = value;
return this;
},
triggerChange: function(reason) {
if (!this._suspendChanges) {
this.trigger("change", reason);
}
return this;
},
triggerSelect: function(range) {
this.trigger("select", { range: range });
},
setDataSource: function(dataSource, columns) {
if (this.dataSourceBinder) {
this.dataSourceBinder.destroy();
}
this.dataSourceBinder = new kendo.spreadsheet.SheetDataSourceBinder({
dataSource: dataSource,
sheet: this,
columns: columns
});
this.dataSource = this.dataSourceBinder.dataSource;
},
hideColumn: function(columnIndex) {
if (this.trigger("hideColumn", { index: columnIndex })) {
return;
}
return this._property(this._columns.hide.bind(this._columns), columnIndex, { layout: true });
},
unhideColumn: function(columnIndex) {
if (this.trigger("unhideColumn", { index: columnIndex })) {
return;
}
return this._property(this._columns.unhide.bind(this._columns), columnIndex, { layout: true });
},
isHiddenColumn: function(columnIndex) {
return this._grid._columns.hidden(columnIndex);
},
_copyRange: function(sourceRangeRef, targetRef) {
var grid = this._grid;
var rowCount = grid.rowCount;
var nextRefTopLeft = grid.normalize(sourceRangeRef.topLeft);
var nextRefBottomRight = grid.normalize(sourceRangeRef.bottomRight);
var nextIndex = nextRefTopLeft.col * rowCount + nextRefTopLeft.row;
var nextBottomIndex = nextRefBottomRight.col * rowCount + nextRefBottomRight.row;
var targetIndex = targetRef.col * rowCount + targetRef.row;
this._properties.copy(nextIndex, nextBottomIndex, targetIndex);
},
_saveModifiedFormulas: function(array, callback) {
var save = MODIFIED_FORMULAS;
MODIFIED_FORMULAS = array;
var ret = callback();
MODIFIED_FORMULAS = save;
return ret;
},
_restoreModifiedFormulas: function(array) {
var wb = this._workbook;
array.forEach(function(f){
var sheet = wb.sheetByName(f.sheet), index;
if (f instanceof kendo.spreadsheet.calc.runtime.Formula) {
index = sheet._grid.cellRefIndex(f); // f has row, col
sheet._properties.set("formula", index, index, f);
}
if (f instanceof kendo.spreadsheet.validation.Validation) {
index = sheet._grid.cellRefIndex(f); // f has row, col
sheet._properties.set("validation", index, index, f);
}
});
},
_adjustReferences: function(operation, start, delta, mergedCells) {
this._mergedCells = mergedCells.reduce(function(a, ref){
ref = ref.adjust(null, null, null, null, operation == "row", start, delta);
if (ref instanceof RangeRef) {
a.push(ref);
}
return a;
}, []);
if (this._workbook) {
var affectedSheet = this._name();
this._workbook._sheets.forEach(function(sheet){
sheet._forFormulas(function(formula){
var prev = formula.adjust(affectedSheet, operation, start, delta);
if (prev && MODIFIED_FORMULAS) {
// if formula.adjust returns non-null,
// that means the formula was indeed
// modified and the returned value is a
// copy of the previous Formula, which we
// can use for undoing the operation.
MODIFIED_FORMULAS.push(prev);
}
});
sheet._forValidations(function(validation){
var prev = validation.adjust(affectedSheet, operation, start, delta);
if (prev && MODIFIED_FORMULAS) {
MODIFIED_FORMULAS.push(prev);
}
});
});
this._workbook.adjustNames(affectedSheet, operation == "row", start, delta);
}
var selection = this.select();
selection = selection.adjust(null, null, null, null, operation == "row", start, delta);
if (selection !== kendo.spreadsheet.NULLREF) {
this.select(selection);
}
// adjust column widths or row heights and hidden attribute
var axis = operation == "col" ? this._columns : this._rows;
axis.adjust(start, delta);
if (operation == "row") {
if (delta < 0) {
this._filteredRows.copy(start - delta, this._rows._count - 1, start);
} else {
this._filteredRows.copy(start, this._rows._count, start + delta);
this._filteredRows.value(start, start + delta - 1, false);
}
}
// adjust drawing anchor cells
this._drawings.forEach(function(drawing){
if (drawing.topLeftCell) {
drawing.topLeftCell = drawing.topLeftCell.adjust(null, null, null, null,
operation == "row", start, delta);
}
});
},
_forFormulas: function(callback) {
var props = this._properties;
var formulas = props.get("formula").values();
var n = formulas.length;
formulas.forEach(function(f, i){
callback.call(this, f.value, i, n);
}, this);
},
_forValidations: function(callback) {
var props = this._properties;
props.get("validation").values().forEach(function(v){
callback.call(this, v.value);
}, this);
},
insertRow: function(rowIndex, skipDataSourceInsert) {
if (this.trigger("insertRow", { index: rowIndex })) {
return;
}
this.batch(function() {
this._resizeAddRow();
var grid = this._grid;
var columnCount = grid.columnCount;
var rowCount = grid.rowCount;
if (rowIndex + 1 == rowCount) {
// we just inserted after the last one, no need to shift data
return;
}
var frozenRows = this.frozenRows();
if (rowIndex < frozenRows) {
this.frozenRows(frozenRows + 1);
}
var mergedCells = this._mergedCells.slice();
for (var ci = 0; ci < columnCount; ci++) {
var ref = new RangeRef(new CellRef(rowIndex, ci), new CellRef(rowIndex, ci));
var topLeft = grid.normalize(ref.topLeft);
var bottomRight = grid.normalize(ref.bottomRight);
var nextRef = new RangeRef(
new CellRef(topLeft.row, topLeft.col),
new CellRef(rowCount - 2, bottomRight.col)
);
this._copyRange(nextRef, new CellRef(topLeft.row + 1, topLeft.col));
new Range(ref, this).clear({ clearAll: true, keepBorders: true });
}
this._adjustReferences("row", rowIndex, 1, mergedCells);
}, {
recalc: true,
layout: true,
insertRow: { index: rowIndex },
ref: new RangeRef(new CellRef(rowIndex, 0), new CellRef(Infinity, Infinity))
});
if (!skipDataSourceInsert) {
this.trigger("afterInsertRow", { index: rowIndex });
}
return this;
},
isEnabledRow: function(rowIndex) {
var ref = new RangeRef(new CellRef(rowIndex, 0), new CellRef(rowIndex, this._grid.columnCount));
return new Range(ref, this).enable();
},
deleteRow: function(rowIndex, skipDataSourceDelete) {
if (!this.isEnabledRow(rowIndex)) {
return this;
}
if (this.trigger("deleteRow", { index: rowIndex })) {
return;
}
this.batch(function() {
var grid = this._grid;
var columnCount = grid.columnCount;
var frozenRows = this.frozenRows();
if (rowIndex < frozenRows) {
this.frozenRows(frozenRows - 1);
}
var mergedCells = this._mergedCells.slice();
for (var ci = 0; ci < columnCount; ci++) {
var ref = new RangeRef(new CellRef(rowIndex, ci), new CellRef(rowIndex, ci));
new Range(ref, this).clear({ clearAll: true, keepBorders: true });
var topLeft = grid.normalize(ref.topLeft);
var bottomRight = grid.normalize(ref.bottomRight);
var nextRef = new RangeRef(
new CellRef(topLeft.row + 1, topLeft.col),
new CellRef(Infinity, bottomRight.col)
);
this._copyRange(nextRef, topLeft);
var nextRefBottomRight = grid.normalize(nextRef.bottomRight);
new Range(new RangeRef(nextRefBottomRight, nextRefBottomRight), this).clear();
}
this._adjustReferences("row", rowIndex, -1, mergedCells);
this._resizeDeleteRow();
}, {
recalc: true,
layout: true,
deleteRow: { index: rowIndex },
ref: new RangeRef(new CellRef(rowIndex, 0), new CellRef(Infinity, Infinity))
});
if (!skipDataSourceDelete) {
this.trigger("afterDeleteRow", { index: rowIndex });
}
return this;
},
insertColumn: function(columnIndex) {
if (this.trigger("insertColumn", { index: columnIndex })) {
return;
}
this.batch(function() {
this._resizeAddColumn();
var grid = this._grid;
var columnCount = grid.columnCount;
if (columnIndex + 1 == columnCount) {
// we just inserted after the last one, no need to shift data
return;
}
var frozenColumns = this.frozenColumns();
if (columnIndex < frozenColumns) {
this.frozenColumns(frozenColumns + 1);
}
var mergedCells = this._mergedCells.slice();
for (var ci = columnCount; ci >= columnIndex; ci--) {
var ref = new RangeRef(new CellRef(0, ci), new CellRef(Infinity, ci));
new Range(ref, this).clear({ clearAll: true, keepBorders: true });
if (ci == columnIndex) {
break;
}
var topLeft = grid.normalize(ref.topLeft);
var bottomRight = grid.normalize(ref.bottomRight);
var nextRef = new RangeRef(
new CellRef(topLeft.row, topLeft.col - 1),
new CellRef(bottomRight.row, bottomRight.col - 1)
);
this._copyRange(nextRef, topLeft);
}
this._adjustReferences("col", columnIndex, 1, mergedCells);
}, {
recalc: true,
layout: true,
insertColumn: { index: columnIndex },
ref: new RangeRef(new CellRef(0, columnIndex), new CellRef(Infinity, Infinity))
});
return this;
},
isEnabledColumn: function(columnIndex) {
var ref = new RangeRef(new CellRef(0, columnIndex), new CellRef(Infinity, columnIndex));
return new Range(ref, this).enable();
},
deleteColumn: function(columnIndex) {
if (!this.isEnabledColumn(columnIndex)) {
return this;
}
if (this.trigger("deleteColumn", { index: columnIndex })) {
return;
}
this.batch(function() {
var grid = this._grid;
var columnCount = grid.columnCount;
var frozenColumns = this.frozenColumns();
if (columnIndex < frozenColumns) {
this.frozenColumns(frozenColumns - 1);
}
var mergedCells = this._mergedCells.slice();
for (var ci = columnIndex; ci < columnCount; ci++) {
var ref = new RangeRef(new CellRef(0, ci), new CellRef(Infinity, ci));
new Range(ref, this).clear({ clearAll: true, keepBorders: true });
if (ci == columnCount - 1) {
break;
}
var topLeft = grid.normalize(ref.topLeft);
var bottomRight = grid.normalize(ref.bottomRight);
var nextRef = new RangeRef(
new CellRef(topLeft.row, topLeft.col + 1),
new CellRef(bottomRight.row, bottomRight.col + 1)
);
this._copyRange(nextRef, topLeft);
}
this._adjustReferences("col", columnIndex, -1, mergedCells);
this._resizeDeleteColumn();
}, {
recalc: true,
layout: true,
deleteColumn: { index: columnIndex },
ref: new RangeRef(new CellRef(0, columnIndex), new CellRef(Infinity, Infinity))
});
return this;
},
_filterRow: function(rowIndex) {
this._rows.hide(rowIndex);
this._filteredRows.value(rowIndex, rowIndex, true);
this.triggerChange({ layout: true });
},
hideRow: function(rowIndex) {
if (this.trigger("hideRow", { index: rowIndex })) {
return;
}
return this._property(this._rows.hide.bind(this._rows), rowIndex, { layout: true });
},
unhideRow: function(rowIndex) {
if (this.trigger("unhideRow", { index: rowIndex })) {
return;
}
return this._property(this._rows.unhide.bind(this._rows), rowIndex, { layout: true });
},
isHiddenRow: function(rowIndex) {
return this._grid._rows.hidden(rowIndex);
},
isFilteredRow: function(rowIndex) {
return this._filteredRows.value(rowIndex);
},
columnWidth: function(columnIndex, width) {
return this._property(this._columns.value.bind(this._columns, columnIndex, columnIndex), width, { layout: true });
},
rowHeight: function(rowIndex, height) {
return this._property(this._rows.value.bind(this._rows, rowIndex, rowIndex), height, { layout: true });
},
frozenRows: function(value) {
return this._field("_frozenRows", value, { layout: true });
},
frozenColumns: function(value) {
return this._field("_frozenColumns", value, { layout: true });
},
showGridLines: function(value) {
return this._field("_showGridLines", value, { layout: true });
},
gridLinesColor: function(value) {
return this._field("_gridLinesColor", value, { layout: true });
},
_ref: function(row, column, numRows, numColumns) {
var ref = null;
if (row instanceof kendo.spreadsheet.Ref) {
return row;
}
if (row instanceof kendo.spreadsheet.Range) {
return row._ref.toRangeRef();
}
if (typeof row === "string") {
ref = kendo.spreadsheet.calc.parseReference(row);
} else {
if (!numRows) {
numRows = 1;
}
if (!numColumns) {
numColumns = 1;
}
ref = new RangeRef(new CellRef(row, column), new CellRef(row + numRows - 1, column + numColumns - 1));
}
return ref;
},
range: function(row, column, numRows, numColumns) {
return new Range(this._ref(row, column, numRows, numColumns), this);
},
_getMergedCells: function(range) {
var grid = this._grid;
var primary = {};
var secondary = {};
var hasMerged = false;
this.forEachMergedCell(range, function(ref) {
var topLeft = ref.topLeft;
grid.forEach(ref, function(cellRef) {
if (topLeft.eq(cellRef)) {
primary[cellRef.print()] = ref;
hasMerged = true;
} else if (range.contains(cellRef)) {
secondary[cellRef.print()] = topLeft;
hasMerged = true;
}
});
});
return { primary: primary, secondary: secondary, hasMerged: hasMerged };
},
forEachMergedCell: function(ref, callback) {
var selectAll = false;
if (typeof callback === "undefined") {
callback = ref;
selectAll = true;
}
this._mergedCells.forEach(function(merged) {
if (selectAll || merged.intersects(ref)) {
callback(merged);
}
});
},
forEachFilterHeader: function(ref, callback) {
var selectAll = false;
if (typeof callback === "undefined") {
callback = ref;
selectAll = true;
}
if (this._filter) {
var refs = [];
// get refs of all columns
this._filter.ref.forEachColumn(function(columnRef) {
if (selectAll || columnRef.intersects(ref)) {
refs.push(columnRef.topLeft);
}
});
// filter out merged references
this._mergedCells.forEach(function(merged) {
refs = refs.map(function(ref) {
if (merged.intersects(ref)) {
return merged;
}
return ref;
});
});
// use only unique refs
refs.reduce(function unique(result, element) {
if (result.indexOf(element) < 0) {
result.push(element);
}
return result;
}, []).forEach(callback);
}
},
forEach: function(ref, callback) {
var self = this;
function forEachRange(ref) {
if (!(ref instanceof RangeRef)) {
ref = ref.toRangeRef();
}
var topLeft = self._grid.normalize(ref.topLeft);
var bottomRight = self._grid.normalize(ref.bottomRight);
var ci, ri;
function doIt(value) {
callback(ri++, ci, value);
}
for (ci = topLeft.col; ci <= bottomRight.col; ci++) {
ri = topLeft.row;
var startCellIndex = self._grid.index(ri, ci);
var endCellIndex = self._grid.index(bottomRight.row, ci);
self._properties.forEach(startCellIndex, endCellIndex, doIt);
}
}
if (!(ref instanceof RangeRef)) {
ref = self._ref(ref);
}
if (ref instanceof UnionRef) {
// _ref() might still return a UnionRef, for instance, if
// `ref` happens to be one. Probably an oversight, but it
// turns out to be useful.
ref.forEach(forEachRange);
} else {
forEachRange(ref);
}
},
startResizing: function(initialPosition) {
this._initialPosition = initialPosition;
this._resizeInProgress = true;
},
startAutoFill: function() {
this._autoFillInProgress = true;
var selection = this.select();
this._autoFillOrigin = selection;
this._autoFillDest = selection;
this.triggerChange({ selection: true });
},
updateAutoFill: function(dest, punch, hint, direction) {
this._autoFillDest = dest;
this._autoFillPunch = punch;
this._autoFillHint = hint;
this._autoFillDirection = direction;
this.triggerChange({ selection: true });
},
autoFillRef: function() {
return this._autoFillDest;
},
autoFillPunch: function() {
return this._autoFillPunch;
},
autoFillInProgress: function() {
return this._autoFillInProgress;
},
resizingInProgress: function() {
return this._resizeInProgress;
},
draggingInProgress: function() {
return this._draggingInProgress;
},
completeResizing: function() {
if (this._resizeInProgress) {
this._resizeInProgress = false;
var hintPosition = this.resizeHintPosition();
if (this._initialPosition && hintPosition) {
var handlePosition = this.resizeHandlePosition();
if (handlePosition.col !== -Infinity) {
this.trigger("commandRequest", {
command: "ColumnWidthCommand",
options: {
target: handlePosition.col,
value: this.columnWidth(handlePosition.col) - (this._initialPosition.x - hintPosition.x)
}
});
} else {
this.trigger("commandRequest", {
command: "RowHeightCommand",
options: {
target: handlePosition.row,
value: this.rowHeight(handlePosition.row) - (this._initialPosition.y - hintPosition.y)
}
});
}
} else {
this.trigger("change", { resize: true });
}
}
},
_visualRange: function(ref) {
var merged = this._mergedCells;
for (var i = merged.length; --i >= 0;) {
if (merged[i].intersects(ref)) {
return this.range(merged[i]);
}
}
return this.range(ref);
},
_renderComment: function(ref) {
var comment = null;
if (ref) {
var range = this._visualRange(ref);
comment = range.comment();
ref = range._ref.toRangeRef().topLeft;
}
if (comment) {
if (!this._commentRef || !ref.eq(this._commentRef)) {
this._commentRef = ref;
this.trigger("change", { comment: true });
}
} else {
if (this._commentRef) {
this._commentRef = null;
this.trigger("change", { comment: true });
}
}
},
resizeHandlePosition: function() {
return this._resizeHandlePosition;
},
resizeHintPosition: function(location) {
if (location !== undefined) {
this._resizeHintPosition = location;
this.trigger("change", { resize: true });
}
return this._resizeHintPosition;
},
removeResizeHandle: function() {
if (this._resizeHandlePosition) {
this._resizeHintPosition = undefined;
this._resizeHandlePosition = undefined;
this._initialPosition = undefined;
this.trigger("change", { resize: true });
}
},
positionResizeHandle: function(ref) {
this._resizeHandlePosition = ref;
this.trigger("change", { resize: true });
},
startDragging: function(data) {
this._draggingInProgress = data;
},
completeDragging: function() {
var drag = this._draggingInProgress;
if (drag) {
this._draggingInProgress = null;
var drawing = drag.drawing;
if (drawing.eq(drag.copy)) {
return;
}
if (drawing.topLeftCell) {
// adjust reference - in case the top-left corner
// was moving, select the cell beneath it.
var box = this.drawingBoundingBox(drawing);
var row = this._rows.indexVisible(box.top);
var col = this._columns.indexVisible(box.left);
var ref = new CellRef(row, col);
var refBox = this.refBoundingBox(ref);
drawing.offsetX = box.left - refBox.left;
drawing.offsetY = box.top - refBox.top;
drawing.topLeftCell = ref;
this.triggerChange({ dragging: true });
}
this.trigger("commandRequest", {
command: "DrawingUpdateCommand",
options: {
sheet : this,
drawing : drawing,
previous : drag.copy
}
});
}
},
startSelection: function(view) {
if (this.frozenRows() || this.frozenColumns()) {
this._currentView = null;
} else if (view && view._sheet === this) {
this._currentView = view;
}
this._selectionInProgress = true;
},
completeSelection: function() {
if (this._selectionInProgress) {
this._selectionInProgress = false;
this._resizeHintPosition = undefined;
this.trigger("change", { selection: true });
}
if (this._autoFillInProgress) {
this._autoFillInProgress = false;
var dest = this._autoFillDest;
var origin = this._autoFillOrigin;
if (this._autoFillPunch) { // we just clear data here
this.trigger("commandRequest", {
command: "ClearContentCommand", options: { operatingRange: this.range(this._autoFillPunch) }
});
} else {
if (!dest.eq(origin)) {
this.trigger("commandRequest", {
command: "AutoFillCommand", options: { operatingRange: this.range(dest), origin: this.range(origin) }
});
} else {
this.triggerChange({ selection: true });
}
}
this._autoFillDest = null;
this._autoFillPunch = null;
this._autoFillOrigin = null;
this.select(dest);
}
},
selectionInProgress: function() {
return this._selectionInProgress;
},
select: function(ref, changeActiveCell) {
var selectionState = this._selectionState();
var expandedRef;
if (ref) {
ref = this._ref(ref);
ref = this._grid.normalize(ref);
expandedRef = this._grid.isAxis(ref) ? ref : this.unionWithMerged(ref);
}
return selectionState.select(ref, expandedRef, changeActiveCell, this._currentView);
},
originalSelect: function() {
return this._selectionState().originalSelection;
},
currentSelectionRange: function() {
return this._selectionState().currentSelectionRange();
},
currentOriginalSelectionRange: function() {
return this._selectionState().currentOriginalNavigationRange();
},
currentNavigationRange: function() {
return this._selectionState().currentNavigationRange();
},
nextNavigationRange: function() {
return this._selectionState().nextNavigationRange();
},
previousNavigationRange: function() {
return this._selectionState().previousNavigationRange();
},
selectionRangeIndex: function() {
return this._selectionState().selectionRangeIndex;
},
activeCell: function(ref) {
return this._selectionState().activeCell(ref);
},
originalActiveCell: function() {
return this._selectionState().originalActiveCell;
},
singleCellSelection: function() {
return this._selectionState().singleCellSelection();
},
unionWithMerged: function(ref) {
var mergedCells = this._mergedCells;
return ref.map(function(ref) {
return ref.toRangeRef().union(mergedCells);
});
},
trim: function(ref) {
var trims = [];
var grid = this._grid;
this._properties.forEachProperty(function(property) {
trims.push(grid.trim(ref, property.list));
});
return this.unionWithMerged(ref.topLeft.toRangeRef().union(trims));
},
focus: function(ref) {
if (ref) {
this._focus = ref.toRangeRef();
} else {
var focus = this._focus;
this._focus = null;
return focus;
}
},
activeCellSelection: function() {
return new Range(this._grid.normalize(this.activeCell()), this);
},
selection: function() {
return new Range(this._grid.normalize(this._selectionState().selection), this);
},
selectedHeaders: function() {
var selection = this.select();
var rows = {};
var cols = {};
var allCols = false;
var allRows = false;
var maxRow = this._grid.rowCount - 1;
var maxCol = this._grid.columnCount - 1;
selection.forEach(function(ref) {
var i;
var rowState = "partial";
var colState = "partial";
ref = ref.toRangeRef();
var bottomRight = ref.bottomRight;
var topLeft = ref.topLeft;
var rowSelection = topLeft.col <= 0 && bottomRight.col >= maxCol;
var colSelection = topLeft.row <= 0 && bottomRight.row >= maxRow;
if (colSelection) { //column selection
allRows = true;
colState = "full";
}
if (rowSelection) { //row selection
allCols = true;
rowState = "full";
}
if (!colSelection) { //column selection
for (i = topLeft.row; i <= bottomRight.row; i++) {
if (rows[i] !== "full") {
rows[i] = rowState;
}
}
}
if (!rowSelection) {
for (i = topLeft.col; i <= bottomRight.col; i++) {
if (cols[i] !== "full") {
cols[i] = colState;
}
}
}
});
return {
rows: rows,
cols: cols,
allRows: allRows,
allCols: allCols,
all: allRows && allCols
};
},
isInEditMode: function(isInEdit) {
if (isInEdit === undefined) {
return this._inEdit;
}
this._inEdit = isInEdit;
if (isInEdit) {
this._editSelection.selection = this._viewSelection.selection.clone();
this._editSelection.originalSelection = this._viewSelection.originalSelection.clone();
this._editSelection._activeCell = this._viewSelection._activeCell.clone();
this._editSelection.originalActiveCell = this._vie