<commit_before>/*
 * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
 */

var ProjectManager = {};

ProjectManager.openProject = function() {
    if (!brackets.inBrowser) {
		// Pop up a folder browse dialog
        NativeFileSystem.showOpenDialog(false, true, "Choose a folder", null, null,
			function(files) {
				// If length == 0, user canceled the dialog; length should never be > 1
				if (files.length > 0)
					ProjectManager.loadProject( files[0] );
			}
		);
    }
}

ProjectManager.loadProject = function(rootPath) {
	// Set title
	var projectName = rootPath.substring(rootPath.lastIndexOf("/") + 1);
	$("#project-title").html(projectName);

	// Populate file tree
	if (brackets.inBrowser) {
		// Hardcoded dummy data for local testing, in jsTree JSON format
		var subfolderInner = { data:"Folder_inner", children:[
			{ data: "subsubfile_1" }, { data: "subsubfile_2" }
		] };
		var treeJSONData = [
			{ data: "Dummy tree content:" },
			{ data:"Folder_1", children:[
				{ data: "subfile_1" }, { data: "subfile_2" }, { data: "subfile_3" }
			] },
			{ data:"Folder_2", children:[
				{ data: "subfile_4" }, subfolderInner, { data: "subfile_5" }
			] },
			{ data: "file_1" },
			{ data: "file_2" }
        ];

        // Show file list in UI synchronously
        ProjectManager._renderTree(treeJSONData);

	} else {
        // Actually scan the folder structure on local disk
        var rootEntry = NativeFileSystem.requestNativeFileSystem(rootPath, null, null);  // TODO: add success/error callbacks

        var reader = rootEntry.createReader();
        var topLevelItems = [];

        function convertEntriesToJSON(entries) {
			var jsonEntryList = [];
			for (var entryI in entries) {
				var entry = entries[entryI];

				// TODO: if (ProjectManager.shouldShowFile(item)) ...

				var jsonEntry = {
				    data: entry.name,
				    metadata: { entry: entry }
				};
				if (entry.isDirectory) {
					jsonEntry.children = [];
					jsonEntry.state = "closed";
				}
				jsonEntryList.push(jsonEntry);
			}
			return jsonEntryList;
        }

        reader.readEntries(function(entries) {
			topLevelItems = convertEntriesToJSON(entries);

			// This will cause the tree to start calling us, pulling down the root's immediate children
			ProjectManager._renderTree(dataProvider);
		},
		function(error) {
			console.log(error);     // TODO: real error handling
		});

		function dataProvider(treeNode, callback) {
		    // Special case: root
		    if (treeNode == -1) {
		        console.log("Displaying prefecthed root...");
		        callback(topLevelItems);
		    } else {
		        var entry = treeNode.data("entry");
		        console.log("Fetching subtree: "+entry.fullPath);

		        entry.createReader().readEntries(
		            function(entries) {
		                var subtreeJSON = convertEntriesToJSON(entries);
		                callback(subtreeJSON);
		            },
		            function(error) {
		                console.log(error);     // TODO: real error handling
		            }
		        );
		    }
		}

		// var rootedTreeJSONData = readDirectory(rootEntry);
		// treeJSONData = rootedTreeJSONData.children;		// we don't want the root folder to be an actual tree node

        // function readDirectory(entry){
        //     var reader = entry.createReader();
        //     var jsonItem = {
        //         data: entry.name,
        //         children: []
        //     };
        //     reader.readEntries(dirReaderSuccessCB, dirReaderErrorCB);
        // }
        //         function dirReaderSuccessCB( entries ){
        //             var tabs = "";
        //             for( i = 0; i < nestingLevel; i++ ){
        //                 tabs += "  ";
        //             }
        //
        //             for ( var entryI in entries ){
        //                 var entry = entries[entryI];
        //                 if( entry.isFile ){
        //                     // create leaf tree node using entry.name
        //                     console.log( tabs+ entry.name );
        //                 }
        //                 else if ( entry.isDirectory ){
        //                     // create branch tree node using entry.name
        //                     console.log( tabs + entry.name );
        //
        //                     nestingLevel++;
        //                     readDirectory( entry )
        //                 }
        //             }
        //         }
        //
        // // Convert to jsTree's JSON model format
        // function _renderSubtree(fileListData) {
        //  var result = [];
        //  $(fileListData).each(function(index, item) {
        //      var entry = {
        //          data: (item.folder ? item.name : item)
        //          // for additional options, see http://www.jstree.com/documentation/json_data
        //      };
        //      if (item.folder) {
        //          entry.children = _renderSubtree(item.items);
        //      }
        //
        //      result.push(entry);
        //  });
        //  return result;
        // }
        //
        // var treeJSON = _renderSubtree(fileListData);

		// TODO: fetch from real file APIs
		// var projectFiles = JSON.parse(brackets.file.getDirectoryListing(rootPath)); //eval(
		// fileListData = [];

		// projectFiles.forEach(function(index, item) {
			// // Filter down to just visible files
			// if (ProjectManager.shouldShowFile(item)) {
				// if (brackets.file.isDirectory(rootPath + "/" + item))
					// fileListData.push( { name:item, folder:true, items:[] } );
				// else
					// fileListData.push(item);
			// }
		// });
	}

	// ProjectManager._currentProjectRoot = rootPath;
};

/**
 * Returns true if the given file should be displayed in the file tree UI.
 * @param fileName File name, including extension but excluding path
 */
ProjectManager.shouldShowFile = function(fileName) {
	// Ignore names starting with "."
	if (item.indexOf(".") == 0) return false;

	return true;
};

/**
 * @private
 *
 * Given an input to jsTree's json_data.data setting, display the data in the file tree UI
 * (replacing any existing file tree that was previously displayed). This input could be
 * raw JSON data, or it could be a dataprovider function. See jsTree docs for details:
 * http://www.jstree.com/documentation/json_data
 */
ProjectManager._renderTree = function(treeDataProvider) {
	// Clear old project files
	var projectList = $("#project-files");
	projectList.html("");

	// Transform into tree widget
	projectList.parent().jstree({
		plugins : ["ui", "themes", "json_data"],
		json_data : { data:treeDataProvider },
		core : { animation:0 },
		themes : { theme:"brackets", url:"styles/jsTreeTheme.css", dots:false, icons:false },
			//(note: our actual jsTree theme CSS lives in brackets.less; we specify an empty .css
			// file because jsTree insists on loading one itself)
		strings : { loading : "Loading ...", new_node : "New node" }	// TODO: localization
	});
};<commit_msg>Remove (unused) file filtering logic for now, since the file APIs already skip "."-prefixed items, and we have no other use cases for filtering yet.<commit_after>/*
 * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
 */

var ProjectManager = {};

ProjectManager.openProject = function() {
    if (!brackets.inBrowser) {
		// Pop up a folder browse dialog
        NativeFileSystem.showOpenDialog(false, true, "Choose a folder", null, null,
			function(files) {
				// If length == 0, user canceled the dialog; length should never be > 1
				if (files.length > 0)
					ProjectManager.loadProject( files[0] );
			}
		);
    }
}

ProjectManager.loadProject = function(rootPath) {
	// Set title
	var projectName = rootPath.substring(rootPath.lastIndexOf("/") + 1);
	$("#project-title").html(projectName);

	// Populate file tree
	if (brackets.inBrowser) {
		// Hardcoded dummy data for local testing, in jsTree JSON format
		var subfolderInner = { data:"Folder_inner", children:[
			{ data: "subsubfile_1" }, { data: "subsubfile_2" }
		] };
		var treeJSONData = [
			{ data: "Dummy tree content:" },
			{ data:"Folder_1", children:[
				{ data: "subfile_1" }, { data: "subfile_2" }, { data: "subfile_3" }
			] },
			{ data:"Folder_2", children:[
				{ data: "subfile_4" }, subfolderInner, { data: "subfile_5" }
			] },
			{ data: "file_1" },
			{ data: "file_2" }
        ];

        // Show file list in UI synchronously
        ProjectManager._renderTree(treeJSONData);

	} else {
        // Actually scan the folder structure on local disk
        var rootEntry = NativeFileSystem.requestNativeFileSystem(rootPath, null, null);  // TODO: add success/error callbacks

        var reader = rootEntry.createReader();
        var topLevelItems = [];

        function convertEntriesToJSON(entries) {
			var jsonEntryList = [];
			for (var entryI in entries) {
				var entry = entries[entryI];

				var jsonEntry = {
				    data: entry.name,
				    metadata: { entry: entry }
				};
				if (entry.isDirectory) {
					jsonEntry.children = [];
					jsonEntry.state = "closed";
				}
				jsonEntryList.push(jsonEntry);
			}
			return jsonEntryList;
        }

        reader.readEntries(function(entries) {
			topLevelItems = convertEntriesToJSON(entries);

			// This will cause the tree to start calling us, pulling down the root's immediate children
			ProjectManager._renderTree(dataProvider);
		},
		function(error) {
			console.log(error);     // TODO: real error handling
		});

		function dataProvider(treeNode, callback) {
		    // Special case: root
		    if (treeNode == -1) {
		        console.log("Displaying prefecthed root...");
		        callback(topLevelItems);
		    } else {
		        var entry = treeNode.data("entry");
		        console.log("Fetching subtree: "+entry.fullPath);

		        entry.createReader().readEntries(
		            function(entries) {
		                var subtreeJSON = convertEntriesToJSON(entries);
		                callback(subtreeJSON);
		            },
		            function(error) {
		                console.log(error);     // TODO: real error handling
		            }
		        );
		    }
		}

		// var rootedTreeJSONData = readDirectory(rootEntry);
		// treeJSONData = rootedTreeJSONData.children;		// we don't want the root folder to be an actual tree node

        // function readDirectory(entry){
        //     var reader = entry.createReader();
        //     var jsonItem = {
        //         data: entry.name,
        //         children: []
        //     };
        //     reader.readEntries(dirReaderSuccessCB, dirReaderErrorCB);
        // }
        //         function dirReaderSuccessCB( entries ){
        //             var tabs = "";
        //             for( i = 0; i < nestingLevel; i++ ){
        //                 tabs += "  ";
        //             }
        //
        //             for ( var entryI in entries ){
        //                 var entry = entries[entryI];
        //                 if( entry.isFile ){
        //                     // create leaf tree node using entry.name
        //                     console.log( tabs+ entry.name );
        //                 }
        //                 else if ( entry.isDirectory ){
        //                     // create branch tree node using entry.name
        //                     console.log( tabs + entry.name );
        //
        //                     nestingLevel++;
        //                     readDirectory( entry )
        //                 }
        //             }
        //         }
        //
        // // Convert to jsTree's JSON model format
        // function _renderSubtree(fileListData) {
        //  var result = [];
        //  $(fileListData).each(function(index, item) {
        //      var entry = {
        //          data: (item.folder ? item.name : item)
        //          // for additional options, see http://www.jstree.com/documentation/json_data
        //      };
        //      if (item.folder) {
        //          entry.children = _renderSubtree(item.items);
        //      }
        //
        //      result.push(entry);
        //  });
        //  return result;
        // }
        //
        // var treeJSON = _renderSubtree(fileListData);

		// TODO: fetch from real file APIs
		// var projectFiles = JSON.parse(brackets.file.getDirectoryListing(rootPath)); //eval(
		// fileListData = [];

		// projectFiles.forEach(function(index, item) {
			// // Filter down to just visible files
			// if (ProjectManager.shouldShowFile(item)) {
				// if (brackets.file.isDirectory(rootPath + "/" + item))
					// fileListData.push( { name:item, folder:true, items:[] } );
				// else
					// fileListData.push(item);
			// }
		// });
	}

	// ProjectManager._currentProjectRoot = rootPath;
};

