<commit_before>angular.module("umbraco").controller("Our.Umbraco.DocTypeGridEditor.GridEditors.DocTypeGridEditor", [

    "$scope",
    "$rootScope",
    "$timeout",
    "editorState",
    'assetsService',
    "Our.Umbraco.DocTypeGridEditor.Resources.DocTypeGridEditorResources",
    "umbRequestHelper",
    "localizationService",
    "editorService",

    function ($scope, $rootScope, $timeout, editorState, assetsService, dtgeResources, umbRequestHelper, localizationService, editorService) {

        var overlayOptions = {
            view: umbRequestHelper.convertVirtualToAbsolutePath(
                "~/App_Plugins/DocTypeGridEditor/Views/doctypegrideditor.dialog.html"),
            model: {},
            titles: {
                insertItem: "Click to insert item",
                editItem: "Edit item",
                selectContentType: "Choose a Content Type",
                selectBlueprint: "Choose a Content Template"
            },
            title: "Edit item",
            submitButtonlabelKey: "bulk_done"
        };

        $scope.icon = "icon-item-arrangement";

        // init cached content types if it doesnt exist.
        if (!$rootScope.dtgeContentTypes) $rootScope.dtgeContentTypes = {};

        // localize strings
        localizationService.localizeMany(["docTypeGridEditor_insertItem", "docTypeGridEditor_editItem", "docTypeGridEditor_selectContentType", "blueprints_selectBlueprint"]).then(function (data) {
            overlayOptions.titles.insertItem = data[0];
            overlayOptions.titles.editItem = data[1];
            overlayOptions.titles.selectContentType = data[2];
            overlayOptions.titles.selectBlueprint = data[3];
        });

        $scope.setValue = function (data, callback) {
            $scope.title = $scope.control.editor.name;
            $scope.icon = $scope.control.editor.icon;
            $scope.control.value = data;

            if (!("id" in $scope.control.value) || $scope.control.value.id == "") {
                $scope.control.value.id = guid();
            }
            if ("name" in $scope.control.value.value && $scope.control.value.value.name) {
                $scope.title = $scope.control.value.value.name;
            }
            if ("dtgeContentTypeAlias" in $scope.control.value && $scope.control.value.dtgeContentTypeAlias) {
                if (!$rootScope.dtgeContentTypes[$scope.control.value.dtgeContentTypeAlias]) {

                    dtgeResources.getContentType($scope.control.value.dtgeContentTypeAlias).then(function (data2) {
                        var contentType = {
                            title: data2.title,
                            description: data2.description,
                            icon: data2.icon
                        };

                        // save to cached content types
                        $rootScope.dtgeContentTypes[$scope.control.value.dtgeContentTypeAlias] = contentType;
                        $scope.setTitleAndDescription(contentType);
                    });
                }
                else {
                    $scope.setTitleAndDescription($rootScope.dtgeContentTypes[$scope.control.value.dtgeContentTypeAlias]);
                }
            }
            if (callback)
                callback();
        };

        $scope.setTitleAndDescription = function (contentType) {
            $scope.title = contentType.title;
            $scope.description = contentType.description;
            $scope.icon = contentType.icon;
        };

        $scope.setDocType = function () {

            overlayOptions.editorName = $scope.control.editor.name;
            overlayOptions.allowedDocTypes = $scope.control.editor.config.allowedDocTypes || [];
            overlayOptions.showDocTypeSelectAsGrid = $scope.control.editor.config.showDocTypeSelectAsGrid === true;
            overlayOptions.nameTemplate = $scope.control.editor.config.nameTemplate;
            overlayOptions.size = $scope.control.editor.config.largeDialog ? null : $scope.control.editor.config.overlaySize || "small";

            overlayOptions.dialogData = {
                docTypeAlias: $scope.control.value.dtgeContentTypeAlias,
                value: $scope.control.value.value,
                id: $scope.control.value.id
            };
            overlayOptions.close = function () {
                // ensure an empty doctype is not persisted
                if($scope.control.$initializing){
                    $scope.removeControl($scope.area, $scope.control.$index -1);
                }

                editorService.close();
            }
            overlayOptions.submit = function (newModel) {

                        // Copy property values to scope model value
                        if (newModel.node) {
                            var value = {
                                name: newModel.editorName
                            };

                            for (var v = 0; v < newModel.node.variants.length; v++) {
                                var variant = newModel.node.variants[v];
                                for (var t = 0; t < variant.tabs.length; t++) {
                                    var tab = variant.tabs[t];
                                    for (var p = 0; p < tab.properties.length; p++) {
                                        var prop = tab.properties[p];
                                        if (typeof prop.value !== "function") {
                                            value[prop.alias] = prop.value;
                                        }
                                    }
                                }
                            }

                            if (newModel.nameExp) {
                                var newName = newModel.nameExp(value); // Run it against the stored dictionary value, NOT the node object
                                if (newName && (newName = $.trim(newName))) {
                                    value.name = newName;
                                }
                            }

                            newModel.dialogData.value = value;
                        } else {
                            newModel.dialogData.value = null;

                        }

                        $scope.setValue({
                            dtgeContentTypeAlias: newModel.dialogData.docTypeAlias,
                            value: newModel.dialogData.value,
                            id: newModel.dialogData.id
                        });
                        $scope.setPreview($scope.control.value);
                        editorService.close();
            };

            editorService.open(overlayOptions);
        };

        $scope.setPreview = function (model) {
            if ($scope.control.editor.config && "enablePreview" in $scope.control.editor.config && $scope.control.editor.config.enablePreview) {
                dtgeResources.getEditorMarkupForDocTypePartial(editorState.current.id, model.id,
                    $scope.control.editor.alias, model.dtgeContentTypeAlias, model.value,
                    $scope.control.editor.config.viewPath,
                    $scope.control.editor.config.previewViewPath,
                    !!editorState.current.publishDate)
                    .then(function (response) {
                        var htmlResult = response.data;
                        if (htmlResult.trim().length > 0) {
                            $scope.preview = htmlResult;
                        }
                    });
            }
        };

        function init() {
            $timeout(function () {
                if ($scope.control.$initializing) {
                    $scope.setDocType();
                } else if ($scope.control.value) {
                    $scope.setPreview($scope.control.value);
                }
            }, 200);
        }

        if ($scope.control.value) {
            if (!$scope.control.value.dtgeContentTypeAlias && $scope.control.value.docType) {
                $scope.control.value.dtgeContentTypeAlias = $scope.control.value.docType;
            }
            if ($scope.control.value.docType) {
                delete $scope.control.value.docType;
            }
            if (isGuid($scope.control.value.dtgeContentTypeAlias)) {
                dtgeResources.getContentTypeAliasByGuid($scope.control.value.dtgeContentTypeAlias).then(function (data1) {
                    $scope.control.value.dtgeContentTypeAlias = data1.alias;
                    $scope.setValue($scope.control.value, init);
                });
            } else {
                $scope.setValue($scope.control.value, init);
            }
        } else {
            $scope.setValue({
                id: guid(),
                dtgeContentTypeAlias: "",
                value: {}
            }, init);
        }

        // Load preview css / js files
        if ($scope.control.editor.config && "enablePreview" in $scope.control.editor.config && $scope.control.editor.config.enablePreview) {
            if ("previewCssFilePath" in $scope.control.editor.config && $scope.control.editor.config.previewCssFilePath) {
                assetsService.loadCss($scope.control.editor.config.previewCssFilePath, $scope);
            };

            if ("previewJsFilePath" in $scope.control.editor.config && $scope.control.editor.config.previewJsFilePath) {
                assetsService.loadJs($scope.control.editor.config.previewJsFilePath, $scope);
            }
        }

        function guid() {
            function s4() {
                return Math.floor((1 + Math.random()) * 0x10000)
                    .toString(16)
                    .substring(1);
            }
            return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
                s4() + '-' + s4() + s4() + s4();
        }

        function isGuid(input) {
            return new RegExp("^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$", "i").test(input.toString());
        }

    }
]);

