<commit_before>'use strict';

const {
    get, pDelay, pRaceWithTimeout, isError
} = require('@terascope/utils');
const ms = require('ms');
const { makeLogger } = require('./terafoundation');

function waitForWorkerShutdown(context, eventName) {
    const shutdownTimeout = get(context, 'sysconfig.teraslice.shutdown_timeout', 30000);
    const events = context.apis.foundation.getSystemEvents();

    return new Promise((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            events.removeListener(eventName, handler);
            reject(new Error('Timeout waiting for worker to shutdown'));
        }, shutdownTimeout);

        function handler(err) {
            clearTimeout(timeoutId);
            if (isError(err)) {
                reject(err);
            } else {
                resolve();
            }
        }
        events.once(eventName, handler);
    });
}

/* istanbul ignore next */
function shutdownHandler(context, shutdownFn) {
    const assignment = context.assignment
        || process.env.NODE_TYPE
        || process.env.assignment
        || 'unknown-assignment';

    const isK8s = get(context, 'sysconfig.teraslice.cluster_manager_type') === 'kubernetes';
    const isProcessRestart = process.env.process_restart;
    const allowNonZeroExitCode = !isK8s || assignment !== 'execution_controller';
    const api = {
        exiting: false,
        exit
    };

    const shutdownTimeout = get(context, 'sysconfig.teraslice.shutdown_timeout', 20 * 1000);

    const events = context.apis.foundation.getSystemEvents();
    const logger = makeLogger(context, `${assignment}:shutdown_handler`);

    if (assignment === 'execution_controller' && isProcessRestart) {
        logger.fatal(
            'Execution Controller runtime error led to a restart, terminating execution with failed status, please use the recover api to return slicer to a consistent state'
        );
        process.exit(0);
    }

    async function flushLogs() {
        try {
            await logger.flush();
            await pDelay(1000);

            const code = process.exitCode || 0;
            logger.debug(`flushed logs successfully, will exit with code ${code}`);
        } catch (err) {
            logger.error(err, 'flush error on shutdown');
        }
    }

    let startTime;

    function exitingIn() {
        if (!api.exiting) {
            return `exiting in ${ms(shutdownTimeout)}...`;
        }

        const elapsed = Date.now() - startTime;
        return `already shutting down, remaining ${ms(shutdownTimeout - elapsed)}`;
    }

    async function callShutdownFn(event, err) {
        await shutdownFn(event, err);
    }

    async function shutdownWithTimeout(event, err) {
        const logError = (_err) => {
            logger.error(_err, 'shutdown error after timeout');
        };
        const timeout = shutdownTimeout - 2000;
        await pRaceWithTimeout(callShutdownFn(event, err), timeout, logError);
    }

    async function exit(event, err) {
        if (api.exiting) return;

        api.exiting = true;
        startTime = Date.now();

        try {
            await shutdownWithTimeout(event, err);
            logger.info(`${assignment} shutdown took ${ms(Date.now() - startTime)}`);
        } catch (error) {
            logger.error(error, `${assignment} while shutting down`);
        } finally {
            await flushLogs();
            if (allowNonZeroExitCode) {
                process.exit();
            } else {
                process.exit(0);
            }
        }
    }

    function setStatusCode(code) {
        if (api.exiting) return;
        if (process.exitCode == null) {
            process.exitCode = code;
        }
    }

    process.on('SIGINT', () => {
        logger.info(`${assignment} received process:SIGINT, ${exitingIn()}`);
        setStatusCode(0);
        exit('SIGINT');
    });

    process.on('SIGTERM', () => {
        logger.info(`${assignment} received process:SIGTERM, ${exitingIn()}`);
        setStatusCode(0);
        exit('SIGTERM');
    });

    process.on('uncaughtException', (err) => {
        logger.error(err, `${assignment} received an uncaughtException, ${exitingIn()}`);
        setStatusCode(1);
        exit('uncaughtException', err);
    });

    process.once('unhandledRejection', (err) => {
        logger.error(err, `${assignment} received an unhandledRejection, ${exitingIn()}`);
        setStatusCode(1);
        exit('unhandledRejection', err);
    });

    // See https://github.com/trentm/node-bunyan/issues/246
    function handleStdError(err) {
        if (err.code === 'EPIPE' || err.code === 'ERR_STREAM_DESTROYED') {
            // ignore
        } else {
            throw err;
        }
    }

    process.stdout.on('error', handleStdError);
    process.stderr.on('error', handleStdError);

    // event is fired from terafoundation when an error occurs during instantiation of a client
    events.once('client:initialization:error', (err) => {
        logger.error(err, `${assignment} received a client initialization error, ${exitingIn()}`);
        setStatusCode(1);
        exit('client:initialization:error', err);
    });

    events.once('worker:shutdown:complete', (err) => {
        setStatusCode(0);
        if (err) {
            logger.error(err, `${assignment} shutdown error, ${exitingIn()}`);
        } else {
            logger.info(`${assignment} shutdown, ${exitingIn()}`);
        }
        exit('worker:shutdown:complete', err);
    });

    return api;
}

module.exports = {
    shutdownHandler,
    waitForWorkerShutdown
};
<commit_msg>update comments in worker-shutdown to be more clear<commit_after>'use strict';

const {
    get, pDelay, pRaceWithTimeout, isError
} = require('@terascope/utils');
const ms = require('ms');
const { makeLogger } = require('./terafoundation');

function waitForWorkerShutdown(context, eventName) {
    const shutdownTimeout = get(context, 'sysconfig.teraslice.shutdown_timeout', 30000);
    const events = context.apis.foundation.getSystemEvents();

    return new Promise((resolve, reject) => {
        const timeoutId = setTimeout(() => {
            events.removeListener(eventName, handler);
            reject(new Error('Timeout waiting for worker to shutdown'));
        }, shutdownTimeout);

        function handler(err) {
            clearTimeout(timeoutId);
            if (isError(err)) {
                reject(err);
            } else {
                resolve();
            }
        }
        events.once(eventName, handler);
    });
}

/* istanbul ignore next */
function shutdownHandler(context, shutdownFn) {
    const assignment = context.assignment
        || process.env.NODE_TYPE
        || process.env.assignment
        || 'unknown-assignment';

    const isK8s = get(context, 'sysconfig.teraslice.cluster_manager_type') === 'kubernetes';
    // this is native clustering only
    const isProcessRestart = process.env.process_restart;
    // everything but the k8s execution_controller should not be allowed be allowed to
    // set a non-zero exit code (to avoid being restarted)
    const allowNonZeroExitCode = !(isK8s && assignment === 'execution_controller');
    const api = {
        exiting: false,
        exit
    };

    const shutdownTimeout = get(context, 'sysconfig.teraslice.shutdown_timeout', 20 * 1000);

    const events = context.apis.foundation.getSystemEvents();
    const logger = makeLogger(context, `${assignment}:shutdown_handler`);

    if (assignment === 'execution_controller' && isProcessRestart) {
        logger.fatal(
            'Execution Controller runtime error led to a restart, terminating execution with failed status, please use the recover api to return slicer to a consistent state'
        );
        process.exit(0);
    }

    async function flushLogs() {
        try {
            await logger.flush();
            await pDelay(1000);

            const code = process.exitCode || 0;
            logger.debug(`flushed logs successfully, will exit with code ${code}`);
        } catch (err) {
            logger.error(err, 'flush error on shutdown');
        }
    }

    let startTime;

    function exitingIn() {
        if (!api.exiting) {
            return `exiting in ${ms(shutdownTimeout)}...`;
        }

        const elapsed = Date.now() - startTime;
        return `already shutting down, remaining ${ms(shutdownTimeout - elapsed)}`;
    }

    async function callShutdownFn(event, err) {
        await shutdownFn(event, err);
    }

    async function shutdownWithTimeout(event, err) {
        const logError = (_err) => {
            logger.error(_err, 'shutdown error after timeout');
        };
        const timeout = shutdownTimeout - 2000;
        await pRaceWithTimeout(callShutdownFn(event, err), timeout, logError);
    }

    async function exit(event, err) {
        if (api.exiting) return;

        api.exiting = true;
        startTime = Date.now();

        try {
            await shutdownWithTimeout(event, err);
            logger.info(`${assignment} shutdown took ${ms(Date.now() - startTime)}`);
        } catch (error) {
            logger.error(error, `${assignment} while shutting down`);
        } finally {
            await flushLogs();
            if (allowNonZeroExitCode) {
                process.exit();
            } else {
                process.exit(0);
            }
        }
    }

    function setStatusCode(code) {
        if (api.exiting) return;
        if (process.exitCode == null) {
            process.exitCode = code;
        }
    }

    process.on('SIGINT', () => {
        logger.info(`${assignment} received process:SIGINT, ${exitingIn()}`);
        setStatusCode(0);
        exit('SIGINT');
    });

    process.on('SIGTERM', () => {
        logger.info(`${assignment} received process:SIGTERM, ${exitingIn()}`);
        setStatusCode(0);
        exit('SIGTERM');
    });

    process.on('uncaughtException', (err) => {
        logger.error(err, `${assignment} received an uncaughtException, ${exitingIn()}`);
        setStatusCode(1);
        exit('uncaughtException', err);
    });

    process.once('unhandledRejection', (err) => {
        logger.error(err, `${assignment} received an unhandledRejection, ${exitingIn()}`);
        setStatusCode(1);
        exit('unhandledRejection', err);
    });

    // See https://github.com/trentm/node-bunyan/issues/246
    function handleStdError(err) {
        if (err.code === 'EPIPE' || err.code === 'ERR_STREAM_DESTROYED') {
            // ignore
        } else {
            throw err;
        }
    }

    process.stdout.on('error', handleStdError);
    process.stderr.on('error', handleStdError);

    // event is fired from terafoundation when an error occurs during instantiation of a client
    // **DEPRECATED:** This handler should be removed on teraslice v1
    events.once('client:initialization:error', (err) => {
        logger.error(err, `${assignment} received a client initialization error, ${exitingIn()}`);
        setStatusCode(1);
        exit('client:initialization:error', err);
    });

    events.once('worker:shutdown:complete', (err) => {
        setStatusCode(0);
        if (err) {
            logger.error(err, `${assignment} shutdown error, ${exitingIn()}`);
        } else {
            logger.info(`${assignment} shutdown, ${exitingIn()}`);
        }
        exit('worker:shutdown:complete', err);
    });

    return api;
}

