<commit_before>/* global __dirname, process */

const {
    BrowserWindow,
    Menu,
    app,
    ipcMain
} = require('electron');
const contextMenu = require('electron-context-menu');
const debug = require('electron-debug');
const isDev = require('electron-is-dev');
const { autoUpdater } = require('electron-updater');
const windowStateKeeper = require('electron-window-state');
const {
    initPopupsConfigurationMain,
    getPopupTarget,
    setupAlwaysOnTopMain,
    setupPowerMonitorMain,
    setupScreenSharingMain
} = require('jitsi-meet-electron-utils');
const path = require('path');
const URL = require('url');
const config = require('./app/features/config');
const { openExternalLink } = require('./app/features/utils/openExternalLink');

const showDevTools = Boolean(process.env.SHOW_DEV_TOOLS) || (process.argv.indexOf('--show-dev-tools') > -1);

// We need this because of https://github.com/electron/electron/issues/18214
app.commandLine.appendSwitch('disable-site-isolation-trials');

// https://bugs.chromium.org/p/chromium/issues/detail?id=1086373
app.commandLine.appendSwitch('disable-webrtc-hw-encoding');
app.commandLine.appendSwitch('disable-webrtc-hw-decoding');

// Needed until robot.js is fixed: https://github.com/octalmage/robotjs/issues/580
app.allowRendererProcessReuse = false;

autoUpdater.logger = require('electron-log');
autoUpdater.logger.transports.file.level = 'info';

// Enable context menu so things like copy and paste work in input fields.
contextMenu({
    showLookUpSelection: false,
    showSearchWithGoogle: false,
    showCopyImage: false,
    showCopyImageAddress: false,
    showSaveImage: false,
    showSaveImageAs: false,
    showInspectElement: true,
    showServices: false
});

// Enable DevTools also on release builds to help troubleshoot issues. Don't
// show them automatically though.
debug({
    isEnabled: true,
    showDevTools
});

/**
 * When in development mode:
 * - Enable automatic reloads
 */
if (isDev) {
    require('electron-reload')(path.join(__dirname, 'build'));
}

/**
 * The window object that will load the iframe with Jitsi Meet.
 * IMPORTANT: Must be defined as global in order to not be garbage collected
 * acidentally.
 */
let mainWindow = null;

/**
 * Add protocol data
 */
const appProtocolSurplus = `${config.default.appProtocolPrefix}://`;
let rendererReady = false;
let protocolDataForFrontApp = null;

/**
 * Sets the application menu. It is hidden on all platforms except macOS because
 * otherwise copy and paste functionality is not available.
 */
function setApplicationMenu() {
    if (process.platform === 'darwin') {
        const template = [ {
            label: app.name,
            submenu: [
                {
                    role: 'services',
                    submenu: []
                },
                { type: 'separator' },
                { role: 'hide' },
                { role: 'hideothers' },
                { role: 'unhide' },
                { type: 'separator' },
                { role: 'quit' }
            ]
        }, {
            label: 'Edit',
            submenu: [ {
                label: 'Undo',
                accelerator: 'CmdOrCtrl+Z',
                selector: 'undo:'
            },
            {
                label: 'Redo',
                accelerator: 'Shift+CmdOrCtrl+Z',
                selector: 'redo:'
            },
            {
                type: 'separator'
            },
            {
                label: 'Cut',
                accelerator: 'CmdOrCtrl+X',
                selector: 'cut:'
            },
            {
                label: 'Copy',
                accelerator: 'CmdOrCtrl+C',
                selector: 'copy:'
            },
            {
                label: 'Paste',
                accelerator: 'CmdOrCtrl+V',
                selector: 'paste:'
            },
            {
                label: 'Select All',
                accelerator: 'CmdOrCtrl+A',
                selector: 'selectAll:'
            } ]
        }, {
            label: '&Window',
            role: 'window',
            submenu: [
                { role: 'minimize' },
                { role: 'close' }
            ]
        } ];

        Menu.setApplicationMenu(Menu.buildFromTemplate(template));
    } else {
        Menu.setApplicationMenu(null);
    }
}

/**
 * Opens new window with index.html(Jitsi Meet is loaded in iframe there).
 */
function createJitsiMeetWindow() {
    // Application menu.
    setApplicationMenu();

    // Check for Updates.
    autoUpdater.checkForUpdatesAndNotify();

    // Load the previous window state with fallback to defaults.
    const windowState = windowStateKeeper({
        defaultWidth: 800,
        defaultHeight: 600
    });

    // Path to root directory.
    const basePath = isDev ? __dirname : app.getAppPath();

    // URL for index.html which will be our entry point.
    const indexURL = URL.format({
        pathname: path.resolve(basePath, './build/index.html'),
        protocol: 'file:',
        slashes: true
    });

    // Options used when creating the main Jitsi Meet window.
    // Use a preload script in order to provide node specific functionality
    // to a isolated BrowserWindow in accordance with electron security
    // guideline.
    const options = {
        x: windowState.x,
        y: windowState.y,
        width: windowState.width,
        height: windowState.height,
        icon: path.resolve(basePath, './resources/icons/icon_512x512.png'),
        minWidth: 800,
        minHeight: 600,
        show: false,
        webPreferences: {
            enableRemoteModule: true,
            experimentalFeatures: true, // Insertable streams, for E2EE.
            nativeWindowOpen: true,
            nodeIntegration: false,
            preload: path.resolve(basePath, './build/preload.js')
        }
    };

    mainWindow = new BrowserWindow(options);
    windowState.manage(mainWindow);
    mainWindow.loadURL(indexURL);

    initPopupsConfigurationMain(mainWindow);
    setupAlwaysOnTopMain(mainWindow);
    setupPowerMonitorMain(mainWindow);
    setupScreenSharingMain(mainWindow, config.default.appName);

    mainWindow.webContents.on('new-window', (event, url, frameName) => {
        const target = getPopupTarget(url, frameName);

        if (!target || target === 'browser') {
            event.preventDefault();
            openExternalLink(url);
        }
    });
    mainWindow.on('closed', () => {
        mainWindow = null;
    });
    mainWindow.once('ready-to-show', () => {
        mainWindow.show();
    });

    /**
     * When someone tries to enter something like jitsi-meet://test
     *  while app is closed
     * it will trigger this event below
     */
    handleProtocolCall(process.argv.pop());
}

/**
 * Handler for application protocol links to initiate a conference.
 */
function handleProtocolCall(fullProtocolCall) {
    // don't touch when something is bad
    if (
        !fullProtocolCall
        || fullProtocolCall.trim() === ''
        || fullProtocolCall.indexOf(appProtocolSurplus) !== 0
    ) {
        return;
    }

    const inputURL = fullProtocolCall.replace(appProtocolSurplus, '');

    if (app.isReady() && mainWindow === null) {
        createJitsiMeetWindow();
    }

    protocolDataForFrontApp = inputURL;

    if (rendererReady) {
        mainWindow
            .webContents
            .send('protocol-data-msg', inputURL);
    }
}

/**
 * Force Single Instance Application.
 */
const gotInstanceLock = app.requestSingleInstanceLock();

if (!gotInstanceLock) {
    app.quit();
    process.exit(0);
}

/**
 * Run the application.
 */

app.on('activate', () => {
    if (mainWindow === null) {
        createJitsiMeetWindow();
    }
});

app.on('certificate-error',
    // eslint-disable-next-line max-params
    (event, webContents, url, error, certificate, callback) => {
        if (isDev) {
            event.preventDefault();
            callback(true);
        } else {
            callback(false);
        }
    }
);

app.on('ready', createJitsiMeetWindow);

app.on('second-instance', (event, commandLine) => {
    /**
     * If someone creates second instance of the application, set focus on
     * existing window.
     */
    if (mainWindow) {
        mainWindow.isMinimized() && mainWindow.restore();
        mainWindow.focus();

        /**
         * This is for windows [win32]
         * so when someone tries to enter something like jitsi-meet://test
         * while app is opened it will trigger protocol handler.
         */
        handleProtocolCall(commandLine.pop());
    }
});

