<commit_before>import React from 'react';

import Notification from '../Notification/Notification';
import {Toolbar, ToolbarGroup} from 'material-ui/Toolbar';
import Badge from 'material-ui/Badge';

export default class RightMenuComponent extends React.Component {
  static propTypes = {
    // notification
    data: React.PropTypes.array,
    open: React.PropTypes.bool,
    setOpen: React.PropTypes.func.isRequired,
    setCountBadges: React.PropTypes.func.isRequired
  };
  state = {
    count: this.props.data.length
  };
  componentWillReceiveProps(nextProps) {
    this.setState({count: nextProps.data.length});
  }
  render() {
    const viewBadge = this.state.count > 0 ? 'visible' : 'hidden';
    return (
      <Toolbar style={{background: 'transparent', marginTop: '-5px'}}>
        <ToolbarGroup>
          <Badge
            badgeContent={this.state.count}
            secondary
            badgeStyle={{top: '25px', right: '25px', visibility: viewBadge}}
            style={{marginBottom: '14px'}}
          >
            <Notification setOpen={this.props.setOpen}
                          data={this.props.data}
                          open={this.props.open}
            />
          </Badge>
        </ToolbarGroup>
      </Toolbar>
    );
  }
}
<commit_msg>sort notification<commit_after>import React from 'react';

import Notification from '../Notification/Notification';
import {Toolbar, ToolbarGroup} from 'material-ui/Toolbar';
import Badge from 'material-ui/Badge';

import moment from 'moment';
import 'moment/locale/ru';

