<commit_before>// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.

var PythonShell = require('python-shell');

LOGO_FILE = ""
PPTX_FILE = ""

basename = function (path) {
    return path.replace(/\\/g, '/').replace(/.*\//, '');
}

String.prototype.toUpperCaseFirstChar = function () {
    return this.substr(0, 1).toUpperCase() + this.substr(1);
}

// Make paths work on windows and unix
normalise = (filepath) => {
    return filepath.replace(/ /g, "%20").replace(/\\/g, '/')
}

var pptx_drag_drop = document.getElementById('drag-drop-pptx');

pptx_drag_drop.ondragover = () => {
    return false;
};

pptx_drag_drop.ondragleave = () => {
    return false;
};

pptx_drag_drop.ondragend = () => {
    return false;
};

pptx_drag_drop.ondrop = (e) => {
    e.preventDefault();
    filename = e.dataTransfer.files[0].path

    $(pptx_drag_drop).text(basename(filename))
    PPTX_FILE = filename

    var options = {
        mode: 'text',
        scriptPath: 'slidedeck_transform',
        args: ['--file', filename]
    };

    PythonShell.run('cli_list_metadata.py', options, function (err, results) {
        if (err) throw err;

        createInputList(JSON.parse(results).tags, JSON.parse(results).templates)
    });

    return false;
};

var logo_drag_drop = document.getElementById('drag-drop-logo');

logo_drag_drop.ondragover = () => {
    return false;
};

logo_drag_drop.ondragleave = () => {
    return false;
};

logo_drag_drop.ondragend = () => {
    return false;
};

logo_drag_drop.ondrop = (e) => {
    e.preventDefault();

    filename = e.dataTransfer.files[0].path
    $(logo_drag_drop).text("")
    // $(logo_drag_drop).css("background","url('" + filename + "')")
    console.log(normalise(filename))
    $(logo_drag_drop).css("backgroundImage", "url('file://" + normalise(filename) + "')")
    LOGO_FILE = filename
    return false;
};

defaultOutputFilename = (filename, client) => {
    return filename.replace(".ppt", "-" + client + ".ppt")
}

var resetButton = document.getElementById('reset')
resetButton.onclick = () => {
    location.reload();
}

var submitButton = document.getElementById('submit')

submitButton.onclick = () => {

    tag_list = Array.prototype.join.call(getTagsToDelete(), ',');

    if (!PPTX_FILE) {
        alert("Select a powerpoint file")
        return
    }

    if (!LOGO_FILE) {
        alert("Select a logo file")
        return
    }

    templates = $("#templates_container input")
    try {
        Array.prototype.forEach.call(templates, function (template) {
            if (template.value == "") {
                alert("Input a value for the template '" + template.getAttribute("data-templatename") + "'")
                templates_bool = true
                throw BreakException;
            }
        });
    }
    catch (e) {
        return
    }

    dialogOptions = {
        title: "Save Powerpoint Presentation",
        defaultPath: defaultOutputFilename(PPTX_FILE, "new"),
        filters: [
            { name: 'Powerpoint Presentation', extension: ['ppt', 'pptx'] }
        ]
    }

    dialog.showSaveDialog(dialogOptions, function (outFile) {
        if (outFile === undefined) return;

        var options = {
            mode: 'text',
            scriptPath: 'slidedeck_transform',
            pythonOptions: '-u',
            args: [
                '--file', PPTX_FILE,
                '--tags', tag_list,
                '--logo', LOGO_FILE,
                '--templates', getTemplateData(),
                '--out', outFile
            ]
        };

        $("#loading").fadeIn()

        $("#loading_text").fadeOut(function () {
            $(this).text("Generating Slidedeck...").fadeIn();
        });

        var pyshell = new PythonShell('cli_transform.py', options);

        pyshell.on('message', function (message) {
            // received a message sent from the Python script (a simple "print" statement)
            $("#loading_text").text("Generating Slidedeck...\n" + message)
            console.log(message);
        });

        pyshell.end(function (err, results) {
            if (err) {
                alert(err);
            } else {
                $("#loading_text").text("Done!")
            }
            $("#loading").delay(400).fadeOut()

        });

    });

}

getTagsToDelete = () => {
    unchecked = $("input:checkbox[name=tag-list]:not(:checked)")
    return unchecked.map(function (x) {
        return unchecked[x].getAttribute("data-tagname");
    });
}

getTemplateData = () => {
    templates = $("#templates_container input")
    data = templates.map(function (x) {
        a = { "name": null, "value": null }
        a["name"] = templates[x].getAttribute("data-templatename")
        a["value"] = templates[x].value
        return JSON.stringify(a)
    });

    return "[" + Array.prototype.join.call(data, ',') + "]";
}

createInputList = (tags, templates) => {
    container = document.getElementById('tag-list')
    container.innerHTML = ""
    console.log(tags)

    var templatesContainer = document.createElement('div');
    templatesContainer.id = "templates_container"
    var templatesHeader = document.createElement('div');
    templatesHeader.innerHTML = "
Text Replacement
"

    templatesContainer.appendChild(templatesHeader)

    templates.forEach(function (template) {
        var templateDiv = document.createElement('div');
        var templateLabel = document.createElement('label')
        var templateInput = document.createElement('input');
        templateDiv.id = "template_textbox_" + template
        templateInput.setAttribute('data-templatename', template)
        templateInput.id = template + "-input";
        templateLabel.innerHTML = template.toUpperCaseFirstChar() + ":"
        templateInput.placeholder = "Value for {{" + template + "}}";
        templateDiv.appendChild(templateLabel);
        templateDiv.appendChild(templateInput);
        templatesContainer.appendChild(templateDiv);
    });

    container.appendChild(templatesContainer)

    var tagDiv = document.createElement('div');
    if (tags.length == 0) {
        tagDiv.innerHTML = "No tags found in presentation"
    } else {
        tagDiv.innerHTML = "
Tags to keep
"
    }

    container.appendChild(tagDiv)

    tags.forEach(function (tag) {

        var div = document.createElement('div');
        div.class = "checkbox"

        var checkbox = document.createElement('input');
        checkbox.type = "checkbox";
        checkbox.name = "tag-list";
        checkbox.checked = true;
        checkbox.id = "tag_checkbox_" + tag;
        checkbox.setAttribute('data-tagname', tag)

        var label = document.createElement('label')
        label.htmlFor = "tag_checkbox_" + tag;
        label.appendChild(document.createTextNode(tag));

        div.appendChild(checkbox);
        div.appendChild(label);
        container.appendChild(div)
    }, this);
}<commit_msg>Use the default values when populating the input list<commit_after>// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.

var PythonShell = require('python-shell');

LOGO_FILE = ""
PPTX_FILE = ""

basename = function (path) {
    return path.replace(/\\/g, '/').replace(/.*\//, '');
}

String.prototype.toUpperCaseFirstChar = function () {
    return this.substr(0, 1).toUpperCase() + this.substr(1);
}

// Make paths work on windows and unix
normalise = (filepath) => {
    return filepath.replace(/ /g, "%20").replace(/\\/g, '/')
}

var pptx_drag_drop = document.getElementById('drag-drop-pptx');

pptx_drag_drop.ondragover = () => {
    return false;
};

pptx_drag_drop.ondragleave = () => {
    return false;
};

pptx_drag_drop.ondragend = () => {
    return false;
};

pptx_drag_drop.ondrop = (e) => {
    e.preventDefault();
    filename = e.dataTransfer.files[0].path

    $(pptx_drag_drop).text(basename(filename))
    PPTX_FILE = filename

    var options = {
        mode: 'text',
        scriptPath: 'slidedeck_transform',
        args: ['--file', filename]
    };

    PythonShell.run('cli_list_metadata.py', options, function (err, results) {
        if (err) throw err;

        createInputList(JSON.parse(results).tags, JSON.parse(results).templates)
    });

    return false;
};

var logo_drag_drop = document.getElementById('drag-drop-logo');

logo_drag_drop.ondragover = () => {
    return false;
};

logo_drag_drop.ondragleave = () => {
    return false;
};

logo_drag_drop.ondragend = () => {
    return false;
};

logo_drag_drop.ondrop = (e) => {
    e.preventDefault();

    filename = e.dataTransfer.files[0].path
    $(logo_drag_drop).text("")
    // $(logo_drag_drop).css("background","url('" + filename + "')")
    console.log(normalise(filename))
    $(logo_drag_drop).css("backgroundImage", "url('file://" + normalise(filename) + "')")
    LOGO_FILE = filename
    return false;
};

defaultOutputFilename = (filename, client) => {
    return filename.replace(".ppt", "-" + client + ".ppt")
}

var resetButton = document.getElementById('reset')
resetButton.onclick = () => {
    location.reload();
}

var submitButton = document.getElementById('submit')

submitButton.onclick = () => {

    tag_list = Array.prototype.join.call(getTagsToDelete(), ',');

    if (!PPTX_FILE) {
        alert("Select a powerpoint file")
        return
    }

    if (!LOGO_FILE) {
        alert("Select a logo file")
        return
    }

    templates = $("#templates_container input")
    try {
        Array.prototype.forEach.call(templates, function (template) {
            if (template.value == "") {
                alert("Input a value for the template '" + template.getAttribute("data-templatename") + "'")
                templates_bool = true
                throw BreakException;
            }
        });
    }
    catch (e) {
        return
    }

    dialogOptions = {
        title: "Save Powerpoint Presentation",
        defaultPath: defaultOutputFilename(PPTX_FILE, "new"),
        filters: [
            { name: 'Powerpoint Presentation', extension: ['ppt', 'pptx'] }
        ]
    }

    dialog.showSaveDialog(dialogOptions, function (outFile) {
        if (outFile === undefined) return;

        var options = {
            mode: 'text',
            scriptPath: 'slidedeck_transform',
            pythonOptions: '-u',
            args: [
                '--file', PPTX_FILE,
                '--tags', tag_list,
                '--logo', LOGO_FILE,
                '--templates', getTemplateData(),
                '--out', outFile
            ]
        };

        $("#loading").fadeIn()

        $("#loading_text").fadeOut(function () {
            $(this).text("Generating Slidedeck...").fadeIn();
        });

        var pyshell = new PythonShell('cli_transform.py', options);

        pyshell.on('message', function (message) {
            // received a message sent from the Python script (a simple "print" statement)
            $("#loading_text").text("Generating Slidedeck...\n" + message)
            console.log(message);
        });

        pyshell.end(function (err, results) {
            if (err) {
                alert(err);
            } else {
                $("#loading_text").text("Done!")
            }
            $("#loading").delay(400).fadeOut()

        });

    });

}

getTagsToDelete = () => {
    unchecked = $("input:checkbox[name=tag-list]:not(:checked)")
    return unchecked.map(function (x) {
        return unchecked[x].getAttribute("data-tagname");
    });
}

getTemplateData = () => {
    templates = $("#templates_container input")
    data = templates.map(function (x) {
        a = { "name": null, "value": null }
        a["name"] = templates[x].getAttribute("data-templatename")
        a["value"] = templates[x].value
        return JSON.stringify(a)
    });

    return "[" + Array.prototype.join.call(data, ',') + "]";
}

createInputList = (tags, templates) => {
    container = document.getElementById('tag-list')
    container.innerHTML = ""
    console.log(tags)

    var templatesContainer = document.createElement('div');
    templatesContainer.id = "templates_container"
    var templatesHeader = document.createElement('div');
    templatesHeader.innerHTML = "
Text Replacement
"

    templatesContainer.appendChild(templatesHeader)

    templates.forEach(function (template_object) {
        var template = template_object.name
        var templateDiv = document.createElement('div');
        var templateLabel = document.createElement('label')
        var templateInput = document.createElement('input');
        templateDiv.id = "template_textbox_" + template
        templateInput.setAttribute('data-templatename', template)
        templateInput.id = template + "-input";
        templateLabel.innerHTML = template.toUpperCaseFirstChar() + ":"
        templateInput.placeholder = "Value for {{" + template + "}}";
        templateInput.value = template_object.default
        templateDiv.appendChild(templateLabel);
        templateDiv.appendChild(templateInput);
        templatesContainer.appendChild(templateDiv);
    });

    container.appendChild(templatesContainer)

    var tagDiv = document.createElement('div');
    if (tags.length == 0) {
        tagDiv.innerHTML = "No tags found in presentation"
    } else {
        tagDiv.innerHTML = "
Tags to keep
"
    }

    container.appendChild(tagDiv)

    tags.forEach(function (tag) {

        var div = document.createElement('div');
        div.class = "checkbox"

        var checkbox = document.createElement('input');
        checkbox.type = "checkbox";
        checkbox.name = "tag-list";
        checkbox.checked = true;
        checkbox.id = "tag_checkbox_" + tag;
        checkbox.setAttribute('data-tagname', tag)

        var label = document.createElement('label')
        label.htmlFor = "tag_checkbox_" + tag;
        label.appendChild(document.createTextNode(tag));

        div.appendChild(checkbox);
        div.appendChild(label);
        container.appendChild(div)
    }, this);
}<|endoftext|>


<commit_before>(function () {
    "use strict";

    function PublishController($scope, localizationService, contentEditingHelper) {

        var vm = this;
        vm.loading = true;
        vm.isNew = true;

        vm.changeSelection = changeSelection;

        /** Returns true if publish meets the requirements of mandatory languages */
        function canPublish() {

            var hasSomethingToPublish = false;

            for (var i = 0; i < vm.variants.length; i++) {
                var variant = vm.variants[i];

                // if varaint is mandatory and not already published, then we require it to be set to publish:
                if (variant.publish === false && notPublishedMandatoryFilter(variant)) {
                    return false;
                }
                if (variant.publish === true) {
                    hasSomethingToPublish = true;
                }

            }
            return hasSomethingToPublish;
        }

        function changeSelection(variant) {
            // update submit button state:
            $scope.model.disableSubmitButton = !canPublish();
            //need to set the Save state to same as publish.
            variant.save = variant.publish;
        }

        function hasAnyDataFilter(variant) {

            // if we have a name, then we have data.
            if (variant.name != null && variant.name.length > 0) {
                return true;
            }

            if(variant.isDirty === true) {
                return true;
            }

            for (var t=0; t < variant.tabs.length; t++){
                for (var p=0; p < variant.tabs[t].properties.length; p++){
                    var property = variant.tabs[t].properties[p];
                    if (property.value != null && property.value.length > 0) {
                        return true;
                    }
                }
            }

            return false;
        }

        function dirtyVariantFilter(variant) {
            //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
            // * it's editor is in a $dirty state
            // * it has pending saves
            // * it is unpublished
            return (variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
        }

        function publishableVariantFilter(variant) {
            //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
            // * variant is active
            // * it's editor is in a $dirty state
            // * it has pending saves
            // * it is unpublished
            return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
        }

        function notPublishedMandatoryFilter(variant) {
            return variant.state !== "Published" && variant.state !== "PublishedPendingChanges" && isMandatoryFilter(variant);
        }
        function isMandatoryFilter(variant) {
            //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
            // * has a mandatory language
            // * without having a segment, segments cant be mandatory at current state of code.
            return (variant.language && variant.language.isMandatory === true && variant.segment == null);
        }
        function notPublishableButMandatoryFilter(variant) {
            //determine a variant is needed, but not already a choice.
            // * publishable — aka. displayed as a publish option.
            // * published — its already published and everything is then fine.
            // * mandatory — this is needed, and thats why we highlight it.
            return !publishableVariantFilter(variant) && variant.state !== "Published" && variant.isMandatory === true;
        }

        function onInit() {

            vm.variants = $scope.model.variants;

            _.each(vm.variants, (variant) => {

                // reset to not be published
                variant.publish = false;
                variant.save = false;

                variant.isMandatory = isMandatoryFilter(variant);

                // If we have a variant thats not in the state of NotCreated, then we know we have adata and its not a new content node.
                if(variant.state !== "NotCreated") {
                    vm.isNew = false;
                }
            });

            _.each(vm.variants, (variant) => {

                // if this is a new node and we have data on this variant.
                if(vm.isNew === true && hasAnyDataFilter(variant)) {
                    variant.save = true;
                }

            });

            vm.availableVariants = vm.variants.filter(publishableVariantFilter);
            vm.missingMandatoryVariants = vm.variants.filter(notPublishableButMandatoryFilter);

            // if any active varaiant that is available for publish, we set it to be published:
            _.each(vm.availableVariants, (v) => {
                if(v.active) {
                    v.save = v.publish = true;
                }
            });

            if (vm.availableVariants.length !== 0) {
                vm.availableVariants.sort(function (a, b) {
                    if (a.language && b.language) {
                        if (a.language.name > b.language.name) {
                            return -1;
                        }
                        if (a.language.name < b.language.name) {
                            return 1;
                        }
                    }
                    if (a.segment && b.segment) {
                        if (a.segment > b.segment) {
                            return -1;
                        }
                        if (a.segment < b.segment) {
                            return 1;
                        }
                    }
                    return 0;
                });
            }

            $scope.model.disableSubmitButton = !canPublish();

            if (vm.missingMandatoryVariants.length > 0) {
                localizationService.localize("content_notReadyToPublish").then(function (value) {
                    $scope.model.title = value;
                    vm.loading = false;
                });
            } else {
                if (!$scope.model.title) {
                    localizationService.localize("content_readyToPublish").then(function (value) {
                        $scope.model.title = value;
                        vm.loading = false;
                    });
                } else {
                    vm.loading = false;
                }
            }

        }

        onInit();

        //when this dialog is closed, reset all 'publish' flags
        $scope.$on('$destroy', function () {
            for (var i = 0; i < vm.variants.length; i++) {
                vm.variants[i].publish = false;
                vm.variants[i].save = false;
            }
        });
    }

    angular.module("umbraco").controller("Umbraco.Overlays.PublishController", PublishController);

})();
<commit_msg>use calculated property instead of calling the method.<commit_after>(function () {
    "use strict";

    function PublishController($scope, localizationService, contentEditingHelper) {

        var vm = this;
        vm.loading = true;
        vm.isNew = true;

        vm.changeSelection = changeSelection;

        /** Returns true if publish meets the requirements of mandatory languages */
        function canPublish() {

            var hasSomethingToPublish = false;

            for (var i = 0; i < vm.variants.length; i++) {
                var variant = vm.variants[i];

                // if varaint is mandatory and not already published, then we require it to be set to publish:
                if (variant.publish === false && notPublishedMandatoryFilter(variant)) {
                    return false;
                }
                if (variant.publish === true) {
                    hasSomethingToPublish = true;
                }

            }
            return hasSomethingToPublish;
        }

        function changeSelection(variant) {
            // update submit button state:
            $scope.model.disableSubmitButton = !canPublish();
            //need to set the Save state to same as publish.
            variant.save = variant.publish;
        }

        function hasAnyDataFilter(variant) {

            // if we have a name, then we have data.
            if (variant.name != null && variant.name.length > 0) {
                return true;
            }

            if(variant.isDirty === true) {
                return true;
            }

            for (var t=0; t < variant.tabs.length; t++){
                for (var p=0; p < variant.tabs[t].properties.length; p++){
                    var property = variant.tabs[t].properties[p];
                    if (property.value != null && property.value.length > 0) {
                        return true;
                    }
                }
            }

            return false;
        }

        function dirtyVariantFilter(variant) {
            //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
            // * it's editor is in a $dirty state
            // * it has pending saves
            // * it is unpublished
            return (variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
        }

        function publishableVariantFilter(variant) {
            //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
            // * variant is active
            // * it's editor is in a $dirty state
            // * it has pending saves
            // * it is unpublished
            return (variant.active || variant.isDirty || variant.state === "Draft" || variant.state === "PublishedPendingChanges");
        }

        function notPublishedMandatoryFilter(variant) {
            return variant.state !== "Published" && variant.state !== "PublishedPendingChanges" && variant.isMandatory === true;
        }
        function isMandatoryFilter(variant) {
            //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's
            // * has a mandatory language
            // * without having a segment, segments cant be mandatory at current state of code.
            return (variant.language && variant.language.isMandatory === true && variant.segment == null);
        }
        function notPublishableButMandatoryFilter(variant) {
            //determine a variant is needed, but not already a choice.
            // * publishable — aka. displayed as a publish option.
            // * published — its already published and everything is then fine.
            // * mandatory — this is needed, and thats why we highlight it.
            return !publishableVariantFilter(variant) && variant.state !== "Published" && variant.isMandatory === true;
        }

        function onInit() {

            vm.variants = $scope.model.variants;

            _.each(vm.variants, (variant) => {

                // reset to not be published
                variant.publish = false;
                variant.save = false;

                variant.isMandatory = isMandatoryFilter(variant);

                // If we have a variant thats not in the state of NotCreated, then we know we have adata and its not a new content node.
                if(variant.state !== "NotCreated") {
                    vm.isNew = false;
                }
            });

            _.each(vm.variants, (variant) => {

                // if this is a new node and we have data on this variant.
                if(vm.isNew === true && hasAnyDataFilter(variant)) {
                    variant.save = true;
                }

            });

            vm.availableVariants = vm.variants.filter(publishableVariantFilter);
            vm.missingMandatoryVariants = vm.variants.filter(notPublishableButMandatoryFilter);

            // if any active varaiant that is available for publish, we set it to be published:
            _.each(vm.availableVariants, (v) => {
                if(v.active) {
                    v.save = v.publish = true;
                }
            });

            if (vm.availableVariants.length !== 0) {
                vm.availableVariants.sort(function (a, b) {
                    if (a.language && b.language) {
                        if (a.language.name > b.language.name) {
                            return -1;
                        }
                        if (a.language.name < b.language.name) {
                            return 1;
                        }
                    }
                    if (a.segment && b.segment) {
                        if (a.segment > b.segment) {
                            return -1;
                        }
                        if (a.segment < b.segment) {
                            return 1;
                        }
                    }
                    return 0;
                });
            }

            $scope.model.disableSubmitButton = !canPublish();

            if (vm.missingMandatoryVariants.length > 0) {
                localizationService.localize("content_notReadyToPublish").then(function (value) {
                    $scope.model.title = value;
                    vm.loading = false;
                });
            } else {
                if (!$scope.model.title) {
                    localizationService.localize("content_readyToPublish").then(function (value) {
                        $scope.model.title = value;
                        vm.loading = false;
                    });
                } else {
                    vm.loading = false;
                }
            }

        }

        onInit();

        //when this dialog is closed, reset all 'publish' flags
        $scope.$on('$destroy', function () {
            for (var i = 0; i < vm.variants.length; i++) {
                vm.variants[i].publish = false;
                vm.variants[i].save = false;
            }
        });
    }

    angular.module("umbraco").controller("Umbraco.Overlays.PublishController", PublishController);

})();
<|endoftext|>


<commit_before>'use strict';
var jurassic = require('jurassic');

/**
 * Get Era from a list onf events documents
 * events will be expanded according to RRULE if any
 *
 *
 * @param {Array} docs
 * @return {Era}
 */
function getExpandedEra(docs, dtstart, dtend)
{

    if (!(dtstart instanceof Date) || !(dtend instanceof Date)) {
        throw new Error('parameters must be dates');
    }

    var events = new jurassic.Era();
    var expanded;

    // it seam that the expand method is based on the event start date
    // we get one more day to get the event overlapping with start search date
    var expandStart = new Date(dtstart);
    expandStart.setDate(expandStart.getDate() -1);

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

        expanded = docs[i].expand(expandStart, dtend);

        // expand event if RRULE
        for(var e =0; e<expanded.length; e++) {

            try {

                // copy properties of expanded event to the jurassic period
                events.addPeriod(expanded[e]);

            } catch(e) {
                // ignore invalid period
                console.log(expanded[e]);
                console.trace(e);
            }
        }
    }
    return events;
}

exports = module.exports = getExpandedEra;
<commit_msg>Fix invalid period error<commit_after>'use strict';
var jurassic = require('jurassic');

/**
 * Normalize event for the jurassic api
 * a dtend is required
 *
 * @param   {CalendarEvent}   event
 * @returns {CalendarEvent}
 */
function normalize(event) {
    if (undefined === event.dtend || null === event.dtend) {
        event.dtend = new Date(event.dtstart);
        event.dtend.setDate(event.dtend.getDate()+1);
    }

    return event;
}

/**
 * Get Era from a list onf events documents
 * events will be expanded according to RRULE if any
 *
 *
 * @param {Array} docs
 * @return {Era}
 */
function getExpandedEra(docs, dtstart, dtend)
{

    if (!(dtstart instanceof Date) || !(dtend instanceof Date)) {
        throw new Error('parameters must be dates');
    }

    var events = new jurassic.Era();
    var expanded;

    // it seam that the expand method is based on the event start date
    // we get one more day to get the event overlapping with start search date
    var expandStart = new Date(dtstart);
    expandStart.setDate(expandStart.getDate() -1);

    for(let i =0; i<docs.length; i++) {

        expanded = normalize(docs[i]).expand(expandStart, dtend);

        // expand event if RRULE
        for(let j =0; j<expanded.length; j++) {

            try {

                // copy properties of expanded event to the jurassic period
                events.addPeriod(expanded[j]);

            } catch(e) {
                // ignore invalid period
                // console.log('expanded.length='+expanded.length+' j='+j+' expanded[e]='+expanded[j]);
                console.log(expanded[j]);
                console.log(e.stack);
            }
        }
    }
    return events;
}

exports = module.exports = getExpandedEra;
<|endoftext|>


<commit_before>import $ from "jquery";
import "timeago";

$(document).ready(function() {

  // Toggle expanding and collapsing sections
  $(".-js-expander-trigger").click(function(){
    $(this).toggleClass("expander-hidden");
  });

  function setTab(tab) {
    if (tab) {
      $(".js-vertical-tab-content").hide();
      tab.show();
      $(".is-active").removeClass("is-active");
      $("a[href^='#"+tab[0].id+"']").addClass("is-active");
    }
  }

  function getTab(selector) {
    var tab = $(".js-vertical-tab-content" + selector);
    return (selector && tab.length) ? tab : null;
  }

  window.onhashchange = function() {
    setTab(getTab(location.hash) || getTab(":first"));
  };

  // Set the tab if the hash is valid, otherwise show the first tab
  setTab(getTab(location.hash) || getTab(":first"));

  // If in tab mode
  $(".-js-vertical-tab").click(function(event) {
    event.preventDefault();
    history.pushState(null, "", $(this).attr("href"));
    setTab(getTab(location.hash));
  });

  // If in accordion mode
  $(".-js-vertical-tab-accordion-heading").click(function(event) {
    event.preventDefault();
    history.pushState(null, "", $(this).attr("href"));
    setTab(getTab(location.hash));
  });

  // Launch filter popover on mobile
  $("body").on("click", ".-js-add-filter", function(e){
    e.preventDefault();
    $(".dark-overlay").show();
    $(".panel-overlay").show();
  });

  $("body").on("click", ".-js-close-panel", function(e){
    e.preventDefault();
    $(".dark-overlay").hide();
    $(".panel-overlay").hide();
  });

  // Position Sticky bar
  function positionWarning(){
    var height = $(".sticky-bar").outerHeight();
    $("body:has(.sticky-bar)").css("paddingTop", height);
  }

  positionWarning();

  $(window).resize(function(){
    positionWarning();
  });

  // Search ordering
  $("#order").on("change", function() {
    this.form.submit();
  });

  $.timeago.settings.cutoff = 7 * 24 * 60 * 60 * 1000;  // One week

  // document.l10n.ready.then(function() {
  //   // Format all of the time.relative tags to display relative time.
  //   $(".-js-relative-time").timeago();
  // });
  $(".-js-relative-time").timeago();  // Add back to document.l10n.ready

});
<commit_msg>Add a note to not add new code to main.js<commit_after>// Note: This file only exists temporarily. Once the rest of this code gets
//       ported to using the ES6 syntax and is part of the warehouse package
//       this will be removed. New code should go in
//       warehouse/static/js/warehouse and not here.

import $ from "jquery";
import "timeago";

$(document).ready(function() {

  // Toggle expanding and collapsing sections
  $(".-js-expander-trigger").click(function(){
    $(this).toggleClass("expander-hidden");
  });

  function setTab(tab) {
    if (tab) {
      $(".js-vertical-tab-content").hide();
      tab.show();
      $(".is-active").removeClass("is-active");
      $("a[href^='#"+tab[0].id+"']").addClass("is-active");
    }
  }

  function getTab(selector) {
    var tab = $(".js-vertical-tab-content" + selector);
    return (selector && tab.length) ? tab : null;
  }

  window.onhashchange = function() {
    setTab(getTab(location.hash) || getTab(":first"));
  };

  // Set the tab if the hash is valid, otherwise show the first tab
  setTab(getTab(location.hash) || getTab(":first"));

  // If in tab mode
  $(".-js-vertical-tab").click(function(event) {
    event.preventDefault();
    history.pushState(null, "", $(this).attr("href"));
    setTab(getTab(location.hash));
  });

  // If in accordion mode
  $(".-js-vertical-tab-accordion-heading").click(function(event) {
    event.preventDefault();
    history.pushState(null, "", $(this).attr("href"));
    setTab(getTab(location.hash));
  });

  // Launch filter popover on mobile
  $("body").on("click", ".-js-add-filter", function(e){
    e.preventDefault();
    $(".dark-overlay").show();
    $(".panel-overlay").show();
  });

  $("body").on("click", ".-js-close-panel", function(e){
    e.preventDefault();
    $(".dark-overlay").hide();
    $(".panel-overlay").hide();
  });

  // Position Sticky bar
  function positionWarning(){
    var height = $(".sticky-bar").outerHeight();
    $("body:has(.sticky-bar)").css("paddingTop", height);
  }

  positionWarning();

  $(window).resize(function(){
    positionWarning();
  });

  // Search ordering
  $("#order").on("change", function() {
    this.form.submit();
  });

  $.timeago.settings.cutoff = 7 * 24 * 60 * 60 * 1000;  // One week

  // document.l10n.ready.then(function() {
  //   // Format all of the time.relative tags to display relative time.
  //   $(".-js-relative-time").timeago();
  // });
  $(".-js-relative-time").timeago();  // Add back to document.l10n.ready

});
<|endoftext|>


<commit_before>// Ember!
import Ember from 'ember';

// Merge method
const { merge } = Ember;

/**
 * Default route for product search,
 * its based on the YeboSDK Query system
 * @todo Define some outlets like 'aggs', 'pagination', 'sort',
 *       to share more components in the search pages
 */
export default Ember.Mixin.create({
  /**
   * Default params
   * @todo Enable the user to add new actions without overwriting this
   */
  queryParams: {
    search: {
      refreshModel: true
    },
    page: {
      refreshModel: true
    },
    sort: {
      refreshModel: true
    }
  },

  /**
   * Default Sort
   */
  defaultSort: 'price-desc',

  /**
   * Products per page
   */
  perPage: 15,

  /**
   * Default price ranges
   */
  defaultPriceRanges: [
    { to: 49.99 },
    { from: 50, to: 99.99 },
    { from: 100 }
  ],

  /**
   * Current aggregation
   */
  currentAggregation: null,

  /**
   * Selected aggregations used to generate
   * the next query
   */
  selectedAggs: {},

  /**
   * Flag to check if its necessary to fetch
   * aggregations again
   * @todo Enable the aggregations to be refreshed
   */
  refreshAggregations: false,

  /**
   * Route Model
   */
  model: function(params) {
    // Create a new query
    let query = new YeboSDK.Products();

    // Define the number of results per page
    query.perPage(this.get('perPage'));

    // Set the search
    if( params.search !== undefined )
      query.search(params.search);

    // Define the sort param
    let sortParam;

    // Check if the sort param exists
    if( params.sort !== undefined )
      sortParam = params.sort;
    else
      sortParam = this.get('defaultSort');

    // Sort options
    let sortOptions = sortParam.split('-');

    // Set the page
    query.sortBy(sortOptions[0], sortOptions[1]);

    // Set the page
    query.page(params.page);

    // Define the search rules
    this.searchRules(query, params, this.get('selectedAggs'));

    // Set the default promises
    let defaultPromises = {
      search: this.get('yebo.products').search(query),
      taxonomies: this.yebo.store.findAll('taxonomy'),
      selectedAggs: this.get('selectedAggs')
    };

    // Check if its necessary to get the aggregations
    if( this.get('currentAggregation') === null || this.get('refreshAggregations') )
      defaultPromises.aggs = query.aggregations(undefined, this.get('defaultPriceRanges'));
    else
      defaultPromises.aggs = this.get('currentAggregation');

    // Return a promise to the route
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Execute the default model promises
      Ember.RSVP.hash(defaultPromises).then((result) => {
        // Set the current aggregation
        this.set('currentAggregation', result.aggs);

        // Define the route model
        let routeModel = this.searchModel(params);

        // Check if it is a promise
        if( routeModel.then === undefined )
          return resolve(merge(routeModel, result));

        // Resolve the promise with the result
        routeModel.then((res) => { resolve(merge(res, result)); });
      });
    });
  },

  /**
   * Clear the query params when the user leaves the search page
   */
  resetController(controller, isExiting, transition) {
    // Check if the user is leaving the page
    if (isExiting) {
      // Reset the page
      controller.set('page', 1);

      // Reset the search
      controller.set('search', undefined);

      // Reset the sort
      controller.set('sort', undefined);

      // Reset aggregations
      this.set('selectedAggs', {});
      this.set('currentAggregation', null);
    }
  },

  /**
   * Route actions
   * @todo Enable the user to add new actions without overwriting this
   */
  actions: {
    changePage: function(pageNumber) {
      // Change the page number
      this.transitionTo({ queryParams: { page: pageNumber } })
    },
    changeSort: function(sort) {
      // Change the sort
      this.transitionTo({ queryParams: { sort: sort } })
    },
    changeAggs: function() {
      // Refresh the current model
      // @todo Make it as transition to store
      //       the selected aggregations into the URL
      this.refresh();
    }
  },

  /**
   * Generate the search rules and put it into the
   * query that is going to be used
   * @todo Enable this method to be async
   * @param {YeboSDK.Products} query The query that is being formed,
   *                           so new rules will be added into this new query.
   * @param {Object} params Parameters that come from the route
   * @param {Object} aggs The selected aggregations
   * @return {void} This method does not return anything
   */
  searchRules(query, params, aggs) {
    // Generated rules
    let rules = [];

    // Set the price filter
    if( aggs.price !== undefined )
      rules.push(new YeboSDK.Products.Rules.price(aggs.price.split('-')));

    // Set filters
    if( aggs.filters !== undefined ) {
      // Each the filters
      for( let filter in aggs.filters ) {
        // Get the filter values
        let values = aggs.filters[filter];

        // Add the rule
        rules.push(new YeboSDK.Products.Rules.filter(filter, values));
      }
    }

    // As default set the relation
    // between the aggregations as `and`
    query.and(rules);
  },

  /**
   * This method enable the route to add info into the
   * model object that is returned
   * @param {Object} params Parameters that come from the route
   * @return {Object} This object will be merged into the default model
   *                  from the search. It can be a Promise that
   *                  returns an Object.
   */
  searchModel(params) {
    // As default return an empty object
    return {};
  }
});
<commit_msg>Default taxon root (for aggregations)<commit_after>// Ember!
import Ember from 'ember';

// Merge method
const { merge } = Ember;

/**
 * Default route for product search,
 * its based on the YeboSDK Query system
 * @todo Define some outlets like 'aggs', 'pagination', 'sort',
 *       to share more components in the search pages
 */
export default Ember.Mixin.create({
  /**
   * Default params
   * @todo Enable the user to add new actions without overwriting this
   */
  queryParams: {
    search: {
      refreshModel: true
    },
    page: {
      refreshModel: true
    },
    sort: {
      refreshModel: true
    }
  },

  /**
   * Default Sort
   */
  defaultSort: 'price-desc',

  /**
   * Products per page
   */
  perPage: 15,

  /**
   * Default price ranges
   */
  defaultPriceRanges: [
    { to: 49.99 },
    { from: 50, to: 99.99 },
    { from: 100 }
  ],

  /**
   * Default taxon root (used to generate the aggregations)
   */
  defaultTaxonRoot: undefined,

  /**
   * Current aggregation
   */
  currentAggregation: null,

  /**
   * Selected aggregations used to generate
   * the next query
   */
  selectedAggs: {},

  /**
   * Flag to check if its necessary to fetch
   * aggregations again
   * @todo Enable the aggregations to be refreshed
   */
  refreshAggregations: false,

  /**
   * Route Model
   */
  model: function(params) {
    // Create a new query
    let query = new YeboSDK.Products();

    // Define the number of results per page
    query.perPage(this.get('perPage'));

    // Set the search
    if( params.search !== undefined )
      query.search(params.search);

    // Define the sort param
    let sortParam;

    // Check if the sort param exists
    if( params.sort !== undefined )
      sortParam = params.sort;
    else
      sortParam = this.get('defaultSort');

    // Sort options
    let sortOptions = sortParam.split('-');

    // Set the page
    query.sortBy(sortOptions[0], sortOptions[1]);

    // Set the page
    query.page(params.page);

    // Define the search rules
    this.searchRules(query, params, this.get('selectedAggs'));

    // Set the default promises
    let defaultPromises = {
      search: this.get('yebo.products').search(query),
      taxonomies: this.yebo.store.findAll('taxonomy'),
      selectedAggs: this.get('selectedAggs')
    };

    // Check if its necessary to get the aggregations
    if( this.get('currentAggregation') === null || this.get('refreshAggregations') )
      defaultPromises.aggs = query.aggregations(this.get('defaultTaxonRoot'), this.get('defaultPriceRanges'));
    else
      defaultPromises.aggs = this.get('currentAggregation');

    // Return a promise to the route
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Execute the default model promises
      Ember.RSVP.hash(defaultPromises).then((result) => {
        // Set the current aggregation
        this.set('currentAggregation', result.aggs);

        // Define the route model
        let routeModel = this.searchModel(params);

        // Check if it is a promise
        if( routeModel.then === undefined )
          return resolve(merge(routeModel, result));

        // Resolve the promise with the result
        routeModel.then((res) => { resolve(merge(res, result)); });
      });
    });
  },

  /**
   * Clear the query params when the user leaves the search page
   */
  resetController(controller, isExiting, transition) {
    // Check if the user is leaving the page
    if (isExiting) {
      // Reset the page
      controller.set('page', 1);

      // Reset the search
      controller.set('search', undefined);

      // Reset the sort
      controller.set('sort', undefined);

      // Reset aggregations
      this.set('selectedAggs', {});
      this.set('currentAggregation', null);
    }
  },

  /**
   * Route actions
   * @todo Enable the user to add new actions without overwriting this
   */
  actions: {
    changePage: function(pageNumber) {
      // Change the page number
      this.transitionTo({ queryParams: { page: pageNumber } })
    },
    changeSort: function(sort) {
      // Change the sort
      this.transitionTo({ queryParams: { sort: sort } })
    },
    changeAggs: function() {
      // Refresh the current model
      // @todo Make it as transition to store
      //       the selected aggregations into the URL
      this.refresh();
    }
  },

  /**
   * Generate the search rules and put it into the
   * query that is going to be used
   * @todo Enable this method to be async
   * @param {YeboSDK.Products} query The query that is being formed,
   *                           so new rules will be added into this new query.
   * @param {Object} params Parameters that come from the route
   * @param {Object} aggs The selected aggregations
   * @return {void} This method does not return anything
   */
  searchRules(query, params, aggs) {
    // Generated rules
    let rules = [];

    // Set the price filter
    if( aggs.price !== undefined )
      rules.push(new YeboSDK.Products.Rules.price(aggs.price.split('-')));

    // Set filters
    if( aggs.filters !== undefined ) {
      // Each the filters
      for( let filter in aggs.filters ) {
        // Get the filter values
        let values = aggs.filters[filter];

        // Add the rule
        rules.push(new YeboSDK.Products.Rules.filter(filter, values));
      }
    }

    // As default set the relation
    // between the aggregations as `and`
    query.and(rules);
  },

  /**
   * This method enable the route to add info into the
   * model object that is returned
   * @param {Object} params Parameters that come from the route
   * @return {Object} This object will be merged into the default model
   *                  from the search. It can be a Promise that
   *                  returns an Object.
   */
  searchModel(params) {
    // As default return an empty object
    return {};
  }
});
<|endoftext|>


<commit_before>'use strict';

const Copterface = require('./lib/copterface');
const arDrone = require('ar-drone');
const Client = arDrone.Client;

//const client = arDrone.createClient({imageSize:"160x90"});
const control = arDrone.createUdpControl();
//const pngStream = client.getPngStream();
const PNG = require('pngjs').PNG;
const PngServer = require('./lib/PngServer');
const readline = require('readline');
const pngServer = new PngServer();
const udpNavdatasStream = new Client.UdpNavdataStream();

const FLYING_OK = "FLYING_OK";
const CTRL_FLYING = "CTRL_FLYING";
const videoOpts = {imageSize:"320x170"};
const altitudeAdjustSpeed = 1;
const adjustSpeed = 0.2;
const forwardVelocity = 200;
const decay = 0.8;

const pngEncoder = new Client.PngEncoder(videoOpts);
const videoStream = new Client.TcpVideoStream(videoOpts);

var videoEventCallback = function(err) {
    if (err) {
      console.log('TcpVideoStream error: %s', err.message);
      setTimeout(function () {
        console.log('Attempting to reconnect to TcpVideoStream...');
        videoStream.connect(videoEventCallback);
      }, 1000);
    }
  }

  pngEncoder.on('error', (e)=>{
  	console.log(e);
  	process.exit();
  });

  videoStream.connect(videoEventCallback);
  videoStream.on('error', videoEventCallback);

  videoStream.on('data', function(data) {
  	pngEncoder.write(data);
  });

var lastLED = 0;
var navdata;
var movement = {x:0,y:0,z:0,yaw:0,desiredForwardSpeed:0};
var pcmd = {};
var flying = false;
var emergency = false;
var bestFaceRect;
var newFrameRecieved = false;
var lastFrameTime = Date.now();
var frameDelayTimeout = 1000;
var copterface = Copterface(pngEncoder,{ outputImage:true  },function(info){
	if( !navdata || !navdata.demo ){
		return;
	}

	newFrameRecieved = true;
	lastFrameTime = Date.now();

	process.stdout.write('\x1B[2J\x1B[0f');
	console.log("SPACE to takeoff and land. Debug face information can be viewed at http://localhost:8000");
	console.log("Escpae does an emergency cut out. The C key does ground calibration");
	var best = info.rects.reduce((a,b)=>(a && a.confidence>b.confidence)?a:b, null);

	console.log("Console State:"+navdata.demo.controlState+"   Fly State:"+navdata.demo.flyState+" battery:"+navdata.demo.batteryPercentage+"% ");
	console.log("altitude:"+navdata.demo.altitude+" flying:"+navdata.droneState.flying);
	if( best && best.confidence > 0){
		var time = Date.now();
		if( ( time - lastLED ) > 1000 ){
			control.animateLeds("blinkOrange",1,1);
			lastLED = time;
		}
		console.log("FACE AT x:"+best.x.toFixed(2)+",y:"+best.y.toFixed(2)+" width:"+best.width.toFixed(2));

		bestFaceRect = best;

	}
	else {
		console.log("no face detected");
		//correctAltitude();

	}

	//driftCompensation();

	if(info.image){
		pngServer.sendPng(info.image);
	}

	var cols = 80;
	drawCentreHeading(cols, "  velocity  ");
	drawCentreFloat( cols, "z="+Math.round( navdata.demo.velocity.x ) );
	drawBar(cols, navdata.demo.velocity.x / 200);
	drawCentreFloat( cols, "x="+Math.round(  navdata.demo.velocity.y ) );
	drawBar(cols, navdata.demo.velocity.y / 200);
	drawCentreFloat( cols, "y="+Math.round( navdata.demo.velocity.z ) );
	drawBar(cols, navdata.demo.velocity.z / 200);
	drawCentreHeading(cols, "  movement  ");

	Object.keys(movement).forEach((key)=>{
		drawCentreFloat( cols, key+"="+movement[key].toFixed() );
		drawBar(cols, movement[key]);
	});

});