app.on('window-all-closed', () => {
    // Don't quit the application on macOS.
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// remove so we can register each time as we run the app.
app.removeAsDefaultProtocolClient(config.default.appProtocolPrefix);

// If we are running a non-packaged version of the app && on windows
if (isDev && process.platform === 'win32') {
    // Set the path of electron.exe and your app.
    // These two additional parameters are only available on windows.
    app.setAsDefaultProtocolClient(
        config.default.appProtocolPrefix,
        process.execPath,
        [ path.resolve(process.argv[1]) ]
    );
} else {
    app.setAsDefaultProtocolClient(config.default.appProtocolPrefix);
}

/**
 * This is for mac [darwin]
 * so when someone tries to enter something like jitsi-meet://test
 * it will trigger this event below
 */
app.on('open-url', (event, data) => {
    event.preventDefault();
    handleProtocolCall(data);
});

/**
 * This is to notify main.js [this] that front app is ready to receive messages.
 */
ipcMain.on('renderer-ready', () => {
    rendererReady = true;
    if (protocolDataForFrontApp) {
        mainWindow
            .webContents
            .send('protocol-data-msg', protocolDataForFrontApp);
    }
});
<commit_msg>Remove no longer needed workaround<commit_after>/* global __dirname, process */

const {
    BrowserWindow,
    Menu,
    app,
    ipcMain
} = require('electron');
const contextMenu = require('electron-context-menu');
const debug = require('electron-debug');
const isDev = require('electron-is-dev');
const { autoUpdater } = require('electron-updater');
const windowStateKeeper = require('electron-window-state');
const {
    initPopupsConfigurationMain,
    getPopupTarget,
    setupAlwaysOnTopMain,
    setupPowerMonitorMain,
    setupScreenSharingMain
} = require('jitsi-meet-electron-utils');
const path = require('path');
const URL = require('url');
const config = require('./app/features/config');
const { openExternalLink } = require('./app/features/utils/openExternalLink');

const showDevTools = Boolean(process.env.SHOW_DEV_TOOLS) || (process.argv.indexOf('--show-dev-tools') > -1);

// We need this because of https://github.com/electron/electron/issues/18214
app.commandLine.appendSwitch('disable-site-isolation-trials');

// Needed until robot.js is fixed: https://github.com/octalmage/robotjs/issues/580
app.allowRendererProcessReuse = false;

autoUpdater.logger = require('electron-log');
autoUpdater.logger.transports.file.level = 'info';

// Enable context menu so things like copy and paste work in input fields.
contextMenu({
    showLookUpSelection: false,
    showSearchWithGoogle: false,
    showCopyImage: false,
    showCopyImageAddress: false,
    showSaveImage: false,
    showSaveImageAs: false,
    showInspectElement: true,
    showServices: false
});

// Enable DevTools also on release builds to help troubleshoot issues. Don't
// show them automatically though.
debug({
    isEnabled: true,
    showDevTools
});

/**
 * When in development mode:
 * - Enable automatic reloads
 */
if (isDev) {
    require('electron-reload')(path.join(__dirname, 'build'));
}

/**
 * The window object that will load the iframe with Jitsi Meet.
 * IMPORTANT: Must be defined as global in order to not be garbage collected
 * acidentally.
 */
let mainWindow = null;

/**
 * Add protocol data
 */
const appProtocolSurplus = `${config.default.appProtocolPrefix}://`;
let rendererReady = false;
let protocolDataForFrontApp = null;

/**
 * Sets the application menu. It is hidden on all platforms except macOS because
 * otherwise copy and paste functionality is not available.
 */
function setApplicationMenu() {
    if (process.platform === 'darwin') {
        const template = [ {
            label: app.name,
            submenu: [
                {
                    role: 'services',
                    submenu: []
                },
                { type: 'separator' },
                { role: 'hide' },
                { role: 'hideothers' },
                { role: 'unhide' },
                { type: 'separator' },
                { role: 'quit' }
            ]
        }, {
            label: 'Edit',
            submenu: [ {
                label: 'Undo',
                accelerator: 'CmdOrCtrl+Z',
                selector: 'undo:'
            },
            {
                label: 'Redo',
                accelerator: 'Shift+CmdOrCtrl+Z',
                selector: 'redo:'
            },
            {
                type: 'separator'
            },
            {
                label: 'Cut',
                accelerator: 'CmdOrCtrl+X',
                selector: 'cut:'
            },
            {
                label: 'Copy',
                accelerator: 'CmdOrCtrl+C',
                selector: 'copy:'
            },
            {
                label: 'Paste',
                accelerator: 'CmdOrCtrl+V',
                selector: 'paste:'
            },
            {
                label: 'Select All',
                accelerator: 'CmdOrCtrl+A',
                selector: 'selectAll:'
            } ]
        }, {
            label: '&Window',
            role: 'window',
            submenu: [
                { role: 'minimize' },
                { role: 'close' }
            ]
        } ];

        Menu.setApplicationMenu(Menu.buildFromTemplate(template));
    } else {
        Menu.setApplicationMenu(null);
    }
}

/**
 * Opens new window with index.html(Jitsi Meet is loaded in iframe there).
 */
function createJitsiMeetWindow() {
    // Application menu.
    setApplicationMenu();

    // Check for Updates.
    autoUpdater.checkForUpdatesAndNotify();

    // Load the previous window state with fallback to defaults.
    const windowState = windowStateKeeper({
        defaultWidth: 800,
        defaultHeight: 600
    });

    // Path to root directory.
    const basePath = isDev ? __dirname : app.getAppPath();

    // URL for index.html which will be our entry point.
    const indexURL = URL.format({
        pathname: path.resolve(basePath, './build/index.html'),
        protocol: 'file:',
        slashes: true
    });

    // Options used when creating the main Jitsi Meet window.
    // Use a preload script in order to provide node specific functionality
    // to a isolated BrowserWindow in accordance with electron security
    // guideline.
    const options = {
        x: windowState.x,
        y: windowState.y,
        width: windowState.width,
        height: windowState.height,
        icon: path.resolve(basePath, './resources/icons/icon_512x512.png'),
        minWidth: 800,
        minHeight: 600,
        show: false,
        webPreferences: {
            enableRemoteModule: true,
            experimentalFeatures: true, // Insertable streams, for E2EE.
            nativeWindowOpen: true,
            nodeIntegration: false,
            preload: path.resolve(basePath, './build/preload.js')
        }
    };

    mainWindow = new BrowserWindow(options);
    windowState.manage(mainWindow);
    mainWindow.loadURL(indexURL);

    initPopupsConfigurationMain(mainWindow);
    setupAlwaysOnTopMain(mainWindow);
    setupPowerMonitorMain(mainWindow);
    setupScreenSharingMain(mainWindow, config.default.appName);

    mainWindow.webContents.on('new-window', (event, url, frameName) => {
        const target = getPopupTarget(url, frameName);

        if (!target || target === 'browser') {
            event.preventDefault();
            openExternalLink(url);
        }
    });
    mainWindow.on('closed', () => {
        mainWindow = null;
    });
    mainWindow.once('ready-to-show', () => {
        mainWindow.show();
    });

    /**
     * When someone tries to enter something like jitsi-meet://test
     *  while app is closed
     * it will trigger this event below
     */
    handleProtocolCall(process.argv.pop());
}

/**
 * Handler for application protocol links to initiate a conference.
 */
function handleProtocolCall(fullProtocolCall) {
    // don't touch when something is bad
    if (
        !fullProtocolCall
        || fullProtocolCall.trim() === ''
        || fullProtocolCall.indexOf(appProtocolSurplus) !== 0
    ) {
        return;
    }

    const inputURL = fullProtocolCall.replace(appProtocolSurplus, '');

    if (app.isReady() && mainWindow === null) {
        createJitsiMeetWindow();
    }

    protocolDataForFrontApp = inputURL;

    if (rendererReady) {
        mainWindow
            .webContents
            .send('protocol-data-msg', inputURL);
    }
}

/**
 * Force Single Instance Application.
 */
const gotInstanceLock = app.requestSingleInstanceLock();

if (!gotInstanceLock) {
    app.quit();
    process.exit(0);
}

/**
 * Run the application.
 */

app.on('activate', () => {
    if (mainWindow === null) {
        createJitsiMeetWindow();
    }
});

app.on('certificate-error',
    // eslint-disable-next-line max-params
    (event, webContents, url, error, certificate, callback) => {
        if (isDev) {
            event.preventDefault();
            callback(true);
        } else {
            callback(false);
        }
    }
);

app.on('ready', createJitsiMeetWindow);

app.on('second-instance', (event, commandLine) => {
    /**
     * If someone creates second instance of the application, set focus on
     * existing window.
     */
    if (mainWindow) {
        mainWindow.isMinimized() && mainWindow.restore();
        mainWindow.focus();

        /**
         * This is for windows [win32]
         * so when someone tries to enter something like jitsi-meet://test
         * while app is opened it will trigger protocol handler.
         */
        handleProtocolCall(commandLine.pop());
    }
});

app.on('window-all-closed', () => {
    // Don't quit the application on macOS.
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

// remove so we can register each time as we run the app.
app.removeAsDefaultProtocolClient(config.default.appProtocolPrefix);

// If we are running a non-packaged version of the app && on windows
if (isDev && process.platform === 'win32') {
    // Set the path of electron.exe and your app.
    // These two additional parameters are only available on windows.
    app.setAsDefaultProtocolClient(
        config.default.appProtocolPrefix,
        process.execPath,
        [ path.resolve(process.argv[1]) ]
    );
} else {
    app.setAsDefaultProtocolClient(config.default.appProtocolPrefix);
}

/**
 * This is for mac [darwin]
 * so when someone tries to enter something like jitsi-meet://test
 * it will trigger this event below
 */
app.on('open-url', (event, data) => {
    event.preventDefault();
    handleProtocolCall(data);
});

/**
 * This is to notify main.js [this] that front app is ready to receive messages.
 */
ipcMain.on('renderer-ready', () => {
    rendererReady = true;
    if (protocolDataForFrontApp) {
        mainWindow
            .webContents
            .send('protocol-data-msg', protocolDataForFrontApp);
    }
});
<|endoftext|>


<commit_before>var amdOptimize = require('amd-optimize');
var fs = require('fs');
var glob = require('glob');
var gulp = require('gulp');
var clean = require('gulp-clean');
var concat = require('gulp-concat');
var eventStream = require('event-stream');
var gulpFile = require('gulp-file');
var ignore = require('gulp-ignore');
var insert = require("gulp-insert");
var install = require("gulp-install");
var order = require('gulp-order');
var minifyCSS = require('gulp-minify-css');
var rename = require('gulp-rename');
var stylus = require('gulp-stylus');
var uglify = require('gulp-uglify');
var gulpUtil = require('gulp-util');
var watch = require('gulp-watch');
var webserver = require('gulp-webserver');
var mergeStream = require('merge-stream');
var path = require('path');
var requireDir = require('require-dir');
var argv = require('yargs').argv;

var config = require('../../config');
var nunjucksBuild = require('./plugins/nunjucks-build');
var imgurlsAbsolutify = require('./plugins/imgurls-absolutify');
var imgurlsCachebust = require('./plugins/imgurls-cachebust');
var imgurlsParse = require('./plugins/imgurls-parse');
var paths = require('./paths');

requireDir('tasks');

gulp.task('install', function(done) {
    // Bumps bower and npm dependencies.
    gulp.src(['bower.json', 'package.json'])
        .pipe(install())
        .pipe(gulpUtil.noop())  // Wait for dependencies to finish installing.
        .on('finish', function() {
            done();
        });
});

gulp.task('bower_copy', ['install'], function() {
    // Copy files from Bower into project.
    Object.keys(config.bowerConfig).forEach(function(source) {
        var dest = config.bowerConfig[source];
        gulp.src(paths.bower + source)
            .pipe(gulp.dest(dest));
    });
});

gulp.task('require_config', ['install'], function() {
    // Build a require.js file that contains a convenience call to
    // require.config that sets up some pre-known paths.
    gulp.src(paths.require)
        .pipe(insert.append(config.inlineRequireConfig))
        .pipe(gulp.dest(config.LIB_DEST_PATH));
});

function templates_build() {
    // Build Nunjucks templates into a templates.js file.
    // Takes about 200ms to compile all templates.
    return gulp.src(paths.html)
        .pipe(nunjucksBuild())
        .pipe(concat('templates.js'))
        .pipe(insert.prepend(
            '(function() {\n' +
            'var templates = {};\n'))
        .pipe(insert.append(
            'define("templates", ["nunjucks", "helpers"], function(nunjucks) {\n' +
            '    nunjucks.env = new nunjucks.Environment([], {autoescape: true});\n' +
            '    nunjucks.env.cache = nunjucks.templates = templates;\n' +
            '    console.log("Templates loaded");\n' +
            '    return nunjucks;\n' +
            '});\n' +
            '})();'
        ))
        .pipe(gulp.dest('src'));
}

gulp.task('templates_build', function() {
    templates_build();
});

gulp.task('templates_build_sync', function() {
    return templates_build();
});

function css_compile_pipe(stream) {
    // Compile .styl files into .styl.css files.
    // Takes about 2s to compile all CSS files.
    return stream
        .pipe(stylus())
        .on('error', function(err) {
            console.log('Stylus compile error: ' + err.name);
            console.log(err.message);
        })
        .pipe(rename(function(path) {
            path.extname = '.styl.css';
        }))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
}

function css_compile() {
    // Uses a helper function because it is also used by gulp-watch for
    // file-by-file CSS compiling.
    return css_compile_pipe(gulp.src(paths.styl));
}

gulp.task('css_compile', function() {
    css_compile();
});

gulp.task('css_compile_sync', function() {
    return css_compile();
});

gulp.task('css_bundles', ['css_compile_sync'], function() {
    // Read the config and build specified CSS bundles (like for splash.css).
    var streams = [];

    Object.keys(config.cssBundles || []).forEach(function(bundle) {
        streams.push(gulp.src(config.CSS_DEST_PATH + config.cssBundles[bundle])
            .pipe(concat(bundle))
            .pipe(minifyCSS())
            .pipe(gulp.dest(config.CSS_DEST_PATH))
        );
    });

    // Yes, cross the streams.
    if (streams.length) {
        return mergeStream.apply(this, streams);
    }
});

gulp.task('css_build_sync', ['css_bundles', 'css_compile_sync'], function() {
    // Bundle and minify all the CSS into include.css.
    var excludes = Object.keys(config.cssBundles || []).map(function(bundle) {
        // Exclude generated bundles if any specified in the config.
        return bundle;
    });
    // Exclude previously generated builds.
    excludes.push(paths.include_css);
    // Exclude from project config.
    if (config.cssExcludes) {
        excludes = excludes.concat(config.cssExcludes);
    }
    excludes = excludes.map(function(css) { return '!' + config.CSS_DEST_PATH + css; });

    // Determine which CSS files and which order to concat through index.html.
    var css_files = [];
    var data = fs.readFileSync(path.resolve('src', 'index.html'));
    data = data.toString();
    var css_pattern = new RegExp('href="/media/css/(.+.css)"', 'g');
    while (match = css_pattern.exec(data)) {
        css_files.push(match[1]);
    }
    css_src = css_files.map(function(css) {
        return config.CSS_DEST_PATH + css;
    });

    return gulp.src(css_src.concat(excludes))
        .pipe(stylus({compress: true}))
        .pipe(imgurlsCachebust())
        .pipe(minifyCSS())
        .pipe(order(css_files,
                    {base: config.CSS_DEST_PATH}))
        .pipe(concat(paths.include_css))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
});

gulp.task('imgurls_write', ['css_build_sync'], function() {
    // imgurls.txt is a list of cachebusted img URLs that is used by Zamboni
    // to generate the appcache manifest.
    gulp.src(config.CSS_DEST_PATH + paths.include_css)
        .pipe(imgurlsAbsolutify())
        .pipe(imgurlsParse())
        .pipe(rename('imgurls.txt'))
        .pipe(gulp.dest('src/media'));
});

gulp.task('buildID_write', function() {
    // Writes build ID to src/media/build_id.txt.
    var buildID = new Date().getTime().toString();
    gulpFile('build_id.txt', buildID)
        .pipe(gulp.dest('src/media'));
});

gulp.task('js_build', ['templates_build_sync'], function() {
    // Uses the AMD optimizer to bundle our JS modules.
    // Will read our RequireJS config to handle shims, paths, and name
    // anonymous modules.
    // Traces all modules and outputs them in the correct order.
    eventStream.merge(
        // Almond loader.
        gulp.src(paths.almond),
        // JS bundle.
        gulp.src(paths.js)
            .pipe(amdOptimize('main', {
                baseUrl: config.JS_DEST_PATH,
                findNestedDependencies: true,
                paths: config.requireConfig.paths,
                shim: config.requireConfig.shim,
                wrapShim: true
            }))
            .pipe(concat(paths.include_js)),
        // Init script.
        gulp.src(paths.init)
    )
        .pipe(order(['**/almond.js', '**/include.js', '**/init.js']))
        .pipe(uglify())
        .pipe(concat(paths.include_js))
        .pipe(gulp.dest(config.JS_DEST_PATH));
});

gulp.task('webserver', ['templates_build'], function() {
    // template -- template to serve (e.g., index (default), app, server).
    // port -- server port, defaults to config port or 8675.
    gulp.src(['src'])
        .pipe(webserver({
            fallback: argv.template || 'index' + '.html',
            port: argv.port || process.env.PORT || config.PORT || 8675
        }));
});

gulp.task('serve', ['webserver', 'css_compile', 'templates_build']);

gulp.task('clean', function() {
    gulp.src([
        config.CSS_DEST_PATH + 'splash.css',
        config.CSS_DEST_PATH + paths.include_css,
        config.JS_DEST_PATH + paths.include_js,
        paths.styl_compiled,
        '_tmp',
        'package/archives/*.zip',
        'src/locales',
        'src/media/locales',
        'src/media/build_id.txt',
        'src/media/imgurls.txt',
        'src/templates.js'
    ], {read: false})
        .pipe(clean({force: true}));
});

gulp.task('watch', function() {
    // Watch and recompile on change.
    // Note: does not detect new and deleted files while running.
    gulp.watch(paths.html, ['templates_build']);

    // CSS compilation uses gulp-watch to only compile modified files.
    gulp.src(paths.styl)
        .pipe(watch(paths.styl, function(files) {
            return css_compile_pipe(files);
        }));

    // Recompile all Stylus files if a lib file was modified.
    gulp.src(paths.styl_lib)
        .pipe(watch(paths.styl_lib, function(files) {
            return css_compile_pipe(gulp.src(paths.styl));
        }));
});

gulp.task('default', ['watch', 'serve']);
gulp.task('update', ['bower_copy', 'require_config']);
gulp.task('build', ['buildID_write', 'css_build_sync', 'js_build',
                    'templates_build_sync', 'imgurls_write']);

module.exports = {
    paths: paths
};
<commit_msg>js linting (reorder `require`s, remove unused variable)<commit_after>var fs = require('fs');
var path = require('path');

var amdOptimize = require('amd-optimize');
var argv = require('yargs').argv;
var clean = require('gulp-clean');
var concat = require('gulp-concat');
var eventStream = require('event-stream');
var gulp = require('gulp');
var gulpFile = require('gulp-file');
var gulpUtil = require('gulp-util');
var ignore = require('gulp-ignore');  // temporarily unused
var insert = require('gulp-insert');
var install = require('gulp-install');
var mergeStream = require('merge-stream');
var minifyCSS = require('gulp-minify-css');
var order = require('gulp-order');
var rename = require('gulp-rename');
var requireDir = require('require-dir');
var stylus = require('gulp-stylus');
var uglify = require('gulp-uglify');
var watch = require('gulp-watch');
var webserver = require('gulp-webserver');

var config = require('../../config');
var nunjucksBuild = require('./plugins/nunjucks-build');
var imgurlsAbsolutify = require('./plugins/imgurls-absolutify');
var imgurlsCachebust = require('./plugins/imgurls-cachebust');
var imgurlsParse = require('./plugins/imgurls-parse');
var paths = require('./paths');

requireDir('tasks');

gulp.task('install', function(done) {
    // Bumps bower and npm dependencies.
    gulp.src(['bower.json', 'package.json'])
        .pipe(install())
        .pipe(gulpUtil.noop())  // Wait for dependencies to finish installing.
        .on('finish', function() {
            done();
        });
});

gulp.task('bower_copy', ['install'], function() {
    // Copy files from Bower into project.
    Object.keys(config.bowerConfig).forEach(function(source) {
        var dest = config.bowerConfig[source];
        gulp.src(paths.bower + source)
            .pipe(gulp.dest(dest));
    });
});

gulp.task('require_config', ['install'], function() {
    // Build a require.js file that contains a convenience call to
    // require.config that sets up some pre-known paths.
    gulp.src(paths.require)
        .pipe(insert.append(config.inlineRequireConfig))
        .pipe(gulp.dest(config.LIB_DEST_PATH));
});

function templates_build() {
    // Build Nunjucks templates into a templates.js file.
    // Takes about 200ms to compile all templates.
    return gulp.src(paths.html)
        .pipe(nunjucksBuild())
        .pipe(concat('templates.js'))
        .pipe(insert.prepend(
            '(function() {\n' +
            'var templates = {};\n'))
        .pipe(insert.append(
            'define("templates", ["nunjucks", "helpers"], function(nunjucks) {\n' +
            '    nunjucks.env = new nunjucks.Environment([], {autoescape: true});\n' +
            '    nunjucks.env.cache = nunjucks.templates = templates;\n' +
            '    console.log("Templates loaded");\n' +
            '    return nunjucks;\n' +
            '});\n' +
            '})();'
        ))
        .pipe(gulp.dest('src'));
}

gulp.task('templates_build', function() {
    templates_build();
});

gulp.task('templates_build_sync', function() {
    return templates_build();
});

function css_compile_pipe(stream) {
    // Compile .styl files into .styl.css files.
    // Takes about 2s to compile all CSS files.
    return stream
        .pipe(stylus())
        .on('error', function(err) {
            console.log('Stylus compile error: ' + err.name);
            console.log(err.message);
        })
        .pipe(rename(function(path) {
            path.extname = '.styl.css';
        }))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
}

function css_compile() {
    // Uses a helper function because it is also used by gulp-watch for
    // file-by-file CSS compiling.
    return css_compile_pipe(gulp.src(paths.styl));
}

gulp.task('css_compile', function() {
    css_compile();
});

gulp.task('css_compile_sync', function() {
    return css_compile();
});

gulp.task('css_bundles', ['css_compile_sync'], function() {
    // Read the config and build specified CSS bundles (like for splash.css).
    var streams = [];

    Object.keys(config.cssBundles || []).forEach(function(bundle) {
        streams.push(gulp.src(config.CSS_DEST_PATH + config.cssBundles[bundle])
            .pipe(concat(bundle))
            .pipe(minifyCSS())
            .pipe(gulp.dest(config.CSS_DEST_PATH))
        );
    });

    // Yes, cross the streams.
    if (streams.length) {
        return mergeStream.apply(this, streams);
    }
});

gulp.task('css_build_sync', ['css_bundles', 'css_compile_sync'], function() {
    // Bundle and minify all the CSS into include.css.
    var excludes = Object.keys(config.cssBundles || []).map(function(bundle) {
        // Exclude generated bundles if any specified in the config.
        return bundle;
    });
    // Exclude previously generated builds.
    excludes.push(paths.include_css);
    // Exclude from project config.
    if (config.cssExcludes) {
        excludes = excludes.concat(config.cssExcludes);
    }
    excludes = excludes.map(function(css) { return '!' + config.CSS_DEST_PATH + css; });

    // Determine which CSS files and which order to concat through index.html.
    var css_files = [];
    var data = fs.readFileSync(path.resolve('src', 'index.html'));
    data = data.toString();
    var css_pattern = new RegExp('href="/media/css/(.+.css)"', 'g');
    while (match = css_pattern.exec(data)) {
        css_files.push(match[1]);
    }
    css_src = css_files.map(function(css) {
        return config.CSS_DEST_PATH + css;
    });

    return gulp.src(css_src.concat(excludes))
        .pipe(stylus({compress: true}))
        .pipe(imgurlsCachebust())
        .pipe(minifyCSS())
        .pipe(order(css_files,
                    {base: config.CSS_DEST_PATH}))
        .pipe(concat(paths.include_css))
        .pipe(gulp.dest(config.CSS_DEST_PATH));
});

gulp.task('imgurls_write', ['css_build_sync'], function() {
    // imgurls.txt is a list of cachebusted img URLs that is used by Zamboni
    // to generate the appcache manifest.
    gulp.src(config.CSS_DEST_PATH + paths.include_css)
        .pipe(imgurlsAbsolutify())
        .pipe(imgurlsParse())
        .pipe(rename('imgurls.txt'))
        .pipe(gulp.dest('src/media'));
});

gulp.task('buildID_write', function() {
    // Writes build ID to src/media/build_id.txt.
    var buildID = new Date().getTime().toString();
    gulpFile('build_id.txt', buildID)
        .pipe(gulp.dest('src/media'));
});

gulp.task('js_build', ['templates_build_sync'], function() {
    // Uses the AMD optimizer to bundle our JS modules.
    // Will read our RequireJS config to handle shims, paths, and name
    // anonymous modules.
    // Traces all modules and outputs them in the correct order.
    eventStream.merge(
        // Almond loader.
        gulp.src(paths.almond),
        // JS bundle.
        gulp.src(paths.js)
            .pipe(amdOptimize('main', {
                baseUrl: config.JS_DEST_PATH,
                findNestedDependencies: true,
                paths: config.requireConfig.paths,
                shim: config.requireConfig.shim,
                wrapShim: true
            }))
            .pipe(concat(paths.include_js)),
        // Init script.
        gulp.src(paths.init)
    )
        .pipe(order(['**/almond.js', '**/include.js', '**/init.js']))
        .pipe(uglify())
        .pipe(concat(paths.include_js))
        .pipe(gulp.dest(config.JS_DEST_PATH));
});

gulp.task('webserver', ['templates_build'], function() {
    // template -- template to serve (e.g., index (default), app, server).
    // port -- server port, defaults to config port or 8675.
    gulp.src(['src'])
        .pipe(webserver({
            fallback: argv.template || 'index' + '.html',
            port: argv.port || process.env.PORT || config.PORT || 8675
        }));
});

gulp.task('serve', ['webserver', 'css_compile', 'templates_build']);

gulp.task('clean', function() {
    gulp.src([
        config.CSS_DEST_PATH + 'splash.css',
        config.CSS_DEST_PATH + paths.include_css,
        config.JS_DEST_PATH + paths.include_js,
        paths.styl_compiled,
        '_tmp',
        'package/archives/*.zip',
        'src/locales',
        'src/media/locales',
        'src/media/build_id.txt',
        'src/media/imgurls.txt',
        'src/templates.js'
    ], {read: false})
        .pipe(clean({force: true}));
});

gulp.task('watch', function() {
    // Watch and recompile on change.
    // Note: does not detect new and deleted files while running.
    gulp.watch(paths.html, ['templates_build']);

    // CSS compilation uses gulp-watch to only compile modified files.
    gulp.src(paths.styl)
        .pipe(watch(paths.styl, function(files) {
            return css_compile_pipe(files);
        }));

    // Recompile all Stylus files if a lib file was modified.
    gulp.src(paths.styl_lib)
        .pipe(watch(paths.styl_lib, function() {
            return css_compile_pipe(gulp.src(paths.styl));
        }));
});

gulp.task('default', ['watch', 'serve']);
gulp.task('update', ['bower_copy', 'require_config']);
gulp.task('build', ['buildID_write', 'css_build_sync', 'js_build',
                    'templates_build_sync', 'imgurls_write']);

module.exports = {
    paths: paths
};
<|endoftext|>


<commit_before>const electron = require("electron");
const {app, BrowserWindow} = electron;

app.on("ready", () => {
    var videoWin=new BrowserWindow({width:900, height:600, frame:false, fullscreen:true});
    videoWin.loadURL(`file://${__dirname}/views/index.html`);
    videoWin.webContents.openDevTools();   // Opening the dev tools
});

exports.quit=()=>{
    app.quit();
}<commit_msg>Disable developer tools.<commit_after>const electron = require("electron");
const {app, BrowserWindow} = electron;

app.on("ready", () => {
    var videoWin=new BrowserWindow({width:900, height:600, frame:false, fullscreen:true});
    videoWin.loadURL(`file://${__dirname}/views/index.html`);
    //videoWin.webContents.openDevTools();   // Opening the dev tools
});

exports.quit=()=>{
    app.quit();
}<|endoftext|>


<commit_before>// ******************
//
//
//
// NOT CURRENTLY USED
// NOT CURRENTLY USED
//
//
//
// ******************

function removeSVGs(element) { //clean the SVG so previous visualizations aren't still there

	d3.select(element)
		.selectAll("svg")
		.remove();

}

function colorForChromosome(d) {

	if (d.toLowerCase() == "x" || d.toLowerCase() == "y") {
		return "#ff8026"
	}

	var colorScale = d3.interpolateRgb("#fff", "#000");
	var ratio = parseFloat(d) / 22;
	var chromColor = colorScale(ratio);

	return chromColor;

}

function colorForNucleotide(d) {

	var colors = {"A" : "red", "G" : "green", "T" : "yellow", "U" : "yellow", "C" : "blue"};
	var c = d in colors ? colors[d] : "darkgrey";

	return c;

}

function displayInfo(value, kind) {

	console.log(value);
	console.log(kind);

	d3.select("#valueInfo")
		.text(value);

	d3.select("#kindInfo")
		.text(kind);
}

console.log(visualizeProteinVariantColumn('p.E343G; p.E401G; p.E357G; p.E413G'))

//given a position and protein variant returns the full names of the proteins
function parseProteinVariantData(proteinVariantData){
	var aminoAAs = proteinVariantData.replace('p.', '').split(";"); //strip the 'p.', then split the amino acids
	var aaSub = aminoAAs[0]; //take the first ones for now
	//ALERT!? Does this work properly for stop/loss?
	var aaS = aaSub.replace(/\d+/, ''); //strip out the positions
	var refAA = aaS[0];
	var altAA = aaS[1];
	return [refAA, altAA]
}

//given the three letter code for an amino acid, returns the correct color for it by first getting its group, and then the color associated with the group
function getColorForAminoAcid(aminoAcid){
	aminoAcidGroups = {'Aliphatic': ['Ala', 'Gly', 'Ile', 'Leu', 'Pro', 'Val'], 'Aromatic': ['Phe', 'Trp', 'Tyr'], 'Acidic': ['Asp', 'Glu'], 'Basic': ['Arg', 'His', 'Lys'], 'Hydroxylic': ['Ser', 'Thr'], 'Sulfur-Containing': ['Cys', 'Met'], 'Amidic': ['Asn', 'Gln'], 'Stop': ['Stop']};
	//TODO fix the amino acid colors and make them better
	aminoAcidColors = {'Aliphatic': ['#20A39E', '#20A39E', '#20A39E', '#20A39E', '#20A39E', '#20A39E'], 'Aromatic': ['#98CE00', '#98CE00', '#98CE00'], 'Acidic': ['#FF715B', '#FF715B'], 'Basic': ['#F0386B', '#F0386B', '#F0386B'], 'Hydroxylic': ['#93E5AB', '#93E5AB'], 'Sulfur-Containing': ['#FB8B24', '#FB8B24'], 'Amidic': ['#FB8B24', '#FB8B24'], 'Stop': ['#FB8B24']};
	for (var key in aminoAcidGroups) {
		var aminoAcids = aminoAcidGroups[key];
		var cntr = 0; //find the correct amino acid match and the index we find it at
		for (idx in aminoAcids){
			var curAA = aminoAcids[idx];
			if (curAA == aminoAcid){
				return aminoAcidColors[key][cntr]
			}
			cntr += 1;
		}
	}
}

//parse the column protein variant data and returns a list with the following form [colorRefAA, colorAltAA, refAA, altAA]
function visualizeProteinVariantColumn(proteinVariantData) {
	//define how amino acid abbreviations work
	var aminoAcidAbbreviations = { 'A':'Ala', 'R':'Arg', 'N':'Asn', 'D':'Asp', 'C':'Cys', 'E':'Glu', 'Q':'Gln', 'G':'Gly', 'H':'His', 'I':'Ile', 'L':'Leu', 'K':'Lys', 'M':'Met', 'F':'Phe', 'P': 'Pro', 'S': 'Ser', 'T':'Thr', 'W':'Trp', 'Y':'Tyr', 'V': 'Val', '*': 'Stop' };
	var aaS = parseProteinVariantData(proteinVariantData);
	var refAALong = aminoAcidAbbreviations[aaS[0]];
	var altAALong = aminoAcidAbbreviations[aaS[1]];
	//console.log(refAALong);
	return [getColorForAminoAcid(refAALong), getColorForAminoAcid(altAALong), refAALong, altAALong];
}

function highlightForTrack() {

	return "white";

}

function renderTracks(element, data) {

	var keysForTracks = ["C", "D"];

	var widthStep = $(element).width() / (data.length + 1);

	$.each(keysForTracks, (index, key) => { //it drives me insane that the parameters are passed in different orders in $.each() and $.map()

		var trackData = $.map(data, (datum, index) => datum.xyz[key] );

		d3.select(element)
			.append("g")
			.attr("class", key)
			.selectAll("rect")
			.data(trackData)
			.enter()
			.append("rect")
			.attr("x", (d, i) => (i + 0.5) * widthStep)
			.attr("y", 150 + index * 10)
			.attr("width", widthStep)
			.attr("height", 10)
			.attr("fill", (d, i) => fillForTrackDatum(d))

		d3.select(element)
			.append("g")
			.attr("class", key + "text")
			.selectAll("text")
			.data(trackData)
			.enter()
			.append("text")
			.attr("x",(d, i) => (i + 1) * widthStep)
			.attr("y", 150 + index * 10 + 5 + 1)
			.style("font-size", 9)
			.style("alignment-baseline", "middle")
			.text(d => d)
			.style("text-anchor", "middle")

	});

}

function fillForTrackDatum(d) {

	return getRandomColor();

}

function deepClone(thing) {

	return JSON.parse(JSON.stringify(thing));

}

function getColor(index, total, highlight) {

	if (!highlight) {

		return d3.interpolateSpectral(index / (total - 1));

	} else {

		var rgb = d3.interpolateSpectral(index / (total - 1)).replace(/ /g, "");
		var rgbValues = rgb.substring(rgb.indexOf("(") + 1, rgb.length - 2);

		var r = parseInt(rgbValues.split(",")[0]);
		var g = parseInt(rgbValues.split(",")[1]);
		var b = parseInt(rgbValues.split(",")[2]);

		var hsvOriginal = RGBtoHSV(r, g, b);
		var h = hsvOriginal.h;
		var s = hsvOriginal.s;
		var v = hsvOriginal.v;

		var finalRGB = HSVtoRGB(h / 360, 0 / 100, 100 / 100); //white (for now)

		return "rgb(" + finalRGB.r + "," + finalRGB.g + "," + finalRGB.b + ")";

	}

}

function renderRadar() {

	var element = "detailSVG";

	if (d3.select("#" + element).size() == 0) {

		d3.select(outerElement)
			.append("svg")
			.attr("id", element);

	}

	element = "#" + element;

	var radarData = getRadarData();

	if (radarData.length == 0) {
		return d3.select(element).selectAll("*").remove();
	}

	var formatted = $.map(radarData, function(variant, index) {
		var toPlot = [];
		for (var key in variant.xyz) {
			toPlot.push({
				"axis" : key,
				"value" : variant.xyz[key]
			});
		}

		return [toPlot];
	});

	var color = d3.scaleLinear()
				.range(["#EDC951","#CC333F","#00A0B0"]);

	var margin = {top: 50, right: 100, bottom: 100, left: 50};

	var radarChartOptions = {
		w: 200,
		h: 200,
		margin: margin,
	  	levels: 5,
	  	roundStrokes: true,
	  	color: color
	};

	RadarChart(element, formatted, radarChartOptions);

}

var lastRadarData = [];

function getRadarData() {

	var keys = [];

	d3.selectAll(".selectedForRadar").each(function(element, index){

		var id = d3.select(this).attr("id");
		keys.push(parseInt(id.substring(id.indexOf("e") + 1)) - 1); //TODO: actually get key

	});

	var selectedData = $.map(keys, key => data[key]); //the data actually selected by the user
	var indexOfLatestItem;

	//make sure latest add is last in array
	var finalData = [];

	$.grep(lastRadarData, function(element) {
        if ($.inArray(element, selectedData) != -1) {
        	finalData.push(element);
        }
	});

	$.grep(selectedData, function(element) {
		if ($.inArray(element, lastRadarData) == -1) { //put this new element at the end
			finalData.push(element);
		};
	});

	lastRadarData = finalData;
	return finalData;

}

function sortOnKeys(data, keys, increasing) {

	var sorted = data.sort(function(a, b) {

		var aEl = a;
		var bEl = b;

		for (i = 0; i < keys.length; i++) {
			aEl = aEl[keys[i]];
			bEl = bEl[keys[i]];
		}

		return increasing ? d3.ascending(aEl, bEl) : d3.descending(aEl, bEl);

	});

	sorted.unshift({"key" : "keyX", "xyz" : {"A" : 0, "B" : 0, "C" : 0, "D" : 0, "E" : 0, "F" : 0}});
	sorted.push({"key" : "keyX", "xyz" : {"A" : 0, "B" : 0, "C" : 0, "D" : 0, "E" : 0, "F" : 0}});

	return sorted;
}

function getRandomColor() {

	return "#" + Math.floor(Math.random() * 16777215).toString(16);

}

function showSpinner() {

	$("#spinnerContainer").show()
	$("#inputContainer").hide()

}

function hideSpinner() {

	console.log("hiding spinner");

	$("#spinnerContainer").hide()
	$("#inputContainer").show()

}

function scrollToElement(element) {

	$("html, body").animate({
        scrollTop: $(element).offset().top
    }, 2000);

}

function HSVtoRGB(h, s, v) {

    var r, g, b, i, f, p, q, t;

    if (arguments.length === 1) {
        s = h.s, v = h.v, h = h.h;
    }

    i = Math.floor(h * 6);

    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);

    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }

    return {
        r: Math.round(r * 255),
        g: Math.round(g * 255),
        b: Math.round(b * 255)
    };
}

function RGBtoHSV() {

    var rr, gg, bb,
        r = arguments[0] / 255,
        g = arguments[1] / 255,
        b = arguments[2] / 255,
        h, s,
        v = Math.max(r, g, b),
        diff = v - Math.min(r, g, b),
        diffc = function(c) {
            return (v - c) / 6 / diff + 1 / 2;
        };

    if (diff == 0) {

        h = s = 0;

    } else {

        s = diff / v;
        rr = diffc(r);
        gg = diffc(g);
        bb = diffc(b);

        if (r === v) {
            h = bb - gg;
        } else if (g === v) {
            h = (1 / 3) + rr - bb;
        } else if (b === v) {
            h = (2 / 3) + gg - rr;
        }

        if (h < 0) {
            h += 1;
        } else if (h > 1) {
            h -= 1;
        }
    }

    return {
        h: Math.round(h * 360),
        s: Math.round(s * 100),
        v: Math.round(v * 100)
    };
}

function highlightForSpindle() {

	return "white";

}

function colorForSpindle() {

	return "darkgrey";

}

function highlightForCircle() {

	return "white";

}

function colorForAnnotation(datum, index, nSpiralAnnotations) {

	return "#" + Math.floor((index + 1) / (nSpiralAnnotations + 1) * 16777215).toString(16);

}

function getSpiralData(nVariants, nSpiralAnnotations) {

	var emptyProbability = .25;

	return $.map(new Array(nVariants), function(index, element) {

		return [$.map(new Array(nSpiralAnnotations), function(i, e) {

			return Math.random() > emptyProbability ? parseFloat(Math.random().toFixed(3)) : -1;

		})];

	});

}

function fixdata(data) { //copied from?
	var o = "", l = 0, w = 10240;
	for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w)));
	o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(l*w)));
	return o;
}
<commit_msg>some local change<commit_after>// ******************
//
//
//
// NOT CURRENTLY USED
// NOT CURRENTLY USED
//
//
//
// ******************