export default class RightMenuComponent extends React.Component {
  static propTypes = {
    // notification
    data: React.PropTypes.array,
    open: React.PropTypes.bool,
    setOpen: React.PropTypes.func.isRequired,
    setCountBadges: React.PropTypes.func.isRequired
  };
  state = {
    count: this.props.data.length
  };
  componentWillReceiveProps(nextProps) {
    this.setState({count: nextProps.data.length});
  }
  render() {
    const viewBadge = this.state.count > 0 ? 'visible' : 'hidden';
    return (
      <Toolbar style={{background: 'transparent', marginTop: '-5px'}}>
        <ToolbarGroup>
          <Badge
            badgeContent={this.state.count}
            secondary
            badgeStyle={{top: '25px', right: '25px', visibility: viewBadge}}
            style={{marginBottom: '14px'}}
          >
            <Notification setOpen={this.props.setOpen}
                          data={this.props.data.sort((item1, item2) => {
                            if (moment(item1.datetime).fromNow() > moment(item2.datetime).fromNow()) return -1;
                            if (moment(item1.datetime).fromNow() < moment(item2.datetime).fromNow()) return 1;
                            return 0;
                          })}
                          open={this.props.open}
            />
          </Badge>
        </ToolbarGroup>
      </Toolbar>
    );
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";

const fs = require("fs-extra");
const program = require("commander");
const pkg = require("../package.json");
const S3rver = require("..");

program.version(pkg.version, "--version");
program
  .option(
    "-h, --hostname [value]",
    "Set the host name or IP to bind to",
    "localhost"
  )
  .option("-p, --port <n>", "Set the port of the http server", 4568)
  .option("-s, --silent", "Suppress log messages", false)
  .option("-i, --indexDocument [path]", "Index Document for Static Web Hosting")
  .option(
    "-e, --errorDocument [path]",
    "Custom Error Document for Static Web Hosting"
  )
  .option("-d, --directory [path]", "Data directory")
  .option("-c, --cors [path]", "Path to S3 CORS configuration XML file")
  .option("--key [path]", "Path to private key file for running with TLS")
  .option("--cert [path]", "Path to certificate file for running with TLS")
  .parse(process.argv);

if (program.directory === undefined) {
  // eslint-disable-next-line no-console
  console.error("Data directory is required");
  process.exit();
}

try {
  const stats = fs.lstatSync(program.directory);
  if (stats.isDirectory() === false) {
    throw Error();
  }
} catch (e) {
  // eslint-disable-next-line no-console
  console.error(
    "Directory does not exist. Please create it and then run the command again"
  );
  process.exit();
}

if (program.cors) {
  program.cors = fs.readFileSync(program.cors);
}

if (program.key && program.cert) {
  program.key = fs.readFileSync(program.key);
  program.cert = fs.readFileSync(program.cert);
}

new S3rver(program).run(function(err, host, port) {
  if (err) {
    // eslint-disable-next-line no-console
    console.error(err);
  } else {
    // eslint-disable-next-line no-console
    console.log("now listening on host %s and port %d", host, port);
  }
});
<commit_msg>CLI: Exit with non-zero status with misconfigured data directory<commit_after>#!/usr/bin/env node
"use strict";

const fs = require("fs-extra");
const program = require("commander");
const pkg = require("../package.json");
const S3rver = require("..");

program.version(pkg.version, "--version");
program
  .option(
    "-h, --hostname [value]",
    "Set the host name or IP to bind to",
    "localhost"
  )
  .option("-p, --port <n>", "Set the port of the http server", 4568)
  .option("-s, --silent", "Suppress log messages", false)
  .option("-i, --indexDocument [path]", "Index Document for Static Web Hosting")
  .option(
    "-e, --errorDocument [path]",
    "Custom Error Document for Static Web Hosting"
  )
  .option("-d, --directory [path]", "Data directory")
  .option("-c, --cors [path]", "Path to S3 CORS configuration XML file")
  .option("--key [path]", "Path to private key file for running with TLS")
  .option("--cert [path]", "Path to certificate file for running with TLS")
  .parse(process.argv);

if (program.directory === undefined) {
  // eslint-disable-next-line no-console
  console.error("Data directory -d is required");
  process.exit(64);
}

try {
  const stats = fs.lstatSync(program.directory);
  if (!stats.isDirectory()) {
    throw new Error();
  }
} catch (e) {
  // eslint-disable-next-line no-console
  console.error(
    "Directory does not exist. Please create it and then run the command again"
  );
  process.exit(1);
}

if (program.cors) {
  program.cors = fs.readFileSync(program.cors);
}

if (program.key && program.cert) {
  program.key = fs.readFileSync(program.key);
  program.cert = fs.readFileSync(program.cert);
}

new S3rver(program).run((err, host, port) => {
  if (err) {
    // eslint-disable-next-line no-console
    console.error(err);
    process.exit(1);
  }
  // eslint-disable-next-line no-console
  console.log("now listening on host %s and port %d", host, port);
});
<|endoftext|>


<commit_before>hqDefine('cloudcare/js/util', function () {
    if (!String.prototype.startsWith) {
        String.prototype.startsWith = function (searchString, position) {
            position = position || 0;
            return this.indexOf(searchString, position) === position;
        };
    }

    NProgress.configure({
        showSpinner: false,
    });

    var getFormUrl = function (urlRoot, appId, moduleId, formId, instanceId) {
        // TODO: make this cleaner
        var url = urlRoot + "view/" + appId + "/modules-" + moduleId + "/forms-" + formId + "/context/";
        if (instanceId) {
            url += '?instance_id=' + instanceId;
        }
        return url;
    };

    var getSubmitUrl = function (urlRoot, appId) {
        // deprecated but still called from "touchforms-inline"
        // which is used to fill out forms from within case details view
        // use app.getSubmitUrl instead
        // todo: replace and remove
        return urlRoot + "/" + appId + "/";
    };

    var showError = function (message, $el) {
        if (message === undefined) {
            message = gettext("Sorry, an error occurred while processing that request.");
        }
        _show(message, $el, null, "alert alert-danger");
        reportFormplayerErrorToHQ({
            type: 'show_error_notification',
            message: message,
        });

    };

    var showWarning = function (message, $el) {
        if (message === undefined) {
            return;
        }
        _show(message, $el, 5000, "alert alert-warning");
    };

    var showHTMLError = function (message, $el, autoHideTime) {
        message = message || gettext("Sorry, an error occurred while processing that request.");
        _show(message, $el, autoHideTime, "", true);
        reportFormplayerErrorToHQ({
            type: 'show_error_notification',
            message: message,
        });
    };

    var showSuccess = function (message, $el, autoHideTime, isHTML) {
        if (message === undefined) {
            message = "Success";
        }
        return _show(message, $el, autoHideTime, "alert alert-success", isHTML);
    };

    var _show = function (message, $el, autoHideTime, classes, isHTML) {
        var $container = $("<div />"),
            $alertDialog;
        $container.addClass(classes);
        if (isHTML) {
            $container.html(message);
        } else {
            $container.text(message);
        }
        // HTML errors may already have an alert dialog
        $alertDialog = $container.hasClass("alert") ? $container : $container.find('.alert');
        $alertDialog
            .prepend(
                $("<a />")
                    .addClass("close")
                    .attr("data-dismiss", "alert")
                    .html("×")
            );
        $el.append($container);
        if (autoHideTime) {
            $container.delay(autoHideTime).fadeOut(500);
        }
        return $container;
    };

    var showLoading = function (selector) {
        NProgress.start();
    };

    var formplayerLoading = function (selector) {
        showLoading();
    };

    var formplayerLoadingComplete = function (isError, message) {
        hideLoading();
        if (isError) {
            showError(message || gettext('Error saving!'), $('#cloudcare-notifications'));
        }
    };

    var formplayerSyncComplete = function (isError) {
        hideLoading();
        if (isError) {
            showError(
                gettext('Could not sync user data. Please report an issue if this persists.'),
                $('#cloudcare-notifications')
            );
        } else {
            showSuccess(gettext('User Data successfully synced.'), $('#cloudcare-notifications'), 5000);
        }
    };

    var clearUserDataComplete = function (isError) {
        hideLoading();
        if (isError) {
            showError(
                gettext('Could not clear user data. Please report an issue if this persists.'),
                $('#cloudcare-notifications')
            );
        } else {
            showSuccess(gettext('User data successfully cleared.'), $('#cloudcare-notifications'), 5000);
        }
    };

    var hideLoading = function (selector) {
        NProgress.done();
    };

    var reportFormplayerErrorToHQ = function (data) {
        try {
            var reverse = hqImport("hqwebapp/js/initial_page_data").reverse;

            $.ajax({
                type: 'POST',
                url: reverse('report_formplayer_error'),
                data: JSON.stringify(data),
                contentType: "application/json",
                dataType: "json",
                success: function () {
                    console.info('Successfully reported error: ' + JSON.stringify(data));
                },
                error: function () {
                    console.error('Failed to report error: ' + JSON.stringify(data));
                },
            });
        } catch (e) {
            console.error(
                "reportFormplayerErrorToHQ failed hard and there is nowhere " +
                "else to report this error: " + JSON.stringify(data),
                e,
            );
        }
    };

    return {
        getFormUrl: getFormUrl,
        getSubmitUrl: getSubmitUrl,
        showError: showError,
        showWarning: showWarning,
        showHTMLError: showHTMLError,
        showSuccess: showSuccess,
        clearUserDataComplete: clearUserDataComplete,
        formplayerLoading: formplayerLoading,
        formplayerLoadingComplete: formplayerLoadingComplete,
        formplayerSyncComplete: formplayerSyncComplete,
        reportFormplayerErrorToHQ: reportFormplayerErrorToHQ,
    };
});
<commit_msg>Appease stickler with console => window.console<commit_after>hqDefine('cloudcare/js/util', function () {
    if (!String.prototype.startsWith) {
        String.prototype.startsWith = function (searchString, position) {
            position = position || 0;
            return this.indexOf(searchString, position) === position;
        };
    }

    NProgress.configure({
        showSpinner: false,
    });

    var getFormUrl = function (urlRoot, appId, moduleId, formId, instanceId) {
        // TODO: make this cleaner
        var url = urlRoot + "view/" + appId + "/modules-" + moduleId + "/forms-" + formId + "/context/";
        if (instanceId) {
            url += '?instance_id=' + instanceId;
        }
        return url;
    };

    var getSubmitUrl = function (urlRoot, appId) {
        // deprecated but still called from "touchforms-inline"
        // which is used to fill out forms from within case details view
        // use app.getSubmitUrl instead
        // todo: replace and remove
        return urlRoot + "/" + appId + "/";
    };

    var showError = function (message, $el) {
        if (message === undefined) {
            message = gettext("Sorry, an error occurred while processing that request.");
        }
        _show(message, $el, null, "alert alert-danger");
        reportFormplayerErrorToHQ({
            type: 'show_error_notification',
            message: message,
        });

    };

    var showWarning = function (message, $el) {
        if (message === undefined) {
            return;
        }
        _show(message, $el, 5000, "alert alert-warning");
    };

    var showHTMLError = function (message, $el, autoHideTime) {
        message = message || gettext("Sorry, an error occurred while processing that request.");
        _show(message, $el, autoHideTime, "", true);
        reportFormplayerErrorToHQ({
            type: 'show_error_notification',
            message: message,
        });
    };

    var showSuccess = function (message, $el, autoHideTime, isHTML) {
        if (message === undefined) {
            message = "Success";
        }
        return _show(message, $el, autoHideTime, "alert alert-success", isHTML);
    };

    var _show = function (message, $el, autoHideTime, classes, isHTML) {
        var $container = $("<div />"),
            $alertDialog;
        $container.addClass(classes);
        if (isHTML) {
            $container.html(message);
        } else {
            $container.text(message);
        }
        // HTML errors may already have an alert dialog
        $alertDialog = $container.hasClass("alert") ? $container : $container.find('.alert');
        $alertDialog
            .prepend(
                $("<a />")
                    .addClass("close")
                    .attr("data-dismiss", "alert")
                    .html("×")
            );
        $el.append($container);
        if (autoHideTime) {
            $container.delay(autoHideTime).fadeOut(500);
        }
        return $container;
    };

    var showLoading = function (selector) {
        NProgress.start();
    };

    var formplayerLoading = function (selector) {
        showLoading();
    };

    var formplayerLoadingComplete = function (isError, message) {
        hideLoading();
        if (isError) {
            showError(message || gettext('Error saving!'), $('#cloudcare-notifications'));
        }
    };

    var formplayerSyncComplete = function (isError) {
        hideLoading();
        if (isError) {
            showError(
                gettext('Could not sync user data. Please report an issue if this persists.'),
                $('#cloudcare-notifications')
            );
        } else {
            showSuccess(gettext('User Data successfully synced.'), $('#cloudcare-notifications'), 5000);
        }
    };

    var clearUserDataComplete = function (isError) {
        hideLoading();
        if (isError) {
            showError(
                gettext('Could not clear user data. Please report an issue if this persists.'),
                $('#cloudcare-notifications')
            );
        } else {
            showSuccess(gettext('User data successfully cleared.'), $('#cloudcare-notifications'), 5000);
        }
    };

    var hideLoading = function (selector) {
        NProgress.done();
    };

    var reportFormplayerErrorToHQ = function (data) {
        try {
            var reverse = hqImport("hqwebapp/js/initial_page_data").reverse;

            $.ajax({
                type: 'POST',
                url: reverse('report_formplayer_error'),
                data: JSON.stringify(data),
                contentType: "application/json",
                dataType: "json",
                success: function () {
                    window.console.info('Successfully reported error: ' + JSON.stringify(data));
                },
                error: function () {
                    window.console.error('Failed to report error: ' + JSON.stringify(data));
                },
            });
        } catch (e) {
            window.console.error(
                "reportFormplayerErrorToHQ failed hard and there is nowhere " +
                "else to report this error: " + JSON.stringify(data),
                e,
            );
        }
    };

    return {
        getFormUrl: getFormUrl,
        getSubmitUrl: getSubmitUrl,
        showError: showError,
        showWarning: showWarning,
        showHTMLError: showHTMLError,
        showSuccess: showSuccess,
        clearUserDataComplete: clearUserDataComplete,
        formplayerLoading: formplayerLoading,
        formplayerLoadingComplete: formplayerLoadingComplete,
        formplayerSyncComplete: formplayerSyncComplete,
        reportFormplayerErrorToHQ: reportFormplayerErrorToHQ,
    };
});
<|endoftext|>


<commit_before>/**
 *
 * This module provides ECharts 3.0 based visualization components.
 *
 * @module dex/charts/echarts
 *
 */
var echarts = {};

echarts.EChart = require("./EChart");
echarts.LineChart = require("./LineChart");
echarts.Timeline = require("./Timeline");
echarts.Network = require("./Network");

module.exports = echarts;<commit_msg>New charts<commit_after>/**
 *
 * This module provides ECharts 3.0 based visualization components.
 *
 * @module dex/charts/echarts
 *
 */
var echarts = {};

echarts.EChart = require("./EChart");
echarts.LineChart = require("./LineChart");
echarts.PolarPlot = require("./PolarPlot");
echarts.Timeline = require("./Timeline");
echarts.Network = require("./Network");
echarts.SingleAxisScatterPlot = require("./SingleAxisScatterPlot");
echarts.PieChart = require("./PieChart");

module.exports = echarts;<|endoftext|>


<commit_before>/**
 * @author Ayman Habib
 */
THREE.SkinnedMuscle = function(geom, points) {
    // Create bones for uuids in geometryPath
    this.pathpoints = points;
    geom.bones = [];
    for (var i=0; i<points.length; i++) {
        var bone = new THREE.Bone();
        bone.pos = [0, 0, 0];
        bone.rotq = [0, 0, 0, 1];
        bone.ppt = this.pathpoints[i];
        geom.bones.push(bone);
    }
    var numVerticesPerLevel = geom.vertices.length / points.length;
    for ( var i = 0; i < geom.vertices.length; i ++ ) {
        var skinIndex = Math.floor(i / numVerticesPerLevel);
        geom.skinIndices.push(new THREE.Vector4(skinIndex, 0, 0, 0));
        geom.skinWeights.push( new THREE.Vector4( 1, 0, 0, 0 ) );
    }
    geom.dynamic = true;
    THREE.SkinnedMesh.call( this, geom );
    this.material.skinning = true;
};
THREE.SkinnedMuscle.prototype = Object.create( THREE.SkinnedMesh.prototype );
THREE.SkinnedMuscle.prototype.constructor = THREE.SkinnedMuscle;
THREE.SkinnedMuscle.prototype.updateMatrixWorld = function( force ) {
// if has pathpoints attribute then it's a muscle
// Cycle through pathpoints, update their matrixworld
// then set the position of the Bones from that
    if (this.skeleton === undefined)
        return;
    var bones = this.skeleton.bones;
    for ( var b=0; b < this.pathpoints.length; b++) {
        var ppt = this.pathpoints[b];
        var pptObject = editor.objectByUuid(ppt);
        if (pptObject !== undefined) {
            this.children[b].position.setFromMatrixPosition(pptObject.matrixWorld);

            //bones[b].pos.setFromMatrixPosition(pptObject.matrixWorld);
            //console.warn('bone '+b+' pos ='+bones[b].position.x, bones[b].position.y, bones[b].position.z);
            this.children[b].updateMatrixWorld();
        }
    }
    this.skeleton.update();
    THREE.SkinnedMesh.prototype.updateMatrixWorld.call( this, true );
};
<commit_msg>Cache PathPoint objects to avoid slow lookup in render loop<commit_after>/**
 * @author Ayman Habib
 */
THREE.SkinnedMuscle = function(geom, points) {
    // Create bones for uuids in geometryPath
    this.pathpoints = points;
    this.pathpointObjects = [];
    geom.bones = [];
    for (var i=0; i<points.length; i++) {
        var bone = new THREE.Bone();
        bone.pos = [0, 0, 0];
        bone.rotq = [0, 0, 0, 1];
        bone.ppt = this.pathpoints[i];
        geom.bones.push(bone);
    }
    var numVerticesPerLevel = geom.vertices.length / points.length;
    for ( var i = 0; i < geom.vertices.length; i ++ ) {
        var skinIndex = Math.floor(i / numVerticesPerLevel);
        geom.skinIndices.push(new THREE.Vector4(skinIndex, 0, 0, 0));
        geom.skinWeights.push( new THREE.Vector4( 1, 0, 0, 0 ) );
    }
    geom.dynamic = true;
    THREE.SkinnedMesh.call( this, geom );
    this.material.skinning = true;
};
THREE.SkinnedMuscle.prototype = Object.create( THREE.SkinnedMesh.prototype );
THREE.SkinnedMuscle.prototype.constructor = THREE.SkinnedMuscle;
THREE.SkinnedMuscle.prototype.updateMatrixWorld = function( force ) {
// if has pathpoints attribute then it's a muscle
// Cycle through pathpoints, update their matrixworld
// then set the position of the Bones from that
    if (this.skeleton === undefined)
        return;
    var bones = this.skeleton.bones;
    if (this.pathpointObjects.length != this.pathpoints.length){
        for ( var b=0; b < this.pathpoints.length; b++) {
            var ppt = this.pathpoints[b];
            var pptObject = editor.objectByUuid(ppt);
            if (pptObject !== undefined) {
                this.pathpointObjects.push(pptObject);
            }
        }
    }
    for ( var b=0; b < this.pathpoints.length; b++) {
        var nextPathpointObject = this.pathpointObjects[b];
        if (nextPathpointObject !== undefined) {
            this.children[b].position.setFromMatrixPosition(nextPathpointObject.matrixWorld);

            //bones[b].pos.setFromMatrixPosition(pptObject.matrixWorld);
            //console.warn('bone '+b+' pos ='+bones[b].position.x, bones[b].position.y, bones[b].position.z);
            this.children[b].updateMatrixWorld();
        }
    }
    this.skeleton.update();
    THREE.SkinnedMesh.prototype.updateMatrixWorld.call( this, true );
};
<|endoftext|>


<commit_before>(function() {
    'use strict';

    angular.module('chaise.record', [
        'ngSanitize',
        'ngCookies',
        'chaise.delete',
        'chaise.errors',
        'chaise.modal',
        'chaise.navbar',
        'chaise.record.display',
        'chaise.record.table',
        'chaise.html',
        'chaise.utils',
        'ermrestjs',
        'ui.bootstrap'
    ])

    .factory('constants', [function(){
        return {
            defaultPageSize: 25,
        };
    }])

    .config(['$cookiesProvider', function($cookiesProvider) {
        $cookiesProvider.defaults.path = '/';
    }])

    // Configure all tooltips to be attached to the body by default. To attach a
    // tooltip on the element instead, set the `tooltip-append-to-body` attribute
    // to `false` on the element.
    .config(['$uibTooltipProvider', function($uibTooltipProvider) {
        $uibTooltipProvider.options({appendToBody: true});
    }])

    .run(['constants', 'DataUtils', 'ERMrest', 'ErrorService', 'headInjector', 'MathUtils', 'Session', 'UiUtils', 'UriUtils', '$log', '$rootScope', '$window',
        function runApp(constants, DataUtils, ERMrest, ErrorService, headInjector, MathUtils, Session, UiUtils, UriUtils, $log, $rootScope, $window) {

        var session,
            context = {};
        $rootScope.displayReady = false;

        UriUtils.setOrigin();
        headInjector.setupHead();

        $rootScope.showEmptyRelatedTables = false;
        $rootScope.modifyRecord = chaiseConfig.editRecord === false ? false : true;
        $rootScope.showDeleteButton = chaiseConfig.deleteRecord === true ? true : false;

        var ermrestUri = UriUtils.chaiseURItoErmrestURI($window.location);

        $rootScope.context = context;

        // The context object won't change unless the app is reloaded
        context.appName = "record";
        context.pageId = MathUtils.uuid();

        ERMrest.appLinkFn(UriUtils.appTagToURL);

        // Subscribe to on change event for session
        var subId = Session.subscribeOnChange(function() {

            // Unsubscribe onchange event to avoid this function getting called again
            Session.unsubscribeOnChange(subId);

            ERMrest.resolve(ermrestUri, {cid: context.appName, pid: context.pageId, wid: $window.name}).then(function getReference(reference) {
                DataUtils.verify(reference.location.filter, 'No filter was defined. Cannot find a record without a filter.');

                // if the user can fetch the reference, they can see the content for the rest of the page
                // set loading to force the loading text to appear and to prevent the on focus from firing while code is initializing
                session = Session.getSessionValue();

                // $rootScope.reference != reference after contextualization
                $rootScope.reference = reference.contextualize.detailed;
                $rootScope.reference.session = session;

                $log.info("Reference: ", $rootScope.reference);

                // There should only ever be one entity related to this reference
                return $rootScope.reference.read(2);
            }, function error(exception) {
                throw exception;
            }).then(function getPage(page) {
                $log.info("Page: ", page);

                if (page.tuples.length < 1) {
                    var noDataError = ErrorService.noRecordError(context.filter.filters);
                    throw noDataError;
                }
                else if(page.tuples.length > 1){
                    console.log("Multiple Data re baba");
                }

                var tuple = $rootScope.tuple = page.tuples[0];
                // Used directly in the record-display directive
                $rootScope.recordDisplayname = tuple.displayname;

                // related references
                $rootScope.relatedReferences = $rootScope.reference.related(tuple);

                $rootScope.loading = $rootScope.relatedReferences.length > 0;

                // Collate tuple.isHTML and tuple.values into an array of objects
                // i.e. {isHTML: false, value: 'sample'}
                $rootScope.recordValues = [];
                tuple.values.forEach(function(value, index) {
                    $rootScope.recordValues.push({
                        isHTML: tuple.isHTML[index],
                        value: value
                    });
                });

                $rootScope.columns = $rootScope.reference.columns;

                $rootScope.tableModels = [];
                $rootScope.lastRendered = null;

                for (var i = 0; i < $rootScope.relatedReferences.length; i++) {
                    $rootScope.relatedReferences[i] = $rootScope.relatedReferences[i].contextualize.compactBrief;

                    var pageSize;
                    if ($rootScope.relatedReferences[i].display.defaultPageSize) {
                        pageSize = $rootScope.relatedReferences[i].display.defaultPageSize;
                    } else {
                        pageSize = constants.defaultPageSize;
                    }

                    (function(i) {
                        if ($rootScope.relatedReferences[i].canCreate && $rootScope.modifyRecord && !$rootScope.showEmptyRelatedTables) {
                            $rootScope.showEmptyRelatedTables = true;
                        }
                        $rootScope.relatedReferences[i].read(pageSize).then(function (page) {
                            var model = {
                                reference: $rootScope.relatedReferences[i],
                                columns: $rootScope.relatedReferences[i].columns,
                                page: page,
                                pageLimit: ($rootScope.relatedReferences[i].display.defaultPageSize ? $rootScope.relatedReferences[i].display.defaultPageSize : constants.defaultPageSize),
                                hasNext: page.hasNext,      // used to determine if a link should be shown
                                hasLoaded: true,            // used to determine if the current table and next table should be rendered
                                open: true,                 // to define if the accordion is open or closed
                                enableSort: true,           // allow sorting on table
                                sortby: null,               // column name, user selected or null
                                sortOrder: null,            // asc (default) or desc
                                rowValues: [],              // array of rows values
                                search: null,                // search term
                                displayType: $rootScope.relatedReferences[i].display.type,
                                context: "compact/brief",
                                fromTuple: $rootScope.tuple
                            };
                            model.rowValues = DataUtils.getRowValuesFromPage(page);
                            model.config = {
                                viewable: true,
                                editable: $rootScope.modifyRecord,
                                deletable: $rootScope.modifyRecord && $rootScope.showDeleteButton,
                                selectable: false
                            };
                            $rootScope.tableModels[i] = model;
                            $rootScope.displayReady = true;

                        }, function readFail(error) {
                            var model = {
                                hasLoaded: true
                            };
                            $rootScope.tableModels[i] = model;
                            $rootScope.displayReady = true;
                            throw error;
                        }).catch(function(e) {
                            // The .catch from the outer promise won't catch errors from this closure
                            // so a .catch needs to be appended here.
                            throw e;
                        });
                    })(i);
                }
            }).catch(function genericCatch(exception) {
                throw exception;
            });
        })

        /**
         * it saves the location in $rootScope.location.
         * When address bar is changed, this code compares the address bar location
         * with the last save recordset location. If it's the same, the change of url was
         * done internally, do not refresh page. If not, the change was done manually
         * outside recordset, refresh page.
         *
         */
        UriUtils.setLocationChangeHandling();

        // This is to allow the dropdown button to open at the top/bottom depending on the space available
        UiUtils.setBootstrapDropdownButtonBehavior();
    }]);
})();
<commit_msg>Changes to handle cases when more than 1 records are fetched<commit_after>(function() {
    'use strict';

    angular.module('chaise.record', [
        'ngSanitize',
        'ngCookies',
        'chaise.delete',
        'chaise.errors',
        'chaise.modal',
        'chaise.navbar',
        'chaise.record.display',
        'chaise.record.table',
        'chaise.html',
        'chaise.utils',
        'ermrestjs',
        'ui.bootstrap'
    ])

    .factory('constants', [function(){
        return {
            defaultPageSize: 25,
        };
    }])

    .config(['$cookiesProvider', function($cookiesProvider) {
        $cookiesProvider.defaults.path = '/';
    }])

    // Configure all tooltips to be attached to the body by default. To attach a
    // tooltip on the element instead, set the `tooltip-append-to-body` attribute
    // to `false` on the element.
    .config(['$uibTooltipProvider', function($uibTooltipProvider) {
        $uibTooltipProvider.options({appendToBody: true});
    }])

    .run(['constants', 'DataUtils', 'ERMrest', 'ErrorService', 'headInjector', 'MathUtils', 'Session', 'UiUtils', 'UriUtils', '$log', '$rootScope', '$window',
        function runApp(constants, DataUtils, ERMrest, ErrorService, headInjector, MathUtils, Session, UiUtils, UriUtils, $log, $rootScope, $window) {

        var session,
            context = {};
        $rootScope.displayReady = false;

        UriUtils.setOrigin();
        headInjector.setupHead();

        $rootScope.showEmptyRelatedTables = false;
        $rootScope.modifyRecord = chaiseConfig.editRecord === false ? false : true;
        $rootScope.showDeleteButton = chaiseConfig.deleteRecord === true ? true : false;

        var ermrestUri = UriUtils.chaiseURItoErmrestURI($window.location);

        $rootScope.context = context;

        // The context object won't change unless the app is reloaded
        context.appName = "record";
        context.pageId = MathUtils.uuid();

        ERMrest.appLinkFn(UriUtils.appTagToURL);

        // Subscribe to on change event for session
        var subId = Session.subscribeOnChange(function() {

            // Unsubscribe onchange event to avoid this function getting called again
            Session.unsubscribeOnChange(subId);

            ERMrest.resolve(ermrestUri, {cid: context.appName, pid: context.pageId, wid: $window.name}).then(function getReference(reference) {
                DataUtils.verify(reference.location.filter, 'No filter was defined. Cannot find a record without a filter.');

                // if the user can fetch the reference, they can see the content for the rest of the page
                // set loading to force the loading text to appear and to prevent the on focus from firing while code is initializing
                session = Session.getSessionValue();

                // $rootScope.reference != reference after contextualization
                $rootScope.reference = reference.contextualize.detailed;
                $rootScope.reference.session = session;

                $log.info("Reference: ", $rootScope.reference);

                // There should only ever be one entity related to this reference
                return $rootScope.reference.read(2);
            }, function error(exception) {
                throw exception;
            }).then(function getPage(page) {
                $log.info("Page: ", page);

                if (page.tuples.length < 1) {
                    var noDataError = ErrorService.noRecordError(context.filter.filters);
                    throw noDataError;
                }
                else if(page.tuples.length > 1){
                    var multipleRecordError = ErrorService.multipleRecordError();
                    throw multipleRecordError;
                }

                var tuple = $rootScope.tuple = page.tuples[0];
                // Used directly in the record-display directive
                $rootScope.recordDisplayname = tuple.displayname;

                // related references
                $rootScope.relatedReferences = $rootScope.reference.related(tuple);

                $rootScope.loading = $rootScope.relatedReferences.length > 0;

                // Collate tuple.isHTML and tuple.values into an array of objects
                // i.e. {isHTML: false, value: 'sample'}
                $rootScope.recordValues = [];
                tuple.values.forEach(function(value, index) {
                    $rootScope.recordValues.push({
                        isHTML: tuple.isHTML[index],
                        value: value
                    });
                });

                $rootScope.columns = $rootScope.reference.columns;

                $rootScope.tableModels = [];
                $rootScope.lastRendered = null;

                for (var i = 0; i < $rootScope.relatedReferences.length; i++) {
                    $rootScope.relatedReferences[i] = $rootScope.relatedReferences[i].contextualize.compactBrief;

                    var pageSize;
                    if ($rootScope.relatedReferences[i].display.defaultPageSize) {
                        pageSize = $rootScope.relatedReferences[i].display.defaultPageSize;
                    } else {
                        pageSize = constants.defaultPageSize;
                    }

                    (function(i) {
                        if ($rootScope.relatedReferences[i].canCreate && $rootScope.modifyRecord && !$rootScope.showEmptyRelatedTables) {
                            $rootScope.showEmptyRelatedTables = true;
                        }
                        $rootScope.relatedReferences[i].read(pageSize).then(function (page) {
                            var model = {
                                reference: $rootScope.relatedReferences[i],
                                columns: $rootScope.relatedReferences[i].columns,
                                page: page,
                                pageLimit: ($rootScope.relatedReferences[i].display.defaultPageSize ? $rootScope.relatedReferences[i].display.defaultPageSize : constants.defaultPageSize),
                                hasNext: page.hasNext,      // used to determine if a link should be shown
                                hasLoaded: true,            // used to determine if the current table and next table should be rendered
                                open: true,                 // to define if the accordion is open or closed
                                enableSort: true,           // allow sorting on table
                                sortby: null,               // column name, user selected or null
                                sortOrder: null,            // asc (default) or desc
                                rowValues: [],              // array of rows values
                                search: null,                // search term
                                displayType: $rootScope.relatedReferences[i].display.type,
                                context: "compact/brief",
                                fromTuple: $rootScope.tuple
                            };
                            model.rowValues = DataUtils.getRowValuesFromPage(page);
                            model.config = {
                                viewable: true,
                                editable: $rootScope.modifyRecord,
                                deletable: $rootScope.modifyRecord && $rootScope.showDeleteButton,
                                selectable: false
                            };
                            $rootScope.tableModels[i] = model;
                            $rootScope.displayReady = true;

                        }, function readFail(error) {
                            var model = {
                                hasLoaded: true
                            };
                            $rootScope.tableModels[i] = model;
                            $rootScope.displayReady = true;
                            throw error;
                        }).catch(function(e) {
                            // The .catch from the outer promise won't catch errors from this closure
                            // so a .catch needs to be appended here.
                            throw e;
                        });
                    })(i);
                }
            }).catch(function genericCatch(exception) {
                throw exception;
            });
        })

        /**
         * it saves the location in $rootScope.location.
         * When address bar is changed, this code compares the address bar location
         * with the last save recordset location. If it's the same, the change of url was
         * done internally, do not refresh page. If not, the change was done manually
         * outside recordset, refresh page.
         *
         */
        UriUtils.setLocationChangeHandling();

        // This is to allow the dropdown button to open at the top/bottom depending on the space available
        UiUtils.setBootstrapDropdownButtonBehavior();
    }]);
})();
<|endoftext|>


<commit_before>const calendarGraph = document.querySelector('.js-calendar-graph')
const contributionsBox = document.querySelector('.js-yearly-contributions')
const observedContainer = document.getElementById('js-contribution-activity')

const COLORS = [
  new obelisk.CubeColor().getByHorizontalColor(0xebedf0),
  new obelisk.CubeColor().getByHorizontalColor(0x9be9a8),
  new obelisk.CubeColor().getByHorizontalColor(0x40c463),
  new obelisk.CubeColor().getByHorizontalColor(0x30a14e),
  new obelisk.CubeColor().getByHorizontalColor(0x216e39)
]

const dateOptions         = {month: "short", day: "numeric"}
const dateWithYearOptions = {month: "short", day: "numeric", year: "numeric"}

let yearTotal           = 0
let averageCount        = 0
let maxCount            = 0
let countTotal          = 0
let streakLongest       = 0
let streakCurrent       = 0
let bestDay             = null
let firstDay            = null
let lastDay             = null
let datesTotal          = null
let datesLongest        = null
let datesCurrent        = null
let dateBest            = null

let toggleSetting = "cubes"

const resetValues = () => {
  yearTotal           = 0
  averageCount        = 0
  maxCount            = 0
  bestDay             = null
  firstDay            = null
  lastDay             = null
}

const getSettings = () => {
  return new Promise(function(resolve, reject) {
    // Check for user preference, if chrome.storage is available.
    // The storage API is not supported in content scripts.
    // https://developer.mozilla.org/Add-ons/WebExtensions/Chrome_incompatibilities#storage
    if (chrome && chrome.storage) {
      chrome.storage.local.get(["toggleSetting"], (settings) => {
        toggleSetting = settings.toggleSetting ? settings.toggleSetting : "cubes"
        resolve('Settings loaded')
      })
    }
    else {
      toggleSetting = localStorage.toggleSetting ? localStorage.toggleSetting : "cubes"
      resolve('Settings loaded')
    }
  })
}

const persistSetting = (key, value) => {
  if (chrome && chrome.storage) {
    let obj = {}
    obj[key] = value
    chrome.storage.local.set(obj)
  }
  else {
    localStorage[key] = value
  }
}

const initUI = () => {
  const contributionsWrapper = document.createElement("div")
  contributionsWrapper.className = "ic-contributions-wrapper position-relative"
  calendarGraph.before(contributionsWrapper)

  const canvas = document.createElement("canvas")
  canvas.id = "isometric-contributions"
  canvas.width = 1000
  canvas.height = 600
  canvas.style.width = "100%"
  contributionsWrapper.appendChild(canvas)

  // Inject toggle
  const insertLocation = contributionsBox.querySelector("h2").parentElement

  const btnGroup = document.createElement("div")
  btnGroup.className = "BtnGroup mt-1 ml-3 position-relative top-0 float-right"

  const squaresButton = document.createElement("button")
  squaresButton.innerHTML = "2D"
  squaresButton.className = "ic-toggle-option squares btn BtnGroup-item btn-sm py-0 px-1"
  squaresButton.setAttribute("data-ic-option", "squares")
  squaresButton.addEventListener("click", handleViewToggle);
  if (toggleSetting === "squares") {
    squaresButton.classList.add("selected")
  }

  const cubesButton = document.createElement("button")
  cubesButton.innerHTML = "3D"
  cubesButton.className = "ic-toggle-option cubes btn BtnGroup-item btn-sm py-0 px-1"
  cubesButton.setAttribute("data-ic-option", "cubes")
  cubesButton.addEventListener("click", handleViewToggle);
  if (toggleSetting === "cubes") {
    cubesButton.classList.add("selected")
  }

  insertLocation.prepend(btnGroup)
  btnGroup.appendChild(squaresButton)
  btnGroup.appendChild(cubesButton)

  setContainerViewType(toggleSetting)
}

const handleViewToggle = (e) => {
  setContainerViewType(e.target.dataset.icOption)

  document.querySelectorAll(".ic-toggle-option").forEach(toggle => { toggle.classList.remove("selected") })
  e.target.classList.add("selected")

  persistSetting("toggleSetting", e.target.dataset.icOption)
  toggleSetting = e.target.dataset.icOption

  // Apply user preference
  document.querySelector(`.ic-toggle-option.${toggleSetting}`).classList.add("selected")
  contributionsBox.classList.add(`ic-${toggleSetting}`)
}

const setContainerViewType = (type) => {
  if (type === "squares") {
    contributionsBox.classList.remove("ic-cubes")
    contributionsBox.classList.add("ic-squares")
  }
  else {
    contributionsBox.classList.remove("ic-squares")
    contributionsBox.classList.add("ic-cubes")
  }
}

const loadStats = () => {
  let tempStreak         = 0
  let tempStreakStart    = null
  let longestStreakStart = null
  let longestStreakEnd   = null
  let currentStreakStart = null
  let currentStreakEnd   = null

  let days = document.querySelectorAll(".js-calendar-graph rect.day")
  days.forEach(d => {
    currentDayCount = parseInt(d.dataset.count)
    yearTotal += parseInt(currentDayCount)

    if (days[0] === d) {
      firstDay = d.dataset.date
    }
    if (days[days.length - 1] === d) {
      lastDay = d.dataset.date
    }

    // Check for best day
    if (currentDayCount > maxCount) {
      bestDay = d.dataset.date
      maxCount = currentDayCount
    }

    // Check for longest streak
    if (currentDayCount > 0) {
      if (tempStreak == 0) {
        tempStreakStart = d.dataset.date
      }

      tempStreak++

      if (tempStreak >= streakLongest) {
        longestStreakStart = tempStreakStart
        longestStreakEnd   = d.dataset.date
        streakLongest      = tempStreak
      }
    }
    else {
      tempStreak         = 0
      tempStreakStart    = null
      tempStreakEnd      = null
    }
  })

  // Check for current streak
  // Convert days NodeList to Array so we can reverse it
  let daysArray = Array.prototype.slice.call(days);
  daysArray.reverse()

  currentStreakEnd = daysArray[0].dataset.date

  for (let i=0; i < daysArray.length; i++) {

    currentDayCount = parseInt(daysArray[i].dataset.count, 10)
    // If there's no activity today, continue on to yesterday
    if (i === 0 && currentDayCount === 0) {
      currentStreakEnd = daysArray[1].dataset.date
      continue
    }

    if (currentDayCount > 0) {
      streakCurrent++
      currentStreakStart = daysArray[i].dataset.date
    }
    else {
      break
    }
  }

  if (streakCurrent > 0) {
    currentStreakStart = formatDateString(currentStreakStart, dateOptions)
    currentStreakEnd   = formatDateString(currentStreakEnd, dateOptions)
    datesCurrent       = `${currentStreakStart} — ${currentStreakEnd}`
  }
  else {
    datesCurrent = "No current streak"
  }

  // Year total
  countTotal = yearTotal.toLocaleString()
  let dateFirst  = formatDateString(firstDay, dateOptions)
  let dateLast   = formatDateString(lastDay, dateOptions)
  datesTotal = `${dateFirst} — ${dateLast}`

  // Average contributions per day
  let dayDifference = datesDayDifference(firstDay, lastDay)
  let averageCount = precisionRound((yearTotal / dayDifference), 2)

  // Best day
  dateBest  = formatDateString(bestDay, dateOptions)
  if (!dateBest) {
    dateBest = 'No activity found'
  }

  // Longest streak
  if (streakLongest > 0) {
    longestStreakStart = formatDateString(longestStreakStart, dateOptions)
    longestStreakEnd   = formatDateString(longestStreakEnd, dateOptions)
    datesLongest       = `${longestStreakStart} — ${longestStreakEnd}`
  }
  else {
    datesLongest = "No longest streak"
  }
}

const getSquareColor = (fill) => {
  switch (fill.toLowerCase()) {
    case '#ebedf0':
      return COLORS[0]
    case '#c6e48b':
      return COLORS[1]
    case '#7bc96f':
      return COLORS[2]
    case '#239a3b':
      return COLORS[3]
    case '#196127':
      return COLORS[4]
    default:
      if (fill.indexOf('#') != -1)
        return new obelisk.CubeColor().getByHorizontalColor(parseInt('0x'+fill.replace("#", "")))
  }
}

const renderIsometricChart = () => {
  const SIZE = 16
  const MAX_HEIGHT = 100
  const firstRect = document.querySelectorAll('.js-calendar-graph-svg g > g')[1]
  const canvas = document.getElementById('isometric-contributions')
  const GH_OFFSET = parseInt(firstRect.getAttribute('transform').match(/(\d+)/)[0]) - 1

  let point= new obelisk.Point(130,90)
  let pixelView = new obelisk.PixelView(canvas, point)
  let contribCount = null
  let weeks = document.querySelectorAll(".js-calendar-graph-svg g > g")

  weeks.forEach(w => {
    let x = parseInt(((w.getAttribute('transform')).match(/(\d+)/))[0]) / (GH_OFFSET + 1)
    w.querySelectorAll('rect').forEach (r => {
      let y = parseInt(r.getAttribute('y')) / GH_OFFSET
      let fill = r.getAttribute('fill')
      let contribCount = parseInt(r.dataset.count)
      let cubeHeight = 3

      if (maxCount > 0) {
        cubeHeight += parseInt(MAX_HEIGHT / maxCount * contribCount)
      }

      let dimension = new obelisk.CubeDimension(SIZE, SIZE, cubeHeight)
      let color = getSquareColor(fill)
      let cube = new obelisk.Cube(dimension, color, false)
      let p3d = new obelisk.Point3D(SIZE * x, SIZE * y, 0)
      pixelView.renderObject(cube, p3d)
    })
  })
}

const renderStats = () => {
  const topMarkup = `

Contributions

${countTotal}

          Total
          ${datesTotal}

${maxCount}

          Best day
          ${dateBest}

  `

  const bottomMarkup = `

Streaks

${streakLongest} days

          Longest
          ${datesLongest}

${streakCurrent} days

          Best day
          ${datesCurrent}

  `
  const icStatsBlockTop = document.createElement("div")
  icStatsBlockTop.innerHTML = topMarkup
  document.querySelector('.ic-contributions-wrapper').appendChild(icStatsBlockTop)

  const icStatsBlockBottom = document.createElement("div")
  icStatsBlockBottom.innerHTML = bottomMarkup
  document.querySelector('.ic-contributions-wrapper').appendChild(icStatsBlockBottom)
}

const generateIsometricChart = () => {
  resetValues()
  initUI()
  loadStats()
  renderStats()
  renderIsometricChart()
}

const precisionRound = (number, precision) => {
  let factor = Math.pow(10, precision)
  return Math.round(number * factor) / factor
}

const datesDayDifference = (dateStr1, dateStr2) => {
  let diffDays = null
  let date1 = null
  let date2 = null

  if (dateStr1) {
    dateParts = dateStr1.split('-')
    date1 = new Date(dateParts[0], dateParts[1] - 1, dateParts[2], 0, 0, 0)
  }
  if (dateStr2) {
    dateParts = dateStr2.split('-')
    date2 = new Date(dateParts[0], dateParts[1] - 1, dateParts[2], 0, 0, 0)
  }
  if (dateStr1 && dateStr2) {
    timeDiff = Math.abs(date2.getTime() - date1.getTime())
    diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24))
  }

