<commit_before>;

import angular from 'angular';
import Immutable from 'immutable';
import squareImageModule from './square-image';
import chatTextFieldModule from './chat-textfield';
import { attach, is, delay, msStringToDate } from '../utils.js'
import { actionCreators }  from '../actions/actions';
import { labels, relativeTime } from '../won-label-utils';
import {
    selectAllByConnections,
    selectOpenConnectionUri,
    selectOpenConnection,
} from '../selectors';
import { selectTimestamp } from '../won-utils'

const serviceDependencies = ['$ngRedux', '$scope', '$element'];

function genComponentConf() {
    let template = `

                Conversation about "{{ self.connectionData.getIn(['remoteNeed', 'won:hasContent', 'dc:title']) }}"

                Options

Loading…

                    <won-square-image
                        title="self.connectionData.getIn(['remoteNeed', 'won:hasContent', 'dc:title'])"
                        src="self.connectionData.getIn(['remoteNeed', 'titleImgSrc'])"
                        uri="self.connectionData.getIn(['remoteNeed', '@id'])"
                        ng-show="message.get('hasSenderNeed') != self.connectionData.getIn(['ownNeed', '@id'])">
                    </won-square-image>

                            {{ message.get('hasTextMessage') }}

                                Pending …

                                {{ message.get('humanReadableTimestamp') }}

        <chat-textfield
            class="pm__footer"
            placeholder="::'Your Message'"
            on-input="::self.input(value)"
            on-submit="::self.send()"
            submit-button-label="::'Send'"
            >
        </chat-textfield>
    `;

    class Controller {
        constructor(/* arguments = dependency injections */) {
            attach(this, serviceDependencies, arguments);
            window.pm4dbg = this;
            window.selectOpenConnectionUri4dbg = selectOpenConnectionUri;
            window.selectChatMessages4dbg = selectChatMessages;

            const self = this;

            this.scrollContainerNg().bind('scroll', e => this.onScroll(e));

            const loadStuff = () => {

                this.eventsPending = true; // TODO should be determined in select

                const state = this.$ngRedux.getState();
                const connectionUri = selectOpenConnectionUri(state);
                const connection = selectOpenConnection(state);
                console.log('post-messages.js: testing for selective loading. ',
                    connectionUri, connection, chatMessages);
                console.log('post-messages.js: calling crawlable query soon. ');
                //TODO determine first if component is actually visible (angular calls the constructor long before that)

                //TODO only do this if the events aren't defined!
                //requiringData AC
                won.getEventsOfConnection(connectionUri, connection.get('belongsToNeed'))
                    .then(events => {
                        this.eventsPending = false; // TODO should be determined in select
                        this.$ngRedux.dispatch({
                            type: 'requiredData',
                            payload: {
                                events: Immutable.fromJS(events)
                            }
                        })

                    })
            }

            //TODO delete unnecessary logging
            //TODO call this when view is visible and the connection has been loaded (sometimes
            // the view is faster, sometimes the connection)
            console.log('post-messages.js: executing constructor.');
            this.$scope.$watch('self.connection', (newCnct, oldCnct) => {
                console.log('post-messages.js: in connection watch');
                if(newCnct && newCnct !== oldCnct) {
                    loadStuff();

                }
            });

            // the connection has finished initializing. Or not? Not indeed,
            // the caching mechanisms should de-dupe the requests.
            const state = self.$ngRedux.getState();
            const connectionUri = selectOpenConnectionUri(state);
            const connection = selectOpenConnection(state);
            const chatMessages = selectChatMessages(state);
            if(connectionUri && connection) {
                loadStuff();
            }

            //this.postmsg = this;
            const selectFromState = state => {

                //TODO seems like rather bad practice to have sideffects here
                //scroll to bottom directly after rendering, if snapped
                delay(0).then(() => {
                    self.updateScrollposition();
                });

                const connectionUri = selectOpenConnectionUri(state);
                const chatMessages = selectChatMessages(state);
                return {
                    connectionUri,
                    connection: selectOpenConnection(state),
                    lastUpdateTime: state.get('lastUpdateTime'),
                    connectionData: selectAllByConnections(state).get(connectionUri),
                    chatMessages: chatMessages && chatMessages.toArray(), //toArray needed as ng-repeat won't work otherwise :|
                    state4dbg: state,
                }
            };

            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

            this.snapToBottom();
        }

        snapToBottom() {
            this._snapBottom = true;
            this.scrollToBottom();
        }
        unsnapFromBottom() {
            this._snapBottom = false;
        }
        updateScrollposition() {
            if(this._snapBottom) {
                this.scrollToBottom();
            }
        }
        scrollToBottom() {
            this._programmaticallyScrolling = true;

            this.scrollContainer().scrollTop = this.scrollContainer().scrollHeight;
        }
        onScroll(e) {
            if(!this._programmaticallyScrolling) {
                //only unsnap if the user scrolled themselves
                this.unsnapFromBottom();
            }

            const sc = this.scrollContainer();
            const isAtBottom = sc.scrollTop + sc.offsetHeight >= sc.scrollHeight;
            if(isAtBottom) {
                this.snapToBottom();
            }

            this._programmaticallyScrolling = false
        }
        scrollContainerNg() {
            if(!this._scrollContainer) {
                this._scrollContainer = this.$element.find('.pm__content');
            }
            return this._scrollContainer;
        }
        scrollContainer() {
            return this.scrollContainerNg()[0];
        }

        input(userInput) {
            this.chatMessage = userInput;
        }

        send() {
            const trimmedMsg = this.chatMessage.trim();
            const connectionUri = this.connectionData.getIn(['connection', 'uri']);
            if(trimmedMsg) {
               this.connections__sendChatMessage(trimmedMsg, connectionUri);
            }
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: { },
        template: template,
    }
}

export default angular.module('won.owner.components.postMessages', [
    squareImageModule,
    chatTextFieldModule,
])
    .directive('wonPostMessages', genComponentConf)
    .name;

//TODO refactor so that it always returns an array of immutable messages to
// allow ng-repeat without giving up the cheaper digestion
function selectChatMessages(state) {
    const connectionUri = selectOpenConnectionUri(state);
    const connectionData = selectAllByConnections(state).get(connectionUri);
    const ownNeedUri = connectionData && connectionData.getIn(['ownNeed', '@id']);

    if (!connectionData || !connectionData.get('events')) {
        return Immutable.List();

    } else {
        const timestamp = (event) =>
            //msStringToDate(selectTimestamp(event, connectionUri))
            msStringToDate(selectTimestamp(event))

        const chatMessages = connectionData.get('events')

            /* filter for valid chat messages */
            .filter(event => {
                if (event.get('hasTextMessage')) return true;
                else {
                    let remote = event.get('hasCorrespondingRemoteMessage');
                    if(is('String', remote)) {
                        remote = state.getIn(['events', 'events', remote]);
                    }
                    return remote && remote.get('hasTextMessage');
                }
            }).map(event => {
                const remote = event.get('hasCorrespondingRemoteMessage');
                if (event.get('hasTextMessage'))
                    return event;
                else
                    return remote;
            })

            /* sort them so the latest get shown last */
            .sort((event1, event2) =>
                timestamp(event1) - timestamp(event2)
            )
            /*
             * sort so the latest, optimistic/unconfirmed
             * messages are always at the bottom.
             */
            .sort((event1, event2) => {
                const u1 = event1.get('unconfirmed');
                const u2 = event2.get('unconfirmed');

                if(u1 && !u2) {
                  return 1;
                }
                else if (!u1 && u2) {
                    return -1;
                }
                else {
                    return 0;
                }
            })

            /* add a nice relative timestamp */
            .map(event => event.set(
                    'humanReadableTimestamp',
                    relativeTime(
                        state.get('lastUpdateTime'),
                        timestamp(event)
                    )
                )
            );

        return chatMessages;
    }

}
<commit_msg>Added comments.<commit_after>;

import angular from 'angular';
import Immutable from 'immutable';
import squareImageModule from './square-image';
import chatTextFieldModule from './chat-textfield';
import { attach, is, delay, msStringToDate } from '../utils.js'
import { actionCreators }  from '../actions/actions';
import { labels, relativeTime } from '../won-label-utils';
import {
    selectAllByConnections,
    selectOpenConnectionUri,
    selectOpenConnection,
} from '../selectors';
import { selectTimestamp } from '../won-utils'

const serviceDependencies = ['$ngRedux', '$scope', '$element'];

function genComponentConf() {
    let template = `

                Conversation about "{{ self.connectionData.getIn(['remoteNeed', 'won:hasContent', 'dc:title']) }}"

                Options

Loading…

                    <won-square-image
                        title="self.connectionData.getIn(['remoteNeed', 'won:hasContent', 'dc:title'])"
                        src="self.connectionData.getIn(['remoteNeed', 'titleImgSrc'])"
                        uri="self.connectionData.getIn(['remoteNeed', '@id'])"
                        ng-show="message.get('hasSenderNeed') != self.connectionData.getIn(['ownNeed', '@id'])">
                    </won-square-image>

                            {{ message.get('hasTextMessage') }}

                                Pending …

                                {{ message.get('humanReadableTimestamp') }}

        <chat-textfield
            class="pm__footer"
            placeholder="::'Your Message'"
            on-input="::self.input(value)"
            on-submit="::self.send()"
            submit-button-label="::'Send'"
            >
        </chat-textfield>
    `;

    class Controller {
        constructor(/* arguments = dependency injections */) {
            attach(this, serviceDependencies, arguments);
            window.pm4dbg = this;
            window.selectOpenConnectionUri4dbg = selectOpenConnectionUri;
            window.selectChatMessages4dbg = selectChatMessages;

            const self = this;

            this.scrollContainerNg().bind('scroll', e => this.onScroll(e));

            // TODO <HACK>

            const loadStuff = () => {
                /*
                 * TODO don't trigger this more than once! When the
                 * events are there, there's no need to dispatch an
                 * action again -- unless paging is involved of course.
                 *
                 * Check during every select?
                 */

                this.eventsPending = true; // TODO should be determined in select

                const state = this.$ngRedux.getState();
                const connectionUri = selectOpenConnectionUri(state);
                const connection = selectOpenConnection(state);
                console.log('post-messages.js: testing for selective loading. ',
                    connectionUri, connection, chatMessages);
                console.log('post-messages.js: calling crawlable query soon. ');
                //TODO determine first if component is actually visible (angular calls the constructor long before that)

                //TODO only do this if the events aren't defined!
                //requiringData AC
                won.getEventsOfConnection(connectionUri, connection.get('belongsToNeed'))
                    .then(events => {
                        this.eventsPending = false; // TODO should be determined in select
                        this.$ngRedux.dispatch({
                            type: 'requiredData',
                            payload: {
                                events: Immutable.fromJS(events)
                            }
                        })

                    })
            }

            //TODO delete unnecessary logging
            //TODO call this when view is visible and the connection has been loaded (sometimes
            // the view is faster, sometimes the connection)
            console.log('post-messages.js: executing constructor.');
            this.$scope.$watch('self.connection', (newCnct, oldCnct) => {
                console.log('post-messages.js: in connection watch');
                if(newCnct && newCnct !== oldCnct) {
                    /*
                     * the component was visible before the
                     * connection had been loaded. But now
                     * it's here.
                     */
                    loadStuff();
                }
            });

            // the connection has finished initializing. Or not? Not indeed,
            // the caching mechanisms should de-dupe the requests.
            const state = self.$ngRedux.getState();
            const connectionUri = selectOpenConnectionUri(state);
            const connection = selectOpenConnection(state);
            const chatMessages = selectChatMessages(state);
            if(connectionUri && connection) {
                /*
                 * The component has been created
                 * after the connection had been loaded.
                 */
                loadStuff();
            }
            // TODO </HACK>

            //this.postmsg = this;
            const selectFromState = state => {

                //TODO seems like rather bad practice to have sideffects here
                //scroll to bottom directly after rendering, if snapped
                delay(0).then(() => {
                    self.updateScrollposition();
                });

                const connectionUri = selectOpenConnectionUri(state);
                const chatMessages = selectChatMessages(state);
                return {
                    connectionUri,
                    connection: selectOpenConnection(state),
                    lastUpdateTime: state.get('lastUpdateTime'),
                    connectionData: selectAllByConnections(state).get(connectionUri),
                    chatMessages: chatMessages && chatMessages.toArray(), //toArray needed as ng-repeat won't work otherwise :|
                    state4dbg: state,
                }
            };

            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);

            this.snapToBottom();
        }

        snapToBottom() {
            this._snapBottom = true;
            this.scrollToBottom();
        }
        unsnapFromBottom() {
            this._snapBottom = false;
        }
        updateScrollposition() {
            if(this._snapBottom) {
                this.scrollToBottom();
            }
        }
        scrollToBottom() {
            this._programmaticallyScrolling = true;

            this.scrollContainer().scrollTop = this.scrollContainer().scrollHeight;
        }
        onScroll(e) {
            if(!this._programmaticallyScrolling) {
                //only unsnap if the user scrolled themselves
                this.unsnapFromBottom();
            }

            const sc = this.scrollContainer();
            const isAtBottom = sc.scrollTop + sc.offsetHeight >= sc.scrollHeight;
            if(isAtBottom) {
                this.snapToBottom();
            }

            this._programmaticallyScrolling = false
        }
        scrollContainerNg() {
            if(!this._scrollContainer) {
                this._scrollContainer = this.$element.find('.pm__content');
            }
            return this._scrollContainer;
        }
        scrollContainer() {
            return this.scrollContainerNg()[0];
        }

        input(userInput) {
            this.chatMessage = userInput;
        }

        send() {
            const trimmedMsg = this.chatMessage.trim();
            const connectionUri = this.connectionData.getIn(['connection', 'uri']);
            if(trimmedMsg) {
               this.connections__sendChatMessage(trimmedMsg, connectionUri);
            }
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: { },
        template: template,
    }
}

export default angular.module('won.owner.components.postMessages', [
    squareImageModule,
    chatTextFieldModule,
])
    .directive('wonPostMessages', genComponentConf)
    .name;

//TODO refactor so that it always returns an array of immutable messages to
// allow ng-repeat without giving up the cheaper digestion
function selectChatMessages(state) {
    const connectionUri = selectOpenConnectionUri(state);
    const connectionData = selectAllByConnections(state).get(connectionUri);
    const ownNeedUri = connectionData && connectionData.getIn(['ownNeed', '@id']);

    if (!connectionData || !connectionData.get('events')) {
        return Immutable.List();

    } else {
        const timestamp = (event) =>
            //msStringToDate(selectTimestamp(event, connectionUri))
            msStringToDate(selectTimestamp(event))

        const chatMessages = connectionData.get('events')

            /* filter for valid chat messages */
            .filter(event => {
                if (event.get('hasTextMessage')) return true;
                else {
                    let remote = event.get('hasCorrespondingRemoteMessage');
                    if(is('String', remote)) {
                        remote = state.getIn(['events', 'events', remote]);
                    }
                    return remote && remote.get('hasTextMessage');
                }
            }).map(event => {
                const remote = event.get('hasCorrespondingRemoteMessage');
                if (event.get('hasTextMessage'))
                    return event;
                else
                    return remote;
            })

            /* sort them so the latest get shown last */
            .sort((event1, event2) =>
                timestamp(event1) - timestamp(event2)
            )
            /*
             * sort so the latest, optimistic/unconfirmed
             * messages are always at the bottom.
             */
            .sort((event1, event2) => {
                const u1 = event1.get('unconfirmed');
                const u2 = event2.get('unconfirmed');

                if(u1 && !u2) {
                  return 1;
                }
                else if (!u1 && u2) {
                    return -1;
                }
                else {
                    return 0;
                }
            })

            /* add a nice relative timestamp */
            .map(event => event.set(
                    'humanReadableTimestamp',
                    relativeTime(
                        state.get('lastUpdateTime'),
                        timestamp(event)
                    )
                )
            );

        return chatMessages;
    }

}
<|endoftext|>


<commit_before>Application.Controllers.controller("FilesAllController",
    ["$scope", "projectFiles", "mcfile", "$state", "pubsub", FilesAllController]);
function FilesAllController($scope, projectFiles, mcfile, $state, pubsub) {
    var f = projectFiles.model.projects[$scope.project.id].dir;

    // Root is name of project. Have it opened by default.
    $scope.files = [f];

    pubsub.waitOn($scope, 'files.refresh', function() {
        $scope.gridOptions.api.refreshView();
    });

    $scope.fileSrc = function (file) {
        return mcfile.src(file.id);
    };

    var columnDefs = [
        {
            displayName: "",
            field: "name",
            width: 350,
            cellRenderer: function (params) {
                return '' +
                    '' +
                    params.node.name + '';
            }
        }];

    $scope.gridOptions = {
        columnDefs: columnDefs,
        rowData: $scope.files,
        rowSelection: 'multiple',
        rowsAlreadyGrouped: true,
        rowClicked: rowClicked,
        enableColResize: true,
        enableSorting: true,
        rowHeight: 30,
        angularCompileRows: true,
        icons: {
            groupExpanded: '',
            groupContracted: ''
        },
        groupInnerCellRenderer: groupInnerCellRenderer
    };

    function groupInnerCellRenderer(params) {
        var eCell = document.createElement('span');
        eCell.innerHTML = params.node.displayname;
        return eCell;
    }

    function rowClicked(params) {
        if (params.node.type == 'datadir') {
            projectFiles.setActiveDirectory(params.node)
        } else {
            projectFiles.setActiveFile(params.node);
        }
        $state.go('projects.project.files.all.edit', {file_id: params.node.df_id, file_type: params.node.type});
    }

    function init() {
        projectFiles.setActiveDirectory($scope.files[0]);
        $state.go('projects.project.files.all.edit', {file_id: $scope.files[0].df_id, file_type: 'datadir'});
    }

    init();
}

<commit_msg>Sort tree entries.<commit_after>Application.Controllers.controller("FilesAllController",
    ["$scope", "projectFiles", "mcfile", "$state", "pubsub","$filter",  FilesAllController]);
function FilesAllController($scope, projectFiles, mcfile, $state, pubsub, $filter) {
    var f = projectFiles.model.projects[$scope.project.id].dir;

    // Root is name of project. Have it opened by default.
    $scope.files = [f];
    $scope.files[0].children = $filter('orderBy')($scope.files[0].children, 'displayname');

    pubsub.waitOn($scope, 'files.refresh', function() {
        $scope.gridOptions.api.refreshView();
    });

    $scope.fileSrc = function (file) {
        return mcfile.src(file.id);
    };

    var columnDefs = [
        {
            displayName: "",
            field: "name",
            width: 350,
            cellRenderer: function (params) {
                return '' +
                    '' +
                    params.node.name + '';
            }
        }];

    $scope.gridOptions = {
        columnDefs: columnDefs,
        rowData: $scope.files,
        rowSelection: 'multiple',
        rowsAlreadyGrouped: true,
        rowClicked: rowClicked,
        enableColResize: true,
        enableSorting: false,
        rowHeight: 30,
        angularCompileRows: true,
        icons: {
            groupExpanded: '',
            groupContracted: ''
        },
        groupInnerCellRenderer: groupInnerCellRenderer
    };

    function groupInnerCellRenderer(params) {
        var eCell = document.createElement('span');
        eCell.innerHTML = params.node.displayname;
        return eCell;
    }

    function rowClicked(params) {
        if (params.node.type == 'datadir') {
            projectFiles.setActiveDirectory(params.node)
            if (!params.node.sorted) {
                var file = projectFiles.findFileByID($scope.project.id, params.node.df_id);
                file.children = $filter('orderBy')(file.children, 'displayname');
                file.sorted = true;
                $scope.gridOptions.api.onNewRows();
            }
        } else {
            projectFiles.setActiveFile(params.node);
        }
        $state.go('projects.project.files.all.edit', {file_id: params.node.df_id, file_type: params.node.type});
    }

    function init() {
        projectFiles.setActiveDirectory($scope.files[0]);
        $state.go('projects.project.files.all.edit', {file_id: $scope.files[0].df_id, file_type: 'datadir'});
    }

    init();
}

<|endoftext|>


<commit_before>class ExperimentsService {
    /*@ngInject*/
    constructor(projectsAPI) {
        this.projectsAPI = projectsAPI;
    }

    getAllForProject(projectID) {
        return this.projectsAPI(projectID).one('experiments').getList();
    }

    createForProject(projectID, experiment) {
        return this.projectsAPI(projectID).one('experiments').customPOST(experiment);
    }

    updateForProject(projectID, experimentID, what) {
        return this.projectsAPI(projectID).one('experiments', experimentID).customPUT(what);
    }

    getForProject(projectID, experimentID) {
        return this.projectsAPI(projectID).one('experiments', experimentID).customGET();
    }

    createTask(projectID, experimentID, experimentTask) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks').customPOST(experimentTask);
    }

    updateTask(projectID, experimentID, taskID, task) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks', taskID).customPUT(task);
    }

    addTemplateToTask(projectID, experimentID, taskID, templateID) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks', taskID)
            .one('template', templateID).customPOST({});
    }

    updateTaskTemplateProperties(projectID, experimentID, taskID, updateArgs) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks', taskID)
            .one('template').customPUT(updateArgs);
    }

    deleteTask(projectID, experimentID, taskID) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('task', taskID).customDELETE();
    }

    addGoals(projectID, experimentID, experimentGoals) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('goals').customPOST(experimentGoals);
    }

    removeGoal() {

    }
}

