• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angularjs / 7458056615

09 Jan 2024 07:41AM CUT coverage: 53.004%. Remained the same
7458056615

Pull #157

github

web-flow
Merge 2ec63ab67 into 2a2e33ff1
Pull Request #157: Bump follow-redirects from 1.14.0 to 1.15.4

194 of 361 branches covered (0.0%)

Branch coverage included in aggregate %.

203 of 388 relevant lines covered (52.32%)

11.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.0
/src/igniteui-angularjs.js
1
/*!@license
2
 * Ignite UI directives for AngularJS   1.1.4
3
 * https://github.com/IgniteUI/igniteui-angularjs
4
 *
5
 * Copyright (c) 2014-2016 Infragistics, Inc.
6
 * Licensed under the MIT license.
7
 * https://github.com/IgniteUI/igniteui-angularjs/blob/master/license.txt
8
 */
9

10
/*global angular*/
11
(function (angular, $) {
1✔
12
        "use strict";
13
        $.ig = $.ig || {};
1!
14
        $.ig.angular = $.ig.angular || {};
1✔
15

16
        // igCombo specific code for two way data binding
17
        $.ig.angular.igCombo = $.ig.angular.igCombo || {};
1✔
18
        $.ig.angular.igCombo.element = $.ig.angular.igCombo.element || "<div></div>";
1✔
19
        $.ig.angular.igCombo.events =  [
1✔
20
                "igcombofiltered",
21
                "igcomboselectionchanged",
22
                "igcombotextchanged"
23
        ];
24

25
        // Mark watchers for discoverability
26
        function markWatcher(scope, controlName, attrs) {
27
                // Angular uses unshift(), so the last watcher is at 0:
28
                scope.$$watchers[ 0 ][ controlName ] = attrs.id || controlName + scope.$$watchers.length;
6!
29
        }
30

31
        // Interrogation functions
32
        function isDate(value) {
33
                return Object.prototype.toString.call(value) === "[object Date]";
×
34
        }
35

36
        function isRegExp(value) {
37
                return Object.prototype.toString.call(value) === "[object RegExp]";
×
38
        }
39

40
        function isScope(obj) {
41
                return obj && obj.$evalAsync && obj.$watch;
×
42
        }
43

44
        function isWindow(obj) {
45
                return obj && obj.document && obj.location && obj.alert && obj.setInterval;
×
46
        }
47

48
        function isFunction(value) { return typeof value === "function"; }
×
49

50
        function isArray(value) {
51
                return Object.prototype.toString.call(value) === "[object Array]";
×
52
        }
53

54
        function equalsDiff(o1, o2, diff) {
55
                if (o1 === o2) { return true; }
2!
56
                if (o1 === null || o2 === null) { return false; }
×
57
                if (o1 !== o1 && o2 !== o2) { return true; }// NaN === NaN
×
58
                var t1 = typeof o1, t2 = typeof o2, length, key, keySet,
×
59
                        dirty, skipDiff = false, changedVals = [];
×
60
                if (t1 === t2) {
×
61
                        if (t1 === "object") {
×
62
                                if (isArray(o1)) {
×
63
                                        if (!isArray(o2)) { return false; }
×
64
                                        if ((length = o1.length) === o2.length) {
×
65
                                                if (!isArray(diff)) {
×
66
                                                        skipDiff = true;
×
67
                                                }
68
                                                for (key = 0; key < length; key++) {
×
69
                                                        // we are comparing objects here
70
                                                        if (!equalsDiff(o1[ key ], o2[ key ], changedVals)) {
×
71
                                                                dirty = true;
×
72
                                                                if (!skipDiff) {
×
73
                                                                        diff.push({ index: key, txlog: changedVals });
×
74
                                                                }
75
                                                        }
76
                                                        changedVals = [];
×
77
                                                }
78
                                                if (dirty) {
×
79
                                                        return false;
×
80
                                                }
81
                                                return true;
×
82
                                        }
83
                                } else if (isDate(o1)) {
×
84
                                        return isDate(o2) && o1.getTime() === o2.getTime();
×
85
                                } else if (isRegExp(o1) && isRegExp(o2)) {
×
86
                                        return o1.toString() === o2.toString();
×
87
                                } else {
88
                                        if (isScope(o1) || isScope(o2) ||
×
89
                                                        isWindow(o1) || isWindow(o2) ||
90
                                                        isArray(o2)) {
91
                                                return false;
×
92
                                        }
93
                                        keySet = {};
×
94
                                        if (!isArray(diff)) {
×
95
                                                skipDiff = true;
×
96
                                        }
97
                                        for (key in o1) {
×
98
                                                if (key.charAt(0) === "$" || isFunction(o1[ key ])) { continue; }
×
99
                                                if (!equalsDiff(o1[ key ], o2[ key ])) {
×
100
                                                        dirty = true;
×
101
                                                        if (!skipDiff) {
×
102
                                                                diff.push({ key: key, oldVal: o2[ key ], newVal: o1[ key ] });
×
103
                                                        }
104
                                                }
105
                                                keySet[ key ] = true;
×
106
                                        }
107
                                        for (key in o2) {
×
108
                                                if (!keySet.hasOwnProperty(key) &&
×
109
                                                        key.charAt(0) !== "$" &&
110
                                                        o2[ key ] !== undefined &&
111
                                                        !isFunction(o2[ key ])) { return false; }
×
112
                                        }
113
                                        if (dirty) {
×
114
                                                return false;
×
115
                                        }
116
                                        return true;
×
117
                                }
118
                        }
119
                }
120
                return false;
×
121
        }
122
        function _notifyRowAdded(gridElem, row) {
123
                var rs = gridElem.data("igGridRowSelectors"),
×
124
                pa = gridElem.data("igGridPaging"),
×
125
                su = gridElem.data("igGridSummaries");
×
126
                if (rs && typeof rs._rowAdded === "function") {
×
127
                        rs._rowAdded(row);
×
128
                }
129
                if (pa && typeof pa._rowAdded === "function") {
×
130
                        pa._rowAdded(row);
×
131
                }
132
                if (su && typeof su._rowAdded === "function") {
×
133
                        su._rowAdded(row);
×
134
                }
135
                gridElem.data("igGrid")._fireInternalEvent("_internalRowAdded", { row: row });
×
136
        }
137
        function _notifyRowDeleted(gridElem, rowId, row) {
138
                var se = gridElem.data("igGridSelection"),
×
139
                        pa = gridElem.data("igGridPaging"),
×
140
                        su = gridElem.data("igGridSummaries");
×
141
                if (se && typeof se._rowDeleted === "function") {
×
142
                        se._rowDeleted(rowId, row);
×
143
                }
144
                if (su && typeof su._rowDeleted === "function") {
×
145
                        su._rowDeleted(rowId, row);
×
146
                }
147
                if (pa && typeof pa._rowDeleted === "function") {
×
148
                        pa._rowDeleted(rowId, row);
×
149
                }
150
                gridElem.data("igGrid")._fireInternalEvent("_internalRowDeleted", { rowID: rowId, row: row });
×
151
        }
152

153
        // Two way data binding for the combo control
154
        $.ig.angular.igCombo.bindEvents = $.ig.angular.igCombo.bindEvents ||
1✔
155
                function (scope, element, attrs, model) {
156
                        if (!model) {
2!
157
                                return;
×
158
                        }
159
                        var controlName = attrs[ "data-ig-control-name" ], unbinder, comboItems;
2✔
160
                        /**
161
                                * Support both versions igCombo value methods before and after 15.1
162
                                */
163
                        function comboValue(widget, value) {
164
                                if ( widget.values ) {
4!
165
                                        return widget.values(value);
×
166
                                } else {
167
                                        // for 15.1 make sure you update to latest SR for value method
168
                                        return widget.value(value);
4✔
169
                                }
170
                        }
171
                        function setControlValue( value ) {
172
                                var combo = element.data(controlName), multiSelection = combo.options.multiSelection;
2✔
173
                                comboItems = value;
2✔
174
                                if (typeof value === "string" && multiSelection && multiSelection.enabled) {
2!
175
                                        // in case view value is changed from text field (default Array.toString representation is comma separated)
176
                                        value = value.split(multiSelection.itemSeparator);
×
177
                                }
178
                                comboValue(combo, value);
2✔
179
                                return combo.text();
2✔
180
                        }
181
                        function parseValue() {
182
                                //"parse" through the control value, ensure no-flicker with formatted values
183
                                //model controller will attempt to set the edit text (not actual value) to the model. Only allow the actual control value to update.
184

185
                                // No events fired when custom value is entered, this change won't affect the old combo
186
                                var combo = element.data(controlName);
×
187
                                if (combo.options.allowCustomValue && combo.refreshValue !== undefined) {
×
188
                                        combo.refreshValue();
×
189
                                }
190

191
                                return comboValue(combo);
×
192
                        }
193
                        element.on($.ig.angular.igCombo.events.join(" "), function (event, args) {
2✔
194
                                scope.$apply(function () {
×
195
                                        model.$setViewValue(comboValue(args.owner));
×
196
                                });
197
                        }).on("click", "div.ui-igcombo-clear", function () {
198
                                scope.$apply(function () {
×
199
                                        model.$setViewValue([]);
×
200
                                });
201
                        }).one("$destroy", function () {
202
                                unbinder();
×
203
                                var index = $.inArray( setControlValue, model.$formatters );
×
204
                                if (index >= 0) {
×
205
                                        model.$formatters.splice(index, 1);
×
206
                                }
207
                        });
208
                        model.$formatters.push(setControlValue);
2✔
209
                        model.$parsers.push(parseValue);
2✔
210
                        model.$isEmpty = function (value) {
2✔
211
                                var isEmpty = !value || (Array.isArray(value) && value.length == 0);
2!
212
                                return isEmpty;
2✔
213
                        };
214

215
                        unbinder = scope.$watch(attrs.source, function (newValue) {
2✔
216
                                var items = [], newDataSource = [], combo = element.data(controlName);
2✔
217
                                items = comboValue(combo);
2✔
218
                                angular.copy(newValue, newDataSource);
2✔
219
                                combo._setOption("dataSource", newDataSource);
2✔
220
                                /* if the items were not in the old data source, use stored values */
221
                                items = items.length !== 0 ? items : comboItems;
2!
222
                                combo.value(items);
2✔
223
                        }, true);
224
                        markWatcher(scope, controlName, attrs);
2✔
225
                };
226

227
        // igEditor specific code for two way data binding
228
        $.ig.angular.igEditor = $.ig.angular.igEditor || {};
1✔
229
        $.ig.angular.igEditor.element = $.ig.angular.igEditor.element || "<input></input>";
1✔
230
        $.ig.angular.igEditor.events = [];
1✔
231

232
        // Two way data binding for the editor controls
233
        $.ig.angular.igEditor.bindEvents = $.ig.angular.igEditor.bindEvents ||
1✔
234
                        function (scope, element, attrs, model) {
235
                                if (!model) {
4!
236
                                        return;
4✔
237
                                }
238
                                var controlName = attrs[ "data-ig-control-name" ];
×
239

240
                                function setControlValue(value) {
241
                                        var editor = element.data(controlName),
×
242
                                                displayFunc = editor.displayValue || editor.text || editor.value;
×
243

244
                                        editor.value(value);
×
245
                                        return displayFunc.call(editor);
×
246
                                }
247
                                function parseValue() {
248
                                        //"parse" through the control value, ensure no-flicker with formatted values
249
                                        //model controller will attempt to set the edit text (not actual value) to the model. Only allow the actual control value to update.
250
                                        //Can be extended in the future with "immediate" update mode and attempt to provide the to-be value
251
                                        return element.data(controlName).value();
×
252
                                }
253
                                if (controlName) {
×
254
                                        $.ig.angular[ controlName ].events = [ controlName.toLowerCase() + "valuechanged" ];
×
255
                                        element.on($.ig.angular[ controlName ].events.join(" "), function (event, args) {
×
256
                                                scope.$apply(function () {
×
257
                                                        // force newer versions of ngModelController(1.3.0+) to update, since we kept the control value while in input was changing
258
                                                        model.$$lastCommittedViewValue = null;
×
259
                                                        model.$setViewValue(args.owner.value());
×
260
                                                });
261
                                        }).one("$destroy", function () {
262
                                                var index = $.inArray(setControlValue, model.$formatters);
×
263
                                                if (index >= 0) {
×
264
                                                        model.$formatters.splice(index, 1);
×
265
                                                }
266
                                        });
267
                                        model.$formatters.push(setControlValue);
×
268
                                        model.$parsers.push(parseValue);
×
269
                                }
270
                        };
271
        $.ig.angular.igCurrencyEditor = angular
1✔
272
                .extend($.ig.angular.igCurrencyEditor || {}, $.ig.angular.igEditor);
2✔
273
        $.ig.angular.igDateEditor = angular
1✔
274
                .extend($.ig.angular.igDateEditor || {}, $.ig.angular.igEditor);
2✔
275
        $.ig.angular.igMaskEditor = angular
1✔
276
                .extend($.ig.angular.igMaskEditor || {}, $.ig.angular.igEditor);
2✔
277
        $.ig.angular.igNumericEditor = angular
1✔
278
                .extend($.ig.angular.igNumericEditor || {}, $.ig.angular.igEditor);
2✔
279
        $.ig.angular.igTextEditor = angular
1✔
280
                .extend($.ig.angular.igTextEditor || {}, $.ig.angular.igEditor);
2✔
281
        $.ig.angular.igDatePicker = angular
1✔
282
                .extend($.ig.angular.igDatePicker || {}, $.ig.angular.igEditor);
2✔
283
        $.ig.angular.igPercentEditor = angular
1✔
284
                .extend($.ig.angular.igPercentEditor || {}, $.ig.angular.igEditor);
2✔
285
        $.ig.angular.igCheckboxEditor = angular
1✔
286
                .extend($.ig.angular.igCheckboxEditor || {}, $.ig.angular.igEditor);
2✔
287

288
        // igGrid specific code for two way data binding
289
        $.ig.angular.igGrid = $.ig.angular.igGrid || {};
1✔
290
        $.ig.angular.igGrid.element = $.ig.angular.igGrid.element || "<table></table>";
1✔
291
        $.ig.angular.igGrid.events = [
1✔
292
                "iggridupdatingeditcellended",
293
                "iggridupdatingeditrowended",
294
                "iggridupdatingrowdeleted",
295
                "iggridupdatingrowadded"
296
        ];
297

298
        // Two way data binding for the grid control
299
        $.ig.angular.igGrid.bindEvents = $.ig.angular.igGrid.bindEvents ||
1✔
300
                        function (scope, element, attrs) {
301
                                var unbinder,
302
                                        collectionWatchMode = attrs && attrs.collectionWatch && attrs.collectionWatch === "true";
1!
303

304
                                function watchGridDataSource(newValue, oldValue) {
305
                                        var i, j, existingDomRow, existingRow,
306
                                                grid = element.data("igGrid"), pkKey = grid.options.primaryKey,
1✔
307
                                                groupByInstance = element.data("igGridGroupBy"),
1✔
308
                                                gridUpdating = element.data("igGridUpdating"), column,
1✔
309
                                                record, td, newFormattedVal, dsRecord,
310
                                                ds = scope.$eval(attrs.source), diff = [], row, grp, idx,
1✔
311
                                                autoCommit = grid.options.autoCommit;
1✔
312
                                        /* check for a change of the data source. In this case rebind the grid */
313
                                        if (ds !== grid.options.dataSource) {
1!
314
                                                //Setting a timeout 0 pushes the slow databind event to the end of the stack, letting the digest cycle finish, improving the overall responsivness of the page
315
                                                setTimeout(function () {
×
316
                                                        grid.options.dataSource = ds;
×
317
                                                        grid.dataBind();
×
318
                                                }, 0);
319
                                                return;
×
320
                                        }
321
                                        equalsDiff(newValue, oldValue, diff);
1✔
322
                                        /* add/delete new rows */
323
                                        if (Array.isArray(newValue) && Array.isArray(oldValue)) {
1!
324
                                                // adding
325
                                                if (newValue.length > oldValue.length) {
1!
326
                                                        //inserted rows
327
                                                        if ((oldValue.length > 0) &&
×
328
                                                                        (oldValue[ oldValue.length - 1 ][ pkKey ] !==
329
                                                                        newValue[ oldValue.length - 1 ][ pkKey ])) {
330
                                                                // rebind the grid when there is a new inserted row, because igGrid doesn't have an API for inserting rows.
331
                                                                grid.dataBind();
×
332
                                                        }
333
                                                        grp = groupByInstance && groupByInstance.options.groupedColumns.length > 0;
×
334
                                                        for (i = oldValue.length; i < newValue.length; i++) {
×
335
                                                                existingDomRow = element.find("tr[data-id='" + newValue[ i ][ pkKey ] + "']").length;
×
336
                                                                if (existingDomRow === 0) {
×
337
                                                                        if (grp) {
×
338
                                                                                groupByInstance._renderNewRow(newValue[ i ], newValue[ i ][ pkKey ]);
×
339
                                                                        } else {
340
                                                                                grid.renderNewRow(newValue[ i ], newValue[ i ][ pkKey ]);
×
341
                                                                        }
342
                                                                }
343
                                                                existingRow = grid.dataSource.findRecordByKey(newValue[ i ][ pkKey ]);
×
344
                                                                if (!existingRow) {
×
345
                                                                        // add the row without affecting the original DS (scope source)
346
                                                                        // TODO: trigger rowAdded event?
347
                                                                        grid.dataSource._addRow(newValue[ i ], -1);
×
348
                                                                }
349

350
                                                                row = grid.rowById(newValue[ i ][ pkKey ]);
×
351
                                                                grid.options.autoCommit = true;
×
352
                                                                _notifyRowAdded(element, row);
×
353
                                                                grid.options.autoCommit = autoCommit;
×
354
                                                        }
355
                                                }
356

357
                                                // deleting a row
358
                                                if (newValue.length < oldValue.length) {
1!
359
                                                        for (i = 0, j = 0; j < oldValue.length; i++, j++) {
×
360
                                                        if ((newValue[ i ] === undefined) ||
×
361
                                                                (newValue[ i ][ pkKey ] !== oldValue[ j ][ pkKey ])) {
362
                                                                        grid.dataSource.deleteRow(oldValue[ j ][ pkKey ], true);
×
363
                                                                        i--;
×
364
                                                                        row = element.find("tr[data-id='" + oldValue[ j ][ pkKey ] + "']");
×
365
                                                                        idx = row.index();
×
366
                                                                        row.remove();
×
367
                                                                        grid._reapplyZebraStyle(idx);
×
368
                                                                        grid.options.autoCommit = true;
×
369
                                                                        _notifyRowDeleted(element, oldValue[ j ][ pkKey ], row);
×
370
                                                                        grid.options.autoCommit = autoCommit;
×
371
                                                                }
372
                                                        }
373
                                                }
374
                                        }
375
                                        /* update a row */
376
                                        if (Array.isArray(diff)) {
1!
377
                                                for (i = 0; i < diff.length; i++) {
1✔
378
                                                        // update cell values
379
                                                        if (!diff[ i ].txlog) {
×
380
                                                                continue;
×
381
                                                        }
382
                                                        for (j = 0; j < diff[ i ].txlog.length; j++) {
×
383
                                                                // if updating in progress - cancel it
384
                                                                if (gridUpdating && gridUpdating.isEditing()) {
×
385
                                                                        gridUpdating.endEdit(false);
×
386
                                                                }
387

388
                                                                // update the DOM of the grid
389
                                                                column = grid.columnByKey(diff[ i ].txlog[ j ].key);
×
390
                                                                record = ds[ diff[ i ].index ];
×
391
                                                                row = grid.rowById(record[ pkKey ]);
×
392
                                                                td = grid.cellById(record[ pkKey ], diff[ i ].txlog[ j ].key);
×
393
                                                                if (column.template || grid.options.rowTemplate) {
×
394
                                                                        newFormattedVal = grid
×
395
                                                                                ._renderTemplatedCell(diff[ i ].txlog[ j ].newVal, column);
396
                                                                } else {
397
                                                                        newFormattedVal = grid
×
398
                                                                                ._renderCell(diff[ i ].txlog[ j ].newVal, column, record);
399
                                                                }
400
                                                                /* updatecell */
401
                                                                $(td).html(newFormattedVal);
×
402
                                                                grid._fireInternalEvent("_internalCellUpdated", { rowID: record[ pkKey ], cell: td });
×
403
                                                                /* update the grid data source */
404
                                                                dsRecord = grid.dataSource.findRecordByKey(record[ pkKey ]);
×
405
                                                                dsRecord[ column.key ] = diff[ i ].txlog[ j ].newVal;
×
406
                                                        }
407
                                                }
408
                                        }
409
                                }
410

411
                                element.on($.ig.angular.igGrid.events.join(" "), function () {
1✔
412
                                        unbinder();
×
413
                                        /* When in collection watch mode, a change is detected only when the collection changes - a element is inserted or removed or the whole collection reference changes.
414
                                        Changes in a specific element inside collection are not detected. This provides huge performance boost when such change detection is not required */
415
                                        unbinder = collectionWatchMode ?
×
416
                                                scope.$watchCollection(attrs.source, watchGridDataSource) :
417
                                                scope.$watch(attrs.source, watchGridDataSource, true);
418
                                        scope.$apply();
×
419
                                        markWatcher(scope, "igGrid", attrs);
×
420
                                }).one("$destroy", function () {
421
                                        unbinder();
×
422
                                });
423

424
                                /* watch for changes from the data source to the view */
425
                                unbinder = collectionWatchMode ?
1!
426
                                        scope.$watchCollection(attrs.source, watchGridDataSource) :
427
                                        scope.$watch(attrs.source, watchGridDataSource, true);
428
                                markWatcher(scope, "igGrid", attrs);
1✔
429
                        };
430

431
        // igHierarchicalGrid specific code for one way data binding
432
        $.ig.angular.igHierarchicalGrid = $.ig.angular.igHierarchicalGrid || {};
1✔
433
        $.ig.angular.igHierarchicalGrid.element = $.ig.angular.igHierarchicalGrid.element ||
1✔
434
                "<table></table>";
435
        $.ig.angular.igHierarchicalGrid.bindEvents = $.ig.angular.igHierarchicalGrid.bindEvents ||
1✔
436
                        function (scope, element, attrs) {
437
                                var unbinder;
438
                            /* rebind data source on changes */
439
                                unbinder = scope.$watch(attrs.source, function (newValue) {
1✔
440
                                        var ds = scope.$eval(attrs.source), grid = element.data("igHierarchicalGrid");
1✔
441

442
                                        if (ds !== grid.options.dataSource) {
1!
443
                                                //Setting a timeout 0 pushes the slow databind event to the end of the stack, letting the digest cycle finish, improving the overall responsivness of the page
444
                                                setTimeout(function () {
1✔
445
                                                        $(element).igHierarchicalGrid("option", "dataSource", newValue);
1✔
446
                                                }, 0);
447
                                                return;
1✔
448
                                        }
449

450
                                }, true);
451
                                markWatcher(scope, "igHierarchicalGrid", attrs);
1✔
452
                                element.one("$destroy", function () {
1✔
453
                                        unbinder();
×
454
                                });
455
                        };
456

457
        // igTree specific code for two way data binding
458
        $.ig.angular.igTree = $.ig.angular.igTree || {};
1✔
459

460
        // Two way data binding for the tree control
461
        $.ig.angular.igTree.bindEvents = $.ig.angular.igTree.bindEvents ||
1✔
462
                        function (scope, element, attrs) {
463
                                var unbinder;
464
                                /* rebind data source on changes */
465
                                unbinder = scope.$watch(attrs.source, function (newValue) {
1✔
466
                                        $(element).igTree("option", "dataSource", newValue);
1✔
467
                                }, true);
468
                                markWatcher(scope, "igTree", attrs);
1✔
469
                                element.one("$destroy", function () {
1✔
470
                                        unbinder();
×
471
                                });
472
                        };
473

474
        // igDataChart specific code for data binding
475
        $.ig.angular.igDataChart = $.ig.angular.igDataChart || {};
1✔
476

477
        // Data binding (one-way) for the data chart control
478
        $.ig.angular.igDataChart.bindEvents = $.ig.angular.igDataChart.bindEvents ||
1✔
479
                        function (scope, element, attrs) {
480
                                var diff = [], ds = scope.$eval(attrs.source), unbinder;
1✔
481
                                var changeHandler = function (newValue, oldValue) {
1✔
482
                                        var $chartElem = $(element), chart = $chartElem.data("igDataChart");
1✔
483
                                        if (newValue && (oldValue === undefined || oldValue === null)) {
1!
484
                                                $chartElem.igDataChart("option", "dataSource", newValue);
×
485
                                                return;
×
486
                                        }
487
                                        /* check for a change of the data source. In this case rebind */
488
                                        if (chart.dataSources[ chart._containerSourceID ] &&
1!
489
                                                        chart.dataSources[ chart._containerSourceID ].data() !== newValue) {
490
                                                $chartElem.igDataChart("option", "dataSource", newValue);
×
491
                                                return;
×
492
                                        }
493
                                        if (newValue && oldValue && newValue.length === oldValue.length) {
1!
494
                                                //attempt to optimize for value changes
495
                                                var equals = equalsDiff(newValue, oldValue, diff);
1✔
496
                                                if ((diff.length > 0) && !equals) {
1!
497
                                                        for (var i = 0; i < diff.length; i++) {
×
498
                                                                $chartElem.igDataChart("notifySetItem",
×
499
                                                                        newValue,
500
                                                                        diff[ i ].index,
501
                                                                        newValue[ diff[ i ].index ],
502
                                                                        oldValue[ diff[ i ].index ]);
503
                                                        }
504
                                                        return;
×
505
                                                }
506
                                        }
507
                                        if (newValue) {
1!
508
                                                $chartElem.igDataChart("notifyClearItems", newValue);
1✔
509
                                        }
510
                                };
511

512
                                //handle push to track added data points, unbind and rebind watcher after.
513
                                ds.push = function () {
1✔
514
                                        unbinder();
×
515
                                        var res = Array.prototype.push.apply(this, arguments);
×
516
                                        $(element).igDataChart("notifyInsertItem", this, this.length - 1, arguments[ 0 ]);
×
517
                                        unbinder = scope.$watch(attrs.source, changeHandler, true);
×
518
                                        markWatcher(scope, "igDataChart", attrs);
×
519
                                        return res;
×
520
                                };
521

522
                                unbinder = scope.$watch(attrs.source, changeHandler, true);
1✔
523
                                markWatcher(scope, "igDataChart", attrs);
1✔
524
                                element.one("$destroy", function () {
1✔
525
                                        unbinder();
1✔
526
                                });
527
                        };
528

529
        // igBaseChart specific code for data binding
530
        $.ig.angular.igBaseChart = $.ig.angular.igBaseChart || {};
1✔
531
        $.ig.angular.igBaseChart.element = $.ig.angular.igBaseChart.element || "<div></div>";
1✔
532

533
        // Data binding (one-way) for the igBaseChart-s
534
        $.ig.angular.igBaseChart.bindEvents = $.ig.angular.igBaseChart.bindEvents ||
1✔
535
                        function (scope, element, attrs) {
536
                                var controlName = attrs[ "data-ig-control-name" ], unbinder;
×
537
                                unbinder = scope.$watch(attrs.source, function (newValue) {
×
538
                                        $(element)[ controlName ]("notifyClearItems", newValue);
×
539
                                }, true);
540
                                markWatcher(scope, controlName, attrs);
×
541
                                element.one("$destroy", function () {
×
542
                                        unbinder();
×
543
                                });
544
                        };
545
        $.ig.angular.igSparkline = angular.extend($.ig.angular.igSparkline ||
1✔
546
                {}, $.ig.angular.igBaseChart);
547
        $.ig.angular.igFunnelChart = angular.extend($.ig.angular.igFunnelChart ||
1✔
548
                {}, $.ig.angular.igBaseChart);
549

550
        // igHtmlEditor specific code for one way data binding
551
        $.ig.angular.igHtmlEditor = $.ig.angular.igHtmlEditor || {};
1✔
552
        $.ig.angular.igHtmlEditor.element = $.ig.angular.igHtmlEditor.element || "<div></div>";
1✔
553

554
        $.ig.angular.igHtmlEditor.bindEvents = $.ig.angular.igHtmlEditor.bindEvents ||
1✔
555
                        function (scope, element, attrs) {
556
                                var controlName = attrs[ "data-ig-control-name" ], unbinder;
×
557
                                unbinder = scope.$watch(attrs.value, function (newValue) {
×
558
                                        $(element)[ controlName ]("setContent", newValue, "html");
×
559
                                }, true);
560
                                markWatcher(scope, controlName, attrs);
×
561
                                element.one("$destroy", function () {
×
562
                                        unbinder();
×
563
                                });
564
                        };
565

566
        // igTreeGrid specific code instantiating the element on table
567
        $.ig.angular.igTreeGrid = $.ig.angular.igTreeGrid || {};
1✔
568
        $.ig.angular.igTreeGrid.element = $.ig.angular.igTreeGrid.element || "<table></table>";
1✔
569

570
        // igPivotGrid specific code instantiating the element on table
571
        $.ig.angular.igPivotGrid = $.ig.angular.igPivotGrid || {};
1✔
572
        $.ig.angular.igPivotGrid.element = $.ig.angular.igPivotGrid.element || "<table></table>";
1✔
573

574
        // Utility functions
575
        function convertToCamelCase(str) {
576
                //convert hyphen to camelCase
577
                return str.replace(/-([a-z])/g, function (group) {
154✔
578
                        return group[ 1 ].toUpperCase();
76✔
579
                });
580
        }
581

582
        function getPropertyType(obj, prop) {
583
                var i;
584
                /* return the type of prop which is a property of the object */
585
                for (i in obj) {
42✔
586
                        if (obj.hasOwnProperty(i)) {
608!
587
                                if (i === prop) {
608✔
588
                                        return $.type(obj[ i ]);
10✔
589
                                }
590
                                if ($.type(obj[ i ]) === "object" || $.type(obj[ i ]) === "array") {
598✔
591
                                        getPropertyType(obj[ i ], prop);
32✔
592
                                }
593
                        }
594
                }
595
                return null;
32✔
596
        }
597

598
        function extractOptions(nodeName, context, options, element, scope) {
599
                //extract all options from the element
600
                var i, name, value, optionName, children = context.children,
45✔
601
                        attrs = context.attributes, eventAttrPrefix = "event-";
45✔
602

603
                for (i = 0; i < attrs.length; i++) {
45✔
604
                        name = attrs[ i ].name;
122✔
605
                        value = attrs[ i ].value;
122✔
606

607
                        if (name.startsWith(eventAttrPrefix)) {
122!
608
                                name = name.substr(eventAttrPrefix.length);
×
609
                                value = scope.$eval(value) || value;
×
610
                        }
611
                        name = convertToCamelCase(name);
122✔
612

613
                        /* if somewhere in the controls there is floting point number use this one /^-?\d+\.?\d*$/ */
614
                        if (value === "true" || value === "false" || /^-?\d+\.?\d*$/.test(value) || /^{{[^}]+}}$/.test(value)) {
122✔
615
                                value = scope.$eval(value.replace(/([{}:])\1/g, ""));
12✔
616
                        }
617
                        options[ name ] = value;
122✔
618
                }
619
                /* extract options from the nested elements */
620
                for (i = 0; i < children.length; i++) {
45✔
621

622
                        if (!context.optionsPath) {
34✔
623
                                context.optionsPath = []; //top level
6✔
624
                        }
625
                        optionName = children[ i ].nodeName.toLowerCase();
34✔
626
                        if (optionName === "content") {
34✔
627
                                continue;
2✔
628
                        }
629
                        optionName = convertToCamelCase(optionName);
32✔
630

631
                        var opts = $.ui[ nodeName ].prototype.options;
32✔
632

633
                        if (context.optionsPath[ 0 ] === "features" && options.name) {
32✔
634
                                //grid feature, proto options come from feature widget:
635
                                opts = $.ui[ nodeName.replace("Hierarchical", "") + options.name ].prototype.options;
1✔
636
                                context.optionsPath = [];
1✔
637
                        }
638

639
                        for (var j = 0; j < context.optionsPath.length; j++) {
32✔
640
                                if (opts[ context.optionsPath[ j ] ] && context.optionsPath[ j ] !== "columnLayouts") {
28✔
641
                                        opts = opts[ context.optionsPath[ j ] ];
19✔
642
                                }
643
                        }
644

645
                        if (children[ i ].childElementCount > 0) {
32✔
646
                                var option;
647
                                if (children[ i ].hasAttributes() || getPropertyType(opts, optionName) === "object") {
12✔
648
                                        // object nodes (can have attributes)
649
                                        option = {};
2✔
650
                                } else {
651
                                        option = [];
10✔
652
                                }
653
                                if ($.type(options) === "array") {
12✔
654
                                        options.push(option);
2✔
655
                                        children[ i ].optionsPath = context.optionsPath;
2✔
656
                                        extractOptions(nodeName, children[ i ], options[ options.length - 1 ], element, scope);
2✔
657
                                } else {
658
                                        options[ optionName ] = option;
10✔
659
                                        children[ i ].optionsPath = context.optionsPath.concat(optionName);
10✔
660
                                        extractOptions(nodeName, children[ i ], options[ optionName ], element, scope);
10✔
661
                                }
662
                        } else {
663
                                // single options to evaluate against the parent object:
664
                                if (!context.hasAttributes() && $.type(options) === "array") {
20✔
665
                                        if (children[ i ].nextSibling && children[ i ].nextSibling.textContent.trim() !== "") {
19!
666
                                                //child with text content, e.g. video source
667
                                                options.push(children[ i ].nextSibling.textContent.trim());
×
668
                                        } else {
669
                                                //child with attributes
670
                                                options.push({});
19✔
671
                                                extractOptions(nodeName, children[ i ], options[ options.length - 1 ], element, scope);
19✔
672
                                        }
673
                                } else {
674
                                        if (children[ i ].nextSibling && children[ i ].nextSibling.textContent.trim() !== "") {
1!
675
                                                //child with text content
676
                                                options[ optionName ] = children[ i ].nextSibling.textContent.trim();
×
677
                                        } else {
678
                                                //child with attributes
679
                                                options[ optionName ] = {};
1✔
680
                                                extractOptions(nodeName, children[ i ], options[ optionName ], element, scope);
1✔
681
                                        }
682
                                }
683
                        }
684
                }
685
                return options;
45✔
686
        }
687

688
        function getHtml(selector) {
689
                return $(selector).html();
×
690
        }
691

692
        // define modules and directives
693
        var module = angular.module("igniteui-directives", []);
1✔
694

695
        // directive constructor for custom tags,  data-* attribute and class initialization
696
        var igniteElementDirectiveConstructor = function () {
1✔
697
                return {
13✔
698
                        restrict: "EAC",
699
                        require: "?ngModel",
700
                        template: function (element, attrs) {
701
                                this.origElementCopy = element.clone();
13✔
702
                                var content, template, templateParts;
703
                                attrs.$set("data-ig-control-name", this.name);
13✔
704
                                content = element.find("content").html();
13✔
705

706
                                // igTextEditor textMode known issue
707
                                if (attrs.textMode && attrs.textMode === "multiline" &&
13✔
708
                                        attrs[ "data-ig-control-name" ] === "igTextEditor") {
709
                                                template = "<textarea></textarea>";
1✔
710
                                } else {
711
                                        template = (attrs.element && "<_el_></_el_>".replace(/_el_/g, attrs.element)) || ($.ig.angular[ this.name ] && $.ig.angular[ this.name ].element || "<div></div>");
12✔
712
                                }
713
                                /* In case there is a content tag in the directive manually construct the template by concatenating start tag + content + end tag */
714
                                if (content) {
13✔
715
                                        templateParts = template.match("(<[^/][\\w]+>)(</[\\w]+>)");
2✔
716
                                        if (templateParts.length === 3) {
2!
717
                                                template = templateParts[ 1 ] + content + templateParts[ 2 ];
2✔
718
                                        }
719
                                }
720
                                return template;
13✔
721
                        },
722
                        replace: true,
723
                        compile: function (tElement, tAttrs, transclude) {
724
                                var context = this.origElementCopy[ 0 ];
13✔
725
                                return function (scope, element, attrs, ngModel) {
13✔
726
                                        var controlName = attrs[ "data-ig-control-name" ];
13✔
727
                                        scope.getHtml = scope.getHtml || getHtml;
13✔
728
                                        if (controlName) {
13!
729
                                                if (context) {
13!
730
                                                        var options = scope.$eval(attrs[ controlName ]) ||
13✔
731
                                                                extractOptions(controlName, context, {}, element, scope);
732

733
                                                        if (attrs.source) {
13✔
734
                                                                options.dataSource = scope.$eval(attrs.source);
6✔
735
                                                        } else {
736
                                                                attrs.source = attrs[ controlName ] + ".dataSource";
7✔
737
                                                                attrs.primaryKey = options.primaryKey;
7✔
738
                                                        }
739

740
                                                        // Two way data binding support using events from the controls
741
                                                        if ($.ig.angular[ controlName ] && $.ig.angular[ controlName ].bindEvents) {
13✔
742
                                                                $.ig.angular[ controlName ].bindEvents(scope, element, attrs, ngModel);
10✔
743
                                                        }
744

745
                                                        // cleanup
746
                                                        scope.$on("$destroy", function () {
13✔
747
                                                                if (typeof element.data(controlName) === "object") {
13✔
748
                                                                        element[ controlName ]("destroy");
12✔
749
                                                                }
750
                                                                if ($.ig.angular[ controlName ] &&
13✔
751
                                                                                $.ig.angular[ controlName ].events &&
752
                                                                                $.ig.angular[ controlName ].events.length) {
753
                                                                        element.off($.ig.angular[ controlName ].events.join(" "));
3✔
754
                                                                }
755
                                                        });
756

757
                                                        /* removing the width and height attributes on the placeholder, because they affect the control dimensions
758
                                                                remove other attributes too, they may confront with some html5 attributes for example draggable
759
                                                                D.K. fix for bug #234087
760
                                                        */
761
                                                        for (var a in attrs) {
13✔
762
                                                                if (a !== "id" &&
180✔
763
                                                                                !a.startsWith("$") &&
764
                                                                                !a.startsWith("data-") &&
765
                                                                                !a.startsWith("ng-") &&
766
                                                                                element.removeAttr) {
767
                                                                        element.removeAttr(a.replace(/([A-Z])/g, "-$1").toLowerCase());
48✔
768
                                                                }
769
                                                        }
770

771
                                                        element[ controlName ](options);
13✔
772

773
                                                }
774
                                        }
775
                                };
776
                        }
777
                };
778
        };
779

780
        for (var control in $.ui) {
1✔
781
                if (control.substring(0, 2) === "ig") {
128✔
782
                        module.directive(control, igniteElementDirectiveConstructor);
101✔
783
                }
784
        }
785
}(angular, jQuery));
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc