<commit_before>define([], function() {
    'use strict';
    /**
     * Directive for a click-to-edit textfield
     *
     * Attributes:
     * * entity: Reference to a restangularified entity
     * * property-name: the name of the property to display/edit
     * * callback: (optional) A callback that will be called after a successful PATCH call to the API.
     * * role: (optional) If a role is needed to edit the field.
     * * own-submit: (optional) submit function to be called, overwrites the intern function. Gets the patch object as the parameter. Must return a promise.
     * * errorCallback: (optional) A function that gets called with the HTTP response in case of an error.
     */
    return ['base.services.user', function(UserService) {
        return {
            restrict: 'E',
            templateUrl: 'src/modules/shared/partials/bsEdit.tpl.html',
            scope: {propertyName: '@', entity: '=', callback: '=', role: '@', ownSubmit: '=', errorCallback: '='},
            link: function(scope, element) {
                if(!scope.role || UserService.userHasAuthority(scope.role)) {
                    var oldValue;

                    //on click show the edit field
                    element.bind('click', function() {
                        //save the old value so we can put it back if the user wants to abort
                        oldValue = scope.entity[scope.propertyName];
                        scope.edit = true;
                        scope.$apply();
                        //focus the cursor on the input element
                        element.find('input')[0].focus();
                    });

                    //on focusout submit
                    element.bind('focusout', function () {
                        //only submit if the value has changed so accidental clicks don't generate requests
                        if(oldValue !== scope.entity[scope.propertyName]) {
                            scope.submit();
                            scope.$apply();
                        } else {
                            scope.edit = false;
                            scope.$apply();
                        }
                    });

                    //on escape cancel
                    element.bind('keydown', function(event) {
                        if(event.which === 27) {
                            scope.entity[scope.propertyName] = oldValue;
                            scope.edit = false;
                            scope.$apply();
                        }
                    });
                }
            },
            controller: ['$scope', 'Restangular', function($scope, Restangular) {
                //Flag if the display form is showed
                $scope.edit = false;
                //Validation errors
                $scope.errors = [];

                $scope.submit = function() {
                    //We only want to submit the field we changed
                    function successCallback() {
                        $scope.edit = false;
                        if($scope.callback) {
                            $scope.callback();
                        }
                    }
                    function errorCallback(response) {
                        if($scope.errorCallback) {
                            $scope.errorCallback(response);
                        }
                        $scope.errors = response.data.errors;
                    }
                    var patchObject = {};
                    patchObject[$scope.propertyName] = $scope.entity[$scope.propertyName];
                    if($scope.ownSubmit) {
                        $scope.ownSubmit(patchObject).then(successCallback, errorCallback);
                    } else {
                        //TODO: how to find out the name of the resource? entity.resource is not correct (e.g. credential, not credentials)
                        Restangular.oneUrl('necessary', $scope.entity._links.self.href).patch(patchObject).then(successCallback, errorCallback);
                    }
                };
            }]
        };
    }];
});<commit_msg>* Save the oldvalue only the first time when clicking * Default to an empty error array for cases where no errors are returned. * Set errors to an empty error after a successful patch<commit_after>define([], function() {
    'use strict';
    /**
     * Directive for a click-to-edit textfield
     *
     * Attributes:
     * * entity: Reference to a restangularified entity
     * * property-name: the name of the property to display/edit
     * * callback: (optional) A callback that will be called after a successful PATCH call to the API.
     * * role: (optional) If a role is needed to edit the field.
     * * own-submit: (optional) submit function to be called, overwrites the intern function. Gets the patch object as the parameter. Must return a promise.
     * * errorCallback: (optional) A function that gets called with the HTTP response in case of an error.
     */
    return ['base.services.user', function(UserService) {
        return {
            restrict: 'E',
            templateUrl: 'src/modules/shared/partials/bsEdit.tpl.html',
            scope: {propertyName: '@', entity: '=', callback: '=', role: '@', ownSubmit: '=', errorCallback: '='},
            link: function(scope, element) {
                if(!scope.role || UserService.userHasAuthority(scope.role)) {
                    var oldValue;

                    //on click show the edit field
                    element.bind('click', function() {
                        if(!oldValue) {
                            oldValue = scope.entity[scope.propertyName];
                        }
                        //save the old value so we can put it back if the user wants to abort
                        scope.edit = true;
                        scope.$apply();
                        //focus the cursor on the input element
                        element.find('input')[0].focus();
                    });

                    //on focusout submit
                    element.bind('focusout', function () {
                        //only submit if the value has changed so accidental clicks don't generate requests
                        if(oldValue !== scope.entity[scope.propertyName]) {
                            scope.submit();
                            scope.$apply();
                        } else {
                            scope.edit = false;
                            scope.$apply();
                        }
                    });

                    //on escape cancel
                    element.bind('keydown', function(event) {
                        if(event.which === 27) {
                            scope.entity[scope.propertyName] = oldValue;
                            scope.edit = false;
                            scope.$apply();
                        }
                    });
                }
            },
            controller: ['$scope', 'Restangular', function($scope, Restangular) {
                //Flag if the display form is showed
                $scope.edit = false;
                //Validation errors
                $scope.errors = [];

                $scope.submit = function() {
                    //We only want to submit the field we changed
                    function successCallback() {
                        $scope.errors = [];
                        $scope.edit = false;
                        if($scope.callback) {
                            $scope.callback();
                        }
                    }
                    function errorCallback(response) {
                        if($scope.errorCallback) {
                            $scope.errorCallback(response);
                        }
                        //Since sometimes the errors array can be undefined (e.g. 409 conflict) default it to an empty array.
                        $scope.errors = response.data.errors || [];
                    }
                    var patchObject = {};
                    patchObject[$scope.propertyName] = $scope.entity[$scope.propertyName];
                    if($scope.ownSubmit) {
                        $scope.ownSubmit(patchObject).then(successCallback, errorCallback);
                    } else {
                        //TODO: how to find out the name of the resource? entity.resource is not correct (e.g. credential, not credentials)
                        Restangular.oneUrl('necessary', $scope.entity._links.self.href).patch(patchObject).then(successCallback, errorCallback);
                    }
                };
            }]
        };
    }];
});<|endoftext|>


<commit_before>/*
 * Copyright 2016-2017 Poggit
 *
 * 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.
 */

function addRowToListInfoTable(tableId, baseId) {
    var clone = $("#" + tableId).clone();
    clone.css("display", "table-row");
    clone.removeAttr("id");
    clone.appendTo($("#" + baseId));
    return clone;
}

function deleteRowFromListInfoTable(span) {
    $(span).parents("tr").remove();
}

function guessReadme(possibleDirs, repoId, repoName) {
    for(var i = 0; i < possibleDirs.length; i++) {
        var url = "repositories/" + repoId + "/contents" + possibleDirs[i];
        ghApi(url, {}, "GET", function(data) {
            for(var j = 0; j < data.length; j++) {
                if(data[j].type === "file" && (data[j].name === "README" || data[j].name === "README.md" || data[j].name === "README.txt")) {
                    var button = $("");
                    button.text("Import description from " + repoName + "/" + data[j].path);
                    button.click((function(datum) {
                        return function() {
                            $.get(datum.download_url, {}, function(data) {
                                $("#submit-pluginDescTextArea").val(data);
                                $("#submit-pluginDescTypeSelect").val("md");
                            })
                        };
                    })(data[j]));
                    button.appendTo($("#possibleDescriptionImports"));
                }
            }
        });
    }
}

function loadDefaultDesc() {
    $.ajax(getRelativeRootPath() + "r/" + pluginSubmitData.lastRelease.description + ".md", {
        dataType: "text",
        headers: {
            Accept: "text/plain"
        },
        success: function(data, status, xhr) {
            document.getElementById("submit-pluginDescTextArea").value = data;
            console.log(xhr.responseURL); // TODO fix this for #submit-pluginDescTypeSelect
        }
    });
}

function setupLicense(licenseSelect, viewLicense, customLicense, releaseLicenseType) {
    viewLicense.click(function() {
        var $this = $(this);
        if($this.hasClass("disabled")) {
            return;
        }
        var dialog = $("#previewLicenseDetailsDialog");
        var aname = dialog.find("#previewLicenseName");
        var pdesc = dialog.find("#previewLicenseDesc");
        var preBody = dialog.find("#previewLicenseBody");
        dialog.dialog("open");
        ghApi("licenses/" + licenseSelect.val(), {}, "GET", function(data) {
            aname.attr("href", data.html_url);
            aname.text(data.name);
            pdesc.text(data.description);
            preBody.text(data.body);
        }, undefined, "Accept: application/vnd.github.drax-preview+json");
    });
    licenseSelect.change(function() {
        customLicense.css("display", this.value == "custom" ? "block" : "none");
        var url = $(this).find(":selected").attr("data-url");
        if(typeof url == "string" && url.length > 0) {
            viewLicense.removeClass("disabled");
        } else {
            viewLicense.addClass("disabled");
        }
    });
    ghApi("licenses", {}, "GET", function(data) {
        data.sort(function(a, b) {
            if(a.featured && !b.featured) {
                return -1;
            }
            if(!a.featured && b.featured) {
                return 1;
            }
            return a.key.localeCompare(b.key);
        });
        for(var i = 0; i < data.length; i++) {
            var option = $("<option></option>");
            option.attr("value", data[i].key);
            option.attr("data-url", data[i].url);
            option.text(data[i].name);
            if(data[i].key == releaseLicenseType) option.attr("selected", true);
            option.appendTo(licenseSelect);
        }
    }, undefined, "Accept: application/vnd.github.drax-preview+json");
    if(releaseLicenseType == "custom") {
        licenseSelect.val("custom");
    }
}

function searchDep(tr) {
    var name = tr.find("#submit-depName").val();
    var releaseSelector = tr.find("#submit-depSelect");
    if(name.length < 3) {
        alert("Please type at least 3 letters to search for releases");
        return;
    }
    ajax("api", {
        data: JSON.stringify({
            request: "releases.get",
            name: name
        }),
        method: "POST",
        dataType: 'json',
        success: function(data) {
            if(data[0] != null) {
                releaseSelector.empty();
                $.each(data, function(key, value) {
                    if(!isNaN(key)) {
                        releaseSelector
                            .append($("<option></option>")
                                .attr("releaseId", value["releaseId"])
                                .attr("projectId", value["projectId"])
                                .attr("version", value["version"])
                                .attr("name", value["name"])
                                .text(value["name"] + " " + value["version"]));
                    }
                });
            } else {
                alert("no results found");
            }
        },
        error: function(xhr) {
            var json = JSON.parse(xhr.responseText);
            if(typeof json === "object") {
                alert("Error: " + json.message);
            }
        }
    });
}

function checkPluginName() {
    var pluginName = $("#submit-pluginName").val();
    var data = {pluginName: pluginName};
    ajax("ajax.relsubvalidate", {
        data: data,
        method: "POST",
        success: function(data) {
            // TODO better validation
            var after = $("#submit-afterPluginName");
            after.text(data.message);
            after.css("color", data.ok ? "green" : "red");
        }
    });
}

function submitPlugin($this, asDraft) {
    if(($("#submit-submitReal").attr("class")).includes("disabled")) return;
    if(($("#submit-submitDraft").attr("class")).includes("disabled")) return;
    $this.addClass("disabled");

    var submitData = {
        buildId: pluginSubmitData.buildInfo.buildId,
        name: $("#submit-pluginName").val(),
        shortDesc: $("#submit-shortDesc").val(),
        version: $("#submit-version").val(),
        desc: {
            text: $("#submit-pluginDescTextArea").val(),
            type: $("#submit-pluginDescTypeSelect").val()
        },
        changeLog: pluginSubmitData.lastRelease === null ? null : {
            text: $("#submit-pluginChangeLogTextArea").val(),
            type: $("#submit-pluginChangeLogTypeSelect").val()
        },
        license: {
            text: $("#submit-customLicense").val(),
            type: $("#submit-chooseLicense").val()
        },
        preRelease: $("#submit-isPreRelease").prop("checked"),
        categories: {
            major: $("#submit-majorCategory").val(),
            minor: $("#submit-minorCats").find(":checkbox.minorCat:checked").map(function() {
                return Number(this.value);
            }).get()
        },
        keywords: $("#submit-keywords").val().split(/[, ]+/),
        spoons: $(".submit-spoonEntry").slice(1).map(function() {
            return {
                spoon: "pmmp",
                api: [
                    $(this).find(".submit-spoonVersion-from").val(),
                    $(this).find(".submit-spoonVersion-to").val()
                ]
            };
        }).get(),
        deps: $(".submit-depEntry").slice(1).map(function() {
            $this = $(this);
            var selected = $('#submit-depSelect option:selected', this);
            var relId = selected.attr('releaseId');
            var name = selected.attr('name');
            var softness = $this.find(".submit-depSoftness").val();
            var version = selected.attr("version");
            return parseInt(relId) > 0 ? {
                name: name,
                releaseId: parseInt(relId),
                version: version,
                softness: softness
            } : null;
        }).get(),
        perms: $("#submit-perms").find(":checkbox.submit-permEntry:checked").map(function() {
            return Number(this.value);
        }).get(),
        reqr: $(".submit-reqrEntry").slice(1).map(function() {
            var $this = $(this);
            return {
                type: $this.find(".submit-reqrType").val(),
                details: $this.find(".submit-reqrSpec").val(),
                enhance: $this.find(".submit-reqrEnhc").val()
            };
        }).get(),
        iconName: pluginSubmitData.iconName,
        asDraft: asDraft
    };

    ajax("release.submit.ajax", {
        data: JSON.stringify(submitData),
        method: "POST",
        success: function(data) {
            $this.removeClass("disabled");
            window.location = getRelativeRootPath() + "p/" + data["release"]["name"] + (data["version"] ? ("/" + data["version"]) : "");
        },
        error: function(xhr) {
            var json = JSON.parse(xhr.responseText);
            setTimeout(function() {
                $this.removeClass("disabled");
            }, 3000);
            if(typeof json === "object") {
                alert("Error submitting plugin: " + json.message);
            }
        }
    });
}

$(document).ready(function() {
    var possible = [""];
    if(pluginSubmitData.projectDetails.path.length > 0) possible.push(pluginSubmitData.projectDetails.path);
    guessReadme(possible, pluginSubmitData.projectDetails.repoId, pluginSubmitData.repo);
    var licType = pluginSubmitData.lastRelease ? pluginSubmitData.lastRelease.license : null;
    setupLicense($("#submit-chooseLicense"), $("#viewLicenseDetails"), $("#submit-customLicense"), licType);
    if(pluginSubmitData.lastRelease == null || pluginSubmitData.spoonCount == 0) addRowToListInfoTable("submit-spoonEntry", "supportedSpoonsValue").find(".deleteSpoonRow").parent("td").remove();
    if(pluginSubmitData.lastRelease !== null) loadDefaultDesc();
    $("#previewLicenseDetailsDialog").dialog({
        autoOpen: false,
        width: 600,
        clickOut: true,
        responsive: true,
        height: window.innerHeight * 0.8,
        position: {my: "center top", at: "center top+100", of: window}
    });

    $("#submit-submitReal").click(function() {
        submitPlugin($(this), false);
    });
    $("#submit-submitDraft").click(function() {
        submitPlugin($(this), true);
    });
});
<commit_msg>Fixed release submit buttons after validation errors<commit_after>/*
 * Copyright 2016-2017 Poggit
 *
 * 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.
 */

function addRowToListInfoTable(tableId, baseId) {
    var clone = $("#" + tableId).clone();
    clone.css("display", "table-row");
    clone.removeAttr("id");
    clone.appendTo($("#" + baseId));
    return clone;
}

function deleteRowFromListInfoTable(span) {
    $(span).parents("tr").remove();
}

function guessReadme(possibleDirs, repoId, repoName) {
    for(var i = 0; i < possibleDirs.length; i++) {
        var url = "repositories/" + repoId + "/contents" + possibleDirs[i];
        ghApi(url, {}, "GET", function(data) {
            for(var j = 0; j < data.length; j++) {
                if(data[j].type === "file" && (data[j].name === "README" || data[j].name === "README.md" || data[j].name === "README.txt")) {
                    var button = $("");
                    button.text("Import description from " + repoName + "/" + data[j].path);
                    button.click((function(datum) {
                        return function() {
                            $.get(datum.download_url, {}, function(data) {
                                $("#submit-pluginDescTextArea").val(data);
                                $("#submit-pluginDescTypeSelect").val("md");
                            })
                        };
                    })(data[j]));
                    button.appendTo($("#possibleDescriptionImports"));
                }
            }
        });
    }
}

function loadDefaultDesc() {
    $.ajax(getRelativeRootPath() + "r/" + pluginSubmitData.lastRelease.description + ".md", {
        dataType: "text",
        headers: {
            Accept: "text/plain"
        },
        success: function(data, status, xhr) {
            document.getElementById("submit-pluginDescTextArea").value = data;
            console.log(xhr.responseURL); // TODO fix this for #submit-pluginDescTypeSelect
        }
    });
}

function setupLicense(licenseSelect, viewLicense, customLicense, releaseLicenseType) {
    viewLicense.click(function() {
        var $this = $(this);
        if($this.hasClass("disabled")) {
            return;
        }
        var dialog = $("#previewLicenseDetailsDialog");
        var aname = dialog.find("#previewLicenseName");
        var pdesc = dialog.find("#previewLicenseDesc");
        var preBody = dialog.find("#previewLicenseBody");
        dialog.dialog("open");
        ghApi("licenses/" + licenseSelect.val(), {}, "GET", function(data) {
            aname.attr("href", data.html_url);
            aname.text(data.name);
            pdesc.text(data.description);
            preBody.text(data.body);
        }, undefined, "Accept: application/vnd.github.drax-preview+json");
    });
    licenseSelect.change(function() {
        customLicense.css("display", this.value == "custom" ? "block" : "none");
        var url = $(this).find(":selected").attr("data-url");
        if(typeof url == "string" && url.length > 0) {
            viewLicense.removeClass("disabled");
        } else {
            viewLicense.addClass("disabled");
        }
    });
    ghApi("licenses", {}, "GET", function(data) {
        data.sort(function(a, b) {
            if(a.featured && !b.featured) {
                return -1;
            }
            if(!a.featured && b.featured) {
                return 1;
            }
            return a.key.localeCompare(b.key);
        });
        for(var i = 0; i < data.length; i++) {
            var option = $("<option></option>");
            option.attr("value", data[i].key);
            option.attr("data-url", data[i].url);
            option.text(data[i].name);
            if(data[i].key == releaseLicenseType) option.attr("selected", true);
            option.appendTo(licenseSelect);
        }
    }, undefined, "Accept: application/vnd.github.drax-preview+json");
    if(releaseLicenseType == "custom") {
        licenseSelect.val("custom");
    }
}

function searchDep(tr) {
    var name = tr.find("#submit-depName").val();
    var releaseSelector = tr.find("#submit-depSelect");
    if(name.length < 3) {
        alert("Please type at least 3 letters to search for releases");
        return;
    }
    ajax("api", {
        data: JSON.stringify({
            request: "releases.get",
            name: name
        }),
        method: "POST",
        dataType: 'json',
        success: function(data) {
            if(data[0] != null) {
                releaseSelector.empty();
                $.each(data, function(key, value) {
                    if(!isNaN(key)) {
                        releaseSelector
                            .append($("<option></option>")
                                .attr("releaseId", value["releaseId"])
                                .attr("projectId", value["projectId"])
                                .attr("version", value["version"])
                                .attr("name", value["name"])
                                .text(value["name"] + " " + value["version"]));
                    }
                });
            } else {
                alert("no results found");
            }
        },
        error: function(xhr) {
            var json = JSON.parse(xhr.responseText);
            if(typeof json === "object") {
                alert("Error: " + json.message);
            }
        }
    });
}

function checkPluginName() {
    var pluginName = $("#submit-pluginName").val();
    var data = {pluginName: pluginName};
    ajax("ajax.relsubvalidate", {
        data: data,
        method: "POST",
        success: function(data) {
            // TODO better validation
            var after = $("#submit-afterPluginName");
            after.text(data.message);
            after.css("color", data.ok ? "green" : "red");
        }
    });
}

