<commit_before>(function(angular) {
    'use strict';

    function isDnDsSupported() {
        return 'ondrag' in document.createElement('a');
    }

    function determineEffectAllowed(e) {
        // Chrome doesn't set dropEffect, so we have to work it out ourselves
        if (typeof e.dataTransfer !== 'undefined' && e.dataTransfer.dropEffect === 'none') {
            if (e.dataTransfer.effectAllowed === 'copy' ||
                e.dataTransfer.effectAllowed === 'move') {
                e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed;
            } else if (e.dataTransfer.effectAllowed === 'copyMove' || e.dataTransfer.effectAllowed === 'copymove') {
                e.dataTransfer.dropEffect = e.ctrlKey ? 'copy' : 'move';
            }
        }
    }

    if (!isDnDsSupported()) {
        angular.module('ang-drag-drop', []);
        return;
    }

    if (window.jQuery && (-1 === window.jQuery.event.props.indexOf('dataTransfer'))) {
        window.jQuery.event.props.push('dataTransfer');
    }

    var module = angular.module('ang-drag-drop', []);

    module.directive('uiDraggable', ['$parse', '$rootScope', '$dragImage', function($parse, $rootScope, $dragImage) {
        return function(scope, element, attrs) {
            var isDragHandleUsed = false,
                dragHandleClass,
                draggingClass = attrs.draggingClass || 'on-dragging',
                dragTarget;

            element.attr('draggable', false);

            scope.$watch(attrs.uiDraggable, function(newValue) {
                if (newValue) {
                    element.attr('draggable', newValue);
                    element.bind('dragend', dragendHandler);
                    element.bind('dragstart', dragstartHandler);
                }
                else {
                    element.removeAttr('draggable');
                    element.unbind('dragend', dragendHandler);
                    element.unbind('dragstart', dragstartHandler);
                }

            });

            if (angular.isString(attrs.dragHandleClass)) {
                isDragHandleUsed = true;
                dragHandleClass = attrs.dragHandleClass.trim() || 'drag-handle';

                element.bind('mousedown', function(e) {
                    dragTarget = e.target;
                });
            }

            function dragendHandler(e) {
                setTimeout(function() {
                    element.unbind('$destroy', dragendHandler);
                }, 0);
                var sendChannel = attrs.dragChannel || 'defaultchannel';
                $rootScope.$broadcast('ANGULAR_DRAG_END', e, sendChannel);

                determineEffectAllowed(e);

                if (e.dataTransfer && e.dataTransfer.dropEffect !== 'none') {
                    if (attrs.onDropSuccess) {
                        var onDropSuccessFn = $parse(attrs.onDropSuccess);
                        scope.$evalAsync(function() {
                            onDropSuccessFn(scope, {$event: e});
                        });
                    } else {
                        if (attrs.onDropFailure) {
                            var onDropFailureFn = $parse(attrs.onDropFailure);
                            scope.$evalAsync(function() {
                                onDropFailureFn(scope, {$event: e});
                            });
                        }
                    }
                }
                element.removeClass(draggingClass);
            }

            function setDragElement(e, dragImageElementId) {
                var dragImageElementFn;

                if (!(e && e.dataTransfer && e.dataTransfer.setDragImage)) {
                    return;
                }

                dragImageElementFn = $parse(dragImageElementId);

                scope.$apply(function() {
                    var elementId = dragImageElementFn(scope, {$event: e}),
                        dragElement;

                    if (!(elementId && angular.isString(elementId))) {
                        return;
                    }

                    dragElement = document.getElementById(elementId);

                    if (!dragElement) {
                        return;
                    }

                    e.dataTransfer.setDragImage(dragElement, 0, 0);
                });
            }

            function dragstartHandler(e) {
                var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);

                if (isDragAllowed) {
                    var sendChannel = attrs.dragChannel || 'defaultchannel';
                    var dragData = '';
                    if (attrs.drag) {
                        dragData = scope.$eval(attrs.drag);
                    }

                    var dragImage = attrs.dragImage || null;

                    element.addClass(draggingClass);
                    element.bind('$destroy', dragendHandler);

                    //Code to make sure that the setDragImage is available. IE 10, 11, and Opera do not support setDragImage.
                    var hasNativeDraggable = !(document.uniqueID || window.opera);

                    //If there is a draggable image passed in, then set the image to be dragged.
                    if (dragImage && hasNativeDraggable) {
                        var dragImageFn = $parse(attrs.dragImage);
                        scope.$apply(function() {
                            var dragImageParameters = dragImageFn(scope, {$event: e});
                            if (dragImageParameters) {
                                if (angular.isString(dragImageParameters)) {
                                    dragImageParameters = $dragImage.generate(dragImageParameters);
                                }
                                if (dragImageParameters.image) {
                                    var xOffset = dragImageParameters.xOffset || 0,
                                        yOffset = dragImageParameters.yOffset || 0;
                                    e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
                                }
                            }
                        });
                    } else if (attrs.dragImageElementId) {
                        setDragElement(e, attrs.dragImageElementId);
                    }

                    var transferDataObject = {data: dragData, channel: sendChannel};
                    var transferDataText = angular.toJson(transferDataObject);

                    e.dataTransfer.setData('text', transferDataText);
                    e.dataTransfer.effectAllowed = 'copyMove';

                    $rootScope.$broadcast('ANGULAR_DRAG_START', e, sendChannel, transferDataObject);
                }
                else {
                    e.preventDefault();
                }
            }
        };
    }
    ]);

    module.directive('uiOnDrop', ['$parse', '$rootScope', function($parse, $rootScope) {
        return function(scope, element, attr) {
            var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
            var dropChannel = attr.dropChannel || 'defaultchannel';
            var dragChannel = '';
            var dragEnterClass = attr.dragEnterClass || 'on-drag-enter';
            var dragHoverClass = attr.dragHoverClass || 'on-drag-hover';
            var customDragEnterEvent = $parse(attr.onDragEnter);
            var customDragLeaveEvent = $parse(attr.onDragLeave);

            function onDragOver(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                var uiOnDragOverFn = $parse(attr.uiOnDragOver);
                scope.$evalAsync(function() {
                    uiOnDragOverFn(scope, {$event: e, $channel: dropChannel});
                });

                return false;
            }

            function onDragLeave(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                dragging--;

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragLeaveEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.addClass(dragEnterClass);
                    element.removeClass(dragHoverClass);
                }

                var uiOnDragLeaveFn = $parse(attr.uiOnDragLeave);
                scope.$evalAsync(function() {
                    uiOnDragLeaveFn(scope, {$event: e, $channel: dropChannel});
                });
            }

            function onDragEnter(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragEnterEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.removeClass(dragEnterClass);
                    element.addClass(dragHoverClass);
                }
                dragging++;

                var uiOnDragEnterFn = $parse(attr.uiOnDragEnter);
                scope.$evalAsync(function() {
                    uiOnDragEnterFn(scope, {$event: e, $channel: dropChannel});
                });

                $rootScope.$broadcast('ANGULAR_HOVER', dragChannel);
            }

            function onDrop(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }
                if (e.stopPropagation) {
                    e.stopPropagation(); // Necessary. Allows us to drop.
                }

                var sendData = e.dataTransfer.getData('text');
                sendData = angular.fromJson(sendData);

                determineEffectAllowed(e);

                var uiOnDropFn = $parse(attr.uiOnDrop);
                scope.$evalAsync(function() {
                    uiOnDropFn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel});
                });
                element.removeClass(dragEnterClass);
                dragging = 0;
            }

            function isDragChannelAccepted(dragChannel, dropChannel) {
                if (dropChannel === '*') {
                    return true;
                }

                var channelMatchPattern = new RegExp('(\\s|[,])+(' + dragChannel + ')(\\s|[,])+', 'i');

                return channelMatchPattern.test(',' + dropChannel + ',');
            }

            function preventNativeDnD(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }
                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                e.dataTransfer.dropEffect = 'none';
                return false;
            }

            var deregisterDragStart = $rootScope.$on('ANGULAR_DRAG_START', function(_, e, channel, transferDataObject) {
                dragChannel = channel;

                var valid = true;

                if (!isDragChannelAccepted(channel, dropChannel)) {
                    valid = false;
                }

                if (valid && attr.dropValidate) {
                    var validateFn = $parse(attr.dropValidate);
                    valid = validateFn(scope, {
                        $drop: {scope: scope, element: element},
                        $event: e,
                        $data: transferDataObject.data,
                        $channel: transferDataObject.channel
                    });
                }

                if (valid) {
                    element.bind('dragover', onDragOver);
                    element.bind('dragenter', onDragEnter);
                    element.bind('dragleave', onDragLeave);
                    element.bind('drop', onDrop);

                    element.addClass(dragEnterClass);
                } else {
                    element.bind('dragover', preventNativeDnD);
                    element.bind('dragenter', preventNativeDnD);
                    element.bind('dragleave', preventNativeDnD);
                    element.bind('drop', preventNativeDnD);

                    element.removeClass(dragEnterClass);
                }

            });

            var deregisterDragEnd = $rootScope.$on('ANGULAR_DRAG_END', function() {
                element.unbind('dragover', onDragOver);
                element.unbind('dragenter', onDragEnter);
                element.unbind('dragleave', onDragLeave);

                element.unbind('drop', onDrop);
                element.removeClass(dragHoverClass);
                element.removeClass(dragEnterClass);

                element.unbind('dragover', preventNativeDnD);
                element.unbind('dragenter', preventNativeDnD);
                element.unbind('dragleave', preventNativeDnD);
                element.unbind('drop', preventNativeDnD);
            });

            scope.$on('$destroy', function() {
                deregisterDragStart();
                deregisterDragEnd();
            });

            attr.$observe('dropChannel', function(value) {
                if (value) {
                    dropChannel = value;
                }
            });

        };
    }
    ]);

    module.constant('$dragImageConfig', {
        height: 20,
        width: 200,
        padding: 10,
        font: 'bold 11px Arial',
        fontColor: '#eee8d5',
        backgroundColor: '#93a1a1',
        xOffset: 0,
        yOffset: 0
    });

    module.service('$dragImage', ['$dragImageConfig', function(defaultConfig) {
        var ELLIPSIS = '…';

        function fitString(canvas, text, config) {
            var width = canvas.measureText(text).width;
            if (width < config.width) {
                return text;
            }
            while (width + config.padding > config.width) {
                text = text.substring(0, text.length - 1);
                width = canvas.measureText(text + ELLIPSIS).width;
            }
            return text + ELLIPSIS;
        }

        this.generate = function(text, options) {
            var config = angular.extend({}, defaultConfig, options || {});
            var el = document.createElement('canvas');

            el.height = config.height;
            el.width = config.width;

            var canvas = el.getContext('2d');

            canvas.fillStyle = config.backgroundColor;
            canvas.fillRect(0, 0, config.width, config.height);
            canvas.font = config.font;
            canvas.fillStyle = config.fontColor;

            var title = fitString(canvas, text, config);
            canvas.fillText(title, 4, config.padding + 4);

            var image = new Image();
            image.src = el.toDataURL();

            return {
                image: image,
                xOffset: config.xOffset,
                yOffset: config.yOffset
            };
        };
    }
    ]);

}(angular));
<commit_msg>Calculate the position where the item is dropped This is passed as $position for the ui-on-drop function<commit_after>(function(angular) {
    'use strict';

    function isDnDsSupported() {
        return 'ondrag' in document.createElement('a');
    }

    function determineEffectAllowed(e) {
        // Chrome doesn't set dropEffect, so we have to work it out ourselves
        if (typeof e.dataTransfer !== 'undefined' && e.dataTransfer.dropEffect === 'none') {
            if (e.dataTransfer.effectAllowed === 'copy' ||
                e.dataTransfer.effectAllowed === 'move') {
                e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed;
            } else if (e.dataTransfer.effectAllowed === 'copyMove' || e.dataTransfer.effectAllowed === 'copymove') {
                e.dataTransfer.dropEffect = e.ctrlKey ? 'copy' : 'move';
            }
        }
    }

    if (!isDnDsSupported()) {
        angular.module('ang-drag-drop', []);
        return;
    }

    if (window.jQuery && (-1 === window.jQuery.event.props.indexOf('dataTransfer'))) {
        window.jQuery.event.props.push('dataTransfer');
    }

    var module = angular.module('ang-drag-drop', []);

    module.directive('uiDraggable', ['$parse', '$rootScope', '$dragImage', function($parse, $rootScope, $dragImage) {
        return function(scope, element, attrs) {
            var isDragHandleUsed = false,
                dragHandleClass,
                draggingClass = attrs.draggingClass || 'on-dragging',
                dragTarget;

            element.attr('draggable', false);

            scope.$watch(attrs.uiDraggable, function(newValue) {
                if (newValue) {
                    element.attr('draggable', newValue);
                    element.bind('dragend', dragendHandler);
                    element.bind('dragstart', dragstartHandler);
                }
                else {
                    element.removeAttr('draggable');
                    element.unbind('dragend', dragendHandler);
                    element.unbind('dragstart', dragstartHandler);
                }

            });

            if (angular.isString(attrs.dragHandleClass)) {
                isDragHandleUsed = true;
                dragHandleClass = attrs.dragHandleClass.trim() || 'drag-handle';

                element.bind('mousedown', function(e) {
                    dragTarget = e.target;
                });
            }

            function dragendHandler(e) {
                setTimeout(function() {
                    element.unbind('$destroy', dragendHandler);
                }, 0);
                var sendChannel = attrs.dragChannel || 'defaultchannel';
                $rootScope.$broadcast('ANGULAR_DRAG_END', e, sendChannel);

                determineEffectAllowed(e);

                if (e.dataTransfer && e.dataTransfer.dropEffect !== 'none') {
                    if (attrs.onDropSuccess) {
                        var onDropSuccessFn = $parse(attrs.onDropSuccess);
                        scope.$evalAsync(function() {
                            onDropSuccessFn(scope, {$event: e});
                        });
                    } else {
                        if (attrs.onDropFailure) {
                            var onDropFailureFn = $parse(attrs.onDropFailure);
                            scope.$evalAsync(function() {
                                onDropFailureFn(scope, {$event: e});
                            });
                        }
                    }
                }
                element.removeClass(draggingClass);
            }

            function setDragElement(e, dragImageElementId) {
                var dragImageElementFn;

                if (!(e && e.dataTransfer && e.dataTransfer.setDragImage)) {
                    return;
                }

                dragImageElementFn = $parse(dragImageElementId);

                scope.$apply(function() {
                    var elementId = dragImageElementFn(scope, {$event: e}),
                        dragElement;

                    if (!(elementId && angular.isString(elementId))) {
                        return;
                    }

                    dragElement = document.getElementById(elementId);

                    if (!dragElement) {
                        return;
                    }

                    e.dataTransfer.setDragImage(dragElement, 0, 0);
                });
            }

            function dragstartHandler(e) {
                var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);

                if (isDragAllowed) {
                    var sendChannel = attrs.dragChannel || 'defaultchannel';
                    var dragData = '';
                    if (attrs.drag) {
                        dragData = scope.$eval(attrs.drag);
                    }

                    var dragImage = attrs.dragImage || null;

                    element.addClass(draggingClass);
                    element.bind('$destroy', dragendHandler);

                    //Code to make sure that the setDragImage is available. IE 10, 11, and Opera do not support setDragImage.
                    var hasNativeDraggable = !(document.uniqueID || window.opera);

                    //If there is a draggable image passed in, then set the image to be dragged.
                    if (dragImage && hasNativeDraggable) {
                        var dragImageFn = $parse(attrs.dragImage);
                        scope.$apply(function() {
                            var dragImageParameters = dragImageFn(scope, {$event: e});
                            if (dragImageParameters) {
                                if (angular.isString(dragImageParameters)) {
                                    dragImageParameters = $dragImage.generate(dragImageParameters);
                                }
                                if (dragImageParameters.image) {
                                    var xOffset = dragImageParameters.xOffset || 0,
                                        yOffset = dragImageParameters.yOffset || 0;
                                    e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
                                }
                            }
                        });
                    } else if (attrs.dragImageElementId) {
                        setDragElement(e, attrs.dragImageElementId);
                    }

                    var offset = {x: e.originalEvent.offsetX, y: e.originalEvent.offsetY};
                    var transferDataObject = {data: dragData, channel: sendChannel, offset: offset};
                    var transferDataText = angular.toJson(transferDataObject);

                    e.dataTransfer.setData('text', transferDataText);
                    e.dataTransfer.effectAllowed = 'copyMove';

                    $rootScope.$broadcast('ANGULAR_DRAG_START', e, sendChannel, transferDataObject);
                }
                else {
                    e.preventDefault();
                }
            }
        };
    }
    ]);

    module.directive('uiOnDrop', ['$parse', '$rootScope', function($parse, $rootScope) {
        return function(scope, element, attr) {
            var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
            var dropChannel = attr.dropChannel || 'defaultchannel';
            var dragChannel = '';
            var dragEnterClass = attr.dragEnterClass || 'on-drag-enter';
            var dragHoverClass = attr.dragHoverClass || 'on-drag-hover';
            var customDragEnterEvent = $parse(attr.onDragEnter);
            var customDragLeaveEvent = $parse(attr.onDragLeave);

            function onDragOver(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                var uiOnDragOverFn = $parse(attr.uiOnDragOver);
                scope.$evalAsync(function() {
                    uiOnDragOverFn(scope, {$event: e, $channel: dropChannel});
                });

                return false;
            }

            function onDragLeave(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                dragging--;

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragLeaveEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.addClass(dragEnterClass);
                    element.removeClass(dragHoverClass);
                }

                var uiOnDragLeaveFn = $parse(attr.uiOnDragLeave);
                scope.$evalAsync(function() {
                    uiOnDragLeaveFn(scope, {$event: e, $channel: dropChannel});
                });
            }

            function onDragEnter(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragEnterEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.removeClass(dragEnterClass);
                    element.addClass(dragHoverClass);
                }
                dragging++;

                var uiOnDragEnterFn = $parse(attr.uiOnDragEnter);
                scope.$evalAsync(function() {
                    uiOnDragEnterFn(scope, {$event: e, $channel: dropChannel});
                });

                $rootScope.$broadcast('ANGULAR_HOVER', dragChannel);
            }

            function onDrop(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }
                if (e.stopPropagation) {
                    e.stopPropagation(); // Necessary. Allows us to drop.
                }

                var sendData = e.dataTransfer.getData('text');
                sendData = angular.fromJson(sendData);

                var position = {
                    x: e.originalEvent.offsetX - sendData.offset.x,
                    y: e.originalEvent.offsetY - sendData.offset.y
                };

                determineEffectAllowed(e);

                var uiOnDropFn = $parse(attr.uiOnDrop);
                scope.$evalAsync(function() {
                    uiOnDropFn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel, $position: position});
                });
                element.removeClass(dragEnterClass);
                dragging = 0;
            }

            function isDragChannelAccepted(dragChannel, dropChannel) {
                if (dropChannel === '*') {
                    return true;
                }

                var channelMatchPattern = new RegExp('(\\s|[,])+(' + dragChannel + ')(\\s|[,])+', 'i');

                return channelMatchPattern.test(',' + dropChannel + ',');
            }

            function preventNativeDnD(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }
                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                e.dataTransfer.dropEffect = 'none';
                return false;
            }

            var deregisterDragStart = $rootScope.$on('ANGULAR_DRAG_START', function(_, e, channel, transferDataObject) {
                dragChannel = channel;

                var valid = true;

                if (!isDragChannelAccepted(channel, dropChannel)) {
                    valid = false;
                }

                if (valid && attr.dropValidate) {
                    var validateFn = $parse(attr.dropValidate);
                    valid = validateFn(scope, {
                        $drop: {scope: scope, element: element},
                        $event: e,
                        $data: transferDataObject.data,
                        $channel: transferDataObject.channel
                    });
                }

                if (valid) {
                    element.bind('dragover', onDragOver);
                    element.bind('dragenter', onDragEnter);
                    element.bind('dragleave', onDragLeave);
                    element.bind('drop', onDrop);

                    element.addClass(dragEnterClass);
                } else {
                    element.bind('dragover', preventNativeDnD);
                    element.bind('dragenter', preventNativeDnD);
                    element.bind('dragleave', preventNativeDnD);
                    element.bind('drop', preventNativeDnD);

                    element.removeClass(dragEnterClass);
                }

            });

            var deregisterDragEnd = $rootScope.$on('ANGULAR_DRAG_END', function() {
                element.unbind('dragover', onDragOver);
                element.unbind('dragenter', onDragEnter);
                element.unbind('dragleave', onDragLeave);

                element.unbind('drop', onDrop);
                element.removeClass(dragHoverClass);
                element.removeClass(dragEnterClass);

                element.unbind('dragover', preventNativeDnD);
                element.unbind('dragenter', preventNativeDnD);
                element.unbind('dragleave', preventNativeDnD);
                element.unbind('drop', preventNativeDnD);
            });

            scope.$on('$destroy', function() {
                deregisterDragStart();
                deregisterDragEnd();
            });

            attr.$observe('dropChannel', function(value) {
                if (value) {
                    dropChannel = value;
                }
            });

        };
    }
    ]);

    module.constant('$dragImageConfig', {
        height: 20,
        width: 200,
        padding: 10,
        font: 'bold 11px Arial',
        fontColor: '#eee8d5',
        backgroundColor: '#93a1a1',
        xOffset: 0,
        yOffset: 0
    });

    module.service('$dragImage', ['$dragImageConfig', function(defaultConfig) {
        var ELLIPSIS = '…';

        function fitString(canvas, text, config) {
            var width = canvas.measureText(text).width;
            if (width < config.width) {
                return text;
            }
            while (width + config.padding > config.width) {
                text = text.substring(0, text.length - 1);
                width = canvas.measureText(text + ELLIPSIS).width;
            }
            return text + ELLIPSIS;
        }

        this.generate = function(text, options) {
            var config = angular.extend({}, defaultConfig, options || {});
            var el = document.createElement('canvas');

            el.height = config.height;
            el.width = config.width;

            var canvas = el.getContext('2d');

            canvas.fillStyle = config.backgroundColor;
            canvas.fillRect(0, 0, config.width, config.height);
            canvas.font = config.font;
            canvas.fillStyle = config.fontColor;

            var title = fitString(canvas, text, config);
            canvas.fillText(title, 4, config.padding + 4);

            var image = new Image();
            image.src = el.toDataURL();

            return {
                image: image,
                xOffset: config.xOffset,
                yOffset: config.yOffset
            };
        };
    }
    ]);

}(angular));
<|endoftext|>