function drawCentreFloat(size,t){
	var half = Math.round((size)/2);
	console.log( (t).toString().padEnd(half," ").padStart(size," ") );
}

function drawCentreHeading(size,label){
	console.log("".padStart(size/2-Math.floor(label.length/2),"#") +label+"".padStart(size/2-Math.ceil(label.length/2),"#"));

}

function drawBar(size,t){
	if( t > 1 ) t = 1;
	if( t < -1 ) t = -1;
	var half = Math.round((size-1)/2);
	if( t<0 ) half++;
	var padSide = ( t < 0 )? String.prototype.padStart : String.prototype.padEnd;
	var oppositeSide = ( t >= 0 ) ? String.prototype.padStart : String.prototype.padEnd;
	t = Math.abs(t);
	console.log( oppositeSide.call(padSide.call(padSide.call("+",Math.round(half*t),"-"),half," "),size," ") );

}

function decayMovement(){
	movement.x *= decay;
	movement.y *= decay;
	movement.z *= decay;
	movement.yaw *= decay;
	movement.desiredForwardSpeed *= decay;
}

function move(){
	//var ref = {  }
	//control.ref(ref);
	if( navdata && navdata.droneState.flying ){
		pcmd[ ( movement.yaw > 0 ) ? "clockwise":"counterClockwise" ] = Math.abs(movement.yaw);
		if(movement.x!=0) pcmd[ ( movement.x > 0 ) ? "right" : "left" ] = Math.abs(movement.x);
		if(movement.y!=0) pcmd[ ( movement.y > 0 ) ? "up":"down" ] = Math.abs(movement.y);
		if(movement.z!=0) pcmd[ ( movement.z > 0 ) ? "front" : "back" ] = Math.abs(movement.z);
	}
	control.ref({ fly:flying, emergency:emergency });
	control.pcmd(pcmd);
	control.flush();
	pcmd = {};

}