  return diffDays
}

const formatDateString = (dateStr, options) => {
  let date = null

  if (dateStr) {
    let dateParts = dateStr.split('-')
    date = new Date(dateParts[0], dateParts[1] - 1, dateParts[2], 0, 0, 0).toLocaleDateString('en-US', options)
  }

  return date
}

if (calendarGraph) {
  const graphContainer = calendarGraph.parentElement

  if (graphContainer) {
    // Watch for changes to the activity overview section
    let config = { attributes: false, childList: true, subtree: true }
    observer = new MutationObserver(renderIsometricChart)
    observer.observe(observedContainer, config)
  }

  let settingsPromise = getSettings()
  settingsPromise.then(generateIsometricChart)
}
<commit_msg>Fix average count<commit_after>const calendarGraph = document.querySelector('.js-calendar-graph')
const contributionsBox = document.querySelector('.js-yearly-contributions')
const observedContainer = document.getElementById('js-contribution-activity')

const COLORS = [
  new obelisk.CubeColor().getByHorizontalColor(0xebedf0),
  new obelisk.CubeColor().getByHorizontalColor(0x9be9a8),
  new obelisk.CubeColor().getByHorizontalColor(0x40c463),
  new obelisk.CubeColor().getByHorizontalColor(0x30a14e),
  new obelisk.CubeColor().getByHorizontalColor(0x216e39)
]

