<commit_before>/* 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';

angular.module('flowableModeler')
    .controller('FormBuilderController', ['$rootScope', '$scope', '$translate', '$http', '$timeout', '$location', '$modal', '$routeParams', '$popover', 'FormBuilderService',
        function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal, $routeParams, $popover, FormBuilderService) {

            // Main page (needed for visual indicator of current page)
            $rootScope.setMainPageById('forms');

            // Needs to be on root scope to be available in the toolbar controller
            $rootScope.formBuilder = {activeTab: 'design'};

            $scope.model = {
                useOutcomes: false
            };

            $rootScope.currentReadonlyFields = {fields: {}};

            $scope.$watch('model.useOutcomes', function (value) {
                if (value) {
                    if (!$rootScope.currentOutcomes || $rootScope.currentOutcomes.length === 0) {
                        $rootScope.currentOutcomes = [{name: ''}];
                    }
                } else {
                    $rootScope.currentOutcomes = [];
                }
            });

            // tabs for tab-control. NOT using templates for the tabs, we control the view
            // ourselves, based on the active tab binding
            $scope.tabs = [
                {
                    id: 'design',
                    title: 'FORM-BUILDER.TITLE.DESIGN'
                },
                {
                    id: 'outcome',
                    title: 'FORM-BUILDER.TITLE.OUTCOME'
                }
            ];

            $scope.form = {
            	name: '',
            	key: '',
            	description: '',
            	version: 1
           	};

            $scope.formElements = [];
            $rootScope.currentOutcomes = [];

            $rootScope.formChanges = false;

            var guidSequence = 0;

            function setFieldDragDropAttributes (field, prefix) {
                if (!field._guid) {
                    field._guid = prefix + guidSequence++;
                }

                if (!field._width) {
                    field._width = 1;
                }
            }

            var lastDropArrayTarget = null;

            $scope.onFieldMoved = function (field, fieldArraySource) {

            };

            $scope.onFieldDrop = function (paletteElementOrField, dropArrayTarget, event, index) {

				// Is it an existing object?
                if (paletteElementOrField.hasOwnProperty('_guid')) {

                    lastDropArrayTarget = dropArrayTarget;

                    if (dropArrayTarget) {
                        var i = -1;
                        dropArrayTarget.forEach(function (f, index) {
                            if (paletteElementOrField._guid == f._guid) {
                                i = index;
                            }
                        });
                        if (i != -1) {
                            dropArrayTarget.splice(i, 1);
                        }
                    }

                    if (navigator.appVersion.indexOf("MSIE 9") != -1) {
                        // update _guid which is what ngRepeat is tracking by to force dom updates
                        paletteElementOrField._guid += '_';
                    }
                    return paletteElementOrField;
                }

                lastDropArrayTarget = null;

                var fieldId = paletteElementOrField.type;
                var fieldType;

                if (fieldId === 'radio-buttons' || fieldId === 'dropdown') {
                    fieldType = 'OptionFormField';

                } else if (fieldId === 'expression') {
                    fieldType = 'ExpressionFormField';
                }

                var field = {
                    type: fieldId,
                    fieldType: fieldType,
                    name: 'Label',
                    required: false,
                    readOnly: false
                };
                setFieldDragDropAttributes(field, 'newField');

                if (fieldId === 'radio-buttons') {
                    field.options = [{
                    	name: $translate.instant('FORM-BUILDER.COMPONENT.RADIO-BUTTON-DEFAULT')
                    }];
                }

                if (fieldId === 'dropdown') {
                    field.options = [
                        {name: $translate.instant('FORM-BUILDER.COMPONENT.DROPDOWN-DEFAULT-EMPTY-SELECTION')}
                    ];
                    field.value = field.options[0];
                    field.hasEmptyValue = true;
                }

                return field;
            };

            if ($routeParams.modelId) {

                var url;
                if ($routeParams.modelHistoryId) {
                    url = FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models/' + $routeParams.modelId + '/history/' + $routeParams.modelHistoryId;
                } else {
                    url = FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models/' + $routeParams.modelId;
                }

                $http({method: 'GET', url: url}).
                    success(function (response, status, headers, config) {
                        if (response.formDefinition.fields) {

                            for (var i = 0; i < response.formDefinition.fields.length; i++) {
                                var field = response.formDefinition.fields[i];
                                if (!field.params) {
                                    field.params = {};
                                }
                                setFieldDragDropAttributes(field, 'savedField');
                            }

                            $scope.formElements = response.formDefinition.fields;
                        } else {
                            $scope.formElements = [];
                        }
                        if (response.formDefinition.outcomes) {
                            $rootScope.currentOutcomes = response.formDefinition.outcomes;
                            if ($rootScope.currentOutcomes.length > 0) {
                                $scope.model.useOutcomes = true;
                            }
                        } else {
                            $rootScope.currentOutcomes = [];
                        }
                        $rootScope.currentForm = response;
                        delete $rootScope.currentForm.formDefinition;

                        $rootScope.formItems = $scope.formElements;

                        $rootScope.formChanges = false;
                        $timeout(function () {
                            // Flip switch in timeout to start watching all form-related models
                            // after next digest cycle, to prevent first false-positive
                            $scope.formLoaded = true;
                        }, 200);

                    }).
                    error(function (response, status, headers, config) {
                        $scope.model.loading = false;
                    });
            } else {
                $scope.formLoaded = true;
            }

            $scope.palletteElements = [
                {'type': 'text', 'title': $translate.instant('FORM-BUILDER.PALLETTE.TEXT'), 'icon': 'images/form-builder/textfield-icon.png', 'width': 1},
                {'type': 'multi-line-text', 'title': $translate.instant('FORM-BUILDER.PALLETTE.MULTILINE-TEXT'), 'icon': 'images/form-builder/multi-line-textfield-icon.png', 'width': 1},
                {'type': 'integer', 'title': $translate.instant('FORM-BUILDER.PALLETTE.NUMBER'), 'icon': 'images/form-builder/numberfield-icon.png', 'width': 1},
                {'type': 'decimal', 'title': $translate.instant('FORM-BUILDER.PALLETTE.DECIMAL'), 'icon': 'images/form-builder/decimalfield-icon.png', 'width': 1},
                {'type': 'boolean', 'title': $translate.instant('FORM-BUILDER.PALLETTE.CHECKBOX'), 'icon': 'images/form-builder/booleanfield-icon.png', 'width': 1},
                {'type': 'date', 'title': $translate.instant('FORM-BUILDER.PALLETTE.DATE'), 'icon': 'images/form-builder/datefield-icon.png', 'width': 1},
                {'type': 'dropdown', 'title': $translate.instant('FORM-BUILDER.PALLETTE.DROPDOWN'), 'icon': 'images/form-builder/dropdownfield-icon.png', 'width': 1},
                {'type': 'radio-buttons', 'title': $translate.instant('FORM-BUILDER.PALLETTE.RADIO'), 'icon': 'images/form-builder/choicefield-icon.png', 'width': 1},
                {'type': 'people', 'title': $translate.instant('FORM-BUILDER.PALLETTE.PEOPLE'), 'icon': 'images/form-builder/peoplefield-icon.png', 'width': 1},
                {'type': 'functional-group', 'title': $translate.instant('FORM-BUILDER.PALLETTE.GROUP-OF-PEOPLE'), 'icon': 'images/form-builder/peoplefield-icon.png', 'width': 1},
                {'type': 'upload', 'title': $translate.instant('FORM-BUILDER.PALLETTE.UPLOAD'), 'icon': 'images/form-builder/uploadfield-icon.png', 'width': 1},
                {'type': 'expression', 'title': $translate.instant('FORM-BUILDER.PALLETTE.EXPRESSION'), 'icon': 'images/form-builder/readonly-icon.png', 'width': 1}
            ];

            $scope.$watch('formItems', function () {
                if ($scope.formLoaded) {
                    $rootScope.formChanges = true;
                }
            }, true);

            $scope.$watch('currentOutcomes', function () {
                if ($scope.formLoaded) {
                    $rootScope.formChanges = true;
                }
            }, true);

            $scope.addOutcome = function () {
                $rootScope.currentOutcomes[$rootScope.currentOutcomes.length] = {name: ''};
            };

            $scope.removeOutcome = function (index) {
                $rootScope.currentOutcomes.splice(index, 1);
            };

            $scope.$on('$locationChangeStart', function (event, next, current) {
                if (!$rootScope.ignoreChanges && $rootScope.formChanges) {
                    // Always prevent location from changing. We'll use a popup to determine the action we want to take
                    event.preventDefault();

                    var discardCallback = function () {
                        $rootScope.ignoreChanges = true;
                        $location.url(next.substring(next.indexOf('/#') + 2));
                    };

                    var continueEditingCallback = function () {
                        // Don't change the location and make sure "main navigation" is still correct
                        $rootScope.ignoreChanges = false;
                        $rootScope.setMainPageById('forms');
                    };

                    $scope.yesNoCancel = false;
                    showDiscardPopup($scope, null, discardCallback, continueEditingCallback);

                } else {
                    // Clear marker
                    $rootScope.ignoreChanges = false;
                }

            });

            $scope.$on("formChangesEvent", function () {
                var discardCallback = function () {
                    $scope.$broadcast("discardDataEvent");
                };

                var saveDataCallback = function () {
                    $scope.$broadcast("mustSaveEvent");
                };

                var continueEditingCallback = function () {
                    $scope.$broadcast("continueEditingEvent");
                };

                $scope.yesNoCancel = true;
                showDiscardPopup($scope, saveDataCallback, discardCallback, continueEditingCallback);
            });

            function showDiscardPopup($scope, saveCallback, discardCallback, cancelCallback) {
                if (!$scope.unsavedChangesModalInstance) {

                    $scope.handleResponseFunction = function (discard) {
                        $scope.unsavedChangesModalInstance = undefined;
                        if (discard == true) {
                            if (discardCallback) {
                                discardCallback();
                            }

                        } else if (discard == false) {
                            if (saveCallback) {
                                saveCallback();
                            }
                        } else {
                            if (cancelCallback) {
                                cancelCallback();
                            }
                        }
                    };

                    _internalCreateModal({
                        template: 'editor-app/popups/unsaved-changes.html',
                        scope: $scope
                    }, $modal, $scope);
                }
            };

            // get current step and sequential steps for form field resolving
            if ($rootScope.editorHistory && $rootScope.editorHistory.length > 0) {
                $scope.stepId = $rootScope.editorHistory[0].stepId;
                $scope.allSteps = $rootScope.editorHistory[0].allSteps;
            }

        }]);
<commit_msg>Update form-builder.js<commit_after>/* 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';

angular.module('flowableModeler')
    .controller('FormBuilderController', ['$rootScope', '$scope', '$translate', '$http', '$timeout', '$location', '$modal', '$routeParams', '$popover', 'FormBuilderService',
        function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal, $routeParams, $popover, FormBuilderService) {

            // Main page (needed for visual indicator of current page)
            $rootScope.setMainPageById('forms');

            // Needs to be on root scope to be available in the toolbar controller
            $rootScope.formBuilder = {activeTab: 'design'};

            $scope.model = {
                useOutcomes: false
            };

            $rootScope.currentReadonlyFields = {fields: {}};

            $scope.$watch('model.useOutcomes', function (value) {
                if (value) {
                    if (!$rootScope.currentOutcomes || $rootScope.currentOutcomes.length === 0) {
                        $rootScope.currentOutcomes = [{name: ''}];
                    }
                } else {
                    $rootScope.currentOutcomes = [];
                }
            });

            // tabs for tab-control. NOT using templates for the tabs, we control the view
            // ourselves, based on the active tab binding
            $scope.tabs = [
                {
                    id: 'design',
                    title: 'FORM-BUILDER.TITLE.DESIGN'
                },
                {
                    id: 'outcome',
                    title: 'FORM-BUILDER.TITLE.OUTCOME'
                }
            ];

            $scope.form = {
            	name: '',
            	key: '',
            	description: '',
            	version: 1
           	};

            $scope.formElements = [];
            $rootScope.currentOutcomes = [];

            $rootScope.formChanges = false;

            var guidSequence = 0;

            function setFieldDragDropAttributes (field, prefix) {
                if (!field._guid) {
                    field._guid = prefix + guidSequence++;
                }

                if (!field._width) {
                    field._width = 1;
                }
            }

            var lastDropArrayTarget = null;

            $scope.onFieldMoved = function (field, fieldArraySource) {

            };

            $scope.onFieldDrop = function (paletteElementOrField, dropArrayTarget, event, index) {

				// Is it an existing object?
                if (paletteElementOrField.hasOwnProperty('_guid')) {

                    lastDropArrayTarget = dropArrayTarget;

                    if (dropArrayTarget) {
                        var i = -1;
                        dropArrayTarget.forEach(function (f, index) {
                            if (paletteElementOrField._guid == f._guid) {
                                i = index;
                            }
                        });
                        if (i != -1) {
                            dropArrayTarget.splice(i, 1);
                        }
                    }

                    if (navigator.appVersion.indexOf("MSIE 9") != -1) {
                        // update _guid which is what ngRepeat is tracking by to force dom updates
                        paletteElementOrField._guid += '_';
                    }
                    return paletteElementOrField;
                }

                lastDropArrayTarget = null;

                var fieldId = paletteElementOrField.type;
                var fieldType;

                if (fieldId === 'radio-buttons' || fieldId === 'dropdown') {
                    fieldType = 'OptionFormField';

                } else if (fieldId === 'expression') {
                    fieldType = 'ExpressionFormField';
                }

                var field = {
                    type: fieldId,
                    fieldType: fieldType,
                    name: 'Label',
                    required: false,
                    readOnly: false
                };
                setFieldDragDropAttributes(field, 'newField');

                if (fieldId === 'radio-buttons') {
                    field.options = [{
                    	name: $translate.instant('FORM-BUILDER.COMPONENT.RADIO-BUTTON-DEFAULT')
                    }];
                }

                if (fieldId === 'dropdown') {
                    field.options = [
                        {name: $translate.instant('FORM-BUILDER.COMPONENT.DROPDOWN-DEFAULT-EMPTY-SELECTION')}
                    ];
                    field.value = field.options[0];
                    field.hasEmptyValue = true;
                }

                return field;
            };

            if ($routeParams.modelId) {

                var url;
                if ($routeParams.modelHistoryId) {
                    url = FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models/' + $routeParams.modelId + '/history/' + $routeParams.modelHistoryId;
                } else {
                    url = FLOWABLE.CONFIG.contextRoot + '/app/rest/form-models/' + $routeParams.modelId;
                }

                $http({method: 'GET', url: url}).
                    success(function (response, status, headers, config) {
                        if (response.formDefinition.fields) {

                            for (var i = 0; i < response.formDefinition.fields.length; i++) {
                                var field = response.formDefinition.fields[i];
                                if (!field.params) {
                                    field.params = {};
                                }
                                setFieldDragDropAttributes(field, 'savedField');
                            }

                            $scope.formElements = response.formDefinition.fields;
                        } else {
                            $scope.formElements = [];
                        }
                        if (response.formDefinition.outcomes) {
                            $rootScope.currentOutcomes = response.formDefinition.outcomes;
                            if ($rootScope.currentOutcomes.length > 0) {
                                $scope.model.useOutcomes = true;
                            }
                        } else {
                            $rootScope.currentOutcomes = [];
                        }
                        $rootScope.currentForm = response;
                        delete $rootScope.currentForm.formDefinition;

                        $rootScope.formItems = $scope.formElements;

                        $rootScope.formChanges = false;
                        $timeout(function () {
                            // Flip switch in timeout to start watching all form-related models
                            // after next digest cycle, to prevent first false-positive
                            $scope.formLoaded = true;
                        }, 200);

                    }).
                    error(function (response, status, headers, config) {
                        $scope.model.loading = false;
                    });
            } else {
                $scope.formLoaded = true;
            }

            $scope.palletteElements = [
                {'type': 'text', 'title': $translate.instant('FORM-BUILDER.PALLETTE.TEXT'), 'icon': 'images/form-builder/textfield-icon.png', 'width': 1},
                {'type': 'multi-line-text', 'title': $translate.instant('FORM-BUILDER.PALLETTE.MULTILINE-TEXT'), 'icon': 'images/form-builder/multi-line-textfield-icon.png', 'width': 1},
                {'type': 'integer', 'title': $translate.instant('FORM-BUILDER.PALLETTE.NUMBER'), 'icon': 'images/form-builder/numberfield-icon.png', 'width': 1},
                {'type': 'decimal', 'title': $translate.instant('FORM-BUILDER.PALLETTE.DECIMAL'), 'icon': 'images/form-builder/decimalfield-icon.png', 'width': 1},
                {'type': 'boolean', 'title': $translate.instant('FORM-BUILDER.PALLETTE.CHECKBOX'), 'icon': 'images/form-builder/booleanfield-icon.png', 'width': 1},
                {'type': 'date', 'title': $translate.instant('FORM-BUILDER.PALLETTE.DATE'), 'icon': 'images/form-builder/datefield-icon.png', 'width': 1},
                {'type': 'dropdown', 'title': $translate.instant('FORM-BUILDER.PALLETTE.DROPDOWN'), 'icon': 'images/form-builder/dropdownfield-icon.png', 'width': 1},
                {'type': 'radio-buttons', 'title': $translate.instant('FORM-BUILDER.PALLETTE.RADIO'), 'icon': 'images/form-builder/choicefield-icon.png', 'width': 1},
                {'type': 'people', 'title': $translate.instant('FORM-BUILDER.PALLETTE.PEOPLE'), 'icon': 'images/form-builder/peoplefield-icon.png', 'width': 1},
                {'type': 'functional-group', 'title': $translate.instant('FORM-BUILDER.PALLETTE.GROUP-OF-PEOPLE'), 'icon': 'images/form-builder/peoplefield-icon.png', 'width': 1},
                {'type': 'upload', 'title': $translate.instant('FORM-BUILDER.PALLETTE.UPLOAD'), 'icon': 'images/form-builder/uploadfield-icon.png', 'width': 1},
                {'type': 'expression', 'title': $translate.instant('FORM-BUILDER.PALLETTE.EXPRESSION'), 'icon': 'images/form-builder/readonly-icon.png', 'width': 1},
		{'type': 'hyperlink', 'title': $translate.instant('FORM-BUILDER.PALLETTE.HYPERLINK'), 'icon': 'images/form-builder/hyperlink-icon.png', 'width':1}
            ];

            $scope.$watch('formItems', function () {
                if ($scope.formLoaded) {
                    $rootScope.formChanges = true;
                }
            }, true);

            $scope.$watch('currentOutcomes', function () {
                if ($scope.formLoaded) {
                    $rootScope.formChanges = true;
                }
            }, true);

            $scope.addOutcome = function () {
                $rootScope.currentOutcomes[$rootScope.currentOutcomes.length] = {name: ''};
            };

            $scope.removeOutcome = function (index) {
                $rootScope.currentOutcomes.splice(index, 1);
            };

            $scope.$on('$locationChangeStart', function (event, next, current) {
                if (!$rootScope.ignoreChanges && $rootScope.formChanges) {
                    // Always prevent location from changing. We'll use a popup to determine the action we want to take
                    event.preventDefault();

                    var discardCallback = function () {
                        $rootScope.ignoreChanges = true;
                        $location.url(next.substring(next.indexOf('/#') + 2));
                    };

                    var continueEditingCallback = function () {
                        // Don't change the location and make sure "main navigation" is still correct
                        $rootScope.ignoreChanges = false;
                        $rootScope.setMainPageById('forms');
                    };

                    $scope.yesNoCancel = false;
                    showDiscardPopup($scope, null, discardCallback, continueEditingCallback);

                } else {
                    // Clear marker
                    $rootScope.ignoreChanges = false;
                }

            });

            $scope.$on("formChangesEvent", function () {
                var discardCallback = function () {
                    $scope.$broadcast("discardDataEvent");
                };

                var saveDataCallback = function () {
                    $scope.$broadcast("mustSaveEvent");
                };

                var continueEditingCallback = function () {
                    $scope.$broadcast("continueEditingEvent");
                };

                $scope.yesNoCancel = true;
                showDiscardPopup($scope, saveDataCallback, discardCallback, continueEditingCallback);
            });

            function showDiscardPopup($scope, saveCallback, discardCallback, cancelCallback) {
                if (!$scope.unsavedChangesModalInstance) {

                    $scope.handleResponseFunction = function (discard) {
                        $scope.unsavedChangesModalInstance = undefined;
                        if (discard == true) {
                            if (discardCallback) {
                                discardCallback();
                            }

                        } else if (discard == false) {
                            if (saveCallback) {
                                saveCallback();
                            }
                        } else {
                            if (cancelCallback) {
                                cancelCallback();
                            }
                        }
                    };

                    _internalCreateModal({
                        template: 'editor-app/popups/unsaved-changes.html',
                        scope: $scope
                    }, $modal, $scope);
                }
            };

            // get current step and sequential steps for form field resolving
            if ($rootScope.editorHistory && $rootScope.editorHistory.length > 0) {
                $scope.stepId = $rootScope.editorHistory[0].stepId;
                $scope.allSteps = $rootScope.editorHistory[0].allSteps;
            }

        }]);
<|endoftext|>


<commit_before>// sources
var flickr = true;
var imgur = true;

// options
var keyword = 'landscape';
var speed = 5000;

$(document).ready(function () {

    // global vars
    var imagesArray = [];
    var globalCounter = 0;
    var renderInterval = null;
    var renderTimeout = null;
    var globalTimer = 0;
    var globalTimerRest = 0;
    var elapsedTime = 0;

    var initApp = {
        init: function () {
            this.getDom();
            this.bindEvents();
        },
        getDom: function () {
            this.getButton = $('#getButton');
            this.input = $('#input');
            this.body = $('body');
        },
        bindEvents: function () {
            this.getButton.on('click', function () {
                controller.handleImageRequest();
            });

            this.input.on('keyup', function (e) {
                if (e.keyCode == 13) {
                    controller.handleImageRequest();
                }
            });
        },
    };

    var controller = {
        input: null,
        handleImageRequest: function () {
            renderImages.reset();

            //refresh searchTerm
            this.input = $('#input');

            if (initApp.input.val() !== '') {
                keyword = initApp.input.val();
            }

            getImages.init();

            initApp.body.on('touchstart mousedown', function (e) {
                if (e.target !== initApp.body[0])
                    return;

                options.pause();
            });
            initApp.body.on('touchend mouseup', function (e) {
                if (e.target !== initApp.body[0])
                    return;

                options.pause(true);
            });

        }
    }

    var getImages = {
        init: function () {
            if (imgur)
                this.getImgur();
            if (flickr)
                this.getFlickr();
        },
        getImgur: function () {

            $.ajax({
                url: "https://api.imgur.com/3/gallery/search/?q=" + keyword,
                type: "GET",
                headers: { "Authorization": "Client-ID 742860e56a4ce17" },
                success: function (resultData) {

                    if (resultData.hasOwnProperty('data') && resultData.data.length > 0) {

                        for (var dataCounter = 0; dataCounter < resultData.data.length; dataCounter++) {

                            if (resultData.data[dataCounter].is_album === true) {

                                for (var imageCounter = 0; imageCounter < resultData.data[dataCounter].images.length; imageCounter++) {
                                    imagesArray.push(resultData.data[dataCounter].images[imageCounter].link);
                                }
                            }
                            else {
                                imagesArray.push(resultData.data[dataCounter].link);
                            }
                        }
                    }
                    else {
                        console.log('no data from imgur');
                    }
                    if (imagesArray.length > 0 && !renderInterval) {
                        renderImages.init(true);
                    }
                }
            });
        },
        getFlickr: function () {
            $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?",
            {
                tags: keyword,
                tagmode: "any",
                format: "json"
            },
            function (data) {
                for (var i = 0; i < data.items.length; i++) {
                    imagesArray.push(data.items[i].media.m.replace("_m", "_b"));
                }

                if (imagesArray.length > 0 && !renderInterval) {
                    renderImages.init(true);
                }
            });
        }
    };

    var renderImages = {
        init: function (paused) {
            var restTime = 0;

            // beeing able to init when paused
            if (paused) {
                restTime = globalTimerRest - globalTimer + elapsedTime;
                elapsedTime = restTime;
            }

            renderTimeout = setTimeout(function() {

                renderImages.render();

                renderInterval = setInterval(function () {
                    //$('#time').css('transition','');
                    //$('#time').css('width','0');
                    renderImages.render();
                }, speed);
            }, restTime > 0 ? speed-restTime : 0);
        },
        render: function () {

            $('#time').removeClass('running');
            $('body').css('background-image', "url('" + imagesArray[globalCounter] + "')");

            globalCounter++;
            globalTimer = Date.now();
            elapsedTime = 0;

            setTimeout(function() {
                $('#time').addClass('running');
            },20)

            if (imagesArray.length <= globalCounter) {
                renderImages.reset();
            }
        },
        reset: function () {
            $('imagePane1').css('background-image', "none");
            clearInterval(renderInterval);
            clearTimeout(renderTimeout);
            imagesArray = [];
            renderInterval = null;
            globalCounter = 0;
            elapsedTime = 0;

            initApp.body.off();
        }
    };

    var options = {
        pause: function (paused) {
            if (paused) {
                renderImages.init(paused);

                // TODO: time animation fixen
                //$('#time').css('transition','width ' + (speed-elapsedTime) + 'ms linear');
                //$('#time').css('width','');
                globalTimer = Date.now();
            }
            else {
                clearInterval(renderInterval);
                clearTimeout(renderTimeout);
                globalTimerRest = Date.now();
                $('#time').css('width',$('#time').css('width'));
                $('#time').css('transition','none');
            }
        }
    };

    initApp.init();
});
<commit_msg>change to timestamp system instead of timeouts<commit_after>// sources
var flickr = true;
var imgur = true;

// options
var keyword = 'landscape';

$(document).ready(function () {

    /* global vars
    var imagesArray = [];
    var globalCounter = 0;
    var renderInterval = null;
    var renderTimeout = null;
    var globalTimer = 0;
    var globalTimerRest = 0;
    var elapsedTime = 0;
    */

    var initApp = {
        init: function () {
            this.getDom();
            this.bindEvents();
        },
        getDom: function () {
            this.getButton = $('#getButton');
            this.input = $('#input');
            this.body = $('body');
        },
        bindEvents: function () {
            this.getButton.on('click', function () {
                controller.handleImageRequest();
            });

            this.input.on('keyup', function (e) {
                if (e.keyCode == 13) {
                    controller.handleImageRequest();
                }
            });
        },
    };

    var controller = {
        input: null,
        handleImageRequest: function () {
            handleDrawingImages.reset();

            //refresh searchTerm
            this.input = $('#input');

            if (initApp.input.val() !== '') {
                keyword = initApp.input.val();
            }

            getImages.init();

            initApp.body.on('touchstart mousedown', function (e) {
                if (e.target !== initApp.body[0])
                    return;

                handleDrawingImages.pause(true);
            });
            initApp.body.on('touchend mouseup', function (e) {
                if (e.target !== initApp.body[0])
                    return;

                handleDrawingImages.pause();
            });
        }
    }

    var getImages = {
        init: function () {
            if (imgur)
                this.getImgur();
            if (flickr)
                this.getFlickr();
        },
        getImgur: function () {

            $.ajax({
                url: "https://api.imgur.com/3/gallery/search/?q=" + keyword,
                type: "GET",
                headers: { "Authorization": "Client-ID 742860e56a4ce17" },
                success: function (resultData) {

                    if (resultData.hasOwnProperty('data') && resultData.data.length > 0) {

                        for (var dataCounter = 0; dataCounter < resultData.data.length; dataCounter++) {

                            if (resultData.data[dataCounter].is_album === true) {

                                for (var imageCounter = 0; imageCounter < resultData.data[dataCounter].images.length; imageCounter++) {
                                    imagesArray.push(resultData.data[dataCounter].images[imageCounter].link);
                                }
                            }
                            else {
                                imagesArray.push(resultData.data[dataCounter].link);
                            }
                        }
                    }
                    else {
                        console.log('no data from imgur');
                    }
                    if (imagesArray.length > 0 && !handleDrawingImages.targetTime) {
                        handleDrawingImages.init(true);
                    }
                }
            });
        },
        getFlickr: function () {
            $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?",
            {
                tags: keyword,
                tagmode: "any",
                format: "json"
            },
            function (data) {
                for (var i = 0; i < data.items.length; i++) {
                    imagesArray.push(data.items[i].media.m.replace("_m", "_b"));
                }

                if (imagesArray.length > 0 && !handleDrawingImages.targetTime) {
                    handleDrawingImages.init(true);
                }
            });
        }
    };

    var handleDrawingImages = {
        init: function () {
            this.varReset;
            this.targetTime = Date.now() - 1;
            this.checkInterval = setInterval(function () {
                handleDrawingImages.check();
            },10)
        },
        varReset: function (){
            this.duration = 5000;
            this.targetTime = 0;
            this.downTime = 0;
            this.pressed = false;
            this.checkInterval = false;
        },
        check: function () {
            if(!this.pressed && Date.now() > this.targetTime){
                this.render();
            }
        },
        render: function () {
            this.targetTime += this.duration;

            $('#time').removeClass('running');
            $('body').css('background-image', "url('" + imagesArray[globalCounter] + "')");

            globalCounter++;

            setTimeout(function() {
                $('#time').addClass('running');
            },20)

            if (imagesArray.length <= globalCounter) {
                handleDrawingImages.reset();
            }
        },
        reset: function () {
            $('imagePane1').css('background-image', "none");
            clearInterval(this.checkInterval);
            this.varReset();
            imagesArray = [];
            globalCounter = 0;
            initApp.body.off();
        },
        pause: function (paused) {
            if (paused) {
                this.downTime = Date.now();
                this.pressed = true;
            }
            else {
                this.targetTime += Date.now() - this.downTime;
                this.pressed = false;

                $('#time').css('width',$('#time').css('width'));
                $('#time').css('transition','none');
            }

        },
        next: function () {
            this.render();
            this.targetTime = Date.now() + this.duration;
        }
    };

    initApp.init();
});
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        // cordova.plugins.email is now available
        app.receivedEvent('deviceready');
        cordova.plugins.email.isAvailable(
          function (isAvailable) {
            if(!isAvailable) alert('Service is not available');
          }
        );
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