angular.module('materialscommons').service('experimentsService', ExperimentsService);
<commit_msg>Call correct route.<commit_after>class ExperimentsService {
    /*@ngInject*/
    constructor(projectsAPI) {
        this.projectsAPI = projectsAPI;
    }

    getAllForProject(projectID) {
        return this.projectsAPI(projectID).one('experiments').getList();
    }

    createForProject(projectID, experiment) {
        return this.projectsAPI(projectID).one('experiments').customPOST(experiment);
    }

    updateForProject(projectID, experimentID, what) {
        return this.projectsAPI(projectID).one('experiments', experimentID).customPUT(what);
    }

    getForProject(projectID, experimentID) {
        return this.projectsAPI(projectID).one('experiments', experimentID).customGET();
    }

    createTask(projectID, experimentID, experimentTask) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks').customPOST(experimentTask);
    }

    updateTask(projectID, experimentID, taskID, task) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks', taskID).customPUT(task);
    }

    addTemplateToTask(projectID, experimentID, taskID, templateID) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks', taskID)
            .one('template', templateID).customPOST({});
    }

    updateTaskTemplateProperties(projectID, experimentID, taskID, updateArgs) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks', taskID)
            .one('template').customPUT(updateArgs);
    }

    deleteTask(projectID, experimentID, taskID) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('tasks', taskID).customDELETE();
    }

    addGoals(projectID, experimentID, experimentGoals) {
        return this.projectsAPI(projectID).one('experiments', experimentID).one('goals').customPOST(experimentGoals);
    }

    removeGoal() {

    }
}

