<commit_before>/**
 * @fileOverview Magic Search JS
 * @requires AngularJS
 *
 */

// Allow the module to be pre-defined with additional dependencies
try{
    angular.module('MagicSearch');
} catch (exception) {
    angular.module('MagicSearch', []);
}

angular.module('MagicSearch')
    .directive('magicSearch', function($compile) {
        return {
            restrict: 'E',
            scope: {
                facets_param: '@facets',
                filter_keys: '=filterKeys',
                strings: '=strings'
            },
            templateUrl: function (scope, elem) {
                return elem.template;
            },
            controller: function ($scope, $element, $timeout) {
                var searchInput = $element.find('.search-input');
                $scope.promptString = $scope.strings.prompt;
                $scope.currentSearch = [];
                $scope.initSearch = function() {
                    if (typeof $scope.facets_param === 'string') {
                        // Parse facets JSON and convert to a list of facets.
                        var tmp = $scope.facets_param.replace(/__apos__/g, "\'").replace(/__dquote__/g, '\\"').replace(/__bslash__/g, "\\");
                        $scope.facetsObj = JSON.parse(tmp);
                    }
                    else {
                        // Assume this is a usable javascript object
                        $scope.facetsObj = $scope.facets_param;
                    }
                    $scope.facetsSave = $scope.copyFacets($scope.facetsObj);
                    $scope.initFacets();
                };
                $scope.initFacets = function() {
                    // set facets selected and remove them from facetsObj
                    var initialFacets = window.location.search;
                    if (initialFacets.indexOf('?') === 0) {
                        initialFacets = initialFacets.slice(1);
                    }
                    initialFacets = initialFacets.split('&');
                    if (initialFacets.length > 1 || initialFacets[0].length > 0) {
                        $timeout(function() {
                            $scope.strings.prompt = '';
                        });
                    }
                    angular.forEach(initialFacets, function(facet, idx) {
                        var facetParts = facet.split('=');
                        angular.forEach($scope.facetsObj, function(value, idx) {
                            if (value.name == facetParts[0]) {
                                if (value.options === undefined) {
                                    $scope.currentSearch.push({'name':facet, 'label':[value.label, facetParts[1]]});
                                    // allow free-form facets to remain
                                }
                                else {
                                    angular.forEach(value.options, function(option, idx) {
                                        if (option.key == facetParts[1]) {
                                            $scope.currentSearch.push({'name':facet, 'label':[value.label, option.label]});
                                            if (value.singleton === true) {
                                                $scope.deleteFacetEntirely(facetParts);
                                            }
                                            else {
                                                $scope.deleteFacetSelection(facetParts);
                                            }
                                        }
                                    });
                                }
                            }
                        });
                    });
                    if ($scope.textSearch !== undefined) {
                        $scope.currentSearch.push({'name':'text='+$scope.textSearch, 'label':[$scope.strings.text, $scope.textSearch]});
                    }
                    $scope.filteredObj = $scope.facetsObj;
                };
                $scope.addFacets = function(facets) {
                    // add a facets javascript object to the existing list
                    for (var facet in facets) {
                        $scope.facetsObj.append(facet);
                    }
                };
                $scope.copyFacets = function(facets) {
                    var ret = [];
                    for (var i=0; i<facets.length; i++) {
                        var facet = Object.create(facets[i]);
                        if (facets[i].options !== undefined) {
                            facet.options = [];
                            for (var j=0; j<facets[i].options.length; j++) {
                                facet.options.push(Object.create(facets[i].options[j]));
                            }
                        }
                        ret.push(facet);
                    }
                    return ret;
                };
                // removes a facet from the menu
                $scope.deleteFacetSelection = function(facetParts) {
                    angular.forEach($scope.facetsObj.slice(), function(facet, idx) {
                        if (facet.name == facetParts[0]) {
                            if (facet.options === undefined) {
                                return;  // allow free-form facets to remain
                            }
                            for (var i=0; i<facet.options.length; i++) {
                                var option = facet.options[i];
                                if (option.key == facetParts[1]) {
                                    $scope.facetsObj[idx].options.splice($scope.facetsObj[idx].options.indexOf(option), 1);
                                }
                            }
                            if (facet.options.length === 0) {
                                $scope.facetsObj.splice($scope.facetsObj.indexOf(facet), 1);
                            }
                        }
                    });
                };
                $scope.deleteFacetEntirely = function(facetParts) {
                    // remove entire facet
                    angular.forEach($scope.facetsObj.slice(), function(facet, idx) {
                        if (facet.name == facetParts[0]) {
                            $scope.facetsObj.splice($scope.facetsObj.indexOf(facet), 1);
                        }
                    });
                };
                searchInput.on('keydown', function($event) {
                    var key = $event.keyCode || $event.charCode;
                    if (key == 9) {  // prevent default when we can.
                        $event.preventDefault();
                    }
                });
                searchInput.on('keyup', function($event) {  // handle ctrl-char input
                    if ($event.metaKey === true) {
                        return;
                    }
                    var searchVal = searchInput.val();
                    var key = $event.keyCode || $event.charCode;
                    if (key == 9) {  // tab, so select facet if narrowed down to 1
                        if ($scope.facetSelected === undefined) {
                            if ($scope.filteredObj.length != 1) return;
                            $scope.facetClicked(0, '', $scope.filteredObj[0].name);
                        }
                        else {
                            if ($scope.filteredOptions === undefined || $scope.filteredOptions.length != 1) return;
                            $scope.optionClicked(0, '', $scope.filteredOptions[0].key);
                            $scope.resetState();
                        }
                        $timeout(function() {
                            searchInput.val('');
                        });
                        return;
                    }
                    if (key == 27) {  // esc, so cancel and reset everthing
                        $timeout(function() {
                            $scope.hideMenu();
                            searchInput.val('');
                        });
                        $scope.resetState();
                        var textFilter = $scope.textSearch;
                        if (textFilter === undefined) {
                            textFilter = '';
                        }
                        $scope.$emit('textSearch', textFilter, $scope.filter_keys);
                        return;
                    }
                    if (key == 13) {  // enter, so accept value
                        // if tag search, treat as regular facet
                        if ($scope.facetSelected && $scope.facetSelected.options === undefined) {
                            var curr = $scope.facetSelected;
                            curr.name = curr.name + '=' + searchVal;
                            curr.label[1] = searchVal;
                            $scope.currentSearch.push(curr);
                            $scope.resetState();
                            $scope.emitQuery();
                            $scope.showMenu();
                        }
                        // if text search treat as search
                        else {
                            for (i=0; i<$scope.currentSearch.length; i++) {
                                if ($scope.currentSearch[i].name.indexOf('text') === 0) {
                                    $scope.currentSearch.splice(i, 1);
                                }
                            }
                            $scope.currentSearch.push({'name':'text='+searchVal, 'label':[$scope.strings.text, searchVal]});
                            $scope.$apply();
                            $scope.hideMenu();
                            searchInput.val('');
                            $scope.$emit('textSearch', searchVal, $scope.filter_keys);
                            $scope.textSearch = searchVal;
                        }
                        $scope.filteredObj = $scope.facetsObj;
                    }
                    else {
                        if (searchVal === '') {
                            $scope.filteredObj = $scope.facetsObj;
                            $scope.$apply();
                            $scope.$emit('textSearch', '', $scope.filter_keys);
                            if ($scope.facetSelected && $scope.facetSelected.options === undefined) {
                                $scope.resetState();
                            }
                        }
                        else {
                            $scope.filterFacets(searchVal);
                        }
                    }
                });
                searchInput.on('keypress', function($event) {  // handle character input
                    var searchVal = searchInput.val();
                    var key = $event.which || $event.keyCode || $event.charCode;
                    if (key != 8 && key != 46 && key != 13 && key != 9 && key != 27) {
                        searchVal = searchVal + String.fromCharCode(key).toLowerCase();
                    }
                    if (searchVal == ' ') {  // space and field is empty, show menu
                        $scope.showMenu();
                        $timeout(function() {
                            searchInput.val('');
                        });
                        return;
                    }
                    if (searchVal === '') {
                        $scope.filteredObj = $scope.facetsObj;
                        $scope.$apply();
                        $scope.$emit('textSearch', '', $scope.filter_keys);
                        if ($scope.facetSelected && $scope.facetSelected.options === undefined) {
                            $scope.resetState();
                        }
                        return;
                    }
                    if (key != 8 && key != 46) {
                        $scope.filterFacets(searchVal);
                    }
                });
                $scope.filterFacets = function(searchVal) {
                    // try filtering facets/options.. if no facets match, do text search
                    var i, idx, label;
                    var filtered = [];
                    if ($scope.facetSelected === undefined) {
                        $scope.filteredObj = $scope.facetsObj;
                        for (i=0; i<$scope.filteredObj.length; i++) {
                            var facet = $scope.filteredObj[i];
                            idx = facet.label.toLowerCase().indexOf(searchVal);
                            if (idx > -1) {
                                label = [facet.label.substring(0, idx), facet.label.substring(idx, idx + searchVal.length), facet.label.substring(idx + searchVal.length)];
                                filtered.push({'name':facet.name, 'label':label, 'options':facet.options});
                            }
                        }
                        if (filtered.length > 0) {
                            $scope.showMenu();
                            $timeout(function() {
                                $scope.filteredObj = filtered;
                            }, 0.1);
                        }
                        else {
                            $scope.$emit('textSearch', searchVal, $scope.filter_keys);
                            $scope.hideMenu();
                        }
                    }
                    else {  // assume option search
                        $scope.filteredOptions = $scope.facetOptions;
                        if ($scope.facetOptions === undefined) { // no options, assume free form text facet
                            return;
                        }
                        for (i=0; i<$scope.filteredOptions.length; i++) {
                            var option = $scope.filteredOptions[i];
                            idx = option.label.toLowerCase().indexOf(searchVal);
                            if (idx > -1) {
                                label = [option.label.substring(0, idx), option.label.substring(idx, idx + searchVal.length), option.label.substring(idx + searchVal.length)];
                                filtered.push({'key':option.key, 'label':label});
                            }
                        }
                        if (filtered.length > 0) {
                            $scope.showMenu();
                            $timeout(function() {
                                $scope.filteredOptions = filtered;
                            }, 0.1);
                        }
                    }
                };
                // enable text entry when mouse clicked anywhere in search box
                $element.find('.search-main-area').on('click', function($event) {
                    var target = $($event.target);
                    if (target.is('.search-main-area')) {
                        searchInput.trigger('focus');
                        $scope.showMenu();
                    }
                });
                // when facet clicked, add 1st part of facet and set up options
                $scope.facetClicked = function($index, $event, name) {
                    $scope.hideMenu();
                    var facet = $scope.filteredObj[$index];
                    var label = facet.label;
                    if (Array.isArray(label)) {
                        label = label.join('');
                    }
                    $scope.facetSelected = {'name':facet.name, 'label':[label, '']};
                    if (facet.options !== undefined) {
                        $scope.filteredOptions = $scope.facetOptions = facet.options;
                        $scope.showMenu();
                    }
                    $timeout(function() {
                        searchInput.val('');
                    });
                    $scope.strings.prompt = '';
                    $timeout(function() {
                        searchInput.focus();
                    });
                };
                // when option clicked, complete facet and send event
                $scope.optionClicked = function($index, $event, name) {
                    $scope.hideMenu();
                    var curr = $scope.facetSelected;
                    curr.name = curr.name + '=' + name;
                    curr.label[1] = $scope.filteredOptions[$index].label;
                    if (Array.isArray(curr.label[1])) {
                        curr.label[1] = curr.label[1].join('');
                    }
                    $scope.currentSearch.push(curr);
                    $scope.resetState();
                    $scope.emitQuery();
                    $scope.showMenu();
                };
                // send event with new query string
                $scope.emitQuery = function(removed) {
                    var query = '';
                    for (var i=0; i<$scope.currentSearch.length; i++) {
                        if ($scope.currentSearch[i].name.indexOf('text') !== 0) {
                            if (query.length > 0) query = query + "&";
                            query = query + $scope.currentSearch[i].name;
                        }
                    }
                    if (removed !== undefined && removed.indexOf('text') === 0) {
                        $scope.$emit('textSearch', '', $scope.filter_keys);
                        $scope.textSearch = undefined;
                    }
                    else {
                        $scope.$emit('searchUpdated', query);
                        if ($scope.currentSearch.length > 0) {
                            // prune facets as needed from menus
                            var newFacet = $scope.currentSearch[$scope.currentSearch.length-1].name;
                            var facetParts = newFacet.split('=');
                            angular.forEach($scope.facetsSave, function(facet, idx) {
                                if (facet.name == facetParts[0]) {
                                    if (facet.singleton === true) {
                                        $scope.deleteFacetEntirely(facetParts);
                                    }
                                    else {
                                        $scope.deleteFacetSelection(facetParts);
                                    }
                                }
                            });
                        }
                    }
                };
                // remove facet and either update filter or search
                $scope.removeFacet = function($index, $event) {
                    var removed = $scope.currentSearch[$index].name;
                    $scope.currentSearch.splice($index, 1);
                    if ($scope.facetSelected === undefined) {
                        $scope.emitQuery(removed);
                    }
                    else {
                        $scope.resetState();
                        searchInput.val('');
                    }
                    if ($scope.currentSearch.length === 0) {
                        $scope.strings.prompt = $scope.promptString;
                    }
                    // re-init to restore facets cleanly
                    $scope.facetsObj = $scope.copyFacets($scope.facetsSave);
                    $scope.currentSearch = [];
                    $scope.initFacets();
                };
                // clear entire searchbar
                $scope.clearSearch = function() {
                    if ($scope.currentSearch.length > 0) {
                        $scope.currentSearch = [];
                        $scope.facetsObj = $scope.copyFacets($scope.facetsSave);
                        $scope.resetState();
                        $scope.$emit('searchUpdated', '');
                        $scope.$emit('textSearch', '', $scope.filter_keys);
                    }
                };
                $scope.isMatchLabel = function(label) {
                    return Array.isArray(label);
                };
                $scope.resetState = function() {
                    searchInput.val('');
                    $scope.filteredObj = $scope.facetsObj;
                    $scope.facetSelected = undefined;
                    $scope.facetOptions = undefined;
                    $scope.filteredOptions = undefined;
                    if ($scope.currentSearch.length === 0) {
                        $scope.strings.prompt = $scope.promptString;
                    }
                };
                // showMenu and hideMenu depend on foundation's dropdown. They need
                // to be modified to work with another dropdown implemenation (i.e. bootstrap)
                $scope.showMenu = function() {
                    $timeout(function() {
                        if ($('#facet-drop').hasClass('open') === false && $scope.filteredObj.length > 0) {
                            $('.search-input').trigger('click');
                        }
                    });
                };
                $scope.hideMenu = function() {
                    $(document).foundation('dropdown', 'closeall');
                };
                $scope.initSearch();
            }
        };
    })
