<commit_before>/* Module to handle files */
var FileSystem = (function (my, SocketAPI, HyperHost) {
    'use strict';

    function getUniqueId() {
        return Math.random().toString();
    }

    function setLocalStorage() {
        try {
            if (localStorage) {
                localStorage.setItem('tethysTree', JSON.stringify(fileTree.map(function(e){
                    return {content: e.content, name:e.name, isRemoved:e.isRemoved, fileId:e.fileId};
                })));
            }
        }catch(err){
            localStorage.setItem('tethysTree', "");
        }
    }

    function getLocalStorage() {
        if (localStorage) {
            var store = localStorage.getItem('tethysTree');
            if (!!store){
                fileTree = JSON.parse(store);
                my.init();
            }
        }
    }

    my.saveLocal = function(){
        setLocalStorage();
    }

    var fileTree = [
        {
            name: "index.html",
            fileId: "welcome",
            content: `

    <link rel="stylesheet" href="style.css">

Welcome to MULTIHACK!

Collaborate in real-time.

Syntax highlighting for every web language.

Voice chat with up to 10 people! (WebRTC only)

Instantly deploy your website via HyperHost (WebRTC only)

Save your project for working offline.

MULTIHACK is the ONLY multi-file, multi-user code editor on the web.

`
        },
        {
            name: "style.css",
            fileId: "welcome2",
            content: `body {
    background: lightgray;
    font-family: Arial;
}

h1 {
    color: darkgreen;
}`
        }
    ];

    SocketAPI.onAllCode = function (newFileTree) {
        fileTree = newFileTree;
        setLocalStorage(); //Save to localstorage on joining a new room
        my.init();
    }

    var rootTreeElement = document.getElementById('tree');
    var rootPlusElement;

    my.editor = null;
    my.workingFile = null;

    /* Renders the tree's DOM entirely (expensive) */
    function renderFullTree(treeElement, nodeList, parentId, parent) {
        while (treeElement.firstChild) { //Empty the tree
            treeElement.removeChild(treeElement.firstChild);
        }
        for (var i = 0; i < nodeList.length; i++) { //Iterate childre
            var file = nodeList[i];
            if (file.isRemoved) continue;
            file.fileId = file.fileId || getUniqueId();
            file.parentElement = (parent && parent.el) || rootTreeElement;

            var li = document.createElement('li');

            if (file.nodes) {
                li.innerHTML = '' + file.name + '<input checked type="checkbox" id="' + file.fileId + '" />'

                var ol = document.createElement('ol');
                file.el = ol;
                file.realEl = li;
                renderFullTree(ol, file.nodes, file.fileId, file); //Recursive call
                li.appendChild(ol);
            } else {
                li.className = "file";
                li.innerHTML = '' + file.name + '';
                file.el = li;
                file.realEl = li;
            }
            treeElement.appendChild(li);
        }
        var plusEl = document.createElement('li');

        if (parentId !== 'root') {
            plusEl.innerHTML = '+-';
        } else {
            plusEl.innerHTML = '+';
        }
        if (parent) {
            parent.plusElement = plusEl;
        } else {
            rootPlusElement = plusEl;
        }
        treeElement.appendChild(plusEl);
    }

    /* Gets a node from the tree */
    function getNode(fileId, nodeList) {
        for (var i = 0; i < nodeList.length; i++) { //Iterate children
            if (nodeList[i].isRemoved) continue;
            if (nodeList[i].fileId == fileId || (fileId == "*" && !nodeList[i].nodes)) {
                return nodeList[i];
            }
            if (nodeList[i].nodes) {
                var recursiveResult = getNode(fileId, nodeList[i].nodes);
                if (recursiveResult) {
                    return recursiveResult;
                }
            }
        }
        return undefined;
    }

    /* Deletes a node from the tree */
    function deleteNode(child) {
        child.parentElement.removeChild(child.realEl);
        child.isRemoved = true;
        setLocalStorage();
    }

    /* Opens any file (first in tree traversal) */
    function openAny() {
        my.workingFile = getNode("*", fileTree);
        if (!my.workingFile) {
            my.mkfile('root', 'index.html');
        }
        my.workingFile = getNode("*", fileTree);
        document.getElementById("workingFile").innerHTML = my.workingFile.name;
        setWorkspaceContent(my.workingFile);
        document.getElementById(my.workingFile.fileId).style.color = "";
    }

    function setWorkspaceContent(fileNode) {
        var ext = fileNode.name.split(".");
        ext = ext[ext.length - 1];

        if (["png", "jpg", "jpeg", , "jpeg2000", "tif", "tiff", "gif", "bmp"].indexOf(ext) !== -1) {
            document.querySelector(".image-wrapper").style.display = "";
            document.querySelector(".image-wrapper img").src = fileNode.content;
        } else {
            document.querySelector(".image-wrapper").style.display = "none";
            my.editor.getDoc().setValue(fileNode.content || "");
            my.editor.setOption("mode", syntaxMapping(fileNode.name));
        }
    }

    /* Gets a file with the specified id */
    my.getFile = function (fileId) {
        return getNode(fileId, fileTree);
    }

    /* Gets the file tree */
    my.getFileTree = function () {
        return fileTree;
    }

    /* Deletes the working file */
    my.delCurrent = function () {
        my.del(my.workingFile.fileId);
    }

    /* Deletes a file */
    my.del = function (fileId) {
        deleteNode(getNode(fileId, fileTree));
        SocketAPI.deleteFile(fileId);
        openAny(); //TODO only open new if working file was inside
    }

    /* Adds a child to another node in the tree's DOM */
    function addChild(parent, child, childElement) {
        (parent.nodes || parent).push(child);
        (parent.el || rootTreeElement).appendChild(childElement);
        var plusEl = (parent.el || rootTreeElement).removeChild((parent.plusElement || rootPlusElement));
        (parent.el || rootTreeElement).appendChild(plusEl);
        child.parentElement = (parent.el || rootTreeElement);
    }

    /* Makes a new directory in the a specific folder (fileId is optional) */
    my.mkdir = function (parentId, name, originalFileId) {
        var fileId = originalFileId || getUniqueId();
        var file = {
            name: name,
            fileId: fileId,
            nodes: []
        }

        var li = document.createElement('li');
        li.innerHTML = '' + file.name + '<input checked type="checkbox" id="' + file.fileId + '" />';
        var ol = document.createElement('ol');
        li.appendChild(ol);
        var plusEl = document.createElement('li');
        if (file.fileId !== 'root') {
            plusEl.innerHTML = '+-';
        } else {
            plusEl.innerHTML = '+';
        }
        ol.appendChild(plusEl);
        file.plusElement = plusEl;
        file.el = ol;
        file.realEl = li;

        if (parentId === 'root') {
            addChild(fileTree, file, li);
        } else {
            addChild(getNode(parentId, fileTree), file, li);
        }

        if (!originalFileId) {
            SocketAPI.addFile({
                parentId: parentId,
                name: file.name,
                fileId: file.fileId,
                type: 'folder'
            });
        }
        setLocalStorage();

        return fileId;
    }

    /* Makes a new file in a specific folder (fileId is optional) */
    my.mkfile = function (parentId, name, originalFileId) {
        var fileId = originalFileId || getUniqueId();
        var file = {
            name: name,
            fileId: fileId,
            content: ""
        }

        var li = document.createElement('li');
        li.className = "file";
        li.innerHTML = '' + file.name + '';
        file.el = li;
        file.realEl = li;

        if (parentId === 'root') {
            addChild(fileTree, file, li);
        } else {
            addChild(getNode(parentId, fileTree), file, li);
        }

        if (!originalFileId) {
            SocketAPI.addFile({
                parentId: parentId,
                name: file.name,
                fileId: file.fileId,
                type: 'file'
            });
        }
        setLocalStorage();

        return fileId;
    }

    /* Opens a file in the editor */
    my.open = function (fileId) {
        my.workingFile = getNode(fileId, fileTree);
        document.getElementById("workingFile").innerHTML = my.workingFile.name;
        setWorkspaceContent(my.workingFile);
        document.getElementById(fileId).style.color = "";
    }

    /* Sets the theme ('material', 'atom') */
    var currentTheme;
    my.setTheme = function (themeName) {
        document.querySelector('html').className = "theme-" + themeName;
        my.editor.setOption("theme", themeName);
        currentTheme = themeName;
    }

    /* Maps extensions to CodeMirror modes */
    function syntaxMapping(fileName) {
        var ext = fileName.split(".");
        ext = ext[ext.length - 1];
        var mapping = {
            "js": "javascript",
            "ts": "javascript",
            "css": "css",
            "sass": "css",
            "less": "css",
            "html": "htmlmixed",
            "xml": "xml",
            "php": "application/x-httpd-php"
        }
        return mapping[ext] || null;
    }

    /* Update part of a file */
    my.softUpdateFile = function (fileId, change) {
        //TODO: Only update difference
        alert("not implemented softUpdateFile");
    }

    /* Initialize the editor and tree */
    my.init = function (firstTime) {
        if (firstTime) getLocalStorage();
        var textArea = document.getElementById("editor");
        var options = {
            mode: "javascript",
            lineNumbers: true,
            theme: currentTheme || "material",
            tabSize: 4,
            indentUnit: 4,
            lineWrapping: true,
            styleActiveLine: true
        };
        my.editor = CodeMirror.fromTextArea(textArea, options);

        var computerActions = ["setValue", "replaceRange", "+move"];

        var lastCursor;
        my.editor.on('beforeChange', function (cm, change) {
            if (computerActions.indexOf(change.origin) !== -1) { //If user is not the one changing
                lastCursor = my.editor.getCursor();
            } else {
                lastCursor = undefined;
            }
        });

        my.editor.on('change', function (cm, change) {
            if (computerActions.indexOf(change.origin) === -1) { //Make sure change is from user input
                my.workingFile.content = cm.getValue();
                SocketAPI.changeFile(my.workingFile.fileId, my.workingFile.content); //TODO: Only send changes
            } else if (lastCursor) {
                my.editor.setCursor(lastCursor);
            }
            setLocalStorage();
        });

        renderFullTree(rootTreeElement, fileTree, 'root', null);

        openAny();
    }

    /* Creates a zip from the tree and attempts to download it */
    my.saveAsZip = function () {
        try {
            var isFileSaverSupported = !!new Blob;

            var zip = new JSZip();
            zipTree(zip, "tethysProject", fileTree);

            zip.generateAsync({
                type: "blob"
            }).then(function (content) {
                saveAs(content, "tethysProject.zip");
            });
        } catch (e) {
            Modal.open("general-alert", {
                msg: "Your browser does not support this!"
            });
        }
    }

    /* Creates a file from an HTML5 File object */
    my.loadFile = function (fileObject, parentId) {
        var ext = fileObject.name.split(".");
        ext = ext[ext.length - 1];
        var isImage = ["png", "jpg", "jpeg", , "jpeg2000", "tif", "tiff", "gif", "bmp"].indexOf(ext) !== -1;

        var reader = new FileReader();
        reader.addEventListener('loadend', function () {
            var fileId = my.mkfile(parentId, fileObject.name);
            getNode(fileId, fileTree).content = reader.result;
            my.open(fileId);
            SocketAPI.changeFile(fileId, reader.result); //Signal new content
            setLocalStorage();
        });

        if (isImage) {
            reader.readAsDataURL(fileObject);
        } else {
            reader.readAsText(fileObject);
        }
    }

    /* Create a .zip file from the tree */
    function zipTree(zip, path, nodeList) {
        for (var i = 0; i < nodeList.length; i++) { //Iterate children
            if (nodeList[i].isRemoved) continue;

            if (nodeList[i].nodes) {
                zipTree(zip, path + "/" + nodeList[i].name, nodeList[i].nodes);
            } else {
                zip.file(path + "/" + nodeList[i].name, nodeList[i].content);
            }
        }
    }

    /* Fires when a file is changed by a peer */
    SocketAPI.onChangeFile = function (fileId, change) {
        if (fileId == my.workingFile.fileId) {
            my.workingFile.content = change;
            setWorkspaceContent(my.workingFile); //TODO: Only send/receive changes
        } else {
            getNode(fileId, fileTree).content = change; //TODO: Only send/receive changes
            document.getElementById(fileId).style.color = "red";
        }
        setLocalStorage();
    }

    /* Fires when a file is added by a peer */
    SocketAPI.onAddFile = function (parentId, name, fileId, type) {
        if (type === 'file') {
            my.mkfile(parentId, name, fileId);
        } else if (type === 'folder') {
            my.mkdir(parentId, name, fileId);
        }
    }

    /* Fires when a file is deleted by a peer */
    SocketAPI.onDeleteFile = function (fileId) {
        deleteNode(getNode(fileId, fileTree));
        if (fileId == my.workingFile.fileId) {
            openAny();
        }
    }

    /* Deploys the tree to HyperHost */
    document.querySelector("#deploy").addEventListener('click', function (e) {
        HyperHost.handleTethys(fileTree);
    });

    /* Creates a file from a string */
    my.openString = function (code, type) {
        if (code) {
            if (!type) type = "js"
            var fileId = my.mkfile("root", "code." + type);
            getNode(fileId, fileTree).content = code;
            my.open(fileId);
        }
    }

    return my;
}({}, SocketAPI, HyperHost));<commit_msg>catch iOS safari private mode<commit_after>/* Module to handle files */
var FileSystem = (function (my, SocketAPI, HyperHost) {
    'use strict';

    function getUniqueId() {
        return Math.random().toString();
    }

    function setLocalStorage() {
        try {
            if (localStorage) {
                localStorage.setItem('tethysTree', JSON.stringify(fileTree.map(function(e){
                    return {content: e.content, name:e.name, isRemoved:e.isRemoved, fileId:e.fileId};
                })));
            }
        }catch(err){
            try {
                localStorage.setItem('tethysTree', "");
            }catch(err){
                //Catch private mode
            }
        }
    }

    function getLocalStorage() {
        if (localStorage) {
            var store = localStorage.getItem('tethysTree');
            if (!!store){
                fileTree = JSON.parse(store);
                my.init();
            }
        }
    }

    my.saveLocal = function(){
        setLocalStorage();
    }

    var fileTree = [
        {
            name: "index.html",
            fileId: "welcome",
            content: `

    <link rel="stylesheet" href="style.css">

Welcome to MULTIHACK!

Collaborate in real-time.

Syntax highlighting for every web language.

Voice chat with up to 10 people! (WebRTC only)

Instantly deploy your website via HyperHost (WebRTC only)

Save your project for working offline.

MULTIHACK is the ONLY multi-file, multi-user code editor on the web.

`
        },
        {
            name: "style.css",
            fileId: "welcome2",
            content: `body {
    background: lightgray;
    font-family: Arial;
}

h1 {
    color: darkgreen;
}`
        }
    ];

    SocketAPI.onAllCode = function (newFileTree) {
        fileTree = newFileTree;
        setLocalStorage(); //Save to localstorage on joining a new room
        my.init();
    }

    var rootTreeElement = document.getElementById('tree');
    var rootPlusElement;

    my.editor = null;
    my.workingFile = null;

    /* Renders the tree's DOM entirely (expensive) */
    function renderFullTree(treeElement, nodeList, parentId, parent) {
        while (treeElement.firstChild) { //Empty the tree
            treeElement.removeChild(treeElement.firstChild);
        }
        for (var i = 0; i < nodeList.length; i++) { //Iterate childre
            var file = nodeList[i];
            if (file.isRemoved) continue;
            file.fileId = file.fileId || getUniqueId();
            file.parentElement = (parent && parent.el) || rootTreeElement;

            var li = document.createElement('li');

            if (file.nodes) {
                li.innerHTML = '' + file.name + '<input checked type="checkbox" id="' + file.fileId + '" />'

                var ol = document.createElement('ol');
                file.el = ol;
                file.realEl = li;
                renderFullTree(ol, file.nodes, file.fileId, file); //Recursive call
                li.appendChild(ol);
            } else {
                li.className = "file";
                li.innerHTML = '' + file.name + '';
                file.el = li;
                file.realEl = li;
            }
            treeElement.appendChild(li);
        }
        var plusEl = document.createElement('li');

        if (parentId !== 'root') {
            plusEl.innerHTML = '+-';
        } else {
            plusEl.innerHTML = '+';
        }
        if (parent) {
            parent.plusElement = plusEl;
        } else {
            rootPlusElement = plusEl;
        }
        treeElement.appendChild(plusEl);
    }

    /* Gets a node from the tree */
    function getNode(fileId, nodeList) {
        for (var i = 0; i < nodeList.length; i++) { //Iterate children
            if (nodeList[i].isRemoved) continue;
            if (nodeList[i].fileId == fileId || (fileId == "*" && !nodeList[i].nodes)) {
                return nodeList[i];
            }
            if (nodeList[i].nodes) {
                var recursiveResult = getNode(fileId, nodeList[i].nodes);
                if (recursiveResult) {
                    return recursiveResult;
                }
            }
        }
        return undefined;
    }

    /* Deletes a node from the tree */
    function deleteNode(child) {
        child.parentElement.removeChild(child.realEl);
        child.isRemoved = true;
        setLocalStorage();
    }

    /* Opens any file (first in tree traversal) */
    function openAny() {
        my.workingFile = getNode("*", fileTree);
        if (!my.workingFile) {
            my.mkfile('root', 'index.html');
        }
        my.workingFile = getNode("*", fileTree);
        document.getElementById("workingFile").innerHTML = my.workingFile.name;
        setWorkspaceContent(my.workingFile);
        document.getElementById(my.workingFile.fileId).style.color = "";
    }

    function setWorkspaceContent(fileNode) {
        var ext = fileNode.name.split(".");
        ext = ext[ext.length - 1];

        if (["png", "jpg", "jpeg", , "jpeg2000", "tif", "tiff", "gif", "bmp"].indexOf(ext) !== -1) {
            document.querySelector(".image-wrapper").style.display = "";
            document.querySelector(".image-wrapper img").src = fileNode.content;
        } else {
            document.querySelector(".image-wrapper").style.display = "none";
            my.editor.getDoc().setValue(fileNode.content || "");
            my.editor.setOption("mode", syntaxMapping(fileNode.name));
        }
    }

    /* Gets a file with the specified id */
    my.getFile = function (fileId) {
        return getNode(fileId, fileTree);
    }

    /* Gets the file tree */
    my.getFileTree = function () {
        return fileTree;
    }

    /* Deletes the working file */
    my.delCurrent = function () {
        my.del(my.workingFile.fileId);
    }

    /* Deletes a file */
    my.del = function (fileId) {
        deleteNode(getNode(fileId, fileTree));
        SocketAPI.deleteFile(fileId);
        openAny(); //TODO only open new if working file was inside
    }

    /* Adds a child to another node in the tree's DOM */
    function addChild(parent, child, childElement) {
        (parent.nodes || parent).push(child);
        (parent.el || rootTreeElement).appendChild(childElement);
        var plusEl = (parent.el || rootTreeElement).removeChild((parent.plusElement || rootPlusElement));
        (parent.el || rootTreeElement).appendChild(plusEl);
        child.parentElement = (parent.el || rootTreeElement);
    }

    /* Makes a new directory in the a specific folder (fileId is optional) */
    my.mkdir = function (parentId, name, originalFileId) {
        var fileId = originalFileId || getUniqueId();
        var file = {
            name: name,
            fileId: fileId,
            nodes: []
        }

        var li = document.createElement('li');
        li.innerHTML = '' + file.name + '<input checked type="checkbox" id="' + file.fileId + '" />';
        var ol = document.createElement('ol');
        li.appendChild(ol);
        var plusEl = document.createElement('li');
        if (file.fileId !== 'root') {
            plusEl.innerHTML = '+-';
        } else {
            plusEl.innerHTML = '+';
        }
        ol.appendChild(plusEl);
        file.plusElement = plusEl;
        file.el = ol;
        file.realEl = li;

        if (parentId === 'root') {
            addChild(fileTree, file, li);
        } else {
            addChild(getNode(parentId, fileTree), file, li);
        }

        if (!originalFileId) {
            SocketAPI.addFile({
                parentId: parentId,
                name: file.name,
                fileId: file.fileId,
                type: 'folder'
            });
        }
        setLocalStorage();

        return fileId;
    }

    /* Makes a new file in a specific folder (fileId is optional) */
    my.mkfile = function (parentId, name, originalFileId) {
        var fileId = originalFileId || getUniqueId();
        var file = {
            name: name,
            fileId: fileId,
            content: ""
        }

        var li = document.createElement('li');
        li.className = "file";
        li.innerHTML = '' + file.name + '';
        file.el = li;
        file.realEl = li;

        if (parentId === 'root') {
            addChild(fileTree, file, li);
        } else {
            addChild(getNode(parentId, fileTree), file, li);
        }

        if (!originalFileId) {
            SocketAPI.addFile({
                parentId: parentId,
                name: file.name,
                fileId: file.fileId,
                type: 'file'
            });
        }
        setLocalStorage();

        return fileId;
    }

    /* Opens a file in the editor */
    my.open = function (fileId) {
        my.workingFile = getNode(fileId, fileTree);
        document.getElementById("workingFile").innerHTML = my.workingFile.name;
        setWorkspaceContent(my.workingFile);
        document.getElementById(fileId).style.color = "";
    }

    /* Sets the theme ('material', 'atom') */
    var currentTheme;
    my.setTheme = function (themeName) {
        document.querySelector('html').className = "theme-" + themeName;
        my.editor.setOption("theme", themeName);
        currentTheme = themeName;
    }

    /* Maps extensions to CodeMirror modes */
    function syntaxMapping(fileName) {
        var ext = fileName.split(".");
        ext = ext[ext.length - 1];
        var mapping = {
            "js": "javascript",
            "ts": "javascript",
            "css": "css",
            "sass": "css",
            "less": "css",
            "html": "htmlmixed",
            "xml": "xml",
            "php": "application/x-httpd-php"
        }
        return mapping[ext] || null;
    }

    /* Update part of a file */
    my.softUpdateFile = function (fileId, change) {
        //TODO: Only update difference
        alert("not implemented softUpdateFile");
    }

    /* Initialize the editor and tree */
    my.init = function (firstTime) {
        if (firstTime) getLocalStorage();
        var textArea = document.getElementById("editor");
        var options = {
            mode: "javascript",
            lineNumbers: true,
            theme: currentTheme || "material",
            tabSize: 4,
            indentUnit: 4,
            lineWrapping: true,
            styleActiveLine: true
        };
        my.editor = CodeMirror.fromTextArea(textArea, options);

        var computerActions = ["setValue", "replaceRange", "+move"];

        var lastCursor;
        my.editor.on('beforeChange', function (cm, change) {
            if (computerActions.indexOf(change.origin) !== -1) { //If user is not the one changing
                lastCursor = my.editor.getCursor();
            } else {
                lastCursor = undefined;
            }
        });

        my.editor.on('change', function (cm, change) {
            if (computerActions.indexOf(change.origin) === -1) { //Make sure change is from user input
                my.workingFile.content = cm.getValue();
                SocketAPI.changeFile(my.workingFile.fileId, my.workingFile.content); //TODO: Only send changes
            } else if (lastCursor) {
                my.editor.setCursor(lastCursor);
            }
            setLocalStorage();
        });

        renderFullTree(rootTreeElement, fileTree, 'root', null);

        openAny();
    }

    /* Creates a zip from the tree and attempts to download it */
    my.saveAsZip = function () {
        try {
            var isFileSaverSupported = !!new Blob;

            var zip = new JSZip();
            zipTree(zip, "tethysProject", fileTree);

            zip.generateAsync({
                type: "blob"
            }).then(function (content) {
                saveAs(content, "tethysProject.zip");
            });
        } catch (e) {
            Modal.open("general-alert", {
                msg: "Your browser does not support this!"
            });
        }
    }

    /* Creates a file from an HTML5 File object */
    my.loadFile = function (fileObject, parentId) {
        var ext = fileObject.name.split(".");
        ext = ext[ext.length - 1];
        var isImage = ["png", "jpg", "jpeg", , "jpeg2000", "tif", "tiff", "gif", "bmp"].indexOf(ext) !== -1;

        var reader = new FileReader();
        reader.addEventListener('loadend', function () {
            var fileId = my.mkfile(parentId, fileObject.name);
            getNode(fileId, fileTree).content = reader.result;
            my.open(fileId);
            SocketAPI.changeFile(fileId, reader.result); //Signal new content
            setLocalStorage();
        });

        if (isImage) {
            reader.readAsDataURL(fileObject);
        } else {
            reader.readAsText(fileObject);
        }
    }

    /* Create a .zip file from the tree */
    function zipTree(zip, path, nodeList) {
        for (var i = 0; i < nodeList.length; i++) { //Iterate children
            if (nodeList[i].isRemoved) continue;

            if (nodeList[i].nodes) {
                zipTree(zip, path + "/" + nodeList[i].name, nodeList[i].nodes);
            } else {
                zip.file(path + "/" + nodeList[i].name, nodeList[i].content);
            }
        }
    }

    /* Fires when a file is changed by a peer */
    SocketAPI.onChangeFile = function (fileId, change) {
        if (fileId == my.workingFile.fileId) {
            my.workingFile.content = change;
            setWorkspaceContent(my.workingFile); //TODO: Only send/receive changes
        } else {
            getNode(fileId, fileTree).content = change; //TODO: Only send/receive changes
            document.getElementById(fileId).style.color = "red";
        }
        setLocalStorage();
    }

    /* Fires when a file is added by a peer */
    SocketAPI.onAddFile = function (parentId, name, fileId, type) {
        if (type === 'file') {
            my.mkfile(parentId, name, fileId);
        } else if (type === 'folder') {
            my.mkdir(parentId, name, fileId);
        }
    }

    /* Fires when a file is deleted by a peer */
    SocketAPI.onDeleteFile = function (fileId) {
        deleteNode(getNode(fileId, fileTree));
        if (fileId == my.workingFile.fileId) {
            openAny();
        }
    }

    /* Deploys the tree to HyperHost */
    document.querySelector("#deploy").addEventListener('click', function (e) {
        HyperHost.handleTethys(fileTree);
    });

    /* Creates a file from a string */
    my.openString = function (code, type) {
        if (code) {
            if (!type) type = "js"
            var fileId = my.mkfile("root", "code." + type);
            getNode(fileId, fileTree).content = code;
            my.open(fileId);
        }
    }

    return my;
}({}, SocketAPI, HyperHost));<|endoftext|>