angular.module('materialscommons').service('experimentsService', ExperimentsService);
<|endoftext|>


<commit_before>$(function(){
    new Dragdealer('layer-opacity', {
      horizontal: true,
      vertical: false,
      steps: 100,
      handleClass: "layer-opacity-handle",
      animationCallback: function(x, y) {
        $("#layer-opacity").find(".layer-opacity-handle").text(Math.round(x * 100));
      }
    });
});
<commit_msg>layertree.js removed<commit_after><|endoftext|>


<commit_before>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */
import { combineReducers } from 'redux/dist/redux';
import VMS_CONFIG from "./config.js";
import { logDebug, isObjectEmpty } from './helpers.js';
import {
    ADD_UI_VM,
    DELETE_UI_VM,
    DELETE_UNLISTED_VMS,
    SET_NODE_MAX_MEMORY,
    SET_LOGGED_IN_USER,
    UNDEFINE_NETWORK,
    UNDEFINE_STORAGE_POOL,
    UNDEFINE_VM,
    UPDATE_ADD_INTERFACE,
    UPDATE_ADD_NETWORK,
    UPDATE_ADD_NODE_DEVICE,
    UPDATE_ADD_VM,
    UPDATE_ADD_STORAGE_POOL,
    UPDATE_LIBVIRT_STATE,
    UPDATE_LIBVIRT_VERSION,
    UPDATE_DOMAIN_SNAPSHOTS,
    UPDATE_OS_INFO_LIST,
    UPDATE_STORAGE_VOLUMES,
    UPDATE_UI_VM,
    UPDATE_VM,
} from './constants/store-action-types.js';