<commit_before>const path = require('path');
const bodyParser = require('body-parser');
const server = require('express')();
const Queue = require('promise-queue');
const {version} = require('../package.json');
const {stage, sync, github} = require('./core');
const log = require('./logger');

const PORT = process.env.PORT || 3000;
const DEPLOY_DIR = path.resolve('/tmp/.stage-ci');
const queue = new Queue(1, process.env.STAGE_CI_MAX_QUEUE || 100);

server.use(bodyParser.json());

server.get('/', (request, response) => {
  response.json({version, queue});
});

server.post('/', (request, response) => {
  let result;
  try {
    const {headers, body} = request;
    result = github({headers, body});
  } catch (error) {
    if (error.asJson && error.asJson.error && error.asJson.error.type === 'fatal') {
      response.status(500).send(error.asJson);
      return;
    }
  }
  const {success, ref, sha, name, alias, cloneUrl, setStatus, deploy} = result;
  response.sendStatus((success) ? 200 : 204);
  if (!success) return;

  queue.add(async () => {
    log.info(`> Deploying ${name}@${ref}#${sha} to ${alias}`);
    const localDirectory = path.join(DEPLOY_DIR, name);

    try {
      await deploy();
      await setStatus('pending', 'Staging...');
      await sync(cloneUrl, localDirectory, {ref, checkout: sha});
      await stage(localDirectory, {alias});
      await setStatus('success', 'Deployed to Now', alias);
    } catch (error) {
      log.error(error.stack);
      await setStatus('error', 'Error', alias);
    }

    log.info('> Done!');
  });
});