;
<commit_msg>use $location to get parameters from url<commit_after>/**
 * @fileOverview Magic Search JS
 * @requires AngularJS
 *
 */

// Allow the module to be pre-defined with additional dependencies
try{
    angular.module('MagicSearch');
} catch (exception) {
    angular.module('MagicSearch', []);
}

angular.module('MagicSearch')
    .directive('magicSearch', function($compile) {
        return {
            restrict: 'E',
            scope: {
                facets_param: '@facets',
                filter_keys: '=filterKeys',
                strings: '=strings'
            },
            templateUrl: function (scope, elem) {
                return elem.template;
            },
            controller: function ($scope, $element, $timeout, $location) {
                var searchInput = $element.find('.search-input');
                $scope.promptString = $scope.strings.prompt;
                $scope.currentSearch = [];
                $scope.initSearch = function() {
                    if (typeof $scope.facets_param === 'string') {
                        // Parse facets JSON and convert to a list of facets.
                        var tmp = $scope.facets_param.replace(/__apos__/g, "\'").replace(/__dquote__/g, '\\"').replace(/__bslash__/g, "\\");
                        $scope.facetsObj = JSON.parse(tmp);
                    }
                    else {
                        // Assume this is a usable javascript object
                        $scope.facetsObj = $scope.facets_param;
                    }
                    $scope.facetsSave = $scope.copyFacets($scope.facetsObj);
                    $scope.initFacets();
                };
                $scope.initFacets = function() {
                    // set facets selected and remove them from facetsObj
                    var initialFacets = $location.search();
                    if (Object.keys(initialFacets).length > 0) {
                        $timeout(function() {
                            $scope.strings.prompt = '';
                        });
                    }
                    angular.forEach(Object.keys(initialFacets), function(facetName, idx) {
                        var facetValue = initialFacets[facetName];
                        angular.forEach($scope.facetsObj, function(value, idx) {
                            if (value.name == facetName) {
                                if (value.options === undefined) {
                                    $scope.currentSearch.push({'name':facetName, 'label':[value.label, facetValue]});
                                    // allow free-form facets to remain
                                }
                                else {
                                    angular.forEach(value.options, function(option, idx) {
                                        if (option.key == facetValue) {
                                            $scope.currentSearch.push({'name':facetName, 'label':[value.label, option.label]});
                                            if (value.singleton === true) {
                                                $scope.deleteFacetEntirely([facetName, facetValue]);
                                            }
                                            else {
                                                $scope.deleteFacetSelection([facetName, facetValue]);
                                            }
                                        }
                                    });
                                }
                            }
                        });
                    });
                    if ($scope.textSearch !== undefined) {
                        $scope.currentSearch.push({'name':'text='+$scope.textSearch, 'label':[$scope.strings.text, $scope.textSearch]});
                    }
                    $scope.filteredObj = $scope.facetsObj;
                };
                $scope.addFacets = function(facets) {
                    // add a facets javascript object to the existing list
                    for (var facet in facets) {
                        $scope.facetsObj.append(facet);
                    }
                };
                $scope.copyFacets = function(facets) {
                    var ret = [];
                    for (var i=0; i<facets.length; i++) {
                        var facet = Object.create(facets[i]);
                        if (facets[i].options !== undefined) {
                            facet.options = [];
                            for (var j=0; j<facets[i].options.length; j++) {
                                facet.options.push(Object.create(facets[i].options[j]));
                            }
                        }
                        ret.push(facet);
                    }
                    return ret;
                };
                // removes a facet from the menu
                $scope.deleteFacetSelection = function(facetParts) {
                    angular.forEach($scope.facetsObj.slice(), function(facet, idx) {
                        if (facet.name == facetParts[0]) {
                            if (facet.options === undefined) {
                                return;  // allow free-form facets to remain
                            }
                            for (var i=0; i<facet.options.length; i++) {
                                var option = facet.options[i];
                                if (option.key == facetParts[1]) {
                                    $scope.facetsObj[idx].options.splice($scope.facetsObj[idx].options.indexOf(option), 1);
                                }
                            }
                            if (facet.options.length === 0) {
                                $scope.facetsObj.splice($scope.facetsObj.indexOf(facet), 1);
                            }
                        }
                    });
                };
                $scope.deleteFacetEntirely = function(facetParts) {
                    // remove entire facet
                    angular.forEach($scope.facetsObj.slice(), function(facet, idx) {
                        if (facet.name == facetParts[0]) {
                            $scope.facetsObj.splice($scope.facetsObj.indexOf(facet), 1);
                        }
                    });
                };
                searchInput.on('keydown', function($event) {
                    var key = $event.keyCode || $event.charCode;
                    if (key == 9) {  // prevent default when we can.
                        $event.preventDefault();
                    }
                });
                searchInput.on('keyup', function($event) {  // handle ctrl-char input
                    if ($event.metaKey === true) {
                        return;
                    }
                    var searchVal = searchInput.val();
                    var key = $event.keyCode || $event.charCode;
                    if (key == 9) {  // tab, so select facet if narrowed down to 1
                        if ($scope.facetSelected === undefined) {
                            if ($scope.filteredObj.length != 1) return;
                            $scope.facetClicked(0, '', $scope.filteredObj[0].name);
                        }
                        else {
                            if ($scope.filteredOptions === undefined || $scope.filteredOptions.length != 1) return;
                            $scope.optionClicked(0, '', $scope.filteredOptions[0].key);
                            $scope.resetState();
                        }
                        $timeout(function() {
                            searchInput.val('');
                        });
                        return;
                    }
                    if (key == 27) {  // esc, so cancel and reset everthing
                        $timeout(function() {
                            $scope.hideMenu();
                            searchInput.val('');
                        });
                        $scope.resetState();
                        var textFilter = $scope.textSearch;
                        if (textFilter === undefined) {
                            textFilter = '';
                        }
                        $scope.$emit('textSearch', textFilter, $scope.filter_keys);
                        return;
                    }
                    if (key == 13) {  // enter, so accept value
                        // if tag search, treat as regular facet
                        if ($scope.facetSelected && $scope.facetSelected.options === undefined) {
                            var curr = $scope.facetSelected;
                            curr.name = curr.name + '=' + searchVal;
                            curr.label[1] = searchVal;
                            $scope.currentSearch.push(curr);
                            $scope.resetState();
                            $scope.emitQuery();
                            $scope.showMenu();
                        }
                        // if text search treat as search
                        else {
                            for (i=0; i<$scope.currentSearch.length; i++) {
                                if ($scope.currentSearch[i].name.indexOf('text') === 0) {
                                    $scope.currentSearch.splice(i, 1);
                                }
                            }
                            $scope.currentSearch.push({'name':'text='+searchVal, 'label':[$scope.strings.text, searchVal]});
                            $scope.$apply();
                            $scope.hideMenu();
                            searchInput.val('');
                            $scope.$emit('textSearch', searchVal, $scope.filter_keys);
                            $scope.textSearch = searchVal;
                        }
                        $scope.filteredObj = $scope.facetsObj;
                    }
                    else {
                        if (searchVal === '') {
                            $scope.filteredObj = $scope.facetsObj;
                            $scope.$apply();
                            $scope.$emit('textSearch', '', $scope.filter_keys);
                            if ($scope.facetSelected && $scope.facetSelected.options === undefined) {
                                $scope.resetState();
                            }
                        }
                        else {
                            $scope.filterFacets(searchVal);
                        }
                    }
                });
                searchInput.on('keypress', function($event) {  // handle character input
                    var searchVal = searchInput.val();
                    var key = $event.which || $event.keyCode || $event.charCode;
                    if (key != 8 && key != 46 && key != 13 && key != 9 && key != 27) {
                        searchVal = searchVal + String.fromCharCode(key).toLowerCase();
                    }
                    if (searchVal == ' ') {  // space and field is empty, show menu
                        $scope.showMenu();
                        $timeout(function() {
                            searchInput.val('');
                        });
                        return;
                    }
                    if (searchVal === '') {
                        $scope.filteredObj = $scope.facetsObj;
                        $scope.$apply();
                        $scope.$emit('textSearch', '', $scope.filter_keys);
                        if ($scope.facetSelected && $scope.facetSelected.options === undefined) {
                            $scope.resetState();
                        }
                        return;
                    }
                    if (key != 8 && key != 46) {
                        $scope.filterFacets(searchVal);
                    }
                });
                $scope.filterFacets = function(searchVal) {
                    // try filtering facets/options.. if no facets match, do text search
                    var i, idx, label;
                    var filtered = [];
                    if ($scope.facetSelected === undefined) {
                        $scope.filteredObj = $scope.facetsObj;
                        for (i=0; i<$scope.filteredObj.length; i++) {
                            var facet = $scope.filteredObj[i];
                            idx = facet.label.toLowerCase().indexOf(searchVal);
                            if (idx > -1) {
                                label = [facet.label.substring(0, idx), facet.label.substring(idx, idx + searchVal.length), facet.label.substring(idx + searchVal.length)];
                                filtered.push({'name':facet.name, 'label':label, 'options':facet.options});
                            }
                        }
                        if (filtered.length > 0) {
                            $scope.showMenu();
                            $timeout(function() {
                                $scope.filteredObj = filtered;
                            }, 0.1);
                        }
                        else {
                            $scope.$emit('textSearch', searchVal, $scope.filter_keys);
                            $scope.hideMenu();
                        }
                    }
                    else {  // assume option search
                        $scope.filteredOptions = $scope.facetOptions;
                        if ($scope.facetOptions === undefined) { // no options, assume free form text facet
                            return;
                        }
                        for (i=0; i<$scope.filteredOptions.length; i++) {
                            var option = $scope.filteredOptions[i];
                            idx = option.label.toLowerCase().indexOf(searchVal);
                            if (idx > -1) {
                                label = [option.label.substring(0, idx), option.label.substring(idx, idx + searchVal.length), option.label.substring(idx + searchVal.length)];
                                filtered.push({'key':option.key, 'label':label});
                            }
                        }
                        if (filtered.length > 0) {
                            $scope.showMenu();
                            $timeout(function() {
                                $scope.filteredOptions = filtered;
                            }, 0.1);
                        }
                    }
                };
                // enable text entry when mouse clicked anywhere in search box
                $element.find('.search-main-area').on('click', function($event) {
                    var target = $($event.target);
                    if (target.is('.search-main-area')) {
                        searchInput.trigger('focus');
                        $scope.showMenu();
                    }
                });
                // when facet clicked, add 1st part of facet and set up options
                $scope.facetClicked = function($index, $event, name) {
                    $scope.hideMenu();
                    var facet = $scope.filteredObj[$index];
                    var label = facet.label;
                    if (Array.isArray(label)) {
                        label = label.join('');
                    }
                    $scope.facetSelected = {'name':facet.name, 'label':[label, '']};
                    if (facet.options !== undefined) {
                        $scope.filteredOptions = $scope.facetOptions = facet.options;
                        $scope.showMenu();
                    }
                    $timeout(function() {
                        searchInput.val('');
                    });
                    $scope.strings.prompt = '';
                    $timeout(function() {
                        searchInput.focus();
                    });
                };
                // when option clicked, complete facet and send event
                $scope.optionClicked = function($index, $event, name) {
                    $scope.hideMenu();
                    var curr = $scope.facetSelected;
                    curr.name = curr.name + '=' + name;
                    curr.label[1] = $scope.filteredOptions[$index].label;
                    if (Array.isArray(curr.label[1])) {
                        curr.label[1] = curr.label[1].join('');
                    }
                    $scope.currentSearch.push(curr);
                    $scope.resetState();
                    $scope.emitQuery();
                    $scope.showMenu();
                };
                // send event with new query string
                $scope.emitQuery = function(removed) {
                    var query = '';
                    for (var i=0; i<$scope.currentSearch.length; i++) {
                        if ($scope.currentSearch[i].name.indexOf('text') !== 0) {
                            if (query.length > 0) query = query + "&";
                            query = query + $scope.currentSearch[i].name;
                        }
                    }
                    if (removed !== undefined && removed.indexOf('text') === 0) {
                        $scope.$emit('textSearch', '', $scope.filter_keys);
                        $scope.textSearch = undefined;
                    }
                    else {
                        $scope.$emit('searchUpdated', query);
                        if ($scope.currentSearch.length > 0) {
                            // prune facets as needed from menus
                            var newFacet = $scope.currentSearch[$scope.currentSearch.length-1].name;
                            var facetParts = newFacet.split('=');
                            angular.forEach($scope.facetsSave, function(facet, idx) {
                                if (facet.name == facetParts[0]) {
                                    if (facet.singleton === true) {
                                        $scope.deleteFacetEntirely(facetParts);
                                    }
                                    else {
                                        $scope.deleteFacetSelection(facetParts);
                                    }
                                }
                            });
                        }
                    }
                };
                // remove facet and either update filter or search
                $scope.removeFacet = function($index, $event) {
                    var removed = $scope.currentSearch[$index].name;
                    $scope.currentSearch.splice($index, 1);
                    if ($scope.facetSelected === undefined) {
                        $scope.emitQuery(removed);
                    }
                    else {
                        $scope.resetState();
                        searchInput.val('');
                    }
                    if ($scope.currentSearch.length === 0) {
                        $scope.strings.prompt = $scope.promptString;
                    }
                    // re-init to restore facets cleanly
                    $scope.facetsObj = $scope.copyFacets($scope.facetsSave);
                    $scope.currentSearch = [];
                    $scope.initFacets();
                };
                // clear entire searchbar
                $scope.clearSearch = function() {
                    if ($scope.currentSearch.length > 0) {
                        $scope.currentSearch = [];
                        $scope.facetsObj = $scope.copyFacets($scope.facetsSave);
                        $scope.resetState();
                        $scope.$emit('searchUpdated', '');
                        $scope.$emit('textSearch', '', $scope.filter_keys);
                    }
                };
                $scope.isMatchLabel = function(label) {
                    return Array.isArray(label);
                };
                $scope.resetState = function() {
                    searchInput.val('');
                    $scope.filteredObj = $scope.facetsObj;
                    $scope.facetSelected = undefined;
                    $scope.facetOptions = undefined;
                    $scope.filteredOptions = undefined;
                    if ($scope.currentSearch.length === 0) {
                        $scope.strings.prompt = $scope.promptString;
                    }
                };
                // showMenu and hideMenu depend on foundation's dropdown. They need
                // to be modified to work with another dropdown implemenation (i.e. bootstrap)
                $scope.showMenu = function() {
                    $timeout(function() {
                        if ($('#facet-drop').hasClass('open') === false && $scope.filteredObj.length > 0) {
                            $('.search-input').trigger('click');
                        }
                    });
                };
                $scope.hideMenu = function() {
                    $(document).foundation('dropdown', 'closeall');
                };
                $scope.initSearch();
            }
        };
    })
