<commit_before>/* A place to dump random javascript */

// Return a link based on the current URL to the archive...

function cytoview_link() {
  URL = document.location.href;
  document.location = URL.replace(/(\w+view)/,'cytoview');
  return true;
}
function archive( release ) {
  URL = document.location.href;
  document.location = URL.replace(/^https?:\/\/[^\/]+/,'http://'+release+'.archive.ensembl.org');
  return true;
}

function login_link() {
  URL = escape(document.location.href);
  document.location = '/common/user_login?url=' + URL;
  return true;
}

function logout_link() {
  URL = escape(document.location.href);
  document.location = '/common/user_logout?url=' + URL;
  return true;
}

function bookmark_link() {
  URL = escape(document.location.href);

  var page_title;
  titles = document.getElementsByTagName("title");
  // assume first title tag is actual page title
  children = titles[0].childNodes;
  for (i=0; i<children.length; i++) {
    child = children[i];
    // look for text node
    if (child.nodeType == 3) {
      page_title = child.nodeValue;
    }
  }

  document.location = '/common/add_bookmark?node=name_bookmark;bm_name=' + page_title + ';bm_url=' + URL;
  return true;
}
<commit_msg>Tweaked logout_link to send users back to home page if they log out from accountview<commit_after>/* A place to dump random javascript */

// Return a link based on the current URL to the archive...

function cytoview_link() {
  URL = document.location.href;
  document.location = URL.replace(/(\w+view)/,'cytoview');
  return true;
}
function archive( release ) {
  URL = document.location.href;
  document.location = URL.replace(/^https?:\/\/[^\/]+/,'http://'+release+'.archive.ensembl.org');
  return true;
}

function login_link() {
  URL = escape(document.location.href);
  document.location = '/common/user_login?url=' + URL;
  return true;
}

function logout_link() {
  URL = escape(document.location.href);
  // return to home page if logging out from account management
  if (URL.indexOf('logout') > -1 || URL.indexOf('account') > -1) {
    URL = '/';
  }
  document.location = '/common/user_logout?url=' + URL;
  return true;
}

function bookmark_link() {
  URL = escape(document.location.href);

  var page_title;
  titles = document.getElementsByTagName("title");
  // assume first title tag is actual page title
  children = titles[0].childNodes;
  for (i=0; i<children.length; i++) {
    child = children[i];
    // look for text node
    if (child.nodeType == 3) {
      page_title = child.nodeValue;
    }
  }

  document.location = '/common/add_bookmark?node=name_bookmark;bm_name=' + page_title + ';bm_url=' + URL;
  return true;
}
<|endoftext|>


<commit_before>/**
 * Copyright 2013, 2014 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var fs = require('fs-extra');
var when = require('when');
var nodeFn = require('when/node/function');
var keys = require('when/keys');
var fspath = require("path");
var mkdirp = fs.mkdirs;

var log = require("../log");

var promiseDir = nodeFn.lift(mkdirp);

var initialFlowLoadComplete = false;
var settings;
var flowsFile;
var flowsFullPath;
var flowsFileBackup;
var credentialsFile;
var credentialsFileBackup;
var oldCredentialsFile;
var sessionsFile;
var libDir;
var libFlowsDir;
var globalSettingsFile;

function getFileMeta(root,path) {
    var fn = fspath.join(root,path);
    var fd = fs.openSync(fn,"r");
    var size = fs.fstatSync(fd).size;
    var meta = {};
    var read = 0;
    var length = 10;
    var remaining = "";
    var buffer = Buffer(length);
    while(read < size) {
        read+=fs.readSync(fd,buffer,0,length);
        var data = remaining+buffer.toString();
        var parts = data.split("\n");
        remaining = parts.splice(-1);
        for (var i=0;i<parts.length;i+=1) {
            var match = /^\/\/ (\w+): (.*)/.exec(parts[i]);
            if (match) {
                meta[match[1]] = match[2];
            } else {
                read = size;
                break;
            }
        }
    }
    fs.closeSync(fd);
    return meta;
}

function getFileBody(root,path) {
    var body = "";
    var fn = fspath.join(root,path);
    var fd = fs.openSync(fn,"r");
    var size = fs.fstatSync(fd).size;
    var scanning = true;
    var read = 0;
    var length = 50;
    var remaining = "";
    var buffer = Buffer(length);
    while(read < size) {
        var thisRead = fs.readSync(fd,buffer,0,length);
        read += thisRead;
        if (scanning) {
            var data = remaining+buffer.slice(0,thisRead).toString();
            var parts = data.split("\n");
            remaining = parts.splice(-1)[0];
            for (var i=0;i<parts.length;i+=1) {
                if (! /^\/\/ \w+: /.test(parts[i])) {
                    scanning = false;
                    body += parts[i]+"\n";
                }
            }
            if (! /^\/\/ \w+: /.test(remaining)) {
                scanning = false;
            }
            if (!scanning) {
                body += remaining;
            }
        } else {
            body += buffer.slice(0,thisRead).toString();
        }
    }
    fs.closeSync(fd);
    return body;
}

/**
 * Write content to a file using UTF8 encoding.
 * This forces a fsync before completing to ensure
 * the write hits disk.
 */
function writeFile(path,content) {
    return when.promise(function(resolve,reject) {
        var stream = fs.createWriteStream(path);
        stream.on('open',function(fd) {
            stream.end(content,'utf8',function() {
                fs.fsync(fd,resolve);
            });
        });
        stream.on('error',function(err) {
            reject(err);
        });
    });
}

var localfilesystem = {
    init: function(_settings) {
        settings = _settings;

        var promises = [];

        if (!settings.userDir) {
            try {
                fs.statSync(fspath.join(process.env.NODE_RED_HOME,".config.json"));
                settings.userDir = process.env.NODE_RED_HOME;
            } catch(err) {
                settings.userDir = fspath.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE || process.env.NODE_RED_HOME,".node-red");
                if (!settings.readOnly) {
                    promises.push(promiseDir(settings.userDir));
                }
            }
        }

        if (settings.flowFile) {
            flowsFile = settings.flowFile;
            // handle Unix and Windows "C:\"
            if ((flowsFile[0] == "/") || (flowsFile[1] == ":")) {
                // Absolute path
                flowsFullPath = flowsFile;
            } else if (flowsFile.substring(0,2) === "./") {
                // Relative to cwd
                flowsFullPath = fspath.join(process.cwd(),flowsFile);
            } else {
                try {
                    fs.statSync(fspath.join(process.cwd(),flowsFile));
                    // Found in cwd
                    flowsFullPath = fspath.join(process.cwd(),flowsFile);
                } catch(err) {
                    // Use userDir
                    flowsFullPath = fspath.join(settings.userDir,flowsFile);
                }
            }

        } else {
            flowsFile = 'flows_'+require('os').hostname()+'.json';
            flowsFullPath = fspath.join(settings.userDir,flowsFile);
        }
        var ffExt = fspath.extname(flowsFullPath);
        var ffName = fspath.basename(flowsFullPath);
        var ffBase = fspath.basename(flowsFullPath,ffExt);
        var ffDir = fspath.dirname(flowsFullPath);

        credentialsFile = fspath.join(settings.userDir,ffBase+"_cred"+ffExt);
        credentialsFileBackup = fspath.join(settings.userDir,"."+ffBase+"_cred"+ffExt+".backup");

        oldCredentialsFile = fspath.join(settings.userDir,"credentials.json");

        flowsFileBackup = fspath.join(ffDir,"."+ffName+".backup");

        sessionsFile = fspath.join(settings.userDir,".sessions.json");

        libDir = fspath.join(settings.userDir,"lib");
        libFlowsDir = fspath.join(libDir,"flows");

        globalSettingsFile = fspath.join(settings.userDir,".config.json");

        if (!settings.readOnly) {
            promises.push(promiseDir(libFlowsDir));
        }

        return when.all(promises);
    },

    getFlows: function() {
        return when.promise(function(resolve) {
            if (!initialFlowLoadComplete) {
                initialFlowLoadComplete = true;
                log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
                log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
            }
            fs.readFile(flowsFullPath,'utf8',function(err,data) {
                if (!err) {
                    return resolve(JSON.parse(data));
                }
                log.info(log._("storage.localfilesystem.create"));
                resolve([]);
            });
        });
    },

    saveFlows: function(flows) {
        if (settings.readOnly) {
            return when.resolve();
        }

        try {
            fs.renameSync(flowsFullPath,flowsFileBackup);
        } catch(err) {
        }

        var flowData;

        if (settings.flowFilePretty) {
            flowData = JSON.stringify(flows,null,4);
        } else {
            flowData = JSON.stringify(flows);
        }
        return writeFile(flowsFullPath, flowData);
    },

    getCredentials: function() {
        return when.promise(function(resolve) {
            fs.readFile(credentialsFile,'utf8',function(err,data) {
                if (!err) {
                    resolve(JSON.parse(data));
                } else {
                    fs.readFile(oldCredentialsFile,'utf8',function(err,data) {
                        if (!err) {
                            resolve(JSON.parse(data));
                        } else {
                            resolve({});
                        }
                    });
                }
            });
        });
    },

    saveCredentials: function(credentials) {
        if (settings.readOnly) {
            return when.resolve();
        }

        try {
            fs.renameSync(credentialsFile,credentialsFileBackup);
        } catch(err) {
        }
        var credentialData;
        if (settings.flowFilePretty) {
            credentialData = JSON.stringify(credentials,null,4);
        } else {
            credentialData = JSON.stringify(credentials);
        }
        return writeFile(credentialsFile, credentialData);
    },

    getSettings: function() {
        return when.promise(function(resolve,reject) {
            fs.readFile(globalSettingsFile,'utf8',function(err,data) {
                if (!err) {
                    try {
                        return resolve(JSON.parse(data));
                    } catch(err2) {
                        log.trace("Corrupted config detected - resetting");
                    }
                }
                return resolve({});
            })
        })
    },
    saveSettings: function(settings) {
        if (settings.readOnly) {
            return when.resolve();
        }
        return writeFile(globalSettingsFile,JSON.stringify(settings,null,1));
    },
    getSessions: function() {
        return when.promise(function(resolve,reject) {
            fs.readFile(sessionsFile,'utf8',function(err,data){
                if (!err) {
                    try {
                        return resolve(JSON.parse(data));
                    } catch(err2) {
                        log.trace("Corrupted sessions file - resetting");
                    }
                }
                resolve({});
            })
        });
    },
    saveSessions: function(sessions) {
        if (settings.readOnly) {
            return when.resolve();
        }
        return writeFile(sessionsFile,JSON.stringify(sessions));
    },

    getLibraryEntry: function(type,path) {
        var root = fspath.join(libDir,type);
        var rootPath = fspath.join(libDir,type,path);
        return promiseDir(root).then(function () {
            return nodeFn.call(fs.lstat, rootPath).then(function(stats) {
                if (stats.isFile()) {
                    return getFileBody(root,path);
                }
                if (path.substr(-1) == '/') {
                    path = path.substr(0,path.length-1);
                }
                return nodeFn.call(fs.readdir, rootPath).then(function(fns) {
                    var dirs = [];
                    var files = [];
                    fns.sort().filter(function(fn) {
                        var fullPath = fspath.join(path,fn);
                        var absoluteFullPath = fspath.join(root,fullPath);
                        if (fn[0] != ".") {
                            var stats = fs.lstatSync(absoluteFullPath);
                            if (stats.isDirectory()) {
                                dirs.push(fn);
                            } else {
                                var meta = getFileMeta(root,fullPath);
                                meta.fn = fn;
                                files.push(meta);
                            }
                        }
                    });
                    return dirs.concat(files);
                });
            }).otherwise(function(err) {
                if (type === "flows" && !/\.json$/.test(path)) {
                    return localfilesystem.getLibraryEntry(type,path+".json")
                        .otherwise(function(e) {
                            throw err;
                        });
                } else {
                    throw err;
                }
            });
        });
    },

    saveLibraryEntry: function(type,path,meta,body) {
        if (settings.readOnly) {
            return when.resolve();
        }
        var fn = fspath.join(libDir, type, path);
        var headers = "";
        for (var i in meta) {
            if (meta.hasOwnProperty(i)) {
                headers += "// "+i+": "+meta[i]+"\n";
            }
        }
        return promiseDir(fspath.dirname(fn)).then(function () {
            writeFile(fn,headers+body);
        });
    }
};