const dateOptions         = {month: "short", day: "numeric"}
const dateWithYearOptions = {month: "short", day: "numeric", year: "numeric"}

let yearTotal           = 0
let averageCount        = 0
let maxCount            = 0
let countTotal          = 0
let streakLongest       = 0
let streakCurrent       = 0
let bestDay             = null
let firstDay            = null
let lastDay             = null
let datesTotal          = null
let datesLongest        = null
let datesCurrent        = null
let dateBest            = null

let toggleSetting = "cubes"

const resetValues = () => {
  yearTotal           = 0
  averageCount        = 0
  maxCount            = 0
  bestDay             = null
  firstDay            = null
  lastDay             = null
}

const getSettings = () => {
  return new Promise(function(resolve, reject) {
    // Check for user preference, if chrome.storage is available.
    // The storage API is not supported in content scripts.
    // https://developer.mozilla.org/Add-ons/WebExtensions/Chrome_incompatibilities#storage
    if (chrome && chrome.storage) {
      chrome.storage.local.get(["toggleSetting"], (settings) => {
        toggleSetting = settings.toggleSetting ? settings.toggleSetting : "cubes"
        resolve('Settings loaded')
      })
    }
    else {
      toggleSetting = localStorage.toggleSetting ? localStorage.toggleSetting : "cubes"
      resolve('Settings loaded')
    }
  })
}