var $ = function(id) {
    var element = document.getElementById(id)
    return element.value === 'getChecked' ? element.checked : element.value;
}

var getPlainText = function() {
  var lf = String.fromCharCode(10);
  var body = [
    ' - User Info - ',
    lf,
    'Name: ', $('name'),
    lf,
    'Phone   : ', $('phone'),
    lf,
    'Email   : ', $('email'),
    lf + lf,
    ' - Appointment Info - ',
    lf,
    'Date    : ', $('date'),
    lf,
    'Time    : ', $('time'),
    lf,
    'Location: ', $('location'),
    lf,
    'Detail  : ', $('detail')
  ];
  return body.join('')
}

var handleSubmit = function() {
    var callback = function() { return true; }
    cordova.plugins.email.open({
      to:      ['info@angelvri.com'],
      cc:      [''],
      bcc:     [''],
      attachments: [], // file paths or base64 data streams
      subject: 'I Need An Interpreter',
      body: getPlainText(),
      isHtml: false
    }, callback);
    //TODO use the callback arg to save the user's preferred email client if possible...
    //TODO do more tests with sending HTML emails
}<commit_msg>does this fix it?<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        // cordova.plugins.email is now available
        app.receivedEvent('deviceready');
        cordova.plugins.email.isAvailable(
          function (isAvailable) {
            if(!isAvailable) alert('Service is not available');
          }
        );
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

