<commit_before>var
  GatewayTimeoutError = require('./errors/gatewayTimeoutError'),
  PluginContext = require('./pluginsContext'),
  fs = require('fs'),
  async = require('async'),
  path = require('path'),
  childProcess = require('child_process'),
  q = require('q'),
  _ = require('lodash'),
  pathConfig = path.join(__dirname, '..', '..', '..', 'config');

/*
 We use the console to display information, as there may be no logger plugin available while installing/launching
 plugins
 */

/*eslint-disable no-console */

module.exports = function PluginsManager (kuzzle) {
  this.kuzzle = kuzzle;
  this.plugins = {};
  this.pipes = {};
  this.controllers = {};
  this.routes = [];
  this.isDummy = false;
  this.isServer = false;
  this.config = kuzzle.config.pluginsManager;

  /**
   * Initialize configured plugin in config/defaultPlugins.json and config/customPlugins.json
   *
   * @param {Boolean} isServer, true if this is a server instance, false for worker instances
   * @param {Boolean} isDummy, true if we are trying to test pluginsManager
   */
  this.init = function (isServer, isDummy) {
    var
      pathDefaultPlugins = path.join(pathConfig, 'defaultPlugins.json'),
      pathCustomPlugins = path.join(pathConfig, 'customPlugins.json'),
      defaultPlugins = require(pathDefaultPlugins),
      customPlugins = {};

    if (!childProcess.hasOwnProperty('execSync')) {
      console.error('███ kuzzle-install: Make sure you\'re using Node version >= 0.12');
      process.exit(1);
    }

    this.isDummy = isDummy;
    this.isServer = isServer;

    if (this.isDummy) {
      return false;
    }

    if (fs.existsSync(pathCustomPlugins)) {
      customPlugins = require(pathCustomPlugins);
    }

    this.plugins = constructList(defaultPlugins, customPlugins, this.isServer);
    loadPlugins(this.plugins);
  };

  /**
   * Attach events hooks and pipes given by plugins
   */
  this.run = function () {
    if (this.isDummy) {
      return false;
    }

    _.forEach(this.plugins, (plugin, pluginName) => {
      var
        context = new PluginContext(kuzzle),
        pipeWarnTime = this.config.pipeWarnTime,
        pipeTimeout = this.config.pipeTimeout;

      if (!plugin.activated) {
        return true;
      }

      console.log('Starting plugin: ', pluginName);

      plugin.object.init(plugin.config, context, this.isDummy);

      if (plugin.object.hooks) {
        _.forEach(plugin.object.hooks, (fn, event) => {
          if (plugin.object[fn]) {
            kuzzle.on(event, function (message) {
              plugin.object[fn](message, event);
            });
          }
        });
      }

      if (plugin.object.pipes) {
        if (plugin.config && plugin.config.pipeWarnTime !== undefined) {
          pipeWarnTime = plugin.config.pipeWarnTime;
        }
        if (plugin.config && plugin.config.pipeTimeout !== undefined) {
          pipeTimeout = plugin.config.pipeTimeout;
        }

        _.forEach(plugin.object.pipes, (fn, pipe) => {
          if (plugin.object[fn]) {
            if (!this.pipes[pipe]) {
              this.pipes[pipe] = [];
            }

            this.pipes[pipe].push((data, callback) => {
              var
                pipeWarnTimer,
                pipeTimeoutTimer;

              if (pipeWarnTime) {
                pipeWarnTimer = setTimeout(() => {
                  this.trigger('log:warn', 'Pipe plugin ' + plugin.name + ' exceeded ' + pipeWarnTime + 'ms to execute.');
                }, pipeWarnTime);
              }

              if (pipeTimeout) {
                pipeTimeoutTimer = setTimeout(() => {
                  var errorMsg = 'Timeout error. Pipe plugin ' + plugin.name + ' exceeded ' + pipeTimeout + 'ms to execute. Aborting pipe';
                  this.trigger('log:error', errorMsg);

                  callback(new GatewayTimeoutError(errorMsg));
                }, pipeTimeout);
              }

              plugin.object[fn](data, function (err, object) {
                if (pipeWarnTimer !== undefined) {
                  clearTimeout(pipeWarnTimer);
                }
                if (pipeTimeoutTimer !== undefined) {
                  clearTimeout(pipeTimeoutTimer);
                }

                callback(err, object);
              });
            });
          }
        });
      }

      if (plugin.object.controllers) {
        _.forEach(plugin.object.controllers, (controller, controllerName) => {
          if (plugin.object[controller]) {
            this.controllers[pluginName+'/'+controllerName] = plugin.object[controller];
          }
        });
      }

      if (plugin.object.routes) {
        async.each(plugin.object.routes, route => {
          route.url = '/_plugin/'+pluginName+route.url;
          route.controller = pluginName+'/'+route.controller;
          this.routes.push(route);
        });
      }

      console.log('Plugin ' + pluginName + ' started');
    });
  };

  /**
   * Trigger an event for emit event and chain pipes
   *
   * @param event
   * @param data
   * @returns {Promise}
   */
  this.trigger = function (event, data) {
    if (this.isDummy) {
      this.kuzzle.emit(event, data);
      return q(data);
    }

    return triggerPipes.call(this, event, data)
      .then(modifiedData => {
        return triggerHooks.call(this, event, modifiedData);
      });
  };

 /**
   * Inject plugin controllers within funnel Controller
   */
  this.injectControllers = function () {
    _.forEach(this.controllers, function (controller, name) {
      kuzzle.funnel[name] = controller();
    });
  };
};

/**
 * Emit event
 * @param event
 * @param data
 * @returns {Promise}
 */
function triggerHooks(event, data) {
  this.kuzzle.emit(event, data);

  return q(data);
}

/**
 * Chain call all attached functions plugins on the specific event
 * @param event
 * @param data
 * @returns {Promise}
 */
function triggerPipes(event, data) {
  var
    deferred = q.defer(),
    preparedPipes = [],
    indexDelimiter,
    wildcard;

  indexDelimiter = event.indexOf(':');
  if (indexDelimiter !== 1) {
    wildcard = event.substring(0, indexDelimiter+1) + '*';
  }

  if (this.pipes && this.pipes[event] && this.pipes[event].length) {
    preparedPipes = this.pipes[event];
  }

  if (wildcard && this.pipes && this.pipes[wildcard] && this.pipes[wildcard].length) {
    preparedPipes = preparedPipes.concat(this.pipes[wildcard]);
  }

  if (preparedPipes.length > 0) {
    // Add a fake function in first position for return the 'data' to other chained functions
    preparedPipes.unshift(function (callback) { callback(null, data); });
  }
  else {
    deferred.resolve(data);
    return deferred.promise;
  }

  async.waterfall(preparedPipes, function (error, result) {
    if (error) {
      return deferred.reject(error);
    }

    deferred.resolve(result);
  });

  return deferred.promise;
}

function constructList(defaultPlugins, customPlugins, isServer) {
  var
    allPlugins = _.extend(defaultPlugins, customPlugins),
    pluginsList = {};

  // Load plugins configuration
  _.forEach(allPlugins, function (plugin, name) {
    if (!plugin.name) {
      plugin.name = name;
    }

    if (!plugin.defaultConfig && !plugin.customConfig) {
      plugin.config = {};
      return true;
    }

    if (!plugin.defaultConfig) {
      plugin.config = plugin.customConfig;
      delete plugin.customConfig;
      return true;
    }

    if (!plugin.customConfig) {
      plugin.config = plugin.defaultConfig;
      delete plugin.defaultConfig;
      return true;
    }

    plugin.config = _.extend(plugin.defaultConfig, plugin.customConfig);

    delete plugin.defaultConfig;
    delete plugin.customConfig;
  });

  /*
   Plugins can be loaded by servers or workers. The "loadedBy" property tells by what type of kuzzle instance
   the plugin should be loaded.
   Accepted values: all, server, worker
   Default value: all
   */
  _.forEach(allPlugins, (plugin, name) => {
    if (plugin.config.loadedBy && plugin.config.loadedBy !== 'all' && (plugin.config.loadedBy === 'server') !== isServer) {
      return true;
    }

    pluginsList[name] = plugin;
  });

  return pluginsList;
}

function loadPlugins(plugins) {
  _.forEach(plugins, (plugin, name) => {
    plugin.object = new (require(name))();
  });
}

/*eslint-enable no-console */
<commit_msg>fixed error log<commit_after>var
  GatewayTimeoutError = require('./errors/gatewayTimeoutError'),
  PluginContext = require('./pluginsContext'),
  fs = require('fs'),
  async = require('async'),
  path = require('path'),
  childProcess = require('child_process'),
  q = require('q'),
  _ = require('lodash'),
  pathConfig = path.join(__dirname, '..', '..', '..', 'config');

/*
 We use the console to display information, as there may be no logger plugin available while installing/launching
 plugins
 */

/*eslint-disable no-console */

