<commit_before>angular.module('PaperUI.controllers.things') //
.controller('ViewThingController', function($scope, $mdDialog, toastService, thingTypeService, thingRepository, thingService, linkService, channelTypeRepository, configService, thingConfigService, util, itemRepository, channelTypeService) {
    $scope.setSubtitle([ 'Things' ]);

    var thingUID = $scope.path[4];
    $scope.thingTypeUID = null;
    $scope.advancedMode;
    $scope.thing;
    $scope.thingType;
    $scope.thingChannels = [];
    $scope.showAdvanced = false;
    $scope.channelTypes;
    $scope.items;

    channelTypeRepository.getAll(function(channels) {
        $scope.channelTypes = channels;
        $scope.refreshChannels(false);
    });

    itemRepository.getAll(function(items) {
        $scope.items = items;
    });

    $scope.remove = function(thing, event) {
        event.stopImmediatePropagation();
        $mdDialog.show({
            controller : 'RemoveThingDialogController',
            templateUrl : 'partials/dialog.removething.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                thing : thing
            }
        }).then(function() {
            $scope.navigateTo('');
        });
    }

    $scope.enableChannel = function(thingUID, channelID, event, longPress) {
        var channel = $scope.getChannelById(channelID);
        event.stopImmediatePropagation();
        if ($scope.advancedMode || channel.kind === 'TRIGGER') {
            if (channel.linkedItems.length > 0) {
                $scope.getLinkedItems(channel, event);
            } else {
                $scope.linkChannel(channelID, event, longPress);
            }
        } else if (channel.linkedItems.length == 0) {
            linkService.link({
                itemName : $scope.thing.UID.replace(/[^a-zA-Z0-9_]/g, "_") + '_' + channelID.replace(/[^a-zA-Z0-9_]/g, "_"),
                channelUID : $scope.thing.UID + ':' + channelID
            }, function(newItem) {
                $scope.getThing(true);
                toastService.showDefaultToast('Channel linked');
            });
        }
    };

    $scope.disableChannel = function(thingUID, channelID, itemName, event) {
        var channel = $scope.getChannelById(channelID);
        event.stopImmediatePropagation();
        var linkedItem = channel.linkedItems[0];
        if (!itemName || itemName === '') {
            itemName = linkedItem;
        }
        if ($scope.advancedMode || channel.kind === 'TRIGGER') {
            $scope.unlinkChannel(channelID, itemName, event);
        } else {
            linkService.unlink({
                itemName : $scope.thing.UID.replace(/[^a-zA-Z0-9_]/g, "_") + '_' + channelID.replace(/[^a-zA-Z0-9_]/g, "_"),
                channelUID : $scope.thing.UID + ':' + channelID
            }, function() {
                $scope.getThing(true);
                toastService.showDefaultToast('Channel unlinked');
            });
        }
    };

    $scope.linkChannel = function(channelID, event, preSelect) {
        var channel = $scope.getChannelById(channelID);
        var channelType = $scope.getChannelTypeByUID(channel.channelTypeUID);

        var params = {
            linkedItems : channel.linkedItems.length > 0 ? channel.linkedItems : '',
            acceptedItemTypes : channel.acceptedItemTypes,
            category : channelType.category ? channelType.category : '',
            suggestedName : getItemNameSuggestion(channelID, channelType.label),
            suggestedLabel : channelType.label,
            suggestedCategory : channelType.category ? channelType.category : '',
            preSelectCreate : preSelect,
            allowNewItemCreation : $scope.advancedMode && channel.kind !== 'TRIGGER' // allow "Create new Item" in
            // advanced mode only, disable
        // for normalMode or trigger
        // channel
        }
        $mdDialog.show({
            controller : 'LinkChannelDialogController',
            templateUrl : 'partials/dialog.linkchannel.html',
            targetEvent : event,
            hasBackdrop : true,
            params : params
        }).then(function(newItem) {
            if (newItem) {
                linkService.link({
                    itemName : newItem.itemName,
                    channelUID : $scope.thing.UID + ':' + channelID
                }, function() {
                    $scope.getThing(true);
                    var item = $.grep($scope.items, function(item) {
                        return item.name == newItem.itemName;
                    });
                    channel.items = channel.items ? channel.items : [];
                    if (item.length > 0) {
                        channel.items.push(item[0]);
                    } else {
                        channel.items.push({
                            name : newItem.itemName,
                            label : newItem.label
                        });
                    }
                    toastService.showDefaultToast('Channel linked');
                });
            }
        });
    }

    function getItemNameSuggestion(channelID, label) {
        var itemName = getInCamelCase($scope.thing.label);
        if (channelID) {
            var id = channelID.split('#');
            if (id.length > 1 && id[0].length > 0) {
                itemName += ('_' + getInCamelCase(id[0]));
            }
            itemName += ('_' + getInCamelCase(label));
        }
        return itemName;
    }

    function getInCamelCase(str) {
        var camelStr = "";
        if (str) {
            var arr = str.split(/[^a-zA-Z0-9_]/g);
            for (var i = 0; i < arr.length; i++) {
                if (arr[i] && arr[i].length > 0) {
                    camelStr += (arr[i][0].toUpperCase() + (arr[i].length > 1 ? arr[i].substring(1, arr[i].length) : ''));
                }
            }
        }
        return camelStr;
    }

    $scope.unlinkChannel = function(channelID, itemName, event) {
        var channel = $scope.getChannelById(channelID);
        $mdDialog.show({
            controller : 'UnlinkChannelDialogController',
            templateUrl : 'partials/dialog.unlinkchannel.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                itemName : itemName
            }
        }).then(function() {
            if (itemName) {
                linkService.unlink({
                    itemName : itemName,
                    channelUID : $scope.thing.UID + ':' + channelID
                }, function() {
                    $scope.getThing(true);
                    var item = $.grep(channel.items, function(item) {
                        return item.name == itemName;
                    });
                    if (item.length > 0) {
                        channel.items.splice(channel.items.indexOf(item[0]), 1);
                    }
                    toastService.showDefaultToast('Channel unlinked');
                });
            }
        });
    }
    $scope.getChannelById = function(channelId) {
        if (!$scope.thing) {
            return;
        }
        return $.grep($scope.thing.channels, function(channel, i) {
            return channelId == channel.id;
        })[0];
    }

    $scope.getChannelTypeByUID = function(channelUID) {
        return thingConfigService.getChannelTypeByUID($scope.thingType, $scope.channelTypes, channelUID);
    };

    $scope.getChannelFromChannelTypes = function(channelUID) {
        if (!$scope.channelTypes) {
            return;
        }
        return thingConfigService.getChannelFromChannelTypes($scope.channelTypes, channelUID);
    };

    var getChannels = function(advanced) {
        if (!$scope.thingType || !$scope.thing || !$scope.channelTypes) {
            return;
        }
        $scope.isAdvanced = checkAdvance($scope.thing.channels);
        var thingChannels = thingConfigService.getThingChannels($scope.thing, $scope.thingType, $scope.channelTypes, advanced);

        // set the linkable item types for each channel
        var channelType2ItemTypeCache = [];
        angular.forEach(thingChannels, function(channelGroup) {
            angular.forEach(channelGroup.channels, function(channel) {
                if (channel.kind !== 'TRIGGER') {
                    channel.acceptedItemTypes = [ channel.itemType ];
                    return true;
                }
                if (channelType2ItemTypeCache[channel.channelTypeUID]) {
                    channel.acceptedItemTypes = channelType2ItemTypeCache[channel.channelTypeUID];
                    return true;
                }

                var acceptedItemTypes = [];
                channelTypeService.getLinkableItemTypes({
                    channelTypeUID : channel.channelTypeUID
                }, function(linkableItemTypes) {
                    if (linkableItemTypes && linkableItemTypes.length > 0) {
                        angular.forEach(linkableItemTypes, function(itemType) {
                            acceptedItemTypes.push(itemType);
                        });
                    } else if (channel.itemType) {
                        acceptedItemTypes.push(channel.itemType);
                    }
                });
                channelType2ItemTypeCache[channel.channelTypeUID] = acceptedItemTypes;
                channel.acceptedItemTypes = acceptedItemTypes;
            });
        });

        return thingChannels;
    };

    $scope.refreshChannels = function(showAdvanced) {
        $scope.thingChannels = getChannels(showAdvanced);
    }

    function checkAdvance(channels) {
        var advancedChannels = channels.filter(function(channel) {
            var channelType = $scope.getChannelTypeByUID(channel.channelTypeUID);
            return channelType && channelType.advanced
        })

        return advancedChannels.length > 0
    }

    $scope.getThing = function(refresh) {
        thingRepository.getOne(function(thing) {
            return thing.UID === thingUID;
        }, function(thing) {
            angular.forEach(thing.channels, function(value, i) {
                value.showItems = $scope.thing ? $scope.thing.channels[i].showItems : false;
                value.items = $scope.thing ? $scope.thing.channels[i].items : null;
            });
            $scope.thing = thing;
            checkThingProperties(thing);
            $scope.thingTypeUID = thing.thingTypeUID;
            getThingType();
            $scope.setSubtitle([ 'Things', thing.label ]);
        }, refresh);
    }

    function checkThingProperties(thing) {
        if (thing.properties) {
            var hasFirmwareVersion = thing.properties['firmwareVersion'];
            if ((Object.keys(thing.properties).length > 0 && !hasFirmwareVersion) || (Object.keys(thing.properties).length > 1 && hasFirmwareVersion)) {
                $scope.thing.hasProperties = true;
            } else {
                $scope.thing.hasProperties = false;
            }
        } else {
            $scope.thing.hasProperties = false;
        }
    }
    $scope.getThing(true);

    function getThingType() {
        thingTypeService.getByUid({
            thingTypeUID : $scope.thingTypeUID
        }, function(thingType) {
            $scope.thingType = thingType;
            if (thingType) {
                $scope.thingTypeChannels = thingType.channels && thingType.channels.length > 0 ? thingType.channels : thingType.channelGroups;
                $scope.setHeaderText(thingType.description);
            }
            $scope.refreshChannels($scope.showAdvanced);
        });
    }

    $scope.configChannel = function(channel, thing, event) {
        var channelType = this.getChannelFromChannelTypes(channel.channelTypeUID);

        $mdDialog.show({
            controller : 'ChannelConfigController',
            templateUrl : 'partials/dialog.channelconfig.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                channelType : channelType,
                channel : channel,
                thing : thing
            }
        });
    };

    $scope.getLinkedItems = function(channel) {
        channel.showItems = !channel.showItems;
        if (channel.showItems && channel.items === null || channel.items === undefined) {
            channel.items = $.grep($scope.items, function(item) {
                return $.grep(channel.linkedItems, function(linkedItemName) {
                    return linkedItemName == item.name;
                }).length > 0;
            });
        }
    }

    $scope.showDescription = function(channel, channelType) {
        var description = channel.description ? channel.description : channel.channelType ? channel.channelType.description : null;
        if (description) {
            popup = $mdDialog.alert({
                title : channel.label ? channel.label : channel.channelType ? channel.channelType.label : channel.id,
                textContent : description,
                ok : 'Close'
            });
            $mdDialog.show(popup);
        }
    }

    $scope.$watch('thing.channels', function() {
        $scope.refreshChannels($scope.showAdvanced);
    });

    $scope.isExtensible = function() {
        var thingType = $scope.thingType
        return thingType && thingType.extensibleChannelTypeIds && thingType.extensibleChannelTypeIds.length > 0
    }

    $scope.isExtensibleChannel = function(channelTypeUID) {
        if (!channelTypeUID || !$scope.isExtensible()) {
            return false
        }

        var channelTypeId = channelTypeUID.split(':')[1]
        return $scope.thingType.extensibleChannelTypeIds.indexOf(channelTypeId) >= 0
    }

    $scope.addChannel = function(event) {
        $mdDialog.show({
            controller : 'AddChannelController',
            templateUrl : 'partials/dialog.addchannel.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                thing : $scope.thing,
                thingType : $scope.thingType,
            }
        });
    }

    $scope.removeChannel = function(channel, event) {
        event.stopImmediatePropagation();
        $mdDialog.show({
            controller : 'RemoveChannelDialogController',
            templateUrl : 'partials/dialog.removechannel.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                thing : $scope.thing,
                channel : channel
            }
        }).then(function() {
            $scope.getThing(false);
        });
    }
}).controller('AddChannelController', function($scope, $mdDialog, toastService, channelTypeRepository, configService, thingService, thingType, thing) {
    $scope.channelTypes = []
    $scope.parameters = undefined

    // values filled form the dialog
    $scope.channelType = undefined
    $scope.channelId = undefined
    $scope.channelLabel = undefined
    $scope.configuration = {}

    var refreshChannelTypes = function() {
        angular.forEach(thingType.extensibleChannelTypeIds, function(channelTypeId) {
            var channelTypeUID = thing.UID.split(":")[0] + ':' + channelTypeId;
            channelTypeRepository.getOne(function filter(element) {
                return element.UID === channelTypeUID
            }, function callback(channelType) {
                $scope.channelTypes.push(channelType)
            })
        })
    }

    $scope.$watch('channelType', function() {
        if ($scope.channelType) {
            $scope.parameters = configService.getRenderingModel($scope.channelType.parameters, $scope.channelType.parameterGroups);
        }
    })

    $scope.close = function() {
        $mdDialog.cancel();
    }

    $scope.save = function() {
        var channel = {
            uid : thing.UID + ':' + $scope.channelId,
            id : $scope.channelId,
            channelTypeUID : $scope.channelType.UID,
            itemType : $scope.channelType.itemType,
            label : $scope.channelLabel,
            configuration : $scope.configuration,
            defaultTags : $scope.channelType.tags
        }

        thing.channels.push(channel);

        thingService.update({
            thingUID : thing.UID
        }, thing, function() {
            $mdDialog.hide();
            toastService.showSuccessToast('Channel added.');
        }, function() {
            $mdDialog.hide();
            toastService.showErrorToast('Error adding channel.');
        });
    }

    refreshChannelTypes();
}).controller('RemoveChannelDialogController', function($scope, $mdDialog, toastService, thingService, thing, channel) {
    $scope.channel = channel;

    $scope.close = function() {
        $mdDialog.cancel();
    }

    $scope.remove = function() {
        var index = thing.channels.indexOf(channel);
        thing.channels.splice(index, 1);
        thingService.update({
            thingUID : thing.UID
        }, thing, function() {
            $mdDialog.hide();
            toastService.showSuccessToast('Channel removed.');
        }, function() {
            $mdDialog.hide();
            toastService.showErrorToast('Error removing channel.');
        });
    }
});
<commit_msg>Paper UI: Use channel config description instead of channel-type (#4890)<commit_after>angular.module('PaperUI.controllers.things') //
.controller('ViewThingController', function($scope, $mdDialog, toastService, thingTypeService, thingRepository, thingService, linkService, channelTypeRepository, configService, thingConfigService, util, itemRepository, channelTypeService, configDescriptionService) {
    $scope.setSubtitle([ 'Things' ]);

    var thingUID = $scope.path[4];
    $scope.thingTypeUID = null;
    $scope.advancedMode;
    $scope.thing;
    $scope.thingType;
    $scope.thingChannels = [];
    $scope.showAdvanced = false;
    $scope.channelTypes;
    $scope.items;

    channelTypeRepository.getAll(function(channels) {
        $scope.channelTypes = channels;
        $scope.refreshChannels(false);
    });

    itemRepository.getAll(function(items) {
        $scope.items = items;
    });

    $scope.remove = function(thing, event) {
        event.stopImmediatePropagation();
        $mdDialog.show({
            controller : 'RemoveThingDialogController',
            templateUrl : 'partials/dialog.removething.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                thing : thing
            }
        }).then(function() {
            $scope.navigateTo('');
        });
    }

    $scope.enableChannel = function(thingUID, channelID, event, longPress) {
        var channel = $scope.getChannelById(channelID);
        event.stopImmediatePropagation();
        if ($scope.advancedMode || channel.kind === 'TRIGGER') {
            if (channel.linkedItems.length > 0) {
                $scope.getLinkedItems(channel, event);
            } else {
                $scope.linkChannel(channelID, event, longPress);
            }
        } else if (channel.linkedItems.length == 0) {
            linkService.link({
                itemName : $scope.thing.UID.replace(/[^a-zA-Z0-9_]/g, "_") + '_' + channelID.replace(/[^a-zA-Z0-9_]/g, "_"),
                channelUID : $scope.thing.UID + ':' + channelID
            }, function(newItem) {
                $scope.getThing(true);
                toastService.showDefaultToast('Channel linked');
            });
        }
    };

    $scope.disableChannel = function(thingUID, channelID, itemName, event) {
        var channel = $scope.getChannelById(channelID);
        event.stopImmediatePropagation();
        var linkedItem = channel.linkedItems[0];
        if (!itemName || itemName === '') {
            itemName = linkedItem;
        }
        if ($scope.advancedMode || channel.kind === 'TRIGGER') {
            $scope.unlinkChannel(channelID, itemName, event);
        } else {
            linkService.unlink({
                itemName : $scope.thing.UID.replace(/[^a-zA-Z0-9_]/g, "_") + '_' + channelID.replace(/[^a-zA-Z0-9_]/g, "_"),
                channelUID : $scope.thing.UID + ':' + channelID
            }, function() {
                $scope.getThing(true);
                toastService.showDefaultToast('Channel unlinked');
            });
        }
    };

    $scope.linkChannel = function(channelID, event, preSelect) {
        var channel = $scope.getChannelById(channelID);
        var channelType = $scope.getChannelTypeByUID(channel.channelTypeUID);

        var params = {
            linkedItems : channel.linkedItems.length > 0 ? channel.linkedItems : '',
            acceptedItemTypes : channel.acceptedItemTypes,
            category : channelType.category ? channelType.category : '',
            suggestedName : getItemNameSuggestion(channelID, channelType.label),
            suggestedLabel : channelType.label,
            suggestedCategory : channelType.category ? channelType.category : '',
            preSelectCreate : preSelect,
            allowNewItemCreation : $scope.advancedMode && channel.kind !== 'TRIGGER' // allow "Create new Item" in
            // advanced mode only, disable
        // for normalMode or trigger
        // channel
        }
        $mdDialog.show({
            controller : 'LinkChannelDialogController',
            templateUrl : 'partials/dialog.linkchannel.html',
            targetEvent : event,
            hasBackdrop : true,
            params : params
        }).then(function(newItem) {
            if (newItem) {
                linkService.link({
                    itemName : newItem.itemName,
                    channelUID : $scope.thing.UID + ':' + channelID
                }, function() {
                    $scope.getThing(true);
                    var item = $.grep($scope.items, function(item) {
                        return item.name == newItem.itemName;
                    });
                    channel.items = channel.items ? channel.items : [];
                    if (item.length > 0) {
                        channel.items.push(item[0]);
                    } else {
                        channel.items.push({
                            name : newItem.itemName,
                            label : newItem.label
                        });
                    }
                    toastService.showDefaultToast('Channel linked');
                });
            }
        });
    }

    function getItemNameSuggestion(channelID, label) {
        var itemName = getInCamelCase($scope.thing.label);
        if (channelID) {
            var id = channelID.split('#');
            if (id.length > 1 && id[0].length > 0) {
                itemName += ('_' + getInCamelCase(id[0]));
            }
            itemName += ('_' + getInCamelCase(label));
        }
        return itemName;
    }

    function getInCamelCase(str) {
        var camelStr = "";
        if (str) {
            var arr = str.split(/[^a-zA-Z0-9_]/g);
            for (var i = 0; i < arr.length; i++) {
                if (arr[i] && arr[i].length > 0) {
                    camelStr += (arr[i][0].toUpperCase() + (arr[i].length > 1 ? arr[i].substring(1, arr[i].length) : ''));
                }
            }
        }
        return camelStr;
    }

    $scope.unlinkChannel = function(channelID, itemName, event) {
        var channel = $scope.getChannelById(channelID);
        $mdDialog.show({
            controller : 'UnlinkChannelDialogController',
            templateUrl : 'partials/dialog.unlinkchannel.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                itemName : itemName
            }
        }).then(function() {
            if (itemName) {
                linkService.unlink({
                    itemName : itemName,
                    channelUID : $scope.thing.UID + ':' + channelID
                }, function() {
                    $scope.getThing(true);
                    var item = $.grep(channel.items, function(item) {
                        return item.name == itemName;
                    });
                    if (item.length > 0) {
                        channel.items.splice(channel.items.indexOf(item[0]), 1);
                    }
                    toastService.showDefaultToast('Channel unlinked');
                });
            }
        });
    }
    $scope.getChannelById = function(channelId) {
        if (!$scope.thing) {
            return;
        }
        return $.grep($scope.thing.channels, function(channel, i) {
            return channelId == channel.id;
        })[0];
    }

    $scope.getChannelTypeByUID = function(channelUID) {
        return thingConfigService.getChannelTypeByUID($scope.thingType, $scope.channelTypes, channelUID);
    };

    $scope.getChannelFromChannelTypes = function(channelUID) {
        if (!$scope.channelTypes) {
            return;
        }
        return thingConfigService.getChannelFromChannelTypes($scope.channelTypes, channelUID);
    };

    var getChannels = function(advanced) {
        if (!$scope.thingType || !$scope.thing || !$scope.channelTypes) {
            return;
        }
        $scope.isAdvanced = checkAdvance($scope.thing.channels);
        var thingChannels = thingConfigService.getThingChannels($scope.thing, $scope.thingType, $scope.channelTypes, advanced);

        // set the linkable item types for each channel
        var channelType2ItemTypeCache = [];
        angular.forEach(thingChannels, function(channelGroup) {
            angular.forEach(channelGroup.channels, function(channel) {
                if (channel.kind !== 'TRIGGER') {
                    channel.acceptedItemTypes = [ channel.itemType ];
                    return true;
                }
                if (channelType2ItemTypeCache[channel.channelTypeUID]) {
                    channel.acceptedItemTypes = channelType2ItemTypeCache[channel.channelTypeUID];
                    return true;
                }

                var acceptedItemTypes = [];
                channelTypeService.getLinkableItemTypes({
                    channelTypeUID : channel.channelTypeUID
                }, function(linkableItemTypes) {
                    if (linkableItemTypes && linkableItemTypes.length > 0) {
                        angular.forEach(linkableItemTypes, function(itemType) {
                            acceptedItemTypes.push(itemType);
                        });
                    } else if (channel.itemType) {
                        acceptedItemTypes.push(channel.itemType);
                    }
                });
                channelType2ItemTypeCache[channel.channelTypeUID] = acceptedItemTypes;
                channel.acceptedItemTypes = acceptedItemTypes;
            });
        });

        return thingChannels;
    };

    $scope.refreshChannels = function(showAdvanced) {
        $scope.thingChannels = getChannels(showAdvanced);
    }

    function checkAdvance(channels) {
        var advancedChannels = channels.filter(function(channel) {
            var channelType = $scope.getChannelTypeByUID(channel.channelTypeUID);
            return channelType && channelType.advanced
        })

        return advancedChannels.length > 0
    }

    $scope.getThing = function(refresh) {
        thingRepository.getOne(function(thing) {
            return thing.UID === thingUID;
        }, function(thing) {
            angular.forEach(thing.channels, function(value, i) {
                value.showItems = $scope.thing ? $scope.thing.channels[i].showItems : false;
                value.items = $scope.thing ? $scope.thing.channels[i].items : null;
            });
            $scope.thing = thing;
            checkThingProperties(thing);
            $scope.thingTypeUID = thing.thingTypeUID;
            getThingType();
            $scope.setSubtitle([ 'Things', thing.label ]);
        }, refresh);
    }

    function checkThingProperties(thing) {
        if (thing.properties) {
            var hasFirmwareVersion = thing.properties['firmwareVersion'];
            if ((Object.keys(thing.properties).length > 0 && !hasFirmwareVersion) || (Object.keys(thing.properties).length > 1 && hasFirmwareVersion)) {
                $scope.thing.hasProperties = true;
            } else {
                $scope.thing.hasProperties = false;
            }
        } else {
            $scope.thing.hasProperties = false;
        }
    }
    $scope.getThing(true);

    function getThingType() {
        thingTypeService.getByUid({
            thingTypeUID : $scope.thingTypeUID
        }, function(thingType) {
            $scope.thingType = thingType;
            if (thingType) {
                $scope.thingTypeChannels = thingType.channels && thingType.channels.length > 0 ? thingType.channels : thingType.channelGroups;
                $scope.setHeaderText(thingType.description);
            }
            $scope.refreshChannels($scope.showAdvanced);
        });
    }

    $scope.configChannel = function(channel, thing, event) {
        configDescriptionService.getByUri({
            uri : 'channel:' + channel.uid
        }, function(configDescription) {
            $mdDialog.show({
                controller : 'ChannelConfigController',
                templateUrl : 'partials/dialog.channelconfig.html',
                targetEvent : event,
                hasBackdrop : true,
                locals : {
                    channelType : configDescription,
                    channel : channel,
                    thing : thing
                }
            });
        });

    };

    $scope.getLinkedItems = function(channel) {
        channel.showItems = !channel.showItems;
        if (channel.showItems && channel.items === null || channel.items === undefined) {
            channel.items = $.grep($scope.items, function(item) {
                return $.grep(channel.linkedItems, function(linkedItemName) {
                    return linkedItemName == item.name;
                }).length > 0;
            });
        }
    }

    $scope.showDescription = function(channel, channelType) {
        var description = channel.description ? channel.description : channel.channelType ? channel.channelType.description : null;
        if (description) {
            popup = $mdDialog.alert({
                title : channel.label ? channel.label : channel.channelType ? channel.channelType.label : channel.id,
                textContent : description,
                ok : 'Close'
            });
            $mdDialog.show(popup);
        }
    }

    $scope.$watch('thing.channels', function() {
        $scope.refreshChannels($scope.showAdvanced);
    });

    $scope.isExtensible = function() {
        var thingType = $scope.thingType
        return thingType && thingType.extensibleChannelTypeIds && thingType.extensibleChannelTypeIds.length > 0
    }

    $scope.isExtensibleChannel = function(channelTypeUID) {
        if (!channelTypeUID || !$scope.isExtensible()) {
            return false
        }

        var channelTypeId = channelTypeUID.split(':')[1]
        return $scope.thingType.extensibleChannelTypeIds.indexOf(channelTypeId) >= 0
    }

    $scope.addChannel = function(event) {
        $mdDialog.show({
            controller : 'AddChannelController',
            templateUrl : 'partials/dialog.addchannel.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                thing : $scope.thing,
                thingType : $scope.thingType,
            }
        });
    }

    $scope.removeChannel = function(channel, event) {
        event.stopImmediatePropagation();
        $mdDialog.show({
            controller : 'RemoveChannelDialogController',
            templateUrl : 'partials/dialog.removechannel.html',
            targetEvent : event,
            hasBackdrop : true,
            locals : {
                thing : $scope.thing,
                channel : channel
            }
        }).then(function() {
            $scope.getThing(false);
        });
    }
}).controller('AddChannelController', function($scope, $mdDialog, toastService, channelTypeRepository, configService, thingService, thingType, thing) {
    $scope.channelTypes = []
    $scope.parameters = undefined

    // values filled form the dialog
    $scope.channelType = undefined
    $scope.channelId = undefined
    $scope.channelLabel = undefined
    $scope.configuration = {}

    var refreshChannelTypes = function() {
        angular.forEach(thingType.extensibleChannelTypeIds, function(channelTypeId) {
            var channelTypeUID = thing.UID.split(":")[0] + ':' + channelTypeId;
            channelTypeRepository.getOne(function filter(element) {
                return element.UID === channelTypeUID
            }, function callback(channelType) {
                $scope.channelTypes.push(channelType)
            })
        })
    }

    $scope.$watch('channelType', function() {
        if ($scope.channelType) {
            $scope.parameters = configService.getRenderingModel($scope.channelType.parameters, $scope.channelType.parameterGroups);
        }
    })

    $scope.close = function() {
        $mdDialog.cancel();
    }

    $scope.save = function() {
        var channel = {
            uid : thing.UID + ':' + $scope.channelId,
            id : $scope.channelId,
            channelTypeUID : $scope.channelType.UID,
            itemType : $scope.channelType.itemType,
            label : $scope.channelLabel,
            configuration : $scope.configuration,
            defaultTags : $scope.channelType.tags
        }

        thing.channels.push(channel);

        thingService.update({
            thingUID : thing.UID
        }, thing, function() {
            $mdDialog.hide();
            toastService.showSuccessToast('Channel added.');
        }, function() {
            $mdDialog.hide();
            toastService.showErrorToast('Error adding channel.');
        });
    }

    refreshChannelTypes();
}).controller('RemoveChannelDialogController', function($scope, $mdDialog, toastService, thingService, thing, channel) {
    $scope.channel = channel;

    $scope.close = function() {
        $mdDialog.cancel();
    }

    $scope.remove = function() {
        var index = thing.channels.indexOf(channel);
        thing.channels.splice(index, 1);
        thingService.update({
            thingUID : thing.UID
        }, thing, function() {
            $mdDialog.hide();
            toastService.showSuccessToast('Channel removed.');
        }, function() {
            $mdDialog.hide();
            toastService.showErrorToast('Error removing channel.');
        });
    }
});
<|endoftext|>