angular.module("umbraco").controller("Our.Umbraco.DocTypeGridEditor.Dialogs.DocTypeGridEditorDialog",
    [
        "$scope",
        "$interpolate",
        "formHelper",
        "contentResource",
        "Our.Umbraco.DocTypeGridEditor.Resources.DocTypeGridEditorResources",
        "Our.Umbraco.DocTypeGridEditor.Services.DocTypeGridEditorUtilityService",
        "blueprintConfig",
        "contentEditingHelper",
        "serverValidationManager",

        function ($scope, $interpolate, formHelper, contentResource, dtgeResources, dtgeUtilityService, blueprintConfig, contentEditingHelper, serverValidationManager) {

            var vm = this;
            vm.submit = submit;
            vm.close = close;
            vm.loading = true;
            vm.blueprintConfig = blueprintConfig;

            function cleanup() {
                if ($scope.model.node.id > 0){
                    // delete any temporary blueprints used for validation
                    contentResource.deleteBlueprint($scope.model.node.id);
                }

                //clear server validation messages when this editor is destroyed
                serverValidationManager.clear();
            }

            $scope.$on('$destroy', cleanup);

            function submit() {
                if ($scope.model.submit) {
                    $scope.model.node.name = "Dtge Temp: " + $scope.model.node.key;
                    $scope.model.node.variants[0].name = $scope.model.node.name
                    $scope.model.node.variants[0].save = true;

                    // save the content as a blueprint, to trigger validation
                    var args = {
                        saveMethod: contentResource.saveBlueprint,
                        scope: $scope,
                        content: $scope.model.node,
                        create: true,
                        action: "save",
                        showNotifications: true,
                        softRedirect: true
                    }

                    contentEditingHelper.contentEditorPerformSave(args).then(function (data) {
                            $scope.model.submit($scope.model);
                        },
                        function (err) {

                        });
                }
            }
            function close() {
                if ($scope.model.close) {
                    $scope.model.close();
                }
            }

            $scope.docTypes = [];
            $scope.dialogMode = null;
            $scope.selectedDocType = null;
            $scope.model.node = null;

            var nameExp = !!$scope.model.nameTemplate
                ? $interpolate($scope.model.nameTemplate)
                : undefined;

            $scope.model.nameExp = nameExp;

            function createBlank() {
                $scope.dialogMode = "edit";
                loadNode();
            };

            function createOrSelectBlueprintIfAny(docType) {

                $scope.model.dialogData.docTypeAlias = docType.alias;
                var blueprintIds = _.keys(docType.blueprints || {});
                $scope.selectedDocType = docType;

                if (blueprintIds.length) {
                    if (blueprintConfig.skipSelect) {
                        createFromBlueprint(blueprintIds[0]);
                    } else {
                        $scope.dialogMode = "selectBlueprint";
                    }
                } else {
                    createBlank();
                }
            };

            function createFromBlueprint(blueprintId) {
                contentResource.getBlueprintScaffold(-1, blueprintId).then(function (data) {
                    // Assign the model to scope
                    $scope.nodeContext = $scope.model.node = data;
                    $scope.dialogMode = "edit";
                    vm.content = $scope.nodeContext.variants[0];
                    vm.loading = false;
                });
            };

            $scope.createBlank = createBlank;
            $scope.createOrSelectBlueprintIfAny = createOrSelectBlueprintIfAny;
            $scope.createFromBlueprint = createFromBlueprint;

            function loadNode() {
                vm.loading = true;
                contentResource.getScaffold(-1, $scope.model.dialogData.docTypeAlias).then(function (data) {

                    // Merge current value
                    if ($scope.model.dialogData.value) {
                        for (var v = 0; v < data.variants.length; v++) {
                            var variant = data.variants[v];
                            for (var t = 0; t < variant.tabs.length; t++) {
                                var tab = variant.tabs[t];
                                for (var p = 0; p < tab.properties.length; p++) {
                                    var prop = tab.properties[p];
                                    if ($scope.model.dialogData.value[prop.alias]) {
                                        prop.value = $scope.model.dialogData.value[prop.alias];
                                    }
                                }
                            }
                        }
                    };

                    // Assign the model to scope
                    $scope.nodeContext = $scope.model.node = data;
                    vm.content = $scope.nodeContext.variants[0];
                    vm.loading = false;
                });
            }

            if ($scope.model.dialogData.docTypeAlias) {
                $scope.dialogMode = "edit";
                loadNode();
            } else {
                $scope.dialogMode = "selectDocType";
                // No data type, so load a list to choose from
                dtgeResources.getContentTypes($scope.model.allowedDocTypes).then(function (docTypes) {
                    $scope.docTypes = docTypes;
                    if ($scope.docTypes.length == 1) {
                        createOrSelectBlueprintIfAny($scope.docTypes[0]);
                    }
                    else {
                        vm.loading = false;
                    }
                });
            }

        }

    ]);
<commit_msg>Cleanup threw a js error when closing the dialog before selecting doctype<commit_after>angular.module("umbraco").controller("Our.Umbraco.DocTypeGridEditor.GridEditors.DocTypeGridEditor", [

    "$scope",
    "$rootScope",
    "$timeout",
    "editorState",
    'assetsService',
    "Our.Umbraco.DocTypeGridEditor.Resources.DocTypeGridEditorResources",
    "umbRequestHelper",
    "localizationService",
    "editorService",

    function ($scope, $rootScope, $timeout, editorState, assetsService, dtgeResources, umbRequestHelper, localizationService, editorService) {

        var overlayOptions = {
            view: umbRequestHelper.convertVirtualToAbsolutePath(
                "~/App_Plugins/DocTypeGridEditor/Views/doctypegrideditor.dialog.html"),
            model: {},
            titles: {
                insertItem: "Click to insert item",
                editItem: "Edit item",
                selectContentType: "Choose a Content Type",
                selectBlueprint: "Choose a Content Template"
            },
            title: "Edit item",
            submitButtonlabelKey: "bulk_done"
        };

        $scope.icon = "icon-item-arrangement";

        // init cached content types if it doesnt exist.
        if (!$rootScope.dtgeContentTypes) $rootScope.dtgeContentTypes = {};

        // localize strings
        localizationService.localizeMany(["docTypeGridEditor_insertItem", "docTypeGridEditor_editItem", "docTypeGridEditor_selectContentType", "blueprints_selectBlueprint"]).then(function (data) {
            overlayOptions.titles.insertItem = data[0];
            overlayOptions.titles.editItem = data[1];
            overlayOptions.titles.selectContentType = data[2];
            overlayOptions.titles.selectBlueprint = data[3];
        });

        $scope.setValue = function (data, callback) {
            $scope.title = $scope.control.editor.name;
            $scope.icon = $scope.control.editor.icon;
            $scope.control.value = data;

            if (!("id" in $scope.control.value) || $scope.control.value.id == "") {
                $scope.control.value.id = guid();
            }
            if ("name" in $scope.control.value.value && $scope.control.value.value.name) {
                $scope.title = $scope.control.value.value.name;
            }
            if ("dtgeContentTypeAlias" in $scope.control.value && $scope.control.value.dtgeContentTypeAlias) {
                if (!$rootScope.dtgeContentTypes[$scope.control.value.dtgeContentTypeAlias]) {

                    dtgeResources.getContentType($scope.control.value.dtgeContentTypeAlias).then(function (data2) {
                        var contentType = {
                            title: data2.title,
                            description: data2.description,
                            icon: data2.icon
                        };

                        // save to cached content types
                        $rootScope.dtgeContentTypes[$scope.control.value.dtgeContentTypeAlias] = contentType;
                        $scope.setTitleAndDescription(contentType);
                    });
                }
                else {
                    $scope.setTitleAndDescription($rootScope.dtgeContentTypes[$scope.control.value.dtgeContentTypeAlias]);
                }
            }
            if (callback)
                callback();
        };

        $scope.setTitleAndDescription = function (contentType) {
            $scope.title = contentType.title;
            $scope.description = contentType.description;
            $scope.icon = contentType.icon;
        };

        $scope.setDocType = function () {

            overlayOptions.editorName = $scope.control.editor.name;
            overlayOptions.allowedDocTypes = $scope.control.editor.config.allowedDocTypes || [];
            overlayOptions.showDocTypeSelectAsGrid = $scope.control.editor.config.showDocTypeSelectAsGrid === true;
            overlayOptions.nameTemplate = $scope.control.editor.config.nameTemplate;
            overlayOptions.size = $scope.control.editor.config.largeDialog ? null : $scope.control.editor.config.overlaySize || "small";

            overlayOptions.dialogData = {
                docTypeAlias: $scope.control.value.dtgeContentTypeAlias,
                value: $scope.control.value.value,
                id: $scope.control.value.id
            };
            overlayOptions.close = function () {
                // ensure an empty doctype is not persisted
                if($scope.control.$initializing){
                    $scope.removeControl($scope.area, $scope.control.$index -1);
                }

                editorService.close();
            }
            overlayOptions.submit = function (newModel) {

                        // Copy property values to scope model value
                        if (newModel.node) {
                            var value = {
                                name: newModel.editorName
                            };

                            for (var v = 0; v < newModel.node.variants.length; v++) {
                                var variant = newModel.node.variants[v];
                                for (var t = 0; t < variant.tabs.length; t++) {
                                    var tab = variant.tabs[t];
                                    for (var p = 0; p < tab.properties.length; p++) {
                                        var prop = tab.properties[p];
                                        if (typeof prop.value !== "function") {
                                            value[prop.alias] = prop.value;
                                        }
                                    }
                                }
                            }

                            if (newModel.nameExp) {
                                var newName = newModel.nameExp(value); // Run it against the stored dictionary value, NOT the node object
                                if (newName && (newName = $.trim(newName))) {
                                    value.name = newName;
                                }
                            }

                            newModel.dialogData.value = value;
                        } else {
                            newModel.dialogData.value = null;

                        }

                        $scope.setValue({
                            dtgeContentTypeAlias: newModel.dialogData.docTypeAlias,
                            value: newModel.dialogData.value,
                            id: newModel.dialogData.id
                        });
                        $scope.setPreview($scope.control.value);
                        editorService.close();
            };

            editorService.open(overlayOptions);
        };

        $scope.setPreview = function (model) {
            if ($scope.control.editor.config && "enablePreview" in $scope.control.editor.config && $scope.control.editor.config.enablePreview) {
                dtgeResources.getEditorMarkupForDocTypePartial(editorState.current.id, model.id,
                    $scope.control.editor.alias, model.dtgeContentTypeAlias, model.value,
                    $scope.control.editor.config.viewPath,
                    $scope.control.editor.config.previewViewPath,
                    !!editorState.current.publishDate)
                    .then(function (response) {
                        var htmlResult = response.data;
                        if (htmlResult.trim().length > 0) {
                            $scope.preview = htmlResult;
                        }
                    });
            }
        };

        function init() {
            $timeout(function () {
                if ($scope.control.$initializing) {
                    $scope.setDocType();
                } else if ($scope.control.value) {
                    $scope.setPreview($scope.control.value);
                }
            }, 200);
        }

        if ($scope.control.value) {
            if (!$scope.control.value.dtgeContentTypeAlias && $scope.control.value.docType) {
                $scope.control.value.dtgeContentTypeAlias = $scope.control.value.docType;
            }
            if ($scope.control.value.docType) {
                delete $scope.control.value.docType;
            }
            if (isGuid($scope.control.value.dtgeContentTypeAlias)) {
                dtgeResources.getContentTypeAliasByGuid($scope.control.value.dtgeContentTypeAlias).then(function (data1) {
                    $scope.control.value.dtgeContentTypeAlias = data1.alias;
                    $scope.setValue($scope.control.value, init);
                });
            } else {
                $scope.setValue($scope.control.value, init);
            }
        } else {
            $scope.setValue({
                id: guid(),
                dtgeContentTypeAlias: "",
                value: {}
            }, init);
        }

        // Load preview css / js files
        if ($scope.control.editor.config && "enablePreview" in $scope.control.editor.config && $scope.control.editor.config.enablePreview) {
            if ("previewCssFilePath" in $scope.control.editor.config && $scope.control.editor.config.previewCssFilePath) {
                assetsService.loadCss($scope.control.editor.config.previewCssFilePath, $scope);
            };

            if ("previewJsFilePath" in $scope.control.editor.config && $scope.control.editor.config.previewJsFilePath) {
                assetsService.loadJs($scope.control.editor.config.previewJsFilePath, $scope);
            }
        }

        function guid() {
            function s4() {
                return Math.floor((1 + Math.random()) * 0x10000)
                    .toString(16)
                    .substring(1);
            }
            return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
                s4() + '-' + s4() + s4() + s4();
        }

        function isGuid(input) {
            return new RegExp("^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$", "i").test(input.toString());
        }

    }
]);