server.listen(PORT, () => {
  log.info(`Server listening on ${PORT}... `);
});
<commit_msg>log more errors<commit_after>const path = require('path');
const bodyParser = require('body-parser');
const server = require('express')();
const Queue = require('promise-queue');
const {version} = require('../package.json');
const {stage, sync, github} = require('./core');
const log = require('./logger');

const PORT = process.env.PORT || 3000;
const DEPLOY_DIR = path.resolve('/tmp/.stage-ci');
const queue = new Queue(1, process.env.STAGE_CI_MAX_QUEUE || 100);

server.use(bodyParser.json());

server.get('/', (request, response) => {
  response.json({version, queue});
});

server.post('/', (request, response) => {
  let result;
  try {
    const {headers, body} = request;
    result = github({headers, body});
  } catch (error) {
    if (error.asJson && error.asJson.error && error.asJson.error.type === 'fatal') {
      response.status(500).send(error.asJson);
      return;
    }
  }
  const {success, ref, sha, name, alias, cloneUrl, setStatus, deploy} = result;
  response.sendStatus((success) ? 200 : 204);
  if (!success) return;

  queue.add(async () => {
    log.info(`> Deploying ${name}@${ref}#${sha} to ${alias}`);
    const localDirectory = path.join(DEPLOY_DIR, name);

    try {
      await deploy();
      await setStatus('pending', 'Staging...');
      await sync(cloneUrl, localDirectory, {ref, checkout: sha});
      await stage(localDirectory, {alias});
      await setStatus('success', 'Deployed to Now', alias);
    } catch (error) {
      log.error(error.stack);
      if (error.response) {
        log.error(error.response.data.message);
        log.error(error.response.data.errors);
        log.error(error.response.data.documentation_url);
      }
      await setStatus('error', 'Error', alias);
    }

    log.info('> Done!');
  });
});

server.listen(PORT, () => {
  log.info(`Server listening on ${PORT}... `);
});
<|endoftext|>


<commit_before>/**
 * Copyright 2018 Google LLC
 *
 * 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.
 */

import path from 'path';
import loaderUtils from 'loader-utils';

export default function loader () { }