module.exports = {
    shutdownHandler,
    waitForWorkerShutdown
};
<|endoftext|>


<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.object.keyvalue.groupspanel");
pimcore.object.keyvalue.groupspanel = Class.create({

    initialize: function () {

    },

    getPanel: function () {
        if (this.layout == null) {
            this.layout = new Ext.Panel({
                title: t("keyValue_groups"),
                border: false,
                layout: "fit",
                region: "center"
            });

            this.createGrid();
        }

        return this.layout;
    },

    createGrid: function(response) {
        this.fields = ['id', 'name', 'description', 'creationDate', 'modificationDate'];

        var readerFields = [];
        for (var i = 0; i < this.fields.length; i++) {
            readerFields.push({name: this.fields[i], allowBlank: true});
        }

        var itemsPerPage = 20;
        var url =  "/admin/key-value/groups?";

        this.store = pimcore.helpers.grid.buildDefaultStore(
            url,
            readerFields,
            itemsPerPage
        );
        this.pagingtoolbar = pimcore.helpers.grid.buildDefaultPagingToolbar(this.store, itemsPerPage);

        this.store.addListener("exception", function (conn, mode, action, request, response, store) {
            if(action == "update") {
                Ext.MessageBox.alert(t('error'), t('cannot_save_object_please_try_to_edit_the_object_in_detail_view'));
                this.store.rejectChanges();
            }
        }.bind(this));

        var gridColumns = [];

        gridColumns.push({header: "ID", width: 40, sortable: true, dataIndex: 'id'});
        gridColumns.push({header: t("name"), width: 200, sortable: true, dataIndex: 'name',editor: new Ext.form.TextField({}), filter: 'string'});
        gridColumns.push({header: t("description"), width: 300, sortable: true, dataIndex: 'description', editor: new Ext.form.TextField({}), filter: 'string'});

        gridColumns.push(
            {header: t("creationDate"), sortable: true, dataIndex: 'creationDate', editable: false, width: 130,
                hidden: true,
                renderer: function(d) {
                    if (d !== undefined) {
                        var date = new Date(d * 1000);
                        return date.format("Y-m-d H:i:s");
                    } else {
                        return "";
                    }
                }
            }
        );

        gridColumns.push(
            {header: t("modificationDate"), sortable: true, dataIndex: 'modificationDate', editable: false, width: 130,
                hidden: true,
                renderer: function(d) {
                    if (d !== undefined) {
                        var date = new Date(d * 1000);
                        return date.format("Y-m-d H:i:s");
                    } else {
                        return "";
                    }
                }
            }
        );

        gridColumns.push({
            hideable: false,
            xtype: 'actioncolumn',
            width: 40,
            items: [
                {
                    tooltip: t('remove'),
                    icon: "/pimcore/static/img/icon/cross.png",
                    handler: function (grid, rowIndex) {
                          var data = grid.getStore().getAt(rowIndex);
                          var id = data.data.id;

                          Ext.Ajax.request({
                                url: "/admin/key-value/deletegroup",
                                params: {
                                    id: id
                                },
                                success: function (response) {
                                    this.store.reload();
                                }.bind(this)});
                    }.bind(this)
                }
            ]
        });

        this.cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
            clicksToEdit: 1
        });

        var plugins = ['gridfilters', this.cellEditing];

        var gridConfig = {
            frame: false,
            store: this.store,
            columns: gridColumns,
            loadMask: true,
            columnLines: true,
            plugins: plugins,
            stripeRows: true,
            trackMouseOver: true,
            viewConfig: {
                forceFit: false
            },
            selModel: Ext.create('Ext.selection.RowModel', {}),
            bbar: this.pagingtoolbar,
            tbar: [

                {
                    text: t('add'),
                    handler: this.onAdd.bind(this),
                    iconCls: "pimcore_icon_add"
                }
            ]
        } ;

        this.grid = Ext.create('Ext.grid.Panel', gridConfig);

        this.grid.on("rowcontextmenu", this.onRowContextmenu.bind(this));

        this.grid.on("afterrender", function (grid) {
            this.updateGridHeaderContextMenu(grid);
        }.bind(this));

        this.grid.on("sortchange", function(grid, sortinfo) {
            this.sortinfo = sortinfo;
        }.bind(this));

        this.store.load();

        this.layout.removeAll();
        this.layout.add(this.grid);
        this.layout.updateLayout();
    },

    updateGridHeaderContextMenu: function(grid) {
        // not needed for now.
    },

    onAdd: function () {
        Ext.MessageBox.prompt(t('keyvalue_mbx_entergroup_title'), t('keyvalue_mbx_entergroup_prompt'),
                                                this.addFieldComplete.bind(this), null, null, "");
    },

    addFieldComplete: function (button, value, object) {

        value = value.trim();
        if (button == "ok" && value.length > 1) {
            Ext.Ajax.request({
                url: "/admin/key-value/addgroup",
                params: {
                    name: value
                },
                success: function (response) {
                    var data = Ext.decode(response.responseText);

                    if(!data || !data.success) {
                        Ext.Msg.alert(t("keyvalue_error_addgroup_title"), t("keyvalue_error_addgroup_msg"));
                    } else {
                        this.store.reload({
                                callback: function() {
                                var rowIndex = this.store.find('name', value);
                                // alert(rowIndex);
                                if (rowIndex != -1) {
                                    var sm = this.grid.getSelectionModel();
                                    sm.select(rowIndex);
                                }

                                var lastOptions = this.store.lastOptions;
                                Ext.apply(lastOptions.params, {
                                    overrideSort: "false"
                                });
                            }.bind(this),
                             params: {
                                "overrideSort": "true"
                            }
                            }
                        );
                    }
                }.bind(this)
            });
        }
        else if (button == "cancel") {
            return;
        }
        else {
            Ext.Msg.alert(t("keyvalue_configuration"), t("keyvalue_invalidname"));
        }
    },

    onRowContextmenu: function (grid, rowIndex, event) {
        // no context menu
    }

});

