<commit_before>import $ from 'jquery';
import _ from 'underscore';

import View from '@girder/core/views/View';
import { SORT_DESC } from '@girder/core/constants';
import eventStream from '@girder/core/utilities/EventStream';
import { restRequest } from '@girder/core/rest';

import JobCollection from '@girder/jobs/collections/JobCollection';
import JobStatus from '@girder/jobs/JobStatus';

import OutputParameterDialog from './OutputParameterDialog';

import jobListWidget from '../templates/jobsListWidget.pug';

// cache parameter file models
const paramFiles = {};

const JobsListWidget = View.extend({
    events: {
        'click .s-param-file': '_clickParamFile'
    },

    initialize() {
        if (!this.collection) {
            this.collection = new JobCollection();

            // We want to display 10 jobs, but we are filtering
            // them on the client, so we fetch extra jobs here.
            // Ideally, we would be able to filter them server side
            // but the /job endpoint doesn't currently have the
            // flexibility to do so.
            this.collection.pageLimit = 50;
            this.collection.sortDir = SORT_DESC;
            this.collection.sortField = 'created';
        }

        this.listenTo(this.collection, 'all', this.render);
        this.listenTo(eventStream, 'g:event.job_status', this.fetchAndRender);
        this.listenTo(eventStream, 'g:event.job_created', this.fetchAndRender);
        this.fetchAndRender();
    },

    render() {
        let jobs = this.collection.filter((job, index) => {
            return (job.get('kwargs') || {}).image && (job.get('kwargs') || {}).container_args;
        });
        if (!jobs.length) {
            jobs = this.collection;
        }
        jobs = jobs.slice(0, 10).map((job) => {
            // make an async request to add output parameter information
            // to the job model
            this._paramFile(job);
            return _.extend({paramFile: paramFiles[job.id]}, job.attributes);
        });
        let root = '/static/built';
        try {
            root = __webpack_public_path__ || root; // eslint-disable-line
        } catch (err) { }
        root = root.replace(/\/$/, '').replace(/\/[^/]+$/, '').replace(/\/[^/]+$/, '');

        this.$el.html(jobListWidget({
            jobs,
            JobStatus,
            rootLink: root
        }));
        this.$('[data-toggle="tooltip"]').tooltip({container: 'body'});
        return this;
    },

    fetchAndRender() {
        this.collection.fetch(null, true)
            .then(() => this.render());
    },

    _paramFile(job) {
        // we already processed this job
        if (_.has(paramFiles, job.id)) {
            return;
        }

        const bindings = job.get('slicerCLIBindings') || {};
        const outputs = bindings.outputs || {};
        const id = outputs.parameters;

        if (id) {
            // avoid processing this job again
            paramFiles[job.id] = {};

            // check if the file still exists
            restRequest({
                url: `file/${id}`,
                error: null
            }).done((file) => {
                paramFiles[job.id] = file;
                this.render();
            });
        }
    },

    _clickParamFile(evt) {
        const fileId = $(evt.currentTarget).data('file-id');
        restRequest({
            url: `file/${fileId}/download`,
            dataType: 'text'
        }).done((parameters) => {
            const view = new OutputParameterDialog({
                parentView: this,
                el: '#g-dialog-container',
                parameters
            });
            view.render();
        });
    }
});

export default JobsListWidget;
<commit_msg>Refresh jobs widget on event stream start.<commit_after>import $ from 'jquery';
import _ from 'underscore';

import View from '@girder/core/views/View';
import { SORT_DESC } from '@girder/core/constants';
import eventStream from '@girder/core/utilities/EventStream';
import { restRequest } from '@girder/core/rest';

import JobCollection from '@girder/jobs/collections/JobCollection';
import JobStatus from '@girder/jobs/JobStatus';

import OutputParameterDialog from './OutputParameterDialog';

import jobListWidget from '../templates/jobsListWidget.pug';

// cache parameter file models
const paramFiles = {};

const JobsListWidget = View.extend({
    events: {
        'click .s-param-file': '_clickParamFile'
    },

    initialize() {
        if (!this.collection) {
            this.collection = new JobCollection();

            // We want to display 10 jobs, but we are filtering
            // them on the client, so we fetch extra jobs here.
            // Ideally, we would be able to filter them server side
            // but the /job endpoint doesn't currently have the
            // flexibility to do so.
            this.collection.pageLimit = 50;
            this.collection.sortDir = SORT_DESC;
            this.collection.sortField = 'created';
        }

        this.listenTo(this.collection, 'all', this.render);
        this.listenTo(eventStream, 'g:event.job_status', this.fetchAndRender);
        this.listenTo(eventStream, 'g:event.job_created', this.fetchAndRender);
        this.listenTo(eventStream, 'g:eventStream.start', this.fetchAndRender);
        this.fetchAndRender();
    },

    render() {
        let jobs = this.collection.filter((job, index) => {
            return (job.get('kwargs') || {}).image && (job.get('kwargs') || {}).container_args;
        });
        if (!jobs.length) {
            jobs = this.collection;
        }
        jobs = jobs.slice(0, 10).map((job) => {
            // make an async request to add output parameter information
            // to the job model
            this._paramFile(job);
            return _.extend({paramFile: paramFiles[job.id]}, job.attributes);
        });
        let root = '/static/built';
        try {
            root = __webpack_public_path__ || root; // eslint-disable-line
        } catch (err) { }
        root = root.replace(/\/$/, '').replace(/\/[^/]+$/, '').replace(/\/[^/]+$/, '');

        this.$el.html(jobListWidget({
            jobs,
            JobStatus,
            rootLink: root
        }));
        this.$('[data-toggle="tooltip"]').tooltip({container: 'body'});
        return this;
    },

    fetchAndRender() {
        this.collection.fetch(null, true)
            .then(() => this.render());
    },

    _paramFile(job) {
        // we already processed this job
        if (_.has(paramFiles, job.id)) {
            return;
        }

        const bindings = job.get('slicerCLIBindings') || {};
        const outputs = bindings.outputs || {};
        const id = outputs.parameters;

        if (id) {
            // avoid processing this job again
            paramFiles[job.id] = {};

            // check if the file still exists
            restRequest({
                url: `file/${id}`,
                error: null
            }).done((file) => {
                paramFiles[job.id] = file;
                this.render();
            });
        }
    },

    _clickParamFile(evt) {
        const fileId = $(evt.currentTarget).data('file-id');
        restRequest({
            url: `file/${fileId}/download`,
            dataType: 'text'
        }).done((parameters) => {
            const view = new OutputParameterDialog({
                parentView: this,
                el: '#g-dialog-container',
                parameters
            });
            view.render();
        });
    }
});

export default JobsListWidget;
<|endoftext|>