function draw(){

}

udpNavdatasStream.on('data',(d)=>{
	navdata=d;
	update();
});

function update(){
	var elapsed = Date.now() - lastFrameTime;

	if( newFrameRecieved ){

		newFrameRecieved = false;
	}

	if(elapsed > frameDelayTimeout){
		flying = false;

	}
	correctAltitude();
	driftCompensation();
	lookAtFace();
	move();
	decayMovement();

}

function lookAtFace(){
		if( !bestFaceRect )
			return;
		var best = bestFaceRect;
		bestFaceRect = null;

		const moveThreshold = 0.15;

		if( best.x < -moveThreshold ){
			movement.yaw = -adjustSpeed;
		}
		else if(best.x > moveThreshold){
			movement.yaw = adjustSpeed;
		}
		else {
			movement.yaw = 0;
		}

		if( best.y < -moveThreshold ){
			movement.y = adjustSpeed;
		}
		else if(best.y > moveThreshold){
			movement.y = -adjustSpeed;

		}
		else {
			movement.y = 0;
		}

		if( best.width > 0 && best.width < moveThreshold ){
			movement.desiredForwardSpeed = forwardVelocity;
		}

}

function driftCompensation(){
	if( navdata.demo ){
		var compensationThresold = 50;
		if( Math.abs( navdata.demo.velocity.x - movement.desiredForwardSpeed ) > compensationThresold ){
			movement.z = (-navdata.demo.velocity.x + movement.desiredForwardSpeed) / 2000 ;
		}
		if( Math.abs(navdata.demo.velocity.y) > compensationThresold ){
			movement.x = -navdata.demo.velocity.y / 2000 ;
		}

	}
}

function correctAltitude(){
	if( !navdata.demo ){
		return;
	}

	if( navdata.demo.altitude > 0 && navdata.demo.altitude < 1.2 ){
		movement.y = altitudeAdjustSpeed;
	}
	else if( navdata.demo.altitude > 1.8 ){
		movement.y = -altitudeAdjustSpeed;
	}

}

udpNavdatasStream.resume();

copterface.start();

setTimeout(()=>{
	control.config('general:navdata_demo', 'TRUE');
},500);

readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);

process.stdin.on('keypress', (str, key) => {

  if (key.ctrl && key.name === 'c') {
    process.exit();
  }else if(key.name === 'c'){
  	//client.calibrate(0);
  	if( navdata.droneState.flying === 0 ){
	  	control.raw('FTRIM',)
		control.flush();
		console.log("setting ground");
  	}
  }
  if( key.name === 'space' && navdata){

  	if( navdata.droneState.emergencyLanding === 1 ){
  		var startTime = Date.now();
  		console.log("Reseting drone");

  		var intervalID = setInterval(()=>{
  			emergency = true;
			control.config('general:navdata_demo', 'TRUE');
			control.ref({ fly:flying, emergency:emergency });
		  	control.flush();
		  	var elapsed = Date.now() - startTime;
		  	if( elapsed >1000 ){
		  		clearInterval(intervalID);
		  		emergency = false;
		  	}
  		},30);

		return;
  	}

  	if( navdata.droneState.flying === 0 ){
	  	//client.takeoff();
	  	flying = true;
 	  }
  	else
  		//client.land();
	  	flying = false;

  }
  if( key.name === 'escape' ){
  	flying = false;
  	control.raw('REF', 1<<8);
  	control.flush();

  }
});