module.exports = localfilesystem;
<commit_msg>Create node_modules in userDir<commit_after>/**
 * Copyright 2013, 2014 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

var fs = require('fs-extra');
var when = require('when');
var nodeFn = require('when/node/function');
var keys = require('when/keys');
var fspath = require("path");
var mkdirp = fs.mkdirs;

var log = require("../log");

var promiseDir = nodeFn.lift(mkdirp);

var initialFlowLoadComplete = false;
var settings;
var flowsFile;
var flowsFullPath;
var flowsFileBackup;
var credentialsFile;
var credentialsFileBackup;
var oldCredentialsFile;
var sessionsFile;
var libDir;
var libFlowsDir;
var globalSettingsFile;

function getFileMeta(root,path) {
    var fn = fspath.join(root,path);
    var fd = fs.openSync(fn,"r");
    var size = fs.fstatSync(fd).size;
    var meta = {};
    var read = 0;
    var length = 10;
    var remaining = "";
    var buffer = Buffer(length);
    while(read < size) {
        read+=fs.readSync(fd,buffer,0,length);
        var data = remaining+buffer.toString();
        var parts = data.split("\n");
        remaining = parts.splice(-1);
        for (var i=0;i<parts.length;i+=1) {
            var match = /^\/\/ (\w+): (.*)/.exec(parts[i]);
            if (match) {
                meta[match[1]] = match[2];
            } else {
                read = size;
                break;
            }
        }
    }
    fs.closeSync(fd);
    return meta;
}

function getFileBody(root,path) {
    var body = "";
    var fn = fspath.join(root,path);
    var fd = fs.openSync(fn,"r");
    var size = fs.fstatSync(fd).size;
    var scanning = true;
    var read = 0;
    var length = 50;
    var remaining = "";
    var buffer = Buffer(length);
    while(read < size) {
        var thisRead = fs.readSync(fd,buffer,0,length);
        read += thisRead;
        if (scanning) {
            var data = remaining+buffer.slice(0,thisRead).toString();
            var parts = data.split("\n");
            remaining = parts.splice(-1)[0];
            for (var i=0;i<parts.length;i+=1) {
                if (! /^\/\/ \w+: /.test(parts[i])) {
                    scanning = false;
                    body += parts[i]+"\n";
                }
            }
            if (! /^\/\/ \w+: /.test(remaining)) {
                scanning = false;
            }
            if (!scanning) {
                body += remaining;
            }
        } else {
            body += buffer.slice(0,thisRead).toString();
        }
    }
    fs.closeSync(fd);
    return body;
}

/**
 * Write content to a file using UTF8 encoding.
 * This forces a fsync before completing to ensure
 * the write hits disk.
 */
function writeFile(path,content) {
    return when.promise(function(resolve,reject) {
        var stream = fs.createWriteStream(path);
        stream.on('open',function(fd) {
            stream.end(content,'utf8',function() {
                fs.fsync(fd,resolve);
            });
        });
        stream.on('error',function(err) {
            reject(err);
        });
    });
}

var localfilesystem = {
    init: function(_settings) {
        settings = _settings;

        var promises = [];

        if (!settings.userDir) {
            try {
                fs.statSync(fspath.join(process.env.NODE_RED_HOME,".config.json"));
                settings.userDir = process.env.NODE_RED_HOME;
            } catch(err) {
                settings.userDir = fspath.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE || process.env.NODE_RED_HOME,".node-red");
                if (!settings.readOnly) {
                    promises.push(promiseDir(fspath.join(settings.userDir,"node_modules")));
                }
            }
        }

        if (settings.flowFile) {
            flowsFile = settings.flowFile;
            // handle Unix and Windows "C:\"
            if ((flowsFile[0] == "/") || (flowsFile[1] == ":")) {
                // Absolute path
                flowsFullPath = flowsFile;
            } else if (flowsFile.substring(0,2) === "./") {
                // Relative to cwd
                flowsFullPath = fspath.join(process.cwd(),flowsFile);
            } else {
                try {
                    fs.statSync(fspath.join(process.cwd(),flowsFile));
                    // Found in cwd
                    flowsFullPath = fspath.join(process.cwd(),flowsFile);
                } catch(err) {
                    // Use userDir
                    flowsFullPath = fspath.join(settings.userDir,flowsFile);
                }
            }

        } else {
            flowsFile = 'flows_'+require('os').hostname()+'.json';
            flowsFullPath = fspath.join(settings.userDir,flowsFile);
        }
        var ffExt = fspath.extname(flowsFullPath);
        var ffName = fspath.basename(flowsFullPath);
        var ffBase = fspath.basename(flowsFullPath,ffExt);
        var ffDir = fspath.dirname(flowsFullPath);

        credentialsFile = fspath.join(settings.userDir,ffBase+"_cred"+ffExt);
        credentialsFileBackup = fspath.join(settings.userDir,"."+ffBase+"_cred"+ffExt+".backup");

        oldCredentialsFile = fspath.join(settings.userDir,"credentials.json");

        flowsFileBackup = fspath.join(ffDir,"."+ffName+".backup");

        sessionsFile = fspath.join(settings.userDir,".sessions.json");

        libDir = fspath.join(settings.userDir,"lib");
        libFlowsDir = fspath.join(libDir,"flows");

        globalSettingsFile = fspath.join(settings.userDir,".config.json");

        if (!settings.readOnly) {
            promises.push(promiseDir(libFlowsDir));
        }

        return when.all(promises);
    },

    getFlows: function() {
        return when.promise(function(resolve) {
            if (!initialFlowLoadComplete) {
                initialFlowLoadComplete = true;
                log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
                log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
            }
            fs.readFile(flowsFullPath,'utf8',function(err,data) {
                if (!err) {
                    return resolve(JSON.parse(data));
                }
                log.info(log._("storage.localfilesystem.create"));
                resolve([]);
            });
        });
    },

    saveFlows: function(flows) {
        if (settings.readOnly) {
            return when.resolve();
        }

        try {
            fs.renameSync(flowsFullPath,flowsFileBackup);
        } catch(err) {
        }

        var flowData;

        if (settings.flowFilePretty) {
            flowData = JSON.stringify(flows,null,4);
        } else {
            flowData = JSON.stringify(flows);
        }
        return writeFile(flowsFullPath, flowData);
    },

    getCredentials: function() {
        return when.promise(function(resolve) {
            fs.readFile(credentialsFile,'utf8',function(err,data) {
                if (!err) {
                    resolve(JSON.parse(data));
                } else {
                    fs.readFile(oldCredentialsFile,'utf8',function(err,data) {
                        if (!err) {
                            resolve(JSON.parse(data));
                        } else {
                            resolve({});
                        }
                    });
                }
            });
        });
    },

    saveCredentials: function(credentials) {
        if (settings.readOnly) {
            return when.resolve();
        }

        try {
            fs.renameSync(credentialsFile,credentialsFileBackup);
        } catch(err) {
        }
        var credentialData;
        if (settings.flowFilePretty) {
            credentialData = JSON.stringify(credentials,null,4);
        } else {
            credentialData = JSON.stringify(credentials);
        }
        return writeFile(credentialsFile, credentialData);
    },

    getSettings: function() {
        return when.promise(function(resolve,reject) {
            fs.readFile(globalSettingsFile,'utf8',function(err,data) {
                if (!err) {
                    try {
                        return resolve(JSON.parse(data));
                    } catch(err2) {
                        log.trace("Corrupted config detected - resetting");
                    }
                }
                return resolve({});
            })
        })
    },
    saveSettings: function(settings) {
        if (settings.readOnly) {
            return when.resolve();
        }
        return writeFile(globalSettingsFile,JSON.stringify(settings,null,1));
    },
    getSessions: function() {
        return when.promise(function(resolve,reject) {
            fs.readFile(sessionsFile,'utf8',function(err,data){
                if (!err) {
                    try {
                        return resolve(JSON.parse(data));
                    } catch(err2) {
                        log.trace("Corrupted sessions file - resetting");
                    }
                }
                resolve({});
            })
        });
    },
    saveSessions: function(sessions) {
        if (settings.readOnly) {
            return when.resolve();
        }
        return writeFile(sessionsFile,JSON.stringify(sessions));
    },

    getLibraryEntry: function(type,path) {
        var root = fspath.join(libDir,type);
        var rootPath = fspath.join(libDir,type,path);
        return promiseDir(root).then(function () {
            return nodeFn.call(fs.lstat, rootPath).then(function(stats) {
                if (stats.isFile()) {
                    return getFileBody(root,path);
                }
                if (path.substr(-1) == '/') {
                    path = path.substr(0,path.length-1);
                }
                return nodeFn.call(fs.readdir, rootPath).then(function(fns) {
                    var dirs = [];
                    var files = [];
                    fns.sort().filter(function(fn) {
                        var fullPath = fspath.join(path,fn);
                        var absoluteFullPath = fspath.join(root,fullPath);
                        if (fn[0] != ".") {
                            var stats = fs.lstatSync(absoluteFullPath);
                            if (stats.isDirectory()) {
                                dirs.push(fn);
                            } else {
                                var meta = getFileMeta(root,fullPath);
                                meta.fn = fn;
                                files.push(meta);
                            }
                        }
                    });
                    return dirs.concat(files);
                });
            }).otherwise(function(err) {
                if (type === "flows" && !/\.json$/.test(path)) {
                    return localfilesystem.getLibraryEntry(type,path+".json")
                        .otherwise(function(e) {
                            throw err;
                        });
                } else {
                    throw err;
                }
            });
        });
    },

    saveLibraryEntry: function(type,path,meta,body) {
        if (settings.readOnly) {
            return when.resolve();
        }
        var fn = fspath.join(libDir, type, path);
        var headers = "";
        for (var i in meta) {
            if (meta.hasOwnProperty(i)) {
                headers += "// "+i+": "+meta[i]+"\n";
            }
        }
        return promiseDir(fspath.dirname(fn)).then(function () {
            writeFile(fn,headers+body);
        });
    }
};