;
<|endoftext|>


<commit_before>var vlocity = require('../vlocity');

var OmniScript = module.exports = function(vlocity) {
    this.vlocity = vlocity;
};

var OMNI_BULK_RECORDS_LIMIT_MIN = 1000;

// Returns an Object of label to value used in the field diff.
OmniScript.prototype.createTitleObjects = function(input) {
    var dataPackData = input.dataPackData;
    var titleObjects = [];

    if (dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c']) {
        dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'] = this.vlocity.datapacksexpand.sortList(dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'], '%vlocity_namespace%__OmniScript__c');
    }

    var elements = dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];

    var sourceKey = dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0].VlocityRecordSourceKey;

    var omniscriptName = `${dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Type__c']} ${dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__SubType__c']} ${dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Language__c']}`;

    var relevantFields = [ 'Name', '%vlocity_namespace%__Type__c', 'VlocityRecordSourceKey' ];

    if (elements) {

        dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'] = this.vlocity.datapacksexpand.sortList(elements, '%vlocity_namespace%__OmniScript__c');

        elements = dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];

        var elementsInOrder = [];
        var elementsAsText = '';
        var indentations = {};
        var elementByKey = {};

        for (var element of elements) {
            var elementIndentation = 1;

            var elementInTree = {};

            for (var field of relevantFields) {
                elementInTree[field] = element[field];
            }

            elementByKey[element.VlocityRecordSourceKey] = elementInTree;

            try {
                if (element['%vlocity_namespace%__ParentElementId__c'] != null) {
                    var parent = elementByKey[element['%vlocity_namespace%__ParentElementId__c'].VlocityMatchingRecordSourceKey];

                    if (!parent.childElements) {
                        parent.childElements = [];
                    }

                    elementIndentation = indentations[element['%vlocity_namespace%__ParentElementId__c'].VlocityMatchingRecordSourceKey] + 2;
                    parent.childElements.push(elementInTree)
                } else {
                    elementsInOrder.push(elementInTree);
                }

                indentations[element.VlocityRecordSourceKey] = elementIndentation;
                elementsAsText += `${' '.repeat(elementIndentation)}${element.Name} (${element['%vlocity_namespace%__Type__c']})\n`;
            } catch (e) {
                VlocityUtils.error('Error Building OmniScript Sequence', e);
            }
        }

        titleObjects.push({
            field: 'Elements',
            value: elementsAsText,
            fieldType: 'DisplayOnly',
            VlocityDataPackType: 'OmniScript',
            revertHandler: true,
            VlocityRecordSourceKey: sourceKey,
            VlocitySObjectRecordLabel: `OmniScript / ${omniscriptName} / Elements`
        });
    }

    return titleObjects;
};