function removeSVGs(element) { //clean the SVG so previous visualizations aren't still there

	d3.select(element)
		.selectAll("svg")
		.remove();

}

function colorForChromosome(d) {

	if (d.toLowerCase() == "x" || d.toLowerCase() == "y") {
		return "#ff8026"
	}

	var colorScale = d3.interpolateRgb("#fff", "#000");
	var ratio = parseFloat(d) / 22;
	var chromColor = colorScale(ratio);

	return chromColor;

}

function colorForNucleotide(d) {

	var colors = {"A" : "red", "G" : "green", "T" : "yellow", "U" : "yellow", "C" : "blue"};
	var c = d in colors ? colors[d] : "darkgrey";

	return c;

}

function displayInfo(value, kind) {

	console.log(value);
	console.log(kind);

	d3.select("#valueInfo")
		.text(value);

	d3.select("#kindInfo")
		.text(kind);
}

console.log(visualizeProteinVariantColumn('p.E343G; p.E401G; p.E357G; p.E413G'))
downloadDataAsXls();
console.log('fibbonaci')

//given a position and protein variant returns the full names of the proteins
function parseProteinVariantData(proteinVariantData){
	var aminoAAs = proteinVariantData.replace('p.', '').split(";"); //strip the 'p.', then split the amino acids
	var aaSub = aminoAAs[0]; //take the first ones for now
	//ALERT!? Does this work properly for stop/loss?
	var aaS = aaSub.replace(/\d+/, ''); //strip out the positions
	var refAA = aaS[0];
	var altAA = aaS[1];
	return [refAA, altAA]
}