angular.module("umbraco").controller("Our.Umbraco.DocTypeGridEditor.Dialogs.DocTypeGridEditorDialog",
    [
        "$scope",
        "$interpolate",
        "formHelper",
        "contentResource",
        "Our.Umbraco.DocTypeGridEditor.Resources.DocTypeGridEditorResources",
        "Our.Umbraco.DocTypeGridEditor.Services.DocTypeGridEditorUtilityService",
        "blueprintConfig",
        "contentEditingHelper",
        "serverValidationManager",

        function ($scope, $interpolate, formHelper, contentResource, dtgeResources, dtgeUtilityService, blueprintConfig, contentEditingHelper, serverValidationManager) {

            var vm = this;
            vm.submit = submit;
            vm.close = close;
            vm.loading = true;
            vm.blueprintConfig = blueprintConfig;

            function cleanup() {
                if ($scope.model.node && $scope.model.node.id > 0){
                    // delete any temporary blueprints used for validation
                    contentResource.deleteBlueprint($scope.model.node.id);
                }

                //clear server validation messages when this editor is destroyed
                serverValidationManager.clear();
            }

            $scope.$on('$destroy', cleanup);

            function submit() {
                if ($scope.model.submit) {
                    $scope.model.node.name = "Dtge Temp: " + $scope.model.node.key;
                    $scope.model.node.variants[0].name = $scope.model.node.name
                    $scope.model.node.variants[0].save = true;

                    // save the content as a blueprint, to trigger validation
                    var args = {
                        saveMethod: contentResource.saveBlueprint,
                        scope: $scope,
                        content: $scope.model.node,
                        create: true,
                        action: "save",
                        showNotifications: true,
                        softRedirect: true
                    }

                    contentEditingHelper.contentEditorPerformSave(args).then(function (data) {
                            $scope.model.submit($scope.model);
                        },
                        function (err) {

                        });
                }
            }
            function close() {
                if ($scope.model.close) {
                    $scope.model.close();
                }
            }

            $scope.docTypes = [];
            $scope.dialogMode = null;
            $scope.selectedDocType = null;
            $scope.model.node = null;

            var nameExp = !!$scope.model.nameTemplate
                ? $interpolate($scope.model.nameTemplate)
                : undefined;

            $scope.model.nameExp = nameExp;

            function createBlank() {
                $scope.dialogMode = "edit";
                loadNode();
            };

            function createOrSelectBlueprintIfAny(docType) {

                $scope.model.dialogData.docTypeAlias = docType.alias;
                var blueprintIds = _.keys(docType.blueprints || {});
                $scope.selectedDocType = docType;

                if (blueprintIds.length) {
                    if (blueprintConfig.skipSelect) {
                        createFromBlueprint(blueprintIds[0]);
                    } else {
                        $scope.dialogMode = "selectBlueprint";
                    }
                } else {
                    createBlank();
                }
            };

            function createFromBlueprint(blueprintId) {
                contentResource.getBlueprintScaffold(-1, blueprintId).then(function (data) {
                    // Assign the model to scope
                    $scope.nodeContext = $scope.model.node = data;
                    $scope.dialogMode = "edit";
                    vm.content = $scope.nodeContext.variants[0];
                    vm.loading = false;
                });
            };

            $scope.createBlank = createBlank;
            $scope.createOrSelectBlueprintIfAny = createOrSelectBlueprintIfAny;
            $scope.createFromBlueprint = createFromBlueprint;

            function loadNode() {
                vm.loading = true;
                contentResource.getScaffold(-1, $scope.model.dialogData.docTypeAlias).then(function (data) {

                    // Merge current value
                    if ($scope.model.dialogData.value) {
                        for (var v = 0; v < data.variants.length; v++) {
                            var variant = data.variants[v];
                            for (var t = 0; t < variant.tabs.length; t++) {
                                var tab = variant.tabs[t];
                                for (var p = 0; p < tab.properties.length; p++) {
                                    var prop = tab.properties[p];
                                    if ($scope.model.dialogData.value[prop.alias]) {
                                        prop.value = $scope.model.dialogData.value[prop.alias];
                                    }
                                }
                            }
                        }
                    };

                    // Assign the model to scope
                    $scope.nodeContext = $scope.model.node = data;
                    vm.content = $scope.nodeContext.variants[0];
                    vm.loading = false;
                });
            }

            if ($scope.model.dialogData.docTypeAlias) {
                $scope.dialogMode = "edit";
                loadNode();
            } else {
                $scope.dialogMode = "selectDocType";
                // No data type, so load a list to choose from
                dtgeResources.getContentTypes($scope.model.allowedDocTypes).then(function (docTypes) {
                    $scope.docTypes = docTypes;
                    if ($scope.docTypes.length == 1) {
                        createOrSelectBlueprintIfAny($scope.docTypes[0]);
                    }
                    else {
                        vm.loading = false;
                    }
                });
            }

        }

    ]);
<|endoftext|>


<commit_before>/*
	StartPage
	http://github.com/abstractOwl
*/