OmniScript.prototype.onActivateError = async function(dataPackData) {

    VlocityUtils.verbose('Handling OmniScript Activation Directly', dataPackData.VlocityDataPackKey)
    var onActivateErrorResult = {};
    var omniScriptDataPack = dataPackData.dataPacks[0].VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0];

    if (omniScriptDataPack['%vlocity_namespace%__IsReusable__c']) {
        var searchKey = `${omniScriptDataPack['%vlocity_namespace%__Type__c']}|${omniScriptDataPack['%vlocity_namespace%__SubType__c']}|${omniScriptDataPack['%vlocity_namespace%__Language__c']}`;
        let reusableOmnis = await this.vlocity.jsForceConnection.query(`SELECT Id, ${this.vlocity.namespace}__OmniScriptId__c FROM ${this.vlocity.namespace}__Element__c WHERE ${this.vlocity.namespace}__SearchKey__c = '${searchKey}' AND ${this.vlocity.namespace}__OmniScriptId__r.${this.vlocity.namespace}__IsActive__c = true`);

        for (var i = 0; i < reusableOmnis.records.length; i++) {
            var activationResult = await this.vlocity.datapacksutils.jsRemote(`${this.vlocity.namespace}.EmbeddingBusinessProcessController`, 'activateScript', [ reusableOmnis.records[i].Id, 1], `${this.vlocity.namespace}__embeddingbusinessprocesspage`);

            if (activationResult && activationResult instanceof Array) {
                activationResult = activationResult[0];
            } else if (activationResult && typeof(activationResult) === 'string') {
                activationResult = JSON.parse(activationResult);
            }

            if (activationResult.statusCode < 400) {
                onActivateErrorResult.ActivationStatus = 'Success';
            } else {
                onActivateErrorResult.ActivationStatus = 'Error';
                onActivateErrorResult.message = activationResult.message;
                break;
            }
        }
    }

    return onActivateErrorResult;
};

OmniScript.prototype.extractAllBulkRecords = function(input) {
    var OmniScriptRecords = [];
    var datapack = input.dataPackData;
    var self = this;

    if (datapack.recordsCount < OMNI_BULK_RECORDS_LIMIT_MIN) {
        return null;
    }

    Object.keys(datapack.VlocityDataPackData).forEach(function (key) {
        if (Array.isArray(datapack.VlocityDataPackData[key])) {
            for (var OmniScriptVersion of datapack.VlocityDataPackData[key]) {
                if (typeof OmniScriptVersion === 'object') {
                    Object.keys(OmniScriptVersion).forEach(function (omniKey) {
                        if (Array.isArray(OmniScriptVersion[omniKey])) {
                            for (var OmniScriptData of OmniScriptVersion[omniKey]) {
                                var tempData = {};
                                Object.keys(OmniScriptData).forEach(function (omniData) {
                                    if (omniData === '%vlocity_namespace%__ParentElementId__c') {
                                        tempData['%vlocity_namespace%__ParentElementId__c'] = OmniScriptData['%vlocity_namespace%__ParentElementId__c']['VlocityMatchingRecordSourceKey'];
                                        //Add entry to parent-child relation map
                                        self.vlocity.relationMap[OmniScriptData['VlocityRecordSourceKey']] = tempData['%vlocity_namespace%__ParentElementId__c'];
                                    }
                                    else if ((omniData.endsWith('__c') || omniData === 'Name' || omniData === 'VlocityRecordSourceKey') && omniData !== '%vlocity_namespace%__ReusableOmniScript__c') {
                                        tempData[omniData] = OmniScriptData[omniData];
                                    }
                                });
                                OmniScriptRecords.push(tempData);
                            }
                            OmniScriptVersion[omniKey] = '';
                        }
                    });
                }

            }
        }
    });
    return OmniScriptRecords;

}

OmniScript.prototype.getUpdatedParentList = async function() {
    var self = this;
    var elementObjList = [];
    for (var key in self.vlocity.nameToSfIdMap) {
        if (key in self.vlocity.relationMap) {
            var elementObj = { Id: self.vlocity.nameToSfIdMap[key] };
            elementObj[self.vlocity.namespacePrefix + 'ParentElementId__c'] = self.vlocity.nameToSfIdMap[self.vlocity.relationMap[key]];
            elementObjList.push(elementObj);
            delete self.vlocity.relationMap[key];
        }
    }
    return elementObjList;
}