// --- helpers -------------------
function getFirstIndexOfResource(state, field, value, connectionName) {
    return state.findIndex(e => {
        return e && e.connectionName === connectionName && e[field] === value;
    });
}

function replaceResource({ state, updatedResource, index }) {
    return state.slice(0, index)
            .concat(updatedResource)
            .concat(state.slice(index + 1));
}

// --- reducers ------------------
function config(state, action) {
    state = state || {
        refreshInterval: VMS_CONFIG.DefaultRefreshInterval,
    };

    switch (action.type) {
    case SET_NODE_MAX_MEMORY: {
        const newState = Object.assign({}, state);
        newState.nodeMaxMemory = action.payload.memory;
        return newState;
    }
    default:
        return state;
    }
}

/**
 * Provider might optionally extend the reducer tree (see state.provider.reducer() function)
 */
function lazyComposedReducer({ parentReducer, getSubreducer, getSubstate, setSubstate }) {
    return (state, action) => {
        let newState = parentReducer(state, action);
        const subreducer = getSubreducer(newState);
        if (subreducer) {
            const newSubstate = subreducer(getSubstate(newState), action);
            if (newSubstate !== getSubstate(newState)) {
                newState = setSubstate(newState, newSubstate);
            }
        }
        return newState;
    };
}

function interfaces(state, action) {
    state = state || [];

    switch (action.type) {
    case UPDATE_ADD_INTERFACE: {
        const { iface } = action.payload;

        if (isObjectEmpty(iface))
            return [...state, iface]; // initialize iface to empty object

        const connectionName = iface.connectionName;
        const index = getFirstIndexOfResource(state, 'name', iface.name, connectionName);
        if (index < 0) { // add
            const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
            if (initObjIndex >= 0)
                state.splice(initObjIndex, 1); // remove empty initial object
            return [...state, iface];
        }

        const updatedIface = Object.assign({}, state[index], iface);
        return replaceResource({ state, updatedIface, index });
    }
    default:
        return state;
    }
}

function networks(state, action) {
    state = state || [];

    switch (action.type) {
    case UNDEFINE_NETWORK: {
        const { connectionName, id } = action.payload;

        return state
                .filter(network => (connectionName !== network.connectionName || id != network.id));
    }
    case UPDATE_ADD_NETWORK: {
        const { network, updateOnly } = action.payload;

        if (isObjectEmpty(network))
            return [...state, network]; // initialize network to empty object

        const connectionName = network.connectionName;
        const index = network.id ? getFirstIndexOfResource(state, 'id', network.id, connectionName)
            : getFirstIndexOfResource(state, 'name', network.name, connectionName);
        if (index < 0) {
            if (!updateOnly) {
                const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
                if (initObjIndex >= 0)
                    state.splice(initObjIndex, 1); // remove empty initial object
                return [...state, network];
            } else {
                return state;
            }
        }

        const updatedNetwork = Object.assign({}, state[index], network);
        return replaceResource({ state, updatedResource: updatedNetwork, index });
    }
    default:
        return state;
    }
}

function nodeDevices(state, action) {
    state = state || [];

    switch (action.type) {
    case UPDATE_ADD_NODE_DEVICE: {
        const { nodedev } = action.payload;

        if (isObjectEmpty(nodedev))
            return [...state, nodedev]; // initialize nodedev to empty object

        const connectionName = nodedev.connectionName;
        const index = getFirstIndexOfResource(state, 'name', nodedev.name, connectionName);
        if (index < 0) { // add
            const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
            if (initObjIndex >= 0)
                state.splice(initObjIndex, 1); // remove empty initial object
            return [...state, nodedev];
        }

        const updatedNodedev = Object.assign({}, state[index], nodedev);
        return replaceResource({ state, updatedNodedev, index });
    }
    default:
        return state;
    }
}

function vms(state, action) {
    state = state || [];

    logDebug('reducer vms: action=' + JSON.stringify(action));

    function findVmToUpdate(state, { connectionName, id, name }) {
        const index = id ? getFirstIndexOfResource(state, 'id', id, connectionName)
            : getFirstIndexOfResource(state, 'name', name, connectionName);
        if (index < 0) {
            if (id)
                logDebug(`vms reducer: vm (id='${id}', connectionName='${connectionName}') not found, skipping`);
            else
                logDebug(`vms reducer: vm (name='${name}', connectionName='${connectionName}') not found, skipping`);
            return null;
        }
        return { // return object of {index, copyOfVm}
            index,
            vmCopy: Object.assign({}, state[index]), // TODO: consider immutableJs
        };
    }

    switch (action.type) {
    case UPDATE_ADD_VM: {
        if (isObjectEmpty(action.vm))
            return [...state, action.vm]; // initialize vm to empty object

        const connectionName = action.vm.connectionName;
        const index = action.vm.id ? getFirstIndexOfResource(state, 'id', action.vm.id, connectionName)
            : getFirstIndexOfResource(state, 'name', action.vm.name, connectionName);
        if (index < 0) { // add
            const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
            if (initObjIndex >= 0)
                state.splice(initObjIndex, 1); // remove empty initial object
            return [...state, action.vm];
        }

        const updatedVm = Object.assign({}, state[index], action.vm);
        return replaceResource({ state, updatedResource: updatedVm, index });
    }
    case UPDATE_VM: {
        const indexedVm = findVmToUpdate(state, action.vm);
        if (!indexedVm) {
            return state;
        }

        let updatedVm;
        if (action.vm.actualTimeInMs < 0) { // clear the usage data (i.e. VM went down)
            logDebug(`Clearing usage data for vm '${action.vm.name}'`);
            updatedVm = Object.assign(indexedVm.vmCopy, action.vm);
            clearUsageData(updatedVm);
        } else {
            timeSampleUsageData(action.vm, indexedVm.vmCopy);
            updatedVm = Object.assign(indexedVm.vmCopy, action.vm);
        }

        // replace whole object
        return replaceResource({ state, updatedResource: updatedVm, index: indexedVm.index });
    }
    case UPDATE_DOMAIN_SNAPSHOTS: {
        const { connectionName, domainPath, snaps } = action.payload;
        const index = getFirstIndexOfResource(state, 'id', domainPath, connectionName);

        if (index < 0)
            return state;

        const indexedVm = findVmToUpdate(state, { connectionName, id: domainPath });
        const updatedVm = Object.assign({}, state[index]);

        updatedVm.snapshots = snaps;

        return replaceResource({ state, updatedResource: updatedVm, index: indexedVm.index });
    }
    case UNDEFINE_VM: {
        if (action.id)
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.id != vm.id ||
                        (action.transientOnly && vm.persistent)));
        else
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.name != vm.name ||
                        (action.transientOnly && vm.persistent)));
    }
    case DELETE_UNLISTED_VMS: {
        if (action.vmIDs)
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.vmIDs.indexOf(vm.id) >= 0));
        else
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.vmNames.indexOf(vm.name) >= 0));
    }
    default: // by default all reducers should return initial state on unknown actions
        return state;
    }
}

function systemInfo(state, action) {
    state = state || {
        libvirtService: {
            name: 'unknown',
            activeState: 'unknown',
            unitState: 'unknown',
        },
        libvirtVersion: 0,
        osInfoList: null,
        loggedUser: null,
    };

    switch (action.type) {
    case UPDATE_OS_INFO_LIST: {
        if (action.osInfoList instanceof Array) {
            return Object.assign({}, state, { osInfoList: action.osInfoList });
        }
        return state;
    }
    case UPDATE_LIBVIRT_STATE: {
        return Object.assign({}, state, { libvirtService:  Object.assign({}, state.libvirtService, action.state) });
    }
    case UPDATE_LIBVIRT_VERSION: {
        return Object.assign({}, state, { libvirtVersion:  action.libvirtVersion });
    }
    case SET_LOGGED_IN_USER: {
        return Object.assign({}, state, { loggedUser: action.payload.loggedUser });
    }
    default: // by default all reducers should return initial state on unknown actions
        return state;
    }
}