(function () { // Wrap strict scope
	'use strict';

	// Set default links
	var defaultData = {
		'hello': {
			'google': "http://www.google.com",
			'identi.ca': 'http://www.identi.ca'
		},
		'goodbye': {
			'test': 'http://b.com'
		}
	}

	// Represent URL category
	function Category(name) {
		this.children	=	{};
		this.name		=	name;
		this.count		=	0;
	}
	// Mold it into any shape you want! Perfect gift for any childNode.
	Category.prototype.htmlDoh		=
		'

{{name}}
'
		+ '
{{links}}
Add Link
'
		+ '

';
	Category.prototype.stringDoh	= '{ name: {{name}}, links: [{{links}}] }';
	// Add a Link object to this Category
	Category.prototype.add = function (name, url) {
		this.children[this.count] = new Link(name, url, this.count);
		this.count++;
	};
	Category.prototype.get = function (idx) {
		return this.children[idx];
	};
	// Given an id, remove Link associated with that id
	Category.prototype.remove = function (id) {
		delete this.children[id];
	};
	// Get element representation of this Category instance
	Category.prototype.toElement = function () {
		return magic(this.html());
	};
	// Get HTML representation of this Category instance
	Category.prototype.html = function () {
		var s = '';

		for (var p in this.children) {
			s += this.children[p].html();
		}

		return (
			compile(this.htmlDoh, {
				name: this.name,
				links: s
			})
		);
	};
	// Can't use .length because we're using an object, not an array
	Category.prototype.size = function () {
		var c = 0;
		for (var n in this.children) {
			c++;
		}
		return c;
	};
	// Get JSON string representation of thie Category instance
	Category.prototype.toJSON = function () {
		var s = '';

		for (var p in this.children) {
			if (this.children.hasOwnProperty(p)) {
				s += this.children[p].toJSON();
			}
		}

		return (
			compile(this.stringDoh, {
				name: this.name,
				links: s
			})
		);
	};

	// Represent Link object
	function Link(name, url, id) {
		this.name	=	name;
		this.url	=	url;
		this.id		=	id;
	}
	// Mold it into any shape you want! Perfect gift for any childNode.
	Link.prototype.htmlDoh		=	'
'
		+ '{{name}}
';
	Link.prototype.stringDoh	=	'{ name: {{name}}, url: {{url}} }';
	// Get element representation of this Link instance
	Link.prototype.toElement = function () {
		return magic(this.html());
	};
	// Get HTML representation of this Link instance
	Link.prototype.html = function () {
		return (
			compile(this.htmlDoh, {
				id: this.id,
				name: this.name,
				url: this.url
			})
		);
	};
	// Get JSON string representation of this Link instance
	Link.prototype.toJSON = function () {
		return compile(this.stringDoh, { name: this.name, url: this.url });
	};

	// Singleton object representing StartPage application
	var StartPage = (function () {
		var	instance,
			categories,
			element,
			position,
			storage,
			storageImplementations;

		//
		//	Storage Strategies: (cookie|dom|none)
		//		Each storage strategy implements the following functions:
		//			- init(): Initializes storage strategy
		//			- get(k): Retrieves item from storage
		//			- set(k, v): Puts item into storage
		//			- supported(): Check whether browser supports strategy
		//
		storageImplementations = {
			cookie: {
				init: function () {},
				get: function (k) {
					return
						unescape(document.cookie.replace(
							new RegExp("(?:^|.*;\\s*)"
								+ escape(sKey).replace(/[\-\.\+\*]/g, "\\$&")
								+ "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"),
								"$1"
							)
						);
				},
				set: function (k, v) {
					// Only stores one item. Which is all we need, for now
					document.cookie = escape(k) + '=' + escape(v) + '; '
						+ 'expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/';
				},
				supported: function () {
					return navigator.cookieEnabled;
				}
			},
			dom: {
				init: function () {},
				get: function (k) {
					return window.localStorage.getItem(k);
				},
				set: function (k, v) {
					window.localStorage.setItem(k, v);
				},
				supported: function () {
					return !!window.localStorage;
				}
			},
			none: {
				init: function () {
					window.alert('DOM and Cookie storage are not supported by '
						+ 'your browser. Your session will not be saved.');
				},
				get: function (k) {},
				set: function (k, v) {},
				supported: function () {
					return true;
				}
			}
		};

		// Append a Category object
		function addCategory() {
			var title = prompt ('Name your shiny new category!');
			if (title) {
				categories.push(new Category(title));
				return true;
			}
			return false;
		}

		// Append a Link object to the current Category
		function addLink() {
			if (position.x >= 0) {
				var title, url;
				// Don't prompt for URL if title prompt cancelled
				(title	=	prompt ('What will you name this link?')) &&
				(url	=	prompt ('What is the link\'s URL?', 'http://'));

				// Add to current category
				if (title && url) {
					categories[position.x].add(title, url);
					return true;
				}
			}
			return false;
		}

		var VK = {
			UP:		38,
			DOWN:	40,
			LEFT:	37,
			RIGHT:	39,
			ENTER:	13,
			DELETE:	46
		};
		function handleKey(key) {
			switch (key) {
			case VK.UP :
				console.log('up');
				if (position.x >= 0) {
					position.y = Math.max(-1, position.y - 1);
				}
				break;
			case VK.DOWN:
				console.log('down');
				if (position.x >= 0) {
					if (position.y + 1 > categories[position.x].size() - 1) {
						position.y += (addLink() ? 1 : 0);
					} else {
						position.y = Math.min(categories[position.x].size() - 1,
							position.y + 1);
					}
				}
				break;
			case VK.LEFT:
				console.log('left');
				position.x = Math.max(-1, position.x - 1);
				position.y = -1;
				break;
			case VK.RIGHT:
				console.log('right');
				if (position.x > categories.length - 2) {
					position.x += (addCategory() ? 1 : 0);
				} else {
					position.x = Math.min(categories.length - 1, position.x + 1);
				}
				position.y = -1;
				break;
			case VK.ENTER:
				console.log('enter');
				if (position.x >= 0 && position.y >= 0) {
					location.href = categories[position.x].get(position.y).url;
				}
				break;
			case VK.DELETE:
				console.log('delete');
				if (position.y >= 0) {
					// Delete Link
					// Find data-id
					var currCategory =
						document.getElementsByClassName('category')[position.x];
					var currLink =
						currCategory.getElementsByClassName('link')[position.y];
					categories[position.x].remove(currLink['data-id']);
				} else if (position.x >= 0) {
					// Delete Category
				}
				break;
			}
			render();
		}

		function init() {
			// Initialize this object
			categories	=	[];

			position = {
				x: -1,
				y: -1
			};

			// Find supported storageImpl
			if (storageImplementations.dom.supported()) {
				storage = storageImplementations.dom;
			} else if (storageImplementations.cookie.supported()) {
				storage = storageImplementations.cookie;
			} else {
				storage = storageImplementations.none;
			}

			window.onkeydown = function (e) {
				var key = e.which ? e.which : e.keyCode;
				handleKey(key);
			};

			return {
				// Load settings or defaults
				loadSettings: function () {
					if (!storage.get('links')) {
						this.fromJSON(JSON.stringify(defaultData));
					} else {
						this.fromJSON(storage.get('links'));
					}

					render();
				},
				// Public functions
				toElement: function () {
					return element;
				},
				fromJSON: function (json) {
					fromJSON(json);
				},
				toJSON: function () {
					return toJSON();
				}
			}
		}
		// Load JSON string into StartPage
		function fromJSON (s) {
			var tmp = JSON.parse(s);
			categories = [];

			// Unwrap JSON object
			for (var m in tmp) {
				if (tmp.hasOwnProperty(m)) {
					var c = new Category(m);

					for (var n in tmp[m]) {
						if (tmp[m].hasOwnProperty(n)) {
							c.add(n, tmp[m][n]);
						}
					}

					categories.push(c);
				}
			}
		}
		// Get JSON string representation of StartPage data
		function toJSON() {
			var s = '';

			for (var i = 0, j = categories.length; i < j; i++) {
				s += categories[i].toJSON();
			}

			return '[' + s + ']';
		}
		function render() {
			console.log('render');
			// Definitely not most efficient way of rendering har har
			var clearfix = document.createElement('div');
			clearfix.className = 'clearfix';

			element = document.createElement('ul');
			element.className = 'container main';

			// Build interface
			for (var i = 0, j = categories.length; i < j; i++) {
				element.appendChild(categories[i].toElement());
			}
			element.appendChild(clearfix);

			document.body.innerHTML = ''; // Clear body
			document.body.appendChild(element);

			var addLinks = document.getElementsByClassName('add-link');
			for (var i = 0, j = addLinks.length; i < j; i++) {
				addLinks[i].onclick = function () {
					console.log('hey');
					addLink();
				}
			}

			if (position.x >= 0) {
				// Highlight selected Category/Link
				var selected =
					document.getElementsByClassName('category')[position.x];
				selected.className += ' active';

				if (position.y >= 0) {
					selected.getElementsByClassName('link')[position.y].className
						+= ' active';
				}
			}
		}
		function save() {
			storage.set('data', this.toJSON());
		}

		return {
			getInstance: function () {
				if (!instance) {
					instance = init();
				}

				return instance;
			}
		}
	}) ();

	// Utility functions

	// Fill in template strings
	function compile(a, b) {
		var s = a;

		for (var p in b) {
			if (b.hasOwnProperty(p)) {
				s = s.replace('{{' + p + '}}', b[p]);
			}
		}

		return s;
	}

	// Bringin' text to life in the DOM world
	function magic(s) {
		var t = document.createElement('div');
		t.innerHTML = s;
		return t.firstChild;
	}

	window.onload = function () {
		StartPage.getInstance().loadSettings();
	};

}) ();

<commit_msg>Delete function now works<commit_after>/*
	StartPage
	http://github.com/abstractOwl
*/