OmniScript.incrementElements = function(elements, orderToIncrementAfter) {
    elements.forEach(element => {
        if (element['%vlocity_namespace%__Order__c'] >= orderToIncrementAfter) {
            element['%vlocity_namespace%__Order__c'] = element['%vlocity_namespace%__Order__c'] + 1;
        }
    })
}

OmniScript.prototype.discardSObject = function(input) {
    var deletedObject = input.deletedObject;
    var parentObject = input.parentObject;

    OmniScript.incrementElements(parentObject['%vlocity_namespace%__Element__c'], deletedObject['%vlocity_namespace%__Order__c']);

    parentObject['%vlocity_namespace%__Element__c'].push(deletedObject);

    return true;
}

OmniScript.staticHandleRevert = async function(input) {

    VlocityUtils.error('staticHandleRevert');

    var comparisonFileSource = input.comparisonFileSource;
    var comparisonFileTarget = input.comparisonFileTarget;
    var revertRecord = input.revertRecord;
    var dataPackKey = revertRecord.VlocityDataPackKey;

    var diffString = revertRecord.gitDiff;
    var diffStringSplit = diffString.split('\n');

    var readdElements = [];
    var removeElements = [];

    var sourceDataPack = comparisonFileSource.dataPacks.find(dataPack => {
        return dataPack.VlocityDataPackKey == dataPackKey;
    });

    var targetDataPack = comparisonFileTarget.dataPacks.find(dataPack => {
        return dataPack.VlocityDataPackKey == dataPackKey;
    });

    var sourceElements = sourceDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];
    var targetElements = targetDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];

    for (var i = 2; i < diffStringSplit.length; i++) {

        var elementLine = diffStringSplit[i];

        if (elementLine) {
            var elementName = elementLine.substr(1, elementLine.indexOf('(')-1).trim();

            if (elementLine[0] == '-') {
                readdElements.push(elementName);
            } else if (elementLine[0] == '+') {
                removeElements.push(elementName);
            }
        }
    }

    removeElements = removeElements.filter(elementName => {
        return readdElements.indexOf(elementName) == -1;
    });

    for (var i = 0; i < readdElements.length; i++) {

        var sourceElementIndex = sourceElements.findIndex(ele => {
            return ele.Name == readdElements[i];
        });

        var targetElementIndex = targetElements.findIndex(ele => {
            return ele.Name == readdElements[i];
        });

        //
        OmniScript.incrementElements(sourceElements, targetElements[targetElementIndex]['%vlocity_namespace%__Order__c']);

        if (sourceElementIndex != -1) {

            //VlocityUtils.error('CHANGING' , readdElements[i], targetElements[targetElementIndex]['%vlocity_namespace%__Order__c'], sourceElements[sourceElementIndex]['%vlocity_namespace%__Order__c'])

            sourceElements[sourceElementIndex]['%vlocity_namespace%__Order__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__Order__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementId__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementId__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementName__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementName__c'];

            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementType__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementType__c'];
        } else {
            sourceElements.push(targetElements[targetElementIndex]);
        }
    }

    for (var i = 0; i < removeElements.length; i++) {
        var sourceElementIndex = sourceElements.findIndex(ele => {
            return ele.Name == removeElements[i];
        });

        var targetElementIndex = targetElements.findIndex(ele => {
            return ele.Name == removeElements[i];
        });

        if (targetElementIndex > -1) {
            sourceElements[sourceElementIndex]['%vlocity_namespace%__Order__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__Order__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementId__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementId__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementName__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementName__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementType__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementType__c'];
        } else if (sourceElementIndex > -1) {
            sourceElements[sourceElementIndex].VlocityDataPackIsIncluded = false;
        }
    }

    if (removeElements.length > 0) {
        sourceDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'] = sourceDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'].filter(element => {
            return element.VlocityDataPackIsIncluded !== false;
        })
    }

    return true;
}

<commit_msg>Fix OmniScripts not Activating Properly when reusable activation fails.<commit_after>var vlocity = require('../vlocity');

var OmniScript = module.exports = function(vlocity) {
    this.vlocity = vlocity;
};

var OMNI_BULK_RECORDS_LIMIT_MIN = 1000;

// Returns an Object of label to value used in the field diff.
OmniScript.prototype.createTitleObjects = function(input) {
    var dataPackData = input.dataPackData;
    var titleObjects = [];

    if (dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c']) {
        dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'] = this.vlocity.datapacksexpand.sortList(dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'], '%vlocity_namespace%__OmniScript__c');
    }

    var elements = dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];

    var sourceKey = dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0].VlocityRecordSourceKey;

    var omniscriptName = `${dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Type__c']} ${dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__SubType__c']} ${dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Language__c']}`;

    var relevantFields = [ 'Name', '%vlocity_namespace%__Type__c', 'VlocityRecordSourceKey' ];

    if (elements) {

        dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'] = this.vlocity.datapacksexpand.sortList(elements, '%vlocity_namespace%__OmniScript__c');

        elements = dataPackData.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];

        var elementsInOrder = [];
        var elementsAsText = '';
        var indentations = {};
        var elementByKey = {};

        for (var element of elements) {
            var elementIndentation = 1;

            var elementInTree = {};

            for (var field of relevantFields) {
                elementInTree[field] = element[field];
            }

            elementByKey[element.VlocityRecordSourceKey] = elementInTree;

            try {
                if (element['%vlocity_namespace%__ParentElementId__c'] != null) {
                    var parent = elementByKey[element['%vlocity_namespace%__ParentElementId__c'].VlocityMatchingRecordSourceKey];

                    if (!parent.childElements) {
                        parent.childElements = [];
                    }

                    elementIndentation = indentations[element['%vlocity_namespace%__ParentElementId__c'].VlocityMatchingRecordSourceKey] + 2;
                    parent.childElements.push(elementInTree)
                } else {
                    elementsInOrder.push(elementInTree);
                }

                indentations[element.VlocityRecordSourceKey] = elementIndentation;
                elementsAsText += `${' '.repeat(elementIndentation)}${element.Name} (${element['%vlocity_namespace%__Type__c']})\n`;
            } catch (e) {
                VlocityUtils.error('Error Building OmniScript Sequence', e);
            }
        }

        titleObjects.push({
            field: 'Elements',
            value: elementsAsText,
            fieldType: 'DisplayOnly',
            VlocityDataPackType: 'OmniScript',
            revertHandler: true,
            VlocityRecordSourceKey: sourceKey,
            VlocitySObjectRecordLabel: `OmniScript / ${omniscriptName} / Elements`
        });
    }

    return titleObjects;
};

OmniScript.prototype.onActivateError = async function(dataPackData) {

    VlocityUtils.verbose('Handling OmniScript Activation Directly', dataPackData.VlocityDataPackKey)
    var onActivateErrorResult = {};
    var omniScriptDataPack = dataPackData.dataPacks[0].VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0];
    var currentOmniScriptId = dataPackData.dataPacks[0].VlocityDataPackRecords[0].VlocityRecordSalesforceId;

    if (currentOmniScriptId && omniScriptDataPack['%vlocity_namespace%__IsReusable__c']) {

        if (!this.vlocity.sfdxUsername) {
            VlocityUtils.verbose('Skipping Individual Activation because using SFDX Authentication is required');
        } else {
            var searchKey = `${omniScriptDataPack['%vlocity_namespace%__Type__c']}|${omniScriptDataPack['%vlocity_namespace%__SubType__c']}|${omniScriptDataPack['%vlocity_namespace%__Language__c']}`;

            VlocityUtils.verbose('Handling Reusable OmniScript Activation Directly', searchKey)
            await this.vlocity.datapacksutils.jsRemote(`${this.vlocity.namespace}.EmbeddingBusinessProcessController`, 'activateScript', [ currentOmniScriptId, 1], `${this.vlocity.namespace}__embeddingbusinessprocesspage`);

            let reusableOmnis = await this.vlocity.jsForceConnection.query(`SELECT Id, ${this.vlocity.namespace}__OmniScriptId__c FROM ${this.vlocity.namespace}__Element__c WHERE ${this.vlocity.namespace}__SearchKey__c = '${searchKey}' AND ${this.vlocity.namespace}__OmniScriptId__r.${this.vlocity.namespace}__IsActive__c = true`);

            for (var i = 0; i < reusableOmnis.records.length; i++) {

                var activationResult = await this.vlocity.datapacksutils.jsRemote(`${this.vlocity.namespace}.EmbeddingBusinessProcessController`, 'activateScript', [ reusableOmnis.records[i][`${this.vlocity.namespace}__OmniScriptId__c`], 1], `${this.vlocity.namespace}__embeddingbusinessprocesspage`);

                if (activationResult && activationResult instanceof Array) {
                    activationResult = activationResult[0];
                } else if (activationResult && typeof(activationResult) === 'string') {
                    activationResult = JSON.parse(activationResult);
                }

                if (activationResult.statusCode < 400) {
                    onActivateErrorResult.ActivationStatus = 'Success';
                } else {
                    onActivateErrorResult.ActivationStatus = 'Error';
                    onActivateErrorResult.message = activationResult.message;
                    break;
                }
            }

            return onActivateErrorResult;
        }
    }

    return null;
};