<commit_msg>Nothing much<commit_after>'use strict';

const Copterface = require('./lib/copterface');
const arDrone = require('ar-drone');
const Client = arDrone.Client;

//const client = arDrone.createClient({imageSize:"160x90"});
const control = arDrone.createUdpControl();
//const pngStream = client.getPngStream();
const PNG = require('pngjs').PNG;
const PngServer = require('./lib/PngServer');
const readline = require('readline');
const pngServer = new PngServer();
const udpNavdatasStream = new Client.UdpNavdataStream();

const FLYING_OK = "FLYING_OK";
const CTRL_FLYING = "CTRL_FLYING";
const videoOpts = {imageSize:"320x170"};
const altitudeAdjustSpeed = 1;
const adjustSpeed = 0.2;
const forwardVelocity = 200;
const decay = 0.8;
const frameDelayTimeout = 1000;

const pngEncoder = new Client.PngEncoder(videoOpts);
const videoStream = new Client.TcpVideoStream(videoOpts);

var videoEventCallback = function(err) {
    if (err) {
      console.log('TcpVideoStream error: %s', err.message);
      setTimeout(function () {
        console.log('Attempting to reconnect to TcpVideoStream...');
        videoStream.connect(videoEventCallback);
      }, 1000);
    }
  }

  pngEncoder.on('error', (e)=>{
  	console.log(e);
  	process.exit();
  });

  videoStream.connect(videoEventCallback);
  videoStream.on('error', videoEventCallback);

  videoStream.on('data', function(data) {
  	pngEncoder.write(data);
  });

var lastLED = 0;
var navdata;
var movement = {x:0,y:0,z:0,yaw:0,desiredForwardSpeed:0};
var pcmd = {};
var flying = false;
var emergency = false;
var bestFaceRect;
var newFrameRecieved = false;
var lastFrameTime = Date.now();

var copterface = Copterface(pngEncoder,{ outputImage:true  },function(info){
	if( !navdata || !navdata.demo ){
		return;
	}

	newFrameRecieved = true;
	lastFrameTime = Date.now();

	process.stdout.write('\x1B[2J\x1B[0f');
	console.log("SPACE to takeoff and land. Debug face information can be viewed at http://localhost:8000");
	console.log("Escpae does an emergency cut out. The C key does ground calibration");
	var best = info.rects.reduce((a,b)=>(a && a.confidence>b.confidence)?a:b, null);

	console.log("Console State:"+navdata.demo.controlState+"   Fly State:"+navdata.demo.flyState+" battery:"+navdata.demo.batteryPercentage+"% ");
	console.log("altitude:"+navdata.demo.altitude+" flying:"+navdata.droneState.flying);
	if( best && best.confidence > 0){
		var time = Date.now();
		if( ( time - lastLED ) > 1000 ){
			control.animateLeds("blinkOrange",1,1);
			lastLED = time;
		}
		console.log("FACE AT x:"+best.x.toFixed(2)+",y:"+best.y.toFixed(2)+" width:"+best.width.toFixed(2));

		bestFaceRect = best;

	}
	else {
		console.log("no face detected");
		//correctAltitude();

	}

	//driftCompensation();

	if(info.image){
		pngServer.sendPng(info.image);
	}

	var cols = 80;
	drawCentreHeading(cols, "  velocity  ");
	drawCentreFloat( cols, "z="+Math.round( navdata.demo.velocity.x ) );
	drawBar(cols, navdata.demo.velocity.x / 200);
	drawCentreFloat( cols, "x="+Math.round(  navdata.demo.velocity.y ) );
	drawBar(cols, navdata.demo.velocity.y / 200);
	drawCentreFloat( cols, "y="+Math.round( navdata.demo.velocity.z ) );
	drawBar(cols, navdata.demo.velocity.z / 200);
	drawCentreHeading(cols, "  movement  ");

	Object.keys(movement).forEach((key)=>{
		drawCentreFloat( cols, key+"="+movement[key].toFixed() );
		drawBar(cols, movement[key]);
	});

});

function drawCentreFloat(size,t){
	var half = Math.round((size)/2);
	console.log( (t).toString().padEnd(half," ").padStart(size," ") );
}

function drawCentreHeading(size,label){
	console.log("".padStart(size/2-Math.floor(label.length/2),"#") +label+"".padStart(size/2-Math.ceil(label.length/2),"#"));

}

function drawBar(size,t){
	if( t > 1 ) t = 1;
	if( t < -1 ) t = -1;
	var half = Math.round((size-1)/2);
	if( t<0 ) half++;
	var padSide = ( t < 0 )? String.prototype.padStart : String.prototype.padEnd;
	var oppositeSide = ( t >= 0 ) ? String.prototype.padStart : String.prototype.padEnd;
	t = Math.abs(t);
	console.log( oppositeSide.call(padSide.call(padSide.call("+",Math.round(half*t),"-"),half," "),size," ") );

}

function decayMovement(){
	movement.x *= decay;
	movement.y *= decay;
	movement.z *= decay;
	movement.yaw *= decay;
	movement.desiredForwardSpeed *= decay;
}

function move(){
	//var ref = {  }
	//control.ref(ref);
	if( navdata && navdata.droneState.flying ){
		pcmd[ ( movement.yaw > 0 ) ? "clockwise":"counterClockwise" ] = Math.abs(movement.yaw);
		if(movement.x!=0) pcmd[ ( movement.x > 0 ) ? "right" : "left" ] = Math.abs(movement.x);
		if(movement.y!=0) pcmd[ ( movement.y > 0 ) ? "up":"down" ] = Math.abs(movement.y);
		if(movement.z!=0) pcmd[ ( movement.z > 0 ) ? "front" : "back" ] = Math.abs(movement.z);
	}
	control.ref({ fly:flying, emergency:emergency });
	control.pcmd(pcmd);
	control.flush();
	pcmd = {};

}

function draw(){

}

udpNavdatasStream.on('data',(d)=>{
	navdata=d;
	update();
});

function update(){
	var elapsed = Date.now() - lastFrameTime;

	if( newFrameRecieved ){

		newFrameRecieved = false;
	}

	if(elapsed > frameDelayTimeout){
		flying = false;

	}
	correctAltitude();
	driftCompensation();
	lookAtFace();
	move();
	decayMovement();

}

function lookAtFace(){
		if( !bestFaceRect )
			return;
		var best = bestFaceRect;
		bestFaceRect = null;

		const moveThreshold = 0.15;

		if( best.x < -moveThreshold ){
			movement.yaw = -adjustSpeed;
		}
		else if(best.x > moveThreshold){
			movement.yaw = adjustSpeed;
		}
		else {
			movement.yaw = 0;
		}

		if( best.y < -moveThreshold ){
			movement.y = adjustSpeed;
		}
		else if(best.y > moveThreshold){
			movement.y = -adjustSpeed;

		}
		else {
			movement.y = 0;
		}

		if( best.width > 0 && best.width < moveThreshold ){
			movement.desiredForwardSpeed = forwardVelocity;
		}

}

function driftCompensation(){
	if( navdata.demo ){
		var compensationThresold = 50;
		if( Math.abs( navdata.demo.velocity.x - movement.desiredForwardSpeed ) > compensationThresold ){
			movement.z = (-navdata.demo.velocity.x + movement.desiredForwardSpeed) / 2000 ;
		}
		if( Math.abs(navdata.demo.velocity.y) > compensationThresold ){
			movement.x = -navdata.demo.velocity.y / 2000 ;
		}

	}
}

function correctAltitude(){
	if( !navdata.demo ){
		return;
	}

	if( navdata.demo.altitude > 0 && navdata.demo.altitude < 1.2 ){
		movement.y = altitudeAdjustSpeed;
	}
	else if( navdata.demo.altitude > 1.8 ){
		movement.y = -altitudeAdjustSpeed;
	}

}

udpNavdatasStream.resume();

copterface.start();

setTimeout(()=>{
	control.config('general:navdata_demo', 'TRUE');
},500);

readline.emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);

process.stdin.on('keypress', (str, key) => {

  if (key.ctrl && key.name === 'c') {
    process.exit();
  }else if(key.name === 'c'){
  	//client.calibrate(0);
  	if( navdata.droneState.flying === 0 ){
	  	control.raw('FTRIM',)
		control.flush();
		console.log("setting ground");
  	}
  }
  if( key.name === 'space' && navdata){

  	if( navdata.droneState.emergencyLanding === 1 ){
  		var startTime = Date.now();
  		console.log("Reseting drone");

  		var intervalID = setInterval(()=>{
  			emergency = true;
			control.config('general:navdata_demo', 'TRUE');
			control.ref({ fly:flying, emergency:emergency });
		  	control.flush();
		  	var elapsed = Date.now() - startTime;
		  	if( elapsed >1000 ){
		  		clearInterval(intervalID);
		  		emergency = false;
		  	}
  		},30);

		return;
  	}

  	if( navdata.droneState.flying === 0 ){
	  	//client.takeoff();
	  	flying = true;
 	  }
  	else
  		//client.land();
	  	flying = false;

  }
  if( key.name === 'escape' ){
  	flying = false;
  	control.raw('REF', 1<<8);
  	control.flush();

  }
});

<|endoftext|>


<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";
var Tinkerforge = require('tinkerforge');
var devices = require('../lib/devices');