<commit_before>(function(d3, fc) {
    'use strict';

    /**
    * This component will generate an RSI data series on
    * a chart based on data generated in the format produced by the dataGenerator component.
    *
    * @type {object}
    * @memberof fc.indicators
    * @namespace fc.indicators.rsi
    */
    fc.indicators.rsi = function() {

        var xScale = d3.time.scale(),
            yScale = d3.scale.linear(),
            samplePeriods = 14,
            upperMarker = 70,
            lowerMarker = 30,
            lambda = 1.0,
            css = 'rsi',
            yValue = fc.utilities.valueAccessor('close');

        var upper = null,
            centre = null,
            lower = null;

        /**
        * Constructs a new instance of the RSI component.
        *
        * @memberof fc.indicators.rsi
        * @param {selection} selection a D3 selection
        */
        var rsi = function(selection) {

            selection.selectAll('.marker').remove();

            upper = selection.append('line')
                .attr('class', 'marker upper')
                .attr('x1', xScale.range()[0])
                .attr('y1', yScale(upperMarker))
                .attr('x2', xScale.range()[1])
                .attr('y2', yScale(upperMarker));

            centre = selection.append('line')
                .attr('class', 'marker centre')
                .attr('x1', xScale.range()[0])
                .attr('y1', yScale(50))
                .attr('x2', xScale.range()[1])
                .attr('y2', yScale(50));

            lower = selection.append('line')
                .attr('class', 'marker lower')
                .attr('x1', xScale.range()[0])
                .attr('y1', yScale(lowerMarker))
                .attr('x2', xScale.range()[1])
                .attr('y2', yScale(lowerMarker));

            var line = d3.svg.line();
            line.x(function(d) { return xScale(d.date); });

            selection.each(function(data) {

                if (samplePeriods === 0) {
                    line.y(function(d) { return yScale(0); });
                } else {
                    line.y(function(d, i) {
                        var from = i - samplePeriods,
                        to = i,
                        up = [],
                        down = [];

                        if (from < 1) {
                            from = 1;
                        }

                        for (var offset = to; offset >= from; offset--) {
                            var dnow = data[offset],
                            dprev = data[offset - 1];

                            var weight = Math.pow(lambda, offset);
                            up.push(yValue(dnow) > yValue(dprev) ? (yValue(dnow) - yValue(dprev)) * weight : 0);
                            down.push(yValue(dnow) < yValue(dprev) ? (yValue(dprev) - yValue(dnow)) * weight : 0);
                        }

                        if (up.length <= 0 || down.length <= 0) {
                            return yScale(0);
                        }

                        var rsi = 100 - (100 / (1 + (d3.mean(up) / d3.mean(down))));
                        return yScale(rsi);
                    });
                }

                var path = d3.select(this).selectAll('.' + css)
                    .data([data]);

                path.enter().append('path');

                path.attr('d', line)
                    .classed(css, true);

                path.exit().remove();
            });
        };

        /**
        * Specifies the X scale which the tracker uses to locate its SVG elements. If not specified, returns
        * the current X scale, which defaults to an unmodified d3.time.scale
        *
        * @memberof fc.indicators.rsi
        * @method xScale
        * @param {scale} scale a D3 scale
        */
        rsi.xScale = function(scale) {
            if (!arguments.length) {
                return xScale;
            }
            xScale = scale;
            return rsi;
        };

        /**
        * Specifies the Y scale which the tracker uses to locate its SVG elements. If not specified, returns
        * the current Y scale, which defaults to an unmodified d3.scale.linear.
        *
        * @memberof fc.indicators.rsi
        * @method yScale
        * @param {scale} scale a D3 scale
        */
        rsi.yScale = function(scale) {
            if (!arguments.length) {
                return yScale;
            }
            yScale = scale;
            return rsi;
        };

        /**
        * Specifies the number of data samples used to calculate the RSI over, much like the number of
        * points for the moving average indicator. If not set the default value is 14, which is the
        * accepted value given by Wilder
        *
        * @memberof fc.indicators.rsi
        * @method samplePeriods
        * @param {integer} value the number of periods
        */
        rsi.samplePeriods = function(value) {
            if (!arguments.length) {
                return samplePeriods;
            }
            samplePeriods = value < 0 ? 0 : value;
            return rsi;
        };

        /**
        * Specifies the location of the upper marker used to mark the level at which the market/instrument is
        * considered over bought. The default value of this 70%. The value is specified as a percentage so
        * 70 as opposed to 0.7.
        *
        * @memberof fc.indicators.rsi
        * @method upperMarker
        * @param {number} value the value of the upper marker
        */
        rsi.upperMarker = function(value) {
            if (!arguments.length) {
                return upperMarker;
            }
            upperMarker = value > 100 ? 100 : (value < 0 ? 0 : value);
            return rsi;
        };

        /**
        * Specifies the location of the lower marker used to mark the level at which the market/instrument is
        * considered over sold. The default value of this 30%. The value is specified as a percentage so 30
        * as opposed to 0.3.
        *
        * @memberof fc.indicators.rsi
        * @method lowerMarker
        * @param {number} value the value of the lower marker
        */
        rsi.lowerMarker = function(value) {
            if (!arguments.length) {
                return lowerMarker;
            }
            lowerMarker = value > 100 ? 100 : (value < 0 ? 0 : value);
            return rsi;
        };

        /**
        * Specifies the relative influence that the samples have on the Exponential Moving average
        * calculation. A value of 1 (Default value) will mean that every data sample will have equal
        * weight in the calculation. The most widely used values are in the region 0.92 to 0.98.
        *
        * @memberof fc.indicators.rsi
        * @method lambda
        * @param {number} value the value of the lower marker
        */
        rsi.lambda = function(value) {
            if (!arguments.length) {
                return lambda;
            }
            lambda = value > 1.0 ? 1.0 : (value < 0.0 ? 0.0 : value);
            return rsi;
        };

        /**
        * Specifies the name of the data field which the component will follow. If not specified,
        * returns the 'close' property of each datapoint
        *
        * @memberof fc.indicators.bollingerBands
        * @method yValue
        * @param {accessor} value a D3 accessor function which returns the Y value for a given point
        */
        rsi.yValue = function(value) {
            if (!arguments.length) {
                return yValue;
            }
            yValue = value;
            return rsi;
        };

        return rsi;
    };
}(d3, fc));<commit_msg>Minor RSI tweaks<commit_after>(function(d3, fc) {
    'use strict';

    /**
    * This component will generate an RSI data series on
    * a chart based on data generated in the format produced by the dataGenerator component.
    *
    * @type {object}
    * @memberof fc.indicators
    * @namespace fc.indicators.rsi
    */
    fc.indicators.rsi = function() {

        var xScale = d3.time.scale(),
            yScale = d3.scale.linear(),
            samplePeriods = 14,
            upperMarker = 70,
            lowerMarker = 30,
            lambda = 1.0,
            css = 'rsi',
            yValue = fc.utilities.valueAccessor('close');

        var upper = null,
            centre = null,
            lower = null;

        /**
        * Constructs a new instance of the RSI component.
        *
        * @memberof fc.indicators.rsi
        * @param {selection} selection a D3 selection
        */
        var rsi = function(selection) {

            var line = d3.svg.line();
            line.x(function(d) { return xScale(d.date); });

            selection.each(function(data) {

                if (samplePeriods === 0) {
                    line.y(function(d) { return yScale(0); });
                } else {
                    line.y(function(d, i) {
                        var from = i - samplePeriods,
                        to = i,
                        up = [],
                        down = [];

                        if (from < 1) {
                            from = 1;
                        }

                        for (var offset = to; offset >= from; offset--) {
                            var dnow = data[offset],
                            dprev = data[offset - 1];

                            var weight = Math.pow(lambda, offset);
                            up.push(yValue(dnow) > yValue(dprev) ? (yValue(dnow) - yValue(dprev)) * weight : 0);
                            down.push(yValue(dnow) < yValue(dprev) ? (yValue(dprev) - yValue(dnow)) * weight : 0);
                        }

                        if (up.length <= 0 || down.length <= 0) {
                            return yScale(0);
                        }

                        var rsi = 100 - (100 / (1 + (d3.mean(up) / d3.mean(down))));
                        return yScale(rsi);
                    });
                }

                // add a 'root' g element on the first enter selection. This ensures
                // that it is just added once
                var container = d3.select(this)
                    .selectAll('.' + css)
                    .data([data]);
                container.enter()
                    .append('g')
                    .classed(css, true);

                // add the marker lines
                container.selectAll('.marker').remove();

                upper = container.append('line')
                    .attr('class', 'marker upper')
                    .attr('x1', xScale.range()[0])
                    .attr('y1', yScale(upperMarker))
                    .attr('x2', xScale.range()[1])
                    .attr('y2', yScale(upperMarker));

                centre = container.append('line')
                    .attr('class', 'marker centre')
                    .attr('x1', xScale.range()[0])
                    .attr('y1', yScale(50))
                    .attr('x2', xScale.range()[1])
                    .attr('y2', yScale(50));

                lower = container.append('line')
                    .attr('class', 'marker lower')
                    .attr('x1', xScale.range()[0])
                    .attr('y1', yScale(lowerMarker))
                    .attr('x2', xScale.range()[1])
                    .attr('y2', yScale(lowerMarker));

                 // create a data-join for the path
                var path = container
                    .selectAll('path')
                    .data([data]);

                // enter
                path.enter()
                    .append('path');

                // update
                path.attr('d', line);

                // exit
                path.exit()
                    .remove();
            });
        };

        /**
        * Specifies the X scale which the tracker uses to locate its SVG elements. If not specified, returns
        * the current X scale, which defaults to an unmodified d3.time.scale
        *
        * @memberof fc.indicators.rsi
        * @method xScale
        * @param {scale} scale a D3 scale
        */
        rsi.xScale = function(scale) {
            if (!arguments.length) {
                return xScale;
            }
            xScale = scale;
            return rsi;
        };

        /**
        * Specifies the Y scale which the tracker uses to locate its SVG elements. If not specified, returns
        * the current Y scale, which defaults to an unmodified d3.scale.linear.
        *
        * @memberof fc.indicators.rsi
        * @method yScale
        * @param {scale} scale a D3 scale
        */
        rsi.yScale = function(scale) {
            if (!arguments.length) {
                return yScale;
            }
            yScale = scale;
            return rsi;
        };

        /**
        * Specifies the number of data samples used to calculate the RSI over, much like the number of
        * points for the moving average indicator. If not set the default value is 14, which is the
        * accepted value given by Wilder
        *
        * @memberof fc.indicators.rsi
        * @method samplePeriods
        * @param {integer} value the number of periods
        */
        rsi.samplePeriods = function(value) {
            if (!arguments.length) {
                return samplePeriods;
            }
            samplePeriods = value < 0 ? 0 : value;
            return rsi;
        };

        /**
        * Specifies the location of the upper marker used to mark the level at which the market/instrument is
        * considered over bought. The default value of this 70%. The value is specified as a percentage so
        * 70 as opposed to 0.7.
        *
        * @memberof fc.indicators.rsi
        * @method upperMarker
        * @param {number} value the value of the upper marker
        */
        rsi.upperMarker = function(value) {
            if (!arguments.length) {
                return upperMarker;
            }
            upperMarker = value > 100 ? 100 : (value < 0 ? 0 : value);
            return rsi;
        };

        /**
        * Specifies the location of the lower marker used to mark the level at which the market/instrument is
        * considered over sold. The default value of this 30%. The value is specified as a percentage so 30
        * as opposed to 0.3.
        *
        * @memberof fc.indicators.rsi
        * @method lowerMarker
        * @param {number} value the value of the lower marker
        */
        rsi.lowerMarker = function(value) {
            if (!arguments.length) {
                return lowerMarker;
            }
            lowerMarker = value > 100 ? 100 : (value < 0 ? 0 : value);
            return rsi;
        };

        /**
        * Specifies the relative influence that the samples have on the Exponential Moving average
        * calculation. A value of 1 (Default value) will mean that every data sample will have equal
        * weight in the calculation. The most widely used values are in the region 0.92 to 0.98.
        *
        * @memberof fc.indicators.rsi
        * @method lambda
        * @param {number} value the value of the lower marker
        */
        rsi.lambda = function(value) {
            if (!arguments.length) {
                return lambda;
            }
            lambda = value > 1.0 ? 1.0 : (value < 0.0 ? 0.0 : value);
            return rsi;
        };

        /**
        * Specifies the name of the data field which the component will follow. If not specified,
        * returns the 'close' property of each datapoint
        *
        * @memberof fc.indicators.bollingerBands
        * @method yValue
        * @param {accessor} value a D3 accessor function which returns the Y value for a given point
        */
        rsi.yValue = function(value) {
            if (!arguments.length) {
                return yValue;
            }
            yValue = value;
            return rsi;
        };

        return rsi;
    };
}(d3, fc));<|endoftext|>


<commit_before>"use strict";
/*
  vsphere-soap.test.js

  tests for the node-vsphere module
*/

var Code = require('code');
var Lab = require('lab');
var util = require('util');
var lab = exports.lab = Lab.script();
var Vsphere = require('../lib/client');
var TestCreds = require('../config-test.js').vCenterTestCreds;
var TestVars = require('../config-test.js').vCenterTestVars;
var _ = require('lodash');

var describe = lab.describe;
var it = lab.it;
var before = lab.before;
var beforeEach = lab.beforeEach;
var after = lab.after;
var expect = Code.expect;

var vc = new Vsphere.Client(TestCreds.vCenterIP, TestCreds.vCenterUser, TestCreds.vCenterPassword, false);

describe('Client object initialization:', function(){
  it('provides a successful login', {timeout: 20000}, function(done) {
    expect(vc).to.exist();
    vc.once('ready', function() {
      expect(vc.serviceContent).to.exist();
      done();
    });
    vc.once('error', function(err) {
      console.error(err);
    });
  });
});