loader.pitch = function (request) {
  const options = loaderUtils.getOptions(this) || {};
  const multi = options.multiple || options.multi || options.singleton === false;

  return `
    import {Comlink} from 'comlinkjs';
    ${multi ? '' : 'var inst;'}
    export default function f() {
      ${multi ? 'var inst =' : 'inst = inst ||'} Comlink.proxy(require('!worker-loader?${JSON.stringify(options)}!${path.resolve(__dirname, 'comlink-worker-loader.js')}!${request}')());
      return this instanceof f ? new inst : inst;
    }
  `.replace(/\n\s*/g, '');
};
<commit_msg>Fix: sanitise options before passing to worker-loader<commit_after>/**
 * Copyright 2018 Google LLC
 *
 * 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.
 */

import path from 'path';
import loaderUtils from 'loader-utils';

const comlinkLoaderSpecificOptions = ['multiple', 'multi', 'singleton'];

export default function loader () { }

loader.pitch = function (request) {
  const options = loaderUtils.getOptions(this) || {};
  const multi = options.multiple || options.multi || options.singleton === false;
  const workerLoaderOptions = { ...options };

  comlinkLoaderSpecificOptions.forEach(key => {
    if (workerLoaderOptions[key]) {
      delete workerLoaderOptions[key];
    }
  });

  return `
    import {Comlink} from 'comlinkjs';
    ${multi ? '' : 'var inst;'}
    export default function f() {
      ${multi ? 'var inst =' : 'inst = inst ||'} Comlink.proxy(require('!worker-loader?${JSON.stringify(workerLoaderOptions)}!${path.resolve(__dirname, 'comlink-worker-loader.js')}!${request}')());
      return this instanceof f ? new inst : inst;
    }
  `.replace(/\n\s*/g, '');
};
<|endoftext|>


<commit_before>const request = require('request');
const baseApiUrl = 'https://www.googleapis.com/fitness/v1/users/me/dataSources/derived%3Acom.google.step_count.delta%3Acom.google.android.gms%3Aestimated_steps/datasets/';
const keyParam = '?key=' + process.env.FIT_API_KEY;
const db = require('../models');

const testing = true; // prevents excessive api calls, change to false to get real data

// This route returns the latest user walking data by contacting the fit api
// It responds with a JSON file containing the users current total distance
const getFitData = (req, res) => {
	// setup API call
	let startTime = convertToNanoS(res.locals.currentUser.fitData.lastUpdate);
	let now = Date.now();
	let endTime = convertToNanoS(now);

	console.log('start: ' + startTime);
	console.log('endTime: ' + endTime);

	const apiURL = `${baseApiUrl}${startTime}-${endTime}${keyParam}`;
	const options = {
		url: apiURL,
		headers: {
			Authorization: 'Bearer ' + res.locals.currentUser.google.accessToken
		}
	};

	// make api call
	if (!testing){ // collect data from fit api

	request(options, (err, response, body) => {
		if (err) return console.log(err);

		// Parse and total step count from JSON data
		let steps = totalSteps(JSON.parse(body).point);
		let currentUserSteps = res.locals.currentUser.fitData.totalSteps;
		let newStepTotal = currentUserSteps + steps;

		// check values in console
		console.log('previous steps: ' + currentUserSteps);
		console.log('recent steps: ' + steps);
		console.log('new stepTotal should be: ' + newStepTotal);

		// add new steps to user and change lastUpdate to now
		db.User.findOneAndUpdate(
			{"google.id": res.locals.currentUser.google.id},
			{
				fitData: {
					totalSteps: newStepTotal,
					lastUpdate: now
				}
			},
			{new: true},
			(err, user) => {
				if (err) return console.log(err);
				console.log(user.fitData.totalSteps);

				// data sent to user
				res.json({'totalDistance': user.getTotalDistance()});
			});
	});
	} else {
		// NOTE: SENDING FAKE DATA FOR TESTING
		res.json({'totalDistance': 225.34521});
	}
};

//***********
//GOAL CONTROLLERS
//*************

const getGoals = (req, res) => {
	// return all the goals
	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		if (err) return console.log(err);
		res.json({goals: user.goals});
	});
};

const postGoal = (req,res) => {
	// req should contain json with goal name, target date, target miles
	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		if (err) return console.log(err);
		// process the query string (get name and milage from str format 'name - nn.n mi')
		let targetName = req.query.name.split(' - ')[0];
		let targetDistance = parseFloat(req.query.name.split(' - ')[1]);
		// create a new goal
		let newGoal = {
			start: {
				distance: user.getTotalDistance()
			},
			target: {
				name: targetName,
				date: new Date(req.query.date),
				distance: targetDistance
			},
			complete: false
		};
		// add goal to user document
		user.goals.push(newGoal);
		user.save(function(err){
			newGoal = user.goals[user.goals.length -1];
			console.log(newGoal);
			res.json(newGoal);
		});
		// save new goal to db and return new goal response
	});
};

const updateGoal = (req, res) => {
	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		// find the requested goal
		console.log('update');
		let goalIndex = user.goals.findIndex((goal) => {
			return goal._id == req.params.id;
		});
		console.log(user.goals[goalIndex]);
		console.log(req.body);
		user.goals[goalIndex].complete = req.body.complete;
		user.save(err => {
			if (err) {
				console.log(err);
				res.json(err);
			}
			res.json(user.goals[goalIndex]);

		});
	});
};

const deleteGoal = (req, res) => {
	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		if (err) return console.log(err);
		console.log(req.params.id);
		let goalIndex = user.goals.findIndex((goal) => {
			return goal._id == req.params.id;
		});
		console.log(goalIndex);
		user.goals.splice(goalIndex, 1);
		user.save(err => {
			if (err) throw err;
			res.json({message: 'goal deleted'});
		});
	});
};

module.exports = { getFitData, postGoal, deleteGoal, getGoals, updateGoal };

//****************
//HELPER FUNCTIONS
//****************

// take data from fit api call and reduce into total steps
function totalSteps(data){
	let total = 0;
	console.log('length: ' + data.length);
	//TODO: use reduce instead of foreach
	data.forEach(point => {
		total += point.value[0].intVal;
	});
	return total;
}

// convert time to nanoseconds (required by fit API)
function convertToNanoS(dateStr){
	const date = new Date(dateStr);
	const dateNano = date.getTime() * 1000000;
	return dateNano;
}
<commit_msg>Refactor user controller methods<commit_after>const request = require('request');
const db = require('../models');

const baseApiUrl = 'https://www.googleapis.com/fitness/v1/users/me/dataSources/derived%3Acom.google.step_count.delta%3Acom.google.android.gms%3Aestimated_steps/datasets/';
const keyParam = '?key=' + process.env.FIT_API_KEY;

const testing = false; // set to false to get real data, true to use fake api call

/**************
 * GOOGLE FIT *
 **************/

const getFitData = (req, res) => {
	/**
	 *This route returns the latest user walking data by contacting the fit api
	 * It responds with a JSON file containing the users current total distance
	 */

	/* setup API call parameters */
	const startTime = convertToNanoS(res.locals.currentUser.fitData.lastUpdate);
	const now = Date.now();
	const endTime = convertToNanoS(now);
	const apiURL = `${baseApiUrl}${startTime}-${endTime}${keyParam}`;
	const options = {
		url: apiURL,
		headers: {
			Authorization: 'Bearer ' + res.locals.currentUser.google.accessToken
		}
	};

	/* make api call */
	if (!testing){
	request(options, (err, response, body) => {
		if (err) return console.log(err);

		/* Parse and total step count from JSON response data */
		const steps = totalSteps(JSON.parse(body).point);
		const currentUserSteps = res.locals.currentUser.fitData.totalSteps;
		const newStepTotal = currentUserSteps + steps;

		/* add new steps to user and change lastUpdate to now */
		db.User.findOneAndUpdate({"google.id": res.locals.currentUser.google.id},
			{ /* data to update */
				fitData: {
					totalSteps: newStepTotal,
					lastUpdate: now
				}
			},
			/* respond with updated user milage */
			{new: true},
			(err, user) => {
				if (err) return console.log(err);
				res.json({'totalDistance': user.getTotalDistance()});
			});
	});
	} else {
		// NOTE: SENDING FAKE DATA FOR TESTING
		res.json({'totalDistance': 225.34521});
	}
};

/*********
 * GOALS *
 *********/

const getGoals = (req, res) => {
	/* return all the users goals */
	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		if (err) return console.log(err);
		res.json({goals: user.goals});
	});
};

const postGoal = (req,res) => {
	/**
	 *Post a new goal with the form data given in the query string
	 *The query should contain the name (destination and distance from start)
	 *  and the target completion date.
	 *Respond with the newly created goal
	 */

	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		if (err) return console.log(err);
		/* create goal according to schema */
		const newGoal = {
			start: {
				distance: user.getTotalDistance()
			},
			target: {
				name: req.query.name.split(' - ')[0], // parse target name from query.name
				date: new Date(req.query.date),
				distance: parseFloat(req.query.name.split(' - ')[1]) // parse distance from query.name
			},
			complete: false
		};
		/* add goal to user, save, respond with the goal */
		user.goals.push(newGoal);
		user.save(err => { res.json(user.goals[user.goals.length - 1]); });
	});
};