function submitPlugin($this, asDraft) {
    if(($("#submit-submitReal").attr("class")).includes("disabled")) return;
    if(($("#submit-submitDraft").attr("class")).includes("disabled")) return;
    $this.addClass("disabled");

    var submitData = {
        buildId: pluginSubmitData.buildInfo.buildId,
        name: $("#submit-pluginName").val(),
        shortDesc: $("#submit-shortDesc").val(),
        version: $("#submit-version").val(),
        desc: {
            text: $("#submit-pluginDescTextArea").val(),
            type: $("#submit-pluginDescTypeSelect").val()
        },
        changeLog: pluginSubmitData.lastRelease === null ? null : {
            text: $("#submit-pluginChangeLogTextArea").val(),
            type: $("#submit-pluginChangeLogTypeSelect").val()
        },
        license: {
            text: $("#submit-customLicense").val(),
            type: $("#submit-chooseLicense").val()
        },
        preRelease: $("#submit-isPreRelease").prop("checked"),
        categories: {
            major: $("#submit-majorCategory").val(),
            minor: $("#submit-minorCats").find(":checkbox.minorCat:checked").map(function() {
                return Number(this.value);
            }).get()
        },
        keywords: $("#submit-keywords").val().split(/[, ]+/),
        spoons: $(".submit-spoonEntry").slice(1).map(function() {
            return {
                spoon: "pmmp",
                api: [
                    $(this).find(".submit-spoonVersion-from").val(),
                    $(this).find(".submit-spoonVersion-to").val()
                ]
            };
        }).get(),
        deps: $(".submit-depEntry").slice(1).map(function() {
            $this = $(this);
            var selected = $('#submit-depSelect option:selected', this);
            var relId = selected.attr('releaseId');
            var name = selected.attr('name');
            var softness = $this.find(".submit-depSoftness").val();
            var version = selected.attr("version");
            return parseInt(relId) > 0 ? {
                name: name,
                releaseId: parseInt(relId),
                version: version,
                softness: softness
            } : null;
        }).get(),
        perms: $("#submit-perms").find(":checkbox.submit-permEntry:checked").map(function() {
            return Number(this.value);
        }).get(),
        reqr: $(".submit-reqrEntry").slice(1).map(function() {
            var $this = $(this);
            return {
                type: $this.find(".submit-reqrType").val(),
                details: $this.find(".submit-reqrSpec").val(),
                enhance: $this.find(".submit-reqrEnhc").val()
            };
        }).get(),
        iconName: pluginSubmitData.iconName,
        asDraft: asDraft
    };

    ajax("release.submit.ajax", {
        data: JSON.stringify(submitData),
        method: "POST",
        success: function(data) {
            window.location = getRelativeRootPath() + "p/" + data["release"]["name"] + (data["version"] ? ("/" + data["version"]) : "");
        },
        error: function(xhr) {
            var json = JSON.parse(xhr.responseText);
            $("#submit-submitReal").removeClass("disabled");
            $("#submit-submitDraft").removeClass("disabled");
            if(typeof json === "object") {
                alert("Error submitting plugin: " + json.message);
            }
        }
    });
}

$(document).ready(function() {
    var possible = [""];
    if(pluginSubmitData.projectDetails.path.length > 0) possible.push(pluginSubmitData.projectDetails.path);
    guessReadme(possible, pluginSubmitData.projectDetails.repoId, pluginSubmitData.repo);
    var licType = pluginSubmitData.lastRelease ? pluginSubmitData.lastRelease.license : null;
    setupLicense($("#submit-chooseLicense"), $("#viewLicenseDetails"), $("#submit-customLicense"), licType);
    if(pluginSubmitData.lastRelease == null || pluginSubmitData.spoonCount == 0) addRowToListInfoTable("submit-spoonEntry", "supportedSpoonsValue").find(".deleteSpoonRow").parent("td").remove();
    if(pluginSubmitData.lastRelease !== null) loadDefaultDesc();
    $("#previewLicenseDetailsDialog").dialog({
        autoOpen: false,
        width: 600,
        clickOut: true,
        responsive: true,
        height: window.innerHeight * 0.8,
        position: {my: "center top", at: "center top+100", of: window}
    });

    $("#submit-submitReal").click(function() {
        submitPlugin($(this), false);
    });
    $("#submit-submitDraft").click(function() {
        submitPlugin($(this), true);
    });
});
<|endoftext|>


<commit_before>// src: http://shouldiprefix.com
import ua from 'vigour-ua/navigator'

const prefix = {}

if (ua.prefix === 'moz') {
  prefix.appearance = 'MozAppearance'
} else if (
  ua.prefix === 'webkit' ||
  ua.browser === 'ie' ||
  ua.browser === 'edge'
) {
  prefix.appearance = 'WebkitAppearance'
  prefix.clipPath = 'WebkitClipPath'
}

if (ua.browser === 'chrome' || ua.browser === 'safari') {
  prefix.filter = 'WebkitFilter'
}

if (ua.platform === 'ios' || ua.browser === 'safari') {
  prefix.flex = 'WebkitFlex'
} else if (ua.browser === 'ie') {
  prefix.flex = 'msFlex'
  if (ua.version === 10) {
    prefix.order = 'msFlexOrder'
  }
}

if (
  (ua.browser === 'chrome' && ua.version < 36) ||
  (ua.browser === 'chrome' && ua.version < 36) ||
  (ua.browser === 'safari' && ua.version > 5.1) ||
  (ua.browser === 'safari' && ua.device === 'tv') || // webOS
  (ua.platform === 'ios' && ua.version < 9.2) ||
  (ua.platform === 'android' && ua.version <= 4.5)// 4.4.4
) {
  prefix.transformOrigin = 'WebkitTransformOrigin'
  prefix.transform = 'WebkitTransform'
  prefix.transition = 'WebkitTransition'
}

if (ua.browser === 'firefox' && ua.version < 48) {
  prefix.transformOrigin = 'MozTransformOrigin'
  prefix.transform = 'MozTransform'
  prefix.transition = 'MozTransition'
}

export default prefix
<commit_msg>removed firefox prefix for style.transform<commit_after>// src: http://shouldiprefix.com
import ua from 'vigour-ua/navigator'

const prefix = {}

if (ua.prefix === 'moz') {
  prefix.appearance = 'MozAppearance'
} else if (
  ua.prefix === 'webkit' ||
  ua.browser === 'ie' ||
  ua.browser === 'edge'
) {
  prefix.appearance = 'WebkitAppearance'
  prefix.clipPath = 'WebkitClipPath'
}

if (ua.browser === 'chrome' || ua.browser === 'safari') {
  prefix.filter = 'WebkitFilter'
}

if (ua.platform === 'ios' || ua.browser === 'safari') {
  prefix.flex = 'WebkitFlex'
} else if (ua.browser === 'ie') {
  prefix.flex = 'msFlex'
  if (ua.version === 10) {
    prefix.order = 'msFlexOrder'
  }
}

if (
  (ua.browser === 'chrome' && ua.version < 36) ||
  (ua.browser === 'chrome' && ua.version < 36) ||
  (ua.browser === 'safari' && ua.version > 5.1) ||
  (ua.browser === 'safari' && ua.device === 'tv') || // webOS
  (ua.platform === 'ios' && ua.version < 9.2) ||
  (ua.platform === 'android' && ua.version <= 4.5)// 4.4.4
) {
  prefix.transformOrigin = 'WebkitTransformOrigin'
  prefix.transform = 'WebkitTransform'
  prefix.transition = 'WebkitTransition'
}

export default prefix
<|endoftext|>


<commit_before>/*!
 * OS.js - JavaScript Cloud/Web Desktop Platform
 *
 * Copyright (c) 2011-2017, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */

/*eslint valid-jsdoc: "off"*/
(function(WindowManager, Window, GUI, Utils, API, VFS) {
  'use strict';

  function createCreateDialog(title, dir, cb) {
    API.createDialog('Input', {
      value: title,
      message: OSjs.Applications.CoreWM._('Create in {0}', dir)
    }, function(ev, button, result) {
      if ( result ) {
        cb(new VFS.File(Utils.pathJoin(dir, result)));
      }
    });
  }

  /////////////////////////////////////////////////////////////////////////////
  // SHORTCUT DIALOG
  /////////////////////////////////////////////////////////////////////////////

  function IconViewShortcutDialog(item, scheme, closeCallback) {
    Window.apply(this, ['IconViewShortcutDialog', {
      title: 'Edit Launcher',
      icon: 'status/appointment-soon.png',
      width: 400,
      height: 220,
      allow_maximize: false,
      allow_resize: false,
      allow_minimize: false
    }]);

    this.scheme = scheme;
    this.values = {
      path: item.path,
      filename: item.filename,
      args: item.args || {}
    };
    this.cb = closeCallback || function() {};
  }

  IconViewShortcutDialog.prototype = Object.create(Window.prototype);
  IconViewShortcutDialog.constructor = Window;

  IconViewShortcutDialog.prototype.init = function(wm, app) {
    var self = this;
    var root = Window.prototype.init.apply(this, arguments);
    this.scheme.render(this, this._name);

    this.scheme.find(this, 'InputShortcutLaunch').set('value', this.values.path);
    this.scheme.find(this, 'InputShortcutLabel').set('value', this.values.filename);
    this.scheme.find(this, 'InputTooltipFormatString').set('value', JSON.stringify(this.values.args || {}));

    this.scheme.find(this, 'ButtonApply').on('click', function() {
      self.applySettings();
      self._close('ok');
    });

    this.scheme.find(this, 'ButtonCancel').on('click', function() {
      self._close();
    });

    return root;
  };

  IconViewShortcutDialog.prototype.applySettings = function() {
    this.values.path = this.scheme.find(this, 'InputShortcutLaunch').get('value');
    this.values.filename = this.scheme.find(this, 'InputShortcutLabel').get('value');
    this.values.args = JSON.parse(this.scheme.find(this, 'InputTooltipFormatString').get('value') || {});
  };

  IconViewShortcutDialog.prototype._close = function(button) {
    this.cb(button, this.values);
    return Window.prototype._close.apply(this, arguments);
  };

  IconViewShortcutDialog.prototype._destroy = function() {
    this.scheme = null;
    return Window.prototype._destroy.apply(this, arguments);
  };

  /////////////////////////////////////////////////////////////////////////////
  // ICON VIEW
  /////////////////////////////////////////////////////////////////////////////

  function DesktopIconView(wm) {
    var self = this;

    this.dialog = null;
    this.$iconview = null;
    this.$element = document.createElement('gui-icon-view');
    this.$element.setAttribute('data-multiple', 'false');
    //this.$element.setAttribute('no-selection', 'true');
    this.$element.id = 'CoreWMDesktopIconView';
    this.shortcutCache = [];
    this.refreshTimeout = null;

    GUI.Elements['gui-icon-view'].build(this.$element);

    GUI.Helpers.createDroppable(this.$element, {
      onOver: function(ev, el, args) {
        wm.onDropOver(ev, el, args);
      },

      onLeave : function() {
        wm.onDropLeave();
      },

      onDrop : function() {
        wm.onDrop();
      },

      onItemDropped: function(ev, el, item, args) {
        wm.onDropItem(ev, el, item, args);
      },

      onFilesDropped: function(ev, el, files, args) {
        wm.onDropFile(ev, el, files, args);
      }
    });

    this.$iconview = new GUI.ElementDataView(this.$element);
    this.$iconview.on('activate', function(ev) {
      if ( ev && ev.detail ) {
        ev.detail.entries.forEach(function(entry) {
          var item = entry.data;
          var file = new VFS.File(item);
          API.open(file, item.args);
        });
      }
    }).on('contextmenu', function(ev) {
      if ( ev && ev.detail && ev.detail.entries ) {
        self.createContextMenu(ev.detail.entries[0], ev);
      }
    });

    this._refresh();
    this.resize(wm);
  }

  DesktopIconView.prototype.destroy = function() {
    Utils.$remove(this.$element);
    this.refreshTimeout = clearTimeout(this.refreshTimeout);
    this.$element = null;
    this.$iconview = null;

    if ( this.dialog ) {
      this.dialog.destroy();
    }
    this.dialog = null;

    this.shortcutCache = [];
  };

  DesktopIconView.prototype.blur = function() {
    var cel = new GUI.ElementDataView(this.$element);
    cel.set('value', null);
  };

  DesktopIconView.prototype.getRoot = function() {
    return this.$element;
  };

  DesktopIconView.prototype.resize = function(wm) {
    var el = this.getRoot();
    var s  = wm.getWindowSpace();

    if ( el ) {
      el.style.top    = (s.top) + 'px';
      el.style.left   = (s.left) + 'px';
      el.style.width  = (s.width) + 'px';
      el.style.height = (s.height) + 'px';
    }
  };

  DesktopIconView.prototype._refresh = function(wm) {
    var self = this;

    var desktopPath = OSjs.Core.getWindowManager().getSetting('desktopPath');
    var shortcutPath = Utils.pathJoin(desktopPath, '.shortcuts.json');

    this.shortcutCache = [];

    this.refreshTimeout = clearTimeout(this.refreshTimeout);
    this.refreshTimeout = setTimeout(function() {
      VFS.scandir(desktopPath, function(error, result) {
        if ( self.$iconview && !error ) {
          self.$iconview.clear().add(result.map(function(iter) {
            if ( iter.type === 'application' || iter.shortcut === true ) {
              var niter = new VFS.File(iter);
              niter.shortcut = true;

              var idx = self.shortcutCache.push(niter) - 1;

              var file = new VFS.File(iter);
              file.__index = idx;

              return {
                _type: iter.type,
                icon: API.getFileIcon(iter, '32x32'),
                label: iter.filename,
                value: file,
                args: iter.args || {}
              };
            }

            return {
              _type: 'vfs',
              icon: API.getFileIcon(iter, '32x32'),
              label: iter.filename,
              value: iter
            };

          }).filter(function(iter) {
            return iter.value.path !== shortcutPath;
          }));
        }
      });
    }, 150);
  };

  DesktopIconView.prototype._save = function(refresh) {
    var desktopPath = OSjs.Core.getWindowManager().getSetting('desktopPath');
    var path = Utils.pathJoin(desktopPath, '.shortcuts.json');
    var cache = this.shortcutCache;

    VFS.mkdir(Utils.dirname(path), function(err) {
      VFS.write(path, JSON.stringify(cache, null, 4), function(e, r) {
        if ( refresh ) { // Normally caught by VFS message in main.js
          //self._refresh();
        }
      });
    });
  };

  DesktopIconView.prototype.updateShortcut = function(data, values) {
    var o = this.shortcutCache[data.__index];
    if ( o.path === data.path ) {
      Object.keys(values).forEach(function(k) {
        o[k] = values[k];
      });

      this._save(true);
    }
  };

  DesktopIconView.prototype.getShortcutByPath = function(path) {
    var found = null;
    var index = -1;

    this.shortcutCache.forEach(function(i, idx) {
      if ( !found ) {
        if ( i.type !== 'application' && i.path === path ) {
          found = i;
          index = idx;
        }
      }
    });

    return {item: found, index: index};
  };

  DesktopIconView.prototype.addShortcut = function(data, wm, save) {
    (['icon']).forEach(function(k) {
      if ( data[k] ) {
        delete data[k];
      }
    });

    if ( data.type === 'application' ) {
      data.args = data.args || {};
    }

    data.shortcut = true;
    this.shortcutCache.push(data);
    this._save(true);
  };

  DesktopIconView.prototype.removeShortcut = function(data) {
    var o = this.shortcutCache[data.__index];
    if ( o && o.path === data.path ) {
      this.shortcutCache.splice(data.__index, 1);
      this._save(true);
    }
  };

  DesktopIconView.prototype._getContextMenu = function(item) {
    var self = this;
    var mm = OSjs.Core.getMountManager();
    var desktopPath = OSjs.Core.getWindowManager().getSetting('desktopPath');
    var menu = [
      {
        title: API._('LBL_UPLOAD'),
        onClick: function() {
          API.createDialog('FileUpload', {
            dest: desktopPath
          }, function() {
            self._refresh();
          });
        }
      },
      {
        title: API._('LBL_CREATE'),
        menu: [{
          title: API._('LBL_FILE'),
          onClick: function() {
            createCreateDialog('New file', desktopPath, function(f) {
              VFS.write(f, '', function(err) {
                if ( err ) {
                  API.error('CoreWM', API._('ERR_VFSMODULE_MKFILE'), err);
                }
              });
            });
          }
        }, {
          title: API._('LBL_DIRECTORY'),
          onClick: function() {
            createCreateDialog('New directory', desktopPath, function(f) {
              VFS.mkdir(f, function(err) {
                if ( err ) {
                  API.error('CoreWM', API._('ERR_VFSMODULE_MKDIR'), err);
                }
              });
            });
          }
        }]
      }
    ];

    if ( item ) {
      var file = item.data;

      if ( file.type === 'application' ) {
        menu.push({
          title: OSjs.Applications.CoreWM._('Edit shortcut'),
          onClick: function() {
            self.openShortcutEdit(file);
          }
        });
      }

      if ( mm.getRootFromPath(file.path) !== desktopPath  ) {
        menu.push({
          title: OSjs.Applications.CoreWM._('Remove shortcut'),
          onClick: function() {
            self.removeShortcut(file);
          }
        });
      } else {
        menu.push({
          title: API._('LBL_DELETE'),
          onClick: function() {
            VFS.unlink(file, function() {
              //self._refresh(); // Caught by VFS message in main.js
            });
          }
        });
      }
    }

    return menu;
  };

  DesktopIconView.prototype.createContextMenu = function(item, ev) {
    var wm = OSjs.Core.getWindowManager();
    var menu = wm._getContextMenu(item);
    API.createMenu(menu, ev);
  };

  DesktopIconView.prototype.openShortcutEdit = function(item) {
    if ( this.dialog ) {
      this.dialog._close();
    }

    var self = this;
    var wm = OSjs.Core.getWindowManager();

    this.dialog = new IconViewShortcutDialog(item, wm._scheme, function(button, values) {
      if ( button === 'ok' ) {
        self.updateShortcut(item, values);
      }
      self.dialog = null;
    });

    wm.addWindow(this.dialog, true);
  };

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  OSjs.Applications                          = OSjs.Applications || {};
  OSjs.Applications.CoreWM                   = OSjs.Applications.CoreWM || {};
  OSjs.Applications.CoreWM.DesktopIconView   = DesktopIconView;

})(OSjs.Core.WindowManager, OSjs.Core.Window, OSjs.GUI, OSjs.Utils, OSjs.API, OSjs.VFS);
<commit_msg>CoreWM: Refresh iconview when shortcut is added by dnd<commit_after>/*!
 * OS.js - JavaScript Cloud/Web Desktop Platform
 *
 * Copyright (c) 2011-2017, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */

/*eslint valid-jsdoc: "off"*/
(function(WindowManager, Window, GUI, Utils, API, VFS) {
  'use strict';

  function createCreateDialog(title, dir, cb) {
    API.createDialog('Input', {
      value: title,
      message: OSjs.Applications.CoreWM._('Create in {0}', dir)
    }, function(ev, button, result) {
      if ( result ) {
        cb(new VFS.File(Utils.pathJoin(dir, result)));
      }
    });
  }

  /////////////////////////////////////////////////////////////////////////////
  // SHORTCUT DIALOG
  /////////////////////////////////////////////////////////////////////////////

  function IconViewShortcutDialog(item, scheme, closeCallback) {
    Window.apply(this, ['IconViewShortcutDialog', {
      title: 'Edit Launcher',
      icon: 'status/appointment-soon.png',
      width: 400,
      height: 220,
      allow_maximize: false,
      allow_resize: false,
      allow_minimize: false
    }]);

    this.scheme = scheme;
    this.values = {
      path: item.path,
      filename: item.filename,
      args: item.args || {}
    };
    this.cb = closeCallback || function() {};
  }

  IconViewShortcutDialog.prototype = Object.create(Window.prototype);
  IconViewShortcutDialog.constructor = Window;

  IconViewShortcutDialog.prototype.init = function(wm, app) {
    var self = this;
    var root = Window.prototype.init.apply(this, arguments);
    this.scheme.render(this, this._name);

    this.scheme.find(this, 'InputShortcutLaunch').set('value', this.values.path);
    this.scheme.find(this, 'InputShortcutLabel').set('value', this.values.filename);
    this.scheme.find(this, 'InputTooltipFormatString').set('value', JSON.stringify(this.values.args || {}));

    this.scheme.find(this, 'ButtonApply').on('click', function() {
      self.applySettings();
      self._close('ok');
    });

    this.scheme.find(this, 'ButtonCancel').on('click', function() {
      self._close();
    });

    return root;
  };

  IconViewShortcutDialog.prototype.applySettings = function() {
    this.values.path = this.scheme.find(this, 'InputShortcutLaunch').get('value');
    this.values.filename = this.scheme.find(this, 'InputShortcutLabel').get('value');
    this.values.args = JSON.parse(this.scheme.find(this, 'InputTooltipFormatString').get('value') || {});
  };

  IconViewShortcutDialog.prototype._close = function(button) {
    this.cb(button, this.values);
    return Window.prototype._close.apply(this, arguments);
  };

  IconViewShortcutDialog.prototype._destroy = function() {
    this.scheme = null;
    return Window.prototype._destroy.apply(this, arguments);
  };

  /////////////////////////////////////////////////////////////////////////////
  // ICON VIEW
  /////////////////////////////////////////////////////////////////////////////

  function DesktopIconView(wm) {
    var self = this;

    this.dialog = null;
    this.$iconview = null;
    this.$element = document.createElement('gui-icon-view');
    this.$element.setAttribute('data-multiple', 'false');
    //this.$element.setAttribute('no-selection', 'true');
    this.$element.id = 'CoreWMDesktopIconView';
    this.shortcutCache = [];
    this.refreshTimeout = null;

    GUI.Elements['gui-icon-view'].build(this.$element);

    GUI.Helpers.createDroppable(this.$element, {
      onOver: function(ev, el, args) {
        wm.onDropOver(ev, el, args);
      },

      onLeave : function() {
        wm.onDropLeave();
      },

      onDrop : function() {
        wm.onDrop();
      },

      onItemDropped: function(ev, el, item, args) {
        wm.onDropItem(ev, el, item, args);
      },

      onFilesDropped: function(ev, el, files, args) {
        wm.onDropFile(ev, el, files, args);
      }
    });

    this.$iconview = new GUI.ElementDataView(this.$element);
    this.$iconview.on('activate', function(ev) {
      if ( ev && ev.detail ) {
        ev.detail.entries.forEach(function(entry) {
          var item = entry.data;
          var file = new VFS.File(item);
          API.open(file, item.args);
        });
      }
    }).on('contextmenu', function(ev) {
      if ( ev && ev.detail && ev.detail.entries ) {
        self.createContextMenu(ev.detail.entries[0], ev);
      }
    });

    this._refresh();
    this.resize(wm);
  }

  DesktopIconView.prototype.destroy = function() {
    Utils.$remove(this.$element);
    this.refreshTimeout = clearTimeout(this.refreshTimeout);
    this.$element = null;
    this.$iconview = null;

    if ( this.dialog ) {
      this.dialog.destroy();
    }
    this.dialog = null;

    this.shortcutCache = [];
  };

  DesktopIconView.prototype.blur = function() {
    var cel = new GUI.ElementDataView(this.$element);
    cel.set('value', null);
  };

  DesktopIconView.prototype.getRoot = function() {
    return this.$element;
  };

  DesktopIconView.prototype.resize = function(wm) {
    var el = this.getRoot();
    var s  = wm.getWindowSpace();

    if ( el ) {
      el.style.top    = (s.top) + 'px';
      el.style.left   = (s.left) + 'px';
      el.style.width  = (s.width) + 'px';
      el.style.height = (s.height) + 'px';
    }
  };

  DesktopIconView.prototype._refresh = function(wm) {
    var self = this;

    var desktopPath = OSjs.Core.getWindowManager().getSetting('desktopPath');
    var shortcutPath = Utils.pathJoin(desktopPath, '.shortcuts.json');

    this.shortcutCache = [];

    this.refreshTimeout = clearTimeout(this.refreshTimeout);
    this.refreshTimeout = setTimeout(function() {
      VFS.scandir(desktopPath, function(error, result) {
        if ( self.$iconview && !error ) {
          self.$iconview.clear().add(result.map(function(iter) {
            if ( iter.type === 'application' || iter.shortcut === true ) {
              var niter = new VFS.File(iter);
              niter.shortcut = true;

              var idx = self.shortcutCache.push(niter) - 1;

              var file = new VFS.File(iter);
              file.__index = idx;

              return {
                _type: iter.type,
                icon: API.getFileIcon(iter, '32x32'),
                label: iter.filename,
                value: file,
                args: iter.args || {}
              };
            }

            return {
              _type: 'vfs',
              icon: API.getFileIcon(iter, '32x32'),
              label: iter.filename,
              value: iter
            };

          }).filter(function(iter) {
            return iter.value.path !== shortcutPath;
          }));
        }
      });
    }, 150);
  };

  DesktopIconView.prototype._save = function(refresh) {
    var desktopPath = OSjs.Core.getWindowManager().getSetting('desktopPath');
    var path = Utils.pathJoin(desktopPath, '.shortcuts.json');
    var cache = this.shortcutCache;
    var self = this;

    VFS.mkdir(Utils.dirname(path), function(err) {
      VFS.write(path, JSON.stringify(cache, null, 4), function(e, r) {
        if ( refresh ) { // Normally caught by VFS message in main.js
          self._refresh();
        }
      });
    });
  };

  DesktopIconView.prototype.updateShortcut = function(data, values) {
    var o = this.shortcutCache[data.__index];
    if ( o.path === data.path ) {
      Object.keys(values).forEach(function(k) {
        o[k] = values[k];
      });

      this._save(true);
    }
  };

  DesktopIconView.prototype.getShortcutByPath = function(path) {
    var found = null;
    var index = -1;

    this.shortcutCache.forEach(function(i, idx) {
      if ( !found ) {
        if ( i.type !== 'application' && i.path === path ) {
          found = i;
          index = idx;
        }
      }
    });

    return {item: found, index: index};
  };

  DesktopIconView.prototype.addShortcut = function(data, wm, save) {
    (['icon']).forEach(function(k) {
      if ( data[k] ) {
        delete data[k];
      }
    });

    if ( data.type === 'application' ) {
      data.args = data.args || {};
    }

    data.shortcut = true;
    this.shortcutCache.push(data);
    this._save(true);
  };

  DesktopIconView.prototype.removeShortcut = function(data) {
    var o = this.shortcutCache[data.__index];
    if ( o && o.path === data.path ) {
      this.shortcutCache.splice(data.__index, 1);
      this._save(true);
    }
  };

  DesktopIconView.prototype._getContextMenu = function(item) {
    var self = this;
    var mm = OSjs.Core.getMountManager();
    var desktopPath = OSjs.Core.getWindowManager().getSetting('desktopPath');
    var menu = [
      {
        title: API._('LBL_UPLOAD'),
        onClick: function() {
          API.createDialog('FileUpload', {
            dest: desktopPath
          }, function() {
            self._refresh();
          });
        }
      },
      {
        title: API._('LBL_CREATE'),
        menu: [{
          title: API._('LBL_FILE'),
          onClick: function() {
            createCreateDialog('New file', desktopPath, function(f) {
              VFS.write(f, '', function(err) {
                if ( err ) {
                  API.error('CoreWM', API._('ERR_VFSMODULE_MKFILE'), err);
                }
              });
            });
          }
        }, {
          title: API._('LBL_DIRECTORY'),
          onClick: function() {
            createCreateDialog('New directory', desktopPath, function(f) {
              VFS.mkdir(f, function(err) {
                if ( err ) {
                  API.error('CoreWM', API._('ERR_VFSMODULE_MKDIR'), err);
                }
              });
            });
          }
        }]
      }
    ];

    if ( item ) {
      var file = item.data;

      if ( file.type === 'application' ) {
        menu.push({
          title: OSjs.Applications.CoreWM._('Edit shortcut'),
          onClick: function() {
            self.openShortcutEdit(file);
          }
        });
      }

      if ( mm.getRootFromPath(file.path) !== desktopPath  ) {
        menu.push({
          title: OSjs.Applications.CoreWM._('Remove shortcut'),
          onClick: function() {
            self.removeShortcut(file);
          }
        });
      } else {
        menu.push({
          title: API._('LBL_DELETE'),
          onClick: function() {
            VFS.unlink(file, function() {
              //self._refresh(); // Caught by VFS message in main.js
            });
          }
        });
      }
    }

    return menu;
  };

  DesktopIconView.prototype.createContextMenu = function(item, ev) {
    var wm = OSjs.Core.getWindowManager();
    var menu = wm._getContextMenu(item);
    API.createMenu(menu, ev);
  };

  DesktopIconView.prototype.openShortcutEdit = function(item) {
    if ( this.dialog ) {
      this.dialog._close();
    }

    var self = this;
    var wm = OSjs.Core.getWindowManager();

    this.dialog = new IconViewShortcutDialog(item, wm._scheme, function(button, values) {
      if ( button === 'ok' ) {
        self.updateShortcut(item, values);
      }
      self.dialog = null;
    });

    wm.addWindow(this.dialog, true);
  };

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  OSjs.Applications                          = OSjs.Applications || {};
  OSjs.Applications.CoreWM                   = OSjs.Applications.CoreWM || {};
  OSjs.Applications.CoreWM.DesktopIconView   = DesktopIconView;

})(OSjs.Core.WindowManager, OSjs.Core.Window, OSjs.GUI, OSjs.Utils, OSjs.API, OSjs.VFS);
<|endoftext|>


<commit_before>'use strict';

Room.prototype.creepMem = function(role, targetId, targetRoom, level, base) {
  return {
    role: role,
    routing: {
      targetRoom: targetRoom || this.name,
      targetId: targetId,
    },
    level: level,
    base: base,
  };
};

/**
 * get priority from config for a creep.
 *
 * @param  {Object} object the creep queue object
 * @return {Number}        the priority for creep
 */
Room.prototype.getPriority = function(object) {
  let priority = config.priorityQueue;
  let target = object.routing && object.routing.targetRoom;
  if (target === this.name) {
    return priority.sameRoom[object.role] || 4;
  } else if (target) {
    return priority.otherRoom[object.role] || 20 + Game.map.getRoomLinearDistance(this.name, target);
  } else {
    return 19;
  }
};