OmniScript.prototype.extractAllBulkRecords = function(input) {
    var OmniScriptRecords = [];
    var datapack = input.dataPackData;
    var self = this;

    if (datapack.recordsCount < OMNI_BULK_RECORDS_LIMIT_MIN) {
        return null;
    }

    Object.keys(datapack.VlocityDataPackData).forEach(function (key) {
        if (Array.isArray(datapack.VlocityDataPackData[key])) {
            for (var OmniScriptVersion of datapack.VlocityDataPackData[key]) {
                if (typeof OmniScriptVersion === 'object') {
                    Object.keys(OmniScriptVersion).forEach(function (omniKey) {
                        if (Array.isArray(OmniScriptVersion[omniKey])) {
                            for (var OmniScriptData of OmniScriptVersion[omniKey]) {
                                var tempData = {};
                                Object.keys(OmniScriptData).forEach(function (omniData) {
                                    if (omniData === '%vlocity_namespace%__ParentElementId__c') {
                                        tempData['%vlocity_namespace%__ParentElementId__c'] = OmniScriptData['%vlocity_namespace%__ParentElementId__c']['VlocityMatchingRecordSourceKey'];
                                        //Add entry to parent-child relation map
                                        self.vlocity.relationMap[OmniScriptData['VlocityRecordSourceKey']] = tempData['%vlocity_namespace%__ParentElementId__c'];
                                    }
                                    else if ((omniData.endsWith('__c') || omniData === 'Name' || omniData === 'VlocityRecordSourceKey') && omniData !== '%vlocity_namespace%__ReusableOmniScript__c') {
                                        tempData[omniData] = OmniScriptData[omniData];
                                    }
                                });
                                OmniScriptRecords.push(tempData);
                            }
                            OmniScriptVersion[omniKey] = '';
                        }
                    });
                }

            }
        }
    });
    return OmniScriptRecords;

}

OmniScript.prototype.getUpdatedParentList = async function() {
    var self = this;
    var elementObjList = [];
    for (var key in self.vlocity.nameToSfIdMap) {
        if (key in self.vlocity.relationMap) {
            var elementObj = { Id: self.vlocity.nameToSfIdMap[key] };
            elementObj[self.vlocity.namespacePrefix + 'ParentElementId__c'] = self.vlocity.nameToSfIdMap[self.vlocity.relationMap[key]];
            elementObjList.push(elementObj);
            delete self.vlocity.relationMap[key];
        }
    }
    return elementObjList;
}

OmniScript.incrementElements = function(elements, orderToIncrementAfter) {
    elements.forEach(element => {
        if (element['%vlocity_namespace%__Order__c'] >= orderToIncrementAfter) {
            element['%vlocity_namespace%__Order__c'] = element['%vlocity_namespace%__Order__c'] + 1;
        }
    })
}

OmniScript.prototype.discardSObject = function(input) {
    var deletedObject = input.deletedObject;
    var parentObject = input.parentObject;

    OmniScript.incrementElements(parentObject['%vlocity_namespace%__Element__c'], deletedObject['%vlocity_namespace%__Order__c']);

    parentObject['%vlocity_namespace%__Element__c'].push(deletedObject);

    return true;
}

OmniScript.staticHandleRevert = async function(input) {

    VlocityUtils.error('staticHandleRevert');

    var comparisonFileSource = input.comparisonFileSource;
    var comparisonFileTarget = input.comparisonFileTarget;
    var revertRecord = input.revertRecord;
    var dataPackKey = revertRecord.VlocityDataPackKey;

    var diffString = revertRecord.gitDiff;
    var diffStringSplit = diffString.split('\n');

    var readdElements = [];
    var removeElements = [];

    var sourceDataPack = comparisonFileSource.dataPacks.find(dataPack => {
        return dataPack.VlocityDataPackKey == dataPackKey;
    });

    var targetDataPack = comparisonFileTarget.dataPacks.find(dataPack => {
        return dataPack.VlocityDataPackKey == dataPackKey;
    });

    var sourceElements = sourceDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];
    var targetElements = targetDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'];

    for (var i = 2; i < diffStringSplit.length; i++) {

        var elementLine = diffStringSplit[i];

        if (elementLine) {
            var elementName = elementLine.substr(1, elementLine.indexOf('(')-1).trim();

            if (elementLine[0] == '-') {
                readdElements.push(elementName);
            } else if (elementLine[0] == '+') {
                removeElements.push(elementName);
            }
        }
    }

    removeElements = removeElements.filter(elementName => {
        return readdElements.indexOf(elementName) == -1;
    });

    for (var i = 0; i < readdElements.length; i++) {

        var sourceElementIndex = sourceElements.findIndex(ele => {
            return ele.Name == readdElements[i];
        });

        var targetElementIndex = targetElements.findIndex(ele => {
            return ele.Name == readdElements[i];
        });

        //
        OmniScript.incrementElements(sourceElements, targetElements[targetElementIndex]['%vlocity_namespace%__Order__c']);

        if (sourceElementIndex != -1) {

            //VlocityUtils.error('CHANGING' , readdElements[i], targetElements[targetElementIndex]['%vlocity_namespace%__Order__c'], sourceElements[sourceElementIndex]['%vlocity_namespace%__Order__c'])

            sourceElements[sourceElementIndex]['%vlocity_namespace%__Order__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__Order__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementId__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementId__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementName__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementName__c'];

            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementType__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementType__c'];
        } else {
            sourceElements.push(targetElements[targetElementIndex]);
        }
    }

    for (var i = 0; i < removeElements.length; i++) {
        var sourceElementIndex = sourceElements.findIndex(ele => {
            return ele.Name == removeElements[i];
        });

        var targetElementIndex = targetElements.findIndex(ele => {
            return ele.Name == removeElements[i];
        });

        if (targetElementIndex > -1) {
            sourceElements[sourceElementIndex]['%vlocity_namespace%__Order__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__Order__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementId__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementId__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementName__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementName__c'];
            sourceElements[sourceElementIndex]['%vlocity_namespace%__ParentElementType__c'] = targetElements[targetElementIndex]['%vlocity_namespace%__ParentElementType__c'];
        } else if (sourceElementIndex > -1) {
            sourceElements[sourceElementIndex].VlocityDataPackIsIncluded = false;
        }
    }

    if (removeElements.length > 0) {
        sourceDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'] = sourceDataPack.VlocityDataPackData['%vlocity_namespace%__OmniScript__c'][0]['%vlocity_namespace%__Element__c'].filter(element => {
            return element.VlocityDataPackIsIncluded !== false;
        })
    }

    return true;
}

<|endoftext|>


<commit_before>import util from 'util';
import {Environment, Network, RecordSource, Store} from 'relay-runtime';
import moment from 'moment';

const LODASH_ISEQUAL = 'lodash.isequal';
let isEqual = null;

const relayEnvironmentPerURL = new Map();
const tokenPerURL = new Map();
const fetchPerURL = new Map();

const responsesByQuery = new Map();

function logRatelimitApi(headers) {
  const remaining = headers.get('x-ratelimit-remaining');
  const total = headers.get('x-ratelimit-limit');
  const resets = headers.get('x-ratelimit-reset');
  const resetsIn = moment.unix(parseInt(resets, 10)).from();

  // eslint-disable-next-line no-console
  console.debug(`GitHub API Rate Limit: ${remaining}/${total} — resets ${resetsIn}`);
}

export function expectRelayQuery(operationPattern, response) {
  let resolve, reject;
  const handler = typeof response === 'function' ? response : () => ({data: response});

  const promise = new Promise((resolve0, reject0) => {
    resolve = resolve0;
    reject = reject0;
  });

  const existing = responsesByQuery.get(operationPattern.name) || [];
  existing.push({
    promise,
    handler,
    variables: operationPattern.variables || {},
    trace: operationPattern.trace,
  });
  responsesByQuery.set(operationPattern.name, existing);

  const disable = () => responsesByQuery.delete(operationPattern.name);

  return {promise, resolve, reject, disable};
}

export function clearRelayExpectations() {
  responsesByQuery.clear();
}