function storagePools(state, action) {
    state = state || [];

    function findStoragePoolToUpdate(state, { connectionName, id, name }) {
        const index = id ? getFirstIndexOfResource(state, 'id', id, connectionName)
            : getFirstIndexOfResource(state, 'name', name, connectionName);
        if (index < 0) {
            return null;
        }
        return {
            index,
            storagePoolCopy: Object.assign({}, state[index]),
        };
    }

    switch (action.type) {
    case UNDEFINE_STORAGE_POOL: {
        const { connectionName, id } = action.payload;

        return state
                .filter(storagePool => (connectionName !== storagePool.connectionName || id != storagePool.id));
    }
    case UPDATE_ADD_STORAGE_POOL: {
        const { storagePool, updateOnly, } = action.payload;

        if (isObjectEmpty(storagePool))
            return [...state, storagePool]; // initialize pool to empty object

        const connectionName = storagePool.connectionName;
        const index = storagePool.id ? getFirstIndexOfResource(state, 'id', storagePool.id, connectionName)
            : getFirstIndexOfResource(state, 'name', storagePool.name, connectionName);
        if (index < 0) {
            if (!updateOnly) {
                const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
                if (initObjIndex >= 0)
                    state.splice(initObjIndex, 1); // remove empty initial object
                return [...state, storagePool];
            } else {
                return state;
            }
        }

        const updatedStoragePool = Object.assign({}, state[index], storagePool);
        return replaceResource({ state, updatedResource: updatedStoragePool, index });
    }
    case UPDATE_STORAGE_VOLUMES: {
        const { connectionName, poolName, volumes } = action.payload;
        const index = getFirstIndexOfResource(state, 'name', poolName, connectionName);

        if (index < 0) {
            return state;
        }

        const indexedStoragePool = findStoragePoolToUpdate(state, { connectionName, name: poolName });
        const updatedStoragePool = Object.assign({}, state[index]);

        updatedStoragePool.volumes = volumes;

        return replaceResource({ state, updatedResource: updatedStoragePool, index: indexedStoragePool.index });
    }
    default:
        return state;
    }
}

function ui(state, action) {
    // transient properties
    state = state || {
        notifications: [],
        vms: [], // transient property
    };
    const addVm = () => {
        const existingVm = state.vms.find(vm => vm.name == action.vm.name && vm.connectionName == action.vm.connectionName);
        if (existingVm === undefined) {
            return {
                ...state,
                vms: [...state.vms, action.vm]
            };
        } else {
            if (existingVm.isUi) {
                const updatedVm = Object.assign(existingVm, action.vm);
                return {
                    ...state,
                    vms: [...state.vms.filter(vm => !(vm.name == action.vm.name && vm.connectionName == action.vm.connectionName)), updatedVm]
                };
            }
        }
    };

    switch (action.type) {
    case ADD_UI_VM:
    case UPDATE_UI_VM:
        return addVm();
    case DELETE_UI_VM: {
        return { ...state, vms: state.vms.filter(vm => !(vm.name == action.vm.name && vm.connectionName == action.vm.connectionName)) };
    }
    default:
        return state;
    }
}

function clearUsageData(updatedVm) {
    updatedVm.actualTimeInMs = undefined;
    updatedVm.cpuTime = undefined;
    updatedVm.cpuUsage = undefined;

    updatedVm.rssMemory = undefined;
}

function timeSampleUsageData(newVmRecord, previousVmRecord) {
    if (newVmRecord.actualTimeInMs) { // new usage data are provided
        if (previousVmRecord.actualTimeInMs) { // diff can be computed
            const timeDiff = (newVmRecord.actualTimeInMs - previousVmRecord.actualTimeInMs) * 1000000; // in nanosecs
            if (timeDiff <= 0) {
                logDebug(`-- timeSampleUsageData(): no time difference`);
                return;
            }
            const cpuTimeDiff = newVmRecord.cpuTime - previousVmRecord.cpuTime; // in nanosecs

            // store computed actual usage stats
            newVmRecord.cpuUsage = (100 * cpuTimeDiff / timeDiff);
        } else {
            logDebug(`timeSampleUsageData(): can't compute diff - missing previous record`);
            newVmRecord.cpuUsage = 0;
        }
    }
}

export default combineReducers({
    config: lazyComposedReducer({
        parentReducer: config,
        getSubreducer: (state) => (state.provider && state.provider.reducer) ? state.provider.reducer : undefined,
        getSubstate: (state) => state.providerState,
        setSubstate: (state, subState) => Object.assign({}, state, { providerState: subState }),
    }),
    interfaces,
    networks,
    nodeDevices,
    vms,
    systemInfo,
    storagePools,
    ui,
});
<commit_msg>machines: Check that VM exists<commit_after>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2016 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */
import { combineReducers } from 'redux/dist/redux';
import VMS_CONFIG from "./config.js";
import { logDebug, isObjectEmpty } from './helpers.js';
import {
    ADD_UI_VM,
    DELETE_UI_VM,
    DELETE_UNLISTED_VMS,
    SET_NODE_MAX_MEMORY,
    SET_LOGGED_IN_USER,
    UNDEFINE_NETWORK,
    UNDEFINE_STORAGE_POOL,
    UNDEFINE_VM,
    UPDATE_ADD_INTERFACE,
    UPDATE_ADD_NETWORK,
    UPDATE_ADD_NODE_DEVICE,
    UPDATE_ADD_VM,
    UPDATE_ADD_STORAGE_POOL,
    UPDATE_LIBVIRT_STATE,
    UPDATE_LIBVIRT_VERSION,
    UPDATE_DOMAIN_SNAPSHOTS,
    UPDATE_OS_INFO_LIST,
    UPDATE_STORAGE_VOLUMES,
    UPDATE_UI_VM,
    UPDATE_VM,
} from './constants/store-action-types.js';

// --- helpers -------------------
function getFirstIndexOfResource(state, field, value, connectionName) {
    return state.findIndex(e => {
        return e && e.connectionName === connectionName && e[field] === value;
    });
}

function replaceResource({ state, updatedResource, index }) {
    return state.slice(0, index)
            .concat(updatedResource)
            .concat(state.slice(index + 1));
}

// --- reducers ------------------
function config(state, action) {
    state = state || {
        refreshInterval: VMS_CONFIG.DefaultRefreshInterval,
    };

    switch (action.type) {
    case SET_NODE_MAX_MEMORY: {
        const newState = Object.assign({}, state);
        newState.nodeMaxMemory = action.payload.memory;
        return newState;
    }
    default:
        return state;
    }
}

/**
 * Provider might optionally extend the reducer tree (see state.provider.reducer() function)
 */
function lazyComposedReducer({ parentReducer, getSubreducer, getSubstate, setSubstate }) {
    return (state, action) => {
        let newState = parentReducer(state, action);
        const subreducer = getSubreducer(newState);
        if (subreducer) {
            const newSubstate = subreducer(getSubstate(newState), action);
            if (newSubstate !== getSubstate(newState)) {
                newState = setSubstate(newState, newSubstate);
            }
        }
        return newState;
    };
}

function interfaces(state, action) {
    state = state || [];

    switch (action.type) {
    case UPDATE_ADD_INTERFACE: {
        const { iface } = action.payload;

        if (isObjectEmpty(iface))
            return [...state, iface]; // initialize iface to empty object

        const connectionName = iface.connectionName;
        const index = getFirstIndexOfResource(state, 'name', iface.name, connectionName);
        if (index < 0) { // add
            const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
            if (initObjIndex >= 0)
                state.splice(initObjIndex, 1); // remove empty initial object
            return [...state, iface];
        }

        const updatedIface = Object.assign({}, state[index], iface);
        return replaceResource({ state, updatedIface, index });
    }
    default:
        return state;
    }
}