Room.prototype.spawnCheckForCreate = function() {
  if (this.memory.queue.length === 0) {
    return false;
  }
  if (!this.exectueEveryTicks(10)) {
    if (this.energyAvailable < this.energyCapacityAvailable - 300) {
      return false;
    }
  }
  this.memory.queue = _.sortBy(this.memory.queue, c => this.getPriority(c));

  let creep = this.memory.queue[0];
  if (this.spawnCreateCreep(creep)) {
    this.memory.queue.shift();
    return true;
  }
  if (creep.ttl === 0) {
    this.log('TTL reached, skipping: ' + JSON.stringify(creep));
    this.memory.queue.shift();
    return false;
  }
  creep.ttl = creep.ttl || config.creep.queueTtl;
  if (this.getSpawnableSpawns().length === 0) {
    creep.ttl--;
  }
  return false;
};

Room.prototype.inQueue = function(creepMemory) {
  this.memory.queue = this.memory.queue || [];
  for (var item of this.memory.queue) {
    if (creepMemory.role !== item.role) {
      continue;
    }
    if (!item.routing) {
      this.log('No item routing: ' + JSON.stringify(item));
      if (item.role !== 'scout') {
        this.log('inQueue: no routing: ' + JSON.stringify(item) + ' ' + JSON.stringify(creepMemory));
      }
      continue;
    }
    if (creepMemory.routing.targetId === item.routing.targetId && creepMemory.routing.targetRoom === item.routing.targetRoom) {
      return true;
    }
  }
  return false;
};

Room.prototype.inRoom = function(creepMemory, amount = 1) {
  if (amount === 0) {
    return false;
  }
  let creepsSpawning = [];
  for (let spawn of this.find(FIND_MY_SPAWNS)) {
    if (spawn.spawning) {
      creepsSpawning.push(Game.creeps[spawn.spawning.name]);
    }
  }
  var creeps = this.find(FIND_MY_CREEPS).concat(creepsSpawning);
  this.memory.roles = this.memory.roles || {};

  let j = 0;
  for (let creep of creeps) {
    let iMem = Memory.creeps[creep.name];

    if (!iMem) {
      this.log(`${creep} ${creep.name} ${Array.isArray(creep)} ${creep instanceof Array} ${typeof creep} ${Object.keys(creep)}`);
      this.log(`inRoom no iMem ${JSON.stringify(creep)}`);
      this.log(`inRoom no iMem ${creep.name}`);
      continue;
    }
    if (creepMemory.role === iMem.role && (!iMem.routing ||
        (creepMemory.routing.targetRoom === iMem.routing.targetRoom &&
          creepMemory.routing.targetId === iMem.routing.targetId))) {
      j++;
    }
    if (j >= amount) {
      this.memory.roles[creepMemory.role] = true;
      /**
      if (config.debug.queue) {
        this.log('Already enough ' + creepMemory.role);
      }
      **/
      return true;
    }
  }
  return false;
};

/**
 * First function call for ask a creep spawn. Add it in queue after check if spawn is allow.
 *
 * @param  {string} role       the role of the creeps to spawn.
 * @param  {number} amount     the amount of creeps asked for (1).
 * @param  {string} targetId   the id of targeted object by creeps (null).
 * @param  {string} targetRoom the targeted room name (base)
 * @param  {number} level      the level of creeps. required by some functions.
 * @param  {string} base       the room which will spawn creep
 * @param  {object} additionalMemory
 * @return {boolean}           if the spawn is not allow, it will return false.
 */
Room.prototype.checkRoleToSpawn = function(role, amount, targetId, targetRoom, level, base, additionalMemory = {}) {
  if (typeof targetId == 'object' && targetId.id) {
    targetId = targetId.id;
  }
  const creepMemory = this.creepMem(role, targetId, targetRoom, level, base);
  Object.assign(creepMemory, additionalMemory);
  if (this.inQueue(creepMemory) || this.inRoom(creepMemory, amount)) { return false; }

  if (config.debug.queue) {
    this.log('Add ' + creepMemory.role + ' to queue. ' + JSON.stringify(creepMemory));
  }
  return this.memory.queue.push(creepMemory);
};

/**
 * Room.prototype.getPartsStringDatas used for parse parts as string and return
 * parts as array, cost, if spawn is allow and length.
 *
 * @param {String} parts String of body parts. e.g. 'MMWC'
 * @param {Number} energyAvailable energy allow for spawn.
 * @return {Object}       The parts datas :
 *                            .fail = true if not enouth energy
 *                            .cost = cost of parts
 *                            .parts = parts as array
 *                            .len = the amount of parts.
 */

Room.prototype.getPartsStringDatas = function(parts, energyAvailable) {
  if (!_.isString(parts) || parts === '') {
    return {
      null: true
    };
  }
  let ret = {};
  Memory.layoutsCost = Memory.layoutsCost || {};
  ret.cost = Memory.layoutsCost[parts] || 0;
  ret.parts = global.utils.stringToParts(parts);
  ret.len = ret.parts.length;
  if (config.debug.spawn) {
    this.log(`getPartsStringDatas ret: ${JSON.stringify(ret)} parts: ${JSON.stringify(parts)}`);
  }
  if (ret.cost) {
    ret.fail = ret.cost > energyAvailable;
    return ret;
  }
  _.each(ret.parts,
    (p) => {
      ret.cost += BODYPART_COST[p];
      ret.fail = ret.cost > energyAvailable;
    }
  );
  Memory.layoutsCost[parts] = ret.cost;
  return ret;
};

/**
 * Room.prototype.getSettings use for return creep spawn settings
 * adapted to room configuration
 *
 * @param {Collection} creep queue's creep spawn basic datas
 */
Room.prototype.getSettings = function(creep) {
  let role = creep.role;
  let updateSettings = roles[role].updateSettings && roles[role].updateSettings(this, creep);
  let settings = _.merge(roles[role].settings, updateSettings);
  if (!settings) {
    this.log('try to spawn ', role, ' but settings are not done. Abort spawn');
    return;
  }
  let param = settings.param;
  return _.mapValues(settings, (setting, settingName) => {
    // Not sure when this happens
    if (!setting || setting === null) {
      return setting;
    }
    if (!param) {
      return setting;
    }
    for (let parameter of param) {
      if (_.isString(setting) || _.isNumber(setting) || _.isArray(setting) || _.isBoolean(setting)) {
        break;
      }
      let valueForI = _.get(this, parameter, 1);
      let foundKey = 0;
      if (!setting) {
        break;
      }
      for (let key of Object.keys(setting)) {
        if (valueForI < key && foundKey !== 0) {
          break;
        }
        foundKey = key;
      }
      setting = setting[foundKey];
    }
    return setting;
  });
};

/**
 * Transform a string using an array char ammount. e.g. ('WMC', [1,2,3]) ==> 'WMMCCC'
 *
 * @param  {String} input  the input parts as string.
 * @param  {Array} amount the amount of each char needed.
 * @return {String}        the new parts string
 */
Room.prototype.applyAmount = function(input, amount) {
  if (!input) {
    return '';
  }
  if (!amount) {
    return input;
  }
  let cost = 0;
  let parts = [];
  let output = '';
  _.forEach(amount, function(element, index) {
    output += _.repeat(input.charAt(index), element);
  });
  if (config.debug.spawn) {
    this.log(`applyAmount input: ${JSON.stringify(input)} amount: ${JSON.stringify(amount)} output: ${JSON.stringify(output)}`);
  }
  return output;
};

/**
 * Sort body parts with the same order used in layout. Parts not in layout are last ones.
 *
 * @param  {array} parts  the parts array to sort.
 * @param  {array} layout the base layout.
 * @return {array}        sorted array.
 */
Room.prototype.sortParts = function(parts, layout) {
  return _.sortBy(parts, function(p) {
    if (p === TOUGH) {
      return 0;
    }
    let order = _.indexOf(layout.parts, p) + 1;
    if (order) {
      return order;
    } else {
      return layout.len;
    }
  });
};

/**
 * Room.prototype.getPartsConfig use for generate adapted body
 *
 * @param {Collection} creep queue's creep spawn basic datas
 */

Room.prototype.getPartConfig = function(creep) {
  let energyAvailable = this.energyAvailable;
  let {
    prefixString,
    layoutString,
    amount,
    maxLayoutAmount,
    sufixString,
    fillTough,
  } = this.getSettings(creep);
  let maxBodyLength = MAX_CREEP_SIZE;
  if (prefixString) { maxBodyLength -= prefixString.length; }
  if (sufixString) { maxBodyLength -= sufixString.length; }

  // prefix
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] prefix: ' + prefixString + ', ' + energyAvailable);
  }
  let prefix = this.getPartsStringDatas(prefixString, energyAvailable);
  if (prefix.fail) {
    return false;
  }
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] prefix data: ' + JSON.stringify(prefix));
  }
  let parts = prefix.parts || [];
  energyAvailable -= prefix.cost || 0;

  // layout
  layoutString = this.applyAmount(layoutString, amount);
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] layout: ' + layoutString + ', ' + energyAvailable);
  }
  let layout = this.getPartsStringDatas(layoutString, energyAvailable);
  if (layout.fail || layout.null) {
    return false;
  }
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] layout data: ' + JSON.stringify(layout));
  }
  let maxRepeat = Math.floor(Math.min(energyAvailable / layout.cost, maxBodyLength / layout.len));
  if (maxLayoutAmount) {
    maxRepeat = Math.min(maxLayoutAmount, maxRepeat);
  }
  parts = parts.concat(_.flatten(Array(maxRepeat).fill(layout.parts)));
  energyAvailable -= layout.cost * maxRepeat;

  // sufix
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] sufix: ' + sufixString + ', ' + energyAvailable);
  }
  let sufix = this.getPartsStringDatas(sufixString, energyAvailable);
  if (!sufix.fail && !sufix.null) {
    if (config.debug.getPartsConfLogs) {
      this.log('[getPartConfig] sufix data: ' + JSON.stringify(sufix));
    }
    parts = parts.concat(sufix.parts);
    energyAvailable -= sufix.cost || 0;
  }

  if (fillTough && parts.length < MAX_CREEP_SIZE) {
    let tough = this.getPartsStringDatas('T', energyAvailable);
    if (!tough.fail && !tough.null) {
      let maxTough = Math.floor(Math.min(energyAvailable / tough.cost, MAX_CREEP_SIZE - parts.length));
      parts = _.flatten(Array(maxTough).fill(tough.parts)).concat(parts);
    }
  }

  if (config.debug.spawn) {
    this.log('Spawning ' + creep.role + ' - - - Body: ' + JSON.stringify(prefix.parts) + ' - ' + maxRepeat + ' * ' + JSON.stringify(layout.parts) + ' - ' + JSON.stringify(sufix.parts));
  }
  return config.creep.sortParts ? this.sortParts(parts, layout) : parts;
};