module.exports = localfilesystem;
<|endoftext|>


<commit_before>"use strict";
var SEARCH_TOKEN_MIN_LENGTH = 3;
/**
 * Performs a click event for a password mask
 * Appends the anti-forgery token in the post message and header.
 * The click logic is complex:
 * 1. The mask click event is bound.
 * 2. on click it is replaced with loading and the ajax request is released. The click handler is removed.
 * 3. the ajax response replaces the mask body with the result, both fail and success. A timer is kicked off.
 * 4. the timer returns the mask and rebinds the click event to the clicked span. It appears to be recursive.
 * @param {any} element
 */
function passwordClick(element) {
    var assetId = element.data("id");
    var postUrl = "/Projects/GetPassword";
    var token = $('input[name="__RequestVerificationToken"]').val(); // prepare token
    var projectId = $("input#projectId").val();
    var postData = {
        __RequestVerificationToken: token,
        formDigest: '2134',
        projectId: projectId,
        assetId: assetId
    };

    // 2. replace with loading, unbind click event and release ajax request with token
    var containerSpan = element;
    containerSpan.html("Loading...");
    containerSpan.removeClass("ob-pointer");
    containerSpan.off("click");

    $.ajax({
        type: 'POST',
        url: postUrl,
        data: JSON.stringify(postData),
        contentType: 'application/json',
        cache: false,
        headers: { 'RequestVerificationToken': token },
        success: function (successResult) {
            // 3. replace mask with result and start timer
            containerSpan.html(successResult);

            setTimeout(function () {
                // 4. return mask and rebind click event
                containerSpan.html("******");
                containerSpan.addClass("ob-pointer");
                containerSpan.on('click', function (e) {
                    passwordClick($(this));
                })
            }, 2000);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            // 3. replace mask with result and start timer
            containerSpan.html(textStatus);
            setTimeout(function () {
                // 4. return mask and rebind click event
                containerSpan.html("******");
                containerSpan.addClass("ob-pointer");
                containerSpan.on('click', function (e) {
                    passwordClick($(this));
                })
            }, 2000);
        }
    });
}

/**
 * Performs a copy to clipboard for the selected password
 * Appends the anti-forgery token in the post message and header.
 * The click logic is complex:
 * @param {any} element
 */