<commit_before>import BaseObject from 'common/object/base';

class Caret extends BaseObject {

  constructor({ editor, marker, notifier }) {
    super({
      editor   : editor,
      marker   : marker,
      notifier : notifier,
      position : 0
    });
  }

  setPosition(position) {
    this.position = Math.max(0, Math.min(this.editor.source.length, position));

    this.notifier.notify({
      type: 'changeCaretPosition'
    });
  }

  moveLeft() {
    this.setPosition(this.position - 1);
  }

  moveRight() {
    console.log('spos');
    this.setPosition(this.position + 1);
  }

  moveUp() {
    this.moveLine(-1);
  }

  moveDown() {
    this.moveLine(1);
  }

  moveToLinePosition(position) {
    var cline = this._getLine();

    var newLinePos = cline.toString().indexOf('\n');
    var eol        = ~newLinePos ? newLinePos : cline.length;

    // TODO - scan position until new line
    this.setPosition(
      cline.getPosition() + Math.max(0, Math.min(eol, position))
    );
  }

  addCharacter(character) {
    this.editor.splice(this.position++, 0, character);
  }

  moveLine(delta) {
    var nline = this._getLine(delta);
    var cline = this._getLine();

    if (nline === cline) {
      // this.setPosition(Infinity * delta);
    } else {
      console.log(this.editor.textRuler)
    }

    // TODO - need to calculate character width here
    this.setPosition(nline === cline ? Infinity * delta : nline.getPosition() + (this.position - cline.getPosition()));
  }

