UNPKG

66.4 kBJavaScriptView Raw
1/**
2 * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
3 * @version v18.1.2
4 * @link http://www.ag-grid.com/
5 * @license MIT
6 */
7"use strict";
8Object.defineProperty(exports, "__esModule", { value: true });
9var constants_1 = require("./constants");
10var FUNCTION_STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
11var FUNCTION_ARGUMENT_NAMES = /([^\s,]+)/g;
12var AG_GRID_STOP_PROPAGATION = '__ag_Grid_Stop_Propagation';
13// util class, only used when debugging, for printing time to console
14var Timer = (function () {
15 function Timer() {
16 this.timestamp = new Date().getTime();
17 }
18 Timer.prototype.print = function (msg) {
19 var duration = (new Date().getTime()) - this.timestamp;
20 console.log(msg + " = " + duration);
21 this.timestamp = new Date().getTime();
22 };
23 return Timer;
24}());
25exports.Timer = Timer;
26/** HTML Escapes. */
27var HTML_ESCAPES = {
28 '&': '&',
29 '<': '&lt;',
30 '>': '&gt;',
31 '"': '&quot;',
32 "'": '&#39;'
33};
34var reUnescapedHtml = /[&<>"']/g;
35var Utils = (function () {
36 function Utils() {
37 }
38 // if the key was passed before, then doesn't execute the func
39 Utils.doOnce = function (func, key) {
40 if (this.doOnceFlags[key]) {
41 return;
42 }
43 func();
44 this.doOnceFlags[key] = true;
45 };
46 // returns true if the event is close to the original event by X pixels either vertically or horizontally.
47 // we only start dragging after X pixels so this allows us to know if we should start dragging yet.
48 Utils.areEventsNear = function (e1, e2, pixelCount) {
49 // by default, we wait 4 pixels before starting the drag
50 if (pixelCount === 0) {
51 return false;
52 }
53 var diffX = Math.abs(e1.clientX - e2.clientX);
54 var diffY = Math.abs(e1.clientY - e2.clientY);
55 return Math.max(diffX, diffY) <= pixelCount;
56 };
57 Utils.shallowCompare = function (arr1, arr2) {
58 // if both are missing, then they are the same
59 if (this.missing(arr1) && this.missing(arr2)) {
60 return true;
61 }
62 // if one is present, but other is missing, then then are different
63 if (this.missing(arr1) || this.missing(arr2)) {
64 return false;
65 }
66 if (arr1.length !== arr2.length) {
67 return false;
68 }
69 for (var i = 0; i < arr1.length; i++) {
70 if (arr1[i] !== arr2[i]) {
71 return false;
72 }
73 }
74 return true;
75 };
76 Utils.getNameOfClass = function (TheClass) {
77 var funcNameRegex = /function (.{1,})\(/;
78 var funcAsString = TheClass.toString();
79 var results = (funcNameRegex).exec(funcAsString);
80 return (results && results.length > 1) ? results[1] : "";
81 };
82 Utils.values = function (object) {
83 var result = [];
84 this.iterateObject(object, function (key, value) {
85 result.push(value);
86 });
87 return result;
88 };
89 Utils.getValueUsingField = function (data, field, fieldContainsDots) {
90 if (!field || !data) {
91 return;
92 }
93 // if no '.', then it's not a deep value
94 if (!fieldContainsDots) {
95 return data[field];
96 }
97 else {
98 // otherwise it is a deep value, so need to dig for it
99 var fields = field.split('.');
100 var currentObject = data;
101 for (var i = 0; i < fields.length; i++) {
102 currentObject = currentObject[fields[i]];
103 if (this.missing(currentObject)) {
104 return null;
105 }
106 }
107 return currentObject;
108 }
109 };
110 Utils.getScrollLeft = function (element, rtl) {
111 var scrollLeft = element.scrollLeft;
112 if (rtl) {
113 // Absolute value - for FF that reports RTL scrolls in negative numbers
114 scrollLeft = Math.abs(scrollLeft);
115 // Get Chrome and Safari to return the same value as well
116 if (this.isBrowserSafari() || this.isBrowserChrome()) {
117 scrollLeft = element.scrollWidth - element.clientWidth - scrollLeft;
118 }
119 }
120 return scrollLeft;
121 };
122 Utils.cleanNumber = function (value) {
123 if (typeof value === 'string') {
124 value = parseInt(value);
125 }
126 if (typeof value === 'number') {
127 value = Math.floor(value);
128 }
129 else {
130 value = null;
131 }
132 return value;
133 };
134 Utils.setScrollLeft = function (element, value, rtl) {
135 if (rtl) {
136 // Chrome and Safari when doing RTL have the END position of the scroll as zero, not the start
137 if (this.isBrowserSafari() || this.isBrowserChrome()) {
138 value = element.scrollWidth - element.clientWidth - value;
139 }
140 // Firefox uses negative numbers when doing RTL scrolling
141 if (this.isBrowserFirefox()) {
142 value *= -1;
143 }
144 }
145 element.scrollLeft = value;
146 };
147 Utils.iterateNamedNodeMap = function (map, callback) {
148 if (!map) {
149 return;
150 }
151 for (var i = 0; i < map.length; i++) {
152 var attr = map[i];
153 callback(attr.name, attr.value);
154 }
155 };
156 Utils.iterateObject = function (object, callback) {
157 if (this.missing(object)) {
158 return;
159 }
160 if (Array.isArray(object)) {
161 object.forEach(function (value, index) {
162 callback(index + '', value);
163 });
164 }
165 else {
166 var keys = Object.keys(object);
167 for (var i = 0; i < keys.length; i++) {
168 var key = keys[i];
169 var value = object[key];
170 callback(key, value);
171 }
172 }
173 };
174 Utils.cloneObject = function (object) {
175 var copy = {};
176 var keys = Object.keys(object);
177 for (var i = 0; i < keys.length; i++) {
178 var key = keys[i];
179 var value = object[key];
180 copy[key] = value;
181 }
182 return copy;
183 };
184 Utils.map = function (array, callback) {
185 var result = [];
186 for (var i = 0; i < array.length; i++) {
187 var item = array[i];
188 var mappedItem = callback(item);
189 result.push(mappedItem);
190 }
191 return result;
192 };
193 Utils.mapObject = function (object, callback) {
194 var result = [];
195 Utils.iterateObject(object, function (key, value) {
196 result.push(callback(value));
197 });
198 return result;
199 };
200 Utils.forEach = function (array, callback) {
201 if (!array) {
202 return;
203 }
204 for (var i = 0; i < array.length; i++) {
205 var value = array[i];
206 callback(value, i);
207 }
208 };
209 Utils.filter = function (array, callback) {
210 var result = [];
211 array.forEach(function (item) {
212 if (callback(item)) {
213 result.push(item);
214 }
215 });
216 return result;
217 };
218 Utils.getAllKeysInObjects = function (objects) {
219 var allValues = {};
220 objects.forEach(function (obj) {
221 if (obj) {
222 Object.keys(obj).forEach(function (key) { return allValues[key] = null; });
223 }
224 });
225 return Object.keys(allValues);
226 };
227 Utils.mergeDeep = function (dest, source) {
228 if (this.exists(source)) {
229 this.iterateObject(source, function (key, newValue) {
230 var oldValue = dest[key];
231 if (oldValue === newValue) {
232 return;
233 }
234 if (typeof oldValue === 'object' && typeof newValue === 'object') {
235 Utils.mergeDeep(oldValue, newValue);
236 }
237 else {
238 dest[key] = newValue;
239 }
240 });
241 }
242 };
243 Utils.assign = function (object) {
244 var _this = this;
245 var sources = [];
246 for (var _i = 1; _i < arguments.length; _i++) {
247 sources[_i - 1] = arguments[_i];
248 }
249 sources.forEach(function (source) {
250 if (_this.exists(source)) {
251 _this.iterateObject(source, function (key, value) {
252 object[key] = value;
253 });
254 }
255 });
256 return object;
257 };
258 Utils.parseYyyyMmDdToDate = function (yyyyMmDd, separator) {
259 try {
260 if (!yyyyMmDd)
261 return null;
262 if (yyyyMmDd.indexOf(separator) === -1)
263 return null;
264 var fields = yyyyMmDd.split(separator);
265 if (fields.length != 3)
266 return null;
267 return new Date(Number(fields[0]), Number(fields[1]) - 1, Number(fields[2]));
268 }
269 catch (e) {
270 return null;
271 }
272 };
273 Utils.serializeDateToYyyyMmDd = function (date, separator) {
274 if (!date)
275 return null;
276 return date.getFullYear() + separator + Utils.pad(date.getMonth() + 1, 2) + separator + Utils.pad(date.getDate(), 2);
277 };
278 Utils.pad = function (num, totalStringSize) {
279 var asString = num + "";
280 while (asString.length < totalStringSize)
281 asString = "0" + asString;
282 return asString;
283 };
284 Utils.pushAll = function (target, source) {
285 if (this.missing(source) || this.missing(target)) {
286 return;
287 }
288 source.forEach(function (func) { return target.push(func); });
289 };
290 Utils.createArrayOfNumbers = function (first, last) {
291 var result = [];
292 for (var i = first; i <= last; i++) {
293 result.push(i);
294 }
295 return result;
296 };
297 Utils.getFunctionParameters = function (func) {
298 var fnStr = func.toString().replace(FUNCTION_STRIP_COMMENTS, '');
299 var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(FUNCTION_ARGUMENT_NAMES);
300 if (result === null) {
301 return [];
302 }
303 else {
304 return result;
305 }
306 };
307 Utils.find = function (collection, predicate, value) {
308 if (collection === null || collection === undefined) {
309 return null;
310 }
311 if (!Array.isArray(collection)) {
312 var objToArray = this.values(collection);
313 return this.find(objToArray, predicate, value);
314 }
315 var collectionAsArray = collection;
316 var firstMatchingItem;
317 for (var i = 0; i < collectionAsArray.length; i++) {
318 var item = collectionAsArray[i];
319 if (typeof predicate === 'string') {
320 if (item[predicate] === value) {
321 firstMatchingItem = item;
322 break;
323 }
324 }
325 else {
326 var callback = predicate;
327 if (callback(item)) {
328 firstMatchingItem = item;
329 break;
330 }
331 }
332 }
333 return firstMatchingItem;
334 };
335 Utils.toStrings = function (array) {
336 return this.map(array, function (item) {
337 if (item === undefined || item === null || !item.toString) {
338 return null;
339 }
340 else {
341 return item.toString();
342 }
343 });
344 };
345 Utils.iterateArray = function (array, callback) {
346 for (var index = 0; index < array.length; index++) {
347 var value = array[index];
348 callback(value, index);
349 }
350 };
351 //Returns true if it is a DOM node
352 //taken from: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
353 Utils.isNode = function (o) {
354 return (typeof Node === "function" ? o instanceof Node :
355 o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string");
356 };
357 //Returns true if it is a DOM element
358 //taken from: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
359 Utils.isElement = function (o) {
360 return (typeof HTMLElement === "function" ? o instanceof HTMLElement :
361 o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string");
362 };
363 Utils.isNodeOrElement = function (o) {
364 return this.isNode(o) || this.isElement(o);
365 };
366 // makes a copy of a node list into a list
367 Utils.copyNodeList = function (nodeList) {
368 var childCount = nodeList ? nodeList.length : 0;
369 var res = [];
370 for (var i = 0; i < childCount; i++) {
371 res.push(nodeList[i]);
372 }
373 return res;
374 };
375 Utils.isEventFromPrintableCharacter = function (event) {
376 var pressedChar = String.fromCharCode(event.charCode);
377 // newline is an exception, as it counts as a printable character, but we don't
378 // want to start editing when it is pressed. without this check, if user is in chrome
379 // and editing a cell, and they press ctrl+enter, the cell stops editing, and then
380 // starts editing again with a blank value (two 'key down' events are fired). to
381 // test this, remove the line below, edit a cell in chrome and hit ctrl+enter while editing.
382 // https://ag-grid.atlassian.net/browse/AG-605
383 if (this.isKeyPressed(event, constants_1.Constants.KEY_NEW_LINE)) {
384 return false;
385 }
386 if (exports._.exists(event.key)) {
387 // modern browser will implement key, so we return if key is length 1, eg if it is 'a' for the
388 // a key, or '2' for the '2' key. non-printable characters have names, eg 'Enter' or 'Backspace'.
389 var printableCharacter = event.key.length === 1;
390 // IE11 & Edge treat the numpad del key differently - with numlock on we get "Del" for key,
391 // so this addition checks if its IE11/Edge and handles that specific case the same was as all other browers
392 var numpadDelWithNumlockOnForEdgeOrIe = Utils.isNumpadDelWithNumlockOnForEdgeOrIe(event);
393 return printableCharacter || numpadDelWithNumlockOnForEdgeOrIe;
394 }
395 else {
396 // otherwise, for older browsers, we test against a list of characters, which doesn't include
397 // accents for non-English, but don't care much, as most users are on modern browsers
398 return Utils.PRINTABLE_CHARACTERS.indexOf(pressedChar) >= 0;
399 }
400 };
401 //adds all type of change listeners to an element, intended to be a text field
402 Utils.addChangeListener = function (element, listener) {
403 element.addEventListener("changed", listener);
404 element.addEventListener("paste", listener);
405 element.addEventListener("input", listener);
406 // IE doesn't fire changed for special keys (eg delete, backspace), so need to
407 // listen for this further ones
408 element.addEventListener("keydown", listener);
409 element.addEventListener("keyup", listener);
410 };
411 //if value is undefined, null or blank, returns null, otherwise returns the value
412 Utils.makeNull = function (value) {
413 var valueNoType = value;
414 if (value === null || value === undefined || valueNoType === "") {
415 return null;
416 }
417 else {
418 return value;
419 }
420 };
421 Utils.missing = function (value) {
422 return !this.exists(value);
423 };
424 Utils.missingOrEmpty = function (value) {
425 return this.missing(value) || value.length === 0;
426 };
427 Utils.missingOrEmptyObject = function (value) {
428 return this.missing(value) || Object.keys(value).length === 0;
429 };
430 Utils.exists = function (value) {
431 if (value === null || value === undefined || value === '') {
432 return false;
433 }
434 else {
435 return true;
436 }
437 };
438 Utils.firstExistingValue = function () {
439 var values = [];
440 for (var _i = 0; _i < arguments.length; _i++) {
441 values[_i] = arguments[_i];
442 }
443 for (var i = 0; i < values.length; i++) {
444 var value = values[i];
445 if (exports._.exists(value))
446 return value;
447 }
448 return null;
449 };
450 Utils.anyExists = function (values) {
451 if (values) {
452 for (var i = 0; i < values.length; i++) {
453 if (this.exists(values[i])) {
454 return true;
455 }
456 }
457 }
458 return false;
459 };
460 Utils.existsAndNotEmpty = function (value) {
461 return this.exists(value) && value.length > 0;
462 };
463 Utils.removeAllChildren = function (node) {
464 if (node) {
465 while (node.hasChildNodes()) {
466 node.removeChild(node.lastChild);
467 }
468 }
469 };
470 Utils.removeElement = function (parent, cssSelector) {
471 this.removeFromParent(parent.querySelector(cssSelector));
472 };
473 Utils.removeFromParent = function (node) {
474 if (node && node.parentNode) {
475 node.parentNode.removeChild(node);
476 }
477 };
478 Utils.isVisible = function (element) {
479 return (element.offsetParent !== null);
480 };
481 /**
482 * loads the template and returns it as an element. makes up for no simple way in
483 * the dom api to load html directly, eg we cannot do this: document.createElement(template)
484 */
485 Utils.loadTemplate = function (template) {
486 var tempDiv = document.createElement("div");
487 tempDiv.innerHTML = template;
488 return tempDiv.firstChild;
489 };
490 Utils.appendHtml = function (eContainer, htmlTemplate) {
491 if (eContainer.lastChild) {
492 // https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
493 // we put the items at the start, so new items appear underneath old items,
494 // so when expanding/collapsing groups, the new rows don't go on top of the
495 // rows below that are moving our of the way
496 eContainer.insertAdjacentHTML('afterbegin', htmlTemplate);
497 }
498 else {
499 eContainer.innerHTML = htmlTemplate;
500 }
501 };
502 Utils.addOrRemoveCssClass = function (element, className, addOrRemove) {
503 if (addOrRemove) {
504 this.addCssClass(element, className);
505 }
506 else {
507 this.removeCssClass(element, className);
508 }
509 };
510 Utils.callIfPresent = function (func) {
511 if (func) {
512 func();
513 }
514 };
515 Utils.addCssClass = function (element, className) {
516 var _this = this;
517 if (!className || className.length === 0) {
518 return;
519 }
520 if (className.indexOf(' ') >= 0) {
521 className.split(' ').forEach(function (value) { return _this.addCssClass(element, value); });
522 return;
523 }
524 if (element.classList) {
525 if (!element.classList.contains(className)) {
526 element.classList.add(className);
527 }
528 }
529 else {
530 if (element.className && element.className.length > 0) {
531 var cssClasses = element.className.split(' ');
532 if (cssClasses.indexOf(className) < 0) {
533 cssClasses.push(className);
534 element.className = cssClasses.join(' ');
535 }
536 }
537 else {
538 element.className = className;
539 }
540 }
541 };
542 Utils.containsClass = function (element, className) {
543 if (element.classList) {
544 // for modern browsers
545 return element.classList.contains(className);
546 }
547 else if (element.className) {
548 // for older browsers, check against the string of class names
549 // if only one class, can check for exact match
550 var onlyClass = element.className === className;
551 // if many classes, check for class name, we have to pad with ' ' to stop other
552 // class names that are a substring of this class
553 var contains = element.className.indexOf(' ' + className + ' ') >= 0;
554 // the padding above then breaks when it's the first or last class names
555 var startsWithClass = element.className.indexOf(className + ' ') === 0;
556 var endsWithClass = element.className.lastIndexOf(' ' + className) === (element.className.length - className.length - 1);
557 return onlyClass || contains || startsWithClass || endsWithClass;
558 }
559 else {
560 // if item is not a node
561 return false;
562 }
563 };
564 Utils.getElementAttribute = function (element, attributeName) {
565 if (element.attributes) {
566 if (element.attributes[attributeName]) {
567 var attribute = element.attributes[attributeName];
568 return attribute.value;
569 }
570 else {
571 return null;
572 }
573 }
574 else {
575 return null;
576 }
577 };
578 Utils.offsetHeight = function (element) {
579 return element && element.clientHeight ? element.clientHeight : 0;
580 };
581 Utils.offsetWidth = function (element) {
582 return element && element.clientWidth ? element.clientWidth : 0;
583 };
584 Utils.sortNumberArray = function (numberArray) {
585 numberArray.sort(function (a, b) { return a - b; });
586 };
587 Utils.removeCssClass = function (element, className) {
588 if (element.classList) {
589 if (element.classList.contains(className)) {
590 element.classList.remove(className);
591 }
592 }
593 else {
594 if (element.className && element.className.length > 0) {
595 var cssClasses = element.className.split(' ');
596 if (cssClasses.indexOf(className) >= 0) {
597 // remove all instances of the item, not just the first, in case it's in more than once
598 while (cssClasses.indexOf(className) >= 0) {
599 cssClasses.splice(cssClasses.indexOf(className), 1);
600 }
601 element.className = cssClasses.join(' ');
602 }
603 }
604 }
605 };
606 Utils.removeRepeatsFromArray = function (array, object) {
607 if (!array) {
608 return;
609 }
610 for (var index = array.length - 2; index >= 0; index--) {
611 var thisOneMatches = array[index] === object;
612 var nextOneMatches = array[index + 1] === object;
613 if (thisOneMatches && nextOneMatches) {
614 array.splice(index + 1, 1);
615 }
616 }
617 };
618 Utils.removeFromArray = function (array, object) {
619 if (array.indexOf(object) >= 0) {
620 array.splice(array.indexOf(object), 1);
621 }
622 };
623 Utils.removeAllFromArray = function (array, toRemove) {
624 toRemove.forEach(function (item) {
625 if (array.indexOf(item) >= 0) {
626 array.splice(array.indexOf(item), 1);
627 }
628 });
629 };
630 Utils.insertIntoArray = function (array, object, toIndex) {
631 array.splice(toIndex, 0, object);
632 };
633 Utils.insertArrayIntoArray = function (dest, src, toIndex) {
634 if (this.missing(dest) || this.missing(src)) {
635 return;
636 }
637 // put items in backwards, otherwise inserted items end up in reverse order
638 for (var i = src.length - 1; i >= 0; i--) {
639 var item = src[i];
640 this.insertIntoArray(dest, item, toIndex);
641 }
642 };
643 Utils.moveInArray = function (array, objectsToMove, toIndex) {
644 var _this = this;
645 // first take out it items from the array
646 objectsToMove.forEach(function (obj) {
647 _this.removeFromArray(array, obj);
648 });
649 // now add the objects, in same order as provided to us, that means we start at the end
650 // as the objects will be pushed to the right as they are inserted
651 objectsToMove.slice().reverse().forEach(function (obj) {
652 _this.insertIntoArray(array, obj, toIndex);
653 });
654 };
655 Utils.defaultComparator = function (valueA, valueB, accentedCompare) {
656 if (accentedCompare === void 0) { accentedCompare = false; }
657 var valueAMissing = valueA === null || valueA === undefined;
658 var valueBMissing = valueB === null || valueB === undefined;
659 // this is for aggregations sum and avg, where the result can be a number that is wrapped.
660 // if we didn't do this, then the toString() value would be used, which would result in
661 // the strings getting used instead of the numbers.
662 if (valueA && valueA.toNumber) {
663 valueA = valueA.toNumber();
664 }
665 if (valueB && valueB.toNumber) {
666 valueB = valueB.toNumber();
667 }
668 if (valueAMissing && valueBMissing) {
669 return 0;
670 }
671 if (valueAMissing) {
672 return -1;
673 }
674 if (valueBMissing) {
675 return 1;
676 }
677 if (typeof valueA === "string") {
678 if (!accentedCompare) {
679 return doQuickCompare(valueA, valueB);
680 }
681 else {
682 try {
683 // using local compare also allows chinese comparisons
684 return valueA.localeCompare(valueB);
685 }
686 catch (e) {
687 // if something wrong with localeCompare, eg not supported
688 // by browser, then just continue with the quick one
689 return doQuickCompare(valueA, valueB);
690 }
691 }
692 }
693 if (valueA < valueB) {
694 return -1;
695 }
696 else if (valueA > valueB) {
697 return 1;
698 }
699 else {
700 return 0;
701 }
702 function doQuickCompare(a, b) {
703 return (a > b ? 1 : (a < b ? -1 : 0));
704 }
705 };
706 Utils.compareArrays = function (array1, array2) {
707 if (this.missing(array1) && this.missing(array2)) {
708 return true;
709 }
710 if (this.missing(array1) || this.missing(array2)) {
711 return false;
712 }
713 if (array1.length !== array2.length) {
714 return false;
715 }
716 for (var i = 0; i < array1.length; i++) {
717 if (array1[i] !== array2[i]) {
718 return false;
719 }
720 }
721 return true;
722 };
723 Utils.ensureDomOrder = function (eContainer, eChild, eChildBefore) {
724 // if already in right order, do nothing
725 if (eChildBefore && eChildBefore.nextSibling === eChild) {
726 return;
727 }
728 if (eChildBefore) {
729 if (eChildBefore.nextSibling) {
730 // insert between the eRowBefore and the row after it
731 eContainer.insertBefore(eChild, eChildBefore.nextSibling);
732 }
733 else {
734 // if nextSibling is missing, means other row is at end, so just append new row at the end
735 eContainer.appendChild(eChild);
736 }
737 }
738 else {
739 // otherwise put at start
740 if (eContainer.firstChild) {
741 // insert it at the first location
742 eContainer.insertBefore(eChild, eContainer.firstChild);
743 }
744 }
745 };
746 Utils.insertWithDomOrder = function (eContainer, eChild, eChildBefore) {
747 if (eChildBefore) {
748 if (eChildBefore.nextSibling) {
749 // insert between the eRowBefore and the row after it
750 eContainer.insertBefore(eChild, eChildBefore.nextSibling);
751 }
752 else {
753 // if nextSibling is missing, means other row is at end, so just append new row at the end
754 eContainer.appendChild(eChild);
755 }
756 }
757 else {
758 if (eContainer.firstChild) {
759 // insert it at the first location
760 eContainer.insertBefore(eChild, eContainer.firstChild);
761 }
762 else {
763 // otherwise eContainer is empty, so just append it
764 eContainer.appendChild(eChild);
765 }
766 }
767 };
768 Utils.insertTemplateWithDomOrder = function (eContainer, htmlTemplate, eChildBefore) {
769 var res;
770 if (eChildBefore) {
771 // if previous element exists, just slot in after the previous element
772 eChildBefore.insertAdjacentHTML('afterend', htmlTemplate);
773 res = eChildBefore.nextSibling;
774 }
775 else {
776 if (eContainer.firstChild) {
777 // insert it at the first location
778 eContainer.insertAdjacentHTML('afterbegin', htmlTemplate);
779 }
780 else {
781 // otherwise eContainer is empty, so just append it
782 eContainer.innerHTML = htmlTemplate;
783 }
784 res = eContainer.firstChild;
785 }
786 return res;
787 };
788 Utils.every = function (items, callback) {
789 if (!items || items.length === 0) {
790 return true;
791 }
792 for (var i = 0; i < items.length; i++) {
793 if (!callback(items[i])) {
794 return false;
795 }
796 }
797 return true;
798 };
799 Utils.toStringOrNull = function (value) {
800 if (this.exists(value) && value.toString) {
801 return value.toString();
802 }
803 else {
804 return null;
805 }
806 };
807 Utils.formatWidth = function (width) {
808 if (typeof width === "number") {
809 return width + "px";
810 }
811 else {
812 return width;
813 }
814 };
815 Utils.formatNumberTwoDecimalPlacesAndCommas = function (value) {
816 if (typeof value !== 'number') {
817 return '';
818 }
819 // took this from: http://blog.tompawlak.org/number-currency-formatting-javascript
820 return (Math.round(value * 100) / 100).toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
821 };
822 // the native method number.toLocaleString(undefined, {minimumFractionDigits: 0}) puts in decimal places in IE,
823 // so we use this method instead
824 Utils.formatNumberCommas = function (value) {
825 if (typeof value !== 'number') {
826 return '';
827 }
828 // took this from: http://blog.tompawlak.org/number-currency-formatting-javascript
829 return value.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
830 };
831 Utils.prependDC = function (parent, documentFragment) {
832 if (this.exists(parent.firstChild)) {
833 parent.insertBefore(documentFragment, parent.firstChild);
834 }
835 else {
836 parent.appendChild(documentFragment);
837 }
838 };
839 /**
840 * If icon provided, use this (either a string, or a function callback).
841 * if not, then use the default icon from the theme
842 */
843 Utils.createIcon = function (iconName, gridOptionsWrapper, column) {
844 var iconContents = this.createIconNoSpan(iconName, gridOptionsWrapper, column);
845 if (iconContents.className.indexOf('ag-icon') > -1) {
846 return iconContents;
847 }
848 else {
849 var eResult = document.createElement('span');
850 eResult.appendChild(iconContents);
851 return eResult;
852 }
853 };
854 Utils.createIconNoSpan = function (iconName, gridOptionsWrapper, column) {
855 var userProvidedIcon;
856 // check col for icon first
857 if (column && column.getColDef().icons) {
858 userProvidedIcon = column.getColDef().icons[iconName];
859 }
860 // it not in col, try grid options
861 if (!userProvidedIcon && gridOptionsWrapper.getIcons()) {
862 userProvidedIcon = gridOptionsWrapper.getIcons()[iconName];
863 }
864 // now if user provided, use it
865 if (userProvidedIcon) {
866 var rendererResult = void 0;
867 if (typeof userProvidedIcon === 'function') {
868 rendererResult = userProvidedIcon();
869 }
870 else if (typeof userProvidedIcon === 'string') {
871 rendererResult = userProvidedIcon;
872 }
873 else {
874 throw 'icon from grid options needs to be a string or a function';
875 }
876 if (typeof rendererResult === 'string') {
877 return this.loadTemplate(rendererResult);
878 }
879 else if (this.isNodeOrElement(rendererResult)) {
880 return rendererResult;
881 }
882 else {
883 throw 'iconRenderer should return back a string or a dom object';
884 }
885 }
886 else {
887 var span = document.createElement('span');
888 var cssClass = this.iconNameClassMap[iconName];
889 if (!cssClass) {
890 throw new Error(iconName + " did not find class");
891 }
892 span.setAttribute("class", "ag-icon ag-icon-" + cssClass);
893 return span;
894 }
895 };
896 Utils.addStylesToElement = function (eElement, styles) {
897 var _this = this;
898 if (!styles) {
899 return;
900 }
901 Object.keys(styles).forEach(function (key) {
902 var keyCamelCase = _this.hyphenToCamelCase(key);
903 eElement.style[keyCamelCase] = styles[key];
904 });
905 };
906 Utils.isHorizontalScrollShowing = function (element) {
907 return element.clientWidth < element.scrollWidth;
908 };
909 Utils.isVerticalScrollShowing = function (element) {
910 return element.clientHeight < element.scrollHeight;
911 };
912 Utils.getMaxDivHeight = function () {
913 if (!document.body) {
914 return -1;
915 }
916 var res = 1000000;
917 // FF reports the height back but still renders blank after ~6M px
918 var testUpTo = navigator.userAgent.toLowerCase().match(/firefox/) ? 6000000 : 1000000000;
919 var div = this.loadTemplate("<div/>");
920 document.body.appendChild(div);
921 while (true) {
922 var test = res * 2;
923 div.style.height = test + 'px';
924 if (test > testUpTo || div.clientHeight !== test) {
925 break;
926 }
927 else {
928 res = test;
929 }
930 }
931 document.body.removeChild(div);
932 return res;
933 };
934 Utils.getScrollbarWidth = function () {
935 var outer = document.createElement("div");
936 outer.style.visibility = "hidden";
937 outer.style.width = "100px";
938 outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps
939 document.body.appendChild(outer);
940 var widthNoScroll = outer.offsetWidth;
941 // force scrollbars
942 outer.style.overflow = "scroll";
943 // add inner div
944 var inner = document.createElement("div");
945 inner.style.width = "100%";
946 outer.appendChild(inner);
947 var widthWithScroll = inner.offsetWidth;
948 // remove divs
949 outer.parentNode.removeChild(outer);
950 return widthNoScroll - widthWithScroll;
951 };
952 Utils.isKeyPressed = function (event, keyToCheck) {
953 var pressedKey = event.which || event.keyCode;
954 return pressedKey === keyToCheck;
955 };
956 Utils.setVisible = function (element, visible) {
957 this.addOrRemoveCssClass(element, 'ag-hidden', !visible);
958 };
959 Utils.setHidden = function (element, hidden) {
960 this.addOrRemoveCssClass(element, 'ag-visibility-hidden', hidden);
961 };
962 Utils.isBrowserIE = function () {
963 if (this.isIE === undefined) {
964 this.isIE = false || !!document.documentMode; // At least IE6
965 }
966 return this.isIE;
967 };
968 Utils.isBrowserEdge = function () {
969 if (this.isEdge === undefined) {
970 this.isEdge = !this.isBrowserIE() && !!window.StyleMedia;
971 }
972 return this.isEdge;
973 };
974 Utils.isBrowserSafari = function () {
975 if (this.isSafari === undefined) {
976 var anyWindow = window;
977 // taken from https://github.com/ag-grid/ag-grid/issues/550
978 this.isSafari = Object.prototype.toString.call(anyWindow.HTMLElement).indexOf('Constructor') > 0
979 || (function (p) {
980 return p.toString() === "[object SafariRemoteNotification]";
981 })(!anyWindow.safari || anyWindow.safari.pushNotification);
982 }
983 return this.isSafari;
984 };
985 Utils.isBrowserChrome = function () {
986 if (this.isChrome === undefined) {
987 var anyWindow = window;
988 this.isChrome = !!anyWindow.chrome && !!anyWindow.chrome.webstore;
989 }
990 return this.isChrome;
991 };
992 Utils.isBrowserFirefox = function () {
993 if (this.isFirefox === undefined) {
994 var anyWindow = window;
995 this.isFirefox = typeof anyWindow.InstallTrigger !== 'undefined';
996 }
997 return this.isFirefox;
998 };
999 Utils.isUserAgentIPad = function () {
1000 if (this.isIPad === undefined) {
1001 // taken from https://davidwalsh.name/detect-ipad
1002 this.isIPad = navigator.userAgent.match(/iPad|iPhone/i) != null;
1003 }
1004 return this.isIPad;
1005 };
1006 // srcElement is only available in IE. In all other browsers it is target
1007 // http://stackoverflow.com/questions/5301643/how-can-i-make-event-srcelement-work-in-firefox-and-what-does-it-mean
1008 Utils.getTarget = function (event) {
1009 var eventNoType = event;
1010 return eventNoType.target || eventNoType.srcElement;
1011 };
1012 Utils.isElementInEventPath = function (element, event) {
1013 if (!event || !element) {
1014 return false;
1015 }
1016 var path = exports._.getEventPath(event);
1017 return path.indexOf(element) >= 0;
1018 };
1019 Utils.createEventPath = function (event) {
1020 var res = [];
1021 var pointer = exports._.getTarget(event);
1022 while (pointer) {
1023 res.push(pointer);
1024 pointer = pointer.parentElement;
1025 }
1026 return res;
1027 };
1028 // firefox doesn't have event.path set, or any alternative to it, so we hack
1029 // it in. this is needed as it's to late to work out the path when the item is
1030 // removed from the dom. used by MouseEventService, where it works out if a click
1031 // was from the current grid, or a detail grid (master / detail).
1032 Utils.addAgGridEventPath = function (event) {
1033 event.__agGridEventPath = this.getEventPath(event);
1034 };
1035 Utils.getEventPath = function (event) {
1036 // https://stackoverflow.com/questions/39245488/event-path-undefined-with-firefox-and-vue-js
1037 // https://developer.mozilla.org/en-US/docs/Web/API/Event
1038 var eventNoType = event;
1039 if (event.deepPath) {
1040 // IE supports deep path
1041 return event.deepPath();
1042 }
1043 else if (eventNoType.path) {
1044 // Chrome supports path
1045 return eventNoType.path;
1046 }
1047 else if (eventNoType.composedPath) {
1048 // Firefox supports composePath
1049 return eventNoType.composedPath();
1050 }
1051 else if (eventNoType.__agGridEventPath) {
1052 // Firefox supports composePath
1053 return eventNoType.__agGridEventPath;
1054 }
1055 else {
1056 // and finally, if none of the above worked,
1057 // we create the path ourselves
1058 return this.createEventPath(event);
1059 }
1060 };
1061 Utils.forEachSnapshotFirst = function (list, callback) {
1062 if (list) {
1063 var arrayCopy = list.slice(0);
1064 arrayCopy.forEach(callback);
1065 }
1066 };
1067 // taken from: http://stackoverflow.com/questions/1038727/how-to-get-browser-width-using-javascript-code
1068 Utils.getBodyWidth = function () {
1069 if (document.body) {
1070 return document.body.clientWidth;
1071 }
1072 if (window.innerHeight) {
1073 return window.innerWidth;
1074 }
1075 if (document.documentElement && document.documentElement.clientWidth) {
1076 return document.documentElement.clientWidth;
1077 }
1078 return -1;
1079 };
1080 // taken from: http://stackoverflow.com/questions/1038727/how-to-get-browser-width-using-javascript-code
1081 Utils.getBodyHeight = function () {
1082 if (document.body) {
1083 return document.body.clientHeight;
1084 }
1085 if (window.innerHeight) {
1086 return window.innerHeight;
1087 }
1088 if (document.documentElement && document.documentElement.clientHeight) {
1089 return document.documentElement.clientHeight;
1090 }
1091 return -1;
1092 };
1093 Utils.setCheckboxState = function (eCheckbox, state) {
1094 if (typeof state === 'boolean') {
1095 eCheckbox.checked = state;
1096 eCheckbox.indeterminate = false;
1097 }
1098 else {
1099 // isNodeSelected returns back undefined if it's a group and the children
1100 // are a mix of selected and unselected
1101 eCheckbox.indeterminate = true;
1102 }
1103 };
1104 Utils.traverseNodesWithKey = function (nodes, callback) {
1105 var keyParts = [];
1106 recursiveSearchNodes(nodes);
1107 function recursiveSearchNodes(nodes) {
1108 nodes.forEach(function (node) {
1109 // also checking for children for tree data
1110 if (node.group || node.hasChildren()) {
1111 keyParts.push(node.key);
1112 var key = keyParts.join('|');
1113 callback(node, key);
1114 recursiveSearchNodes(node.childrenAfterGroup);
1115 keyParts.pop();
1116 }
1117 });
1118 }
1119 };
1120 // from https://gist.github.com/youssman/745578062609e8acac9f
1121 Utils.camelCaseToHyphen = function (str) {
1122 if (str === null || str === undefined) {
1123 return null;
1124 }
1125 return str.replace(/([A-Z])/g, function (g) { return '-' + g[0].toLowerCase(); });
1126 };
1127 // from https://stackoverflow.com/questions/6660977/convert-hyphens-to-camel-case-camelcase
1128 Utils.hyphenToCamelCase = function (str) {
1129 if (str === null || str === undefined) {
1130 return null;
1131 }
1132 return str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
1133 };
1134 // pas in an object eg: {color: 'black', top: '25px'} and it returns "color: black; top: 25px;" for html
1135 Utils.cssStyleObjectToMarkup = function (stylesToUse) {
1136 var _this = this;
1137 if (!stylesToUse) {
1138 return '';
1139 }
1140 var resParts = [];
1141 this.iterateObject(stylesToUse, function (styleKey, styleValue) {
1142 var styleKeyDashed = _this.camelCaseToHyphen(styleKey);
1143 resParts.push(styleKeyDashed + ": " + styleValue + ";");
1144 });
1145 return resParts.join(' ');
1146 };
1147 /**
1148 * From http://stackoverflow.com/questions/9716468/is-there-any-function-like-isnumeric-in-javascript-to-validate-numbers
1149 */
1150 Utils.isNumeric = function (value) {
1151 if (value === '')
1152 return false;
1153 return !isNaN(parseFloat(value)) && isFinite(value);
1154 };
1155 Utils.escape = function (toEscape) {
1156 if (toEscape === null || toEscape === undefined || !toEscape.replace) {
1157 return toEscape;
1158 }
1159 return toEscape.replace(reUnescapedHtml, function (chr) { return HTML_ESCAPES[chr]; });
1160 };
1161 // Taken from here: https://github.com/facebook/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js
1162 /**
1163 * Mouse wheel (and 2-finger trackpad) support on the web sucks. It is
1164 * complicated, thus this doc is long and (hopefully) detailed enough to answer
1165 * your questions.
1166 *
1167 * If you need to react to the mouse wheel in a predictable way, this code is
1168 * like your bestest friend. * hugs *
1169 *
1170 * As of today, there are 4 DOM event types you can listen to:
1171 *
1172 * 'wheel' -- Chrome(31+), FF(17+), IE(9+)
1173 * 'mousewheel' -- Chrome, IE(6+), Opera, Safari
1174 * 'MozMousePixelScroll' -- FF(3.5 only!) (2010-2013) -- don't bother!
1175 * 'DOMMouseScroll' -- FF(0.9.7+) since 2003
1176 *
1177 * So what to do? The is the best:
1178 *
1179 * normalizeWheel.getEventType();
1180 *
1181 * In your event callback, use this code to get sane interpretation of the
1182 * deltas. This code will return an object with properties:
1183 *
1184 * spinX -- normalized spin speed (use for zoom) - x plane
1185 * spinY -- " - y plane
1186 * pixelX -- normalized distance (to pixels) - x plane
1187 * pixelY -- " - y plane
1188 *
1189 * Wheel values are provided by the browser assuming you are using the wheel to
1190 * scroll a web page by a number of lines or pixels (or pages). Values can vary
1191 * significantly on different platforms and browsers, forgetting that you can
1192 * scroll at different speeds. Some devices (like trackpads) emit more events
1193 * at smaller increments with fine granularity, and some emit massive jumps with
1194 * linear speed or acceleration.
1195 *
1196 * This code does its best to normalize the deltas for you:
1197 *
1198 * - spin is trying to normalize how far the wheel was spun (or trackpad
1199 * dragged). This is super useful for zoom support where you want to
1200 * throw away the chunky scroll steps on the PC and make those equal to
1201 * the slow and smooth tiny steps on the Mac. Key data: This code tries to
1202 * resolve a single slow step on a wheel to 1.
1203 *
1204 * - pixel is normalizing the desired scroll delta in pixel units. You'll
1205 * get the crazy differences between browsers, but at least it'll be in
1206 * pixels!
1207 *
1208 * - positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This
1209 * should translate to positive value zooming IN, negative zooming OUT.
1210 * This matches the newer 'wheel' event.
1211 *
1212 * Why are there spinX, spinY (or pixels)?
1213 *
1214 * - spinX is a 2-finger side drag on the trackpad, and a shift + wheel turn
1215 * with a mouse. It results in side-scrolling in the browser by default.
1216 *
1217 * - spinY is what you expect -- it's the classic axis of a mouse wheel.
1218 *
1219 * - I dropped spinZ/pixelZ. It is supported by the DOM 3 'wheel' event and
1220 * probably is by browsers in conjunction with fancy 3D controllers .. but
1221 * you know.
1222 *
1223 * Implementation info:
1224 *
1225 * Examples of 'wheel' event if you scroll slowly (down) by one step with an
1226 * average mouse:
1227 *
1228 * OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120)
1229 * OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12)
1230 * OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A)
1231 * Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120)
1232 * Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120)
1233 *
1234 * On the trackpad:
1235 *
1236 * OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6)
1237 * OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A)
1238 *
1239 * On other/older browsers.. it's more complicated as there can be multiple and
1240 * also missing delta values.
1241 *
1242 * The 'wheel' event is more standard:
1243 *
1244 * http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
1245 *
1246 * The basics is that it includes a unit, deltaMode (pixels, lines, pages), and
1247 * deltaX, deltaY and deltaZ. Some browsers provide other values to maintain
1248 * backward compatibility with older events. Those other values help us
1249 * better normalize spin speed. Example of what the browsers provide:
1250 *
1251 * | event.wheelDelta | event.detail
1252 * ------------------+------------------+--------------
1253 * Safari v5/OS X | -120 | 0
1254 * Safari v5/Win7 | -120 | 0
1255 * Chrome v17/OS X | -120 | 0
1256 * Chrome v17/Win7 | -120 | 0
1257 * IE9/Win7 | -120 | undefined
1258 * Firefox v4/OS X | undefined | 1
1259 * Firefox v4/Win7 | undefined | 3
1260 *
1261 */
1262 Utils.normalizeWheel = function (event) {
1263 var PIXEL_STEP = 10;
1264 var LINE_HEIGHT = 40;
1265 var PAGE_HEIGHT = 800;
1266 // spinX, spinY
1267 var sX = 0;
1268 var sY = 0;
1269 // pixelX, pixelY
1270 var pX = 0;
1271 var pY = 0;
1272 // Legacy
1273 if ('detail' in event) {
1274 sY = event.detail;
1275 }
1276 if ('wheelDelta' in event) {
1277 sY = -event.wheelDelta / 120;
1278 }
1279 if ('wheelDeltaY' in event) {
1280 sY = -event.wheelDeltaY / 120;
1281 }
1282 if ('wheelDeltaX' in event) {
1283 sX = -event.wheelDeltaX / 120;
1284 }
1285 // side scrolling on FF with DOMMouseScroll
1286 if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
1287 sX = sY;
1288 sY = 0;
1289 }
1290 pX = sX * PIXEL_STEP;
1291 pY = sY * PIXEL_STEP;
1292 if ('deltaY' in event) {
1293 pY = event.deltaY;
1294 }
1295 if ('deltaX' in event) {
1296 pX = event.deltaX;
1297 }
1298 if ((pX || pY) && event.deltaMode) {
1299 if (event.deltaMode == 1) {
1300 pX *= LINE_HEIGHT;
1301 pY *= LINE_HEIGHT;
1302 }
1303 else {
1304 pX *= PAGE_HEIGHT;
1305 pY *= PAGE_HEIGHT;
1306 }
1307 }
1308 // Fall-back if spin cannot be determined
1309 if (pX && !sX) {
1310 sX = (pX < 1) ? -1 : 1;
1311 }
1312 if (pY && !sY) {
1313 sY = (pY < 1) ? -1 : 1;
1314 }
1315 return {
1316 spinX: sX,
1317 spinY: sY,
1318 pixelX: pX,
1319 pixelY: pY
1320 };
1321 };
1322 /**
1323 * https://stackoverflow.com/questions/24004791/can-someone-explain-the-debounce-function-in-javascript
1324 */
1325 Utils.debounce = function (func, wait, immediate) {
1326 if (immediate === void 0) { immediate = false; }
1327 // 'private' variable for instance
1328 // The returned function will be able to reference this due to closure.
1329 // Each call to the returned function will share this common timer.
1330 var timeout;
1331 // Calling debounce returns a new anonymous function
1332 return function () {
1333 // reference the context and args for the setTimeout function
1334 var context = this, args = arguments;
1335 // Should the function be called now? If immediate is true
1336 // and not already in a timeout then the answer is: Yes
1337 var callNow = immediate && !timeout;
1338 // This is the basic debounce behaviour where you can call this
1339 // function several times, but it will only execute once
1340 // [before or after imposing a delay].
1341 // Each time the returned function is called, the timer starts over.
1342 clearTimeout(timeout);
1343 // Set the new timeout
1344 timeout = setTimeout(function () {
1345 // Inside the timeout function, clear the timeout variable
1346 // which will let the next execution run when in 'immediate' mode
1347 timeout = null;
1348 // Check if the function already ran with the immediate flag
1349 if (!immediate) {
1350 // Call the original function with apply
1351 // apply lets you define the 'this' object as well as the arguments
1352 // (both captured before setTimeout)
1353 func.apply(context, args);
1354 }
1355 }, wait);
1356 // Immediate mode and no wait timer? Execute the function..
1357 if (callNow)
1358 func.apply(context, args);
1359 };
1360 };
1361 ;
1362 // a user once raised an issue - they said that when you opened a popup (eg context menu)
1363 // and then clicked on a selection checkbox, the popup wasn't closed. this is because the
1364 // popup listens for clicks on the body, however ag-grid WAS stopping propagation on the
1365 // checkbox clicks (so the rows didn't pick them up as row selection selection clicks).
1366 // to get around this, we have a pattern to stop propagation for the purposes of ag-Grid,
1367 // but we still let the event pass back to teh body.
1368 Utils.stopPropagationForAgGrid = function (event) {
1369 event[AG_GRID_STOP_PROPAGATION] = true;
1370 };
1371 Utils.isStopPropagationForAgGrid = function (event) {
1372 return event[AG_GRID_STOP_PROPAGATION] === true;
1373 };
1374 Utils.executeInAWhile = function (funcs) {
1375 this.executeAfter(funcs, 400);
1376 };
1377 Utils.executeNextVMTurn = function (funcs) {
1378 this.executeAfter(funcs, 0);
1379 };
1380 Utils.executeAfter = function (funcs, millis) {
1381 if (funcs.length > 0) {
1382 setTimeout(function () {
1383 funcs.forEach(function (func) { return func(); });
1384 }, millis);
1385 }
1386 };
1387 Utils.referenceCompare = function (left, right) {
1388 if (left == null && right == null)
1389 return true;
1390 if (left == null && right)
1391 return false;
1392 if (left && right == null)
1393 return false;
1394 return left === right;
1395 };
1396 Utils.get = function (source, expression, defaultValue) {
1397 if (source == null)
1398 return defaultValue;
1399 if (expression.indexOf('.') > -1) {
1400 var fields = expression.split('.');
1401 var thisKey = fields[0];
1402 var nextValue = source[thisKey];
1403 if (nextValue != null) {
1404 return Utils.get(nextValue, fields.slice(1, fields.length).join('.'), defaultValue);
1405 }
1406 else {
1407 return defaultValue;
1408 }
1409 }
1410 else {
1411 var nextValue = source[expression];
1412 return nextValue != null ? nextValue : defaultValue;
1413 }
1414 };
1415 Utils.addSafePassiveEventListener = function (eElement, event, listener) {
1416 eElement.addEventListener(event, listener, (Utils.passiveEvents.indexOf(event) > -1 ? { passive: true } : undefined));
1417 };
1418 Utils.camelCaseToHumanText = function (camelCase) {
1419 if (camelCase == null)
1420 return null;
1421 // Who needs to learn how to code when you have stack overflow!
1422 // from: https://stackoverflow.com/questions/15369566/putting-space-in-camel-case-string-using-regular-expression
1423 var rex = /([A-Z])([A-Z])([a-z])|([a-z])([A-Z])/g;
1424 var words = camelCase.replace(rex, '$1$4 $2$3$5').replace('.', ' ').split(' ');
1425 return words.map(function (word) { return word.substring(0, 1).toUpperCase() + ((word.length > 1) ? word.substring(1, word.length) : ''); }).join(' ');
1426 };
1427 // displays a message to the browser. this is useful in iPad, where you can't easily see the console.
1428 // so the javascript code can use this to give feedback. this is NOT intended to be called in production.
1429 // it is intended the ag-Grid developer calls this to troubleshoot, but then takes out the calls before
1430 // checking in.
1431 Utils.message = function (msg) {
1432 var eMessage = document.createElement('div');
1433 eMessage.innerHTML = msg;
1434 var eBox = document.querySelector('#__ag__message');
1435 if (!eBox) {
1436 var template = "<div id=\"__ag__message\" style=\"display: inline-block; position: absolute; top: 0px; left: 0px; color: white; background-color: black; z-index: 20; padding: 2px; border: 1px solid darkred; height: 200px; overflow-y: auto;\"></div>";
1437 eBox = this.loadTemplate(template);
1438 if (document.body) {
1439 document.body.appendChild(eBox);
1440 }
1441 }
1442 eBox.appendChild(eMessage);
1443 };
1444 // gets called by: a) ClientSideNodeManager and b) GroupStage to do sorting.
1445 // when in ClientSideNodeManager we always have indexes (as this sorts the items the
1446 // user provided) but when in GroupStage, the nodes can contain filler nodes that
1447 // don't have order id's
1448 Utils.sortRowNodesByOrder = function (rowNodes, rowNodeOrder) {
1449 if (!rowNodes) {
1450 return;
1451 }
1452 rowNodes.sort(function (nodeA, nodeB) {
1453 var positionA = rowNodeOrder[nodeA.id];
1454 var positionB = rowNodeOrder[nodeB.id];
1455 var aHasIndex = positionA !== undefined;
1456 var bHasIndex = positionB !== undefined;
1457 var bothNodesAreUserNodes = aHasIndex && bHasIndex;
1458 var bothNodesAreFillerNodes = !aHasIndex && !bHasIndex;
1459 if (bothNodesAreUserNodes) {
1460 // when comparing two nodes the user has provided, they always
1461 // have indexes
1462 return positionA - positionB;
1463 }
1464 else if (bothNodesAreFillerNodes) {
1465 // when comparing two filler nodes, we have no index to compare them
1466 // against, however we want this sorting to be deterministic, so that
1467 // the rows don't jump around as the user does delta updates. so we
1468 // want the same sort result. so we use the id - which doesn't make sense
1469 // from a sorting point of view, but does give consistent behaviour between
1470 // calls. otherwise groups jump around as delta updates are done.
1471 return nodeA.id > nodeB.id ? 1 : -1;
1472 }
1473 else if (aHasIndex) {
1474 return 1;
1475 }
1476 else {
1477 return -1;
1478 }
1479 });
1480 };
1481 Utils.fuzzyCheckStrings = function (inputValues, validValues, allSuggestions) {
1482 var _this = this;
1483 var fuzzyMatches = {};
1484 var invalidInputs = inputValues.filter(function (inputValue) {
1485 return !validValues.some(function (validValue) { return validValue === inputValue; });
1486 });
1487 if (invalidInputs.length > 0) {
1488 invalidInputs.forEach(function (invalidInput) {
1489 return fuzzyMatches[invalidInput] = _this.fuzzySuggestions(invalidInput, validValues, allSuggestions);
1490 });
1491 }
1492 return fuzzyMatches;
1493 };
1494 Utils.fuzzySuggestions = function (inputValue, validValues, allSuggestions) {
1495 var thisSuggestions = allSuggestions.slice(0);
1496 thisSuggestions.sort(function (suggestedValueLeft, suggestedValueRight) {
1497 var leftDifference = exports._.string_similarity(suggestedValueLeft.toLowerCase(), inputValue.toLowerCase());
1498 var rightDifference = exports._.string_similarity(suggestedValueRight.toLowerCase(), inputValue.toLowerCase());
1499 return leftDifference > rightDifference ? -1 :
1500 leftDifference === rightDifference ? 0 :
1501 1;
1502 });
1503 return thisSuggestions;
1504 };
1505 //Algorithm to do fuzzy search
1506 //https://stackoverflow.com/questions/23305000/javascript-fuzzy-search-that-makes-sense
1507 Utils.get_bigrams = function (from) {
1508 var i, j, ref, s, v;
1509 s = from.toLowerCase();
1510 v = new Array(s.length - 1);
1511 for (i = j = 0, ref = v.length; j <= ref; i = j += 1) {
1512 v[i] = s.slice(i, i + 2);
1513 }
1514 return v;
1515 };
1516 Utils.isNumpadDelWithNumlockOnForEdgeOrIe = function (event) {
1517 if (Utils.isBrowserEdge() || Utils.isBrowserIE()) {
1518 return event.key === Utils.NUMPAD_DEL_NUMLOCK_ON_KEY &&
1519 event.charCode === Utils.NUMPAD_DEL_NUMLOCK_ON_CHARCODE;
1520 }
1521 return false;
1522 };
1523 Utils.PRINTABLE_CHARACTERS = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!"£$%^&*()_+-=[];\'#,./\\|<>?:@~{}';
1524 Utils.NUMPAD_DEL_NUMLOCK_ON_KEY = 'Del';
1525 Utils.NUMPAD_DEL_NUMLOCK_ON_CHARCODE = 46;
1526 Utils.doOnceFlags = {};
1527 // static prepend(parent: HTMLElement, child: HTMLElement): void {
1528 // if (this.exists(parent.firstChild)) {
1529 // parent.insertBefore(child, parent.firstChild);
1530 // } else {
1531 // parent.appendChild(child);
1532 // }
1533 // }
1534 Utils.iconNameClassMap = {
1535 'columnMovePin': 'pin',
1536 'columnMoveAdd': 'plus',
1537 'columnMoveHide': 'eye-slash',
1538 'columnMoveMove': 'arrows',
1539 'columnMoveLeft': 'left',
1540 'columnMoveRight': 'right',
1541 'columnMoveGroup': 'group',
1542 'columnMoveValue': 'aggregation',
1543 'columnMovePivot': 'pivot',
1544 'dropNotAllowed': 'not-allowed',
1545 'groupContracted': 'expanded',
1546 'groupExpanded': 'contracted',
1547 'checkboxChecked': 'checkbox-checked',
1548 'checkboxUnchecked': 'checkbox-unchecked',
1549 'checkboxIndeterminate': 'checkbox-indeterminate',
1550 'checkboxCheckedReadOnly': 'checkbox-checked-readonly',
1551 'checkboxUncheckedReadOnly': 'checkbox-unchecked-readonly',
1552 'checkboxIndeterminateReadOnly': 'checkbox-indeterminate-readonly',
1553 'groupLoading': 'loading',
1554 'menu': 'menu',
1555 'filter': 'filter',
1556 'columns': 'columns',
1557 'menuPin': 'pin',
1558 'menuValue': 'aggregation',
1559 'menuAddRowGroup': 'group',
1560 'menuRemoveRowGroup': 'group',
1561 'clipboardCopy': 'copy',
1562 'clipboardCut': 'cut',
1563 'clipboardPaste': 'paste',
1564 'pivotPanel': 'pivot',
1565 'rowGroupPanel': 'group',
1566 'valuePanel': 'aggregation',
1567 'columnGroupOpened': 'expanded',
1568 'columnGroupClosed': 'contracted',
1569 'columnSelectClosed': 'tree-closed',
1570 'columnSelectOpen': 'tree-open',
1571 // from deprecated header, remove at some point
1572 'sortAscending': 'asc',
1573 'sortDescending': 'desc',
1574 'sortUnSort': 'none'
1575 };
1576 Utils.passiveEvents = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
1577 Utils.string_similarity = function (str1, str2) {
1578 var hit_count, j, k, len, len1, pairs1, pairs2, union, x, y;
1579 if (str1.length > 0 && str2.length > 0) {
1580 pairs1 = Utils.get_bigrams(str1);
1581 pairs2 = Utils.get_bigrams(str2);
1582 union = pairs1.length + pairs2.length;
1583 hit_count = 0;
1584 for (j = 0, len = pairs1.length; j < len; j++) {
1585 x = pairs1[j];
1586 for (k = 0, len1 = pairs2.length; k < len1; k++) {
1587 y = pairs2[k];
1588 if (x === y) {
1589 hit_count++;
1590 }
1591 }
1592 }
1593 if (hit_count > 0) {
1594 return (2.0 * hit_count) / union;
1595 }
1596 }
1597 return 0.0;
1598 };
1599 return Utils;
1600}());
1601exports.Utils = Utils;
1602var NumberSequence = (function () {
1603 function NumberSequence(initValue, step) {
1604 if (initValue === void 0) { initValue = 0; }
1605 if (step === void 0) { step = 1; }
1606 this.nextValue = initValue;
1607 this.step = step;
1608 }
1609 NumberSequence.prototype.next = function () {
1610 var valToReturn = this.nextValue;
1611 this.nextValue += this.step;
1612 return valToReturn;
1613 };
1614 NumberSequence.prototype.peek = function () {
1615 return this.nextValue;
1616 };
1617 NumberSequence.prototype.skip = function (count) {
1618 this.nextValue += count;
1619 };
1620 return NumberSequence;
1621}());
1622exports.NumberSequence = NumberSequence;
1623exports._ = Utils;
1624var PromiseStatus;
1625(function (PromiseStatus) {
1626 PromiseStatus[PromiseStatus["IN_PROGRESS"] = 0] = "IN_PROGRESS";
1627 PromiseStatus[PromiseStatus["RESOLVED"] = 1] = "RESOLVED";
1628})(PromiseStatus = exports.PromiseStatus || (exports.PromiseStatus = {}));
1629var Promise = (function () {
1630 function Promise(callback) {
1631 this.status = PromiseStatus.IN_PROGRESS;
1632 this.resolution = null;
1633 this.listOfWaiters = [];
1634 callback(this.onDone.bind(this), this.onReject.bind(this));
1635 }
1636 Promise.all = function (toCombine) {
1637 return new Promise(function (resolve) {
1638 var combinedValues = [];
1639 var remainingToResolve = toCombine.length;
1640 toCombine.forEach(function (source, index) {
1641 source.then(function (sourceResolved) {
1642 remainingToResolve--;
1643 combinedValues[index] = sourceResolved;
1644 if (remainingToResolve == 0) {
1645 resolve(combinedValues);
1646 }
1647 });
1648 combinedValues.push(null);
1649 });
1650 });
1651 };
1652 Promise.resolve = function (value) {
1653 return new Promise(function (resolve) { return resolve(value); });
1654 };
1655 Promise.external = function () {
1656 var capture;
1657 var promise = new Promise(function (resolve) {
1658 capture = resolve;
1659 });
1660 return {
1661 promise: promise,
1662 resolve: function (value) {
1663 capture(value);
1664 }
1665 };
1666 };
1667 Promise.prototype.then = function (func) {
1668 if (this.status === PromiseStatus.IN_PROGRESS) {
1669 this.listOfWaiters.push(func);
1670 }
1671 else {
1672 func(this.resolution);
1673 }
1674 };
1675 Promise.prototype.firstOneOnly = function (func) {
1676 if (this.status === PromiseStatus.IN_PROGRESS) {
1677 if (this.listOfWaiters.length === 0) {
1678 this.listOfWaiters.push(func);
1679 }
1680 }
1681 else {
1682 func(this.resolution);
1683 }
1684 };
1685 Promise.prototype.map = function (adapter) {
1686 var _this = this;
1687 return new Promise(function (resolve) {
1688 _this.then(function (unmapped) {
1689 resolve(adapter(unmapped));
1690 });
1691 });
1692 };
1693 Promise.prototype.resolveNow = function (ifNotResolvedValue, ifResolved) {
1694 if (this.status == PromiseStatus.IN_PROGRESS)
1695 return ifNotResolvedValue;
1696 return ifResolved(this.resolution);
1697 };
1698 Promise.prototype.onDone = function (value) {
1699 this.status = PromiseStatus.RESOLVED;
1700 this.resolution = value;
1701 this.listOfWaiters.forEach(function (waiter) { return waiter(value); });
1702 };
1703 Promise.prototype.onReject = function (params) {
1704 console.warn('TBI');
1705 };
1706 return Promise;
1707}());
1708exports.Promise = Promise;