function passwordCopy(element) {
    var assetId = element.data("id");
    var postUrl = "/Projects/GetPassword";
    var token = $('input[name="__RequestVerificationToken"]').val(); // prepare token
    var projectId = $("input#projectId").val();
    var postData = {
        __RequestVerificationToken: token,
        formDigest: '2134',
        projectId: projectId,
        assetId: assetId
    };

    $.ajax({
        type: 'POST',
        url: postUrl,
        data: JSON.stringify(postData),
        contentType: 'application/json',
        cache: false,
        async: false, //The AJAX should be SYNCHRONOUS, in order document.execCommand("copy") to work successfully
        headers: { 'RequestVerificationToken': token },
        success: function (successResult) {
            copyText(successResult, element);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            var toolTip = $("
Copied!
");
            toolTip.insertAfter(copyIconElement);

            toolTip.text(textStatus);
            toolTip.css("background-color", "#f00");
            toolTip.css("color", "#fff");
            toolTip.show();

            setTimeout(function () {
                toolTip.remove();
            }, 1000);
        }
    });
}

/**
 * Performs a copy to clipboard for the selected login
 * The click logic is complex:
 * @param {any} element
 */
function loginCopy(element) {
    var assetId = element.data("id");
    var textAfter = $(element).get(0).nextSibling.textContent.trim();
    copyText(textAfter, element);
}

/**
 * Performs a copy to clipboard for the given text and shows a tooltip message for success/error near the given parentElement
 * @param {string} text The text to copy to clipboard
 * @param {any} parentElement The element to put the tooltip message next to
 */
function copyText(text, parentElement) {
    let copyFrom = document.createElement("textarea");
    copyFrom.style.width = 0;
    copyFrom.style.height = 0;
    copyFrom.style.position = 'fixed';
    copyFrom.style.top = '-1000px';
    copyFrom.style.left = '-1000px';
    document.body.appendChild(copyFrom);

    // This element here is added only for IE check - if the copy-paste is successed - then the user allowed the browser to copy
    let pasteTo = document.createElement("textarea");
    pasteTo.style.width = 0;
    pasteTo.style.height = 0;
    pasteTo.style.position = 'fixed';
    pasteTo.style.top = '-1000px';
    pasteTo.style.left = '-1000px';
    document.body.appendChild(pasteTo);

    copyFrom.textContent = text;
    copyFrom.select();
    var canCopy = document.execCommand("copy");
    pasteTo.select();
    var canPaste = document.execCommand('paste');

    var copySuccess;
    if (canPaste) {
        // This is the actual check (for IE only) - if the copy-paste is successed - then the user allowed the browser to copy
        copySuccess = pasteTo.value === copyFrom.value;

        // Since IE supports paste,
        // if user allowed clipboard: copySuccess === true
        // else: copySuccess === false
        if (copySuccess) {
            var toolTip = $("
Copied!
");
            toolTip.insertAfter(parentElement);

            setTimeout(function () {
                toolTip.remove();
            }, 2000);
        }
        else {
            var toolTip = $("
NOT COPIED! Please, refresh the page and allow your browser to copy!
");
            toolTip.insertAfter(parentElement);

            toolTip.css("background-color", "#f00");
            toolTip.css("color", "#fff");
            toolTip.css("width", "300px");
            toolTip.css("left", "80px");

            setTimeout(function () {
                toolTip.remove();
            }, 2000);
        }

    }
    else if (canPaste == false) { // This means that the Browser is not IE (Chrome, Firefox, Edge, etc.)
        var toolTip = $("
Copied!
");
        toolTip.insertAfter(parentElement);

        setTimeout(function () {
            toolTip.remove();
        }, 2000);
    }
}

/**
 * Binds the click event on all password labels
 */
function bindAllPasswordLabels() {
    $("span.ob-passwordLabel").on('click', function (e) {
        window.appInsights.trackEvent("passwordClicked", $(this).text());
        passwordClick($(this));
    });
};

/**
 * Binds the click event on all password copy-to-clipboard
 */
function bindAllPasswordCopyToClipboard() {
    $("button.ob-copyIcon").on('click', function (e) {
        var assetId = $(this).data("id");
        var iconType = $(this).data("type");

        if (iconType == "password") {
            passwordCopy($(this));
        }
        else if (iconType == "login") {
            loginCopy($(this));
        }
    });
};

function bindSearchAssetInput() {
    $("div.dropdown#assetSearchAutocomplete input").on('focusin', function (e) {
        if ($(this).val().length >= SEARCH_TOKEN_MIN_LENGTH) {
            $(this).triggerHandler('input');
        }
    });

    $(document).on('click', function (e) {
        var dropdown = $('div.dropdown#assetSearchAutocomplete ul.dropdown-menu');
        if (!dropdown.find($(e.target)).length) {
            dropdown.addClass('invisible-asset-holder');
        }
    });

    var timeout = null;
    $("div.dropdown#assetSearchAutocomplete input").on('input', function (e) {
        var searchPrefix = $(this).val(),
            getUrl = "/Home/GetAssetResults?searchToken=" + searchPrefix,
            dropdown = $(this).siblings('ul.dropdown-menu'),
            spinner = $(this).parents('div.row').find('div.loader');

        if (searchPrefix.length < SEARCH_TOKEN_MIN_LENGTH) {
            spinner.hide();
            dropdown.addClass('invisible-asset-holder');
            return;
        }

        spinner.show();
        clearTimeout(timeout);
        timeout = setTimeout(function () {
            var context = {
                input: this,
                searchPrefix: searchPrefix,
                spinner: spinner,
                dropdown: dropdown
            };

            $.ajax({
                type: 'GET',
                url: getUrl,
                contentType: 'application/json',
                cache: false,
                success: function (successResult) {
                    if ($(this.input).val() !== this.searchPrefix) {
                        return;
                    }

                    var dropdown = this.dropdown;
                    if (successResult.length) {
                        successResult.sort(function (a, b) {
                            return a.displayTitle.toString().toLowerCase() > b.displayTitle.toString().toLowerCase();
                        });

                        dropdown.empty();
                        for (var i = 0; i < successResult.length; i++) {
                            var entry = successResult[i];
                            dropdown.append('
' + entry.displayTitle + '
');
                        }

                        dropdown.removeClass('invisible-asset-holder');
                    }
                    else {
                        dropdown.addClass('invisible-asset-holder');
                    }

                    this.spinner.hide();
                }.bind(context),
                error: function () {
                    spinner.hide();
                    dropdown.addClass('invisible-asset-holder');
                }.bind(context)
            });
        }.bind(this), 800);
    });
};

$(document).ready(function () {
    // Bind all password label click functions
    bindAllPasswordLabels();
    bindAllPasswordCopyToClipboard();
    // bind asset search box
    bindSearchAssetInput();
});<commit_msg>JS style fixes<commit_after>"use strict";
var SEARCH_TOKEN_MIN_LENGTH = 3;
/**
 * Performs a click event for a password mask
 * Appends the anti-forgery token in the post message and header.
 * The click logic is complex:
 * 1. The mask click event is bound.
 * 2. on click it is replaced with loading and the ajax request is released. The click handler is removed.
 * 3. the ajax response replaces the mask body with the result, both fail and success. A timer is kicked off.
 * 4. the timer returns the mask and rebinds the click event to the clicked span. It appears to be recursive.
 * @param {any} element
 */
function passwordClick(element) {
    var assetId = element.data("id");
    var postUrl = "/Projects/GetPassword";
    var token = $('input[name="__RequestVerificationToken"]').val(); // prepare token
    var projectId = $("input#projectId").val();
    var postData = {
        __RequestVerificationToken: token,
        formDigest: '2134',
        projectId: projectId,
        assetId: assetId
    };

    // 2. replace with loading, unbind click event and release ajax request with token
    var containerSpan = element;
    containerSpan.html("Loading...");
    containerSpan.removeClass("ob-pointer");
    containerSpan.off("click");

    $.ajax({
        type: 'POST',
        url: postUrl,
        data: JSON.stringify(postData),
        contentType: 'application/json',
        cache: false,
        headers: { 'RequestVerificationToken': token },
        success: function (successResult) {
            // 3. replace mask with result and start timer
            containerSpan.html(successResult);

            setTimeout(function () {
                // 4. return mask and rebind click event
                containerSpan.html("******");
                containerSpan.addClass("ob-pointer");
                containerSpan.on('click', function (e) {
                    passwordClick($(this));
                })
            }, 2000);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            // 3. replace mask with result and start timer
            containerSpan.html(textStatus);
            setTimeout(function () {
                // 4. return mask and rebind click event
                containerSpan.html("******");
                containerSpan.addClass("ob-pointer");
                containerSpan.on('click', function (e) {
                    passwordClick($(this));
                })
            }, 2000);
        }
    });
}

/**
 * Performs a copy to clipboard for the selected password
 * Appends the anti-forgery token in the post message and header.
 * The click logic is complex:
 * @param {any} element
 */
function passwordCopy(element) {
    var assetId = element.data("id");
    var postUrl = "/Projects/GetPassword";
    var token = $('input[name="__RequestVerificationToken"]').val(); // prepare token
    var projectId = $("input#projectId").val();
    var postData = {
        __RequestVerificationToken: token,
        formDigest: '2134',
        projectId: projectId,
        assetId: assetId
    };

    $.ajax({
        type: 'POST',
        url: postUrl,
        data: JSON.stringify(postData),
        contentType: 'application/json',
        cache: false,
        async: false, //The AJAX should be SYNCHRONOUS, in order document.execCommand("copy") to work successfully
        headers: { 'RequestVerificationToken': token },
        success: function (successResult) {
            copyText(successResult, element);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            var toolTip = $("
Copied!
");
            toolTip.insertAfter(copyIconElement);

            toolTip.text(textStatus);
            toolTip.css("background-color", "#f00");
            toolTip.css("color", "#fff");
            toolTip.show();

            setTimeout(function () {
                toolTip.remove();
            }, 1000);
        }
    });
}

/**
 * Performs a copy to clipboard for the selected login
 * The click logic is complex:
 * @param {any} element
 */
function loginCopy(element) {
    var assetId = element.data("id");
    var textAfter = $(element).get(0).nextSibling.textContent.trim();
    copyText(textAfter, element);
}

/**
 * Performs a copy to clipboard for the given text and shows a tooltip message for success/error near the given parentElement
 * @param {string} text The text to copy to clipboard
 * @param {any} parentElement The element to put the tooltip message next to
 */
function copyText(text, parentElement) {
    let copyFrom = document.createElement("textarea");
    copyFrom.style.width = 0;
    copyFrom.style.height = 0;
    copyFrom.style.position = 'fixed';
    copyFrom.style.top = '-1000px';
    copyFrom.style.left = '-1000px';
    document.body.appendChild(copyFrom);

    // This element here is added only for IE check - if the copy-paste is successed - then the user allowed the browser to copy
    let pasteTo = document.createElement("textarea");
    pasteTo.style.width = 0;
    pasteTo.style.height = 0;
    pasteTo.style.position = 'fixed';
    pasteTo.style.top = '-1000px';
    pasteTo.style.left = '-1000px';
    document.body.appendChild(pasteTo);

    copyFrom.textContent = text;
    copyFrom.select();
    var canCopy = document.execCommand("copy");
    pasteTo.select();
    var canPaste = document.execCommand('paste');

    var copySuccess;
    if (canPaste) {
        // This is the actual check (for IE only) - if the copy-paste is successed - then the user allowed the browser to copy
        copySuccess = pasteTo.value === copyFrom.value;

        // Since IE supports paste,
        // if user allowed clipboard: copySuccess === true
        // else: copySuccess === false
        if (copySuccess) {
            var toolTip = $("
Copied!
");
            toolTip.insertAfter(parentElement);

            setTimeout(function () {
                toolTip.remove();
            }, 2000);
        }
        else {
            var toolTipNot = $("
NOT COPIED! Please, refresh the page and allow your browser to copy!
");
            toolTipNot.insertAfter(parentElement);

            toolTipNot.css("background-color", "#f00");
            toolTipNot.css("color", "#fff");
            toolTipNot.css("width", "300px");
            toolTipNot.css("left", "80px");

            setTimeout(function () {
                toolTipNot.remove();
            }, 2000);
        }

    }
    else if (canPaste === false) { // This means that the Browser is not IE (Chrome, Firefox, Edge, etc.)
        var toolTipCopied = $("
Copied!
");
        toolTipCopied.insertAfter(parentElement);

        setTimeout(function () {
            toolTipCopied.remove();
        }, 2000);
    }
}

/**
 * Binds the click event on all password labels
 */
function bindAllPasswordLabels() {
    $("span.ob-passwordLabel").on('click', function (e) {
        window.appInsights.trackEvent("passwordClicked", $(this).text());
        passwordClick($(this));
    });
};

/**
 * Binds the click event on all password copy-to-clipboard
 */
function bindAllPasswordCopyToClipboard() {
    $("button.ob-copyIcon").on('click', function (e) {
        var assetId = $(this).data("id");
        var iconType = $(this).data("type");

        if (iconType === "password") {
            passwordCopy($(this));
        }
        else if (iconType === "login") {
            loginCopy($(this));
        }
    });
};

function bindSearchAssetInput() {
    $("div.dropdown#assetSearchAutocomplete input").on('focusin', function (e) {
        if ($(this).val().length >= SEARCH_TOKEN_MIN_LENGTH) {
            $(this).triggerHandler('input');
        }
    });

    $(document).on('click', function (e) {
        var dropdown = $('div.dropdown#assetSearchAutocomplete ul.dropdown-menu');
        if (!dropdown.find($(e.target)).length) {
            dropdown.addClass('invisible-asset-holder');
        }
    });

    var timeout = null;
    $("div.dropdown#assetSearchAutocomplete input").on('input', function (e) {
        var searchPrefix = $(this).val(),
            getUrl = "/Home/GetAssetResults?searchToken=" + searchPrefix,
            dropdown = $(this).siblings('ul.dropdown-menu'),
            spinner = $(this).parents('div.row').find('div.loader');

        if (searchPrefix.length < SEARCH_TOKEN_MIN_LENGTH) {
            spinner.hide();
            dropdown.addClass('invisible-asset-holder');
            return;
        }

        spinner.show();
        clearTimeout(timeout);
        timeout = setTimeout(function () {
            var context = {
                input: this,
                searchPrefix: searchPrefix,
                spinner: spinner,
                dropdown: dropdown
            };

            $.ajax({
                type: 'GET',
                url: getUrl,
                contentType: 'application/json',
                cache: false,
                success: function (successResult) {
                    if ($(this.input).val() !== this.searchPrefix) {
                        return;
                    }

                    var dropdown = this.dropdown;
                    if (successResult.length) {
                        successResult.sort(function (a, b) {
                            return a.displayTitle.toString().toLowerCase() > b.displayTitle.toString().toLowerCase();
                        });

                        dropdown.empty();
                        for (var i = 0; i < successResult.length; i++) {
                            var entry = successResult[i];
                            dropdown.append('
' + entry.displayTitle + '
');
                        }

                        dropdown.removeClass('invisible-asset-holder');
                    }
                    else {
                        dropdown.addClass('invisible-asset-holder');
                    }

                    this.spinner.hide();
                }.bind(context),
                error: function () {
                    spinner.hide();
                    dropdown.addClass('invisible-asset-holder');
                }.bind(context)
            });
        }.bind(this), 800);
    });
};

$(document).ready(function () {
    // Bind all password label click functions
    bindAllPasswordLabels();
    bindAllPasswordCopyToClipboard();
    // bind asset search box
    bindSearchAssetInput();
});<|endoftext|>


<commit_before>
var _ = require('lodash');
var moment = require('moment');
var fs = require('fs');
var path = require('path');
var mockery = require('mockery');

describe('pipelineFeedReader', function () {

  var gocdSampleRequestor = require('../../lib/gocd/gocdSampleRequestor');
  var thePipelineFeedReader;

  var NUM_ENTRIES_IN_FIXTURE = 5;

  beforeEach(function() {

    mockery.enable({
      warnOnUnregistered: false,
      warnOnReplace: false
    });

    var globalOptions = {
      getGocdRequestor: function() {
        return gocdSampleRequestor;
      }
    };

    mockery.registerMock('../options', globalOptions);

    thePipelineFeedReader = require('../../lib/gocd/pipelineFeedReader');

  });

  beforeEach(function() {
    thePipelineFeedReader.clear();
  });

  describe('readHistory()', function () {
    it('should write a sample to a file, for documentation purposes', function (done) {

      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        var pipelineRunToLog = '2066';
        expect(results[pipelineRunToLog]).toBeDefined();
        var base = path.resolve(__dirname, 'samples');
        fs.writeFile(base + '/history.json', JSON.stringify(results[pipelineRunToLog], undefined, 2), function() {
          done();
        });
      });
    });

    it('should initialise a set of pipeline runs', function (done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE);
        expect(results['2066']).toBeDefined();
        done();
      });

    });

    it('should exclude pipelines if specified', function(done) {
      thePipelineFeedReader.readPipelineRuns({ exclude: ['2066', '2065'] }).then(function (results) {
        expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE - 2);

        done();
      });
    });

    it('should cache pipeline run even if its excluded in results when first present', function(done) {
      thePipelineFeedReader.readPipelineRuns({ exclude: ['2066'] }).then(function (results) {
        expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE - 1);

        thePipelineFeedReader.readPipelineRuns().then(function (results) {
          expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE);
          expect(results['2066']).toBeDefined();
          done();
        });

      });
    });

    it('should pass no url to the requestor in initial call', function(done) {
      spyOn(gocdSampleRequestor, 'getHistory').andCallThrough();
      thePipelineFeedReader.readPipelineRuns().then(function () {
        done();
      });
      expect(gocdSampleRequestor.getHistory.mostRecentCall.args[0]).toBeUndefined();
    });

    it('should add stages to respective pipeline runs', function (done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].stages.length).toBe(7);
        done();
      });

    });

    it('should determine the time the last stage got scheduled', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        var expectedTime = moment(1419000842499);
        var actualTime = moment(results['2066']['last_scheduled']);
        expect(actualTime.hours()).toBe(expectedTime.hours());
        expect(actualTime.minutes()).toBe(expectedTime.minutes());
        expect(actualTime.seconds()).toBe(expectedTime.seconds());

        done();
      });
    });

    it('should determine the result of the pipeline', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].stages.length).toBe(7);
        expect(results['2066'].result).toBe('Passed');
        expect(results['2062'].stages.length).toBe(7);
        expect(results['2062'].result).toBe('Failed');
        expect(results['2062'].stageFailed).toBe('functional-test');

        done();
      });
    });

    it('should determine the author of the latest change that triggered the run', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].author).toBeDefined();
        expect(results['2066'].author.name).toBe('Edward Norton');
        expect(results['2063'].author.name).toBe('Brad Pitt');

        done();
      });
    });

    it('should parse committer and commit message from material HTML, sorted by latest change first', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        var buildCause = results['2066']['build_cause'];
        expect(buildCause.committer).toContain('Edward Norton');
        expect(buildCause.comment).toContain('Some comment');
        expect(buildCause.revision).toBe('cb855ca1516888541722d8c0ed8973792f30ee57');

        done();
      });
    });

    it('should put author and commit message of the latest change into info text, if present', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].info).toEqual('[2066] Passed | Edward Norton | Some comment | 15:54:02, December 19th 2014');

        done();
      });
    });

    it('should create initials of person that authored changes for a failed job', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].author.initials).toContain('eno');

        done();
      });
    });

  });
});<commit_msg>Trying to fix Snap build<commit_after>
var _ = require('lodash');
var moment = require('moment');
var fs = require('fs');
var path = require('path');
var mockery = require('mockery');