  _getLine(shift = 0) {
    return this.editor.lines[
      Math.max(0, Math.min(this.editor.lines.length - 1, this.getCell().row + shift))
    ];
  }

  removeCharsUntilEndOfLine() {
    var line = this._getLine();

    this.editor.splice(this.position, line.getPosition() + line.length - this.position);
  }

  getLine() {
    return this.editor.lines[this.getCell().row];
  }

  removePreviousCharacter() {
    this.editor.splice(--this.position, 1);
  }

  removeNextCharacter() {
    this.editor.splice(this.position, 1);
  }

  getCell() {
    return this.editor.getCellFromPosition(this.position);
  }

  notify(message) {

    var k = message.keyCode;

    if (message.ctrlKey) {
      if (k === 65) {
        this.moveToLinePosition(0);
      } else if (k === 69) {
        this.moveToLinePosition(Infinity);
      } else if (k === 68) {
        this.removeNextCharacter();
      } else if (k === 75) {
        this.removeCharsUntilEndOfLine();
      }
    }

    if (message.type === 'input') {
      this.addCharacter(message.text);
    } else if (message.type === 'keyCommand') {
      if (message.keyCode === 8) {
        this.removePreviousCharacter();
      } else if (message.keyCode === 39) {
        this.moveRight();
      } else if (message.keyCode === 37) {
        this.moveLeft();
      } else if (message.keyCode === 40) {
        this.moveDown();
      } else if (message.keyCode === 38) {
        this.moveUp();
      }
    }

  }
}