//given the three letter code for an amino acid, returns the correct color for it by first getting its group, and then the color associated with the group
function getColorForAminoAcid(aminoAcid){
	aminoAcidGroups = {'Aliphatic': ['Ala', 'Gly', 'Ile', 'Leu', 'Pro', 'Val'], 'Aromatic': ['Phe', 'Trp', 'Tyr'], 'Acidic': ['Asp', 'Glu'], 'Basic': ['Arg', 'His', 'Lys'], 'Hydroxylic': ['Ser', 'Thr'], 'Sulfur-Containing': ['Cys', 'Met'], 'Amidic': ['Asn', 'Gln'], 'Stop': ['Stop']};
	//TODO fix the amino acid colors and make them better
	aminoAcidColors = {'Aliphatic': ['#20A39E', '#20A39E', '#20A39E', '#20A39E', '#20A39E', '#20A39E'], 'Aromatic': ['#98CE00', '#98CE00', '#98CE00'], 'Acidic': ['#FF715B', '#FF715B'], 'Basic': ['#F0386B', '#F0386B', '#F0386B'], 'Hydroxylic': ['#93E5AB', '#93E5AB'], 'Sulfur-Containing': ['#FB8B24', '#FB8B24'], 'Amidic': ['#FB8B24', '#FB8B24'], 'Stop': ['#FB8B24']};
	for (var key in aminoAcidGroups) {
		var aminoAcids = aminoAcidGroups[key];
		var cntr = 0; //find the correct amino acid match and the index we find it at
		for (idx in aminoAcids){
			var curAA = aminoAcids[idx];
			if (curAA == aminoAcid){
				return aminoAcidColors[key][cntr]
			}
			cntr += 1;
		}
	}
}