/**
 * @private
 *
 * Given an input to jsTree's json_data.data setting, display the data in the file tree UI
 * (replacing any existing file tree that was previously displayed). This input could be
 * raw JSON data, or it could be a dataprovider function. See jsTree docs for details:
 * http://www.jstree.com/documentation/json_data
 */
ProjectManager._renderTree = function(treeDataProvider) {
	// Clear old project files
	var projectList = $("#project-files");
	projectList.html("");

	// Transform into tree widget
	projectList.parent().jstree({
		plugins : ["ui", "themes", "json_data"],
		json_data : { data:treeDataProvider },
		core : { animation:0 },
		themes : { theme:"brackets", url:"styles/jsTreeTheme.css", dots:false, icons:false },
			//(note: our actual jsTree theme CSS lives in brackets.less; we specify an empty .css
			// file because jsTree insists on loading one itself)
		strings : { loading : "Loading ...", new_node : "New node" }	// TODO: localization
	});
};<|endoftext|>


<commit_before>import {NotificationQueue} from "./webcomponents/Notification.js";

export default class UtilsNew {

    static get MESSAGE_SUCCESS() {
        return "success";
    }

    static get MESSAGE_ERROR() {
        return "danger";
    }

    static get MESSAGE_INFO() {
        return "info";
    }

    static get MESSAGE_WARNING() {
        return "warning";
    }

    static isUndefined(obj) {
        return typeof obj === "undefined";
    }

    static isNotUndefined(obj) {
        return typeof obj !== "undefined";
    }

    static isNull(obj) {
        return obj === null;
    }

    static isNotNull(obj) {
        return obj !== null;
    }

    static isUndefinedOrNull(obj) {
        return typeof obj === "undefined" || obj === null;
    }

    static isNotUndefinedOrNull(obj) {
        return typeof obj !== "undefined" && obj !== null;
    }

    static isEmpty(obj) {
        if (typeof obj === "undefined" || obj === null) {
            return true;
        }

        // obj is an actual Object
        if (typeof obj === "object") {
            for (const key in obj) {
                if (obj.hasOwnProperty(key)) {
                    return false;
                }
            }
            return true;
        } else {
            // obj is a String
            if (typeof obj === "string") {
                return obj === "";
            }
        }
    }

    static isNotEmpty(obj) {
        return !this.isEmpty(obj);
    }

    static isEmptyArray(arr) {
        return typeof arr !== "undefined" && arr !== null && arr.length === 0;
    }

    static isNotEmptyArray(arr) {
        return typeof arr !== "undefined" && arr !== null && arr.length > 0;
    }

    static defaultString(str, str2) {
        return this.isNotEmpty(str) ? str : str2;
    }

    static containsArray(arr, item) {
        if (UtilsNew.isNotEmptyArray(arr) && UtilsNew.isNotUndefinedOrNull(item)) {
            return arr.indexOf(item) > -1;
        }

        return false;
    }

    static removeDuplicates(array, prop) {
        const newArray = [];
        const lookupObject = {};

        for (const i in array) {
            lookupObject[array[i][prop]] = array[i];
        }

        for (const i in lookupObject) {
            newArray.push(lookupObject[i]);
        }
        return newArray;
    }

    static checkPermissions(project) {
        return UtilsNew.isUndefinedOrNull(project) || (UtilsNew.isNotUndefinedOrNull(project) && Object.keys(project).length === 0);
    }

    static isNotEqual(str, str2) {
        return str !== str2;
    }

    static isEqual(str, str2) {
        return str === str2;
    }

    static randomString(length) {
        let result = "";
        const _length = length || 6;
        const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        for (let i = 0; i < _length; i++) {
            result += characters.charAt(Math.floor(Math.random() * characters.length));
        }
        return result;
    }

    // safe check if the field is an object (NOTE null is an object, the constructor check is not enough)
    static isObject(o) {
        return o != null && o.constructor.name === "Object";
    }

    static getDiskUsage(bytes, numDecimals) {
        if (bytes === 0) {
            return "0 Byte";
        }
        const k = 1000;
        const dm = numDecimals ? numDecimals : 2;
        const sizes = [" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + sizes[i];
    }

    static getDatetime(timestamp) {
        function pad2(n) { // always returns a string
            return (n < 10 ? "0" : "") + n;
        }
        const date = timestamp ? new Date(timestamp) : new Date();
        return date.getFullYear() +
            pad2(date.getMonth() + 1) +
            pad2(date.getDate()) +
            pad2(date.getHours()) +
            pad2(date.getMinutes()) +
            pad2(date.getSeconds());
    }

    static initTooltip(scope) {
        $("a[tooltip-title]", scope).each(function () {
            $(this).qtip({
                content: {
                    title: $(this).attr("tooltip-title"),
                    text: $(this).attr("tooltip-text")
                },
                // jQuery UI Position plugin
                position: {
                    target: "mouse",
                    adjust: {x: 2, y: 2, mouse: false},
                    my: $(this).attr("tooltip-position-my") ?? "top left",
                    at: $(this).attr("tooltip-position-at") ?? "bottom right"},
                style: {width: true, classes: "qtip-light qtip-rounded qtip-shadow"},
                // show: {delay: 200},
                show: {
                    delay: 200,
                    event: "click mouseenter"
                },
                hide: {fixed: true, delay: 300}
            });
        });
    }

    static dateFormatter(date, format) {
        const _format = format ? format : "D MMM YYYY";
        return moment(date, "YYYYMMDDHHmmss").format(_format);
    }

    static arrayDimension(array) {
        const shape = (array, i) => Array.isArray(array) ? shape(array[0], i+1) : i;
        return shape(array, 0);
    }

    static renderHTML(html) {
        return document.createRange().createContextualFragment(`${html}`);
    }

    static jobStatusFormatter(status) {
        switch (status) {
            case "PENDING":
            case "QUEUED":
            case "REGISTERING":
            case "UNREGISTERED":
                return `<span class="text-primary"><i class="far fa-clock"></i> ${status}</span>`;
            case "RUNNING":
                return `<span class="text-primary"><i class="fas fa-sync-alt anim-rotate"></i> ${status}</span>`;
            case "DONE":
                return `<span class="text-success"><i class="fas fa-check-circle"></i> ${status}</span>`;
            case "ERROR":
                return `<span class="text-danger"><i class="fas fa-exclamation-circle"></i> ${status}</span>`;
            case "UNKNOWN":
                return `<span class="text-danger"><i class="fas fa-exclamation-circle"></i> ${status}</span>`;
            case "ABORTED":
                return `<span class="text-warning"><i class="fas fa-ban"></i> ${status}</span>`;
            case "DELETED":
                return `<span class="text-primary"><i class="fas fa-trash-alt"></i> ${status}</span>`;
        }
        return "-";
    }

    /**
     * This function creates a table (rows and columns) a given Object or array of Objects using the fields provided.
     * Id fields is not defined or empty then it uses the Object keys. Fields can contain arrays and nested arrays.
     */
    static toTableString(objects, fields) {
        const table = [];
        if (objects) {
            // Make sure objects is an array
            if (!Array.isArray(objects)) {
                objects = [objects];
            }

            // Use Object keys as default
            if (!fields || fields.length === 0) {
                fields = Object.keys(objects[0]);
            }

            // Print headers and get the rows and columns
            table.push(fields);
            for (const object of objects) {
                const row = [];
                for (const field of fields) {
                    let value;
                    const subfields = field.split(".");
                    // Check if subfields are arrays, eg. samples.id
                    if (subfields.length > 1) {
                        // Get the last subfield which is an array
                        for (let i = 0; i < subfields.length - 1; i++) {
                            value = subfields.slice(0, i + 1).reduce((res, prop) => res?.[prop], object);
                            if (!Array.isArray(value)) {
                                break;
                            }
                        }
                        // Check if any subfield was an array
                        if (Array.isArray(value)) {
                            value = value.map(val => val[subfields[subfields.length - 1]]).join(",");
                        } else {
                            value = subfields.reduce((res, prop) => res?.[prop], object);
                        }
                    } else {
                        value = subfields.reduce((res, prop) => res?.[prop], object);
                    }
                    row.push(value);
                }
                table.push(row);
            }
        }
        return table;
    }

    /**
     * Download data in the browser.
     * data can be a string, and arrays of string or an array of arrays
     */
    static downloadData(data, filename, mimeType = "application/json") {
        // data can be a string, and arrays of string or an array of arrays
        let dataString = data;
        if (Array.isArray(data)) {
            if (Array.isArray(data[0])) {
                dataString = data.map(row => row.join("\t")).join("\n");
            } else {
                dataString = data.join("\n");
            }
        }

        // Build file and anchor link
        const blob = new Blob([dataString], {type: mimeType});
        const file = window.URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = file;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        setTimeout(function () {
            document.body.removeChild(a);
        }, 0);
    }

    static range(start, stop, step = 1) {
        return Array.from({length: (stop - start) / step}, (x, i) => (i + start) * step);
    }

    static errorStringify(error) {
        return (err => JSON.stringify(Object.getOwnPropertyNames(Object.getPrototypeOf(err)).reduce(function (accumulator, currentValue) {
            return accumulator[currentValue] = err[currentValue], accumulator;
        }, {})))(error);
    }

    static notifyError(response) {
        if (response?.getEvents?.("ERROR")?.length) {
            const errors = response.getEvents("ERROR");
            errors.forEach(error => new NotificationQueue().push(error.name, error.message, "ERROR"));
        } else if (response instanceof Error) {
            new NotificationQueue().push(response.name, response.message, "ERROR");
        } else {
            new NotificationQueue().push("Generic Error", JSON.stringify(response), "ERROR");
        }
    }

    /**
     * Returns the object sorted by key in lexicographic order.
     * @param {Object} unordered Unordered object
     * @returns {Object} ordered Ordered object
     */
    static objectSort(unordered) {
        const keys = Object.keys(unordered).sort();
        return Object.assign({}, ...keys.map(k => ({[k]: unordered[k]})));
    }

    /**
     * Compares the objects by key and value (nested object are not supported yet)
     * @param {Object} a First object
     * @param {Object} b Second object
     * @returns {boolean} true if the oject are equals
     */
    static objectCompare(a, b) {
        if (a && b) {
            const _a = UtilsNew.objectSort(a);
            const _b = UtilsNew.objectSort(b);
            return JSON.stringify(_a) === JSON.stringify(_b);
        } else {
            return false;
        }
    }

    /**
     * This function return the object sorted by the keys provided.
     * @param {Object} unordered Unordered object
     * @param {Array} keys ordered keys
     * @param {boolean} addMissingKeys Flag for adding or not keys not present in the array `keys`
     * @returns {Object} ordered Ordered object
     */
    static objectKeySort(unordered, keys, addMissingKeys) {
        if (!unordered) {
            console.log("Parameter unordered is not valued: ", unordered);
            return null;
        }

        if (!keys || keys.length === 0) {
            console.log("Parameter keys is undefined or empty: ", keys);
            return unordered;
        }

        const ordered = {};
        for (const key of keys) {
            if (typeof unordered[key] !== "undefined") {
                ordered[key] = unordered[key];
            }
        }
        // We check if there is any other unordered key not present in the keys array
        if (addMissingKeys && Object.keys(unordered).length !== keys.length) {
            for (const unorderedKey of Object.keys(unordered)) {
                if (!keys.includes(unorderedKey)) {
                    ordered[unorderedKey] = unordered[unorderedKey];
                }
            }
        }
        return ordered;
    }

    static substring(string, maxLength) {
        if (typeof maxLength === "undefined") {
            return string;
        }

        if (string && string.length > maxLength) {
            return string.substring(0, maxLength) + "...";
        }
    }

    static sleep(ms) {
        return new Promise(resolve => setTimeout(() => resolve(), ms));
    }

}
<commit_msg>minor improvement in objectKeySort fn<commit_after>import {NotificationQueue} from "./webcomponents/Notification.js";

export default class UtilsNew {

