<commit_before>{
  "widgetsBundle": {
    "alias": "entity_admin_widgets",
    "title": "Entity admin widgets",
    "image": null
  },
  "widgetTypes": [
    {
      "alias": "device_admin_table",
      "name": "Device admin table",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 6.5,
        "resources": [],
        "templateHtml": "<tb-entities-table-widget \n    [ctx]=\"ctx\">\n</tb-entities-table-widget>",
        "templateCss": "",
        "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n    self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        hasDataPageLink: true,\n        warnOnPageDataOverflow: false,\n        dataKeysOptional: true\n    };\n}\n\nself.actionSources = function() {\n    return {\n        'actionCellButton': {\n            name: 'widget-action.action-cell-button',\n            multiple: true\n        },\n        'rowClick': {\n            name: 'widget-action.row-click',\n            multiple: false\n        },\n        'rowDoubleClick': {\n            name: 'widget-action.row-double-click',\n            multiple: false\n        }\n    };\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"EntitiesTableSettings\",\n        \"properties\": {\n            \"entitiesTitle\": {\n                \"title\": \"Entities table title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"enableSearch\": {\n                \"title\": \"Enable entities search\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"enableSelectColumnDisplay\": {\n                \"title\": \"Enable select columns to display\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayEntityName\": {\n                \"title\": \"Display entity name column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"entityNameColumnTitle\": {\n                \"title\": \"Entity name column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityLabel\": {\n                \"title\": \"Display entity label column\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"entityLabelColumnTitle\": {\n                \"title\": \"Entity label column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityType\": {\n                \"title\": \"Display entity type column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayPagination\": {\n                \"title\": \"Display pagination\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"defaultPageSize\": {\n                \"title\": \"Default page size\",\n                \"type\": \"number\",\n                \"default\": 10\n            },\n            \"defaultSortOrder\": {\n                \"title\": \"Default sort order\",\n                \"type\": \"string\",\n                \"default\": \"entityName\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"entitiesTitle\",\n        \"enableSearch\",\n        \"enableSelectColumnDisplay\",\n        \"displayEntityName\",\n        \"entityNameColumnTitle\",\n        \"displayEntityLabel\",\n        \"entityLabelColumnTitle\",\n        \"displayEntityType\",\n        \"displayPagination\",\n        \"defaultPageSize\",\n        \"defaultSortOrder\"\n    ]\n}",
        "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"columnWidth\": {\n                \"title\": \"Column width (px or %)\",\n                \"type\": \"string\",\n                \"default\": \"0px\"\n            },\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, entity, filter)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"columnWidth\",\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\"\n        },\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\"\n        }\n    ]\n}",
        "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Device admin table\",\"enableSelectColumnDisplay\":true},\"title\":\"Device admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add device\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"<form #addDeviceForm=\\\"ngForm\\\" [formGroup]=\\\"addDeviceFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Add device</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Device name</mat-label>\\n                <input matInput formControlName=\\\"deviceName\\\" required>\\n                <mat-error *ngIf=\\\"addDeviceFormGroup.get('deviceName').hasError('required')\\\">\\n                    Device name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"deviceType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'DEVICE'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"deviceLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || addDeviceForm.invalid || !addDeviceForm.dirty\\\">\\n      Create\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenAddDeviceDialog();\\n\\nfunction openAddDeviceDialog() {\\n    customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\\n}\\n\\nfunction AddDeviceDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.addDeviceFormGroup = vm.fb.group({\\n      deviceName: ['', [vm.validators.required]],\\n      deviceType: ['', [vm.validators.required]],\\n      deviceLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.addDeviceFormGroup.markAsPristine();\\n        let device = {\\n            name: vm.addDeviceFormGroup.get('deviceName').value,\\n            type: vm.addDeviceFormGroup.get('deviceType').value,\\n            label: vm.addDeviceFormGroup.get('deviceLabel').value\\n        };\\n        deviceService.saveDevice(device).subscribe(\\n            function (device) {\\n                saveAttributes(device.id).subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    function saveAttributes(entityId) {\\n        let attributes = vm.addDeviceFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, \\\"SERVER_SCOPE\\\", attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit device\",\"icon\":\"edit\",\"type\":\"customPretty\",\"customHtml\":\"<form #editDeviceForm=\\\"ngForm\\\" [formGroup]=\\\"editDeviceFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Edit device</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Device name</mat-label>\\n                <input matInput formControlName=\\\"deviceName\\\" required>\\n                <mat-error *ngIf=\\\"editDeviceFormGroup.get('deviceName').hasError('required')\\\">\\n                    Device name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"deviceType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'DEVICE'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"deviceLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || editDeviceForm.invalid || !editDeviceForm.dirty\\\">\\n      Update\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenEditDeviceDialog();\\n\\nfunction openEditDeviceDialog() {\\n    customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\\n}\\n\\nfunction EditDeviceDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.device = null;\\n    vm.attributes = {};\\n    \\n    vm.editDeviceFormGroup = vm.fb.group({\\n      deviceName: ['', [vm.validators.required]],\\n      deviceType: ['', [vm.validators.required]],\\n      deviceLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.editDeviceFormGroup.markAsPristine();\\n        vm.device.name = vm.editDeviceFormGroup.get('deviceName').value,\\n        vm.device.type = vm.editDeviceFormGroup.get('deviceType').value,\\n        vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value\\n        deviceService.saveDevice(vm.device).subscribe(\\n            function () {\\n                saveAttributes().subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    getEntityInfo();\\n    \\n    function getEntityInfo() {\\n        deviceService.getDevice(entityId.id).subscribe(\\n            function (device) {\\n                attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n                                                    ['latitude', 'longitude']).subscribe(\\n                   function (attributes) {\\n                        for (let i = 0; i < attributes.length; i++) {\\n                            vm.attributes[attributes[i].key] = attributes[i].value; \\n                        }\\n                        vm.device = device;\\n                        vm.editDeviceFormGroup.patchValue(\\n                            {\\n                                deviceName: vm.device.name,\\n                                deviceType: vm.device.type,\\n                                deviceLabel: vm.device.label,\\n                                attributes: {\\n                                    latitude: vm.attributes.latitude,\\n                                    longitude: vm.attributes.longitude\\n                                }\\n                            }, {emitEvent: false}\\n                        );\\n                   } \\n                );\\n            }\\n        );    \\n    }\\n    \\n    function saveAttributes() {\\n        let attributes = vm.editDeviceFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete device\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\n\\nopenDeleteDeviceDialog();\\n\\nfunction openDeleteDeviceDialog() {\\n    let title = \\\"Are you sure you want to delete the device \\\" + entityName +  \\\"?\\\";\\n    let content = \\\"Be careful, after the confirmation, the device and all related data will become unrecoverable!\\\";\\n    dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n        function (result) {\\n            if (result) {\\n                deleteDevice();\\n            }\\n        }\\n    );\\n}\\n\\nfunction deleteDevice() {\\n    deviceService.deleteDevice(entityId.id).subscribe(\\n        function () {\\n            widgetContext.updateAliases();\\n        }\\n    );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}"
      }
    },
    {
      "alias": "asset_admin_table",
      "name": "Asset admin table",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 6.5,
        "resources": [],
        "templateHtml": "<tb-entities-table-widget \n    [ctx]=\"ctx\">\n</tb-entities-table-widget>",
        "templateCss": "",
        "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n    self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        hasDataPageLink: true,\n        warnOnPageDataOverflow: false,\n        dataKeysOptional: true\n    };\n}\n\nself.actionSources = function() {\n    return {\n        'actionCellButton': {\n            name: 'widget-action.action-cell-button',\n            multiple: true\n        },\n        'rowClick': {\n            name: 'widget-action.row-click',\n            multiple: false\n        },\n        'rowDoubleClick': {\n            name: 'widget-action.row-double-click',\n            multiple: false\n        }\n    };\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"EntitiesTableSettings\",\n        \"properties\": {\n            \"entitiesTitle\": {\n                \"title\": \"Entities table title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"enableSearch\": {\n                \"title\": \"Enable entities search\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"enableSelectColumnDisplay\": {\n                \"title\": \"Enable select columns to display\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayEntityName\": {\n                \"title\": \"Display entity name column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"entityNameColumnTitle\": {\n                \"title\": \"Entity name column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityLabel\": {\n                \"title\": \"Display entity label column\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"entityLabelColumnTitle\": {\n                \"title\": \"Entity label column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityType\": {\n                \"title\": \"Display entity type column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayPagination\": {\n                \"title\": \"Display pagination\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"defaultPageSize\": {\n                \"title\": \"Default page size\",\n                \"type\": \"number\",\n                \"default\": 10\n            },\n            \"defaultSortOrder\": {\n                \"title\": \"Default sort order\",\n                \"type\": \"string\",\n                \"default\": \"entityName\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"entitiesTitle\",\n        \"enableSearch\",\n        \"enableSelectColumnDisplay\",\n        \"displayEntityName\",\n        \"entityNameColumnTitle\",\n        \"displayEntityLabel\",\n        \"entityLabelColumnTitle\",\n        \"displayEntityType\",\n        \"displayPagination\",\n        \"defaultPageSize\",\n        \"defaultSortOrder\"\n    ]\n}",
        "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"columnWidth\": {\n                \"title\": \"Column width (px or %)\",\n                \"type\": \"string\",\n                \"default\": \"0px\"\n            },\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, entity, filter)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"columnWidth\",\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\"\n        },\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\"\n        }\n    ]\n}",
        "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Asset admin table\",\"enableSelectColumnDisplay\":true},\"title\":\"Asset admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add asset\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"<form #addAssetForm=\\\"ngForm\\\" [formGroup]=\\\"addAssetFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Add asset</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Asset name</mat-label>\\n                <input matInput formControlName=\\\"assetName\\\" required>\\n                <mat-error *ngIf=\\\"addAssetFormGroup.get('assetName').hasError('required')\\\">\\n                    Asset name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"assetType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'ASSET'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"assetLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || addAssetForm.invalid || !addAssetForm.dirty\\\">\\n      Create\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenAddAssetDialog();\\n\\nfunction openAddAssetDialog() {\\n    customDialog.customDialog(htmlTemplate, AddAssetDialogController).subscribe();\\n}\\n\\nfunction AddAssetDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.addAssetFormGroup = vm.fb.group({\\n      assetName: ['', [vm.validators.required]],\\n      assetType: ['', [vm.validators.required]],\\n      assetLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.addAssetFormGroup.markAsPristine();\\n        let asset = {\\n            name: vm.addAssetFormGroup.get('assetName').value,\\n            type: vm.addAssetFormGroup.get('assetType').value,\\n            label: vm.addAssetFormGroup.get('assetLabel').value\\n        };\\n        assetService.saveAsset(asset).subscribe(\\n            function (asset) {\\n                saveAttributes(asset.id).subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    function saveAttributes(entityId) {\\n        let attributes = vm.addAssetFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, \\\"SERVER_SCOPE\\\", attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit asset\",\"icon\":\"edit\",\"type\":\"customPretty\",\"customHtml\":\"<form #editAssetForm=\\\"ngForm\\\" [formGroup]=\\\"editAssetFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Edit asset</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Asset name</mat-label>\\n                <input matInput formControlName=\\\"assetName\\\" required>\\n                <mat-error *ngIf=\\\"editAssetFormGroup.get('assetName').hasError('required')\\\">\\n                    Asset name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"assetType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'ASSET'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"assetLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxFlex fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || editAssetForm.invalid || !editAssetForm.dirty\\\">\\n      Update\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenEditAssetDialog();\\n\\nfunction openEditAssetDialog() {\\n    customDialog.customDialog(htmlTemplate, EditAssetDialogController).subscribe();\\n}\\n\\nfunction EditAssetDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.asset = null;\\n    vm.attributes = {};\\n    \\n    vm.editAssetFormGroup = vm.fb.group({\\n      assetName: ['', [vm.validators.required]],\\n      assetType: ['', [vm.validators.required]],\\n      assetLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.editAssetFormGroup.markAsPristine();\\n        vm.asset.name = vm.editAssetFormGroup.get('assetName').value,\\n        vm.asset.type = vm.editAssetFormGroup.get('assetType').value,\\n        vm.asset.label = vm.editAssetFormGroup.get('assetLabel').value\\n        assetService.saveAsset(vm.asset).subscribe(\\n            function () {\\n                saveAttributes().subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    getEntityInfo();\\n    \\n    function getEntityInfo() {\\n        assetService.getAsset(entityId.id).subscribe(\\n            function (asset) {\\n                attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n                                                    ['latitude', 'longitude']).subscribe(\\n                   function (attributes) {\\n                        for (let i = 0; i < attributes.length; i++) {\\n                            vm.attributes[attributes[i].key] = attributes[i].value; \\n                        }\\n                        vm.asset = asset;\\n                        vm.editAssetFormGroup.patchValue(\\n                            {\\n                                assetName: vm.asset.name,\\n                                assetType: vm.asset.type,\\n                                assetLabel: vm.asset.label,\\n                                attributes: {\\n                                    latitude: vm.attributes.latitude,\\n                                    longitude: vm.attributes.longitude\\n                                }\\n                            }, {emitEvent: false}\\n                        );\\n                   } \\n                );\\n            }\\n        );    \\n    }\\n    \\n    function saveAttributes() {\\n        let attributes = vm.editAssetFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete asset\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\n\\nopenDeleteAssetDialog();\\n\\nfunction openDeleteAssetDialog() {\\n    let title = \\\"Are you sure you want to delete the asset \\\" + entityName +  \\\"?\\\";\\n    let content = \\\"Be careful, after the confirmation, the asset and all related data will become unrecoverable!\\\";\\n    dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n        function (result) {\\n            if (result) {\\n                deleteAsset();\\n            }\\n        }\\n    );\\n}\\n\\nfunction deleteAsset() {\\n    assetService.deleteAsset(entityId.id).subscribe(\\n        function () {\\n            widgetContext.updateAliases();\\n        }\\n    );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}"
      }
    }
  ]
}<commit_msg>UI: Fixed admin widget in safari browser<commit_after>{
  "widgetsBundle": {
    "alias": "entity_admin_widgets",
    "title": "Entity admin widgets",
    "image": null
  },
  "widgetTypes": [
    {
      "alias": "device_admin_table",
      "name": "Device admin table",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 6.5,
        "resources": [],
        "templateHtml": "<tb-entities-table-widget \n    [ctx]=\"ctx\">\n</tb-entities-table-widget>",
        "templateCss": "",
        "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n    self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        hasDataPageLink: true,\n        warnOnPageDataOverflow: false,\n        dataKeysOptional: true\n    };\n}\n\nself.actionSources = function() {\n    return {\n        'actionCellButton': {\n            name: 'widget-action.action-cell-button',\n            multiple: true\n        },\n        'rowClick': {\n            name: 'widget-action.row-click',\n            multiple: false\n        },\n        'rowDoubleClick': {\n            name: 'widget-action.row-double-click',\n            multiple: false\n        }\n    };\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"EntitiesTableSettings\",\n        \"properties\": {\n            \"entitiesTitle\": {\n                \"title\": \"Entities table title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"enableSearch\": {\n                \"title\": \"Enable entities search\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"enableSelectColumnDisplay\": {\n                \"title\": \"Enable select columns to display\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayEntityName\": {\n                \"title\": \"Display entity name column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"entityNameColumnTitle\": {\n                \"title\": \"Entity name column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityLabel\": {\n                \"title\": \"Display entity label column\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"entityLabelColumnTitle\": {\n                \"title\": \"Entity label column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityType\": {\n                \"title\": \"Display entity type column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayPagination\": {\n                \"title\": \"Display pagination\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"defaultPageSize\": {\n                \"title\": \"Default page size\",\n                \"type\": \"number\",\n                \"default\": 10\n            },\n            \"defaultSortOrder\": {\n                \"title\": \"Default sort order\",\n                \"type\": \"string\",\n                \"default\": \"entityName\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"entitiesTitle\",\n        \"enableSearch\",\n        \"enableSelectColumnDisplay\",\n        \"displayEntityName\",\n        \"entityNameColumnTitle\",\n        \"displayEntityLabel\",\n        \"entityLabelColumnTitle\",\n        \"displayEntityType\",\n        \"displayPagination\",\n        \"defaultPageSize\",\n        \"defaultSortOrder\"\n    ]\n}",
        "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"columnWidth\": {\n                \"title\": \"Column width (px or %)\",\n                \"type\": \"string\",\n                \"default\": \"0px\"\n            },\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, entity, filter)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"columnWidth\",\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\"\n        },\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\"\n        }\n    ]\n}",
        "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Device admin table\",\"enableSelectColumnDisplay\":true},\"title\":\"Device admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add device\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"<form #addDeviceForm=\\\"ngForm\\\" [formGroup]=\\\"addDeviceFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Add device</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Device name</mat-label>\\n                <input matInput formControlName=\\\"deviceName\\\" required>\\n                <mat-error *ngIf=\\\"addDeviceFormGroup.get('deviceName').hasError('required')\\\">\\n                    Device name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"deviceType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'DEVICE'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"deviceLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || addDeviceForm.invalid || !addDeviceForm.dirty\\\">\\n      Create\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenAddDeviceDialog();\\n\\nfunction openAddDeviceDialog() {\\n    customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\\n}\\n\\nfunction AddDeviceDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.addDeviceFormGroup = vm.fb.group({\\n      deviceName: ['', [vm.validators.required]],\\n      deviceType: ['', [vm.validators.required]],\\n      deviceLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.addDeviceFormGroup.markAsPristine();\\n        let device = {\\n            name: vm.addDeviceFormGroup.get('deviceName').value,\\n            type: vm.addDeviceFormGroup.get('deviceType').value,\\n            label: vm.addDeviceFormGroup.get('deviceLabel').value\\n        };\\n        deviceService.saveDevice(device).subscribe(\\n            function (device) {\\n                saveAttributes(device.id).subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    function saveAttributes(entityId) {\\n        let attributes = vm.addDeviceFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, \\\"SERVER_SCOPE\\\", attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit device\",\"icon\":\"edit\",\"type\":\"customPretty\",\"customHtml\":\"<form #editDeviceForm=\\\"ngForm\\\" [formGroup]=\\\"editDeviceFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Edit device</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Device name</mat-label>\\n                <input matInput formControlName=\\\"deviceName\\\" required>\\n                <mat-error *ngIf=\\\"editDeviceFormGroup.get('deviceName').hasError('required')\\\">\\n                    Device name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"deviceType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'DEVICE'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"deviceLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || editDeviceForm.invalid || !editDeviceForm.dirty\\\">\\n      Update\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenEditDeviceDialog();\\n\\nfunction openEditDeviceDialog() {\\n    customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\\n}\\n\\nfunction EditDeviceDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.device = null;\\n    vm.attributes = {};\\n    \\n    vm.editDeviceFormGroup = vm.fb.group({\\n      deviceName: ['', [vm.validators.required]],\\n      deviceType: ['', [vm.validators.required]],\\n      deviceLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.editDeviceFormGroup.markAsPristine();\\n        vm.device.name = vm.editDeviceFormGroup.get('deviceName').value,\\n        vm.device.type = vm.editDeviceFormGroup.get('deviceType').value,\\n        vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value\\n        deviceService.saveDevice(vm.device).subscribe(\\n            function () {\\n                saveAttributes().subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    getEntityInfo();\\n    \\n    function getEntityInfo() {\\n        deviceService.getDevice(entityId.id).subscribe(\\n            function (device) {\\n                attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n                                                    ['latitude', 'longitude']).subscribe(\\n                   function (attributes) {\\n                        for (let i = 0; i < attributes.length; i++) {\\n                            vm.attributes[attributes[i].key] = attributes[i].value; \\n                        }\\n                        vm.device = device;\\n                        vm.editDeviceFormGroup.patchValue(\\n                            {\\n                                deviceName: vm.device.name,\\n                                deviceType: vm.device.type,\\n                                deviceLabel: vm.device.label,\\n                                attributes: {\\n                                    latitude: vm.attributes.latitude,\\n                                    longitude: vm.attributes.longitude\\n                                }\\n                            }, {emitEvent: false}\\n                        );\\n                   } \\n                );\\n            }\\n        );    \\n    }\\n    \\n    function saveAttributes() {\\n        let attributes = vm.editDeviceFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete device\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\\n\\nopenDeleteDeviceDialog();\\n\\nfunction openDeleteDeviceDialog() {\\n    let title = \\\"Are you sure you want to delete the device \\\" + entityName +  \\\"?\\\";\\n    let content = \\\"Be careful, after the confirmation, the device and all related data will become unrecoverable!\\\";\\n    dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n        function (result) {\\n            if (result) {\\n                deleteDevice();\\n            }\\n        }\\n    );\\n}\\n\\nfunction deleteDevice() {\\n    deviceService.deleteDevice(entityId.id).subscribe(\\n        function () {\\n            widgetContext.updateAliases();\\n        }\\n    );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}"
      }
    },
    {
      "alias": "asset_admin_table",
      "name": "Asset admin table",
      "descriptor": {
        "type": "latest",
        "sizeX": 7.5,
        "sizeY": 6.5,
        "resources": [],
        "templateHtml": "<tb-entities-table-widget \n    [ctx]=\"ctx\">\n</tb-entities-table-widget>",
        "templateCss": "",
        "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n    self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n    return {\n        maxDatasources: 1,\n        hasDataPageLink: true,\n        warnOnPageDataOverflow: false,\n        dataKeysOptional: true\n    };\n}\n\nself.actionSources = function() {\n    return {\n        'actionCellButton': {\n            name: 'widget-action.action-cell-button',\n            multiple: true\n        },\n        'rowClick': {\n            name: 'widget-action.row-click',\n            multiple: false\n        },\n        'rowDoubleClick': {\n            name: 'widget-action.row-double-click',\n            multiple: false\n        }\n    };\n}\n\nself.onDestroy = function() {\n}\n",
        "settingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"EntitiesTableSettings\",\n        \"properties\": {\n            \"entitiesTitle\": {\n                \"title\": \"Entities table title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"enableSearch\": {\n                \"title\": \"Enable entities search\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"enableSelectColumnDisplay\": {\n                \"title\": \"Enable select columns to display\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayEntityName\": {\n                \"title\": \"Display entity name column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"entityNameColumnTitle\": {\n                \"title\": \"Entity name column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityLabel\": {\n                \"title\": \"Display entity label column\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"entityLabelColumnTitle\": {\n                \"title\": \"Entity label column title\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"displayEntityType\": {\n                \"title\": \"Display entity type column\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"displayPagination\": {\n                \"title\": \"Display pagination\",\n                \"type\": \"boolean\",\n                \"default\": true\n            },\n            \"defaultPageSize\": {\n                \"title\": \"Default page size\",\n                \"type\": \"number\",\n                \"default\": 10\n            },\n            \"defaultSortOrder\": {\n                \"title\": \"Default sort order\",\n                \"type\": \"string\",\n                \"default\": \"entityName\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"entitiesTitle\",\n        \"enableSearch\",\n        \"enableSelectColumnDisplay\",\n        \"displayEntityName\",\n        \"entityNameColumnTitle\",\n        \"displayEntityLabel\",\n        \"entityLabelColumnTitle\",\n        \"displayEntityType\",\n        \"displayPagination\",\n        \"defaultPageSize\",\n        \"defaultSortOrder\"\n    ]\n}",
        "dataKeySettingsSchema": "{\n    \"schema\": {\n        \"type\": \"object\",\n        \"title\": \"DataKeySettings\",\n        \"properties\": {\n            \"columnWidth\": {\n                \"title\": \"Column width (px or %)\",\n                \"type\": \"string\",\n                \"default\": \"0px\"\n            },\n            \"useCellStyleFunction\": {\n                \"title\": \"Use cell style function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellStyleFunction\": {\n                \"title\": \"Cell style function: f(value)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            },\n            \"useCellContentFunction\": {\n                \"title\": \"Use cell content function\",\n                \"type\": \"boolean\",\n                \"default\": false\n            },\n            \"cellContentFunction\": {\n                \"title\": \"Cell content function: f(value, entity, filter)\",\n                \"type\": \"string\",\n                \"default\": \"\"\n            }\n        },\n        \"required\": []\n    },\n    \"form\": [\n        \"columnWidth\",\n        \"useCellStyleFunction\",\n        {\n            \"key\": \"cellStyleFunction\",\n            \"type\": \"javascript\"\n        },\n        \"useCellContentFunction\",\n        {\n            \"key\": \"cellContentFunction\",\n            \"type\": \"javascript\"\n        }\n    ]\n}",
        "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSearch\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true,\"entitiesTitle\":\"Asset admin table\",\"enableSelectColumnDisplay\":true},\"title\":\"Asset admin table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{\"headerButton\":[{\"name\":\"Add asset\",\"icon\":\"add\",\"type\":\"customPretty\",\"customHtml\":\"<form #addAssetForm=\\\"ngForm\\\" [formGroup]=\\\"addAssetFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Add asset</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Asset name</mat-label>\\n                <input matInput formControlName=\\\"assetName\\\" required>\\n                <mat-error *ngIf=\\\"addAssetFormGroup.get('assetName').hasError('required')\\\">\\n                    Asset name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"assetType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'ASSET'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"assetLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || addAssetForm.invalid || !addAssetForm.dirty\\\">\\n      Create\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenAddAssetDialog();\\n\\nfunction openAddAssetDialog() {\\n    customDialog.customDialog(htmlTemplate, AddAssetDialogController).subscribe();\\n}\\n\\nfunction AddAssetDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.addAssetFormGroup = vm.fb.group({\\n      assetName: ['', [vm.validators.required]],\\n      assetType: ['', [vm.validators.required]],\\n      assetLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.addAssetFormGroup.markAsPristine();\\n        let asset = {\\n            name: vm.addAssetFormGroup.get('assetName').value,\\n            type: vm.addAssetFormGroup.get('assetType').value,\\n            label: vm.addAssetFormGroup.get('assetLabel').value\\n        };\\n        assetService.saveAsset(asset).subscribe(\\n            function (asset) {\\n                saveAttributes(asset.id).subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    function saveAttributes(entityId) {\\n        let attributes = vm.addAssetFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, \\\"SERVER_SCOPE\\\", attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"70837a9d-c3de-a9a7-03c5-dccd14998758\"}],\"actionCellButton\":[{\"name\":\"Edit asset\",\"icon\":\"edit\",\"type\":\"customPretty\",\"customHtml\":\"<form #editAssetForm=\\\"ngForm\\\" [formGroup]=\\\"editAssetFormGroup\\\"\\n      (ngSubmit)=\\\"save()\\\" style=\\\"width: 480px;\\\">\\n  <mat-toolbar fxLayout=\\\"row\\\" color=\\\"primary\\\">\\n    <h2>Edit asset</h2>\\n    <span fxFlex></span>\\n    <button mat-button mat-icon-button\\n            (click)=\\\"cancel()\\\"\\n            type=\\\"button\\\">\\n      <mat-icon class=\\\"material-icons\\\">close</mat-icon>\\n    </button>\\n  </mat-toolbar>\\n  <mat-progress-bar color=\\\"warn\\\" mode=\\\"indeterminate\\\" *ngIf=\\\"isLoading$ | async\\\">\\n  </mat-progress-bar>\\n  <div style=\\\"height: 4px;\\\" *ngIf=\\\"!(isLoading$ | async)\\\"></div>\\n  <div mat-dialog-content>\\n      <div class=\\\"mat-padding\\\" fxLayout=\\\"column\\\">\\n            <mat-form-field class=\\\"mat-block\\\">\\n                <mat-label>Asset name</mat-label>\\n                <input matInput formControlName=\\\"assetName\\\" required>\\n                <mat-error *ngIf=\\\"editAssetFormGroup.get('assetName').hasError('required')\\\">\\n                    Asset name is required.\\n                </mat-error>\\n            </mat-form-field>\\n            <div fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <tb-entity-subtype-autocomplete\\n                    fxFlex=\\\"50\\\"\\n                    formControlName=\\\"assetType\\\"\\n                    [required]=\\\"true\\\"\\n                    [entityType]=\\\"'ASSET'\\\"\\n                ></tb-entity-subtype-autocomplete>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Label</mat-label>\\n                    <input matInput formControlName=\\\"assetLabel\\\">\\n                </mat-form-field>\\n            </div>\\n            <div formGroupName=\\\"attributes\\\" fxLayout=\\\"row\\\" fxLayoutGap=\\\"8px\\\">\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Latitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"latitude\\\">\\n                </mat-form-field>\\n                <mat-form-field fxFlex=\\\"50\\\" class=\\\"mat-block\\\">\\n                    <mat-label>Longitude</mat-label>\\n                    <input type=\\\"number\\\" step=\\\"any\\\" matInput formControlName=\\\"longitude\\\">\\n                </mat-form-field>\\n            </div>\\n      </div>  \\n  </div>\\n  <div mat-dialog-actions fxLayout=\\\"row\\\">\\n    <span fxFlex></span>\\n    <button mat-button mat-raised-button color=\\\"primary\\\"\\n            type=\\\"submit\\\"\\n            [disabled]=\\\"(isLoading$ | async) || editAssetForm.invalid || !editAssetForm.dirty\\\">\\n      Update\\n    </button>\\n    <button mat-button color=\\\"primary\\\"\\n            style=\\\"margin-right: 20px;\\\"\\n            type=\\\"button\\\"\\n            [disabled]=\\\"(isLoading$ | async)\\\"\\n            (click)=\\\"cancel()\\\" cdkFocusInitial>\\n      Cancel\\n    </button>\\n  </div>\\n</form>\\n\",\"customCss\":\"\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\\n\\nopenEditAssetDialog();\\n\\nfunction openEditAssetDialog() {\\n    customDialog.customDialog(htmlTemplate, EditAssetDialogController).subscribe();\\n}\\n\\nfunction EditAssetDialogController(instance) {\\n    let vm = instance;\\n    \\n    vm.asset = null;\\n    vm.attributes = {};\\n    \\n    vm.editAssetFormGroup = vm.fb.group({\\n      assetName: ['', [vm.validators.required]],\\n      assetType: ['', [vm.validators.required]],\\n      assetLabel: [''],\\n      attributes: vm.fb.group({\\n          latitude: [null],\\n          longitude: [null]\\n      })      \\n    });\\n    \\n    vm.cancel = function() {\\n        vm.dialogRef.close(null);\\n    };\\n    \\n    vm.save = function() {\\n        vm.editAssetFormGroup.markAsPristine();\\n        vm.asset.name = vm.editAssetFormGroup.get('assetName').value,\\n        vm.asset.type = vm.editAssetFormGroup.get('assetType').value,\\n        vm.asset.label = vm.editAssetFormGroup.get('assetLabel').value\\n        assetService.saveAsset(vm.asset).subscribe(\\n            function () {\\n                saveAttributes().subscribe(\\n                    function () {\\n                        widgetContext.updateAliases();\\n                        vm.dialogRef.close(null);\\n                    }\\n                );\\n            }\\n        );\\n    };\\n    \\n    getEntityInfo();\\n    \\n    function getEntityInfo() {\\n        assetService.getAsset(entityId.id).subscribe(\\n            function (asset) {\\n                attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\\n                                                    ['latitude', 'longitude']).subscribe(\\n                   function (attributes) {\\n                        for (let i = 0; i < attributes.length; i++) {\\n                            vm.attributes[attributes[i].key] = attributes[i].value; \\n                        }\\n                        vm.asset = asset;\\n                        vm.editAssetFormGroup.patchValue(\\n                            {\\n                                assetName: vm.asset.name,\\n                                assetType: vm.asset.type,\\n                                assetLabel: vm.asset.label,\\n                                attributes: {\\n                                    latitude: vm.attributes.latitude,\\n                                    longitude: vm.attributes.longitude\\n                                }\\n                            }, {emitEvent: false}\\n                        );\\n                   } \\n                );\\n            }\\n        );    \\n    }\\n    \\n    function saveAttributes() {\\n        let attributes = vm.editAssetFormGroup.get('attributes').value;\\n        let attributesArray = [];\\n        for (let key in attributes) {\\n            attributesArray.push({key: key, value: attributes[key]});\\n        }\\n        if (attributesArray.length > 0) {\\n            return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\\n        } else {\\n            return widgetContext.rxjs.of([]);\\n        }\\n    }\\n}\",\"customResources\":[],\"id\":\"93931e52-5d7c-903e-67aa-b9435df44ff4\"},{\"name\":\"Delete asset\",\"icon\":\"delete\",\"type\":\"custom\",\"customFunction\":\"let $injector = widgetContext.$scope.$injector;\\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\\n\\nopenDeleteAssetDialog();\\n\\nfunction openDeleteAssetDialog() {\\n    let title = \\\"Are you sure you want to delete the asset \\\" + entityName +  \\\"?\\\";\\n    let content = \\\"Be careful, after the confirmation, the asset and all related data will become unrecoverable!\\\";\\n    dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\\n        function (result) {\\n            if (result) {\\n                deleteAsset();\\n            }\\n        }\\n    );\\n}\\n\\nfunction deleteAsset() {\\n    assetService.deleteAsset(entityId.id).subscribe(\\n        function () {\\n            widgetContext.updateAliases();\\n        }\\n    );\\n}\\n\",\"id\":\"ec2708f6-9ff0-186b-e4fc-7635ebfa3074\"}]}}"
      }
    }
  ]
}
{'fraction_non_alphanumeric': 0.2303428479516969, 'fraction_numerical': 0.010244608589139231, 'mean_word_length': 1.6564995891536565, 'pattern_counts': {'":': 686, '<': 467, '<?xml version=': 0, '>': 471, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6002748', 'n_tokens_mistral': 24116, 'n_tokens_neox': 22877, 'n_words': 3788}