var $ = function(id) {
    var element = document.getElementById(id)
    return element.value === 'getChecked' ? element.checked : element.value;
}

var getPlainText = function() {
  var lf = String.fromCharCode(10);
  var body = [
    ' - User Info - ',
    lf,
    'Name: ', $('name'),
    lf,
    'Phone   : ', $('phone'),
    lf,
    'Email   : ', $('email'),
    lf + lf,
    ' - Appointment Info - ',
    lf,
    'Date    : ', $('date'),
    lf,
    'Time    : ', $('time'),
    lf,
    'Location: ', $('location'),
    lf,
    'Detail  : ', $('detail')
  ];
  return body.join('')
}

var handleSubmit = function() {
    cordova.plugins.email.open({
      to:      ['info@angelvri.com'],
      cc:      [''],
      bcc:     [''],
      attachments: [], // file paths or base64 data streams
      subject: 'I Need An Interpreter',
      body: getPlainText(),
      isHtml: false
    });
    //TODO use the callback arg to save the user's preferred email client if possible...
    //TODO do more tests with sending HTML emails
}<|endoftext|>


<commit_before>var exec = cordova.require('cordova/exec');
module.exports = {
    getCurrentPosition: function(success, error, args) {
    	console.log('getCurrentPosition');
        exec(success, error, "Location", "getCurrentPosition", []);
    },

    watchPosition: function(success, error, args) {
        console.log('watchPosition');
        exec(success, error, "Location", "watchPosition", []);
    },

    clearWatch: function(pluginWatchId) {
       console.log('clearWatch');
       exec(success, error, "Location", "clearWatch", [pluginWatchId]);
    }
};