    static get MESSAGE_SUCCESS() {
        return "success";
    }

    static get MESSAGE_ERROR() {
        return "danger";
    }

    static get MESSAGE_INFO() {
        return "info";
    }

    static get MESSAGE_WARNING() {
        return "warning";
    }

    static isUndefined(obj) {
        return typeof obj === "undefined";
    }

    static isNotUndefined(obj) {
        return typeof obj !== "undefined";
    }

    static isNull(obj) {
        return obj === null;
    }

    static isNotNull(obj) {
        return obj !== null;
    }

    static isUndefinedOrNull(obj) {
        return typeof obj === "undefined" || obj === null;
    }

    static isNotUndefinedOrNull(obj) {
        return typeof obj !== "undefined" && obj !== null;
    }

    static isEmpty(obj) {
        if (typeof obj === "undefined" || obj === null) {
            return true;
        }

        // obj is an actual Object
        if (typeof obj === "object") {
            for (const key in obj) {
                if (obj.hasOwnProperty(key)) {
                    return false;
                }
            }
            return true;
        } else {
            // obj is a String
            if (typeof obj === "string") {
                return obj === "";
            }
        }
    }

    static isNotEmpty(obj) {
        return !this.isEmpty(obj);
    }

    static isEmptyArray(arr) {
        return typeof arr !== "undefined" && arr !== null && arr.length === 0;
    }

    static isNotEmptyArray(arr) {
        return typeof arr !== "undefined" && arr !== null && arr.length > 0;
    }

    static defaultString(str, str2) {
        return this.isNotEmpty(str) ? str : str2;
    }

    static containsArray(arr, item) {
        if (UtilsNew.isNotEmptyArray(arr) && UtilsNew.isNotUndefinedOrNull(item)) {
            return arr.indexOf(item) > -1;
        }

        return false;
    }

    static removeDuplicates(array, prop) {
        const newArray = [];
        const lookupObject = {};

        for (const i in array) {
            lookupObject[array[i][prop]] = array[i];
        }

        for (const i in lookupObject) {
            newArray.push(lookupObject[i]);
        }
        return newArray;
    }

    static checkPermissions(project) {
        return UtilsNew.isUndefinedOrNull(project) || (UtilsNew.isNotUndefinedOrNull(project) && Object.keys(project).length === 0);
    }

    static isNotEqual(str, str2) {
        return str !== str2;
    }

    static isEqual(str, str2) {
        return str === str2;
    }

    static randomString(length) {
        let result = "";
        const _length = length || 6;
        const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        for (let i = 0; i < _length; i++) {
            result += characters.charAt(Math.floor(Math.random() * characters.length));
        }
        return result;
    }

    // safe check if the field is an object (NOTE null is an object, the constructor check is not enough)
    static isObject(o) {
        return o != null && o.constructor.name === "Object";
    }