<commit_before>import * as React from "react";
import PropTypes from "prop-types";
import debounce from "lodash.debounce";
import isEqual from "lodash.isequal";
import Input from "@crave/farmblocks-input-text";

import DropdownWrapper from "./styledComponents/DropdownWrapper";
import Menu from "./components/Menu";

class SearchField extends React.Component {
  state = {
    highlightedIndex: -1,
    focused: false,
    inputValue: "",
    items: [],
    selectedItem: null
  };

  debouncedOnSearchChange = debounce(
    this.props.onSearchChange,
    this.props.debounceDelay
  );

  setValueFromProps = () => {
    const { value, items } = this.props;
    const selectedItem = items && items.find(item => item.value === value);
    if (selectedItem) {
      this.setState({
        inputValue: selectedItem.label,
        selectedItem
      });
    }
  };

  componentDidMount = () => {
    const { value, items } = this.props;
    if (items) {
      this.setState({ items });
    }

    if (value) {
      this.setValueFromProps();
    }
  };

  componentDidUpdate = prevProps => {
    if (
      prevProps.debounceDelay !== this.props.debounceDelay ||
      prevProps.onSearchChange !== this.props.onSearchChange
    ) {
      this.debouncedOnSearchChange && this.debouncedOnSearchChange.cancel();
      this.debouncedOnSearchChange = debounce(
        this.props.onSearchChange,
        this.props.debounceDelay
      );
    }

    if (!isEqual(prevProps.items, this.props.items)) {
      this.setState({ items: this.props.items });
    }

    if (prevProps.value !== this.props.value) {
      this.setValueFromProps();
    }
  };