<commit_msg>extjs6 - cleanup<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.object.keyvalue.groupspanel");
pimcore.object.keyvalue.groupspanel = Class.create({

    initialize: function () {

    },

    getPanel: function () {
        if (this.layout == null) {
            this.layout = new Ext.Panel({
                title: t("keyValue_groups"),
                border: false,
                layout: "fit",
                region: "center"
            });

            this.createGrid();
        }

        return this.layout;
    },

    createGrid: function(response) {
        this.fields = ['id', 'name', 'description', 'creationDate', 'modificationDate'];

        var readerFields = [];
        for (var i = 0; i < this.fields.length; i++) {
            readerFields.push({name: this.fields[i], allowBlank: true});
        }

        var itemsPerPage = 20;
        var url =  "/admin/key-value/groups?";

        this.store = pimcore.helpers.grid.buildDefaultStore(
            url,
            readerFields,
            itemsPerPage
        );
        this.pagingtoolbar = pimcore.helpers.grid.buildDefaultPagingToolbar(this.store, itemsPerPage);

        this.store.addListener("exception", function (conn, mode, action, request, response, store) {
            if(action == "update") {
                Ext.MessageBox.alert(t('error'), t('cannot_save_object_please_try_to_edit_the_object_in_detail_view'));
                this.store.rejectChanges();
            }
        }.bind(this));

        var gridColumns = [];

        gridColumns.push({header: "ID", width: 40, sortable: true, dataIndex: 'id'});
        gridColumns.push({header: t("name"), width: 200, sortable: true, dataIndex: 'name',editor: new Ext.form.TextField({}), filter: 'string'});
        gridColumns.push({header: t("description"), width: 300, sortable: true, dataIndex: 'description', editor: new Ext.form.TextField({}), filter: 'string'});

        gridColumns.push(
            {header: t("creationDate"), sortable: true, dataIndex: 'creationDate', editable: false, width: 130,
                hidden: true,
                renderer: function(d) {
                    if (d !== undefined) {
                        var date = new Date(d * 1000);
                        return date.format("Y-m-d H:i:s");
                    } else {
                        return "";
                    }
                }
            }
        );

        gridColumns.push(
            {header: t("modificationDate"), sortable: true, dataIndex: 'modificationDate', editable: false, width: 130,
                hidden: true,
                renderer: function(d) {
                    if (d !== undefined) {
                        var date = new Date(d * 1000);
                        return date.format("Y-m-d H:i:s");
                    } else {
                        return "";
                    }
                }
            }
        );

        gridColumns.push({
            hideable: false,
            xtype: 'actioncolumn',
            width: 40,
            items: [
                {
                    tooltip: t('remove'),
                    icon: "/pimcore/static/img/icon/cross.png",
                    handler: function (grid, rowIndex) {
                          var data = grid.getStore().getAt(rowIndex);
                          var id = data.data.id;

                          Ext.Ajax.request({
                                url: "/admin/key-value/deletegroup",
                                params: {
                                    id: id
                                },
                                success: function (response) {
                                    this.store.reload();
                                }.bind(this)});
                    }.bind(this)
                }
            ]
        });

        this.cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
            clicksToEdit: 1
        });

        var plugins = ['gridfilters', this.cellEditing];

        var gridConfig = {
            frame: false,
            store: this.store,
            columns: gridColumns,
            loadMask: true,
            columnLines: true,
            plugins: plugins,
            stripeRows: true,
            trackMouseOver: true,
            viewConfig: {
                forceFit: false
            },
            selModel: Ext.create('Ext.selection.RowModel', {}),
            bbar: this.pagingtoolbar,
            tbar: [

                {
                    text: t('add'),
                    handler: this.onAdd.bind(this),
                    iconCls: "pimcore_icon_add"
                }
            ]
        } ;

        this.grid = Ext.create('Ext.grid.Panel', gridConfig);

        this.grid.on("sortchange", function(grid, sortinfo) {
            this.sortinfo = sortinfo;
        }.bind(this));

        this.store.load();

        this.layout.removeAll();
        this.layout.add(this.grid);
        this.layout.updateLayout();
    },

    onAdd: function () {
        Ext.MessageBox.prompt(t('keyvalue_mbx_entergroup_title'), t('keyvalue_mbx_entergroup_prompt'),
                                                this.addFieldComplete.bind(this), null, null, "");
    },

    addFieldComplete: function (button, value, object) {

        value = value.trim();
        if (button == "ok" && value.length > 1) {
            Ext.Ajax.request({
                url: "/admin/key-value/addgroup",
                params: {
                    name: value
                },
                success: function (response) {
                    var data = Ext.decode(response.responseText);

                    if(!data || !data.success) {
                        Ext.Msg.alert(t("keyvalue_error_addgroup_title"), t("keyvalue_error_addgroup_msg"));
                    } else {
                        this.store.reload({
                                callback: function() {
                                var rowIndex = this.store.find('name', value);
                                // alert(rowIndex);
                                if (rowIndex != -1) {
                                    var sm = this.grid.getSelectionModel();
                                    sm.select(rowIndex);
                                }

                                var lastOptions = this.store.lastOptions;
                                Ext.apply(lastOptions.params, {
                                    overrideSort: "false"
                                });
                            }.bind(this),
                             params: {
                                "overrideSort": "true"
                            }
                            }
                        );
                    }
                }.bind(this)
            });
        }
        else if (button == "cancel") {
            return;
        }
        else {
            Ext.Msg.alert(t("keyvalue_configuration"), t("keyvalue_invalidname"));
        }
    }

});

<|endoftext|>