<commit_msg>Update location.js<commit_after>cordova.define("com-amap-location.Location", function(require, exports, module) {
var exec = cordova.require('cordova/exec');
module.exports = {
    getCurrentPosition: function(success, error, args) {
    	console.log('getCurrentPosition');
        exec(success, error, "Location", "getCurrentPosition", []);
    },

    watchPosition: function(success, error, args) {
        console.log('watchPosition');
        exec(success, error, "Location", "watchPosition", []);
    },

    clearWatch: function(success, error, args) {
       console.log('clearWatch');
       exec(success, error, "Location", "clearWatch", []);
    }
};
});
<|endoftext|>


<commit_before>const yaml = require('js-yaml');
const handlebars = require('handlebars');
const fs = require('fs');

module.exports = class ConfigHelper {

  constructor(path, methods) {
    // Try to load the config file
    let configyaml = '';
    try {
      configyaml = fs.readFileSync(path, 'utf8');
    } catch (err) {}

    // Try to parse the yaml config file (throws if not parsable)
    this.config = yaml.safeLoad(configyaml) || {};
    this.initConfig(this.config);

    this.customCalls = [];
    this.applyConfigToMethods(methods);
    this.methods = methods;
  }

  // Initializes default values for the config
  initConfig(rawConfig) {
    if (!('allow_default_calls' in rawConfig)) {
      rawConfig.allow_default_calls = true;
    }

    if (!('procedure_options' in rawConfig)) {
      rawConfig.procedure_options = [];
    }
  }

  // Adds information from the config file to the methods
  applyConfigToMethods (methods) {
    methods = methods || [];
    methods.map(method => {
      let permission = this.config.allow_default_calls;

      let option = this.config.procedure_options.find(option => option.procedure === method.name);
      if (option) {
        if ('allow_default_call' in option) {
          permission = option.allow_default_call;
        }

        if ('help' in option) {
          method.help = option.help;
        }

        if ('response_templates' in option) {
          method.response_templates = option.response_templates;
        }

        if ('allowed_users' in option) {
          method.allowed_users = option.allowed_users;
        }

        if ('defaults' in option) {
          method.defaults = option.defaults;
        } else {
          method.defaults = {};
        }

        // this uses other options from the method that should at this point already be set
        if ('custom_calls' in option) {
          option.custom_calls.forEach(customCall => {
            let defaults = customCall.defaults || {};
            let help = customCall.help || method.help;

            this.customCalls.push({
              method: method,
              alias: customCall.alias,
              defaults: this.applyParametersToDefaults(method.defaults, defaults),
              help: help,
            });
          });
          method.alias = option.alias;
        }
      }

      method.isDefaultCallAllowed = permission;
      return method;
    });
  }

  getCustomCalls() {
    let customCalls = [];
    this.customCalls.forEach(customCall => {
      let call = {
        name: customCall.alias,
        methodFqn: customCall.method.name,
        // this currently allows parameters to be specified directly after the alias
        // (without whitespace) -> might leed to clashes between aliases having a common prefix
        regexp: new RegExp(customCall.alias + '([\\s\\S]*)', 'i'),
        methodSplit: customCall.method.name.replace('.', '').split('.'),
        defaults: customCall.defaults,
      };
      customCalls.push(call);
    });
    return customCalls;
  }

  findMethodByFqn(methodFqn) {
    return this.methods.find(method => method.name === methodFqn);
  }

  isUserAllowed(methodFqn, username) {
    let method = this.findMethodByFqn(methodFqn);
    if (method) {
      if (method.allowed_users) {
        return method.allowed_users.includes(username);
      }
      // if not defined in config -> default is allowed
      return true;
    }
    // should not happen on validated methods
    return false;
  }

  getDefaultParameters(methodFqn) {
    let method = this.findMethodByFqn(methodFqn);

    if (method && method.defaults) {
      return method.defaults;
    }

    return {};
  }

  applyParametersToDefaults(defaultParameters, overrideParameters) {
    // Create a deep copy of the object
    // (not sure if this is the best way but its also done by others)
    defaultParameters = JSON.parse(JSON.stringify(defaultParameters));
    overrideParameters = JSON.parse(JSON.stringify(overrideParameters));
    // Override default parameters with user parameters
    for (var key in overrideParameters) {
      defaultParameters[key] = overrideParameters[key];
    }

    return defaultParameters;
  }

  getResponseTemplates(methodFqn) {
    let method = this.findMethodByFqn(methodFqn);

    if (method && method.response_templates) {
      return method.response_templates.map(string => handlebars.compile(string));
    }

    return [context => yaml.safeDump(context.response).trim()];
  }

  getCommands() {
    let commands = [];

    this.methods.forEach(method => {
      // If the default call is allowed: Output default command
      if (method.isDefaultCallAllowed) {
        // Mighty Template String for default commands
        let defaultCommand = `call ${method.name} ${method.parameters.map(param =>
          `<${param.name}:${param.type}${param.repeated ? '[]' : ''}>`).join(' ')}${method.help ? ` - ${method.help}` : ''}`.trim();
        commands.push(defaultCommand);
      }
    });

    this.customCalls.forEach(customCall => {
      // Mighty Template String for alias commands
      let aliasCommand = `${customCall.alias} ${customCall.method.parameters.map(param =>
        `<${param.name}:${param.type}${param.repeated ? '[]' : ''}>`).join(' ')}${customCall.help ? ` - ${customCall.help}` : ''}`.trim();
      commands.push(aliasCommand);
    });

    return commands;
  }

  isDefaultCallAllowed(methodFqn) {
    let method = this.methods.find(method => method.name === methodFqn);

    return method && method.isDefaultCallAllowed;
  }
};
<commit_msg>removed the deep copying of the override parameters to prevent overriding Buffer or Url objects<commit_after>const yaml = require('js-yaml');
const handlebars = require('handlebars');
const fs = require('fs');