describe('Client tests - query commands:', function(){

  it('retrieves current time', {timeout: 5000}, function(done){
    vc.runCommand('CurrentTime', { _this: 'ServiceInstance'} )
      .once('result', function(result){
        expect(result.returnval).to.be.a.date();
        done();
      })
      .once('error', function(err) {
        console.error(err);
    });
  });

  it('can get ManagedObjectReferences in rootFolder container by type (Datacenter)', {timeout: 20000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByType( rootFolder, 'Datacenter')
    .once('result', function(result) {
      expect(result.returnval.objects.obj.attributes.type).to.equal('Datacenter');
      expect(result.returnval.objects.propSet).to.exist();
      done();
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can get ManagedObjectReferences in rootFolder container by type (VirtualMachine)', {timeout: 20000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByType( rootFolder, 'VirtualMachine')
    .once('result', function(result) {

      if( _.isEmpty(result) ) {
        // no vms to see
        done();
      } else if( result.returnval.objects.length ) {
        // more than one vm returned
        expect(result.returnval.objects[0].obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects[0].propSet).to.exist();
        done();
      } else if( result.returnval.objects ) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      } else {
        console.error('issue!');
      }
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can retrieve all properties of a specific VirtualMachine object by ManagedObjectReference', {timeout: 20000}, function(done) {

    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByType( rootFolder, 'VirtualMachine')
    .once('result', function(vmRefsResult) {
      vc.getMORefProperties( _.sample(vmRefsResult.returnval.objects).obj)
      .once('result', function(result) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      })
      .once('error', function(err) {
        console.error(err);
      });
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can retrieve all properties of a specific VirtualMachine object by ManagedObjectReference derived by name', {timeout: 20000}, function(done) {

    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByTypeName( rootFolder, 'VirtualMachine', _.sample(TestVars.testVMs))
    .once('result', function( vmRefResult) {
      vc.getMORefProperties( vmRefResult )
      .once('result', function(result) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      })
      .once('error', function(err) {
        console.error(err);
      });
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can obtain the names of all Virtual Machines using getMORefsInContainerByTypeAndPropertyArray', {timeout: 20000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByTypePropertyArray( rootFolder, 'VirtualMachine', ['name'])
    .once('result', function(result) {
      if( _.isEmpty(result) ) {
        // no vms to see
        done();
      } else if( result.returnval.objects.length ) {
        // more than one vm returned
        expect(result.returnval.objects[1].obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects[1].propSet).to.exist();
        done();
      } else if( result.returnval.objects ) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      } else {
        console.error('issue!');
      }
    })
    .once('error', function(err) {
      console.error(err);
    });
  });
});

describe('Client tests - VM power operations:', function(){

  var testVMLinuxPowerOn = false;
  var testVMWindowsPowerOn = false;

  it('can obtain information about VM power state', {timeout: 5000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getVMinContainerPowerState( rootFolder )
    .once('result', function(result) {
      expect(result).to.be.an.array();
      if(result.length > 0) {
        expect(result[0].obj.attributes.type).to.equal('VirtualMachine');
        expect( parseInt(result[0].obj['$value']) , 'vmId').to.be.a.number();
        expect(result[0].name, 'VM Name').to.exist();
        expect(result[0].name, 'VM Name').to.be.a.string();
        expect(result[0].powerState, 'VM power state').to.exist();
        expect(result[0].powerState, 'VM power state').to.be.a.string();
      }
      done();
    })
    .once('error', function(err){
      //catch err;
      console.error(util.inspect(err, {depth: null}));
    });
  });

  it('powers on and off a VM (by name)', {timeout: 60000}, function(done){

    // turn off all test vms before running test
    vc.powerOpVMByName( _.values( TestVars.testVMs), 'powerOff')
    .once('result', runTest)
    .once('error', runTest);

    // run test
    function runTest() {
      // uses a random VM in testVMs
      vc.powerOpVMByName( _.sample(TestVars.testVMs), 'powerOn')
      .once('result', function(powerOnResult) {

        // ensure VM PowerOn task successfully fired
        expect(powerOnResult[0].result['$value']).to.be.equal('success');

        // get the Virtual Machine ManagedObjectReference
        var vmObj = powerOnResult[0].obj;

        vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOn')
        .once('result', function(result) {

          // verify VM is powered on
          expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOn');

          vc.powerOpVMByMORef( vmObj, 'powerOff')
          .once('result', function(powerOffResult) {

            // ensure VM PowerOff task successfully fired
            expect(powerOffResult[0].result['$value']).to.be.equal('success');

            vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOff')
            .once('result', function(result) {

              // verify VM is powered off
              expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOff');
              done();

            })
            .once('error', function(err) {
            console.error(err);
            });
          })
          .once('error', function(err) {
            console.error(err);
          });
        })
        .once('error', function(err) {
          console.error(err);
        });

      })
      .once('error', function(err) {
        console.error(err);
      });
    }
  });

  it('powers on and off multiple VMs (by array of names)', {timeout: 120000}, function(done){

    // turn off all test VMs

    vc.powerOpVMByName( _.values( TestVars.testVMs), 'powerOff')
    .once('result', runTest)
    .once('error', runTest);

    // now run test

    function runTest() {

      var allTestVMNames = _.values( TestVars.testVMs);
      var numProcessed = 0;

      vc.powerOpVMByName( _.values( TestVars.testVMs), 'powerOn')
      .once('result', function(powerOnResult) {

        // check that all submitted PowerOn tasks have successfully fired
        _.forEach( powerOnResult, function(result) {
          expect(result.result['$value']).to.be.equal('success');
        });

        var vmObjArray = _.pluck( powerOnResult, 'obj');
        // wait for them all to be powered on
        _.forEach(vmObjArray, function(vmObj) {
          vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOn')
          .once('result', function(result) {

            // verify all test VMs are powered on
            expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOn');

            vc.powerOpVMByMORef( vmObj, 'powerOff')
            .once('result', function(powerOffResult) {

              // verify that all submitted PowerOff tasks have successfully fired
              expect(powerOffResult[0].result['$value']).to.be.equal('success');

              vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOff')
              .once('result', function(result) {

                // verify all test VMs are powered off
                expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOff');
                numProcessed++;
                if(numProcessed == allTestVMNames.length) {
                  done();
                }
              })
              .once('error', function(err) {
              console.error(err);
              });
            })
            .once('error', function(err) {
              console.error(err);
            });
          })
          .once('error', function(err) {
            console.error(err);
          });
        });

      })
      .once('error', function(err) {
        console.error(err);
      });
    }
  });

});

<commit_msg>changed powerOff to shutdown in tests<commit_after>"use strict";
/*
  vsphere-soap.test.js

  tests for the node-vsphere module
*/

var Code = require('code');
var Lab = require('lab');
var util = require('util');
var lab = exports.lab = Lab.script();
var Vsphere = require('../lib/client');
var TestCreds = require('../config-test.js').vCenterTestCreds;
var TestVars = require('../config-test.js').vCenterTestVars;
var _ = require('lodash');

var describe = lab.describe;
var it = lab.it;
var before = lab.before;
var beforeEach = lab.beforeEach;
var after = lab.after;
var expect = Code.expect;

var vc = new Vsphere.Client(TestCreds.vCenterIP, TestCreds.vCenterUser, TestCreds.vCenterPassword, false);

describe('Client object initialization:', function(){
  it('provides a successful login', {timeout: 20000}, function(done) {
    expect(vc).to.exist();
    vc.once('ready', function() {
      expect(vc.serviceContent).to.exist();
      done();
    });
    vc.once('error', function(err) {
      console.error(err);
    });
  });
});

describe('Client tests - query commands:', function(){

  it('retrieves current time', {timeout: 5000}, function(done){
    vc.runCommand('CurrentTime', { _this: 'ServiceInstance'} )
      .once('result', function(result){
        expect(result.returnval).to.be.a.date();
        done();
      })
      .once('error', function(err) {
        console.error(err);
    });
  });

  it('can get ManagedObjectReferences in rootFolder container by type (Datacenter)', {timeout: 20000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByType( rootFolder, 'Datacenter')
    .once('result', function(result) {
      expect(result.returnval.objects.obj.attributes.type).to.equal('Datacenter');
      expect(result.returnval.objects.propSet).to.exist();
      done();
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can get ManagedObjectReferences in rootFolder container by type (VirtualMachine)', {timeout: 20000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByType( rootFolder, 'VirtualMachine')
    .once('result', function(result) {

      if( _.isEmpty(result) ) {
        // no vms to see
        done();
      } else if( result.returnval.objects.length ) {
        // more than one vm returned
        expect(result.returnval.objects[0].obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects[0].propSet).to.exist();
        done();
      } else if( result.returnval.objects ) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      } else {
        console.error('issue!');
      }
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can retrieve all properties of a specific VirtualMachine object by ManagedObjectReference', {timeout: 20000}, function(done) {

    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByType( rootFolder, 'VirtualMachine')
    .once('result', function(vmRefsResult) {
      vc.getMORefProperties( _.sample(vmRefsResult.returnval.objects).obj)
      .once('result', function(result) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      })
      .once('error', function(err) {
        console.error(err);
      });
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can retrieve all properties of a specific VirtualMachine object by ManagedObjectReference derived by name', {timeout: 20000}, function(done) {

    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByTypeName( rootFolder, 'VirtualMachine', _.sample(TestVars.testVMs))
    .once('result', function( vmRefResult) {
      vc.getMORefProperties( vmRefResult )
      .once('result', function(result) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      })
      .once('error', function(err) {
        console.error(err);
      });
    })
    .once('error', function(err) {
      console.error(err);
    });
  });

  it('can obtain the names of all Virtual Machines using getMORefsInContainerByTypeAndPropertyArray', {timeout: 20000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getMORefsInContainerByTypePropertyArray( rootFolder, 'VirtualMachine', ['name'])
    .once('result', function(result) {
      if( _.isEmpty(result) ) {
        // no vms to see
        done();
      } else if( result.returnval.objects.length ) {
        // more than one vm returned
        expect(result.returnval.objects[1].obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects[1].propSet).to.exist();
        done();
      } else if( result.returnval.objects ) {
        expect(result.returnval.objects.obj.attributes.type).to.equal('VirtualMachine');
        expect(result.returnval.objects.propSet).to.exist();
        done();
      } else {
        console.error('issue!');
      }
    })
    .once('error', function(err) {
      console.error(err);
    });
  });
});

describe('Client tests - VM power operations:', function(){

  var testVMLinuxPowerOn = false;
  var testVMWindowsPowerOn = false;

  it('can obtain information about VM power state', {timeout: 5000}, function(done){
    var rootFolder = vc.serviceContent.rootFolder;
    vc.getVMinContainerPowerState( rootFolder )
    .once('result', function(result) {
      expect(result).to.be.an.array();
      if(result.length > 0) {
        expect(result[0].obj.attributes.type).to.equal('VirtualMachine');
        expect( parseInt(result[0].obj['$value']) , 'vmId').to.be.a.number();
        expect(result[0].name, 'VM Name').to.exist();
        expect(result[0].name, 'VM Name').to.be.a.string();
        expect(result[0].powerState, 'VM power state').to.exist();
        expect(result[0].powerState, 'VM power state').to.be.a.string();
      }
      done();
    })
    .once('error', function(err){
      //catch err;
      console.error(util.inspect(err, {depth: null}));
    });
  });

  it('powers on and off a VM (by name)', {timeout: 60000}, function(done){

    // turn off all test vms before running test
    vc.powerOpVMByName( _.values( TestVars.testVMs), 'shutdown')
    .once('result', runTest)
    .once('error', runTest);

    // run test
    function runTest() {
      // uses a random VM in testVMs
      vc.powerOpVMByName( _.sample(TestVars.testVMs), 'powerOn')
      .once('result', function(powerOnResult) {

        // ensure VM PowerOn task successfully fired
        expect(powerOnResult[0].result['$value']).to.be.equal('success');

        // get the Virtual Machine ManagedObjectReference
        var vmObj = powerOnResult[0].obj;

        vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOn')
        .once('result', function(result) {

          // verify VM is powered on
          expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOn');

          vc.powerOpVMByMORef( vmObj, 'shutdown')
          .once('result', function(powerOffResult) {

            // ensure VM PowerOff task successfully fired
            expect(powerOffResult[0].result['$value']).to.be.equal('success');

            vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOff')
            .once('result', function(result) {

              // verify VM is powered off
              expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOff');
              done();

            })
            .once('error', function(err) {
            console.error(err);
            });
          })
          .once('error', function(err) {
            console.error(err);
          });
        })
        .once('error', function(err) {
          console.error(err);
        });

      })
      .once('error', function(err) {
        console.error(err);
      });
    }
  });

  it('powers on and off multiple VMs (by array of names)', {timeout: 120000}, function(done){

    // turn off all test VMs

    vc.powerOpVMByName( _.values( TestVars.testVMs), 'shutdown')
    .once('result', runTest)
    .once('error', runTest);

    // now run test

    function runTest() {

      var allTestVMNames = _.values( TestVars.testVMs);
      var numProcessed = 0;

      vc.powerOpVMByName( _.values( TestVars.testVMs), 'powerOn')
      .once('result', function(powerOnResult) {

        // check that all submitted PowerOn tasks have successfully fired
        _.forEach( powerOnResult, function(result) {
          expect(result.result['$value']).to.be.equal('success');
        });

        var vmObjArray = _.pluck( powerOnResult, 'obj');
        // wait for them all to be powered on
        _.forEach(vmObjArray, function(vmObj) {
          vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOn')
          .once('result', function(result) {

            // verify all test VMs are powered on
            expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOn');

            vc.powerOpVMByMORef( vmObj, 'shutdown')
            .once('result', function(powerOffResult) {

              // verify that all submitted PowerOff tasks have successfully fired
              expect(powerOffResult[0].result['$value']).to.be.equal('success');

              vc.waitForValues( vmObj, 'summary.runtime.powerState', 'powerState', 'poweredOff')
              .once('result', function(result) {

                // verify all test VMs are powered off
                expect(result['summary.runtime.powerState']['$value']).to.be.equal('poweredOff');
                numProcessed++;
                if(numProcessed == allTestVMNames.length) {
                  done();
                }
              })
              .once('error', function(err) {
              console.error(err);
              });
            })
            .once('error', function(err) {
              console.error(err);
            });
          })
          .once('error', function(err) {
            console.error(err);
          });
        });

      })
      .once('error', function(err) {
        console.error(err);
      });
    }
  });

});

<|endoftext|>


<commit_before>/* global describe, it, expect */

import React from 'react'
import ReactDOM from 'react-dom'
import TestUtils from 'react-addons-test-utils'
import {Linear, Circular} from '../'

describe('Progress', () => {
  describe('Linear', () => {
    it('should work', (done) => {
      const instance = TestUtils.renderIntoDocument(<Linear />)
      // use setTimeout to make component is rendered
      window.setTimeout(() => {
        const node = ReactDOM.findDOMNode(instance)
        expect(node).toBeTruthy()
        done()
      }, 100)
    })

    it('should render a progress bar with given percentage', (done) => {
      const instance = TestUtils.renderIntoDocument(<Linear percentage={50} />)
      window.setTimeout(() => {
        const inner = TestUtils.findRenderedDOMComponentWithClass(instance, 'Progress-linear-inner')
        expect(inner.style.width).toBe('50%')
        done()
      }, 100)
    })

    it('should remove itself from the DOM when done', (done) => {
      const instance = TestUtils.renderIntoDocument(<Linear />)
      // use setTimeout to make component is rendered
      window.setTimeout(() => {
        const node = ReactDOM.findDOMNode(instance)
        expect(node).toBeTruthy()
        // let it be done
        instance.componentWillReceiveProps({
          percentage: 100
        })
        window.setTimeout(() => {
          const node = ReactDOM.findDOMNode(instance)
          expect(node).toBeFalsy()
          done()
        }, 1500)
      }, 100)
    }, 10000)

    // make sure else path is taken for test coverage
    it('should not remove itself from the DOM when below 100 percent', (done) => {
      const instance = TestUtils.renderIntoDocument(<Linear />)
      window.setTimeout(() => {
        const node = ReactDOM.findDOMNode(instance)
        expect(node).toBeTruthy
        instance.componentWillReceiveProps({
          percentage: 99
        })
        window.setTimeout(() => {
          const node = ReactDOM.findDOMNode(instance)
          // different to test above
          expect(node).toBeTruthy()
          done()
        }, 1500)
      })
    })
  })

  describe('Circular', () => {
    it('should work', () => {
      const instance = TestUtils.renderIntoDocument(<Circular />)
      const node = ReactDOM.findDOMNode(instance)
      expect(node).toBeTruthy()
    })

    it('should render circular progress with given percentage', () => {
      const instance = TestUtils.renderIntoDocument(<Circular percentage={50} />)
      const inner = TestUtils.findRenderedDOMComponentWithClass(instance, 'Progress-circular-path')
      expect(parseFloat(inner.style.strokeDasharray)).toBeCloseTo(2 * Math.PI * 30)
      expect(inner.style.strokeDashoffset).toBeCloseTo(Math.PI * 30)
    })
  })
})
<commit_msg>progress: rewrite tests using enzyme<commit_after>/* global describe, it, expect */

import React from 'react'
import {mount} from 'enzyme'
import {Linear, Circular} from '../'

describe('Progress', () => {
  describe('Linear', () => {
    it('should work', (done) => {
      const wrapper = mount(<Linear />)
      // use setTimeout to make component is rendered
      window.setTimeout(() => {
        expect(wrapper.find('.Progress-linear').length).toBe(1)
        done()
      }, 100)
    })

    it('should render a progress bar with given percentage', (done) => {
      const wrapper = mount(<Linear percentage={50} />)
      window.setTimeout(() => {
        expect(wrapper.find('.Progress-linear-inner').node.style.width).toBe('50%')
        done()
      }, 100)
    })

    it('should remove itself from the DOM when done', (done) => {
      const wrapper = mount(<Linear />)
      window.setTimeout(() => {
        expect(wrapper.find('.Progress-linear').length).toBe(1)
        // let it be done
        wrapper.setProps({
          percentage: 100
        })
        window.setTimeout(() => {
          expect(wrapper.find('.Progress-linear').length).toBe(0)
          done()
        }, 1500)
      }, 100)
    }, 10000)

    // make sure else path is taken for test coverage
    it('should not remove itself from the DOM when below 100 percent', (done) => {
      const wrapper = mount(<Linear />)
      window.setTimeout(() => {
        expect(wrapper.find('.Progress-linear').length).toBe(1)
        wrapper.setProps({
          percentage: 99
        })
        window.setTimeout(() => {
          // different to test above
          expect(wrapper.find('.Progress-linear').length).toBe(1)
          done()
        }, 1500)
      }, 100)
    })
  })

  describe('Circular', () => {
    it('should work', () => {
      const wrapper = mount(<Circular />)
      expect(wrapper.find('.Progress-circular').length).toBe(1)
    })

    it('should render circular progress with given percentage', () => {
      const wrapper = mount(<Circular percentage={50} />)
      const inner = wrapper.find('.Progress-circular-path')
      expect(parseFloat(inner.node.style.strokeDasharray)).toBeCloseTo(2 * Math.PI * 30)
      expect(inner.node.style.strokeDashoffset).toBeCloseTo(Math.PI * 30)
    })
  })
})
<|endoftext|>


<commit_before>var tape = require('tape'),
	Frame = require('../lib/frame');

// simple instructive test cases
function simpleTestCases(){

	tape("sum works with where", function(t){
		t.plan(1);
		var frame = new Frame({
			"id_0"  : [0, 0, 0, 1, 1, 0, 1, 0, 1],
			"id_1"  : [0, 0, 1, 1, 0, 0, 1, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 9; // 1 + 2 + 2 + 3 + 1 + 3 + 4 + 2 + 1

		var actual = frame.where("id_0", 1).sum("value");

		t.equals(actual, expected);
	});

	tape("sum works with where", function(t){
		t.plan(1);
		var frame = new Frame({
			"id_0"  : [0, 0, 0, 1, 1, 0, 1, 0, 1],
			"id_1"  : [0, 0, 1, 1, 0, 0, 1, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 10; // 1 + 2 + 2 + 3 + 1 + 3 + 4 + 2 + 1

		var actual = frame.where("id_0", 0).sum("value");

		t.equals(actual, expected);
	});

	tape("sum works with multiple wheres", function(t){
		t.plan(1);
		var frame = new Frame({
			"id_0"  : [0, 0, 0, 1, 1, 0, 1, 0, 1],
			"id_1"  : [0, 0, 1, 1, 0, 0, 1, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 8;
		var actual = frame.where("id_0", 1).where("id_1", 1).sum("value");

		t.equals(actual, expected);
	});

	tape("sum works with where in", function(t){
		t.plan(1);
		var frame = new Frame({
			"id"  :   [0, 2, 0, 1, 1, 0, 2, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 14;
		frame = frame.where("id", [0, 2]);
		var actual = frame.sum("value");

		t.equals(actual, expected);
	});
}

//simpleTestCases();

var SAMPLE = 10;
function numberCompare(a, b){ return a - b; }
// get a predefined subset of a column (matches test data generation)
function generate_subset(column){
	//column = id_columns[id_name]
	var uniques = {};
	for(var i = 0; i < SAMPLE; i++){
		uniques[column[i]] = column[i];
	}
	var keys = Object.keys(uniques);
	var subset = keys.map(function(k){ return uniques[k]});

	l = Math.ceil(subset.length / 2);
	return subset.sort(numberCompare).slice(0, l);
}

var RTOL = 1e-05, // 1e-05
	ATOL = 1e-12; // 1e-12

var dataDirectory = 'test/data/where.in.sum/',
	testFile = 'small.json';

var floader = require('floader'),
	dtest = require('../lib/test');

floader.load(dataDirectory + testFile, function(err, config){

	var suite = JSON.parse(config);
	simpleTestCases();

	for(var i = 0; i < suite.length; i++){

		var prefix = String("0000" + (i + 1)).slice(-4);

		// directory containing matrix data files for current test
		var directory = dataDirectory + prefix + '/';

		var test = suite[i];

		var names = test.id.map(function(spec, i){ return "id_" + i;});
		var types = test.id.map(function(spec, i){ return spec['type'];});

		var N = test.N; // number of rows
		var distincts = test.id.map(function(spec, i){ return spec.K; });

		var testName = "where.in.sum: " + N + " x " + "(" + distincts.join(", ") + ")"
		tape(testName, generateTestCase(directory, names, types, ["value_0"], [test.value[0].type]));
	}
});

var OUT_FILENAME = "out.json";

function generateTestCase(directory, id_names, id_types, value_names, value_types){
	return function(t){
		t.plan(1);

		var names = id_names.concat(value_names);
		var types = id_types.concat(value_types);

		// which columns require a key file?
		var key_names = id_names.filter(function(item, i){
			return id_types[i] in dtest.string_types
		});
		var key_types = id_types.filter(function(item, i){
			return item in dtest.string_types
		});

		// load columns from files
		dtest.load(directory, names, types, function(err, columns){

			// load key files
			dtest.load_key(directory, key_names, key_types, function(err, keys){

				floader.load(directory + OUT_FILENAME, function(err, out){
					var expected = JSON.parse(out);

					var column_set = {};
					for (var i = 0; i < names.length; i++){
						var name = names[i];
						var column = columns[i];
						column_set[name] = column;
					}
					// keys map a small set of integers to other things (like strings)
					// they're a very simple form of fixed length coding
					var key_set = {};
					for (var i = 0; i < keys.length; i++){
						var name = key_names[i];
						var key = keys[i];
						key_set[name] = key;
					}

					var frame = new Frame(column_set, key_set);

					var subset = generate_subset(column_set["id_0"]);
					//console.log(subset);
					frame = frame.where("id_0", subset);
					var actual = frame.sum("value_0");

					dtest.assert.close(t, actual, expected, "close", RTOL, ATOL);
				});

			});
		});
	};
}
<commit_msg>basic test for immutability<commit_after>var tape = require('tape'),
	Frame = require('../lib/frame');

// simple instructive test cases
function simpleTestCases(){

	tape("sum works with where", function(t){
		t.plan(1);
		var frame = new Frame({
			"id_0"  : [0, 0, 0, 1, 1, 0, 1, 0, 1],
			"id_1"  : [0, 0, 1, 1, 0, 0, 1, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 9; // 1 + 2 + 2 + 3 + 1 + 3 + 4 + 2 + 1

		var actual = frame.where("id_0", 1).sum("value");

		t.equals(actual, expected);
	});

	tape("sum works with where", function(t){
		t.plan(1);
		var frame = new Frame({
			"id_0"  : [0, 0, 0, 1, 1, 0, 1, 0, 1],
			"id_1"  : [0, 0, 1, 1, 0, 0, 1, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 10; // 1 + 2 + 2 + 3 + 2

		var actual = frame.where("id_0", 0).sum("value");

		t.equals(actual, expected);
	});

	tape("where does not modify sum on original Frame", function(t){
		t.plan(1);
		var frame = new Frame({
			"id_0"  : [0, 0, 0, 1, 1, 0, 1, 0, 1],
			"id_1"  : [0, 0, 1, 1, 0, 0, 1, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 19; // 1 + 2 + 2 + 3 + 1 + 3 + 4 + 2 + 1

		var fw = frame.where("id_0", 0);
		var actual = frame.sum("value");

		t.equals(actual, expected);
	});

	tape("sum works with multiple wheres", function(t){
		t.plan(1);
		var frame = new Frame({
			"id_0"  : [0, 0, 0, 1, 1, 0, 1, 0, 1],
			"id_1"  : [0, 0, 1, 1, 0, 0, 1, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 8;
		var actual = frame.where("id_0", 1).where("id_1", 1).sum("value");

		t.equals(actual, expected);
	});

	tape("sum works with where in", function(t){
		t.plan(1);
		var frame = new Frame({
			"id"  :   [0, 2, 0, 1, 1, 0, 2, 0, 1],
			"value" : [1, 2, 2, 3, 1, 3, 4, 2, 1]
		});

		var expected = 14;
		frame = frame.where("id", [0, 2]);
		var actual = frame.sum("value");

		t.equals(actual, expected);
	});
}

//simpleTestCases();

var SAMPLE = 10;
function numberCompare(a, b){ return a - b; }
// get a predefined subset of a column (matches test data generation)
function generate_subset(column){
	//column = id_columns[id_name]
	var uniques = {};
	for(var i = 0; i < SAMPLE; i++){
		uniques[column[i]] = column[i];
	}
	var keys = Object.keys(uniques);
	var subset = keys.map(function(k){ return uniques[k]});

	l = Math.ceil(subset.length / 2);
	return subset.sort(numberCompare).slice(0, l);
}

var RTOL = 1e-05, // 1e-05
	ATOL = 1e-12; // 1e-12

var dataDirectory = 'test/data/where.in.sum/',
	testFile = 'small.json';

var floader = require('floader'),
	dtest = require('../lib/test');

floader.load(dataDirectory + testFile, function(err, config){

	var suite = JSON.parse(config);
	simpleTestCases();

	for(var i = 0; i < suite.length; i++){

		var prefix = String("0000" + (i + 1)).slice(-4);

		// directory containing matrix data files for current test
		var directory = dataDirectory + prefix + '/';

		var test = suite[i];

		var names = test.id.map(function(spec, i){ return "id_" + i;});
		var types = test.id.map(function(spec, i){ return spec['type'];});

		var N = test.N; // number of rows
		var distincts = test.id.map(function(spec, i){ return spec.K; });

		var testName = "where.in.sum: " + N + " x " + "(" + distincts.join(", ") + ")"
		tape(testName, generateTestCase(directory, names, types, ["value_0"], [test.value[0].type]));
	}
});

var OUT_FILENAME = "out.json";

function generateTestCase(directory, id_names, id_types, value_names, value_types){
	return function(t){
		t.plan(1);

		var names = id_names.concat(value_names);
		var types = id_types.concat(value_types);

		// which columns require a key file?
		var key_names = id_names.filter(function(item, i){
			return id_types[i] in dtest.string_types
		});
		var key_types = id_types.filter(function(item, i){
			return item in dtest.string_types
		});

		// load columns from files
		dtest.load(directory, names, types, function(err, columns){

			// load key files
			dtest.load_key(directory, key_names, key_types, function(err, keys){

				floader.load(directory + OUT_FILENAME, function(err, out){
					var expected = JSON.parse(out);

					var column_set = {};
					for (var i = 0; i < names.length; i++){
						var name = names[i];
						var column = columns[i];
						column_set[name] = column;
					}
					// keys map a small set of integers to other things (like strings)
					// they're a very simple form of fixed length coding
					var key_set = {};
					for (var i = 0; i < keys.length; i++){
						var name = key_names[i];
						var key = keys[i];
						key_set[name] = key;
					}

					var frame = new Frame(column_set, key_set);

					var subset = generate_subset(column_set["id_0"]);
					//console.log(subset);
					frame = frame.where("id_0", subset);
					var actual = frame.sum("value_0");

					dtest.assert.close(t, actual, expected, "close", RTOL, ATOL);
				});

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


<commit_before>/**
 * @flow
 * updateStoredCourses() handles updating the cached course data from the server
 */

import type {CourseType, TermType} from './types'
import {COURSE_DATA_PAGE, INFO_PAGE} from './urls'
import * as storage from '../storage'

type TermInfoType = {
	files: Array<TermType>,
	type: string,
}

export async function updateStoredCourses(): Promise<boolean> {
	const outdatedTerms: Array<TermType> = await determineOutdatedTerms()
	// console.log(outdatedTerms)
	await Promise.all(outdatedTerms.map(term => storeTermCoursesFromServer(term)))
	return outdatedTerms.length === 0 ? false : true
}

async function loadCurrentTermsFromServer(): Promise<Array<TermType>> {
	const today = new Date()
	const thisYear = today.getFullYear()
	const resp: TermInfoType = await fetchJson(INFO_PAGE).catch(() => ({
		files: [],
		type: 'error',
	}))
	const terms: Array<TermType> = resp.files.filter(
		file => file.type === 'json' && file.year > thisYear - 5,
	)
	return terms
}

async function determineOutdatedTerms(): Promise<Array<TermType>> {
	const remoteTerms: Array<TermType> = await loadCurrentTermsFromServer()
	console.log(remoteTerms)
	const localTerms: Array<TermType> = await storage.getTermInfo()
	console.log(localTerms)
	if (!localTerms) {
		storage.setTermInfo(remoteTerms)
		return remoteTerms
	}
	let outdatedTerms = localTerms.filter(localTerm => {
		const match = remoteTerms.find(
			remoteTerm => remoteTerm.term === localTerm.term,
		)
		return match ? match.hash !== localTerm.hash : true
	})
	if (outdatedTerms.length !== 0) {
		storage.setTermInfo(remoteTerms)
	}
	console.log(outdatedTerms)
	return outdatedTerms
}

async function storeTermCoursesFromServer(term: TermType) {
	const url = COURSE_DATA_PAGE + term.path
	const resp: Array<CourseType> = await fetchJson(url).catch(() => [])
	storage.setTermCourseData(term.term, resp)
}
<commit_msg>removed console.logs<commit_after>/**
 * @flow
 * updateStoredCourses() handles updating the cached course data from the server
 */

import type {CourseType, TermType} from './types'
import {COURSE_DATA_PAGE, INFO_PAGE} from './urls'
import * as storage from '../storage'

type TermInfoType = {
	files: Array<TermType>,
	type: string,
}

export async function updateStoredCourses(): Promise<boolean> {
	const outdatedTerms: Array<TermType> = await determineOutdatedTerms()
	// console.log(outdatedTerms)
	await Promise.all(outdatedTerms.map(term => storeTermCoursesFromServer(term)))
	return outdatedTerms.length === 0 ? false : true
}

async function loadCurrentTermsFromServer(): Promise<Array<TermType>> {
	const today = new Date()
	const thisYear = today.getFullYear()
	const resp: TermInfoType = await fetchJson(INFO_PAGE).catch(() => ({
		files: [],
		type: 'error',
	}))
	const terms: Array<TermType> = resp.files.filter(
		file => file.type === 'json' && file.year > thisYear - 5,
	)
	return terms
}

async function determineOutdatedTerms(): Promise<Array<TermType>> {
	const remoteTerms: Array<TermType> = await loadCurrentTermsFromServer()
	const localTerms: Array<TermType> = await storage.getTermInfo()
	if (!localTerms) {
		storage.setTermInfo(remoteTerms)
		return remoteTerms
	}
	let outdatedTerms = localTerms.filter(localTerm => {
		const match = remoteTerms.find(
			remoteTerm => remoteTerm.term === localTerm.term,
		)
		return match ? match.hash !== localTerm.hash : true
	})
	if (outdatedTerms.length !== 0) {
		storage.setTermInfo(remoteTerms)
	}
	return outdatedTerms
}

async function storeTermCoursesFromServer(term: TermType) {
	const url = COURSE_DATA_PAGE + term.path
	const resp: Array<CourseType> = await fetchJson(url).catch(() => [])
	storage.setTermCourseData(term.term, resp)
}
<|endoftext|>


<commit_before>exports.tests = {
 addmetatohead : function() {
    var meta = window.document.createElement("meta");
    var head = window.document.getElementsByTagName("body").item(0);
    head.appendChild(meta);
    assertTrue("last element should be the new meta tag",
                head.lastChild === meta);
    assertTrue("meta should not be stringified with a closing tag",
               window.document.innerHTML.indexOf("<meta>") > -1 &&
               window.document.innerHTML.indexOf("</meta>") === -1
               );
  }
};
<commit_msg>Revert "fix the meta tag test"<commit_after>exports.tests = {
 addmetatohead : function() {
    var meta = window.document.createElement("meta");
    window.document.getElementsByTagName("head").item(0).appendChild(meta);
    var elements = window.document.getElementsByTagName("head").item(0).childNodes;

    assertTrue("last element should be the new meta tag",
                elements.item(elements.length-1) === meta);
    assertTrue("meta should not be stringified with a closing tag",
               window.document.innerHTML.indexOf("<meta>") > -1 &&
               window.document.innerHTML.indexOf("</meta>") === -1
               );
  }
};
<|endoftext|>


<commit_before>// @flow
import React from 'react'
import {ScrollView} from 'react-native'
import {Cell, Section, TableView} from 'react-native-tableview-simple'
import openUrl from '../../components/open-url'
import type {FullJobType} from './types'
import getUrls from 'get-urls'
import {SelectableCell} from './selectable'

function Title({job}: {job: FullJobType}) {
  return (

      <Cell
        cellStyle="Subtitle"
        title={job.title}
        detail={job.offCampus ? 'Off-Campus' : ''}
      />

  )
}

function Information({job}: {job: FullJobType}) {
  const department = job.department
    ? <Cell
        cellStyle="RightDetail"
        title="Department"
        detail={job.department}
      />
    : null

  const opens = (
    <Cell cellStyle="RightDetail" title="Date Open" detail={job.dateOpen} />
  )

  const term = job.duringTerm
    ? <Cell cellStyle="Basic" title="Position Available During Term" />
    : null

  const brk = job.duringBreak
    ? <Cell cellStyle="Basic" title="Position Available During Break" />
    : null

  return (

      {department}
      {opens}
      {term}
      {brk}

  )
}

function Description({job}: {job: FullJobType}) {
  return (

      <SelectableCell text={job.description} />

  )
}

function Links({job}: {job: FullJobType}) {
  const links = Array.from(getUrls(job).values())
  return links.length
    ?

        {links.map(url =>
          <Cell
            key={url}
            title={url}
            accessory="DisclosureIndicator"
            onPress={() => openUrl(url)}
          />,
        )}

    : null
}

export class JobDetailView extends React.PureComponent {
  props: {
    job: FullJobType,
  }

  render() {
    const {job} = this.props

    return (
      <ScrollView>
        <TableView>
          <Title job={job} />
          <Information job={job} />
          <Description job={job} />
          <Links job={job} />
        </TableView>
      </ScrollView>
    )
  }
}
<commit_msg>getUrls from a string<commit_after>// @flow
import React from 'react'
import {ScrollView} from 'react-native'
import {Cell, Section, TableView} from 'react-native-tableview-simple'
import openUrl from '../../components/open-url'
import type {FullJobType} from './types'
import getUrls from 'get-urls'
import {SelectableCell} from './selectable'

function Title({job}: {job: FullJobType}) {
  return (

      <Cell
        cellStyle="Subtitle"
        title={job.title}
        detail={job.offCampus ? 'Off-Campus' : ''}
      />

  )
}

function Information({job}: {job: FullJobType}) {
  const department = job.department
    ? <Cell
        cellStyle="RightDetail"
        title="Department"
        detail={job.department}
      />
    : null

  const opens = (
    <Cell cellStyle="RightDetail" title="Date Open" detail={job.dateOpen} />
  )

  const term = job.duringTerm
    ? <Cell cellStyle="Basic" title="Position Available During Term" />
    : null

  const brk = job.duringBreak
    ? <Cell cellStyle="Basic" title="Position Available During Break" />
    : null

  return (

      {department}
      {opens}
      {term}
      {brk}

  )
}

function Description({job}: {job: FullJobType}) {
  return (

      <SelectableCell text={job.description} />

  )
}

function Links({job}: {job: FullJobType}) {
  const links = [...getUrls(job.description)]
  return links.length
    ?

        {links.map(url =>
          <Cell
            key={url}
            title={url}
            accessory="DisclosureIndicator"
            onPress={() => openUrl(url)}
          />,
        )}

    : null
}

export class JobDetailView extends React.PureComponent {
  props: {
    job: FullJobType,
  }

  render() {
    const {job} = this.props

    return (
      <ScrollView>
        <TableView>
          <Title job={job} />
          <Information job={job} />
          <Description job={job} />
          <Links job={job} />
        </TableView>
      </ScrollView>
    )
  }
}
<|endoftext|>


<commit_before>// cookie helper functions
function setCookie(name,value) {
  document.cookie = name+"="+value+"; path=/";
}

function getCookie(name) {
  name += "=";
  var ca = document.cookie.split(';');
  for(var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) == ' ') c = c.substring(1, c.length);
    if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
  }
  return null;
}

// yacs namespace
var nsYacs = {}
// user namespace (holds user-specific data, in particular selected courses)
var nsUser = {
  // Array of section IDs the user selects. Allows selected courses to persist
  // across content changes in the page. The schedule API needs to pass a list
  // of all selected sections in its GET request.
  getSelectionsRaw: function() {
    return getCookie('selections');
  }

  getSelections: function() {
    var selections = getCookie('selections');
    return selections ? selections.split(',') : [];
  },

  addSelection: function(sid) {
    arr = this.getSelections();
    if (arr.indexOf(sid) != -1) return false;
    arr.push(sid);
    setCookie('selections', arr.join(','));
    return true;
  },

  removeSelection: function(sid) {
    arr = this.getSelections();
    i = arr.indexOf(sid);
    if (i == -1) return false;
    arr.splice(i, 1);
    setCookie('selections', arr.join(','));
    return true;
  },

  hasSelection: function(sid) {
    return this.getSelections().indexOf(sid) != -1;
  }
}

// Global constants are all set here to keep them in one place.
nsYacs.deptColumnWidth = 600; // should be the same as the width
                              // defined for <school> in yacs-main.css
nsYacs.deptColumnMargin = 10; // should be the same as side margins defined
                              // for <school> in yacs-main.css

/* Format some items which appear on the search results page into their final
   display. The API does not load text like "credits" or "Section"; the
   application is responsible for this process.
*/
function formatSearchResults() {
  $('course-credits').html(function(index, oldhtml) {
    if(oldhtml == '1') { return oldhtml + ' credit'; }
    else { return oldhtml + ' credits'; }
  });
  $('section-name').prepend('Section ');
  $('section-seats-available').append(' seats');
}

/* Helper function to do the actual AJAX request. Takes a filename (same public
   XML document on the server) and a callback function which will get called
   with a single argument of the response text.
   This will work with API documents, which should be on an root-relative path
   like "/api/v5/whatever.xml"
   Schedules get a JSON object. Using this will return it as text, which can
   then be parsed into JSON with JSON.parse().
   If this is being called in any "loader" function, make sure it's the last
   call in that function. Otherwise, any code after it can't assume that the
   code in the callback has finished.
*/
function doAjaxRequest(filename, callback) {
  var request = new XMLHttpRequest();
  request.addEventListener("load", function() {
    callback(request.response);
  });
  request.open("GET", filename, true);
  request.send();
}

// Clear the main div of any content and possibly put a loading indicator on
// the page.
function clearForNewPage() {
  // Using jQuery empty() is guaranteed to remove all event handlers that have
  // been applied to anything in the content. Without this, event handlers may
  // build up over time and slow down the page.
  $('div#content').empty();

  // can add in some code to set the inner HTML of the content container to some
  // default "Loading..." message or whatever here
}

// Once the departments XML has been loaded into div#content, do any other steps
// needed to crunch it into presentable form.
function setupHomePage() {
  // When loading the home page, this must determine how many columns to
  // place the departments in, and then apply styling as needed to make them fit
  // into that many columns. Our approach is to always use the maximum possible
  // number of columns. The width of the output columns is always assumed to be
  // the maximum width of any school or department element.
  var numColumns =
    Math.floor($(document).width() /
  	       (nsYacs.deptColumnWidth + (nsYacs.deptColumnMargin * 2)));
  var schoolsFinalWidth = numColumns *
    (nsYacs.deptColumnWidth + (nsYacs.deptColumnMargin * 2));

  // This application does not know whether there are schools defined in the
  // database. If there are, the <schools> element will have a nonzero number of
  // children.
  var schoolsArray = $('schools').children();
  var numSchools = schoolsArray.length;
console.log(numSchools);

  if(numSchools == 0) {
    // schools are not defined
    $('schools').remove();
    if(numColumns > 1) {
      // height calculations are much simpler; all we have to do is find a
      // roughly equal number of departments per column. Still have to do
      // wrapping though.
      var columnCtr = 0;
      var deptsPerColumn =
  	Math.ceil($('departments').children().length / numColumns);
      var appendLastTD = true;
      $('department').each(function(i, dept) {
  	if(columnCtr == 0) {
  	  $(dept).before('');
  	}
  	columnCtr++;
  	if(columnCtr == deptsPerColumn) {
  	  $(dept).after('	');
  	  columnCtr = 0;
  	}
      });

      if(appendLastTD) {
  	$('departments').append('	');
      }
      $('departments').wrapInner('

');
    }
  }
  else {
    // schools are defined
    console.log(46);
    if(numColumns > 1) {
      // need to calculate the "height" of each school, not in pixels, but in
      // some arbitrary unit of height independent of the styling.
      schoolHeights = [];
      $('school').each(function(i, obj) {
  	// school height = the number of <department> tags within its list plus
  	// 1 for the school heading (assumed to be around the same height as a
  	// department)
  	schoolHeights[i] =
  	  { 'height' : $(obj).find('department').length + 1,
  	    'data' : '<school>'+$(obj).html()+'</school>' };
      });
      // Order the schoolHeights array by height
      schoolHeights.sort(function(x,y) { return x.height < y.height });

      // I'm not implementing the full general solution to this problem (how
      // best to distribute elements of varying heights evenly in a number of
      // columns) at this time. The current algorithm is: put them in columns
      // 1, 2, ..., n; 1, 2, ..., n; and so on until all schools are gone.

      // Iterate over the <school>s and wrap each group of numColumns of them
      // in a td tag.
      var columnCtr = 0;
      var schoolsPerColumn = Math.ceil(numSchools / numColumns);
      var appendLastTD = true;
      if(numSchools / numColumns == 0) {
  	appendLastTD = false; // it will be appended by the loop
      }
      $('school').each(function(i, school) {
  	if(columnCtr == 0) {
  	  $(school).before('');
  	}
  	columnCtr++;
  	if(columnCtr == schoolsPerColumn) {
  	  $(school).after('	');
  	  columnCtr = 0;
  	}
      });

      if(appendLastTD) {
  	$('schools').append('	');
      }
      $('schools').wrapInner('

');
    }
  }
  // homeTable is either the child of departments or schools
  // they are unnecessary containers that get in the way, so remove them from
  // the DOM
  $('table#homeTable').unwrap();
  // page will not center unless homeTable is given a definite width
  $('table#homeTable').css('width', schoolsFinalWidth);

  // Add a click event listener to all departments to load that department's
  // courses from the API
  $('department').click(function() {
    var dept = $(this);
    nsYacs.searchbar.value = dept.children('department-code').html();
    loadCourses("/api/v5/courses.xml?department_id=" +
  		dept.children('department-id').html());

  });
}

// Anything that has to be done when loading up the front page.
function loadHomePage() {
  clearForNewPage();
  doAjaxRequest("/api/v5/departments.xml", function(response) {
    nsYacs.contentContainer.innerHTML = response;
    setupHomePage();
  });
}

// Once a courses XML has been loaded into div#content, do any other steps
// needed to crunch it into presentable form.
function setupCourses() {
  formatSearchResults();

  // mark any sections that are already in the selected array with .selected
  // class (used in revisiting pages)
  $('section').each(function(i, section) {
    var sid = $(section).find('section-id').html();
    if (nsUser.hasSelection(sid)) {
      $(this).addClass('selected');
    }
  });

  // bind section storing function to clicks
  $('section').click(function(event) {
    var sid = $(this).find('section-id').html();
    // care more about the data - so use that to determine how to change
    // the styling; i.e. if the id is in the array, we will always deselect it
    // regardless of whether it was being rendered as selected or not
    if(nsUser.removeSelection(sid)) {
      // index is real, section is selected, deselect it
      $(this).removeClass('selected');
    }
    else {
      // section is not selected, select it and add it to the array
      nsUser.addSelection(sid);
      $(this).addClass('selected');
    }
    // don't bubble up to the course click handler!
    event.stopPropagation();
  });

  // courses can also be clicked
  // if a course is clicked and all sections are selected, deselect all
  // sections. Otherwise, select all sections.
  $('course').click(function(event) {
    // we are guaranteed that the user clicked on the course and not a section
    var allSectionsSelected = true;
    var selections = nsUser.getSelections();
    $(this).find('section-id').each(function(i, sid) {
      // if a section id cannot be found in the selected array, they cannot
      // all be selected
      sid = $(sid).html();
      if(selections.indexOf(sid) < 0) {
  	   allSectionsSelected = false;
  	   return false; // break the .each() loop
      }
    });

    $(this).find('section').each(function(i, section) {
      var sid = $(section).find('section-id').html();
      if(allSectionsSelected) {
      	nsUser.removeSelection(sid);
      	$(section).removeClass('selected');
      }
      else {
      	nsUser.addSelection(sid);
      	$(section).addClass('selected');
      }
    });
  });
}

// Anything that has to be done when loading up the courses/search results.
// The string argument is the literal API request that will be made, unchanged.
// If performing a search, you should call searchToQuery first.
function loadCourses(apiString) {
  clearForNewPage();
  doAjaxRequest(apiString, function(response) {
    nsYacs.contentContainer.innerHTML = response;
    setupCourses();
  });
}

/* Given a search string (what the user entered in the search bar), restructure
   it as a query string for the courses API.
   If anyone decides to implement a more intelligent search, the code for
   parsing things like "Tuesday class at 4" will go in here.
   The courses API expects a page request structured like
   /api/v5/courses?q=BIOL+1010+Hardwick. This is responsible for providing
   everything after the "q=".
   Possible improvement: let users quote multiword strings
*/
function searchToQuery(searchString) {
  var searchTerms = searchString.split(" ");
  var query = "";
  var first = true;
  for(var i=0; i<searchTerms.length; i++) {
    var term = searchTerms[i];
    if(term.length != 0) { // ignore double spaces creating "" terms
      if(first) {
	first = false;
      } else {
	query += "+";
      }
      query += term;
    }
  }
  return query;
}

/* Schedule loading function; takes the selected section IDs, builds the API
   string out of them (all the scheduling logic is done on the backend), gets
   the JSON version of the schedules, then calls setupSchedules to format the
   page.
*/
function loadSchedules() {
  // If nothing is selected, take no action
  selectionsRaw = nsUser.getSelectionsRaw();
  if (selectionsRaw.length < 1) return;

  clearForNewPage();

  // Construct the API request string that will be passed
  // expects a comma-delimited list of numeric section IDs
  var schedURL = "/api/v5/schedules.json?section_ids=" + selectionsRaw;

  // Get the schedules as a JSON object.
  doAjaxRequest(schedURL, function(response) {
    var schedulesData = JSON.parse(response);
    setupSchedules(schedulesData);
  });
}

/* Given an empty div#content and a JSON object representing all possible
   schedules, create the schedules page with that data. */
function setupSchedules(schedData) {

}

/* Setup function. Initializes all data that needs to be used by this script,
   and adds any necessary event listeners. */
function setup() {
  // Initialize all variables in the yacs namespace
  nsYacs.contentContainer = document.getElementById("content");
  nsYacs.homeButton = document.getElementById("page-title");
  nsYacs.schedButton = document.getElementById("schedule-btn");
  nsYacs.searchbar = document.getElementById("searchbar");

  // Add click event to the YACS button
  nsYacs.homeButton.addEventListener("click", loadHomePage);

  // Add click event to the schedule button
  nsYacs.schedButton.addEventListener("click", loadSchedules);

  //Add enter key listener to the searchbar
  nsYacs.searchbar.addEventListener("keyup", function(event) {
    if(event.keyCode == 13) {
      var searchURL = "/api/v5/courses.xml?q="+
	searchToQuery(nsYacs.searchbar.value);
      loadCourses(searchURL);
    }
  });

  // Load the default home page
  loadHomePage();

  //
}

// Only actually run this when the page finishes loading
document.addEventListener("DOMContentLoaded", setup, false);
<commit_msg>adds attribution to W3C<commit_after>/*
  cookie helper functions
  based on W3C javascript cookie reference
  http://www.w3schools.com/js/js_cookies.asp
*/

function setCookie(name,value) {
  document.cookie = name+"="+value+"; path=/";
}

function getCookie(name) {
  name += "=";
  var ca = document.cookie.split(';');
  for(var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c.charAt(0) == ' ') c = c.substring(1, c.length);
    if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
  }
  return null;
}

// yacs namespace
var nsYacs = {}
// user namespace (holds user-specific data, in particular selected courses)
var nsUser = {
  // Array of section IDs the user selects. Allows selected courses to persist
  // across content changes in the page. The schedule API needs to pass a list
  // of all selected sections in its GET request.
  getSelectionsRaw: function() {
    return getCookie('selections');
  }

  getSelections: function() {
    var selections = getCookie('selections');
    return selections ? selections.split(',') : [];
  },

  addSelection: function(sid) {
    arr = this.getSelections();
    if (arr.indexOf(sid) != -1) return false;
    arr.push(sid);
    setCookie('selections', arr.join(','));
    return true;
  },

  removeSelection: function(sid) {
    arr = this.getSelections();
    i = arr.indexOf(sid);
    if (i == -1) return false;
    arr.splice(i, 1);
    setCookie('selections', arr.join(','));
    return true;
  },

  hasSelection: function(sid) {
    return this.getSelections().indexOf(sid) != -1;
  }
}

// Global constants are all set here to keep them in one place.
nsYacs.deptColumnWidth = 600; // should be the same as the width
                              // defined for <school> in yacs-main.css
nsYacs.deptColumnMargin = 10; // should be the same as side margins defined
                              // for <school> in yacs-main.css

/* Format some items which appear on the search results page into their final
   display. The API does not load text like "credits" or "Section"; the
   application is responsible for this process.
*/
function formatSearchResults() {
  $('course-credits').html(function(index, oldhtml) {
    if(oldhtml == '1') { return oldhtml + ' credit'; }
    else { return oldhtml + ' credits'; }
  });
  $('section-name').prepend('Section ');
  $('section-seats-available').append(' seats');
}

/* Helper function to do the actual AJAX request. Takes a filename (same public
   XML document on the server) and a callback function which will get called
   with a single argument of the response text.
   This will work with API documents, which should be on an root-relative path
   like "/api/v5/whatever.xml"
   Schedules get a JSON object. Using this will return it as text, which can
   then be parsed into JSON with JSON.parse().
   If this is being called in any "loader" function, make sure it's the last
   call in that function. Otherwise, any code after it can't assume that the
   code in the callback has finished.
*/
function doAjaxRequest(filename, callback) {
  var request = new XMLHttpRequest();
  request.addEventListener("load", function() {
    callback(request.response);
  });
  request.open("GET", filename, true);
  request.send();
}

// Clear the main div of any content and possibly put a loading indicator on
// the page.
function clearForNewPage() {
  // Using jQuery empty() is guaranteed to remove all event handlers that have
  // been applied to anything in the content. Without this, event handlers may
  // build up over time and slow down the page.
  $('div#content').empty();

  // can add in some code to set the inner HTML of the content container to some
  // default "Loading..." message or whatever here
}

// Once the departments XML has been loaded into div#content, do any other steps
// needed to crunch it into presentable form.
function setupHomePage() {
  // When loading the home page, this must determine how many columns to
  // place the departments in, and then apply styling as needed to make them fit
  // into that many columns. Our approach is to always use the maximum possible
  // number of columns. The width of the output columns is always assumed to be
  // the maximum width of any school or department element.
  var numColumns =
    Math.floor($(document).width() /
  	       (nsYacs.deptColumnWidth + (nsYacs.deptColumnMargin * 2)));
  var schoolsFinalWidth = numColumns *
    (nsYacs.deptColumnWidth + (nsYacs.deptColumnMargin * 2));

  // This application does not know whether there are schools defined in the
  // database. If there are, the <schools> element will have a nonzero number of
  // children.
  var schoolsArray = $('schools').children();
  var numSchools = schoolsArray.length;
console.log(numSchools);

  if(numSchools == 0) {
    // schools are not defined
    $('schools').remove();
    if(numColumns > 1) {
      // height calculations are much simpler; all we have to do is find a
      // roughly equal number of departments per column. Still have to do
      // wrapping though.
      var columnCtr = 0;
      var deptsPerColumn =
  	Math.ceil($('departments').children().length / numColumns);
      var appendLastTD = true;
      $('department').each(function(i, dept) {
  	if(columnCtr == 0) {
  	  $(dept).before('');
  	}
  	columnCtr++;
  	if(columnCtr == deptsPerColumn) {
  	  $(dept).after('	');
  	  columnCtr = 0;
  	}
      });

      if(appendLastTD) {
  	$('departments').append('	');
      }
      $('departments').wrapInner('

');
    }
  }
  else {
    // schools are defined
    console.log(46);
    if(numColumns > 1) {
      // need to calculate the "height" of each school, not in pixels, but in
      // some arbitrary unit of height independent of the styling.
      schoolHeights = [];
      $('school').each(function(i, obj) {
  	// school height = the number of <department> tags within its list plus
  	// 1 for the school heading (assumed to be around the same height as a
  	// department)
  	schoolHeights[i] =
  	  { 'height' : $(obj).find('department').length + 1,
  	    'data' : '<school>'+$(obj).html()+'</school>' };
      });
      // Order the schoolHeights array by height
      schoolHeights.sort(function(x,y) { return x.height < y.height });

      // I'm not implementing the full general solution to this problem (how
      // best to distribute elements of varying heights evenly in a number of
      // columns) at this time. The current algorithm is: put them in columns
      // 1, 2, ..., n; 1, 2, ..., n; and so on until all schools are gone.

      // Iterate over the <school>s and wrap each group of numColumns of them
      // in a td tag.
      var columnCtr = 0;
      var schoolsPerColumn = Math.ceil(numSchools / numColumns);
      var appendLastTD = true;
      if(numSchools / numColumns == 0) {
  	appendLastTD = false; // it will be appended by the loop
      }
      $('school').each(function(i, school) {
  	if(columnCtr == 0) {
  	  $(school).before('');
  	}
  	columnCtr++;
  	if(columnCtr == schoolsPerColumn) {
  	  $(school).after('	');
  	  columnCtr = 0;
  	}
      });

      if(appendLastTD) {
  	$('schools').append('	');
      }
      $('schools').wrapInner('

');
    }
  }
  // homeTable is either the child of departments or schools
  // they are unnecessary containers that get in the way, so remove them from
  // the DOM
  $('table#homeTable').unwrap();
  // page will not center unless homeTable is given a definite width
  $('table#homeTable').css('width', schoolsFinalWidth);

  // Add a click event listener to all departments to load that department's
  // courses from the API
  $('department').click(function() {
    var dept = $(this);
    nsYacs.searchbar.value = dept.children('department-code').html();
    loadCourses("/api/v5/courses.xml?department_id=" +
  		dept.children('department-id').html());

  });
}

// Anything that has to be done when loading up the front page.
function loadHomePage() {
  clearForNewPage();
  doAjaxRequest("/api/v5/departments.xml", function(response) {
    nsYacs.contentContainer.innerHTML = response;
    setupHomePage();
  });
}

// Once a courses XML has been loaded into div#content, do any other steps
// needed to crunch it into presentable form.
function setupCourses() {
  formatSearchResults();

  // mark any sections that are already in the selected array with .selected
  // class (used in revisiting pages)
  $('section').each(function(i, section) {
    var sid = $(section).find('section-id').html();
    if (nsUser.hasSelection(sid)) {
      $(this).addClass('selected');
    }
  });

  // bind section storing function to clicks
  $('section').click(function(event) {
    var sid = $(this).find('section-id').html();
    // care more about the data - so use that to determine how to change
    // the styling; i.e. if the id is in the array, we will always deselect it
    // regardless of whether it was being rendered as selected or not
    if(nsUser.removeSelection(sid)) {
      // index is real, section is selected, deselect it
      $(this).removeClass('selected');
    }
    else {
      // section is not selected, select it and add it to the array
      nsUser.addSelection(sid);
      $(this).addClass('selected');
    }
    // don't bubble up to the course click handler!
    event.stopPropagation();
  });

  // courses can also be clicked
  // if a course is clicked and all sections are selected, deselect all
  // sections. Otherwise, select all sections.
  $('course').click(function(event) {
    // we are guaranteed that the user clicked on the course and not a section
    var allSectionsSelected = true;
    var selections = nsUser.getSelections();
    $(this).find('section-id').each(function(i, sid) {
      // if a section id cannot be found in the selected array, they cannot
      // all be selected
      sid = $(sid).html();
      if(selections.indexOf(sid) < 0) {
  	   allSectionsSelected = false;
  	   return false; // break the .each() loop
      }
    });

    $(this).find('section').each(function(i, section) {
      var sid = $(section).find('section-id').html();
      if(allSectionsSelected) {
      	nsUser.removeSelection(sid);
      	$(section).removeClass('selected');
      }
      else {
      	nsUser.addSelection(sid);
      	$(section).addClass('selected');
      }
    });
  });
}

// Anything that has to be done when loading up the courses/search results.
// The string argument is the literal API request that will be made, unchanged.
// If performing a search, you should call searchToQuery first.
function loadCourses(apiString) {
  clearForNewPage();
  doAjaxRequest(apiString, function(response) {
    nsYacs.contentContainer.innerHTML = response;
    setupCourses();
  });
}

/* Given a search string (what the user entered in the search bar), restructure
   it as a query string for the courses API.
   If anyone decides to implement a more intelligent search, the code for
   parsing things like "Tuesday class at 4" will go in here.
   The courses API expects a page request structured like
   /api/v5/courses?q=BIOL+1010+Hardwick. This is responsible for providing
   everything after the "q=".
   Possible improvement: let users quote multiword strings
*/
function searchToQuery(searchString) {
  var searchTerms = searchString.split(" ");
  var query = "";
  var first = true;
  for(var i=0; i<searchTerms.length; i++) {
    var term = searchTerms[i];
    if(term.length != 0) { // ignore double spaces creating "" terms
      if(first) {
	first = false;
      } else {
	query += "+";
      }
      query += term;
    }
  }
  return query;
}

/* Schedule loading function; takes the selected section IDs, builds the API
   string out of them (all the scheduling logic is done on the backend), gets
   the JSON version of the schedules, then calls setupSchedules to format the
   page.
*/
function loadSchedules() {
  // If nothing is selected, take no action
  selectionsRaw = nsUser.getSelectionsRaw();
  if (selectionsRaw.length < 1) return;

  clearForNewPage();

  // Construct the API request string that will be passed
  // expects a comma-delimited list of numeric section IDs
  var schedURL = "/api/v5/schedules.json?section_ids=" + selectionsRaw;

  // Get the schedules as a JSON object.
  doAjaxRequest(schedURL, function(response) {
    var schedulesData = JSON.parse(response);
    setupSchedules(schedulesData);
  });
}

/* Given an empty div#content and a JSON object representing all possible
   schedules, create the schedules page with that data. */
function setupSchedules(schedData) {

}

/* Setup function. Initializes all data that needs to be used by this script,
   and adds any necessary event listeners. */
function setup() {
  // Initialize all variables in the yacs namespace
  nsYacs.contentContainer = document.getElementById("content");
  nsYacs.homeButton = document.getElementById("page-title");
  nsYacs.schedButton = document.getElementById("schedule-btn");
  nsYacs.searchbar = document.getElementById("searchbar");

  // Add click event to the YACS button
  nsYacs.homeButton.addEventListener("click", loadHomePage);

  // Add click event to the schedule button
  nsYacs.schedButton.addEventListener("click", loadSchedules);

  //Add enter key listener to the searchbar
  nsYacs.searchbar.addEventListener("keyup", function(event) {
    if(event.keyCode == 13) {
      var searchURL = "/api/v5/courses.xml?q="+
	searchToQuery(nsYacs.searchbar.value);
      loadCourses(searchURL);
    }
  });

  // Load the default home page
  loadHomePage();

  //
}

// Only actually run this when the page finishes loading
document.addEventListener("DOMContentLoaded", setup, false);
<|endoftext|>