(function () { // Wrap strict scope
	'use strict';

	// Set default links
	var defaultData = [
		{
			'name': 'hello',
			'links': [
				{
					'name': 'google',
					'url': "http://www.google.com",
				}, {
					'name': 'identi.ca',
					'url': 'http://www.identi.ca'
				}
			]
		}, {
			'name': 'goodbye',
			'links':  [
				{
					'name': 'test',
					'url': 'http://b.com'
				}
			]
		}
	];

	// Represent URL category
	function Category(name) {
		this.children	=	{};
		this.name		=	name;
		this.count		=	0;
	}
	// Mold it into any shape you want! Perfect gift for any childNode.
	Category.prototype.htmlDoh		=
		'

{{name}}
'
		+ '
{{links}}
Add Link
'
		+ '

';
	Category.prototype.stringDoh	=
		'{ "name": "{{name}}", "links": [{{links}}] }';
	// Add a Link object to this Category
	Category.prototype.add = function (name, url) {
		this.children[this.count] = new Link(name, url, this.count);
		this.count++;
	};
	Category.prototype.get = function (idx) {
		return this.children[idx];
	};
	// Given an id, remove Link associated with that id
	Category.prototype.remove = function (id) {
		delete this.children[id];
	};
	// Get element representation of this Category instance
	Category.prototype.toElement = function () {
		return magic(this.html());
	};
	// Get HTML representation of this Category instance
	Category.prototype.html = function () {
		var s = '';

		for (var p in this.children) {
			s += this.children[p].html();
		}

		return (
			compile(this.htmlDoh, {
				name: this.name,
				links: s
			})
		);
	};
	// Can't use .length because we're using an object, not an array
	Category.prototype.size = function () {
		var c = 0;
		for (var n in this.children) {
			c++;
		}
		return c;
	};
	// Get JSON string representation of thie Category instance
	Category.prototype.toJSON = function () {
		var a = [];

		for (var p in this.children) {
			if (this.children.hasOwnProperty(p)) {
				a.push(this.children[p].toJSON());
			}
		}

		return (
			compile(this.stringDoh, {
				name: this.name,
				links: a.join(', ')
			})
		);
	};

	// Represent Link object
	function Link(name, url, id) {
		this.name	=	name;
		this.url	=	url;
		this.id		=	id;
	}
	// Mold it into any shape you want! Perfect gift for any childNode.
	Link.prototype.htmlDoh		=	'
'
		+ '{{name}}
';
	Link.prototype.stringDoh	=	'{ "name": "{{name}}", "url": "{{url}}" }';
	// Get element representation of this Link instance
	Link.prototype.toElement = function () {
		return magic(this.html());
	};
	// Get HTML representation of this Link instance
	Link.prototype.html = function () {
		return (
			compile(this.htmlDoh, {
				id: this.id,
				name: this.name,
				url: this.url
			})
		);
	};
	// Get JSON string representation of this Link instance
	Link.prototype.toJSON = function () {
		return compile(this.stringDoh, { name: this.name, url: this.url });
	};

	// Singleton object representing StartPage application
	var StartPage = (function () {
		var	instance,
			categories,
			element,
			position,
			storage,
			storageImplementations;

		//
		//	Storage Strategies: (cookie|dom|none)
		//		Each storage strategy implements the following functions:
		//			- init(): Initializes storage strategy
		//			- get(k): Retrieves item from storage
		//			- set(k, v): Puts item into storage
		//			- supported(): Check whether browser supports strategy
		//
		storageImplementations = {
			cookie: {
				init: function () {},
				get: function (k) {
					return
						unescape(document.cookie.replace(
							new RegExp("(?:^|.*;\\s*)"
								+ escape(sKey).replace(/[\-\.\+\*]/g, "\\$&")
								+ "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"),
								"$1"
							)
						);
				},
				set: function (k, v) {
					// Only stores one item. Which is all we need, for now
					document.cookie = escape(k) + '=' + escape(v) + '; '
						+ 'expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/';
				},
				supported: function () {
					return navigator.cookieEnabled;
				}
			},
			dom: {
				init: function () {},
				get: function (k) {
					return window.localStorage.getItem(k);
				},
				set: function (k, v) {
					window.localStorage.setItem(k, v);
				},
				supported: function () {
					return !!window.localStorage;
				}
			},
			none: {
				init: function () {
					window.alert('DOM and Cookie storage are not supported by '
						+ 'your browser. Your session will not be saved.');
				},
				get: function (k) {},
				set: function (k, v) {},
				supported: function () {
					return true;
				}
			}
		};

		// Append a Category object
		function addCategory() {
			var title = prompt ('Name your shiny new category!');
			if (title) {
				categories.push(new Category(title));
				return true;
			}
			return false;
		}

		// Append a Link object to the current Category
		function addLink() {
			if (position.x >= 0) {
				var title, url;
				// Don't prompt for URL if title prompt cancelled
				(title	=	prompt ('What will you name this link?')) &&
				(url	=	prompt ('What is the link\'s URL?', 'http://'));

				// Add to current category
				if (title && url) {
					categories[position.x].add(title, url);
					return true;
				}
			}
			return false;
		}

		var VK = {
			UP:		38,
			DOWN:	40,
			LEFT:	37,
			RIGHT:	39,
			ENTER:	13,
			DELETE:	46
		};
		function handleKey(key) {
			switch (key) {
			case VK.UP :
				console.log('up');
				if (position.x >= 0) {
					position.y = Math.max(-1, position.y - 1);
				}
				break;
			case VK.DOWN:
				console.log('down');
				if (position.x >= 0) {
					if (position.y + 1 > categories[position.x].size() - 1) {
						position.y += (addLink() ? 1 : 0);
					} else {
						position.y = Math.min(categories[position.x].size() - 1,
							position.y + 1);
					}
				}
				break;
			case VK.LEFT:
				console.log('left');
				position.x = Math.max(-1, position.x - 1);
				position.y = -1;
				break;
			case VK.RIGHT:
				console.log('right');
				if (position.x > categories.length - 2) {
					position.x += (addCategory() ? 1 : 0);
				} else {
					position.x = Math.min(categories.length - 1, position.x + 1);
				}
				position.y = -1;
				break;
			case VK.ENTER:
				console.log('enter');
				if (position.x >= 0 && position.y >= 0) {
					location.href = categories[position.x].get(position.y).url;
				}
				break;
			case VK.DELETE:
				console.log('delete');
				if (position.y >= 0) {
					// Delete Link
					// Find data-id
					var	currCategory =
						document.getElementsByClassName('category')[position.x],
						currLink =
						currCategory.getElementsByClassName('link')[position.y];
					categories[position.x].remove(
						currLink.getAttribute('data-id')
					);
				} else if (position.x >= 0) {
					// Delete Category
				}
				break;
			}
			save();
			render();
		}

		function init() {
			// Initialize this object
			categories	=	[];

			position = {
				x: -1,
				y: -1
			};

			// Find supported storageImpl
			if (storageImplementations.dom.supported()) {
				storage = storageImplementations.dom;
			} else if (storageImplementations.cookie.supported()) {
				storage = storageImplementations.cookie;
			} else {
				storage = storageImplementations.none;
			}

			window.onkeydown = function (e) {
				var key = e.which ? e.which : e.keyCode;
				handleKey(key);
			};

			return {
				// Load settings or defaults
				loadSettings: function () {
					if (!storage.get('data')) {
						this.fromJSON(JSON.stringify(defaultData));
					} else {
						this.fromJSON(storage.get('data'));
					}

					render();
				},
				// Public functions
				toElement: function () {
					return element;
				},
				fromJSON: function (json) {
					fromJSON(json);
				},
				toJSON: function () {
					return toJSON();
				}
			}
		}
		// Load JSON string into StartPage
		function fromJSON (s) {
			var tmp = JSON.parse(s);
			categories = [];

			// Unwrap JSON object
			for (var i = 0, j = tmp.length; i < j; i++) {
				var c = new Category(tmp[i].name);

				for (var p = 0, q = tmp[i].links, r = q.length; p < r; p++) {
					c.add(q[p].name, q[p].url);
				}

				categories.push(c);
			}
		}
		// Get JSON string representation of StartPage data
		function toJSON() {
			var a = [];

			for (var i = 0, j = categories.length; i < j; i++) {
				a.push(categories[i].toJSON());
			}

			return '[' + a.join(', ') + ']';
		}
		function render() {
			console.log('render');
			// Definitely not most efficient way of rendering har har
			var clearfix = document.createElement('div');
			clearfix.className = 'clearfix';

			element = document.createElement('ul');
			element.className = 'container main';

			// Build interface
			for (var i = 0, j = categories.length; i < j; i++) {
				element.appendChild(categories[i].toElement());
			}
			element.appendChild(clearfix);

			document.body.innerHTML = ''; // Clear body
			document.body.appendChild(element);

			var addLinks = document.getElementsByClassName('add-link');
			for (var i = 0, j = addLinks.length; i < j; i++) {
				addLinks[i].onclick = function () {
					console.log('hey');
					addLink();
				}
			}

			if (position.x >= 0) {
				// Highlight selected Category/Link
				var selected =
					document.getElementsByClassName('category')[position.x];
				selected.className += ' active';

				if (position.y >= 0) {
					selected.getElementsByClassName('link')[position.y].className
						+= ' active';
				}
			}
		}
		function save() {
			storage.set('data', toJSON());
		}

		return {
			getInstance: function () {
				if (!instance) {
					instance = init();
				}

				return instance;
			}
		}
	}) ();

	// Utility functions

	// Fill in template strings
	function compile(a, b) {
		var s = a;

		for (var p in b) {
			if (b.hasOwnProperty(p)) {
				s = s.replace('{{' + p + '}}', b[p]);
			}
		}

		return s;
	}

	// Bringin' text to life in the DOM world
	function magic(s) {
		var t = document.createElement('div');
		t.innerHTML = s;
		return t.firstChild;
	}

	window.onload = function () {
		StartPage.getInstance().loadSettings();
	};

}) ();

<|endoftext|>


<commit_before>// Dependencies
var Fs = require("fs")
  , Path = require("path")
  , ExecLimiter = require("exec-limiter")
  , Ul = require("ul")
  ;

/**
 * Gry
 * Creates a new `Gry` instance.
 *
 * @name Gry
 * @function
 * @param {Object} options An object containing the following fields:
 *
 *  - `path` (String): The path to the git repository.
 *  - `limit` (Number): The limit of commands to run same time.
 *
 * @return {Gry} The `Gry` instance.
 */
