core.js 111 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394
  1. 'use strict';
  2. exports.__esModule = true;
  3. 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"); } }; }();
  4. 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; };
  5. exports.default = Core;
  6. var _numbro = require('numbro');
  7. var _numbro2 = _interopRequireDefault(_numbro);
  8. var _element = require('./helpers/dom/element');
  9. var _setting = require('./helpers/setting');
  10. var _function = require('./helpers/function');
  11. var _mixed = require('./helpers/mixed');
  12. var _browser = require('./helpers/browser');
  13. var _dataMap = require('./dataMap');
  14. var _dataMap2 = _interopRequireDefault(_dataMap);
  15. var _editorManager = require('./editorManager');
  16. var _editorManager2 = _interopRequireDefault(_editorManager);
  17. var _eventManager = require('./eventManager');
  18. var _eventManager2 = _interopRequireDefault(_eventManager);
  19. var _object = require('./helpers/object');
  20. var _array = require('./helpers/array');
  21. var _plugins = require('./plugins');
  22. var _renderers = require('./renderers');
  23. var _validators = require('./validators');
  24. var _string = require('./helpers/string');
  25. var _number = require('./helpers/number');
  26. var _tableView = require('./tableView');
  27. var _tableView2 = _interopRequireDefault(_tableView);
  28. var _dataSource = require('./dataSource');
  29. var _dataSource2 = _interopRequireDefault(_dataSource);
  30. var _data = require('./helpers/data');
  31. var _recordTranslator = require('./utils/recordTranslator');
  32. var _src = require('./3rdparty/walkontable/src');
  33. var _pluginHooks = require('./pluginHooks');
  34. var _pluginHooks2 = _interopRequireDefault(_pluginHooks);
  35. var _defaultSettings = require('./defaultSettings');
  36. var _defaultSettings2 = _interopRequireDefault(_defaultSettings);
  37. var _cellTypes = require('./cellTypes');
  38. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  39. 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); } }
  40. var activeGuid = null;
  41. /**
  42. * Handsontable constructor
  43. *
  44. * @core
  45. * @dependencies numbro
  46. * @constructor Core
  47. * @description
  48. *
  49. * After Handsontable is constructed, you can modify the grid behavior using the available public methods.
  50. *
  51. * ---
  52. * ## How to call methods
  53. *
  54. * These are 2 equal ways to call a Handsontable method:
  55. *
  56. * ```js
  57. * // all following examples assume that you constructed Handsontable like this
  58. * var ht = new Handsontable(document.getElementById('example1'), options);
  59. *
  60. * // now, to use setDataAtCell method, you can either:
  61. * ht.setDataAtCell(0, 0, 'new value');
  62. * ```
  63. *
  64. * Alternatively, you can call the method using jQuery wrapper (__obsolete__, requires initialization using our jQuery guide
  65. * ```js
  66. * $('#example1').handsontable('setDataAtCell', 0, 0, 'new value');
  67. * ```
  68. * ---
  69. */
  70. function Core(rootElement, userSettings) {
  71. var priv,
  72. datamap,
  73. dataSource,
  74. grid,
  75. selection,
  76. editorManager,
  77. instance = this,
  78. GridSettings = function GridSettings() {},
  79. eventManager = new _eventManager2.default(instance);
  80. (0, _object.extend)(GridSettings.prototype, _defaultSettings2.default.prototype); // create grid settings as a copy of default settings
  81. (0, _object.extend)(GridSettings.prototype, userSettings); // overwrite defaults with user settings
  82. (0, _object.extend)(GridSettings.prototype, expandType(userSettings));
  83. this.rootElement = rootElement;
  84. this.isHotTableEnv = (0, _element.isChildOfWebComponentTable)(this.rootElement);
  85. _eventManager2.default.isHotTableEnv = this.isHotTableEnv;
  86. this.container = document.createElement('DIV');
  87. this.renderCall = false;
  88. rootElement.insertBefore(this.container, rootElement.firstChild);
  89. this.guid = 'ht_' + (0, _string.randomString)(); // this is the namespace for global events
  90. var recordTranslator = (0, _recordTranslator.getTranslator)(instance);
  91. dataSource = new _dataSource2.default(instance);
  92. if (!this.rootElement.id || this.rootElement.id.substring(0, 3) === 'ht_') {
  93. this.rootElement.id = this.guid; // if root element does not have an id, assign a random id
  94. }
  95. priv = {
  96. cellSettings: [],
  97. columnSettings: [],
  98. columnsSettingConflicts: ['data', 'width'],
  99. settings: new GridSettings(), // current settings instance
  100. selRange: null, // exposed by public method `getSelectedRange`
  101. isPopulated: null,
  102. scrollable: null,
  103. firstRun: true
  104. };
  105. grid = {
  106. /**
  107. * Inserts or removes rows and columns
  108. *
  109. * @memberof Core#
  110. * @function alter
  111. * @private
  112. * @param {String} action Possible values: "insert_row", "insert_col", "remove_row", "remove_col"
  113. * @param {Number} index
  114. * @param {Number} amount
  115. * @param {String} [source] Optional. Source of hook runner.
  116. * @param {Boolean} [keepEmptyRows] Optional. Flag for preventing deletion of empty rows.
  117. */
  118. alter: function alter(action, index, amount, source, keepEmptyRows) {
  119. var delta;
  120. amount = amount || 1;
  121. function spliceWith(data, index, count, toInject) {
  122. var valueFactory = function valueFactory() {
  123. var result = void 0;
  124. if (toInject === 'array') {
  125. result = [];
  126. } else if (toInject === 'object') {
  127. result = {};
  128. }
  129. return result;
  130. };
  131. var spliceArgs = (0, _array.arrayMap)(new Array(count), function () {
  132. return valueFactory();
  133. });
  134. spliceArgs.unshift(index, 0);
  135. data.splice.apply(data, _toConsumableArray(spliceArgs));
  136. }
  137. /* eslint-disable no-case-declarations */
  138. switch (action) {
  139. case 'insert_row':
  140. var numberOfSourceRows = instance.countSourceRows();
  141. if (instance.getSettings().maxRows === numberOfSourceRows) {
  142. return;
  143. }
  144. index = (0, _mixed.isDefined)(index) ? index : numberOfSourceRows;
  145. delta = datamap.createRow(index, amount, source);
  146. spliceWith(priv.cellSettings, index, amount, 'array');
  147. if (delta) {
  148. if (selection.isSelected() && priv.selRange.from.row >= index) {
  149. priv.selRange.from.row += delta;
  150. selection.transformEnd(delta, 0); // will call render() internally
  151. } else {
  152. selection.refreshBorders(); // it will call render and prepare methods
  153. }
  154. }
  155. break;
  156. case 'insert_col':
  157. delta = datamap.createCol(index, amount, source);
  158. for (var row = 0, len = instance.countSourceRows(); row < len; row++) {
  159. if (priv.cellSettings[row]) {
  160. spliceWith(priv.cellSettings[row], index, amount);
  161. }
  162. }
  163. if (delta) {
  164. if (Array.isArray(instance.getSettings().colHeaders)) {
  165. var spliceArray = [index, 0];
  166. spliceArray.length += delta; // inserts empty (undefined) elements at the end of an array
  167. Array.prototype.splice.apply(instance.getSettings().colHeaders, spliceArray); // inserts empty (undefined) elements into the colHeader array
  168. }
  169. if (selection.isSelected() && priv.selRange.from.col >= index) {
  170. priv.selRange.from.col += delta;
  171. selection.transformEnd(0, delta); // will call render() internally
  172. } else {
  173. selection.refreshBorders(); // it will call render and prepare methods
  174. }
  175. }
  176. break;
  177. case 'remove_row':
  178. datamap.removeRow(index, amount, source);
  179. priv.cellSettings.splice(index, amount);
  180. var totalRows = instance.countRows();
  181. var fixedRowsTop = instance.getSettings().fixedRowsTop;
  182. if (fixedRowsTop >= index + 1) {
  183. instance.getSettings().fixedRowsTop -= Math.min(amount, fixedRowsTop - index);
  184. }
  185. var fixedRowsBottom = instance.getSettings().fixedRowsBottom;
  186. if (fixedRowsBottom && index >= totalRows - fixedRowsBottom) {
  187. instance.getSettings().fixedRowsBottom -= Math.min(amount, fixedRowsBottom);
  188. }
  189. grid.adjustRowsAndCols();
  190. selection.refreshBorders(); // it will call render and prepare methods
  191. break;
  192. case 'remove_col':
  193. var logicalColumnIndex = recordTranslator.toPhysicalColumn(index);
  194. datamap.removeCol(index, amount, source);
  195. for (var _row = 0, _len = instance.countSourceRows(); _row < _len; _row++) {
  196. if (priv.cellSettings[_row]) {
  197. // if row hasn't been rendered it wouldn't have cellSettings
  198. priv.cellSettings[_row].splice(logicalColumnIndex, amount);
  199. }
  200. }
  201. var fixedColumnsLeft = instance.getSettings().fixedColumnsLeft;
  202. if (fixedColumnsLeft >= index + 1) {
  203. instance.getSettings().fixedColumnsLeft -= Math.min(amount, fixedColumnsLeft - index);
  204. }
  205. if (Array.isArray(instance.getSettings().colHeaders)) {
  206. if (typeof logicalColumnIndex == 'undefined') {
  207. logicalColumnIndex = -1;
  208. }
  209. instance.getSettings().colHeaders.splice(logicalColumnIndex, amount);
  210. }
  211. grid.adjustRowsAndCols();
  212. selection.refreshBorders(); // it will call render and prepare methods
  213. break;
  214. default:
  215. throw new Error('There is no such action "' + action + '"');
  216. }
  217. if (!keepEmptyRows) {
  218. grid.adjustRowsAndCols(); // makes sure that we did not add rows that will be removed in next refresh
  219. }
  220. },
  221. /**
  222. * Makes sure there are empty rows at the bottom of the table
  223. */
  224. adjustRowsAndCols: function adjustRowsAndCols() {
  225. if (priv.settings.minRows) {
  226. // should I add empty rows to data source to meet minRows?
  227. var rows = instance.countRows();
  228. if (rows < priv.settings.minRows) {
  229. for (var r = 0, minRows = priv.settings.minRows; r < minRows - rows; r++) {
  230. datamap.createRow(instance.countRows(), 1, 'auto');
  231. }
  232. }
  233. }
  234. if (priv.settings.minSpareRows) {
  235. var emptyRows = instance.countEmptyRows(true);
  236. // should I add empty rows to meet minSpareRows?
  237. if (emptyRows < priv.settings.minSpareRows) {
  238. for (; emptyRows < priv.settings.minSpareRows && instance.countSourceRows() < priv.settings.maxRows; emptyRows++) {
  239. datamap.createRow(instance.countRows(), 1, 'auto');
  240. }
  241. }
  242. }
  243. {
  244. var emptyCols = void 0;
  245. // count currently empty cols
  246. if (priv.settings.minCols || priv.settings.minSpareCols) {
  247. emptyCols = instance.countEmptyCols(true);
  248. }
  249. // should I add empty cols to meet minCols?
  250. if (priv.settings.minCols && !priv.settings.columns && instance.countCols() < priv.settings.minCols) {
  251. for (; instance.countCols() < priv.settings.minCols; emptyCols++) {
  252. datamap.createCol(instance.countCols(), 1, 'auto');
  253. }
  254. }
  255. // should I add empty cols to meet minSpareCols?
  256. if (priv.settings.minSpareCols && !priv.settings.columns && instance.dataType === 'array' && emptyCols < priv.settings.minSpareCols) {
  257. for (; emptyCols < priv.settings.minSpareCols && instance.countCols() < priv.settings.maxCols; emptyCols++) {
  258. datamap.createCol(instance.countCols(), 1, 'auto');
  259. }
  260. }
  261. }
  262. var rowCount = instance.countRows();
  263. var colCount = instance.countCols();
  264. if (rowCount === 0 || colCount === 0) {
  265. selection.deselect();
  266. }
  267. if (selection.isSelected()) {
  268. var selectionChanged = false;
  269. var fromRow = priv.selRange.from.row;
  270. var fromCol = priv.selRange.from.col;
  271. var toRow = priv.selRange.to.row;
  272. var toCol = priv.selRange.to.col;
  273. // if selection is outside, move selection to last row
  274. if (fromRow > rowCount - 1) {
  275. fromRow = rowCount - 1;
  276. selectionChanged = true;
  277. if (toRow > fromRow) {
  278. toRow = fromRow;
  279. }
  280. } else if (toRow > rowCount - 1) {
  281. toRow = rowCount - 1;
  282. selectionChanged = true;
  283. if (fromRow > toRow) {
  284. fromRow = toRow;
  285. }
  286. }
  287. // if selection is outside, move selection to last row
  288. if (fromCol > colCount - 1) {
  289. fromCol = colCount - 1;
  290. selectionChanged = true;
  291. if (toCol > fromCol) {
  292. toCol = fromCol;
  293. }
  294. } else if (toCol > colCount - 1) {
  295. toCol = colCount - 1;
  296. selectionChanged = true;
  297. if (fromCol > toCol) {
  298. fromCol = toCol;
  299. }
  300. }
  301. if (selectionChanged) {
  302. instance.selectCell(fromRow, fromCol, toRow, toCol);
  303. }
  304. }
  305. if (instance.view) {
  306. instance.view.wt.wtOverlays.adjustElementsSize();
  307. }
  308. },
  309. /**
  310. * Populate the data from the provided 2d array from the given cell coordinates.
  311. *
  312. * @private
  313. * @param {Object} start Start selection position.
  314. * @param {Array} input 2d data array.
  315. * @param {Object} [end] End selection position (only for drag-down mode).
  316. * @param {String} [source="populateFromArray"] Source information string.
  317. * @param {String} [method="overwrite"] Populate method. Possible options: `shift_down`, `shift_right`, `overwrite`.
  318. * @param {String} direction (left|right|up|down) String specifying the direction.
  319. * @param {Array} deltas The deltas array.
  320. * @returns {Object|undefined} ending td in pasted area (only if any cell was changed).
  321. */
  322. populateFromArray: function populateFromArray(start, input, end, source, method, direction, deltas) {
  323. var r,
  324. rlen,
  325. c,
  326. clen,
  327. setData = [],
  328. current = {};
  329. rlen = input.length;
  330. if (rlen === 0) {
  331. return false;
  332. }
  333. var repeatCol,
  334. repeatRow,
  335. cmax,
  336. rmax,
  337. baseEnd = {
  338. row: end === null ? null : end.row,
  339. col: end === null ? null : end.col
  340. };
  341. /* eslint-disable no-case-declarations */
  342. // insert data with specified pasteMode method
  343. switch (method) {
  344. case 'shift_down':
  345. repeatCol = end ? end.col - start.col + 1 : 0;
  346. repeatRow = end ? end.row - start.row + 1 : 0;
  347. input = (0, _data.translateRowsToColumns)(input);
  348. for (c = 0, clen = input.length, cmax = Math.max(clen, repeatCol); c < cmax; c++) {
  349. if (c < clen) {
  350. var _instance;
  351. for (r = 0, rlen = input[c].length; r < repeatRow - rlen; r++) {
  352. input[c].push(input[c][r % rlen]);
  353. }
  354. input[c].unshift(start.col + c, start.row, 0);
  355. (_instance = instance).spliceCol.apply(_instance, _toConsumableArray(input[c]));
  356. } else {
  357. var _instance2;
  358. input[c % clen][0] = start.col + c;
  359. (_instance2 = instance).spliceCol.apply(_instance2, _toConsumableArray(input[c % clen]));
  360. }
  361. }
  362. break;
  363. case 'shift_right':
  364. repeatCol = end ? end.col - start.col + 1 : 0;
  365. repeatRow = end ? end.row - start.row + 1 : 0;
  366. for (r = 0, rlen = input.length, rmax = Math.max(rlen, repeatRow); r < rmax; r++) {
  367. if (r < rlen) {
  368. var _instance3;
  369. for (c = 0, clen = input[r].length; c < repeatCol - clen; c++) {
  370. input[r].push(input[r][c % clen]);
  371. }
  372. input[r].unshift(start.row + r, start.col, 0);
  373. (_instance3 = instance).spliceRow.apply(_instance3, _toConsumableArray(input[r]));
  374. } else {
  375. var _instance4;
  376. input[r % rlen][0] = start.row + r;
  377. (_instance4 = instance).spliceRow.apply(_instance4, _toConsumableArray(input[r % rlen]));
  378. }
  379. }
  380. break;
  381. case 'overwrite':
  382. default:
  383. // overwrite and other not specified options
  384. current.row = start.row;
  385. current.col = start.col;
  386. var selected = { // selected range
  387. row: end && start ? end.row - start.row + 1 : 1,
  388. col: end && start ? end.col - start.col + 1 : 1
  389. };
  390. var skippedRow = 0;
  391. var skippedColumn = 0;
  392. var pushData = true;
  393. var cellMeta = void 0;
  394. var getInputValue = function getInputValue(row) {
  395. var col = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  396. var rowValue = input[row % input.length];
  397. if (col !== null) {
  398. return rowValue[col % rowValue.length];
  399. }
  400. return rowValue;
  401. };
  402. var rowInputLength = input.length;
  403. var rowSelectionLength = end ? end.row - start.row + 1 : 0;
  404. if (end) {
  405. rlen = rowSelectionLength;
  406. } else {
  407. rlen = Math.max(rowInputLength, rowSelectionLength);
  408. }
  409. for (r = 0; r < rlen; r++) {
  410. if (end && current.row > end.row && rowSelectionLength > rowInputLength || !priv.settings.allowInsertRow && current.row > instance.countRows() - 1 || current.row >= priv.settings.maxRows) {
  411. break;
  412. }
  413. var logicalRow = r - skippedRow;
  414. var colInputLength = getInputValue(logicalRow).length;
  415. var colSelectionLength = end ? end.col - start.col + 1 : 0;
  416. if (end) {
  417. clen = colSelectionLength;
  418. } else {
  419. clen = Math.max(colInputLength, colSelectionLength);
  420. }
  421. current.col = start.col;
  422. cellMeta = instance.getCellMeta(current.row, current.col);
  423. if ((source === 'CopyPaste.paste' || source === 'Autofill.autofill') && cellMeta.skipRowOnPaste) {
  424. skippedRow++;
  425. current.row++;
  426. rlen++;
  427. /* eslint-disable no-continue */
  428. continue;
  429. }
  430. skippedColumn = 0;
  431. for (c = 0; c < clen; c++) {
  432. if (end && current.col > end.col && colSelectionLength > colInputLength || !priv.settings.allowInsertColumn && current.col > instance.countCols() - 1 || current.col >= priv.settings.maxCols) {
  433. break;
  434. }
  435. cellMeta = instance.getCellMeta(current.row, current.col);
  436. if ((source === 'CopyPaste.paste' || source === 'Autofill.fill') && cellMeta.skipColumnOnPaste) {
  437. skippedColumn++;
  438. current.col++;
  439. clen++;
  440. continue;
  441. }
  442. if (cellMeta.readOnly) {
  443. current.col++;
  444. /* eslint-disable no-continue */
  445. continue;
  446. }
  447. var logicalColumn = c - skippedColumn;
  448. var value = getInputValue(logicalRow, logicalColumn);
  449. var orgValue = instance.getDataAtCell(current.row, current.col);
  450. var index = {
  451. row: logicalRow,
  452. col: logicalColumn
  453. };
  454. if (source === 'Autofill.fill') {
  455. var result = instance.runHooks('beforeAutofillInsidePopulate', index, direction, input, deltas, {}, selected);
  456. if (result) {
  457. value = (0, _mixed.isUndefined)(result.value) ? value : result.value;
  458. }
  459. }
  460. if (value !== null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
  461. if (orgValue === null || (typeof orgValue === 'undefined' ? 'undefined' : _typeof(orgValue)) !== 'object') {
  462. pushData = false;
  463. } else {
  464. var orgValueSchema = (0, _object.duckSchema)(orgValue[0] || orgValue);
  465. var valueSchema = (0, _object.duckSchema)(value[0] || value);
  466. /* eslint-disable max-depth */
  467. if ((0, _object.isObjectEquals)(orgValueSchema, valueSchema)) {
  468. value = (0, _object.deepClone)(value);
  469. } else {
  470. pushData = false;
  471. }
  472. }
  473. } else if (orgValue !== null && (typeof orgValue === 'undefined' ? 'undefined' : _typeof(orgValue)) === 'object') {
  474. pushData = false;
  475. }
  476. if (pushData) {
  477. setData.push([current.row, current.col, value]);
  478. }
  479. pushData = true;
  480. current.col++;
  481. }
  482. current.row++;
  483. }
  484. instance.setDataAtCell(setData, null, null, source || 'populateFromArray');
  485. break;
  486. }
  487. }
  488. };
  489. /* eslint-disable no-multi-assign */
  490. this.selection = selection = { // this public assignment is only temporary
  491. inProgress: false,
  492. selectedHeader: {
  493. cols: false,
  494. rows: false
  495. },
  496. /**
  497. * @param {Boolean} [rows=false]
  498. * @param {Boolean} [cols=false]
  499. * @param {Boolean} [corner=false]
  500. */
  501. setSelectedHeaders: function setSelectedHeaders() {
  502. var rows = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  503. var cols = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  504. var corner = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  505. instance.selection.selectedHeader.rows = rows;
  506. instance.selection.selectedHeader.cols = cols;
  507. instance.selection.selectedHeader.corner = corner;
  508. },
  509. /**
  510. * Sets inProgress to `true`. This enables onSelectionEnd and onSelectionEndByProp to function as desired.
  511. */
  512. begin: function begin() {
  513. instance.selection.inProgress = true;
  514. },
  515. /**
  516. * Sets inProgress to `false`. Triggers onSelectionEnd and onSelectionEndByProp.
  517. */
  518. finish: function finish() {
  519. var sel = instance.getSelected();
  520. instance.runHooks('afterSelectionEnd', sel[0], sel[1], sel[2], sel[3]);
  521. instance.runHooks('afterSelectionEndByProp', sel[0], instance.colToProp(sel[1]), sel[2], instance.colToProp(sel[3]));
  522. instance.selection.inProgress = false;
  523. },
  524. /**
  525. * @returns {Boolean}
  526. */
  527. isInProgress: function isInProgress() {
  528. return instance.selection.inProgress;
  529. },
  530. /**
  531. * Starts selection range on given td object.
  532. *
  533. * @param {CellCoords} coords
  534. * @param keepEditorOpened
  535. */
  536. setRangeStart: function setRangeStart(coords, keepEditorOpened) {
  537. instance.runHooks('beforeSetRangeStart', coords);
  538. priv.selRange = new _src.CellRange(coords, coords, coords);
  539. selection.setRangeEnd(coords, null, keepEditorOpened);
  540. },
  541. /**
  542. * Starts selection range on given td object.
  543. *
  544. * @param {CellCoords} coords
  545. * @param keepEditorOpened
  546. */
  547. setRangeStartOnly: function setRangeStartOnly(coords) {
  548. instance.runHooks('beforeSetRangeStartOnly', coords);
  549. priv.selRange = new _src.CellRange(coords, coords, coords);
  550. },
  551. /**
  552. * Ends selection range on given td object.
  553. *
  554. * @param {CellCoords} coords
  555. * @param {Boolean} [scrollToCell=true] If `true`, viewport will be scrolled to range end
  556. * @param {Boolean} [keepEditorOpened] If `true`, cell editor will be still opened after changing selection range
  557. */
  558. setRangeEnd: function setRangeEnd(coords, scrollToCell, keepEditorOpened) {
  559. if (priv.selRange === null) {
  560. return;
  561. }
  562. var disableVisualSelection,
  563. isHeaderSelected = false,
  564. areCoordsPositive = true;
  565. var firstVisibleRow = instance.view.wt.wtTable.getFirstVisibleRow();
  566. var firstVisibleColumn = instance.view.wt.wtTable.getFirstVisibleColumn();
  567. var newRangeCoords = {
  568. row: null,
  569. col: null
  570. };
  571. // trigger handlers
  572. instance.runHooks('beforeSetRangeEnd', coords);
  573. instance.selection.begin();
  574. newRangeCoords.row = coords.row < 0 ? firstVisibleRow : coords.row;
  575. newRangeCoords.col = coords.col < 0 ? firstVisibleColumn : coords.col;
  576. priv.selRange.to = new _src.CellCoords(newRangeCoords.row, newRangeCoords.col);
  577. if (!priv.settings.multiSelect) {
  578. priv.selRange.from = coords;
  579. }
  580. // set up current selection
  581. instance.view.wt.selections.current.clear();
  582. disableVisualSelection = instance.getCellMeta(priv.selRange.highlight.row, priv.selRange.highlight.col).disableVisualSelection;
  583. if (typeof disableVisualSelection === 'string') {
  584. disableVisualSelection = [disableVisualSelection];
  585. }
  586. if (disableVisualSelection === false || Array.isArray(disableVisualSelection) && disableVisualSelection.indexOf('current') === -1) {
  587. instance.view.wt.selections.current.add(priv.selRange.highlight);
  588. }
  589. // set up area selection
  590. instance.view.wt.selections.area.clear();
  591. if ((disableVisualSelection === false || Array.isArray(disableVisualSelection) && disableVisualSelection.indexOf('area') === -1) && selection.isMultiple()) {
  592. instance.view.wt.selections.area.add(priv.selRange.from);
  593. instance.view.wt.selections.area.add(priv.selRange.to);
  594. }
  595. // set up highlight
  596. if (priv.settings.currentHeaderClassName || priv.settings.currentRowClassName || priv.settings.currentColClassName) {
  597. instance.view.wt.selections.highlight.clear();
  598. instance.view.wt.selections.highlight.add(priv.selRange.from);
  599. instance.view.wt.selections.highlight.add(priv.selRange.to);
  600. }
  601. var preventScrolling = (0, _object.createObjectPropListener)('value');
  602. // trigger handlers
  603. instance.runHooks('afterSelection', priv.selRange.from.row, priv.selRange.from.col, priv.selRange.to.row, priv.selRange.to.col, preventScrolling);
  604. instance.runHooks('afterSelectionByProp', priv.selRange.from.row, datamap.colToProp(priv.selRange.from.col), priv.selRange.to.row, datamap.colToProp(priv.selRange.to.col), preventScrolling);
  605. 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) {
  606. isHeaderSelected = true;
  607. }
  608. if (coords.row < 0 || coords.col < 0) {
  609. areCoordsPositive = false;
  610. }
  611. if (preventScrolling.isTouched()) {
  612. scrollToCell = !preventScrolling.value;
  613. }
  614. if (scrollToCell !== false && !isHeaderSelected && areCoordsPositive) {
  615. if (priv.selRange.from && !selection.isMultiple()) {
  616. instance.view.scrollViewport(priv.selRange.from);
  617. } else {
  618. instance.view.scrollViewport(coords);
  619. }
  620. }
  621. if (selection.selectedHeader.rows && selection.selectedHeader.cols) {
  622. (0, _element.addClass)(instance.rootElement, ['ht__selection--rows', 'ht__selection--columns']);
  623. } else if (selection.selectedHeader.rows) {
  624. (0, _element.removeClass)(instance.rootElement, 'ht__selection--columns');
  625. (0, _element.addClass)(instance.rootElement, 'ht__selection--rows');
  626. } else if (selection.selectedHeader.cols) {
  627. (0, _element.removeClass)(instance.rootElement, 'ht__selection--rows');
  628. (0, _element.addClass)(instance.rootElement, 'ht__selection--columns');
  629. } else {
  630. (0, _element.removeClass)(instance.rootElement, ['ht__selection--rows', 'ht__selection--columns']);
  631. }
  632. selection.refreshBorders(null, keepEditorOpened);
  633. },
  634. /**
  635. * Destroys editor, redraws borders around cells, prepares editor.
  636. *
  637. * @param {Boolean} [revertOriginal]
  638. * @param {Boolean} [keepEditor]
  639. */
  640. refreshBorders: function refreshBorders(revertOriginal, keepEditor) {
  641. if (!keepEditor) {
  642. editorManager.destroyEditor(revertOriginal);
  643. }
  644. instance.view.render();
  645. if (selection.isSelected() && !keepEditor) {
  646. editorManager.prepareEditor();
  647. }
  648. },
  649. /**
  650. * Returns information if we have a multiselection.
  651. *
  652. * @returns {Boolean}
  653. */
  654. isMultiple: function isMultiple() {
  655. var isMultiple = !(priv.selRange.to.col === priv.selRange.from.col && priv.selRange.to.row === priv.selRange.from.row),
  656. modifier = instance.runHooks('afterIsMultipleSelection', isMultiple);
  657. if (isMultiple) {
  658. return modifier;
  659. }
  660. },
  661. /**
  662. * Selects cell relative to current cell (if possible).
  663. */
  664. transformStart: function transformStart(rowDelta, colDelta, force, keepEditorOpened) {
  665. var delta = new _src.CellCoords(rowDelta, colDelta),
  666. rowTransformDir = 0,
  667. colTransformDir = 0,
  668. totalRows,
  669. totalCols,
  670. coords,
  671. fixedRowsBottom;
  672. instance.runHooks('modifyTransformStart', delta);
  673. totalRows = instance.countRows();
  674. totalCols = instance.countCols();
  675. fixedRowsBottom = instance.getSettings().fixedRowsBottom;
  676. if (priv.selRange.highlight.row + rowDelta > totalRows - 1) {
  677. if (force && priv.settings.minSpareRows > 0 && !(fixedRowsBottom && priv.selRange.highlight.row >= totalRows - fixedRowsBottom - 1)) {
  678. instance.alter('insert_row', totalRows);
  679. totalRows = instance.countRows();
  680. } else if (priv.settings.autoWrapCol) {
  681. delta.row = 1 - totalRows;
  682. delta.col = priv.selRange.highlight.col + delta.col == totalCols - 1 ? 1 - totalCols : 1;
  683. }
  684. } else if (priv.settings.autoWrapCol && priv.selRange.highlight.row + delta.row < 0 && priv.selRange.highlight.col + delta.col >= 0) {
  685. delta.row = totalRows - 1;
  686. delta.col = priv.selRange.highlight.col + delta.col == 0 ? totalCols - 1 : -1;
  687. }
  688. if (priv.selRange.highlight.col + delta.col > totalCols - 1) {
  689. if (force && priv.settings.minSpareCols > 0) {
  690. instance.alter('insert_col', totalCols);
  691. totalCols = instance.countCols();
  692. } else if (priv.settings.autoWrapRow) {
  693. delta.row = priv.selRange.highlight.row + delta.row == totalRows - 1 ? 1 - totalRows : 1;
  694. delta.col = 1 - totalCols;
  695. }
  696. } else if (priv.settings.autoWrapRow && priv.selRange.highlight.col + delta.col < 0 && priv.selRange.highlight.row + delta.row >= 0) {
  697. delta.row = priv.selRange.highlight.row + delta.row == 0 ? totalRows - 1 : -1;
  698. delta.col = totalCols - 1;
  699. }
  700. coords = new _src.CellCoords(priv.selRange.highlight.row + delta.row, priv.selRange.highlight.col + delta.col);
  701. if (coords.row < 0) {
  702. rowTransformDir = -1;
  703. coords.row = 0;
  704. } else if (coords.row > 0 && coords.row >= totalRows) {
  705. rowTransformDir = 1;
  706. coords.row = totalRows - 1;
  707. }
  708. if (coords.col < 0) {
  709. colTransformDir = -1;
  710. coords.col = 0;
  711. } else if (coords.col > 0 && coords.col >= totalCols) {
  712. colTransformDir = 1;
  713. coords.col = totalCols - 1;
  714. }
  715. instance.runHooks('afterModifyTransformStart', coords, rowTransformDir, colTransformDir);
  716. selection.setRangeStart(coords, keepEditorOpened);
  717. },
  718. /**
  719. * Sets selection end cell relative to current selection end cell (if possible).
  720. */
  721. transformEnd: function transformEnd(rowDelta, colDelta) {
  722. var delta = new _src.CellCoords(rowDelta, colDelta),
  723. rowTransformDir = 0,
  724. colTransformDir = 0,
  725. totalRows,
  726. totalCols,
  727. coords;
  728. instance.runHooks('modifyTransformEnd', delta);
  729. totalRows = instance.countRows();
  730. totalCols = instance.countCols();
  731. coords = new _src.CellCoords(priv.selRange.to.row + delta.row, priv.selRange.to.col + delta.col);
  732. if (coords.row < 0) {
  733. rowTransformDir = -1;
  734. coords.row = 0;
  735. } else if (coords.row > 0 && coords.row >= totalRows) {
  736. rowTransformDir = 1;
  737. coords.row = totalRows - 1;
  738. }
  739. if (coords.col < 0) {
  740. colTransformDir = -1;
  741. coords.col = 0;
  742. } else if (coords.col > 0 && coords.col >= totalCols) {
  743. colTransformDir = 1;
  744. coords.col = totalCols - 1;
  745. }
  746. instance.runHooks('afterModifyTransformEnd', coords, rowTransformDir, colTransformDir);
  747. selection.setRangeEnd(coords, true);
  748. },
  749. /**
  750. * Returns `true` if currently there is a selection on screen, `false` otherwise.
  751. *
  752. * @returns {Boolean}
  753. */
  754. isSelected: function isSelected() {
  755. return priv.selRange !== null;
  756. },
  757. /**
  758. * Returns `true` if coords is within current selection coords.
  759. *
  760. * @param {CellCoords} coords
  761. * @returns {Boolean}
  762. */
  763. inInSelection: function inInSelection(coords) {
  764. if (!selection.isSelected()) {
  765. return false;
  766. }
  767. return priv.selRange.includes(coords);
  768. },
  769. /**
  770. * Deselects all selected cells
  771. */
  772. deselect: function deselect() {
  773. if (!selection.isSelected()) {
  774. return;
  775. }
  776. instance.selection.inProgress = false; // needed by HT inception
  777. priv.selRange = null;
  778. instance.view.wt.selections.current.clear();
  779. instance.view.wt.selections.area.clear();
  780. if (priv.settings.currentHeaderClassName || priv.settings.currentRowClassName || priv.settings.currentColClassName) {
  781. instance.view.wt.selections.highlight.clear();
  782. }
  783. editorManager.destroyEditor();
  784. selection.refreshBorders();
  785. (0, _element.removeClass)(instance.rootElement, ['ht__selection--rows', 'ht__selection--columns']);
  786. instance.runHooks('afterDeselect');
  787. },
  788. /**
  789. * Select all cells
  790. */
  791. selectAll: function selectAll() {
  792. if (!priv.settings.multiSelect) {
  793. return;
  794. }
  795. selection.setSelectedHeaders(true, true, true);
  796. selection.setRangeStart(new _src.CellCoords(0, 0));
  797. selection.setRangeEnd(new _src.CellCoords(instance.countRows() - 1, instance.countCols() - 1), false);
  798. },
  799. /**
  800. * Deletes data from selected cells
  801. */
  802. empty: function empty() {
  803. if (!selection.isSelected()) {
  804. return;
  805. }
  806. var topLeft = priv.selRange.getTopLeftCorner();
  807. var bottomRight = priv.selRange.getBottomRightCorner();
  808. var r,
  809. c,
  810. changes = [];
  811. for (r = topLeft.row; r <= bottomRight.row; r++) {
  812. for (c = topLeft.col; c <= bottomRight.col; c++) {
  813. if (!instance.getCellMeta(r, c).readOnly) {
  814. changes.push([r, c, '']);
  815. }
  816. }
  817. }
  818. instance.setDataAtCell(changes);
  819. }
  820. };
  821. this.init = function () {
  822. dataSource.setData(priv.settings.data);
  823. instance.runHooks('beforeInit');
  824. if ((0, _browser.isMobileBrowser)()) {
  825. (0, _element.addClass)(instance.rootElement, 'mobile');
  826. }
  827. this.updateSettings(priv.settings, true);
  828. this.view = new _tableView2.default(this);
  829. editorManager = new _editorManager2.default(instance, priv, selection, datamap);
  830. this.forceFullRender = true; // used when data was changed
  831. instance.runHooks('init');
  832. this.view.render();
  833. if (_typeof(priv.firstRun) === 'object') {
  834. instance.runHooks('afterChange', priv.firstRun[0], priv.firstRun[1]);
  835. priv.firstRun = false;
  836. }
  837. instance.runHooks('afterInit');
  838. };
  839. function ValidatorsQueue() {
  840. // moved this one level up so it can be used in any function here. Probably this should be moved to a separate file
  841. var resolved = false;
  842. return {
  843. validatorsInQueue: 0,
  844. valid: true,
  845. addValidatorToQueue: function addValidatorToQueue() {
  846. this.validatorsInQueue++;
  847. resolved = false;
  848. },
  849. removeValidatorFormQueue: function removeValidatorFormQueue() {
  850. this.validatorsInQueue = this.validatorsInQueue - 1 < 0 ? 0 : this.validatorsInQueue - 1;
  851. this.checkIfQueueIsEmpty();
  852. },
  853. onQueueEmpty: function onQueueEmpty(valid) {},
  854. checkIfQueueIsEmpty: function checkIfQueueIsEmpty() {
  855. if (this.validatorsInQueue == 0 && resolved == false) {
  856. resolved = true;
  857. this.onQueueEmpty(this.valid);
  858. }
  859. }
  860. };
  861. }
  862. function validateChanges(changes, source, callback) {
  863. var waitingForValidator = new ValidatorsQueue();
  864. waitingForValidator.onQueueEmpty = resolve;
  865. for (var i = changes.length - 1; i >= 0; i--) {
  866. if (changes[i] === null) {
  867. changes.splice(i, 1);
  868. } else {
  869. var row = changes[i][0];
  870. var col = datamap.propToCol(changes[i][1]);
  871. var cellProperties = instance.getCellMeta(row, col);
  872. if (cellProperties.type === 'numeric' && typeof changes[i][3] === 'string') {
  873. if (changes[i][3].length > 0 && (/^-?[\d\s]*(\.|,)?\d*$/.test(changes[i][3]) || cellProperties.format)) {
  874. var len = changes[i][3].length;
  875. if ((0, _mixed.isUndefined)(cellProperties.language)) {
  876. _numbro2.default.culture('en-US');
  877. } else if (changes[i][3].indexOf('.') === len - 3 && changes[i][3].indexOf(',') === -1) {
  878. // this input in format XXXX.XX is likely to come from paste. Let's parse it using international rules
  879. _numbro2.default.culture('en-US');
  880. } else {
  881. _numbro2.default.culture(cellProperties.language);
  882. }
  883. var _numbro$cultureData = _numbro2.default.cultureData(_numbro2.default.culture()),
  884. delimiters = _numbro$cultureData.delimiters;
  885. // try to parse to float - https://github.com/foretagsplatsen/numbro/pull/183
  886. if (_numbro2.default.validate(changes[i][3]) && !isNaN(changes[i][3])) {
  887. changes[i][3] = parseFloat(changes[i][3]);
  888. } else {
  889. changes[i][3] = (0, _numbro2.default)().unformat(changes[i][3]) || changes[i][3];
  890. }
  891. }
  892. }
  893. /* eslint-disable no-loop-func */
  894. if (instance.getCellValidator(cellProperties)) {
  895. waitingForValidator.addValidatorToQueue();
  896. instance.validateCell(changes[i][3], cellProperties, function (i, cellProperties) {
  897. return function (result) {
  898. if (typeof result !== 'boolean') {
  899. throw new Error('Validation error: result is not boolean');
  900. }
  901. if (result === false && cellProperties.allowInvalid === false) {
  902. changes.splice(i, 1); // cancel the change
  903. cellProperties.valid = true; // we cancelled the change, so cell value is still valid
  904. var cell = instance.getCell(cellProperties.row, cellProperties.col);
  905. (0, _element.removeClass)(cell, instance.getSettings().invalidCellClassName);
  906. --i;
  907. }
  908. waitingForValidator.removeValidatorFormQueue();
  909. };
  910. }(i, cellProperties), source);
  911. }
  912. }
  913. }
  914. waitingForValidator.checkIfQueueIsEmpty();
  915. function resolve() {
  916. var beforeChangeResult;
  917. if (changes.length) {
  918. beforeChangeResult = instance.runHooks('beforeChange', changes, source);
  919. if ((0, _function.isFunction)(beforeChangeResult)) {
  920. 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).');
  921. } else if (beforeChangeResult === false) {
  922. changes.splice(0, changes.length); // invalidate all changes (remove everything from array)
  923. }
  924. }
  925. callback(); // called when async validators are resolved and beforeChange was not async
  926. }
  927. }
  928. /**
  929. * Internal function to apply changes. Called after validateChanges
  930. *
  931. * @private
  932. * @param {Array} changes Array in form of [row, prop, oldValue, newValue]
  933. * @param {String} source String that identifies how this change will be described in changes array (useful in onChange callback)
  934. * @fires Hooks#beforeChangeRender
  935. * @fires Hooks#afterChange
  936. */
  937. function applyChanges(changes, source) {
  938. var i = changes.length - 1;
  939. if (i < 0) {
  940. return;
  941. }
  942. for (; i >= 0; i--) {
  943. var skipThisChange = false;
  944. if (changes[i] === null) {
  945. changes.splice(i, 1);
  946. /* eslint-disable no-continue */
  947. continue;
  948. }
  949. if (changes[i][2] == null && changes[i][3] == null) {
  950. /* eslint-disable no-continue */
  951. continue;
  952. }
  953. if (priv.settings.allowInsertRow) {
  954. while (changes[i][0] > instance.countRows() - 1) {
  955. var numberOfCreatedRows = datamap.createRow(void 0, void 0, source);
  956. if (numberOfCreatedRows === 0) {
  957. skipThisChange = true;
  958. break;
  959. }
  960. }
  961. }
  962. if (skipThisChange) {
  963. /* eslint-disable no-continue */
  964. continue;
  965. }
  966. if (instance.dataType === 'array' && (!priv.settings.columns || priv.settings.columns.length === 0) && priv.settings.allowInsertColumn) {
  967. while (datamap.propToCol(changes[i][1]) > instance.countCols() - 1) {
  968. datamap.createCol(void 0, void 0, source);
  969. }
  970. }
  971. datamap.set(changes[i][0], changes[i][1], changes[i][3]);
  972. }
  973. instance.forceFullRender = true; // used when data was changed
  974. grid.adjustRowsAndCols();
  975. instance.runHooks('beforeChangeRender', changes, source);
  976. selection.refreshBorders(null, true);
  977. instance.view.wt.wtOverlays.adjustElementsSize();
  978. instance.runHooks('afterChange', changes, source || 'edit');
  979. var activeEditor = instance.getActiveEditor();
  980. if (activeEditor && (0, _mixed.isDefined)(activeEditor.refreshValue)) {
  981. activeEditor.refreshValue();
  982. }
  983. }
  984. this.validateCell = function (value, cellProperties, callback, source) {
  985. var validator = instance.getCellValidator(cellProperties);
  986. function done(valid) {
  987. var col = cellProperties.visualCol,
  988. row = cellProperties.visualRow,
  989. td = instance.getCell(row, col, true);
  990. if (td && td.nodeName != 'TH') {
  991. instance.view.wt.wtSettings.settings.cellRenderer(row, col, td);
  992. }
  993. callback(valid);
  994. }
  995. if ((0, _mixed.isRegExp)(validator)) {
  996. validator = function (validator) {
  997. return function (value, callback) {
  998. callback(validator.test(value));
  999. };
  1000. }(validator);
  1001. }
  1002. if ((0, _function.isFunction)(validator)) {
  1003. value = instance.runHooks('beforeValidate', value, cellProperties.visualRow, cellProperties.prop, source);
  1004. // To provide consistent behaviour, validation should be always asynchronous
  1005. instance._registerTimeout(setTimeout(function () {
  1006. validator.call(cellProperties, value, function (valid) {
  1007. valid = instance.runHooks('afterValidate', valid, value, cellProperties.visualRow, cellProperties.prop, source);
  1008. cellProperties.valid = valid;
  1009. done(valid);
  1010. instance.runHooks('postAfterValidate', valid, value, cellProperties.visualRow, cellProperties.prop, source);
  1011. });
  1012. }, 0));
  1013. } else {
  1014. // resolve callback even if validator function was not found
  1015. instance._registerTimeout(setTimeout(function () {
  1016. cellProperties.valid = true;
  1017. done(cellProperties.valid);
  1018. }, 0));
  1019. }
  1020. };
  1021. function setDataInputToArray(row, propOrCol, value) {
  1022. if ((typeof row === 'undefined' ? 'undefined' : _typeof(row)) === 'object') {
  1023. // is it an array of changes
  1024. return row;
  1025. }
  1026. return [[row, propOrCol, value]];
  1027. }
  1028. /**
  1029. * @description
  1030. * Set new value to a cell. To change many cells at once, pass an array of `changes` in format `[[row, col, value], ...]` as
  1031. * the only parameter. `col` is the index of a __visible__ column (note that if columns were reordered,
  1032. * the current visible order will be used). `source` is a flag for before/afterChange events. If you pass only array of
  1033. * changes then `source` could be set as second parameter.
  1034. *
  1035. * @memberof Core#
  1036. * @function setDataAtCell
  1037. * @param {Number|Array} row Row index or array of changes in format `[[row, col, value], ...]`.
  1038. * @param {Number} col Column index.
  1039. * @param {String} value New value.
  1040. * @param {String} [source] String that identifies how this change will be described in the changes array (useful in onAfterChange or onBeforeChange callback).
  1041. */
  1042. this.setDataAtCell = function (row, col, value, source) {
  1043. var input = setDataInputToArray(row, col, value),
  1044. i,
  1045. ilen,
  1046. changes = [],
  1047. prop;
  1048. for (i = 0, ilen = input.length; i < ilen; i++) {
  1049. if (_typeof(input[i]) !== 'object') {
  1050. throw new Error('Method `setDataAtCell` accepts row number or changes array of arrays as its first parameter');
  1051. }
  1052. if (typeof input[i][1] !== 'number') {
  1053. throw new Error('Method `setDataAtCell` accepts row and column number as its parameters. If you want to use object property name, use method `setDataAtRowProp`');
  1054. }
  1055. prop = datamap.colToProp(input[i][1]);
  1056. changes.push([input[i][0], prop, dataSource.getAtCell(recordTranslator.toPhysicalRow(input[i][0]), input[i][1]), input[i][2]]);
  1057. }
  1058. if (!source && (typeof row === 'undefined' ? 'undefined' : _typeof(row)) === 'object') {
  1059. source = col;
  1060. }
  1061. instance.runHooks('afterSetDataAtCell', changes, source);
  1062. validateChanges(changes, source, function () {
  1063. applyChanges(changes, source);
  1064. });
  1065. };
  1066. /**
  1067. * @description
  1068. * Set new value to a cell. To change many cells at once, pass an array of `changes` in format `[[row, prop, value], ...]` as
  1069. * the only parameter. `prop` is the name of the object property (e.g. `first.name`). `source` is a flag for before/afterChange events.
  1070. * If you pass only array of changes then `source` could be set as second parameter.
  1071. *
  1072. * @memberof Core#
  1073. * @function setDataAtRowProp
  1074. * @param {Number|Array} row Row index or array of changes in format `[[row, prop, value], ...]`.
  1075. * @param {String} prop Property name or the source string.
  1076. * @param {String} value Value to be set.
  1077. * @param {String} [source] String that identifies how this change will be described in changes array (useful in onChange callback).
  1078. */
  1079. this.setDataAtRowProp = function (row, prop, value, source) {
  1080. var input = setDataInputToArray(row, prop, value),
  1081. i,
  1082. ilen,
  1083. changes = [];
  1084. for (i = 0, ilen = input.length; i < ilen; i++) {
  1085. changes.push([input[i][0], input[i][1], dataSource.getAtCell(recordTranslator.toPhysicalRow(input[i][0]), input[i][1]), input[i][2]]);
  1086. }
  1087. if (!source && (typeof row === 'undefined' ? 'undefined' : _typeof(row)) === 'object') {
  1088. source = prop;
  1089. }
  1090. instance.runHooks('afterSetDataAtRowProp', changes, source);
  1091. validateChanges(changes, source, function () {
  1092. applyChanges(changes, source);
  1093. });
  1094. };
  1095. /**
  1096. * Listen to the keyboard input on document body.
  1097. *
  1098. * @memberof Core#
  1099. * @function listen
  1100. * @since 0.11
  1101. */
  1102. this.listen = function () {
  1103. activeGuid = instance.guid;
  1104. };
  1105. /**
  1106. * Stop listening to keyboard input on the document body.
  1107. *
  1108. * @memberof Core#
  1109. * @function unlisten
  1110. * @since 0.11
  1111. */
  1112. this.unlisten = function () {
  1113. activeGuid = null;
  1114. };
  1115. /**
  1116. * Returns `true` if the current Handsontable instance is listening to keyboard input on document body.
  1117. *
  1118. * @memberof Core#
  1119. * @function isListening
  1120. * @since 0.11
  1121. * @returns {Boolean} `true` if the instance is listening, `false` otherwise.
  1122. */
  1123. this.isListening = function () {
  1124. return activeGuid === instance.guid;
  1125. };
  1126. /**
  1127. * Destroys the current editor, renders and selects the current cell.
  1128. *
  1129. * @memberof Core#
  1130. * @function destroyEditor
  1131. * @param {Boolean} [revertOriginal] If != `true`, edited data is saved. Otherwise the previous value is restored.
  1132. */
  1133. this.destroyEditor = function (revertOriginal) {
  1134. selection.refreshBorders(revertOriginal);
  1135. };
  1136. /**
  1137. * Populate cells at position with 2D input array (e.g. `[[1, 2], [3, 4]]`).
  1138. * Use `endRow`, `endCol` when you want to cut input when a certain row is reached.
  1139. * Optional `source` parameter (default value "populateFromArray") is used to identify this call in the resulting events (beforeChange, afterChange).
  1140. * Optional `populateMethod` parameter (default value "overwrite", possible values "shift_down" and "shift_right")
  1141. * has the same effect as pasteMode option {@link Options#pasteMode}
  1142. *
  1143. * @memberof Core#
  1144. * @function populateFromArray
  1145. * @since 0.9.0
  1146. * @param {Number} row Start row
  1147. * @param {Number} col Start column
  1148. * @param {Array} input 2d array
  1149. * @param {Number} [endRow] End row (use when you want to cut input when certain row is reached)
  1150. * @param {Number} [endCol] End column (use when you want to cut input when certain column is reached)
  1151. * @param {String} [source="populateFromArray"] Source string.
  1152. * @param {String} [method="overwrite"] Populate method. Possible options: `shift_down`, `shift_right`, `overwrite`.
  1153. * @param {String} direction Populate direction. (left|right|up|down)
  1154. * @param {Array} deltas Deltas array.
  1155. * @returns {Object|undefined} The ending TD element in pasted area (only if any cells were changed).
  1156. */
  1157. this.populateFromArray = function (row, col, input, endRow, endCol, source, method, direction, deltas) {
  1158. var c;
  1159. if (!((typeof input === 'undefined' ? 'undefined' : _typeof(input)) === 'object' && _typeof(input[0]) === 'object')) {
  1160. 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
  1161. }
  1162. c = typeof endRow === 'number' ? new _src.CellCoords(endRow, endCol) : null;
  1163. return grid.populateFromArray(new _src.CellCoords(row, col), input, c, source, method, direction, deltas);
  1164. };
  1165. /**
  1166. * Adds/removes data from the column. This function is modelled after Array.splice.
  1167. * Parameter `col` is the index of the column in which do you want to do splice.
  1168. * Parameter `index` is the row index at which to start changing the array.
  1169. * If negative, will begin that many elements from the end. Parameter `amount`, is the number of the old array elements to remove.
  1170. * If the amount is 0, no elements are removed. Fourth and further parameters are the `elements` to add to the array.
  1171. * If you don't specify any elements, spliceCol simply removes elements from the array.
  1172. * {@link DataMap#spliceCol}
  1173. *
  1174. * @memberof Core#
  1175. * @function spliceCol
  1176. * @since 0.9-beta2
  1177. * @param {Number} col Index of the column in which do you want to do splice.
  1178. * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
  1179. * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed.
  1180. * @param {*} [elements] The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array.
  1181. */
  1182. this.spliceCol = function (col, index, amount /* , elements... */) {
  1183. var _datamap;
  1184. return (_datamap = datamap).spliceCol.apply(_datamap, arguments);
  1185. };
  1186. /**
  1187. * Adds/removes data from the row. This function works is modelled after Array.splice.
  1188. * Parameter `row` is the index of row in which do you want to do splice.
  1189. * Parameter `index` is the column index at which to start changing the array.
  1190. * If negative, will begin that many elements from the end. Parameter `amount`, is the number of old array elements to remove.
  1191. * If the amount is 0, no elements are removed. Fourth and further parameters are the `elements` to add to the array.
  1192. * If you don't specify any elements, spliceCol simply removes elements from the array.
  1193. * {@link DataMap#spliceRow}
  1194. *
  1195. * @memberof Core#
  1196. * @function spliceRow
  1197. * @since 0.11
  1198. * @param {Number} row Index of column in which do you want to do splice.
  1199. * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
  1200. * @param {Number} amount An integer indicating the number of old array elements to remove. If amount is 0, no elements are removed.
  1201. * @param {*} [elements] The elements to add to the array. If you don't specify any elements, spliceCol simply removes elements from the array.
  1202. */
  1203. this.spliceRow = function (row, index, amount /* , elements... */) {
  1204. var _datamap2;
  1205. return (_datamap2 = datamap).spliceRow.apply(_datamap2, arguments);
  1206. };
  1207. /**
  1208. * Returns indexes of the currently selected cells as an array `[startRow, startCol, endRow, endCol]`.
  1209. *
  1210. * Start row and start col are the coordinates of the active cell (where the selection was started).
  1211. *
  1212. * @memberof Core#
  1213. * @function getSelected
  1214. * @returns {Array} Array of the selection's indexes.
  1215. */
  1216. this.getSelected = function () {
  1217. // https://github.com/handsontable/handsontable/issues/44 //cjl
  1218. if (selection.isSelected()) {
  1219. return [priv.selRange.from.row, priv.selRange.from.col, priv.selRange.to.row, priv.selRange.to.col];
  1220. }
  1221. };
  1222. /**
  1223. * Returns the current selection as a CellRange object.
  1224. *
  1225. * @memberof Core#
  1226. * @function getSelectedRange
  1227. * @since 0.11
  1228. * @returns {CellRange} Selected range object or undefined` if there is no selection.
  1229. */
  1230. this.getSelectedRange = function () {
  1231. // https://github.com/handsontable/handsontable/issues/44 //cjl
  1232. if (selection.isSelected()) {
  1233. return priv.selRange;
  1234. }
  1235. };
  1236. /**
  1237. * Rerender the table.
  1238. *
  1239. * @memberof Core#
  1240. * @function render
  1241. */
  1242. this.render = function () {
  1243. if (instance.view) {
  1244. instance.renderCall = true;
  1245. instance.forceFullRender = true; // used when data was changed
  1246. selection.refreshBorders(null, true);
  1247. }
  1248. };
  1249. /**
  1250. * Reset all cells in the grid to contain data from the data array.
  1251. *
  1252. * @memberof Core#
  1253. * @function loadData
  1254. * @param {Array} data Array of arrays or array of objects containing data.
  1255. * @fires Hooks#afterLoadData
  1256. * @fires Hooks#afterChange
  1257. */
  1258. this.loadData = function (data) {
  1259. if (Array.isArray(priv.settings.dataSchema)) {
  1260. instance.dataType = 'array';
  1261. } else if ((0, _function.isFunction)(priv.settings.dataSchema)) {
  1262. instance.dataType = 'function';
  1263. } else {
  1264. instance.dataType = 'object';
  1265. }
  1266. if (datamap) {
  1267. datamap.destroy();
  1268. }
  1269. datamap = new _dataMap2.default(instance, priv, GridSettings);
  1270. if ((typeof data === 'undefined' ? 'undefined' : _typeof(data)) === 'object' && data !== null) {
  1271. if (!(data.push && data.splice)) {
  1272. // check if data is array. Must use duck-type check so Backbone Collections also pass it
  1273. // when data is not an array, attempt to make a single-row array of it
  1274. data = [data];
  1275. }
  1276. } else if (data === null) {
  1277. data = [];
  1278. var row;
  1279. var r = 0;
  1280. var rlen = 0;
  1281. var dataSchema = datamap.getSchema();
  1282. for (r = 0, rlen = priv.settings.startRows; r < rlen; r++) {
  1283. if ((instance.dataType === 'object' || instance.dataType === 'function') && priv.settings.dataSchema) {
  1284. row = (0, _object.deepClone)(dataSchema);
  1285. data.push(row);
  1286. } else if (instance.dataType === 'array') {
  1287. row = (0, _object.deepClone)(dataSchema[0]);
  1288. data.push(row);
  1289. } else {
  1290. row = [];
  1291. for (var c = 0, clen = priv.settings.startCols; c < clen; c++) {
  1292. row.push(null);
  1293. }
  1294. data.push(row);
  1295. }
  1296. }
  1297. } else {
  1298. throw new Error('loadData only accepts array of objects or array of arrays (' + (typeof data === 'undefined' ? 'undefined' : _typeof(data)) + ' given)');
  1299. }
  1300. priv.isPopulated = false;
  1301. GridSettings.prototype.data = data;
  1302. if (Array.isArray(data[0])) {
  1303. instance.dataType = 'array';
  1304. }
  1305. datamap.dataSource = data;
  1306. dataSource.data = data;
  1307. dataSource.dataType = instance.dataType;
  1308. dataSource.colToProp = datamap.colToProp.bind(datamap);
  1309. dataSource.propToCol = datamap.propToCol.bind(datamap);
  1310. clearCellSettingCache();
  1311. grid.adjustRowsAndCols();
  1312. instance.runHooks('afterLoadData', priv.firstRun);
  1313. if (priv.firstRun) {
  1314. priv.firstRun = [null, 'loadData'];
  1315. } else {
  1316. instance.runHooks('afterChange', null, 'loadData');
  1317. instance.render();
  1318. }
  1319. priv.isPopulated = true;
  1320. function clearCellSettingCache() {
  1321. priv.cellSettings.length = 0;
  1322. }
  1323. };
  1324. /**
  1325. * Returns the current data object (the same one that was passed by `data` configuration option or `loadData` method,
  1326. * unless the `modifyRow` hook was used to trim some of the rows. If that's the case - use the {@link Core#getSourceData} method.).
  1327. * Optionally you can provide cell range by defining `row`, `col`, `row2`, `col2` to get only a fragment of grid data.
  1328. *
  1329. * Note: getData functionality changed with the release of version 0.20. If you're looking for the previous functionality,
  1330. * you should use the {@link Core#getSourceData} method.
  1331. *
  1332. * @memberof Core#
  1333. * @function getData
  1334. * @param {Number} [r] From row.
  1335. * @param {Number} [c] From column.
  1336. * @param {Number} [r2] To row.
  1337. * @param {Number} [c2] To column.
  1338. * @returns {Array} Array with the data.
  1339. */
  1340. this.getData = function (r, c, r2, c2) {
  1341. if ((0, _mixed.isUndefined)(r)) {
  1342. return datamap.getAll();
  1343. }
  1344. return datamap.getRange(new _src.CellCoords(r, c), new _src.CellCoords(r2, c2), datamap.DESTINATION_RENDERER);
  1345. };
  1346. /**
  1347. * Returns a string value of the selected range. Each column is separated by tab, each row is separated by a new line character.
  1348. * {@link DataMap#getCopyableText}
  1349. *
  1350. * @memberof Core#
  1351. * @function getCopyableText
  1352. * @since 0.11
  1353. * @param {Number} startRow From row.
  1354. * @param {Number} startCol From column.
  1355. * @param {Number} endRow To row.
  1356. * @param {Number} endCol To column.
  1357. * @returns {String}
  1358. */
  1359. this.getCopyableText = function (startRow, startCol, endRow, endCol) {
  1360. return datamap.getCopyableText(new _src.CellCoords(startRow, startCol), new _src.CellCoords(endRow, endCol));
  1361. };
  1362. /**
  1363. * Returns the data's copyable value at specified row and column index ({@link DataMap#getCopyable}).
  1364. *
  1365. * @memberof Core#
  1366. * @function getCopyableData
  1367. * @since 0.19.0
  1368. * @param {Number} row Row index.
  1369. * @param {Number} column Column index.
  1370. * @returns {String}
  1371. */
  1372. this.getCopyableData = function (row, column) {
  1373. return datamap.getCopyable(row, datamap.colToProp(column));
  1374. };
  1375. /**
  1376. * Returns schema provided by constructor settings. If it doesn't exist then it returns the schema based on the data
  1377. * structure in the first row.
  1378. *
  1379. * @memberof Core#
  1380. * @function getSchema
  1381. * @since 0.13.2
  1382. * @returns {Object} Schema object.
  1383. */
  1384. this.getSchema = function () {
  1385. return datamap.getSchema();
  1386. };
  1387. /**
  1388. * Use it if you need to change configuration after initialization. The `settings` parameter is an object containing the new
  1389. * settings, declared the same way as in the initial settings object.
  1390. * Note, that although the `updateSettings` method doesn't overwrite the previously declared settings, it might reset
  1391. * the settings made post-initialization. (for example - ignore changes made using the columnResize feature).
  1392. *
  1393. * @memberof Core#
  1394. * @function updateSettings
  1395. * @param {Object} settings New settings object.
  1396. * @param {Boolean} init
  1397. * @example
  1398. * ```js
  1399. * hot.updateSettings({
  1400. * contextMenu: true,
  1401. * colHeaders: true,
  1402. * fixedRowsTop: 2
  1403. * });
  1404. * ```
  1405. * @fires Hooks#afterCellMetaReset
  1406. * @fires Hooks#afterUpdateSettings
  1407. */
  1408. this.updateSettings = function (settings, init) {
  1409. var columnsAsFunc = false;
  1410. var i = void 0;
  1411. var j = void 0;
  1412. var clen = void 0;
  1413. if ((0, _mixed.isDefined)(settings.rows)) {
  1414. throw new Error('"rows" setting is no longer supported. do you mean startRows, minRows or maxRows?');
  1415. }
  1416. if ((0, _mixed.isDefined)(settings.cols)) {
  1417. throw new Error('"cols" setting is no longer supported. do you mean startCols, minCols or maxCols?');
  1418. }
  1419. for (i in settings) {
  1420. if (i === 'data') {
  1421. /* eslint-disable no-continue */
  1422. continue; // loadData will be triggered later
  1423. } else if (_pluginHooks2.default.getSingleton().getRegistered().indexOf(i) > -1) {
  1424. if ((0, _function.isFunction)(settings[i]) || Array.isArray(settings[i])) {
  1425. settings[i].initialHook = true;
  1426. instance.addHook(i, settings[i]);
  1427. }
  1428. } else if (!init && (0, _object.hasOwnProperty)(settings, i)) {
  1429. // Update settings
  1430. GridSettings.prototype[i] = settings[i];
  1431. }
  1432. }
  1433. // Load data or create data map
  1434. if (settings.data === void 0 && priv.settings.data === void 0) {
  1435. instance.loadData(null); // data source created just now
  1436. } else if (settings.data !== void 0) {
  1437. instance.loadData(settings.data); // data source given as option
  1438. } else if (settings.columns !== void 0) {
  1439. datamap.createMap();
  1440. }
  1441. clen = instance.countCols();
  1442. var columnSetting = settings.columns || GridSettings.prototype.columns;
  1443. // Init columns constructors configuration
  1444. if (columnSetting && (0, _function.isFunction)(columnSetting)) {
  1445. clen = instance.countSourceCols();
  1446. columnsAsFunc = true;
  1447. }
  1448. // Clear cellSettings cache
  1449. if (settings.cell !== void 0 || settings.cells !== void 0 || settings.columns !== void 0) {
  1450. priv.cellSettings.length = 0;
  1451. }
  1452. if (clen > 0) {
  1453. var proto = void 0;
  1454. var column = void 0;
  1455. for (i = 0, j = 0; i < clen; i++) {
  1456. if (columnsAsFunc && !columnSetting(i)) {
  1457. /* eslint-disable no-continue */
  1458. continue;
  1459. }
  1460. priv.columnSettings[j] = (0, _setting.columnFactory)(GridSettings, priv.columnsSettingConflicts);
  1461. // shortcut for prototype
  1462. proto = priv.columnSettings[j].prototype;
  1463. // Use settings provided by user
  1464. if (columnSetting) {
  1465. if (columnsAsFunc) {
  1466. column = columnSetting(i);
  1467. } else {
  1468. column = columnSetting[j];
  1469. }
  1470. if (column) {
  1471. (0, _object.extend)(proto, column);
  1472. (0, _object.extend)(proto, expandType(column));
  1473. }
  1474. }
  1475. j++;
  1476. }
  1477. }
  1478. if ((0, _mixed.isDefined)(settings.cell)) {
  1479. for (var key in settings.cell) {
  1480. if ((0, _object.hasOwnProperty)(settings.cell, key)) {
  1481. var cell = settings.cell[key];
  1482. instance.setCellMetaObject(cell.row, cell.col, cell);
  1483. }
  1484. }
  1485. }
  1486. instance.runHooks('afterCellMetaReset');
  1487. if ((0, _mixed.isDefined)(settings.className)) {
  1488. if (GridSettings.prototype.className) {
  1489. (0, _element.removeClass)(instance.rootElement, GridSettings.prototype.className);
  1490. }
  1491. if (settings.className) {
  1492. (0, _element.addClass)(instance.rootElement, settings.className);
  1493. }
  1494. }
  1495. var currentHeight = instance.rootElement.style.height;
  1496. if (currentHeight !== '') {
  1497. currentHeight = parseInt(instance.rootElement.style.height, 10);
  1498. }
  1499. var height = settings.height;
  1500. if ((0, _function.isFunction)(height)) {
  1501. height = height();
  1502. }
  1503. if (init) {
  1504. var initialStyle = instance.rootElement.getAttribute('style');
  1505. if (initialStyle) {
  1506. instance.rootElement.setAttribute('data-initialstyle', instance.rootElement.getAttribute('style'));
  1507. }
  1508. }
  1509. if (height === null) {
  1510. var _initialStyle = instance.rootElement.getAttribute('data-initialstyle');
  1511. if (_initialStyle && (_initialStyle.indexOf('height') > -1 || _initialStyle.indexOf('overflow') > -1)) {
  1512. instance.rootElement.setAttribute('style', _initialStyle);
  1513. } else {
  1514. instance.rootElement.style.height = '';
  1515. instance.rootElement.style.overflow = '';
  1516. }
  1517. } else if (height !== void 0) {
  1518. instance.rootElement.style.height = height + 'px';
  1519. instance.rootElement.style.overflow = 'hidden';
  1520. }
  1521. if (typeof settings.width != 'undefined') {
  1522. var width = settings.width;
  1523. if ((0, _function.isFunction)(width)) {
  1524. width = width();
  1525. }
  1526. instance.rootElement.style.width = width + 'px';
  1527. }
  1528. if (!init) {
  1529. datamap.clearLengthCache(); // force clear cache length on updateSettings() #3416
  1530. if (instance.view) {
  1531. instance.view.wt.wtViewport.resetHasOversizedColumnHeadersMarked();
  1532. }
  1533. instance.runHooks('afterUpdateSettings');
  1534. }
  1535. grid.adjustRowsAndCols();
  1536. if (instance.view && !priv.firstRun) {
  1537. instance.forceFullRender = true; // used when data was changed
  1538. selection.refreshBorders(null, true);
  1539. }
  1540. if (!init && instance.view && (currentHeight === '' || height === '' || height === void 0) && currentHeight !== height) {
  1541. instance.view.wt.wtOverlays.updateMainScrollableElements();
  1542. }
  1543. };
  1544. /**
  1545. * Get value from the selected cell.
  1546. *
  1547. * @memberof Core#
  1548. * @function getValue
  1549. * @since 0.11
  1550. * @returns {*} Value of selected cell.
  1551. */
  1552. this.getValue = function () {
  1553. var sel = instance.getSelected();
  1554. if (GridSettings.prototype.getValue) {
  1555. if ((0, _function.isFunction)(GridSettings.prototype.getValue)) {
  1556. return GridSettings.prototype.getValue.call(instance);
  1557. } else if (sel) {
  1558. return instance.getData()[sel[0]][GridSettings.prototype.getValue];
  1559. }
  1560. } else if (sel) {
  1561. return instance.getDataAtCell(sel[0], sel[1]);
  1562. }
  1563. };
  1564. function expandType(obj) {
  1565. if (!(0, _object.hasOwnProperty)(obj, 'type')) {
  1566. // ignore obj.prototype.type
  1567. return;
  1568. }
  1569. var type,
  1570. expandedType = {};
  1571. if (_typeof(obj.type) === 'object') {
  1572. type = obj.type;
  1573. } else if (typeof obj.type === 'string') {
  1574. type = (0, _cellTypes.getCellType)(obj.type);
  1575. }
  1576. for (var i in type) {
  1577. if ((0, _object.hasOwnProperty)(type, i) && !(0, _object.hasOwnProperty)(obj, i)) {
  1578. expandedType[i] = type[i];
  1579. }
  1580. }
  1581. return expandedType;
  1582. }
  1583. /**
  1584. * Returns the object settings.
  1585. *
  1586. * @memberof Core#
  1587. * @function getSettings
  1588. * @returns {Object} Object containing the current grid settings.
  1589. */
  1590. this.getSettings = function () {
  1591. return priv.settings;
  1592. };
  1593. /**
  1594. * Clears the data from the grid. (The table settings remain intact.)
  1595. *
  1596. * @memberof Core#
  1597. * @function clear
  1598. * @since 0.11
  1599. */
  1600. this.clear = function () {
  1601. selection.selectAll();
  1602. selection.empty();
  1603. };
  1604. /**
  1605. * @memberof Core#
  1606. * @function alter
  1607. * @param {String} action See grid.alter for possible values: `"insert_row"`, `"insert_col"`, `"remove_row"`, `"remove_col"`
  1608. * @param {Number} index Index of the row/column before which the new row/column will be inserted/removed.
  1609. * @param {Number} [amount = 1] Amound of rows/columns to be inserted/removed.
  1610. * @param {String} [source] Source indicator.
  1611. * @param {Boolean} [keepEmptyRows] Flag for preventing deletion of empty rows.
  1612. * @description
  1613. *
  1614. * Allows altering the table structure by either inserting/removing rows or inserting/removing columns:
  1615. *
  1616. * Insert new row(s) above the row with a given `index`. If index is `null` or `undefined`, the new row will be
  1617. * added after the last row.
  1618. * ```js
  1619. * var hot = new Handsontable(document.getElementById('example'));
  1620. * hot.alter('insert_row', 10);
  1621. * ```
  1622. *
  1623. * Insert new column(s) before the column with a given `index`. If index is `null` or `undefined`, the new column
  1624. * will be added after the last column.
  1625. * ```js
  1626. * var hot = new Handsontable(document.getElementById('example'));
  1627. * hot.alter('insert_col', 10);
  1628. * ```
  1629. *
  1630. * Remove the row(s) at the given `index`.
  1631. * ```js
  1632. * var hot = new Handsontable(document.getElementById('example'));
  1633. * hot.alter('remove_row', 10);
  1634. * ```
  1635. *
  1636. * Remove the column(s) at the given `index`.
  1637. * ```js
  1638. * var hot = new Handsontable(document.getElementById('example'));
  1639. * hot.alter('remove_col', 10);
  1640. * ```
  1641. */
  1642. this.alter = function (action, index, amount, source, keepEmptyRows) {
  1643. grid.alter(action, index, amount, source, keepEmptyRows);
  1644. };
  1645. /**
  1646. * Returns a TD element for the given `row` and `col` arguments, if it is rendered on screen.
  1647. * Returns `null` if the TD is not rendered on screen (probably because that part of the table is not visible).
  1648. *
  1649. * @memberof Core#
  1650. * @function getCell
  1651. * @param {Number} row Row index.
  1652. * @param {Number} col Column index.
  1653. * @param {Boolean} topmost If set to true, it returns the TD element from the topmost overlay. For example,
  1654. * if the wanted cell is in the range of fixed rows, it will return a TD element from the `top` overlay.
  1655. * @returns {Element} The cell's TD element.
  1656. */
  1657. this.getCell = function (row, col, topmost) {
  1658. return instance.view.getCellAtCoords(new _src.CellCoords(row, col), topmost);
  1659. };
  1660. /**
  1661. * Returns the coordinates of the cell, provided as a HTML Element.
  1662. *
  1663. * @memberof Core#
  1664. * @function getCoords
  1665. * @param {Element} elem The HTML Element representing the cell.
  1666. * @returns {CellCoords} Coordinates object.
  1667. */
  1668. this.getCoords = function (elem) {
  1669. return this.view.wt.wtTable.getCoords.call(this.view.wt.wtTable, elem);
  1670. };
  1671. /**
  1672. * Returns the property name that corresponds with the given column index. {@link DataMap#colToProp}
  1673. * If the data source is an array of arrays, it returns the columns index.
  1674. *
  1675. * @memberof Core#
  1676. * @function colToProp
  1677. * @param {Number} col Column index
  1678. * @returns {String|Number} Column property or column index.
  1679. */
  1680. this.colToProp = function (col) {
  1681. return datamap.colToProp(col);
  1682. };
  1683. /**
  1684. * Returns column index that corresponds with the given property. {@link DataMap#propToCol}
  1685. *
  1686. * @memberof Core#
  1687. * @function propToCol
  1688. * @param {String|Number} prop Property name or column index.
  1689. * @returns {Number} Column index.
  1690. */
  1691. this.propToCol = function (prop) {
  1692. return datamap.propToCol(prop);
  1693. };
  1694. /**
  1695. * Translate physical row index into visual.
  1696. *
  1697. * @since 0.29.0
  1698. * @memberof Core#
  1699. * @function toVisualRow
  1700. * @param {Number} row Physical row index.
  1701. * @returns {Number} Returns visual row index.
  1702. */
  1703. this.toVisualRow = function (row) {
  1704. return recordTranslator.toVisualRow(row);
  1705. };
  1706. /**
  1707. * Translate physical column index into visual.
  1708. *
  1709. * @since 0.29.0
  1710. * @memberof Core#
  1711. * @function toVisualColumn
  1712. * @param {Number} column Physical column index.
  1713. * @returns {Number} Returns visual column index.
  1714. */
  1715. this.toVisualColumn = function (column) {
  1716. return recordTranslator.toVisualColumn(column);
  1717. };
  1718. /**
  1719. * Translate visual row index into physical.
  1720. * If displayed rows order is different than the order of rows stored in memory (i.e. sorting is applied)
  1721. * to retrieve valid physical row index you can use this method.
  1722. *
  1723. * @since 0.29.0
  1724. * @memberof Core#
  1725. * @function toPhysicalRow
  1726. * @param {Number} row Visual row index.
  1727. * @returns {Number} Returns physical row index.
  1728. */
  1729. this.toPhysicalRow = function (row) {
  1730. return recordTranslator.toPhysicalRow(row);
  1731. };
  1732. /**
  1733. * Translate visual column index into physical.
  1734. * If displayed columns order is different than the order of columns stored in memory (i.e. manual column move is applied)
  1735. * to retrieve valid physical column index you can use this method.
  1736. *
  1737. * @since 0.29.0
  1738. * @memberof Core#
  1739. * @function toPhysicalColumn
  1740. * @param {Number} column Visual column index.
  1741. * @returns {Number} Returns physical column index.
  1742. */
  1743. this.toPhysicalColumn = function (column) {
  1744. return recordTranslator.toPhysicalColumn(column);
  1745. };
  1746. /**
  1747. * @description
  1748. * Returns the cell value at `row`, `col`. `row` and `col` are the __visible__ indexes (note, that if columns were reordered or sorted,
  1749. * the currently visible order will be used).
  1750. *
  1751. * @memberof Core#
  1752. * @function getDataAtCell
  1753. * @param {Number} row Row index.
  1754. * @param {Number} col Column index.
  1755. * @returns {String|Boolean|null} Data at cell.
  1756. */
  1757. this.getDataAtCell = function (row, col) {
  1758. return datamap.get(row, datamap.colToProp(col));
  1759. };
  1760. /**
  1761. * Return value at `row`, `prop`. (Uses {@link DataMap#get})
  1762. *
  1763. * @memberof Core#
  1764. * @function getDataAtRowProp
  1765. * @param {Number} row Row index.
  1766. * @param {String} prop Property name.
  1767. * @returns {*} Cell value.
  1768. */
  1769. this.getDataAtRowProp = function (row, prop) {
  1770. return datamap.get(row, prop);
  1771. };
  1772. /**
  1773. * @description
  1774. * Returns array of column values from the data source. `col` is the __visible__ index of the column.
  1775. * Note, that if columns were reordered or sorted, the currently visible order will be used.
  1776. *
  1777. * @memberof Core#
  1778. * @function getDataAtCol
  1779. * @since 0.9-beta2
  1780. * @param {Number} col Column index.
  1781. * @returns {Array} Array of cell values.
  1782. */
  1783. this.getDataAtCol = function (col) {
  1784. var out = [];
  1785. return out.concat.apply(out, _toConsumableArray(datamap.getRange(new _src.CellCoords(0, col), new _src.CellCoords(priv.settings.data.length - 1, col), datamap.DESTINATION_RENDERER)));
  1786. };
  1787. /**
  1788. * Given the object property name (e.g. `'first.name'`), returns an array of column's values from the data source.
  1789. * You can also provide a column index as the first argument.
  1790. *
  1791. * @memberof Core#
  1792. * @function getDataAtProp
  1793. * @since 0.9-beta2
  1794. * @param {String|Number} prop Property name / column index.
  1795. * @returns {Array} Array of cell values.
  1796. */
  1797. this.getDataAtProp = function (prop) {
  1798. var out = [],
  1799. range;
  1800. range = datamap.getRange(new _src.CellCoords(0, datamap.propToCol(prop)), new _src.CellCoords(priv.settings.data.length - 1, datamap.propToCol(prop)), datamap.DESTINATION_RENDERER);
  1801. return out.concat.apply(out, _toConsumableArray(range));
  1802. };
  1803. /**
  1804. * Returns the source data object (the same that was passed by `data` configuration option or `loadData` method).
  1805. * Optionally you can provide a cell range by using the `row`, `col`, `row2`, `col2` arguments, to get only a fragment of grid data.
  1806. *
  1807. * @memberof Core#
  1808. * @function getSourceData
  1809. * @since 0.20.0
  1810. * @param {Number} [r] From row.
  1811. * @param {Number} [c] From column.
  1812. * @param {Number} [r2] To row.
  1813. * @param {Number} [c2] To column.
  1814. * @returns {Array} Array of grid data.
  1815. */
  1816. this.getSourceData = function (r, c, r2, c2) {
  1817. var data = void 0;
  1818. if (r === void 0) {
  1819. data = dataSource.getData();
  1820. } else {
  1821. data = dataSource.getByRange(new _src.CellCoords(r, c), new _src.CellCoords(r2, c2));
  1822. }
  1823. return data;
  1824. };
  1825. /**
  1826. * Returns the source data object as an arrays of arrays format even when source data was provided in another format.
  1827. * Optionally you can provide a cell range by using the `row`, `col`, `row2`, `col2` arguments, to get only a fragment of grid data.
  1828. *
  1829. * @memberof Core#
  1830. * @function getSourceDataArray
  1831. * @since 0.28.0
  1832. * @param {Number} [r] From row.
  1833. * @param {Number} [c] From column.
  1834. * @param {Number} [r2] To row.
  1835. * @param {Number} [c2] To column.
  1836. * @returns {Array} An array of arrays.
  1837. */
  1838. this.getSourceDataArray = function (r, c, r2, c2) {
  1839. var data = void 0;
  1840. if (r === void 0) {
  1841. data = dataSource.getData(true);
  1842. } else {
  1843. data = dataSource.getByRange(new _src.CellCoords(r, c), new _src.CellCoords(r2, c2), true);
  1844. }
  1845. return data;
  1846. };
  1847. /**
  1848. * Returns an array of column values from the data source. `col` is the index of the row in the data source.
  1849. *
  1850. * @memberof Core#
  1851. * @function getSourceDataAtCol
  1852. * @since 0.11.0-beta3
  1853. * @param {Number} column Column index.
  1854. * @returns {Array} Array of the column's cell values.
  1855. */
  1856. this.getSourceDataAtCol = function (column) {
  1857. return dataSource.getAtColumn(column);
  1858. };
  1859. /**
  1860. * 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.
  1861. *
  1862. * @memberof Core#
  1863. * @function getSourceDataAtRow
  1864. * @since 0.11.0-beta3
  1865. * @param {Number} row Row index.
  1866. * @returns {Array|Object} Single row of data.
  1867. */
  1868. this.getSourceDataAtRow = function (row) {
  1869. return dataSource.getAtRow(row);
  1870. };
  1871. /**
  1872. * Returns a single value from the data source.
  1873. *
  1874. * @memberof Core#
  1875. * @function getSourceDataAtCell
  1876. * @param {Number} row Row index.
  1877. * @param {Number} column Column index.
  1878. * @returns {*} Cell data.
  1879. * @since 0.20.0
  1880. */
  1881. this.getSourceDataAtCell = function (row, column) {
  1882. return dataSource.getAtCell(row, column);
  1883. };
  1884. /**
  1885. * @description
  1886. * Returns a single row of the data. The `row` argument is the __visible__ index of the row.
  1887. *
  1888. * @memberof Core#
  1889. * @function getDataAtRow
  1890. * @param {Number} row Row index.
  1891. * @returns {Array} Array of row's cell data.
  1892. * @since 0.9-beta2
  1893. */
  1894. this.getDataAtRow = function (row) {
  1895. var data = datamap.getRange(new _src.CellCoords(row, 0), new _src.CellCoords(row, this.countCols() - 1), datamap.DESTINATION_RENDERER);
  1896. return data[0] || [];
  1897. };
  1898. /**
  1899. * @description
  1900. * Returns a data type defined in the Handsontable settings under the `type` key ([Options#type](http://docs.handsontable.com/Options.html#type)).
  1901. * If there are cells with different types in the selected range, it returns `'mixed'`.
  1902. *
  1903. * @since 0.18.1
  1904. * @memberof Core#
  1905. * @function getDataType
  1906. * @param {Number} rowFrom From row index.
  1907. * @param {Number} columnFrom To row index.
  1908. * @param {Number} rowTo From column index.
  1909. * @param {Number} columnTo To column index.
  1910. * @returns {String} Cell type (e.q: `'mixed'`, `'text'`, `'numeric'`, `'autocomplete'`).
  1911. */
  1912. this.getDataType = function (rowFrom, columnFrom, rowTo, columnTo) {
  1913. var _this = this;
  1914. var previousType = null;
  1915. var currentType = null;
  1916. if (rowFrom === void 0) {
  1917. rowFrom = 0;
  1918. rowTo = this.countRows();
  1919. columnFrom = 0;
  1920. columnTo = this.countCols();
  1921. }
  1922. if (rowTo === void 0) {
  1923. rowTo = rowFrom;
  1924. }
  1925. if (columnTo === void 0) {
  1926. columnTo = columnFrom;
  1927. }
  1928. var type = 'mixed';
  1929. (0, _number.rangeEach)(Math.min(rowFrom, rowTo), Math.max(rowFrom, rowTo), function (row) {
  1930. var isTypeEqual = true;
  1931. (0, _number.rangeEach)(Math.min(columnFrom, columnTo), Math.max(columnFrom, columnTo), function (column) {
  1932. var cellType = _this.getCellMeta(row, column);
  1933. currentType = cellType.type;
  1934. if (previousType) {
  1935. isTypeEqual = previousType === currentType;
  1936. } else {
  1937. previousType = currentType;
  1938. }
  1939. return isTypeEqual;
  1940. });
  1941. type = isTypeEqual ? currentType : 'mixed';
  1942. return isTypeEqual;
  1943. });
  1944. return type;
  1945. };
  1946. /**
  1947. * Remove a property defined by the `key` argument from the cell meta object for the provided `row` and `col` coordinates.
  1948. *
  1949. * @memberof Core#
  1950. * @function removeCellMeta
  1951. * @param {Number} row Row index.
  1952. * @param {Number} col Column index.
  1953. * @param {String} key Property name.
  1954. */
  1955. this.removeCellMeta = function (row, col, key) {
  1956. var cellMeta = instance.getCellMeta(row, col);
  1957. if (cellMeta[key] != undefined) {
  1958. delete priv.cellSettings[row][col][key];
  1959. }
  1960. };
  1961. /**
  1962. * Remove one or more rows from the cell meta object.
  1963. *
  1964. * @since 0.30.0
  1965. * @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.
  1966. * @param {Number} deleteAmount The number of items to be removed. If set to 0, no items will be removed.
  1967. * @param {Array} items The new items to be added to the array.
  1968. */
  1969. this.spliceCellsMeta = function (index, deleteAmount) {
  1970. var _priv$cellSettings;
  1971. for (var _len2 = arguments.length, items = Array(_len2 > 2 ? _len2 - 2 : 0), _key = 2; _key < _len2; _key++) {
  1972. items[_key - 2] = arguments[_key];
  1973. }
  1974. (_priv$cellSettings = priv.cellSettings).splice.apply(_priv$cellSettings, [index, deleteAmount].concat(items));
  1975. };
  1976. /**
  1977. * Set cell meta data object defined by `prop` to the corresponding params `row` and `col`.
  1978. *
  1979. * @memberof Core#
  1980. * @function setCellMetaObject
  1981. * @since 0.11
  1982. * @param {Number} row Row index.
  1983. * @param {Number} col Column index.
  1984. * @param {Object} prop Meta object.
  1985. */
  1986. this.setCellMetaObject = function (row, col, prop) {
  1987. if ((typeof prop === 'undefined' ? 'undefined' : _typeof(prop)) === 'object') {
  1988. for (var key in prop) {
  1989. if ((0, _object.hasOwnProperty)(prop, key)) {
  1990. var value = prop[key];
  1991. this.setCellMeta(row, col, key, value);
  1992. }
  1993. }
  1994. }
  1995. };
  1996. /**
  1997. * Sets a property defined by the `key` object to the meta object of a cell corresponding to params `row` and `col`.
  1998. *
  1999. * @memberof Core#
  2000. * @function setCellMeta
  2001. * @since 0.11
  2002. * @param {Number} row Row index.
  2003. * @param {Number} col Column index.
  2004. * @param {String} key Property name.
  2005. * @param {String} val Property value.
  2006. * @fires Hooks#afterSetCellMeta
  2007. */
  2008. this.setCellMeta = function (row, col, key, val) {
  2009. var _recordTranslator$toP = recordTranslator.toPhysical(row, col);
  2010. var _recordTranslator$toP2 = _slicedToArray(_recordTranslator$toP, 2);
  2011. row = _recordTranslator$toP2[0];
  2012. col = _recordTranslator$toP2[1];
  2013. if (!priv.columnSettings[col]) {
  2014. priv.columnSettings[col] = (0, _setting.columnFactory)(GridSettings, priv.columnsSettingConflicts);
  2015. }
  2016. if (!priv.cellSettings[row]) {
  2017. priv.cellSettings[row] = [];
  2018. }
  2019. if (!priv.cellSettings[row][col]) {
  2020. priv.cellSettings[row][col] = new priv.columnSettings[col]();
  2021. }
  2022. priv.cellSettings[row][col][key] = val;
  2023. instance.runHooks('afterSetCellMeta', row, col, key, val);
  2024. };
  2025. /**
  2026. * Get all the cells meta settings at least once generated in the table (in order of cell initialization).
  2027. *
  2028. * @since 0.19.0
  2029. * @returns {Array} Returns Array of ColumnSettings object.
  2030. */
  2031. this.getCellsMeta = function () {
  2032. return (0, _array.arrayFlatten)(priv.cellSettings);
  2033. };
  2034. /**
  2035. * Returns the cell properties object for the given `row` and `col` coordinates.
  2036. *
  2037. * @memberof Core#
  2038. * @function getCellMeta
  2039. * @param {Number} row Row index.
  2040. * @param {Number} col Column index.
  2041. * @returns {Object} The cell properties object.
  2042. * @fires Hooks#beforeGetCellMeta
  2043. * @fires Hooks#afterGetCellMeta
  2044. */
  2045. this.getCellMeta = function (row, col) {
  2046. var prop = datamap.colToProp(col),
  2047. cellProperties;
  2048. var visualRow = row;
  2049. var visualCol = col;
  2050. var _recordTranslator$toP3 = recordTranslator.toPhysical(row, col);
  2051. var _recordTranslator$toP4 = _slicedToArray(_recordTranslator$toP3, 2);
  2052. row = _recordTranslator$toP4[0];
  2053. col = _recordTranslator$toP4[1];
  2054. if (!priv.columnSettings[col]) {
  2055. priv.columnSettings[col] = (0, _setting.columnFactory)(GridSettings, priv.columnsSettingConflicts);
  2056. }
  2057. if (!priv.cellSettings[row]) {
  2058. priv.cellSettings[row] = [];
  2059. }
  2060. if (!priv.cellSettings[row][col]) {
  2061. priv.cellSettings[row][col] = new priv.columnSettings[col]();
  2062. }
  2063. cellProperties = priv.cellSettings[row][col]; // retrieve cellProperties from cache
  2064. cellProperties.row = row;
  2065. cellProperties.col = col;
  2066. cellProperties.visualRow = visualRow;
  2067. cellProperties.visualCol = visualCol;
  2068. cellProperties.prop = prop;
  2069. cellProperties.instance = instance;
  2070. instance.runHooks('beforeGetCellMeta', row, col, cellProperties);
  2071. (0, _object.extend)(cellProperties, expandType(cellProperties)); // for `type` added in beforeGetCellMeta
  2072. if (cellProperties.cells) {
  2073. var settings = cellProperties.cells.call(cellProperties, row, col, prop);
  2074. if (settings) {
  2075. (0, _object.extend)(cellProperties, settings);
  2076. (0, _object.extend)(cellProperties, expandType(settings)); // for `type` added in cells
  2077. }
  2078. }
  2079. instance.runHooks('afterGetCellMeta', row, col, cellProperties);
  2080. return cellProperties;
  2081. };
  2082. /**
  2083. * Returns a row off the cell meta array.
  2084. *
  2085. * @memberof Core#
  2086. * @function getCellMetaAtRow
  2087. * @since 0.30.0
  2088. * @param {Number} row Index of the row to return cell meta for.
  2089. * @returns {Array}
  2090. */
  2091. this.getCellMetaAtRow = function (row) {
  2092. return priv.cellSettings[row];
  2093. };
  2094. /**
  2095. * Checks if the data format and config allows user to modify the column structure.
  2096. * @returns {boolean}
  2097. */
  2098. this.isColumnModificationAllowed = function () {
  2099. return !(instance.dataType === 'object' || instance.getSettings().columns);
  2100. };
  2101. var rendererLookup = (0, _data.cellMethodLookupFactory)('renderer');
  2102. /**
  2103. * Returns the cell renderer function by given `row` and `col` arguments.
  2104. *
  2105. * @memberof Core#
  2106. * @function getCellRenderer
  2107. * @since 0.11
  2108. * @param {Number|Object} row Row index or cell meta object.
  2109. * @param {Number} [col] Column index.
  2110. * @returns {Function} The renderer function.
  2111. */
  2112. this.getCellRenderer = function (row, col) {
  2113. return (0, _renderers.getRenderer)(rendererLookup.call(this, row, col));
  2114. };
  2115. /**
  2116. * Returns the cell editor by the provided `row` and `col` arguments.
  2117. *
  2118. * @memberof Core#
  2119. * @function getCellEditor
  2120. * @param {Number} row Row index.
  2121. * @param {Number} col Column index.
  2122. * @returns {Object} The Editor object.
  2123. */
  2124. this.getCellEditor = (0, _data.cellMethodLookupFactory)('editor');
  2125. var validatorLookup = (0, _data.cellMethodLookupFactory)('validator');
  2126. /**
  2127. * Returns the cell validator by `row` and `col`, provided a validator is defined. If not - it doesn't return anything.
  2128. *
  2129. * @memberof Core#
  2130. * @function getCellValidator
  2131. * @param {Number} row Row index.
  2132. * @param {Number} col Column index.
  2133. * @returns {Function|RegExp|undefined} The validator function.
  2134. */
  2135. this.getCellValidator = function (row, col) {
  2136. var validator = validatorLookup.call(this, row, col);
  2137. if (typeof validator === 'string') {
  2138. validator = (0, _validators.getValidator)(validator);
  2139. }
  2140. return validator;
  2141. };
  2142. /**
  2143. * Validates all cells using their validator functions and calls callback when finished.
  2144. *
  2145. * If one of the cells is invalid, the callback will be fired with `'valid'` arguments as `false` - otherwise it would equal `true`.
  2146. *
  2147. * @memberof Core#
  2148. * @function validateCells
  2149. * @param {Function} [callback] The callback function.
  2150. */
  2151. this.validateCells = function (callback) {
  2152. var waitingForValidator = new ValidatorsQueue();
  2153. if (callback) {
  2154. waitingForValidator.onQueueEmpty = callback;
  2155. }
  2156. var i = instance.countRows() - 1;
  2157. while (i >= 0) {
  2158. var j = instance.countCols() - 1;
  2159. while (j >= 0) {
  2160. waitingForValidator.addValidatorToQueue();
  2161. instance.validateCell(instance.getDataAtCell(i, j), instance.getCellMeta(i, j), function (result) {
  2162. if (typeof result !== 'boolean') {
  2163. throw new Error('Validation error: result is not boolean');
  2164. }
  2165. if (result === false) {
  2166. waitingForValidator.valid = false;
  2167. }
  2168. waitingForValidator.removeValidatorFormQueue();
  2169. }, 'validateCells');
  2170. j--;
  2171. }
  2172. i--;
  2173. }
  2174. waitingForValidator.checkIfQueueIsEmpty();
  2175. };
  2176. /**
  2177. * 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.
  2178. *
  2179. * @memberof Core#
  2180. * @function getRowHeader
  2181. * @param {Number} [row] Row index.
  2182. * @fires Hooks#modifyRowHeader
  2183. * @returns {Array|String} Array of header values / single header value.
  2184. */
  2185. this.getRowHeader = function (row) {
  2186. var rowHeader = priv.settings.rowHeaders;
  2187. if (row !== void 0) {
  2188. row = instance.runHooks('modifyRowHeader', row);
  2189. }
  2190. if (row === void 0) {
  2191. rowHeader = [];
  2192. (0, _number.rangeEach)(instance.countRows() - 1, function (i) {
  2193. rowHeader.push(instance.getRowHeader(i));
  2194. });
  2195. } else if (Array.isArray(rowHeader) && rowHeader[row] !== void 0) {
  2196. rowHeader = rowHeader[row];
  2197. } else if ((0, _function.isFunction)(rowHeader)) {
  2198. rowHeader = rowHeader(row);
  2199. } else if (rowHeader && typeof rowHeader !== 'string' && typeof rowHeader !== 'number') {
  2200. rowHeader = row + 1;
  2201. }
  2202. return rowHeader;
  2203. };
  2204. /**
  2205. * Returns information about if this table is configured to display row headers.
  2206. *
  2207. * @memberof Core#
  2208. * @function hasRowHeaders
  2209. * @returns {Boolean} `true` if the instance has the row headers enabled, `false` otherwise.
  2210. * @since 0.11
  2211. */
  2212. this.hasRowHeaders = function () {
  2213. return !!priv.settings.rowHeaders;
  2214. };
  2215. /**
  2216. * Returns information about if this table is configured to display column headers.
  2217. *
  2218. * @memberof Core#
  2219. * @function hasColHeaders
  2220. * @since 0.11
  2221. * @returns {Boolean} `True` if the instance has the column headers enabled, `false` otherwise.
  2222. */
  2223. this.hasColHeaders = function () {
  2224. if (priv.settings.colHeaders !== void 0 && priv.settings.colHeaders !== null) {
  2225. // Polymer has empty value = null
  2226. return !!priv.settings.colHeaders;
  2227. }
  2228. for (var i = 0, ilen = instance.countCols(); i < ilen; i++) {
  2229. if (instance.getColHeader(i)) {
  2230. return true;
  2231. }
  2232. }
  2233. return false;
  2234. };
  2235. /**
  2236. * 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.
  2237. *
  2238. * @memberof Core#
  2239. * @function getColHeader
  2240. * @param {Number} [col] Column index.
  2241. * @fires Hooks#modifyColHeader
  2242. * @returns {Array|String} The column header(s).
  2243. */
  2244. this.getColHeader = function (col) {
  2245. var columnsAsFunc = priv.settings.columns && (0, _function.isFunction)(priv.settings.columns);
  2246. var result = priv.settings.colHeaders;
  2247. col = instance.runHooks('modifyColHeader', col);
  2248. if (col === void 0) {
  2249. var out = [];
  2250. var ilen = columnsAsFunc ? instance.countSourceCols() : instance.countCols();
  2251. for (var i = 0; i < ilen; i++) {
  2252. out.push(instance.getColHeader(i));
  2253. }
  2254. result = out;
  2255. } else {
  2256. var translateVisualIndexToColumns = function translateVisualIndexToColumns(col) {
  2257. var arr = [];
  2258. var columnsLen = instance.countSourceCols();
  2259. var index = 0;
  2260. for (; index < columnsLen; index++) {
  2261. if ((0, _function.isFunction)(instance.getSettings().columns) && instance.getSettings().columns(index)) {
  2262. arr.push(index);
  2263. }
  2264. }
  2265. return arr[col];
  2266. };
  2267. var baseCol = col;
  2268. col = instance.runHooks('modifyCol', col);
  2269. var prop = translateVisualIndexToColumns(col);
  2270. if (priv.settings.columns && (0, _function.isFunction)(priv.settings.columns) && priv.settings.columns(prop) && priv.settings.columns(prop).title) {
  2271. result = priv.settings.columns(prop).title;
  2272. } else if (priv.settings.columns && priv.settings.columns[col] && priv.settings.columns[col].title) {
  2273. result = priv.settings.columns[col].title;
  2274. } else if (Array.isArray(priv.settings.colHeaders) && priv.settings.colHeaders[col] !== void 0) {
  2275. result = priv.settings.colHeaders[col];
  2276. } else if ((0, _function.isFunction)(priv.settings.colHeaders)) {
  2277. result = priv.settings.colHeaders(col);
  2278. } else if (priv.settings.colHeaders && typeof priv.settings.colHeaders !== 'string' && typeof priv.settings.colHeaders !== 'number') {
  2279. result = (0, _data.spreadsheetColumnLabel)(baseCol); // see #1458
  2280. }
  2281. }
  2282. return result;
  2283. };
  2284. /**
  2285. * Return column width from settings (no guessing). Private use intended.
  2286. *
  2287. * @private
  2288. * @memberof Core#
  2289. * @function _getColWidthFromSettings
  2290. * @param {Number} col
  2291. * @returns {Number}
  2292. */
  2293. this._getColWidthFromSettings = function (col) {
  2294. var cellProperties = instance.getCellMeta(0, col);
  2295. var width = cellProperties.width;
  2296. if (width === void 0 || width === priv.settings.width) {
  2297. width = cellProperties.colWidths;
  2298. }
  2299. if (width !== void 0 && width !== null) {
  2300. switch (typeof width === 'undefined' ? 'undefined' : _typeof(width)) {
  2301. case 'object':
  2302. // array
  2303. width = width[col];
  2304. break;
  2305. case 'function':
  2306. width = width(col);
  2307. break;
  2308. default:
  2309. break;
  2310. }
  2311. if (typeof width === 'string') {
  2312. width = parseInt(width, 10);
  2313. }
  2314. }
  2315. return width;
  2316. };
  2317. /**
  2318. * Returns the width of the requested column.
  2319. *
  2320. * @memberof Core#
  2321. * @function getColWidth
  2322. * @since 0.11
  2323. * @param {Number} col Column index.
  2324. * @returns {Number} Column width.
  2325. * @fires Hooks#modifyColWidth
  2326. */
  2327. this.getColWidth = function (col) {
  2328. var width = instance._getColWidthFromSettings(col);
  2329. width = instance.runHooks('modifyColWidth', width, col);
  2330. if (width === void 0) {
  2331. width = _src.ViewportColumnsCalculator.DEFAULT_WIDTH;
  2332. }
  2333. return width;
  2334. };
  2335. /**
  2336. * Return row height from settings (no guessing). Private use intended.
  2337. *
  2338. * @private
  2339. * @memberof Core#
  2340. * @function _getRowHeightFromSettings
  2341. * @param {Number} row
  2342. * @returns {Number}
  2343. */
  2344. this._getRowHeightFromSettings = function (row) {
  2345. // let cellProperties = instance.getCellMeta(row, 0);
  2346. // let height = cellProperties.height;
  2347. //
  2348. // if (height === void 0 || height === priv.settings.height) {
  2349. // height = cellProperties.rowHeights;
  2350. // }
  2351. var height = priv.settings.rowHeights;
  2352. if (height !== void 0 && height !== null) {
  2353. switch (typeof height === 'undefined' ? 'undefined' : _typeof(height)) {
  2354. case 'object':
  2355. // array
  2356. height = height[row];
  2357. break;
  2358. case 'function':
  2359. height = height(row);
  2360. break;
  2361. default:
  2362. break;
  2363. }
  2364. if (typeof height === 'string') {
  2365. height = parseInt(height, 10);
  2366. }
  2367. }
  2368. return height;
  2369. };
  2370. /**
  2371. * Returns the row height.
  2372. *
  2373. * @memberof Core#
  2374. * @function getRowHeight
  2375. * @since 0.11
  2376. * @param {Number} row Row index.
  2377. * @returns {Number} The given row's height.
  2378. * @fires Hooks#modifyRowHeight
  2379. */
  2380. this.getRowHeight = function (row) {
  2381. var height = instance._getRowHeightFromSettings(row);
  2382. height = instance.runHooks('modifyRowHeight', height, row);
  2383. return height;
  2384. };
  2385. /**
  2386. * Returns the total number of rows in the data source.
  2387. *
  2388. * @memberof Core#
  2389. * @function countSourceRows
  2390. * @since 0.20.0
  2391. * @returns {Number} Total number in rows in data source.
  2392. */
  2393. this.countSourceRows = function () {
  2394. var sourceLength = instance.runHooks('modifySourceLength');
  2395. return sourceLength || (instance.getSourceData() ? instance.getSourceData().length : 0);
  2396. };
  2397. /**
  2398. * Returns the total number of columns in the data source.
  2399. *
  2400. * @memberof Core#
  2401. * @function countSourceCols
  2402. * @since 0.26.1
  2403. * @returns {Number} Total number in columns in data source.
  2404. */
  2405. this.countSourceCols = function () {
  2406. var len = 0;
  2407. var obj = instance.getSourceData() && instance.getSourceData()[0] ? instance.getSourceData()[0] : [];
  2408. if ((0, _object.isObject)(obj)) {
  2409. len = (0, _object.deepObjectSize)(obj);
  2410. } else {
  2411. len = obj.length || 0;
  2412. }
  2413. return len;
  2414. };
  2415. /**
  2416. * Returns the total number of rows in the grid.
  2417. *
  2418. * @memberof Core#
  2419. * @function countRows
  2420. * @returns {Number} Total number in rows the grid.
  2421. */
  2422. this.countRows = function () {
  2423. return datamap.getLength();
  2424. };
  2425. /**
  2426. * Returns the total number of columns in the grid.
  2427. *
  2428. * @memberof Core#
  2429. * @function countCols
  2430. * @returns {Number} Total number of columns.
  2431. */
  2432. this.countCols = function () {
  2433. var maxCols = this.getSettings().maxCols;
  2434. var dataHasLength = false;
  2435. var dataLen = 0;
  2436. if (instance.dataType === 'array') {
  2437. dataHasLength = priv.settings.data && priv.settings.data[0] && priv.settings.data[0].length;
  2438. }
  2439. if (dataHasLength) {
  2440. dataLen = priv.settings.data[0].length;
  2441. }
  2442. if (priv.settings.columns) {
  2443. var columnsIsFunction = (0, _function.isFunction)(priv.settings.columns);
  2444. if (columnsIsFunction) {
  2445. if (instance.dataType === 'array') {
  2446. var columnLen = 0;
  2447. for (var i = 0; i < dataLen; i++) {
  2448. if (priv.settings.columns(i)) {
  2449. columnLen++;
  2450. }
  2451. }
  2452. dataLen = columnLen;
  2453. } else if (instance.dataType === 'object' || instance.dataType === 'function') {
  2454. dataLen = datamap.colToPropCache.length;
  2455. }
  2456. } else {
  2457. dataLen = priv.settings.columns.length;
  2458. }
  2459. } else if (instance.dataType === 'object' || instance.dataType === 'function') {
  2460. dataLen = datamap.colToPropCache.length;
  2461. }
  2462. return Math.min(maxCols, dataLen);
  2463. };
  2464. /**
  2465. * Returns an index of the first rendered row.
  2466. *
  2467. * @memberof Core#
  2468. * @function rowOffset
  2469. * @returns {Number} Index of first rendered row.
  2470. */
  2471. this.rowOffset = function () {
  2472. return instance.view.wt.wtTable.getFirstRenderedRow();
  2473. };
  2474. /**
  2475. * Returns the index of the first rendered column.
  2476. *
  2477. * @memberof Core#
  2478. * @function colOffset
  2479. * @returns {Number} Index of the first visible column.
  2480. */
  2481. this.colOffset = function () {
  2482. return instance.view.wt.wtTable.getFirstRenderedColumn();
  2483. };
  2484. /**
  2485. * Returns the number of rendered rows (including rows partially or fully rendered outside viewport).
  2486. *
  2487. * @memberof Core#
  2488. * @function countRenderedRows
  2489. * @returns {Number} Returns -1 if table is not visible.
  2490. */
  2491. this.countRenderedRows = function () {
  2492. return instance.view.wt.drawn ? instance.view.wt.wtTable.getRenderedRowsCount() : -1;
  2493. };
  2494. /**
  2495. * Returns the number of visible rows (rendered rows that fully fit inside viewport).
  2496. *
  2497. * @memberof Core#
  2498. * @function countVisibleRows
  2499. * @returns {Number} Number of visible rows or -1.
  2500. */
  2501. this.countVisibleRows = function () {
  2502. return instance.view.wt.drawn ? instance.view.wt.wtTable.getVisibleRowsCount() : -1;
  2503. };
  2504. /**
  2505. * Returns the number of rendered columns (including columns partially or fully rendered outside viewport).
  2506. *
  2507. * @memberof Core#
  2508. * @function countRenderedCols
  2509. * @returns {Number} Returns -1 if table is not visible.
  2510. */
  2511. this.countRenderedCols = function () {
  2512. return instance.view.wt.drawn ? instance.view.wt.wtTable.getRenderedColumnsCount() : -1;
  2513. };
  2514. /**
  2515. * Returns the number of visible columns. Returns -1 if table is not visible
  2516. *
  2517. * @memberof Core#
  2518. * @function countVisibleCols
  2519. * @return {Number} Number of visible columns or -1.
  2520. */
  2521. this.countVisibleCols = function () {
  2522. return instance.view.wt.drawn ? instance.view.wt.wtTable.getVisibleColumnsCount() : -1;
  2523. };
  2524. /**
  2525. * Returns the number of empty rows. If the optional ending parameter is `true`, returns the
  2526. * number of empty rows at the bottom of the table.
  2527. *
  2528. * @memberof Core#
  2529. * @function countEmptyRows
  2530. * @param {Boolean} [ending] If `true`, will only count empty rows at the end of the data source.
  2531. * @returns {Number} Count empty rows
  2532. * @fires Hooks#modifyRow
  2533. */
  2534. this.countEmptyRows = function (ending) {
  2535. var i = instance.countRows() - 1,
  2536. empty = 0,
  2537. row;
  2538. while (i >= 0) {
  2539. row = instance.runHooks('modifyRow', i);
  2540. if (instance.isEmptyRow(row)) {
  2541. empty++;
  2542. } else if (ending) {
  2543. break;
  2544. }
  2545. i--;
  2546. }
  2547. return empty;
  2548. };
  2549. /**
  2550. * Returns the number of empty columns. If the optional ending parameter is `true`, returns the number of empty
  2551. * columns at right hand edge of the table.
  2552. *
  2553. * @memberof Core#
  2554. * @function countEmptyCols
  2555. * @param {Boolean} [ending] If `true`, will only count empty columns at the end of the data source row.
  2556. * @returns {Number} Count empty cols
  2557. */
  2558. this.countEmptyCols = function (ending) {
  2559. if (instance.countRows() < 1) {
  2560. return 0;
  2561. }
  2562. var i = instance.countCols() - 1,
  2563. empty = 0;
  2564. while (i >= 0) {
  2565. if (instance.isEmptyCol(i)) {
  2566. empty++;
  2567. } else if (ending) {
  2568. break;
  2569. }
  2570. i--;
  2571. }
  2572. return empty;
  2573. };
  2574. /**
  2575. * Check if all cells in the row declared by the `row` argument are empty.
  2576. *
  2577. * @memberof Core#
  2578. * @function isEmptyRow
  2579. * @param {Number} row Row index.
  2580. * @returns {Boolean} `true` if the row at the given `row` is empty, `false` otherwise.
  2581. */
  2582. this.isEmptyRow = function (row) {
  2583. return priv.settings.isEmptyRow.call(instance, row);
  2584. };
  2585. /**
  2586. * Check if all cells in the the column declared by the `col` argument are empty.
  2587. *
  2588. * @memberof Core#
  2589. * @function isEmptyCol
  2590. * @param {Number} col Column index.
  2591. * @returns {Boolean} `true` if the column at the given `col` is empty, `false` otherwise.
  2592. */
  2593. this.isEmptyCol = function (col) {
  2594. return priv.settings.isEmptyCol.call(instance, col);
  2595. };
  2596. /**
  2597. * Select cell specified by `row` and `col` values or a range of cells finishing at `endRow`, `endCol`.
  2598. * By default, viewport will be scrolled to selection.
  2599. * After the `selectCell` method had finished, the instance will be listening to keyboard input on the document.
  2600. *
  2601. * @memberof Core#
  2602. * @function selectCell
  2603. * @param {Number} row Row index.
  2604. * @param {Number} col Column index.
  2605. * @param {Number} [endRow] End row index (if selecting a range).
  2606. * @param {Number} [endCol] End column index (if selecting a range).
  2607. * @param {Boolean} [scrollToCell=true] If `true`, the viewport will be scrolled to the selection.
  2608. * @param {Boolean} [changeListener=true] If `false`, Handsontable will not change keyboard events listener to himself.
  2609. * @returns {Boolean} `true` if selection was successful, `false` otherwise.
  2610. */
  2611. this.selectCell = function (row, col, endRow, endCol, scrollToCell, changeListener) {
  2612. var coords;
  2613. changeListener = (0, _mixed.isUndefined)(changeListener) || changeListener === true;
  2614. if (typeof row !== 'number' || row < 0 || row >= instance.countRows()) {
  2615. return false;
  2616. }
  2617. if (typeof col !== 'number' || col < 0 || col >= instance.countCols()) {
  2618. return false;
  2619. }
  2620. if ((0, _mixed.isDefined)(endRow)) {
  2621. if (typeof endRow !== 'number' || endRow < 0 || endRow >= instance.countRows()) {
  2622. return false;
  2623. }
  2624. if (typeof endCol !== 'number' || endCol < 0 || endCol >= instance.countCols()) {
  2625. return false;
  2626. }
  2627. }
  2628. coords = new _src.CellCoords(row, col);
  2629. priv.selRange = new _src.CellRange(coords, coords, coords);
  2630. if (changeListener) {
  2631. instance.listen();
  2632. }
  2633. if ((0, _mixed.isUndefined)(endRow)) {
  2634. selection.setRangeEnd(priv.selRange.from, scrollToCell);
  2635. } else {
  2636. selection.setRangeEnd(new _src.CellCoords(endRow, endCol), scrollToCell);
  2637. }
  2638. instance.selection.finish();
  2639. return true;
  2640. };
  2641. /**
  2642. * Select the cell specified by the `row` and `prop` arguments, or a range finishing at `endRow`, `endProp`.
  2643. * By default, viewport will be scrolled to selection.
  2644. *
  2645. * @memberof Core#
  2646. * @function selectCellByProp
  2647. * @param {Number} row Row index.
  2648. * @param {String} prop Property name.
  2649. * @param {Number} [endRow] End row index (if selecting a range).
  2650. * @param {String} [endProp] End property name (if selecting a range).
  2651. * @param {Boolean} [scrollToCell=true] If `true`, viewport will be scrolled to the selection.
  2652. * @returns {Boolean} `true` if selection was successful, `false` otherwise.
  2653. */
  2654. this.selectCellByProp = function (row, prop, endRow, endProp, scrollToCell) {
  2655. var _instance5;
  2656. arguments[1] = datamap.propToCol(arguments[1]);
  2657. if ((0, _mixed.isDefined)(arguments[3])) {
  2658. arguments[3] = datamap.propToCol(arguments[3]);
  2659. }
  2660. return (_instance5 = instance).selectCell.apply(_instance5, arguments);
  2661. };
  2662. /**
  2663. * Deselects the current cell selection on grid.
  2664. *
  2665. * @memberof Core#
  2666. * @function deselectCell
  2667. */
  2668. this.deselectCell = function () {
  2669. selection.deselect();
  2670. };
  2671. /**
  2672. * Scroll viewport to coords specified by the `row` and `column` arguments.
  2673. *
  2674. * @since 0.24.3
  2675. * @memberof Core#
  2676. * @function scrollViewportTo
  2677. * @param {Number} [row] Row index.
  2678. * @param {Number} [column] Column index.
  2679. * @param {Boolean} [snapToBottom = false] If `true`, viewport is scrolled to show the cell on the bottom of the table.
  2680. * @param {Boolean} [snapToRight = false] If `true`, viewport is scrolled to show the cell on the right side of the table.
  2681. * @returns {Boolean} `true` if scroll was successful, `false` otherwise.
  2682. */
  2683. this.scrollViewportTo = function (row, column) {
  2684. var snapToBottom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  2685. var snapToRight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
  2686. if (row !== void 0 && (row < 0 || row >= instance.countRows())) {
  2687. return false;
  2688. }
  2689. if (column !== void 0 && (column < 0 || column >= instance.countCols())) {
  2690. return false;
  2691. }
  2692. var result = false;
  2693. if (row !== void 0 && column !== void 0) {
  2694. instance.view.wt.wtOverlays.topOverlay.scrollTo(row, snapToBottom);
  2695. instance.view.wt.wtOverlays.leftOverlay.scrollTo(column, snapToRight);
  2696. result = true;
  2697. }
  2698. if (typeof row === 'number' && typeof column !== 'number') {
  2699. instance.view.wt.wtOverlays.topOverlay.scrollTo(row, snapToBottom);
  2700. result = true;
  2701. }
  2702. if (typeof column === 'number' && typeof row !== 'number') {
  2703. instance.view.wt.wtOverlays.leftOverlay.scrollTo(column, snapToRight);
  2704. result = true;
  2705. }
  2706. return result;
  2707. };
  2708. /**
  2709. * Removes grid from the DOM.
  2710. *
  2711. * @memberof Core#
  2712. * @function destroy
  2713. * @fires Hooks#afterDestroy
  2714. */
  2715. this.destroy = function () {
  2716. instance._clearTimeouts();
  2717. if (instance.view) {
  2718. // in case HT is destroyed before initialization has finished
  2719. instance.view.destroy();
  2720. }
  2721. if (dataSource) {
  2722. dataSource.destroy();
  2723. }
  2724. dataSource = null;
  2725. (0, _element.empty)(instance.rootElement);
  2726. eventManager.destroy();
  2727. instance.runHooks('afterDestroy');
  2728. _pluginHooks2.default.getSingleton().destroy(instance);
  2729. for (var i in instance) {
  2730. if ((0, _object.hasOwnProperty)(instance, i)) {
  2731. // replace instance methods with post mortem
  2732. if ((0, _function.isFunction)(instance[i])) {
  2733. instance[i] = postMortem;
  2734. } else if (i !== 'guid') {
  2735. // replace instance properties with null (restores memory)
  2736. // it should not be necessary but this prevents a memory leak side effects that show itself in Jasmine tests
  2737. instance[i] = null;
  2738. }
  2739. }
  2740. }
  2741. // replace private properties with null (restores memory)
  2742. // it should not be necessary but this prevents a memory leak side effects that show itself in Jasmine tests
  2743. if (datamap) {
  2744. datamap.destroy();
  2745. }
  2746. datamap = null;
  2747. priv = null;
  2748. grid = null;
  2749. selection = null;
  2750. editorManager = null;
  2751. instance = null;
  2752. GridSettings = null;
  2753. };
  2754. /**
  2755. * Replacement for all methods after Handsotnable was destroyed.
  2756. *
  2757. * @private
  2758. */
  2759. function postMortem() {
  2760. throw new Error('This method cannot be called because this Handsontable instance has been destroyed');
  2761. }
  2762. /**
  2763. * Returns the active editor object.
  2764. *
  2765. * @memberof Core#
  2766. * @function getActiveEditor
  2767. * @returns {Object} The active editor object.
  2768. */
  2769. this.getActiveEditor = function () {
  2770. return editorManager.getActiveEditor();
  2771. };
  2772. /**
  2773. * Returns plugin instance using the plugin name provided.
  2774. *
  2775. * @memberof Core#
  2776. * @function getPlugin
  2777. * @param {String} pluginName The plugin name.
  2778. * @returns {*} The plugin instance.
  2779. * @since 0.15.0
  2780. */
  2781. this.getPlugin = function (pluginName) {
  2782. return (0, _plugins.getPlugin)(this, pluginName);
  2783. };
  2784. /**
  2785. * Returns the Handsontable instance.
  2786. *
  2787. * @memberof Core#
  2788. * @function getInstance
  2789. * @returns {Handsontable} The Handsontable instance.
  2790. */
  2791. this.getInstance = function () {
  2792. return instance;
  2793. };
  2794. /**
  2795. * Adds listener to the specified hook name (only for this Handsontable instance).
  2796. *
  2797. * @memberof Core#
  2798. * @function addHook
  2799. * @see Hooks#add
  2800. * @param {String} key Hook name.
  2801. * @param {Function|Array} callback Function or array of Functions.
  2802. *
  2803. * @example
  2804. * ```js
  2805. * hot.addHook('beforeInit', myCallback);
  2806. * ```
  2807. */
  2808. this.addHook = function (key, callback) {
  2809. _pluginHooks2.default.getSingleton().add(key, callback, instance);
  2810. };
  2811. /**
  2812. * Check if for a specified hook name there are added listeners (only for this Handsontable instance).
  2813. *
  2814. * @memberof Core#
  2815. * @function hasHook
  2816. * @see Hooks#has
  2817. * @param {String} key Hook name
  2818. * @return {Boolean}
  2819. *
  2820. * @example
  2821. * ```js
  2822. * var hasBeforeInitListeners = hot.hasHook('beforeInit');
  2823. * ```
  2824. */
  2825. this.hasHook = function (key) {
  2826. return _pluginHooks2.default.getSingleton().has(key, instance);
  2827. };
  2828. /**
  2829. * Adds listener to specified hook name (only for this Handsontable instance).
  2830. * After the listener is triggered, it will be automatically removed.
  2831. *
  2832. * @memberof Core#
  2833. * @function addHookOnce
  2834. * @see Hooks#once
  2835. * @param {String} key Hook name.
  2836. * @param {Function|Array} callback Function or array of Functions.
  2837. *
  2838. * @example
  2839. * ```js
  2840. * hot.addHookOnce('beforeInit', myCallback);
  2841. * ```
  2842. */
  2843. this.addHookOnce = function (key, callback) {
  2844. _pluginHooks2.default.getSingleton().once(key, callback, instance);
  2845. };
  2846. /**
  2847. * Removes the hook listener previously registered with {@link Core#addHook}.
  2848. *
  2849. * @memberof Core#
  2850. * @function removeHook
  2851. * @see Hooks#remove
  2852. * @param {String} key Hook name.
  2853. * @param {Function} callback Function which have been registered via {@link Core#addHook}.
  2854. *
  2855. * @example
  2856. * ```js
  2857. * hot.removeHook('beforeInit', myCallback);
  2858. * ```
  2859. */
  2860. this.removeHook = function (key, callback) {
  2861. _pluginHooks2.default.getSingleton().remove(key, callback, instance);
  2862. };
  2863. /**
  2864. * Run the callbacks for the hook provided in the `key` argument using the parameters given in the other arguments.
  2865. *
  2866. * @memberof Core#
  2867. * @function runHooks
  2868. * @see Hooks#run
  2869. * @param {String} key Hook name.
  2870. * @param {*} [p1] Argument passed to the callback.
  2871. * @param {*} [p2] Argument passed to the callback.
  2872. * @param {*} [p3] Argument passed to the callback.
  2873. * @param {*} [p4] Argument passed to the callback.
  2874. * @param {*} [p5] Argument passed to the callback.
  2875. * @param {*} [p6] Argument passed to the callback.
  2876. * @returns {*}
  2877. *
  2878. * @example
  2879. * ```js
  2880. * hot.runHooks('beforeInit');
  2881. * ```
  2882. */
  2883. this.runHooks = function (key, p1, p2, p3, p4, p5, p6) {
  2884. return _pluginHooks2.default.getSingleton().run(instance, key, p1, p2, p3, p4, p5, p6);
  2885. };
  2886. this.timeouts = [];
  2887. /**
  2888. * Sets timeout. Purpose of this method is to clear all known timeouts when `destroy` method is called.
  2889. *
  2890. * @param {*} handle
  2891. * @private
  2892. */
  2893. this._registerTimeout = function (handle) {
  2894. this.timeouts.push(handle);
  2895. };
  2896. /**
  2897. * Clears all known timeouts.
  2898. *
  2899. * @private
  2900. */
  2901. this._clearTimeouts = function () {
  2902. for (var i = 0, ilen = this.timeouts.length; i < ilen; i++) {
  2903. clearTimeout(this.timeouts[i]);
  2904. }
  2905. };
  2906. /**
  2907. * Handsontable version
  2908. *
  2909. * @type {String}
  2910. */
  2911. // this.version = Handsontable.version;
  2912. _pluginHooks2.default.getSingleton().run(instance, 'construct');
  2913. };