module.exports = function(RED) {

	function tinkerForgeRemoteSwitch(n) {
        RED.nodes.createNode(this,n);
        this.device = n.device;
        this.sensor = n.sensor;
        this.name = n.name;
        this.mode = n.mode;
	    this.addr1 = n.addr1;
	    this.addr2 = n.addr2;
        this.topic = n.topic;
        var node = this;

        node.ipcon = new Tinkerforge.IPConnection(); //devices[this.device].ipcon;
        node.ipcon.setAutoReconnect(true);
        var devs = devices.getDevices();
        node.ipcon.connect(devs[node.device].host, devs[node.device].port,function(error){
            if(error) {
                node.warn("couldn't connect");
            }
        });

        node.ipcon.on(Tinkerforge.IPConnection.CALLBACK_CONNECTED,
        function(connectReason) {
            node.md = new Tinkerforge.BrickletRemoteSwitch(node.sensor, node.ipcon);
        });

	node.on('input', function(msg){
	    if(node.md) {
	      if (typeof msg.payload === 'number') {
	        if (msg.payload == 1) {
                      var switchto = Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_ON;
		    }
		    else {
                      var switchto = Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_OFF;
 		    }
                switch (node.mode) {
                case 'A':node.md.switchSocketA(node.addr1, node.addr2, switchto);break;
                case 'B':node.md.switchSocketB(node.addr1, node.addr2, switchto);break;
                }
	      }
	    }
    });

        node.on('close',function() {
            node.ipcon.disconnect();
        });
    }

    RED.nodes.registerType('Tinkerforge RemoteSwitch', tinkerForgeRemoteSwitch);
}<commit_msg>Add Support for Type C Switches<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";
var Tinkerforge = require('tinkerforge');
var devices = require('../lib/devices');

module.exports = function(RED) {

	function tinkerForgeRemoteSwitch(n) {
        RED.nodes.createNode(this,n);
        this.device = n.device;
        this.sensor = n.sensor;
        this.name = n.name;
        this.mode = n.mode;
	    this.addr1 = n.addr1;
	    this.addr2 = n.addr2;
        this.topic = n.topic;
        var node = this;

        node.ipcon = new Tinkerforge.IPConnection(); //devices[this.device].ipcon;
        node.ipcon.setAutoReconnect(true);
        var devs = devices.getDevices();
        node.ipcon.connect(devs[node.device].host, devs[node.device].port,function(error){
            if(error) {
                node.warn("couldn't connect");
            }
        });

        node.ipcon.on(Tinkerforge.IPConnection.CALLBACK_CONNECTED,
        function(connectReason) {
            node.md = new Tinkerforge.BrickletRemoteSwitch(node.sensor, node.ipcon);
        });

	node.on('input', function(msg){
	    if(node.md) {
	      if (typeof msg.payload === 'number') {
	        if (msg.payload == 1) {
                      var switchto = Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_ON;
		    }
		    else {
                      var switchto = Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_OFF;
 		    }
                switch (node.mode) {
                case 'A':node.md.switchSocketA(node.addr1, node.addr2, switchto);break;
                case 'B':node.md.switchSocketB(node.addr1, node.addr2, switchto);break;
                case 'C':node.md.switchSocketC(node.addr1, node.addr2, switchto);break;
                }
	      }
	    }
    });

        node.on('close',function() {
            node.ipcon.disconnect();
        });
    }

    RED.nodes.registerType('Tinkerforge RemoteSwitch', tinkerForgeRemoteSwitch);
}<|endoftext|>


<commit_before>// @flow

import React, { Component, createElement, cloneElement } from 'react';
import type { Element } from 'react';
import classNames from 'classnames';
import { createStyleSheet } from 'jss-theme-reactor';
import withStyles from '../styles/withStyles';
import { duration } from '../styles/transitions';
import ClickAwayListener from '../internal/ClickAwayListener';
import { capitalizeFirstLetter, createChainedFunction } from '../utils/helpers';
import Slide from '../transitions/Slide';
import SnackbarContent from './SnackbarContent';

export const styleSheet = createStyleSheet('MuiSnackbar', theme => {
  const gutter = theme.spacing.unit * 3;
  const top = { top: 0 };
  const bottom = { bottom: 0 };
  const right = { justifyContent: 'flex-end' };
  const left = { justifyContent: 'flex-start' };
  const topSpace = { top: gutter };
  const bottomSpace = { bottom: gutter };
  const rightSpace = { right: gutter };
  const leftSpace = { left: gutter };

  return {
    root: {
      zIndex: theme.zIndex.snackbar,
      position: 'fixed',
      display: 'flex',
      left: 0,
      right: 0,
      justifyContent: 'center',
      alignItems: 'center',
      pointerEvents: 'none',
    },
    anchorTopCenter: {
      extend: [top],
    },
    anchorBottomCenter: {
      extend: [bottom],
    },
    anchorTopRight: {
      extend: [top, right],
      [theme.breakpoints.up('md')]: {
        extend: [topSpace, rightSpace],
      },
    },
    anchorBottomRight: {
      extend: [bottom, right],
      [theme.breakpoints.up('md')]: {
        extend: [bottomSpace, rightSpace],
      },
    },
    anchorTopLeft: {
      extend: [top, left],
      [theme.breakpoints.up('md')]: {
        extend: [topSpace, leftSpace],
      },
    },
    anchorBottomLeft: {
      extend: [bottom, left],
      [theme.breakpoints.up('md')]: {
        extend: [bottomSpace, leftSpace],
      },
    },
  };
});

type Origin = {
  horizontal: 'left' | 'center' | 'right' | number,
  vertical: 'top' | 'center' | 'bottom' | number,
};

type DefaultProps = {
  anchorOrigin: Origin,
  autoHideDuration: ?number,
  enterTransitionDuration: number,
  leaveTransitionDuration: number,
};

type Props = DefaultProps & {
  /**
   * The action to display.
   */
  action?: Element<*>,
  /**
   * The anchor of the `Snackbar`.
   */
  anchorOrigin?: Origin,
  /**
   * The number of milliseconds to wait before automatically dismissing.
   * This behavior is disabled by default with the `null` value.
   */
  autoHideDuration?: number,
  /**
   * If you wish the take control over the children of the component you can use that property.
   * When using it, no `SnackbarContent` component will be rendered.
   */
  children?: Element<*>,
  /**
   * Useful to extend the style applied to components.
   */
  classes: Object,
  /**
   * @ignore
   */
  className?: string,
  /**
   * Customizes duration of enter animation (ms)
   */
  enterTransitionDuration?: number,
  /**
   * When displaying multiple consecutive Snackbars from a parent renedering a single
   * <Snackbar/>, add the key property to ensure independent treatment of each message.
   * e.g. <Snackbar key={message} />, otherwise, the message may update-in-place and
   * features such as autoHideDuration may be canceled.
   */
  key?: any,
  /**
   * Customizes duration of leave animation (ms)
   */
  leaveTransitionDuration?: number,
  /**
   * The message to display.
   */
  message?: Element<*>,
  /**
   * Callback fired before the transition is entering.
   */
  onEnter?: Function,
  /**
   * Callback fired when the transition is entering.
   */
  onEntering?: Function,
  /**
   * Callback fired when the transition has entered.
   */
  onEntered?: Function,
  /**
   * Callback fired before the transition is exiting.
   */
  onExit?: Function,
  /**
   * Callback fired when the transition is exiting.
   */
  onExiting?: Function,
  /**
   * Callback fired when the transition has exited.
   */
  onExited?: Function,
  /**
   * @ignore
   */
  onMouseEnter?: Function,
  /**
   * @ignore
   */
  onMouseLeave?: Function,
  /**
   * Callback fired when the component requests to be closed.
   *
   * Typically `onRequestClose` is used to set state in the parent component,
   * which is used to control the `Snackbar` `open` prop.
   *
   * The `reason` parameter can optionally be used to control the response to `onRequestClose`,
   * for example ignoring `clickaway`.
   *
   * @param {event} event The event that triggered the close request
   * @param {string} reason Can be:`"timeout"` (`autoHideDuration` expired) or: `"clickaway"`
   */
  onRequestClose?: (event: ?SyntheticUIEvent, reason: string) => void,
  /**
   * If true, `Snackbar` is open.
   */
  open: boolean,
  /**
   * Properties applied to the `SnackbarContent` element.
   */
  SnackbarContentProps?: Object,
  /**
   * Object with Transition component, props & create Fn.
   */
  transition?: Function | Element<*>,
};

type State = {
  exited: boolean,
};

class Snackbar extends Component<DefaultProps, Props, State> {
  props: Props;
  static defaultProps: DefaultProps = {
    anchorOrigin: { vertical: 'bottom', horizontal: 'center' },
    autoHideDuration: null,
    enterTransitionDuration: duration.enteringScreen,
    leaveTransitionDuration: duration.leavingScreen,
  };

  state: State = {
    // Used to only render active snackbars.
    exited: false,
  };

  componentWillMount() {
    if (!this.props.open) {
      this.setState({ exited: true });
    }
  }

  componentDidMount() {
    if (this.props.open) {
      this.setAutoHideTimer();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.open && this.state.exited) {
      this.setState({ exited: false });
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.open !== this.props.open) {
      if (this.props.open) {
        this.setAutoHideTimer();
      } else {
        clearTimeout(this.timerAutoHide);
      }
    }
  }

  componentWillUnmount() {
    clearTimeout(this.timerAutoHide);
  }

  timerAutoHide = null;

  // Timer that controls delay before snackbar auto hides
  setAutoHideTimer(autoHideDuration = null) {
    if (this.props.autoHideDuration === null) {
      return;
    }

    clearTimeout(this.timerAutoHide);
    this.timerAutoHide = setTimeout(() => {
      if (this.props.onRequestClose) {
        this.props.onRequestClose(null, 'timeout');
      }
    }, autoHideDuration || this.props.autoHideDuration);
  }

  handleMouseEnter = event => {
    if (this.props.onMouseEnter) {
      this.props.onMouseEnter(event);
    }
    clearTimeout(this.timerAutoHide);
  };

  handleMouseLeave = event => {
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
    this.setAutoHideTimer(2e3);
  };

  handleClickAway = event => {
    if (this.props.onRequestClose) {
      this.props.onRequestClose(event, 'clickaway');
    }
  };

  handleTransitionExited = () => {
    this.setState({ exited: true });
  };