  componentWillUnmount = () => {
    this.debouncedOnSearchChange.cancel();
  };

  changeHighlight = modifier => {
    this.setState(prevState => {
      const { items } = this.props;
      const highlightedIndex = items
        ? Math.max(
            Math.min(prevState.highlightedIndex + modifier, items.length - 1),
            -1
          )
        : -1;
      this.scroller && this.scroller.centerChildByIndex(highlightedIndex);
      return {
        highlightedIndex
      };
    });
  };

  onKeyDown = event => {
    const { key } = event;
    switch (key) {
      case "Enter":
        if (this.state.highlightedIndex < 0) {
          this.debouncedOnSearchChange.flush();
        } else {
          event.target && event.target.blur && event.target.blur();
          this.selectResult(this.state.highlightedIndex);
        }
        break;
      case "Escape":
        this.setState({ highlightedIndex: -1 });
        break;
      case "ArrowUp":
        event.preventDefault();
        this.changeHighlight(-1);
        break;
      case "ArrowDown":
        event.preventDefault();
        this.changeHighlight(1);
        break;
    }
  };

  onFocus = () => this.setState({ focused: true, highlightedIndex: -1 });

  onSearchChange = event => {
    const { value } = event.target;
    this.debouncedOnSearchChange(value);
    if (!value) {
      this.debouncedOnSearchChange.flush();
      this.valueUpdated();
    }
    this.setState({ inputValue: value });
  };

  valueUpdated = value => {
    const { onChange } = this.props;
    return onChange && onChange(value);
  };

  onBlur = () => {
    const { selectedItem } = this.state;
    const inputValue =
      selectedItem && selectedItem.label ? selectedItem.label : "";
    this.props.onSearchChange(inputValue);
    this.props.onChange(inputValue);
    return this.setState({ focused: false, highlightedIndex: -1, inputValue });
  };

  selectResult = index => {
    const { items } = this.props;
    const selectedItem = items && items[index];
    return (
      selectedItem &&
      this.setState(
        { selectedItem, focused: false, inputValue: selectedItem.label },
        () => this.valueUpdated(selectedItem.value)
      )
    );
  };

  onItemClick = ({ currentTarget }) => {
    const selectedIndex =
      this.scroller &&
      this.scroller.wrapper && // ref inside a ref 😜
      Array.from(this.scroller.wrapper.childNodes).indexOf(currentTarget);

    this.selectResult(selectedIndex);
  };

  _renderMenu = props => {
    const { highlightedIndex } = this.state;

    return (
      <Menu
        {...props}
        innerRef={node => (this.scroller = node)}
        onItemClick={this.onItemClick}
        highlightedIndex={highlightedIndex}
      />
    );
  };

  render() {
    const {
      width,
      onSearchChange,
      debounceDelay,
      maxMenuHeight,
      items,
      onScrollReachEnd,
      onChange,
      footer,
      value,
      zIndex,
      ...inputProps
    } = this.props;

    const menuProps = {
      maxMenuHeight,
      items: this.state.items,
      onScrollReachEnd,
      footer
    };

    const { focused, selectedItem, inputValue } = this.state;

    return (
      <DropdownWrapper style={{ width, zIndex }}>
        <Input
          {...inputProps}
          type="search"
          protected={!!selectedItem}
          disableManualReplace
          value={inputValue}
          onKeyDown={this.onKeyDown}
          onFocus={this.onFocus}
          onChange={this.onSearchChange}
          onBlur={this.onBlur}
        />
        {focused &&
          ((this.state.items && this.state.items.length) || footer) &&
          this._renderMenu(menuProps)}
      </DropdownWrapper>
    );
  }

  static defaultProps = {
    onSearchChange: () => false,
    onScrollReachEnd: () => false,
    onChange: () => false,
    width: 200,
    maxMenuHeight: 353,
    debounceDelay: 500
  };

  static propTypes = {
    width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    debounceDelay: PropTypes.number,
    onSearchChange: PropTypes.func,
    onChange: PropTypes.func,
    zIndex: PropTypes.number,
    ...Menu.propTypes
  };
}

export default SearchField;
<commit_msg>fix(search-field): fix bug of extra onChange and onSearchChange on selection<commit_after>import * as React from "react";
import PropTypes from "prop-types";
import debounce from "lodash.debounce";
import isEqual from "lodash.isequal";
import Input from "@crave/farmblocks-input-text";

import DropdownWrapper from "./styledComponents/DropdownWrapper";
import Menu from "./components/Menu";

class SearchField extends React.Component {
  state = {
    highlightedIndex: -1,
    focused: false,
    inputValue: "",
    items: [],
    selectedItem: null
  };

  debouncedOnSearchChange = debounce(
    this.props.onSearchChange,
    this.props.debounceDelay
  );

  setValueFromProps = () => {
    const { value, items } = this.props;
    const selectedItem = items && items.find(item => item.value === value);
    if (selectedItem) {
      this.setState({
        inputValue: selectedItem.label,
        selectedItem
      });
    }
  };

  componentDidMount = () => {
    const { value, items } = this.props;
    if (items) {
      this.setState({ items });
    }

    if (value) {
      this.setValueFromProps();
    }
  };

  componentDidUpdate = prevProps => {
    if (
      prevProps.debounceDelay !== this.props.debounceDelay ||
      prevProps.onSearchChange !== this.props.onSearchChange
    ) {
      this.debouncedOnSearchChange && this.debouncedOnSearchChange.cancel();
      this.debouncedOnSearchChange = debounce(
        this.props.onSearchChange,
        this.props.debounceDelay
      );
    }

    if (!isEqual(prevProps.items, this.props.items)) {
      this.setState({ items: this.props.items });
    }

    if (prevProps.value !== this.props.value) {
      this.setValueFromProps();
    }
  };

  componentWillUnmount = () => {
    this.debouncedOnSearchChange.cancel();
  };

  changeHighlight = modifier => {
    this.setState(prevState => {
      const { items } = this.props;
      const highlightedIndex = items
        ? Math.max(
            Math.min(prevState.highlightedIndex + modifier, items.length - 1),
            -1
          )
        : -1;
      this.scroller && this.scroller.centerChildByIndex(highlightedIndex);
      return {
        highlightedIndex
      };
    });
  };

  onKeyDown = event => {
    const { key } = event;
    switch (key) {
      case "Enter":
        if (this.state.highlightedIndex < 0) {
          this.debouncedOnSearchChange.flush();
        } else {
          event.target && event.target.blur && event.target.blur();
          this.selectResult(this.state.highlightedIndex);
        }
        break;
      case "Escape":
        this.setState({ highlightedIndex: -1 });
        break;
      case "ArrowUp":
        event.preventDefault();
        this.changeHighlight(-1);
        break;
      case "ArrowDown":
        event.preventDefault();
        this.changeHighlight(1);
        break;
    }
  };

  onFocus = () => this.setState({ focused: true, highlightedIndex: -1 });

  onSearchChange = event => {
    const { value } = event.target;
    this.debouncedOnSearchChange(value);
    if (!value) {
      this.debouncedOnSearchChange.flush();
      this.valueUpdated();
    }
    this.setState({ inputValue: value });
  };

  valueUpdated = value => {
    const { onChange } = this.props;
    return onChange && onChange(value);
  };

  onBlur = () => {
    const { selectedItem } = this.state;
    const inputValue =
      selectedItem && selectedItem.label ? selectedItem.label : "";
    if (!selectedItem) {
      this.props.onSearchChange(inputValue);
      this.props.onChange(inputValue);
    }
    return this.setState({ focused: false, highlightedIndex: -1, inputValue });
  };