const updateGoal = (req, res) => {
	/**
	 *Find goal by req param id and update its completion status with the req JSON data
	 *Respond with the updated goal
	 */
	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		if (err) return console.log(err);

		const goalIndex = user.goals.findIndex(goal => (goal._id == req.params.id));

		user.goals[goalIndex].complete = req.body.complete;
		user.save(err => {
			if (err) throw err;
			res.json(user.goals[goalIndex]);
		});
	});
};

const deleteGoal = (req, res) => {
	/**
	 *Find the goal by the ID given the req params and remove it from the current user
	 *Respond with a confirmation message
	 */
	db.User.findOne({"google.id": res.locals.currentUser.google.id}, (err, user) => {
		if (err) return console.log(err);

		const goalIndex = user.goals.findIndex(goal => (goal._id == req.params.id));

		user.goals.splice(goalIndex, 1);
		user.save(err => {
			if (err) throw err;
			res.json({message: 'goal deleted'});
		});
	});
};

module.exports = { getFitData, postGoal, deleteGoal, getGoals, updateGoal };

/********************
 * HELPER FUNCTIONS *
 ********************/

function totalSteps(data){
	/* take data from fit api call and reduce into total steps */
	return data.reduce((sum, point) => sum + point.value[0].intVal, 0);
}

function convertToNanoS(dateStr){
	/* convert time to nanoseconds (required by fit API) */
	return (new Date(dateStr)).getTime() * 1000000;
}
<|endoftext|>