function Gry (options) {

    var self = this;

    if (typeof options === "string") {
        options = {
            path: options
        };
    }

    options = Ul.merge({
        limit: 30
    }, options);

    options.path = Path.resolve(options.path);

    self.options = options;
    self.cwd = options.path;

    self.limiter = new ExecLimiter(options.limit);
}

/**
 * exec
 * Executes a git command in the repository directory.
 *
 * @name exec
 * @function
 * @param {String} command The git command that should be executed in the repository directory.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.exec = function (command, callback) {

    debugger
    this.limiter.add("git " + command.trim(), [{ cwd: this.cwd }], function (err, stdout) {
        if (err) { return callback(err); }
        callback(null, stdout.trimRight());
    });

    return this;
};

/**
 * init
 * Inits the git repository.
 *
 * @name init
 * @function
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.init = function (callback) {
    return this.exec("init", callback);
};

/**
 * create
 * Creates a git repository.
 *
 * @name create
 * @function
 * @param {String} path The path of the repository.
 * @param {Function} callback The callback function
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.create = function (callback) {
    var self = this;
    Fs.exists(self.cwd, function (e) {
        if (e) {
            callback(new Error("A directory already exists."));
            return self;
        }
        Fs.mkdir(self.cwd, function (err) {
            if (err) {
                callback(err);
                return self;
            }
            self.init(callback);
        });
    });
    return self;
};

/**
 * commit
 * Creates a commit, providing the `message`.
 *
 * @name commit
 * @function
 * @param {String} message The commit message
 * @param {String} options Additional options passed to the commit command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.commit = function (message, options, callback) {
    message = message.replace(/\"/g, "\\");
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("commit -m \"" + message + "\" " + options, callback)
};

/**
 * pull
 * Runs `git pull`.
 *
 * @name pull
 * @function
 * @param {String} options Additional options passed to the `pull` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.pull = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("pull " + options, callback);
};

/**
 * add
 * Runs `git add`.
 *
 * @name add
 * @function
 * @param {String} options Additional options passed to the `add` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.add = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = ".";
    }
    return this.exec("add " + options, callback);
};

/**
 * branch
 * Runs `git branch`.
 *
 * @name branch
 * @function
 * @param {String} options Additional options passed to the `branch` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.branch = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("branch " + options, callback);
};

/**
 * checkout
 * Runs `git checkout`.
 *
 * @name checkout
 * @function
 * @param {String} options Additional options passed to the `checkout` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.checkout = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("checkout " + options, callback);
};

/**
 * clone
 * Runs `git clone`.
 *
 * @name clone
 * @function
 * @param {String} gitUrl The git url of the repository that should be cloned.
 * @param {String} options Additional options passed to the `checkout` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.clone = function (gitUrl, options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("clone " + gitUrl + " " + options, callback);
};

module.exports = Gry;
<commit_msg>Initial commit.<commit_after>// Dependencies
var Fs = require("fs")
  , Path = require("path")
  , ExecLimiter = require("exec-limiter")
  , Ul = require("ul")
  ;

/**
 * Gry
 * Creates a new `Gry` instance.
 *
 * @name Gry
 * @function
 * @param {Object} options An object containing the following fields:
 *
 *  - `path` (String): The path to the git repository.
 *  - `limit` (Number): The limit of commands to run same time.
 *
 * @return {Gry} The `Gry` instance.
 */
function Gry (options) {

    var self = this;

    if (typeof options === "string") {
        options = {
            path: options
        };
    }

    options = Ul.merge({
        limit: 30
    }, options);

    options.path = Path.resolve(options.path);

    self.options = options;
    self.cwd = options.path;

    self.limiter = new ExecLimiter(options.limit);
}

/**
 * exec
 * Executes a git command in the repository directory.
 *
 * @name exec
 * @function
 * @param {String} command The git command that should be executed in the repository directory.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.exec = function (command, callback) {

    this.limiter.add("git " + command.trim(), [{ cwd: this.cwd }], function (err, stdout) {
        if (err) { return callback(err); }
        callback(null, stdout.trimRight());
    });

    return this;
};

/**
 * init
 * Inits the git repository.
 *
 * @name init
 * @function
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.init = function (callback) {
    return this.exec("init", callback);
};

/**
 * create
 * Creates a git repository.
 *
 * @name create
 * @function
 * @param {String} path The path of the repository.
 * @param {Function} callback The callback function
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.create = function (callback) {
    var self = this;
    Fs.exists(self.cwd, function (e) {
        if (e) {
            callback(new Error("A directory already exists."));
            return self;
        }
        Fs.mkdir(self.cwd, function (err) {
            if (err) {
                callback(err);
                return self;
            }
            self.init(callback);
        });
    });
    return self;
};

/**
 * commit
 * Creates a commit, providing the `message`.
 *
 * @name commit
 * @function
 * @param {String} message The commit message
 * @param {String} options Additional options passed to the commit command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.commit = function (message, options, callback) {
    message = message.replace(/\"/g, "\\");
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("commit -m \"" + message + "\" " + options, callback)
};

/**
 * pull
 * Runs `git pull`.
 *
 * @name pull
 * @function
 * @param {String} options Additional options passed to the `pull` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.pull = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("pull " + options, callback);
};

/**
 * add
 * Runs `git add`.
 *
 * @name add
 * @function
 * @param {String} options Additional options passed to the `add` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.add = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = ".";
    }
    return this.exec("add " + options, callback);
};

/**
 * branch
 * Runs `git branch`.
 *
 * @name branch
 * @function
 * @param {String} options Additional options passed to the `branch` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.branch = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("branch " + options, callback);
};

/**
 * checkout
 * Runs `git checkout`.
 *
 * @name checkout
 * @function
 * @param {String} options Additional options passed to the `checkout` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.checkout = function (options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("checkout " + options, callback);
};

/**
 * clone
 * Runs `git clone`.
 *
 * @name clone
 * @function
 * @param {String} gitUrl The git url of the repository that should be cloned.
 * @param {String} options Additional options passed to the `checkout` command.
 * @param {Function} callback The callback function.
 * @return {Gry} The `Gry` intance.
 */
Gry.prototype.clone = function (gitUrl, options, callback) {
    if (typeof options === "function") {
        callback = options;
        options = "";
    }
    return this.exec("clone " + gitUrl + " " + options, callback);
};

module.exports = Gry;
<|endoftext|>


<commit_before>/*
 Copyright 2016 - 2018 by Jan Dockx

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

'use strict'

const ContractError = require('./ContractError')
const util = require('../_private/util')

const displayNamePrefix = 'contract function '

/**
 * Abstract definition of a function contract.
 *
 * An AbstractContract consists of an array of preconditions, an array of nominal postconditions,
 * and an array of exceptional postconditions.
 *
 * The conditions are functions whose result is interpreted as a booleany value.
 * The conditions are called with the same `this` and arguments as the functional call in which they are
 * verified. When verifying nominal postconditions, the result of the function call is added as extra argument.
 * When verifying exceptional postconditions, the exception thrown by the function call is added as extra argument.
 * Finally, a version of the contract function bound to `this` is supplied as final parameter when verifying
 * nominal and exceptional postconditions. This function reference can be used in contracts that use recursion.
 *
 * Furthermore, an instance contains a `location` property, which is a line of text
 * that refers to the source code where the contract was created. For internal contracts, this is
 * `AbstractContract.internalLocation.
 *
 * @constructor
 */
function AbstractContract (kwargs, /* Object? */ internalLocation) {
  util.pre(() => !!kwargs)
  util.pre(() => !kwargs.pre || util.typeOf(kwargs.pre) === 'array')
  util.pre(() => !kwargs.post || util.typeOf(kwargs.post) === 'array')
  util.pre(() => !kwargs.exception || util.typeOf(kwargs.exception) === 'array')
  // MUDO util.pre(() => !internalLocation || internalLocation === AbstractContract.internalLocation)

  const self = this

  function abstract () { throw new AbstractContract.AbstractError(self) }

  const location = internalLocation || util.firstLocationOutsideLibrary()
  AbstractContract.bless(abstract, self, abstract, location)
  util.setAndFreezeProperty(self, '_pre', Object.freeze(kwargs.pre ? kwargs.pre.slice() : []))
  util.setAndFreezeProperty(self, '_post', Object.freeze(kwargs.post ? kwargs.post.slice() : []))
  util.setAndFreezeProperty(self, '_exception', Object.freeze(kwargs.exception ? kwargs.exception.slice() : []))
  util.setAndFreezeProperty(self, 'location', Object.freeze(location))
  util.setAndFreezeProperty(self, 'abstract', abstract)
}

/**
 * Object to be used as location for contracts and implementations that are generated inside this library.
 */
AbstractContract.internalLocation = Object.freeze({
  toString: function () { return 'INTERNAL' }
})

AbstractContract.prototype = {
  constructor: AbstractContract,
  _pre: null,
  _post: null,
  _exception: null,
  isImplementedBy: function (f) {
    return AbstractContract.isAGeneralContractFunction(f) && f.contract === this
  }
}
util.defineFrozenReadOnlyArrayProperty(AbstractContract.prototype, 'pre', '_pre')
util.defineFrozenReadOnlyArrayProperty(AbstractContract.prototype, 'post', '_post')
util.defineFrozenReadOnlyArrayProperty(AbstractContract.prototype, 'exception', '_exception')
util.setAndFreezeProperty(AbstractContract.prototype, 'location', AbstractContract.internalLocation)
util.setAndFreezeProperty(AbstractContract.prototype, 'abstract', null)