const persistSetting = (key, value) => {
  if (chrome && chrome.storage) {
    let obj = {}
    obj[key] = value
    chrome.storage.local.set(obj)
  }
  else {
    localStorage[key] = value
  }
}

const initUI = () => {
  const contributionsWrapper = document.createElement("div")
  contributionsWrapper.className = "ic-contributions-wrapper position-relative"
  calendarGraph.before(contributionsWrapper)

  const canvas = document.createElement("canvas")
  canvas.id = "isometric-contributions"
  canvas.width = 1000
  canvas.height = 600
  canvas.style.width = "100%"
  contributionsWrapper.appendChild(canvas)

  // Inject toggle
  const insertLocation = contributionsBox.querySelector("h2").parentElement

  const btnGroup = document.createElement("div")
  btnGroup.className = "BtnGroup mt-1 ml-3 position-relative top-0 float-right"

  const squaresButton = document.createElement("button")
  squaresButton.innerHTML = "2D"
  squaresButton.className = "ic-toggle-option squares btn BtnGroup-item btn-sm py-0 px-1"
  squaresButton.setAttribute("data-ic-option", "squares")
  squaresButton.addEventListener("click", handleViewToggle);
  if (toggleSetting === "squares") {
    squaresButton.classList.add("selected")
  }

  const cubesButton = document.createElement("button")
  cubesButton.innerHTML = "3D"
  cubesButton.className = "ic-toggle-option cubes btn BtnGroup-item btn-sm py-0 px-1"
  cubesButton.setAttribute("data-ic-option", "cubes")
  cubesButton.addEventListener("click", handleViewToggle);
  if (toggleSetting === "cubes") {
    cubesButton.classList.add("selected")
  }

  insertLocation.prepend(btnGroup)
  btnGroup.appendChild(squaresButton)
  btnGroup.appendChild(cubesButton)

  setContainerViewType(toggleSetting)
}