<commit_before>/**
 * @file DraggableGridItem component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {DragSource, DropTarget} from 'react-dnd';

import Checkbox from '../Checkbox';
import CircularLoading from '../CircularLoading';
import Tip from '../Tip';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import DragDrop from '../_vendors/DragDrop';
import SelectMode from '../_statics/SelectMode';

const DRAG_GRID_ITEM_SYMBOL = Symbol('DRAG_GRID_ITEM');

@DropTarget(DRAG_GRID_ITEM_SYMBOL, DragDrop.getHorizontalTarget(), connect => ({
    connectDropTarget: connect.dropTarget()
}))
@DragSource(DRAG_GRID_ITEM_SYMBOL, DragDrop.getSource(), (connect, monitor) => ({
    connectDragPreview: connect.dragPreview(),
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
}))
export default class DraggableGridItem extends Component {

    static SelectMode = SelectMode;

    constructor(props, ...rest) {

        super(props, ...rest);

        this.state = {
            checked: props.checked,
            tipVisible: false,
            tipTriggerEl: null
        };

        this.showTip = ::this.showTip;
        this.hideTip = ::this.hideTip;
        this.checkboxChangeHandler = ::this.checkboxChangeHandler;
        this.radioChangeHandler = ::this.radioChangeHandler;
        this.touchTapHandler = ::this.touchTapHandler;
        this.mouseEnterHandler = ::this.mouseEnterHandler;
        this.mouseOverHandler = ::this.mouseOverHandler;

    }

    showTip(e) {
        if (!this.state.tipVisible) {
            this.setState({
                tipVisible: true,
                tipTriggerEl: e.target
            });
        }
    }

    hideTip() {
        this.setState({
            tipVisible: false
        });
    }

    checkboxChangeHandler(checked, callback) {
        this.setState({
            checked
        }, () => {

            const {onSelect, onDeselect} = this.props;

            if (checked) {
                onSelect && onSelect();
            } else {
                onDeselect && onDeselect();
            }

            callback && typeof callback === 'function' && callback();

        });
    }

    radioChangeHandler(callback) {

        const {checked} = this.state;

        if (!checked) {
            this.setState({
                checked: true
            }, () => {
                const {onSelect} = this.props;
                onSelect && onSelect();
                callback && typeof callback === 'function' && callback();
            });
        } else {
            callback && typeof callback === 'function' && callback();
        }

    }

    touchTapHandler(e) {

        e.preventDefault();

        const {disabled, isLoading, isGroupTitle} = this.props;

        if (disabled || isLoading || isGroupTitle) {
            return;
        }

        const {selectMode} = this.props,
            callback = () => {
                const {onTouchTap} = this.props;
                onTouchTap && onTouchTap(e);
            };

        switch (selectMode) {
            case DraggableGridItem.SelectMode.MULTI_SELECT:
                this.checkboxChangeHandler(!this.state.checked, callback);
                return;
            case DraggableGridItem.SelectMode.SINGLE_SELECT:
                this.radioChangeHandler(callback);
                return;
            case DraggableGridItem.SelectMode.NORMAL:
                callback();
                return;
        }

    }

    mouseEnterHandler(e) {

        this.showTip(e);

        const {onMouseEnter} = this.props;
        onMouseEnter && onMouseEnter(e);

    }

    mouseOverHandler(e) {

        this.showTip(e);

        const {onMouseOver} = this.props;
        onMouseOver && onMouseOver(e);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.checked !== this.state.checked) {
            this.setState({
                checked: nextProps.checked
            });
        }
    }

    render() {

        const {
                connectDragPreview, connectDragSource, connectDropTarget, isDragging,
                index, className, style, itemColWidth, theme, data, text, desc, iconCls, rightIconCls,
                selectMode, disabled, isLoading, itemRenderer, renderer, isGroupTitle, anchorIconCls, isDraggableAnyWhere,
                tip, tipPosition, onMouseLeave
            } = this.props,
            {checked, tipVisible, tipTriggerEl} = this.state,

            listItemClassName = (theme ? ` theme-${theme}` : '') + (checked ? ' activated' : '')
                + (isDragging ? ' dragging' : '') + (isDraggableAnyWhere ? ' draggable' : '')
                + (className ? ' ' + className : ''),

            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left',

            anchorEl = ,

            el = (
                <div className={'draggable-grid-item-wrapper'}
                     style={itemColWidth ? {width: `${itemColWidth}%`} : null}>

                        {
                            selectMode === DraggableGridItem.SelectMode.MULTI_SELECT ?
                                <Checkbox className="draggable-grid-item-checkbox"
                                          checked={checked}
                                          disabled={disabled || isLoading}/>
                                :
                                null
                        }

                        {
                            selectMode === DraggableGridItem.SelectMode.SINGLE_SELECT ?

                                :
                                null
                        }

                        {
                            isLoading && loadingIconPosition === 'left' ?

                                    <CircularLoading className="button-loading-icon"
                                                     size="small"/>

                                :
                                (
                                    iconCls ?
                                        <i className={`button-icon button-icon-left ${iconCls}`}
                                           aria-hidden="true">
                                        :
                                        null
                                )
                        }

                        {
                            itemRenderer && typeof itemRenderer === 'function' ?
                                itemRenderer(data, index)
                                :
                                (
                                    renderer && typeof renderer === 'function' ?
                                        renderer(data, index)
                                        :
                                        (
                                            desc ?

                                                        {text}

                                                        {desc}

                                                :
                                                text
                                        )
                                )
                        }

                        {
                            isLoading && loadingIconPosition === 'right' ?
                                <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                                 size="small"/>
                                :
                                (
                                    rightIconCls ?
                                        <i className={`button-icon button-icon-right ${rightIconCls}`}
                                           aria-hidden="true">
                                        :
                                        null
                                )
                        }

                        {
                            isGroupTitle ?
                                null
                                :
                                (
                                    isDraggableAnyWhere ?
                                        anchorEl
                                        :
                                        connectDragSource(anchorEl)
                                )
                        }

                    {
                        tip ?
                            <Tip text={tip}
                                 visible={tipVisible}
                                 triggerEl={tipTriggerEl}
                                 position={tipPosition}
                                 onRequestClose={this.hideTip}/>
                            :
                            null
                    }

            );

        return isGroupTitle ?
            el
            :
            (
                isDraggableAnyWhere ?
                    connectDragSource(connectDropTarget(el))
                    :
                    connectDragPreview(connectDropTarget(el))
            );

    }
};

DraggableGridItem.propTypes = {

    connectDragPreview: PropTypes.func,
    connectDragSource: PropTypes.func,
    connectDropTarget: PropTypes.func,
    isDragging: PropTypes.bool,

    index: PropTypes.number,

    className: PropTypes.string,
    style: PropTypes.object,
    theme: PropTypes.oneOf(Object.keys(Theme).map(key => Theme[key])),

    data: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    text: PropTypes.any,
    desc: PropTypes.string,
    disabled: PropTypes.bool,
    isLoading: PropTypes.bool,
    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,
    itemRenderer: PropTypes.func,
    renderer: PropTypes.func,
    checked: PropTypes.bool,
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),
    groupIndex: PropTypes.number,
    isGroupTitle: PropTypes.bool,
    anchorIconCls: PropTypes.string,
    isDraggableAnyWhere: PropTypes.bool,

    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

    onTouchTap: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    onMouseOver: PropTypes.func

};

DraggableGridItem.defaultProps = {

    index: 0,

    className: '',
    style: null,

    theme: Theme.DEFAULT,

    data: '',
    value: '',
    text: '',
    desc: '',

    disabled: false,
    isLoading: false,

    iconCls: '',
    rightIconCls: '',

    checked: false,

    selectMode: SelectMode.NORMAL,

    isGroupTitle: false,

    anchorIconCls: 'fa fa-bars',
    isDraggableAnyWhere: false,

    tip: ''

};<commit_msg>DraggableGridItem bug fixed<commit_after>/**
 * @file DraggableGridItem component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {DragSource, DropTarget} from 'react-dnd';

import Checkbox from '../Checkbox';
import CircularLoading from '../CircularLoading';
import Tip from '../Tip';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import DragDrop from '../_vendors/DragDrop';
import SelectMode from '../_statics/SelectMode';

const DRAG_GRID_ITEM_SYMBOL = Symbol('DRAG_GRID_ITEM');

@DropTarget(DRAG_GRID_ITEM_SYMBOL, DragDrop.getHorizontalTarget(), connect => ({
    connectDropTarget: connect.dropTarget()
}))
@DragSource(DRAG_GRID_ITEM_SYMBOL, DragDrop.getSource(), (connect, monitor) => ({
    connectDragPreview: connect.dragPreview(),
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
}))
export default class DraggableGridItem extends Component {

    static SelectMode = SelectMode;

    constructor(props, ...rest) {

        super(props, ...rest);

        this.state = {
            checked: props.checked,
            tipVisible: false,
            tipTriggerEl: null
        };

        this.showTip = ::this.showTip;
        this.hideTip = ::this.hideTip;
        this.checkboxChangeHandler = ::this.checkboxChangeHandler;
        this.radioChangeHandler = ::this.radioChangeHandler;
        this.touchTapHandler = ::this.touchTapHandler;
        this.mouseEnterHandler = ::this.mouseEnterHandler;
        this.mouseOverHandler = ::this.mouseOverHandler;

    }

    showTip(e) {
        if (!this.state.tipVisible) {
            this.setState({
                tipVisible: true,
                tipTriggerEl: e.target
            });
        }
    }

    hideTip() {
        this.setState({
            tipVisible: false
        });
    }

    checkboxChangeHandler(checked, callback) {
        this.setState({
            checked
        }, () => {

            const {onSelect, onDeselect} = this.props;

            if (checked) {
                onSelect && onSelect();
            } else {
                onDeselect && onDeselect();
            }

            callback && typeof callback === 'function' && callback();

        });
    }

    radioChangeHandler(callback) {

        const {checked} = this.state;

        if (!checked) {
            this.setState({
                checked: true
            }, () => {
                const {onSelect} = this.props;
                onSelect && onSelect();
                callback && typeof callback === 'function' && callback();
            });
        } else {
            callback && typeof callback === 'function' && callback();
        }

    }

    touchTapHandler(e) {

        e.preventDefault();

        const {disabled, isLoading, isGroupTitle} = this.props;

        if (disabled || isLoading || isGroupTitle) {
            return;
        }

        const {selectMode} = this.props,
            callback = () => {
                const {onTouchTap} = this.props;
                onTouchTap && onTouchTap(e);
            };

        switch (selectMode) {
            case DraggableGridItem.SelectMode.MULTI_SELECT:
                this.checkboxChangeHandler(!this.state.checked, callback);
                return;
            case DraggableGridItem.SelectMode.SINGLE_SELECT:
                this.radioChangeHandler(callback);
                return;
            case DraggableGridItem.SelectMode.NORMAL:
                callback();
                return;
        }

    }

    mouseEnterHandler(e) {

        this.showTip(e);

        const {onMouseEnter} = this.props;
        onMouseEnter && onMouseEnter(e);

    }

    mouseOverHandler(e) {

        this.showTip(e);

        const {onMouseOver} = this.props;
        onMouseOver && onMouseOver(e);

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.checked !== this.state.checked) {
            this.setState({
                checked: nextProps.checked
            });
        }
    }

    render() {

        const {
                connectDragPreview, connectDragSource, connectDropTarget, isDragging,
                index, className, style, itemColWidth, theme, data, text, desc, iconCls, rightIconCls,
                selectMode, disabled, isLoading, itemRenderer, renderer, isGroupTitle, anchorIconCls, isDraggableAnyWhere,
                tip, tipPosition, onMouseLeave
            } = this.props,
            {checked, tipVisible, tipTriggerEl} = this.state,

            listItemClassName = (theme ? ` theme-${theme}` : '') + (checked ? ' activated' : '')
                + (isDragging ? ' dragging' : '') + (isDraggableAnyWhere ? ' draggable' : '')
                + (className ? ' ' + className : ''),

            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left',

            anchorEl = ,

            el = (
                <div className={'draggable-grid-item-wrapper'}
                     style={itemColWidth ? {width: `${itemColWidth}%`} : null}>

                        {
                            selectMode === DraggableGridItem.SelectMode.MULTI_SELECT ?
                                <Checkbox className="draggable-grid-item-checkbox"
                                          checked={checked}
                                          disabled={disabled || isLoading}/>
                                :
                                null
                        }

                        {
                            selectMode === DraggableGridItem.SelectMode.SINGLE_SELECT ?

                                :
                                null
                        }

                        {
                            isLoading && loadingIconPosition === 'left' ?

                                    <CircularLoading className="button-loading-icon"
                                                     size="small"/>

                                :
                                (
                                    iconCls ?
                                        <i className={`button-icon button-icon-left ${iconCls}`}
                                           aria-hidden="true">
                                        :
                                        null
                                )
                        }

                        {
                            itemRenderer && typeof itemRenderer === 'function' ?
                                itemRenderer(data, index)
                                :
                                (
                                    renderer && typeof renderer === 'function' ?
                                        renderer(data, index)
                                        :
                                        (
                                            desc ?

                                                        {text}

                                                        {desc}

                                                :
                                                text
                                        )
                                )
                        }

                        {
                            isLoading && loadingIconPosition === 'right' ?
                                <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                                 size="small"/>
                                :
                                (
                                    rightIconCls ?
                                        <i className={`button-icon button-icon-right ${rightIconCls}`}
                                           aria-hidden="true">
                                        :
                                        null
                                )
                        }

                        {
                            isGroupTitle ?
                                null
                                :
                                (
                                    isDraggableAnyWhere ?
                                        anchorEl
                                        :
                                        connectDragSource(anchorEl)
                                )
                        }

                    {
                        tip ?
                            <Tip visible={tipVisible}
                                 triggerEl={tipTriggerEl}
                                 position={tipPosition}
                                 onRequestClose={this.hideTip}>
                                {tip}
                            </Tip>
                            :
                            null
                    }

            );

        return isGroupTitle ?
            el
            :
            (
                isDraggableAnyWhere ?
                    connectDragSource(connectDropTarget(el))
                    :
                    connectDragPreview(connectDropTarget(el))
            );

    }
};

DraggableGridItem.propTypes = {

    connectDragPreview: PropTypes.func,
    connectDragSource: PropTypes.func,
    connectDropTarget: PropTypes.func,
    isDragging: PropTypes.bool,

    index: PropTypes.number,

    className: PropTypes.string,
    style: PropTypes.object,
    theme: PropTypes.oneOf(Object.keys(Theme).map(key => Theme[key])),

    data: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    text: PropTypes.any,
    desc: PropTypes.string,
    disabled: PropTypes.bool,
    isLoading: PropTypes.bool,
    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,
    itemRenderer: PropTypes.func,
    renderer: PropTypes.func,
    checked: PropTypes.bool,
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),
    groupIndex: PropTypes.number,
    isGroupTitle: PropTypes.bool,
    anchorIconCls: PropTypes.string,
    isDraggableAnyWhere: PropTypes.bool,

    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

    onTouchTap: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    onMouseOver: PropTypes.func

};

DraggableGridItem.defaultProps = {

    index: 0,

    className: '',
    style: null,

    theme: Theme.DEFAULT,

    data: '',
    value: '',
    text: '',
    desc: '',

    disabled: false,
    isLoading: false,

    iconCls: '',
    rightIconCls: '',

    checked: false,

    selectMode: SelectMode.NORMAL,

    isGroupTitle: false,

    anchorIconCls: 'fa fa-bars',
    isDraggableAnyWhere: false,

    tip: ''

};<|endoftext|>


<commit_before>
modulejs.define('parser/apache-autoindex', ['_', '$', 'core/mode', 'core/settings', 'core/format', 'model/entry'], function (_, $, mode, settings, format, Entry) {

	var parseTableRow = function (absHref, tr) {

			var $tds = $(tr).find('td'),
				$a = $tds.eq(1).find('a'),
				label = $a.text(),
				time = format.parseDate($tds.eq(2).text(), 'YYYY-MM-DD HH:mm'),
				size = format.parseSize($tds.eq(3).text());

			absHref = absHref + $a.attr('href');

			return label === 'Parent Directory' ? null : Entry.get(absHref, time, size);
		},

		parseTable = function (absHref, table) {

			return _.compact(_.map($(table).find('td').closest('tr'), function (tr) {

				return parseTableRow(absHref, tr);
			}));
		},

		parse = function (absHref, html) {

			var id = '#data-apache-autoindex',
				$html = $(html),
				$id = $html.filter(id);

			if (!$id.length) {
				$id = $html.find(id);
			}

			return parseTable(absHref, $id.find('table'));
		};

	mode.id = 'aai';
	mode.dataType = 'apache-autoindex';
	mode.serverName = 'apache';
	mode.serverVersion = null;

	return {
		dataType: 'apache-autoindex',
		parse: parse
	};
});
<commit_msg>Hopefully fixes date parsing in aai mode.<commit_after>
modulejs.define('parser/apache-autoindex', ['_', '$', 'core/mode', 'core/settings', 'core/format', 'model/entry'], function (_, $, mode, settings, format, Entry) {

	var parseTableRow = function (absHref, tr) {

			var $tds = $(tr).find('td'),
				$a = $tds.eq(1).find('a'),
				label = $a.text(),
				time = format.parseDate($tds.eq(2).text(), ['YYYY-MM-DD HH:mm', 'DD-MMM-YYYY HH:mm']),
				size = format.parseSize($tds.eq(3).text());

			absHref = absHref + $a.attr('href');

			return label === 'Parent Directory' ? null : Entry.get(absHref, time, size);
		},

		parseTable = function (absHref, table) {

			return _.compact(_.map($(table).find('td').closest('tr'), function (tr) {

				return parseTableRow(absHref, tr);
			}));
		},

		parse = function (absHref, html) {

			var id = '#data-apache-autoindex',
				$html = $(html),
				$id = $html.filter(id);

			if (!$id.length) {
				$id = $html.find(id);
			}

			return parseTable(absHref, $id.find('table'));
		};

	mode.id = 'aai';
	mode.dataType = 'apache-autoindex';
	mode.serverName = 'apache';
	mode.serverVersion = null;

	return {
		dataType: 'apache-autoindex',
		parse: parse
	};
});
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 SUSE Linux
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE.txt file for details.
 */