//parse the column protein variant data and returns a list with the following form [colorRefAA, colorAltAA, refAA, altAA]
function visualizeProteinVariantColumn(proteinVariantData) {
	//define how amino acid abbreviations work
	var aminoAcidAbbreviations = { 'A':'Ala', 'R':'Arg', 'N':'Asn', 'D':'Asp', 'C':'Cys', 'E':'Glu', 'Q':'Gln', 'G':'Gly', 'H':'His', 'I':'Ile', 'L':'Leu', 'K':'Lys', 'M':'Met', 'F':'Phe', 'P': 'Pro', 'S': 'Ser', 'T':'Thr', 'W':'Trp', 'Y':'Tyr', 'V': 'Val', '*': 'Stop' };
	var aaS = parseProteinVariantData(proteinVariantData);
	var refAALong = aminoAcidAbbreviations[aaS[0]];
	var altAALong = aminoAcidAbbreviations[aaS[1]];
	//console.log(refAALong);
	return [getColorForAminoAcid(refAALong), getColorForAminoAcid(altAALong), refAALong, altAALong];
}

function highlightForTrack() {

	return "white";

}

function renderTracks(element, data) {

	var keysForTracks = ["C", "D"];

	var widthStep = $(element).width() / (data.length + 1);

	$.each(keysForTracks, (index, key) => { //it drives me insane that the parameters are passed in different orders in $.each() and $.map()

		var trackData = $.map(data, (datum, index) => datum.xyz[key] );

		d3.select(element)
			.append("g")
			.attr("class", key)
			.selectAll("rect")
			.data(trackData)
			.enter()
			.append("rect")
			.attr("x", (d, i) => (i + 0.5) * widthStep)
			.attr("y", 150 + index * 10)
			.attr("width", widthStep)
			.attr("height", 10)
			.attr("fill", (d, i) => fillForTrackDatum(d))

		d3.select(element)
			.append("g")
			.attr("class", key + "text")
			.selectAll("text")
			.data(trackData)
			.enter()
			.append("text")
			.attr("x",(d, i) => (i + 1) * widthStep)
			.attr("y", 150 + index * 10 + 5 + 1)
			.style("font-size", 9)
			.style("alignment-baseline", "middle")
			.text(d => d)
			.style("text-anchor", "middle")

	});

}

function fillForTrackDatum(d) {

	return getRandomColor();

}

function deepClone(thing) {

	return JSON.parse(JSON.stringify(thing));

}

function getColor(index, total, highlight) {

	if (!highlight) {

		return d3.interpolateSpectral(index / (total - 1));

	} else {

		var rgb = d3.interpolateSpectral(index / (total - 1)).replace(/ /g, "");
		var rgbValues = rgb.substring(rgb.indexOf("(") + 1, rgb.length - 2);

		var r = parseInt(rgbValues.split(",")[0]);
		var g = parseInt(rgbValues.split(",")[1]);
		var b = parseInt(rgbValues.split(",")[2]);

		var hsvOriginal = RGBtoHSV(r, g, b);
		var h = hsvOriginal.h;
		var s = hsvOriginal.s;
		var v = hsvOriginal.v;

		var finalRGB = HSVtoRGB(h / 360, 0 / 100, 100 / 100); //white (for now)

		return "rgb(" + finalRGB.r + "," + finalRGB.g + "," + finalRGB.b + ")";

	}

}

function renderRadar() {

	var element = "detailSVG";

	if (d3.select("#" + element).size() == 0) {

		d3.select(outerElement)
			.append("svg")
			.attr("id", element);

	}

	element = "#" + element;

	var radarData = getRadarData();

	if (radarData.length == 0) {
		return d3.select(element).selectAll("*").remove();
	}

	var formatted = $.map(radarData, function(variant, index) {
		var toPlot = [];
		for (var key in variant.xyz) {
			toPlot.push({
				"axis" : key,
				"value" : variant.xyz[key]
			});
		}

		return [toPlot];
	});

	var color = d3.scaleLinear()
				.range(["#EDC951","#CC333F","#00A0B0"]);

	var margin = {top: 50, right: 100, bottom: 100, left: 50};

	var radarChartOptions = {
		w: 200,
		h: 200,
		margin: margin,
	  	levels: 5,
	  	roundStrokes: true,
	  	color: color
	};

	RadarChart(element, formatted, radarChartOptions);

}

var lastRadarData = [];

function getRadarData() {

	var keys = [];

	d3.selectAll(".selectedForRadar").each(function(element, index){

		var id = d3.select(this).attr("id");
		keys.push(parseInt(id.substring(id.indexOf("e") + 1)) - 1); //TODO: actually get key

	});

	var selectedData = $.map(keys, key => data[key]); //the data actually selected by the user
	var indexOfLatestItem;

	//make sure latest add is last in array
	var finalData = [];

	$.grep(lastRadarData, function(element) {
        if ($.inArray(element, selectedData) != -1) {
        	finalData.push(element);
        }
	});

	$.grep(selectedData, function(element) {
		if ($.inArray(element, lastRadarData) == -1) { //put this new element at the end
			finalData.push(element);
		};
	});

	lastRadarData = finalData;
	return finalData;

}

function sortOnKeys(data, keys, increasing) {

	var sorted = data.sort(function(a, b) {

		var aEl = a;
		var bEl = b;

		for (i = 0; i < keys.length; i++) {
			aEl = aEl[keys[i]];
			bEl = bEl[keys[i]];
		}

		return increasing ? d3.ascending(aEl, bEl) : d3.descending(aEl, bEl);

	});

	sorted.unshift({"key" : "keyX", "xyz" : {"A" : 0, "B" : 0, "C" : 0, "D" : 0, "E" : 0, "F" : 0}});
	sorted.push({"key" : "keyX", "xyz" : {"A" : 0, "B" : 0, "C" : 0, "D" : 0, "E" : 0, "F" : 0}});

	return sorted;
}

function getRandomColor() {

	return "#" + Math.floor(Math.random() * 16777215).toString(16);

}

function showSpinner() {

	$("#spinnerContainer").show()
	$("#inputContainer").hide()

}

function hideSpinner() {

	console.log("hiding spinner");

	$("#spinnerContainer").hide()
	$("#inputContainer").show()

}

function scrollToElement(element) {

	$("html, body").animate({
        scrollTop: $(element).offset().top
    }, 2000);

}

function HSVtoRGB(h, s, v) {

    var r, g, b, i, f, p, q, t;

    if (arguments.length === 1) {
        s = h.s, v = h.v, h = h.h;
    }

    i = Math.floor(h * 6);

    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);

    switch (i % 6) {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
    }

    return {
        r: Math.round(r * 255),
        g: Math.round(g * 255),
        b: Math.round(b * 255)
    };
}

function RGBtoHSV() {

    var rr, gg, bb,
        r = arguments[0] / 255,
        g = arguments[1] / 255,
        b = arguments[2] / 255,
        h, s,
        v = Math.max(r, g, b),
        diff = v - Math.min(r, g, b),
        diffc = function(c) {
            return (v - c) / 6 / diff + 1 / 2;
        };

    if (diff == 0) {

        h = s = 0;

    } else {

        s = diff / v;
        rr = diffc(r);
        gg = diffc(g);
        bb = diffc(b);

        if (r === v) {
            h = bb - gg;
        } else if (g === v) {
            h = (1 / 3) + rr - bb;
        } else if (b === v) {
            h = (2 / 3) + gg - rr;
        }

        if (h < 0) {
            h += 1;
        } else if (h > 1) {
            h -= 1;
        }
    }

    return {
        h: Math.round(h * 360),
        s: Math.round(s * 100),
        v: Math.round(v * 100)
    };
}

function highlightForSpindle() {

	return "white";

}

function colorForSpindle() {

	return "darkgrey";

}

function highlightForCircle() {

	return "white";

}

function colorForAnnotation(datum, index, nSpiralAnnotations) {

	return "#" + Math.floor((index + 1) / (nSpiralAnnotations + 1) * 16777215).toString(16);

}

function getSpiralData(nVariants, nSpiralAnnotations) {

	var emptyProbability = .25;

	return $.map(new Array(nVariants), function(index, element) {

		return [$.map(new Array(nSpiralAnnotations), function(i, e) {

			return Math.random() > emptyProbability ? parseFloat(Math.random().toFixed(3)) : -1;

		})];

	});

}

function fixdata(data) { //copied from?
	var o = "", l = 0, w = 10240;
	for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w)));
	o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(l*w)));
	return o;
}
<|endoftext|>


<commit_before>jQuery = $ = require('jquery');
Backbone = require('backbone');
Backbone.$ = jQuery;

Handlebars = require('handlebars');
_ = require('underscore');

require('./js/helpers.js'); // Handelbars helpers
require('bootstrap/dist/js/bootstrap.min.js'); // bootstrap
require('./js/lib/jquery.minicolors.min.js'); // minicolors
require('./js/lib/jLouvain.js'); // Community detection

App = {};

App.init = function(options){

    //Model
    var Model = require('./js/models/main.js');
    App.model = new Model(options);

    //Server collection
    var Servers = require('./js/collections/psicquicServers.js');
    App.model.servers = new Servers();

    App.model.servers.fetch();

    //Views
    var Main = require('./js/views/main');
    var Navbar = require('./js/views/navbar');
    var SideMenu = require('./js/views/sidemenu');
    var Logger = require('./js/views/logger');
    var Graph = require('./js/views/graph');
    var UPDialog = require('./js/views/UPdialog');
    var Pathway = require('./js/views/pathway');
    var Import = require('./js/views/import');
    var Layout = require('./js/views/layout');

    App.views = {};

    //Main
    App.views.main = new Main({el:'body'});
    App.views.main.render();

    //Nav bar
    App.views.navbar = new Navbar({el:'#navbar'});
    App.views.navbar.render();

    App.views.side = new SideMenu({el:'#sidemenu'});
    App.views.side.render();

    //Dialogs
    App.views.UPDialog = new UPDialog({el:'#dialog'});
    App.views.import = new Import({el:'#dialog'});
    App.views.layout = new Layout({el:'#dialog'});
    App.views.pathway = new Pathway({el:'#dialog'});

    //Logger
    App.views.logger = new Logger({el:'#logger'});

    //Graph
    App.views.graph = new Graph({el:'network'});

    //Import additional layout libraries
    Springy = require('springy');

    //Init minicolors
    $('.minicolors-input').minicolors({ theme:'bootstrap'});

    //Init tooltips
    $('[data-toggle="tooltip"]').tooltip();
};