describe('pipelineFeedReader', function () {

  var gocdSampleRequestor = require('../../lib/gocd/gocdSampleRequestor');
  var thePipelineFeedReader;

  var NUM_ENTRIES_IN_FIXTURE = 5;

  beforeEach(function() {

    mockery.enable({
      warnOnUnregistered: false,
      warnOnReplace: false
    });

    var globalOptions = {
      getGocdRequestor: function() {
        return gocdSampleRequestor;
      }
    };

    mockery.registerMock('../options', globalOptions);

    thePipelineFeedReader = require('../../lib/gocd/pipelineFeedReader');

  });

  beforeEach(function() {
    thePipelineFeedReader.clear();
  });

  describe('readHistory()', function () {
    it('should write a sample to a file, for documentation purposes', function (done) {

      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        var pipelineRunToLog = '2066';
        expect(results[pipelineRunToLog]).toBeDefined();
        var base = path.resolve(__dirname, 'samples');
        fs.writeFile(base + '/history.json', JSON.stringify(results[pipelineRunToLog], undefined, 2), function() {
          done();
        });
      });
    });

    it('should initialise a set of pipeline runs', function (done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE);
        expect(results['2066']).toBeDefined();
        done();
      });

    });

    it('should exclude pipelines if specified', function(done) {
      thePipelineFeedReader.readPipelineRuns({ exclude: ['2066', '2065'] }).then(function (results) {
        expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE - 2);

        done();
      });
    });

    it('should cache pipeline run even if its excluded in results when first present', function(done) {
      thePipelineFeedReader.readPipelineRuns({ exclude: ['2066'] }).then(function (results) {
        expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE - 1);

        thePipelineFeedReader.readPipelineRuns().then(function (results) {
          expect(_.keys(results).length).toBe(NUM_ENTRIES_IN_FIXTURE);
          expect(results['2066']).toBeDefined();
          done();
        });

      });
    });

    it('should pass no url to the requestor in initial call', function(done) {
      spyOn(gocdSampleRequestor, 'getHistory').andCallThrough();
      thePipelineFeedReader.readPipelineRuns().then(function () {
        done();
      });
      expect(gocdSampleRequestor.getHistory.mostRecentCall.args[0]).toBeUndefined();
    });

    it('should add stages to respective pipeline runs', function (done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].stages.length).toBe(7);
        done();
      });

    });

    it('should determine the time the last stage got scheduled', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        var expectedTime = moment(1419000842499);
        var actualTime = moment(results['2066']['last_scheduled']);
        expect(actualTime.hours()).toBe(expectedTime.hours());
        expect(actualTime.minutes()).toBe(expectedTime.minutes());
        expect(actualTime.seconds()).toBe(expectedTime.seconds());

        done();
      });
    });

    it('should determine the result of the pipeline', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].stages.length).toBe(7);
        expect(results['2066'].result).toBe('Passed');
        expect(results['2062'].stages.length).toBe(7);
        expect(results['2062'].result).toBe('Failed');
        expect(results['2062'].stageFailed).toBe('functional-test');

        done();
      });
    });

    it('should determine the author of the latest change that triggered the run', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].author).toBeDefined();
        expect(results['2066'].author.name).toBe('Edward Norton');
        expect(results['2063'].author.name).toBe('Brad Pitt');

        done();
      });
    });

    it('should parse committer and commit message from material HTML, sorted by latest change first', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        var buildCause = results['2066']['build_cause'];
        expect(buildCause.committer).toContain('Edward Norton');
        expect(buildCause.comment).toContain('Some comment');
        expect(buildCause.revision).toBe('cb855ca1516888541722d8c0ed8973792f30ee57');

        done();
      });
    });

    it('should put author and commit message of the latest change into info text, if present', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].info).toBe('[2066] Passed | Edward Norton | Some comment | 15:54:02, December 19th 2014');

        done();
      });
    });

    it('should create initials of person that authored changes for a failed job', function(done) {
      thePipelineFeedReader.readPipelineRuns().then(function (results) {
        expect(results['2066'].author.initials).toContain('eno');

        done();
      });
    });

  });
});<|endoftext|>


<commit_before><commit_msg>Fix collision problem with the hotsand<commit_after><|endoftext|>