(function () {
  'use strict';

  angular.module('janusHangouts')
    .service('StatesService', StatesService);

  StatesService.$inject = ['$q', '$stateParams', 'RoomService', 'UserService'];

  function StatesService($q, $stateParams, RoomService, UserService) {
    /**
     * Sets the current user and room according to params and/or cookies.
     */
    this.setRoomAndUser = function() {
      var deferred = $q.defer();

      // Set user
      // TODO: Read userName from a cookie if not received as param
      var userName = $stateParams.user;
      if (userName !== undefined) {
        UserService.signin(userName);
      }

      // Set room
      var roomId = parseInt($stateParams.room);
      if (Number.isNaN(roomId)) {
        RoomService.setRoom(null);
        deferred.resolve();
      } else {
        RoomService.getRooms().then(function(rooms) {
          var result = _.find(rooms, function(room) { return room.id === roomId; });
          RoomService.setRoom(result || null);
          deferred.resolve();
        });
      }
      return deferred.promise;
    };
  }
})();
<commit_msg>Set default user name on persistent URLs<commit_after>/*
 * Copyright (C) 2015 SUSE Linux
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE.txt file for details.
 */

(function () {
  'use strict';

  angular.module('janusHangouts')
    .service('StatesService', StatesService);

  StatesService.$inject = ['$q', '$stateParams', 'RoomService', 'UserService'];

  function StatesService($q, $stateParams, RoomService, UserService) {
    /**
     * Sets the current user and room according to params and/or cookies.
     */
    this.setRoomAndUser = function() {
      var deferred = $q.defer();

      // Set user
      var userName = $stateParams.user || UserService.getSetting("lastUsername");
      if (userName !== undefined) {
        UserService.signin(userName);
      }

      // Set room
      var roomId = parseInt($stateParams.room);
      if (Number.isNaN(roomId)) {
        RoomService.setRoom(null);
        deferred.resolve();
      } else {
        RoomService.getRooms().then(function(rooms) {
          var result = _.find(rooms, function(room) { return room.id === roomId; });
          RoomService.setRoom(result || null);
          deferred.resolve();
        });
      }
      return deferred.promise;
    };
  }
})();
<|endoftext|>