module.exports = function PluginsManager (kuzzle) {
  this.kuzzle = kuzzle;
  this.plugins = {};
  this.pipes = {};
  this.controllers = {};
  this.routes = [];
  this.isDummy = false;
  this.isServer = false;
  this.config = kuzzle.config.pluginsManager;

  /**
   * Initialize configured plugin in config/defaultPlugins.json and config/customPlugins.json
   *
   * @param {Boolean} isServer, true if this is a server instance, false for worker instances
   * @param {Boolean} isDummy, true if we are trying to test pluginsManager
   */
  this.init = function (isServer, isDummy) {
    var
      pathDefaultPlugins = path.join(pathConfig, 'defaultPlugins.json'),
      pathCustomPlugins = path.join(pathConfig, 'customPlugins.json'),
      defaultPlugins = require(pathDefaultPlugins),
      customPlugins = {};

    if (!childProcess.hasOwnProperty('execSync')) {
      console.error(' Make sure you\'re using Node version >= 0.12');
      process.exit(1);
    }

    this.isDummy = isDummy;
    this.isServer = isServer;

    if (this.isDummy) {
      return false;
    }

    if (fs.existsSync(pathCustomPlugins)) {
      customPlugins = require(pathCustomPlugins);
    }

    this.plugins = constructList(defaultPlugins, customPlugins, this.isServer);
    loadPlugins(this.plugins);
  };

  /**
   * Attach events hooks and pipes given by plugins
   */
  this.run = function () {
    if (this.isDummy) {
      return false;
    }

    _.forEach(this.plugins, (plugin, pluginName) => {
      var
        context = new PluginContext(kuzzle),
        pipeWarnTime = this.config.pipeWarnTime,
        pipeTimeout = this.config.pipeTimeout;

      if (!plugin.activated) {
        return true;
      }

      console.log('Starting plugin: ', pluginName);

      plugin.object.init(plugin.config, context, this.isDummy);

      if (plugin.object.hooks) {
        _.forEach(plugin.object.hooks, (fn, event) => {
          if (plugin.object[fn]) {
            kuzzle.on(event, function (message) {
              plugin.object[fn](message, event);
            });
          }
        });
      }

      if (plugin.object.pipes) {
        if (plugin.config && plugin.config.pipeWarnTime !== undefined) {
          pipeWarnTime = plugin.config.pipeWarnTime;
        }
        if (plugin.config && plugin.config.pipeTimeout !== undefined) {
          pipeTimeout = plugin.config.pipeTimeout;
        }

        _.forEach(plugin.object.pipes, (fn, pipe) => {
          if (plugin.object[fn]) {
            if (!this.pipes[pipe]) {
              this.pipes[pipe] = [];
            }

            this.pipes[pipe].push((data, callback) => {
              var
                pipeWarnTimer,
                pipeTimeoutTimer;

              if (pipeWarnTime) {
                pipeWarnTimer = setTimeout(() => {
                  this.trigger('log:warn', 'Pipe plugin ' + plugin.name + ' exceeded ' + pipeWarnTime + 'ms to execute.');
                }, pipeWarnTime);
              }

              if (pipeTimeout) {
                pipeTimeoutTimer = setTimeout(() => {
                  var errorMsg = 'Timeout error. Pipe plugin ' + plugin.name + ' exceeded ' + pipeTimeout + 'ms to execute. Aborting pipe';
                  this.trigger('log:error', errorMsg);

                  callback(new GatewayTimeoutError(errorMsg));
                }, pipeTimeout);
              }

              plugin.object[fn](data, function (err, object) {
                if (pipeWarnTimer !== undefined) {
                  clearTimeout(pipeWarnTimer);
                }
                if (pipeTimeoutTimer !== undefined) {
                  clearTimeout(pipeTimeoutTimer);
                }

                callback(err, object);
              });
            });
          }
        });
      }

      if (plugin.object.controllers) {
        _.forEach(plugin.object.controllers, (controller, controllerName) => {
          if (plugin.object[controller]) {
            this.controllers[pluginName+'/'+controllerName] = plugin.object[controller];
          }
        });
      }

      if (plugin.object.routes) {
        async.each(plugin.object.routes, route => {
          route.url = '/_plugin/'+pluginName+route.url;
          route.controller = pluginName+'/'+route.controller;
          this.routes.push(route);
        });
      }

      console.log('Plugin ' + pluginName + ' started');
    });
  };

  /**
   * Trigger an event for emit event and chain pipes
   *
   * @param event
   * @param data
   * @returns {Promise}
   */
  this.trigger = function (event, data) {
    if (this.isDummy) {
      this.kuzzle.emit(event, data);
      return q(data);
    }

    return triggerPipes.call(this, event, data)
      .then(modifiedData => {
        return triggerHooks.call(this, event, modifiedData);
      });
  };

 /**
   * Inject plugin controllers within funnel Controller
   */
  this.injectControllers = function () {
    _.forEach(this.controllers, function (controller, name) {
      kuzzle.funnel[name] = controller();
    });
  };
};

/**
 * Emit event
 * @param event
 * @param data
 * @returns {Promise}
 */
function triggerHooks(event, data) {
  this.kuzzle.emit(event, data);

  return q(data);
}

/**
 * Chain call all attached functions plugins on the specific event
 * @param event
 * @param data
 * @returns {Promise}
 */
function triggerPipes(event, data) {
  var
    deferred = q.defer(),
    preparedPipes = [],
    indexDelimiter,
    wildcard;

  indexDelimiter = event.indexOf(':');
  if (indexDelimiter !== 1) {
    wildcard = event.substring(0, indexDelimiter+1) + '*';
  }

  if (this.pipes && this.pipes[event] && this.pipes[event].length) {
    preparedPipes = this.pipes[event];
  }

  if (wildcard && this.pipes && this.pipes[wildcard] && this.pipes[wildcard].length) {
    preparedPipes = preparedPipes.concat(this.pipes[wildcard]);
  }

  if (preparedPipes.length > 0) {
    // Add a fake function in first position for return the 'data' to other chained functions
    preparedPipes.unshift(function (callback) { callback(null, data); });
  }
  else {
    deferred.resolve(data);
    return deferred.promise;
  }

  async.waterfall(preparedPipes, function (error, result) {
    if (error) {
      return deferred.reject(error);
    }

    deferred.resolve(result);
  });

  return deferred.promise;
}

function constructList(defaultPlugins, customPlugins, isServer) {
  var
    allPlugins = _.extend(defaultPlugins, customPlugins),
    pluginsList = {};

  // Load plugins configuration
  _.forEach(allPlugins, function (plugin, name) {
    if (!plugin.name) {
      plugin.name = name;
    }

    if (!plugin.defaultConfig && !plugin.customConfig) {
      plugin.config = {};
      return true;
    }

    if (!plugin.defaultConfig) {
      plugin.config = plugin.customConfig;
      delete plugin.customConfig;
      return true;
    }

    if (!plugin.customConfig) {
      plugin.config = plugin.defaultConfig;
      delete plugin.defaultConfig;
      return true;
    }

    plugin.config = _.extend(plugin.defaultConfig, plugin.customConfig);

    delete plugin.defaultConfig;
    delete plugin.customConfig;
  });

  /*
   Plugins can be loaded by servers or workers. The "loadedBy" property tells by what type of kuzzle instance
   the plugin should be loaded.
   Accepted values: all, server, worker
   Default value: all
   */
  _.forEach(allPlugins, (plugin, name) => {
    if (plugin.config.loadedBy && plugin.config.loadedBy !== 'all' && (plugin.config.loadedBy === 'server') !== isServer) {
      return true;
    }

    pluginsList[name] = plugin;
  });

  return pluginsList;
}

function loadPlugins(plugins) {
  _.forEach(plugins, (plugin, name) => {
    plugin.object = new (require(name))();
  });
}

/*eslint-enable no-console */
<|endoftext|>


<commit_before>import React from 'react';
import {connectToStores} from 'fluxible-addons-react';
import {NavLink, navigateAction} from 'fluxible-router';
import ImportStore from '../../stores/ImportStore';
import storeFile from '../../actions/import/storeFile';
import importFinished from '../../actions/import/importFinished';
//import FileUploader from './FileUploader';
let ReactDOM = require('react-dom');
let classNames = require('classnames');
//TODO - nice feature (later/non-critical) = drag & drop + upload multiple files

const MAX_FILESIZE_MB = 300;
const MAX_FILESIZE = MAX_FILESIZE_MB * 1024 * 1024;

class Import extends React.Component {
    constructor(props) {
        super(props);
    }
    componentDidMount(){

    }
    componentDidUpdate(){
        if (this.props.ImportStore.file === null)
            $('#import_file_chooser').val('');
    }
    handleFileSelect(evt){
        //console.log('handleFileSelect()');

        this.context.executeAction(importFinished, null);

        console.log(evt.target.files[0]);
        let file = evt.target.files[0];
        if (file === null || file === undefined)
            file = {
                type: '',
                size: 0
            };

        //check metadata like size, file ending, ...
        let isCorrect = true;
        const filetype = file.type;
        const size = file.size;
        isCorrect = ( filetype === 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ) && ( size < MAX_FILESIZE );

        if (isCorrect) {
            let reader = new FileReader();

            function errorHandler(evt) {
                switch(evt.target.error.code) {
                    case evt.target.error.NOT_FOUND_ERR:
                        console.error('File Not Found!');
                        break;
                    case evt.target.error.NOT_READABLE_ERR:
                        console.error('File is not readable');
                        break;
                    case evt.target.error.ABORT_ERR:
                        console.info('Cancel clicked');
                        break; // noop
                    default:
                        console.error('An error occurred reading this file.', evt.target.error);
                };
            }

            let that = this;

            // Closures to capture the file information/data
            reader.onloadend = (function(theFile) {
                return function(e) {
                    console.log('file was read: ', file);
                    console.log('also', theFile);
                    console.log(e.target.result.length, 'bytes');

                    //Save it to store
                    const payload = {
                        file: file ? file : theFile,
                        base64: e.target.result
                    };
                    that.context.executeAction(storeFile, payload);
                };
            })();
            reader.onerror = errorHandler;
            reader.onabort = function(e) {
                console.error('File read cancelled');
            };

            // Read in the file
            reader.readAsDataURL(file);

            ReactDOM.findDOMNode(this.refs.submitbutton).focus();
        }

        return false;
    }
    render() {
        //variable for intermediate storage of output
        let outputDIV = '';

        let uploadBtn_classes = classNames({
            'ui': true,
            'animated': true,
            'approve': true,
            'disabled': !this.props.ImportStore.fileReadyForUpload,
            'button': true,
            'green': true
        });

        outputDIV =

Upload your presentation

Select your presentation file and upload it to SlideWiki.

Only PowerPoint (.pptx) is supported (Max size: {MAX_FILESIZE_MB}MB).

                                    <input ref="selectbutton" accept="application/vnd.openxmlformats-officedocument.presentationml.presentation" type="file" tabIndex="0" onChange={this.handleFileSelect.bind(this)} id="import_file_chooser" ></input>

                              <button tabIndex="0" ref="submitbutton" className={uploadBtn_classes} >

Upload

To SlideWiki

                              </button>

Cancel

;

                      //from https://github.com/risis-eu/risis-datasets/blob/2a790c3b20b6c83c775d144cd69393032cdfaf82/components/object/ObjectIEditor.js
                      //editor = <FileUploader spec={this.props.spec} config={this.props.config} onDataEdit={this.handleDataEdit.bind(this)} onEnterPress={this.handleEnterPress.bind(this)} allowActionByKey="1"/>;

        return outputDIV;
    }
}