<commit_before>beforeEach(function() {
  this.addMatchers({
    toChangeAST: function(grammar) {
      function matchDetails(value, details) {
        function isArray(value) {
          return Object.prototype.toString.apply(value) === "[object Array]";
        }

        function isObject(value) {
          return value !== null && typeof value === "object";
        }

        var i, key;

        if (isArray(details)) {
          if (!isArray(value)) { return false; }

          if (value.length !== details.length) { return false; }
          for (i = 0; i < details.length; i++) {
            if (!matchDetails(value[i], details[i])) { return false; }
          }

          return true;
        } else if (isObject(details)) {
          if (!isObject(value)) { return false; }

          for (key in details) {
            if (details.hasOwnProperty(key)) {
              if (!(key in value)) { return false; }

              if (!matchDetails(value[key], details[key])) { return false; }
            }
          }

          return true;
        } else {
          return value === details;
        }
      }

      var options = arguments.length > 2 ? arguments[1] : {},
          details = arguments[arguments.length - 1],
          ast     = PEG.parser.parse(grammar);

      this.actual(ast, options);

      this.message = function() {
        return "Expected the pass "
             + "with options " + jasmine.pp(options) + " "
             + (this.isNot ? "not " : "")
             + "to change the AST " + jasmine.pp(ast) + " "
             + "to match " + jasmine.pp(details) + ", "
             + "but it " + (this.isNot ? "did" : "didn't") + ".";
      };

      return matchDetails(ast, details);
    },

    toReportError: function(grammar, details) {
      var ast = PEG.parser.parse(grammar);

      try {
        this.actual(ast);

        this.message = function() {
          return "Expected the pass to report an error"
               + (details ? " with details " + jasmine.pp(details) : "") + ", "
               + "for grammar " + jasmine.pp(grammar) + ", "
               + "but it didn't.";
        };

        return false;
      } catch (e) {
        /*
         * Should be at the top level but then JSHint complains about bad for
         * in variable.
         */
        var key;

        if (this.isNot) {
          this.message = function() {
            return "Expected the pass not to report an error"
                 + "for grammar " + jasmine.pp(grammar) + ", "
                 + "but it did.";
          };
        } else {
          if (details) {
            for (key in details) {
              if (details.hasOwnProperty(key)) {
                if (!this.env.equals_(e[key], details[key])) {
                  this.message = function() {
                    return "Expected the pass to report an error"
                         + (details ? " with details " + jasmine.pp(details) : "") + ", "
                         + "for grammar " + jasmine.pp(grammar) + ", "
                         + "but " + jasmine.pp(key) + " "
                         + "is " + jasmine.pp(e[key]) + ".";
                  };

                  return false;
                }
              }
            }
          }
        }

        return true;
      }
    }
  });
});
<commit_msg>Fix small errors in Jasmine matcher messages<commit_after>beforeEach(function() {
  this.addMatchers({
    toChangeAST: function(grammar) {
      function matchDetails(value, details) {
        function isArray(value) {
          return Object.prototype.toString.apply(value) === "[object Array]";
        }

        function isObject(value) {
          return value !== null && typeof value === "object";
        }

        var i, key;

        if (isArray(details)) {
          if (!isArray(value)) { return false; }

          if (value.length !== details.length) { return false; }
          for (i = 0; i < details.length; i++) {
            if (!matchDetails(value[i], details[i])) { return false; }
          }

          return true;
        } else if (isObject(details)) {
          if (!isObject(value)) { return false; }

          for (key in details) {
            if (details.hasOwnProperty(key)) {
              if (!(key in value)) { return false; }

              if (!matchDetails(value[key], details[key])) { return false; }
            }
          }

          return true;
        } else {
          return value === details;
        }
      }

      var options = arguments.length > 2 ? arguments[1] : {},
          details = arguments[arguments.length - 1],
          ast     = PEG.parser.parse(grammar);

      this.actual(ast, options);

      this.message = function() {
        return "Expected the pass "
             + "with options " + jasmine.pp(options) + " "
             + (this.isNot ? "not " : "")
             + "to change the AST " + jasmine.pp(ast) + " "
             + "to match " + jasmine.pp(details) + ", "
             + "but it " + (this.isNot ? "did" : "didn't") + ".";
      };

      return matchDetails(ast, details);
    },

    toReportError: function(grammar, details) {
      var ast = PEG.parser.parse(grammar);

      try {
        this.actual(ast);

        this.message = function() {
          return "Expected the pass to report an error "
               + (details ? "with details " + jasmine.pp(details) + " ": "")
               + "for grammar " + jasmine.pp(grammar) + ", "
               + "but it didn't.";
        };

        return false;
      } catch (e) {
        /*
         * Should be at the top level but then JSHint complains about bad for
         * in variable.
         */
        var key;

        if (this.isNot) {
          this.message = function() {
            return "Expected the pass not to report an error "
                 + "for grammar " + jasmine.pp(grammar) + ", "
                 + "but it did.";
          };
        } else {
          if (details) {
            for (key in details) {
              if (details.hasOwnProperty(key)) {
                if (!this.env.equals_(e[key], details[key])) {
                  this.message = function() {
                    return "Expected the pass to report an error "
                         + "with details " + jasmine.pp(details) + " "
                         + "for grammar " + jasmine.pp(grammar) + ", "
                         + "but " + jasmine.pp(key) + " "
                         + "is " + jasmine.pp(e[key]) + ".";
                  };

                  return false;
                }
              }
            }
          }
        }

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


<commit_before>/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

const { app } = require('electron')
const fs = require('fs')
const path = require('path')

const dbApi = require('../db/db-api.js')
const args = require('../util/args.js')
const env = require('../util/env.js')
const zclLoader = require('../zcl/zcl-loader.js')
const windowJs = require('./window.js')
const httpServer = require('../server/http-server.js')
const generatorEngine = require('../generator/generation-engine.js')
const querySession = require('../db/query-session.js')
const util = require('../util/util.js')
const importJs = require('../importexport/import.js')
const exportJs = require('../importexport/export.js')
const uiJs = require('./ui.js')

// This file contains various startup modes.

/**
 * Start up application in a normal mode.
 *
 * @param {*} uiEnabled
 * @param {*} showUrl
 * @param {*} uiMode
 * @param {*} zapFiles An array of .zap files to open, can be empty.
 */
function startNormal(uiEnabled, showUrl, zapFiles, options) {
  return dbApi
    .initDatabaseAndLoadSchema(
      env.sqliteFile(),
      env.schemaFile(),
      env.zapVersion()
    )
    .then((db) => env.resolveMainDatabase(db))
    .then((db) => zclLoader.loadZcl(db, args.zclPropertiesFile))
    .then((ctx) =>
      generatorEngine.loadTemplates(ctx.db, args.genTemplateJsonFile)
    )
    .then((ctx) => {
      if (ctx.error) {
        env.logWarning(ctx.error)
      }
      return ctx
    })
    .then((ctx) => {
      if (!args.noServer)
        return httpServer.initHttpServer(
          ctx.db,
          args.httpPort,
          args.studioHttpPort
        )
      else return true
    })
    .then(() => {
      if (uiEnabled) {
        windowJs.initializeElectronUi(httpServer.httpServerPort())
        if (zapFiles.length == 0) {
          uiJs.openNewConfiguration(httpServer.httpServerPort(), options)
        } else {
          return util.executePromisesSequentially(zapFiles, (f) =>
            uiJs.readAndOpenFile(
              env.mainDatabase(),
              f,
              httpServer.httpServerPort()
            )
          )
        }
      } else {
        if (app.dock) {
          app.dock.hide()
        }
        if (showUrl && !args.noServer) {
          // NOTE: this is parsed/used by Studio as the default landing page.
          console.log(
            `ZAP Server started at: http://localhost:${httpServer.httpServerPort()}`
          )
        }
      }
    })
    .then(() => {
      if (args.noServer && app != null) app.quit()
    })
    .catch((err) => {
      env.logError(err)
      throw err
    })
}

function outputFile(inputFile, outputPattern) {
  if (outputPattern.includes('{0}')) {
    let dir = path.dirname(inputFile)
    let basename = path.basename(inputFile)
    let output = outputPattern.replace('{0}', basename)
    return path.join(dir, output)
  } else {
    return outputPattern
  }
}

/**
 * Perform file conversion.
 *
 * @param {*} files
 * @param {*} output
 */
function startConvert(files, output, options = { log: true, quit: true }) {
  if (options.log) console.log(`🤖 Conversion started`)
  if (options.log) console.log(`    👉 input files: ${files}`)
  if (options.log) console.log(`    👉 output pattern: ${output}`)

  let dbFile = env.sqliteFile('convert')

  return dbApi
    .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion())
    .then((d) => {
      db = d
      if (options.log) console.log('    👉 database and schema initialized')
      return zclLoader.loadZcl(db, args.zclPropertiesFile)
    })
    .then((d) => {
      return util.executePromisesSequentially(files, (singlePath) =>
        importJs
          .importDataFromFile(db, singlePath)
          .then((sessionId) => {
            if (options.log) console.log(`    👈 read in: ${singlePath}`)
            let of = outputFile(singlePath, output)
            let parent = path.dirname(of)
            if (!fs.existsSync(parent)) {
              fs.mkdirSync(parent, { recursive: true })
            }
            return exportJs.exportDataIntoFile(db, sessionId, of)
          })
          .then((outputPath) => {
            if (options.log) console.log(`    👉 write out: ${outputPath}`)
          })
      )
    })
    .then(() => {
      if (options.log) console.log('😎 Conversion done!')
      if (options.quit && app != null) {
        app.quit()
      }
    })
}

/**
 * Perform file analysis.
 *
 * @param {*} paths List of paths to analyze
 * @param {boolean} [options={ log: true, quit: true }]
 */
function startAnalyze(
  paths,
  options = { log: true, quit: true, cleanDb: true }
) {
  let dbFile = env.sqliteFile('analysis')
  if (options.log) console.log(`🤖 Starting analysis: ${paths}`)
  if (options.cleanDb && fs.existsSync(dbFile)) {
    if (options.log) console.log('    👉 remove old database file')
    fs.unlinkSync(dbFile)
  }
  let db
  return dbApi
    .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion())
    .then((d) => {
      db = d
      if (options.log) console.log('    👉 database and schema initialized')
      return zclLoader.loadZcl(db, args.zclPropertiesFile)
    })
    .then((d) => {
      return util.executePromisesSequentially(paths, (singlePath) =>
        importJs
          .importDataFromFile(db, singlePath)
          .then((sessionId) => util.sessionReport(db, sessionId))
          .then((report) => {
            if (options.log) console.log(`🤖 File: ${singlePath}\n`)
            if (options.log) console.log(report)
          })
      )
    })
    .then(() => {
      if (options.log) console.log('😎 Analysis done!')
      if (options.quit && app != null) app.quit()
    })
}

/**
 * Start up applicationa in self-check mode.
 */
function startSelfCheck(options = { log: true, quit: true, cleanDb: true }) {
  env.logInitStdout()
  if (options.log) console.log('🤖 Starting self-check')
  let dbFile = env.sqliteFile('self-check')
  if (options.cleanDb && fs.existsSync(dbFile)) {
    if (options.log) console.log('    👉 remove old database file')
    fs.unlinkSync(dbFile)
  }
  return dbApi
    .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion())
    .then((db) => {
      if (options.log) console.log('    👉 database and schema initialized')
      return zclLoader.loadZcl(db, args.zclPropertiesFile)
    })
    .then((ctx) => {
      if (options.log) console.log('    👉 zcl data loaded')
      return generatorEngine.loadTemplates(ctx.db, args.genTemplateJsonFile)
    })
    .then((ctx) => {
      if (options.log) {
        if (ctx.error) {
          console.log(`    ⚠  ${ctx.error}`)
        } else {
          console.log('    👉 generation templates loaded')
        }
      }
      if (options.log) console.log('😎 Self-check done!')
      if (options.quit && app != null) app.quit()
    })
    .catch((err) => {
      env.logError(err)
      throw err
    })
}

/**
 * Performs headless regeneration for given parameters.
 *
 * @param {*} output Directory where to write files.
 * @param {*} genTemplateJsonFile gen-teplate.json file to use for template loading.
 * @param {*} zclProperties zcl.properties file to use for ZCL properties.
 * @param {*} [zapFile=null] .zap file that contains application stater, or null if generating from clean state.
 * @returns Nothing, triggers app.quit()
 */
async function startGeneration(
  output,
  genTemplateJsonFile,
  zclProperties,
  zapFiles = [],
  options = {
    quit: true,
    cleanDb: true,
    log: true,
  }
) {
  if (options.log)
    console.log(
      `🤖 ZAP generation information:
    👉 into: ${output}
    👉 using templates: ${genTemplateJsonFile}
    👉 using zcl data: ${zclProperties}`
    )
  let zapFile = null
  if (zapFiles != null && zapFiles.length > 0) {
    zapFile = zapFiles[0]
    if (zapFiles.length > 1 && options.log)
      console.log(`    ⚠  Multiple files passed. Using only first one.`)
  }
  if (zapFile != null) {
    if (fs.existsSync(zapFile)) {
      let stat = fs.statSync(zapFile)
      if (stat.isDirectory()) {
        if (options.log) console.log(`    👉 using input directory: ${zapFile}`)
        let dirents = fs.readdirSync(zapFile, { withFileTypes: true })
        let usedFile = []
        dirents.forEach((element) => {
          if (element.name.endsWith('.zap') || element.name.endsWith('.ZAP')) {
            usedFile.push(path.join(zapFile, element.name))
          }
        })
        if (usedFile.length == 0) {
          if (options.log)
            console.log(`    👎 no zap files found in directory: ${zapFile}`)
          throw `👎 no zap files found in directory: ${zapFile}`
        } else if (usedFile.length > 1) {
          if (options.log)
            console.log(
              `    👎 multiple zap files found in directory, only one is allowed: ${zapFile}`
            )
          throw `👎 multiple zap files found in directory, only one is allowed: ${zapFile}`
        } else {
          zapFile = usedFile[0]
          if (options.log) console.log(`    👉 using input file: ${zapFile}`)
        }
      } else {
        if (options.log) console.log(`    👉 using input file: ${zapFile}`)
      }
    } else {
      if (options.log) console.log(`    👎 file not found: ${zapFile}`)
      throw `👎 file not found: ${zapFile}`
    }
  } else {
    if (options.log) console.log(`    👉 using empty configuration`)
  }
  if (options.log)
    console.log(`    👉 zap version: ${env.zapVersionAsString()}`)
  let dbFile = env.sqliteFile('generate')
  if (options.cleanDb && fs.existsSync(dbFile)) fs.unlinkSync(dbFile)
  let mainDb = await dbApi.initDatabaseAndLoadSchema(
    dbFile,
    env.schemaFile(),
    env.zapVersion()
  )
  let ctx = await zclLoader.loadZcl(mainDb, zclProperties)
  ctx = await generatorEngine.loadTemplates(ctx.db, genTemplateJsonFile)
  if (ctx.error) {
    throw ctx.error
  }
  let packageId = ctx.packageId

  let sessionId
  if (zapFile == null) sessionId = await querySession.createBlankSession(mainDb)
  else sessionId = await importJs.importDataFromFile(mainDb, zapFile)

  await util.initializeSessionPackage(mainDb, sessionId)

  let genResult = await generatorEngine.generateAndWriteFiles(
    mainDb,
    sessionId,
    packageId,
    output,
    {
      log: options.log,
      backup: false,
      genResultFile: args.genResultFile,
      skipPostGeneration: args.skipPostGeneration,
    }
  )

  if (genResult.hasErrors) throw 'Generation failed.'
  if (options.quit && app != null) app.quit()
}
/**
 * Move database file out of the way into the backup location.
 *
 * @param {*} path
 */
function clearDatabaseFile(dbPath) {
  util.createBackupFile(dbPath)
}

/**
 * Default startup method.
 *
 * @param {*} isElectron
 */
function startUp(isElectron) {
  let argv = args.processCommandLineArguments(process.argv)

  if (argv.logToStdout) {
    env.logInitStdout()
  } else {
    env.logInitLogFile()
  }

  // For now delete the DB file. There is some weird constraint we run into.
  if (argv.clearDb != null) {
    clearDatabaseFile(env.sqliteFile())
  }

  if (argv._.includes('selfCheck')) {
    return startSelfCheck()
  } else if (argv._.includes('analyze')) {
    if (argv.zapFiles.length < 1)
      throw 'You need to specify at least one zap file.'
    return startAnalyze(argv.zapFiles)
  } else if (argv._.includes('convert')) {
    if (argv.zapFiles.length < 1)
      throw 'You need to specify at least one zap file.'
    if (argv.output == null) throw 'You need to specify output file.'
    return startConvert(argv.zapFiles, argv.output).catch((code) => {
      console.log(code)
      process.exit(1)
    })
  } else if (argv._.includes('generate')) {
    return startGeneration(
      argv.output,
      argv.generationTemplate,
      argv.zclProperties,
      argv.zapFiles
    ).catch((code) => {
      console.log(code)
      process.exit(1)
    })
  } else {
    if (isElectron) {
      return startNormal(!argv.noUi, argv.showUrl, argv.zapFiles, {
        uiMode: argv.uiMode,
        embeddedMode: argv.embeddedMode,
      })
    } else {
      console.log(`Can't start UI with node, must start with electron.`)
    }
  }
}

exports.startGeneration = startGeneration
exports.startNormal = startNormal
exports.startSelfCheck = startSelfCheck
exports.clearDatabaseFile = clearDatabaseFile
exports.startAnalyze = startAnalyze
exports.startUp = startUp
<commit_msg>Support {name} and {basename} substitutions for auto-conversion, so SDK upgraders have an easier job.<commit_after>/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

const { app } = require('electron')
const fs = require('fs')
const path = require('path')

const dbApi = require('../db/db-api.js')
const args = require('../util/args.js')
const env = require('../util/env.js')
const zclLoader = require('../zcl/zcl-loader.js')
const windowJs = require('./window.js')
const httpServer = require('../server/http-server.js')
const generatorEngine = require('../generator/generation-engine.js')
const querySession = require('../db/query-session.js')
const util = require('../util/util.js')
const importJs = require('../importexport/import.js')
const exportJs = require('../importexport/export.js')
const uiJs = require('./ui.js')

// This file contains various startup modes.

/**
 * Start up application in a normal mode.
 *
 * @param {*} uiEnabled
 * @param {*} showUrl
 * @param {*} uiMode
 * @param {*} zapFiles An array of .zap files to open, can be empty.
 */
function startNormal(uiEnabled, showUrl, zapFiles, options) {
  return dbApi
    .initDatabaseAndLoadSchema(
      env.sqliteFile(),
      env.schemaFile(),
      env.zapVersion()
    )
    .then((db) => env.resolveMainDatabase(db))
    .then((db) => zclLoader.loadZcl(db, args.zclPropertiesFile))
    .then((ctx) =>
      generatorEngine.loadTemplates(ctx.db, args.genTemplateJsonFile)
    )
    .then((ctx) => {
      if (ctx.error) {
        env.logWarning(ctx.error)
      }
      return ctx
    })
    .then((ctx) => {
      if (!args.noServer)
        return httpServer.initHttpServer(
          ctx.db,
          args.httpPort,
          args.studioHttpPort
        )
      else return true
    })
    .then(() => {
      if (uiEnabled) {
        windowJs.initializeElectronUi(httpServer.httpServerPort())
        if (zapFiles.length == 0) {
          uiJs.openNewConfiguration(httpServer.httpServerPort(), options)
        } else {
          return util.executePromisesSequentially(zapFiles, (f) =>
            uiJs.readAndOpenFile(
              env.mainDatabase(),
              f,
              httpServer.httpServerPort()
            )
          )
        }
      } else {
        if (app.dock) {
          app.dock.hide()
        }
        if (showUrl && !args.noServer) {
          // NOTE: this is parsed/used by Studio as the default landing page.
          console.log(
            `ZAP Server started at: http://localhost:${httpServer.httpServerPort()}`
          )
        }
      }
    })
    .then(() => {
      if (args.noServer && app != null) app.quit()
    })
    .catch((err) => {
      env.logError(err)
      throw err
    })
}

/**
 * Returns the output file out of input file and a pattern
 *
 * @param {*} inputFile
 * @param {*} outputPattern
 * @returns the path to the output file.
 */
function outputFile(inputFile, outputPattern) {
  let output = outputPattern
  if (output.includes('{')) {
    let dir = path.dirname(inputFile)
    let name = path.basename(inputFile)
    let basename
    let i = name.indexOf('.')
    if (i == -1) {
      basename = name
    } else {
      basename = name.substring(0, i)
    }
    output = output.replace('{name}', name)
    output = output.replace('{basename}', basename)
    output = path.join(dir, output)
  }
  return output
}

/**
 * Perform file conversion.
 *
 * @param {*} files
 * @param {*} output
 */
function startConvert(files, output, options = { log: true, quit: true }) {
  if (options.log) console.log(`🤖 Conversion started`)
  if (options.log) console.log(`    👉 input files: ${files}`)
  if (options.log) console.log(`    👉 output pattern: ${output}`)

  let dbFile = env.sqliteFile('convert')

  return dbApi
    .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion())
    .then((d) => {
      db = d
      if (options.log) console.log('    👉 database and schema initialized')
      return zclLoader.loadZcl(db, args.zclPropertiesFile)
    })
    .then((d) => {
      return util.executePromisesSequentially(files, (singlePath) =>
        importJs
          .importDataFromFile(db, singlePath)
          .then((sessionId) => {
            if (options.log) console.log(`    👈 read in: ${singlePath}`)
            let of = outputFile(singlePath, output)
            let parent = path.dirname(of)
            if (!fs.existsSync(parent)) {
              fs.mkdirSync(parent, { recursive: true })
            }
            return exportJs.exportDataIntoFile(db, sessionId, of)
          })
          .then((outputPath) => {
            if (options.log) console.log(`    👉 write out: ${outputPath}`)
          })
      )
    })
    .then(() => {
      if (options.log) console.log('😎 Conversion done!')
      if (options.quit && app != null) {
        app.quit()
      }
    })
}

/**
 * Perform file analysis.
 *
 * @param {*} paths List of paths to analyze
 * @param {boolean} [options={ log: true, quit: true }]
 */
function startAnalyze(
  paths,
  options = { log: true, quit: true, cleanDb: true }
) {
  let dbFile = env.sqliteFile('analysis')
  if (options.log) console.log(`🤖 Starting analysis: ${paths}`)
  if (options.cleanDb && fs.existsSync(dbFile)) {
    if (options.log) console.log('    👉 remove old database file')
    fs.unlinkSync(dbFile)
  }
  let db
  return dbApi
    .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion())
    .then((d) => {
      db = d
      if (options.log) console.log('    👉 database and schema initialized')
      return zclLoader.loadZcl(db, args.zclPropertiesFile)
    })
    .then((d) => {
      return util.executePromisesSequentially(paths, (singlePath) =>
        importJs
          .importDataFromFile(db, singlePath)
          .then((sessionId) => util.sessionReport(db, sessionId))
          .then((report) => {
            if (options.log) console.log(`🤖 File: ${singlePath}\n`)
            if (options.log) console.log(report)
          })
      )
    })
    .then(() => {
      if (options.log) console.log('😎 Analysis done!')
      if (options.quit && app != null) app.quit()
    })
}

/**
 * Start up applicationa in self-check mode.
 */
function startSelfCheck(options = { log: true, quit: true, cleanDb: true }) {
  env.logInitStdout()
  if (options.log) console.log('🤖 Starting self-check')
  let dbFile = env.sqliteFile('self-check')
  if (options.cleanDb && fs.existsSync(dbFile)) {
    if (options.log) console.log('    👉 remove old database file')
    fs.unlinkSync(dbFile)
  }
  return dbApi
    .initDatabaseAndLoadSchema(dbFile, env.schemaFile(), env.zapVersion())
    .then((db) => {
      if (options.log) console.log('    👉 database and schema initialized')
      return zclLoader.loadZcl(db, args.zclPropertiesFile)
    })
    .then((ctx) => {
      if (options.log) console.log('    👉 zcl data loaded')
      return generatorEngine.loadTemplates(ctx.db, args.genTemplateJsonFile)
    })
    .then((ctx) => {
      if (options.log) {
        if (ctx.error) {
          console.log(`    ⚠  ${ctx.error}`)
        } else {
          console.log('    👉 generation templates loaded')
        }
      }
      if (options.log) console.log('😎 Self-check done!')
      if (options.quit && app != null) app.quit()
    })
    .catch((err) => {
      env.logError(err)
      throw err
    })
}

/**
 * Performs headless regeneration for given parameters.
 *
 * @param {*} output Directory where to write files.
 * @param {*} genTemplateJsonFile gen-teplate.json file to use for template loading.
 * @param {*} zclProperties zcl.properties file to use for ZCL properties.
 * @param {*} [zapFile=null] .zap file that contains application stater, or null if generating from clean state.
 * @returns Nothing, triggers app.quit()
 */
async function startGeneration(
  output,
  genTemplateJsonFile,
  zclProperties,
  zapFiles = [],
  options = {
    quit: true,
    cleanDb: true,
    log: true,
  }
) {
  if (options.log)
    console.log(
      `🤖 ZAP generation information:
    👉 into: ${output}
    👉 using templates: ${genTemplateJsonFile}
    👉 using zcl data: ${zclProperties}`
    )
  let zapFile = null
  if (zapFiles != null && zapFiles.length > 0) {
    zapFile = zapFiles[0]
    if (zapFiles.length > 1 && options.log)
      console.log(`    ⚠  Multiple files passed. Using only first one.`)
  }
  if (zapFile != null) {
    if (fs.existsSync(zapFile)) {
      let stat = fs.statSync(zapFile)
      if (stat.isDirectory()) {
        if (options.log) console.log(`    👉 using input directory: ${zapFile}`)
        let dirents = fs.readdirSync(zapFile, { withFileTypes: true })
        let usedFile = []
        dirents.forEach((element) => {
          if (element.name.endsWith('.zap') || element.name.endsWith('.ZAP')) {
            usedFile.push(path.join(zapFile, element.name))
          }
        })
        if (usedFile.length == 0) {
          if (options.log)
            console.log(`    👎 no zap files found in directory: ${zapFile}`)
          throw `👎 no zap files found in directory: ${zapFile}`
        } else if (usedFile.length > 1) {
          if (options.log)
            console.log(
              `    👎 multiple zap files found in directory, only one is allowed: ${zapFile}`
            )
          throw `👎 multiple zap files found in directory, only one is allowed: ${zapFile}`
        } else {
          zapFile = usedFile[0]
          if (options.log) console.log(`    👉 using input file: ${zapFile}`)
        }
      } else {
        if (options.log) console.log(`    👉 using input file: ${zapFile}`)
      }
    } else {
      if (options.log) console.log(`    👎 file not found: ${zapFile}`)
      throw `👎 file not found: ${zapFile}`
    }
  } else {
    if (options.log) console.log(`    👉 using empty configuration`)
  }
  if (options.log)
    console.log(`    👉 zap version: ${env.zapVersionAsString()}`)
  let dbFile = env.sqliteFile('generate')
  if (options.cleanDb && fs.existsSync(dbFile)) fs.unlinkSync(dbFile)
  let mainDb = await dbApi.initDatabaseAndLoadSchema(
    dbFile,
    env.schemaFile(),
    env.zapVersion()
  )
  let ctx = await zclLoader.loadZcl(mainDb, zclProperties)
  ctx = await generatorEngine.loadTemplates(ctx.db, genTemplateJsonFile)
  if (ctx.error) {
    throw ctx.error
  }
  let packageId = ctx.packageId

  let sessionId
  if (zapFile == null) sessionId = await querySession.createBlankSession(mainDb)
  else sessionId = await importJs.importDataFromFile(mainDb, zapFile)

  await util.initializeSessionPackage(mainDb, sessionId)

  let genResult = await generatorEngine.generateAndWriteFiles(
    mainDb,
    sessionId,
    packageId,
    output,
    {
      log: options.log,
      backup: false,
      genResultFile: args.genResultFile,
      skipPostGeneration: args.skipPostGeneration,
    }
  )

  if (genResult.hasErrors) throw 'Generation failed.'
  if (options.quit && app != null) app.quit()
}
/**
 * Move database file out of the way into the backup location.
 *
 * @param {*} path
 */
function clearDatabaseFile(dbPath) {
  util.createBackupFile(dbPath)
}

/**
 * Default startup method.
 *
 * @param {*} isElectron
 */
function startUp(isElectron) {
  let argv = args.processCommandLineArguments(process.argv)

  if (argv.logToStdout) {
    env.logInitStdout()
  } else {
    env.logInitLogFile()
  }

  // For now delete the DB file. There is some weird constraint we run into.
  if (argv.clearDb != null) {
    clearDatabaseFile(env.sqliteFile())
  }

  if (argv._.includes('selfCheck')) {
    return startSelfCheck()
  } else if (argv._.includes('analyze')) {
    if (argv.zapFiles.length < 1)
      throw 'You need to specify at least one zap file.'
    return startAnalyze(argv.zapFiles)
  } else if (argv._.includes('convert')) {
    if (argv.zapFiles.length < 1)
      throw 'You need to specify at least one zap file.'
    if (argv.output == null) throw 'You need to specify output file.'
    return startConvert(argv.zapFiles, argv.output).catch((code) => {
      console.log(code)
      process.exit(1)
    })
  } else if (argv._.includes('generate')) {
    return startGeneration(
      argv.output,
      argv.generationTemplate,
      argv.zclProperties,
      argv.zapFiles
    ).catch((code) => {
      console.log(code)
      process.exit(1)
    })
  } else {
    if (isElectron) {
      return startNormal(!argv.noUi, argv.showUrl, argv.zapFiles, {
        uiMode: argv.uiMode,
        embeddedMode: argv.embeddedMode,
      })
    } else {
      console.log(`Can't start UI with node, must start with electron.`)
    }
  }
}

exports.startGeneration = startGeneration
exports.startNormal = startNormal
exports.startSelfCheck = startSelfCheck
exports.clearDatabaseFile = clearDatabaseFile
exports.startAnalyze = startAnalyze
exports.startUp = startUp
<|endoftext|>