<commit_before>(function(angular) {
    'use strict';

    function isDnDsSupported() {
        return 'ondrag' in document.createElement('a');
    }

    if (!isDnDsSupported()) {
        angular.module('ang-drag-drop', []);
        return;
    }

    if (window.jQuery && (-1 === window.jQuery.event.props.indexOf('dataTransfer'))) {
        window.jQuery.event.props.push('dataTransfer');
    }

    var currentData;

    var module = angular.module('ang-drag-drop', []);

    module.directive('uiDraggable', ['$parse', '$rootScope', '$dragImage', function($parse, $rootScope, $dragImage) {
        return function(scope, element, attrs) {
            var isDragHandleUsed = false,
                dragHandleClass,
                draggingClass = attrs.draggingClass || 'on-dragging',
                dragTarget;

            element.attr('draggable', false);

            scope.$watch(attrs.uiDraggable, function(newValue) {
                if (newValue) {
                    element.attr('draggable', newValue);
                    element.bind('dragend', dragendHandler);
                    element.bind('dragstart', dragstartHandler);
                }
                else {
                    element.removeAttr('draggable');
                    element.unbind('dragend', dragendHandler);
                    element.unbind('dragstart', dragstartHandler);
                }

            });

            if (angular.isString(attrs.dragHandleClass)) {
                isDragHandleUsed = true;
                dragHandleClass = attrs.dragHandleClass.trim() || 'drag-handle';

                element.bind('mousedown', function(e) {
                    dragTarget = e.target;
                });
            }

            function dragendHandler(e) {
                setTimeout(function() {
                    element.unbind('$destroy', dragendHandler);
                }, 0);
                var sendChannel = attrs.dragChannel || 'defaultchannel';
                $rootScope.$broadcast('ANGULAR_DRAG_END', sendChannel);
                if (e.dataTransfer && e.dataTransfer.dropEffect !== 'none') {
                    if (attrs.onDropSuccess) {
                        var onDropSuccessFn = $parse(attrs.onDropSuccess);
                        scope.$evalAsync(function() {
                            onDropSuccessFn(scope, {$event: e});
                        });
                    } else {
                        if (attrs.onDropFailure) {
                            var onDropFailureFn = $parse(attrs.onDropFailure);
                            scope.$evalAsync(function() {
                                onDropFailureFn(scope, {$event: e});
                            });
                        }
                    }
                }
                element.removeClass(draggingClass);
            }

            function dragstartHandler(e) {
                var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);

                if (isDragAllowed) {
                    var sendChannel = attrs.dragChannel || 'defaultchannel';
                    var dragData = '';
                    if (attrs.drag) {
                        dragData = scope.$eval(attrs.drag);
                    }
                    var sendData = angular.toJson({data: dragData, channel: sendChannel});
                    var dragImage = attrs.dragImage || null;

                    element.addClass(draggingClass);
                    element.bind('$destroy', dragendHandler);

                    if (dragImage) {
                        var dragImageFn = $parse(attrs.dragImage);
                        scope.$apply(function() {
                            var dragImageParameters = dragImageFn(scope, {$event: e});
                            if (dragImageParameters) {
                                if (angular.isString(dragImageParameters)) {
                                    dragImageParameters = $dragImage.generate(dragImageParameters);
                                }
                                if (dragImageParameters.image) {
                                    var xOffset = dragImageParameters.xOffset || 0,
                                        yOffset = dragImageParameters.yOffset || 0;
                                    e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
                                }
                            }
                        });
                    }

                    e.dataTransfer.setData('text', sendData);
                    currentData = angular.fromJson(sendData);
                    e.dataTransfer.effectAllowed = 'copyMove';
                    $rootScope.$broadcast('ANGULAR_DRAG_START', sendChannel, currentData.data);
                }
                else {
                    e.preventDefault();
                }
            }
        };
    }
    ]);

    module.directive('uiOnDrop', ['$parse', '$rootScope', function($parse, $rootScope) {
        return function(scope, element, attr) {
            var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
            var dropChannel = attr.dropChannel || 'defaultchannel';
            var dragChannel = '';
            var dragEnterClass = attr.dragEnterClass || 'on-drag-enter';
            var dragHoverClass = attr.dragHoverClass || 'on-drag-hover';
            var customDragEnterEvent = $parse(attr.onDragEnter);
            var customDragLeaveEvent = $parse(attr.onDragLeave);

            function onDragOver(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                var uiOnDragOverFn = $parse(attr.uiOnDragOver);
                scope.$evalAsync(function() {
                    uiOnDragOverFn(scope, {$event: e, $channel: dropChannel});
                });

                return false;
            }

            function onDragLeave(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                dragging--;

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragLeaveEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.removeClass(dragHoverClass);
                }

                var uiOnDragLeaveFn = $parse(attr.uiOnDragLeave);
                scope.$evalAsync(function() {
                    uiOnDragLeaveFn(scope, {$event: e, $channel: dropChannel});
                });
            }

            function onDragEnter(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragEnterEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.addClass(dragHoverClass);
                }
                dragging++;

                var uiOnDragEnterFn = $parse(attr.uiOnDragEnter);
                scope.$evalAsync(function() {
                    uiOnDragEnterFn(scope, {$event: e, $channel: dropChannel});
                });

                $rootScope.$broadcast('ANGULAR_HOVER', dragChannel);
            }

            function onDrop(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }
                if (e.stopPropagation) {
                    e.stopPropagation(); // Necessary. Allows us to drop.
                }

                var sendData = e.dataTransfer.getData('text');
                sendData = angular.fromJson(sendData);

                // Chrome doesn't set dropEffect, so we have to work it out ourselves
                if (e.dataTransfer.dropEffect === 'none') {
                    if (e.dataTransfer.effectAllowed === 'copy' ||
                        e.dataTransfer.effectAllowed === 'move') {
                        e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed;
                    } else if (e.dataTransfer.effectAllowed === 'copyMove') {
                        e.dataTransfer.dropEffect = e.ctrlKey ? 'copy' : 'move';
                    }
                }

                var uiOnDropFn = $parse(attr.uiOnDrop);
                scope.$evalAsync(function() {
                    uiOnDropFn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel});
                });
                element.removeClass(dragEnterClass);
                dragging = 0;
            }

            function isDragChannelAccepted(dragChannel, dropChannel) {
                if (dropChannel === '*') {
                    return true;
                }

                var channelMatchPattern = new RegExp('(\\s|[,])+(' + dragChannel + ')(\\s|[,])+', 'i');

                return channelMatchPattern.test(',' + dropChannel + ',');
            }

            function preventNativeDnD(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }
                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                e.dataTransfer.dropEffect = 'none';
                return false;
            }

            var deregisterDragStart = $rootScope.$on('ANGULAR_DRAG_START', function(event, channel) {
                dragChannel = channel;
                if (isDragChannelAccepted(channel, dropChannel)) {
                    if (attr.dropValidate) {
                        var validateFn = $parse(attr.dropValidate);
                        var valid = validateFn(scope, {$data: currentData.data, $channel: currentData.channel});
                        if (!valid) {
                            element.bind('dragover', preventNativeDnD);
                            element.bind('dragenter', preventNativeDnD);
                            element.bind('dragleave', preventNativeDnD);
                            element.bind('drop', preventNativeDnD);
                            return;
                        }
                    }

                    element.bind('dragover', onDragOver);
                    element.bind('dragenter', onDragEnter);
                    element.bind('dragleave', onDragLeave);

                    element.bind('drop', onDrop);
                    element.addClass(dragEnterClass);
                }
                else {
                    element.bind('dragover', preventNativeDnD);
                    element.bind('dragenter', preventNativeDnD);
                    element.bind('dragleave', preventNativeDnD);
                    element.bind('drop', preventNativeDnD);
                }

            });

            var deregisterDragEnd = $rootScope.$on('ANGULAR_DRAG_END', function(e, channel) {
                dragChannel = '';
                if (isDragChannelAccepted(channel, dropChannel)) {

                    element.unbind('dragover', onDragOver);
                    element.unbind('dragenter', onDragEnter);
                    element.unbind('dragleave', onDragLeave);

                    element.unbind('drop', onDrop);
                    element.removeClass(dragHoverClass);
                    element.removeClass(dragEnterClass);
                }

                element.unbind('dragover', preventNativeDnD);
                element.unbind('dragenter', preventNativeDnD);
                element.unbind('dragleave', preventNativeDnD);
                element.unbind('drop', preventNativeDnD);
            });

            var deregisterDragHover = $rootScope.$on('ANGULAR_HOVER', function(e, channel) {
                if (isDragChannelAccepted(channel, dropChannel)) {
                    element.removeClass(dragHoverClass);
                }
            });

            scope.$on('$destroy', function() {
                deregisterDragStart();
                deregisterDragEnd();
                deregisterDragHover();
            });

            scope.$watch(attr.dropChannel, function(value) {
                if (value) {
                    dropChannel = value;
                }
            });

        };
    }
    ]);

    module.constant('$dragImageConfig', {
        height: 20,
        width: 200,
        padding: 10,
        font: 'bold 11px Arial',
        fontColor: '#eee8d5',
        backgroundColor: '#93a1a1',
        xOffset: 0,
        yOffset: 0
    });

    module.service('$dragImage', ['$dragImageConfig', function(defaultConfig) {
        var ELLIPSIS = '…';

        function fitString(canvas, text, config) {
            var width = canvas.measureText(text).width;
            if (width < config.width) {
                return text;
            }
            while (width + config.padding > config.width) {
                text = text.substring(0, text.length - 1);
                width = canvas.measureText(text + ELLIPSIS).width;
            }
            return text + ELLIPSIS;
        }

        this.generate = function(text, options) {
            var config = angular.extend({}, defaultConfig, options || {});
            var el = document.createElement('canvas');

            el.height = config.height;
            el.width = config.width;

            var canvas = el.getContext('2d');

            canvas.fillStyle = config.backgroundColor;
            canvas.fillRect(0, 0, config.width, config.height);
            canvas.font = config.font;
            canvas.fillStyle = config.fontColor;

            var title = fitString(canvas, text, config);
            canvas.fillText(title, 4, config.padding + 4);

            var image = new Image();
            image.src = el.toDataURL();

            return {
                image: image,
                xOffset: config.xOffset,
                yOffset: config.yOffset
            };
        };
    }
    ]);

}(angular));
<commit_msg>Revert dropChannel to attr.$observe<commit_after>(function(angular) {
    'use strict';

    function isDnDsSupported() {
        return 'ondrag' in document.createElement('a');
    }

    if (!isDnDsSupported()) {
        angular.module('ang-drag-drop', []);
        return;
    }

    if (window.jQuery && (-1 === window.jQuery.event.props.indexOf('dataTransfer'))) {
        window.jQuery.event.props.push('dataTransfer');
    }

    var currentData;

    var module = angular.module('ang-drag-drop', []);

    module.directive('uiDraggable', ['$parse', '$rootScope', '$dragImage', function($parse, $rootScope, $dragImage) {
        return function(scope, element, attrs) {
            var isDragHandleUsed = false,
                dragHandleClass,
                draggingClass = attrs.draggingClass || 'on-dragging',
                dragTarget;

            element.attr('draggable', false);

            scope.$watch(attrs.uiDraggable, function(newValue) {
                if (newValue) {
                    element.attr('draggable', newValue);
                    element.bind('dragend', dragendHandler);
                    element.bind('dragstart', dragstartHandler);
                }
                else {
                    element.removeAttr('draggable');
                    element.unbind('dragend', dragendHandler);
                    element.unbind('dragstart', dragstartHandler);
                }

            });

            if (angular.isString(attrs.dragHandleClass)) {
                isDragHandleUsed = true;
                dragHandleClass = attrs.dragHandleClass.trim() || 'drag-handle';

                element.bind('mousedown', function(e) {
                    dragTarget = e.target;
                });
            }

            function dragendHandler(e) {
                setTimeout(function() {
                    element.unbind('$destroy', dragendHandler);
                }, 0);
                var sendChannel = attrs.dragChannel || 'defaultchannel';
                $rootScope.$broadcast('ANGULAR_DRAG_END', sendChannel);
                if (e.dataTransfer && e.dataTransfer.dropEffect !== 'none') {
                    if (attrs.onDropSuccess) {
                        var onDropSuccessFn = $parse(attrs.onDropSuccess);
                        scope.$evalAsync(function() {
                            onDropSuccessFn(scope, {$event: e});
                        });
                    } else {
                        if (attrs.onDropFailure) {
                            var onDropFailureFn = $parse(attrs.onDropFailure);
                            scope.$evalAsync(function() {
                                onDropFailureFn(scope, {$event: e});
                            });
                        }
                    }
                }
                element.removeClass(draggingClass);
            }

            function dragstartHandler(e) {
                var isDragAllowed = !isDragHandleUsed || dragTarget.classList.contains(dragHandleClass);

                if (isDragAllowed) {
                    var sendChannel = attrs.dragChannel || 'defaultchannel';
                    var dragData = '';
                    if (attrs.drag) {
                        dragData = scope.$eval(attrs.drag);
                    }
                    var sendData = angular.toJson({data: dragData, channel: sendChannel});
                    var dragImage = attrs.dragImage || null;

                    element.addClass(draggingClass);
                    element.bind('$destroy', dragendHandler);

                    if (dragImage) {
                        var dragImageFn = $parse(attrs.dragImage);
                        scope.$apply(function() {
                            var dragImageParameters = dragImageFn(scope, {$event: e});
                            if (dragImageParameters) {
                                if (angular.isString(dragImageParameters)) {
                                    dragImageParameters = $dragImage.generate(dragImageParameters);
                                }
                                if (dragImageParameters.image) {
                                    var xOffset = dragImageParameters.xOffset || 0,
                                        yOffset = dragImageParameters.yOffset || 0;
                                    e.dataTransfer.setDragImage(dragImageParameters.image, xOffset, yOffset);
                                }
                            }
                        });
                    }

                    e.dataTransfer.setData('text', sendData);
                    currentData = angular.fromJson(sendData);
                    e.dataTransfer.effectAllowed = 'copyMove';
                    $rootScope.$broadcast('ANGULAR_DRAG_START', sendChannel, currentData.data);
                }
                else {
                    e.preventDefault();
                }
            }
        };
    }
    ]);

    module.directive('uiOnDrop', ['$parse', '$rootScope', function($parse, $rootScope) {
        return function(scope, element, attr) {
            var dragging = 0; //Ref. http://stackoverflow.com/a/10906204
            var dropChannel = attr.dropChannel || 'defaultchannel';
            var dragChannel = '';
            var dragEnterClass = attr.dragEnterClass || 'on-drag-enter';
            var dragHoverClass = attr.dragHoverClass || 'on-drag-hover';
            var customDragEnterEvent = $parse(attr.onDragEnter);
            var customDragLeaveEvent = $parse(attr.onDragLeave);

            function onDragOver(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                var uiOnDragOverFn = $parse(attr.uiOnDragOver);
                scope.$evalAsync(function() {
                    uiOnDragOverFn(scope, {$event: e, $channel: dropChannel});
                });

                return false;
            }

            function onDragLeave(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                dragging--;

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragLeaveEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.removeClass(dragHoverClass);
                }

                var uiOnDragLeaveFn = $parse(attr.uiOnDragLeave);
                scope.$evalAsync(function() {
                    uiOnDragLeaveFn(scope, {$event: e, $channel: dropChannel});
                });
            }

            function onDragEnter(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }

                if (e.stopPropagation) {
                    e.stopPropagation();
                }

                if (dragging === 0) {
                    scope.$evalAsync(function() {
                        customDragEnterEvent(scope, {$event: e, $channel: dropChannel});
                    });
                    element.addClass(dragHoverClass);
                }
                dragging++;

                var uiOnDragEnterFn = $parse(attr.uiOnDragEnter);
                scope.$evalAsync(function() {
                    uiOnDragEnterFn(scope, {$event: e, $channel: dropChannel});
                });

                $rootScope.$broadcast('ANGULAR_HOVER', dragChannel);
            }

            function onDrop(e) {
                if (e.preventDefault) {
                    e.preventDefault(); // Necessary. Allows us to drop.
                }
                if (e.stopPropagation) {
                    e.stopPropagation(); // Necessary. Allows us to drop.
                }

                var sendData = e.dataTransfer.getData('text');
                sendData = angular.fromJson(sendData);

                // Chrome doesn't set dropEffect, so we have to work it out ourselves
                if (e.dataTransfer.dropEffect === 'none') {
                    if (e.dataTransfer.effectAllowed === 'copy' ||
                        e.dataTransfer.effectAllowed === 'move') {
                        e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed;
                    } else if (e.dataTransfer.effectAllowed === 'copyMove') {
                        e.dataTransfer.dropEffect = e.ctrlKey ? 'copy' : 'move';
                    }
                }

                var uiOnDropFn = $parse(attr.uiOnDrop);
                scope.$evalAsync(function() {
                    uiOnDropFn(scope, {$data: sendData.data, $event: e, $channel: sendData.channel});
                });
                element.removeClass(dragEnterClass);
                dragging = 0;
            }

            function isDragChannelAccepted(dragChannel, dropChannel) {
                if (dropChannel === '*') {
                    return true;
                }

                var channelMatchPattern = new RegExp('(\\s|[,])+(' + dragChannel + ')(\\s|[,])+', 'i');

                return channelMatchPattern.test(',' + dropChannel + ',');
            }

            function preventNativeDnD(e) {
                if (e.preventDefault) {
                    e.preventDefault();
                }
                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                e.dataTransfer.dropEffect = 'none';
                return false;
            }

            var deregisterDragStart = $rootScope.$on('ANGULAR_DRAG_START', function(event, channel) {
                dragChannel = channel;
                if (isDragChannelAccepted(channel, dropChannel)) {
                    if (attr.dropValidate) {
                        var validateFn = $parse(attr.dropValidate);
                        var valid = validateFn(scope, {$data: currentData.data, $channel: currentData.channel});
                        if (!valid) {
                            element.bind('dragover', preventNativeDnD);
                            element.bind('dragenter', preventNativeDnD);
                            element.bind('dragleave', preventNativeDnD);
                            element.bind('drop', preventNativeDnD);
                            return;
                        }
                    }

                    element.bind('dragover', onDragOver);
                    element.bind('dragenter', onDragEnter);
                    element.bind('dragleave', onDragLeave);

                    element.bind('drop', onDrop);
                    element.addClass(dragEnterClass);
                }
                else {
                    element.bind('dragover', preventNativeDnD);
                    element.bind('dragenter', preventNativeDnD);
                    element.bind('dragleave', preventNativeDnD);
                    element.bind('drop', preventNativeDnD);
                }

            });

            var deregisterDragEnd = $rootScope.$on('ANGULAR_DRAG_END', function(e, channel) {
                dragChannel = '';
                if (isDragChannelAccepted(channel, dropChannel)) {

                    element.unbind('dragover', onDragOver);
                    element.unbind('dragenter', onDragEnter);
                    element.unbind('dragleave', onDragLeave);

                    element.unbind('drop', onDrop);
                    element.removeClass(dragHoverClass);
                    element.removeClass(dragEnterClass);
                }

                element.unbind('dragover', preventNativeDnD);
                element.unbind('dragenter', preventNativeDnD);
                element.unbind('dragleave', preventNativeDnD);
                element.unbind('drop', preventNativeDnD);
            });

            var deregisterDragHover = $rootScope.$on('ANGULAR_HOVER', function(e, channel) {
                if (isDragChannelAccepted(channel, dropChannel)) {
                    element.removeClass(dragHoverClass);
                }
            });

            scope.$on('$destroy', function() {
                deregisterDragStart();
                deregisterDragEnd();
                deregisterDragHover();
            });

            attr.$observe('dropChannel', function(value) {
                if (value) {
                    dropChannel = value;
                }
            });

        };
    }
    ]);

    module.constant('$dragImageConfig', {
        height: 20,
        width: 200,
        padding: 10,
        font: 'bold 11px Arial',
        fontColor: '#eee8d5',
        backgroundColor: '#93a1a1',
        xOffset: 0,
        yOffset: 0
    });

    module.service('$dragImage', ['$dragImageConfig', function(defaultConfig) {
        var ELLIPSIS = '…';

        function fitString(canvas, text, config) {
            var width = canvas.measureText(text).width;
            if (width < config.width) {
                return text;
            }
            while (width + config.padding > config.width) {
                text = text.substring(0, text.length - 1);
                width = canvas.measureText(text + ELLIPSIS).width;
            }
            return text + ELLIPSIS;
        }

        this.generate = function(text, options) {
            var config = angular.extend({}, defaultConfig, options || {});
            var el = document.createElement('canvas');

            el.height = config.height;
            el.width = config.width;

            var canvas = el.getContext('2d');

            canvas.fillStyle = config.backgroundColor;
            canvas.fillRect(0, 0, config.width, config.height);
            canvas.font = config.font;
            canvas.fillStyle = config.fontColor;

            var title = fitString(canvas, text, config);
            canvas.fillText(title, 4, config.padding + 4);

            var image = new Image();
            image.src = el.toDataURL();

            return {
                image: image,
                xOffset: config.xOffset,
                yOffset: config.yOffset
            };
        };
    }
    ]);

}(angular));
<|endoftext|>