function networks(state, action) {
    state = state || [];

    switch (action.type) {
    case UNDEFINE_NETWORK: {
        const { connectionName, id } = action.payload;

        return state
                .filter(network => (connectionName !== network.connectionName || id != network.id));
    }
    case UPDATE_ADD_NETWORK: {
        const { network, updateOnly } = action.payload;

        if (isObjectEmpty(network))
            return [...state, network]; // initialize network to empty object

        const connectionName = network.connectionName;
        const index = network.id ? getFirstIndexOfResource(state, 'id', network.id, connectionName)
            : getFirstIndexOfResource(state, 'name', network.name, connectionName);
        if (index < 0) {
            if (!updateOnly) {
                const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
                if (initObjIndex >= 0)
                    state.splice(initObjIndex, 1); // remove empty initial object
                return [...state, network];
            } else {
                return state;
            }
        }

        const updatedNetwork = Object.assign({}, state[index], network);
        return replaceResource({ state, updatedResource: updatedNetwork, index });
    }
    default:
        return state;
    }
}

function nodeDevices(state, action) {
    state = state || [];

    switch (action.type) {
    case UPDATE_ADD_NODE_DEVICE: {
        const { nodedev } = action.payload;

        if (isObjectEmpty(nodedev))
            return [...state, nodedev]; // initialize nodedev to empty object

        const connectionName = nodedev.connectionName;
        const index = getFirstIndexOfResource(state, 'name', nodedev.name, connectionName);
        if (index < 0) { // add
            const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
            if (initObjIndex >= 0)
                state.splice(initObjIndex, 1); // remove empty initial object
            return [...state, nodedev];
        }

        const updatedNodedev = Object.assign({}, state[index], nodedev);
        return replaceResource({ state, updatedNodedev, index });
    }
    default:
        return state;
    }
}

function vms(state, action) {
    state = state || [];

    logDebug('reducer vms: action=' + JSON.stringify(action));

    function findVmToUpdate(state, { connectionName, id, name }) {
        const index = id ? getFirstIndexOfResource(state, 'id', id, connectionName)
            : getFirstIndexOfResource(state, 'name', name, connectionName);
        if (index < 0) {
            if (id)
                logDebug(`vms reducer: vm (id='${id}', connectionName='${connectionName}') not found, skipping`);
            else
                logDebug(`vms reducer: vm (name='${name}', connectionName='${connectionName}') not found, skipping`);
            return null;
        }
        return { // return object of {index, copyOfVm}
            index,
            vmCopy: Object.assign({}, state[index]), // TODO: consider immutableJs
        };
    }

    switch (action.type) {
    case UPDATE_ADD_VM: {
        if (isObjectEmpty(action.vm))
            return [...state, action.vm]; // initialize vm to empty object

        const connectionName = action.vm.connectionName;
        const index = action.vm.id ? getFirstIndexOfResource(state, 'id', action.vm.id, connectionName)
            : getFirstIndexOfResource(state, 'name', action.vm.name, connectionName);
        if (index < 0) { // add
            const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
            if (initObjIndex >= 0)
                state.splice(initObjIndex, 1); // remove empty initial object
            return [...state, action.vm];
        }

        const updatedVm = Object.assign({}, state[index], action.vm);
        return replaceResource({ state, updatedResource: updatedVm, index });
    }
    case UPDATE_VM: {
        const indexedVm = findVmToUpdate(state, action.vm);
        if (!indexedVm) {
            return state;
        }

        let updatedVm;
        if (action.vm.actualTimeInMs < 0) { // clear the usage data (i.e. VM went down)
            logDebug(`Clearing usage data for vm '${action.vm.name}'`);
            updatedVm = Object.assign(indexedVm.vmCopy, action.vm);
            clearUsageData(updatedVm);
        } else {
            timeSampleUsageData(action.vm, indexedVm.vmCopy);
            updatedVm = Object.assign(indexedVm.vmCopy, action.vm);
        }

        // replace whole object
        return replaceResource({ state, updatedResource: updatedVm, index: indexedVm.index });
    }
    case UPDATE_DOMAIN_SNAPSHOTS: {
        const { connectionName, domainPath, snaps } = action.payload;
        const index = getFirstIndexOfResource(state, 'id', domainPath, connectionName);
        const indexedVm = findVmToUpdate(state, { connectionName, id: domainPath });

        if (index < 0 || !indexedVm)
            return state;

        const updatedVm = Object.assign({}, state[index]);

        updatedVm.snapshots = snaps;

        return replaceResource({ state, updatedResource: updatedVm, index: indexedVm.index });
    }
    case UNDEFINE_VM: {
        if (action.id)
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.id != vm.id ||
                        (action.transientOnly && vm.persistent)));
        else
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.name != vm.name ||
                        (action.transientOnly && vm.persistent)));
    }
    case DELETE_UNLISTED_VMS: {
        if (action.vmIDs)
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.vmIDs.indexOf(vm.id) >= 0));
        else
            return state
                    .filter(vm => (action.connectionName !== vm.connectionName || action.vmNames.indexOf(vm.name) >= 0));
    }
    default: // by default all reducers should return initial state on unknown actions
        return state;
    }
}

function systemInfo(state, action) {
    state = state || {
        libvirtService: {
            name: 'unknown',
            activeState: 'unknown',
            unitState: 'unknown',
        },
        libvirtVersion: 0,
        osInfoList: null,
        loggedUser: null,
    };

    switch (action.type) {
    case UPDATE_OS_INFO_LIST: {
        if (action.osInfoList instanceof Array) {
            return Object.assign({}, state, { osInfoList: action.osInfoList });
        }
        return state;
    }
    case UPDATE_LIBVIRT_STATE: {
        return Object.assign({}, state, { libvirtService:  Object.assign({}, state.libvirtService, action.state) });
    }
    case UPDATE_LIBVIRT_VERSION: {
        return Object.assign({}, state, { libvirtVersion:  action.libvirtVersion });
    }
    case SET_LOGGED_IN_USER: {
        return Object.assign({}, state, { loggedUser: action.payload.loggedUser });
    }
    default: // by default all reducers should return initial state on unknown actions
        return state;
    }
}

function storagePools(state, action) {
    state = state || [];

    function findStoragePoolToUpdate(state, { connectionName, id, name }) {
        const index = id ? getFirstIndexOfResource(state, 'id', id, connectionName)
            : getFirstIndexOfResource(state, 'name', name, connectionName);
        if (index < 0) {
            return null;
        }
        return {
            index,
            storagePoolCopy: Object.assign({}, state[index]),
        };
    }

    switch (action.type) {
    case UNDEFINE_STORAGE_POOL: {
        const { connectionName, id } = action.payload;

        return state
                .filter(storagePool => (connectionName !== storagePool.connectionName || id != storagePool.id));
    }
    case UPDATE_ADD_STORAGE_POOL: {
        const { storagePool, updateOnly, } = action.payload;

        if (isObjectEmpty(storagePool))
            return [...state, storagePool]; // initialize pool to empty object

        const connectionName = storagePool.connectionName;
        const index = storagePool.id ? getFirstIndexOfResource(state, 'id', storagePool.id, connectionName)
            : getFirstIndexOfResource(state, 'name', storagePool.name, connectionName);
        if (index < 0) {
            if (!updateOnly) {
                const initObjIndex = state.findIndex(obj => isObjectEmpty(obj));
                if (initObjIndex >= 0)
                    state.splice(initObjIndex, 1); // remove empty initial object
                return [...state, storagePool];
            } else {
                return state;
            }
        }

        const updatedStoragePool = Object.assign({}, state[index], storagePool);
        return replaceResource({ state, updatedResource: updatedStoragePool, index });
    }
    case UPDATE_STORAGE_VOLUMES: {
        const { connectionName, poolName, volumes } = action.payload;
        const index = getFirstIndexOfResource(state, 'name', poolName, connectionName);

        if (index < 0) {
            return state;
        }

        const indexedStoragePool = findStoragePoolToUpdate(state, { connectionName, name: poolName });
        const updatedStoragePool = Object.assign({}, state[index]);

        updatedStoragePool.volumes = volumes;

        return replaceResource({ state, updatedResource: updatedStoragePool, index: indexedStoragePool.index });
    }
    default:
        return state;
    }
}