function createFetchQuery(url) {
  if (atom.inSpecMode()) {
    return function specFetchQuery(operation, variables, _cacheConfig, _uploadables) {
      const expectations = responsesByQuery.get(operation.name) || [];
      const match = expectations.find(expectation => {
        if (isEqual === null) {
          // Lazily require lodash.isequal so we can keep it as a dev dependency.
          // Require indirectly to trick electron-link into not following this.
          isEqual = require(LODASH_ISEQUAL);
        }

        return isEqual(expectation.variables, variables);
      });

      if (!match) {
        // eslint-disable-next-line no-console
        console.log(
          `GraphQL query ${operation.name} was:\n  ${operation.text.replace(/\n/g, '\n  ')}\n` +
          util.inspect(variables),
        );

        const e = new Error(`Unexpected GraphQL query: ${operation.name}`);
        e.rawStack = e.stack;
        throw e;
      }

      const responsePromise = match.promise.then(() => {
        return match.handler(operation);
      });

      if (match.trace) {
        // eslint-disable-next-line no-console
        console.log(`[Relay] query "${operation.name}":\n${operation.text}`);
        responsePromise.then(result => {
          // eslint-disable-next-line no-console
          console.log(`[Relay] response "${operation.name}":`, result);
        }, err => {
          // eslint-disable-next-line no-console
          console.error(`[Relay] error "${operation.name}":\n${err.stack || err}`);
          throw err;
        });
      }

      return responsePromise;
    };
  }

  return async function fetchQuery(operation, variables, _cacheConfig, _uploadables) {
    const currentToken = tokenPerURL.get(url);

    let response;
    try {
      response = await fetch(url, {
        method: 'POST',
        headers: {
          'content-type': 'application/json',
          'Authorization': `bearer ${currentToken}`,
          'Accept': 'application/vnd.github.antiope-preview+json',
        },
        body: JSON.stringify({
          query: operation.text,
          variables,
        }),
      });
    } catch (e) {
      // A network error was encountered. Mark it so that QueryErrorView and ErrorView can distinguish these, because
      // the errors from "fetch" are TypeErrors without much information.
      e.network = true;
      e.rawStack = e.stack;
      throw e;
    }

    try {
      atom && atom.inDevMode() && logRatelimitApi(response.headers);
    } catch (_e) { /* do nothing */ }

    if (response.status !== 200) {
      const e = new Error(`GraphQL API endpoint at ${url} returned ${response.status}`);
      e.response = response;
      e.responseText = await response.text();
      e.rawStack = e.stack;
      throw e;
    }

    const payload = await response.json();

    if (payload && payload.errors && payload.errors.length > 0) {
      const e = new Error(`GraphQL API endpoint at ${url} returned an error for query ${operation.name}.`);
      e.response = response;
      e.errors = payload.errors;
      e.rawStack = e.stack;
      throw e;
    }

    return payload;
  };
}

export default class RelayNetworkLayerManager {
  static getEnvironmentForHost(endpoint, token) {
    const url = endpoint.getGraphQLRoot();
    let {environment, network} = relayEnvironmentPerURL.get(url) || {};
    tokenPerURL.set(url, token);
    if (!environment) {
      const source = new RecordSource();
      const store = new Store(source);
      network = Network.create(this.getFetchQuery(endpoint, token));
      environment = new Environment({network, store});

      relayEnvironmentPerURL.set(url, {environment, network});
    }
    return environment;
  }

  static getFetchQuery(endpoint, token) {
    const url = endpoint.getGraphQLRoot();
    tokenPerURL.set(url, token);
    let fetch = fetchPerURL.get(url);
    if (!fetch) {
      fetch = createFetchQuery(url);
      fetchPerURL.set(fetch);
    }
    return fetch;
  }
}
<commit_msg>Allow RelayNetworkLayerManager to be called without a token<commit_after>import util from 'util';
import {Environment, Network, RecordSource, Store} from 'relay-runtime';
import moment from 'moment';

const LODASH_ISEQUAL = 'lodash.isequal';
let isEqual = null;

const relayEnvironmentPerURL = new Map();
const tokenPerURL = new Map();
const fetchPerURL = new Map();

const responsesByQuery = new Map();

function logRatelimitApi(headers) {
  const remaining = headers.get('x-ratelimit-remaining');
  const total = headers.get('x-ratelimit-limit');
  const resets = headers.get('x-ratelimit-reset');
  const resetsIn = moment.unix(parseInt(resets, 10)).from();

  // eslint-disable-next-line no-console
  console.debug(`GitHub API Rate Limit: ${remaining}/${total} — resets ${resetsIn}`);
}

export function expectRelayQuery(operationPattern, response) {
  let resolve, reject;
  const handler = typeof response === 'function' ? response : () => ({data: response});

  const promise = new Promise((resolve0, reject0) => {
    resolve = resolve0;
    reject = reject0;
  });

  const existing = responsesByQuery.get(operationPattern.name) || [];
  existing.push({
    promise,
    handler,
    variables: operationPattern.variables || {},
    trace: operationPattern.trace,
  });
  responsesByQuery.set(operationPattern.name, existing);

  const disable = () => responsesByQuery.delete(operationPattern.name);

  return {promise, resolve, reject, disable};
}

export function clearRelayExpectations() {
  responsesByQuery.clear();
}

function createFetchQuery(url) {
  if (atom.inSpecMode()) {
    return function specFetchQuery(operation, variables, _cacheConfig, _uploadables) {
      const expectations = responsesByQuery.get(operation.name) || [];
      const match = expectations.find(expectation => {
        if (isEqual === null) {
          // Lazily require lodash.isequal so we can keep it as a dev dependency.
          // Require indirectly to trick electron-link into not following this.
          isEqual = require(LODASH_ISEQUAL);
        }

        return isEqual(expectation.variables, variables);
      });

      if (!match) {
        // eslint-disable-next-line no-console
        console.log(
          `GraphQL query ${operation.name} was:\n  ${operation.text.replace(/\n/g, '\n  ')}\n` +
          util.inspect(variables),
        );

        const e = new Error(`Unexpected GraphQL query: ${operation.name}`);
        e.rawStack = e.stack;
        throw e;
      }

      const responsePromise = match.promise.then(() => {
        return match.handler(operation);
      });

      if (match.trace) {
        // eslint-disable-next-line no-console
        console.log(`[Relay] query "${operation.name}":\n${operation.text}`);
        responsePromise.then(result => {
          // eslint-disable-next-line no-console
          console.log(`[Relay] response "${operation.name}":`, result);
        }, err => {
          // eslint-disable-next-line no-console
          console.error(`[Relay] error "${operation.name}":\n${err.stack || err}`);
          throw err;
        });
      }

      return responsePromise;
    };
  }

  return async function fetchQuery(operation, variables, _cacheConfig, _uploadables) {
    const currentToken = tokenPerURL.get(url);

    let response;
    try {
      response = await fetch(url, {
        method: 'POST',
        headers: {
          'content-type': 'application/json',
          'Authorization': `bearer ${currentToken}`,
          'Accept': 'application/vnd.github.antiope-preview+json',
        },
        body: JSON.stringify({
          query: operation.text,
          variables,
        }),
      });
    } catch (e) {
      // A network error was encountered. Mark it so that QueryErrorView and ErrorView can distinguish these, because
      // the errors from "fetch" are TypeErrors without much information.
      e.network = true;
      e.rawStack = e.stack;
      throw e;
    }

    try {
      atom && atom.inDevMode() && logRatelimitApi(response.headers);
    } catch (_e) { /* do nothing */ }

    if (response.status !== 200) {
      const e = new Error(`GraphQL API endpoint at ${url} returned ${response.status}`);
      e.response = response;
      e.responseText = await response.text();
      e.rawStack = e.stack;
      throw e;
    }

    const payload = await response.json();

    if (payload && payload.errors && payload.errors.length > 0) {
      const e = new Error(`GraphQL API endpoint at ${url} returned an error for query ${operation.name}.`);
      e.response = response;
      e.errors = payload.errors;
      e.rawStack = e.stack;
      throw e;
    }

    return payload;
  };
}

export default class RelayNetworkLayerManager {
  static getEnvironmentForHost(endpoint, token) {
    const url = endpoint.getGraphQLRoot();
    let {environment, network} = relayEnvironmentPerURL.get(url) || {};
    tokenPerURL.set(url, token);
    if (!environment) {
      if (!token) {
        throw new Error(`You must authenticate to ${endpoint.getHost()} first.`);
      }

      const source = new RecordSource();
      const store = new Store(source);
      network = Network.create(this.getFetchQuery(endpoint, token));
      environment = new Environment({network, store});

      relayEnvironmentPerURL.set(url, {environment, network});
    }
    return environment;
  }

  static getFetchQuery(endpoint, token) {
    const url = endpoint.getGraphQLRoot();
    tokenPerURL.set(url, token);
    let fetch = fetchPerURL.get(url);
    if (!fetch) {
      fetch = createFetchQuery(url);
      fetchPerURL.set(fetch);
    }
    return fetch;
  }
}
<|endoftext|>


<commit_before>fn.extend({

  append(content) {
    this[0].appendChild(cash(content)[0]);
    return this;
  },

  appendTo(content) {
    cash(content)[0].appendChild(this[0]);
    return this;
  },

  clone() {
    return cash(this[0].cloneNode(true));
  },

  empty() {
    this.each(v => v.innerHTML = '');
    return this;
  },

  html(content) {
    var source;

    if (content === 'undefined') {
      return this[0].innerHTML;
    } else {
      source = typeof content === 'object' ? cash(content)[0].outerHTML : content;
      this.each(v => v.innerHTML = `${source}`);
      return this;
    }
  },

  insertAfter(selector) {
    cash(selector)[0].insertAdjacentHTML('afterend', this[0].outerHTML);
    return this;
  },

  insertBefore(selector) {
    cash(selector)[0].insertAdjacentHTML('beforebegin', this[0].outerHTML);
    return this;
  },

  prepend(selector) {
    cash(this)[0].insertAdjacentHTML('afterBegin', cash(selector)[0].outerHTML);
    return this;
  },

  prependTo(selector) {
    cash(selector)[0].insertAdjacentHTML('afterBegin', this[0].outerHTML);
    return this;
  },

  remove() {
    this.each(v => v.parentNode.removeChild(v));
  },

  text(content) {
    if (!content) {
      return this[0].textContent;
    } else {
      this.each(v => v.textContent = content);
      return this;
    }
  }

});
<commit_msg>This is embarassing<commit_after>fn.extend({

  append(content) {
    this[0].appendChild(cash(content)[0]);
    return this;
  },

  appendTo(content) {
    cash(content)[0].appendChild(this[0]);
    return this;
  },

  clone() {
    return cash(this[0].cloneNode(true));
  },

  empty() {
    this.each(v => v.innerHTML = '');
    return this;
  },

  html(content) {
    var source;

    if (typeof content === 'undefined') {
      return this[0].innerHTML;
    } else {
      source = typeof content === 'object' ? cash(content)[0].outerHTML : content;
      this.each(v => v.innerHTML = `${source}`);
      return this;
    }
  },

  insertAfter(selector) {
    cash(selector)[0].insertAdjacentHTML('afterend', this[0].outerHTML);
    return this;
  },

  insertBefore(selector) {
    cash(selector)[0].insertAdjacentHTML('beforebegin', this[0].outerHTML);
    return this;
  },

  prepend(selector) {
    cash(this)[0].insertAdjacentHTML('afterBegin', cash(selector)[0].outerHTML);
    return this;
  },

  prependTo(selector) {
    cash(selector)[0].insertAdjacentHTML('afterBegin', this[0].outerHTML);
    return this;
  },

  remove() {
    this.each(v => v.parentNode.removeChild(v));
  },

  text(content) {
    if (!content) {
      return this[0].textContent;
    } else {
      this.each(v => v.textContent = content);
      return this;
    }
  }

});
<|endoftext|>