  selectResult = index => {
    const { items } = this.props;
    const selectedItem = items && items[index];
    return (
      selectedItem &&
      this.setState(
        { selectedItem, focused: false, inputValue: selectedItem.label },
        () => this.valueUpdated(selectedItem.value)
      )
    );
  };

  onItemClick = ({ currentTarget }) => {
    const selectedIndex =
      this.scroller &&
      this.scroller.wrapper && // ref inside a ref 😜
      Array.from(this.scroller.wrapper.childNodes).indexOf(currentTarget);

    this.selectResult(selectedIndex);
  };

  _renderMenu = props => {
    const { highlightedIndex } = this.state;

    return (
      <Menu
        {...props}
        innerRef={node => (this.scroller = node)}
        onItemClick={this.onItemClick}
        highlightedIndex={highlightedIndex}
      />
    );
  };

  render() {
    const {
      width,
      onSearchChange,
      debounceDelay,
      maxMenuHeight,
      items,
      onScrollReachEnd,
      onChange,
      footer,
      value,
      zIndex,
      ...inputProps
    } = this.props;

    const menuProps = {
      maxMenuHeight,
      items: this.state.items,
      onScrollReachEnd,
      footer
    };

    const { focused, selectedItem, inputValue } = this.state;

    return (
      <DropdownWrapper style={{ width, zIndex }}>
        <Input
          {...inputProps}
          type="search"
          protected={!!selectedItem}
          disableManualReplace
          value={inputValue}
          onKeyDown={this.onKeyDown}
          onFocus={this.onFocus}
          onChange={this.onSearchChange}
          onBlur={this.onBlur}
        />
        {focused &&
          ((this.state.items && this.state.items.length) || footer) &&
          this._renderMenu(menuProps)}
      </DropdownWrapper>
    );
  }

  static defaultProps = {
    onSearchChange: () => false,
    onScrollReachEnd: () => false,
    onChange: () => false,
    width: 200,
    maxMenuHeight: 353,
    debounceDelay: 500
  };

  static propTypes = {
    width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    debounceDelay: PropTypes.number,
    onSearchChange: PropTypes.func,
    onChange: PropTypes.func,
    zIndex: PropTypes.number,
    ...Menu.propTypes
  };
}

export default SearchField;
<|endoftext|>


<commit_before>"use strict";
define(['../objects/View'],function(view){
	return view.extend({
		_columnsConfig:null,
		_filterConfig:null,
		_paginatorConfig:null,
		_actions:null,
		_config:null,
		_recordActions:null,
		_dataGridEL:'.data-grid',
		collection:null,
		initialize:function(){
			openbiz.View.prototype.initialize.apply(this);
			return this;
		},
		_bindEvents:function(){
			this.undelegateEvents();
			var actions = this._getActions();
			for (var i in actions){
				var action = actions[i];
				var key = action.event + " ." + action.className;
				this.events[key] = action.action;
			}
			var recordActions = this._getRecordActions();
			for (var i in recordActions){
				var recordAction = recordActions[i];
				var className = recordAction["className"];
				var selector = "rec-act-"+recordAction["name"].toLowerCase();
				var key = recordAction.event + " ." + selector;
				this.events[key] = recordAction.action;
			}
			this.delegateEvents();
			return this;
		},
		render:function(){
			$(window).off('resize');
			openbiz.ui.update($(this.el));
			if(this._canDisplayView())
			{
				$(this.el).html(this.template(this.locale));
				this._renderDataGridConfig()._bindEvents();
			}
			else
			{
				this._renderNoPermissionView();
			}
		},
		_renderDataGridConfig:function(){
			var columns = [];
			var columnConfigs = this._getColumnsConfig();
			for (var i in columnConfigs){
				var column = columnConfigs[i];
				if(this._canDisplayColumn(column)){
					var type = column['type'];
					if(typeof  type == 'undefined' || type == null){
						type = 'text';
					}
					var field = openbiz.elements.columns[type].getConfig(this,column,this._getRecordActions());
					if(field != null){
						columns.push(field);
					}
				}
			}
			if(this._getFilterConfig()){
				var filter = new Backgrid.Extension.ServerSideFilter({
					collection: this.collection,
					name: "query",
					placeholder: ""
				});
				$(this.el).find(this._dataGridEL).append(filter.render().el);
			}
			var grid = new Backgrid.Grid({
				columns:columns,
				collection: this.collection,
				className: 'backgrid table table-striped table-bordered text-center',
				emptyText: 'emptyText '
			});
			$(this.el).find(this._dataGridEL).append(grid.render().el);
			if(this._getPaginatorConfig()){
				var paginator = new Backgrid.Extension.Paginator({
					windowSize: 10,
					slideScale: 0.5,
					goBackFirstOnSort: true,
					collection: this.collection,
					className:'pagination'
				});
				$(this.el).find(this._dataGridEL).append(paginator.render().el);
			}
			this.collection.fetch();
			return this;
		},
		_renderNoPermissionView:function(){
			//render 403 page
		},
		_canDisplayView:function(){
			if(typeof this._config.permission == 'undefined' || this._config.permission == null){
				return true;
			}
			return openbiz.session.me.hasPermission(this._config.permission);
		},
		_canDisplayColumn:function(column){
			if(typeof column.permission != 'undefined' && column.permission)
				return openbiz.session.me.hasPermission(column.permission);
			return true;
		},
		_getRecordActions:function(){
			if(! this._recordActions){
				this._recordActions = this._config.recordActions;
			}
			return this._recordActions;
		},
		_getActions:function(){

			if(! this._actions){
				this._actions = this._config.action;
			}
			return this._actions;
		},
		_getColumnsConfig:function(){
			if(! this._columnsConfig){
				this._columnsConfig = this._config.fields;
			}
			return this._columnsConfig;
		},
		_getFilterConfig:function(){
			if(! this._filterConfig){
				this._filterConfig = this._config.filter;
			}
			return this._filterConfig;
		},
		_getPaginatorConfig:function(){
			if(! this._paginatorConfig){
				this._paginatorConfig = this._config.paginator;
			}
			return this._paginatorConfig;
		}
	});
});<commit_msg>update<commit_after>"use strict";
define(['../objects/View'],function(view){
	return view.extend({
		_columnsConfig:null,
		_filterConfig:null,
		_paginatorConfig:null,
		_actions:null,
		_metadata:null,
		_recordActions:null,
		_dataGridEL:'.data-grid',
		collection:null,
		initialize:function(){
			openbiz.View.prototype.initialize.apply(this);
			return this;
		},
		_bindEvents:function(){
			this.undelegateEvents();
			var actions = this._getActions();
			for (var i in actions){
				var action = actions[i];
				var key = action.event + " ." + action.className;
				this.events[key] = action.action;
			}
			var recordActions = this._getRecordActions();
			for (var i in recordActions){
				var recordAction = recordActions[i];
				var className = recordAction["className"];
				var selector = "rec-act-"+recordAction["name"].toLowerCase();
				var key = recordAction.event + " ." + selector;
				this.events[key] = recordAction.action;
			}
			this.delegateEvents();
			return this;
		},
		render:function(){
			$(window).off('resize');
			openbiz.ui.update($(this.el));
			if(this._canDisplayView())
			{
				$(this.el).html(this.template(this.locale));
				this._renderDataGridConfig()._bindEvents();
			}
			else
			{
				this._renderNoPermissionView();
			}
		},
		_renderDataGridConfig:function(){
			var columns = [];
			var columnConfigs = this._getColumnsConfig();
			for (var i in columnConfigs){
				var column = columnConfigs[i];
				if(this._canDisplayColumn(column)){
					var type = column['type'];
					if(typeof  type == 'undefined' || type == null){
						type = 'text';
					}
					var field = openbiz.elements.columns[type].getConfig(this,column,this._getRecordActions());
					if(field != null){
						columns.push(field);
					}
				}
			}
			if(this._getFilterConfig()){
				var filter = new Backgrid.Extension.ServerSideFilter({
					collection: this.collection,
					name: "query",
					placeholder: ""
				});
				$(this.el).find(this._dataGridEL).append(filter.render().el);
			}
			var grid = new Backgrid.Grid({
				columns:columns,
				collection: this.collection,
				className: 'backgrid table table-striped table-bordered text-center',
				emptyText: 'emptyText '
			});
			$(this.el).find(this._dataGridEL).append(grid.render().el);
			if(this._getPaginatorConfig()){
				var paginator = new Backgrid.Extension.Paginator({
					windowSize: 10,
					slideScale: 0.5,
					goBackFirstOnSort: true,
					collection: this.collection,
					className:'pagination'
				});
				$(this.el).find(this._dataGridEL).append(paginator.render().el);
			}
			this.collection.fetch();
			return this;
		},
		_renderNoPermissionView:function(){
			//render 403 page
		},
		_canDisplayView:function(){
			if(typeof this._metadata.permission == 'undefined' || this._metadata.permission == null){
				return true;
			}
			return openbiz.session.me.hasPermission(this._metadata.permission);
		},
		_canDisplayColumn:function(column){
			if(typeof column.permission != 'undefined' && column.permission)
				return openbiz.session.me.hasPermission(column.permission);
			return true;
		},
		_getRecordActions:function(){
			if(! this._recordActions){
				this._recordActions = this._metadata.recordActions;
			}
			return this._recordActions;
		},
		_getActions:function(){

			if(! this._actions){
				this._actions = this._metadata.action;
			}
			return this._actions;
		},
		_getColumnsConfig:function(){
			if(! this._columnsConfig){
				this._columnsConfig = this._metadata.fields;
			}
			return this._columnsConfig;
		},
		_getFilterConfig:function(){
			if(! this._filterConfig){
				this._filterConfig = this._metadata.filter;
			}
			return this._filterConfig;
		},
		_getPaginatorConfig:function(){
			if(! this._paginatorConfig){
				this._paginatorConfig = this._config.paginator;
			}
			return this._paginatorConfig;
		}
	});
});<|endoftext|>


<commit_before>// @flow
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

const fs = require('fs')
const path = require('path')
const electron = require('electron')
const app = electron.app
const webContents = electron.webContents
const appActions = require('./actions/appActions')
const Filtering = require('../app/filtering')
const settings = require('./constants/settings')

// set to true if `init` has been called
let initialized = false
// set to true if the flash install check has succeeded
let flashInstalled = false
// set to true if a flash install url has been loaded
let flashMaybeInstalled = false

const getPepperFlashPath = () => {
  if (['darwin', 'win32'].includes(process.platform)) {
    return app.getPath('pepperFlashSystemPlugin')
  }
  const basePath = '/usr/lib'
  const plugin = 'libpepflashplayer.so'
  let pluginPath = path.resolve(basePath, 'pepperflashplugin-nonfree', plugin)
  try {
    fs.statSync(pluginPath)
  } catch (e) {
    pluginPath = path.resolve(basePath, 'PepperFlash', plugin)
    try {
      fs.statSync(pluginPath)
    } catch (e) {
      // Throws error if not found
      pluginPath = path.resolve('/usr/lib64/chromium', 'PepperFlash', plugin)
    }
  }
  return pluginPath
}

/**
 * Checks whether a link is an Flash installer URL.
 * @param {string} url
 * @return {boolean}
 */
const isFlashInstallUrl = (url) => {
  const adobeRegex = new RegExp('//(get\\.adobe\\.com/([a-z_-]+/)*flashplayer|www\\.macromedia\\.com/go/getflash|www\\.adobe\\.com/go/getflash)', 'i')
  return adobeRegex.test(url)
}

const handleFlashInstallUrl = (details, isPrivate) => {
  const result = {
    resourceName: module.exports.resourceName,
    redirectURL: null,
    cancel: false
  }

  const url = details.url
  if (!url || details.resourceType !== 'mainFrame') {
    return result
  }

  if (!isFlashInstallUrl(url)) {
    return result
  }

  if (!flashInstalled) {
    if (flashMaybeInstalled) {
      setImmediate(() => {
        module.exports.checkFlashInstalled((installed) => {
          flashMaybeInstalled = installed
          let tab = webContents.fromTabID(details.tabId)
          if (tab && !tab.isDestroyed()) {
            tab.loadURL(url)
          }
        })
      })
      result.cancel = true
    } else {
      flashMaybeInstalled = true
    }
  }

  return result
}