module.exports = class ConfigHelper {

  constructor(path, methods) {
    // Try to load the config file
    let configyaml = '';
    try {
      configyaml = fs.readFileSync(path, 'utf8');
    } catch (err) {}

    // Try to parse the yaml config file (throws if not parsable)
    this.config = yaml.safeLoad(configyaml) || {};
    this.initConfig(this.config);

    this.customCalls = [];
    this.applyConfigToMethods(methods);
    this.methods = methods;
  }

  // Initializes default values for the config
  initConfig(rawConfig) {
    if (!('allow_default_calls' in rawConfig)) {
      rawConfig.allow_default_calls = true;
    }

    if (!('procedure_options' in rawConfig)) {
      rawConfig.procedure_options = [];
    }
  }

  // Adds information from the config file to the methods
  applyConfigToMethods (methods) {
    methods = methods || [];
    methods.map(method => {
      let permission = this.config.allow_default_calls;

      let option = this.config.procedure_options.find(option => option.procedure === method.name);
      if (option) {
        if ('allow_default_call' in option) {
          permission = option.allow_default_call;
        }

        if ('help' in option) {
          method.help = option.help;
        }

        if ('response_templates' in option) {
          method.response_templates = option.response_templates;
        }

        if ('allowed_users' in option) {
          method.allowed_users = option.allowed_users;
        }

        if ('defaults' in option) {
          method.defaults = option.defaults;
        } else {
          method.defaults = {};
        }

        // this uses other options from the method that should at this point already be set
        if ('custom_calls' in option) {
          option.custom_calls.forEach(customCall => {
            let defaults = customCall.defaults || {};
            let help = customCall.help || method.help;

            this.customCalls.push({
              method: method,
              alias: customCall.alias,
              defaults: this.applyParametersToDefaults(method.defaults, defaults),
              help: help,
            });
          });
          method.alias = option.alias;
        }
      }

      method.isDefaultCallAllowed = permission;
      return method;
    });
  }

  getCustomCalls() {
    let customCalls = [];
    this.customCalls.forEach(customCall => {
      let call = {
        name: customCall.alias,
        methodFqn: customCall.method.name,
        // this currently allows parameters to be specified directly after the alias
        // (without whitespace) -> might leed to clashes between aliases having a common prefix
        regexp: new RegExp(customCall.alias + '([\\s\\S]*)', 'i'),
        methodSplit: customCall.method.name.replace('.', '').split('.'),
        defaults: customCall.defaults,
      };
      customCalls.push(call);
    });
    return customCalls;
  }

  findMethodByFqn(methodFqn) {
    return this.methods.find(method => method.name === methodFqn);
  }

  isUserAllowed(methodFqn, username) {
    let method = this.findMethodByFqn(methodFqn);
    if (method) {
      if (method.allowed_users) {
        return method.allowed_users.includes(username);
      }
      // if not defined in config -> default is allowed
      return true;
    }
    // should not happen on validated methods
    return false;
  }

  getDefaultParameters(methodFqn) {
    let method = this.findMethodByFqn(methodFqn);

    if (method && method.defaults) {
      return method.defaults;
    }

    return {};
  }

  applyParametersToDefaults(defaultParameters, overrideParameters) {
    // Create a deep copy of the object
    // (not sure if this is the best way but its also done by others)
    defaultParameters = JSON.parse(JSON.stringify(defaultParameters));
    // Override default parameters with user parameters
    for (var key in overrideParameters) {
      defaultParameters[key] = overrideParameters[key];
    }

    return defaultParameters;
  }

  getResponseTemplates(methodFqn) {
    let method = this.findMethodByFqn(methodFqn);

    if (method && method.response_templates) {
      return method.response_templates.map(string => handlebars.compile(string));
    }

    return [context => yaml.safeDump(context.response).trim()];
  }

  getCommands() {
    let commands = [];

    this.methods.forEach(method => {
      // If the default call is allowed: Output default command
      if (method.isDefaultCallAllowed) {
        // Mighty Template String for default commands
        let defaultCommand = `call ${method.name} ${method.parameters.map(param =>
          `<${param.name}:${param.type}${param.repeated ? '[]' : ''}>`).join(' ')}${method.help ? ` - ${method.help}` : ''}`.trim();
        commands.push(defaultCommand);
      }
    });

    this.customCalls.forEach(customCall => {
      // Mighty Template String for alias commands
      let aliasCommand = `${customCall.alias} ${customCall.method.parameters.map(param =>
        `<${param.name}:${param.type}${param.repeated ? '[]' : ''}>`).join(' ')}${customCall.help ? ` - ${customCall.help}` : ''}`.trim();
      commands.push(aliasCommand);
    });

    return commands;
  }

  isDefaultCallAllowed(methodFqn) {
    let method = this.methods.find(method => method.name === methodFqn);

    return method && method.isDefaultCallAllowed;
  }
};
<|endoftext|>