export default Caret;
<commit_msg>end of line<commit_after>import BaseObject from 'common/object/base';

class Caret extends BaseObject {

  constructor({ editor, marker, notifier }) {
    super({
      editor   : editor,
      marker   : marker,
      notifier : notifier,
      position : 0
    });
  }

  setPosition(position) {
    this.position = Math.max(0, Math.min(this.editor.source.length, position));

    this.notifier.notify({
      type: 'changeCaretPosition'
    });
  }

  moveLeft() {
    this.setPosition(this.position - 1);
  }

  moveRight() {
    console.log('spos');
    this.setPosition(this.position + 1);
  }

  moveUp() {
    this.moveLine(-1);
  }

  moveDown() {
    this.moveLine(1);
  }

  moveToLinePosition(position) {
    var cline = this._getLine();

    var newLineToken = cline.tokens.find(function(token) {
      return token.type === 'newLine';
    });

    var eol        = newLineToken ? cline.length - 1 : cline.length;

    // TODO - scan position until new line
    this.setPosition(
      cline.getPosition() + Math.max(0, Math.min(eol, position))
    );
  }

  addCharacter(character) {
    this.editor.splice(this.position++, 0, character);
  }

  moveLine(delta) {
    var nline = this._getLine(delta);
    var cline = this._getLine();

    if (nline === cline) {
      // this.setPosition(Infinity * delta);
    } else {
      console.log(this.editor.textRuler)
    }

    // TODO - need to calculate character width here
    this.setPosition(nline === cline ? Infinity * delta : nline.getPosition() + (this.position - cline.getPosition()));
  }