    static getDiskUsage(bytes, numDecimals) {
        if (bytes === 0) {
            return "0 Byte";
        }
        const k = 1000;
        const dm = numDecimals ? numDecimals : 2;
        const sizes = [" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + sizes[i];
    }

    static getDatetime(timestamp) {
        function pad2(n) { // always returns a string
            return (n < 10 ? "0" : "") + n;
        }
        const date = timestamp ? new Date(timestamp) : new Date();
        return date.getFullYear() +
            pad2(date.getMonth() + 1) +
            pad2(date.getDate()) +
            pad2(date.getHours()) +
            pad2(date.getMinutes()) +
            pad2(date.getSeconds());
    }

    static initTooltip(scope) {
        $("a[tooltip-title]", scope).each(function () {
            $(this).qtip({
                content: {
                    title: $(this).attr("tooltip-title"),
                    text: $(this).attr("tooltip-text")
                },
                // jQuery UI Position plugin
                position: {
                    target: "mouse",
                    adjust: {x: 2, y: 2, mouse: false},
                    my: $(this).attr("tooltip-position-my") ?? "top left",
                    at: $(this).attr("tooltip-position-at") ?? "bottom right"},
                style: {width: true, classes: "qtip-light qtip-rounded qtip-shadow"},
                // show: {delay: 200},
                show: {
                    delay: 200,
                    event: "click mouseenter"
                },
                hide: {fixed: true, delay: 300}
            });
        });
    }

    static dateFormatter(date, format) {
        const _format = format ? format : "D MMM YYYY";
        return moment(date, "YYYYMMDDHHmmss").format(_format);
    }

    static arrayDimension(array) {
        const shape = (array, i) => Array.isArray(array) ? shape(array[0], i+1) : i;
        return shape(array, 0);
    }

    static renderHTML(html) {
        return document.createRange().createContextualFragment(`${html}`);
    }

    static jobStatusFormatter(status) {
        switch (status) {
            case "PENDING":
            case "QUEUED":
            case "REGISTERING":
            case "UNREGISTERED":
                return `<span class="text-primary"><i class="far fa-clock"></i> ${status}</span>`;
            case "RUNNING":
                return `<span class="text-primary"><i class="fas fa-sync-alt anim-rotate"></i> ${status}</span>`;
            case "DONE":
                return `<span class="text-success"><i class="fas fa-check-circle"></i> ${status}</span>`;
            case "ERROR":
                return `<span class="text-danger"><i class="fas fa-exclamation-circle"></i> ${status}</span>`;
            case "UNKNOWN":
                return `<span class="text-danger"><i class="fas fa-exclamation-circle"></i> ${status}</span>`;
            case "ABORTED":
                return `<span class="text-warning"><i class="fas fa-ban"></i> ${status}</span>`;
            case "DELETED":
                return `<span class="text-primary"><i class="fas fa-trash-alt"></i> ${status}</span>`;
        }
        return "-";
    }

    /**
     * This function creates a table (rows and columns) a given Object or array of Objects using the fields provided.
     * Id fields is not defined or empty then it uses the Object keys. Fields can contain arrays and nested arrays.
     */
    static toTableString(objects, fields) {
        const table = [];
        if (objects) {
            // Make sure objects is an array
            if (!Array.isArray(objects)) {
                objects = [objects];
            }

            // Use Object keys as default
            if (!fields || fields.length === 0) {
                fields = Object.keys(objects[0]);
            }

            // Print headers and get the rows and columns
            table.push(fields);
            for (const object of objects) {
                const row = [];
                for (const field of fields) {
                    let value;
                    const subfields = field.split(".");
                    // Check if subfields are arrays, eg. samples.id
                    if (subfields.length > 1) {
                        // Get the last subfield which is an array
                        for (let i = 0; i < subfields.length - 1; i++) {
                            value = subfields.slice(0, i + 1).reduce((res, prop) => res?.[prop], object);
                            if (!Array.isArray(value)) {
                                break;
                            }
                        }
                        // Check if any subfield was an array
                        if (Array.isArray(value)) {
                            value = value.map(val => val[subfields[subfields.length - 1]]).join(",");
                        } else {
                            value = subfields.reduce((res, prop) => res?.[prop], object);
                        }
                    } else {
                        value = subfields.reduce((res, prop) => res?.[prop], object);
                    }
                    row.push(value);
                }
                table.push(row);
            }
        }
        return table;
    }

    /**
     * Download data in the browser.
     * data can be a string, and arrays of string or an array of arrays
     */
    static downloadData(data, filename, mimeType = "application/json") {
        // data can be a string, and arrays of string or an array of arrays
        let dataString = data;
        if (Array.isArray(data)) {
            if (Array.isArray(data[0])) {
                dataString = data.map(row => row.join("\t")).join("\n");
            } else {
                dataString = data.join("\n");
            }
        }

        // Build file and anchor link
        const blob = new Blob([dataString], {type: mimeType});
        const file = window.URL.createObjectURL(blob);
        const a = document.createElement("a");
        a.href = file;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        setTimeout(function () {
            document.body.removeChild(a);
        }, 0);
    }

    static range(start, stop, step = 1) {
        return Array.from({length: (stop - start) / step}, (x, i) => (i + start) * step);
    }

    static errorStringify(error) {
        return (err => JSON.stringify(Object.getOwnPropertyNames(Object.getPrototypeOf(err)).reduce(function (accumulator, currentValue) {
            return accumulator[currentValue] = err[currentValue], accumulator;
        }, {})))(error);
    }

    static notifyError(response) {
        if (response?.getEvents?.("ERROR")?.length) {
            const errors = response.getEvents("ERROR");
            errors.forEach(error => new NotificationQueue().push(error.name, error.message, "ERROR"));
        } else if (response instanceof Error) {
            new NotificationQueue().push(response.name, response.message, "ERROR");
        } else {
            new NotificationQueue().push("Generic Error", JSON.stringify(response), "ERROR");
        }
    }

    /**
     * Returns the object sorted by key in lexicographic order.
     * @param {Object} unordered Unordered object
     * @returns {Object} ordered Ordered object
     */
    static objectSort(unordered) {
        const keys = Object.keys(unordered).sort();
        return Object.assign({}, ...keys.map(k => ({[k]: unordered[k]})));
    }

    /**
     * Compares the objects by key and value (nested object are not supported yet)
     * @param {Object} a First object
     * @param {Object} b Second object
     * @returns {boolean} true if the oject are equals
     */
    static objectCompare(a, b) {
        if (a && b) {
            const _a = UtilsNew.objectSort(a);
            const _b = UtilsNew.objectSort(b);
            return JSON.stringify(_a) === JSON.stringify(_b);
        } else {
            return false;
        }
    }

    /**
     * This function return the object sorted by the keys provided.
     * @param {Object} unordered Unordered object
     * @param {Array} keys ordered keys
     * @param {boolean} addMissingKeys Flag for adding or not keys not present in the array `keys`
     * @returns {Object} ordered Ordered object
     */
    static objectKeySort(unordered, keys, addMissingKeys = false) {
        if (!unordered) {
            console.log("Parameter unordered is not valued: ", unordered);
            return null;
        }

        if (!keys || keys.length === 0) {
            console.log("Parameter keys is undefined or empty: ", keys);
            return unordered;
        }

        const ordered = {};
        for (const key of keys) {
            if (typeof unordered[key] !== "undefined") {
                ordered[key] = unordered[key];
            }
        }
        // We check if there is any other unordered key not present in the keys array
        if (addMissingKeys === true && Object.keys(unordered).length !== keys.length) {
            for (const unorderedKey of Object.keys(unordered)) {
                if (!keys.includes(unorderedKey)) {
                    ordered[unorderedKey] = unordered[unorderedKey];
                }
            }
        }
        return ordered;
    }

    static substring(string, maxLength) {
        if (typeof maxLength === "undefined") {
            return string;
        }

        if (string && string.length > maxLength) {
            return string.substring(0, maxLength) + "...";
        }
    }

    static sleep(ms) {
        return new Promise(resolve => setTimeout(() => resolve(), ms));
    }

}
<|endoftext|>


<commit_before>// Uses AMD or browser globals to create a module.

// Grabbed from https://github.com/umdjs/umd/blob/master/amdWeb.js.
// Check out https://github.com/umdjs/umd for more patterns.

// Defines a module "cart".
// Note that the name of the module is implied by the file name. It is best
// if the file name and the exported global have matching names.

// If you do not want to support the browser global path, then you
// can remove the `root` use and the passing `this` as the first arg to
// the top function.

(function (root, factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    define(['eventemitter2'], function (EventEmitter2) {
      return factory(EventEmitter2);
    });
  }
  else if (typeof module !== 'undefined' && module.exports && require) {
    module.exports = factory(require('../bower_components/eventemitter2/index.js').EventEmitter2);
  }
  else {
    root.Cart = factory(root.EventEmitter2);
  }
}(this, function (EventEmitter) {
  'use strict';

  var _uid = 0
    , UNSIGNED_PRODUCT = null;

  function toArray(arr, singleArgArray) {
    if (singleArgArray && 1 === arr.length && Array.isArray(arr[0])) { // single item arguments that is array
      return arr[0].slice();
    }
    else if (Array.isArray(arr)) {
      return arr.slice();
    }
    else if (typeof arr === 'object' && !!arr && 'length' in arr) { // duck. object, not null, has length property
      return Array.prototype.slice.call(arr);
    }
    else {
      return [arr];
    }
  }

  // http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
  function hashCode(s) {
    var hash = 0
      , strlen = s.length
      , i, c;
    if (0 === strlen) {
      return hash;
    }
    for (i = 0; i < strlen; i++) {
      c = s.charCodeAt(i);
      hash = ((hash << 5) - hash) + c;
      hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
  }

  function Item(data) {
    data = data || {};

    var id, product, price, signature, properties, quantity;
    id          = 'id' in data ? data.id : ++_uid;
    product     = 'product' in data ? data.product : id;
    price       = 'price' in data ? data.price : Number.MAX_VALUE;
    quantity    = 'quantity' in data ? data.quantity : 1;
    signature   = 'signature' in data ? data.signature : UNSIGNED_PRODUCT;
    properties  = 'properties' in data ? data.properties : {};

    Object.defineProperties(this, {
      id: {
        get: function() {
          return id;
        }
      },

      product: {
        get: function() {
          return product;
        }
      },

      price: {
        get: function() {
          return price;
        },
        set: function(value) {
          if (this.locked) {
            throw new Error('Cannot set price because cart is locked');
          }
          if (signature !== UNSIGNED_PRODUCT) {
            throw new Error('Cannot modify price of signed product');
          }
          price = value;
          this.trigger('price', value);
          this.trigger('total');
          this.trigger('change');
        }
      },

      signature: {
        get: function() {
          return signature;
        }
      },

      quantity: {
        get: function() {
          return quantity;
        },
        set: function(value) {
          if (this.locked) {
            throw new Error('Cannot set quantity because cart is locked');
          }
          quantity = value;
          this.trigger('quantity', value);
          this.trigger('total');
          this.trigger('change');
        }
      },

      properties: {
        get: function() {
          return Object.create(properties);
        }
      },

      property: {
        value: function(key, value) {
          switch (arguments.length) {
            case 1:
              return properties[key];
            case 2:
              if (this.locked) {
                throw new Error('Cannot set property because cart is locked');
              }
              var prev = properties[key];
              properties[key] = value;
              this.trigger('property:change', key, value, prev);
              this.trigger('change');
              return this;
            default:
              throw new Error('Invalid argument length');
          }
        }
      },

      subtotal: {
        get: function() {
          return this.price * this.quantity;
        }
      },
    });

    var _locked = false;
    Object.defineProperty(this, 'locked', {
      get: function() {
        return _locked;
      }
    });
    Object.defineProperty(this, 'lock', {
      value: function(isLocked) {
        if (typeof isLocked === 'boolean') {
          if (_locked !== isLocked) {
            _locked = isLocked;
            this.trigger(_locked ? 'lock' : 'unlock');
          }
        }
        else {
          throw new Error('Invalid lock value');
        }
      }
    });
  }

  Item.prototype = Object.create(EventEmitter.prototype);
  Item.prototype.constructor = Item;

  // delay event emits
  Item.prototype.trigger = function() {
    var _this = this
      , args = arguments;
    setTimeout(function() {
      _this.emit.apply(_this, args);
    }, 0);
  };

  Item.prototype.toJSON = function() {
    return {
        product: this.product
      , price: this.price
      , quantity: this.quantity
      , signature: this.signature
      , subtotal: this.subtotal
      , properties: this.properties
    };
  };

  function Cart(items, options) {
    var _this = this;
    if (!Array.isArray(items)) {
      options = items;
      items = null;
    }

    this.options = {
      // unique ID for the current store
      store: 'store',

      // user ID
      user: 'guest',

      meta: {},

      // if adding a product that already exists, how should it be handled:
      //  - add: dupe item quantity should be added to current item quantity
      //  - error: do noting and emit an error
      //  - allow: allow dupe items to exist
      dupeItemMode: 'error',

      // create a signature for current cart
      signer: function(cart) {
        var itemsAsStr = _this._items.map(function(item) {
          return [ item.product, item.price, item.signature, item.quantity ].join(':');
        }).join('|');
        return hashCode([ _this.store, _this.user, itemsAsStr ].join(';'));
      },

      // payment processor strategy
      paymentDriver: function(cart, callback) {
        callback(new Error('No payment processor strategy provided'));
      }
    };

    options = options || {};
    for (var k in options) {
      if (k in this.options) {
        this.options[k] = options[k];
      }
      else {
        throw new Error('Invalid Cart option: ' + k);
      }
    }
    Object.freeze(this.options);

    this._items = !Array.isArray(items) ? [] : items.map(function(element) {
      return element instanceof Item ? element : new Item(element);
    });

    Object.defineProperty(this, 'total', {
      get: function() {
        return !this._items.length ? 0 : this._items.reduce(function(prev, cur) {
          return prev + cur.subtotal;
        }, 0);
      }
    });

    Object.defineProperty(this, 'count', {
      get: function() {
        return this._items.length;
      }
    });

    Object.defineProperty(this, 'signature', {
      get: function() {
        try {
          return this.options.signer(this);
        }
        catch (err) {
          this.trigger('error', new Error('Cart signer threw an error'));
          throw err;
        }
      }
    });

    var _locked = false;
    Object.defineProperty(this, 'locked', {
      get: function() {
        return _locked;
      }
    });
    Object.defineProperty(this, 'lock', {
      value: function(isLocked) {
        if (typeof isLocked === 'boolean') {
          if (_locked !== isLocked) {
            _locked = isLocked;
            this._items.forEach(function(item) {
              item.lock(_locked);
            });
            this.trigger(_locked ? 'lock' : 'unlock');
          }
        }
        else {
          throw new Error('Invalid lock value');
        }
      }
    });
  }

  Cart.prototype = Object.create(EventEmitter.prototype);
  Cart.prototype.constructor = Cart;

  // delay event emits
  Cart.prototype.trigger = function() {
    var _this = this
      , args = arguments;
    setTimeout(function() {
      _this.emit.apply(_this, args);
    }, 0);
  };

  Cart.prototype.itemAt = function(index) {
    return this._items[index];
  };

  Cart.prototype._watchItem = function(item) {
    var _this = this;
    item
      .on('error', function(err) {
        _this.trigger('item:error', err, this);
        _this.trigger('error', err);
      })
      .on('change', function() {
        _this.trigger('item:change', this);
        _this.trigger('change');
      })
      .on('total', function() {
        _this.trigger('item:total', this);
        _this.trigger('total');
      });
    return this;
  };

  Cart.prototype.add = function() {
    if (this.locked) {
      throw new Error('Cannot add because cart is locked');
    }
    var additions = toArray(arguments, true)
      , addition
      , item;
    if (additions.length) {
      for (var i=0; i < additions.length; i++) {
        addition = additions[i];
        item = addition instanceof Item ? addition : new Item(addition);
        if (this._items.indexOf(item) < 0) {
          var dupe = this.findBy('product', item.product);
          if (dupe) {
            switch (this.options.dupeItemMode) {
              default:
              case 'error':
                this.trigger('error', new Error('Duplicate product exists and dupeItemMode is set to error'), item, dupe);
                continue;
              case 'add':
                dupe.quantity += item.quantity;
                continue;
              case 'allow':
                // do nothing, let it be added
              break;
            }
          }
          this._items.push(item);
          this._watchItem(item);
          this.trigger('item:add', item);
          this.trigger('change');
        }
        else {
          this.trigger('error', new Error('Attempting to add an item that already exists in cart'), item);
        }
      }
      this.trigger('count');
      this.trigger('total');
    }
    return this;
  };

  Cart.prototype.remove = function() {
    if (this.locked) {
      throw new Error('Cannot remove because cart is locked');
    }
    var removals = toArray(arguments, true)
      , removal
      , item;
    if (removals.length) {
      for (var i=0; i < removals.length; i++) {
        removal = removals[i];
        item = removal instanceof Item ? removal : this.find(removal);
        if (item) {
          this._items.splice(this._items.indexOf(item), 1);
          this.trigger('item:remove', item);
          this.trigger('change');
        }
        else {
          this.trigger('error', new Error('Item "' + removal + '" was not found in cart'));
        }
      }
      this.trigger('count');
      this.trigger('total');
    }
    return this;
  };

  Cart.prototype.removeIndex = function(index) {
    var item = this._items[index];
    if (item) {
      this.remove(item);
    }
    return this;
  };

  Cart.prototype.findBy = function(property, value) {
    for (var i=0; i < this._items.length; i++) {
      if (this._items[i][property] === value) {
        return this._items[i];
      }
    }
    return null;
  };

  Cart.prototype.find = function(id) {
    return this.findBy('id', id);
  };

  Cart.prototype.clear = function() {
    Cart.prototype.remove.apply(this, this._items);
    this.trigger('clear');
    return this;
  };

  Cart.prototype.purchase = function(callback) {
    if (this.locked) {
      throw new Error('Cannot purchase because cart is locked');
    }
    var _this = this
      , wrappedCallback;
    callback = callback || function(){};
    wrappedCallback = function() {
      callback.apply(this, arguments);
      callback = function() {
        _this.trigger('error', new Error('Purchase callback already called'));
      };
    }
    if (this._items.length) {
      this.lock(true);
      this.trigger('purchasing');
      try {
        this.options.paymentDriver(this, function(err, success) {
          _this.lock(false);
          wrappedCallback(err, success);
          if (err) {
            _this.trigger('error', err);
            return;
          }
          _this.trigger('purchase', success);
        });
      }
      catch(err) {
        this.trigger('error', new Error('Payment driver lead to error'));
        this.lock(false);
        wrappedCallback(err);
        throw err;
      }
    }
    else {
      var emptyErr = new Error('Cannot purchase because cart is empty');
      wrappedCallback(emptyErr);
      this.trigger('error', emptyErr);
    }
    return this;
  };

  Cart.prototype.toJSON = function() {
    return {
        store: this.options.store
      , user: this.options.user
      , meta: this.options.meta
      , total: this.total
      , signature: this.signature
      , items: this._items.map(Function.prototype.call, Item.prototype.toJSON)
    };
  };

  Cart.from = function(json, signer, paymentDriver) {
    var options = {
      store: json.store,
      user: json.user,
      meta: json.meta
    };
    if (signer) {
      options.signer = signer;
    }
    else if (json.signer) {
      options.signer = json.signer;
    }
    if (paymentDriver) {
      options.paymentDriver = paymentDriver;
    }
    else if (json.paymentDriver) {
      options.paymentDriver = json.paymentDriver;
    }
    return new Cart(json.items, options);
  };

  Cart.Item = Item;

  Cart.utils = {
    toArray: toArray,
  };

  return Cart;
}));
<commit_msg>change way properties returned<commit_after>// Uses AMD or browser globals to create a module.

// Grabbed from https://github.com/umdjs/umd/blob/master/amdWeb.js.
// Check out https://github.com/umdjs/umd for more patterns.

// Defines a module "cart".
// Note that the name of the module is implied by the file name. It is best
// if the file name and the exported global have matching names.

// If you do not want to support the browser global path, then you
// can remove the `root` use and the passing `this` as the first arg to
// the top function.

(function (root, factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    define(['eventemitter2'], function (EventEmitter2) {
      return factory(EventEmitter2);
    });
  }
  else if (typeof module !== 'undefined' && module.exports && require) {
    module.exports = factory(require('../bower_components/eventemitter2/index.js').EventEmitter2);
  }
  else {
    root.Cart = factory(root.EventEmitter2);
  }
}(this, function (EventEmitter) {
  'use strict';

  var _uid = 0
    , UNSIGNED_PRODUCT = null;

  function toArray(arr, singleArgArray) {
    if (singleArgArray && 1 === arr.length && Array.isArray(arr[0])) { // single item arguments that is array
      return arr[0].slice();
    }
    else if (Array.isArray(arr)) {
      return arr.slice();
    }
    else if (typeof arr === 'object' && !!arr && 'length' in arr) { // duck. object, not null, has length property
      return Array.prototype.slice.call(arr);
    }
    else {
      return [arr];
    }
  }

  // http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
  function hashCode(s) {
    var hash = 0
      , strlen = s.length
      , i, c;
    if (0 === strlen) {
      return hash;
    }
    for (i = 0; i < strlen; i++) {
      c = s.charCodeAt(i);
      hash = ((hash << 5) - hash) + c;
      hash = hash & hash; // Convert to 32bit integer
    }
    return hash;
  }

  function Item(data) {
    data = data || {};

    var id, product, price, signature, properties, quantity;
    id          = 'id' in data ? data.id : ++_uid;
    product     = 'product' in data ? data.product : id;
    price       = 'price' in data ? data.price : Number.MAX_VALUE;
    quantity    = 'quantity' in data ? data.quantity : 1;
    signature   = 'signature' in data ? data.signature : UNSIGNED_PRODUCT;
    properties  = 'properties' in data ? data.properties : {};

    Object.defineProperties(this, {
      id: {
        get: function() {
          return id;
        }
      },

      product: {
        get: function() {
          return product;
        }
      },

      price: {
        get: function() {
          return price;
        },
        set: function(value) {
          if (this.locked) {
            throw new Error('Cannot set price because cart is locked');
          }
          if (signature !== UNSIGNED_PRODUCT) {
            throw new Error('Cannot modify price of signed product');
          }
          price = value;
          this.trigger('price', value);
          this.trigger('total');
          this.trigger('change');
        }
      },

      signature: {
        get: function() {
          return signature;
        }
      },

      quantity: {
        get: function() {
          return quantity;
        },
        set: function(value) {
          if (this.locked) {
            throw new Error('Cannot set quantity because cart is locked');
          }
          quantity = value;
          this.trigger('quantity', value);
          this.trigger('total');
          this.trigger('change');
        }
      },

      properties: {
        get: function() {
          var obj = {};
          for (var k in properties) {
            obj[k] = properties[k];
          }
          Object.freeze(obj);
          return obj;
        }
      },

      property: {
        value: function(key, value) {
          switch (arguments.length) {
            case 1:
              return properties[key];
            case 2:
              if (this.locked) {
                throw new Error('Cannot set property because cart is locked');
              }
              var prev = properties[key];
              properties[key] = value;
              this.trigger('property:change', key, value, prev);
              this.trigger('change');
              return this;
            default:
              throw new Error('Invalid argument length');
          }
        }
      },

      subtotal: {
        get: function() {
          return this.price * this.quantity;
        }
      },
    });

    var _locked = false;
    Object.defineProperty(this, 'locked', {
      get: function() {
        return _locked;
      }
    });
    Object.defineProperty(this, 'lock', {
      value: function(isLocked) {
        if (typeof isLocked === 'boolean') {
          if (_locked !== isLocked) {
            _locked = isLocked;
            this.trigger(_locked ? 'lock' : 'unlock');
          }
        }
        else {
          throw new Error('Invalid lock value');
        }
      }
    });
  }

  Item.prototype = Object.create(EventEmitter.prototype);
  Item.prototype.constructor = Item;

  // delay event emits
  Item.prototype.trigger = function() {
    var _this = this
      , args = arguments;
    setTimeout(function() {
      _this.emit.apply(_this, args);
    }, 0);
  };

  Item.prototype.toJSON = function() {
    return {
        product: this.product
      , price: this.price
      , quantity: this.quantity
      , signature: this.signature
      , subtotal: this.subtotal
      , properties: this.properties
    };
  };

  function Cart(items, options) {
    var _this = this;
    if (!Array.isArray(items)) {
      options = items;
      items = null;
    }

    this.options = {
      // unique ID for the current store
      store: 'store',

      // user ID
      user: 'guest',

      meta: {},

      // if adding a product that already exists, how should it be handled:
      //  - add: dupe item quantity should be added to current item quantity
      //  - error: do noting and emit an error
      //  - allow: allow dupe items to exist
      dupeItemMode: 'error',

      // create a signature for current cart
      signer: function(cart) {
        var itemsAsStr = _this._items.map(function(item) {
          return [ item.product, item.price, item.signature, item.quantity ].join(':');
        }).join('|');
        return hashCode([ _this.store, _this.user, itemsAsStr ].join(';'));
      },

      // payment processor strategy
      paymentDriver: function(cart, callback) {
        callback(new Error('No payment processor strategy provided'));
      }
    };

    options = options || {};
    for (var k in options) {
      if (k in this.options) {
        this.options[k] = options[k];
      }
      else {
        throw new Error('Invalid Cart option: ' + k);
      }
    }
    Object.freeze(this.options);

    this._items = !Array.isArray(items) ? [] : items.map(function(element) {
      return element instanceof Item ? element : new Item(element);
    });

    Object.defineProperty(this, 'total', {
      get: function() {
        return !this._items.length ? 0 : this._items.reduce(function(prev, cur) {
          return prev + cur.subtotal;
        }, 0);
      }
    });

    Object.defineProperty(this, 'count', {
      get: function() {
        return this._items.length;
      }
    });

    Object.defineProperty(this, 'signature', {
      get: function() {
        try {
          return this.options.signer(this);
        }
        catch (err) {
          this.trigger('error', new Error('Cart signer threw an error'));
          throw err;
        }
      }
    });

    var _locked = false;
    Object.defineProperty(this, 'locked', {
      get: function() {
        return _locked;
      }
    });
    Object.defineProperty(this, 'lock', {
      value: function(isLocked) {
        if (typeof isLocked === 'boolean') {
          if (_locked !== isLocked) {
            _locked = isLocked;
            this._items.forEach(function(item) {
              item.lock(_locked);
            });
            this.trigger(_locked ? 'lock' : 'unlock');
          }
        }
        else {
          throw new Error('Invalid lock value');
        }
      }
    });
  }

  Cart.prototype = Object.create(EventEmitter.prototype);
  Cart.prototype.constructor = Cart;

  // delay event emits
  Cart.prototype.trigger = function() {
    var _this = this
      , args = arguments;
    setTimeout(function() {
      _this.emit.apply(_this, args);
    }, 0);
  };

  Cart.prototype.itemAt = function(index) {
    return this._items[index];
  };

  Cart.prototype._watchItem = function(item) {
    var _this = this;
    item
      .on('error', function(err) {
        _this.trigger('item:error', err, this);
        _this.trigger('error', err);
      })
      .on('change', function() {
        _this.trigger('item:change', this);
        _this.trigger('change');
      })
      .on('total', function() {
        _this.trigger('item:total', this);
        _this.trigger('total');
      });
    return this;
  };

  Cart.prototype.add = function() {
    if (this.locked) {
      throw new Error('Cannot add because cart is locked');
    }
    var additions = toArray(arguments, true)
      , addition
      , item;
    if (additions.length) {
      for (var i=0; i < additions.length; i++) {
        addition = additions[i];
        item = addition instanceof Item ? addition : new Item(addition);
        if (this._items.indexOf(item) < 0) {
          var dupe = this.findBy('product', item.product);
          if (dupe) {
            switch (this.options.dupeItemMode) {
              default:
              case 'error':
                this.trigger('error', new Error('Duplicate product exists and dupeItemMode is set to error'), item, dupe);
                continue;
              case 'add':
                dupe.quantity += item.quantity;
                continue;
              case 'allow':
                // do nothing, let it be added
              break;
            }
          }
          this._items.push(item);
          this._watchItem(item);
          this.trigger('item:add', item);
          this.trigger('change');
        }
        else {
          this.trigger('error', new Error('Attempting to add an item that already exists in cart'), item);
        }
      }
      this.trigger('count');
      this.trigger('total');
    }
    return this;
  };

  Cart.prototype.remove = function() {
    if (this.locked) {
      throw new Error('Cannot remove because cart is locked');
    }
    var removals = toArray(arguments, true)
      , removal
      , item;
    if (removals.length) {
      for (var i=0; i < removals.length; i++) {
        removal = removals[i];
        item = removal instanceof Item ? removal : this.find(removal);
        if (item) {
          this._items.splice(this._items.indexOf(item), 1);
          this.trigger('item:remove', item);
          this.trigger('change');
        }
        else {
          this.trigger('error', new Error('Item "' + removal + '" was not found in cart'));
        }
      }
      this.trigger('count');
      this.trigger('total');
    }
    return this;
  };

  Cart.prototype.removeIndex = function(index) {
    var item = this._items[index];
    if (item) {
      this.remove(item);
    }
    return this;
  };

  Cart.prototype.findBy = function(property, value) {
    for (var i=0; i < this._items.length; i++) {
      if (this._items[i][property] === value) {
        return this._items[i];
      }
    }
    return null;
  };

  Cart.prototype.find = function(id) {
    return this.findBy('id', id);
  };

  Cart.prototype.clear = function() {
    Cart.prototype.remove.apply(this, this._items);
    this.trigger('clear');
    return this;
  };

  Cart.prototype.purchase = function(callback) {
    if (this.locked) {
      throw new Error('Cannot purchase because cart is locked');
    }
    var _this = this
      , wrappedCallback;
    callback = callback || function(){};
    wrappedCallback = function() {
      callback.apply(this, arguments);
      callback = function() {
        _this.trigger('error', new Error('Purchase callback already called'));
      };
    }
    if (this._items.length) {
      this.lock(true);
      this.trigger('purchasing');
      try {
        this.options.paymentDriver(this, function(err, success) {
          _this.lock(false);
          wrappedCallback(err, success);
          if (err) {
            _this.trigger('error', err);
            return;
          }
          _this.trigger('purchase', success);
        });
      }
      catch(err) {
        this.trigger('error', new Error('Payment driver lead to error'));
        this.lock(false);
        wrappedCallback(err);
        throw err;
      }
    }
    else {
      var emptyErr = new Error('Cannot purchase because cart is empty');
      wrappedCallback(emptyErr);
      this.trigger('error', emptyErr);
    }
    return this;
  };

  Cart.prototype.toJSON = function() {
    return {
        store: this.options.store
      , user: this.options.user
      , meta: this.options.meta
      , total: this.total
      , signature: this.signature
      , items: this._items.map(Function.prototype.call, Item.prototype.toJSON)
    };
  };

  Cart.from = function(json, signer, paymentDriver) {
    var options = {
      store: json.store,
      user: json.user,
      meta: json.meta
    };
    if (signer) {
      options.signer = signer;
    }
    else if (json.signer) {
      options.signer = json.signer;
    }
    if (paymentDriver) {
      options.paymentDriver = paymentDriver;
    }
    else if (json.paymentDriver) {
      options.paymentDriver = json.paymentDriver;
    }
    return new Cart(json.items, options);
  };

  Cart.Item = Item;

  Cart.utils = {
    toArray: toArray,
  };

  return Cart;
}));
<|endoftext|>


<commit_before>var Base    = require('./base'),
    xhtmlIm = require('./utils/xep-0071'),
    state   = require('./utils/xep-0085'),
    delay   = require('./utils/xep-0203')

var Chat = function() {}

Chat.prototype = new Base()

Chat.prototype.XHTML = 'xhtml'
Chat.prototype.PLAIN = 'plain'

Chat.prototype._events = {
    'xmpp.chat.message': 'sendMessage'
}

Chat.prototype.handles = function(stanza) {
    return (stanza.is('message') &&
            (!stanza.attrs.type || ('chat' === stanza.attrs.type)))
}

Chat.prototype.handle = function(stanza) {
    var chat = { from: this._getJid(stanza.attrs.from) }

    if (stanza.getChild('body') ||
        stanza.getChild('html', xhtmlIm.NS_XHTML_IM)
    ) {
        var format  = this.PLAIN
        var content = stanza.getChild('body').getText()
        var message
        if (!!(message = stanza.getChild('html', xhtmlIm.NS_XHTML_IM))) {
            content = message.getChild('body', xhtmlIm.NS_XHTML)
                .children
                .join('')
                .toString()
                .trim()
            format  = this.XHTML
        }
        chat.content = content
        chat.format = format
    }
    delay.parse(stanza, chat)
    state.parse(stanza, chat)
    var self = this
    stanza.getChildren('archived').forEach(function(archived) {
        if (!chat.archived) chat.archived = []
        chat.archived.push({
            by: self._getJid(archived.attrs.by), id: archived.attrs.id
        })
    })
    this.socket.emit('xmpp.chat.message', chat)
    return true
}

Chat.prototype.sendMessage = function(data) {
    if (!data.to) return this._clientError("Missing 'to' key", data)
    data.type = 'chat'
    if (!data.content && !data.state)
        return this._clientError(
            'Message content or chat state not provided', data
        )
     var stanza
     if (!(stanza = xhtmlIm.builder(data, { to: data.to, type: 'chat' }, this)))
         return
     if (data.state)
         stanza.root().c(data.state, { xmlns: state.NS })
     this.client.send(stanza)
}

module.exports = Chat
<commit_msg>Move message formatting around to get at content parser<commit_after>var Base    = require('./base'),
    xhtmlIm = require('./utils/xep-0071'),
    state   = require('./utils/xep-0085'),
    delay   = require('./utils/xep-0203')

var Chat = function() {}

Chat.prototype = new Base()

Chat.prototype.XHTML = 'xhtml'
Chat.prototype.PLAIN = 'plain'

Chat.prototype._events = {
    'xmpp.chat.message': 'sendMessage'
}

Chat.prototype.handles = function(stanza) {
    return (stanza.is('message') &&
            (!stanza.attrs.type || ('chat' === stanza.attrs.type)))
}

Chat.prototype.handle = function(stanza) {
    var chat = { from: this._getJid(stanza.attrs.from) }
    this._getMessageContent(stanza, chat)
    delay.parse(stanza, chat)
    state.parse(stanza, chat)
    var self = this
    stanza.getChildren('archived').forEach(function(archived) {
        if (!chat.archived) chat.archived = []
        chat.archived.push({
            by: self._getJid(archived.attrs.by), id: archived.attrs.id
        })
    })
    this.socket.emit('xmpp.chat.message', chat)
    return true
}

Chat.prototype._getMessageContent = function(stanza, chat) {
    if (!stanza.getChild('body') &&
        !stanza.getChild('html', xhtmlIm.NS_XHTML_IM)
    ) return

    var format  = this.PLAIN
    var content = stanza.getChild('body').getText()
    var message
    if (!!(message = stanza.getChild('html', xhtmlIm.NS_XHTML_IM))) {
        content = message.getChild('body', xhtmlIm.NS_XHTML)
            .children
            .join('')
            .toString()
            .trim()
        format  = this.XHTML
    }
    chat.content = content
    chat.format = format
}

Chat.prototype.sendMessage = function(data) {
    if (!data.to) return this._clientError("Missing 'to' key", data)
    data.type = 'chat'
    if (!data.content && !data.state)
        return this._clientError(
            'Message content or chat state not provided', data
        )
     var stanza
     if (!(stanza = xhtmlIm.builder(data, { to: data.to, type: 'chat' }, this)))
         return
     if (data.state)
         stanza.root().c(data.state, { xmlns: state.NS })
     this.client.send(stanza)
}

module.exports = Chat
<|endoftext|>


<commit_before>/**
 * @ngdoc directive
 * @name faFlipper
 * @module famous.angular
 * @restrict EA
 * @description
 * This directive will create a Famo.us Flipper containing the
 * specified front and back elements. The provided `options` object
 * will pass directly through to the Famo.us Flipper's
 * constructor.  See [https://famo.us/docs/views/Flipper]
 *
 * @usage
 * ```html
 * <fa-flipper fa-options="scopeOptionsObject">
 *   <!-- two render nodes -->
 * </fa-flipper>
 * ```
 *@example
 * A Famous Flipper has a `.flip()` method that toggles a rotation between front and back sides.
 * In the example below, when an `fa-surface` is clicked, it calls the function `flipIt`.
 *
 * This function attempts a DOM lookup for an isolate of an `fa-flipper` element, and calls the `.flip()` function of `fa-flipper`.
 *
 *```html
 * <fa-flipper>
 *    <fa-surface fa-background-color="'yellow'" fa-click="flipIt()"></fa-surface>
 *    <fa-surface fa-background-color="'red'" fa-click="flipIt()"></fa-surface>
 * </fa-flipper>
 *```
 *```javascript
 * $scope.flipIt = function() {
 *    $famous.find('fa-flipper')[0].flip();
 * };
 *```
 */

angular.module('famous.angular')
  .directive('faFlipper', ["$famous", "$famousDecorator",
    function ($famous, $famousDecorator) {
      return {
        template: '

',
        restrict: 'E',
        transclude: true,
        scope: true,
        compile: function (tElem, tAttrs, transclude) {
          return {
            pre: function (scope, element, attrs) {
              var isolate = $famousDecorator.ensureIsolate(scope);
              var Flipper = $famous["famous/views/Flipper"];

              //TODO:  $watch and update, or $parse and attr.$observe
              var options = scope.$eval(attrs.faOptions) || {};

              isolate.renderNode = new Flipper(options);
              isolate.children = [];

              isolate.flip = function (overrideOptions) {
                isolate.renderNode.flip(overrideOptions || scope.$eval(attrs.faOptions));
              };

              $famousDecorator.sequenceWith(
                scope,
                function(data) {
                  var _childCount = isolate.children.length;
                  if (_childCount === 0) {
                    isolate.renderNode.setFront(data.renderNode);
                  } else if (_childCount === 1) {
                    isolate.renderNode.setBack(data.renderNode);
                  } else {
                    throw new Error('fa-flipper accepts only two child elements; more than two have been provided');
                  }

                  isolate.children.push(data.renderNode);
                },
                // TODO: support removing children
                function(childScopeId) {
                  throw new Error('unimplemented: fa-flipper does not support removing children');
                }
              );
            },
            post: function (scope, element, attrs) {
              var isolate = $famousDecorator.ensureIsolate(scope);
              transclude(scope, function (clone) {
                element.find('div').append(clone);
              });
              $famousDecorator.registerChild(scope, element, isolate);
            }
          };
        }
      };
    }
  ]);<commit_msg>feat: support removing children from fa-flipper<commit_after>/**
 * @ngdoc directive
 * @name faFlipper
 * @module famous.angular
 * @restrict EA
 * @description
 * This directive will create a Famo.us Flipper containing the
 * specified front and back elements. The provided `options` object
 * will pass directly through to the Famo.us Flipper's
 * constructor.  See [https://famo.us/docs/views/Flipper]
 *
 * @usage
 * ```html
 * <fa-flipper fa-options="scopeOptionsObject">
 *   <!-- two render nodes -->
 * </fa-flipper>
 * ```
 *@example
 * A Famous Flipper has a `.flip()` method that toggles a rotation between front and back sides.
 * In the example below, when an `fa-surface` is clicked, it calls the function `flipIt`.
 *
 * This function attempts a DOM lookup for an isolate of an `fa-flipper` element, and calls the `.flip()` function of `fa-flipper`.
 *
 *```html
 * <fa-flipper>
 *    <fa-surface fa-background-color="'yellow'" fa-click="flipIt()"></fa-surface>
 *    <fa-surface fa-background-color="'red'" fa-click="flipIt()"></fa-surface>
 * </fa-flipper>
 *```
 *```javascript
 * $scope.flipIt = function() {
 *    $famous.find('fa-flipper')[0].flip();
 * };
 *```
 */

angular.module('famous.angular')
  .directive('faFlipper', ["$famous", "$famousDecorator",
    function ($famous, $famousDecorator) {
      return {
        template: '

',
        restrict: 'E',
        transclude: true,
        scope: true,
        compile: function (tElem, tAttrs, transclude) {
          return {
            pre: function (scope, element, attrs) {
              var isolate = $famousDecorator.ensureIsolate(scope);
              var Flipper = $famous["famous/views/Flipper"];

              //TODO:  $watch and update, or $parse and attr.$observe
              var options = scope.$eval(attrs.faOptions) || {};

              isolate.renderNode = new Flipper(options);
              isolate.children = [];

              isolate.flip = function (overrideOptions) {
                isolate.renderNode.flip(overrideOptions || scope.$eval(attrs.faOptions));
              };

              $famousDecorator.sequenceWith(
                scope,
                function(data) {
                  //TODO:  support fa-index + sorting children instead of just a stack
                  var _childCount = isolate.children.length;
                  if (_childCount === 0) {
                    isolate.renderNode.setFront(data.renderNode);
                  } else if (_childCount === 1) {
                    isolate.renderNode.setBack(data.renderNode);
                  } else {
                    throw new Error('fa-flipper accepts only two child elements; more than two have been provided');
                  }

                  isolate.children.push(data.renderNode);
                },
                function(childScopeId) {
                  //TODO:  support fa-index + sorting children and removing
                  //       the child at the proper index instead of just popping off a stack

                  //Since children should handle hiding themselves, all we need to do is
                  //update our children array
                  isolate.children.splice(isolate.children.length - 1, 1);
                }
              );
            },
            post: function (scope, element, attrs) {
              var isolate = $famousDecorator.ensureIsolate(scope);
              transclude(scope, function (clone) {
                element.find('div').append(clone);
              });
              $famousDecorator.registerChild(scope, element, isolate);
            }
          };
        }
      };
    }
  ]);<|endoftext|>


<commit_before>db.user4
  .find({ title: 'BOT', 'count.winH': { $gt: 50 } })
  .sort({ createdAt: -1 })
  .limit(500)
  .forEach(user => {
    agg = db.game5
      .aggregate([
        { $match: { us: user._id } },
        { $project: { us: 1, wid: 1, t: 1 } },
        {
          $addFields: {
            sus: {
              $and: [
                { $eq: ['$wid', user._id] },
                { $lt: ['$t', 15] },
                { $or: [{ $eq: [{ $first: '$us' }, ''] }, { $eq: [{ $size: '$us' }, 1] }] },
              ],
            },
            players: { $or: [{ $eq: [{ $first: '$us' }, ''] }, { $eq: [{ $size: '$us' }, 1] }] },
          },
        },
        { $sort: { ca: -1 } },
        { $limit: 1000 },
        {
          $group: {
            _id: '$sus',
            nb: { $sum: 1 },
          },
        },
      ])
      .toArray();
    oks = agg.filter(o => !o._id)[0];
    oks = oks ? oks.nb : 0;
    kos = agg.filter(o => o._id)[0];
    kos = kos ? kos.nb : 0;
    ratio = kos / (oks + kos);
    if (ratio > 0.3) {
      print(`https://lichess.org/@/${user.username} ${user.enabled} ${kos} / ${oks + kos}`);
    }
  });
<commit_msg>remove sys script<commit_after><|endoftext|>


<commit_before>const Joi = require(`joi`)

export const gatsbyConfigSchema = Joi.object().keys({
  __experimentalThemes: Joi.array(),
  polyfill: Joi.boolean(),
  siteMetadata: Joi.object(),
  pathPrefix: Joi.string(),
  mapping: Joi.object(),
  plugins: Joi.array(),
  proxy: Joi.object().keys({
    prefix: Joi.string().required(),
    url: Joi.string().required(),
  }),
  developMiddleware: Joi.func(),
})

export const pageSchema = Joi.object()
  .keys({
    path: Joi.string().required(),
    matchPath: Joi.string(),
    component: Joi.string().required(),
    componentChunkName: Joi.string().required(),
    context: Joi.object(),
    pluginCreator___NODE: Joi.string(),
    pluginCreatorId: Joi.string(),
  })
  .unknown()

export const nodeSchema = Joi.object()
  .keys({
    id: Joi.string().required(),
    children: Joi.array().items(Joi.string(), Joi.object().forbidden()),
    parent: Joi.string().allow(null),
    fields: Joi.object(),
    internal: Joi.object()
      .keys({
        contentDigest: Joi.string().required(),
        mediaType: Joi.string(),
        type: Joi.string().required(),
        owner: Joi.string().required(),
        fieldOwners: Joi.array(),
        content: Joi.string().allow(``),
        description: Joi.string(),
        ignoreType: Joi.boolean(),
      })
      .unknown({
        allow: false,
      }), // Don't allow non-standard fields
  })
  .unknown()
<commit_msg>Correct fieldOwners in joi schema (#11419)<commit_after>const Joi = require(`joi`)

export const gatsbyConfigSchema = Joi.object().keys({
  __experimentalThemes: Joi.array(),
  polyfill: Joi.boolean(),
  siteMetadata: Joi.object(),
  pathPrefix: Joi.string(),
  mapping: Joi.object(),
  plugins: Joi.array(),
  proxy: Joi.object().keys({
    prefix: Joi.string().required(),
    url: Joi.string().required(),
  }),
  developMiddleware: Joi.func(),
})

export const pageSchema = Joi.object()
  .keys({
    path: Joi.string().required(),
    matchPath: Joi.string(),
    component: Joi.string().required(),
    componentChunkName: Joi.string().required(),
    context: Joi.object(),
    pluginCreator___NODE: Joi.string(),
    pluginCreatorId: Joi.string(),
  })
  .unknown()

export const nodeSchema = Joi.object()
  .keys({
    id: Joi.string().required(),
    children: Joi.array().items(Joi.string(), Joi.object().forbidden()),
    parent: Joi.string().allow(null),
    fields: Joi.object(),
    internal: Joi.object()
      .keys({
        contentDigest: Joi.string().required(),
        mediaType: Joi.string(),
        type: Joi.string().required(),
        owner: Joi.string().required(),
        fieldOwners: Joi.object(),
        content: Joi.string().allow(``),
        description: Joi.string(),
        ignoreType: Joi.boolean(),
      })
      .unknown({
        allow: false,
      }), // Don't allow non-standard fields
  })
  .unknown()
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Route.extend({
  beforeModel() {
    debugger
  },
  actions: {
    selectTab(tab) {
      this.transitionTo(tab.routeName);
    },
  }
});
<commit_msg>Removed trace<commit_after>import Ember from 'ember';

export default Ember.Route.extend({
  actions: {
    selectTab(tab) {
      this.transitionTo(tab.routeName);
    },
  }
});
<|endoftext|>


<commit_before>var cell;

function setup(){
  createCanvas(windowWidth, windowHeight-45);

  cell = new Cell(width/2, height/2);
}

function draw(){
  background(50);

  cell.draw();
  cell.tick();
}

class CellCycle {
  static get INTERPHASE()  { return 0; }
  // static get PREPROPHASE() { return 1; } // Only in Plant Cells
  static get PROPHASE()  { return 1; }
  static get PROMETAPHASE()  { return 2; }
  static get ANAPHASE()  { return 3; }
  static get TELOPHASE()  { return 4; }
  static get CYTOKINESIS()  { return 5; }

  static get STATES(){
    return [
      {id: CellCycle.INTERPHASE, name: 'Interphase', next: CellCycle.PROPHASE},
      {id: CellCycle.PROPHASE, name: 'Prophase', next: CellCycle.PROMETAPHASE},
      {id: CellCycle.PROMETAPHASE, name: 'Prometaphase', next: CellCycle.ANAPHASE},
      {id: CellCycle.ANAPHASE, name: 'Anaphase', next: CellCycle.TELOPHASE},
      {id: CellCycle.TELOPHASE, name: 'Telophase', next: CellCycle.CYTOKINESIS},
      {id: CellCycle.CYTOKINESIS, name: 'Cytokinesis', next: CellCycle.INTERPHASE}
    ];
  }
}

class Cell {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.size = 150;
    this.state = CellCycle.INTERPHASE;
    this.initMembrane();
    this.fluidity = 0.05;
    this.lifeCount = 0;
    this.durationPerCycle = 200;
  }

  get x(){ return this.pos.x; }
  get y(){ return this.pos.y; }

  initMembrane(){
    this.membrane = [];
    const numSegments = 30;
    let thetaStep = TWO_PI / numSegments;
    let tmpSegment;
    let tmpPoint = createVector(0, 0);

    for (var i = 0; i < numSegments; i++){
      tmpPoint.x = this.x + cos(thetaStep*i) * this.size/2;
      tmpPoint.y = this.y + sin(thetaStep*i) * this.size/2;
      tmpSegment = new CellMembraneSegment(tmpPoint.x, tmpPoint.y);
      this.membrane.push(tmpSegment);
    }
  }

  tick(){
    this.lifeCount++;
    if (this.lifeCount % this.durationPerCycle == 0){
      this.triggerLifecyle();
    }

    for (var i = 0; i < this.membrane.length; i++){
      this.membrane[i].tick();
    }
  }

  draw(){
    fill(160, 180, 250);
    stroke(40,100,120);
    strokeWeight(5);
    beginShape();
    for (var i = 0; i < this.membrane.length; i++){
      vertex(this.membrane[i].x, this.membrane[i].y);
    }
    endShape(CLOSE);

    this.drawNucleus();
  }

  drawNucleus(){
    let alpha = 255 * this.nucleusDefinition();
    fill(220, 190, 160, alpha);
    stroke(120,90,50, alpha);
    strokeWeight(5);
    ellipse(this.x, this.y, 50, 50);
  }

  nucleusDefinition(){
    let offsetInCycle = this.lifeCount - this.phaseStartedAt;
    switch (this.state) {
      case CellCycle.INTERPHASE: return 1.0;
      case CellCycle.PROPHASE: return 1.0;
      case CellCycle.PROMETAPHASE: return 1 - offsetInCycle / this.durationPerCycle;
      case CellCycle.ANAPHASE: return 0;
      case CellCycle.TELOPHASE: return offsetInCycle / this.durationPerCycle;
      case CellCycle.CYTOKINESIS: return 1;
    }
  }

  triggerLifecyle(){
    this.state = CellCycle.STATES[this.state].next;
    console.log("Starting: " + CellCycle.STATES[this.state].name);
    this.phaseStartedAt = this.lifeCount;
  }
}

class CellMembraneSegment {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.accel = createVector(0, 0);
    this.speed = createVector(0, 0);
  }