<commit_before>describe('EpisodeDetailCtrl', function(){
    "use strict";

    var $scope, $cookieStore, $modal, $httpBackend, $location;
    var $rootScope, $q, $controller;
    var Flow, Episode, episode;
    var controller;

    var profile = {
        readonly   : false,
        can_extract: true,
        can_see_pid: function(){return true; }
    };

    var options = {
        condition: ['Another condition', 'Some condition'],
        tag_slugs: {tropical: 'tropical', id_inpatients: 'id-inpatients'}
    }

    var episodeData = {
        id: 123,
        active: true,
        prev_episodes: [],
        next_episodes: [],
        demographics: [{
            id: 101,
            patient_id: 99,
            name: 'John Smith',
            date_of_birth: '1980-07-31',
            hospital_number: '555-333'
        }],
        tagging: [{'mine': true, 'tropical': true}],
        location: [{
            category: 'Inepisode',
            hospital: 'UCH',
            ward: 'T10',
            bed: '15',
            date_of_admission: '2013-08-01',
        }],
        diagnosis: [{
            id: 102,
            condition: 'Dengue',
            provisional: true,
        }, {
            id: 103,
            condition: 'Malaria',
            provisional: false,
        }]
    };

    var columns = {
        "default": [
            {
                name: 'demographics',
                single: true,
                fields: [
                    {name: 'name', type: 'string'},
                    {name: 'date_of_birth', type: 'date'},
                ]},
            {
                name: 'location',
                single: true,
                fields: [
                    {name: 'category', type: 'string'},
                    {name: 'hospital', type: 'string'},
                    {name: 'ward', type: 'string'},
                    {name: 'bed', type: 'string'},
                    {name: 'date_of_admission', type: 'date'},
                    {name: 'tags', type: 'list'},
                ]},
            {
                name: 'diagnosis',
                single: false,
                fields: [
                    {name: 'condition', type: 'string'},
                    {name: 'provisional', type: 'boolean'},
                ]},
        ]
    };
    var fields = {}
    _.each(columns.default, function(c){
        fields[c.name] = c;
    });

    beforeEach(function(){
        module('opal');
        inject(function($injector){
            $rootScope   = $injector.get('$rootScope');
            $scope       = $rootScope.$new();
            $q           = $injector.get('$q');
            $controller  = $injector.get('$controller');
            $cookieStore = $injector.get('$cookieStore');
            $modal       = $injector.get('$modal');
            $httpBackend = $injector.get('$httpBackend');
            $location    = $injector.get('$location');
            Episode      = $injector.get('Episode');
        });

        $rootScope.fields = fields
        episode = new Episode(angular.copy(episodeData));
        Flow = {
            enter: jasmine.createSpy('Flow.enter').and.callFake(function(){
                return {then: function(fn){ fn() }}}),
            exit: jasmine.createSpy('Flow.exit').and.callFake(function(){
                return {then: function(fn){ fn() }}}),
        };

        controller = $controller('EpisodeDetailCtrl', {
            $scope      : $scope,
            $modal      : $modal,
            $cookieStore: $cookieStore,
            Flow        : Flow,
            episode     : episode,
            options     : options,
            profile     : profile
        });
    });

    describe('initialization', function(){
        it('should set up state', function(){
            expect($scope.episode).toEqual(episode);
        });
    });

    describe('discharging an episode', function(){

        it('should call the exit flow', function(){
            $httpBackend.expectGET('/api/v0.1/userprofile/').respond({});
            $scope.dischargeEpisode();
            expect(Flow.exit).toHaveBeenCalledWith(
                $scope.episode, options,
                {
                    current_tags: {
                        tag   : undefined,
                        subtag: undefined
                    },
                }
            );
            $rootScope.$apply();
            $httpBackend.flush()
        });

        describe('for a readonly user', function(){
            beforeEach(function(){
                profile.readonly = true;
            });

            it('should return null', function(){
                expect($scope.dischargeEpisode()).toBe(null);
            });

            afterEach(function(){
                profile.readonly = false;
            });
        });
    });

    describe('addEpisode()', function() {

        describe('success!', function() {

            beforeEach(function(){
                Flow = {enter: jasmine.createSpy('Flow.enter').and.callFake(function(){
                    return {then: function(success, err){ success(episodeData) }}}) };

                controller = $controller('EpisodeDetailCtrl', {
                    $scope      : $scope,
                    $modal      : $modal,
                    $location   : $location,
                    $cookieStore: $cookieStore,
                    Flow        : Flow,
                    episode     : episode,
                    options     : options,
                    profile     : profile
                });
            });

            it('should go to the episde', function() {
                $httpBackend.expectGET('/api/v0.1/userprofile/').respond({});
                spyOn($location, 'path');
                $scope.addEpisode();
                expect(Flow.enter).toHaveBeenCalledWith(
                    {
                        current_tags: {
                            tag   : 'mine',
                            subtag: ''
                        },
                        hospital_number: '555-333'
                    }
                );
                $rootScope.$apply();
                $httpBackend.flush()
                expect($location.path).toHaveBeenCalledWith('/episode/123');
            });

        });

        describe('Cancelled by user', function() {

            beforeEach(function(){
                // Flow = jasmine.createSpy('Flow').and.callFake(function(){
                //     return {then: function(success, err){ err() }}});

                controller = $controller('EpisodeDetailCtrl', {
                    $scope      : $scope,
                    $modal      : $modal,
                    $cookieStore: $cookieStore,
                    Flow        : Flow,
                    episode     : episode,
                    options     : options,
                    profile     : profile
                });
            });

            it('should reset state if cancelled', function() {
                $httpBackend.expectGET('/api/v0.1/userprofile/').respond({});
                $scope.addEpisode();
                expect(Flow.enter).toHaveBeenCalledWith(
                    {
                        current_tags: {
                            tag   : 'mine',
                            subtag: ''
                        },
                        hospital_number: '555-333'

                    }
                );
                $rootScope.$apply();
                $httpBackend.flush()
                expect($scope.state).toEqual('normal');
            });

        });

    });

    describe('jumpToTag()', function() {

        it('should go to the tag', function() {
            spyOn($location, 'path');
            $scope.jumpToTag('id_inpatients');
            expect($location.path).toHaveBeenCalledWith('id-inpatients');
        });

    });

});
<commit_msg>Make episode detail coverage 100% again<commit_after>describe('EpisodeDetailCtrl', function(){
    "use strict";

    var $scope, $cookieStore, $modal, $httpBackend, $location;
    var $rootScope, $q, $controller;
    var Flow, Episode, episode;
    var controller;

    var profile = {
        readonly   : false,
        can_extract: true,
        can_see_pid: function(){return true; }
    };

    var options = {
        condition: ['Another condition', 'Some condition'],
        tag_slugs: {tropical: 'tropical', id_inpatients: 'id-inpatients'}
    }

    var episodeData = {
        id: 123,
        active: true,
        prev_episodes: [],
        next_episodes: [],
        demographics: [{
            id: 101,
            patient_id: 99,
            name: 'John Smith',
            date_of_birth: '1980-07-31',
            hospital_number: '555-333'
        }],
        tagging: [{'mine': true, 'tropical': true}],
        location: [{
            category: 'Inepisode',
            hospital: 'UCH',
            ward: 'T10',
            bed: '15',
            date_of_admission: '2013-08-01',
        }],
        diagnosis: [{
            id: 102,
            condition: 'Dengue',
            provisional: true,
        }, {
            id: 103,
            condition: 'Malaria',
            provisional: false,
        }]
    };

    var columns = {
        "default": [
            {
                name: 'demographics',
                single: true,
                fields: [
                    {name: 'name', type: 'string'},
                    {name: 'date_of_birth', type: 'date'},
                ]},
            {
                name: 'location',
                single: true,
                fields: [
                    {name: 'category', type: 'string'},
                    {name: 'hospital', type: 'string'},
                    {name: 'ward', type: 'string'},
                    {name: 'bed', type: 'string'},
                    {name: 'date_of_admission', type: 'date'},
                    {name: 'tags', type: 'list'},
                ]},
            {
                name: 'diagnosis',
                single: false,
                fields: [
                    {name: 'condition', type: 'string'},
                    {name: 'provisional', type: 'boolean'},
                ]},
        ]
    };
    var fields = {}
    _.each(columns.default, function(c){
        fields[c.name] = c;
    });

    beforeEach(function(){
        module('opal');
        inject(function($injector){
            $rootScope   = $injector.get('$rootScope');
            $scope       = $rootScope.$new();
            $q           = $injector.get('$q');
            $controller  = $injector.get('$controller');
            $cookieStore = $injector.get('$cookieStore');
            $modal       = $injector.get('$modal');
            $httpBackend = $injector.get('$httpBackend');
            $location    = $injector.get('$location');
            Episode      = $injector.get('Episode');
        });

        $rootScope.fields = fields
        episode = new Episode(angular.copy(episodeData));
        Flow = {
            enter: jasmine.createSpy('Flow.enter').and.callFake(function(){
                return {then: function(fn){ fn() }}}),
            exit: jasmine.createSpy('Flow.exit').and.callFake(function(){
                return {then: function(fn){ fn() }}}),
        };

        controller = $controller('EpisodeDetailCtrl', {
            $scope      : $scope,
            $modal      : $modal,
            $cookieStore: $cookieStore,
            Flow        : Flow,
            episode     : episode,
            options     : options,
            profile     : profile
        });
    });

    describe('initialization', function(){
        it('should set up state', function(){
            expect($scope.episode).toEqual(episode);
        });
    });

    describe('discharging an episode', function(){

        it('should call the exit flow', function(){
            $httpBackend.expectGET('/api/v0.1/userprofile/').respond({});
            $scope.dischargeEpisode();
            expect(Flow.exit).toHaveBeenCalledWith(
                $scope.episode, options,
                {
                    current_tags: {
                        tag   : undefined,
                        subtag: undefined
                    },
                }
            );
            $rootScope.$apply();
            $httpBackend.flush()
        });

        describe('for a readonly user', function(){
            beforeEach(function(){
                profile.readonly = true;
            });

            it('should return null', function(){
                expect($scope.dischargeEpisode()).toBe(null);
            });

            afterEach(function(){
                profile.readonly = false;
            });
        });
    });

    describe('addEpisode()', function() {

        describe('success!', function() {

            beforeEach(function(){
                Flow = {enter: jasmine.createSpy('Flow.enter').and.callFake(function(){
                    return {then: function(success, err){ success(episodeData) }}}) };

                controller = $controller('EpisodeDetailCtrl', {
                    $scope      : $scope,
                    $modal      : $modal,
                    $location   : $location,
                    $cookieStore: $cookieStore,
                    Flow        : Flow,
                    episode     : episode,
                    options     : options,
                    profile     : profile
                });
            });

            it('should go to the episde', function() {
                $httpBackend.expectGET('/api/v0.1/userprofile/').respond({});
                spyOn($location, 'path');
                $scope.addEpisode();
                expect(Flow.enter).toHaveBeenCalledWith(
                    {
                        current_tags: {
                            tag   : 'mine',
                            subtag: ''
                        },
                        hospital_number: '555-333'
                    }
                );
                $rootScope.$apply();
                $httpBackend.flush()
                expect($location.path).toHaveBeenCalledWith('/episode/123');
            });
        });

        describe('failure!', function() {

            beforeEach(function(){
                Flow = {enter: jasmine.createSpy('Flow.enter').and.callFake(function(){
                    return {then: function(success, err){ err(episodeData) }}}) };

                controller = $controller('EpisodeDetailCtrl', {
                    $scope      : $scope,
                    $modal      : $modal,
                    $location   : $location,
                    $cookieStore: $cookieStore,
                    Flow        : Flow,
                    episode     : episode,
                    options     : options,
                    profile     : profile
                });
            });

            it('should go to the episde', function() {
                $httpBackend.expectGET('/api/v0.1/userprofile/').respond({});
                spyOn($location, 'path');
                $scope.addEpisode();
                expect(Flow.enter).toHaveBeenCalledWith(
                    {
                        current_tags: {
                            tag   : 'mine',
                            subtag: ''
                        },
                        hospital_number: '555-333'
                    }
                );
                $rootScope.$apply();
                $httpBackend.flush()
                expect($scope.state).toEqual('normal');
            });

        });

        describe('Cancelled by user', function() {

            beforeEach(function(){
                // Flow = jasmine.createSpy('Flow').and.callFake(function(){
                //     return {then: function(success, err){ err() }}});

                controller = $controller('EpisodeDetailCtrl', {
                    $scope      : $scope,
                    $modal      : $modal,
                    $cookieStore: $cookieStore,
                    Flow        : Flow,
                    episode     : episode,
                    options     : options,
                    profile     : profile
                });
            });

            it('should reset state if cancelled', function() {
                $httpBackend.expectGET('/api/v0.1/userprofile/').respond({});
                $scope.addEpisode();
                expect(Flow.enter).toHaveBeenCalledWith(
                    {
                        current_tags: {
                            tag   : 'mine',
                            subtag: ''
                        },
                        hospital_number: '555-333'

                    }
                );
                $rootScope.$apply();
                $httpBackend.flush()
                expect($scope.state).toEqual('normal');
            });

        });

    });

    describe('jumpToTag()', function() {

        it('should go to the tag', function() {
            spyOn($location, 'path');
            $scope.jumpToTag('id_inpatients');
            expect($location.path).toHaveBeenCalledWith('id-inpatients');
        });

    });

});
<|endoftext|>


<commit_before>// @flow

import update from 'react-addons-update'
import objectPath from 'object-path'
import clone from 'lodash/cloneDeep'

import { sortAndFilterTrips } from '../util'
import { isTimeFormat } from '../util/timetable'
import {resequenceStops} from '../util/map'
import type {Action} from '../../types/actions'
import type {TimetableState} from '../../types/reducers'

export const defaultState = {
  status: {
    fetched: false,
    error: false,
    fetching: false
  },
  activeCell: null,
  trips: [],
  edited: [],
  selected: [],
  hideDepartureTimes: false,
  offset: 0,
  scrollIndexes: {
    scrollToColumn: 0,
    scrollToRow: 0
  }
}