AbstractContract.displayNamePrefix = displayNamePrefix

/**
 * Return a string that is a sensible display name for the given function as a contract function.
 */
AbstractContract.contractFunctionDisplayName = function (f) {
  util.pre(() => util.typeOf(f) === 'function')

  return displayNamePrefix +
         (f.name ||
            (f.implementation && (f.implementation.displayName || f.implementation.name)) ||
            '<<anonymous>>')
}

/**
 * This function is intended to be used as the bind function of contract functions. It makes sure
 * that, when applied to a contract function, the result
 * [is also a contract function]{@linkplain AbstractContract#isAContractFunction}.
 * The bind aspect of the functionality is the same as {@link Function#prototype#bind}.
 * The implementation of the resulting contract function is also bound in the same
 * way as the resulting contract function itself.
 */
AbstractContract.bindContractFunction = function bind () {
  util.pre(() => AbstractContract.isAGeneralContractFunction(this))

  const bound = Function.prototype.bind.apply(this, arguments)
  const boundImplementation = Function.prototype.bind.apply(this.implementation, arguments)
  AbstractContract.bless(bound, this.contract, boundImplementation, this.location)
  return bound
}

/**
 * A General Contract Function is an implementation of an AbstractContract. This function verifies whether a function
 * given as a parameter is a General Contract Function.
 *
 * To be a General Contract Function, the subject must
 *

 *
be a function,

 *
have a frozen `contract` property that refers to a AbstractContract,

 *
have a frozen `implementation` property that refers to a function (which realizes the contract),

 *
have a frozen `location` property, that has a value,

 *
have a frozen `bind` property, which is {@link AbstractContract.bindContractFunction}, and

 *
have a `displayName` that is a contract function display name, which is a string that gives
 *     information for a programmer to understand which contract function this is, and

 *
if the `implementation` function has a `prototype`, have a `prototype` property,
 *

 *
that is an object,

 *
that has a `constructor` property that is the contract function, and

 *
that has `f.implementation.prototype` in its prototype chain, or is equal to it.
 *

 *

 */
AbstractContract.isAGeneralContractFunction = function (f) {
  // Apart from this, we expect f to have a name. But it is controlled by the JavaScript engine, and we cannot
  // freeze it, and not guaranteed in all engines.
  return true
/*
  return util.typeOf(f) === 'function' &&
         f.contract instanceof AbstractContract &&
         util.isFrozenOwnProperty(f, 'contract') &&
         util.typeOf(f.implementation) === 'function' &&
         util.isFrozenOwnProperty(f, 'implementation') &&
         util.isFrozenOwnProperty(f, 'location') &&
         f.location &&
         f.displayName === AbstractContract.contractFunctionDisplayName(f) &&
         util.isFrozenOwnProperty(f, 'bind') &&
         f.bind === AbstractContract.bindContractFunction &&
         (!f.implementation.hasOwnProperty('prototype') ||
             (util.typeOf(f.prototype) === 'object' &&
                 f.prototype.constructor === f &&
                 (f.prototype === f.implementation.prototype || f.prototype instanceof f.implementation)))
*/
}

/**
 * A Contract Function is an implementation of a Contract. This function verifies whether a function
 * given as a parameter is a Contract Function.
 *
 * To be a Contract Function, the subject must
 *

 *
be a [general contract function]{@linkplain #isAGeneralContractFunction()},

 *
have a frozen `location` property, which is a string that represents a location in source code,
 *     outside this library.

 *

 */
AbstractContract.isAContractFunction = function (f) {
  return AbstractContract.isAGeneralContractFunction(f) && util.isALocationOutsideLibrary(f.location)
}

/**
 * Helper function that transforms any function given as contractFunction
 * into a [contract function]{@linkplain AbstractContract#isAContractFunction}
 * for the given parameters.
 * If {@code implFunction.prototype} exists, the {@code contractFunction.prototype} is changed to
 * an object that refers to {@code contractFunction} as {@code contractFunction.prototype.constructor},
 * is otherwise empty, and has {@code implFunction.prototype} as prototype.
 *
 * @param contractFunction {Function} the regular {Function} to be transformed into a contract function
 * @param contract {AbstractContract} the contract contractFunction is a realisation of
 * @param implFunction {Function} the function that is used in contractFunction
 *                     to realize the postconditions of contract under its preconditions
 * @param location {String} the location outside this library that the resulting
 *                          [contract function]{@linkplain AbstractContract#isAContractFunction} will carry,
 *                          that says where it is defined.
 */
AbstractContract.bless = function bless (contractFunction, contract, implFunction, location) {
  util.pre(() => util.typeOf(contractFunction) === 'function')
  // noinspection JSUnresolvedVariable
  util.pre(() => !contractFunction.contract)
  // noinspection JSUnresolvedVariable
  util.pre(() => !contractFunction.implementation)
  // noinspection JSUnresolvedVariable
  util.pre(() => !contractFunction.location)
  util.pre(() => contractFunction.bind === Function.prototype.bind)
  util.pre(() => contract instanceof AbstractContract)
  util.pre(() => util.typeOf(implFunction) === 'function')
  // MUDO util.pre(() => location === AbstractContract.internalLocation || util.isALocationOutsideLibrary(location))

  util.setAndFreezeProperty(contractFunction, 'contract', contract)
  util.setAndFreezeProperty(contractFunction, 'implementation', implFunction)
  util.setAndFreezeProperty(contractFunction, 'location', location)
  util.setAndFreezeProperty(contractFunction, 'bind', AbstractContract.bindContractFunction)
  if (implFunction.hasOwnProperty('prototype')) {
    contractFunction.prototype = Object.create(implFunction.prototype)
    util.setAndFreezeProperty(contractFunction.prototype, 'constructor', contractFunction)
  }
  util.defineFrozenDerivedProperty(
    contractFunction,
    'displayName',
    function () { return AbstractContract.contractFunctionDisplayName(this) }
  )
}

/**
 * Function that always returns false.
 */
AbstractContract.falseCondition = function () { return false }

/**
 * Singleton array of {@linkplain AbstractContract#falseCondition}. Can be used the clearly signal
 * that a function should never throw exceptions, or never end nominally, or should never be called,
 * because the preconditions will always fail.
 */
AbstractContract.mustNotHappen = [AbstractContract.falseCondition]

/**
 * The most general function AbstractContract. This has the most strict preconditions (nothing is allowed), which can
 * be weakened by specializations, and the most general nominal and exceptional postconditions (anything goes),
 * which can be strengthened by specializations.
 */
AbstractContract.root = new AbstractContract(
  {
    pre: AbstractContract.mustNotHappen,
    post: [],
    exception: []
  },
  AbstractContract.internalLocation
)

const message = 'an abstract function cannot be executed'

/**
 * Thrown when an abstract method is called. You shouldn't.
 *
 * @constructor
 */
function AbstractError (contract) {
  util.pre(() => contract instanceof AbstractContract)

  ContractError.call(this)
  util.setAndFreezeProperty(this, 'name', AbstractError.name)
  util.setAndFreezeProperty(this, 'message', message)
  util.setAndFreezeProperty(this, 'contract', contract)
}

AbstractError.prototype = new ContractError()
AbstractError.prototype.constructor = AbstractError
util.setAndFreezeProperty(AbstractError.prototype, 'name', AbstractError.name)
util.setAndFreezeProperty(AbstractError.prototype, 'message', message)
util.setAndFreezeProperty(AbstractError.prototype, 'contract', null)

AbstractError.message = message

AbstractContract.AbstractError = AbstractError

module.exports = AbstractContract
<commit_msg>Make it the tests work in node again<commit_after>/*
 Copyright 2016 - 2018 by Jan Dockx

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

'use strict'

const ContractError = require('./ContractError')
const util = require('../_private/util')

const displayNamePrefix = 'contract function '

/**
 * Abstract definition of a function contract.
 *
 * An AbstractContract consists of an array of preconditions, an array of nominal postconditions,
 * and an array of exceptional postconditions.
 *
 * The conditions are functions whose result is interpreted as a booleany value.
 * The conditions are called with the same `this` and arguments as the functional call in which they are
 * verified. When verifying nominal postconditions, the result of the function call is added as extra argument.
 * When verifying exceptional postconditions, the exception thrown by the function call is added as extra argument.
 * Finally, a version of the contract function bound to `this` is supplied as final parameter when verifying
 * nominal and exceptional postconditions. This function reference can be used in contracts that use recursion.
 *
 * Furthermore, an instance contains a `location` property, which is a line of text
 * that refers to the source code where the contract was created. For internal contracts, this is
 * `AbstractContract.internalLocation.
 *
 * @constructor
 */
