12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347 |
- var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
- var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
- function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
- import numbro from 'numbro';
- import { addClass, empty, isChildOfWebComponentTable, removeClass } from './helpers/dom/element';
- import { columnFactory } from './helpers/setting';
- import { isFunction } from './helpers/function';
- import { isDefined, isUndefined, isRegExp } from './helpers/mixed';
- import { isMobileBrowser } from './helpers/browser';
- import DataMap from './dataMap';
- import EditorManager from './editorManager';
- import EventManager from './eventManager';
- import { deepClone, duckSchema, extend, isObject, isObjectEquals, deepObjectSize, hasOwnProperty, createObjectPropListener } from './helpers/object';
- import { arrayFlatten, arrayMap } from './helpers/array';
- import { getPlugin } from './plugins';
- import { getRenderer } from './renderers';
- import { getValidator } from './validators';
- import { randomString } from './helpers/string';
- import { rangeEach } from './helpers/number';
- import TableView from './tableView';
- import DataSource from './dataSource';
- import { translateRowsToColumns, cellMethodLookupFactory, spreadsheetColumnLabel } from './helpers/data';
- import { getTranslator } from './utils/recordTranslator';
- import { CellCoords, CellRange, ViewportColumnsCalculator } from './3rdparty/walkontable/src';
- import Hooks from './pluginHooks';
- import DefaultSettings from './defaultSettings';
- import { getCellType } from './cellTypes';
- var activeGuid = null;
- /**
- * Handsontable constructor
- *
- * @core
- * @dependencies numbro
- * @constructor Core
- * @description
- *
- * After Handsontable is constructed, you can modify the grid behavior using the available public methods.
- *
- * ---
- * ## How to call methods
- *
- * These are 2 equal ways to call a Handsontable method:
- *
- * ```js
- * // all following examples assume that you constructed Handsontable like this
- * var ht = new Handsontable(document.getElementById('example1'), options);
- *
- * // now, to use setDataAtCell method, you can either:
- * ht.setDataAtCell(0, 0, 'new value');
- * ```
- *
- * Alternatively, you can call the method using jQuery wrapper (__obsolete__, requires initialization using our jQuery guide
- * ```js
- * $('#example1').handsontable('setDataAtCell', 0, 0, 'new value');
- * ```
- * ---
- */
- export default function Core(rootElement, userSettings) {
- var priv,
- datamap,
- dataSource,
- grid,
- selection,
- editorManager,
- instance = this,
- GridSettings = function GridSettings() {},
- eventManager = new EventManager(instance);
- extend(GridSettings.prototype, DefaultSettings.prototype); // create grid settings as a copy of default settings
- extend(GridSettings.prototype, userSettings); // overwrite defaults with user settings
- extend(GridSettings.prototype, expandType(userSettings));
- this.rootElement = rootElement;
- this.isHotTableEnv = isChildOfWebComponentTable(this.rootElement);
- EventManager.isHotTableEnv = this.isHotTableEnv;
- this.container = document.createElement('DIV');
- this.renderCall = false;
- rootElement.insertBefore(this.container, rootElement.firstChild);
- this.guid = 'ht_' + randomString(); // this is the namespace for global events
- var recordTranslator = getTranslator(instance);
- dataSource = new DataSource(instance);
- if (!this.rootElement.id || this.rootElement.id.substring(0, 3) === 'ht_') {
- this.rootElement.id = this.guid; // if root element does not have an id, assign a random id
- }
- priv = {
- cellSettings: [],
- columnSettings: [],
- columnsSettingConflicts: ['data', 'width'],
- settings: new GridSettings(), // current settings instance
- selRange: null, // exposed by public method `getSelectedRange`
- isPopulated: null,
- scrollable: null,
- firstRun: true
- };
- grid = {
- /**
- * Inserts or removes rows and columns
- *
- * @memberof Core#
- * @function alter
- * @private
- * @param {String} action Possible values: "insert_row", "insert_col", "remove_row", "remove_col"
- * @param {Number} index
- * @param {Number} amount
- * @param {String} [source] Optional. Source of hook runner.
- * @param {Boolean} [keepEmptyRows] Optional. Flag for preventing deletion of empty rows.
- */
- alter: function alter(action, index, amount, source, keepEmptyRows) {
- var delta;
- amount = amount || 1;
- function spliceWith(data, index, count, toInject) {
- var valueFactory = function valueFactory() {
- var result = void 0;
- if (toInject === 'array') {
- result = [];
- } else if (toInject === 'object') {
- result = {};
- }
- return result;
- };
- var spliceArgs = arrayMap(new Array(count), function () {
- return valueFactory();
- });
- spliceArgs.unshift(index, 0);
- data.splice.apply(data, _toConsumableArray(spliceArgs));
- }
- /* eslint-disable no-case-declarations */
- switch (action) {
- case 'insert_row':
- var numberOfSourceRows = instance.countSourceRows();
- if (instance.getSettings().maxRows === numberOfSourceRows) {
- return;
- }
- index = isDefined(index) ? index : numberOfSourceRows;
- delta = datamap.createRow(index, amount, source);
- spliceWith(priv.cellSettings, index, amount, 'array');
- if (delta) {
- if (selection.isSelected() && priv.selRange.from.row >= index) {
- priv.selRange.from.row += delta;
- selection.transformEnd(delta, 0); // will call render() internally
- } else {
- selection.refreshBorders(); // it will call render and prepare methods
- }
- }
- break;
- case 'insert_col':
- delta = datamap.createCol(index, amount, source);
- for (var row = 0, len = instance.countSourceRows(); row < len; row++) {
- if (priv.cellSettings[row]) {
- spliceWith(priv.cellSettings[row], index, amount);
- }
- }
- if (delta) {
- if (Array.isArray(instance.getSettings().colHeaders)) {
- var spliceArray = [index, 0];
- spliceArray.length += delta; // inserts empty (undefined) elements at the end of an array
- Array.prototype.splice.apply(instance.getSettings().colHeaders, spliceArray); // inserts empty (undefined) elements into the colHeader array
- }
- if (selection.isSelected() && priv.selRange.from.col >= index) {
- priv.selRange.from.col += delta;
- selection.transformEnd(0, delta); // will call render() internally
- } else {
- selection.refreshBorders(); // it will call render and prepare methods
- }
- }
- break;
- case 'remove_row':
- datamap.removeRow(index, amount, source);
- priv.cellSettings.splice(index, amount);
- var totalRows = instance.countRows();
- var fixedRowsTop = instance.getSettings().fixedRowsTop;
- if (fixedRowsTop >= index + 1) {
- instance.getSettings().fixedRowsTop -= Math.min(amount, fixedRowsTop - index);
- }
- var fixedRowsBottom = instance.getSettings().fixedRowsBottom;
- if (fixedRowsBottom && index >= totalRows - fixedRowsBottom) {
- instance.getSettings().fixedRowsBottom -= Math.min(amount, fixedRowsBottom);
- }
- grid.adjustRowsAndCols();
- selection.refreshBorders(); // it will call render and prepare methods
- break;
- case 'remove_col':
- var logicalColumnIndex = recordTranslator.toPhysicalColumn(index);
- datamap.removeCol(index, amount, source);
- for (var _row = 0, _len = instance.countSourceRows(); _row < _len; _row++) {
- if (priv.cellSettings[_row]) {
- // if row hasn't been rendered it wouldn't have cellSettings
- priv.cellSettings[_row].splice(logicalColumnIndex, amount);
- }
- }
- var fixedColumnsLeft = instance.getSettings().fixedColumnsLeft;
- if (fixedColumnsLeft >= index + 1) {
- instance.getSettings().fixedColumnsLeft -= Math.min(amount, fixedColumnsLeft - index);
- }
- if (Array.isArray(instance.getSettings().colHeaders)) {
- if (typeof logicalColumnIndex == 'undefined') {
- logicalColumnIndex = -1;
- }
- instance.getSettings().colHeaders.splice(logicalColumnIndex, amount);
- }
- grid.adjustRowsAndCols();
- selection.refreshBorders(); // it will call render and prepare methods
- break;
- default:
- throw new Error('There is no such action "' + action + '"');
- }
- if (!keepEmptyRows) {
- grid.adjustRowsAndCols(); // makes sure that we did not add rows that will be removed in next refresh
- }
- },
- /**
- * Makes sure there are empty rows at the bottom of the table
- */
- adjustRowsAndCols: function adjustRowsAndCols() {
- if (priv.settings.minRows) {
- // should I add empty rows to data source to meet minRows?
- var rows = instance.countRows();
- if (rows < priv.settings.minRows) {
- for (var r = 0, minRows = priv.settings.minRows; r < minRows - rows; r++) {
- datamap.createRow(instance.countRows(), 1, 'auto');
- }
- }
- }
- if (priv.settings.minSpareRows) {
- var emptyRows = instance.countEmptyRows(true);
- // should I add empty rows to meet minSpareRows?
- if (emptyRows < priv.settings.minSpareRows) {
- for (; emptyRows < priv.settings.minSpareRows && instance.countSourceRows() < priv.settings.maxRows; emptyRows++) {
- datamap.createRow(instance.countRows(), 1, 'auto');
- }
- }
- }
- {
- var emptyCols = void 0;
- // count currently empty cols
- if (priv.settings.minCols || priv.settings.minSpareCols) {
- emptyCols = instance.countEmptyCols(true);
- }
- // should I add empty cols to meet minCols?
- if (priv.settings.minCols && !priv.settings.columns && instance.countCols() < priv.settings.minCols) {
- for (; instance.countCols() < priv.settings.minCols; emptyCols++) {
- datamap.createCol(instance.countCols(), 1, 'auto');
- }
- }
- // should I add empty cols to meet minSpareCols?
- if (priv.settings.minSpareCols && !priv.settings.columns && instance.dataType === 'array' && emptyCols < priv.settings.minSpareCols) {
- for (; emptyCols < priv.settings.minSpareCols && instance.countCols() < priv.settings.maxCols; emptyCols++) {
- datamap.createCol(instance.countCols(), 1, 'auto');
- }
- }
- }
- var rowCount = instance.countRows();
- var colCount = instance.countCols();
- if (rowCount === 0 || colCount === 0) {
- selection.deselect();
- }
- if (selection.isSelected()) {
- var selectionChanged = false;
- var fromRow = priv.selRange.from.row;
- var fromCol = priv.selRange.from.col;
- var toRow = priv.selRange.to.row;
- var toCol = priv.selRange.to.col;
- // if selection is outside, move selection to last row
- if (fromRow > rowCount - 1) {
- fromRow = rowCount - 1;
- selectionChanged = true;
- if (toRow > fromRow) {
- toRow = fromRow;
- }
- } else if (toRow > rowCount - 1) {
- toRow = rowCount - 1;
- selectionChanged = true;
- if (fromRow > toRow) {
- fromRow = toRow;
- }
- }
- // if selection is outside, move selection to last row
- if (fromCol > colCount - 1) {
- fromCol = colCount - 1;
- selectionChanged = true;
- if (toCol > fromCol) {
- toCol = fromCol;
- }
- } else if (toCol > colCount - 1) {
- toCol = colCount - 1;
- selectionChanged = true;
- if (fromCol > toCol) {
- fromCol = toCol;
- }
- }
- if (selectionChanged) {
- instance.selectCell(fromRow, fromCol, toRow, toCol);
- }
- }
- if (instance.view) {
- instance.view.wt.wtOverlays.adjustElementsSize();
- }
- },
- /**
- * Populate the data from the provided 2d array from the given cell coordinates.
- *
- * @private
- * @param {Object} start Start selection position.
- * @param {Array} input 2d data array.
- * @param {Object} [end] End selection position (only for drag-down mode).
- * @param {String} [source="populateFromArray"] Source information string.
- * @param {String} [method="overwrite"] Populate method. Possible options: `shift_down`, `shift_right`, `overwrite`.
- * @param {String} direction (left|right|up|down) String specifying the direction.
- * @param {Array} deltas The deltas array.
- * @returns {Object|undefined} ending td in pasted area (only if any cell was changed).
- */
- populateFromArray: function populateFromArray(start, input, end, source, method, direction, deltas) {
- var r,
- rlen,
- c,
- clen,
- setData = [],
- current = {};
- rlen = input.length;
- if (rlen === 0) {
- return false;
- }
- var repeatCol,
- repeatRow,
- cmax,
- rmax,
- baseEnd = {
- row: end === null ? null : end.row,
- col: end === null ? null : end.col
- };
- /* eslint-disable no-case-declarations */
- // insert data with specified pasteMode method
- switch (method) {
- case 'shift_down':
- repeatCol = end ? end.col - start.col + 1 : 0;
- repeatRow = end ? end.row - start.row + 1 : 0;
- input = translateRowsToColumns(input);
- for (c = 0, clen = input.length, cmax = Math.max(clen, repeatCol); c < cmax; c++) {
- if (c < clen) {
- var _instance;
- for (r = 0, rlen = input[c].length; r < repeatRow - rlen; r++) {
- input[c].push(input[c][r % rlen]);
- }
- input[c].unshift(start.col + c, start.row, 0);
- (_instance = instance).spliceCol.apply(_instance, _toConsumableArray(input[c]));
- } else {
- var _instance2;
- input[c % clen][0] = start.col + c;
- (_instance2 = instance).spliceCol.apply(_instance2, _toConsumableArray(input[c % clen]));
- }
- }
- break;
- case 'shift_right':
- repeatCol = end ? end.col - start.col + 1 : 0;
- repeatRow = end ? end.row - start.row + 1 : 0;
- for (r = 0, rlen = input.length, rmax = Math.max(rlen, repeatRow); r < rmax; r++) {
- if (r < rlen) {
- var _instance3;
- for (c = 0, clen = input[r].length; c < repeatCol - clen; c++) {
- input[r].push(input[r][c % clen]);
- }
- input[r].unshift(start.row + r, start.col, 0);
- (_instance3 = instance).spliceRow.apply(_instance3, _toConsumableArray(input[r]));
- } else {
- var _instance4;
- input[r % rlen][0] = start.row + r;
- (_instance4 = instance).spliceRow.apply(_instance4, _toConsumableArray(input[r % rlen]));
- }
- }
- break;
- case 'overwrite':
- default:
- // overwrite and other not specified options
- current.row = start.row;
- current.col = start.col;
- var selected = { // selected range
- row: end && start ? end.row - start.row + 1 : 1,
- col: end && start ? end.col - start.col + 1 : 1
- };
- var skippedRow = 0;
- var skippedColumn = 0;
- var pushData = true;
- var cellMeta = void 0;
- var getInputValue = function getInputValue(row) {
- var col = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
- var rowValue = input[row % input.length];
- if (col !== null) {
- return rowValue[col % rowValue.length];
- }
- return rowValue;
- };
- var rowInputLength = input.length;
- var rowSelectionLength = end ? end.row - start.row + 1 : 0;
- if (end) {
- rlen = rowSelectionLength;
- } else {
- rlen = Math.max(rowInputLength, rowSelectionLength);
- }
- for (r = 0; r < rlen; r++) {
- if (end && current.row > end.row && rowSelectionLength > rowInputLength || !priv.settings.allowInsertRow && current.row > instance.countRows() - 1 || current.row >= priv.settings.maxRows) {
- break;
- }
- var logicalRow = r - skippedRow;
- var colInputLength = getInputValue(logicalRow).length;
- var colSelectionLength = end ? end.col - start.col + 1 : 0;
- if (end) {
- clen = colSelectionLength;
- } else {
- clen = Math.max(colInputLength, colSelectionLength);
- }
- current.col = start.col;
- cellMeta = instance.getCellMeta(current.row, current.col);
- if ((source === 'CopyPaste.paste' || source === 'Autofill.autofill') && cellMeta.skipRowOnPaste) {
- skippedRow++;
- current.row++;
- rlen++;
- /* eslint-disable no-continue */
- continue;
- }
- skippedColumn = 0;
- for (c = 0; c < clen; c++) {
- if (end && current.col > end.col && colSelectionLength > colInputLength || !priv.settings.allowInsertColumn && current.col > instance.countCols() - 1 || current.col >= priv.settings.maxCols) {
- break;
- }
- cellMeta = instance.getCellMeta(current.row, current.col);
- if ((source === 'CopyPaste.paste' || source === 'Autofill.fill') && cellMeta.skipColumnOnPaste) {
- skippedColumn++;
- current.col++;
- clen++;
- continue;
- }
- if (cellMeta.readOnly) {
- current.col++;
- /* eslint-disable no-continue */
- continue;
- }
- var logicalColumn = c - skippedColumn;
- var value = getInputValue(logicalRow, logicalColumn);
- var orgValue = instance.getDataAtCell(current.row, current.col);
- var index = {
- row: logicalRow,
- col: logicalColumn
- };
- if (source === 'Autofill.fill') {
- var result = instance.runHooks('beforeAutofillInsidePopulate', index, direction, input, deltas, {}, selected);
- if (result) {
- value = isUndefined(result.value) ? value : result.value;
- }
- }
- if (value !== null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
- if (orgValue === null || (typeof orgValue === 'undefined' ? 'undefined' : _typeof(orgValue)) !== 'object') {
- pushData = false;
- } else {
- var orgValueSchema = duckSchema(orgValue[0] || orgValue);
- var valueSchema = duckSchema(value[0] || value);
- /* eslint-disable max-depth */
- if (isObjectEquals(orgValueSchema, valueSchema)) {
- value = deepClone(value);
- } else {
- pushData = false;
- }
- }
- } else if (orgValue !== null && (typeof orgValue === 'undefined' ? 'undefined' : _typeof(orgValue)) === 'object') {
- pushData = false;
- }
- if (pushData) {
- setData.push([current.row, current.col, value]);
- }
- pushData = true;
- current.col++;
- }
- current.row++;
- }
- instance.setDataAtCell(setData, null, null, source || 'populateFromArray');
- break;
- }
- }
- };
- /* eslint-disable no-multi-assign */
- this.selection = selection = { // this public assignment is only temporary
- inProgress: false,
- selectedHeader: {
- cols: false,
- rows: false
- },
- /**
- * @param {Boolean} [rows=false]
- * @param {Boolean} [cols=false]
- * @param {Boolean} [corner=false]
- */
- setSelectedHeaders: function setSelectedHeaders() {
- var rows = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
- var cols = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
- var corner = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
- instance.selection.selectedHeader.rows = rows;
- instance.selection.selectedHeader.cols = cols;
- instance.selection.selectedHeader.corner = corner;
- },
- /**
- * Sets inProgress to `true`. This enables onSelectionEnd and onSelectionEndByProp to function as desired.
- */
- begin: function begin() {
- instance.selection.inProgress = true;
- },
- /**
- * Sets inProgress to `false`. Triggers onSelectionEnd and onSelectionEndByProp.
- */
- finish: function finish() {
- var sel = instance.getSelected();
- instance.runHooks('afterSelectionEnd', sel[0], sel[1], sel[2], sel[3]);
- instance.runHooks('afterSelectionEndByProp', sel[0], instance.colToProp(sel[1]), sel[2], instance.colToProp(sel[3]));
- instance.selection.inProgress = false;
- },
- /**
- * @returns {Boolean}
- */
- isInProgress: function isInProgress() {
- return instance.selection.inProgress;
- },
- /**
- * Starts selection range on given td object.
- *
- * @param {CellCoords} coords
- * @param keepEditorOpened
- */
- setRangeStart: function setRangeStart(coords, keepEditorOpened) {
- instance.runHooks('beforeSetRangeStart', coords);
- priv.selRange = new CellRange(coords, coords, coords);
- selection.setRangeEnd(coords, null, keepEditorOpened);
- },
- /**
- * Starts selection range on given td object.
- *
- * @param {CellCoords} coords
- * @param keepEditorOpened
- */
- setRangeStartOnly: function setRangeStartOnly(coords) {
- instance.runHooks('beforeSetRangeStartOnly', coords);
- priv.selRange = new CellRange(coords, coords, coords);
- },
- /**
- * Ends selection range on given td object.
- *
- * @param {CellCoords} coords
- * @param {Boolean} [scrollToCell=true] If `true`, viewport will be scrolled to range end
- * @param {Boolean} [keepEditorOpened] If `true`, cell editor will be still opened after changing selection range
- */
- setRangeEnd: function setRangeEnd(coords, scrollToCell, keepEditorOpened) {
- if (priv.selRange === null) {
- return;
- }
- var disableVisualSelection,
- isHeaderSelected = false,
- areCoordsPositive = true;
- var firstVisibleRow = instance.view.wt.wtTable.getFirstVisibleRow();
- var firstVisibleColumn = instance.view.wt.wtTable.getFirstVisibleColumn();
- var newRangeCoords = {
- row: null,
- col: null
- };
- // trigger handlers
- instance.runHooks('beforeSetRangeEnd', coords);
- instance.selection.begin();
- newRangeCoords.row = coords.row < 0 ? firstVisibleRow : coords.row;
- newRangeCoords.col = coords.col < 0 ? firstVisibleColumn : coords.col;
- priv.selRange.to = new CellCoords(newRangeCoords.row, newRangeCoords.col);
- if (!priv.settings.multiSelect) {
- priv.selRange.from = coords;
- }
- // set up current selection
- instance.view.wt.selections.current.clear();
- disableVisualSelection = instance.getCellMeta(priv.selRange.highlight.row, priv.selRange.highlight.col).disableVisualSelection;
- if (typeof disableVisualSelection === 'string') {
- disableVisualSelection = [disableVisualSelection];
- }
- if (disableVisualSelection === false || Array.isArray(disableVisualSelection) && disableVisualSelection.indexOf('current') === -1) {
- instance.view.wt.selections.current.add(priv.selRange.highlight);
- }
- // set up area selection
- instance.view.wt.selections.area.clear();
- if ((disableVisualSelection === false || Array.isArray(disableVisualSelection) && disableVisualSelection.indexOf('area') === -1) && selection.isMultiple()) {
- instance.view.wt.selections.area.add(priv.selRange.from);
- instance.view.wt.selections.area.add(priv.selRange.to);
- }
- // set up highlight
- if (priv.settings.currentHeaderClassName || priv.settings.currentRowClassName || priv.settings.currentColClassName) {
- instance.view.wt.selections.highlight.clear();
- instance.view.wt.selections.highlight.add(priv.selRange.from);
- instance.view.wt.selections.highlight.add(priv.selRange.to);
- }
- var preventScrolling = createObjectPropListener('value');
- // trigger handlers
- instance.runHooks('afterSelection', priv.selRange.from.row, priv.selRange.from.col, priv.selRange.to.row, priv.selRange.to.col, preventScrolling);
- instance.runHooks('afterSelectionByProp', priv.selRange.from.row, datamap.colToProp(priv.selRange.from.col), priv.selRange.to.row, datamap.colToProp(priv.selRange.to.col), preventScrolling);
- if (priv.selRange.from.row === 0 && priv.selRange.to.row === instance.countRows() - 1 && instance.countRows() > 1 || priv.selRange.from.col === 0 && priv.selRange.to.col === instance.countCols() - 1 && instance.countCols() > 1) {
- isHeaderSelected = true;
- }
- if (coords.row < 0 || coords.col < 0) {
- areCoordsPositive = false;
- }
- if (preventScrolling.isTouched()) {
- scrollToCell = !preventScrolling.value;
- }
- if (scrollToCell !== false && !isHeaderSelected && areCoordsPositive) {
- if (priv.selRange.from && !selection.isMultiple()) {
- instance.view.scrollViewport(priv.selRange.from);
- } else {
- instance.view.scrollViewport(coords);
- }
- }
- if (selection.selectedHeader.rows && selection.selectedHeader.cols) {
- addClass(instance.rootElement, ['ht__selection--rows', 'ht__selection--columns']);
- } else if (selection.selectedHeader.rows) {
- removeClass(instance.rootElement, 'ht__selection--columns');
- addClass(instance.rootElement, 'ht__selection--rows');
- } else if (selection.selectedHeader.cols) {
- removeClass(instance.rootElement, 'ht__selection--rows');
- addClass(instance.rootElement, 'ht__selection--columns');
- } else {
- removeClass(instance.rootElement, ['ht__selection--rows', 'ht__selection--columns']);
- }
- selection.refreshBorders(null, keepEditorOpened);
- },
- /**
- * Destroys editor, redraws borders around cells, prepares editor.
- *
- * @param {Boolean} [revertOriginal]
- * @param {Boolean} [keepEditor]
- */
- refreshBorders: function refreshBorders(revertOriginal, keepEditor) {
- if (!keepEditor) {
- editorManager.destroyEditor(revertOriginal);
- }
- instance.view.render();
- if (selection.isSelected() && !keepEditor) {
- editorManager.prepareEditor();
- }
- },
- /**
- * Returns information if we have a multiselection.
- *
- * @returns {Boolean}
- */
- isMultiple: function isMultiple() {
- var isMultiple = !(priv.selRange.to.col === priv.selRange.from.col && priv.selRange.to.row === priv.selRange.from.row),
- modifier = instance.runHooks('afterIsMultipleSelection', isMultiple);
- if (isMultiple) {
- return modifier;
- }
- },
- /**
- * Selects cell relative to current cell (if possible).
- */
- transformStart: function transformStart(rowDelta, colDelta, force, keepEditorOpened) {
- var delta = new CellCoords(rowDelta, colDelta),
- rowTransformDir = 0,
- colTransformDir = 0,
- totalRows,
- totalCols,
- coords,
- fixedRowsBottom;
- instance.runHooks('modifyTransformStart', delta);
- totalRows = instance.countRows();
- totalCols = instance.countCols();
- fixedRowsBottom = instance.getSettings().fixedRowsBottom;
- if (priv.selRange.highlight.row + rowDelta > totalRows - 1) {
- if (force && priv.settings.minSpareRows > 0 && !(fixedRowsBottom && priv.selRange.highlight.row >= totalRows - fixedRowsBottom - 1)) {
- instance.alter('insert_row', totalRows);
- totalRows = instance.countRows();
- } else if (priv.settings.autoWrapCol) {
- delta.row = 1 - totalRows;
- delta.col = priv.selRange.highlight.col + delta.col == totalCols - 1 ? 1 - totalCols : 1;
- }
- } else if (priv.settings.autoWrapCol && priv.selRange.highlight.row + delta.row < 0 && priv.selRange.highlight.col + delta.col >= 0) {
- delta.row = totalRows - 1;
- delta.col = priv.selRange.highlight.col + delta.col == 0 ? totalCols - 1 : -1;
- }
- if (priv.selRange.highlight.col + delta.col > totalCols - 1) {
- if (force && priv.settings.minSpareCols > 0) {
- instance.alter('insert_col', totalCols);
- totalCols = instance.countCols();
- } else if (priv.settings.autoWrapRow) {
- delta.row = priv.selRange.highlight.row + delta.row == totalRows - 1 ? 1 - totalRows : 1;
- delta.col = 1 - totalCols;
- }
- } else if (priv.settings.autoWrapRow && priv.selRange.highlight.col + delta.col < 0 && priv.selRange.highlight.row + delta.row >= 0) {
- delta.row = priv.selRange.highlight.row + delta.row == 0 ? totalRows - 1 : -1;
- delta.col = totalCols - 1;
- }
- coords = new CellCoords(priv.selRange.highlight.row + delta.row, priv.selRange.highlight.col + delta.col);
- if (coords.row < 0) {
- rowTransformDir = -1;
- coords.row = 0;
- } else if (coords.row > 0 && coords.row >= totalRows) {
- rowTransformDir = 1;
- coords.row = totalRows - 1;
- }
- if (coords.col < 0) {
- colTransformDir = -1;
- coords.col = 0;
- } else if (coords.col > 0 && coords.col >= totalCols) {
- colTransformDir = 1;
- coords.col = totalCols - 1;
- }
- instance.runHooks('afterModifyTransformStart', coords, rowTransformDir, colTransformDir);
- selection.setRangeStart(coords, keepEditorOpened);
- },
- /**
- * Sets selection end cell relative to current selection end cell (if possible).
- */
- transformEnd: function transformEnd(rowDelta, colDelta) {
- var delta = new CellCoords(rowDelta, colDelta),
- rowTransformDir = 0,
- colTransformDir = 0,
- totalRows,
- totalCols,
- coords;
- instance.runHooks('modifyTransformEnd', delta);
- totalRows = instance.countRows();
- totalCols = instance.countCols();
- coords = new CellCoords(priv.selRange.to.row + delta.row, priv.selRange.to.col + delta.col);
- if (coords.row < 0) {
- rowTransformDir = -1;
- coords.row = 0;
- } else if (coords.row > 0 && coords.row >= totalRows) {
- rowTransformDir = 1;
- coords.row = totalRows - 1;
- }
- if (coords.col < 0) {
- colTransformDir = -1;
- coords.col = 0;
- } else if (coords.col > 0 && coords.col >= totalCols) {
- colTransformDir = 1;
- coords.col = totalCols - 1;
- }
- instance.runHooks('afterModifyTransformEnd', coords, rowTransformDir, colTransformDir);
- selection.setRangeEnd(coords, true);
- },
- /**
- * Returns `true` if currently there is a selection on screen, `false` otherwise.
- *
- * @returns {Boolean}
- */
- isSelected: function isSelected() {
- return priv.selRange !== null;
- },
- /**
- * Returns `true` if coords is within current selection coords.
- *
- * @param {CellCoords} coords
- * @returns {Boolean}
- */
- inInSelection: function inInSelection(coords) {
- if (!selection.isSelected()) {
- return false;
- }
- return priv.selRange.includes(coords);
- },
- /**
- * Deselects all selected cells
- */
- deselect: function deselect() {
- if (!selection.isSelected()) {
- return;
- }
- instance.selection.inProgress = false; // needed by HT inception
- priv.selRange = null;
- instance.view.wt.selections.current.clear();
- instance.view.wt.selections.area.clear();
- if (priv.settings.currentHeaderClassName || priv.settings.currentRowClassName || priv.settings.currentColClassName) {
- instance.view.wt.selections.highlight.clear();
- }
- editorManager.destroyEditor();
- selection.refreshBorders();
- removeClass(instance.rootElement, ['ht__selection--rows', 'ht__selection--columns']);
- instance.runHooks('afterDeselect');
- },
- /**
- * Select all cells
- */
- selectAll: function selectAll() {
- if (!priv.settings.multiSelect) {
- return;
- }
- selection.setSelectedHeaders(true, true, true);
- selection.setRangeStart(new CellCoords(0, 0));
- selection.setRangeEnd(new CellCoords(instance.countRows() - 1, instance.countCols() - 1), false);
- },
- /**
- * Deletes data from selected cells
- */
- empty: function empty() {
- if (!selection.isSelected()) {
- return;
- }
- var topLeft = priv.selRange.getTopLeftCorner();
- var bottomRight = priv.selRange.getBottomRightCorner();
- var r,
- c,
- changes = [];
- for (r = topLeft.row; r <= bottomRight.row; r++) {
- for (c = topLeft.col; c <= bottomRight.col; c++) {
- if (!instance.getCellMeta(r, c).readOnly) {
- changes.push([r, c, '']);
- }
- }
- }
- instance.setDataAtCell(changes);
- }
- };
- this.init = function () {
- dataSource.setData(priv.settings.data);
- instance.runHooks('beforeInit');
- if (isMobileBrowser()) {
- addClass(instance.rootElement, 'mobile');
- }
- this.updateSettings(priv.settings, true);
- this.view = new TableView(this);
- editorManager = new EditorManager(instance, priv, selection, datamap);
- this.forceFullRender = true; // used when data was changed
- instance.runHooks('init');
- this.view.render();
- if (_typeof(priv.firstRun) === 'object') {
- instance.runHooks('afterChange', priv.firstRun[0], priv.firstRun[1]);
- priv.firstRun = false;
- }
- instance.runHooks('afterInit');
- };
- function ValidatorsQueue() {
- // moved this one level up so it can be used in any function here. Probably this should be moved to a separate file
- var resolved = false;
- return {
- validatorsInQueue: 0,
- valid: true,
- addValidatorToQueue: function addValidatorToQueue() {
- this.validatorsInQueue++;
- resolved = false;
- },
- removeValidatorFormQueue: function removeValidatorFormQueue() {
- this.validatorsInQueue = this.validatorsInQueue - 1 < 0 ? 0 : this.validatorsInQueue - 1;
- this.checkIfQueueIsEmpty();
- },
- onQueueEmpty: function onQueueEmpty(valid) {},
- checkIfQueueIsEmpty: function checkIfQueueIsEmpty() {
- if (this.validatorsInQueue == 0 && resolved == false) {
- resolved = true;
- this.onQueueEmpty(this.valid);
- }
- }
- };
- }
- function validateChanges(changes, source, callback) {
- var waitingForValidator = new ValidatorsQueue();
- waitingForValidator.onQueueEmpty = resolve;
- for (var i = changes.length - 1; i >= 0; i--) {
- if (changes[i] === null) {
- changes.splice(i, 1);
- } else {
- var row = changes[i][0];
- var col = datamap.propToCol(changes[i][1]);
- var cellProperties = instance.getCellMeta(row, col);
- if (cellProperties.type === 'numeric' && typeof changes[i][3] === 'string') {
- if (changes[i][3].length > 0 && (/^-?[\d\s]*(\.|,)?\d*$/.test(changes[i][3]) || cellProperties.format)) {
- var len = changes[i][3].length;
- if (isUndefined(cellProperties.language)) {
- numbro.culture('en-US');
- } else if (changes[i][3].indexOf('.') === len - 3 && changes[i][3].indexOf(',') === -1) {
- // this input in format XXXX.XX is likely to come from paste. Let's parse it using international rules
- numbro.culture('en-US');
- } else {
- numbro.culture(cellProperties.language);
- }
- var _numbro$cultureData = numbro.cultureData(numbro.culture()),
- delimiters = _numbro$cultureData.delimiters;
- // try to parse to float - https://github.com/foretagsplatsen/numbro/pull/183
- if (numbro.validate(changes[i][3]) && !isNaN(changes[i][3])) {
- changes[i][3] = parseFloat(changes[i][3]);
- } else {
- changes[i][3] = numbro().unformat(changes[i][3]) || changes[i][3];
- }
- }
- }
- /* eslint-disable no-loop-func */
- if (instance.getCellValidator(cellProperties)) {
- waitingForValidator.addValidatorToQueue();
- instance.validateCell(changes[i][3], cellProperties, function (i, cellProperties) {
- return function (result) {
- if (typeof result !== 'boolean') {
- throw new Error('Validation error: result is not boolean');
- }
- if (result === false && cellProperties.allowInvalid === false) {
- changes.splice(i, 1); // cancel the change
- cellProperties.valid = true; // we cancelled the change, so cell value is still valid
- var cell = instance.getCell(cellProperties.row, cellProperties.col);
- removeClass(cell, instance.getSettings().invalidCellClassName);
- --i;
- }
- waitingForValidator.removeValidatorFormQueue();
- };
- }(i, cellProperties), source);
- }
- }
- }
- waitingForValidator.checkIfQueueIsEmpty();
- function resolve() {
- var beforeChangeResult;
- if (changes.length) {
- beforeChangeResult = instance.runHooks('beforeChange', changes, source);
- if (isFunction(beforeChangeResult)) {
- console.warn('Your beforeChange callback returns a function. It\'s not supported since Handsontable 0.12.1 (and the returned function will not be executed).');
- } else if (beforeChangeResult === false) {
- changes.splice(0, changes.length); // invalidate all changes (remove everything from array)
- }
- }
- callback(); // called when async validators are resolved and beforeChange was not async
- }
- }
- /**
- * Internal function to apply changes. Called after validateChanges
- *
- * @private
- * @param {Array} changes Array in form of [row, prop, oldValue, newValue]
- * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
- * @fires Hooks#beforeChangeRender
- * @fires Hooks#afterChange
- */
- function applyChanges(changes, source) {
- var i = changes.length - 1;
- if (i < 0) {
- return;
- }
- for (; i >= 0; i--) {
- var skipThisChange = false;
- if (changes[i] === null) {
- changes.splice(i, 1);
- /* eslint-disable no-continue */
- continue;
- }
- if (changes[i][2] == null && changes[i][3] == null) {
- /* eslint-disable no-continue */
- continue;
- }
- if (priv.settings.allowInsertRow) {
- while (changes[i][0] > instance.countRows() - 1) {
- var numberOfCreatedRows = datamap.createRow(void 0, void 0, source);
- if (numberOfCreatedRows === 0) {
- skipThisChange = true;
- break;
- }
- }
- }
- if (skipThisChange) {
- /* eslint-disable no-continue */
- continue;
- }
- if (instance.dataType === 'array' && (!priv.settings.columns || priv.settings.columns.length === 0) && priv.settings.allowInsertColumn) {
- while (datamap.propToCol(changes[i][1]) > instance.countCols() - 1) {
- datamap.createCol(void 0, void 0, source);
- }
- }
- datamap.set(changes[i][0], changes[i][1], changes[i][3]);
- }
- instance.forceFullRender = true; // used when data was changed
- grid.adjustRowsAndCols();
- instance.runHooks('beforeChangeRender', changes, source);
- selection.refreshBorders(null, true);
- instance.view.wt.wtOverlays.adjustElementsSize();
- instance.runHooks('afterChange', changes, source || 'edit');
- var activeEditor = instance.getActiveEditor();
- if (activeEditor && isDefined(activeEditor.refreshValue)) {
- activeEditor.refreshValue();
- }
- }
- this.validateCell = function (value, cellProperties, callback, source) {
- var validator = instance.getCellValidator(cellProperties);
- function done(valid) {
- var col = cellProperties.visualCol,
- row = cellProperties.visualRow,
- td = instance.getCell(row, col, true);
- if (td && td.nodeName != 'TH') {
- instance.view.wt.wtSettings.settings.cellRenderer(row, col, td);
- }
- callback(valid);
- }
- if (isRegExp(validator)) {
- validator = function (validator) {
- return function (value, callback) {
- callback(validator.test(value));
- };
- }(validator);
- }
- if (isFunction(validator)) {
- value = instance.runHooks('beforeValidate', value, cellProperties.visualRow, cellProperties.prop, source);
- // To provide consistent behaviour, validation should be always asynchronous
- instance._registerTimeout(setTimeout(function () {
- validator.call(cellProperties, value, function (valid) {
- valid = instance.runHooks('afterValidate', valid, value, cellProperties.visualRow, cellProperties.prop, source);
- cellProperties.valid = valid;
- done(valid);
- instance.runHooks('postAfterValidate', valid, value, cellProperties.visualRow, cellProperties.prop, source);
- });
- }, 0));
- } else {
- // resolve callback even if validator function was not found
- instance._registerTimeout(setTimeout(function () {
- cellProperties.valid = true;
- done(cellProperties.valid);
- }, 0));
- }
- };
- function setDataInputToArray(row, propOrCol, value) {
- if ((typeof row === 'undefined' ? 'undefined' : _typeof(row)) === 'object') {
- // is it an array of changes
- return row;
- }
- return [[row, propOrCol, value]];
- }
- /**
- * @description
- * Set new value to a cell. To change many cells at once, pass an array of `changes` in format `[[row, col, value], ...]` as
- * the only parameter. `col` is the index of a __visible__ column (note that if columns were reordered,
- * the current visible order will be used). `source` is a flag for before/afterChange events. If you pass only array of
- * changes then `source` could be set as second parameter.
- *
- * @memberof Core#
- * @function setDataAtCell
- * @param {Number|Array} row Row index or array of changes in format `[[row, col, value], ...]`.
- * @param {Number} col Column index.
- * @param {String} value New value.
- * @param {String} [source] String that identifies how this change will be described in the changes array (useful in onAfterChange or onBeforeChange callback).
- */
- this.setDataAtCell = function (row, col, value, source) {
- var input = setDataInputToArray(row, col, value),
- i,
- ilen,
- changes = [],
- prop;
- for (i = 0, ilen = input.length; i < ilen; i++) {
- if (_typeof(input[i]) !== 'object') {
- throw new Error('Method `setDataAtCell` accepts row number or changes array of arrays as its first parameter');
- }
- if (typeof input[i][1] !== 'number') {
- throw new Error('Method `setDataAtCell` accepts row and column number as its parameters. If you want to use object property name, use method `setDataAtRowProp`');
- }
- prop = datamap.colToProp(input[i][1]);
- changes.push([input[i][0], prop, dataSource.getAtCell(recordTranslator.toPhysicalRow(input[i][0]), input[i][1]), input[i][2]]);
- }
- if (!source && (typeof row === 'undefined' ? 'undefined' : _typeof(row)) === 'object') {
- source = col;
- }
- instance.runHooks('afterSetDataAtCell', changes, source);
- validateChanges(changes, source, function () {
- applyChanges(changes, source);
- });
- };
- /**
- * @description
- * Set new value to a cell. To change many cells at once, pass an array of `changes` in format `[[row, prop, value], ...]` as
- * the only parameter. `prop` is the name of the object property (e.g. `first.name`). `source` is a flag for before/afterChange events.
- * If you pass only array of changes then `source` could be set as second parameter.
- *
- * @memberof Core#
- * @function setDataAtRowProp
- * @param {Number|Array} row Row index or array of changes in format `[[row, prop, value], ...]`.
- * @param {String} prop Property name or the source string.
- * @param {String} value Value to be set.
- * @param {String} [source] String that identifies how this change will be described in changes array (useful in onChange callback).
- */
- this.setDataAtRowProp = function (row, prop, value, source) {
- var input = setDataInputToArray(row, prop, value),
- i,
- ilen,
- changes = [];
- for (i = 0, ilen = input.length; i < ilen; i++) {
- changes.push([input[i][0], input[i][1], dataSource.getAtCell(recordTranslator.toPhysicalRow(input[i][0]), input[i][1]), input[i][2]]);
- }
- if (!source && (typeof row === 'undefined' ? 'undefined' : _typeof(row)) === 'object') {
- source = prop;
- }
- instance.runHooks('afterSetDataAtRowProp', changes, source);
- validateChanges(changes, source, function () {
- applyChanges(changes, source);
- });
- };
- /**
- * Listen to the keyboard input on document body.
- *
- * @memberof Core#
- * @function listen
- * @since 0.11
- */
- this.listen = function () {
- activeGuid = instance.guid;
- };
- /**
- * Stop listening to keyboard input on the document body.
- *
- * @memberof Core#
- * @function unlisten
- * @since 0.11
- */
- this.unlisten = function () {
- activeGuid = null;
- };
- /**
- * Returns `true` if the current Handsontable instance is listening to keyboard input on document body.
- *
- * @memberof Core#
- * @function isListening
- * @since 0.11
- * @returns {Boolean} `true` if the instance is listening, `false` otherwise.
- */
- this.isListening = function () {
- return activeGuid === instance.guid;
- };
- /**
- * Destroys the current editor, renders and selects the current cell.
- *
- * @memberof Core#
- * @function destroyEditor
- * @param {Boolean} [revertOriginal] If != `true`, edited data is saved. Otherwise the previous value is restored.
- */
- this.destroyEditor = function (revertOriginal) {
- selection.refreshBorders(revertOriginal);
- };
- /**
- * Populate cells at position with 2D input array (e.g. `[[1, 2], [3, 4]]`).
- * Use `endRow`, `endCol` when you want to cut input when a certain row is reached.
- * Optional `source` parameter (default value "populateFromArray") is used to identify this call in the resulting events (beforeChange, afterChange).
- * Optional `populateMethod` parameter (default value "overwrite", possible values "shift_down" and "shift_right")
- * has the same effect as pasteMode option {@link Options#pasteMode}
- *
- * @memberof Core#
- * @function populateFromArray
- * @since 0.9.0
- * @param {Number} row Start row
- * @param {Number} col Start column
- * @param {Array} input 2d array
- * @param {Number} [endRow] End row (use when you want to cut input when certain row is reached)
- * @param {Number} [endCol] End column (use when you want to cut input when certain column is reached)
- * @param {String} [source="populateFromArray"] Source string.
- * @param {String} [method="overwrite"] Populate method. Possible options: `shift_down`, `shift_right`, `overwrite`.
- * @param {String} direction Populate direction. (left|right|up|down)
- * @param {Array} deltas Deltas array.
- * @returns {Object|undefined} The ending TD element in pasted area (only if any cells were changed).
- */
- this.populateFromArray = function (row, col, input, endRow, endCol, source, method, direction, deltas) {
- var c;
- if (!((typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && _typeof(input[0]) === 'object')) {
- throw new Error('populateFromArray parameter `input` must be an array of arrays'); // API changed in 0.9-beta2, let's check if you use it correctly
- }
- c = typeof endRow === 'number' ? new CellCoords(endRow, endCol) : null;
- return grid.populateFromArray(new CellCoords(row, col), input, c, source, method, direction, deltas);
- };
- /**
- * Adds/removes data from the column. This function is modelled after Array.splice.
- * Parameter `col` is the index of the column in which do you want to do splice.
- * Parameter `index` is the row index at which to start changing the array.
- * If negative, will begin that many elements from the end. Parameter `amount`, is the number of the old array elements to remove.
- * If the amount is 0, no elements are removed. Fourth and further parameters are the `elements` to add to the array.
- * If you don't specify any elements, spliceCol simply removes elements from the array.
- * {@link DataMap#spliceCol}
- *
- * @memberof Core#
- * @function spliceCol
- * @since 0.9-beta2
- * @param {Number} col Index of the column in which do you want to do splice.
- * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
- * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed.
- * @param {*} [elements] The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array.
- */
- this.spliceCol = function (col, index, amount /* , elements... */) {
- var _datamap;
- return (_datamap = datamap).spliceCol.apply(_datamap, arguments);
- };
- /**
- * Adds/removes data from the row. This function works is modelled after Array.splice.
- * Parameter `row` is the index of row in which do you want to do splice.
- * Parameter `index` is the column index at which to start changing the array.
- * If negative, will begin that many elements from the end. Parameter `amount`, is the number of old array elements to remove.
- * If the amount is 0, no elements are removed. Fourth and further parameters are the `elements` to add to the array.
- * If you don't specify any elements, spliceCol simply removes elements from the array.
- * {@link DataMap#spliceRow}
- *
- * @memberof Core#
- * @function spliceRow
- * @since 0.11
- * @param {Number} row Index of column in which do you want to do splice.
- * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
- * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed.
- * @param {*} [elements] The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array.
- */
- this.spliceRow = function (row, index, amount /* , elements... */) {
- var _datamap2;
- return (_datamap2 = datamap).spliceRow.apply(_datamap2, arguments);
- };
- /**
- * Returns indexes of the currently selected cells as an array `[startRow, startCol, endRow, endCol]`.
- *
- * Start row and start col are the coordinates of the active cell (where the selection was started).
- *
- * @memberof Core#
- * @function getSelected
- * @returns {Array} Array of the selection's indexes.
- */
- this.getSelected = function () {
- // https://github.com/handsontable/handsontable/issues/44 //cjl
- if (selection.isSelected()) {
- return [priv.selRange.from.row, priv.selRange.from.col, priv.selRange.to.row, priv.selRange.to.col];
- }
- };
- /**
- * Returns the current selection as a CellRange object.
- *
- * @memberof Core#
- * @function getSelectedRange
- * @since 0.11
- * @returns {CellRange} Selected range object or undefined` if there is no selection.
- */
- this.getSelectedRange = function () {
- // https://github.com/handsontable/handsontable/issues/44 //cjl
- if (selection.isSelected()) {
- return priv.selRange;
- }
- };
- /**
- * Rerender the table.
- *
- * @memberof Core#
- * @function render
- */
- this.render = function () {
- if (instance.view) {
- instance.renderCall = true;
- instance.forceFullRender = true; // used when data was changed
- selection.refreshBorders(null, true);
- }
- };
- /**
- * Reset all cells in the grid to contain data from the data array.
- *
- * @memberof Core#
- * @function loadData
- * @param {Array} data Array of arrays or array of objects containing data.
- * @fires Hooks#afterLoadData
- * @fires Hooks#afterChange
- */
- this.loadData = function (data) {
- if (Array.isArray(priv.settings.dataSchema)) {
- instance.dataType = 'array';
- } else if (isFunction(priv.settings.dataSchema)) {
- instance.dataType = 'function';
- } else {
- instance.dataType = 'object';
- }
- if (datamap) {
- datamap.destroy();
- }
- datamap = new DataMap(instance, priv, GridSettings);
- if ((typeof data === 'undefined' ? 'undefined' : _typeof(data)) === 'object' && data !== null) {
- if (!(data.push && data.splice)) {
- // check if data is array. Must use duck-type check so Backbone Collections also pass it
- // when data is not an array, attempt to make a single-row array of it
- data = [data];
- }
- } else if (data === null) {
- data = [];
- var row;
- var r = 0;
- var rlen = 0;
- var dataSchema = datamap.getSchema();
- for (r = 0, rlen = priv.settings.startRows; r < rlen; r++) {
- if ((instance.dataType === 'object' || instance.dataType === 'function') && priv.settings.dataSchema) {
- row = deepClone(dataSchema);
- data.push(row);
- } else if (instance.dataType === 'array') {
- row = deepClone(dataSchema[0]);
- data.push(row);
- } else {
- row = [];
- for (var c = 0, clen = priv.settings.startCols; c < clen; c++) {
- row.push(null);
- }
- data.push(row);
- }
- }
- } else {
- throw new Error('loadData only accepts array of objects or array of arrays (' + (typeof data === 'undefined' ? 'undefined' : _typeof(data)) + ' given)');
- }
- priv.isPopulated = false;
- GridSettings.prototype.data = data;
- if (Array.isArray(data[0])) {
- instance.dataType = 'array';
- }
- datamap.dataSource = data;
- dataSource.data = data;
- dataSource.dataType = instance.dataType;
- dataSource.colToProp = datamap.colToProp.bind(datamap);
- dataSource.propToCol = datamap.propToCol.bind(datamap);
- clearCellSettingCache();
- grid.adjustRowsAndCols();
- instance.runHooks('afterLoadData', priv.firstRun);
- if (priv.firstRun) {
- priv.firstRun = [null, 'loadData'];
- } else {
- instance.runHooks('afterChange', null, 'loadData');
- instance.render();
- }
- priv.isPopulated = true;
- function clearCellSettingCache() {
- priv.cellSettings.length = 0;
- }
- };
- /**
- * Returns the current data object (the same one that was passed by `data` configuration option or `loadData` method,
- * unless the `modifyRow` hook was used to trim some of the rows. If that's the case - use the {@link Core#getSourceData} method.).
- * Optionally you can provide cell range by defining `row`, `col`, `row2`, `col2` to get only a fragment of grid data.
- *
- * Note: getData functionality changed with the release of version 0.20. If you're looking for the previous functionality,
- * you should use the {@link Core#getSourceData} method.
- *
- * @memberof Core#
- * @function getData
- * @param {Number} [r] From row.
- * @param {Number} [c] From column.
- * @param {Number} [r2] To row.
- * @param {Number} [c2] To column.
- * @returns {Array} Array with the data.
- */
- this.getData = function (r, c, r2, c2) {
- if (isUndefined(r)) {
- return datamap.getAll();
- }
- return datamap.getRange(new CellCoords(r, c), new CellCoords(r2, c2), datamap.DESTINATION_RENDERER);
- };
- /**
- * Returns a string value of the selected range. Each column is separated by tab, each row is separated by a new line character.
- * {@link DataMap#getCopyableText}
- *
- * @memberof Core#
- * @function getCopyableText
- * @since 0.11
- * @param {Number} startRow From row.
- * @param {Number} startCol From column.
- * @param {Number} endRow To row.
- * @param {Number} endCol To column.
- * @returns {String}
- */
- this.getCopyableText = function (startRow, startCol, endRow, endCol) {
- return datamap.getCopyableText(new CellCoords(startRow, startCol), new CellCoords(endRow, endCol));
- };
- /**
- * Returns the data's copyable value at specified row and column index ({@link DataMap#getCopyable}).
- *
- * @memberof Core#
- * @function getCopyableData
- * @since 0.19.0
- * @param {Number} row Row index.
- * @param {Number} column Column index.
- * @returns {String}
- */
- this.getCopyableData = function (row, column) {
- return datamap.getCopyable(row, datamap.colToProp(column));
- };
- /**
- * Returns schema provided by constructor settings. If it doesn't exist then it returns the schema based on the data
- * structure in the first row.
- *
- * @memberof Core#
- * @function getSchema
- * @since 0.13.2
- * @returns {Object} Schema object.
- */
- this.getSchema = function () {
- return datamap.getSchema();
- };
- /**
- * Use it if you need to change configuration after initialization. The `settings` parameter is an object containing the new
- * settings, declared the same way as in the initial settings object.
- * Note, that although the `updateSettings` method doesn't overwrite the previously declared settings, it might reset
- * the settings made post-initialization. (for example - ignore changes made using the columnResize feature).
- *
- * @memberof Core#
- * @function updateSettings
- * @param {Object} settings New settings object.
- * @param {Boolean} init
- * @example
- * ```js
- * hot.updateSettings({
- * contextMenu: true,
- * colHeaders: true,
- * fixedRowsTop: 2
- * });
- * ```
- * @fires Hooks#afterCellMetaReset
- * @fires Hooks#afterUpdateSettings
- */
- this.updateSettings = function (settings, init) {
- var columnsAsFunc = false;
- var i = void 0;
- var j = void 0;
- var clen = void 0;
- if (isDefined(settings.rows)) {
- throw new Error('"rows" setting is no longer supported. do you mean startRows, minRows or maxRows?');
- }
- if (isDefined(settings.cols)) {
- throw new Error('"cols" setting is no longer supported. do you mean startCols, minCols or maxCols?');
- }
- for (i in settings) {
- if (i === 'data') {
- /* eslint-disable no-continue */
- continue; // loadData will be triggered later
- } else if (Hooks.getSingleton().getRegistered().indexOf(i) > -1) {
- if (isFunction(settings[i]) || Array.isArray(settings[i])) {
- settings[i].initialHook = true;
- instance.addHook(i, settings[i]);
- }
- } else if (!init && hasOwnProperty(settings, i)) {
- // Update settings
- GridSettings.prototype[i] = settings[i];
- }
- }
- // Load data or create data map
- if (settings.data === void 0 && priv.settings.data === void 0) {
- instance.loadData(null); // data source created just now
- } else if (settings.data !== void 0) {
- instance.loadData(settings.data); // data source given as option
- } else if (settings.columns !== void 0) {
- datamap.createMap();
- }
- clen = instance.countCols();
- var columnSetting = settings.columns || GridSettings.prototype.columns;
- // Init columns constructors configuration
- if (columnSetting && isFunction(columnSetting)) {
- clen = instance.countSourceCols();
- columnsAsFunc = true;
- }
- // Clear cellSettings cache
- if (settings.cell !== void 0 || settings.cells !== void 0 || settings.columns !== void 0) {
- priv.cellSettings.length = 0;
- }
- if (clen > 0) {
- var proto = void 0;
- var column = void 0;
- for (i = 0, j = 0; i < clen; i++) {
- if (columnsAsFunc && !columnSetting(i)) {
- /* eslint-disable no-continue */
- continue;
- }
- priv.columnSettings[j] = columnFactory(GridSettings, priv.columnsSettingConflicts);
- // shortcut for prototype
- proto = priv.columnSettings[j].prototype;
- // Use settings provided by user
- if (columnSetting) {
- if (columnsAsFunc) {
- column = columnSetting(i);
- } else {
- column = columnSetting[j];
- }
- if (column) {
- extend(proto, column);
- extend(proto, expandType(column));
- }
- }
- j++;
- }
- }
- if (isDefined(settings.cell)) {
- for (var key in settings.cell) {
- if (hasOwnProperty(settings.cell, key)) {
- var cell = settings.cell[key];
- instance.setCellMetaObject(cell.row, cell.col, cell);
- }
- }
- }
- instance.runHooks('afterCellMetaReset');
- if (isDefined(settings.className)) {
- if (GridSettings.prototype.className) {
- removeClass(instance.rootElement, GridSettings.prototype.className);
- }
- if (settings.className) {
- addClass(instance.rootElement, settings.className);
- }
- }
- var currentHeight = instance.rootElement.style.height;
- if (currentHeight !== '') {
- currentHeight = parseInt(instance.rootElement.style.height, 10);
- }
- var height = settings.height;
- if (isFunction(height)) {
- height = height();
- }
- if (init) {
- var initialStyle = instance.rootElement.getAttribute('style');
- if (initialStyle) {
- instance.rootElement.setAttribute('data-initialstyle', instance.rootElement.getAttribute('style'));
- }
- }
- if (height === null) {
- var _initialStyle = instance.rootElement.getAttribute('data-initialstyle');
- if (_initialStyle && (_initialStyle.indexOf('height') > -1 || _initialStyle.indexOf('overflow') > -1)) {
- instance.rootElement.setAttribute('style', _initialStyle);
- } else {
- instance.rootElement.style.height = '';
- instance.rootElement.style.overflow = '';
- }
- } else if (height !== void 0) {
- instance.rootElement.style.height = height + 'px';
- instance.rootElement.style.overflow = 'hidden';
- }
- if (typeof settings.width != 'undefined') {
- var width = settings.width;
- if (isFunction(width)) {
- width = width();
- }
- instance.rootElement.style.width = width + 'px';
- }
- if (!init) {
- datamap.clearLengthCache(); // force clear cache length on updateSettings() #3416
- if (instance.view) {
- instance.view.wt.wtViewport.resetHasOversizedColumnHeadersMarked();
- }
- instance.runHooks('afterUpdateSettings');
- }
- grid.adjustRowsAndCols();
- if (instance.view && !priv.firstRun) {
- instance.forceFullRender = true; // used when data was changed
- selection.refreshBorders(null, true);
- }
- if (!init && instance.view && (currentHeight === '' || height === '' || height === void 0) && currentHeight !== height) {
- instance.view.wt.wtOverlays.updateMainScrollableElements();
- }
- };
- /**
- * Get value from the selected cell.
- *
- * @memberof Core#
- * @function getValue
- * @since 0.11
- * @returns {*} Value of selected cell.
- */
- this.getValue = function () {
- var sel = instance.getSelected();
- if (GridSettings.prototype.getValue) {
- if (isFunction(GridSettings.prototype.getValue)) {
- return GridSettings.prototype.getValue.call(instance);
- } else if (sel) {
- return instance.getData()[sel[0]][GridSettings.prototype.getValue];
- }
- } else if (sel) {
- return instance.getDataAtCell(sel[0], sel[1]);
- }
- };
- function expandType(obj) {
- if (!hasOwnProperty(obj, 'type')) {
- // ignore obj.prototype.type
- return;
- }
- var type,
- expandedType = {};
- if (_typeof(obj.type) === 'object') {
- type = obj.type;
- } else if (typeof obj.type === 'string') {
- type = getCellType(obj.type);
- }
- for (var i in type) {
- if (hasOwnProperty(type, i) && !hasOwnProperty(obj, i)) {
- expandedType[i] = type[i];
- }
- }
- return expandedType;
- }
- /**
- * Returns the object settings.
- *
- * @memberof Core#
- * @function getSettings
- * @returns {Object} Object containing the current grid settings.
- */
- this.getSettings = function () {
- return priv.settings;
- };
- /**
- * Clears the data from the grid. (The table settings remain intact.)
- *
- * @memberof Core#
- * @function clear
- * @since 0.11
- */
- this.clear = function () {
- selection.selectAll();
- selection.empty();
- };
- /**
- * @memberof Core#
- * @function alter
- * @param {String} action See grid.alter for possible values: `"insert_row"`, `"insert_col"`, `"remove_row"`, `"remove_col"`
- * @param {Number} index Index of the row/column before which the new row/column will be inserted/removed.
- * @param {Number} [amount = 1] Amound of rows/columns to be inserted/removed.
- * @param {String} [source] Source indicator.
- * @param {Boolean} [keepEmptyRows] Flag for preventing deletion of empty rows.
- * @description
- *
- * Allows altering the table structure by either inserting/removing rows or inserting/removing columns:
- *
- * Insert new row(s) above the row with a given `index`. If index is `null` or `undefined`, the new row will be
- * added after the last row.
- * ```js
- * var hot = new Handsontable(document.getElementById('example'));
- * hot.alter('insert_row', 10);
- * ```
- *
- * Insert new column(s) before the column with a given `index`. If index is `null` or `undefined`, the new column
- * will be added after the last column.
- * ```js
- * var hot = new Handsontable(document.getElementById('example'));
- * hot.alter('insert_col', 10);
- * ```
- *
- * Remove the row(s) at the given `index`.
- * ```js
- * var hot = new Handsontable(document.getElementById('example'));
- * hot.alter('remove_row', 10);
- * ```
- *
- * Remove the column(s) at the given `index`.
- * ```js
- * var hot = new Handsontable(document.getElementById('example'));
- * hot.alter('remove_col', 10);
- * ```
- */
- this.alter = function (action, index, amount, source, keepEmptyRows) {
- grid.alter(action, index, amount, source, keepEmptyRows);
- };
- /**
- * Returns a TD element for the given `row` and `col` arguments, if it is rendered on screen.
- * Returns `null` if the TD is not rendered on screen (probably because that part of the table is not visible).
- *
- * @memberof Core#
- * @function getCell
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @param {Boolean} topmost If set to true, it returns the TD element from the topmost overlay. For example,
- * if the wanted cell is in the range of fixed rows, it will return a TD element from the `top` overlay.
- * @returns {Element} The cell's TD element.
- */
- this.getCell = function (row, col, topmost) {
- return instance.view.getCellAtCoords(new CellCoords(row, col), topmost);
- };
- /**
- * Returns the coordinates of the cell, provided as a HTML Element.
- *
- * @memberof Core#
- * @function getCoords
- * @param {Element} elem The HTML Element representing the cell.
- * @returns {CellCoords} Coordinates object.
- */
- this.getCoords = function (elem) {
- return this.view.wt.wtTable.getCoords.call(this.view.wt.wtTable, elem);
- };
- /**
- * Returns the property name that corresponds with the given column index. {@link DataMap#colToProp}
- * If the data source is an array of arrays, it returns the columns index.
- *
- * @memberof Core#
- * @function colToProp
- * @param {Number} col Column index
- * @returns {String|Number} Column property or column index.
- */
- this.colToProp = function (col) {
- return datamap.colToProp(col);
- };
- /**
- * Returns column index that corresponds with the given property. {@link DataMap#propToCol}
- *
- * @memberof Core#
- * @function propToCol
- * @param {String|Number} prop Property name or column index.
- * @returns {Number} Column index.
- */
- this.propToCol = function (prop) {
- return datamap.propToCol(prop);
- };
- /**
- * Translate physical row index into visual.
- *
- * @since 0.29.0
- * @memberof Core#
- * @function toVisualRow
- * @param {Number} row Physical row index.
- * @returns {Number} Returns visual row index.
- */
- this.toVisualRow = function (row) {
- return recordTranslator.toVisualRow(row);
- };
- /**
- * Translate physical column index into visual.
- *
- * @since 0.29.0
- * @memberof Core#
- * @function toVisualColumn
- * @param {Number} column Physical column index.
- * @returns {Number} Returns visual column index.
- */
- this.toVisualColumn = function (column) {
- return recordTranslator.toVisualColumn(column);
- };
- /**
- * Translate visual row index into physical.
- * If displayed rows order is different than the order of rows stored in memory (i.e. sorting is applied)
- * to retrieve valid physical row index you can use this method.
- *
- * @since 0.29.0
- * @memberof Core#
- * @function toPhysicalRow
- * @param {Number} row Visual row index.
- * @returns {Number} Returns physical row index.
- */
- this.toPhysicalRow = function (row) {
- return recordTranslator.toPhysicalRow(row);
- };
- /**
- * Translate visual column index into physical.
- * If displayed columns order is different than the order of columns stored in memory (i.e. manual column move is applied)
- * to retrieve valid physical column index you can use this method.
- *
- * @since 0.29.0
- * @memberof Core#
- * @function toPhysicalColumn
- * @param {Number} column Visual column index.
- * @returns {Number} Returns physical column index.
- */
- this.toPhysicalColumn = function (column) {
- return recordTranslator.toPhysicalColumn(column);
- };
- /**
- * @description
- * Returns the cell value at `row`, `col`. `row` and `col` are the __visible__ indexes (note, that if columns were reordered or sorted,
- * the currently visible order will be used).
- *
- * @memberof Core#
- * @function getDataAtCell
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @returns {String|Boolean|null} Data at cell.
- */
- this.getDataAtCell = function (row, col) {
- return datamap.get(row, datamap.colToProp(col));
- };
- /**
- * Return value at `row`, `prop`. (Uses {@link DataMap#get})
- *
- * @memberof Core#
- * @function getDataAtRowProp
- * @param {Number} row Row index.
- * @param {String} prop Property name.
- * @returns {*} Cell value.
- */
- this.getDataAtRowProp = function (row, prop) {
- return datamap.get(row, prop);
- };
- /**
- * @description
- * Returns array of column values from the data source. `col` is the __visible__ index of the column.
- * Note, that if columns were reordered or sorted, the currently visible order will be used.
- *
- * @memberof Core#
- * @function getDataAtCol
- * @since 0.9-beta2
- * @param {Number} col Column index.
- * @returns {Array} Array of cell values.
- */
- this.getDataAtCol = function (col) {
- var out = [];
- return out.concat.apply(out, _toConsumableArray(datamap.getRange(new CellCoords(0, col), new CellCoords(priv.settings.data.length - 1, col), datamap.DESTINATION_RENDERER)));
- };
- /**
- * Given the object property name (e.g. `'first.name'`), returns an array of column's values from the data source.
- * You can also provide a column index as the first argument.
- *
- * @memberof Core#
- * @function getDataAtProp
- * @since 0.9-beta2
- * @param {String|Number} prop Property name / column index.
- * @returns {Array} Array of cell values.
- */
- this.getDataAtProp = function (prop) {
- var out = [],
- range;
- range = datamap.getRange(new CellCoords(0, datamap.propToCol(prop)), new CellCoords(priv.settings.data.length - 1, datamap.propToCol(prop)), datamap.DESTINATION_RENDERER);
- return out.concat.apply(out, _toConsumableArray(range));
- };
- /**
- * Returns the source data object (the same that was passed by `data` configuration option or `loadData` method).
- * Optionally you can provide a cell range by using the `row`, `col`, `row2`, `col2` arguments, to get only a fragment of grid data.
- *
- * @memberof Core#
- * @function getSourceData
- * @since 0.20.0
- * @param {Number} [r] From row.
- * @param {Number} [c] From column.
- * @param {Number} [r2] To row.
- * @param {Number} [c2] To column.
- * @returns {Array} Array of grid data.
- */
- this.getSourceData = function (r, c, r2, c2) {
- var data = void 0;
- if (r === void 0) {
- data = dataSource.getData();
- } else {
- data = dataSource.getByRange(new CellCoords(r, c), new CellCoords(r2, c2));
- }
- return data;
- };
- /**
- * Returns the source data object as an arrays of arrays format even when source data was provided in another format.
- * Optionally you can provide a cell range by using the `row`, `col`, `row2`, `col2` arguments, to get only a fragment of grid data.
- *
- * @memberof Core#
- * @function getSourceDataArray
- * @since 0.28.0
- * @param {Number} [r] From row.
- * @param {Number} [c] From column.
- * @param {Number} [r2] To row.
- * @param {Number} [c2] To column.
- * @returns {Array} An array of arrays.
- */
- this.getSourceDataArray = function (r, c, r2, c2) {
- var data = void 0;
- if (r === void 0) {
- data = dataSource.getData(true);
- } else {
- data = dataSource.getByRange(new CellCoords(r, c), new CellCoords(r2, c2), true);
- }
- return data;
- };
- /**
- * Returns an array of column values from the data source. `col` is the index of the row in the data source.
- *
- * @memberof Core#
- * @function getSourceDataAtCol
- * @since 0.11.0-beta3
- * @param {Number} column Column index.
- * @returns {Array} Array of the column's cell values.
- */
- this.getSourceDataAtCol = function (column) {
- return dataSource.getAtColumn(column);
- };
- /**
- * Returns a single row of the data (array or object, depending on what you have). `row` is the index of the row in the data source.
- *
- * @memberof Core#
- * @function getSourceDataAtRow
- * @since 0.11.0-beta3
- * @param {Number} row Row index.
- * @returns {Array|Object} Single row of data.
- */
- this.getSourceDataAtRow = function (row) {
- return dataSource.getAtRow(row);
- };
- /**
- * Returns a single value from the data source.
- *
- * @memberof Core#
- * @function getSourceDataAtCell
- * @param {Number} row Row index.
- * @param {Number} column Column index.
- * @returns {*} Cell data.
- * @since 0.20.0
- */
- this.getSourceDataAtCell = function (row, column) {
- return dataSource.getAtCell(row, column);
- };
- /**
- * @description
- * Returns a single row of the data. The `row` argument is the __visible__ index of the row.
- *
- * @memberof Core#
- * @function getDataAtRow
- * @param {Number} row Row index.
- * @returns {Array} Array of row's cell data.
- * @since 0.9-beta2
- */
- this.getDataAtRow = function (row) {
- var data = datamap.getRange(new CellCoords(row, 0), new CellCoords(row, this.countCols() - 1), datamap.DESTINATION_RENDERER);
- return data[0] || [];
- };
- /**
- * @description
- * Returns a data type defined in the Handsontable settings under the `type` key ([Options#type](http://docs.handsontable.com/Options.html#type)).
- * If there are cells with different types in the selected range, it returns `'mixed'`.
- *
- * @since 0.18.1
- * @memberof Core#
- * @function getDataType
- * @param {Number} rowFrom From row index.
- * @param {Number} columnFrom To row index.
- * @param {Number} rowTo From column index.
- * @param {Number} columnTo To column index.
- * @returns {String} Cell type (e.q: `'mixed'`, `'text'`, `'numeric'`, `'autocomplete'`).
- */
- this.getDataType = function (rowFrom, columnFrom, rowTo, columnTo) {
- var _this = this;
- var previousType = null;
- var currentType = null;
- if (rowFrom === void 0) {
- rowFrom = 0;
- rowTo = this.countRows();
- columnFrom = 0;
- columnTo = this.countCols();
- }
- if (rowTo === void 0) {
- rowTo = rowFrom;
- }
- if (columnTo === void 0) {
- columnTo = columnFrom;
- }
- var type = 'mixed';
- rangeEach(Math.min(rowFrom, rowTo), Math.max(rowFrom, rowTo), function (row) {
- var isTypeEqual = true;
- rangeEach(Math.min(columnFrom, columnTo), Math.max(columnFrom, columnTo), function (column) {
- var cellType = _this.getCellMeta(row, column);
- currentType = cellType.type;
- if (previousType) {
- isTypeEqual = previousType === currentType;
- } else {
- previousType = currentType;
- }
- return isTypeEqual;
- });
- type = isTypeEqual ? currentType : 'mixed';
- return isTypeEqual;
- });
- return type;
- };
- /**
- * Remove a property defined by the `key` argument from the cell meta object for the provided `row` and `col` coordinates.
- *
- * @memberof Core#
- * @function removeCellMeta
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @param {String} key Property name.
- */
- this.removeCellMeta = function (row, col, key) {
- var cellMeta = instance.getCellMeta(row, col);
- if (cellMeta[key] != undefined) {
- delete priv.cellSettings[row][col][key];
- }
- };
- /**
- * Remove one or more rows from the cell meta object.
- *
- * @since 0.30.0
- * @param {Number} index An integer that specifies at what position to add/remove items, Use negative values to specify the position from the end of the array.
- * @param {Number} deleteAmount The number of items to be removed. If set to 0, no items will be removed.
- * @param {Array} items The new items to be added to the array.
- */
- this.spliceCellsMeta = function (index, deleteAmount) {
- var _priv$cellSettings;
- for (var _len2 = arguments.length, items = Array(_len2 > 2 ? _len2 - 2 : 0), _key = 2; _key < _len2; _key++) {
- items[_key - 2] = arguments[_key];
- }
- (_priv$cellSettings = priv.cellSettings).splice.apply(_priv$cellSettings, [index, deleteAmount].concat(items));
- };
- /**
- * Set cell meta data object defined by `prop` to the corresponding params `row` and `col`.
- *
- * @memberof Core#
- * @function setCellMetaObject
- * @since 0.11
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @param {Object} prop Meta object.
- */
- this.setCellMetaObject = function (row, col, prop) {
- if ((typeof prop === 'undefined' ? 'undefined' : _typeof(prop)) === 'object') {
- for (var key in prop) {
- if (hasOwnProperty(prop, key)) {
- var value = prop[key];
- this.setCellMeta(row, col, key, value);
- }
- }
- }
- };
- /**
- * Sets a property defined by the `key` object to the meta object of a cell corresponding to params `row` and `col`.
- *
- * @memberof Core#
- * @function setCellMeta
- * @since 0.11
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @param {String} key Property name.
- * @param {String} val Property value.
- * @fires Hooks#afterSetCellMeta
- */
- this.setCellMeta = function (row, col, key, val) {
- var _recordTranslator$toP = recordTranslator.toPhysical(row, col);
- var _recordTranslator$toP2 = _slicedToArray(_recordTranslator$toP, 2);
- row = _recordTranslator$toP2[0];
- col = _recordTranslator$toP2[1];
- if (!priv.columnSettings[col]) {
- priv.columnSettings[col] = columnFactory(GridSettings, priv.columnsSettingConflicts);
- }
- if (!priv.cellSettings[row]) {
- priv.cellSettings[row] = [];
- }
- if (!priv.cellSettings[row][col]) {
- priv.cellSettings[row][col] = new priv.columnSettings[col]();
- }
- priv.cellSettings[row][col][key] = val;
- instance.runHooks('afterSetCellMeta', row, col, key, val);
- };
- /**
- * Get all the cells meta settings at least once generated in the table (in order of cell initialization).
- *
- * @since 0.19.0
- * @returns {Array} Returns Array of ColumnSettings object.
- */
- this.getCellsMeta = function () {
- return arrayFlatten(priv.cellSettings);
- };
- /**
- * Returns the cell properties object for the given `row` and `col` coordinates.
- *
- * @memberof Core#
- * @function getCellMeta
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @returns {Object} The cell properties object.
- * @fires Hooks#beforeGetCellMeta
- * @fires Hooks#afterGetCellMeta
- */
- this.getCellMeta = function (row, col) {
- var prop = datamap.colToProp(col),
- cellProperties;
- var visualRow = row;
- var visualCol = col;
- var _recordTranslator$toP3 = recordTranslator.toPhysical(row, col);
- var _recordTranslator$toP4 = _slicedToArray(_recordTranslator$toP3, 2);
- row = _recordTranslator$toP4[0];
- col = _recordTranslator$toP4[1];
- if (!priv.columnSettings[col]) {
- priv.columnSettings[col] = columnFactory(GridSettings, priv.columnsSettingConflicts);
- }
- if (!priv.cellSettings[row]) {
- priv.cellSettings[row] = [];
- }
- if (!priv.cellSettings[row][col]) {
- priv.cellSettings[row][col] = new priv.columnSettings[col]();
- }
- cellProperties = priv.cellSettings[row][col]; // retrieve cellProperties from cache
- cellProperties.row = row;
- cellProperties.col = col;
- cellProperties.visualRow = visualRow;
- cellProperties.visualCol = visualCol;
- cellProperties.prop = prop;
- cellProperties.instance = instance;
- instance.runHooks('beforeGetCellMeta', row, col, cellProperties);
- extend(cellProperties, expandType(cellProperties)); // for `type` added in beforeGetCellMeta
- if (cellProperties.cells) {
- var settings = cellProperties.cells.call(cellProperties, row, col, prop);
- if (settings) {
- extend(cellProperties, settings);
- extend(cellProperties, expandType(settings)); // for `type` added in cells
- }
- }
- instance.runHooks('afterGetCellMeta', row, col, cellProperties);
- return cellProperties;
- };
- /**
- * Returns a row off the cell meta array.
- *
- * @memberof Core#
- * @function getCellMetaAtRow
- * @since 0.30.0
- * @param {Number} row Index of the row to return cell meta for.
- * @returns {Array}
- */
- this.getCellMetaAtRow = function (row) {
- return priv.cellSettings[row];
- };
- /**
- * Checks if the data format and config allows user to modify the column structure.
- * @returns {boolean}
- */
- this.isColumnModificationAllowed = function () {
- return !(instance.dataType === 'object' || instance.getSettings().columns);
- };
- var rendererLookup = cellMethodLookupFactory('renderer');
- /**
- * Returns the cell renderer function by given `row` and `col` arguments.
- *
- * @memberof Core#
- * @function getCellRenderer
- * @since 0.11
- * @param {Number|Object} row Row index or cell meta object.
- * @param {Number} [col] Column index.
- * @returns {Function} The renderer function.
- */
- this.getCellRenderer = function (row, col) {
- return getRenderer(rendererLookup.call(this, row, col));
- };
- /**
- * Returns the cell editor by the provided `row` and `col` arguments.
- *
- * @memberof Core#
- * @function getCellEditor
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @returns {Object} The Editor object.
- */
- this.getCellEditor = cellMethodLookupFactory('editor');
- var validatorLookup = cellMethodLookupFactory('validator');
- /**
- * Returns the cell validator by `row` and `col`, provided a validator is defined. If not - it doesn't return anything.
- *
- * @memberof Core#
- * @function getCellValidator
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @returns {Function|RegExp|undefined} The validator function.
- */
- this.getCellValidator = function (row, col) {
- var validator = validatorLookup.call(this, row, col);
- if (typeof validator === 'string') {
- validator = getValidator(validator);
- }
- return validator;
- };
- /**
- * Validates all cells using their validator functions and calls callback when finished.
- *
- * If one of the cells is invalid, the callback will be fired with `'valid'` arguments as `false` - otherwise it would equal `true`.
- *
- * @memberof Core#
- * @function validateCells
- * @param {Function} [callback] The callback function.
- */
- this.validateCells = function (callback) {
- var waitingForValidator = new ValidatorsQueue();
- if (callback) {
- waitingForValidator.onQueueEmpty = callback;
- }
- var i = instance.countRows() - 1;
- while (i >= 0) {
- var j = instance.countCols() - 1;
- while (j >= 0) {
- waitingForValidator.addValidatorToQueue();
- instance.validateCell(instance.getDataAtCell(i, j), instance.getCellMeta(i, j), function (result) {
- if (typeof result !== 'boolean') {
- throw new Error('Validation error: result is not boolean');
- }
- if (result === false) {
- waitingForValidator.valid = false;
- }
- waitingForValidator.removeValidatorFormQueue();
- }, 'validateCells');
- j--;
- }
- i--;
- }
- waitingForValidator.checkIfQueueIsEmpty();
- };
- /**
- * Returns an array of row headers' values (if they are enabled). If param `row` was given, it returns the header of the given row as a string.
- *
- * @memberof Core#
- * @function getRowHeader
- * @param {Number} [row] Row index.
- * @fires Hooks#modifyRowHeader
- * @returns {Array|String} Array of header values / single header value.
- */
- this.getRowHeader = function (row) {
- var rowHeader = priv.settings.rowHeaders;
- if (row !== void 0) {
- row = instance.runHooks('modifyRowHeader', row);
- }
- if (row === void 0) {
- rowHeader = [];
- rangeEach(instance.countRows() - 1, function (i) {
- rowHeader.push(instance.getRowHeader(i));
- });
- } else if (Array.isArray(rowHeader) && rowHeader[row] !== void 0) {
- rowHeader = rowHeader[row];
- } else if (isFunction(rowHeader)) {
- rowHeader = rowHeader(row);
- } else if (rowHeader && typeof rowHeader !== 'string' && typeof rowHeader !== 'number') {
- rowHeader = row + 1;
- }
- return rowHeader;
- };
- /**
- * Returns information about if this table is configured to display row headers.
- *
- * @memberof Core#
- * @function hasRowHeaders
- * @returns {Boolean} `true` if the instance has the row headers enabled, `false` otherwise.
- * @since 0.11
- */
- this.hasRowHeaders = function () {
- return !!priv.settings.rowHeaders;
- };
- /**
- * Returns information about if this table is configured to display column headers.
- *
- * @memberof Core#
- * @function hasColHeaders
- * @since 0.11
- * @returns {Boolean} `True` if the instance has the column headers enabled, `false` otherwise.
- */
- this.hasColHeaders = function () {
- if (priv.settings.colHeaders !== void 0 && priv.settings.colHeaders !== null) {
- // Polymer has empty value = null
- return !!priv.settings.colHeaders;
- }
- for (var i = 0, ilen = instance.countCols(); i < ilen; i++) {
- if (instance.getColHeader(i)) {
- return true;
- }
- }
- return false;
- };
- /**
- * Returns an array of column headers (in string format, if they are enabled). If param `col` is given, it returns the header at the given column as a string.
- *
- * @memberof Core#
- * @function getColHeader
- * @param {Number} [col] Column index.
- * @fires Hooks#modifyColHeader
- * @returns {Array|String} The column header(s).
- */
- this.getColHeader = function (col) {
- var columnsAsFunc = priv.settings.columns && isFunction(priv.settings.columns);
- var result = priv.settings.colHeaders;
- col = instance.runHooks('modifyColHeader', col);
- if (col === void 0) {
- var out = [];
- var ilen = columnsAsFunc ? instance.countSourceCols() : instance.countCols();
- for (var i = 0; i < ilen; i++) {
- out.push(instance.getColHeader(i));
- }
- result = out;
- } else {
- var translateVisualIndexToColumns = function translateVisualIndexToColumns(col) {
- var arr = [];
- var columnsLen = instance.countSourceCols();
- var index = 0;
- for (; index < columnsLen; index++) {
- if (isFunction(instance.getSettings().columns) && instance.getSettings().columns(index)) {
- arr.push(index);
- }
- }
- return arr[col];
- };
- var baseCol = col;
- col = instance.runHooks('modifyCol', col);
- var prop = translateVisualIndexToColumns(col);
- if (priv.settings.columns && isFunction(priv.settings.columns) && priv.settings.columns(prop) && priv.settings.columns(prop).title) {
- result = priv.settings.columns(prop).title;
- } else if (priv.settings.columns && priv.settings.columns[col] && priv.settings.columns[col].title) {
- result = priv.settings.columns[col].title;
- } else if (Array.isArray(priv.settings.colHeaders) && priv.settings.colHeaders[col] !== void 0) {
- result = priv.settings.colHeaders[col];
- } else if (isFunction(priv.settings.colHeaders)) {
- result = priv.settings.colHeaders(col);
- } else if (priv.settings.colHeaders && typeof priv.settings.colHeaders !== 'string' && typeof priv.settings.colHeaders !== 'number') {
- result = spreadsheetColumnLabel(baseCol); // see #1458
- }
- }
- return result;
- };
- /**
- * Return column width from settings (no guessing). Private use intended.
- *
- * @private
- * @memberof Core#
- * @function _getColWidthFromSettings
- * @param {Number} col
- * @returns {Number}
- */
- this._getColWidthFromSettings = function (col) {
- var cellProperties = instance.getCellMeta(0, col);
- var width = cellProperties.width;
- if (width === void 0 || width === priv.settings.width) {
- width = cellProperties.colWidths;
- }
- if (width !== void 0 && width !== null) {
- switch (typeof width === 'undefined' ? 'undefined' : _typeof(width)) {
- case 'object':
- // array
- width = width[col];
- break;
- case 'function':
- width = width(col);
- break;
- default:
- break;
- }
- if (typeof width === 'string') {
- width = parseInt(width, 10);
- }
- }
- return width;
- };
- /**
- * Returns the width of the requested column.
- *
- * @memberof Core#
- * @function getColWidth
- * @since 0.11
- * @param {Number} col Column index.
- * @returns {Number} Column width.
- * @fires Hooks#modifyColWidth
- */
- this.getColWidth = function (col) {
- var width = instance._getColWidthFromSettings(col);
- width = instance.runHooks('modifyColWidth', width, col);
- if (width === void 0) {
- width = ViewportColumnsCalculator.DEFAULT_WIDTH;
- }
- return width;
- };
- /**
- * Return row height from settings (no guessing). Private use intended.
- *
- * @private
- * @memberof Core#
- * @function _getRowHeightFromSettings
- * @param {Number} row
- * @returns {Number}
- */
- this._getRowHeightFromSettings = function (row) {
- // let cellProperties = instance.getCellMeta(row, 0);
- // let height = cellProperties.height;
- //
- // if (height === void 0 || height === priv.settings.height) {
- // height = cellProperties.rowHeights;
- // }
- var height = priv.settings.rowHeights;
- if (height !== void 0 && height !== null) {
- switch (typeof height === 'undefined' ? 'undefined' : _typeof(height)) {
- case 'object':
- // array
- height = height[row];
- break;
- case 'function':
- height = height(row);
- break;
- default:
- break;
- }
- if (typeof height === 'string') {
- height = parseInt(height, 10);
- }
- }
- return height;
- };
- /**
- * Returns the row height.
- *
- * @memberof Core#
- * @function getRowHeight
- * @since 0.11
- * @param {Number} row Row index.
- * @returns {Number} The given row's height.
- * @fires Hooks#modifyRowHeight
- */
- this.getRowHeight = function (row) {
- var height = instance._getRowHeightFromSettings(row);
- height = instance.runHooks('modifyRowHeight', height, row);
- return height;
- };
- /**
- * Returns the total number of rows in the data source.
- *
- * @memberof Core#
- * @function countSourceRows
- * @since 0.20.0
- * @returns {Number} Total number in rows in data source.
- */
- this.countSourceRows = function () {
- var sourceLength = instance.runHooks('modifySourceLength');
- return sourceLength || (instance.getSourceData() ? instance.getSourceData().length : 0);
- };
- /**
- * Returns the total number of columns in the data source.
- *
- * @memberof Core#
- * @function countSourceCols
- * @since 0.26.1
- * @returns {Number} Total number in columns in data source.
- */
- this.countSourceCols = function () {
- var len = 0;
- var obj = instance.getSourceData() && instance.getSourceData()[0] ? instance.getSourceData()[0] : [];
- if (isObject(obj)) {
- len = deepObjectSize(obj);
- } else {
- len = obj.length || 0;
- }
- return len;
- };
- /**
- * Returns the total number of rows in the grid.
- *
- * @memberof Core#
- * @function countRows
- * @returns {Number} Total number in rows the grid.
- */
- this.countRows = function () {
- return datamap.getLength();
- };
- /**
- * Returns the total number of columns in the grid.
- *
- * @memberof Core#
- * @function countCols
- * @returns {Number} Total number of columns.
- */
- this.countCols = function () {
- var maxCols = this.getSettings().maxCols;
- var dataHasLength = false;
- var dataLen = 0;
- if (instance.dataType === 'array') {
- dataHasLength = priv.settings.data && priv.settings.data[0] && priv.settings.data[0].length;
- }
- if (dataHasLength) {
- dataLen = priv.settings.data[0].length;
- }
- if (priv.settings.columns) {
- var columnsIsFunction = isFunction(priv.settings.columns);
- if (columnsIsFunction) {
- if (instance.dataType === 'array') {
- var columnLen = 0;
- for (var i = 0; i < dataLen; i++) {
- if (priv.settings.columns(i)) {
- columnLen++;
- }
- }
- dataLen = columnLen;
- } else if (instance.dataType === 'object' || instance.dataType === 'function') {
- dataLen = datamap.colToPropCache.length;
- }
- } else {
- dataLen = priv.settings.columns.length;
- }
- } else if (instance.dataType === 'object' || instance.dataType === 'function') {
- dataLen = datamap.colToPropCache.length;
- }
- return Math.min(maxCols, dataLen);
- };
- /**
- * Returns an index of the first rendered row.
- *
- * @memberof Core#
- * @function rowOffset
- * @returns {Number} Index of first rendered row.
- */
- this.rowOffset = function () {
- return instance.view.wt.wtTable.getFirstRenderedRow();
- };
- /**
- * Returns the index of the first rendered column.
- *
- * @memberof Core#
- * @function colOffset
- * @returns {Number} Index of the first visible column.
- */
- this.colOffset = function () {
- return instance.view.wt.wtTable.getFirstRenderedColumn();
- };
- /**
- * Returns the number of rendered rows (including rows partially or fully rendered outside viewport).
- *
- * @memberof Core#
- * @function countRenderedRows
- * @returns {Number} Returns -1 if table is not visible.
- */
- this.countRenderedRows = function () {
- return instance.view.wt.drawn ? instance.view.wt.wtTable.getRenderedRowsCount() : -1;
- };
- /**
- * Returns the number of visible rows (rendered rows that fully fit inside viewport).
- *
- * @memberof Core#
- * @function countVisibleRows
- * @returns {Number} Number of visible rows or -1.
- */
- this.countVisibleRows = function () {
- return instance.view.wt.drawn ? instance.view.wt.wtTable.getVisibleRowsCount() : -1;
- };
- /**
- * Returns the number of rendered columns (including columns partially or fully rendered outside viewport).
- *
- * @memberof Core#
- * @function countRenderedCols
- * @returns {Number} Returns -1 if table is not visible.
- */
- this.countRenderedCols = function () {
- return instance.view.wt.drawn ? instance.view.wt.wtTable.getRenderedColumnsCount() : -1;
- };
- /**
- * Returns the number of visible columns. Returns -1 if table is not visible
- *
- * @memberof Core#
- * @function countVisibleCols
- * @return {Number} Number of visible columns or -1.
- */
- this.countVisibleCols = function () {
- return instance.view.wt.drawn ? instance.view.wt.wtTable.getVisibleColumnsCount() : -1;
- };
- /**
- * Returns the number of empty rows. If the optional ending parameter is `true`, returns the
- * number of empty rows at the bottom of the table.
- *
- * @memberof Core#
- * @function countEmptyRows
- * @param {Boolean} [ending] If `true`, will only count empty rows at the end of the data source.
- * @returns {Number} Count empty rows
- * @fires Hooks#modifyRow
- */
- this.countEmptyRows = function (ending) {
- var i = instance.countRows() - 1,
- empty = 0,
- row;
- while (i >= 0) {
- row = instance.runHooks('modifyRow', i);
- if (instance.isEmptyRow(row)) {
- empty++;
- } else if (ending) {
- break;
- }
- i--;
- }
- return empty;
- };
- /**
- * Returns the number of empty columns. If the optional ending parameter is `true`, returns the number of empty
- * columns at right hand edge of the table.
- *
- * @memberof Core#
- * @function countEmptyCols
- * @param {Boolean} [ending] If `true`, will only count empty columns at the end of the data source row.
- * @returns {Number} Count empty cols
- */
- this.countEmptyCols = function (ending) {
- if (instance.countRows() < 1) {
- return 0;
- }
- var i = instance.countCols() - 1,
- empty = 0;
- while (i >= 0) {
- if (instance.isEmptyCol(i)) {
- empty++;
- } else if (ending) {
- break;
- }
- i--;
- }
- return empty;
- };
- /**
- * Check if all cells in the row declared by the `row` argument are empty.
- *
- * @memberof Core#
- * @function isEmptyRow
- * @param {Number} row Row index.
- * @returns {Boolean} `true` if the row at the given `row` is empty, `false` otherwise.
- */
- this.isEmptyRow = function (row) {
- return priv.settings.isEmptyRow.call(instance, row);
- };
- /**
- * Check if all cells in the the column declared by the `col` argument are empty.
- *
- * @memberof Core#
- * @function isEmptyCol
- * @param {Number} col Column index.
- * @returns {Boolean} `true` if the column at the given `col` is empty, `false` otherwise.
- */
- this.isEmptyCol = function (col) {
- return priv.settings.isEmptyCol.call(instance, col);
- };
- /**
- * Select cell specified by `row` and `col` values or a range of cells finishing at `endRow`, `endCol`.
- * By default, viewport will be scrolled to selection.
- * After the `selectCell` method had finished, the instance will be listening to keyboard input on the document.
- *
- * @memberof Core#
- * @function selectCell
- * @param {Number} row Row index.
- * @param {Number} col Column index.
- * @param {Number} [endRow] End row index (if selecting a range).
- * @param {Number} [endCol] End column index (if selecting a range).
- * @param {Boolean} [scrollToCell=true] If `true`, the viewport will be scrolled to the selection.
- * @param {Boolean} [changeListener=true] If `false`, Handsontable will not change keyboard events listener to himself.
- * @returns {Boolean} `true` if selection was successful, `false` otherwise.
- */
- this.selectCell = function (row, col, endRow, endCol, scrollToCell, changeListener) {
- var coords;
- changeListener = isUndefined(changeListener) || changeListener === true;
- if (typeof row !== 'number' || row < 0 || row >= instance.countRows()) {
- return false;
- }
- if (typeof col !== 'number' || col < 0 || col >= instance.countCols()) {
- return false;
- }
- if (isDefined(endRow)) {
- if (typeof endRow !== 'number' || endRow < 0 || endRow >= instance.countRows()) {
- return false;
- }
- if (typeof endCol !== 'number' || endCol < 0 || endCol >= instance.countCols()) {
- return false;
- }
- }
- coords = new CellCoords(row, col);
- priv.selRange = new CellRange(coords, coords, coords);
- if (changeListener) {
- instance.listen();
- }
- if (isUndefined(endRow)) {
- selection.setRangeEnd(priv.selRange.from, scrollToCell);
- } else {
- selection.setRangeEnd(new CellCoords(endRow, endCol), scrollToCell);
- }
- instance.selection.finish();
- return true;
- };
- /**
- * Select the cell specified by the `row` and `prop` arguments, or a range finishing at `endRow`, `endProp`.
- * By default, viewport will be scrolled to selection.
- *
- * @memberof Core#
- * @function selectCellByProp
- * @param {Number} row Row index.
- * @param {String} prop Property name.
- * @param {Number} [endRow] End row index (if selecting a range).
- * @param {String} [endProp] End property name (if selecting a range).
- * @param {Boolean} [scrollToCell=true] If `true`, viewport will be scrolled to the selection.
- * @returns {Boolean} `true` if selection was successful, `false` otherwise.
- */
- this.selectCellByProp = function (row, prop, endRow, endProp, scrollToCell) {
- var _instance5;
- arguments[1] = datamap.propToCol(arguments[1]);
- if (isDefined(arguments[3])) {
- arguments[3] = datamap.propToCol(arguments[3]);
- }
- return (_instance5 = instance).selectCell.apply(_instance5, arguments);
- };
- /**
- * Deselects the current cell selection on grid.
- *
- * @memberof Core#
- * @function deselectCell
- */
- this.deselectCell = function () {
- selection.deselect();
- };
- /**
- * Scroll viewport to coords specified by the `row` and `column` arguments.
- *
- * @since 0.24.3
- * @memberof Core#
- * @function scrollViewportTo
- * @param {Number} [row] Row index.
- * @param {Number} [column] Column index.
- * @param {Boolean} [snapToBottom = false] If `true`, viewport is scrolled to show the cell on the bottom of the table.
- * @param {Boolean} [snapToRight = false] If `true`, viewport is scrolled to show the cell on the right side of the table.
- * @returns {Boolean} `true` if scroll was successful, `false` otherwise.
- */
- this.scrollViewportTo = function (row, column) {
- var snapToBottom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
- var snapToRight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
- if (row !== void 0 && (row < 0 || row >= instance.countRows())) {
- return false;
- }
- if (column !== void 0 && (column < 0 || column >= instance.countCols())) {
- return false;
- }
- var result = false;
- if (row !== void 0 && column !== void 0) {
- instance.view.wt.wtOverlays.topOverlay.scrollTo(row, snapToBottom);
- instance.view.wt.wtOverlays.leftOverlay.scrollTo(column, snapToRight);
- result = true;
- }
- if (typeof row === 'number' && typeof column !== 'number') {
- instance.view.wt.wtOverlays.topOverlay.scrollTo(row, snapToBottom);
- result = true;
- }
- if (typeof column === 'number' && typeof row !== 'number') {
- instance.view.wt.wtOverlays.leftOverlay.scrollTo(column, snapToRight);
- result = true;
- }
- return result;
- };
- /**
- * Removes grid from the DOM.
- *
- * @memberof Core#
- * @function destroy
- * @fires Hooks#afterDestroy
- */
- this.destroy = function () {
- instance._clearTimeouts();
- if (instance.view) {
- // in case HT is destroyed before initialization has finished
- instance.view.destroy();
- }
- if (dataSource) {
- dataSource.destroy();
- }
- dataSource = null;
- empty(instance.rootElement);
- eventManager.destroy();
- instance.runHooks('afterDestroy');
- Hooks.getSingleton().destroy(instance);
- for (var i in instance) {
- if (hasOwnProperty(instance, i)) {
- // replace instance methods with post mortem
- if (isFunction(instance[i])) {
- instance[i] = postMortem;
- } else if (i !== 'guid') {
- // replace instance properties with null (restores memory)
- // it should not be necessary but this prevents a memory leak side effects that show itself in Jasmine tests
- instance[i] = null;
- }
- }
- }
- // replace private properties with null (restores memory)
- // it should not be necessary but this prevents a memory leak side effects that show itself in Jasmine tests
- if (datamap) {
- datamap.destroy();
- }
- datamap = null;
- priv = null;
- grid = null;
- selection = null;
- editorManager = null;
- instance = null;
- GridSettings = null;
- };
- /**
- * Replacement for all methods after Handsotnable was destroyed.
- *
- * @private
- */
- function postMortem() {
- throw new Error('This method cannot be called because this Handsontable instance has been destroyed');
- }
- /**
- * Returns the active editor object.
- *
- * @memberof Core#
- * @function getActiveEditor
- * @returns {Object} The active editor object.
- */
- this.getActiveEditor = function () {
- return editorManager.getActiveEditor();
- };
- /**
- * Returns plugin instance using the plugin name provided.
- *
- * @memberof Core#
- * @function getPlugin
- * @param {String} pluginName The plugin name.
- * @returns {*} The plugin instance.
- * @since 0.15.0
- */
- this.getPlugin = function (pluginName) {
- return getPlugin(this, pluginName);
- };
- /**
- * Returns the Handsontable instance.
- *
- * @memberof Core#
- * @function getInstance
- * @returns {Handsontable} The Handsontable instance.
- */
- this.getInstance = function () {
- return instance;
- };
- /**
- * Adds listener to the specified hook name (only for this Handsontable instance).
- *
- * @memberof Core#
- * @function addHook
- * @see Hooks#add
- * @param {String} key Hook name.
- * @param {Function|Array} callback Function or array of Functions.
- *
- * @example
- * ```js
- * hot.addHook('beforeInit', myCallback);
- * ```
- */
- this.addHook = function (key, callback) {
- Hooks.getSingleton().add(key, callback, instance);
- };
- /**
- * Check if for a specified hook name there are added listeners (only for this Handsontable instance).
- *
- * @memberof Core#
- * @function hasHook
- * @see Hooks#has
- * @param {String} key Hook name
- * @return {Boolean}
- *
- * @example
- * ```js
- * var hasBeforeInitListeners = hot.hasHook('beforeInit');
- * ```
- */
- this.hasHook = function (key) {
- return Hooks.getSingleton().has(key, instance);
- };
- /**
- * Adds listener to specified hook name (only for this Handsontable instance).
- * After the listener is triggered, it will be automatically removed.
- *
- * @memberof Core#
- * @function addHookOnce
- * @see Hooks#once
- * @param {String} key Hook name.
- * @param {Function|Array} callback Function or array of Functions.
- *
- * @example
- * ```js
- * hot.addHookOnce('beforeInit', myCallback);
- * ```
- */
- this.addHookOnce = function (key, callback) {
- Hooks.getSingleton().once(key, callback, instance);
- };
- /**
- * Removes the hook listener previously registered with {@link Core#addHook}.
- *
- * @memberof Core#
- * @function removeHook
- * @see Hooks#remove
- * @param {String} key Hook name.
- * @param {Function} callback Function which have been registered via {@link Core#addHook}.
- *
- * @example
- * ```js
- * hot.removeHook('beforeInit', myCallback);
- * ```
- */
- this.removeHook = function (key, callback) {
- Hooks.getSingleton().remove(key, callback, instance);
- };
- /**
- * Run the callbacks for the hook provided in the `key` argument using the parameters given in the other arguments.
- *
- * @memberof Core#
- * @function runHooks
- * @see Hooks#run
- * @param {String} key Hook name.
- * @param {*} [p1] Argument passed to the callback.
- * @param {*} [p2] Argument passed to the callback.
- * @param {*} [p3] Argument passed to the callback.
- * @param {*} [p4] Argument passed to the callback.
- * @param {*} [p5] Argument passed to the callback.
- * @param {*} [p6] Argument passed to the callback.
- * @returns {*}
- *
- * @example
- * ```js
- * hot.runHooks('beforeInit');
- * ```
- */
- this.runHooks = function (key, p1, p2, p3, p4, p5, p6) {
- return Hooks.getSingleton().run(instance, key, p1, p2, p3, p4, p5, p6);
- };
- this.timeouts = [];
- /**
- * Sets timeout. Purpose of this method is to clear all known timeouts when `destroy` method is called.
- *
- * @param {*} handle
- * @private
- */
- this._registerTimeout = function (handle) {
- this.timeouts.push(handle);
- };
- /**
- * Clears all known timeouts.
- *
- * @private
- */
- this._clearTimeouts = function () {
- for (var i = 0, ilen = this.timeouts.length; i < ilen; i++) {
- clearTimeout(this.timeouts[i]);
- }
- };
- /**
- * Handsontable version
- *
- * @type {String}
- */
- // this.version = Handsontable.version;
- Hooks.getSingleton().run(instance, 'construct');
- };
|