  get x(){ return this.pos.x; }
  get y(){ return this.pos.y; }

  tick(){
    this.accel.x = cell.fluidity * (randomGaussian(0, 1)) + (0 - this.speed.x) / 2;
    this.accel.y = cell.fluidity * (randomGaussian(0, 1)) + (0 - this.speed.y) / 2;

    this.speed.x += this.accel.x;
    this.speed.y += this.accel.y;

    this.pos.x += this.speed.x;
    this.pos.y += this.speed.y;
  }
}
<commit_msg>p5js/mitosis - Refactor CellCycle behavior to have function per phase<commit_after>var cell;

function setup(){
  createCanvas(windowWidth, windowHeight-45);

  cell = new Cell(width/2, height/2);
}

function draw(){
  background(50);

  cell.draw();
  cell.tick();
}

class CellCycle {
  static get INTERPHASE()  { return 0; }
  // static get PREPROPHASE() { return 1; } // Only in Plant Cells
  static get PROPHASE()  { return 1; }
  static get PROMETAPHASE()  { return 2; }
  static get ANAPHASE()  { return 3; }
  static get TELOPHASE()  { return 4; }
  static get CYTOKINESIS()  { return 5; }

  static get STATES(){
    return [
      {id: CellCycle.INTERPHASE, name: 'Interphase', next: CellCycle.PROPHASE},
      {id: CellCycle.PROPHASE, name: 'Prophase', next: CellCycle.PROMETAPHASE},
      {id: CellCycle.PROMETAPHASE, name: 'Prometaphase', next: CellCycle.ANAPHASE},
      {id: CellCycle.ANAPHASE, name: 'Anaphase', next: CellCycle.TELOPHASE},
      {id: CellCycle.TELOPHASE, name: 'Telophase', next: CellCycle.CYTOKINESIS},
      {id: CellCycle.CYTOKINESIS, name: 'Cytokinesis', next: CellCycle.INTERPHASE}
    ];
  }
}