const handleViewToggle = (e) => {
  setContainerViewType(e.target.dataset.icOption)

  document.querySelectorAll(".ic-toggle-option").forEach(toggle => { toggle.classList.remove("selected") })
  e.target.classList.add("selected")

  persistSetting("toggleSetting", e.target.dataset.icOption)
  toggleSetting = e.target.dataset.icOption

  // Apply user preference
  document.querySelector(`.ic-toggle-option.${toggleSetting}`).classList.add("selected")
  contributionsBox.classList.add(`ic-${toggleSetting}`)
}

const setContainerViewType = (type) => {
  if (type === "squares") {
    contributionsBox.classList.remove("ic-cubes")
    contributionsBox.classList.add("ic-squares")
  }
  else {
    contributionsBox.classList.remove("ic-squares")
    contributionsBox.classList.add("ic-cubes")
  }
}

const loadStats = () => {
  let tempStreak         = 0
  let tempStreakStart    = null
  let longestStreakStart = null
  let longestStreakEnd   = null
  let currentStreakStart = null
  let currentStreakEnd   = null

  let days = document.querySelectorAll(".js-calendar-graph rect.day")
  days.forEach(d => {
    currentDayCount = parseInt(d.dataset.count)
    yearTotal += parseInt(currentDayCount)

    if (days[0] === d) {
      firstDay = d.dataset.date
    }
    if (days[days.length - 1] === d) {
      lastDay = d.dataset.date
    }

    // Check for best day
    if (currentDayCount > maxCount) {
      bestDay = d.dataset.date
      maxCount = currentDayCount
    }

    // Check for longest streak
    if (currentDayCount > 0) {
      if (tempStreak == 0) {
        tempStreakStart = d.dataset.date
      }

      tempStreak++

      if (tempStreak >= streakLongest) {
        longestStreakStart = tempStreakStart
        longestStreakEnd   = d.dataset.date
        streakLongest      = tempStreak
      }
    }
    else {
      tempStreak         = 0
      tempStreakStart    = null
      tempStreakEnd      = null
    }
  })

  // Check for current streak
  // Convert days NodeList to Array so we can reverse it
  let daysArray = Array.prototype.slice.call(days);
  daysArray.reverse()

  currentStreakEnd = daysArray[0].dataset.date

  for (let i=0; i < daysArray.length; i++) {

    currentDayCount = parseInt(daysArray[i].dataset.count, 10)
    // If there's no activity today, continue on to yesterday
    if (i === 0 && currentDayCount === 0) {
      currentStreakEnd = daysArray[1].dataset.date
      continue
    }

    if (currentDayCount > 0) {
      streakCurrent++
      currentStreakStart = daysArray[i].dataset.date
    }
    else {
      break
    }
  }

  if (streakCurrent > 0) {
    currentStreakStart = formatDateString(currentStreakStart, dateOptions)
    currentStreakEnd   = formatDateString(currentStreakEnd, dateOptions)
    datesCurrent       = `${currentStreakStart} → ${currentStreakEnd}`
  }
  else {
    datesCurrent = "No current streak"
  }

  // Year total
  countTotal = yearTotal.toLocaleString()
  let dateFirst  = formatDateString(firstDay, dateOptions)
  let dateLast   = formatDateString(lastDay, dateOptions)
  datesTotal = `${dateFirst} → ${dateLast}`

  // Average contributions per day
  let dayDifference = datesDayDifference(firstDay, lastDay)
  averageCount = precisionRound((yearTotal / dayDifference), 2)

  // Best day
  dateBest  = formatDateString(bestDay, dateOptions)
  if (!dateBest) {
    dateBest = 'No activity found'
  }

  // Longest streak
  if (streakLongest > 0) {
    longestStreakStart = formatDateString(longestStreakStart, dateOptions)
    longestStreakEnd   = formatDateString(longestStreakEnd, dateOptions)
    datesLongest       = `${longestStreakStart} → ${longestStreakEnd}`
  }
  else {
    datesLongest = "No longest streak"
  }
}

const getSquareColor = (fill) => {
  switch (fill.toLowerCase()) {
    case '#ebedf0':
      return COLORS[0]
    case '#c6e48b':
      return COLORS[1]
    case '#7bc96f':
      return COLORS[2]
    case '#239a3b':
      return COLORS[3]
    case '#196127':
      return COLORS[4]
    default:
      if (fill.indexOf('#') != -1)
        return new obelisk.CubeColor().getByHorizontalColor(parseInt('0x'+fill.replace("#", "")))
  }
}

const renderIsometricChart = () => {
  const SIZE = 16
  const MAX_HEIGHT = 100
  const firstRect = document.querySelectorAll('.js-calendar-graph-svg g > g')[1]
  const canvas = document.getElementById('isometric-contributions')
  const GH_OFFSET = parseInt(firstRect.getAttribute('transform').match(/(\d+)/)[0]) - 1

  let point= new obelisk.Point(130,90)
  let pixelView = new obelisk.PixelView(canvas, point)
  let contribCount = null
  let weeks = document.querySelectorAll(".js-calendar-graph-svg g > g")

  weeks.forEach(w => {
    let x = parseInt(((w.getAttribute('transform')).match(/(\d+)/))[0]) / (GH_OFFSET + 1)
    w.querySelectorAll('rect').forEach (r => {
      let y = parseInt(r.getAttribute('y')) / GH_OFFSET
      let fill = r.getAttribute('fill')
      let contribCount = parseInt(r.dataset.count)
      let cubeHeight = 3

      if (maxCount > 0) {
        cubeHeight += parseInt(MAX_HEIGHT / maxCount * contribCount)
      }

      let dimension = new obelisk.CubeDimension(SIZE, SIZE, cubeHeight)
      let color = getSquareColor(fill)
      let cube = new obelisk.Cube(dimension, color, false)
      let p3d = new obelisk.Point3D(SIZE * x, SIZE * y, 0)
      pixelView.renderObject(cube, p3d)
    })
  })
}

const renderStats = () => {
  const topMarkup = `

Contributions

${countTotal}

          Total
          ${datesTotal}

${maxCount}

          Best day
          ${dateBest}

        Average: ${averageCount} / day

  `

  const bottomMarkup = `

Streaks

${streakLongest} days

          Longest
          ${datesLongest}

${streakCurrent} days

          Best day
          ${datesCurrent}

  `
  const icStatsBlockTop = document.createElement("div")
  icStatsBlockTop.innerHTML = topMarkup
  document.querySelector('.ic-contributions-wrapper').appendChild(icStatsBlockTop)

  const icStatsBlockBottom = document.createElement("div")
  icStatsBlockBottom.innerHTML = bottomMarkup
  document.querySelector('.ic-contributions-wrapper').appendChild(icStatsBlockBottom)
}

const generateIsometricChart = () => {
  resetValues()
  initUI()
  loadStats()
  renderStats()
  renderIsometricChart()
}

const precisionRound = (number, precision) => {
  let factor = Math.pow(10, precision)
  return Math.round(number * factor) / factor
}

const datesDayDifference = (dateStr1, dateStr2) => {
  let diffDays = null
  let date1 = null
  let date2 = null

  if (dateStr1) {
    dateParts = dateStr1.split('-')
    date1 = new Date(dateParts[0], dateParts[1] - 1, dateParts[2], 0, 0, 0)
  }
  if (dateStr2) {
    dateParts = dateStr2.split('-')
    date2 = new Date(dateParts[0], dateParts[1] - 1, dateParts[2], 0, 0, 0)
  }
  if (dateStr1 && dateStr2) {
    timeDiff = Math.abs(date2.getTime() - date1.getTime())
    diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24))
  }

  return diffDays
}

const formatDateString = (dateStr, options) => {
  let date = null

  if (dateStr) {
    let dateParts = dateStr.split('-')
    date = new Date(dateParts[0], dateParts[1] - 1, dateParts[2], 0, 0, 0).toLocaleDateString('en-US', options)
  }

  return date
}

if (calendarGraph) {
  const graphContainer = calendarGraph.parentElement

  if (graphContainer) {
    // Watch for changes to the activity overview section
    let config = { attributes: false, childList: true, subtree: true }
    observer = new MutationObserver(renderIsometricChart)
    observer.observe(observedContainer, config)
  }

  let settingsPromise = getSettings()
  settingsPromise.then(generateIsometricChart)
}
<|endoftext|>


<commit_before>var default_colors = ['#fdbb84','#fee8c8','#e34a33', '#3182bd', '#000000', '#bdbdbd'];
// [orange, light orange, RED, blue, black, gray]