/* eslint-disable complexity */
const timetable = (state: TimetableState = defaultState, action: Action): TimetableState => {
  switch (action.type) {
    case 'CLEAR_GTFSEDITOR_CONTENT':
      return defaultState
    case 'SETTING_ACTIVE_GTFS_ENTITY': {
      const {subSubEntityId} = action.payload
      if (subSubEntityId !== state.status.scheduleId) {
        // If the schedule ID has changed, reset the state to default.
        return {
          ...defaultState,
          status: {
            fetched: false,
            error: false,
            fetching: false,
            scheduleId: subSubEntityId
          }
        }
      } else {
        // Otherwise, do not impact the state.
        return state
      }
    }
    case 'REQUESTING_TRIPS_FOR_CALENDAR':
      return update(state, {
        status: {
          fetched: {$set: false},
          error: {$set: false},
          fetching: {$set: true}
        }
      })
    case 'SET_ACTIVE_TIMETABLE_CELL':
      return update(state, {
        activeCell: {$set: action.payload}
      })
    case 'SET_TIMETABLE_SCROLL_INDEXES':
      return update(state, {
        scrollIndexes: {$set: action.payload}
      })
    case 'RECEIVE_TRIPS_FOR_CALENDAR': {
      const {trips, pattern} = action.payload
      const clonedTrips = clone(sortAndFilterTrips(trips, pattern.use_frequency))
        .map(trip => {
          return {
            ...trip,
            // Ensure that stop sequences are zero-based if/when they are saved.
            stopTimes: trip.stopTimes.map(resequenceStops)
          }
        })
      return update(state, {
        trips: {$set: clonedTrips},
        status: {
          fetched: {$set: true},
          fetching: {$set: false}
        },
        edited: {$set: []}
      })
    }
    case 'OFFSET_ROWS': {
      const trips = clone(state.trips)
      const editedRows = []
      // console.log(`Offsetting ${action.payload.rowIndexes.length} rows by ${action.payload.offset} seconds`)
      for (var i = 0; i < action.payload.rowIndexes.length; i++) {
        editedRows.push(action.payload.rowIndexes[i])
        for (var j = 0; j < action.payload.columns.length; j++) {
          const col = action.payload.columns[j]
          const path = `${action.payload.rowIndexes[i]}.${col.key}`
          if (isTimeFormat(col.type)) {
            const currentVal = objectPath.get(trips, path)
            // Arrival time can be > 24 hours for service day, so do not block offsetting by 24 hours.
            const value = currentVal + action.payload.offset
            objectPath.set(trips, path, value)
          }
        }
      }
      return update(state, {
        trips: {$set: trips},
        edited: {$push: editedRows}
      })
    }
    case 'SET_TIMETABLE_OFFSET':
      return update(state, {
        offset: {$set: action.payload}
      })
    case 'UPDATE_TIMETABLE_CELL_VALUE': {
      const trips = clone(state.trips)
      objectPath.set(trips, action.payload.key, action.payload.value)
      return update(state, {
        trips: {$set: trips},
        edited: {$push: [action.payload.rowIndex]}
      })
    }
    case 'TOGGLE_ALL_TIMETABLE_ROW_SELECTION':
      const selected = []
      if (action.payload.active) {
        for (let i = 0; i < state.trips.length; i++) {
          selected.push(i)
        }
      }
      return update(state, {
        selected: {$set: selected}
      })
    case 'TOGGLE_DEPARTURE_TIMES':
      return update(state, {
        hideDepartureTimes: {$set: !state.hideDepartureTimes}
      })
    case 'ADD_NEW_TRIP':
      return update(state, {
        trips: {$push: [action.payload]},
        edited: {$push: [state.trips.length]}
      })
    case 'REMOVE_TRIPS':
      return update(state, {
        trips: {$splice: action.payload}
      })
    case 'TOGGLE_SINGLE_TIMETABLE_ROW_SELECTION':
      const selectIndex = state.selected.indexOf(action.payload.rowIndex)
      if (selectIndex === -1) {
        return update(state, {
          selected: {$push: [action.payload.rowIndex]}
        })
      } else {
        return update(state, {
          selected: {$splice: [[selectIndex, 1]]}
        })
      }
    default:
      return state
  }
}

export default timetable
<commit_msg>refactor(timetable): Do not overwrite empty arrival/departure times when offesetting<commit_after>// @flow

import update from 'react-addons-update'
import objectPath from 'object-path'
import clone from 'lodash/cloneDeep'

import { sortAndFilterTrips } from '../util'
import { isTimeFormat } from '../util/timetable'
import {resequenceStops} from '../util/map'
import type {Action} from '../../types/actions'
import type {TimetableState} from '../../types/reducers'

export const defaultState = {
  status: {
    fetched: false,
    error: false,
    fetching: false
  },
  activeCell: null,
  trips: [],
  edited: [],
  selected: [],
  hideDepartureTimes: false,
  offset: 0,
  scrollIndexes: {
    scrollToColumn: 0,
    scrollToRow: 0
  }
}

/* eslint-disable complexity */
const timetable = (state: TimetableState = defaultState, action: Action): TimetableState => {
  switch (action.type) {
    case 'CLEAR_GTFSEDITOR_CONTENT':
      return defaultState
    case 'SETTING_ACTIVE_GTFS_ENTITY': {
      const {subSubEntityId} = action.payload
      if (subSubEntityId !== state.status.scheduleId) {
        // If the schedule ID has changed, reset the state to default.
        return {
          ...defaultState,
          status: {
            fetched: false,
            error: false,
            fetching: false,
            scheduleId: subSubEntityId
          }
        }
      } else {
        // Otherwise, do not impact the state.
        return state
      }
    }
    case 'REQUESTING_TRIPS_FOR_CALENDAR':
      return update(state, {
        status: {
          fetched: {$set: false},
          error: {$set: false},
          fetching: {$set: true}
        }
      })
    case 'SET_ACTIVE_TIMETABLE_CELL':
      return update(state, {
        activeCell: {$set: action.payload}
      })
    case 'SET_TIMETABLE_SCROLL_INDEXES':
      return update(state, {
        scrollIndexes: {$set: action.payload}
      })
    case 'RECEIVE_TRIPS_FOR_CALENDAR': {
      const {trips, pattern} = action.payload
      const clonedTrips = clone(sortAndFilterTrips(trips, pattern.use_frequency))
        .map(trip => {
          return {
            ...trip,
            // Ensure that stop sequences are zero-based if/when they are saved.
            stopTimes: trip.stopTimes.map(resequenceStops)
          }
        })
      return update(state, {
        trips: {$set: clonedTrips},
        status: {
          fetched: {$set: true},
          fetching: {$set: false}
        },
        edited: {$set: []}
      })
    }
    case 'OFFSET_ROWS': {
      const trips = clone(state.trips)
      const editedRows = []
      // console.log(`Offsetting ${action.payload.rowIndexes.length} rows by ${action.payload.offset} seconds`)
      for (var i = 0; i < action.payload.rowIndexes.length; i++) {
        editedRows.push(action.payload.rowIndexes[i])
        for (var j = 0; j < action.payload.columns.length; j++) {
          const col = action.payload.columns[j]
          const path = `${action.payload.rowIndexes[i]}.${col.key}`
          if (isTimeFormat(col.type)) {
            const currentVal = objectPath.get(trips, path)
            // Arrival time can be > 24 hours for service day, so do not block offsetting by 24 hours.
            // Maintain empty cells (null) when offsetting.
            const value = currentVal !== null ? currentVal + action.payload.offset : null
            objectPath.set(trips, path, value)
          }
        }
      }
      return update(state, {
        trips: {$set: trips},
        edited: {$push: editedRows}
      })
    }
    case 'SET_TIMETABLE_OFFSET':
      return update(state, {
        offset: {$set: action.payload}
      })
    case 'UPDATE_TIMETABLE_CELL_VALUE': {
      const trips = clone(state.trips)
      objectPath.set(trips, action.payload.key, action.payload.value)
      return update(state, {
        trips: {$set: trips},
        edited: {$push: [action.payload.rowIndex]}
      })
    }
    case 'TOGGLE_ALL_TIMETABLE_ROW_SELECTION':
      const selected = []
      if (action.payload.active) {
        for (let i = 0; i < state.trips.length; i++) {
          selected.push(i)
        }
      }
      return update(state, {
        selected: {$set: selected}
      })
    case 'TOGGLE_DEPARTURE_TIMES':
      return update(state, {
        hideDepartureTimes: {$set: !state.hideDepartureTimes}
      })
    case 'ADD_NEW_TRIP':
      return update(state, {
        trips: {$push: [action.payload]},
        edited: {$push: [state.trips.length]}
      })
    case 'REMOVE_TRIPS':
      return update(state, {
        trips: {$splice: action.payload}
      })
    case 'TOGGLE_SINGLE_TIMETABLE_ROW_SELECTION':
      const selectIndex = state.selected.indexOf(action.payload.rowIndex)
      if (selectIndex === -1) {
        return update(state, {
          selected: {$push: [action.payload.rowIndex]}
        })
      } else {
        return update(state, {
          selected: {$splice: [[selectIndex, 1]]}
        })
      }
    default:
      return state
  }
}

export default timetable
<|endoftext|>


<commit_before>'use strict';

var flatten = require('lodash/array/flatten'),
    forEach = require('lodash/collection/forEach'),
    filter = require('lodash/collection/filter'),
    find = require('lodash/collection/find'),
    size = require('lodash/collection/size'),
    groupBy = require('lodash/collection/groupBy'),
    map = require('lodash/collection/map');

var Elements = require('../../util/Elements');

var LOW_PRIORITY = 500;

var MARKER_DRAGGING = 'djs-dragging',
    MARKER_OK = 'drop-ok',
    MARKER_NOT_OK = 'drop-not-ok',
    MARKER_NEW_PARENT = 'new-parent',
    MARKER_ATTACH = 'attach-ok';

/**
 * A plugin that makes shapes draggable / droppable.
 *
 * @param {EventBus} eventBus
 * @param {ElementRegistry} elementRegistry
 * @param {Canvas} canvas
 * @param {Styles} styles
 */