Room.prototype.getSpawnableSpawns = function() {
  let spawnsNotSpawning = this.find(FIND_MY_SPAWNS, { filter: spawn => !spawn.spawning });
  return spawnsNotSpawning;
};

Room.prototype.getCreepConfig = function(creep) {
  let role = creep.role;
  let unit = roles[role];
  if (!unit) {
    this.log('Can not find role: ' + role + ' creep_' + role);
    return false;
  }
  var id = Math.floor((Math.random() * 10000) + 1);
  var name = role + '-' + id;
  var partConfig = this.getPartConfig(creep);
  if (!partConfig) { return; }
  let memory = Object.assign({}, creep, {
    number: id,
    step: 0,
    base: creep.base || this.name,
    born: Game.time,
    killPrevious: unit.killPrevious,
    flee: unit.flee,
    buildRoad: unit.buildRoad
  });
  return {
    name: name,
    memory: memory,
    partConfig: partConfig
  };
};

/**
 * Room.prototype.spawnCreateCreep use for launch spawn of first creep in queue.
 *
 * @param {Collection} creep Object with queue's creep datas.
 */
Room.prototype.spawnCreateCreep = function(creep) {
  let spawns = this.getSpawnableSpawns();
  if (spawns.length === 0) { return; }

  let creepConfig = this.getCreepConfig(creep);
  if (!creepConfig) {
    return false;
  }

  for (let spawn of spawns) {
    let returnCode = spawn.createCreep(creepConfig.partConfig, creepConfig.name, creepConfig.memory);
    if (returnCode != creepConfig.name) {
      this.log(`spawnCreateCreep: ${returnCode} ${creepConfig.name}`);
      continue;
    }
    brain.stats.modifyRoleAmount(creep.role, 1);
    return true;
  }
  return false;

};

Room.prototype.checkAndSpawnSourcer = function() {
  var sources = this.find(FIND_SOURCES);

  let source;
  let isSourcer = object => object.memory.routing.targetId === source.id && object.memory.routing.targetRoom === source.pos.roomName;
  for (source of sources) {
    let sourcers = this.findPropertyFilter(FIND_MY_CREEPS, 'memory.role', ['sourcer'], false, { filter: isSourcer });
    if (sourcers.length === 0) {
      //      this.log(source.id);
      this.checkRoleToSpawn('sourcer', 1, source.id, this.name);
    }
  }
};
<commit_msg>Fix TOUGH amount max to MOVE part amount<commit_after>'use strict';

Room.prototype.creepMem = function(role, targetId, targetRoom, level, base) {
  return {
    role: role,
    routing: {
      targetRoom: targetRoom || this.name,
      targetId: targetId,
    },
    level: level,
    base: base,
  };
};

/**
 * get priority from config for a creep.
 *
 * @param  {Object} object the creep queue object
 * @return {Number}        the priority for creep
 */
Room.prototype.getPriority = function(object) {
  let priority = config.priorityQueue;
  let target = object.routing && object.routing.targetRoom;
  if (target === this.name) {
    return priority.sameRoom[object.role] || 4;
  } else if (target) {
    return priority.otherRoom[object.role] || 20 + Game.map.getRoomLinearDistance(this.name, target);
  } else {
    return 19;
  }
};

Room.prototype.spawnCheckForCreate = function() {
  if (this.memory.queue.length === 0) {
    return false;
  }
  if (!this.exectueEveryTicks(10)) {
    if (this.energyAvailable < this.energyCapacityAvailable - 300) {
      return false;
    }
  }
  this.memory.queue = _.sortBy(this.memory.queue, c => this.getPriority(c));

  let creep = this.memory.queue[0];
  if (this.spawnCreateCreep(creep)) {
    this.memory.queue.shift();
    return true;
  }
  if (creep.ttl === 0) {
    this.log('TTL reached, skipping: ' + JSON.stringify(creep));
    this.memory.queue.shift();
    return false;
  }
  creep.ttl = creep.ttl || config.creep.queueTtl;
  if (this.getSpawnableSpawns().length === 0) {
    creep.ttl--;
  }
  return false;
};

Room.prototype.inQueue = function(creepMemory) {
  this.memory.queue = this.memory.queue || [];
  for (var item of this.memory.queue) {
    if (creepMemory.role !== item.role) {
      continue;
    }
    if (!item.routing) {
      this.log('No item routing: ' + JSON.stringify(item));
      if (item.role !== 'scout') {
        this.log('inQueue: no routing: ' + JSON.stringify(item) + ' ' + JSON.stringify(creepMemory));
      }
      continue;
    }
    if (creepMemory.routing.targetId === item.routing.targetId && creepMemory.routing.targetRoom === item.routing.targetRoom) {
      return true;
    }
  }
  return false;
};

Room.prototype.inRoom = function(creepMemory, amount = 1) {
  if (amount === 0) {
    return false;
  }
  let creepsSpawning = [];
  for (let spawn of this.find(FIND_MY_SPAWNS)) {
    if (spawn.spawning) {
      creepsSpawning.push(Game.creeps[spawn.spawning.name]);
    }
  }
  var creeps = this.find(FIND_MY_CREEPS).concat(creepsSpawning);
  this.memory.roles = this.memory.roles || {};

  let j = 0;
  for (let creep of creeps) {
    let iMem = Memory.creeps[creep.name];

    if (!iMem) {
      this.log(`${creep} ${creep.name} ${Array.isArray(creep)} ${creep instanceof Array} ${typeof creep} ${Object.keys(creep)}`);
      this.log(`inRoom no iMem ${JSON.stringify(creep)}`);
      this.log(`inRoom no iMem ${creep.name}`);
      continue;
    }
    if (creepMemory.role === iMem.role && (!iMem.routing ||
        (creepMemory.routing.targetRoom === iMem.routing.targetRoom &&
          creepMemory.routing.targetId === iMem.routing.targetId))) {
      j++;
    }
    if (j >= amount) {
      this.memory.roles[creepMemory.role] = true;
      /**
      if (config.debug.queue) {
        this.log('Already enough ' + creepMemory.role);
      }
      **/
      return true;
    }
  }
  return false;
};

/**
 * First function call for ask a creep spawn. Add it in queue after check if spawn is allow.
 *
 * @param  {string} role       the role of the creeps to spawn.
 * @param  {number} amount     the amount of creeps asked for (1).
 * @param  {string} targetId   the id of targeted object by creeps (null).
 * @param  {string} targetRoom the targeted room name (base)
 * @param  {number} level      the level of creeps. required by some functions.
 * @param  {string} base       the room which will spawn creep
 * @param  {object} additionalMemory
 * @return {boolean}           if the spawn is not allow, it will return false.
 */
Room.prototype.checkRoleToSpawn = function(role, amount, targetId, targetRoom, level, base, additionalMemory = {}) {
  if (typeof targetId == 'object' && targetId.id) {
    targetId = targetId.id;
  }
  const creepMemory = this.creepMem(role, targetId, targetRoom, level, base);
  Object.assign(creepMemory, additionalMemory);
  if (this.inQueue(creepMemory) || this.inRoom(creepMemory, amount)) { return false; }

  if (config.debug.queue) {
    this.log('Add ' + creepMemory.role + ' to queue. ' + JSON.stringify(creepMemory));
  }
  return this.memory.queue.push(creepMemory);
};

/**
 * Room.prototype.getPartsStringDatas used for parse parts as string and return
 * parts as array, cost, if spawn is allow and length.
 *
 * @param {String} parts String of body parts. e.g. 'MMWC'
 * @param {Number} energyAvailable energy allow for spawn.
 * @return {Object}       The parts datas :
 *                            .fail = true if not enouth energy
 *                            .cost = cost of parts
 *                            .parts = parts as array
 *                            .len = the amount of parts.
 */

Room.prototype.getPartsStringDatas = function(parts, energyAvailable) {
  if (!_.isString(parts) || parts === '') {
    return {
      null: true
    };
  }
  let ret = {};
  Memory.layoutsCost = Memory.layoutsCost || {};
  ret.cost = Memory.layoutsCost[parts] || 0;
  ret.parts = global.utils.stringToParts(parts);
  ret.len = ret.parts.length;
  if (config.debug.spawn) {
    this.log(`getPartsStringDatas ret: ${JSON.stringify(ret)} parts: ${JSON.stringify(parts)}`);
  }
  if (ret.cost) {
    ret.fail = ret.cost > energyAvailable;
    return ret;
  }
  _.each(ret.parts,
    (p) => {
      ret.cost += BODYPART_COST[p];
      ret.fail = ret.cost > energyAvailable;
    }
  );
  Memory.layoutsCost[parts] = ret.cost;
  return ret;
};

/**
 * Room.prototype.getSettings use for return creep spawn settings
 * adapted to room configuration
 *
 * @param {Collection} creep queue's creep spawn basic datas
 */
Room.prototype.getSettings = function(creep) {
  let role = creep.role;
  let updateSettings = roles[role].updateSettings && roles[role].updateSettings(this, creep);
  let settings = _.merge(roles[role].settings, updateSettings);
  if (!settings) {
    this.log('try to spawn ', role, ' but settings are not done. Abort spawn');
    return;
  }
  let param = settings.param;
  return _.mapValues(settings, (setting, settingName) => {
    // Not sure when this happens
    if (!setting || setting === null) {
      return setting;
    }
    if (!param) {
      return setting;
    }
    for (let parameter of param) {
      if (_.isString(setting) || _.isNumber(setting) || _.isArray(setting) || _.isBoolean(setting)) {
        break;
      }
      let valueForI = _.get(this, parameter, 1);
      let foundKey = 0;
      if (!setting) {
        break;
      }
      for (let key of Object.keys(setting)) {
        if (valueForI < key && foundKey !== 0) {
          break;
        }
        foundKey = key;
      }
      setting = setting[foundKey];
    }
    return setting;
  });
};