function drawCytoscape (div_id, model_elements) {
    cy = cytoscape({
      container: document.getElementById(div_id),

      elements: model_elements,

      style: [
        {
          selector: 'node',
          style: {
            'label': 'data(name)',
            'width': '200px',
            'height': '200px',
            'border-width': 7,
            'border-color': default_colors[4],
            'background-color':default_colors[5],
            'background-opacity': 1,
            'font-size': '40px',
            'text-halign': 'above',
            'text-valign': 'center',
            'z-index': 2,
            'color': '#FFFFFF',
            'text-outline-color': '#000000',
            'text-outline-width': 5,
            'font-weight': 700,
            'text-wrap': 'wrap',
            'text-max-width': '200px'
          }
        },

        {
          selector: ':parent',
          style: {
            'label': '',
            'background-color': default_colors[1],
            'background-opacity': 1,
            'z-index': 1
          }
        },

        {
          selector: 'edge',
          style: {
          'line-color': default_colors[4],
          'target-arrow-color': default_colors[4],
          'width':13,
          'target-arrow-shape': 'triangle',
          'control-point-step-size': '140px',
          'z-index': 0,
          'curve-style':'bezier'
          },

        },

        {
          selector: '.complex',
          style: {
          'line-color': default_colors[3],
          'target-arrow-color': default_colors[3],
          'source-arrow-color': default_colors[3],
          //'width': '6px',
          'target-arrow-shape': 'circle',
          'source-arrow-shape': 'circle',
          'control-point-step-size': '140px',
          'z-index': 0
        }},

        {  selector: '.negative',
          style: {
          'line-color': default_colors[2],
          'target-arrow-color': default_colors[2],
          'source-arrow-color': default_colors[2],
          //'width': '6px',
          'target-arrow-shape': 'tee',
          'source-arrow-shape': 'none',
          'control-point-step-size': '140px',
          'z-index': 0
        }},

          {  selector: '.Attractor',
          style: {
            'display': 'none',
            //'visibility':'hidden',
            'z-index': 0,
            'curve-style':'bezier'

          }},

          {  selector: '.virtual',
          style: {
            'display': 'none',
            //'visibility':'hidden',
            'z-index': 0,
            'curve-style':'bezier'

          }},

          {  selector: '.transparentNode',
          style: {
            'opacity':0.7,
            'border-opacity': 0.15,
            'text-outline-opacity': 0.0,
            'color': default_colors[4],
            'text-opacity': 1
          }},

          {  selector: '.transparentEdge',
          style: {
            'opacity':0.3,

          }},

          {  selector: '.nAttractor',
          style: {
            'label': null,
            'width': '1px',
            'height': '1px',
            'padding-left': '1px',
            'padding-right': '1px',
            'display': 'none',
            'z-index': 0

          }},

          {  selector: '.hasMembers',
          style: {
            'width': '200px',
            'height': '200px',
            'content': 'data(name)',
            'pie-size': '100%',
            'border-width': 7,
            'border-color': default_colors[4],
            'background-color':default_colors[4],
            'pie-1-background-size':function(node){
              return node.data().pie_sizes[0];},
            'pie-2-background-size':function(node){
              return node.data().pie_sizes[1];},
            'pie-3-background-size':function(node){
              return node.data().pie_sizes[2];},
            'pie-4-background-size':function(node){
              return node.data().pie_sizes[3];},
            'pie-5-background-size':function(node){
              return node.data().pie_sizes[4];},
            'pie-6-background-size':function(node){
              return node.data().pie_sizes[5];},
            'pie-7-background-size':function(node){
              return node.data().pie_sizes[6];},
            'pie-8-background-size':function(node){
              return node.data().pie_sizes[7];},
            'pie-9-background-size':function(node){
              return node.data().pie_sizes[8];},
            'pie-10-background-size':function(node){
              return node.data().pie_sizes[9];},
            'pie-11-background-size':function(node){
              return node.data().pie_sizes[10];},
            'pie-12-background-size':function(node){
              return node.data().pie_sizes[11];},
            'pie-13-background-size':function(node){
              return node.data().pie_sizes[12];},
            'pie-14-background-size':function(node){
              return node.data().pie_sizes[13];},
            'pie-15-background-size':function(node){
              return node.data().pie_sizes[14];},
            'pie-16-background-size':function(node){
              return node.data().pie_sizes[15];},
            // slice colors according to expression bin
            'pie-1-background-color': function(node){
              return node.data().pie_colors[0];},
            'pie-2-background-color': function(node){
              return node.data().pie_colors[1];},
            'pie-3-background-color': function(node){
              return node.data().pie_colors[2];},
            'pie-4-background-color': function(node){
              return node.data().pie_colors[3];},
            'pie-5-background-color': function(node){
              return node.data().pie_colors[4];},
            'pie-6-background-color': function(node){
              return node.data().pie_colors[5];},
            'pie-7-background-color': function(node){
              return node.data().pie_colors[6];},
            'pie-8-background-color': function(node){
              return node.data().pie_colors[7];},
            'pie-9-background-color': function(node){
              return node.data().pie_colors[8];},
            'pie-10-background-color': function(node){
              return node.data().pie_colors[9];},
            'pie-11-background-color': function(node){
              return node.data().pie_colors[10];},
            'pie-12-background-color': function(node){
              return node.data().pie_colors[11];},
            'pie-13-background-color': function(node){
              return node.data().pie_colors[12];},
            'pie-14-background-color': function(node){
              return node.data().pie_colors[13];},
            'pie-15-background-color': function(node){
              return node.data().pie_colors[14];},
            'pie-16-background-color': function(node){
              return node.data().pie_colors[15];},
          }}
      ],

    });
    cy.startBatch();
    var params = {
      name: 'dagre',
      directed: 'true',
      fit: 'true',
      rankDir: 'TB',
      padding: 0,
    };
    // if a prior model has been built, use its positions for layout
    if (Object.keys(preset_pos).length !== 0) {
      cy.nodes().forEach(function(n){
        id_pos[n.id()] = preset_pos[n.data().name];
        // if a node is added and not in preset_pos, set its avg position based
        // on the other nodes it's connected to
        if (id_pos[n.id()] === undefined) {
          var cedges = n.connectedEdges();
        	var cnode_ids = [];
        	cedges.forEach(function(e){
        		if (e.target().id() !== n.id()){
        			cnode_ids.push(e.target().id());
        		}
        		if (e.source().id() !== n.id()){
        			cnode_ids.push(e.source().id());
        		}
        	});
        	var posxs = [];
        	var posys = [];
        	cnode_ids.forEach(function(i) {
        		if (id_pos[i] !== undefined){
        			posxs.push(id_pos[i]['x']);
        			posys.push(id_pos[i]['y']);
        		}
        	});
          // update preset_pos
        	preset_pos[n.data().name] = getAvgPos(posxs, posys);
          // update id_pos
          id_pos[n.id()] = preset_pos[n.data().name];
      }
    });
      params = {
        name: 'preset',
        positions: id_pos, // map of (node id) => (position obj); or function(node){ return somPos; }
        zoom: undefined, // the zoom level to set (prob want fit = false if set)
        fit: true, // whether to fit to viewport
        padding: 30, // padding on fit
        animate: false, // whether to transition the node positions
        animationDuration: 500, // duration of animation in ms if enabled
        animationEasing: undefined, // easing of animation if enabled
        ready: undefined, // callback on layoutready
        stop: undefined, // on layoutstop
      };
    }

    var layout = cy.makeLayout( params );
    layout.run();

    var params = {
      name: 'cola',
      nodeSpacing: 40,
      flow: { axis: 'y', },
      animate: true,
      randomize: false,
      maxSimulationTime: 2000,
      fit: false,
      infinite: false,
      ungrabifyWhileSimulating: false,
      // layout event callbacks
      ready: function(){
        cy.fit(30);
      }, // on layoutready
      stop: undefined, // on layoutstop
    };
    var layout = cy.makeLayout( params );
    layout.run();

    var params = {
      name: 'cola',
      nodeSpacing: 40,
      flow: { axis: 'y', },
      animate: true,
      randomize: false,
      maxSimulationTime: 2000,
      fit: false,
      infinite: false,
      ungrabifyWhileSimulating: false,
      // layout event callbacks
      ready: function(){
        //cy.fit(30)
      }, // on layoutready
      stop: undefined, // on layoutstop
    };
    var layout = cy.makeLayout( params );
    //layout.run();

    cy.panzoom({
  zoomFactor: 0.05, // zoom factor per zoom tick
  zoomDelay: 45, // how many ms between zoom ticks
  minZoom: 0.1, // min zoom level
  maxZoom: 10, // max zoom level
  fitPadding: 30, // padding when fitting
  panSpeed: 10, // how many ms in between pan ticks
  panDistance: 10, // max pan distance per tick
  panDragAreaSize: 75, // the length of the pan drag box in which the vector for panning is calculated (bigger = finer control of pan speed and direction)
  panMinPercentSpeed: 0.25, // the slowest speed we can pan by (as a percent of panSpeed)
  panInactiveArea: 8, // radius of inactive area in pan drag box
  panIndicatorMinOpacity: 0.5, // min opacity of pan indicator (the draggable nib); scales from this to 1.0
  zoomOnly: false, // a minimal version of the ui only with zooming (useful on systems with bad mousewheel resolution)
  fitSelector: undefined, // selector of elements to fit
  animateOnFit: function(){ // whether to animate on fit
    return false;
  },
  fitAnimationDuration: 1000, // duration of animation on fit

  // icon class names
  sliderHandleIcon: 'fa fa-minus',
  zoomInIcon: 'fa fa-plus',
  zoomOutIcon: 'fa fa-minus',
  resetIcon: 'fa fa-expand'
}
);

    cy.endBatch();

    var dragged = false;
    cy.on(('mousedown'),function(){
      layout.stop();
      cy.nodes().on(('drag'), function(){
        dragged = true;
    });
    });
    cy.on(('mouseup'),function(){
      if (dragged === true){
        layout.run();
        dragged = false;
      }
    });
    cy.on(('touchstart'),function(){
      layout.stop();
      cy.nodes().on(('drag'), function(){
        dragged = true;
      });
    });
    cy.on(('touchend'),function(){
      if (dragged === true){
        layout.run();
        dragged = false;
      }
    });

    cy.on(('layoutstop'),function(){
      nds = (cy.json()).elements.nodes;
      nds.forEach( function(n) {
        preset_pos[n.data.name] = n.position;
    });
    });

    cy.edges().forEach(function(e){
      if (e.data('i') === 'Complex'){
        e.addClass('complex');
      }
      if (e.data('polarity') === 'negative'){
        e.addClass('negative');
      }
      if (e.data('i') === 'Attractor'){
        e.addClass('Attractor');
      }
      if (e.data('i') === 'Virtual'){
        e.addClass('virtual');
      }
    });
    scapes[div_id] = cy;
}

function getAvgPos(posxs, posys) {
    if ((posxs.length > 0) && (posys.length > 0)){
        var x = posxs.reduce(function (p, c) {
            return p + c;
            }) / posxs.length;
        var y = posys.reduce(function (p, c) {
            return p + c;
            }) / posys.length;
        return ({'x': x, 'y' : y});
    }
  }
<commit_msg>modalEdges(cy) called when drawCytoscape called<commit_after>var default_colors = ['#fdbb84','#fee8c8','#e34a33', '#3182bd', '#000000', '#bdbdbd'];
// [orange, light orange, RED, blue, black, gray]