function AbstractContract (kwargs, /* Object? */ internalLocation) {
  util.pre(() => !!kwargs)
  util.pre(() => !kwargs.pre || util.typeOf(kwargs.pre) === 'array')
  util.pre(() => !kwargs.post || util.typeOf(kwargs.post) === 'array')
  util.pre(() => !kwargs.exception || util.typeOf(kwargs.exception) === 'array')
  // MUDO util.pre(() => !internalLocation || internalLocation === AbstractContract.internalLocation)

  const self = this

  function abstract () { throw new AbstractContract.AbstractError(self) }

  const location = internalLocation || util.firstLocationOutsideLibrary()
  AbstractContract.bless(abstract, self, abstract, location)
  util.setAndFreezeProperty(self, '_pre', Object.freeze(kwargs.pre ? kwargs.pre.slice() : []))
  util.setAndFreezeProperty(self, '_post', Object.freeze(kwargs.post ? kwargs.post.slice() : []))
  util.setAndFreezeProperty(self, '_exception', Object.freeze(kwargs.exception ? kwargs.exception.slice() : []))
  util.setAndFreezeProperty(self, 'location', Object.freeze(location))
  util.setAndFreezeProperty(self, 'abstract', abstract)
}

/**
 * Object to be used as location for contracts and implementations that are generated inside this library.
 */
AbstractContract.internalLocation = Object.freeze({
  toString: function () { return 'INTERNAL' }
})

AbstractContract.prototype = {
  constructor: AbstractContract,
  _pre: null,
  _post: null,
  _exception: null,
  isImplementedBy: function (f) {
    return AbstractContract.isAGeneralContractFunction(f) && f.contract === this
  }
}
util.defineFrozenReadOnlyArrayProperty(AbstractContract.prototype, 'pre', '_pre')
util.defineFrozenReadOnlyArrayProperty(AbstractContract.prototype, 'post', '_post')
util.defineFrozenReadOnlyArrayProperty(AbstractContract.prototype, 'exception', '_exception')
util.setAndFreezeProperty(AbstractContract.prototype, 'location', AbstractContract.internalLocation)
util.setAndFreezeProperty(AbstractContract.prototype, 'abstract', null)

AbstractContract.displayNamePrefix = displayNamePrefix

/**
 * Return a string that is a sensible display name for the given function as a contract function.
 */
AbstractContract.contractFunctionDisplayName = function (f) {
  util.pre(() => util.typeOf(f) === 'function')

  return displayNamePrefix +
         (f.name ||
            (f.implementation && (f.implementation.displayName || f.implementation.name)) ||
            '<<anonymous>>')
}

/**
 * This function is intended to be used as the bind function of contract functions. It makes sure
 * that, when applied to a contract function, the result
 * [is also a contract function]{@linkplain AbstractContract#isAContractFunction}.
 * The bind aspect of the functionality is the same as {@link Function#prototype#bind}.
 * The implementation of the resulting contract function is also bound in the same
 * way as the resulting contract function itself.
 */
AbstractContract.bindContractFunction = function bind () {
  util.pre(() => AbstractContract.isAGeneralContractFunction(this))

  const bound = Function.prototype.bind.apply(this, arguments)
  const boundImplementation = Function.prototype.bind.apply(this.implementation, arguments)
  AbstractContract.bless(bound, this.contract, boundImplementation, this.location)
  return bound
}

/**
 * A General Contract Function is an implementation of an AbstractContract. This function verifies whether a function
 * given as a parameter is a General Contract Function.
 *
 * To be a General Contract Function, the subject must
 *

 *
be a function,

 *
have a frozen `contract` property that refers to a AbstractContract,

 *
have a frozen `implementation` property that refers to a function (which realizes the contract),

 *
have a frozen `location` property, that has a value,

 *
have a frozen `bind` property, which is {@link AbstractContract.bindContractFunction}, and

 *
have a `displayName` that is a contract function display name, which is a string that gives
 *     information for a programmer to understand which contract function this is, and

 *
if the `implementation` function has a `prototype`, have a `prototype` property,
 *

 *
that is an object,

 *
that has a `constructor` property that is the contract function, and

 *
that has `f.implementation.prototype` in its prototype chain, or is equal to it.
 *

 *

 */
AbstractContract.isAGeneralContractFunction = function (f) {
  // Apart from this, we expect f to have a name. But it is controlled by the JavaScript engine, and we cannot
  // freeze it, and not guaranteed in all engines.
  return util.typeOf(f) === 'function' &&
         f.contract instanceof AbstractContract &&
         util.isFrozenOwnProperty(f, 'contract') &&
         util.typeOf(f.implementation) === 'function' &&
         util.isFrozenOwnProperty(f, 'implementation') &&
         util.isFrozenOwnProperty(f, 'location') &&
         f.location &&
         f.displayName === AbstractContract.contractFunctionDisplayName(f) &&
         util.isFrozenOwnProperty(f, 'bind') &&
         f.bind === AbstractContract.bindContractFunction &&
         (!f.implementation.hasOwnProperty('prototype') ||
             (util.typeOf(f.prototype) === 'object' &&
                 f.prototype.constructor === f &&
                 (f.prototype === f.implementation.prototype || f.prototype instanceof f.implementation)))
}

/**
 * A Contract Function is an implementation of a Contract. This function verifies whether a function
 * given as a parameter is a Contract Function.
 *
 * To be a Contract Function, the subject must
 *

 *
be a [general contract function]{@linkplain #isAGeneralContractFunction()},

 *
have a frozen `location` property, which is a string that represents a location in source code,
 *     outside this library.

 *

 */
AbstractContract.isAContractFunction = function (f) {
  return AbstractContract.isAGeneralContractFunction(f) && util.isALocationOutsideLibrary(f.location)
}

/**
 * Helper function that transforms any function given as contractFunction
 * into a [contract function]{@linkplain AbstractContract#isAContractFunction}
 * for the given parameters.
 * If {@code implFunction.prototype} exists, the {@code contractFunction.prototype} is changed to
 * an object that refers to {@code contractFunction} as {@code contractFunction.prototype.constructor},
 * is otherwise empty, and has {@code implFunction.prototype} as prototype.
 *
 * @param contractFunction {Function} the regular {Function} to be transformed into a contract function
 * @param contract {AbstractContract} the contract contractFunction is a realisation of
 * @param implFunction {Function} the function that is used in contractFunction
 *                     to realize the postconditions of contract under its preconditions
 * @param location {String} the location outside this library that the resulting
 *                          [contract function]{@linkplain AbstractContract#isAContractFunction} will carry,
 *                          that says where it is defined.
 */
AbstractContract.bless = function bless (contractFunction, contract, implFunction, location) {
  util.pre(() => util.typeOf(contractFunction) === 'function')
  // noinspection JSUnresolvedVariable
  util.pre(() => !contractFunction.contract)
  // noinspection JSUnresolvedVariable
  util.pre(() => !contractFunction.implementation)
  // noinspection JSUnresolvedVariable
  util.pre(() => !contractFunction.location)
  util.pre(() => contractFunction.bind === Function.prototype.bind)
  util.pre(() => contract instanceof AbstractContract)
  util.pre(() => util.typeOf(implFunction) === 'function')
  // MUDO util.pre(() => location === AbstractContract.internalLocation || util.isALocationOutsideLibrary(location))

  util.setAndFreezeProperty(contractFunction, 'contract', contract)
  util.setAndFreezeProperty(contractFunction, 'implementation', implFunction)
  util.setAndFreezeProperty(contractFunction, 'location', location)
  util.setAndFreezeProperty(contractFunction, 'bind', AbstractContract.bindContractFunction)
  if (implFunction.hasOwnProperty('prototype')) {
    contractFunction.prototype = Object.create(implFunction.prototype)
    util.setAndFreezeProperty(contractFunction.prototype, 'constructor', contractFunction)
  }
  util.defineFrozenDerivedProperty(
    contractFunction,
    'displayName',
    function () { return AbstractContract.contractFunctionDisplayName(this) }
  )
}

/**
 * Function that always returns false.
 */
AbstractContract.falseCondition = function () { return false }

/**
 * Singleton array of {@linkplain AbstractContract#falseCondition}. Can be used the clearly signal
 * that a function should never throw exceptions, or never end nominally, or should never be called,
 * because the preconditions will always fail.
 */
AbstractContract.mustNotHappen = [AbstractContract.falseCondition]

/**
 * The most general function AbstractContract. This has the most strict preconditions (nothing is allowed), which can
 * be weakened by specializations, and the most general nominal and exceptional postconditions (anything goes),
 * which can be strengthened by specializations.
 */
AbstractContract.root = new AbstractContract(
  {
    pre: AbstractContract.mustNotHappen,
    post: [],
    exception: []
  },
  AbstractContract.internalLocation
)

const message = 'an abstract function cannot be executed'

/**
 * Thrown when an abstract method is called. You shouldn't.
 *
 * @constructor
 */
function AbstractError (contract) {
  util.pre(() => contract instanceof AbstractContract)

  ContractError.call(this)
  util.setAndFreezeProperty(this, 'name', AbstractError.name)
  util.setAndFreezeProperty(this, 'message', message)
  util.setAndFreezeProperty(this, 'contract', contract)
}

AbstractError.prototype = new ContractError()
AbstractError.prototype.constructor = AbstractError
util.setAndFreezeProperty(AbstractError.prototype, 'name', AbstractError.name)
util.setAndFreezeProperty(AbstractError.prototype, 'message', message)
util.setAndFreezeProperty(AbstractError.prototype, 'contract', null)

AbstractError.message = message

AbstractContract.AbstractError = AbstractError

module.exports = AbstractContract
<|endoftext|>