module.exports.checkFlashInstalled = (cb) => {
  const pepperFlashSystemPluginPath = getPepperFlashPath()
  const pepperFlashManifestPath = path.resolve(pepperFlashSystemPluginPath, '..', 'manifest.json')
  fs.readFile(pepperFlashManifestPath, (err, data) => {
    try {
      if (err || !data) {
        flashInstalled = false
      } else {
        const manifest = JSON.parse(data)
        app.commandLine.appendSwitch('ppapi-flash-path', pepperFlashSystemPluginPath)
        app.commandLine.appendSwitch('ppapi-flash-version', manifest.version)
        flashInstalled = true
      }
    } finally {
      appActions.changeSetting(settings.FLASH_INSTALLED, flashInstalled)
      cb && cb(flashInstalled)
    }
  })
}

module.exports.init = () => {
  if (initialized) {
    return
  }
  initialized = true

  Filtering.registerBeforeRequestFilteringCB(handleFlashInstallUrl)
  module.exports.checkFlashInstalled()
}

module.exports.resourceName = 'flash'
<commit_msg>catch errors in `getPepperFlashPath` fixes https://github.com/brave/electron/issues/118<commit_after>// @flow
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

const fs = require('fs')
const path = require('path')
const electron = require('electron')
const app = electron.app
const webContents = electron.webContents
const appActions = require('./actions/appActions')
const Filtering = require('../app/filtering')
const settings = require('./constants/settings')

// set to true if `init` has been called
let initialized = false
// set to true if the flash install check has succeeded
let flashInstalled = false
// set to true if a flash install url has been loaded
let flashMaybeInstalled = false

const getPepperFlashPath = () => {
  if (['darwin', 'win32'].includes(process.platform)) {
    return app.getPath('pepperFlashSystemPlugin')
  }
  const basePath = '/usr/lib'
  const plugin = 'libpepflashplayer.so'
  let pluginPath = path.resolve(basePath, 'pepperflashplugin-nonfree', plugin)
  try {
    fs.statSync(pluginPath)
  } catch (e) {
    pluginPath = path.resolve(basePath, 'PepperFlash', plugin)
    try {
      fs.statSync(pluginPath)
    } catch (e) {
      // Throws error if not found
      pluginPath = path.resolve('/usr/lib64/chromium', 'PepperFlash', plugin)
    }
  }
  return pluginPath
}

/**
 * Checks whether a link is an Flash installer URL.
 * @param {string} url
 * @return {boolean}
 */
const isFlashInstallUrl = (url) => {
  const adobeRegex = new RegExp('//(get\\.adobe\\.com/([a-z_-]+/)*flashplayer|www\\.macromedia\\.com/go/getflash|www\\.adobe\\.com/go/getflash)', 'i')
  return adobeRegex.test(url)
}

const handleFlashInstallUrl = (details, isPrivate) => {
  const result = {
    resourceName: module.exports.resourceName,
    redirectURL: null,
    cancel: false
  }

  const url = details.url
  if (!url || details.resourceType !== 'mainFrame') {
    return result
  }

  if (!isFlashInstallUrl(url)) {
    return result
  }

  if (!flashInstalled) {
    if (flashMaybeInstalled) {
      setImmediate(() => {
        module.exports.checkFlashInstalled((installed) => {
          flashMaybeInstalled = installed
          let tab = webContents.fromTabID(details.tabId)
          if (tab && !tab.isDestroyed()) {
            tab.loadURL(url)
          }
        })
      })
      result.cancel = true
    } else {
      flashMaybeInstalled = true
    }
  }

  return result
}

module.exports.checkFlashInstalled = (cb) => {
  try {
    const pepperFlashSystemPluginPath = getPepperFlashPath()
    const pepperFlashManifestPath = path.resolve(pepperFlashSystemPluginPath, '..', 'manifest.json')
    fs.readFile(pepperFlashManifestPath, (err, data) => {
      try {
        if (err || !data) {
          flashInstalled = false
        } else {
          const manifest = JSON.parse(data)
          app.commandLine.appendSwitch('ppapi-flash-path', pepperFlashSystemPluginPath)
          app.commandLine.appendSwitch('ppapi-flash-version', manifest.version)
          flashInstalled = true
        }
      } finally {
        appActions.changeSetting(settings.FLASH_INSTALLED, flashInstalled)
        cb && cb(flashInstalled)
      }
    })
  } catch (e) {
    cb && cb(flashInstalled)
  }
}

module.exports.init = () => {
  if (initialized) {
    return
  }
  initialized = true

  Filtering.registerBeforeRequestFilteringCB(handleFlashInstallUrl)
  module.exports.checkFlashInstalled()
}

module.exports.resourceName = 'flash'
<|endoftext|>


<commit_before><commit_msg>adds tests for starting standup<commit_after>// jshint expr:true
var chai = require('chai')
  , expect = chai.expect
  , sinon = require('sinon')
  , proxyquire = require('proxyquire').noCallThru()
  , _ = require('lodash')
  , helpers = require('../helpers');

chai.use(require('sinon-chai'));

describe('Standup Controller', function() {
  var botMock
    , botController
    , usersModelMock
    , standupModelMock
    , user
    , userIterator
    , standupController;

  beforeEach(function() {
    botMock = {
      reply: sinon.stub(),
      startConversation: sinon.stub(),
      say: sinon.stub(),
      utterances: {yes: /yes/, no: /no/}
    };

    botController = {
      hears: sinon.stub()
    };

    user = {name: 'heisenberg', id: 'walterwhite'};

    userIterator = {
      hasNext: sinon.stub(),
      next: sinon.stub().returns(user)
    };

    usersModelMock = {
      list: sinon.stub().returns([user]),
      iterator: sinon.stub().returns(userIterator)
    };

    standupModelMock = {
      getSummaryChannel: sinon.stub().returns('Netflix'),
      setSummaryChannel: sinon.stub(),
      addStatus: sinon.stub(),
      clearStatuses: sinon.stub(),
      summarize: sinon.stub()
    };

    standupController = proxyquire('../../src/controller/standup-controller', {
      '../model/users-model': usersModelMock,
      '../model/standup-model': standupModelMock
    });
  });

  it('should set up listeners', function() {
    standupController.use(botController);
    expect(botController.hears.callCount).to.equal(4);

    _.forEach(botController.hears.args, function(args) {
      expect(args).to.have.length(3);
      expect(args[2]).to.be.a.function;
    });
  });

  describe('hears', function() {
    var hearsMap
      , messageMock;

    beforeEach(function() {
      standupController.use(botController);
      hearsMap = helpers.createHearsMap(botController);

      messageMock = {
        channel: 'AMC',
        user: 'heisenberg'
      };
    });

    describe('start standup', function() {
      var callback;

      beforeEach(function() {
        callback = _.find(hearsMap, function(val, key) {
          return /^start$/.test(key);
        });

        expect(callback).to.exist;
      });

      it('should start a standup', function() {
        var sayArgs;

        callback(botMock, messageMock);

        expect(botMock.reply).to.have.been.calledWithMatch(messageMock, /^Alright!/);
        expect(standupModelMock.getSummaryChannel).to.have.been.called;
        expect(standupModelMock.setSummaryChannel).not.to.have.been.called;
        expect(usersModelMock.list).to.have.been.called;
        expect(usersModelMock.iterator).to.have.been.called;
        expect(userIterator.next).to.have.been.called;
        expect(botMock.say).to.have.been.called;

        sayArgs = botMock.say.firstCall.args[0];
        expect(sayArgs.text).to.match(new RegExp(messageMock.user.id));
        expect(sayArgs.channel).to.match(new RegExp(messageMock.channel));
      });

      it('should set a summary channel if one is not already set', function() {
        standupModelMock.getSummaryChannel.returns();

        callback(botMock, messageMock);

        expect(botMock.reply).to.have.been.calledWithMatch(messageMock, /^Alright!/);
        expect(standupModelMock.getSummaryChannel).to.have.been.called;
        expect(standupModelMock.setSummaryChannel).to.have.been.calledWith(messageMock.channel);
      });

      it('should not start if a standup is in progress', function() {
        callback(botMock, messageMock);
        botMock.reply.reset();
        botMock.say.reset();
        standupModelMock.getSummaryChannel.reset();
        usersModelMock.list.reset();
        usersModelMock.iterator.reset();
        userIterator.next.reset();

        callback(botMock, messageMock);
        expect(botMock.reply).to.have.been.calledWithMatch(messageMock, /^Standup has already started!/);
        expect(standupModelMock.getSummaryChannel).not.to.have.been.called;
        expect(usersModelMock.list).not.to.have.been.called;
        expect(usersModelMock.iterator).not.to.have.been.called;
        expect(userIterator.next).not.to.have.been.called;
        expect(botMock.say).not.to.have.been.called;
      });

      it('should not start if there are no users', function() {
        usersModelMock.list.returns([]);

        callback(botMock, messageMock);
        expect(botMock.reply).to.have.been.calledWithMatch(messageMock, /^Looks like no one/);
        expect(standupModelMock.getSummaryChannel).to.have.been.called;
        expect(usersModelMock.list).to.have.been.called;

        expect(usersModelMock.iterator).not.to.have.been.called;
        expect(userIterator.next).not.to.have.been.called;
        expect(botMock.say).not.to.have.been.called;
      });

    });

    describe('end', function() {

      beforeEach(function() {

      });

      it('should ', function() {

      });
    });

    describe('start check-in', function() {

      beforeEach(function() {

      });

      it('should ', function() {

      });
    });

    describe('skip', function() {

      beforeEach(function() {

      });

      it('should ', function() {

      });
    });
  });
});
<|endoftext|>


<commit_before>var fs = Npm.require('fs');
var child_process = Npm.require('child_process');
var querystring = Npm.require('querystring');
var urlParser = Npm.require('url');

// list of bot user agents that we want to serve statically, but do
// not obey the _escaped_fragment_ protocol. The page is served
// statically to any client whos user agent matches any of these
// regexps. Users may modify this array.
//
// An original goal with the spiderable package was to avoid doing
// user-agent based tests. But the reality is not enough bots support
// the _escaped_fragment_ protocol, so we need to hardcode a list
// here. I shed a silent tear.
Spiderable.userAgentRegExps = [
    /^facebookexternalhit/i, /^linkedinbot/i, /^twitterbot/i];

// how long to let phantomjs run before we kill it
var REQUEST_TIMEOUT = 15*1000;
// maximum size of result HTML. node's default is 200k which is too
// small for our docs.
var MAX_BUFFER = 5*1024*1024; // 5MB

// Exported for tests.
Spiderable._urlForPhantom = function (siteAbsoluteUrl, requestUrl) {
  // reassembling url without escaped fragment if exists
  var parsedUrl = urlParser.parse(requestUrl);
  var parsedQuery = querystring.parse(parsedUrl.query);
  var escapedFragment = parsedQuery['_escaped_fragment_'];
  delete parsedQuery['_escaped_fragment_'];

  var parsedAbsoluteUrl = urlParser.parse(siteAbsoluteUrl);
  // If the ROOT_URL contains a path, Meteor strips that path off of the
  // request's URL before we see it. So we concatenate the pathname from
  // the request's URL with the root URL's pathname to get the full
  // pathname.
  if (parsedUrl.pathname.charAt(0) === "/") {
    parsedUrl.pathname = parsedUrl.pathname.substring(1);
  }
  parsedAbsoluteUrl.pathname = urlParser.resolve(parsedAbsoluteUrl.pathname,
                                                 parsedUrl.pathname);
  parsedAbsoluteUrl.query = parsedQuery;
  // `url.format` will only use `query` if `search` is absent
  parsedAbsoluteUrl.search = null;

  if (escapedFragment !== undefined && escapedFragment !== null && escapedFragment.length > 0) {
    parsedAbsoluteUrl.hash = '!' + decodeURIComponent(escapedFragment);
  }

  return urlParser.format(parsedAbsoluteUrl);
};