class Cell {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.size = 150;
    this.state = CellCycle.INTERPHASE;
    this.initMembrane();
    this.fluidity = 0.05;

    this.lifeCount = 0;
    this.phaseStartedAt = 0;
    this.durationPerCycle = 200;
    this.offsetInCycle = 0;

    this.nucleusDefinition = 1;
  }

  get x(){ return this.pos.x; }
  get y(){ return this.pos.y; }

  initMembrane(){
    this.membrane = [];
    const numSegments = 30;
    let thetaStep = TWO_PI / numSegments;
    let tmpSegment;
    let tmpPoint = createVector(0, 0);

    for (var i = 0; i < numSegments; i++){
      tmpPoint.x = this.x + cos(thetaStep*i) * this.size/2;
      tmpPoint.y = this.y + sin(thetaStep*i) * this.size/2;
      tmpSegment = new CellMembraneSegment(tmpPoint.x, tmpPoint.y);
      this.membrane.push(tmpSegment);
    }
  }

  tick(){
    this.lifeCount++;
    if (this.lifeCount % this.durationPerCycle == 0){
      this.triggerLifecyle();
    }
    this.offsetInCycle = this.lifeCount - this.phaseStartedAt;

    for (var i = 0; i < this.membrane.length; i++){
      this.membrane[i].tick();
    }

    // This could probably be called based on Cycle Name
    switch (this.state) {
      case CellCycle.INTERPHASE:
        this.tickInterphase();
        break;
      case CellCycle.PROPHASE:
        this.tickProphase();
        break;
      case CellCycle.PROMETAPHASE:
        this.tickPrometaphase();
        break;
      case CellCycle.ANAPHASE:
        this.tickAnaphase();
        break;
      case CellCycle.TELOPHASE:
        this.tickTelophase();
        break;
      case CellCycle.CYTOKINESIS:
        this.tickCytokinesis();
        break;
    }
  }

  draw(){
    fill(160, 180, 250);
    stroke(40,100,120);
    strokeWeight(5);
    beginShape();
    for (var i = 0; i < this.membrane.length; i++){
      vertex(this.membrane[i].x, this.membrane[i].y);
    }
    endShape(CLOSE);

    this.drawNucleus();
  }

  drawNucleus(){
    let alpha = 255 * this.nucleusDefinition;
    fill(220, 190, 160, alpha);
    stroke(120,90,50, alpha);
    strokeWeight(5);
    ellipse(this.x, this.y, 50, 50);
  }

  tickInterphase(){
    // Do Interphase stuff here
  }

  tickProphase(){
    // Do Prophase stuff here
  }

  tickPrometaphase(){
    // Do Prometaphase stuff here
    this.nucleusDefinition = 1 - this.offsetInCycle / this.durationPerCycle;
  }
  tickAnaphase(){
    // Do Anaphase stuff here
  }

  tickTelophase(){
    // Do Prophase stuff here
    this.nucleusDefinition = this.offsetInCycle / this.durationPerCycle;
  }

  tickCytokinesis(){
    // Do Prophase stuff here
  }

  triggerLifecyle(){
    this.state = CellCycle.STATES[this.state].next;
    console.log("Starting: " + CellCycle.STATES[this.state].name);
    this.phaseStartedAt = this.lifeCount;
  }
}