function ui(state, action) {
    // transient properties
    state = state || {
        notifications: [],
        vms: [], // transient property
    };
    const addVm = () => {
        const existingVm = state.vms.find(vm => vm.name == action.vm.name && vm.connectionName == action.vm.connectionName);
        if (existingVm === undefined) {
            return {
                ...state,
                vms: [...state.vms, action.vm]
            };
        } else {
            if (existingVm.isUi) {
                const updatedVm = Object.assign(existingVm, action.vm);
                return {
                    ...state,
                    vms: [...state.vms.filter(vm => !(vm.name == action.vm.name && vm.connectionName == action.vm.connectionName)), updatedVm]
                };
            }
        }
    };

    switch (action.type) {
    case ADD_UI_VM:
    case UPDATE_UI_VM:
        return addVm();
    case DELETE_UI_VM: {
        return { ...state, vms: state.vms.filter(vm => !(vm.name == action.vm.name && vm.connectionName == action.vm.connectionName)) };
    }
    default:
        return state;
    }
}

function clearUsageData(updatedVm) {
    updatedVm.actualTimeInMs = undefined;
    updatedVm.cpuTime = undefined;
    updatedVm.cpuUsage = undefined;

    updatedVm.rssMemory = undefined;
}

function timeSampleUsageData(newVmRecord, previousVmRecord) {
    if (newVmRecord.actualTimeInMs) { // new usage data are provided
        if (previousVmRecord.actualTimeInMs) { // diff can be computed
            const timeDiff = (newVmRecord.actualTimeInMs - previousVmRecord.actualTimeInMs) * 1000000; // in nanosecs
            if (timeDiff <= 0) {
                logDebug(`-- timeSampleUsageData(): no time difference`);
                return;
            }
            const cpuTimeDiff = newVmRecord.cpuTime - previousVmRecord.cpuTime; // in nanosecs

            // store computed actual usage stats
            newVmRecord.cpuUsage = (100 * cpuTimeDiff / timeDiff);
        } else {
            logDebug(`timeSampleUsageData(): can't compute diff - missing previous record`);
            newVmRecord.cpuUsage = 0;
        }
    }
}

export default combineReducers({
    config: lazyComposedReducer({
        parentReducer: config,
        getSubreducer: (state) => (state.provider && state.provider.reducer) ? state.provider.reducer : undefined,
        getSubstate: (state) => state.providerState,
        setSubstate: (state, subState) => Object.assign({}, state, { providerState: subState }),
    }),
    interfaces,
    networks,
    nodeDevices,
    vms,
    systemInfo,
    storagePools,
    ui,
});
<|endoftext|>