var PHANTOM_SCRIPT = Assets.getText("phantom_script.js");

WebApp.connectHandlers.use(function (req, res, next) {
  // _escaped_fragment_ comes from Google's AJAX crawling spec:
  // https://developers.google.com/webmasters/ajax-crawling/docs/specification
  // This spec was designed during the brief era where using "#!" URLs was
  // common, so it mostly describes how to translate "#!" URLs into
  // _escaped_fragment_ URLs. Since then, "#!" URLs have gone out of style, but
  // the <meta name="fragment" content="!"> (see spiderable.html) approach also
  // described in the spec is still common and used by several crawlers.
  if (/\?.*_escaped_fragment_=/.test(req.url) ||
      _.any(Spiderable.userAgentRegExps, function (re) {
        return re.test(req.headers['user-agent']); })) {

    var url = Spiderable._urlForPhantom(Meteor.absoluteUrl(), req.url);

    // This string is going to be put into a bash script, so it's important
    // that 'url' (which comes from the network) can neither exploit phantomjs
    // or the bash script. JSON stringification should prevent it from
    // exploiting phantomjs, and since the output of JSON.stringify shouldn't
    // be able to contain newlines, it should be unable to exploit bash as
    // well.
    var phantomScript = "var url = " + JSON.stringify(url) + ";" +
          PHANTOM_SCRIPT;

    // Allow override of phantomjs args via env var
    // We use one env var to try to keep env-var explosion under control.
    // We're not going to document this unless it is actually needed;
    // (if you find yourself needing this please let us know the use case!)
    var phantomJsArgs = process.env.METEOR_PKG_SPIDERABLE_PHANTOMJS_ARGS || '';

    // Default image loading to off (we don't need images)
    if (phantomJsArgs.indexOf("--load-images=") === -1) {
      phantomJsArgs += " --load-images=no";
    }

    // POODLE means SSLv3 is being turned off everywhere.
    // phantomjs currently defaults to SSLv3, and won't use TLS.
    // Use --ssl-protocol to set the default to TLSv1
    // (another option would be 'any', but really, we want to say >= TLSv1)
    // More info: https://groups.google.com/forum/#!topic/meteor-core/uZhT3AHwpsI
    if (phantomJsArgs.indexOf("--ssl-protocol=") === -1) {
      phantomJsArgs += " --ssl-protocol=TLSv1";
    }

    // Run phantomjs.
    //
    // Use '/dev/stdin' to avoid writing to a temporary file. We can't
    // just omit the file, as PhantomJS takes that to mean 'use a
    // REPL' and exits as soon as stdin closes.
    //
    // However, Node 0.8 broke the ability to open /dev/stdin in the
    // subprocess, so we can't just write our string to the process's stdin
    // directly; see https://gist.github.com/3751746 for the gory details. We
    // work around this with a bash heredoc. (We previous used a "cat |"
    // instead, but that meant we couldn't use exec and had to manage several
    // processes.)
    child_process.execFile(
      '/bin/bash',
      ['-c',
       ("exec phantomjs " + phantomJsArgs + " /dev/stdin <<'END'\n" +
        phantomScript + "END\n")],
      {timeout: REQUEST_TIMEOUT, maxBuffer: MAX_BUFFER},
      function (error, stdout, stderr) {
        if (!error && /<html/i.test(stdout)) {
          res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
          res.end(stdout);
        } else {
          // phantomjs failed. Don't send the error, instead send the
          // normal page.
          if (error && error.code === 127)
            Meteor._debug("spiderable: phantomjs not installed. Download and install from http://phantomjs.org/");
          else
            Meteor._debug("spiderable: phantomjs failed:", error, "\nstderr:", stderr);

          next();
        }
      });
  } else {
    next();
  }
});
<commit_msg>Remove obsoleted comment<commit_after>var fs = Npm.require('fs');
var child_process = Npm.require('child_process');
var querystring = Npm.require('querystring');
var urlParser = Npm.require('url');

// list of bot user agents that we want to serve statically, but do
// not obey the _escaped_fragment_ protocol. The page is served
// statically to any client whos user agent matches any of these
// regexps. Users may modify this array.
//
// An original goal with the spiderable package was to avoid doing
// user-agent based tests. But the reality is not enough bots support
// the _escaped_fragment_ protocol, so we need to hardcode a list
// here. I shed a silent tear.
Spiderable.userAgentRegExps = [
    /^facebookexternalhit/i, /^linkedinbot/i, /^twitterbot/i];

// how long to let phantomjs run before we kill it
var REQUEST_TIMEOUT = 15*1000;
// maximum size of result HTML. node's default is 200k which is too
// small for our docs.
var MAX_BUFFER = 5*1024*1024; // 5MB

// Exported for tests.
Spiderable._urlForPhantom = function (siteAbsoluteUrl, requestUrl) {
  // reassembling url without escaped fragment if exists
  var parsedUrl = urlParser.parse(requestUrl);
  var parsedQuery = querystring.parse(parsedUrl.query);
  var escapedFragment = parsedQuery['_escaped_fragment_'];
  delete parsedQuery['_escaped_fragment_'];

  var parsedAbsoluteUrl = urlParser.parse(siteAbsoluteUrl);
  // If the ROOT_URL contains a path, Meteor strips that path off of the
  // request's URL before we see it. So we concatenate the pathname from
  // the request's URL with the root URL's pathname to get the full
  // pathname.
  if (parsedUrl.pathname.charAt(0) === "/") {
    parsedUrl.pathname = parsedUrl.pathname.substring(1);
  }
  parsedAbsoluteUrl.pathname = urlParser.resolve(parsedAbsoluteUrl.pathname,
                                                 parsedUrl.pathname);
  parsedAbsoluteUrl.query = parsedQuery;
  // `url.format` will only use `query` if `search` is absent
  parsedAbsoluteUrl.search = null;

  if (escapedFragment !== undefined && escapedFragment !== null && escapedFragment.length > 0) {
    parsedAbsoluteUrl.hash = '!' + decodeURIComponent(escapedFragment);
  }

  return urlParser.format(parsedAbsoluteUrl);
};

var PHANTOM_SCRIPT = Assets.getText("phantom_script.js");