  _getLine(shift = 0) {
    return this.editor.lines[
      Math.max(0, Math.min(this.editor.lines.length - 1, this.getCell().row + shift))
    ];
  }

  removeCharsUntilEndOfLine() {
    var line = this._getLine();

    this.editor.splice(this.position, line.getPosition() + line.length - this.position);
  }

  getLine() {
    return this.editor.lines[this.getCell().row];
  }

  removePreviousCharacter() {
    this.editor.splice(--this.position, 1);
  }

  removeNextCharacter() {
    this.editor.splice(this.position, 1);
  }

  getCell() {
    return this.editor.getCellFromPosition(this.position);
  }

  notify(message) {

    var k = message.keyCode;

    if (message.ctrlKey) {
      if (k === 65) {
        this.moveToLinePosition(0);
      } else if (k === 69) {
        this.moveToLinePosition(Infinity);
      } else if (k === 68) {
        this.removeNextCharacter();
      } else if (k === 75) {
        this.removeCharsUntilEndOfLine();
      }
    }

    if (message.type === 'input') {
      this.addCharacter(message.text);
    } else if (message.type === 'keyCommand') {
      if (message.keyCode === 8) {
        this.removePreviousCharacter();
      } else if (message.keyCode === 39) {
        this.moveRight();
      } else if (message.keyCode === 37) {
        this.moveLeft();
      } else if (message.keyCode === 40) {
        this.moveDown();
      } else if (message.keyCode === 38) {
        this.moveUp();
      }
    }

  }
}

export default Caret;
<|endoftext|>


<commit_before>const bodyParser = require('body-parser');
const compression = require('compression');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const csrf = require('csurf');
const session = require('express-session');
const passport = require('passport');

const auth = require('./auth');
const debug = require('debug')('cmyk:server');
const server = require('express')();

server.locals.port = process.env.PORT || 3000;
server.locals.domain =
  process.env.NODE_ENV === 'production' ?
    'https://api.cmyk.nyc' : `http://localhost:${server.locals.port}`;

// Redirect for HTTPS
server.set('trust proxy', true);
server.use(function (req, res, next) {
  if (process.env.NODE_ENV !== 'production')
    return next();

  // If https terminated by heroku, or express asserts the conn. is encrypted,
  // or the protocol matches https, then we're good!
  if (req.secure ||
      req.headers['x-forwarded-proto'] === 'https' ||
      req.connection.encrypted ||
      req.protocol === 'https') {
    res.header(
      'Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

    next();
  }
  else
    res.redirect(`https://${req.get('host')}${req.url}`);
});

server.use(cors({origin: true, credentials: true}));
server.use(compression());
server.use(bodyParser.json());
server.use(
  session({
    cookie: auth.COOKIE,
    secret: auth.SECRET,
    saveUninitialized: false
}));
server.use(cookieParser(auth.SECRET, auth.COOKIE));
server.use(csrf({cookie: true}));
server.use(passport.initialize());
server.use(passport.session());

auth.github.connect(server);

server.get('/logout', function logout (request, response) {
  request.session.destroy(function (err) {
    response.redirect(request.get('referrer') || '/');
  });
});

server.get('/profile', auth.requireUser, function (req, res, next) {
  res.json(req.user.profile);
  next();
});

server.get('/', auth.requireUser, function (req, res, next) {
  res.json(req.user);
  next();
});

var connection = server.listen(server.locals.port, () =>
  debug(`Listening at port ${connection.address().port}`));
<commit_msg>Use auth route connector<commit_after>const bodyParser = require('body-parser');
const compression = require('compression');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const csrf = require('csurf');
const session = require('express-session');
const passport = require('passport');

const auth = require('./auth');
const debug = require('debug')('cmyk:server');
const server = require('express')();

server.locals.port = process.env.PORT || 3000;
server.locals.domain =
  process.env.NODE_ENV === 'production' ?
    'https://api.cmyk.nyc' : `http://localhost:${server.locals.port}`;

// Redirect for HTTPS
server.set('trust proxy', true);
server.use(function (req, res, next) {
  if (process.env.NODE_ENV !== 'production')
    return next();

  // If https terminated by heroku, or express asserts the conn. is encrypted,
  // or the protocol matches https, then we're good!
  if (req.secure ||
      req.headers['x-forwarded-proto'] === 'https' ||
      req.connection.encrypted ||
      req.protocol === 'https') {
    res.header(
      'Strict-Transport-Security', 'max-age=31536000; includeSubDomains');

    next();
  }
  else
    res.redirect(`https://${req.get('host')}${req.url}`);
});

server.use(cors({origin: true, credentials: true}));
server.options('*', cors());
server.use(compression());
server.use(bodyParser.json());
server.use(
  session({
    cookie: auth.COOKIE,
    secret: auth.SECRET,
    saveUninitialized: false
}));
server.use(cookieParser(auth.SECRET, auth.COOKIE));
server.use(csrf({cookie: true}));
server.use(passport.initialize());
server.use(passport.session());

auth.connect(server);
auth.github.connect(server);

server.get('/profile', auth.requireUser, function (req, res, next) {
  res.json(req.user.profile);
  next();
});

server.get('/', auth.requireUser, function (req, res, next) {
  res.json(req.user);
  next();
});

var connection = server.listen(server.locals.port, () =>
  debug(`Listening at port ${connection.address().port}`));
<|endoftext|>


<commit_before>// @flow

type ManifestContentMapType = {
  [key: string]: {|
    id: number
  |}
};

type ResourceMapType = {
  [key: string]: number
};

/**
 * Creates an object that maps request to module ID.
 *
 * A 'request' is a require path used in the application
 * resolved to a relative path to the project context, e.g.
 * if `./src/test/index.js` requires `require('./style.css')`
 * the the 'request' is `./src/test.style.css`.
 */
export default (manifestContent: ManifestContentMapType): ResourceMapType => {
  const map = {};

  for (const [path, {id}] of Object.entries(manifestContent)) {
    map[path] = id;
  }

  return map;
};
<commit_msg>fix: use Object.keys<commit_after>// @flow

type ManifestContentMapType = {
  [key: string]: {|
    id: number
  |}
};

type ResourceMapType = {
  [key: string]: number
};

/**
 * Creates an object that maps request to module ID.
 *
 * A 'request' is a require path used in the application
 * resolved to a relative path to the project context, e.g.
 * if `./src/test/index.js` requires `require('./style.css')`
 * the the 'request' is `./src/test.style.css`.
 */
export default (manifestContent: ManifestContentMapType): ResourceMapType => {
  const map = {};

  const paths = Object.keys(manifestContent);

  for (const path of paths) {
    map[path] = manifestContent[path].id;
  }

  return map;
};
<|endoftext|>


<commit_before>var Q = require('q');
var os = require('os');
//var spawn = require('cordova-common').superspawn.spawn;
var child_process = require('child_process');

/*
	adb.on('close', function(code) {
		if (code > 0) {
			d.reject('Failed to run logcat command.');
		} else d.resolve();
	});
	*/
var Adb = {};

function spawn(command, args, options) {
	var d = Q.defer();
	var child = child_process.spawn(command, args, options);
	var capturedOut = '';
	var capturedErr = '';

	if (child.stdout) {
		child.stdout.on('data', function(data) {
			capturedOut += data;
			d.notify({ 'stdout': data });
		});
	}

	if (child.stderr) {
		child.stderr.on('data', function(data) {
			capturedErr += data;
			d.notify({ 'stderr': data });
		});
	}

	child.on('close', whenDone);
	child.on('error', whenDone);

	function whenDone(arg) {
		child.removeListener('close', whenDone);
		child.removeListener('error', whenDone);

		var code = typeof arg == 'number' ? arg : arg && arg.code;

		if (code === 0) {
			d.resolve(capturedOut.trim());
		}
		else {
			var errMsg = command + ': Command failed with exit code ' + code;
			if (capturedErr) {
				errMsg += ' Error output:\n' + capturedErr.trim();
			}
			var err = new Error(errMsg);
			err.code = code;
			d.reject(err);
		}
	}

	return d.promise;
}

function isDevice(line) {
	return line.match(/\w+\tdevice/) && !line.match(/emulator/);
}

function isEmulator(line) {
	return line.match(/device/) && line.match(/emulator/);
}

/**
 * Lists available/connected devices and emulators
 *
 * @param   {Object}   opts			Various options
 * @param   {Boolean}  opts.emulators  Specifies whether this method returns
 *   emulators only
 *
 * @return  {Promise<String[]>}		list of available/connected
 *   devices/emulators
 */
Adb.devices = function(opts) {
	return spawn('adb', ['devices'], { cwd: os.tmpdir() })
		.then(function(output) {
			return output.split('\n').filter(function(line) {
				// Filter out either real devices or emulators, depending on options
				return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
			}).map(function(line) {
				return line.replace(/\tdevice/, '').replace('\r', '');
			});
		});
};

Adb.install = function(target, packagePath, opts) {
	console.log('Installing apk ' + packagePath + ' on target ' + target + '...');

	var args = ['-s', target, 'install'];

	if (opts && opts.replace)
		args.push('-r');

	args.push('-g');

	return spawn('adb', args.concat(packagePath), { cwd: os.tmpdir() })
		.then(function(output) {
			// 'adb install' seems to always returns no error, even if installation fails
			// so we catching output to detect installation failure
			if (output.match(/Failure/)) {
				if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
					output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
						' or sign and deploy the unsigned apk manually using Android tools.';
				}
				else if (output.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
					output += '\n\n' + 'You\'re trying to install apk with a lower versionCode that is already installed.' +
						'\nEither uninstall an app or increment the versionCode.';
				}

				return Q.reject(new Error('Failed to install apk to device: ' + output));
			}
		});
};