<commit_before>import React from 'react';
import Radium from 'radium';
import {TextField, RaisedButton, IconButton, Colors} from 'material-ui';
import LinkedStateMixin from 'react-addons-linked-state-mixin';
import LeftBar from './LeftBar';

export default Radium(React.createClass({
  displayName: 'LandingRegister',

  mixins: [LinkedStateMixin],

  getInitialState() {
    return {
      emailAddress: ''
    };
  },

  getStyles() {
    return {
      componentBody: {
        display: 'flex',
        flexDirection: 'row',
        justifyContent: 'center',
        paddingTop: 76
      },
      logoBar: {
        display: 'flex',
        flexDirection: 'column',
        maxWidth: 324
      },
      contentBar: {
        display: 'flex',
        flexDirection: 'column',
        width: 554,
        paddingLeft: 38,
        fontSize: 14
      },
      hacklagLogo: {
        width: 324,
        height: 324,
        padding: 20
      },
      hacklagLogoDialog: {
        position: 'relative',
        left: -76,
        top: -404,
        width: 164,
        height: 114
      },
      socialBar: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        display: 'flex',
        marginTop: -24
      },
      inviteButton: {
        width: '50%'
      },
      headlineText: {
        fontSize: 18
      },
      smallerText: {
        fontSize: 12
      }
    };
  },

  render() {
    let styles = this.getStyles();

    return (

        <LeftBar/>

            We hope to start first Hackerspace in Białystok

            We are working hard to make it real. To be part of this initiative
            or just to be up to date with the progress please type in your email
            and we will send you an invitation to our Forum and Chat.

            Demonstrating size and engagement of the community is
             extremely important to gather founds and start operating -
            please sign up!

            <TextField
              floatingLabelText="Your Email"
              type="email"
              fullWidth="true"
              valueLink={this.linkState('emailAddress')} />

            <RaisedButton
              style={styles.inviteButton}
              primary={true}
              label="Cool, let me in!" />

              On our forum, you can find information for members, volunteers, partners,
              sponsors. We are sharing there information about the potential property and the
              whole process of adopting place for Hacklag.

    );
  }
}));
<commit_msg>[HAC-157] Changed variable type on passing from string to boolean<commit_after>import React from 'react';
import Radium from 'radium';
import {TextField, RaisedButton, IconButton, Colors} from 'material-ui';
import LinkedStateMixin from 'react-addons-linked-state-mixin';
import LeftBar from './LeftBar';