<commit_before>var events = require('events');
var util = require('util');

module.exports = Worker;

function Worker(queues, options) {
  options = options || {};

  this.queues = queues || {};
  this.interval = options.interval || 5000;

  this.callbacks = options.callbacks || {};
  this.strategies = options.strategies || {};

  // Default retry strategies
  this.strategies.linear = this.strategies.linear || linear;
  this.strategies.exponential = this.strategies.exponential || exponential;

  // This worker will only process jobs of this priority or higher
  this.minPriority = options.minPriority;
  this.pollTimeouts = {};
}

util.inherits(Worker, events.EventEmitter);

Worker.prototype.register = function (callbacks) {
  for (var name in callbacks) {
    if (callbacks.hasOwnProperty(name)) {
      this.callbacks[name] = callbacks[name];
    }
  }
};

Worker.prototype.strategies = function (strategies) {
  for (var name in strategies) {
    if (strategies.hasOwnProperty(name)) {
      this.strategies[name] = strategies[name];
    }
  }
};

Worker.prototype.start = function () {
  this.working = true;
  if (Object.keys(this.queues).length !== 0) {
    for (var name in this.queues) {
      if (this.queues.hasOwnProperty(name)) {
        this.poll(name);
      }
    }
  }
};

Worker.prototype.stop = function (callback) {
  function done() {
    if (callback) {
      callback();
    }
  }

  if (!this.working) {
    done();
  }
  this.working = false;

  var tCnt = Object.keys(this.pollTimeouts).length;
  if (tCnt > 0) {
    for (var name in this.pollTimeouts) {
      if (this.pollTimeouts.hasOwnProperty(name)) {
        clearTimeout(this.pollTimeouts[name]);
        delete this.pollTimeouts[name];
      }
    }
    var qCnt = Object.keys(this.queues).length;
    if (tCnt === qCnt) {
      return done();
    }
  }

  this.once('stopped', done);
};

Worker.prototype.poll = function (queueName) {
  if (!this.working) {
    return this.emit('stopped');
  }
  var self = this;

  this.queues[queueName].dequeue({
    minPriority: this.minPriority,
    callbacks: this.callbacks
  }, function (err, job) {
    if (err) {
      return self.emit('error', {
        queue: queueName,
        job: job.data,
        error: err
      });
    }

    if (job) {
      self.emit('dequeued', {queue: queueName, job: job.data});
      self.work(queueName, job);
    } else {
      self.emit('empty', {queue: queueName});
      // Queue is empty - wait
      self.pollTimeouts[queueName] = setTimeout(function () {
        delete self.pollTimeouts[queueName];
        self.poll(queueName);
      }, self.interval);
    }
  });
};

Worker.prototype.addQueue = function (name, queue) {
  if (typeof name !== 'string' || typeof queue !== 'object') {
    throw new Error('Wrong params');
  }
  this.queues[name] = queue;
  this.poll(name);
};

Worker.prototype.getQueue = function (name) {
  return this.queues[name];
};

Worker.prototype.dropQueue = function (name) {
  if (this.pollTimeouts[name]) {
    delete this.pollTimeouts[name];
  }
  if (this.queues[name]) {
    delete this.queues[name];
  }
};

Worker.prototype.work = function (queueName, job) {
  var self = this;
  var finished = false;
  var timer;

  if (job.data.timeout) {
    timer = setTimeout(function () {
      done(new Error('timeout'));
    }, job.data.timeout);
  }

  function done(err, result) {
    // It's possible that this could be called twice in the case that a job times out,
    // but the handler ends up finishing later on
    if (finished) {
      console.log('twice');
      return;
    }
    finished = true;

    clearTimeout(timer);
    self.emit('done', {queue: queueName, job: job.data});

    if (err) {
      self.error(job, err, function (err) {
        if (err) {
          return self.emit('error', {
            queue: queueName,
            job: job.data,
            error: err
          });
        }

        self.emit('failed', {queue: queueName, job: job.data});
        self.poll(queueName);
      });
    } else {
      job.complete(result, function (err) {
        if (err) {
          return self.emit('error', {
            queue: queueName,
            job: job.data,
            error: err
          });
        }

        self.emit('complete', {queue: queueName, job: job.data});
        self.poll(queueName);
      });
    }
  }

  this.process(job.data, done);
};