Import.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};
Import = connectToStores(Import, [ImportStore], (context, props) => {
    return {
        ImportStore: context.getStore(ImportStore).getState()
    };
});
export default Import;
<commit_msg>Restricting file formats in the input tag.<commit_after>import React from 'react';
import {connectToStores} from 'fluxible-addons-react';
import {NavLink, navigateAction} from 'fluxible-router';
import ImportStore from '../../stores/ImportStore';
import storeFile from '../../actions/import/storeFile';
import importFinished from '../../actions/import/importFinished';
//import FileUploader from './FileUploader';
let ReactDOM = require('react-dom');
let classNames = require('classnames');
//TODO - nice feature (later/non-critical) = drag & drop + upload multiple files

const MAX_FILESIZE_MB = 300;
const MAX_FILESIZE = MAX_FILESIZE_MB * 1024 * 1024;

class Import extends React.Component {
    constructor(props) {
        super(props);
    }
    componentDidMount(){

    }
    componentDidUpdate(){
        if (this.props.ImportStore.file === null)
            $('#import_file_chooser').val('');
    }
    handleFileSelect(evt){
        //console.log('handleFileSelect()');

        this.context.executeAction(importFinished, null);

        console.log(evt.target.files[0]);
        let file = evt.target.files[0];
        if (file === null || file === undefined)
            file = {
                type: '',
                size: 0
            };

        //check metadata like size, file ending, ...
        let isCorrect = true;
        const filetype = file.type;
        const size = file.size;
        isCorrect = ( filetype === 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ) && ( size < MAX_FILESIZE );

        if (isCorrect) {
            let reader = new FileReader();

            function errorHandler(evt) {
                switch(evt.target.error.code) {
                    case evt.target.error.NOT_FOUND_ERR:
                        console.error('File Not Found!');
                        break;
                    case evt.target.error.NOT_READABLE_ERR:
                        console.error('File is not readable');
                        break;
                    case evt.target.error.ABORT_ERR:
                        console.info('Cancel clicked');
                        break; // noop
                    default:
                        console.error('An error occurred reading this file.', evt.target.error);
                };
            }

            let that = this;

            // Closures to capture the file information/data
            reader.onloadend = (function(theFile) {
                return function(e) {
                    console.log('file was read: ', file);
                    console.log('also', theFile);
                    console.log(e.target.result.length, 'bytes');

                    //Save it to store
                    const payload = {
                        file: file ? file : theFile,
                        base64: e.target.result
                    };
                    that.context.executeAction(storeFile, payload);
                };
            })();
            reader.onerror = errorHandler;
            reader.onabort = function(e) {
                console.error('File read cancelled');
            };

            // Read in the file
            reader.readAsDataURL(file);

            ReactDOM.findDOMNode(this.refs.submitbutton).focus();
        }

        return false;
    }
    render() {
        //variable for intermediate storage of output
        let outputDIV = '';

        let uploadBtn_classes = classNames({
            'ui': true,
            'animated': true,
            'approve': true,
            'disabled': !this.props.ImportStore.fileReadyForUpload,
            'button': true,
            'green': true
        });
        let acceptedFormats = '.key, .odp, .pps, .ppsx, .ppt, .pptm, .pptx,  ';
        outputDIV =

Upload your presentation

Select your presentation file and upload it to SlideWiki.

Only PowerPoint (.pptx) is supported (Max size: {MAX_FILESIZE_MB}MB).

                                    <input ref="selectbutton" accept={ acceptedFormats + "application/vnd.openxmlformats-officedocument.presentationml.presentation"} type="file" tabIndex="0" onChange={this.handleFileSelect.bind(this)} id="import_file_chooser" ></input>

                              <button tabIndex="0" ref="submitbutton" className={uploadBtn_classes} >

Upload

To SlideWiki

                              </button>

Cancel

;

                      //from https://github.com/risis-eu/risis-datasets/blob/2a790c3b20b6c83c775d144cd69393032cdfaf82/components/object/ObjectIEditor.js
                      //editor = <FileUploader spec={this.props.spec} config={this.props.config} onDataEdit={this.handleDataEdit.bind(this)} onEnterPress={this.handleEnterPress.bind(this)} allowActionByKey="1"/>;

        return outputDIV;
    }
}

Import.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};
Import = connectToStores(Import, [ImportStore], (context, props) => {
    return {
        ImportStore: context.getStore(ImportStore).getState()
    };
});
export default Import;
<|endoftext|>


<commit_before>var config = require('../config/app');
var _ = require('underscore');
var moment = require('moment');
var MongoClient = require('mongodb').MongoClient;
var ObjectId = require('mongodb').ObjectId;
var db;
MongoClient.connect(config.mongoUrl, function(err, database) {
  db = database;
});

var JOB_ACTIVE = "Active";
var JOB_ERROR = "Error";
var JOB_COMPLETE = "Complete";

var command = {
  upload: function(req, res) {
    var uploadSuccesses = 0;

    if(req.body) {
      var recordType = req.body.type;
      startJob(recordType, startUpload);
      db.collection(recordType).createIndex({
        action_date: -1,
        library_code: 1
      });
    }
    res.end('');

    function startUpload(jobId) {
      var collection = db.collection(recordType);
      insert(req.body.rows);

      function insert(values) {
        if(values.length > 0) {
          var tmpValues = _.map(_.first(values, 1000), function(value) {
            value.job_id = jobId;
            value.action_date = moment(value.action_date, 'YYYY-MM-DD HH:mm:ss').toDate();
            return value;
          });
          collection.insertMany(tmpValues, function(err, result) {
            if(err) {
              console.log(err);
              finish(JOB_ERROR);
            }
            else {
              uploadSuccesses += result.insertedCount;
              updateJob(jobId, JOB_ACTIVE, uploadSuccesses);
              insert(_.rest(values, 1000));
            }
          });
        }
        else {
          finish(JOB_COMPLETE);
        }
      }

      function finish(state) {
        updateJob(jobId, state, uploadSuccesses);
      }
    }
  },
}

function startJob(type, callback) {
  db.collection('jobs').insertOne({
    type: type,
    start: new Date(),
    updated: new Date(),
    state: JOB_ACTIVE,
    processed: 0
  }, function(err, result) {
    if(err) {
      console.log(err);
    }
    else {
      callback(result.insertedId);
    }
  });
}

function updateJob(id, state, processed) {
  db.collection('jobs').updateOne({_id: id}, {$set: {
    updated: new Date(),
    state: state,
    processed: processed
  }});
}

module.exports = command;<commit_msg>Make index creation a background operation<commit_after>var config = require('../config/app');
var _ = require('underscore');
var moment = require('moment');
var MongoClient = require('mongodb').MongoClient;
var ObjectId = require('mongodb').ObjectId;
var db;
MongoClient.connect(config.mongoUrl, function(err, database) {
  db = database;
});

var JOB_ACTIVE = "Active";
var JOB_ERROR = "Error";
var JOB_COMPLETE = "Complete";

var command = {
  upload: function(req, res) {
    var uploadSuccesses = 0;

    if(req.body) {
      var recordType = req.body.type;
      startJob(recordType, startUpload);
      db.collection(recordType).createIndex({
        action_date: -1,
        library_code: 1
      }, {
        background: true
      });
    }
    res.end('');

    function startUpload(jobId) {
      var collection = db.collection(recordType);
      insert(req.body.rows);

      function insert(values) {
        if(values.length > 0) {
          var tmpValues = _.map(_.first(values, 1000), function(value) {
            value.job_id = jobId;
            value.action_date = moment(value.action_date, 'YYYY-MM-DD HH:mm:ss').toDate();
            return value;
          });
          collection.insertMany(tmpValues, function(err, result) {
            if(err) {
              console.log(err);
              finish(JOB_ERROR);
            }
            else {
              uploadSuccesses += result.insertedCount;
              updateJob(jobId, JOB_ACTIVE, uploadSuccesses);
              insert(_.rest(values, 1000));
            }
          });
        }
        else {
          finish(JOB_COMPLETE);
        }
      }

      function finish(state) {
        updateJob(jobId, state, uploadSuccesses);
      }
    }
  },
}

function startJob(type, callback) {
  db.collection('jobs').insertOne({
    type: type,
    start: new Date(),
    updated: new Date(),
    state: JOB_ACTIVE,
    processed: 0
  }, function(err, result) {
    if(err) {
      console.log(err);
    }
    else {
      callback(result.insertedId);
    }
  });
}

function updateJob(id, state, processed) {
  db.collection('jobs').updateOne({_id: id}, {$set: {
    updated: new Date(),
    state: state,
    processed: processed
  }});
}

module.exports = command;<|endoftext|>