WebApp.connectHandlers.use(function (req, res, next) {
  // _escaped_fragment_ comes from Google's AJAX crawling spec:
  // https://developers.google.com/webmasters/ajax-crawling/docs/specification
  if (/\?.*_escaped_fragment_=/.test(req.url) ||
      _.any(Spiderable.userAgentRegExps, function (re) {
        return re.test(req.headers['user-agent']); })) {

    var url = Spiderable._urlForPhantom(Meteor.absoluteUrl(), req.url);

    // This string is going to be put into a bash script, so it's important
    // that 'url' (which comes from the network) can neither exploit phantomjs
    // or the bash script. JSON stringification should prevent it from
    // exploiting phantomjs, and since the output of JSON.stringify shouldn't
    // be able to contain newlines, it should be unable to exploit bash as
    // well.
    var phantomScript = "var url = " + JSON.stringify(url) + ";" +
          PHANTOM_SCRIPT;

    // Allow override of phantomjs args via env var
    // We use one env var to try to keep env-var explosion under control.
    // We're not going to document this unless it is actually needed;
    // (if you find yourself needing this please let us know the use case!)
    var phantomJsArgs = process.env.METEOR_PKG_SPIDERABLE_PHANTOMJS_ARGS || '';

    // Default image loading to off (we don't need images)
    if (phantomJsArgs.indexOf("--load-images=") === -1) {
      phantomJsArgs += " --load-images=no";
    }

    // POODLE means SSLv3 is being turned off everywhere.
    // phantomjs currently defaults to SSLv3, and won't use TLS.
    // Use --ssl-protocol to set the default to TLSv1
    // (another option would be 'any', but really, we want to say >= TLSv1)
    // More info: https://groups.google.com/forum/#!topic/meteor-core/uZhT3AHwpsI
    if (phantomJsArgs.indexOf("--ssl-protocol=") === -1) {
      phantomJsArgs += " --ssl-protocol=TLSv1";
    }

    // Run phantomjs.
    //
    // Use '/dev/stdin' to avoid writing to a temporary file. We can't
    // just omit the file, as PhantomJS takes that to mean 'use a
    // REPL' and exits as soon as stdin closes.
    //
    // However, Node 0.8 broke the ability to open /dev/stdin in the
    // subprocess, so we can't just write our string to the process's stdin
    // directly; see https://gist.github.com/3751746 for the gory details. We
    // work around this with a bash heredoc. (We previous used a "cat |"
    // instead, but that meant we couldn't use exec and had to manage several
    // processes.)
    child_process.execFile(
      '/bin/bash',
      ['-c',
       ("exec phantomjs " + phantomJsArgs + " /dev/stdin <<'END'\n" +
        phantomScript + "END\n")],
      {timeout: REQUEST_TIMEOUT, maxBuffer: MAX_BUFFER},
      function (error, stdout, stderr) {
        if (!error && /<html/i.test(stdout)) {
          res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
          res.end(stdout);
        } else {
          // phantomjs failed. Don't send the error, instead send the
          // normal page.
          if (error && error.code === 127)
            Meteor._debug("spiderable: phantomjs not installed. Download and install from http://phantomjs.org/");
          else
            Meteor._debug("spiderable: phantomjs failed:", error, "\nstderr:", stderr);

          next();
        }
      });
  } else {
    next();
  }
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

FOAM_FILES([
  { name: 'foam/nanos/client/ClientBuilder' },
  { name: 'foam/nanos/controller/AppStyles', flags: ['web'] },
  { name: "foam/nanos/logger/LogMessage" },
  { name: "foam/nanos/logger/AbstractLogger" },
  { name: "foam/nanos/logger/ProxyLogger" },
  { name: "foam/nanos/logger/FileLogger" },
  { name: "foam/nanos/logger/LogLevelFilterLogger" },
  { name: "foam/nanos/logger/NotificationLogMessageDAO" },
  { name: "foam/nanos/logger/RepeatLogMessageDAO" },
  { name: 'foam/nanos/fs/File' },
  { name: 'foam/nanos/fs/FileProperty' },
  { name: 'foam/nanos/fs/FileDAODecorator' },
  { name: 'foam/nanos/fs/FileArray' },
  { name: 'foam/nanos/fs/FileArrayDAODecorator' },
  { name: "foam/nanos/app/AppConfig" },
  { name: "foam/nanos/app/ContextLookupAppConfigService"},
  { name: "foam/nanos/app/AppConfigService" },
  { name: "foam/nanos/app/EmailConfig" },
  { name: "foam/nanos/app/ClientAppConfigService" },
  { name: "foam/nanos/theme/Theme" },
  { name: "foam/nanos/controller/ApplicationController" },
  { name: "foam/nanos/app/Mode" },
  { name: "foam/nanos/auth/DayOfWeek" },
  { name: "foam/nanos/auth/Hours" },
  { name: "foam/nanos/auth/Address" },
  { name: "foam/nanos/auth/ChangePassword" },
  { name: "foam/nanos/auth/DeletedAware" },
  { name: "foam/nanos/auth/Group" },
  { name: "foam/nanos/auth/ServiceProvider" },
  { name: "foam/nanos/auth/Language" },
  { name: "foam/nanos/auth/CreatedAware" },
  { name: "foam/nanos/auth/CreatedAwareDAO" },
  { name: "foam/nanos/auth/LastModifiedAware" },
  { name: "foam/nanos/auth/LastModifiedAwareDAO" },
  { name: "foam/nanos/auth/Login" },
  { name: "foam/nanos/auth/Permission" },
  { name: "foam/nanos/auth/Country" },
  { name: "foam/nanos/auth/Region" },
  { name: 'foam/nanos/auth/ResendVerificationEmail', flags: ['web'] },
  { name: "foam/nanos/auth/Phone" },
  { name: "foam/nanos/auth/HtmlDoc" },
  { name: "foam/nanos/auth/SignOutView" },
  { name: 'foam/nanos/auth/email/EmailTokenService'},
  { name: "foam/nanos/auth/email/EmailDocInterface" },
  { name: "foam/nanos/auth/email/EmailDocService" },
  { name: "foam/nanos/auth/email/ClientEmailDocService" },
  { name: "foam/nanos/auth/resetPassword/EmailView" },
  { name: "foam/nanos/auth/resetPassword/ResendView" },
  { name: "foam/nanos/auth/resetPassword/ResetView" },
  { name: "foam/nanos/auth/resetPassword/SuccessView" },
  { name: "foam/nanos/auth/resetPassword/ResetPasswordTokenService" },
  { name: "foam/nanos/auth/token/TokenService" },
  { name: "foam/nanos/auth/token/ClientTokenService" },
  { name: "foam/nanos/auth/token/Token" },
  { name: "foam/nanos/auth/token/AbstractTokenService" },
  { name: "foam/nanos/auth/ChangePasswordView"},
  { name: 'foam/nanos/auth/HumanNameTrait' },
  { name: "foam/nanos/auth/User" },
  { name: "foam/nanos/auth/CreatedByAware" },
  { name: "foam/nanos/auth/CreatedByAwareDAO" },
  { name: "foam/nanos/auth/LastModifiedByAware" },
  { name: "foam/nanos/auth/LastModifiedByAwareDAO" },
  { name: "foam/nanos/auth/AddressDetailView", flags: ['web'] },
  { name: "foam/nanos/auth/SignInView", flags: ['web'] },
  { name: "foam/nanos/auth/SignUpView", flags: ['web'] },
  { name: "foam/nanos/auth/PermissionedPropertyDAO" },
  { name: "foam/nanos/auth/ProfilePictureView", flags: ['web'] },
  { name: "foam/nanos/auth/twofactor/OTPAuthService" },
  { name: "foam/nanos/auth/twofactor/AbstractOTPAuthService" },
  { name: "foam/nanos/auth/twofactor/AbstractTOTPAuthService" },
  { name: "foam/nanos/auth/twofactor/ClientOTPAuthService" },
  { name: "foam/nanos/auth/twofactor/TwoFactorSignInView" },
  { name: "foam/nanos/auth/twofactor/refinements" },
  { name: "foam/nanos/auth/UserAndGroupAuthService" },
  { name: "foam/nanos/bench/Benchmark" },
  { name: "foam/nanos/boot/DAOConfigSummaryView", flags: ['web'] },
  { name: "foam/nanos/session/Session" },
  { name: "foam/nanos/session/SessionTimer" },
  { name: "foam/nanos/menu/AbstractMenu" },
  { name: "foam/nanos/menu/DAOMenu" },
  { name: "foam/nanos/menu/DAOMenu2" },
  { name: "foam/nanos/menu/DocumentMenu" },
  { name: "foam/nanos/menu/DocumentFileMenu" },
  { name: "foam/nanos/menu/LinkMenu" },
  { name: "foam/nanos/menu/ListMenu" },
  { name: "foam/nanos/menu/Menu" },
  { name: "foam/nanos/menu/MenuBar" },
  { name: "foam/nanos/menu/PopupMenu" },
  { name: "foam/nanos/menu/SubMenu" },
  { name: "foam/nanos/menu/SubMenuView" },
  { name: "foam/nanos/menu/TabsMenu" },
  { name: "foam/nanos/menu/ViewMenu" },
  { name: "foam/nanos/menu/ScriptMenu" },
  { name: "foam/nanos/menu/TreeAltView", flags: ['web'] },
  { name: "foam/nanos/menu/TreeGraphAltView" },
  { name: "foam/nanos/auth/PermissionTableView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/TopNavigation", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/FooterView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/SideNavigation", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/SideNavigationItemView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/NavigationView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/BusinessLogoView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/UserView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/SubMenuBar", flags: ['web'] },
  { name: "foam/nanos/script/Language" },
  { name: "foam/nanos/script/ScriptStatus" },
  { name: "foam/nanos/script/Script" },
  { name: "foam/nanos/script/TestRunnerScript" },
  { name: "foam/nanos/jetty/HttpServer" },
  { name: "foam/nanos/servlet/Servlet" },
  { name: "foam/nanos/servlet/ErrorPageMapping" },
  { name: "foam/nanos/servlet/FilterMapping" },
  { name: "foam/nanos/servlet/ServletMapping" },
  { name: "foam/nanos/servlet/VirtualHostRoutingServlet" },
  { name: "foam/nanos/test/Test" },
  { name: "foam/nanos/test/TestBorder" },
  { name: "foam/nanos/cron/Cron" },
  { name: "foam/nanos/cron/test/IntervalScheduleTest" },
  { name: "foam/nanos/export/ExportDriverRegistry"},
  { name: "foam/nanos/export/ExportDriver" },
  { name: "foam/nanos/export/JSONDriver"},
  { name: "foam/nanos/export/JSONJDriver"},
  { name: "foam/nanos/export/XMLDriver"},
  { name: "foam/nanos/export/CSVDriver"},
  { name: "foam/nanos/auth/Relationships" },
  { name: "foam/nanos/NanoService" },
  { name: "foam/nanos/auth/twofactor/OTPKey" },
  { name: "foam/nanos/auth/AuthService" },
  { name: "foam/nanos/auth/ProxyAuthService" },
  { name: "foam/nanos/auth/CachedAuthServiceProxy" },
  { name: "foam/nanos/auth/ClientAuthService" },
  { name: "foam/nanos/auth/ClientLoginAuthService" },
  { name: "foam/nanos/auth/AgentAuthService" },
  { name: "foam/nanos/auth/ClientAgentAuthService" },
  { name: "foam/nanos/pm/PMTemperatureCellFormatter" },
  { name: "foam/nanos/pm/NullPM" },
  { name: "foam/nanos/pm/PM" },
  { name: "foam/nanos/pm/TemperatureCView" },
  { name: 'foam/nanos/notification/email/EmailMessage' },
  { name: 'foam/nanos/notification/email/EmailService' },
  { name: 'foam/nanos/notification/email/EmailTemplate' },
  { name: 'foam/nanos/notification/email/SMTPEmailService' },
  { name: 'foam/nanos/notification/email/Status' },
  { name: 'foam/nanos/notification/push/PushService' },
  { name: 'foam/nanos/notification/push/FirebasePushService' },
  { name: 'foam/nanos/demo/DemoObject' },
  { name: 'foam/nanos/demo/Demo' },
  { name: 'foam/nanos/http/Format' },
  { name: 'foam/nanos/http/Command' },
  { name: 'foam/nanos/http/WebAgent' },
  { name: "foam/nanos/http/ProxyWebAgent" },
  { name: "foam/nanos/http/HttpParameters" },
  { name: "foam/nanos/http/DefaultHttpParameters" },
  { name: "foam/nanos/doc/DocumentationView" },
  { name: 'foam/nanos/demo/relationship/CourseType' },
  { name: 'foam/nanos/demo/relationship/Course' },
  { name: 'foam/nanos/demo/relationship/Professor' },
  { name: 'foam/nanos/demo/relationship/Student' },
  { name: 'foam/nanos/demo/relationship/Controller' },
  { name: 'foam/nanos/notification/Notification'},
  { name: 'foam/nanos/notification/notifications/ScriptRunNotification'},
  { name: 'foam/nanos/notification/NotificationListView'},
  { name: 'foam/nanos/notification/NotificationRowView'},
  { name: 'foam/nanos/notification/NotificationSettingsView'},
  { name: 'foam/nanos/notification/NotificationView'},
  { name: 'foam/nanos/notification/NotificationNotificationView'},
  { name: 'foam/nanos/notification/notifications/ScriptRunNotificationNotificationView'},
  { name: 'foam/nanos/dashboard/Demo' },

  // Dig
  { name: "foam/nanos/dig/exception/DigErrorMessage" },
  { name: 'foam/nanos/dig/Argument'},
  { name: 'foam/nanos/dig/DIG' },
  { name: 'foam/nanos/dig/DigFileUploadView' },
  { name: 'foam/nanos/dig/DigSnippetView' },
  { name: 'foam/nanos/dig/DUG' },
  { name: 'foam/nanos/dig/LinkView' },
  { name: 'foam/nanos/dig/SUGAR' },

  { name: 'foam/nanos/notification/email/ClientPOP3EmailService'},
  { name: 'foam/nanos/notification/email/POP3Email'},

  // foam/nanos/ruler
  { name: "foam/nanos/ruler/Operations" },
  { name: "foam/nanos/ruler/Rule" },
  { name: "foam/nanos/ruler/RuleAction" },
  { name: "foam/nanos/ruler/RuleHistory" },
  { name: "foam/nanos/ruler/RuleHistoryStatus" },
  { name: "foam/nanos/ruler/RulerDAO" },
  { name: "foam/nanos/ruler/ScriptPredicate"},
  { name: "foam/nanos/ruler/UpdateRulesListSink" },
  { name: "foam/nanos/ruler/TestedRule" },
  { name: "foam/nanos/ruler/RulerProbe" },
  { name: "foam/nanos/ruler/UserRefines" },
  { name: "foam/nanos/test/EchoService" },
  { name: "foam/nanos/test/SerializationTestEchoService" },
  { name: "foam/nanos/analytics/Foldable" },
  { name: "foam/nanos/analytics/Candlestick" },
  { name: "foam/nanos/test/ClientEchoService" },

  { name: "foam/dao/jdbc/JDBCConnectionSpec" },

  // foam/nanos/crunch
  { name: "foam/nanos/crunch/crunchtest/FakeTestObject" },
  // models
  { name: "foam/nanos/crunch/Capability" },
  { name: "foam/nanos/crunch/CapabilityJunctionStatus" },
  { name: "foam/nanos/crunch/UserCapabilityJunctionRefine" },
  //daos
  { name: "foam/nanos/crunch/UserCapabilityJunctionDAO" },
  //rules
  { name: "foam/nanos/crunch/SendNotificationOnTopLevelCapabilityStatusUpdate" },
  { name: "foam/nanos/crunch/IsUserCapabilityJunctionStatusUpdate" },
  { name: "foam/nanos/crunch/RemoveJunctionsOnUserRemoval" },
  //authservice
  { name: "foam/nanos/auth/CapabilityAuthService" },
  // google
  { name: "foam/nanos/geocode/GoogleMapsCredentials" }
]);
<commit_msg>Add TimeOfDayScheduleTest to nanos.js<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

FOAM_FILES([
  { name: 'foam/nanos/client/ClientBuilder' },
  { name: 'foam/nanos/controller/AppStyles', flags: ['web'] },
  { name: "foam/nanos/logger/LogMessage" },
  { name: "foam/nanos/logger/AbstractLogger" },
  { name: "foam/nanos/logger/ProxyLogger" },
  { name: "foam/nanos/logger/FileLogger" },
  { name: "foam/nanos/logger/LogLevelFilterLogger" },
  { name: "foam/nanos/logger/NotificationLogMessageDAO" },
  { name: "foam/nanos/logger/RepeatLogMessageDAO" },
  { name: 'foam/nanos/fs/File' },
  { name: 'foam/nanos/fs/FileProperty' },
  { name: 'foam/nanos/fs/FileDAODecorator' },
  { name: 'foam/nanos/fs/FileArray' },
  { name: 'foam/nanos/fs/FileArrayDAODecorator' },
  { name: "foam/nanos/app/AppConfig" },
  { name: "foam/nanos/app/ContextLookupAppConfigService"},
  { name: "foam/nanos/app/AppConfigService" },
  { name: "foam/nanos/app/EmailConfig" },
  { name: "foam/nanos/app/ClientAppConfigService" },
  { name: "foam/nanos/theme/Theme" },
  { name: "foam/nanos/controller/ApplicationController" },
  { name: "foam/nanos/app/Mode" },
  { name: "foam/nanos/auth/DayOfWeek" },
  { name: "foam/nanos/auth/Hours" },
  { name: "foam/nanos/auth/Address" },
  { name: "foam/nanos/auth/ChangePassword" },
  { name: "foam/nanos/auth/DeletedAware" },
  { name: "foam/nanos/auth/Group" },
  { name: "foam/nanos/auth/ServiceProvider" },
  { name: "foam/nanos/auth/Language" },
  { name: "foam/nanos/auth/CreatedAware" },
  { name: "foam/nanos/auth/CreatedAwareDAO" },
  { name: "foam/nanos/auth/LastModifiedAware" },
  { name: "foam/nanos/auth/LastModifiedAwareDAO" },
  { name: "foam/nanos/auth/Login" },
  { name: "foam/nanos/auth/Permission" },
  { name: "foam/nanos/auth/Country" },
  { name: "foam/nanos/auth/Region" },
  { name: 'foam/nanos/auth/ResendVerificationEmail', flags: ['web'] },
  { name: "foam/nanos/auth/Phone" },
  { name: "foam/nanos/auth/HtmlDoc" },
  { name: "foam/nanos/auth/SignOutView" },
  { name: 'foam/nanos/auth/email/EmailTokenService'},
  { name: "foam/nanos/auth/email/EmailDocInterface" },
  { name: "foam/nanos/auth/email/EmailDocService" },
  { name: "foam/nanos/auth/email/ClientEmailDocService" },
  { name: "foam/nanos/auth/resetPassword/EmailView" },
  { name: "foam/nanos/auth/resetPassword/ResendView" },
  { name: "foam/nanos/auth/resetPassword/ResetView" },
  { name: "foam/nanos/auth/resetPassword/SuccessView" },
  { name: "foam/nanos/auth/resetPassword/ResetPasswordTokenService" },
  { name: "foam/nanos/auth/token/TokenService" },
  { name: "foam/nanos/auth/token/ClientTokenService" },
  { name: "foam/nanos/auth/token/Token" },
  { name: "foam/nanos/auth/token/AbstractTokenService" },
  { name: "foam/nanos/auth/ChangePasswordView"},
  { name: 'foam/nanos/auth/HumanNameTrait' },
  { name: "foam/nanos/auth/User" },
  { name: "foam/nanos/auth/CreatedByAware" },
  { name: "foam/nanos/auth/CreatedByAwareDAO" },
  { name: "foam/nanos/auth/LastModifiedByAware" },
  { name: "foam/nanos/auth/LastModifiedByAwareDAO" },
  { name: "foam/nanos/auth/AddressDetailView", flags: ['web'] },
  { name: "foam/nanos/auth/SignInView", flags: ['web'] },
  { name: "foam/nanos/auth/SignUpView", flags: ['web'] },
  { name: "foam/nanos/auth/PermissionedPropertyDAO" },
  { name: "foam/nanos/auth/ProfilePictureView", flags: ['web'] },
  { name: "foam/nanos/auth/twofactor/OTPAuthService" },
  { name: "foam/nanos/auth/twofactor/AbstractOTPAuthService" },
  { name: "foam/nanos/auth/twofactor/AbstractTOTPAuthService" },
  { name: "foam/nanos/auth/twofactor/ClientOTPAuthService" },
  { name: "foam/nanos/auth/twofactor/TwoFactorSignInView" },
  { name: "foam/nanos/auth/twofactor/refinements" },
  { name: "foam/nanos/auth/UserAndGroupAuthService" },
  { name: "foam/nanos/bench/Benchmark" },
  { name: "foam/nanos/boot/DAOConfigSummaryView", flags: ['web'] },
  { name: "foam/nanos/session/Session" },
  { name: "foam/nanos/session/SessionTimer" },
  { name: "foam/nanos/menu/AbstractMenu" },
  { name: "foam/nanos/menu/DAOMenu" },
  { name: "foam/nanos/menu/DAOMenu2" },
  { name: "foam/nanos/menu/DocumentMenu" },
  { name: "foam/nanos/menu/DocumentFileMenu" },
  { name: "foam/nanos/menu/LinkMenu" },
  { name: "foam/nanos/menu/ListMenu" },
  { name: "foam/nanos/menu/Menu" },
  { name: "foam/nanos/menu/MenuBar" },
  { name: "foam/nanos/menu/PopupMenu" },
  { name: "foam/nanos/menu/SubMenu" },
  { name: "foam/nanos/menu/SubMenuView" },
  { name: "foam/nanos/menu/TabsMenu" },
  { name: "foam/nanos/menu/ViewMenu" },
  { name: "foam/nanos/menu/ScriptMenu" },
  { name: "foam/nanos/menu/TreeAltView", flags: ['web'] },
  { name: "foam/nanos/menu/TreeGraphAltView" },
  { name: "foam/nanos/auth/PermissionTableView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/TopNavigation", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/FooterView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/SideNavigation", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/SideNavigationItemView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/NavigationView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/BusinessLogoView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/UserView", flags: ['web'] },
  { name: "foam/nanos/u2/navigation/SubMenuBar", flags: ['web'] },
  { name: "foam/nanos/script/Language" },
  { name: "foam/nanos/script/ScriptStatus" },
  { name: "foam/nanos/script/Script" },
  { name: "foam/nanos/script/TestRunnerScript" },
  { name: "foam/nanos/jetty/HttpServer" },
  { name: "foam/nanos/servlet/Servlet" },
  { name: "foam/nanos/servlet/ErrorPageMapping" },
  { name: "foam/nanos/servlet/FilterMapping" },
  { name: "foam/nanos/servlet/ServletMapping" },
  { name: "foam/nanos/servlet/VirtualHostRoutingServlet" },
  { name: "foam/nanos/test/Test" },
  { name: "foam/nanos/test/TestBorder" },
  { name: "foam/nanos/cron/Cron" },
  { name: "foam/nanos/cron/test/IntervalScheduleTest" },
  { name: "foam/nanos/cron/test/TimeOfDayScheduleTest" },
  { name: "foam/nanos/export/ExportDriverRegistry"},
  { name: "foam/nanos/export/ExportDriver" },
  { name: "foam/nanos/export/JSONDriver"},
  { name: "foam/nanos/export/JSONJDriver"},
  { name: "foam/nanos/export/XMLDriver"},
  { name: "foam/nanos/export/CSVDriver"},
  { name: "foam/nanos/auth/Relationships" },
  { name: "foam/nanos/NanoService" },
  { name: "foam/nanos/auth/twofactor/OTPKey" },
  { name: "foam/nanos/auth/AuthService" },
  { name: "foam/nanos/auth/ProxyAuthService" },
  { name: "foam/nanos/auth/CachedAuthServiceProxy" },
  { name: "foam/nanos/auth/ClientAuthService" },
  { name: "foam/nanos/auth/ClientLoginAuthService" },
  { name: "foam/nanos/auth/AgentAuthService" },
  { name: "foam/nanos/auth/ClientAgentAuthService" },
  { name: "foam/nanos/pm/PMTemperatureCellFormatter" },
  { name: "foam/nanos/pm/NullPM" },
  { name: "foam/nanos/pm/PM" },
  { name: "foam/nanos/pm/TemperatureCView" },
  { name: 'foam/nanos/notification/email/EmailMessage' },
  { name: 'foam/nanos/notification/email/EmailService' },
  { name: 'foam/nanos/notification/email/EmailTemplate' },
  { name: 'foam/nanos/notification/email/SMTPEmailService' },
  { name: 'foam/nanos/notification/email/Status' },
  { name: 'foam/nanos/notification/push/PushService' },
  { name: 'foam/nanos/notification/push/FirebasePushService' },
  { name: 'foam/nanos/demo/DemoObject' },
  { name: 'foam/nanos/demo/Demo' },
  { name: 'foam/nanos/http/Format' },
  { name: 'foam/nanos/http/Command' },
  { name: 'foam/nanos/http/WebAgent' },
  { name: "foam/nanos/http/ProxyWebAgent" },
  { name: "foam/nanos/http/HttpParameters" },
  { name: "foam/nanos/http/DefaultHttpParameters" },
  { name: "foam/nanos/doc/DocumentationView" },
  { name: 'foam/nanos/demo/relationship/CourseType' },
  { name: 'foam/nanos/demo/relationship/Course' },
  { name: 'foam/nanos/demo/relationship/Professor' },
  { name: 'foam/nanos/demo/relationship/Student' },
  { name: 'foam/nanos/demo/relationship/Controller' },
  { name: 'foam/nanos/notification/Notification'},
  { name: 'foam/nanos/notification/notifications/ScriptRunNotification'},
  { name: 'foam/nanos/notification/NotificationListView'},
  { name: 'foam/nanos/notification/NotificationRowView'},
  { name: 'foam/nanos/notification/NotificationSettingsView'},
  { name: 'foam/nanos/notification/NotificationView'},
  { name: 'foam/nanos/notification/NotificationNotificationView'},
  { name: 'foam/nanos/notification/notifications/ScriptRunNotificationNotificationView'},
  { name: 'foam/nanos/dashboard/Demo' },

  // Dig
  { name: "foam/nanos/dig/exception/DigErrorMessage" },
  { name: 'foam/nanos/dig/Argument'},
  { name: 'foam/nanos/dig/DIG' },
  { name: 'foam/nanos/dig/DigFileUploadView' },
  { name: 'foam/nanos/dig/DigSnippetView' },
  { name: 'foam/nanos/dig/DUG' },
  { name: 'foam/nanos/dig/LinkView' },
  { name: 'foam/nanos/dig/SUGAR' },

  { name: 'foam/nanos/notification/email/ClientPOP3EmailService'},
  { name: 'foam/nanos/notification/email/POP3Email'},

  // foam/nanos/ruler
  { name: "foam/nanos/ruler/Operations" },
  { name: "foam/nanos/ruler/Rule" },
  { name: "foam/nanos/ruler/RuleAction" },
  { name: "foam/nanos/ruler/RuleHistory" },
  { name: "foam/nanos/ruler/RuleHistoryStatus" },
  { name: "foam/nanos/ruler/RulerDAO" },
  { name: "foam/nanos/ruler/ScriptPredicate"},
  { name: "foam/nanos/ruler/UpdateRulesListSink" },
  { name: "foam/nanos/ruler/TestedRule" },
  { name: "foam/nanos/ruler/RulerProbe" },
  { name: "foam/nanos/ruler/UserRefines" },
  { name: "foam/nanos/test/EchoService" },
  { name: "foam/nanos/test/SerializationTestEchoService" },
  { name: "foam/nanos/analytics/Foldable" },
  { name: "foam/nanos/analytics/Candlestick" },
  { name: "foam/nanos/test/ClientEchoService" },

  { name: "foam/dao/jdbc/JDBCConnectionSpec" },

  // foam/nanos/crunch
  { name: "foam/nanos/crunch/crunchtest/FakeTestObject" },
  // models
  { name: "foam/nanos/crunch/Capability" },
  { name: "foam/nanos/crunch/CapabilityJunctionStatus" },
  { name: "foam/nanos/crunch/UserCapabilityJunctionRefine" },
  //daos
  { name: "foam/nanos/crunch/UserCapabilityJunctionDAO" },
  //rules
  { name: "foam/nanos/crunch/SendNotificationOnTopLevelCapabilityStatusUpdate" },
  { name: "foam/nanos/crunch/IsUserCapabilityJunctionStatusUpdate" },
  { name: "foam/nanos/crunch/RemoveJunctionsOnUserRemoval" },
  //authservice
  { name: "foam/nanos/auth/CapabilityAuthService" },
  // google
  { name: "foam/nanos/geocode/GoogleMapsCredentials" }
]);
<|endoftext|>