export default Radium(React.createClass({
  displayName: 'LandingRegister',

  mixins: [LinkedStateMixin],

  getInitialState() {
    return {
      emailAddress: ''
    };
  },

  getStyles() {
    return {
      componentBody: {
        display: 'flex',
        flexDirection: 'row',
        justifyContent: 'center',
        paddingTop: 76
      },
      logoBar: {
        display: 'flex',
        flexDirection: 'column',
        maxWidth: 324
      },
      contentBar: {
        display: 'flex',
        flexDirection: 'column',
        width: 554,
        paddingLeft: 38,
        fontSize: 14
      },
      hacklagLogo: {
        width: 324,
        height: 324,
        padding: 20
      },
      hacklagLogoDialog: {
        position: 'relative',
        left: -76,
        top: -404,
        width: 164,
        height: 114
      },
      socialBar: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        display: 'flex',
        marginTop: -24
      },
      inviteButton: {
        width: '50%'
      },
      headlineText: {
        fontSize: 18
      },
      smallerText: {
        fontSize: 12
      }
    };
  },

  render() {
    let styles = this.getStyles();

    return (

        <LeftBar/>

            We hope to start first Hackerspace in Białystok

            We are working hard to make it real. To be part of this initiative
            or just to be up to date with the progress please type in your email
            and we will send you an invitation to our Forum and Chat.

            Demonstrating size and engagement of the community is
             extremely important to gather founds and start operating -
            please sign up!

            <TextField
              floatingLabelText="Your Email"
              type="email"
              fullWidth={true}
              valueLink={this.linkState('emailAddress')} />

            <RaisedButton
              style={styles.inviteButton}
              primary={true}
              label="Cool, let me in!" />

              On our forum, you can find information for members, volunteers, partners,
              sponsors. We are sharing there information about the potential property and the
              whole process of adopting place for Hacklag.

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


<commit_before>'use strict';

var defineProperties = require('functions/define-properties'),
    onValueUpdate = require('functions/on-value-update'),
    removeClass = require('functions/remove-class'),
    renderTemplate = require('functions/render-template'),
    Tracker = require('classes/tracker');

var storage = window.localStorage;

/**
 * TrackerCollection class
 * @param {node}   wrap     trackers main wrapper
 * @param {string} template tracker template string
 * @return {void}
 */
function TrackerCollection(wrap, template) {

    var $ = {wrap: wrap},
        deleteMode = false,
        mergeMode = false,
        nextTrackerID = 0,
        trackers = {};

    if (!($.wrap instanceof HTMLDivElement)) throw new Error('invalid container');
    if (typeof template !== 'string') throw new Error('invalid template');

    $.list = $.wrap.querySelector('.trackers-list');
    $.add = $.wrap.querySelector('.trackers-button--add');
    $.remAll = $.wrap.querySelector('.trackers-button--rem-all');
    $.delete = $.wrap.querySelector('.trackers-button--delete');
    $.cancelDel = $.wrap.querySelector('.trackers-button--cancel-del');
    $.merge = $.wrap.querySelector('.trackers-button--merge');
    $.mergeSel = $.wrap.querySelector('.trackers-button--merge-sel');
    $.cancelMerge = $.wrap.querySelector('.trackers-button--cancel-merge');

    if (!storage) {
        throw new Error('localStorage not available');
    }

    defineProperties(this, {

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

        trackersSize: {
            get: function() {
                return Object.keys(trackers).length;
            }
        },

        minified: {
            get: function() {
                return Object.keys(trackers).reduce(function(trackerArr, trackerID) {
                    return trackerArr.concat([trackers[trackerID].minified]);
                }, []);
            }
        },

        mergeMode: {
            get: function() {
                return mergeMode;
            },
            set: function(mode) {
                if (typeof mode === 'boolean') {
                    if (mode && this.trackersSize === 0 || mergeMode === mode) return;
                    if (deleteMode && mode) deleteMode = false;
                    mergeMode = mode;
                    setModeStatus.call(this, $);
                }
            }
        },

        deleteMode: {
            get: function() {
                return deleteMode;
            },
            set: function(mode) {
                if (typeof mode === 'boolean') {
                    if (mode && this.trackersSize === 0 || deleteMode === mode) return;
                    if (mergeMode && mode) mergeMode = false;
                    deleteMode = mode;
                    setModeStatus.call(this, $);
                }
            }
        }

    });

    /**
     * add a tracker to tracker list
     * @param {tracker} tracker tracker instance
     * @param {boolean} store   whether to store the tracker collection after adding or not
     * @return {trackerCollection} this
     */
    this.add = function(tracker, store) {
        var container,
            trackerID = nextTrackerID++;
        store = typeof store === 'boolean' ? store : true;
        tracker = trackers[trackerID] = tracker instanceof Tracker ? tracker : new Tracker();
        container = renderTemplate(template, {
            tracked: tracker.format(),
            description: tracker.description,
            tracking: tracker.tracking,
            id: trackerID
        });
        $.list.insertBefore(container, $.list.firstChild);
        addTrackerEvents.apply(this, [tracker, trackerID, {
            container: container,
            tracked: container.querySelector('.tracker-tracked'),
            description: container.querySelector('.tracker-description'),
            toggle: container.querySelector('.tracker-toggle'),
            merge: container.querySelector('.tracker-merge'),
            remove: container.querySelector('.tracker-remove')
        }]);
        this.deleteMode = false;
        this.mergeMode = false;
        if (store) {
            onTrackersListEdit.call(this, $);
            this.store();
        }
        return this;
    };

    this.remove = function(trackerID, store) {
        store = typeof store === 'boolean' ? store : true;
        if (trackers.hasOwnProperty(trackerID)) {
            delete trackers[trackerID];
            $.list.removeChild(document.getElementById('tracker-' + trackerID));
            if (store) {
                onTrackersListEdit.call(this, $);
                this.store();
            }
        }
        return this;
    };

    this.new = function() {
        var trackerID = nextTrackerID;
        this.add();
        trackers[trackerID].start();
        document.getElementById('tracker-' + trackerID).querySelector('.tracker-description').focus();
    }.bind(this);

    setUiEvents.call(this, $);

}

function onTrackersListEdit($) {
    if (this.trackersSize === 0) {
        $.delete.parentNode.className += ' inactive';
        this.deleteMode = false;
    }
    else removeClass($.delete.parentNode, 'inactive');
    if (this.trackersSize <= 1) {
        $.merge.parentNode.className += ' inactive';
        this.mergeMode = false;
    }
    else removeClass($.merge.parentNode, 'inactive');
}

function setModeStatus($) {
    var inputs = $.list.querySelectorAll('input, textarea'),
        noMode = !this.mergeMode && !this.deleteMode;
    removeClass($.wrap, 'trackers-(merge|delete)-mode', '$3');
    if (this.mergeMode) $.wrap.className += ' trackers-merge-mode';
    if (this.deleteMode) $.wrap.className += ' trackers-delete-mode';
    Object.keys(inputs).forEach(function(i) {
        inputs[i].readOnly = !noMode;
    });
}

function setUiEvents($) {

    $.add.addEventListener('click', function() {
        this.new();
    }.bind(this));

    $.remAll.addEventListener('click', this.removeAll.bind(this));

    $.delete.addEventListener('click', function() {
        this.deleteMode = !this.deleteMode;
    }.bind(this));

    $.cancelDel.addEventListener('click', function() {
        this.deleteMode = false;
    }.bind(this));

    $.mergeSel.addEventListener('click', this.mergeSelected.bind(this));

    $.merge.addEventListener('click', function() {
        this.mergeMode = !this.mergeMode;
    }.bind(this));

    $.cancelMerge.addEventListener('click', function() {
        this.mergeMode = false;
    }.bind(this));

}

/**
 * store tracker in local storage
 * @return {void}
 */
TrackerCollection.prototype.store = function() {
    try {
        storage.setItem('trackers', JSON.stringify(this.minified));
    } catch (err) {
        throw new Error('could not store trackers');
    }
    return this;
};

/**
 * restore trackers from local storage
 * @return {void}
 */
TrackerCollection.prototype.restore = function() {
    var trackersArr;
    try {
        Object.keys(this.trackers).forEach(function(trackerID) {
            this.remove(trackerID);
        }.bind(this));
        trackersArr = JSON.parse(storage.getItem('trackers')) || [];
        trackersArr.forEach(function(data, i) {
            data.unshift(null);
            this.add(new (Function.prototype.bind.apply(Tracker, data)), trackersArr.length-1 === i);
        }.bind(this));
    } catch (err) {
        throw new Error('could not restore trackers');
    }
    return this;
};

TrackerCollection.prototype.removeAll = function() {
    Object.keys(this.trackers).forEach(function(trackerID) {
        this.remove(trackerID, this.trackersSize === 1);
    }.bind(this));
    this.deleteMode = false;
    return this;
};

TrackerCollection.prototype.mergeSelected = function() {
    return this;
};

/**
 * add events to a tracker
 * @param {Tracker} tracker   tracker instance
 * @param {number}  trackerID plain object containing dom nodes
 * @param {object}  $         plain object containing dom nodes
 * @return {void}
 */
function addTrackerEvents(tracker, trackerID, $) {

    var trackerInterval;

    onValueUpdate($.description, function() {
        if (this.mergeMode || this.deleteMode) return;
        tracker.description = $.description.value;
        resizeTextarea();
        this.store();
    }.bind(this));

    $.tracked.addEventListener('focus', function() {
        $.tracked.dataset.prevValue = $.tracked.value;
    });

    onValueUpdate($.tracked, function() {
        if (this.mergeMode || this.deleteMode) return;
        if (tracker.format('%h:%m:%s', Tracker.anyToDuration($.tracked.value)) !== $.tracked.dataset.prevValue) {
            tracker.tracked = Tracker.stringToDuration($.tracked.value);
            if (tracker.tracking) tracker.trackingSince = Tracker.now();
            this.store();
        }
    }.bind(this));

    $.tracked.addEventListener('blur', function() {
        $.tracked.value = tracker.format();
    });

    $.toggle.addEventListener('click', function() {
        tracker.toggle();
        this.store();
    }.bind(this));

    $.remove.addEventListener('click', function() {
        this.remove(trackerID);
    }.bind(this));

    $.merge.addEventListener('click', function() {
        $.container.className += ' tracker-merge-select';
    });

    tracker.on('start', function() {
        Object.keys(this.trackers).forEach(function(trackerID) {
            if (this.trackers[trackerID] !== tracker) this.trackers[trackerID].stop();
        }.bind(this));
        startTrackerUpdate.call(this);
        setTrackerClass();
        resizeTextarea();
    }.bind(this));

    tracker.on('stop', function() {
        stopTrackerUpdate();
        setTrackerClass();
        resizeTextarea();
    });

    if (tracker.tracking) startTrackerUpdate.call(this);

    resizeTextarea();

    /**
     * set tracker container class attribute depending on tracking status
     * @return {void}
     */
    function setTrackerClass() {
        removeClass($.container, 'tracker--(not-)?tracking', '$3');
        $.container.className += ' tracker--' + (tracker.tracking ? '' : 'not-') + 'tracking';
    }

    /**
     * start auto-updating total tracking duration input value
     * @return {void}
     */
    function startTrackerUpdate() {
        if (trackerInterval) clearInterval(trackerInterval);
        trackerInterval = window.setInterval(function() {
            if ($.tracked !== document.activeElement || this.mergeMode || this.deleteMode) {
                $.tracked.value = tracker.format();
            }
        }.bind(this));
    }

    /**
     * stop auto-updating total tracking duration input value
     * @return {void}
     */
    function stopTrackerUpdate() {
        clearInterval(trackerInterval);
    }

    /**
     * resize tracker description textarea depending on content height
     * @return {void}
     */
    function resizeTextarea() {
        $.description.style.height = 'auto';
        $.description.style.height = $.description.scrollHeight + 'px';
    }

    return this;

};

// export class
module.exports = TrackerCollection;
<commit_msg>make eslint happy<commit_after>'use strict';

var defineProperties = require('functions/define-properties'),
    onValueUpdate = require('functions/on-value-update'),
    removeClass = require('functions/remove-class'),
    renderTemplate = require('functions/render-template'),
    Tracker = require('classes/tracker');

var storage = window.localStorage;

/**
 * TrackerCollection class
 * @param {node}   wrap     trackers main wrapper
 * @param {string} template tracker template string
 * @return {void}
 */
function TrackerCollection(wrap, template) {

    var $ = {wrap: wrap},
        deleteMode = false,
        mergeMode = false,
        nextTrackerID = 0,
        trackers = {};

    if (!($.wrap instanceof HTMLDivElement)) throw new Error('invalid container');
    if (typeof template !== 'string') throw new Error('invalid template');

    $.list = $.wrap.querySelector('.trackers-list');
    $.add = $.wrap.querySelector('.trackers-button--add');
    $.remAll = $.wrap.querySelector('.trackers-button--rem-all');
    $.delete = $.wrap.querySelector('.trackers-button--delete');
    $.cancelDel = $.wrap.querySelector('.trackers-button--cancel-del');
    $.merge = $.wrap.querySelector('.trackers-button--merge');
    $.mergeSel = $.wrap.querySelector('.trackers-button--merge-sel');
    $.cancelMerge = $.wrap.querySelector('.trackers-button--cancel-merge');

    if (!storage) {
        throw new Error('localStorage not available');
    }

    defineProperties(this, {

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

        trackersSize: {
            get: function() {
                return Object.keys(trackers).length;
            }
        },

        minified: {
            get: function() {
                return Object.keys(trackers).reduce(function(trackerArr, trackerID) {
                    return trackerArr.concat([trackers[trackerID].minified]);
                }, []);
            }
        },

        mergeMode: {
            get: function() {
                return mergeMode;
            },
            set: function(mode) {
                if (typeof mode === 'boolean') {
                    if (mode && this.trackersSize === 0 || mergeMode === mode) return;
                    if (deleteMode && mode) deleteMode = false;
                    mergeMode = mode;
                    setModeStatus.call(this, $);
                }
            }
        },

        deleteMode: {
            get: function() {
                return deleteMode;
            },
            set: function(mode) {
                if (typeof mode === 'boolean') {
                    if (mode && this.trackersSize === 0 || deleteMode === mode) return;
                    if (mergeMode && mode) mergeMode = false;
                    deleteMode = mode;
                    setModeStatus.call(this, $);
                }
            }
        }

    });

    /**
     * add a tracker to tracker list
     * @param {tracker} tracker tracker instance
     * @param {boolean} store   whether to store the tracker collection after adding or not
     * @return {trackerCollection} this
     */
    this.add = function(tracker, store) {
        var container,
            trackerID = nextTrackerID++;
        store = typeof store === 'boolean' ? store : true;
        tracker = trackers[trackerID] = tracker instanceof Tracker ? tracker : new Tracker();
        container = renderTemplate(template, {
            tracked: tracker.format(),
            description: tracker.description,
            tracking: tracker.tracking,
            id: trackerID
        });
        $.list.insertBefore(container, $.list.firstChild);
        addTrackerEvents.apply(this, [tracker, trackerID, {
            container: container,
            tracked: container.querySelector('.tracker-tracked'),
            description: container.querySelector('.tracker-description'),
            toggle: container.querySelector('.tracker-toggle'),
            merge: container.querySelector('.tracker-merge'),
            remove: container.querySelector('.tracker-remove')
        }]);
        this.deleteMode = false;
        this.mergeMode = false;
        if (store) {
            onTrackersListEdit.call(this, $);
            this.store();
        }
        return this;
    };

    this.remove = function(trackerID, store) {
        store = typeof store === 'boolean' ? store : true;
        if (trackers.hasOwnProperty(trackerID)) {
            delete trackers[trackerID];
            $.list.removeChild(document.getElementById('tracker-' + trackerID));
            if (store) {
                onTrackersListEdit.call(this, $);
                this.store();
            }
        }
        return this;
    };

    this.new = function() {
        var trackerID = nextTrackerID;
        this.add();
        trackers[trackerID].start();
        document.getElementById('tracker-' + trackerID).querySelector('.tracker-description').focus();
    }.bind(this);

    setUiEvents.call(this, $);

}

function onTrackersListEdit($) {
    if (this.trackersSize === 0) {
        $.delete.parentNode.className += ' inactive';
        this.deleteMode = false;
    }
    else removeClass($.delete.parentNode, 'inactive');
    if (this.trackersSize <= 1) {
        $.merge.parentNode.className += ' inactive';
        this.mergeMode = false;
    }
    else removeClass($.merge.parentNode, 'inactive');
}

function setModeStatus($) {
    var inputs = $.list.querySelectorAll('input, textarea'),
        noMode = !this.mergeMode && !this.deleteMode;
    removeClass($.wrap, 'trackers-(merge|delete)-mode', '$3');
    if (this.mergeMode) $.wrap.className += ' trackers-merge-mode';
    if (this.deleteMode) $.wrap.className += ' trackers-delete-mode';
    Object.keys(inputs).forEach(function(i) {
        inputs[i].readOnly = !noMode;
    });
}

function setUiEvents($) {

    $.add.addEventListener('click', function() {
        this.new();
    }.bind(this));

    $.remAll.addEventListener('click', this.removeAll.bind(this));

    $.delete.addEventListener('click', function() {
        this.deleteMode = !this.deleteMode;
    }.bind(this));

    $.cancelDel.addEventListener('click', function() {
        this.deleteMode = false;
    }.bind(this));

    $.mergeSel.addEventListener('click', this.mergeSelected.bind(this));

    $.merge.addEventListener('click', function() {
        this.mergeMode = !this.mergeMode;
    }.bind(this));

    $.cancelMerge.addEventListener('click', function() {
        this.mergeMode = false;
    }.bind(this));

}

/**
 * store tracker in local storage
 * @return {void}
 */
TrackerCollection.prototype.store = function() {
    try {
        storage.setItem('trackers', JSON.stringify(this.minified));
    } catch (err) {
        throw new Error('could not store trackers');
    }
    return this;
};

/**
 * restore trackers from local storage
 * @return {void}
 */
TrackerCollection.prototype.restore = function() {
    var trackersArr;
    try {
        Object.keys(this.trackers).forEach(function(trackerID) {
            this.remove(trackerID);
        }.bind(this));
        trackersArr = JSON.parse(storage.getItem('trackers')) || [];
        trackersArr.forEach(function(data, i) {
            data.unshift(null);
            this.add(new (Function.prototype.bind.apply(Tracker, data)), trackersArr.length - 1 === i);
        }.bind(this));
    } catch (err) {
        throw new Error('could not restore trackers');
    }
    return this;
};

TrackerCollection.prototype.removeAll = function() {
    Object.keys(this.trackers).forEach(function(trackerID) {
        this.remove(trackerID, this.trackersSize === 1);
    }.bind(this));
    this.deleteMode = false;
    return this;
};

TrackerCollection.prototype.mergeSelected = function() {
    return this;
};

/**
 * add events to a tracker
 * @param {Tracker} tracker   tracker instance
 * @param {number}  trackerID plain object containing dom nodes
 * @param {object}  $         plain object containing dom nodes
 * @return {void}
 */
function addTrackerEvents(tracker, trackerID, $) {

    var trackerInterval;

    onValueUpdate($.description, function() {
        if (this.mergeMode || this.deleteMode) return;
        tracker.description = $.description.value;
        resizeTextarea();
        this.store();
    }.bind(this));

    $.tracked.addEventListener('focus', function() {
        $.tracked.dataset.prevValue = $.tracked.value;
    });

    onValueUpdate($.tracked, function() {
        if (this.mergeMode || this.deleteMode) return;
        if (tracker.format('%h:%m:%s', Tracker.anyToDuration($.tracked.value)) !== $.tracked.dataset.prevValue) {
            tracker.tracked = Tracker.stringToDuration($.tracked.value);
            if (tracker.tracking) tracker.trackingSince = Tracker.now();
            this.store();
        }
    }.bind(this));

    $.tracked.addEventListener('blur', function() {
        $.tracked.value = tracker.format();
    });

    $.toggle.addEventListener('click', function() {
        tracker.toggle();
        this.store();
    }.bind(this));

    $.remove.addEventListener('click', function() {
        this.remove(trackerID);
    }.bind(this));

    $.merge.addEventListener('click', function() {
        $.container.className += ' tracker-merge-select';
    });

    tracker.on('start', function() {
        Object.keys(this.trackers).forEach(function(trackerID) {
            if (this.trackers[trackerID] !== tracker) this.trackers[trackerID].stop();
        }.bind(this));
        startTrackerUpdate.call(this);
        setTrackerClass();
        resizeTextarea();
    }.bind(this));

    tracker.on('stop', function() {
        stopTrackerUpdate();
        setTrackerClass();
        resizeTextarea();
    });

    if (tracker.tracking) startTrackerUpdate.call(this);

    resizeTextarea();

    /**
     * set tracker container class attribute depending on tracking status
     * @return {void}
     */
    function setTrackerClass() {
        removeClass($.container, 'tracker--(not-)?tracking', '$3');
        $.container.className += ' tracker--' + (tracker.tracking ? '' : 'not-') + 'tracking';
    }

    /**
     * start auto-updating total tracking duration input value
     * @return {void}
     */
    function startTrackerUpdate() {
        if (trackerInterval) clearInterval(trackerInterval);
        trackerInterval = window.setInterval(function() {
            if ($.tracked !== document.activeElement || this.mergeMode || this.deleteMode) {
                $.tracked.value = tracker.format();
            }
        }.bind(this));
    }

    /**
     * stop auto-updating total tracking duration input value
     * @return {void}
     */
    function stopTrackerUpdate() {
        clearInterval(trackerInterval);
    }

    /**
     * resize tracker description textarea depending on content height
     * @return {void}
     */
    function resizeTextarea() {
        $.description.style.height = 'auto';
        $.description.style.height = $.description.scrollHeight + 'px';
    }

    return this;

}

// export class
module.exports = TrackerCollection;
<|endoftext|>


<commit_before>(() => {
	"use strict";

	// Module definition
	angular.module("home.component", []);

	// Options
	const options = {};

	// Template
	options.templateUrl = "home/home.component.html";

	// Controller and ViewModel
	options.controllerAs = "vm";
	options.controller = function HomeController() {
		const vm = this;
	};

	// Register the component
	angular.module("home.component").component("home", options);
})();
<commit_msg>Update home.component.js<commit_after>(() => {
	"use strict";

	// Module definition
	const home = angular.module("home.component", []);

	// Options
	const options = {};

	// Template
	options.templateUrl = "home/home.component.html";

	// Controller and ViewModel
	options.controllerAs = "vm";
	options.controller = function HomeController() {
		const vm = this;
	};

	// Register the component
	home.component("home", options);
})();
<|endoftext|>