App.init();<commit_msg>initialize the servers list with harcoded values in case the call to the registry fails<commit_after>jQuery = $ = require('jquery');
Backbone = require('backbone');
Backbone.$ = jQuery;

Handlebars = require('handlebars');
_ = require('underscore');

require('./js/helpers.js'); // Handelbars helpers
require('bootstrap/dist/js/bootstrap.min.js'); // bootstrap
require('./js/lib/jquery.minicolors.min.js'); // minicolors
require('./js/lib/jLouvain.js'); // Community detection

App = {};

App.init = function(options){

    //Model
    var Model = require('./js/models/main.js');
    App.model = new Model(options);

    var serverLst = require('./js/collections/serversList.js'); //JSON list of servers in case of error
    //Server collection
    var Servers = require('./js/collections/psicquicServers.js');
    App.model.servers = new Servers(serverLst);

    App.model.servers.fetch();

    //Views
    var Main = require('./js/views/main');
    var Navbar = require('./js/views/navbar');
    var SideMenu = require('./js/views/sidemenu');
    var Logger = require('./js/views/logger');
    var Graph = require('./js/views/graph');
    var UPDialog = require('./js/views/UPdialog');
    var Pathway = require('./js/views/pathway');
    var Import = require('./js/views/import');
    var Layout = require('./js/views/layout');

    App.views = {};

    //Main
    App.views.main = new Main({el:'body'});
    App.views.main.render();

    //Nav bar
    App.views.navbar = new Navbar({el:'#navbar'});
    App.views.navbar.render();

    App.views.side = new SideMenu({el:'#sidemenu'});
    App.views.side.render();

    //Dialogs
    App.views.UPDialog = new UPDialog({el:'#dialog'});
    App.views.import = new Import({el:'#dialog'});
    App.views.layout = new Layout({el:'#dialog'});
    App.views.pathway = new Pathway({el:'#dialog'});

    //Logger
    App.views.logger = new Logger({el:'#logger'});

    //Graph
    App.views.graph = new Graph({el:'network'});

    //Import additional layout libraries
    Springy = require('springy');

    //Init minicolors
    $('.minicolors-input').minicolors({ theme:'bootstrap'});

    //Init tooltips
    $('[data-toggle="tooltip"]').tooltip();
};

App.init();<|endoftext|>


<commit_before>
the_input = "SELECT sum(value), year, description \n FROM database \n WHERE commodity = 'jrice' \n GROUP BY year, description"