class CellMembraneSegment {
  constructor(x, y) {
    this.pos = createVector(x, y);
    this.accel = createVector(0, 0);
    this.speed = createVector(0, 0);
  }

  get x(){ return this.pos.x; }
  get y(){ return this.pos.y; }

  tick(){
    this.accel.x = cell.fluidity * (randomGaussian(0, 1)) + (0 - this.speed.x) / 2;
    this.accel.y = cell.fluidity * (randomGaussian(0, 1)) + (0 - this.speed.y) / 2;

    this.speed.x += this.accel.x;
    this.speed.y += this.accel.y;

    this.pos.x += this.speed.x;
    this.pos.y += this.speed.y;
  }
}
<|endoftext|>


<commit_before>'use strict';

const m = require('mochainon');
const fs = require('fs');
const path = require('path');
const angular = require('angular');
require('angular-mocks');

describe('Browser: SVGIcon', function() {

  beforeEach(angular.mock.module(
    require('../../../lib/gui/components/svg-icon/svg-icon')
  ));

  describe('svgIcon', function() {

    let $compile;
    let $rootScope;

    beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_, $templateCache) {
      $compile = _$compile_;
      $rootScope = _$rootScope_;

      // Workaround `Unexpected request: GET template.html. No more request expected` error.
      // See http://stackoverflow.com/a/29437480/1641422
      const templatePath = './components/svg-icon/templates/svg-icon.tpl.html';
      const template = fs.readFileSync(path.resolve('lib', 'gui', templatePath), {
        encoding: 'utf8'
      });
      $templateCache.put(templatePath, template);

    }));

    it('should inline the svg contents in the element', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      let iconContents = fs.readFileSync(path.join(__dirname, '../../../lib/gui/assets/etcher.svg'), {
        encoding: 'utf8'
      }).split('\n');

      // Injecting XML as HTML causes the XML header to be commented out.
      // Modify here to ease assertions later on.
      iconContents[0] = '<!--' + iconContents[0].slice(1, iconContents[0].length - 1) + '-->';
      iconContents = iconContents.join('\n');

      const element = $compile(`<svg-icon path="${icon}">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.html()).to.equal(iconContents);
    });

    it('should default the size to 40x40 pixels', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      const element = $compile(`<svg-icon path="${icon}">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.css('width')).to.equal('40px');
      m.chai.expect(element.css('height')).to.equal('40px');
    });

    it('should be able to set a custom height', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      const element = $compile(`<svg-icon path="${icon}" width="20px">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.css('width')).to.equal('20px');
      m.chai.expect(element.css('height')).to.equal('40px');
    });

    it('should be able to set a custom height', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      const element = $compile(`<svg-icon path="${icon}" height="20px">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.css('width')).to.equal('40px');
      m.chai.expect(element.css('height')).to.equal('20px');
    });

  });

});
<commit_msg>Consider carriage returns when asserting SVG contents equality (#398)<commit_after>'use strict';

const m = require('mochainon');
const fs = require('fs');
const path = require('path');
const angular = require('angular');
require('angular-mocks');

describe('Browser: SVGIcon', function() {

  beforeEach(angular.mock.module(
    require('../../../lib/gui/components/svg-icon/svg-icon')
  ));

  describe('svgIcon', function() {

    let $compile;
    let $rootScope;

    beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_, $templateCache) {
      $compile = _$compile_;
      $rootScope = _$rootScope_;

      // Workaround `Unexpected request: GET template.html. No more request expected` error.
      // See http://stackoverflow.com/a/29437480/1641422
      const templatePath = './components/svg-icon/templates/svg-icon.tpl.html';
      const template = fs.readFileSync(path.resolve('lib', 'gui', templatePath), {
        encoding: 'utf8'
      });
      $templateCache.put(templatePath, template);

    }));

    it('should inline the svg contents in the element', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      let iconContents = fs.readFileSync(path.join(__dirname, '../../../lib/gui/assets/etcher.svg'), {
        encoding: 'utf8'
      }).split(/\r?\n/);

      // Injecting XML as HTML causes the XML header to be commented out.
      // Modify here to ease assertions later on.
      iconContents[0] = '<!--' + iconContents[0].slice(1, iconContents[0].length - 1) + '-->';
      iconContents = iconContents.join('\n');

      const element = $compile(`<svg-icon path="${icon}">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.html()).to.equal(iconContents);
    });

    it('should default the size to 40x40 pixels', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      const element = $compile(`<svg-icon path="${icon}">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.css('width')).to.equal('40px');
      m.chai.expect(element.css('height')).to.equal('40px');
    });

    it('should be able to set a custom height', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      const element = $compile(`<svg-icon path="${icon}" width="20px">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.css('width')).to.equal('20px');
      m.chai.expect(element.css('height')).to.equal('40px');
    });

    it('should be able to set a custom height', function() {
      const icon = '../../../../../lib/gui/assets/etcher.svg';
      const element = $compile(`<svg-icon path="${icon}" height="20px">Resin.io</svg-icon>`)($rootScope);
      $rootScope.$digest();
      m.chai.expect(element.css('width')).to.equal('40px');
      m.chai.expect(element.css('height')).to.equal('20px');
    });

  });

});
<|endoftext|>


<commit_before>aahQuery = "aah"
aahResultName = "aah"
aahResultDescription = /Allocation adulte handicapé/
aahDefinitionPeriod = /month/
impotRevenuBaremeQuery = "impot_revenu.bareme"
impotRevenuBaremeResultDescription = "Tranches de l'IR"
smicQuery = "smic_h_b"
smicResultTitle = "/smic_h_b/"
smicResultDescription = /SMIC horaire brut/
bouclierFiscalQuery = "bouclier_fiscal.taux"
bouclierFiscalDescription = "Taux d'imposition maximal"
salaireBaseQuery = "salaire base"
salaireBaseResultName = "salaire_de_base"
enumVariableQuery = "statut marital"
<commit_msg>Fix test on ParameterComponent.title<commit_after>aahQuery = "aah"
aahResultName = "aah"
aahResultDescription = /Allocation adulte handicapé/
aahDefinitionPeriod = /month/
impotRevenuBaremeQuery = "impot_revenu.bareme"
impotRevenuBaremeResultDescription = "Tranches de l'IR"
smicQuery = "smic_h_b"
smicResultTitle = "cotsoc.gen.smic_h_b"
smicResultDescription = /SMIC horaire brut/
bouclierFiscalQuery = "bouclier_fiscal.taux"
bouclierFiscalDescription = "Taux d'imposition maximal"
salaireBaseQuery = "salaire base"
salaireBaseResultName = "salaire_de_base"
enumVariableQuery = "statut marital"
<|endoftext|>