<commit_before>angular.module('app').controller('previewReportCtrl', function($scope,$rootScope) {
	// console.log($scope.eventdoc);
	// console.log($scope.customizedDoc);
//	$scope.previewedGrid = [];
	var checked = $rootScope.checkedColumns;
	$scope.numberOfColumns=checked.length;
	var columnArry = [];
   // pick a grid to iterate
   	var oneGrid =  $scope.eventData.gridData[0];
   	if (oneGrid) {  // at least one grid exist
       for (var columnName in oneGrid.dailyData[0]) {
          if (oneGrid.dailyData[0].hasOwnProperty(columnName)) {
            if (columnName !== '$$hashKey' && columnName != 'label')  {
                columnArry.push(columnName);
            }
          }
       }
       columnArry.sort();

    }
//	var gridData = $scope.eventData.gridData;
//	var combinedGrid = JSON.parse(JSON.stringify($rootScope.combinedGrid));

	//checked = ['1435861626006','1436463022438']
	//var gridData = $scope.eventData.gridData;
	//var combinedGrid = JSON.parse(JSON.stringify($rootScope.combinedGrid));

	$scope.customizedData = JSON.parse(JSON.stringify($scope.eventData));
	//console.log('combinedGrid',combinedGrid);
	// if (checked.length > 0) {
	// 	for(var j = 0; j < combinedGrid.length; j++) {
	// 		for(key in combinedGrid[j]) {
	// 			//console.log('Key:',key);
	// 			if(key === 'label') {
	// 				//do nothing
	// 			} else if (checked.indexOf(key) == -1) {
	// 				delete combinedGrid[j][key];
	// 			}
	// 		}
	// 	}
	// }

	// console.log(combinedGrid);
	//$scope.previewedGrid = combinedGrid;

	/////GRID//////

		// var previewHeaderCellTemplate =
	 //  '
'+
	 //  '

'+
	 //  '
'+
	 //  '  {{ col.displayName CUSTOM_FILTERS }}'+
	 //  '
'+
	 //  '

'+
	 //  '
'+
	 //  '  <input type="text" ng-model="colFilter.term" ng-click="$event.stopPropagation()" ng-attr-placeholder="{{colFilter.placeholder || \'\'}}" class="ui-grid-filter-input"/>'+
	 //  '
 '+
	 //  '    '+
	 //  '
'+
	 //  '
'+
	 //  '
'

 // $scope.checkGridColumn = function(column,checked) {
 // 	var toDelete = '';

 // 	if(checked) {
 // 		$rootScope.checkedColumns.push(column);
 // 	} else {
 // 		if($rootScopecheckedColumns.length > 0) {
 // 			toDelete = $scope.checkedColumns.indexOf(column);
 // 			if(toDelete != -1) {
 // 				$rootScope.checkedColumns.splice(toDelete, 1);
 // 			}
 // 		}
 // 	}
 // };

 // $scope.getPreviewTableHeight = function(grid,id) {
 //       var rowHeight = 30; // your row height
 //       var headerHeight = 30; // your header height
 //       if (id.split('_')[1] ==='0') {
 //          return {
 //              height: (($scope.previewedGrid.length+1) * rowHeight + headerHeight-12) + "px" };
 //       }
 //       else {
 //       return {
 //          height: ($scope.previewedGrid.length * rowHeight + headerHeight-14) + "px" };
 //       }
 //    };

// $scope.previewGenerateColumnDefs= function() {
//    var columnArry = [];
//    var columnLayout = [];
//    // pick a grid to iterate
//    var oneGrid =  $scope.previewedGrid;
// 		if (oneGrid) { // at least one grid exist
// 			for (var  i=0; i<  oneGrid.length; i++) {

// 				for (var columnName in oneGrid[i]) {

// 					if (oneGrid[i].hasOwnProperty(columnName)) {

// 						if (columnName !== '$$hashKey' && columnName != 'label') {
// 							if (columnArry.indexOf(columnName) == -1) {
// 								columnArry.push(columnName);
// 							}
// 						}
// 					}
// 				}

// 			}

// 		}
//     else {
//        //  $scope.addDataColumn('label');
//        // // columnArry.push('label');
//        //  $scope.addDataColumn(''+$scope.eventdoc.dateCreated)
//        //  columnArry.push(''+$scope.eventdoc.dateCreated);
//     }
//        columnArry.sort();
//        columnArry.unshift('label');
//        for(i=0; i< columnArry.length; i++) {
//       // build columns defition object
//          if (columnArry[i] === 'label') {
//             oneColumnDef = {'field': columnArry[i], 'displayName':$scope.eventData.colDisplayNames[columnArry[i]] , enableSorting:false, minWidth: $scope.minTopicWidth,pinnedLeft:true,enableColumnMenu:false};
//           }
//          else {
//             //var formattedDate = $filter('date')(columnArry[i],'mediumDate');
//             oneColumnDef = {'field': columnArry[i], 'displayName' : $scope.eventData.colDisplayNames[columnArry[i]], enableSorting:false, minWidth:$scope.minColWidth, enablePinning:false, enableColumnMenu:false,headerCellTemplate: previewHeaderCellTemplate
//           };
//          }
//             columnLayout.push(oneColumnDef);
//        }
//        //console.log(columnLayout)
//        return columnLayout;

// };

//$scope.previewColumns = $scope.previewGenerateColumnDefs();

$scope.filterSelected = function(items) {
    var result = {};
    angular.forEach(items, function(value,key) {
        //console.log(key,' ',value)
        if (key != 'label' && (checked.indexOf(key) > -1)) {
            result[key] = value;
        }
    });
    return result;
}

$scope.percentChanged = function(row,col) {
	var curIdx = columnArry.indexOf(col);
    var delta = 0;
    if ( curIdx > -1) { // col exist in array
 		if (curIdx  === 0) { // check if this is the first element
 		   // no percent change calculation
 		}
 		else {
 			var previousValue =  row[columnArry[curIdx - 1]];
 			var currentValue = row[col];
 			delta = Math.floor((currentValue - previousValue) / currentValue *100);
 		}
	}
	if (delta > 0) {
		return '(+'+delta+'%)';
	}
	else if(delta ==0) {
		return '(nc%)';
	}
	else {
		return '('+delta+'%)';
	}
}

	////END GRID///
});<commit_msg>add logic to match with latest checkbox logic from customized screen.<commit_after>angular.module('app').controller('previewReportCtrl', function($scope,$rootScope) {
	// console.log($scope.eventdoc);

//	$scope.previewedGrid = [];
	//var checked = $scope.customizedDoc.checkedColumns;
	$scope.numberOfColumns = getCheckedColCounts();
	var columnArry = [];
   // pick a grid to iterate
   	var oneGrid =  $scope.eventData.gridData[0];
   	if (oneGrid) {  // at least one grid exist
       for (var columnName in oneGrid.dailyData[0]) {
          if (oneGrid.dailyData[0].hasOwnProperty(columnName)) {
            if (columnName !== '$$hashKey' && columnName != 'label')  {
                columnArry.push(columnName);
            }
          }
       }
       columnArry.sort();

    }
//	var gridData = $scope.eventData.gridData;
//	var combinedGrid = JSON.parse(JSON.stringify($rootScope.combinedGrid));

	//checked = ['1435861626006','1436463022438']
	//var gridData = $scope.eventData.gridData;
	//var combinedGrid = JSON.parse(JSON.stringify($rootScope.combinedGrid));

	$scope.customizedData = JSON.parse(JSON.stringify($scope.eventData));
	//console.log('combinedGrid',combinedGrid);
	// if (checked.length > 0) {
	// 	for(var j = 0; j < combinedGrid.length; j++) {
	// 		for(key in combinedGrid[j]) {
	// 			//console.log('Key:',key);
	// 			if(key === 'label') {
	// 				//do nothing
	// 			} else if (checked.indexOf(key) == -1) {
	// 				delete combinedGrid[j][key];
	// 			}
	// 		}
	// 	}
	// }

	// console.log(combinedGrid);
	//$scope.previewedGrid = combinedGrid;

	/////GRID//////

		// var previewHeaderCellTemplate =
	 //  '
'+
	 //  '

'+
	 //  '
'+
	 //  '  {{ col.displayName CUSTOM_FILTERS }}'+
	 //  '
'+
	 //  '

'+
	 //  '
'+
	 //  '  <input type="text" ng-model="colFilter.term" ng-click="$event.stopPropagation()" ng-attr-placeholder="{{colFilter.placeholder || \'\'}}" class="ui-grid-filter-input"/>'+
	 //  '
 '+
	 //  '    '+
	 //  '
'+
	 //  '
'+
	 //  '
'

 // $scope.checkGridColumn = function(column,checked) {
 // 	var toDelete = '';

 // 	if(checked) {
 // 		$rootScope.checkedColumns.push(column);
 // 	} else {
 // 		if($rootScopecheckedColumns.length > 0) {
 // 			toDelete = $scope.checkedColumns.indexOf(column);
 // 			if(toDelete != -1) {
 // 				$rootScope.checkedColumns.splice(toDelete, 1);
 // 			}
 // 		}
 // 	}
 // };

 // $scope.getPreviewTableHeight = function(grid,id) {
 //       var rowHeight = 30; // your row height
 //       var headerHeight = 30; // your header height
 //       if (id.split('_')[1] ==='0') {
 //          return {
 //              height: (($scope.previewedGrid.length+1) * rowHeight + headerHeight-12) + "px" };
 //       }
 //       else {
 //       return {
 //          height: ($scope.previewedGrid.length * rowHeight + headerHeight-14) + "px" };
 //       }
 //    };

// $scope.previewGenerateColumnDefs= function() {
//    var columnArry = [];
//    var columnLayout = [];
//    // pick a grid to iterate
//    var oneGrid =  $scope.previewedGrid;
// 		if (oneGrid) { // at least one grid exist
// 			for (var  i=0; i<  oneGrid.length; i++) {

// 				for (var columnName in oneGrid[i]) {

// 					if (oneGrid[i].hasOwnProperty(columnName)) {

// 						if (columnName !== '$$hashKey' && columnName != 'label') {
// 							if (columnArry.indexOf(columnName) == -1) {
// 								columnArry.push(columnName);
// 							}
// 						}
// 					}
// 				}

// 			}

// 		}
//     else {
//        //  $scope.addDataColumn('label');
//        // // columnArry.push('label');
//        //  $scope.addDataColumn(''+$scope.eventdoc.dateCreated)
//        //  columnArry.push(''+$scope.eventdoc.dateCreated);
//     }
//        columnArry.sort();
//        columnArry.unshift('label');
//        for(i=0; i< columnArry.length; i++) {
//       // build columns defition object
//          if (columnArry[i] === 'label') {
//             oneColumnDef = {'field': columnArry[i], 'displayName':$scope.eventData.colDisplayNames[columnArry[i]] , enableSorting:false, minWidth: $scope.minTopicWidth,pinnedLeft:true,enableColumnMenu:false};
//           }
//          else {
//             //var formattedDate = $filter('date')(columnArry[i],'mediumDate');
//             oneColumnDef = {'field': columnArry[i], 'displayName' : $scope.eventData.colDisplayNames[columnArry[i]], enableSorting:false, minWidth:$scope.minColWidth, enablePinning:false, enableColumnMenu:false,headerCellTemplate: previewHeaderCellTemplate
//           };
//          }
//             columnLayout.push(oneColumnDef);
//        }
//        //console.log(columnLayout)
//        return columnLayout;

// };

//$scope.previewColumns = $scope.previewGenerateColumnDefs();

$scope.filterSelected = function(items) {
    var result = {};
    angular.forEach(items, function(value,key) {
        //console.log(key,' ',value)
        if (key != 'label' && ($scope.customizedDoc.checkedColumns.hasOwnProperty(key))) {
            if ($scope.customizedDoc.checkedColumns[key].checked) {
         	   result[key] = value;
        	}
        }
    });
    return result;
}

$scope.percentChanged = function(row,col) {
	var curIdx = columnArry.indexOf(col);
    var delta = 0;
    if ( curIdx > -1) { // col exist in array
 		if (curIdx  === 0) { // check if this is the first element
 		   // no percent change calculation
 		}
 		else {
 			var previousValue =  row[columnArry[curIdx - 1]];
 			var currentValue = row[col];
 			delta = Math.floor((currentValue - previousValue) / currentValue *100);
 		}
	}
	if (delta > 0) {
		return '(+'+delta+'%)';
	}
	else if(delta ==0) {
		return '(nc%)';
	}
	else {
		return '('+delta+'%)';
	}
}

function getCheckedColCounts() {
	var count = 0;
	for(col in $scope.customizedDoc.checkedColumns) {
		if ($scope.customizedDoc.checkedColumns[col].checked) {
			count++
		}
	}
 	return count
}
	////END GRID///
});<|endoftext|>


<commit_before>/**
 * @module Math
 * @submodule Linear Congruential Generator
 * @for p5
 * @requires core
 */

'use strict';

var p5 = require('../core/main');

// Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
// m is basically chosen to be large (as it is the max period)
// and for its relationships to a and c
var m = 4294967296;
// a - 1 should be divisible by m's prime factors
var a = 1664525;
// c and m should be co-prime
var c = 1013904223;

// Linear Congruential Generator that stores its state at instance[stateProperty]
p5.prototype._lcg = function(stateProperty) {
  // define the recurrence relationship
  this[stateProperty] = (a * this[stateProperty] + c) % m;
  // return a float in [0, 1)
  // we've just used % m, so / m is always < 1
  return this[stateProperty] / m;
};

p5.prototype._lcgSetSeed = function(stateProperty, val) {
  // pick a random seed if val is undefined or null
  // the >>> 0 casts the seed to an unsigned 32-bit integer
  this[stateProperty] = (val == null ? Math.random() * m : val) >>> 0;
};
<commit_msg>removing erroneously committed file<commit_after><|endoftext|>