Worker.prototype.process = function (data, callback) {
  console.log(data);
  var func = this.callbacks[data.name];

  if (func) {
    func(data, callback);
  } else {
    callback(new Error('No callback registered for `' + data.name + '`'));
  }
};

Worker.prototype.error = function (job, err, callback) {
  var attempts = job.data.attempts;
  var remaining = 0;
  var wait;

  if (attempts) {
    remaining = attempts.remaining = (attempts.remaining || attempts.count) - 1;
  }

  if (remaining > 0) {
    var strategy = this.strategies[attempts.strategy || 'linear'];
    if (!strategy) {
      strategy = linear;

      console.error('No such retry strategy: `' + attempts.strategy + '`');
      console.error('Using linear strategy');
    }

    if (attempts.delay === undefined) {
      wait = 0;
    } else {
      wait = strategy(attempts);
    }

    job.delay(wait, callback);
  } else {
    job.fail(err, callback);
  }
};

// Strategies
// ---------------

function linear(attempts) {
  return attempts.delay;
}

function exponential(attempts) {
  return attempts.delay * (attempts.count - attempts.remaining);
}
<commit_msg>worker parallel process per queue<commit_after>var events = require('events');
var util = require('util');

module.exports = Worker;

function Worker(queues, options) {
  options = options || {};

  this.queues = queues || {};
  this.interval = options.interval || 3000;
  this.maxPerQueue = options.maxPerQueue || 1;

  this.callbacks = options.callbacks || {};
  this.strategies = options.strategies || {};

  // Default retry strategies
  this.strategies.linear = this.strategies.linear || linear;
  this.strategies.exponential = this.strategies.exponential || exponential;

  // This worker will only process jobs of this priority or higher
  this.minPriority = options.minPriority;
  this.timers = {};
  this.counter = {};
}

util.inherits(Worker, events.EventEmitter);

Worker.prototype.register = function (callbacks) {
  for (var name in callbacks) {
    if (callbacks.hasOwnProperty(name)) {
      this.callbacks[name] = callbacks[name];
    }
  }
};

Worker.prototype.strategies = function (strategies) {
  for (var name in strategies) {
    if (strategies.hasOwnProperty(name)) {
      this.strategies[name] = strategies[name];
    }
  }
};

Worker.prototype.start = function () {
  this.working = true;
  if (Object.keys(this.queues).length !== 0) {
    for (var queueName in this.queues) {
      if (this.queues.hasOwnProperty(queueName)) {
        this.startPolling(queueName);
      }
    }
  }
};

Worker.prototype.startPolling = function (queueName) {
  this.timers[queueName] = setInterval(() => {
    if (!this.counter[queueName]) {
      this.counter[queueName] = 0;
    }
    if (this.counter[queueName] < this.maxPerQueue) {
      this.poll(queueName);
    }
  }, this.interval);
};

Worker.prototype.incCounter = function (queueName, amt) {
  if (!amt) {
    amt = 1;
  }
  if (!this.counter[queueName]) {
    this.counter[queueName] = 0;
  }
  this.counter[queueName] += amt;
};

Worker.prototype.stop = function (callback) {
  function done() {
    if (callback) {
      callback();
    }
  }

  if (!this.working) {
    done();
  }
  this.working = false;

  var tCnt = Object.keys(this.timers).length;
  if (tCnt > 0) {
    for (var name in this.timers) {
      if (this.timers.hasOwnProperty(name)) {
        clearInterval(this.timers[name]);
      }
    }
    var qCnt = Object.keys(this.queues).length;
    if (tCnt === qCnt) {
      return done();
    }
  }

  this.once('stopped', done);
};

Worker.prototype.poll = function (queueName) {
  if (!this.working) {
    return this.emit('stopped');
  }
  var self = this;

  this.queues[queueName].dequeue({
    minPriority: this.minPriority,
    callbacks: this.callbacks
  }, function (err, job) {
    if (err) {
      return self.emit('error', {
        queue: queueName,
        job: job.data,
        error: err
      });
    }

    if (job) {
      self.incCounter(queueName);
      self.emit('dequeued', {queue: queueName, job: job.data});
      self.work(queueName, job);
    } else {
      self.emit('empty', {queue: queueName});
    }
  });
};

Worker.prototype.addQueue = function (name, queue) {
  if (typeof name !== 'string' || typeof queue !== 'object') {
    throw new Error('Wrong params');
  }
  this.queues[name] = queue;
  this.startPolling(name);
};

Worker.prototype.getQueue = function (name) {
  return this.queues[name];
};

Worker.prototype.dropQueue = function (name) {
  if (this.timers[name]) {
    clearInterval(this.timers[name]);
    delete this.timers[name];
  }
  if (this.queues[name]) {
    delete this.queues[name];
  }
};

Worker.prototype.work = function (queueName, job) {
  var self = this;
  var finished = false;
  var timer;

  if (job.data.timeout) {
    timer = setTimeout(function () {
      done(new Error('timeout'));
    }, job.data.timeout);
  }

  function done(err, result) {
    // It's possible that this could be called twice in the case that a job times out,
    // but the handler ends up finishing later on
    if (finished) {
      console.log(queueName, 'twice');
      return;
    }
    finished = true;
    self.incCounter(queueName, -1);

    clearTimeout(timer);
    self.emit('done', {queue: queueName, job: job.data});

    if (err) {
      self.error(job, err, function (err) {
        if (err) {
          return self.emit('error', {
            queue: queueName,
            job: job.data,
            error: err
          });
        }

        self.emit('failed', {queue: queueName, job: job.data});
      });
    } else {
      job.complete(result, function (err) {
        if (err) {
          return self.emit('error', {
            queue: queueName,
            job: job.data,
            error: err
          });
        }

        self.emit('complete', {queue: queueName, job: job.data});
      });
    }
  }

  this.process(job.data, done);
};

Worker.prototype.process = function (data, callback) {
  console.log(data);
  var func = this.callbacks[data.name];

  if (func) {
    func(data, callback);
  } else {
    callback(new Error('No callback registered for `' + data.name + '`'));
  }
};

Worker.prototype.error = function (job, err, callback) {
  var attempts = job.data.attempts;
  var remaining = 0;
  var wait;

  if (attempts) {
    remaining = attempts.remaining = (attempts.remaining || attempts.count) - 1;
  }

  if (remaining > 0) {
    var strategy = this.strategies[attempts.strategy || 'linear'];
    if (!strategy) {
      strategy = linear;

      console.error('No such retry strategy: `' + attempts.strategy + '`');
      console.error('Using linear strategy');
    }

    if (attempts.delay === undefined) {
      wait = 0;
    } else {
      wait = strategy(attempts);
    }

    job.delay(wait, callback);
  } else {
    job.fail(err, callback);
  }
};

// Strategies
// ---------------

function linear(attempts) {
  return attempts.delay;
}

function exponential(attempts) {
  return attempts.delay * (attempts.count - attempts.remaining);
}
<|endoftext|>


<commit_before>import electron from 'electron';

const app = electron.app;
const BrowserWindow = electron.BrowserWindow;

// The browser window
let window;

// Create the main app window
function createWindow() {
    // Create the browser window
    window = new BrowserWindow({
        fullscreen: true,
        show: false
    });

    // Open developer tools
    window.webContents.openDevTools();

    // Load the app's webpage
    window.loadURL('file://' + __dirname + '/index.html');

    // Show the application once it's loaded
    window.once('ready-to-show', () => {
        window.show();
    });

    // Dereference the window when it's closed
    window.on('closed', () => {
        window = null;
    });
}

// Create window once Electron is initialized
app.on('ready', createWindow);

// Quit when all windows are closed
app.on('window-all-closed', () => {
    app.quit();
});
<commit_msg>Removed Menu bar and fixed bug<commit_after>import electron from 'electron';

const app = electron.app;
const BrowserWindow = electron.BrowserWindow;

// The browser window
let window;

// Create the main app window
function createWindow() {
    // Create the browser window
    window = new BrowserWindow({
        fullscreen: true,
        show: false
    });

    // Open developer tools
    window.webContents.openDevTools();

    // Load the app's webpage
    window.loadURL('file://' + __dirname + '/index.html');

    // Show the application once it's loaded
    window.once('ready-to-show', () => {
        window.show();
    });

    // Dereference the window when it's closed
    window.on('closed', () => {
        window = null;
    });

    window.setMenu(null);
}

// Create window once Electron is initialized
app.on('ready', createWindow);

// Quit when all windows are closed
app.on('window-all-closed', () => {
    app.quit();
});
<|endoftext|>