  render() {
    const {
      action,
      anchorOrigin: { vertical, horizontal },
      autoHideDuration,
      children,
      classes,
      className,
      enterTransitionDuration,
      leaveTransitionDuration,
      message,
      onEnter,
      onEntering,
      onEntered,
      onExit,
      onExiting,
      onExited,
      onMouseEnter,
      onMouseLeave,
      onRequestClose,
      open,
      SnackbarContentProps,
      // $FlowFixMe - invalid error? Property cannot be accessed on any member of intersection type
      transition: transitionProp,
      ...other
    } = this.props;

    if (!open && this.state.exited) {
      return null;
    }

    const createTransitionFn = typeof transitionProp === 'function' ? createElement : cloneElement;
    const transition = transitionProp || <Slide direction={vertical === 'top' ? 'down' : 'up'} />;

    return (
      <ClickAwayListener onClickAway={this.handleClickAway}>
        <div
          className={classNames(
            classes.root,
            classes[`anchor${capitalizeFirstLetter(vertical)}${capitalizeFirstLetter(horizontal)}`],
            className,
          )}
          onMouseEnter={this.handleMouseEnter}
          onMouseLeave={this.handleMouseLeave}
          {...other}
        >
          {createTransitionFn(
            transition,
            {
              in: open,
              transitionAppear: true,
              enterTransitionDuration,
              leaveTransitionDuration,
              onEnter,
              onEntering,
              onEntered,
              onExit,
              onExiting,
              onExited: createChainedFunction(this.handleTransitionExited, onExited),
            },
            children ||
              <SnackbarContent message={message} action={action} {...SnackbarContentProps} />,
          )}
        </div>
      </ClickAwayListener>
    );
  }
}

export default withStyles(styleSheet)(Snackbar);
<commit_msg>[Snackbar] Fix flow issue<commit_after>// @flow

import React, { Component, createElement, cloneElement } from 'react';
import type { Element } from 'react';
import classNames from 'classnames';
import { createStyleSheet } from 'jss-theme-reactor';
import withStyles from '../styles/withStyles';
import { duration } from '../styles/transitions';
import ClickAwayListener from '../internal/ClickAwayListener';
import { capitalizeFirstLetter, createChainedFunction } from '../utils/helpers';
import Slide from '../transitions/Slide';
import SnackbarContent from './SnackbarContent';

export const styleSheet = createStyleSheet('MuiSnackbar', theme => {
  const gutter = theme.spacing.unit * 3;
  const top = { top: 0 };
  const bottom = { bottom: 0 };
  const right = { justifyContent: 'flex-end' };
  const left = { justifyContent: 'flex-start' };
  const topSpace = { top: gutter };
  const bottomSpace = { bottom: gutter };
  const rightSpace = { right: gutter };
  const leftSpace = { left: gutter };

  return {
    root: {
      zIndex: theme.zIndex.snackbar,
      position: 'fixed',
      display: 'flex',
      left: 0,
      right: 0,
      justifyContent: 'center',
      alignItems: 'center',
      pointerEvents: 'none',
    },
    anchorTopCenter: {
      extend: [top],
    },
    anchorBottomCenter: {
      extend: [bottom],
    },
    anchorTopRight: {
      extend: [top, right],
      [theme.breakpoints.up('md')]: {
        extend: [topSpace, rightSpace],
      },
    },
    anchorBottomRight: {
      extend: [bottom, right],
      [theme.breakpoints.up('md')]: {
        extend: [bottomSpace, rightSpace],
      },
    },
    anchorTopLeft: {
      extend: [top, left],
      [theme.breakpoints.up('md')]: {
        extend: [topSpace, leftSpace],
      },
    },
    anchorBottomLeft: {
      extend: [bottom, left],
      [theme.breakpoints.up('md')]: {
        extend: [bottomSpace, leftSpace],
      },
    },
  };
});

type Origin = {
  horizontal?: 'left' | 'center' | 'right' | number,
  vertical?: 'top' | 'center' | 'bottom' | number,
};

type DefaultProps = {
  anchorOrigin: Origin,
  autoHideDuration: ?number,
  enterTransitionDuration: number,
  leaveTransitionDuration: number,
};

type Props = DefaultProps & {
  /**
   * The action to display.
   */
  action?: Element<*>,
  /**
   * The anchor of the `Snackbar`.
   */
  anchorOrigin?: Origin,
  /**
   * The number of milliseconds to wait before automatically dismissing.
   * This behavior is disabled by default with the `null` value.
   */
  autoHideDuration?: number,
  /**
   * If you wish the take control over the children of the component you can use that property.
   * When using it, no `SnackbarContent` component will be rendered.
   */
  children?: Element<*>,
  /**
   * Useful to extend the style applied to components.
   */
  classes: Object,
  /**
   * @ignore
   */
  className?: string,
  /**
   * Customizes duration of enter animation (ms)
   */
  enterTransitionDuration?: number,
  /**
   * When displaying multiple consecutive Snackbars from a parent renedering a single
   * <Snackbar/>, add the key property to ensure independent treatment of each message.
   * e.g. <Snackbar key={message} />, otherwise, the message may update-in-place and
   * features such as autoHideDuration may be canceled.
   */
  key?: any,
  /**
   * Customizes duration of leave animation (ms)
   */
  leaveTransitionDuration?: number,
  /**
   * The message to display.
   */
  message?: Element<*>,
  /**
   * Callback fired before the transition is entering.
   */
  onEnter?: Function,
  /**
   * Callback fired when the transition is entering.
   */
  onEntering?: Function,
  /**
   * Callback fired when the transition has entered.
   */
  onEntered?: Function,
  /**
   * Callback fired before the transition is exiting.
   */
  onExit?: Function,
  /**
   * Callback fired when the transition is exiting.
   */
  onExiting?: Function,
  /**
   * Callback fired when the transition has exited.
   */
  onExited?: Function,
  /**
   * @ignore
   */
  onMouseEnter?: Function,
  /**
   * @ignore
   */
  onMouseLeave?: Function,
  /**
   * Callback fired when the component requests to be closed.
   *
   * Typically `onRequestClose` is used to set state in the parent component,
   * which is used to control the `Snackbar` `open` prop.
   *
   * The `reason` parameter can optionally be used to control the response to `onRequestClose`,
   * for example ignoring `clickaway`.
   *
   * @param {event} event The event that triggered the close request
   * @param {string} reason Can be:`"timeout"` (`autoHideDuration` expired) or: `"clickaway"`
   */
  onRequestClose?: (event: ?SyntheticUIEvent, reason: string) => void,
  /**
   * If true, `Snackbar` is open.
   */
  open: boolean,
  /**
   * Properties applied to the `SnackbarContent` element.
   */
  SnackbarContentProps?: Object,
  /**
   * Object with Transition component, props & create Fn.
   */
  transition?: Function | Element<*>,
};

type State = {
  exited: boolean,
};

class Snackbar extends Component<DefaultProps, Props, State> {
  props: Props;
  static defaultProps: DefaultProps = {
    anchorOrigin: { vertical: 'bottom', horizontal: 'center' },
    autoHideDuration: null,
    enterTransitionDuration: duration.enteringScreen,
    leaveTransitionDuration: duration.leavingScreen,
  };

  state: State = {
    // Used to only render active snackbars.
    exited: false,
  };

  componentWillMount() {
    if (!this.props.open) {
      this.setState({ exited: true });
    }
  }

  componentDidMount() {
    if (this.props.open) {
      this.setAutoHideTimer();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.open && this.state.exited) {
      this.setState({ exited: false });
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.open !== this.props.open) {
      if (this.props.open) {
        this.setAutoHideTimer();
      } else {
        clearTimeout(this.timerAutoHide);
      }
    }
  }

  componentWillUnmount() {
    clearTimeout(this.timerAutoHide);
  }

  timerAutoHide = null;

  // Timer that controls delay before snackbar auto hides
  setAutoHideTimer(autoHideDuration = null) {
    if (this.props.autoHideDuration === null) {
      return;
    }

    clearTimeout(this.timerAutoHide);
    this.timerAutoHide = setTimeout(() => {
      if (this.props.onRequestClose) {
        this.props.onRequestClose(null, 'timeout');
      }
    }, autoHideDuration || this.props.autoHideDuration);
  }

  handleMouseEnter = event => {
    if (this.props.onMouseEnter) {
      this.props.onMouseEnter(event);
    }
    clearTimeout(this.timerAutoHide);
  };

  handleMouseLeave = event => {
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
    this.setAutoHideTimer(2e3);
  };

  handleClickAway = event => {
    if (this.props.onRequestClose) {
      this.props.onRequestClose(event, 'clickaway');
    }
  };

  handleTransitionExited = () => {
    this.setState({ exited: true });
  };

  render() {
    const {
      action,
      anchorOrigin: { vertical, horizontal },
      autoHideDuration,
      children,
      classes,
      className,
      enterTransitionDuration,
      leaveTransitionDuration,
      message,
      onEnter,
      onEntering,
      onEntered,
      onExit,
      onExiting,
      onExited,
      onMouseEnter,
      onMouseLeave,
      onRequestClose,
      open,
      SnackbarContentProps,
      // $FlowFixMe - invalid error? Property cannot be accessed on any member of intersection type
      transition: transitionProp,
      ...other
    } = this.props;

    if (!open && this.state.exited) {
      return null;
    }

    const createTransitionFn = typeof transitionProp === 'function' ? createElement : cloneElement;
    const transition = transitionProp || <Slide direction={vertical === 'top' ? 'down' : 'up'} />;

    return (
      <ClickAwayListener onClickAway={this.handleClickAway}>
        <div
          className={classNames(
            classes.root,
            classes[`anchor${capitalizeFirstLetter(vertical)}${capitalizeFirstLetter(horizontal)}`],
            className,
          )}
          onMouseEnter={this.handleMouseEnter}
          onMouseLeave={this.handleMouseLeave}
          {...other}
        >
          {createTransitionFn(
            transition,
            {
              in: open,
              transitionAppear: true,
              enterTransitionDuration,
              leaveTransitionDuration,
              onEnter,
              onEntering,
              onEntered,
              onExit,
              onExiting,
              onExited: createChainedFunction(this.handleTransitionExited, onExited),
            },
            children ||
              <SnackbarContent message={message} action={action} {...SnackbarContentProps} />,
          )}
        </div>
      </ClickAwayListener>
    );
  }
}

export default withStyles(styleSheet)(Snackbar);
<|endoftext|>