Adb.uninstall = function(target, packageId) {
	console.log('Uninstalling package ' + packageId + ' from target ' + target + '...');

	return spawn('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() });
};

Adb.shell = function(target, shellCommand) {
	console.log('Running adb shell command "' + shellCommand + '" on target ' + target + '...');
	var args = ['-s', target, 'shell'];
	shellCommand = shellCommand.split(/\s+/);
	return spawn('adb', args.concat(shellCommand), { cwd: os.tmpdir() })
		.catch(function(output) {
			return Q.reject(new Error('Failed to execute shell command "' +
				shellCommand + '"" on device: ' + output));
		});
};

Adb.start = function(target, activityName) {
	console.log('Starting application "' + activityName + '" on target ' + target + '...');
	return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName)
		.catch(function(output) {
			return Q.reject(new Error('Failed to start application "' +
				activityName + '"" on device: ' + output));
		});
};

module.exports = Adb;
<commit_msg>Fixed the -g option on adb install<commit_after>var Q = require('q');
var os = require('os');
//var spawn = require('cordova-common').superspawn.spawn;
var child_process = require('child_process');

/*
	adb.on('close', function(code) {
		if (code > 0) {
			d.reject('Failed to run logcat command.');
		} else d.resolve();
	});
	*/
var Adb = {};

function spawn(command, args, options) {
	var d = Q.defer();
	var child = child_process.spawn(command, args, options);
	var capturedOut = '';
	var capturedErr = '';

	if (child.stdout) {
		child.stdout.on('data', function(data) {
			capturedOut += data;
			d.notify({ 'stdout': data });
		});
	}

	if (child.stderr) {
		child.stderr.on('data', function(data) {
			capturedErr += data;
			d.notify({ 'stderr': data });
		});
	}

	child.on('close', whenDone);
	child.on('error', whenDone);

	function whenDone(arg) {
		child.removeListener('close', whenDone);
		child.removeListener('error', whenDone);

		var code = typeof arg == 'number' ? arg : arg && arg.code;

		if (code === 0) {
			d.resolve(capturedOut.trim());
		}
		else {
			var errMsg = command + ': Command failed with exit code ' + code;
			if (capturedErr) {
				errMsg += ' Error output:\n' + capturedErr.trim();
			}
			var err = new Error(errMsg);
			err.code = code;
			d.reject(err);
		}
	}

	return d.promise;
}

function isDevice(line) {
	return line.match(/\w+\tdevice/) && !line.match(/emulator/);
}

function isEmulator(line) {
	return line.match(/device/) && line.match(/emulator/);
}

/**
 * Lists available/connected devices and emulators
 *
 * @param   {Object}   opts			Various options
 * @param   {Boolean}  opts.emulators  Specifies whether this method returns
 *   emulators only
 *
 * @return  {Promise<String[]>}		list of available/connected
 *   devices/emulators
 */
Adb.devices = function(opts) {
	return spawn('adb', ['devices'], { cwd: os.tmpdir() })
		.then(function(output) {
			return output.split('\n').filter(function(line) {
				// Filter out either real devices or emulators, depending on options
				return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
			}).map(function(line) {
				return line.replace(/\tdevice/, '').replace('\r', '');
			});
		});
};

Adb.install = function(target, packagePath, opts) {
	console.log('Installing apk ' + packagePath + ' on target ' + target + '...');

	var args = ['-s', target, 'install'];

	if (opts && opts.replace)
		args.push('-r');

	//
	return spawn('adb', ['shell', 'getprop', 'ro.build.version.sdk'])
		.then(function(output) {
			output = Number(output);

			//Beginning in Android 6.0 (API level 23), users grant permissions to apps
			//while the app is running, not when they install the app.
			//The -g arg grants the  permissions automaticaly.
			if (output >= 23)
				args.push('-g');

			//console.log('"adb shell getprop ro.build.version.sdk" returned: ' + output);

			return spawn('adb', args.concat(packagePath), { cwd: os.tmpdir() });
		})
		.then(function(output) {
			// 'adb install' seems to always returns no error, even if installation fails
			// so we catching output to detect installation failure
			if (output.match(/Failure/)) {
				if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
					output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
						' or sign and deploy the unsigned apk manually using Android tools.';
				}
				else if (output.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
					output += '\n\n' + 'You\'re trying to install apk with a lower versionCode that is already installed.' +
						'\nEither uninstall an app or increment the versionCode.';
				}

				return Q.reject(new Error('Failed to install apk to device: ' + output));
			}
		});
};

Adb.uninstall = function(target, packageId) {
	console.log('Uninstalling package ' + packageId + ' from target ' + target + '...');

	return spawn('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() });
};

Adb.shell = function(target, shellCommand) {
	console.log('Running adb shell command "' + shellCommand + '" on target ' + target + '...');
	var args = ['-s', target, 'shell'];
	shellCommand = shellCommand.split(/\s+/);
	return spawn('adb', args.concat(shellCommand), { cwd: os.tmpdir() })
		.catch(function(output) {
			return Q.reject(new Error('Failed to execute shell command "' +
				shellCommand + '"" on device: ' + output));
		});
};

Adb.start = function(target, activityName) {
	console.log('Starting application "' + activityName + '" on target ' + target + '...');
	return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName)
		.catch(function(output) {
			return Q.reject(new Error('Failed to start application "' +
				activityName + '"" on device: ' + output));
		});
};

module.exports = Adb;
<|endoftext|>