function drawCytoscape (div_id, model_elements) {
    cy = cytoscape({
      container: document.getElementById(div_id),

      elements: model_elements,

      style: [
        {
          selector: 'node',
          style: {
            'label': 'data(name)',
            'width': '200px',
            'height': '200px',
            'border-width': 7,
            'border-color': default_colors[4],
            'background-color':default_colors[5],
            'background-opacity': 1,
            'font-size': '40px',
            'text-halign': 'above',
            'text-valign': 'center',
            'z-index': 2,
            'color': '#FFFFFF',
            'text-outline-color': '#000000',
            'text-outline-width': 5,
            'font-weight': 700,
            'text-wrap': 'wrap',
            'text-max-width': '200px'
          }
        },

        {
          selector: ':parent',
          style: {
            'label': '',
            'background-color': default_colors[1],
            'background-opacity': 1,
            'z-index': 1
          }
        },

        {
          selector: 'edge',
          style: {
          'line-color': default_colors[4],
          'target-arrow-color': default_colors[4],
          'width':13,
          'target-arrow-shape': 'triangle',
          'control-point-step-size': '140px',
          'z-index': 0,
          'curve-style':'bezier'
          },

        },

        {
          selector: '.complex',
          style: {
          'line-color': default_colors[3],
          'target-arrow-color': default_colors[3],
          'source-arrow-color': default_colors[3],
          //'width': '6px',
          'target-arrow-shape': 'circle',
          'source-arrow-shape': 'circle',
          'control-point-step-size': '140px',
          'z-index': 0
        }},

        {  selector: '.negative',
          style: {
          'line-color': default_colors[2],
          'target-arrow-color': default_colors[2],
          'source-arrow-color': default_colors[2],
          //'width': '6px',
          'target-arrow-shape': 'tee',
          'source-arrow-shape': 'none',
          'control-point-step-size': '140px',
          'z-index': 0
        }},

          {  selector: '.Attractor',
          style: {
            'display': 'none',
            //'visibility':'hidden',
            'z-index': 0,
            'curve-style':'bezier'

          }},

          {  selector: '.virtual',
          style: {
            'display': 'none',
            //'visibility':'hidden',
            'z-index': 0,
            'curve-style':'bezier'

          }},

          {  selector: '.transparentNode',
          style: {
            'opacity':0.7,
            'border-opacity': 0.15,
            'text-outline-opacity': 0.0,
            'color': default_colors[4],
            'text-opacity': 1
          }},

          {  selector: '.transparentEdge',
          style: {
            'opacity':0.3,

          }},

          {  selector: '.nAttractor',
          style: {
            'label': null,
            'width': '1px',
            'height': '1px',
            'padding-left': '1px',
            'padding-right': '1px',
            'display': 'none',
            'z-index': 0

          }},

          {  selector: '.hasMembers',
          style: {
            'width': '200px',
            'height': '200px',
            'content': 'data(name)',
            'pie-size': '100%',
            'border-width': 7,
            'border-color': default_colors[4],
            'background-color':default_colors[4],
            'pie-1-background-size':function(node){
              return node.data().pie_sizes[0];},
            'pie-2-background-size':function(node){
              return node.data().pie_sizes[1];},
            'pie-3-background-size':function(node){
              return node.data().pie_sizes[2];},
            'pie-4-background-size':function(node){
              return node.data().pie_sizes[3];},
            'pie-5-background-size':function(node){
              return node.data().pie_sizes[4];},
            'pie-6-background-size':function(node){
              return node.data().pie_sizes[5];},
            'pie-7-background-size':function(node){
              return node.data().pie_sizes[6];},
            'pie-8-background-size':function(node){
              return node.data().pie_sizes[7];},
            'pie-9-background-size':function(node){
              return node.data().pie_sizes[8];},
            'pie-10-background-size':function(node){
              return node.data().pie_sizes[9];},
            'pie-11-background-size':function(node){
              return node.data().pie_sizes[10];},
            'pie-12-background-size':function(node){
              return node.data().pie_sizes[11];},
            'pie-13-background-size':function(node){
              return node.data().pie_sizes[12];},
            'pie-14-background-size':function(node){
              return node.data().pie_sizes[13];},
            'pie-15-background-size':function(node){
              return node.data().pie_sizes[14];},
            'pie-16-background-size':function(node){
              return node.data().pie_sizes[15];},
            // slice colors according to expression bin
            'pie-1-background-color': function(node){
              return node.data().pie_colors[0];},
            'pie-2-background-color': function(node){
              return node.data().pie_colors[1];},
            'pie-3-background-color': function(node){
              return node.data().pie_colors[2];},
            'pie-4-background-color': function(node){
              return node.data().pie_colors[3];},
            'pie-5-background-color': function(node){
              return node.data().pie_colors[4];},
            'pie-6-background-color': function(node){
              return node.data().pie_colors[5];},
            'pie-7-background-color': function(node){
              return node.data().pie_colors[6];},
            'pie-8-background-color': function(node){
              return node.data().pie_colors[7];},
            'pie-9-background-color': function(node){
              return node.data().pie_colors[8];},
            'pie-10-background-color': function(node){
              return node.data().pie_colors[9];},
            'pie-11-background-color': function(node){
              return node.data().pie_colors[10];},
            'pie-12-background-color': function(node){
              return node.data().pie_colors[11];},
            'pie-13-background-color': function(node){
              return node.data().pie_colors[12];},
            'pie-14-background-color': function(node){
              return node.data().pie_colors[13];},
            'pie-15-background-color': function(node){
              return node.data().pie_colors[14];},
            'pie-16-background-color': function(node){
              return node.data().pie_colors[15];},
          }}
      ],

    });
    cy.startBatch();
    var params = {
      name: 'dagre',
      directed: 'true',
      fit: 'true',
      rankDir: 'TB',
      padding: 0,
    };
    // if a prior model has been built, use its positions for layout
    if (Object.keys(preset_pos).length !== 0) {
      cy.nodes().forEach(function(n){
        id_pos[n.id()] = preset_pos[n.data().name];
        // if a node is added and not in preset_pos, set its avg position based
        // on the other nodes it's connected to
        if (id_pos[n.id()] === undefined) {
          var cedges = n.connectedEdges();
        	var cnode_ids = [];
        	cedges.forEach(function(e){
        		if (e.target().id() !== n.id()){
        			cnode_ids.push(e.target().id());
        		}
        		if (e.source().id() !== n.id()){
        			cnode_ids.push(e.source().id());
        		}
        	});
        	var posxs = [];
        	var posys = [];
        	cnode_ids.forEach(function(i) {
        		if (id_pos[i] !== undefined){
        			posxs.push(id_pos[i]['x']);
        			posys.push(id_pos[i]['y']);
        		}
        	});
          // update preset_pos
        	preset_pos[n.data().name] = getAvgPos(posxs, posys);
          // update id_pos
          id_pos[n.id()] = preset_pos[n.data().name];
      }
    });
      params = {
        name: 'preset',
        positions: id_pos, // map of (node id) => (position obj); or function(node){ return somPos; }
        zoom: undefined, // the zoom level to set (prob want fit = false if set)
        fit: true, // whether to fit to viewport
        padding: 30, // padding on fit
        animate: false, // whether to transition the node positions
        animationDuration: 500, // duration of animation in ms if enabled
        animationEasing: undefined, // easing of animation if enabled
        ready: undefined, // callback on layoutready
        stop: undefined, // on layoutstop
      };
    }

    var layout = cy.makeLayout( params );
    layout.run();

    var params = {
      name: 'cola',
      nodeSpacing: 40,
      flow: { axis: 'y', },
      animate: true,
      randomize: false,
      maxSimulationTime: 2000,
      fit: false,
      infinite: false,
      ungrabifyWhileSimulating: false,
      // layout event callbacks
      ready: function(){
        cy.fit(30);
      }, // on layoutready
      stop: undefined, // on layoutstop
    };
    var layout = cy.makeLayout( params );
    layout.run();

    var params = {
      name: 'cola',
      nodeSpacing: 40,
      flow: { axis: 'y', },
      animate: true,
      randomize: false,
      maxSimulationTime: 2000,
      fit: false,
      infinite: false,
      ungrabifyWhileSimulating: false,
      // layout event callbacks
      ready: function(){
        //cy.fit(30)
      }, // on layoutready
      stop: undefined, // on layoutstop
    };
    var layout = cy.makeLayout( params );
    //layout.run();

    cy.panzoom({
  zoomFactor: 0.05, // zoom factor per zoom tick
  zoomDelay: 45, // how many ms between zoom ticks
  minZoom: 0.1, // min zoom level
  maxZoom: 10, // max zoom level
  fitPadding: 30, // padding when fitting
  panSpeed: 10, // how many ms in between pan ticks
  panDistance: 10, // max pan distance per tick
  panDragAreaSize: 75, // the length of the pan drag box in which the vector for panning is calculated (bigger = finer control of pan speed and direction)
  panMinPercentSpeed: 0.25, // the slowest speed we can pan by (as a percent of panSpeed)
  panInactiveArea: 8, // radius of inactive area in pan drag box
  panIndicatorMinOpacity: 0.5, // min opacity of pan indicator (the draggable nib); scales from this to 1.0
  zoomOnly: false, // a minimal version of the ui only with zooming (useful on systems with bad mousewheel resolution)
  fitSelector: undefined, // selector of elements to fit
  animateOnFit: function(){ // whether to animate on fit
    return false;
  },
  fitAnimationDuration: 1000, // duration of animation on fit

  // icon class names
  sliderHandleIcon: 'fa fa-minus',
  zoomInIcon: 'fa fa-plus',
  zoomOutIcon: 'fa fa-minus',
  resetIcon: 'fa fa-expand'
}
);

    cy.endBatch();

    var dragged = false;
    cy.on(('mousedown'),function(){
      layout.stop();
      cy.nodes().on(('drag'), function(){
        dragged = true;
    });
    });
    cy.on(('mouseup'),function(){
      if (dragged === true){
        layout.run();
        dragged = false;
      }
    });
    cy.on(('touchstart'),function(){
      layout.stop();
      cy.nodes().on(('drag'), function(){
        dragged = true;
      });
    });
    cy.on(('touchend'),function(){
      if (dragged === true){
        layout.run();
        dragged = false;
      }
    });

    cy.on(('layoutstop'),function(){
      nds = (cy.json()).elements.nodes;
      nds.forEach( function(n) {
        preset_pos[n.data.name] = n.position;
    });
    });

    cy.edges().forEach(function(e){
      if (e.data('i') === 'Complex'){
        e.addClass('complex');
      }
      if (e.data('polarity') === 'negative'){
        e.addClass('negative');
      }
      if (e.data('i') === 'Attractor'){
        e.addClass('Attractor');
      }
      if (e.data('i') === 'Virtual'){
        e.addClass('virtual');
      }
    });
    scapes[div_id] = cy;

    modalEdges(cy);
}

function getAvgPos(posxs, posys) {
    if ((posxs.length > 0) && (posys.length > 0)){
        var x = posxs.reduce(function (p, c) {
            return p + c;
            }) / posxs.length;
        var y = posys.reduce(function (p, c) {
            return p + c;
            }) / posys.length;
        return ({'x': x, 'y' : y});
    }
  }
<|endoftext|>