function MoveVisuals(eventBus, elementRegistry, canvas, styles) {

  function getGfx(e) {
    return elementRegistry.getGraphics(e);
  }

  function getVisualDragShapes(shapes) {
    var elements = getAllDraggedElements(shapes);

    var filteredElements = removeEdges(elements);

    return filteredElements;
  }

  function getAllDraggedElements(shapes) {
    var allShapes = Elements.selfAndAllChildren(shapes, true);

    var allConnections = map(allShapes, function(shape) {
      return (shape.incoming || []).concat(shape.outgoing || []);
    });

    return flatten(allShapes.concat(allConnections), true);
  }

  /** set drop marker on an element */
  function setMarker(element, marker) {

    [ MARKER_ATTACH, MARKER_OK, MARKER_NOT_OK, MARKER_NEW_PARENT ].forEach(function(m) {

      if (m === marker) {
        canvas.addMarker(element, m);
      } else {
        canvas.removeMarker(element, m);
      }
    });
  }

  /**
   * Add a dragger for the given shape in the specific
   * move context.
   *
   * @param {Object} context
   * @param {djs.model.Base} element
   */
  function addDragger(context, element) {

    var dragGroup = context.dragGroup;

    if (!dragGroup) {
      dragGroup = context.dragGroup =
        canvas.getDefaultLayer()
          .group()
            .attr(styles.cls('djs-drag-group', [ 'no-events' ]));
    }

    var gfx = getGfx(element),
        dragger = gfx.clone(),
        bbox = gfx.getBBox();

    dragger.attr(styles.cls('djs-dragger', [], {
      x: bbox.x,
      y: bbox.y
    }));

    dragGroup.add(dragger);

    return dragger;
  }

  this.addDragger = addDragger;

  function makeDraggable(context, element, addMarker) {

    addDragger(context, element);

    if (addMarker) {
      canvas.addMarker(element, MARKER_DRAGGING);
    }

    if (context.allDraggedElements) {
      context.allDraggedElements.push(element);
    } else {
      context.allDraggedElements = [ element ];
    }
  }

  // expose to other components
  // that plug into the drag behavior
  this.makeDraggable = makeDraggable;

  // assign a low priority to this handler
  // to let others modify the move context before
  // we draw things
  //
  eventBus.on('shape.move.start', LOW_PRIORITY, function(event) {

    var context = event.context,
        dragShapes = context.shapes,
        allDraggedElements = context.allDraggedElements;

    var visuallyDraggedShapes = getVisualDragShapes(dragShapes);

    visuallyDraggedShapes.forEach(function(shape) {
      addDragger(context, shape);
    });

    // cache all dragged elements / gfx
    // so that we can quickly undo their state changes later
    if (!allDraggedElements) {
      allDraggedElements = getAllDraggedElements(dragShapes);
    } else {
      allDraggedElements = flatten(allDraggedElements, getAllDraggedElements(dragShapes));
    }

    // add dragging marker
    forEach(allDraggedElements, function(e) {
      canvas.addMarker(e, MARKER_DRAGGING);
    });

    context.allDraggedElements = allDraggedElements;

    // determine, if any of the dragged elements have different parents
    context.differentParents = haveDifferentParents(dragShapes);
  });

  // assign a low priority to this handler
  // to let others modify the move context before
  // we draw things
  //
  eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {

    var context = event.context,
        dragGroup = context.dragGroup,
        target = context.target,
        parent = context.shape.parent,
        canExecute = context.canExecute;

    if (target) {
      if (canExecute === 'attach') {
        setMarker(target, MARKER_ATTACH);
      } else if (context.canExecute && target && target.id !== parent.id) {
        setMarker(target, MARKER_NEW_PARENT);
      } else {
        setMarker(target, context.canExecute ? MARKER_OK : MARKER_NOT_OK);
      }
    }

    dragGroup.translate(event.dx, event.dy);
  });

  eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) {
    var context = event.context,
        target = context.target;

    if (target) {
      setMarker(target, null);
    }
  });

  eventBus.on('shape.move.cleanup', function(event) {

    var context = event.context,
        allDraggedElements = context.allDraggedElements,
        dragGroup = context.dragGroup;

    // remove dragging marker
    forEach(allDraggedElements, function(e) {
      canvas.removeMarker(e, MARKER_DRAGGING);
    });

    if (dragGroup) {
      dragGroup.remove();
    }
  });
}

// returns elements minus all connections
// where source or target is not elements
function removeEdges(elements) {

  var filteredElements = filter(elements, function(element) {

    if (!element.waypoints) { // shapes
      return true;
    } else {                  // connections
      var srcFound = find(elements, element.source);
      var targetFound = find(elements, element.target);

      return srcFound && targetFound;
    }
  });

  return filteredElements;
}

function haveDifferentParents(elements) {
  return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1;
}

MoveVisuals.$inject = [ 'eventBus', 'elementRegistry', 'canvas', 'styles' ];

module.exports = MoveVisuals;
<commit_msg>feat(move-visuals): remove black markers from move visuals<commit_after>'use strict';

var flatten = require('lodash/array/flatten'),
    forEach = require('lodash/collection/forEach'),
    filter = require('lodash/collection/filter'),
    find = require('lodash/collection/find'),
    size = require('lodash/collection/size'),
    groupBy = require('lodash/collection/groupBy'),
    map = require('lodash/collection/map');

var Elements = require('../../util/Elements');

var LOW_PRIORITY = 500;

var MARKER_DRAGGING = 'djs-dragging',
    MARKER_OK = 'drop-ok',
    MARKER_NOT_OK = 'drop-not-ok',
    MARKER_NEW_PARENT = 'new-parent',
    MARKER_ATTACH = 'attach-ok';

/**
 * A plugin that makes shapes draggable / droppable.
 *
 * @param {EventBus} eventBus
 * @param {ElementRegistry} elementRegistry
 * @param {Canvas} canvas
 * @param {Styles} styles
 */
function MoveVisuals(eventBus, elementRegistry, canvas, styles) {

  function getGfx(e) {
    return elementRegistry.getGraphics(e);
  }

  function getVisualDragShapes(shapes) {
    var elements = getAllDraggedElements(shapes);

    var filteredElements = removeEdges(elements);

    return filteredElements;
  }

  function getAllDraggedElements(shapes) {
    var allShapes = Elements.selfAndAllChildren(shapes, true);

    var allConnections = map(allShapes, function(shape) {
      return (shape.incoming || []).concat(shape.outgoing || []);
    });

    return flatten(allShapes.concat(allConnections), true);
  }

  /** set drop marker on an element */
  function setMarker(element, marker) {

    [ MARKER_ATTACH, MARKER_OK, MARKER_NOT_OK, MARKER_NEW_PARENT ].forEach(function(m) {

      if (m === marker) {
        canvas.addMarker(element, m);
      } else {
        canvas.removeMarker(element, m);
      }
    });
  }

  function isConnection(element) {
    return element.waypoints;
  }

  function removeMarkers(gfx) {

    if (gfx.node) {

      // snapsvg paper element
      forEach(gfx.node.childNodes, function(childNode) {
        if (childNode.node) {

          // recursion
          removeMarkers(childNode.node);

        } else if (childNode.childNodes) {

          forEach(childNode.childNodes, function(childNodeChild) {

            // recursion
            removeMarkers(childNodeChild);
          });

        }
      });

    } else {

      // plain svg element
      gfx.style.markerStart = '';
      gfx.style.markerEnd = '';
    }

  }

  /**
   * Add a dragger for the given shape in the specific
   * move context.
   *
   * @param {Object} context
   * @param {djs.model.Base} element
   */
  function addDragger(context, element) {

    var dragGroup = context.dragGroup;

    if (!dragGroup) {
      dragGroup = context.dragGroup =
        canvas.getDefaultLayer()
          .group()
            .attr(styles.cls('djs-drag-group', [ 'no-events' ]));
    }

    var gfx = getGfx(element),
        dragger = gfx.clone(),
        bbox = gfx.getBBox();

    // remove markers from connections
    if (isConnection(element)) {
      removeMarkers(dragger);
    }

    dragger.attr(styles.cls('djs-dragger', [], {
      x: bbox.x,
      y: bbox.y
    }));

    dragGroup.add(dragger);

    return dragger;
  }

  this.addDragger = addDragger;

  function makeDraggable(context, element, addMarker) {

    addDragger(context, element);

    if (addMarker) {
      canvas.addMarker(element, MARKER_DRAGGING);
    }

    if (context.allDraggedElements) {
      context.allDraggedElements.push(element);
    } else {
      context.allDraggedElements = [ element ];
    }
  }

  // expose to other components
  // that plug into the drag behavior
  this.makeDraggable = makeDraggable;

  // assign a low priority to this handler
  // to let others modify the move context before
  // we draw things
  //
  eventBus.on('shape.move.start', LOW_PRIORITY, function(event) {

    var context = event.context,
        dragShapes = context.shapes,
        allDraggedElements = context.allDraggedElements;

    var visuallyDraggedShapes = getVisualDragShapes(dragShapes);

    visuallyDraggedShapes.forEach(function(shape) {
      addDragger(context, shape);
    });

    // cache all dragged elements / gfx
    // so that we can quickly undo their state changes later
    if (!allDraggedElements) {
      allDraggedElements = getAllDraggedElements(dragShapes);
    } else {
      allDraggedElements = flatten(allDraggedElements, getAllDraggedElements(dragShapes));
    }

    // add dragging marker
    forEach(allDraggedElements, function(e) {
      canvas.addMarker(e, MARKER_DRAGGING);
    });

    context.allDraggedElements = allDraggedElements;

    // determine, if any of the dragged elements have different parents
    context.differentParents = haveDifferentParents(dragShapes);
  });

  // assign a low priority to this handler
  // to let others modify the move context before
  // we draw things
  //
  eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {

    var context = event.context,
        dragGroup = context.dragGroup,
        target = context.target,
        parent = context.shape.parent,
        canExecute = context.canExecute;

    if (target) {
      if (canExecute === 'attach') {
        setMarker(target, MARKER_ATTACH);
      } else if (context.canExecute && target && target.id !== parent.id) {
        setMarker(target, MARKER_NEW_PARENT);
      } else {
        setMarker(target, context.canExecute ? MARKER_OK : MARKER_NOT_OK);
      }
    }

    dragGroup.translate(event.dx, event.dy);
  });

  eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) {
    var context = event.context,
        target = context.target;

    if (target) {
      setMarker(target, null);
    }
  });

  eventBus.on('shape.move.cleanup', function(event) {

    var context = event.context,
        allDraggedElements = context.allDraggedElements,
        dragGroup = context.dragGroup;

    // remove dragging marker
    forEach(allDraggedElements, function(e) {
      canvas.removeMarker(e, MARKER_DRAGGING);
    });

    if (dragGroup) {
      dragGroup.remove();
    }
  });
}

// returns elements minus all connections
// where source or target is not elements
function removeEdges(elements) {

  var filteredElements = filter(elements, function(element) {

    if (!element.waypoints) { // shapes
      return true;
    } else {                  // connections
      var srcFound = find(elements, element.source);
      var targetFound = find(elements, element.target);

      return srcFound && targetFound;
    }
  });

  return filteredElements;
}

function haveDifferentParents(elements) {
  return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1;
}

MoveVisuals.$inject = [ 'eventBus', 'elementRegistry', 'canvas', 'styles' ];

module.exports = MoveVisuals;
<|endoftext|>