<commit_before>describe('Column suggestion service', function () {
    'use strict';

    var firstSelectedColumn = {id: '0001', name: 'col1'};

    beforeEach(module('data-prep.services.transformation'));
    beforeEach(module('pascalprecht.translate', function ($translateProvider) {
        $translateProvider.translations('en', {
            'ACTION_SUGGESTION': 'Suggestion'
        });
        $translateProvider.preferredLanguage('en');
    }));

    beforeEach(inject(function ($q, TransformationCacheService) {
        spyOn(TransformationCacheService, 'getTransformations').and.returnValue($q.when(
            [
                {name: 'rename', category: 'column_metadata', label: 'z', description: 'test'},
                {name: 'cluster', category: 'quickfix', label: 'f', description: 'test'},
                {name: 'split', category: 'column_metadata', label: 'c', description: 'test'},
                {name: 'tolowercase', category: 'case', label: 'v', description: 'test'},
                {name: 'touppercase', category: 'case', label: 'u', description: 'test'},
                {name: 'removeempty', category: 'clear', label: 'a', description: 'test', actionScope: ['empty', 'invalid']},
                {name: 'totitlecase', category: 'case', label: 't', description: 'test', actionScope: ['invalid']},
                {name: 'removetrailingspaces', category: 'quickfix', label: 'm', description: 'test', actionScope: ['empty']},
                {name: 'split', category: 'split', label: 'l', dynamic: true, description: 'test'}
            ]
        ));

        spyOn(TransformationCacheService, 'getSuggestions').and.returnValue($q.when(
            [
                {name: 'tolowercase', category: 'case', label: 'v', description: 'test'},
                {name: 'touppercase', category: 'case', label: 'u', description: 'test'}
            ]
        ));
    }));

    it('should reset the transformations', inject(function (ColumnSuggestionService) {
        //given
        ColumnSuggestionService.allTransformations = [{}];
        ColumnSuggestionService.allSuggestions = [{}];
        ColumnSuggestionService.searchActionString = 'azeaz';
        ColumnSuggestionService.filteredTransformations = [{}];

        //when
        ColumnSuggestionService.reset();

        //then
        expect(ColumnSuggestionService.allTransformations).toEqual([]);
        expect(ColumnSuggestionService.allSuggestions).toEqual([]);
        expect(ColumnSuggestionService.searchActionString).toEqual('');
        expect(ColumnSuggestionService.filteredTransformations).toBeFalsy();
    }));

    it('should group the transformations by category', inject(function ($rootScope, ColumnSuggestionService, TransformationCacheService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        expect(TransformationCacheService.getTransformations).toHaveBeenCalledWith(firstSelectedColumn);
        expect(TransformationCacheService.getSuggestions).toHaveBeenCalledWith(firstSelectedColumn);

        var suggestedTransformations = ColumnSuggestionService.filteredTransformations;
        expect(suggestedTransformations[0].category).toBe('suggestion');
        expect(suggestedTransformations[0].categoryHtml).toBe('SUGGESTION');
        expect(suggestedTransformations[0].transformations.length).toBe(2);
        expect(suggestedTransformations[1].category).toBe('case');
        expect(suggestedTransformations[1].categoryHtml).toBe('CASE');
        expect(suggestedTransformations[1].transformations.length).toBe(3);
        expect(suggestedTransformations[2].category).toBe('clear');
        expect(suggestedTransformations[2].categoryHtml).toBe('CLEAR');
        expect(suggestedTransformations[2].transformations.length).toBe(1);
        expect(suggestedTransformations[3].category).toBe('quickfix');
        expect(suggestedTransformations[3].categoryHtml).toBe('QUICKFIX');
        expect(suggestedTransformations[3].transformations.length).toBe(2);
        expect(suggestedTransformations[4].category).toBe('split');
        expect(suggestedTransformations[4].categoryHtml).toBe('SPLIT');
        expect(suggestedTransformations[4].transformations.length).toBe(1);
    }));

    it('should insert html label (with "..." with parameters) in each transformation/suggestions', inject(function ($rootScope, ColumnSuggestionService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        var suggestedTransformations = ColumnSuggestionService.filteredTransformations;

        expect(suggestedTransformations[0].transformations[0].labelHtml).toBe('v');
        expect(suggestedTransformations[0].transformations[1].labelHtml).toBe('u');
        expect(suggestedTransformations[1].transformations[0].labelHtml).toBe('t');
        expect(suggestedTransformations[1].transformations[1].labelHtml).toBe('u');
        expect(suggestedTransformations[1].transformations[2].labelHtml).toBe('v');
        expect(suggestedTransformations[2].transformations[0].labelHtml).toBe('a');
        expect(suggestedTransformations[3].transformations[0].labelHtml).toBe('f');
        expect(suggestedTransformations[3].transformations[1].labelHtml).toBe('m');
        expect(suggestedTransformations[4].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter "column metadata" category', inject(function ($rootScope, ColumnSuggestionService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        ColumnSuggestionService.filteredTransformations.forEach(function(categoryTransformations) {
            expect(categoryTransformations.category).not.toBe('column_metadata');
        });
    }));

    it('should not filter transformations when searchActionString is empty', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.searchActionString = '';

        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();
        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;

        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('v');
        expect(filteredTransformations[0].transformations[1].labelHtml).toBe('u');
        expect(filteredTransformations[1].transformations[0].labelHtml).toBe('t');
        expect(filteredTransformations[1].transformations[1].labelHtml).toBe('u');
        expect(filteredTransformations[1].transformations[2].labelHtml).toBe('v');
        expect(filteredTransformations[2].transformations[0].labelHtml).toBe('a');
        expect(filteredTransformations[3].transformations[0].labelHtml).toBe('f');
        expect(filteredTransformations[3].transformations[1].labelHtml).toBe('m');
        expect(filteredTransformations[4].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter transformations when searchActionString is not empty', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = 'l';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations[0].category).toBe('clear');
        expect(filteredTransformations[0].transformations.length).toBe(1);
        expect(filteredTransformations[1].category).toBe('split');
        expect(filteredTransformations[1].transformations.length).toBe(1);
    }));

    it('should highlight categories and transformations labels when searchActionString is not empty', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = 'l';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations.length).toBe(2);
        expect(filteredTransformations[0].categoryHtml).toBe('CLEAR');
        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('a');
        expect(filteredTransformations[1].categoryHtml).toBe('SPLIT');
        expect(filteredTransformations[1].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter transformations with case insensitive', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = 'L';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations.length).toBe(2);
        expect(filteredTransformations[0].categoryHtml).toBe('CLEAR');
        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('a');
        expect(filteredTransformations[1].categoryHtml).toBe('SPLIT');
        expect(filteredTransformations[1].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter transformations by escaping regex', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = '...';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations.length).toBe(1);
        expect(filteredTransformations[0].categoryHtml).toBe('SPLIT');
        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('l...');
    }));

    it('should initialize transformationsForEmptyCells', inject(function ($rootScope, ColumnSuggestionService, TransformationCacheService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        expect(TransformationCacheService.getTransformations).toHaveBeenCalledWith(firstSelectedColumn);

        var transformationsForEmptyCells = ColumnSuggestionService.transformationsForEmptyCells;
        expect(transformationsForEmptyCells.length).toBe(2);
        expect(transformationsForEmptyCells[0].label).toBe('a');
        expect(transformationsForEmptyCells[1].label).toBe('m');
    }));

    it('should initialize transformationsForInvalidCells', inject(function ($rootScope, ColumnSuggestionService, TransformationCacheService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        expect(TransformationCacheService.getTransformations).toHaveBeenCalledWith(firstSelectedColumn);

        var transformationsForInvalidCells = ColumnSuggestionService.transformationsForInvalidCells;
        expect(transformationsForInvalidCells.length).toBe(2);
        expect(transformationsForInvalidCells[0].label).toBe('a');
        expect(transformationsForInvalidCells[1].label).toBe('t');
    }));
});<commit_msg>TDP-767: improve  unit tests<commit_after>describe('Column suggestion service', function () {
    'use strict';

    var firstSelectedColumn = {id: '0001', name: 'col1'};

    beforeEach(module('data-prep.services.transformation'));
    beforeEach(module('pascalprecht.translate', function ($translateProvider) {
        $translateProvider.translations('en', {
            'ACTION_SUGGESTION': 'Suggestion'
        });
        $translateProvider.preferredLanguage('en');
    }));

    beforeEach(inject(function ($q, TransformationCacheService) {
        spyOn(TransformationCacheService, 'getTransformations').and.returnValue($q.when(
            [
                {name: 'rename', category: 'column_metadata', label: 'z', description: 'test'},
                {name: 'cluster', category: 'quickfix', label: 'f', description: 'test'},
                {name: 'split', category: 'column_metadata', label: 'c', description: 'test'},
                {name: 'tolowercase', category: 'case', label: 'v', description: 'test'},
                {name: 'touppercase', category: 'case', label: 'u', description: 'test',  actionScope: ['unknown']},
                {name: 'removeempty', category: 'clear', label: 'a', description: 'test', actionScope: ['empty', 'invalid']},
                {name: 'totitlecase', category: 'case', label: 't', description: 'test', actionScope: ['invalid']},
                {name: 'removetrailingspaces', category: 'quickfix', label: 'm', description: 'test', actionScope: ['empty', 'unknown']},
                {name: 'split', category: 'split', label: 'l', dynamic: true, description: 'test'}
            ]
        ));

        spyOn(TransformationCacheService, 'getSuggestions').and.returnValue($q.when(
            [
                {name: 'tolowercase', category: 'case', label: 'v', description: 'test'},
                {name: 'touppercase', category: 'case', label: 'u', description: 'test'}
            ]
        ));
    }));

    it('should reset the transformations', inject(function (ColumnSuggestionService) {
        //given
        ColumnSuggestionService.allTransformations = [{}];
        ColumnSuggestionService.allSuggestions = [{}];
        ColumnSuggestionService.searchActionString = 'azeaz';
        ColumnSuggestionService.filteredTransformations = [{}];

        //when
        ColumnSuggestionService.reset();

        //then
        expect(ColumnSuggestionService.allTransformations).toEqual([]);
        expect(ColumnSuggestionService.allSuggestions).toEqual([]);
        expect(ColumnSuggestionService.searchActionString).toEqual('');
        expect(ColumnSuggestionService.filteredTransformations).toBeFalsy();
    }));

    it('should group the transformations by category', inject(function ($rootScope, ColumnSuggestionService, TransformationCacheService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        expect(TransformationCacheService.getTransformations).toHaveBeenCalledWith(firstSelectedColumn);
        expect(TransformationCacheService.getSuggestions).toHaveBeenCalledWith(firstSelectedColumn);

        var suggestedTransformations = ColumnSuggestionService.filteredTransformations;
        expect(suggestedTransformations[0].category).toBe('suggestion');
        expect(suggestedTransformations[0].categoryHtml).toBe('SUGGESTION');
        expect(suggestedTransformations[0].transformations.length).toBe(2);
        expect(suggestedTransformations[1].category).toBe('case');
        expect(suggestedTransformations[1].categoryHtml).toBe('CASE');
        expect(suggestedTransformations[1].transformations.length).toBe(3);
        expect(suggestedTransformations[2].category).toBe('clear');
        expect(suggestedTransformations[2].categoryHtml).toBe('CLEAR');
        expect(suggestedTransformations[2].transformations.length).toBe(1);
        expect(suggestedTransformations[3].category).toBe('quickfix');
        expect(suggestedTransformations[3].categoryHtml).toBe('QUICKFIX');
        expect(suggestedTransformations[3].transformations.length).toBe(2);
        expect(suggestedTransformations[4].category).toBe('split');
        expect(suggestedTransformations[4].categoryHtml).toBe('SPLIT');
        expect(suggestedTransformations[4].transformations.length).toBe(1);
    }));

    it('should insert html label (with "..." with parameters) in each transformation/suggestions', inject(function ($rootScope, ColumnSuggestionService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        var suggestedTransformations = ColumnSuggestionService.filteredTransformations;

        expect(suggestedTransformations[0].transformations[0].labelHtml).toBe('v');
        expect(suggestedTransformations[0].transformations[1].labelHtml).toBe('u');
        expect(suggestedTransformations[1].transformations[0].labelHtml).toBe('t');
        expect(suggestedTransformations[1].transformations[1].labelHtml).toBe('u');
        expect(suggestedTransformations[1].transformations[2].labelHtml).toBe('v');
        expect(suggestedTransformations[2].transformations[0].labelHtml).toBe('a');
        expect(suggestedTransformations[3].transformations[0].labelHtml).toBe('f');
        expect(suggestedTransformations[3].transformations[1].labelHtml).toBe('m');
        expect(suggestedTransformations[4].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter "column metadata" category', inject(function ($rootScope, ColumnSuggestionService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        ColumnSuggestionService.filteredTransformations.forEach(function(categoryTransformations) {
            expect(categoryTransformations.category).not.toBe('column_metadata');
        });
    }));

    it('should not filter transformations when searchActionString is empty', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.searchActionString = '';

        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();
        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;

        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('v');
        expect(filteredTransformations[0].transformations[1].labelHtml).toBe('u');
        expect(filteredTransformations[1].transformations[0].labelHtml).toBe('t');
        expect(filteredTransformations[1].transformations[1].labelHtml).toBe('u');
        expect(filteredTransformations[1].transformations[2].labelHtml).toBe('v');
        expect(filteredTransformations[2].transformations[0].labelHtml).toBe('a');
        expect(filteredTransformations[3].transformations[0].labelHtml).toBe('f');
        expect(filteredTransformations[3].transformations[1].labelHtml).toBe('m');
        expect(filteredTransformations[4].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter transformations when searchActionString is not empty', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = 'l';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations[0].category).toBe('clear');
        expect(filteredTransformations[0].transformations.length).toBe(1);
        expect(filteredTransformations[1].category).toBe('split');
        expect(filteredTransformations[1].transformations.length).toBe(1);
    }));

    it('should highlight categories and transformations labels when searchActionString is not empty', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = 'l';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations.length).toBe(2);
        expect(filteredTransformations[0].categoryHtml).toBe('CLEAR');
        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('a');
        expect(filteredTransformations[1].categoryHtml).toBe('SPLIT');
        expect(filteredTransformations[1].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter transformations with case insensitive', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = 'L';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations.length).toBe(2);
        expect(filteredTransformations[0].categoryHtml).toBe('CLEAR');
        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('a');
        expect(filteredTransformations[1].categoryHtml).toBe('SPLIT');
        expect(filteredTransformations[1].transformations[0].labelHtml).toBe('l...');
    }));

    it('should filter transformations by escaping regex', inject(function ($rootScope, ColumnSuggestionService) {
        //given
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //when
        ColumnSuggestionService.searchActionString = '...';

        ColumnSuggestionService.filterTransformations();
        $rootScope.$digest();

        //then
        var filteredTransformations = ColumnSuggestionService.filteredTransformations;
        expect(filteredTransformations.length).toBe(1);
        expect(filteredTransformations[0].categoryHtml).toBe('SPLIT');
        expect(filteredTransformations[0].transformations[0].labelHtml).toBe('l...');
    }));

    it('should initialize transformationsForEmptyCells', inject(function ($rootScope, ColumnSuggestionService, TransformationCacheService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        expect(TransformationCacheService.getTransformations).toHaveBeenCalledWith(firstSelectedColumn);

        var transformationsForEmptyCells = ColumnSuggestionService.transformationsForEmptyCells;
        expect(transformationsForEmptyCells.length).toBe(2);
        expect(transformationsForEmptyCells[0].label).toBe('a');
        expect(transformationsForEmptyCells[1].label).toBe('m');
    }));

    it('should initialize transformationsForInvalidCells', inject(function ($rootScope, ColumnSuggestionService, TransformationCacheService) {
        //when
        ColumnSuggestionService.initTransformations(firstSelectedColumn);
        $rootScope.$digest();

        //then
        expect(TransformationCacheService.getTransformations).toHaveBeenCalledWith(firstSelectedColumn);

        var transformationsForInvalidCells = ColumnSuggestionService.transformationsForInvalidCells;
        expect(transformationsForInvalidCells.length).toBe(2);
        expect(transformationsForInvalidCells[0].label).toBe('a');
        expect(transformationsForInvalidCells[1].label).toBe('t');
    }));
});<|endoftext|>