var main = function(input) {

    split = input.split(/SELECT|FROM|WHERE|GROUP BY/)

    i_select = split[1].replace(/\n| /g,'').split(',')
    i_from = split[2].replace(/\n| /g,'').split(',')
    i_where = split[3].replace(/\n/g,'').split('AND ')
    i_groupby = split[4].replace(/\n| /g,'').split(',')

    // database -> endpoint

    endpoint = '0.0.0.0:9200/' + i_from + '/_search?'

    // anything wrapped in the select statement becomes the end filter: sum, avg, min, max to start

    last_filter = []
    for (var i = 0; i < i_select.length; i++) {
        m = i_select[i].match(/sum\(|avg\(|min\(|max\(/g);
        if (m) {
            last_filter.push(m[0].replace(/\(/,''))
            last_filter.push(i_select[i].replace(m[0],'').replace(')',''))
        }
    }

    // anything in where becomes the filter (in order), currently only supports =, should add 'in' soon
    filters = []
    for (var i = 0; i < i_where.length; i++) {
        filters.push(i_where[i].replace(/\(|\)/g,'').split(/= |in|,/))
    }

    // make the object

    a = agg_wrap(where_obj(filters,groupby_obj(i_groupby,last_filter_obj())))

    console.log(endpoint)
    return JSON.stringify(a)
}

var last_filter_obj = function() {
    var last_filter_object = {};
    last_filter_object[last_filter[0]] = { "field" : last_filter[1] };
    return last_filter_object;
}

var groupby_obj = function(array,object) {
    var dis_array = array.slice(0);
    if (dis_array.length === 1) {
        obj = {};
        gb_name = 'group_by_' + dis_array[0];
        obj[gb_name] = { "terms" : { "field" : dis_array[0]}, "aggs" : object };
        return obj;
      }
    else {
        var aggs = {}; // important to use var here to scope properly
        gb = dis_array.shift();
        gb_name = 'group_by_' + gb;
        aggs[gb_name] = { "terms" : { "field" : gb}, "aggs" : groupby_obj(dis_array,object) };
        return aggs;
      }
}

var where_obj = function(array,object) {
    var dis_array = array.slice(0);
    if (dis_array.length === 1) {
        obj = {};
        w = dis_array[0]
        tw = w[0].replace(/ /g,'')
        if (w.length === 2){
          var term = {};
          term[tw] = w[1].replace(/\\|\'| /g,'')
          w_name = 'where_' + tw + '_' + term[tw];
          obj[w_name] = { "filter" : { "term" : term}, "aggs" : object };
        }
        else {
          terms = []
          for (var t = 0; t < w.length - 1; t++){
            var term = {};
            term[tw] = w[t+1].replace(/\\|\'| /g,'');
            terms.push({ "term" : term })
          }
          w_name = 'where_' + tw + '_multiple';
          obj[w_name] = { "filter" : { "and" : terms}, "aggs" : object };
        }
        return obj;
      }
    else {
        var aggs = {}; // important to use var here to scope properly
        w = dis_array.shift();
        tw = w[0].replace(/ /g,'')
        if (w.length === 2){
          var term = {};
          term[tw] = w[1].replace(/\\|\'| /g,'');
          w_name = 'where_' + tw + '_' + term[tw];
          aggs[w_name] = { "filter" : { "term" : term}, "aggs" : where_obj(dis_array,object) };
        }
        else {
          terms = [];
          for (var t = 0; t < w.length - 1; t++){
            var term = {};
            term[tw] = w[t+1].replace(/\\|\'| /g,'');
            terms.push({ "term" : term })
          };
          w_name = 'where_' + tw + '_multiple';
          aggs[w_name] = { "filter" : { "and" : terms}, "aggs" : where_obj(dis_array,object) };
        }
        return aggs;
      }
}

var agg_wrap = function(object) {
  wrap = {};
  wrap['aggs'] = object;
  return wrap;
}

module.exports.main = main;
module.exports.last_filter_obj = last_filter_obj;
module.exports.groupby_obj = groupby_obj;
module.exports.where_obj = where_obj;
module.exports.agg_wrap = agg_wrap;
<commit_msg>fixed es parsing error for last filter object (needed a wrapper)<commit_after>
the_input = "SELECT sum(value), year, description \n FROM database \n WHERE commodity = 'jrice' \n GROUP BY year, description"

var main = function(input) {

    split = input.split(/SELECT|FROM|WHERE|GROUP BY/)

    i_select = split[1].replace(/\n| /g,'').split(',')
    i_from = split[2].replace(/\n| /g,'').split(',')
    i_where = split[3].replace(/\n/g,'').split('AND ')
    i_groupby = split[4].replace(/\n| /g,'').split(',')

    // database -> endpoint

    endpoint = '0.0.0.0:9200/' + i_from + '/_search?'

    // anything wrapped in the select statement becomes the end filter: sum, avg, min, max to start

    last_filter = []
    for (var i = 0; i < i_select.length; i++) {
        m = i_select[i].match(/sum\(|avg\(|min\(|max\(/g);
        if (m) {
            last_filter.push(m[0].replace(/\(/,''))
            last_filter.push(i_select[i].replace(m[0],'').replace(')',''))
        }
    }

    // anything in where becomes the filter (in order), currently only supports =, should add 'in' soon
    filters = []
    for (var i = 0; i < i_where.length; i++) {
        filters.push(i_where[i].replace(/\(|\)/g,'').split(/= |in|,/))
    }

    // make the object

    a = agg_wrap(where_obj(filters,groupby_obj(i_groupby,last_filter_obj())))

    console.log(endpoint)
    return JSON.stringify(a)
}

var last_filter_obj = function() {
    var last_filter_object = {}, wrapper = {};
    last_filter_object[last_filter[0]] = { "field" : last_filter[1] };
    wrapper[last_filter[0]+'_'+last_filter[1]] = last_filter_object;
    return wrapper;
}

var groupby_obj = function(array,object) {
    var dis_array = array.slice(0);
    if (dis_array.length === 1) {
        obj = {};
        gb_name = 'group_by_' + dis_array[0];
        obj[gb_name] = { "terms" : { "field" : dis_array[0]}, "aggs" : object };
        return obj;
      }
    else {
        var aggs = {}; // important to use var here to scope properly
        gb = dis_array.shift();
        gb_name = 'group_by_' + gb;
        aggs[gb_name] = { "terms" : { "field" : gb}, "aggs" : groupby_obj(dis_array,object) };
        return aggs;
      }
}

var where_obj = function(array,object) {
    var dis_array = array.slice(0);
    if (dis_array.length === 1) {
        obj = {};
        w = dis_array[0]
        tw = w[0].replace(/ /g,'')
        if (w.length === 2){
          var term = {};
          term[tw] = w[1].replace(/\\|\'| /g,'')
          w_name = 'where_' + tw + '_' + term[tw];
          obj[w_name] = { "filter" : { "term" : term}, "aggs" : object };
        }
        else {
          terms = []
          for (var t = 0; t < w.length - 1; t++){
            var term = {};
            term[tw] = w[t+1].replace(/\\|\'| /g,'');
            terms.push({ "term" : term })
          }
          w_name = 'where_' + tw + '_multiple';
          obj[w_name] = { "filter" : { "and" : terms}, "aggs" : object };
        }
        return obj;
      }
    else {
        var aggs = {}; // important to use var here to scope properly
        w = dis_array.shift();
        tw = w[0].replace(/ /g,'')
        if (w.length === 2){
          var term = {};
          term[tw] = w[1].replace(/\\|\'| /g,'');
          w_name = 'where_' + tw + '_' + term[tw];
          aggs[w_name] = { "filter" : { "term" : term}, "aggs" : where_obj(dis_array,object) };
        }
        else {
          terms = [];
          for (var t = 0; t < w.length - 1; t++){
            var term = {};
            term[tw] = w[t+1].replace(/\\|\'| /g,'');
            terms.push({ "term" : term })
          };
          w_name = 'where_' + tw + '_multiple';
          aggs[w_name] = { "filter" : { "and" : terms}, "aggs" : where_obj(dis_array,object) };
        }
        return aggs;
      }
}

var agg_wrap = function(object) {
  wrap = {};
  wrap['aggs'] = object;
  return wrap;
}

module.exports.main = main;
module.exports.last_filter_obj = last_filter_obj;
module.exports.groupby_obj = groupby_obj;
module.exports.where_obj = where_obj;
module.exports.agg_wrap = agg_wrap;
<|endoftext|>


<commit_before>/**
 * @fileOverview Line
 */
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import Animate from 'react-smooth';
import classNames from 'classnames';
import pureRender from '../util/PureRender';
import Curve from '../shape/Curve';
import Dot from '../shape/Dot';
import Layer from '../container/Layer';
import { PRESENTATION_ATTRIBUTES, getPresentationAttributes } from '../util/ReactUtils';

@pureRender
class Line extends Component {

  static displayName = 'Line';

  static propTypes = {
    ...PRESENTATION_ATTRIBUTES,
    className: PropTypes.string,
    type: PropTypes.oneOf(['linear', 'monotone', 'step', 'stepBefore', 'stepAfter']),
    unit: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    name: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    dataKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    yAxisId: PropTypes.number,
    xAxisId: PropTypes.number,
    legendType: PropTypes.string,
    layout: PropTypes.oneOf(['horizontal', 'vertical']),

     // whether have dot in line
    dot: PropTypes.oneOfType([PropTypes.object, PropTypes.element, PropTypes.bool]),
    label: PropTypes.oneOfType([PropTypes.object, PropTypes.element, PropTypes.bool]),

    points: PropTypes.arrayOf(PropTypes.shape({
      x: PropTypes.number,
      y: PropTypes.number,
      value: PropTypes.value,
    })),
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    onClick: PropTypes.func,
    isAnimationActive: PropTypes.bool,
    animationBegin: PropTypes.number,
    animationDuration: PropTypes.number,
    animationEasing: PropTypes.oneOf([
      'ease',
      'ease-in',
      'ease-out',
      'ease-in-out',
      'linear',
    ]),
  };

  static defaultProps = {
    xAxisId: 0,
    yAxisId: 0,
    dot: true,
    legendType: 'line',
    stroke: '#3182bd',
    strokeWidth: 1,
    fill: '#fff',
    points: [],
    onClick() {},
    onMouseEnter() {},
    onMouseLeave() {},
    isAnimationActive: true,
    animationBegin: 0,
    animationDuration: 1500,
    animationEasing: 'ease',
  };

  constructor(props, ctx) {
    super(props, ctx);

    this.state = {
      isAnimationFinished: false,
      steps: [],
      totalLength: 0,
    };
  }

  componentDidMount() {
    const { isAnimationActive } = this.props;

    if (!isAnimationActive) {
      return;
    }

    const curveDom = findDOMNode(this.refs.animate);
    const totalLength = (curveDom && curveDom.getTotalLength && curveDom.getTotalLength()) || 0;

    this.setState({ totalLength });
  }

  getStrokeDasharray(length, totalLength, lines) {
    const lineLength = lines.reduce((pre, next) => pre + next);

    const count = parseInt(length / lineLength, 10);
    const remainLength = length % lineLength;
    const restLength = totalLength - length;

    let remainLines = [];
    for (let i = 0, sum = 0; true; sum += lines[i], ++i) {
      if (sum + lines[i] > remainLength) {
        remainLines = [...lines.slice(0, i), remainLength - sum];
        break;
      }
    }

    const emptyLines = remainLines.length % 2 === 0 ? [0, restLength] : [restLength];

    return [...this.repeat(lines, count), ...remainLines, ...emptyLines]
      .map(line => `${line}px`)
      .join(', ');
  }

  repeat(lines, count) {
    const linesUnit = lines.length % 2 !== 0 ? [...lines, 0] : lines;
    let result = [];

    for (let i = 0; i < count; ++i) {
      result = [...result, ...linesUnit];
    }

    return result;
  }

  handleAnimationEnd = () => {
    this.setState({ isAnimationFinished: true });
  };

  renderLabels() {
    const { points, label } = this.props;
    const lineProps = getPresentationAttributes(this.props);
    const customLabelProps = getPresentationAttributes(label);
    const isLabelElement = React.isValidElement(label);

    const labels = points.map((entry, i) => {
      const x = entry.x + entry.width / 2;
      const y = entry.y;
      const labelProps = {
        textAnchor: 'middle',
        ...entry,
        ...lineProps,
        ...customLabelProps,
        index: i,
        key: `label-${i}`,
        payload: entry,
      };

      return isLabelElement ?
        React.cloneElement(label, labelProps) :
        (<text {...labelProps} className="recharts-line-label">{entry.value}</text>);
    });

    return <Layer className="recharts-line-labels">{labels}</Layer>;
  }

  renderDots() {
    const { isAnimationActive } = this.props;

    if (isAnimationActive && !this.state.isAnimationFinished) {
      return null;
    }

    const { dot, points } = this.props;
    const lineProps = getPresentationAttributes(this.props);
    const customDotProps = getPresentationAttributes(dot);
    const isDotElement = React.isValidElement(dot);

    const dots = points.map((entry, i) => {
      const dotProps = {
        key: `dot-${i}`,
        r: 3,
        ...lineProps,
        ...customDotProps,
        cx: entry.x,
        cy: entry.y,
        index: i,
        payload: entry,
      };

      return isDotElement ?
        React.cloneElement(dot, dotProps) :
        <Dot className="recharts-line-dot" {...dotProps} />;
    });

    return <Layer className="recharts-line-dots">{dots}</Layer>;
  }

  renderCurve() {
    const {
      dot,
      points,
      label,
      className,
      isAnimationActive,
      animationBegin,
      animationDuration,
      animationEasing,
      onClick,
      onMouseEnter,
      onMouseLeave,
      strokeDasharray,
      ...other,
    } = this.props;

    const { totalLength } = this.state;
    const animationProps = {
      isActive: isAnimationActive,
      begin: animationBegin,
      canBegin: totalLength > 0,
      easing: animationEasing,
      duration: animationDuration,
      onAnimationEnd: this.handleAnimationEnd,
      ref: 'animate',
    };
    const curveProps = {
      ...other,
      className: 'recharts-line-curve',
      fill: 'none',
      onClick,
      onMouseEnter,
      onMouseLeave,
      points,
    };

    if (strokeDasharray) {
      const lines = strokeDasharray.split(/[,\s]+/gim)
        .map(num => parseInt(num, 10));

      return (
        <Animate { ...animationProps }
          from={{ length: 0 }}
          to={{ length: totalLength }}
        >
          {
            ({ length }) => (
              <Curve { ...curveProps }
                strokeDasharray={this.getStrokeDasharray(length, totalLength, lines)}
              />
            )
          }
        </Animate>
      );
    }

    return (
      <Animate { ...animationProps }
        from={`0px ${(totalLength === 0 ? 1 : totalLength)}px`}
        to={`${totalLength}px 0px`}
        attributeName="strokeDasharray"
      >
        <Curve { ...curveProps } />
      </Animate>
    );
  }

  render() {
    const {
      dot,
      points,
      label,
      className,
      onClick,
      onMouseEnter,
      onMouseLeave,
    } = this.props;

    if (!points || !points.length) {
      return null;
    }

    const hasSinglePoint = points.length === 1;
    const layerClass = classNames('recharts-line', className);

    return (
      <Layer className={layerClass}>
        {!hasSinglePoint && this.renderCurve()}
        {(hasSinglePoint || dot) && this.renderDots()}
        {label && this.renderLabels()}
      </Layer>
    );
  }
}

export default Line;
<commit_msg>format(Line): format Line code<commit_after>/**
 * @fileOverview Line
 */
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import Animate from 'react-smooth';
import classNames from 'classnames';
import pureRender from '../util/PureRender';
import Curve from '../shape/Curve';
import Dot from '../shape/Dot';
import Layer from '../container/Layer';
import { PRESENTATION_ATTRIBUTES, getPresentationAttributes } from '../util/ReactUtils';

@pureRender
class Line extends Component {

  static displayName = 'Line';

  static propTypes = {
    ...PRESENTATION_ATTRIBUTES,
    className: PropTypes.string,
    type: PropTypes.oneOf(['linear', 'monotone', 'step', 'stepBefore', 'stepAfter']),
    unit: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    name: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    dataKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
    yAxisId: PropTypes.number,
    xAxisId: PropTypes.number,
    legendType: PropTypes.string,
    layout: PropTypes.oneOf(['horizontal', 'vertical']),

     // whether have dot in line
    dot: PropTypes.oneOfType([PropTypes.object, PropTypes.element, PropTypes.bool]),
    label: PropTypes.oneOfType([PropTypes.object, PropTypes.element, PropTypes.bool]),

    points: PropTypes.arrayOf(PropTypes.shape({
      x: PropTypes.number,
      y: PropTypes.number,
      value: PropTypes.value,
    })),
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    onClick: PropTypes.func,
    isAnimationActive: PropTypes.bool,
    animationBegin: PropTypes.number,
    animationDuration: PropTypes.number,
    animationEasing: PropTypes.oneOf([
      'ease',
      'ease-in',
      'ease-out',
      'ease-in-out',
      'linear',
    ]),
  };

  static defaultProps = {
    xAxisId: 0,
    yAxisId: 0,
    dot: true,
    legendType: 'line',
    stroke: '#3182bd',
    strokeWidth: 1,
    fill: '#fff',
    points: [],
    onClick() {},
    onMouseEnter() {},
    onMouseLeave() {},
    isAnimationActive: true,
    animationBegin: 0,
    animationDuration: 1500,
    animationEasing: 'ease',
  };

  constructor(props, ctx) {
    super(props, ctx);

    this.state = {
      isAnimationFinished: false,
      steps: [],
      totalLength: 0,
    };
  }

  componentDidMount() {
    const { isAnimationActive } = this.props;

    if (!isAnimationActive) {
      return;
    }

    const curveDom = findDOMNode(this.refs.animate);
    const totalLength = (curveDom && curveDom.getTotalLength && curveDom.getTotalLength()) || 0;

    this.setState({ totalLength });
  }

  getStrokeDasharray(length, totalLength, lines) {
    const lineLength = lines.reduce((pre, next) => pre + next);

    const count = parseInt(length / lineLength, 10);
    const remainLength = length % lineLength;
    const restLength = totalLength - length;

    let remainLines = [];
    for (let i = 0, sum = 0; true; sum += lines[i], ++i) {
      if (sum + lines[i] > remainLength) {
        remainLines = [...lines.slice(0, i), remainLength - sum];
        break;
      }
    }

    const emptyLines = remainLines.length % 2 === 0 ? [0, restLength] : [restLength];

    return [...this.repeat(lines, count), ...remainLines, ...emptyLines]
      .map(line => `${line}px`)
      .join(', ');
  }

  repeat(lines, count) {
    const linesUnit = lines.length % 2 !== 0 ? [...lines, 0] : lines;
    let result = [];

    for (let i = 0; i < count; ++i) {
      result = [...result, ...linesUnit];
    }

    return result;
  }

  handleAnimationEnd = () => {
    this.setState({ isAnimationFinished: true });
  };

  renderLabels() {
    const { points, label } = this.props;
    const lineProps = getPresentationAttributes(this.props);
    const customLabelProps = getPresentationAttributes(label);
    const isLabelElement = React.isValidElement(label);

    const labels = points.map((entry, i) => {
      const x = entry.x + entry.width / 2;
      const y = entry.y;
      const labelProps = {
        textAnchor: 'middle',
        ...entry,
        ...lineProps,
        ...customLabelProps,
        index: i,
        key: `label-${i}`,
        payload: entry,
      };

      return isLabelElement ?
        React.cloneElement(label, labelProps) :
        (<text {...labelProps} className="recharts-line-label">{entry.value}</text>);
    });

    return <Layer className="recharts-line-labels">{labels}</Layer>;
  }

  renderDots() {
    const { isAnimationActive } = this.props;

    if (isAnimationActive && !this.state.isAnimationFinished) {
      return null;
    }

    const { dot, points } = this.props;
    const lineProps = getPresentationAttributes(this.props);
    const customDotProps = getPresentationAttributes(dot);
    const isDotElement = React.isValidElement(dot);

    const dots = points.map((entry, i) => {
      const dotProps = {
        key: `dot-${i}`,
        r: 3,
        ...lineProps,
        ...customDotProps,
        cx: entry.x,
        cy: entry.y,
        index: i,
        payload: entry,
      };

      return isDotElement ?
        React.cloneElement(dot, dotProps) :
        <Dot className="recharts-line-dot" {...dotProps} />;
    });

    return <Layer className="recharts-line-dots">{dots}</Layer>;
  }

  renderCurve() {
    const {
      dot, points, label, className,
      isAnimationActive, animationBegin, animationDuration, animationEasing,
      onClick, onMouseEnter, onMouseLeave,
      strokeDasharray,
      ...other,
    } = this.props;

    const { totalLength } = this.state;
    const animationProps = {
      isActive: isAnimationActive,
      begin: animationBegin,
      canBegin: totalLength > 0,
      easing: animationEasing,
      duration: animationDuration,
      onAnimationEnd: this.handleAnimationEnd,
      ref: 'animate',
    };
    const curveProps = {
      ...other,
      className: 'recharts-line-curve',
      fill: 'none',
      onClick,
      onMouseEnter,
      onMouseLeave,
      points,
    };

    if (strokeDasharray) {
      const lines = strokeDasharray.split(/[,\s]+/gim)
        .map(num => parseInt(num, 10));

      return (
        <Animate { ...animationProps }
          from={{ length: 0 }}
          to={{ length: totalLength }}
        >
          {
            ({ length }) => (
              <Curve { ...curveProps }
                strokeDasharray={this.getStrokeDasharray(length, totalLength, lines)}
              />
            )
          }
        </Animate>
      );
    }

    return (
      <Animate { ...animationProps }
        from={`0px ${(totalLength === 0 ? 1 : totalLength)}px`}
        to={`${totalLength}px 0px`}
        attributeName="strokeDasharray"
      >
        <Curve { ...curveProps } />
      </Animate>
    );
  }

  render() {
    const {
      dot, points, label, className,
      onClick, onMouseEnter, onMouseLeave,
    } = this.props;

    if (!points || !points.length) {
      return null;
    }

    const hasSinglePoint = points.length === 1;
    const layerClass = classNames('recharts-line', className);

    return (
      <Layer className={layerClass}>
        {!hasSinglePoint && this.renderCurve()}
        {(hasSinglePoint || dot) && this.renderDots()}
        {label && this.renderLabels()}
      </Layer>
    );
  }
}

export default Line;
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import NotificationBell from './src/js/NotificationBell';
import NotificationApi from './src/js/NotificationApi';
import './main.scss';
import NotificationContainer from './src/js/NotificationContainer';
import Drawer from '@pearson-components/drawer/main';
import CoachmarkListener from './src/js/CoachmarkListener';
import NotificationRealTimeApi from './src/js/NotificationRealTimeApi';

/**
 *  NotificationComponent.
 *    NotificationComponent will get larger as it is in charge of making API calls for all of the notification-apis.
 *    To get an instance of it just create require the module inside your javascript file and call 'getInstance' with configuration
 *    as a parameter.  This component needs to make several api calls from places that could change, so we need to pass configuration
 *    into it.
 */
class NotificationComponent {

	constructor(config, element) {
		this.notApi = new NotificationApi(config);
		const userNotifications = this.notApi.getNotifications();

		// Connect up the drawer component here.
		const dom = document.createElement('div');
		dom.setAttribute('id', 'notification-component');
		dom.setAttribute('data-o-component', 'o-drawer');
		dom.classList.add('o-drawer-right', 'o-drawer-animated');
		this.listDrawer = new Drawer(dom);
		document.body.appendChild(dom);

		this.notificationList = [];
		this.archivedNotificationList = [];
		userNotifications.then((result) => {
			// create the react classes for reference later
			this._createBellReactClass();
			this._createListReactClass(config);

			this.notificationList = result.list;
			this.archivedNotificationList = result.archivedList;
			this.newNotifications = result.newNotifications;
			this.unreadCount = result.unreadCount;

			this.notificationList.sort((x, y) => {
				return y.createdAt - x.createdAt;
			});
			this.archivedNotificationList.sort((x, y) => {
				return y.createdAt - x.createdAt;
			});

			// Keep reference to the components to set state later and render the react components now that we have the data
			this.containerComponent = ReactDOM.render(<this.containerClass/>, dom);
			this.bellComponent = ReactDOM.render(<this.bellClass/>, element);

			(new CoachmarkListener(config)).launchCoachmarkIfFromNewUrl();

		}).catch(function(error) {
			console.log(error);
		});

		this.realTimeNotification = new NotificationRealTimeApi(config, this._messageListener.bind(this));

	}

	_messageListener(message) {
		this.notificationList.push({
			id: message.payload.userNotificationId,
			isRead: false,
			isComplete: false,
			status: 'CREATED',
			message: JSON.parse(message.payload.data),
			createdAt: new Date(message.payload.createdAt),
			updatedAt: new Date(message.payload.createdAt), //just for sorting purposes this needs to be here
			notificationType: 'inbrowser',
			recipientId: message.payload.recipientId
		});

		this.notificationList.sort((x, y) => {
			return y.createdAt - x.createdAt;
		});
		this.unreadCount++;
		this.newNotifications = true;

		this.bellComponent.forceUpdate();
		this.containerComponent.forceUpdate();

	}

	_createBellReactClass() {
		//  Keep track of the parent react class
		const _this = this;//i'm not happy i need to do this....but it would be really complicated since i don't want to actually pass context down to the child except for the notificationList property.

		this.bellClass = React.createClass({
			render: function() {
				return (

						<NotificationBell newNotifications={_this.newNotifications} unreadCount={_this.unreadCount} toggleList={_this.toggleList.bind(_this)}/>

				);
			}
		});
	}

	_createListReactClass(config) {

		const _this = this;
		this.containerClass = React.createClass({
			render: function() {
				return (

						<NotificationContainer list={_this.notificationList} notificationRead={_this.notificationRead.bind(_this)}
						archivedList={_this.archivedNotificationList} closeDrawer={_this.closeDrawer.bind(_this)} archiveNotification={_this.archiveNotification.bind(_this)}/>

				);
			}
		});
	}

	notificationRead(notification) {
		this.notApi.markAsRead(notification.id);
		for(let i = 0; i < this.notificationList.length; i++) {
			if(this.notificationList[i].id === notification.id) {
				this.notificationList[i].isRead = true;
				break;
			}
		}
		this.unreadCount--;
		this.bellComponent.forceUpdate();
	}

	archiveNotification(archivedNotification) {
		this.notificationList = this.notificationList.filter(function(notification) {
			if (notification.id !== archivedNotification.id) {
				return notification;
			}
		});
		this.notApi.markAsArchivedAndRead(archivedNotification.id);
		if(!archivedNotification.isRead) {
			archivedNotification.isRead = true;
			this.unreadCount--;
			this.bellComponent.forceUpdate();
		}
		archivedNotification.status = 'ARCHIVED';
		this.archivedNotificationList.push(archivedNotification);
		this.archivedNotificationList.sort((x, y) => {
			return y.createdAt - x.createdAt;
		});
		this.containerComponent.forceUpdate();
	}

	toggleList() {
        const drawerDiv = document.getElementById('notification-component');
        while (drawerDiv.firstChild) {
            drawerDiv.removeChild(drawerDiv.firstChild);
            this.containerComponent = ReactDOM.render(<this.containerClass/>, drawerDiv);
        }

		this.listDrawer.toggle();
		if (this.newNotifications) {
			// need to call the route that will change the status of all the notifications.
			const viewedList = this.notificationList.filter(function(notification) {
				if (notification.status === 'CREATED') {
					return notification;
				}
			});

			viewedList.forEach((notification) => {
				this.notApi.markAsViewed(notification.id).then(function(result) {
					// we don't care to do anything here...
				}, function(err) {
					// we really don't care about this...
				});
			});

			this.newNotifications = false;
			this.bellComponent.forceUpdate();
		}
	}

	closeDrawer() {
		this.listDrawer.close();
	}
}

export default NotificationComponent;

document.body.addEventListener('o.InitNotificationComponent', e => new NotificationComponent(e.detail.config, e.detail.element));
<commit_msg>fix for tests failing<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import NotificationBell from './src/js/NotificationBell';
import NotificationApi from './src/js/NotificationApi';
import './main.scss';
import NotificationContainer from './src/js/NotificationContainer';
import Drawer from '@pearson-components/drawer/main';
import CoachmarkListener from './src/js/CoachmarkListener';
import NotificationRealTimeApi from './src/js/NotificationRealTimeApi';

/**
 *  NotificationComponent.
 *    NotificationComponent will get larger as it is in charge of making API calls for all of the notification-apis.
 *    To get an instance of it just create require the module inside your javascript file and call 'getInstance' with configuration
 *    as a parameter.  This component needs to make several api calls from places that could change, so we need to pass configuration
 *    into it.
 */
class NotificationComponent {

	constructor(config, element) {
		this.notApi = new NotificationApi(config);
		const userNotifications = this.notApi.getNotifications();

		// Connect up the drawer component here.
		const dom = document.createElement('div');
		dom.setAttribute('id', 'notification-component');
		dom.setAttribute('data-o-component', 'o-drawer');
		dom.classList.add('o-drawer-right', 'o-drawer-animated');
		this.listDrawer = new Drawer(dom);
		document.body.appendChild(dom);

		this.notificationList = [];
		this.archivedNotificationList = [];
		userNotifications.then((result) => {
			// create the react classes for reference later
			this._createBellReactClass();
			this._createListReactClass(config);

			this.notificationList = result.list;
			this.archivedNotificationList = result.archivedList;
			this.newNotifications = result.newNotifications;
			this.unreadCount = result.unreadCount;

			this.notificationList.sort((x, y) => {
				return y.createdAt - x.createdAt;
			});
			this.archivedNotificationList.sort((x, y) => {
				return y.createdAt - x.createdAt;
			});

			// Keep reference to the components to set state later and render the react components now that we have the data
			this.containerComponent = ReactDOM.render(<this.containerClass/>, dom);
			this.bellComponent = ReactDOM.render(<this.bellClass/>, element);

			(new CoachmarkListener(config)).launchCoachmarkIfFromNewUrl();

		}).catch(function(error) {
			console.log(error);
		});

		this.realTimeNotification = new NotificationRealTimeApi(config, this._messageListener.bind(this));

	}

	_messageListener(message) {
		this.notificationList.push({
			id: message.payload.userNotificationId,
			isRead: false,
			isComplete: false,
			status: 'CREATED',
			message: JSON.parse(message.payload.data),
			createdAt: new Date(message.payload.createdAt),
			updatedAt: new Date(message.payload.createdAt), //just for sorting purposes this needs to be here
			notificationType: 'inbrowser',
			recipientId: message.payload.recipientId
		});

		this.notificationList.sort((x, y) => {
			return y.createdAt - x.createdAt;
		});
		this.unreadCount++;
		this.newNotifications = true;

		this.bellComponent.forceUpdate();
		this.containerComponent.forceUpdate();

	}

	_createBellReactClass() {
		//  Keep track of the parent react class
		const _this = this;//i'm not happy i need to do this....but it would be really complicated since i don't want to actually pass context down to the child except for the notificationList property.

		this.bellClass = React.createClass({
			render: function() {
				return (

						<NotificationBell newNotifications={_this.newNotifications} unreadCount={_this.unreadCount} toggleList={_this.toggleList.bind(_this)}/>

				);
			}
		});
	}

	_createListReactClass(config) {

		const _this = this;
		this.containerClass = React.createClass({
			render: function() {
				return (

						<NotificationContainer list={_this.notificationList} notificationRead={_this.notificationRead.bind(_this)}
						archivedList={_this.archivedNotificationList} closeDrawer={_this.closeDrawer.bind(_this)} archiveNotification={_this.archiveNotification.bind(_this)}/>

				);
			}
		});
	}

	notificationRead(notification) {
		this.notApi.markAsRead(notification.id);
		for(let i = 0; i < this.notificationList.length; i++) {
			if(this.notificationList[i].id === notification.id) {
				this.notificationList[i].isRead = true;
				break;
			}
		}
		this.unreadCount--;
		this.bellComponent.forceUpdate();
	}

	archiveNotification(archivedNotification) {
		this.notificationList = this.notificationList.filter(function(notification) {
			if (notification.id !== archivedNotification.id) {
				return notification;
			}
		});
		this.notApi.markAsArchivedAndRead(archivedNotification.id);
		if(!archivedNotification.isRead) {
			archivedNotification.isRead = true;
			this.unreadCount--;
			this.bellComponent.forceUpdate();
		}
		archivedNotification.status = 'ARCHIVED';
		this.archivedNotificationList.push(archivedNotification);
		this.archivedNotificationList.sort((x, y) => {
			return y.createdAt - x.createdAt;
		});
		this.containerComponent.forceUpdate();
	}

	toggleList() {
        const drawerDiv = document.getElementById('notification-component');
        while (drawerDiv.firstChild) {
            drawerDiv.removeChild(drawerDiv.firstChild);
        }
        this.containerComponent = this.containerClass && drawerDiv ? ReactDOM.render( <this.containerClass/>, drawerDiv): '';

		this.listDrawer.toggle();
		if (this.newNotifications) {
			// need to call the route that will change the status of all the notifications.
			const viewedList = this.notificationList.filter(function(notification) {
				if (notification.status === 'CREATED') {
					return notification;
				}
			});

			viewedList.forEach((notification) => {
				this.notApi.markAsViewed(notification.id).then(function(result) {
					// we don't care to do anything here...
				}, function(err) {
					// we really don't care about this...
				});
			});

			this.newNotifications = false;
			this.bellComponent.forceUpdate();
		}
	}

	closeDrawer() {
		this.listDrawer.close();
	}
}

export default NotificationComponent;

document.body.addEventListener('o.InitNotificationComponent', e => new NotificationComponent(e.detail.config, e.detail.element));
<|endoftext|>