<commit_before>'use strict'

var util = require('util')
var _ = require('lodash')
var inflection = require('inflection')
var customDefaults = null

function getDefaults () {
  return _.defaults(customDefaults || {}, {
    prefix: '/api',
    version: '/v1',
    idProperty: '_id',
    findOneAndUpdate: true,
    findOneAndRemove: true,
    lean: true,
    lowercase: false,
    plural: true,
    restify: false,
    runValidators: false,
    preMiddleware: [],
    private: [],
    protected: [],
    postCreate: [],
    postRead: [],
    postUpdate: [],
    postDelete: []
  })
}

var restify = function (app, model, opts) {
  var options = {}
  _.assign(options, getDefaults(), opts || {})

  var access = require('./middleware/access')
  var ensureContentType = require('./middleware/ensureContentType')(options)
  var onError = require('./middleware/onError')
  var outputFn = require('./middleware/outputFn')
  var prepareQuery = require('./middleware/prepareQuery')(options)
  var prepareOutput = require('./middleware/prepareOutput')(options)

  model.schema.eachPath(function (name, path) {
    if (path.options.access) {
      switch (path.options.access.toLowerCase()) {
        case 'private':
          options.private.push(name)
          break
        case 'protected':
          options.protected.push(name)
          break
      }
    }
  })

  if (!_.isArray(options.preMiddleware)) {
    options.preMiddleware = [options.preMiddleware]
  }

  if (options.access) {
    options.preMiddleware.push(access(options))
  }

  if (!options.contextFilter) {
    options.contextFilter = function (model, req, done) {
      done(model)
    }
  }

  if (!_.isArray(options.postCreate)) {
    options.postCreate = [options.postCreate]
  }

  if (!_.isArray(options.postRead)) {
    options.postRead = [options.postRead]
  }

  if (!_.isArray(options.postUpdate)) {
    options.postUpdate = [options.postUpdate]
  }

  if (!_.isArray(options.postDelete)) {
    options.postDelete = [options.postDelete]
  }

  if (!options.onError) {
    options.onError = onError(!options.restify)
  }

  if (!options.outputFn) {
    options.outputFn = outputFn(!options.restify)
  }

  options.name = options.name || model.modelName

  if (options.plural) {
    options.name = inflection.pluralize(options.name)
  }

  if (options.lowercase) {
    options.name = options.name.toLowerCase()
  }

  var ops = require('./operations')(model, options)

  var uri_item = util.format('%s%s/%s', options.prefix, options.version, options.name)
  if (uri_item.indexOf('/:id') === -1) {
    uri_item += '/:id'
  }

  var uri_items = uri_item.replace('/:id', '')
  var uri_count = uri_items + '/count'
  var uri_shallow = uri_item + '/shallow'

  if (undefined === app.delete) {
    app.delete = app.del
  }

  app.get(uri_items, prepareQuery, options.preMiddleware, ops.getItems, prepareOutput)
  app.get(uri_count, prepareQuery, options.preMiddleware, ops.getCount, prepareOutput)
  app.get(uri_item, prepareQuery, options.preMiddleware, ops.getItem, prepareOutput)
  app.get(uri_shallow, prepareQuery, options.preMiddleware, ops.getShallow, prepareOutput)

  app.post(uri_items, ensureContentType, options.preMiddleware, ops.createObject, prepareOutput)
  app.post(uri_item, ensureContentType, options.preMiddleware, ops.modifyObject, prepareOutput)

  app.put(uri_item, ensureContentType, options.preMiddleware, ops.modifyObject, prepareOutput)

  app.delete(uri_items, options.preMiddleware, ops.deleteItems, prepareOutput)
  app.delete(uri_item, options.preMiddleware, ops.deleteItem, prepareOutput)

  return uri_items
}

module.exports = {
  defaults: function (options) {
    customDefaults = options
  },
  serve: restify
}
<commit_msg>refactor: throw when private/protected are not an array<commit_after>'use strict'

var util = require('util')
var _ = require('lodash')
var inflection = require('inflection')
var customDefaults = null

function getDefaults () {
  return _.defaults(customDefaults || {}, {
    prefix: '/api',
    version: '/v1',
    idProperty: '_id',
    findOneAndUpdate: true,
    findOneAndRemove: true,
    lean: true,
    lowercase: false,
    plural: true,
    restify: false,
    runValidators: false,
    preMiddleware: [],
    private: [],
    protected: [],
    postCreate: [],
    postRead: [],
    postUpdate: [],
    postDelete: []
  })
}

var restify = function (app, model, opts) {
  var options = {}
  _.assign(options, getDefaults(), opts || {})

  var access = require('./middleware/access')
  var ensureContentType = require('./middleware/ensureContentType')(options)
  var onError = require('./middleware/onError')
  var outputFn = require('./middleware/outputFn')
  var prepareQuery = require('./middleware/prepareQuery')(options)
  var prepareOutput = require('./middleware/prepareOutput')(options)

  if (!_.isArray(options.private)) {
    throw new Error('"options.private" must be an array of fields')
  }

  if (!_.isArray(options.protected)) {
    throw new Error('"options.protected" must be an array of fields')
  }

  model.schema.eachPath(function (name, path) {
    if (path.options.access) {
      switch (path.options.access.toLowerCase()) {
        case 'private':
          options.private.push(name)
          break
        case 'protected':
          options.protected.push(name)
          break
      }
    }
  })

  if (!_.isArray(options.preMiddleware)) {
    options.preMiddleware = [options.preMiddleware]
  }

  if (options.access) {
    options.preMiddleware.push(access(options))
  }

  if (!options.contextFilter) {
    options.contextFilter = function (model, req, done) {
      done(model)
    }
  }

  if (!_.isArray(options.postCreate)) {
    options.postCreate = [options.postCreate]
  }

  if (!_.isArray(options.postRead)) {
    options.postRead = [options.postRead]
  }

  if (!_.isArray(options.postUpdate)) {
    options.postUpdate = [options.postUpdate]
  }

  if (!_.isArray(options.postDelete)) {
    options.postDelete = [options.postDelete]
  }

  if (!options.onError) {
    options.onError = onError(!options.restify)
  }

  if (!options.outputFn) {
    options.outputFn = outputFn(!options.restify)
  }

  options.name = options.name || model.modelName

  if (options.plural) {
    options.name = inflection.pluralize(options.name)
  }

  if (options.lowercase) {
    options.name = options.name.toLowerCase()
  }

  var ops = require('./operations')(model, options)

  var uri_item = util.format('%s%s/%s', options.prefix, options.version, options.name)
  if (uri_item.indexOf('/:id') === -1) {
    uri_item += '/:id'
  }

  var uri_items = uri_item.replace('/:id', '')
  var uri_count = uri_items + '/count'
  var uri_shallow = uri_item + '/shallow'

  if (undefined === app.delete) {
    app.delete = app.del
  }

  app.get(uri_items, prepareQuery, options.preMiddleware, ops.getItems, prepareOutput)
  app.get(uri_count, prepareQuery, options.preMiddleware, ops.getCount, prepareOutput)
  app.get(uri_item, prepareQuery, options.preMiddleware, ops.getItem, prepareOutput)
  app.get(uri_shallow, prepareQuery, options.preMiddleware, ops.getShallow, prepareOutput)

  app.post(uri_items, ensureContentType, options.preMiddleware, ops.createObject, prepareOutput)
  app.post(uri_item, ensureContentType, options.preMiddleware, ops.modifyObject, prepareOutput)

  app.put(uri_item, ensureContentType, options.preMiddleware, ops.modifyObject, prepareOutput)

  app.delete(uri_items, options.preMiddleware, ops.deleteItems, prepareOutput)
  app.delete(uri_item, options.preMiddleware, ops.deleteItem, prepareOutput)

  return uri_items
}

module.exports = {
  defaults: function (options) {
    customDefaults = options
  },
  serve: restify
}
<|endoftext|>