/**
 * Transform a string using an array char ammount. e.g. ('WMC', [1,2,3]) ==> 'WMMCCC'
 *
 * @param  {String} input  the input parts as string.
 * @param  {Array} amount the amount of each char needed.
 * @return {String}        the new parts string
 */
Room.prototype.applyAmount = function(input, amount) {
  if (!input) {
    return '';
  }
  if (!amount) {
    return input;
  }
  let cost = 0;
  let parts = [];
  let output = '';
  _.forEach(amount, function(element, index) {
    output += _.repeat(input.charAt(index), element);
  });
  if (config.debug.spawn) {
    this.log(`applyAmount input: ${JSON.stringify(input)} amount: ${JSON.stringify(amount)} output: ${JSON.stringify(output)}`);
  }
  return output;
};

/**
 * Sort body parts with the same order used in layout. Parts not in layout are last ones.
 *
 * @param  {array} parts  the parts array to sort.
 * @param  {array} layout the base layout.
 * @return {array}        sorted array.
 */
Room.prototype.sortParts = function(parts, layout) {
  return _.sortBy(parts, function(p) {
    if (p === TOUGH) {
      return 0;
    }
    let order = _.indexOf(layout.parts, p) + 1;
    if (order) {
      return order;
    } else {
      return layout.len;
    }
  });
};

/**
 * Room.prototype.getPartsConfig use for generate adapted body
 *
 * @param {Collection} creep queue's creep spawn basic datas
 */

Room.prototype.getPartConfig = function(creep) {
  let energyAvailable = this.energyAvailable;
  let {
    prefixString,
    layoutString,
    amount,
    maxLayoutAmount,
    sufixString,
    fillTough,
  } = this.getSettings(creep);
  let maxBodyLength = MAX_CREEP_SIZE;
  if (prefixString) { maxBodyLength -= prefixString.length; }
  if (sufixString) { maxBodyLength -= sufixString.length; }

  // prefix
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] prefix: ' + prefixString + ', ' + energyAvailable);
  }
  let prefix = this.getPartsStringDatas(prefixString, energyAvailable);
  if (prefix.fail) {
    return false;
  }
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] prefix data: ' + JSON.stringify(prefix));
  }
  let parts = prefix.parts || [];
  energyAvailable -= prefix.cost || 0;

  // layout
  layoutString = this.applyAmount(layoutString, amount);
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] layout: ' + layoutString + ', ' + energyAvailable);
  }
  let layout = this.getPartsStringDatas(layoutString, energyAvailable);
  if (layout.fail || layout.null) {
    return false;
  }
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] layout data: ' + JSON.stringify(layout));
  }
  let maxRepeat = Math.floor(Math.min(energyAvailable / layout.cost, maxBodyLength / layout.len));
  if (maxLayoutAmount) {
    maxRepeat = Math.min(maxLayoutAmount, maxRepeat);
  }
  parts = parts.concat(_.flatten(Array(maxRepeat).fill(layout.parts)));
  energyAvailable -= layout.cost * maxRepeat;

  // sufix
  if (config.debug.getPartsConfLogs) {
    this.log('[getPartConfig] sufix: ' + sufixString + ', ' + energyAvailable);
  }
  let sufix = this.getPartsStringDatas(sufixString, energyAvailable);
  if (!sufix.fail && !sufix.null) {
    if (config.debug.getPartsConfLogs) {
      this.log('[getPartConfig] sufix data: ' + JSON.stringify(sufix));
    }
    parts = parts.concat(sufix.parts);
    energyAvailable -= sufix.cost || 0;
  }

  if (fillTough && parts.length < MAX_CREEP_SIZE) {
    let tough = this.getPartsStringDatas('T', energyAvailable);
    if (!tough.fail && !tough.null) {
      let maxTough = Math.floor(Math.min(energyAvailable / tough.cost, MAX_CREEP_SIZE - parts.length, parts.filter(p => p === MOVE).length));
      parts = _.flatten(Array(maxTough).fill(tough.parts)).concat(parts);
    }
  }

  if (config.debug.spawn) {
    this.log('Spawning ' + creep.role + ' - - - Body: ' + JSON.stringify(prefix.parts) + ' - ' + maxRepeat + ' * ' + JSON.stringify(layout.parts) + ' - ' + JSON.stringify(sufix.parts));
  }
  return config.creep.sortParts ? this.sortParts(parts, layout) : parts;
};

Room.prototype.getSpawnableSpawns = function() {
  let spawnsNotSpawning = this.find(FIND_MY_SPAWNS, { filter: spawn => !spawn.spawning });
  return spawnsNotSpawning;
};

Room.prototype.getCreepConfig = function(creep) {
  let role = creep.role;
  let unit = roles[role];
  if (!unit) {
    this.log('Can not find role: ' + role + ' creep_' + role);
    return false;
  }
  var id = Math.floor((Math.random() * 10000) + 1);
  var name = role + '-' + id;
  var partConfig = this.getPartConfig(creep);
  if (!partConfig) { return; }
  let memory = Object.assign({}, creep, {
    number: id,
    step: 0,
    base: creep.base || this.name,
    born: Game.time,
    killPrevious: unit.killPrevious,
    flee: unit.flee,
    buildRoad: unit.buildRoad
  });
  return {
    name: name,
    memory: memory,
    partConfig: partConfig
  };
};

/**
 * Room.prototype.spawnCreateCreep use for launch spawn of first creep in queue.
 *
 * @param {Collection} creep Object with queue's creep datas.
 */
Room.prototype.spawnCreateCreep = function(creep) {
  let spawns = this.getSpawnableSpawns();
  if (spawns.length === 0) { return; }

  let creepConfig = this.getCreepConfig(creep);
  if (!creepConfig) {
    return false;
  }

  for (let spawn of spawns) {
    let returnCode = spawn.createCreep(creepConfig.partConfig, creepConfig.name, creepConfig.memory);
    if (returnCode != creepConfig.name) {
      this.log(`spawnCreateCreep: ${returnCode} ${creepConfig.name}`);
      continue;
    }
    brain.stats.modifyRoleAmount(creep.role, 1);
    return true;
  }
  return false;

};

Room.prototype.checkAndSpawnSourcer = function() {
  var sources = this.find(FIND_SOURCES);

  let source;
  let isSourcer = object => object.memory.routing.targetId === source.id && object.memory.routing.targetRoom === source.pos.roomName;
  for (source of sources) {
    let sourcers = this.findPropertyFilter(FIND_MY_CREEPS, 'memory.role', ['sourcer'], false, { filter: isSourcer });
    if (sourcers.length === 0) {
      //      this.log(source.id);
      this.checkRoleToSpawn('sourcer', 1, source.id, this.name);
    }
  }
};
<|endoftext|>


<commit_before>/**
 * JSONP-sec uses spawned web workers to
 * insulate the current UI thread and DOM
 * from potential attacks from JSONP requests.
 * This is not meant to be a replacement for
 * a reasonable CORS setup, only when you do
 * not have access to the server returning data.
 */
(function(global) {

	/**
	 * Make a secure JSONP request, within the context of the web worker thread.
	 * This will use importScripts, so you must have a solution defined in the
	 * degraded iframe state that works with traditional JSONP <script> tags.
	 * @param {Object} options The options to use
	 * @param {String} options.url The url for the request
	 * @param {String} options.callbackParam The name of the callback function (will be executed in the global scope)
	 * @param {Function} callback The callback to respond to
	 * @return {Null}
	 */
	var JSONPsec = operative(function(options, callback) {
		var url, script,
			callbackParam = 'callback';

			// Bail if we have no options
			if (!(options && options.url)) {
				throw new Error('Need options and options.url defined');
			}

			// Set up options for the request
			url = options.url;
			callbackParam = options.callbackParam || callbackParam;

			// Set up callback param and create the script tag
			url += (url.indexOf('?') !== -1 && '&' || '?') + callbackParam + '=cb';

			// Set callback
			this[callbackParam] = callback;

			// Define importScripts for the degraded experience
			if (!importScripts) {
				this.importScripts = function(url) {
					var doc = document.body || document.documentElement,
						script = document.createElement('script');
					script.async = true;
					script.src = url;
					doc.appendChild(script);
				};
			}

			// Make the request
			importScripts(url);
		});

	// Expose the api
	if (typeof define !== 'undefined' && define.amd) {
		define([], function() {
			return JSONPsec;
		});
	}
	else {
		global.JSONPsec = JSONPsec;
	}

}(this));
<commit_msg>Using try/catch for IE to work<commit_after>/**
 * JSONP-sec uses spawned web workers to
 * insulate the current UI thread and DOM
 * from potential attacks from JSONP requests.
 * This is not meant to be a replacement for
 * a reasonable CORS setup, only when you do
 * not have access to the server returning data.
 */
(function(global) {

	/**
	 * Make a secure JSONP request, within the context of the web worker thread.
	 * This will use importScripts, so you must have a solution defined in the
	 * degraded iframe state that works with traditional JSONP <script> tags.
	 * @param {Object} options The options to use
	 * @param {String} options.url The url for the request
	 * @param {String} options.callbackParam The name of the callback function (will be executed in the global scope)
	 * @param {Function} callback The callback to respond to
	 * @return {Null}
	 */
	var JSONPsec = operative(function(options, callback) {
		var url, script,
			callbackParam = 'callback';

			// Bail if we have no options
			if (!(options && options.url)) {
				throw new Error('Need options and options.url defined');
			}

			// Set up options for the request
			url = options.url;
			callbackParam = options.callbackParam || callbackParam;

			// Set up callback param and create the script tag
			url += (url.indexOf('?') !== -1 && '&' || '?') + callbackParam + '=cb';

			// Set callback
			this[callbackParam] = callback;

			// Make the request
			try {
				importScripts(url);
			}
			// Append script in the degraded experience
			catch (err) {
				(function(url) {
					var doc = document.body || document.documentElement,
						script = document.createElement('script');
					script.async = true;
					script.src = url;
					doc.appendChild(script);
				}(url));
			}
		});

	// Expose the api
	if (typeof define !== 'undefined' && define.amd) {
		define([], function() {
			return JSONPsec;
		});
	}
	else {
		global.JSONPsec = JSONPsec;
	}

}(this));
<|endoftext|>