<commit_before>/*
 * grunt-makedocs
 * https://github.com/chris5marsh/makedocs
 *
 * Copyright (c) 2015 Chris Marsh
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  grunt.registerMultiTask('makedocs', "Make your documentation using Grunt", function() {

    var beautify = require('js-beautify').html;
    var yfm = require('yaml-front-matter');
    var diveSync = require('diveSync');
    var cheerio = require('cheerio');
    var marked = require('marked');
    var path = require('path');
    var vm = require('vm');
    var fs = require('fs');
    var Handlebars = require('handlebars');

    var options = this.options({
      layoutsDir: './layouts',
      partialsDir: './partials',
      componentsDir: './components',
      build: false,
      nav: false
    });

    var templates = {};

    // Could move this out seperately? So that front and back end could use it?
    // Needs compiled Handlebars templates (OK), forEach and Object.keys
    function component(type, config) {

      var template = templates[type];

      // if config is just a string, it's simple content
      var configType = toType(config);
      if (configType === 'string') {
        config = { "content": config };
      }

      // if config has an atoms key, compile those and replace atom object with HTML
      var atomsType = toType(config.atoms);
      if (atomsType !== 'undefined') {
        if (atomsType === 'object') {
          // Just a single atom
          var c = Object.keys(config.atoms)[0];
          var o = config.atoms[c];
          o.parentConfig = config;
          var atomHTML = component(c, o);
          config.atoms = [atomHTML];
        } else if (atomsType === 'array') {
          // Multiple atoms
          config.atoms.forEach(function(atom, i) {
            var c, o;
            if (toType(atom.component) !== 'undefined') {
              // object passed with "component" key and "options" key
              c = atom.component;
              o = atom.options;
             } else {
              // Shorthand { "component": "options" } object
              c = Object.keys(atom)[0];
              o = atom[c];
            }
            o.parentConfig = config;
            var atomHTML = component(c, o);
            config.atoms[i] = atomHTML;
          });
        }
      }

      // compile it with options
      var html = template(config);
      return html;

    }

    // https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
    var toType = function(obj) {
      return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
    }

    function MAKEDOCS(task) {

      this.task = task;
      this.templates = {};
      this.pages = [];

      this.run = function() {

        this.setupPartials();
        this.setupComponents();

        task.files.forEach(function(file) {

          file.src.filter(function(filepath) {
            // Remove nonexistent files
            if (!grunt.file.exists(filepath)) {
              grunt.log.warn('Source file "' + filepath + '" not found.');
              return false;
            } else {
              return true;
            }
          }).map(function(filepath) {
            // Read and return the file's YAML front-matter
            var contents = grunt.file.read(filepath);
            var config = yfm.loadFront(contents);
            if (typeof config.layout === 'undefined') {
              config.layout = 'default';
            }
            config.content = marked(config['__content']);
            delete config['__content'];
            config.dest = path.resolve(file.dest);
            config.build = options.build;
            this.pages.push(config);
          }, this);

        }, this);

        // Set up nav if needed
        if (options.nav && typeof options.nav === 'function') {
          options.nav(this.pages);
        }

        // Make all the pages
        this.pages.forEach(function(page, i) {
          this.makeLayout(page);
        }, this);

      };

      // Set up partials
      this.setupPartials = function() {
        var partialsPath = path.resolve(options.partialsDir);
        diveSync(partialsPath, function(err, file) {
          var partialName = path.basename(file, path.extname(file));
          var partialHTML = grunt.file.read(file);
          Handlebars.registerPartial(partialName, partialHTML);
        });
      };

      // Compile component templates
      this.setupComponents = function() {
        var componentsPath = path.resolve(options.componentsDir);
        diveSync(componentsPath, function(err, file) {
          var componentName = path.basename(file, path.extname(file));
          var componentHTML = grunt.file.read(file);
          templates[componentName] = Handlebars.compile(componentHTML, { noEscape: true });
        });
      };

      // Create HTML from layout template
      this.makeLayout = function(config) {

        var writePath = path.resolve(config.dest);
        var layoutPath = path.resolve(path.join(options.layoutsDir, config.layout+'.mustache'));
        var layoutHTML = grunt.file.read(layoutPath);
        var template = Handlebars.compile(layoutHTML, { noEscape: true });
        var html = template(config);
        // Better to precompile scripts into components instead of doing it at runtime
        this.addComponents(html, function(err, completeHTML) {
          if (err) {
            grunt.log.warn('Could not add components');
          }
          grunt.file.write(writePath, completeHTML);
          grunt.log.ok("Wrote file to " + writePath);
        });

      };

      // This is a horrible way to get the JS function calls
      this.addComponents = function(html, onComplete) {

        var $ = cheerio.load(html);
        // Get all the scripts
        var scripts = $('body script');
        var htmlEntities = this.htmlEntities;
        var beautifyOptions = {
          "indent_size": 2,
          "indent_char": " "
        }
        // For each script, get the individual lines
        if (scripts.length === 0) {
          onComplete(null, html);
        }
        scripts.each(function(i, script) {
          var scriptContent = '';
          // Remove blank lines
          if (script.children.length !== 0) {
            script.children[0].data.split(';').filter(function(l) {
              if (l === '' || l === null || l === false) {
                return false;
              }
              return true;
            }).map(function(l) {
              // Eval HTML in new context - pass component function into context
              var ev = vm.runInNewContext(l, { component: component });
              if (typeof ev === 'undefined') {
                return;
              }
              scriptContent+= '\n'+ev;
            });
            scriptContent = beautify(scriptContent, beautifyOptions);
            // If we're doing documentation
            var docContent = '
';
            docContent+= htmlEntities(scriptContent);
            docContent+= '\n
';
            $(script).after('\n\n'+docContent).after('\n\n'+scriptContent).remove();
          }
          if (i === scripts.length - 1) {
            onComplete(null, $.html());
          }
        });

      };

      this.htmlEntities = function(str) {
        return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
      };

    }

    var makedocs = new MAKEDOCS(this);
    makedocs.run();

  });

};
<commit_msg>Not decoding entities - ' was messing up <IE8<commit_after>/*
 * grunt-makedocs
 * https://github.com/chris5marsh/makedocs
 *
 * Copyright (c) 2015 Chris Marsh
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  grunt.registerMultiTask('makedocs', "Make your documentation using Grunt", function() {

    var beautify = require('js-beautify').html;
    var yfm = require('yaml-front-matter');
    var diveSync = require('diveSync');
    var cheerio = require('cheerio');
    var marked = require('marked');
    var path = require('path');
    var vm = require('vm');
    var fs = require('fs');
    var Handlebars = require('handlebars');

    var options = this.options({
      layoutsDir: './layouts',
      partialsDir: './partials',
      componentsDir: './components',
      build: false,
      nav: false
    });

    var templates = {};

    // Could move this out seperately? So that front and back end could use it?
    // Needs compiled Handlebars templates (OK), forEach and Object.keys
    function component(type, config) {

      var template = templates[type];

      // if config is just a string, it's simple content
      var configType = toType(config);
      if (configType === 'string') {
        config = { "content": config };
      }

      // if config has an atoms key, compile those and replace atom object with HTML
      var atomsType = toType(config.atoms);
      if (atomsType !== 'undefined') {
        if (atomsType === 'object') {
          // Just a single atom
          var c = Object.keys(config.atoms)[0];
          var o = config.atoms[c];
          o.parentConfig = config;
          var atomHTML = component(c, o);
          config.atoms = [atomHTML];
        } else if (atomsType === 'array') {
          // Multiple atoms
          config.atoms.forEach(function(atom, i) {
            var c, o;
            if (toType(atom.component) !== 'undefined') {
              // object passed with "component" key and "options" key
              c = atom.component;
              o = atom.options;
             } else {
              // Shorthand { "component": "options" } object
              c = Object.keys(atom)[0];
              o = atom[c];
            }
            o.parentConfig = config;
            var atomHTML = component(c, o);
            config.atoms[i] = atomHTML;
          });
        }
      }

      // compile it with options
      var html = template(config);
      return html;

    }

    // https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
    var toType = function(obj) {
      return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
    }

    function MAKEDOCS(task) {

      this.task = task;
      this.templates = {};
      this.pages = [];

      this.run = function() {

        this.setupPartials();
        this.setupComponents();

        task.files.forEach(function(file) {

          file.src.filter(function(filepath) {
            // Remove nonexistent files
            if (!grunt.file.exists(filepath)) {
              grunt.log.warn('Source file "' + filepath + '" not found.');
              return false;
            } else {
              return true;
            }
          }).map(function(filepath) {
            // Read and return the file's YAML front-matter
            var contents = grunt.file.read(filepath);
            var config = yfm.loadFront(contents);
            if (typeof config.layout === 'undefined') {
              config.layout = 'default';
            }
            config.content = marked(config['__content']);
            delete config['__content'];
            config.dest = path.resolve(file.dest);
            config.build = options.build;
            this.pages.push(config);
          }, this);

        }, this);

        // Set up nav if needed
        if (options.nav && typeof options.nav === 'function') {
          options.nav(this.pages);
        }

        // Make all the pages
        this.pages.forEach(function(page, i) {
          this.makeLayout(page);
        }, this);

      };

      // Set up partials
      this.setupPartials = function() {
        var partialsPath = path.resolve(options.partialsDir);
        diveSync(partialsPath, function(err, file) {
          var partialName = path.basename(file, path.extname(file));
          var partialHTML = grunt.file.read(file);
          Handlebars.registerPartial(partialName, partialHTML);
        });
      };

      // Compile component templates
      this.setupComponents = function() {
        var componentsPath = path.resolve(options.componentsDir);
        diveSync(componentsPath, function(err, file) {
          var componentName = path.basename(file, path.extname(file));
          var componentHTML = grunt.file.read(file);
          templates[componentName] = Handlebars.compile(componentHTML, { noEscape: true });
        });
      };

      // Create HTML from layout template
      this.makeLayout = function(config) {

        var writePath = path.resolve(config.dest);
        var layoutPath = path.resolve(path.join(options.layoutsDir, config.layout+'.mustache'));
        var layoutHTML = grunt.file.read(layoutPath);
        var template = Handlebars.compile(layoutHTML, { noEscape: true });
        var html = template(config);
        // Better to precompile scripts into components instead of doing it at runtime
        this.addComponents(html, function(err, completeHTML) {
          if (err) {
            grunt.log.warn('Could not add components');
          }
          grunt.file.write(writePath, completeHTML);
          grunt.log.ok("Wrote file to " + writePath);
        });

      };

      // This is a horrible way to get the JS function calls
      this.addComponents = function(html, onComplete) {

        var $ = cheerio.load(html, { decodeEntities: false });
        // Get all the scripts
        var scripts = $('body script');
        var htmlEntities = this.htmlEntities;
        var beautifyOptions = {
          "indent_size": 2,
          "indent_char": " "
        }
        // For each script, get the individual lines
        if (scripts.length === 0) {
          onComplete(null, html);
        }
        scripts.each(function(i, script) {
          var scriptContent = '';
          // Remove blank lines
          if (script.children.length !== 0) {
            script.children[0].data.split(';').filter(function(l) {
              if (l === '' || l === null || l === false) {
                return false;
              }
              return true;
            }).map(function(l) {
              // Eval HTML in new context - pass component function into context
              var ev = vm.runInNewContext(l, { component: component });
              if (typeof ev === 'undefined') {
                return;
              }
              scriptContent+= '\n'+ev;
            });
            scriptContent = beautify(scriptContent, beautifyOptions);
            // If we're doing documentation
            var docContent = '
';
            docContent+= htmlEntities(scriptContent);
            docContent+= '\n
';
            $(script).after('\n\n'+docContent).after('\n\n'+scriptContent).remove();
          }
          if (i === scripts.length - 1) {
            onComplete(null, $.html());
          }
        });

      };

      this.htmlEntities = function(str) {
        return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
      };

    }

    var makedocs = new MAKEDOCS(this);
    makedocs.run();

  });

};
<|endoftext|>


<commit_before>var timeoverride = null;

window.onerror = function(message, url, lineNumber) {
  document.getElementById("loading").innerHTML = "Error loading page
" + message + " Line " + lineNumber;
  return true;
};

//on DOM load
document.addEventListener('DOMContentLoaded', function() {
  var element = document.querySelector('.loadprogress');

  var mp = new ElasticProgress(element, {
    buttonSize: 60,
    fontFamily: "Montserrat",
    colorBg: "#7dbde8",
    colorFg: "#0f3a57",
    onClose:function(){
      document.getElementById("loading").style.visibility = 'hidden';
      document.getElementById("results").style.visibility = 'visible';
    }
  });

  // SET ON CLOSE TO RUN THIS

  //run every X period of time the main loop.
  startTimer(180);

  RunForestRun(mp);
});

$(document).on('change', 'input[name=slide]:radio', function() {
  console.log(this.value);
  timeoverride = (this.value == "reset" ? null : this.value);
  RunForestRun();
});

//refresh button
$(document).ready(function() {
  document.getElementById("refresh").onclick = function() {
    RunForestRun();
  }
});

function myScript() {
  console.log("radio");
}

function getSearchParameters() {
  var prmstr = window.location.search.substr(1);
  return prmstr != null && prmstr != "" ? transformToAssocArray(prmstr) : {};
}

function transformToAssocArray(prmstr) {
  var params = {};
  var prmarr = prmstr.split("&");
  for (var i = 0; i < prmarr.length; i++) {
    var tmparr = prmarr[i].split("=");
    params[tmparr[0]] = tmparr[1];
  }
  return params;
}

var timeperiod;
var unit = null;

var params = getSearchParameters();

//update every X seconds
function startTimer(duration) {
  var display = document.querySelector('#time');
  var timer = duration,
  minutes, seconds;
  setInterval(function() {
    minutes = parseInt(timer / 60, 10)
    seconds = parseInt(timer % 60, 10);

    minutes = minutes < 10 ? "0" + minutes : minutes;
    seconds = seconds < 10 ? "0" + seconds : seconds;

    display.innerText = minutes + ":" + seconds;

    if (--timer < 0) { //when the timer is 0 run the code
      timer = duration;
      RunForestRun();
    }
  }, 1000);
}

//Get times vars for the call
function RunForestRun(mp) {

  mp && mp.open();

  if (timeoverride !== null) { //we are using a time override

    var end = new Date();

    var start = new Date();
    start.setDate(start.getDate() - (timeoverride / 24));

    starttime = start.toISOString();
    endtime = end.toISOString();

    console.log(starttime);
    console.log(endtime);

    params.start = starttime;
    params.end = endtime;

  } else {
    params = getSearchParameters();
  }

  if (unit == null) {
    console.log("firstrun...will fetch vars");

    if (typeof params.hq !== 'undefined') {
      if (params.hq.split(",").length == 1) { //one HQ was passed
        GetUnitNamefromBeacon(params.hq, params.host, function(result) {
          unit = result;
          HackTheMatrix(unit, params.host,mp);
        });
      } else {
        unit = [];
        console.log("passed array of units");
        var hqsGiven = params.hq.split(",");
        console.log(hqsGiven);
        hqsGiven.forEach(function(d) {
          GetUnitNamefromBeacon(d, params.host, function(result) {
            unit.push(result);
            if (unit.length == params.hq.split(",").length) {
              HackTheMatrix(unit, params.host,mp);
            }
          });
        });
      }
    } else { //no hq was sent, get them all
      unit = [];
      HackTheMatrix(unit, params.host,mp);
    }
  } else {
    console.log("rerun...will NOT fetch vars");
    HackTheMatrix(unit, params.host);
  }

}

//make the call to beacon
function HackTheMatrix(unit, host, progressBar) {

  var start = new Date(decodeURIComponent(params.start));
  var end = new Date(decodeURIComponent(params.end));

  GetJSONfromBeacon(unit, host, start, end,
    function(jobs) {
      var facts = crossfilter(jobs.Results);
      var all = facts.groupAll();

      var JobStatus = facts.dimension(function(d) {
        return d.JobStatusType.Name;
      });

      var JobType = facts.dimension(function(d) {
        return d.JobType.ParentId;
      });

      var JobStatusGroup = JobStatus.group().reduceCount(function(d) {
        return d.JobStatusType.Name;
      });
      var JobTypeGroup = JobType.group().reduceCount(function(d) {
        return d.JobType.ParentId;
      });

      var completeJob = 0;
      var newJob = 0;
      var ackJob = 0;
      var refJob = 0;
      var finJob = 0;
      var canJob = 0;
      var rejJob = 0;
      var tskJob = 0;

      JobStatusGroup.top(Infinity).forEach(function(d) {
        console.log(d.key + " " + d.value);
        switch (d.key) {
          case "New":
            newJob = d.value;
            break;
          case "Acknowledged":
            ackJob = d.value;
            break;
          case "Tasked":
            tskJob = d.value;
            break;
          case "Complete":
            completeJob = d.value;
            break;
          case "Finalised":
            finJob = d.value;
            break;
          case "Referred":
            refJob = d.value;
            break;
          case "Rejected":
            rejJob = d.value;
            break;
          case "Cancelled":
            canJob = d.value;
            break;
        }
      });

      var storm = 0;
      var flood = 0;
      var rescue = 0;
      var support = 0;

      JobTypeGroup.top(Infinity).forEach(function(d) {
          switch (d.key) {
            case 1: // Parent: Storm
              storm = d.value;
              break;
            case 2: // Parent: Support
              support = d.value;
              break;
            case 4: // Parent: Flood Assistance
              flood = d.value;
              break;
            case 5: // Parent: Rescue
              rescue = d.value;
              break;
          }
      });

      document.getElementById("new").innerHTML = newJob + "
" + Math.round(newJob / jobs.Results.length * 100) + "%
";
      document.getElementById("ack").innerHTML = ackJob + "
" + Math.round(ackJob / jobs.Results.length * 100) + "%
";
      document.getElementById("comp").innerHTML = completeJob + "
" + Math.round(completeJob / jobs.Results.length * 100) + "%
";
      document.getElementById("ref").innerHTML = refJob + "
" + Math.round(refJob / jobs.Results.length * 100) + "%
";
      document.getElementById("can").innerHTML = canJob + "
" + Math.round(canJob / jobs.Results.length * 100) + "%
";
      document.getElementById("rej").innerHTML = rejJob + "
" + Math.round(rejJob / jobs.Results.length * 100) + "%
";
      document.getElementById("tsk").innerHTML = tskJob + "
" + Math.round(tskJob / jobs.Results.length * 100) + "%
";
      document.getElementById("fin").innerHTML = finJob + "
" + Math.round(finJob / jobs.Results.length * 100) + "%
";

      document.getElementById("support").innerHTML = support + "
" + Math.round(support / jobs.Results.length * 100) + "%
";
      document.getElementById("flood").innerHTML = flood + "
" + Math.round(flood / jobs.Results.length * 100) + "%
";
      document.getElementById("rescue").innerHTML = rescue + "
" + Math.round(rescue / jobs.Results.length * 100) + "%
";
      document.getElementById("storm").innerHTML = storm + "
" + Math.round(storm / jobs.Results.length * 100) + "%
";
      var open = newJob + ackJob + tskJob + refJob;
      var closed = canJob + completeJob + finJob + rejJob;

      document.getElementById("open").innerHTML = open + "
" + Math.round(open / jobs.Results.length * 100) + "%
";
      document.getElementById("closed").innerHTML = closed + "
" + Math.round(closed / jobs.Results.length * 100) + "%
";
      document.getElementById("totalnumber").innerHTML = jobs.Results.length + "
" + Math.round(jobs.Results.length / jobs.Results.length * 100) + "%
";

      var options = {
        weekday: "short",
        year: "numeric",
        month: "2-digit",
        day: "numeric",
        hour12: false
      };

      if (unit.length == 0) { //whole nsw state
        document.title = "NSW Job Summary";
        document.getElementById("banner").innerHTML = "
Job summary for NSW
";
      } else {
        if (Array.isArray(unit) == false) { //1 lga
          document.title = unit.Name + " Job Summary";
          document.getElementById("banner").innerHTML = '
Job summary for ' + unit.Name + "
";
        }
        if (unit.length == 1) { //more than one
          document.title = "Group Job Summary";
          document.getElementById("banner").innerHTML = "
Job summary for Group
";
        };
      }

      document.getElementById("banner").innerHTML = document.getElementById("banner").innerHTML + "
" + start.toLocaleTimeString("en-au", options) + " to " + end.toLocaleTimeString("en-au", options) + "
";

      progressBar && progressBar.setValue(1);
      progressBar && progressBar.close();

    },
    function(val,total){
      if (progressBar) { //if its a first load
        if (val == -1 && total == -1) {
          progressBar.fail();
        } else {
          progressBar.setValue(val/total)
        }
      }
    }
  );

}
<commit_msg>remove old code<commit_after>var timeoverride = null;

window.onerror = function(message, url, lineNumber) {
  document.getElementById("loading").innerHTML = "Error loading page
" + message + " Line " + lineNumber;
  return true;
};

//on DOM load
document.addEventListener('DOMContentLoaded', function() {
  var element = document.querySelector('.loadprogress');

  var mp = new ElasticProgress(element, {
    buttonSize: 60,
    fontFamily: "Montserrat",
    colorBg: "#7dbde8",
    colorFg: "#0f3a57",
    onClose:function(){
      document.getElementById("loading").style.visibility = 'hidden';
      document.getElementById("results").style.visibility = 'visible';
    }
  });

  // SET ON CLOSE TO RUN THIS

  //run every X period of time the main loop.
  startTimer(180);

  RunForestRun(mp);
});

$(document).on('change', 'input[name=slide]:radio', function() {
  console.log(this.value);
  timeoverride = (this.value == "reset" ? null : this.value);
  RunForestRun();
});

//refresh button
$(document).ready(function() {
  document.getElementById("refresh").onclick = function() {
    RunForestRun();
  }
});

function getSearchParameters() {
  var prmstr = window.location.search.substr(1);
  return prmstr != null && prmstr != "" ? transformToAssocArray(prmstr) : {};
}

function transformToAssocArray(prmstr) {
  var params = {};
  var prmarr = prmstr.split("&");
  for (var i = 0; i < prmarr.length; i++) {
    var tmparr = prmarr[i].split("=");
    params[tmparr[0]] = tmparr[1];
  }
  return params;
}

var timeperiod;
var unit = null;

var params = getSearchParameters();

//update every X seconds
function startTimer(duration) {
  var display = document.querySelector('#time');
  var timer = duration,
  minutes, seconds;
  setInterval(function() {
    minutes = parseInt(timer / 60, 10)
    seconds = parseInt(timer % 60, 10);

    minutes = minutes < 10 ? "0" + minutes : minutes;
    seconds = seconds < 10 ? "0" + seconds : seconds;

    display.innerText = minutes + ":" + seconds;

    if (--timer < 0) { //when the timer is 0 run the code
      timer = duration;
      RunForestRun();
    }
  }, 1000);
}

//Get times vars for the call
function RunForestRun(mp) {

  mp && mp.open();

  if (timeoverride !== null) { //we are using a time override

    var end = new Date();

    var start = new Date();
    start.setDate(start.getDate() - (timeoverride / 24));

    starttime = start.toISOString();
    endtime = end.toISOString();

    console.log(starttime);
    console.log(endtime);

    params.start = starttime;
    params.end = endtime;

  } else {
    params = getSearchParameters();
  }

  if (unit == null) {
    console.log("firstrun...will fetch vars");

    if (typeof params.hq !== 'undefined') {
      if (params.hq.split(",").length == 1) { //one HQ was passed
        GetUnitNamefromBeacon(params.hq, params.host, function(result) {
          unit = result;
          HackTheMatrix(unit, params.host,mp);
        });
      } else {
        unit = [];
        console.log("passed array of units");
        var hqsGiven = params.hq.split(",");
        console.log(hqsGiven);
        hqsGiven.forEach(function(d) {
          GetUnitNamefromBeacon(d, params.host, function(result) {
            unit.push(result);
            if (unit.length == params.hq.split(",").length) {
              HackTheMatrix(unit, params.host,mp);
            }
          });
        });
      }
    } else { //no hq was sent, get them all
      unit = [];
      HackTheMatrix(unit, params.host,mp);
    }
  } else {
    console.log("rerun...will NOT fetch vars");
    HackTheMatrix(unit, params.host);
  }

}

//make the call to beacon
function HackTheMatrix(unit, host, progressBar) {

  var start = new Date(decodeURIComponent(params.start));
  var end = new Date(decodeURIComponent(params.end));

  GetJSONfromBeacon(unit, host, start, end,
    function(jobs) {
      var facts = crossfilter(jobs.Results);
      var all = facts.groupAll();

      var JobStatus = facts.dimension(function(d) {
        return d.JobStatusType.Name;
      });

      var JobType = facts.dimension(function(d) {
        return d.JobType.ParentId;
      });

      var JobStatusGroup = JobStatus.group().reduceCount(function(d) {
        return d.JobStatusType.Name;
      });
      var JobTypeGroup = JobType.group().reduceCount(function(d) {
        return d.JobType.ParentId;
      });

      var completeJob = 0;
      var newJob = 0;
      var ackJob = 0;
      var refJob = 0;
      var finJob = 0;
      var canJob = 0;
      var rejJob = 0;
      var tskJob = 0;

      JobStatusGroup.top(Infinity).forEach(function(d) {
        console.log(d.key + " " + d.value);
        switch (d.key) {
          case "New":
            newJob = d.value;
            break;
          case "Acknowledged":
            ackJob = d.value;
            break;
          case "Tasked":
            tskJob = d.value;
            break;
          case "Complete":
            completeJob = d.value;
            break;
          case "Finalised":
            finJob = d.value;
            break;
          case "Referred":
            refJob = d.value;
            break;
          case "Rejected":
            rejJob = d.value;
            break;
          case "Cancelled":
            canJob = d.value;
            break;
        }
      });

      var storm = 0;
      var flood = 0;
      var rescue = 0;
      var support = 0;

      JobTypeGroup.top(Infinity).forEach(function(d) {
          switch (d.key) {
            case 1: // Parent: Storm
              storm = d.value;
              break;
            case 2: // Parent: Support
              support = d.value;
              break;
            case 4: // Parent: Flood Assistance
              flood = d.value;
              break;
            case 5: // Parent: Rescue
              rescue = d.value;
              break;
          }
      });

      document.getElementById("new").innerHTML = newJob + "
" + Math.round(newJob / jobs.Results.length * 100) + "%
";
      document.getElementById("ack").innerHTML = ackJob + "
" + Math.round(ackJob / jobs.Results.length * 100) + "%
";
      document.getElementById("comp").innerHTML = completeJob + "
" + Math.round(completeJob / jobs.Results.length * 100) + "%
";
      document.getElementById("ref").innerHTML = refJob + "
" + Math.round(refJob / jobs.Results.length * 100) + "%
";
      document.getElementById("can").innerHTML = canJob + "
" + Math.round(canJob / jobs.Results.length * 100) + "%
";
      document.getElementById("rej").innerHTML = rejJob + "
" + Math.round(rejJob / jobs.Results.length * 100) + "%
";
      document.getElementById("tsk").innerHTML = tskJob + "
" + Math.round(tskJob / jobs.Results.length * 100) + "%
";
      document.getElementById("fin").innerHTML = finJob + "
" + Math.round(finJob / jobs.Results.length * 100) + "%
";

      document.getElementById("support").innerHTML = support + "
" + Math.round(support / jobs.Results.length * 100) + "%
";
      document.getElementById("flood").innerHTML = flood + "
" + Math.round(flood / jobs.Results.length * 100) + "%
";
      document.getElementById("rescue").innerHTML = rescue + "
" + Math.round(rescue / jobs.Results.length * 100) + "%
";
      document.getElementById("storm").innerHTML = storm + "
" + Math.round(storm / jobs.Results.length * 100) + "%
";
      var open = newJob + ackJob + tskJob + refJob;
      var closed = canJob + completeJob + finJob + rejJob;

      document.getElementById("open").innerHTML = open + "
" + Math.round(open / jobs.Results.length * 100) + "%
";
      document.getElementById("closed").innerHTML = closed + "
" + Math.round(closed / jobs.Results.length * 100) + "%
";
      document.getElementById("totalnumber").innerHTML = jobs.Results.length + "
" + Math.round(jobs.Results.length / jobs.Results.length * 100) + "%
";

      var options = {
        weekday: "short",
        year: "numeric",
        month: "2-digit",
        day: "numeric",
        hour12: false
      };

      if (unit.length == 0) { //whole nsw state
        document.title = "NSW Job Summary";
        document.getElementById("banner").innerHTML = "
Job summary for NSW
";
      } else {
        if (Array.isArray(unit) == false) { //1 lga
          document.title = unit.Name + " Job Summary";
          document.getElementById("banner").innerHTML = '
Job summary for ' + unit.Name + "
";
        }
        if (unit.length == 1) { //more than one
          document.title = "Group Job Summary";
          document.getElementById("banner").innerHTML = "
Job summary for Group
";
        };
      }

      document.getElementById("banner").innerHTML = document.getElementById("banner").innerHTML + "
" + start.toLocaleTimeString("en-au", options) + " to " + end.toLocaleTimeString("en-au", options) + "
";

      progressBar && progressBar.setValue(1);
      progressBar && progressBar.close();

    },
    function(val,total){
      if (progressBar) { //if its a first load
        if (val == -1 && total == -1) {
          progressBar.fail();
        } else {
          progressBar.setValue(val/total)
        }
      }
    }
  );

}
<|endoftext|>


<commit_before>const facetClass = 'b-facet-box__facet';
const facetTerm = `${facetClass}-term-toggle`;
const facetTermTick = `${facetTerm}-tick`;
const facetTextClass = `${facetClass}-text`;
const facetTermContainerClass = `${facetClass}-term-container`;

const termClass = 'b-facet-box__term';
const termListClass = `${termClass}-list`;
const termList = document.querySelector(`.${termListClass}`);
const termCheckboxClass = `${facetClass}-term-toggle-checkbox`;

const facetCloseClass = 'b-facet-box__close-button';

const facetsWithIndex = {};

const termButtonHTML = (facet, term) => `

        ${facet}: ${term}

`;

const dateFacetHTML = () => `

      Dates

          From year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="after_year">

          To year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="before_year">

        <button class="b-facet-box__facet-date-button">
          <svg class="b-facet-box__facet-date-button-icon" role="img">
            <use xlink:href="/svg/vamicons.svg#search"></use>
          </svg>
        </button>

`;

const termCheckbox = (facet, paramName, term, value, count) => {
  const checkbox = document.createElement('LI');
  checkbox.className = 'b-facet-box__facet-term-toggle';
  checkbox.dataset.id = `${paramName}-${value}`;
  checkbox.dataset.facet = facet;
  checkbox.dataset.paramName = paramName;
  checkbox.dataset.term = term;
  checkbox.dataset.value = value;
  checkbox.dataset.count = count;

  checkbox.innerHTML = `

      <svg class="b-facet-box__facet-term-toggle-tick" role="img">
        <use xlink:href="/svg/vamicons.svg#tick"></use>
      </svg>

      ${term}

      (${count})

  `;

  const hiddenInput = document.createElement('INPUT');
  hiddenInput.type = 'checkbox';
  hiddenInput.className = 'b-facet-box__hidden-input';
  hiddenInput.name = paramName;
  hiddenInput.value = value;
  hiddenInput.id = `${paramName}=${value}`;

  checkbox.addEventListener('termToggle', (e) => {
    const existingHiddenInput = document.querySelector(`input[id="${`${paramName}=${value}`}"]`);
    // GOTTA ASSUME THERE'S A FORM ON THE PAGE FOR THIS TO WORK!!!
    // this is because formData has an order which is annoying to change

    if (existingHiddenInput) {
      existingHiddenInput.checked = false;
      existingHiddenInput.remove();
    } else {
      document.querySelector('#vam-etc-search').appendChild(hiddenInput);
      document.querySelector(`input[id="${`${paramName}=${value}`}"]`).checked = true;
    }

    e.target.querySelector(`.${facetTermTick}`).classList.toggle(
      `${facetTermTick}--active`
    );

    if (!e.detail.refreshing_page) {
      document.querySelector('.b-facet-box').dispatchEvent(new Event('boxChecked', { bubbles: true }));
    }
  });

  return checkbox;
};

const facetHTML = (facet, seeMore) => `

    ${facet}

${
  seeMore ? `
    See more
  ` : ''
}
`;

const revealMoreFacets = (e) => {
  e.preventDefault();
  const linkEl = e.target;
  const facetContainer = e.target.parentNode;
  const { terms, index, facet, paramName } = facetsWithIndex[e.target.dataset.facet];
  e.target.remove();
  terms.slice(index, index + 5).forEach(({ term, count, value }) => {
    facetContainer.appendChild(termCheckbox(facet, paramName, term, value, count));
  });
  facetsWithIndex[facet].index += 5;

  if (facetsWithIndex[facet].index !== terms.length) {
    facetContainer.appendChild(linkEl);
  }
};

const createFacets = (activeFacets) => {
  const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
  const facetToTerm = Array.from(activeFacets).reduce((res, termfacet) => {
    const facet = termfacet.split('-')[0];
    const term = termfacet.split('-')[1];

    if (res[facet]) {
      res[facet].push(term);
    } else {
      res[facet] = [term];
    }

    return res;
  }, {});

  Object.values(facetsWithIndex).forEach(({ facet, terms, paramName, index }) => {
    const newFacet = document.createElement('DIV');
    newFacet.className = 'b-facet-box__facet';
    newFacet.innerHTML = facetHTML(facet, terms.length > 5);

    newFacet.addEventListener('click', (e) => {
      if (e.target.classList.contains(facetTextClass)) {
        e.target.classList.toggle(`${e.target.classList[0]}--active`);
        e.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });

    const termValues = terms.map(t => t.value);

    let newIndex = (facetToTerm[paramName] && facetToTerm[paramName].reduce((current, term) => {
      const test = termValues.indexOf(term);
      return (current > test ? current : test);
    }, 5)) || 0;

    newIndex = ((Math.ceil(newIndex / 5) * 5));

    newIndex = newIndex > terms.length ? terms.length : (newIndex || 5);

    terms.slice(index, newIndex).forEach(({ term, count, value }) => {
      newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(termCheckbox(facet, paramName, term, value, count));
    });

    facetsWithIndex[facet].index += (newIndex);

    if (terms.length > 5) {
      if (facetsWithIndex[facet].index < terms.length) {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more'));
        newFacet.querySelector(`.${facetTermContainerClass} .b-facet-box__term-more`).onclick = e => revealMoreFacets(e);
      } else {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more')).remove();
      }
    }

    facetBoxContainer.appendChild(newFacet);
  });
};

const newTermToggleEvent = (detail, bubbles = true) => new CustomEvent('termToggle', {
  detail,
  bubbles
});

const initialiseFacetOverlay = () => {
  const toggleTerm = ({ id, facet, term, paramName }) => {
    if (id) {
      // if term already exists, get rid of it
      if (document.querySelector(`div[data-id='${id}']`)) {
        Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.remove());
        if (!document.querySelector('.b-search-form__facets').children.length) {
          document.querySelector('.b-search-form__facet-pane').classList.remove('b-search-form__facet-pane--active');
        }
        window.dispatchEvent(new Event('resize'));
      } else {
        const newTermOnClick = () => {
          Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.dispatchEvent(newTermToggleEvent({ id, facet, term, paramName })));
          if (document.querySelector(`li[data-id='${id}']`)) {
            document.querySelector(`li[data-id='${id}']`).dispatchEvent(newTermToggleEvent({ id, facet, term, paramName }));
          }
        };

        const newTerm = document.createElement('DIV');
        newTerm.dataset.id = id;
        newTerm.className = 'b-facet-box__term';
        newTerm.innerHTML = termButtonHTML(facet, term);
        newTerm.onclick = e => newTermOnClick(e);
        termList.appendChild(newTerm);

        const newFormTerm = newTerm.cloneNode(true);
        newFormTerm.onclick = e => newTermOnClick(e);
        newFormTerm.classList.add('b-facet-box__term--form');
        if (document.querySelector('.b-search-form__facets')) {
          document.querySelector('.b-search-form__facets').appendChild(newFormTerm);
        }

        if (!document.querySelector('.b-search-form__facet-pane--active')) {
          document.querySelector('.b-search-form__facet-pane').classList.add('b-search-form__facet-pane--active');
        }

        window.dispatchEvent(new Event('resize'));
      }
    }
  };

  termList.addEventListener('termToggle', (e) => {
    e.stopPropagation();
    toggleTerm(e.detail);
  });

  document.querySelector('.b-facet-box').addEventListener('newFacets', (e) => {
    const { facets, activeFacets } = e.detail;
    const currentBeforeDate = document.querySelector('input[name="before_year"]') ? document.querySelector('input[name="before_year"]').value : '';
    const currentAfterDate = document.querySelector('input[name="after_year"]') ? document.querySelector('input[name="after_year"]').value : '';

    facets.forEach((facet) => {
      Object.assign(facetsWithIndex, {
        [facet.facet]: Object.assign(facet, { index: 0 })
      });
    });

    Array.from(document.querySelectorAll('.b-facet-box__hidden-input')).forEach(el => el.remove());

    const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
    facetBoxContainer.innerHTML = '';
    termList.innerHTML = '';

    createFacets(activeFacets);

    const dateFacet = document.createElement('DIV');
    dateFacet.className = 'b-facet-box__facet b-facet-box__facet-date';
    dateFacet.innerHTML = dateFacetHTML();
    dateFacet.addEventListener('click', (ev) => {
      if (ev.target.classList.contains(facetTextClass)) {
        ev.target.classList.toggle(`${ev.target.classList[0]}--active`);
        ev.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });
    dateFacet.querySelector('input[name="before_year"]').value = currentBeforeDate;
    dateFacet.querySelector('input[name="after_year"]').value = currentAfterDate;

    facetBoxContainer.append(dateFacet);

    if (activeFacets) {
      // is a set...
      Array.from(activeFacets).forEach((facetId) => {
        const target = document.querySelector(`li[data-id='${facetId}'`);
        if (target) {
          target.dispatchEvent(newTermToggleEvent(Object.assign(target.dataset, {refreshing_page: true})));
          document.querySelector(`.${termListClass}`).dispatchEvent(newTermToggleEvent(target.dataset));
        }
      });
    }

    window.dispatchEvent(new Event('resize'));
  }, true);

  document.onclick = (e) => {
    if (e.target.classList.contains(facetCloseClass)) {
      e.target.dispatchEvent(new Event('closeFacetOverlay', {
        bubbles: true
      }));
    }

    if (e.target.parentElement && e.target.parentElement.classList.contains(facetTerm)) {
      const parent = e.target.closest(facetTerm) || e.target.parentElement;
      termList.dispatchEvent(newTermToggleEvent(parent.dataset, false));
      parent.dispatchEvent(newTermToggleEvent(parent.dataset));
    }
  };
};

(() => {
  if (document.querySelector('.b-facet-box')) {
    initialiseFacetOverlay();

    if (document.querySelector('.b-facet-box__modal-button-open')) {
      document.querySelectorAll('.b-facet-box__modal-button-open').forEach(el => el.addEventListener('click', () => document.querySelector('.b-facet-box').classList.add('b-facet-box--active')));
    }
    if (document.querySelector('.b-facet-box__close-button')) {
      document.querySelector('.b-facet-box__close-button').addEventListener('click', () => document.querySelector('.b-facet-box').classList.remove('b-facet-box--active'));
    }

    window.onresize = () => {
      const facetFormTerms = Array.from(document.querySelectorAll('.b-facet-box__term.b-facet-box__term--form'));
      if (document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross')) {
        if (window.innerWidth > 499 && window.innerWidth < 992) {
          const facetContainerWidth = document.querySelector('.b-search-form__facets').offsetWidth;
          let cutOffWidth = 0;
          let currentIndex = 1;
          facetFormTerms.forEach((el) => {
            cutOffWidth += el.offsetWidth + 10;
            if (cutOffWidth < facetContainerWidth) {
              currentIndex += 1;
            }
          });
          if ((facetFormTerms.length - currentIndex) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `+${facetFormTerms.length - currentIndex}`;
        } else if (window.innerWidth < 500) {
          if ((facetFormTerms.length) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `${facetFormTerms.length - 1} filters applied`;
        } else {
          document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
        }
      }
    };
  }
})();
<commit_msg>fix for facets<commit_after>const facetClass = 'b-facet-box__facet';
const facetTerm = `${facetClass}-term-toggle`;
const facetTermTick = `${facetTerm}-tick`;
const facetTextClass = `${facetClass}-text`;
const facetTermContainerClass = `${facetClass}-term-container`;

const termClass = 'b-facet-box__term';
const termListClass = `${termClass}-list`;
const termList = document.querySelector(`.${termListClass}`);
const termCheckboxClass = `${facetClass}-term-toggle-checkbox`;

const facetCloseClass = 'b-facet-box__close-button';

const facetsWithIndex = {};

const termButtonHTML = (facet, term) => `

        ${facet}: ${term}

`;

const dateFacetHTML = () => `

      Dates

          From year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="after_year">

          To year:

        <input class="b-facet-box__facet-date-input" placeholder="Year" type="number" name="before_year">

        <button class="b-facet-box__facet-date-button">
          <svg class="b-facet-box__facet-date-button-icon" role="img">
            <use xlink:href="/svg/vamicons.svg#search"></use>
          </svg>
        </button>

`;

const termCheckbox = (facet, paramName, term, value, count) => {
  const checkbox = document.createElement('LI');
  checkbox.className = 'b-facet-box__facet-term-toggle';
  checkbox.dataset.id = `${paramName}-${value}`;
  checkbox.dataset.facet = facet;
  checkbox.dataset.paramName = paramName;
  checkbox.dataset.term = term;
  checkbox.dataset.value = value;
  checkbox.dataset.count = count;

  checkbox.innerHTML = `

      <svg class="b-facet-box__facet-term-toggle-tick" role="img">
        <use xlink:href="/svg/vamicons.svg#tick"></use>
      </svg>

      ${term}

      (${count})

  `;

  const hiddenInput = document.createElement('INPUT');
  hiddenInput.type = 'checkbox';
  hiddenInput.className = 'b-facet-box__hidden-input';
  hiddenInput.name = paramName;
  hiddenInput.value = value;
  hiddenInput.id = `${paramName}=${value}`;

  checkbox.addEventListener('termToggle', (e) => {
    const existingHiddenInput = document.querySelector(`input[id="${`${paramName}=${value}`}"]`);
    // GOTTA ASSUME THERE'S A FORM ON THE PAGE FOR THIS TO WORK!!!
    // this is because formData has an order which is annoying to change

    if (existingHiddenInput) {
      existingHiddenInput.click();
      existingHiddenInput.remove();
    } else {
      document.querySelector('#vam-etc-search').appendChild(hiddenInput);
      document.querySelector(`input[id="${`${paramName}=${value}`}"]`).checked = true;
    }

    e.target.querySelector(`.${facetTermTick}`).classList.toggle(
      `${facetTermTick}--active`
    );

    if (!e.detail.refreshing_page) {
      document.querySelector('.b-facet-box').dispatchEvent(new Event('boxChecked', { bubbles: true }));
    }
  });

  return checkbox;
};

const facetHTML = (facet, seeMore) => `

    ${facet}

${
  seeMore ? `
    See more
  ` : ''
}
`;

const revealMoreFacets = (e) => {
  e.preventDefault();
  const linkEl = e.target;
  const facetContainer = e.target.parentNode;
  const { terms, index, facet, paramName } = facetsWithIndex[e.target.dataset.facet];
  e.target.remove();
  terms.slice(index, index + 5).forEach(({ term, count, value }) => {
    facetContainer.appendChild(termCheckbox(facet, paramName, term, value, count));
  });
  facetsWithIndex[facet].index += 5;

  if (facetsWithIndex[facet].index !== terms.length) {
    facetContainer.appendChild(linkEl);
  }
};

const createFacets = (activeFacets) => {
  const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
  const facetToTerm = Array.from(activeFacets).reduce((res, termfacet) => {
    const facet = termfacet.split('-')[0];
    const term = termfacet.split('-')[1];

    if (res[facet]) {
      res[facet].push(term);
    } else {
      res[facet] = [term];
    }

    return res;
  }, {});

  Object.values(facetsWithIndex).forEach(({ facet, terms, paramName, index }) => {
    const newFacet = document.createElement('DIV');
    newFacet.className = 'b-facet-box__facet';
    newFacet.innerHTML = facetHTML(facet, terms.length > 5);

    newFacet.addEventListener('click', (e) => {
      if (e.target.classList.contains(facetTextClass)) {
        e.target.classList.toggle(`${e.target.classList[0]}--active`);
        e.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });

    const termValues = terms.map(t => t.value);

    let newIndex = (facetToTerm[paramName] && facetToTerm[paramName].reduce((current, term) => {
      const test = termValues.indexOf(term);
      return (current > test ? current : test);
    }, 5)) || 0;

    newIndex = ((Math.ceil(newIndex / 5) * 5));

    newIndex = newIndex > terms.length ? terms.length : (newIndex || 5);

    terms.slice(index, newIndex).forEach(({ term, count, value }) => {
      newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(termCheckbox(facet, paramName, term, value, count));
    });

    facetsWithIndex[facet].index += (newIndex);

    if (terms.length > 5) {
      if (facetsWithIndex[facet].index < terms.length) {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more'));
        newFacet.querySelector(`.${facetTermContainerClass} .b-facet-box__term-more`).onclick = e => revealMoreFacets(e);
      } else {
        newFacet.querySelector(`.${facetTermContainerClass}`).appendChild(newFacet.querySelector('.b-facet-box__term-more')).remove();
      }
    }

    facetBoxContainer.appendChild(newFacet);
  });
};

const newTermToggleEvent = (detail, bubbles = true) => new CustomEvent('termToggle', {
  detail,
  bubbles
});

const initialiseFacetOverlay = () => {
  const toggleTerm = ({ id, facet, term, paramName }) => {
    if (id) {
      // if term already exists, get rid of it
      if (document.querySelector(`div[data-id='${id}']`)) {
        Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.remove());
        if (!document.querySelector('.b-search-form__facets').children.length) {
          document.querySelector('.b-search-form__facet-pane').classList.remove('b-search-form__facet-pane--active');
        }
        window.dispatchEvent(new Event('resize'));
      } else {
        const newTermOnClick = () => {
          Array.from(document.querySelectorAll(`div[data-id='${id}']`)).forEach(el => el.dispatchEvent(newTermToggleEvent({ id, facet, term, paramName })));
          if (document.querySelector(`li[data-id='${id}']`)) {
            document.querySelector(`li[data-id='${id}']`).dispatchEvent(newTermToggleEvent({ id, facet, term, paramName }));
          }
        };

        const newTerm = document.createElement('DIV');
        newTerm.dataset.id = id;
        newTerm.className = 'b-facet-box__term';
        newTerm.innerHTML = termButtonHTML(facet, term);
        newTerm.onclick = e => newTermOnClick(e);
        termList.appendChild(newTerm);

        const newFormTerm = newTerm.cloneNode(true);
        newFormTerm.onclick = e => newTermOnClick(e);
        newFormTerm.classList.add('b-facet-box__term--form');
        if (document.querySelector('.b-search-form__facets')) {
          document.querySelector('.b-search-form__facets').appendChild(newFormTerm);
        }

        if (!document.querySelector('.b-search-form__facet-pane--active')) {
          document.querySelector('.b-search-form__facet-pane').classList.add('b-search-form__facet-pane--active');
        }

        window.dispatchEvent(new Event('resize'));
      }
    }
  };

  termList.addEventListener('termToggle', (e) => {
    e.stopPropagation();
    toggleTerm(e.detail);
  });

  document.querySelector('.b-facet-box').addEventListener('newFacets', (e) => {
    const { facets, activeFacets } = e.detail;
    const currentBeforeDate = document.querySelector('input[name="before_year"]') ? document.querySelector('input[name="before_year"]').value : '';
    const currentAfterDate = document.querySelector('input[name="after_year"]') ? document.querySelector('input[name="after_year"]').value : '';

    facets.forEach((facet) => {
      Object.assign(facetsWithIndex, {
        [facet.facet]: Object.assign(facet, { index: 0 })
      });
    });

    Array.from(document.querySelectorAll('.b-facet-box__hidden-input')).forEach(el => el.remove());

    const facetBoxContainer = document.querySelector('.b-facet-box__facet-container');
    facetBoxContainer.innerHTML = '';
    termList.innerHTML = '';

    createFacets(activeFacets);

    const dateFacet = document.createElement('DIV');
    dateFacet.className = 'b-facet-box__facet b-facet-box__facet-date';
    dateFacet.innerHTML = dateFacetHTML();
    dateFacet.addEventListener('click', (ev) => {
      if (ev.target.classList.contains(facetTextClass)) {
        ev.target.classList.toggle(`${ev.target.classList[0]}--active`);
        ev.target.parentNode.querySelector(`.${facetTermContainerClass}`).classList.toggle(`${facetTermContainerClass}--active`);
      }
    });
    dateFacet.querySelector('input[name="before_year"]').value = currentBeforeDate;
    dateFacet.querySelector('input[name="after_year"]').value = currentAfterDate;

    facetBoxContainer.append(dateFacet);

    if (activeFacets) {
      // is a set...
      Array.from(activeFacets).forEach((facetId) => {
        const target = document.querySelector(`li[data-id='${facetId}'`);
        if (target) {
          target.dispatchEvent(newTermToggleEvent(Object.assign(target.dataset, {refreshing_page: true})));
          document.querySelector(`.${termListClass}`).dispatchEvent(newTermToggleEvent(target.dataset));
        }
      });
    }

    window.dispatchEvent(new Event('resize'));
  }, true);

  document.onclick = (e) => {
    if (e.target.classList.contains(facetCloseClass)) {
      e.target.dispatchEvent(new Event('closeFacetOverlay', {
        bubbles: true
      }));
    }

    if (e.target.parentElement && e.target.parentElement.classList.contains(facetTerm)) {
      const parent = e.target.closest(facetTerm) || e.target.parentElement;
      termList.dispatchEvent(newTermToggleEvent(parent.dataset, false));
      parent.dispatchEvent(newTermToggleEvent(parent.dataset));
    }
  };
};

(() => {
  if (document.querySelector('.b-facet-box')) {
    initialiseFacetOverlay();

    if (document.querySelector('.b-facet-box__modal-button-open')) {
      document.querySelectorAll('.b-facet-box__modal-button-open').forEach(el => el.addEventListener('click', () => document.querySelector('.b-facet-box').classList.add('b-facet-box--active')));
    }
    if (document.querySelector('.b-facet-box__close-button')) {
      document.querySelector('.b-facet-box__close-button').addEventListener('click', () => document.querySelector('.b-facet-box').classList.remove('b-facet-box--active'));
    }

    window.onresize = () => {
      const facetFormTerms = Array.from(document.querySelectorAll('.b-facet-box__term.b-facet-box__term--form'));
      if (document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross')) {
        if (window.innerWidth > 499 && window.innerWidth < 992) {
          const facetContainerWidth = document.querySelector('.b-search-form__facets').offsetWidth;
          let cutOffWidth = 0;
          let currentIndex = 1;
          facetFormTerms.forEach((el) => {
            cutOffWidth += el.offsetWidth + 10;
            if (cutOffWidth < facetContainerWidth) {
              currentIndex += 1;
            }
          });
          if ((facetFormTerms.length - currentIndex) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `+${facetFormTerms.length - currentIndex}`;
        } else if (window.innerWidth < 500) {
          if ((facetFormTerms.length) > 0) {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'block';
          } else {
            document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
          }
          document.querySelector('.b-facet-box__term-text.b-facet-box__term-text--no-cross').innerHTML = `${facetFormTerms.length - 1} filters applied`;
        } else {
          document.querySelector('.b-search-form__facets-mobile').style.display = 'none';
        }
      }
    };
  }
})();
<|endoftext|>