<commit_before>"use strict";

const path = require('path');

const chalk = require('chalk');

const CommandManager = require('./CommandManager.js');
const Module = require('./API/Module');

class Main {
  constructor(opts) {
    process.on('unhandledRejection', function onError(err) {
      throw err;
    });

    this.packageOptions = require('../package.json');
    this.options = opts;

    this.commandManager = null;
    this.loadedModules = null;

    this.buildContainer();
    this.run();
    this.buildRepl();
  }

  buildRepl() {
    this.repl = require('repl').start('> ');
    this.initializeReplContext(this.repl.context);

    this.repl.on('reset', this.initializeReplContext.bind(this));
    this.repl.on('exit', this.shutdown.bind(this));
  }

  initializeReplContext(context) {
    context.Module = Module;
    context.CommandManager = CommandManager;

    context.main = this;
    context.container = this.container;
  }

  buildContainer() {
    this.container = new Map();

    this.container.set('shutdownMode', false);

    this.setupLogger();
    this.setupBrain();

    this.commandManager = new CommandManager(this.container);
    this.container.set('commandManager', this.commandManager);

    this.loadedModules = new Map();
    this.container.set('loadedModules', this.loadedModules);
  }

  setupLogger() {
    this.logger = require('./Logger')(path.join(__dirname, '..', 'logs'), 'cardinal');
    this.container.set('logger', this.logger);
  }

  setupBrain() {
    const RedisBrain = require('./Brain/Redis');

    this.brain = new RedisBrain();
    this.container.set('redisBrain', this.brain);
  }

  loadClient() {
    this.logger.info('Main::loadClient()');

    this.bot = require('./Bot')(this.container);
    this.container.set('bot', this.bot);

    this.commandManager.botReady();
  }

  loadModules() {
    const modules = this.options.modules;

    this.logger.info('Main::loadModules()');

    for (const module of modules) {
      if (!(module.prototype instanceof Module)) {
        this.logger.warn(`${module.name} does not inherit from the Module class`);
        continue;
      }

      this.logger.info(`Initializing ${module.name}`);

      const moduleInstance = new (module)(this.container);
      this.loadedModules.set(module.name, moduleInstance);

      this.logger.info(`Initialized ${module.name}`);
    }
  }

  shutdownModules() {
    this.logger.info('Main::shutdownModules()');

    for (const [name, module] of this.loadedModules) {
      if (module.shutdown && typeof(module.shutdown) === 'function') {
        this.logger.info(`Shutting down ${module.constructor.name}`);

        module.shutdown();

        this.logger.info(`Shut down ${module.constructor.name}`);
      }
    }
  }

  run() {
    console.log(chalk.blue(`\n\n\t${this.packageOptions.name} v${this.packageOptions.version} - by ${this.packageOptions.author}\n\n`));

    if (this.options.prefix) {
      this.logger.info(`Set command prefix to '${this.options.prefix}'`);
      this.commandManager.setPrefix(this.options.prefix);
    }

    this.loadClient();
    this.loadModules();

    this.bot.start();
  }

  shutdown() {
    this.logger.info('Main::shutdown()');

    this.container.set('shutdownMode', true);
    this.shutdownModules();

    this.bot.client.disconnect();
    this.brain.quit();

    //process.exit();
  }
}

exports.initialize = function initialize(opts) {
  return new Main(opts);
};
<commit_msg>repl: auto-import MusicPlayer Utils<commit_after>"use strict";

const path = require('path');

const chalk = require('chalk');

const CommandManager = require('./CommandManager.js');
const Module = require('./API/Module');

class Main {
  constructor(opts) {
    process.on('unhandledRejection', function onError(err) {
      throw err;
    });

    this.packageOptions = require('../package.json');
    this.options = opts;

    this.commandManager = null;
    this.loadedModules = null;

    this.buildContainer();
    this.run();
    this.buildRepl();
  }

  buildRepl() {
    this.repl = require('repl').start('> ');
    this.initializeReplContext(this.repl.context);

    this.repl.on('reset', this.initializeReplContext.bind(this));
    this.repl.on('exit', this.shutdown.bind(this));
  }

  initializeReplContext(context) {
    context.Module = Module;
    context.CommandManager = CommandManager;

    context.Utils = require('../queue/utils');

    context.main = this;
    context.container = this.container;
  }

  buildContainer() {
    this.container = new Map();

    this.container.set('shutdownMode', false);

    this.setupLogger();
    this.setupBrain();

    this.commandManager = new CommandManager(this.container);
    this.container.set('commandManager', this.commandManager);

    this.loadedModules = new Map();
    this.container.set('loadedModules', this.loadedModules);
  }

  setupLogger() {
    this.logger = require('./Logger')(path.join(__dirname, '..', 'logs'), 'cardinal');
    this.container.set('logger', this.logger);
  }

  setupBrain() {
    const RedisBrain = require('./Brain/Redis');

    this.brain = new RedisBrain();
    this.container.set('redisBrain', this.brain);
  }

  loadClient() {
    this.logger.info('Main::loadClient()');

    this.bot = require('./Bot')(this.container);
    this.container.set('bot', this.bot);

    this.commandManager.botReady();
  }

  loadModules() {
    const modules = this.options.modules;

    this.logger.info('Main::loadModules()');

    for (const module of modules) {
      if (!(module.prototype instanceof Module)) {
        this.logger.warn(`${module.name} does not inherit from the Module class`);
        continue;
      }

      this.logger.info(`Initializing ${module.name}`);

      const moduleInstance = new (module)(this.container);
      this.loadedModules.set(module.name, moduleInstance);

      this.logger.info(`Initialized ${module.name}`);
    }
  }

  shutdownModules() {
    this.logger.info('Main::shutdownModules()');

    for (const [name, module] of this.loadedModules) {
      if (module.shutdown && typeof(module.shutdown) === 'function') {
        this.logger.info(`Shutting down ${module.constructor.name}`);

        module.shutdown();

        this.logger.info(`Shut down ${module.constructor.name}`);
      }
    }
  }

  run() {
    console.log(chalk.blue(`\n\n\t${this.packageOptions.name} v${this.packageOptions.version} - by ${this.packageOptions.author}\n\n`));

    if (this.options.prefix) {
      this.logger.info(`Set command prefix to '${this.options.prefix}'`);
      this.commandManager.setPrefix(this.options.prefix);
    }

    this.loadClient();
    this.loadModules();

    this.bot.start();
  }

  shutdown() {
    this.logger.info('Main::shutdown()');

    this.container.set('shutdownMode', true);
    this.shutdownModules();

    this.bot.client.disconnect();
    this.brain.quit();

    //process.exit();
  }
}

exports.initialize = function initialize(opts) {
  return new Main(opts);
};
<|endoftext|>


<commit_before>const KAMEHAMEHA_REGEX = /KAM\sHWY/g;

function processAddress(address) {
  // Check if address contains "KAM"
  address = address.replace(KAMEHAMEHA_REGEX, " KAMEHAMEHA HWY");
  return address;
}

module.exports = {processAddress};
<commit_msg>Remove unnecessary space.<commit_after>const KAMEHAMEHA_REGEX = /KAM\sHWY/g;

function processAddress(address) {
  // Check if address contains "KAM"
  address = address.replace(KAMEHAMEHA_REGEX, "KAMEHAMEHA HWY");
  return address;
}

module.exports = {processAddress};
<|endoftext|>


<commit_before>mol.modules.map.menu = function(mol) {

    mol.map.menu = {};

    mol.map.menu.MenuEngine = mol.mvp.Engine.extend(
        {
            init: function(proxy, bus) {
                this.proxy = proxy;
                this.bus = bus;
            },

            /**
             * Starts the MenuEngine. Note that the container parameter is
             * ignored.
             */
            start: function() {
                this.display = new mol.map.menu.MenuDisplay();
                this.bottomdisplay = new mol.map.menu.BottomMenuDisplay();

                this.display.toggle(true);
                this.bottomdisplay.toggle(true);

                this.addEventHandlers();
                this.fireEvents();
            },

            /**
             * Adds a handler for the 'search-display-toggle' event which
             * controls display visibility. Also adds UI event handlers for the
             * display.
             */
            addEventHandlers: function() {
                var self = this;

                this.display.dashboardItem.click(
                    function(event) {
                        self.bus.fireEvent(
                            new mol.bus.Event('taxonomy-dashboard-toggle'));
                    }
                );

                this.display.layersToggle.click(
                    function(event) {
                        if(self.display.layersToggle[0].src
                                .indexOf('collapse.png') > 0)  {
                            self.bus.fireEvent(
                                new mol.bus.Event(
                                    'layer-display-toggle',
                                    {visible : false}));
                            self.display.layersToggle[0].src =
                                '/static/maps/layers/expand.png';
                        } else {
                            self.bus.fireEvent(
                                new mol.bus.Event(
                                    'layer-display-toggle',
                                    {visible : true}));
                            self.display.layersToggle[0].src =
                                '/static/maps/layers/collapse.png';
                        }
                    }
                );

                this.bus.addHandler(
                    'add-legend-toggle-button',
                    function(event) {
                        $(self.bottomdisplay).prepend(event.button);
                        self.bottomdisplay.legendItem =
                            $(self.bottomdisplay).find('#legend');

                        self.bottomdisplay.legendItem.click(
                            function(event) {
                                self.bus.fireEvent(
                                    new mol.bus.Event('legend-display-toggle'));
                            }
                        );
                    }
                );

                this.bus.addHandler(
                    'hide-layer-display-toggle',
                    function(event) {
                        self.display.layersToggle[0].style.visibility="hidden";
                    }
                );
                this.bus.addHandler(
                    'show-layer-display-toggle',
                    function(event) {
                        self.display.layersToggle[0].style.visibility="visible";
                    }
                );
                this.bus.addHandler(
                    'menu-display-toggle',
                    function(event) {
                        var params = null,
                        e = null;

                        if (event.visible === undefined) {
                            self.display.toggle();
                            params = {visible: self.display.is(':visible')};
                        } else {
                            self.display.toggle(event.visible);
                        }
                    }
                );
            },

            /**
             * Fires the 'add-map-control' event. The mol.map.MapEngine handles
             * this event and adds the display to the map.
             */
            fireEvents: function() {
                var params = {
                        display: this.display,
                        slot: mol.map.ControlDisplay.Slot.MIDDLE,
                        position: google.maps.ControlPosition.TOP_RIGHT
                    },
                    bottomparams = {
                        display: this.bottomdisplay,
                        slot: mol.map.ControlDisplay.Slot.LAST,
                        position: google.maps.ControlPosition.RIGHT_BOTTOM
                    },
                    event = new mol.bus.Event('add-map-control', params),
                    bottomevent = new mol.bus.Event(
                                    'add-map-control', bottomparams);

                this.bus.fireEvent(event);
                this.bus.fireEvent(bottomevent);

            }
        }
    );

    mol.map.menu.MenuDisplay = mol.mvp.View.extend(
        {
            init: function() {
                var html = '' +
                    '
' +
                    '
' +
                    '    ' +
                    '
' +
                    '
' +
                            'Dashboard' +
                    '
' +
                    '
';

                this._super(html);
                this.searchItem = $(this).find('#search');
                this.dashboardItem = $(this).find('#dashboard');
                this.layersToggle = $(this).find('.layersToggle');
            }
        }
    );

    mol.map.menu.BottomMenuDisplay = mol.mvp.View.extend(
        {
            init: function() {
                var html = '' +
                    '
' +
                    '
';

                this._super(html);
            }
        }
    );
};

<commit_msg>Sidebar options in, dashboard out.<commit_after>mol.modules.map.menu = function(mol) {

    mol.map.menu = {};

    mol.map.menu.MenuEngine = mol.mvp.Engine.extend(
        {
            init: function(proxy, bus) {
                this.proxy = proxy;
                this.bus = bus;
            },

            /**
             * Starts the MenuEngine. Note that the container parameter is
             * ignored.
             */
            start: function() {
                this.display = new mol.map.menu.MenuDisplay();
                this.bottomdisplay = new mol.map.menu.BottomMenuDisplay();

                this.display.toggle(true);
                this.bottomdisplay.toggle(true);

                this.addEventHandlers();
                this.fireEvents();
            },

            /**
             * Adds a handler for the 'search-display-toggle' event which
             * controls display visibility. Also adds UI event handlers for the
             * display.
             */
            addEventHandlers: function() {
                var self = this;

                this.display.layersToggle.click(
                    function(event) {
                        if(self.display.layersToggle[0].src
                                .indexOf('collapse.png') > 0)  {
                            self.bus.fireEvent(
                                new mol.bus.Event(
                                    'layer-display-toggle',
                                    {visible : false}));
                            self.display.layersToggle[0].src =
                                '/static/maps/layers/expand.png';
                        } else {
                            self.bus.fireEvent(
                                new mol.bus.Event(
                                    'layer-display-toggle',
                                    {visible : true}));
                            self.display.layersToggle[0].src =
                                '/static/maps/layers/collapse.png';
                        }
                    }
                );

                this.bottomdisplay.about.click(
                    function(Event) {
                        window.open('/about/');
                    }
                );

                this.bottomdisplay.help.click(
                    function(Event) {
                        self.bus.fireEvent(
                            new mol.bus.Event('help-display-dialog')
                        );
                    }
                );

                this.bottomdisplay.status.click(
                    function(Event) {
                        self.bus.fireEvent(
                            new mol.bus.Event('status-display-dialog')
                        );
                    }
                );

                this.bottomdisplay.feedback.click(
                    function(Event) {
                        self.bus.fireEvent(
                            new mol.bus.Event('feedback-display-toggle')
                        );
                    }
                );

                this.bus.addHandler(
                    'add-legend-toggle-button',
                    function(event) {
                        $(self.bottomdisplay).prepend(event.button);
                        self.bottomdisplay.legendItem =
                            $(self.bottomdisplay).find('#legend');

                        self.bottomdisplay.legendItem.click(
                            function(event) {
                                self.bus.fireEvent(
                                    new mol.bus.Event('legend-display-toggle'));
                            }
                        );
                    }
                );

                this.bus.addHandler(
                    'add-dashboard-toggle-button',
                    function(event) {
                        $(self.bottomdisplay).prepend(event.button);
                        self.bottomdisplay.dashboardItem =
                            $(self.bottomdisplay).find('#dashboard');

                        self.bottomdisplay.dashboardItem.click(
                            function(event) {
                                self.bus.fireEvent(
                                    new mol.bus.Event('taxonomy-dashboard-toggle'));
                            }
                        );
                    }
                );

                this.bus.addHandler(
                    'hide-layer-display-toggle',
                    function(event) {
                        self.display.layersToggle[0].style.visibility="hidden";
                    }
                );
                this.bus.addHandler(
                    'show-layer-display-toggle',
                    function(event) {
                        self.display.layersToggle[0].style.visibility="visible";
                    }
                );
                this.bus.addHandler(
                    'menu-display-toggle',
                    function(event) {
                        var params = null,
                        e = null;

                        if (event.visible === undefined) {
                            self.display.toggle();
                            params = {visible: self.display.is(':visible')};
                        } else {
                            self.display.toggle(event.visible);
                        }
                    }
                );
            },

            /**
             * Fires the 'add-map-control' event. The mol.map.MapEngine handles
             * this event and adds the display to the map.
             */
            fireEvents: function() {
                var params = {
                        display: this.display,
                        slot: mol.map.ControlDisplay.Slot.MIDDLE,
                        position: google.maps.ControlPosition.TOP_RIGHT
                    },
                    bottomparams = {
                        display: this.bottomdisplay,
                        slot: mol.map.ControlDisplay.Slot.LAST,
                        position: google.maps.ControlPosition.RIGHT_BOTTOM
                    },
                    event = new mol.bus.Event('add-map-control', params),
                    bottomevent = new mol.bus.Event(
                                    'add-map-control', bottomparams);

                this.bus.fireEvent(event);
                this.bus.fireEvent(bottomevent);

            }
        }
    );

    mol.map.menu.MenuDisplay = mol.mvp.View.extend(
        {
            init: function() {
                var html = '' +
                    '
' +
                    '
' +
                    '    ' +
                    '
' +
                    '
';

                this._super(html);
                this.layersToggle = $(this).find('.layersToggle');
            }
        }
    );

    mol.map.menu.BottomMenuDisplay = mol.mvp.View.extend(
        {
            init: function() {
                var html = '' +
                    '
' +
                    '
Status
' +
                    '
About
' +
                    '
Feedback
' +
                    '
Help
' +
                    '
';

                this._super(html);
                this.about = $(this).find('.about');
                this.help = $(this).find('.help');
                this.feedback = $(this).find('.feedback');
                this.status = $(this).find('.status');
            }
        }
    );
};

<|endoftext|>


<commit_before>/*
 * Copyright 2014 Thoughtworks Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define(
  [
    'component/data/github_user',
    'component/data/github_issues',
    'component/track',
    'component/data/issues_exporter'
  ],
  function (githubUser, githubIssues, track, issuesExporter) {
  'use strict';

  return initialize;

  function initialize() {
    githubIssues.attachTo(document);
    githubUser.attachTo(document);
    issuesExporter.attachTo(document);

    track.attachTo('.issue-track.backlog', {trackType: '0 - Backlog'});
    track.attachTo('.issue-track.ready', {trackType: '1 - Ready'});
    track.attachTo('.issue-track.development', {trackType: '2 - Development'});
    track.attachTo('.issue-track.quality-assurance', {trackType: '3 - Quality Assurance'});
    track.attachTo('.issue-track.done', {trackType: '4 - Done'});

    $(document).trigger('ui:needs:issues', {projectName: 'all' });

    $("#create_issue").click(function() {
      $(document).trigger('ui:create:issue',
        { 'issueTitle': $("#issueTitle").val(),
          'issueBody':  $("#issueBody").val(),
          'projectName': $("#projects").val() });

      $("#myModal").modal('hide')
      $("#myModal input, textarea").val('')
    });

    $(".panel-heading.done img.plain").click(function () {
      triggerRocketAnimation();
    });

    $("#filter-repo").val('All repositories').change(function() {
      $('.issue').remove();

      if ($(this).val() == 'All repositories') {
        $(document).trigger('ui:needs:issues', {projectName: 'all' });
      } else {
        $(document).trigger('ui:needs:issues', {projectName: $(this).val()});
      }
    });

    $("#projects").change(function(){
      $(document).trigger("ui:issue:createIssuesURL", $(this).val());
    });

    $(document).trigger("ui:issue:createIssuesURL", $("#projects").val());
    $(document).trigger('ui:draggable');
    $(document).trigger('ui:needs:githubUser');
  }
});
<commit_msg>remove rocket trigger on click<commit_after>/*
 * Copyright 2014 Thoughtworks Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
define(
  [
    'component/data/github_user',
    'component/data/github_issues',
    'component/track',
    'component/data/issues_exporter'
  ],
  function (githubUser, githubIssues, track, issuesExporter) {
  'use strict';

  return initialize;

  function initialize() {
    githubIssues.attachTo(document);
    githubUser.attachTo(document);
    issuesExporter.attachTo(document);

    track.attachTo('.issue-track.backlog', {trackType: '0 - Backlog'});
    track.attachTo('.issue-track.ready', {trackType: '1 - Ready'});
    track.attachTo('.issue-track.development', {trackType: '2 - Development'});
    track.attachTo('.issue-track.quality-assurance', {trackType: '3 - Quality Assurance'});
    track.attachTo('.issue-track.done', {trackType: '4 - Done'});

    $(document).trigger('ui:needs:issues', {projectName: 'all' });

    $("#create_issue").click(function() {
      $(document).trigger('ui:create:issue',
        { 'issueTitle': $("#issueTitle").val(),
          'issueBody':  $("#issueBody").val(),
          'projectName': $("#projects").val() });

      $("#myModal").modal('hide')
      $("#myModal input, textarea").val('')
    });

    $("#filter-repo").val('All repositories').change(function() {
      $('.issue').remove();

      if ($(this).val() == 'All repositories') {
        $(document).trigger('ui:needs:issues', {projectName: 'all' });
      } else {
        $(document).trigger('ui:needs:issues', {projectName: $(this).val()});
      }
    });

    $("#projects").change(function(){
      $(document).trigger("ui:issue:createIssuesURL", $(this).val());
    });

    $(document).trigger("ui:issue:createIssuesURL", $("#projects").val());
    $(document).trigger('ui:draggable');
    $(document).trigger('ui:needs:githubUser');
  }
});
<|endoftext|>


<commit_before>import update from "react-addons-update";

class PermissionManager {
  constructor(permissions) {
    if(!permissions) {
      throw new Error("Missing permission hash");
    }

    // Remove the hidden __dataID__ from the permissions object (which comes
    // from Relay)
    this.permissions = update(permissions, {});
    delete this.permissions.__dataID__;
  }

  // Returns true if a specific permission is allowed
  isPermissionAllowed(key) {
    return this.find(key).allowed;
  }

  // Returns true if this permission is the only one that's allowed
  isPermissionOnlyOneAllowed(key) {
    let thisPermissionAllowed = this.find(key).allowed;
    let othersAllowed = false;

    // Check to see if one of the other permissions is allowed
    for(let name in this.permissions) {
      if(name != key && this.find(name).allowed) {
        othersAllowed = true;
      }
    }

    return thisPermissionAllowed && !othersAllowed;
  }

  // Returns true if _any_ of the permissions in the hash are allowed -
  // otherwise returns false.
  areAnyPermissionsAllowed() {
    for(let name in this.permissions) {
      if(this.find(name).allowed) {
        return true
      }
    }

    return false;
  }

  // Returns true if _all of the permissions in the hash are allowed -
  // otherwise returns false.
  areAllPermissionsAllowed() {
    for(let name in this.permissions) {
      if(!this.find(name).allowed) {
        return false
      }
    }

    return true;
  }

  check(config, args) {
    args = args || [];

    if(config.all) {
      if(this.areAllPermissionsAllowed()) return config.render(...args);
    } else if (config.allowed) {
      if(this.isPermissionAllowed(config.allowed)) return config.render(...args);
    } else if (config.any) {
      if(this.areAllPermissionsAllowed()) return config.render(...args);
    } else if (config.only) {
      if(this.isPermissionOnlyOneAllowed(config.only)) return config.render(...args);
    } else {
      throw new Error('Missing permission config attribute. Must be either `all`, `allowed`, `any` or `only`');
    }

    return null;
  }

  // Returns the result of the first permission that's allowed
  first() {
    for(let config of arguments) {
      let result = this.check(config);

      if(result) return config.render();
    }

    return null;
  }

  // Loops through the arguments, and collects the results of each check. The
  // resulting array has all null values removed.
  collect() {
    let results = [];
    for(let idx in arguments) {
      let config = arguments[idx];
      let result = this.check(config, idx);

      if(result) results.push(result);
    }

    return (results.length > 0) ? results : null;
  }

  find(key) {
    let result = this.permissions[key]

    // Ensure sure the permission is even there
    if(!result) {
      throw new Error('Missing permission "' + key + '"', this.permissions);
    }

    // Validate that the 'allowed' key is present
    if(result.allowed == undefined) {
      throw new Error('Missing `allowed` property for "' + key + '" permission');
    }

    return result;
  }
}

export default (permissions) => { return new PermissionManager(permissions) }
<commit_msg>`arguments` isn't always an array, so force it to be one<commit_after>import update from "react-addons-update";

class PermissionManager {
  constructor(permissions) {
    if(!permissions) {
      throw new Error("Missing permission hash");
    }

    // Remove the hidden __dataID__ from the permissions object (which comes
    // from Relay)
    this.permissions = update(permissions, {});
    delete this.permissions.__dataID__;
  }

  // Returns true if a specific permission is allowed
  isPermissionAllowed(key) {
    return this.find(key).allowed;
  }

  // Returns true if this permission is the only one that's allowed
  isPermissionOnlyOneAllowed(key) {
    let thisPermissionAllowed = this.find(key).allowed;
    let othersAllowed = false;

    // Check to see if one of the other permissions is allowed
    for(let name in this.permissions) {
      if(name != key && this.find(name).allowed) {
        othersAllowed = true;
      }
    }

    return thisPermissionAllowed && !othersAllowed;
  }

  // Returns true if _any_ of the permissions in the hash are allowed -
  // otherwise returns false.
  areAnyPermissionsAllowed() {
    for(let name in this.permissions) {
      if(this.find(name).allowed) {
        return true
      }
    }

    return false;
  }

  // Returns true if _all of the permissions in the hash are allowed -
  // otherwise returns false.
  areAllPermissionsAllowed() {
    for(let name in this.permissions) {
      if(!this.find(name).allowed) {
        return false
      }
    }

    return true;
  }

  check(config, args) {
    args = args || [];

    if(config.all) {
      if(this.areAllPermissionsAllowed()) return config.render(...args);
    } else if (config.allowed) {
      if(this.isPermissionAllowed(config.allowed)) return config.render(...args);
    } else if (config.any) {
      if(this.areAllPermissionsAllowed()) return config.render(...args);
    } else if (config.only) {
      if(this.isPermissionOnlyOneAllowed(config.only)) return config.render(...args);
    } else {
      throw new Error('Missing permission config attribute. Must be either `all`, `allowed`, `any` or `only`');
    }

    return null;
  }

  // Returns the result of the first permission that's allowed
  first() {
    let args = [].slice.call(arguments);
    for(let config of args) {
      let result = this.check(config);

      if(result) return config.render();
    }

    return null;
  }

  // Loops through the arguments, and collects the results of each check. The
  // resulting array has all null values removed.
  collect() {
    let args = [].slice.call(arguments);
    let results = [];
    for(let idx in args) {
      let config = args[idx];
      let result = this.check(config, idx);

      if(result) results.push(result);
    }

    return (results.length > 0) ? results : null;
  }

  find(key) {
    let result = this.permissions[key]

    // Ensure sure the permission is even there
    if(!result) {
      throw new Error('Missing permission "' + key + '"', this.permissions);
    }

    // Validate that the 'allowed' key is present
    if(result.allowed == undefined) {
      throw new Error('Missing `allowed` property for "' + key + '" permission');
    }

    return result;
  }
}

export default (permissions) => { return new PermissionManager(permissions) }
<|endoftext|>


<commit_before>/** @jsx React.DOM */

// Treeview component
var Treeview = React.createClass({
    name: 'treeview',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'selectType', type:'string', required:false, defaultValue:'n', note:'select type:none/checkbox/radio' },
            { name:'expandLevel', type:'number', required:false, defaultValue:3, note:'levels to expand' },
            { name:'treedata', type:'array', required:false, defaultValue:[], note:'input treeview data' },
            // internal, don't pass in
            { name:'treedataObject', type:'object', required:false, defaultValue:[], note:'treeview data object' },
            { name:'treedataCol', type:'array', required:false, defaultValue:[], note:'treeview data in hash array' }
        ];
        return attributes;
    },

    componentWillMount: function() {
        this.state.treedataObject = this.prepareTreedata(this.state.treedata);
        this.state.nodes = this.getNodesFromTreeItem(this.state.treedataObject);
    },

    // add unique id to treedata
    prepareTreedata: function(treedata) {
        // convert object into array
        var startLevel = 1;
        this.state.treedataCol = {};
        if (typeof treedata === 'object') {
            if (treedata.constructor.name === 'Array') {
                startLevel = 0;
                treedata = { children:treedata };
            }
        }
        this.processTreedataItem(treedata, startLevel);
        return treedata;
    },

    processTreedataItem: function(item, level) {
        item.level = level;
        item.uid = this.generateUid();
        item.selectStatus = 'n'; // select status can be all | partial | none
        item.isExpanded = (level < this.state.expandLevel) || false;
        item.hasChildren = (item.children && item.children.length > 0) || false;
        if (level > 0) {
            this.state.treedataCol[item.uid] = item;
        }
        if (item.children) {
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                this.processTreedataItem(childItem, level + 1);
            }
        }
    },

    /*
    Getting nodes from treedata item
    Example input treedata:
    [
        {
            "name": "Top Level",
            "children": [
                {
                    "name": "Level 2: A",
                    "children": [
                        {
                            "name": "Son of A"
                        },
                        {
                            "name": "Daughter of A"
                        }
                    ]
                },
                {
                    "name": "Level 2: B"
                },
                {
                    "name": "Level 2: C",
                    "children": [
                        {
                            "name": "Son of C"
                        }
                    ]
                }
            ]
        }
    ]
    output nodes:
    [
        { display:'Top Level', level:1 },
        { display:'Level 2: A', level:2 },
        { display:'Son of A', level:3 },
        { display:'Daughter of A', level:3 },
        { display:'Level 2: B', level:2 },
        { display:'Level 2: C', level:2 },
        { display:'Son of C', level:3 }
    ]
    */
    getNodesFromTreeItem: function(item) {
        var nodes = [];
        var currentNode = { isExpanded:true };
        var display = item.name || item.text || 'NA';
        // skip level 0, it is artificial object for array input
        if (item.level > 0) {
            currentNode = {
                uid: item.uid,
                item: item,
                display: display,
                level: item.level,
                hasChildren: item.hasChildren,
                isExpanded: item.isExpanded,
                selectStatus: item.selectStatus,
                boxClass: item.boxClass
            };
            nodes.push(currentNode);
        }
        if (item.children && currentNode.isExpanded) {
            var childNodes = [];
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                var childNodes = this.getNodesFromTreeItem(childItem);
                nodes = nodes.concat(childNodes);
            }
        }
        return nodes;
    },

    /* update select status under item
     * @param item Object item under update
     * @param changedItem Object the item that has change of status originally
     * @param inheritStatus String optional, status to be updated to (inherited from parent)
     */
    updateTreeSelectStatus: function(item, changedItem, inheritStatus) {
        // set initial select status to tree item
        var deduceSelectStatus = false;
        if (item.uid === changedItem.uid) {
            // reached item that changes select status originally, no change is needed for item
            // set inheritStatus for children to set select status to
            inheritStatus = item.selectStatus; // should be 'a' or 'n'
        } else if (inheritStatus) {
            // Set select status to inheritStatus if available
            item.selectStatus = inheritStatus;
        } else {
            // need to deduce selectStatus from children's select statuses
            deduceSelectStatus = true;
        }
        // process child items, keep track of selected child items
        var selectChildrenCount = 0;
        if (item.children && item.children.length > 0) {
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                this.updateTreeSelectStatus(childItem, changedItem, inheritStatus);
                if (childItem.selectStatus === 'a') {
                    selectChildrenCount = selectChildrenCount + 1;
                } else if (childItem.selectStatus === 'p') {
                    selectChildrenCount = selectChildrenCount + .5;
                }
            }
        }
        // to deduced select status, compare children count to selected children count
        if (deduceSelectStatus) {
            if (item.children && item.children.length > 0) {
                if (selectChildrenCount === item.children.length) {
                    item.selectStatus = 'a';
                } else if (selectChildrenCount === 0) {
                    item.selectStatus = 'n';
                } else {
                    item.selectStatus = 'p';
                }
            } else {
                item.selectStatus = 'n';
            }
        }
    },

    onNodeClick: function(event) {
        var target = $(event.target);
        // get cellId from cell icon  or from cell container
        var cellId = target.attr('data-uid');
        if (target.hasClass('treeview-cell-expand-container')) {
            cellId = target.find('i').attr('data-uid');
        }
        if (!cellId) {
            return;
        }
        // cellId format: "<nodeKey>-<cellType>-<select status>-<expand status>"
        // cellId example: "36c2b148338c8d7a7f99ef6b881e281f-expand-a-e"
        var parts = cellId.split('-');
        var nodeKey = parts[0];
        var cellType = parts[1];
        var treedataItem = this.state.treedataCol[nodeKey];
        // change this.state.treedata according to type of icon clicked
        switch (cellType) {
            case 'expand':
                treedataItem.isExpanded = !treedataItem.isExpanded;
                break;
            case 'icon':
                break;
            case 'select':
                switch(treedataItem.selectStatus) {
                case 'a':
                    treedataItem.selectStatus = 'n';
                    break;
                case 'p':
                    treedataItem.selectStatus = 'a';
                    break;
                case 'n':
                    treedataItem.selectStatus = 'a';
                    break;
                }
                break;
        }
        // sync select status of all tree nodes
        this.updateTreeSelectStatus(this.state.treedataObject, treedataItem);
        this.dumpTree(this.state.treedataObject);
        this.state.nodes = this.getNodesFromTreeItem(this.state.treedataObject);
        // update display
        this.forceUpdate();
    },

    dumpTree: function(item) {
        if (item.level > 0) {
            var output = '';
            for (var i = 0; i < item.level; i++) {
                output = output + '  ';
            }
            output = output + item.name + '-' + item.selectStatus;
            console.log(output);
        }
        if (item.children && item.children.length > 0) {
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                this.dumpTree(childItem);
            }
        }
    },

    // set data for display
    setData: function(treedata) {
        this.state.treedata = treedata;
        this.state.treedataObject = this.prepareTreedata(this.state.treedata);
        this.state.nodes = this.getNodesFromTreeItem(this.state.treedataObject);
        this.forceUpdate();
    },

    // get selected items
    getSelected: function() {
        var resultItems = [];
        var domNode = this.getDOMNode();
        var selectedIcons = $(domNode).find('.treeview-cell-select-container .fa-check-square-o');
        for (var i = 0; i < selectedIcons.length; i ++) {
            var uid = $(selectedIcons[i]).attr('data-uid').split('-')[0];
            var item = this.state.treedataCol[uid];
            resultItems.push(item);
        }
        return resultItems;
    },

    render: function() {
        var treenodes = [];
        for (var i = 0; i < this.state.nodes.length; i++) {
            var node = this.state.nodes[i];
            node.selectType = node.selectType || this.state.selectType;
            // need to include node properties tied with UI change, for example: isExpanded
            var nodeKey = 'treenode-' + node.uid;
            nodeKey += '-' + node.selectStatus;
            nodeKey += '-' + (node.isExpanded ? 'e' : 'n');
            treenodes.push(<TreeviewNode data={ node } key={ nodeKey } />);
        }
        return (

                    { treenodes }

        );
    }
});

// TreeviewNode component
var TreeviewNode = React.createClass({
    name: 'treeview-node',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'uid', type:'string', required:false, defaultValue:'', note:'unique id for node' },
            { name:'level', type:'number', required:false, defaultValue:1, note:'node level' },
            { name:'display', type:'string', required:false, defaultValue:'', note:'text display' },
            { name:'hasChildren', type:'boolean', required:false, defaultValue:false, note:'has children flag' },
            { name:'isExpanded', type:'boolean', required:false, defaultValue:false, note:'is expanded flag' },
            { name:'selectType', type:'string', required:false, defaultValue:'n', note:'select type:none/checkbox/radio' },
            { name:'selectStatus', type:'boolean', required:false, defaultValue:'n', note:'selecte state all/partial/none' },
            { name:'item', type:'object', required:false, defaultValue:'', note:'original data item' }
        ];
        return attributes;
    },

    render: function() {
        var items = [];
        var cells = [];
        var level = this.state.level;

        // add branch cells
        for (var i = 1; i < level; i++) {
            var cellData = { text:i, type:'space' };
            var cellKey = 'treeview-text-cell-space-' + i;
            cells.push(
                <TreeviewCell data={ cellData } key={ cellKey } />
            );
        }
        // add expand cell if there are children
        var expandCellData = {
            uid: this.state.uid + '-expand',
            type: 'expand',
            iconClass: this.state.isExpanded ? 'fa fa-caret-down' : 'fa fa-caret-right'
        };
        if (!this.state.hasChildren) {
            expandCellData.iconClass = 'fa';
        }
        cells.push(<TreeviewCell data={ expandCellData } key='treeview-expand-cell' />);

        // add select cell
        if (this.state.selectType === 'c') {
            // checkbox select type
            var iconClass = '';
            switch(this.state.selectStatus) {
            case 'a':
                iconClass = 'fa fa-check-square-o';
                break;
            case 'p':
                iconClass = 'fa fa-square';
                break;
            case 'n':
                iconClass = 'fa fa-square-o';
                break;
            }
            var selectCellData = {
                uid: this.state.uid + '-select',
                type: 'select',
                iconClass: iconClass
            };
            var selectCellKey = 'treeview-select-cell';
            cells.push(<TreeviewCell data={ selectCellData } key={ selectCellKey } />);
        }

        // only add icon cell when iconClass is present
        if (this.state.item.iconClass) {
            var iconCellData = {
                uid: this.state.uid + '-icon',
                type: 'icon',
                iconClass: this.state.item.iconClass
            };
            cells.push(<TreeviewCell data={ iconCellData } key='treeview-icon-cell' />);
        }

        // add textbox
        var textboxData = {
            uid: this.state.uid + '-textbox',
            type: 'text',
            text: this.state.display
        };
        cells.push(<TreeviewTextBox data={ textboxData } key='treeview-textbox' />);

        return (

                { cells }

        );
    }
});

// TreeviewCell component
var TreeviewCell = React.createClass({
    name: 'treeview-cell',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'iconClass', type:'string', required:false, defaultValue:'', note:'icon CSS class' },
            { name:'uid', type:'string', required:false, defaultValue:'', note:'unique id for cell' },
            { name:'type', type:'string', required:false, defaultValue:'', note:'treeview type' }
        ];
        return attributes;
    },

    render: function() {
        // set iconClass
        var displayText = '';
        var iconClass = this.state.iconClass || '';
        var iconContainerClass = 'treeview-cell-' + this.state.type + '-container';
        switch (this.state.type) {
            case 'text':
                displayText = this.state.text;
                break;
            case 'expand':
                iconClass = iconClass || 'fa fa-plus-square-o';
                break;
            case 'select':
                iconClass = iconClass || 'fa fa-check-square-o';
                break;
            case 'icon':
                iconClass = iconClass || '';
                break;
        }
        return (

                    { displayText }

        );
    }
});

// Treeview textbox component
var TreeviewTextBox = React.createClass({
    name: 'treeview-textbox',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'uid', type:'string', required:false, defaultValue:'', note:'unique id for cell' },
            { name:'text', type:'string', required:false, defaultValue:'', note:'display text' },
            { name:'type', type:'string', required:false, defaultValue:'', note:'treeview type' }
        ];
        return attributes;
    },

    render: function() {
        // set content
        var displayText = this.state.text;
        return (

        );
    }
});
<commit_msg>fixed multi select bug<commit_after>/** @jsx React.DOM */

// Treeview component
var Treeview = React.createClass({
    name: 'treeview',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'selectType', type:'string', required:false, defaultValue:'n', note:'select type:none/checkbox/radio' },
            { name:'expandLevel', type:'number', required:false, defaultValue:3, note:'levels to expand' },
            { name:'treedata', type:'array', required:false, defaultValue:[], note:'input treeview data' },
            // internal, don't pass in
            { name:'treedataObject', type:'object', required:false, defaultValue:[], note:'treeview data object' },
            { name:'treedataCol', type:'array', required:false, defaultValue:[], note:'treeview data in hash array' }
        ];
        return attributes;
    },

    componentWillMount: function() {
        this.state.treedataObject = this.prepareTreedata(this.state.treedata);
        this.state.nodes = this.getNodesFromTreeItem(this.state.treedataObject);
    },

    // add unique id to treedata
    prepareTreedata: function(treedata) {
        // convert object into array
        var startLevel = 1;
        this.state.treedataCol = {};
        if (typeof treedata === 'object') {
            if (treedata.constructor.name === 'Array') {
                startLevel = 0;
                treedata = { children:treedata };
            }
        }
        this.processTreedataItem(treedata, startLevel);
        return treedata;
    },

    processTreedataItem: function(item, level) {
        item.level = level;
        item.uid = this.generateUid();
        item.selectStatus = 'n'; // select status can be all | partial | none
        item.isExpanded = (level < this.state.expandLevel) || false;
        item.hasChildren = (item.children && item.children.length > 0) || false;
        if (level > 0) {
            this.state.treedataCol[item.uid] = item;
        }
        if (item.children) {
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                this.processTreedataItem(childItem, level + 1);
            }
        }
    },

    /*
    Getting nodes from treedata item
    Example input treedata:
    [
        {
            "name": "Top Level",
            "children": [
                {
                    "name": "Level 2: A",
                    "children": [
                        {
                            "name": "Son of A"
                        },
                        {
                            "name": "Daughter of A"
                        }
                    ]
                },
                {
                    "name": "Level 2: B"
                },
                {
                    "name": "Level 2: C",
                    "children": [
                        {
                            "name": "Son of C"
                        }
                    ]
                }
            ]
        }
    ]
    output nodes:
    [
        { display:'Top Level', level:1 },
        { display:'Level 2: A', level:2 },
        { display:'Son of A', level:3 },
        { display:'Daughter of A', level:3 },
        { display:'Level 2: B', level:2 },
        { display:'Level 2: C', level:2 },
        { display:'Son of C', level:3 }
    ]
    */
    getNodesFromTreeItem: function(item) {
        var nodes = [];
        var currentNode = { isExpanded:true };
        var display = item.name || item.text || 'NA';
        // skip level 0, it is artificial object for array input
        if (item.level > 0) {
            currentNode = {
                uid: item.uid,
                item: item,
                display: display,
                level: item.level,
                hasChildren: item.hasChildren,
                isExpanded: item.isExpanded,
                selectStatus: item.selectStatus,
                boxClass: item.boxClass
            };
            nodes.push(currentNode);
        }
        if (item.children && currentNode.isExpanded) {
            var childNodes = [];
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                var childNodes = this.getNodesFromTreeItem(childItem);
                nodes = nodes.concat(childNodes);
            }
        }
        return nodes;
    },

    /* update select status under item
     * @param item Object item under update
     * @param changedItem Object the item that has change of status originally
     * @param inheritStatus String optional, status to be updated to (inherited from parent)
     */
    updateTreeSelectStatus: function(item, changedItem, inheritStatus) {
        // set initial select status to tree item
        var deduceSelectStatus = false;
        if (item.uid === changedItem.uid) {
            // reached item that changes select status originally, no change is needed for item
            // set inheritStatus for children to set select status to
            inheritStatus = item.selectStatus; // should be 'a' or 'n'
        } else if (inheritStatus) {
            // Set select status to inheritStatus if available
            item.selectStatus = inheritStatus;
        } else {
            // need to deduce selectStatus from children's select statuses
            if (item.children && item.children.length > 0) {
                deduceSelectStatus = true;
            } else {
                // item remains current select status
            }
        }
        // process child items, keep track of selected child items
        var selectChildrenCount = 0;
        if (item.children && item.children.length > 0) {
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                this.updateTreeSelectStatus(childItem, changedItem, inheritStatus);
                if (childItem.selectStatus === 'a') {
                    selectChildrenCount = selectChildrenCount + 1;
                } else if (childItem.selectStatus === 'p') {
                    selectChildrenCount = selectChildrenCount + .5;
                }
            }
        }
        // to deduced select status, compare children count to selected children count
        if (deduceSelectStatus) {
            if (item.children && item.children.length > 0) {
                if (selectChildrenCount === item.children.length) {
                    item.selectStatus = 'a';
                } else if (selectChildrenCount === 0) {
                    item.selectStatus = 'n';
                } else {
                    item.selectStatus = 'p';
                }
            } else {
                item.selectStatus = 'n';
            }
        }
    },

    onNodeClick: function(event) {
        var target = $(event.target);
        // get cellId from cell icon  or from cell container
        var cellId = target.attr('data-uid');
        if (target.hasClass('treeview-cell-expand-container')) {
            cellId = target.find('i').attr('data-uid');
        }
        if (!cellId) {
            return;
        }
        // cellId format: "<nodeKey>-<cellType>-<select status>-<expand status>"
        // cellId example: "36c2b148338c8d7a7f99ef6b881e281f-expand-a-e"
        var parts = cellId.split('-');
        var nodeKey = parts[0];
        var cellType = parts[1];
        var treedataItem = this.state.treedataCol[nodeKey];
        // change this.state.treedata according to type of icon clicked
        switch (cellType) {
            case 'expand':
                treedataItem.isExpanded = !treedataItem.isExpanded;
                break;
            case 'icon':
                break;
            case 'select':
                switch(treedataItem.selectStatus) {
                case 'a':
                    treedataItem.selectStatus = 'n';
                    break;
                case 'p':
                    treedataItem.selectStatus = 'a';
                    break;
                case 'n':
                    treedataItem.selectStatus = 'a';
                    break;
                }
                break;
        }
        // sync select status of all tree nodes
        this.updateTreeSelectStatus(this.state.treedataObject, treedataItem);
        //this.dumpTree(this.state.treedataObject);
        this.state.nodes = this.getNodesFromTreeItem(this.state.treedataObject);
        // update display
        this.forceUpdate();
    },

    dumpTree: function(item) {
        if (item.level > 0) {
            var output = '';
            for (var i = 0; i < item.level; i++) {
                output = output + '  ';
            }
            output = output + item.name + '-' + item.selectStatus;
            console.log(output);
        }
        if (item.children && item.children.length > 0) {
            for (var i = 0; i < item.children.length; i++) {
                var childItem = item.children[i];
                this.dumpTree(childItem);
            }
        }
    },

    // set data for display
    setData: function(treedata) {
        this.state.treedata = treedata;
        this.state.treedataObject = this.prepareTreedata(this.state.treedata);
        this.state.nodes = this.getNodesFromTreeItem(this.state.treedataObject);
        this.forceUpdate();
    },

    // get selected items
    getSelected: function() {
        var resultItems = [];
        var domNode = this.getDOMNode();
        var selectedIcons = $(domNode).find('.treeview-cell-select-container .fa-check-square-o');
        for (var i = 0; i < selectedIcons.length; i ++) {
            var uid = $(selectedIcons[i]).attr('data-uid').split('-')[0];
            var item = this.state.treedataCol[uid];
            resultItems.push(item);
        }
        return resultItems;
    },

    render: function() {
        var treenodes = [];
        for (var i = 0; i < this.state.nodes.length; i++) {
            var node = this.state.nodes[i];
            node.selectType = node.selectType || this.state.selectType;
            // need to include node properties tied with UI change, for example: isExpanded
            var nodeKey = 'treenode-' + node.uid;
            nodeKey += '-' + node.selectStatus;
            nodeKey += '-' + (node.isExpanded ? 'e' : 'n');
            treenodes.push(<TreeviewNode data={ node } key={ nodeKey } />);
        }
        return (

                    { treenodes }

        );
    }
});

// TreeviewNode component
var TreeviewNode = React.createClass({
    name: 'treeview-node',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'uid', type:'string', required:false, defaultValue:'', note:'unique id for node' },
            { name:'level', type:'number', required:false, defaultValue:1, note:'node level' },
            { name:'display', type:'string', required:false, defaultValue:'', note:'text display' },
            { name:'hasChildren', type:'boolean', required:false, defaultValue:false, note:'has children flag' },
            { name:'isExpanded', type:'boolean', required:false, defaultValue:false, note:'is expanded flag' },
            { name:'selectType', type:'string', required:false, defaultValue:'n', note:'select type:none/checkbox/radio' },
            { name:'selectStatus', type:'boolean', required:false, defaultValue:'n', note:'selecte state all/partial/none' },
            { name:'item', type:'object', required:false, defaultValue:'', note:'original data item' }
        ];
        return attributes;
    },

    render: function() {
        var items = [];
        var cells = [];
        var level = this.state.level;

        // add branch cells
        for (var i = 1; i < level; i++) {
            var cellData = { text:i, type:'space' };
            var cellKey = 'treeview-text-cell-space-' + i;
            cells.push(
                <TreeviewCell data={ cellData } key={ cellKey } />
            );
        }
        // add expand cell if there are children
        var expandCellData = {
            uid: this.state.uid + '-expand',
            type: 'expand',
            iconClass: this.state.isExpanded ? 'fa fa-caret-down' : 'fa fa-caret-right'
        };
        if (!this.state.hasChildren) {
            expandCellData.iconClass = 'fa';
        }
        cells.push(<TreeviewCell data={ expandCellData } key='treeview-expand-cell' />);

        // add select cell
        if (this.state.selectType === 'c') {
            // checkbox select type
            var iconClass = '';
            switch(this.state.selectStatus) {
            case 'a':
                iconClass = 'fa fa-check-square-o';
                break;
            case 'p':
                iconClass = 'fa fa-square';
                break;
            case 'n':
                iconClass = 'fa fa-square-o';
                break;
            }
            var selectCellData = {
                uid: this.state.uid + '-select',
                type: 'select',
                iconClass: iconClass
            };
            var selectCellKey = 'treeview-select-cell';
            cells.push(<TreeviewCell data={ selectCellData } key={ selectCellKey } />);
        }

        // only add icon cell when iconClass is present
        if (this.state.item.iconClass) {
            var iconCellData = {
                uid: this.state.uid + '-icon',
                type: 'icon',
                iconClass: this.state.item.iconClass
            };
            cells.push(<TreeviewCell data={ iconCellData } key='treeview-icon-cell' />);
        }

        // add textbox
        var textboxData = {
            uid: this.state.uid + '-textbox',
            type: 'text',
            text: this.state.display
        };
        cells.push(<TreeviewTextBox data={ textboxData } key='treeview-textbox' />);

        return (

                { cells }

        );
    }
});

// TreeviewCell component
var TreeviewCell = React.createClass({
    name: 'treeview-cell',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'iconClass', type:'string', required:false, defaultValue:'', note:'icon CSS class' },
            { name:'uid', type:'string', required:false, defaultValue:'', note:'unique id for cell' },
            { name:'type', type:'string', required:false, defaultValue:'', note:'treeview type' }
        ];
        return attributes;
    },

    render: function() {
        // set iconClass
        var displayText = '';
        var iconClass = this.state.iconClass || '';
        var iconContainerClass = 'treeview-cell-' + this.state.type + '-container';
        switch (this.state.type) {
            case 'text':
                displayText = this.state.text;
                break;
            case 'expand':
                iconClass = iconClass || 'fa fa-plus-square-o';
                break;
            case 'select':
                iconClass = iconClass || 'fa fa-check-square-o';
                break;
            case 'icon':
                iconClass = iconClass || '';
                break;
        }
        return (

                    { displayText }

        );
    }
});

// Treeview textbox component
var TreeviewTextBox = React.createClass({
    name: 'treeview-textbox',
    mixins: [getCommonMixin],

    // attribute definitions
    getAttributes: function() {
        var attributes = [
            { name:'boxClass', type:'string', required:false, defaultValue:'', note:'container CSS class' },
            { name:'uid', type:'string', required:false, defaultValue:'', note:'unique id for cell' },
            { name:'text', type:'string', required:false, defaultValue:'', note:'display text' },
            { name:'type', type:'string', required:false, defaultValue:'', note:'treeview type' }
        ];
        return attributes;
    },

    render: function() {
        // set content
        var displayText = this.state.text;
        return (

        );
    }
});
<|endoftext|>


<commit_before>var db = require("../../db.js");
var mongojs = require('mongojs');
var ObjectId = mongojs.ObjectId;
var forEach = require('async-foreach').forEach;

module.exports = {
	createQuestions:function (params, callback) {
		var text = params.text;
		var questionDoc = {
			"text":text,
			"_random_sample": Math.random()
		};
		db.question.insert(questionDoc, function(err, docs) {
            callback(err,docs);
        });
	},
	getQuestions : function(params, callback){
		db.question.find({},function(err,docs){
			callback(err,docs);
		})
	},
	getQuestionsWithAnswers : function(params, callback){
		var answer = db.collection('answer');
		var rand = Math.random();
		console.log(rand);
		db.question.find({_random_sample:{$gte:rand}},
			{
							"_id" :true,
							"text":true
						}).limit(10).toArray(
			function(err,docs){

			if(!err && docs.length != 0){

				forEach(docs,function(doc,i,docs){
					var done = this.async();
					db.answer.find({"question_id": doc._id },
						{
							"_id" :true,
							"text":true
						},
						function(er,dc){
	    					docs[i].answers = dc;
	    					done();
	    				}
	    			);

				},function(notaborted,docs){
					callback(err,docs);
				});
			}else{
				callback(err,docs);
			}
		})
	},
	getQuestionWithAnswers : function(params, callback){
		var answer = db.collection('answer');
		var question_id = ObjectId(params.id);
		db.question.findOne({_id:question_id},function(err,docs){
			console.log(err,docs);
			if(!err){
				db.answer.find({"question_id": question_id },
					{
						"_id" :true,
						"text":true
					},
					function(er,dc){
						console.log(err,dc);
    				docs.answers = dc;
    				callback(er,docs);
    			});
			}else{
				callback(err,docs);
			}
		})
	}
}<commit_msg>Removed randomization for testing<commit_after>var db = require("../../db.js");
var mongojs = require('mongojs');
var ObjectId = mongojs.ObjectId;
var forEach = require('async-foreach').forEach;

module.exports = {
	createQuestions:function (params, callback) {
		var text = params.text;
		var questionDoc = {
			"text":text,
			"_random_sample": Math.random()
		};
		db.question.insert(questionDoc, function(err, docs) {
            callback(err,docs);
        });
	},
	getQuestions : function(params, callback){
		db.question.find({},function(err,docs){
			callback(err,docs);
		})
	},
	getQuestionsWithAnswers : function(params, callback){
		var answer = db.collection('answer');
		var rand = Math.random();
		console.log(rand);
		//db.question.find({_random_sample:{$gte:rand}},
			db.question.find({},
			{
							"_id" :true,
							"text":true
						}).limit(10).toArray(
			function(err,docs){

			if(!err && docs.length != 0){

				forEach(docs,function(doc,i,docs){
					var done = this.async();
					db.answer.find({"question_id": doc._id },
						{
							"_id" :true,
							"text":true
						},
						function(er,dc){
	    					docs[i].answers = dc;
	    					done();
	    				}
	    			);

				},function(notaborted,docs){
					callback(err,docs);
				});
			}else{
				callback(err,docs);
			}
		})
	},
	getQuestionWithAnswers : function(params, callback){
		var answer = db.collection('answer');
		var question_id = ObjectId(params.id);
		db.question.findOne({_id:question_id},function(err,docs){
			console.log(err,docs);
			if(!err){
				db.answer.find({"question_id": question_id },
					{
						"_id" :true,
						"text":true
					},
					function(er,dc){
						console.log(err,dc);
    				docs.answers = dc;
    				callback(er,docs);
    			});
			}else{
				callback(err,docs);
			}
		})
	}
}<|endoftext|>


<commit_before>/**
 * MongoDB storage implementation.
 */

'use strict';

var mongodb = require('mongodb'),
    BSON = mongodb.BSONPure,
    config = diana.getConfig().mongodb;

exports.createInstance = function() {
  return new MongoDbStorage();
};

var MongoDbStorage = function() {};

// Db instance.
MongoDbStorage.prototype.link = null;

// Read from app/config.js.
MongoDbStorage.prototype.eventCollection = null;

/**
 * Post-process events found via findOne(), find(), etc.
 *
 * @param docs {Array|Object} Query result(s).
 * @param options {Object} Query options used to produce 'docs'.
 * @return {Object}
 * - info {Object}
 *   prevPage {Boolean}
 *   nextPage {Boolean}
 * - docs {Array}
 */
MongoDbStorage.prototype.eventPostFind = function(docs, options) {
  docs = _.isArray(docs) ? docs : [docs];
  options = options || {};
  var post = {
    info: {
      prevPage: docs && options.skip > 0,
      nextPage: docs && (docs.length == options.limit)
    },
    docs: docs
  };
  if (post.docs) {
    if (post.info.nextPage) { // Discard next-page hint doc.
      post.docs.pop();
    }
    post.docs = _.map(post.docs, function(doc) {
      doc.time = doc.time.getTime();
      return doc;
    });
  }
  return post;
};

/**
 * Extract find()-compatible options from an object containing app-specific
 * keys, e.g. 'sort-dir' and 'sort-attr' which are converted into {sort: {...}}.
 *
 * @param params {Object}
 * @return {Object}
 */
MongoDbStorage.prototype.extractSortOptions = function(params) {
  var options = {};
  if (params['sort-attr']) {
    if ('desc' == params['sort-dir']) {
      options.sort = [[params['sort-attr'], 'desc']];
      delete params['sort-dir'];
    } else if ('asc' == params['sort-dir']) {
      options.sort = [[params['sort-attr'], 'asc']];
      delete params['sort-dir'];
    }
    delete params['sort-attr'];

    if ('time' == options.sort[0][0]) {
      options.sort.push(['_id', options.sort[0][1]]);
    }
  }
  if (params.limit) {
    options.limit = Math.min(parseInt(params.limit, 10), config.maxResultSize);
    delete params.limit;
  } else {
    options.limit = config.maxResultSize;
  }
  if (params.skip) {
    options.skip = parseInt(params.skip, 10);
    delete params.skip;
  }
  options.limit += 1; // For next-page detection.
  return options;
};

/**
 * Extract find()-compatible filters from an object containing app-specific
 * keys, e.g. 'time-gte' which is converted into {time: {$gte: ...}}.
 *
 * @param params {Object} Modified in-place with extracted options.
 */
MongoDbStorage.prototype.extractFilterOptions = function(params) {
  _.each(params, function(value, key) {
    var matches = null;
    if ((matches = key.match(/^(.*)-(gte|gt|lte|lt|ne)$/))) {
      if ('time' == matches[1]) {
        value = new Date(value);
      }
      if (!params[matches[1]]) {
        params[matches[1]] = {};
      }
      params[matches[1]]['$' + matches[2]] = value;
      delete params[key];
    } else if ('_id' == key && _.isString(value)) {
      params[key] = new BSON.ObjectID(value);
    }
  });
};

/**
 * Connect to the DB based on config.js. Reuse a link if available.
 *
 * @param error {Function} Fired after error.
 * @param success {Function} Fired after success.
 */
MongoDbStorage.prototype.dbConnectAndOpen = function(error, success) {
  if (this.link) {
    success(null, this.link);
  } else {
    this.eventCollection = config.eventCollection;
    this.link = new mongodb.Db(
      config.db,
      new mongodb.Server(config.host, config.port, {})
    );
    this.link.open(function(err, db) {
      if (err) {
        error('Could not access database.', null);
      } else {
        success(err, db);
      }
    });
  }
};

/**
 * Open a DB collection.
 *
 * @param db {Object} Connection link.
 * @param collection {String} Collection name.
 * @param error {Function} Fired on error.
 * @param success {Function} Fired on success.
 */
MongoDbStorage.prototype.dbCollection = function(db, collection, error, success) {
  var mongo = this;
  db.collection(collection, function(err, collection) {
    if (err) {
      mongo.dbClose('Could not access collection.', error);
    } else {
      success(err, collection);
    }
  });
};

/**
 * Disconnect from the DB.
 *
 * @param err {String} Optional error value passed to 'callback'.
 * @param callback {Function} Optional function called after link is closed.
 * - Receives two arguments, 'err' and null.
 */
MongoDbStorage.prototype.dbClose = function(err, callback) {
  if (this.link) {
    this.link.close();
    this.link = null;
  }
  if (callback) {
    callback(err, null);
  }
};

/**
 * Insert a single parsed log line.
 *
 * @param logs {Array|Object} Output from a parser module's parse() function.
 * @param callback {Function} Receives insert() results.
 * @param bulk {Boolean} (Optional, Default: false) If true, auto-close connection.
 */
MongoDbStorage.prototype.insertLog = function(logs, callback, bulk) {
  var mongo = this;
  this.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, mongo.eventCollection, callback, function(err, collection) {
      var docs = [];
      logs = _.isArray(logs) ? logs : [logs];
      _.each(logs, function(log) {
        log.time = new Date(log.time);
        docs.push(log);
      });
      collection.insert(docs, {safe: true}, function(err, docs) {
        if (!bulk) {
          mongo.dbClose();
        }
        callback(err, docs);
      });
    });
  });
};

/**
 * Retrieve a single log's attributes.
 *
 * @param id {String} Document ID.
 * @param callback {Function} Receives findOne() results.
 */
MongoDbStorage.prototype.getLog = function(id, callback) {
  var mongo = this;
  this.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, mongo.eventCollection, callback, function(err, collection) {
      collection.findOne({_id: new BSON.ObjectID(id)}, function(err, doc) {
        if (err) { mongo.dbClose(err, callback); return; }
        mongo.dbClose();
        if (doc) {
          var post = mongo.eventPostFind(doc);
          callback(err, post.docs[0]);
        } else {
          callback(err, null);
        }
      });
    });
  });
};

/**
 * Retrieve the attributes of all matching logs.
 *
 * @param params {Object}
 * - sort-dir {String} 'desc' or 'asc'
 * - sort-attr {String} Ex. 'time'
 * - skip {Number} Amount of documents skipped.
 * - limit {Number} Maximum amount of documents retrieved.
 * - All other key/value pairs are considered conditions.
 * @param callback {Function} Receives find() results.
 * - {String} MongoDB driver error message.
 * - {Array} Result set.
 * - {Object} Additional result details.
 *   'nextPage' {Boolean} True if additional documents exist.
 */
MongoDbStorage.prototype.getTimeline = function(params, callback) {
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, mongo.eventCollection, callback, function(err, collection) {
      var options = mongo.extractSortOptions(params);
      mongo.extractFilterOptions(params);
      collection.find(params, options).toArray(function(err, docs) {
        if (err) { mongo.dbClose(err, callback); return; }
        mongo.dbClose();
        var post = mongo.eventPostFind(docs, options);
        callback(err, post.docs, post.info);
      });
    });
  });
};

/**
 * Retrieve the attributes of all logs newer than a given ID/time.
 *
 * @param id {String}
 * @param time {Number} Timestamp in milliseconds.
 * @param params {Object} getTimeline() compatible parameters.
 * @param callback {Function} Receives find() results.
 */
MongoDbStorage.prototype.getTimelineUpdates = function(id, time, params, callback) {
  params._id = {$gt: new BSON.ObjectID(id)};
  params['time-gte'] = time;
  params['sort-attr'] = 'time';
  params['sort-dir'] = 'desc';
  this.getTimeline(params, callback);
};

/**
 * Retrieve the attributes of all logs newer than a given ID.
 *
 * @param job {Object}
 * - name {String} Name of collection to receive results.
 *   - Optionally supply __filename of a job script and the basename will be used.
 * - map {Function}
 * - reduce {Function}
 * - options {Object} (Optional) Collection.mapReduce() options.
 *   out {Object} (Default: {replace: <name>}) Output directive.
 * - return {String} (Optional, Default: none) Callback receives 'cursor' or 'array'.
 * - callback {Function} Fires after success/error.
 *   - If 'return' not set:
 *     err {String}
 *     stats {Object}
 *   - If 'return' set:
 *     err {String}
 *     return {Object|Array}
 *     stats {Object}
 */
MongoDbStorage.prototype.mapReduce = function(job) {
  job.name = diana.extractJobName(job.name);
  var collectionName = job.suffix ? job.name + '_' + job.suffix : job.name;
  var returnAsArray = job.return != 'cursor';

  var mongo = this;
  mongo.dbConnectAndOpen(job.callback, function(err, db) {
    job.options = job.options || {};
    job.options.out = job.options.out || {replace: collectionName};
    mongo.dbCollection(db, mongo.eventCollection, job.callback, function(err, collection) {
      collection.mapReduce(job.map, job.reduce, job.options, function(err, stats) {
        if (err) { mongo.dbClose(); job.callback(err); return; }
        if (job.return) {
          mongo.getMapReduceResults(collectionName, function(err, results) {
            mongo.dbClose();
            job.callback(err, results, stats);
          }, returnAsArray);
        } else {
          mongo.dbClose();
          job.callback(err, stats);
        }
      });
    });
  });
};

/**
 * Time-ranged wrapped for mapReduce().
 *
 * @param startTime {Number} UNIX timestamp in milliseconds.
 * @param endTime {Number} UNIX timestamp in milliseconds.
 * @param job {Object} MongoDbStorage.mapReduce() 'job' argument.
 */
MongoDbStorage.prototype.mapReduceTimeRange = function(startTime, endTime, job) {
  job.options = job.options || {};
  job.options.query = job.options.query || {};
  job.options.query.time = {
    $gte: new Date(parseInt(startTime, 10)),
    $lte: new Date(parseInt(endTime, 10))
  };
  this.mapReduce(job);
};

/**
 * Return all map reduce results from collection.
 *
 * @param name {String}
 * @param callback {Function} Fires after success/error.
 * - asArray = true: Receives an object with _id values as keys and remaining
 *   pairs as values.
 * - asArray = false: Receives a Cursor object.
 * @param asArray {Boolean} (Optional, Default: true)
 */
MongoDbStorage.prototype.getMapReduceResults = function(name, callback, asArray) {
  asArray = _.isUndefined(asArray) ? true : asArray;
  name = diana.extractJobName(name);
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, name, callback, function(err, collection) {
      if (asArray) {
        collection.find().toArray(function(err, docs) {
          mongo.dbClose();
          var results = {};
          _.each(docs, function(doc) {
            results[doc._id] = doc.value;
          })
          callback(err, results);
        });
      } else {
        collection.find(callback);
      }
    });
  });
};

/**
 * Check if a collection exists.
 *
 * @param name {String}
 * @param callback {Function} Fires after success/error.
 * - {Boolean}
 */
MongoDbStorage.prototype.collectionExists = function(name, callback) {
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    db.collections(function(err, results) {
      var names = {};
      _.each(results, function(collection) {
        names[collection.collectionName] = true;
      });
      callback(err, _.has(names, name));
    });
  });
};

/**
 * Retrieve a find() cursor.
 *
 * @param name {String}
 * @param params {Object} find() filters.
 * @param options {Object} find() options.
 * @param callback {Function} Receives a Cursor object.
 */
MongoDbStorage.prototype.getCollectionCursor = function(name, params, options, callback) {
  var mongo = this;
  params = params || {};
  options = options || {};
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, name, callback, function(err, collection) {
      callback(collection.find(params, options));
    });
  });
};

/**
 * Drop a collection.
 *
 * @param name {String}
 * @param callback {Function} Fires on drop completion.
 */
MongoDbStorage.prototype.dropCollection = function(name, callback) {
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, name, callback, function(err, collection) {
      collection.drop(function() {
        mongo.dbClose();
        callback();
      });
    });
  });
};
<commit_msg>MongoDBStorage -> MongoDB<commit_after>/**
 * MongoDB storage implementation.
 */

'use strict';

var mongodb = require('mongodb'),
    BSON = mongodb.BSONPure,
    config = diana.getConfig().mongodb;

exports.createInstance = function() {
  return new MongoDb();
};

var MongoDb = function() {};

// Db instance.
MongoDb.prototype.link = null;

// Read from app/config.js.
MongoDb.prototype.eventCollection = null;

/**
 * Post-process events found via findOne(), find(), etc.
 *
 * @param docs {Array|Object} Query result(s).
 * @param options {Object} Query options used to produce 'docs'.
 * @return {Object}
 * - info {Object}
 *   prevPage {Boolean}
 *   nextPage {Boolean}
 * - docs {Array}
 */
MongoDb.prototype.eventPostFind = function(docs, options) {
  docs = _.isArray(docs) ? docs : [docs];
  options = options || {};
  var post = {
    info: {
      prevPage: docs && options.skip > 0,
      nextPage: docs && (docs.length == options.limit)
    },
    docs: docs
  };
  if (post.docs) {
    if (post.info.nextPage) { // Discard next-page hint doc.
      post.docs.pop();
    }
    post.docs = _.map(post.docs, function(doc) {
      doc.time = doc.time.getTime();
      return doc;
    });
  }
  return post;
};

/**
 * Extract find()-compatible options from an object containing app-specific
 * keys, e.g. 'sort-dir' and 'sort-attr' which are converted into {sort: {...}}.
 *
 * @param params {Object}
 * @return {Object}
 */
MongoDb.prototype.extractSortOptions = function(params) {
  var options = {};
  if (params['sort-attr']) {
    if ('desc' == params['sort-dir']) {
      options.sort = [[params['sort-attr'], 'desc']];
      delete params['sort-dir'];
    } else if ('asc' == params['sort-dir']) {
      options.sort = [[params['sort-attr'], 'asc']];
      delete params['sort-dir'];
    }
    delete params['sort-attr'];

    if ('time' == options.sort[0][0]) {
      options.sort.push(['_id', options.sort[0][1]]);
    }
  }
  if (params.limit) {
    options.limit = Math.min(parseInt(params.limit, 10), config.maxResultSize);
    delete params.limit;
  } else {
    options.limit = config.maxResultSize;
  }
  if (params.skip) {
    options.skip = parseInt(params.skip, 10);
    delete params.skip;
  }
  options.limit += 1; // For next-page detection.
  return options;
};

/**
 * Extract find()-compatible filters from an object containing app-specific
 * keys, e.g. 'time-gte' which is converted into {time: {$gte: ...}}.
 *
 * @param params {Object} Modified in-place with extracted options.
 */
MongoDb.prototype.extractFilterOptions = function(params) {
  _.each(params, function(value, key) {
    var matches = null;
    if ((matches = key.match(/^(.*)-(gte|gt|lte|lt|ne)$/))) {
      if ('time' == matches[1]) {
        value = new Date(value);
      }
      if (!params[matches[1]]) {
        params[matches[1]] = {};
      }
      params[matches[1]]['$' + matches[2]] = value;
      delete params[key];
    } else if ('_id' == key && _.isString(value)) {
      params[key] = new BSON.ObjectID(value);
    }
  });
};

/**
 * Connect to the DB based on config.js. Reuse a link if available.
 *
 * @param error {Function} Fired after error.
 * @param success {Function} Fired after success.
 */
MongoDb.prototype.dbConnectAndOpen = function(error, success) {
  if (this.link) {
    success(null, this.link);
  } else {
    this.eventCollection = config.eventCollection;
    this.link = new mongodb.Db(
      config.db,
      new mongodb.Server(config.host, config.port, {})
    );
    this.link.open(function(err, db) {
      if (err) {
        error('Could not access database.', null);
      } else {
        success(err, db);
      }
    });
  }
};

/**
 * Open a DB collection.
 *
 * @param db {Object} Connection link.
 * @param collection {String} Collection name.
 * @param error {Function} Fired on error.
 * @param success {Function} Fired on success.
 */
MongoDb.prototype.dbCollection = function(db, collection, error, success) {
  var mongo = this;
  db.collection(collection, function(err, collection) {
    if (err) {
      mongo.dbClose('Could not access collection.', error);
    } else {
      success(err, collection);
    }
  });
};

/**
 * Disconnect from the DB.
 *
 * @param err {String} Optional error value passed to 'callback'.
 * @param callback {Function} Optional function called after link is closed.
 * - Receives two arguments, 'err' and null.
 */
MongoDb.prototype.dbClose = function(err, callback) {
  if (this.link) {
    this.link.close();
    this.link = null;
  }
  if (callback) {
    callback(err, null);
  }
};

/**
 * Insert a single parsed log line.
 *
 * @param logs {Array|Object} Output from a parser module's parse() function.
 * @param callback {Function} Receives insert() results.
 * @param bulk {Boolean} (Optional, Default: false) If true, auto-close connection.
 */
MongoDb.prototype.insertLog = function(logs, callback, bulk) {
  var mongo = this;
  this.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, mongo.eventCollection, callback, function(err, collection) {
      var docs = [];
      logs = _.isArray(logs) ? logs : [logs];
      _.each(logs, function(log) {
        log.time = new Date(log.time);
        docs.push(log);
      });
      collection.insert(docs, {safe: true}, function(err, docs) {
        if (!bulk) {
          mongo.dbClose();
        }
        callback(err, docs);
      });
    });
  });
};

/**
 * Retrieve a single log's attributes.
 *
 * @param id {String} Document ID.
 * @param callback {Function} Receives findOne() results.
 */
MongoDb.prototype.getLog = function(id, callback) {
  var mongo = this;
  this.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, mongo.eventCollection, callback, function(err, collection) {
      collection.findOne({_id: new BSON.ObjectID(id)}, function(err, doc) {
        if (err) { mongo.dbClose(err, callback); return; }
        mongo.dbClose();
        if (doc) {
          var post = mongo.eventPostFind(doc);
          callback(err, post.docs[0]);
        } else {
          callback(err, null);
        }
      });
    });
  });
};

/**
 * Retrieve the attributes of all matching logs.
 *
 * @param params {Object}
 * - sort-dir {String} 'desc' or 'asc'
 * - sort-attr {String} Ex. 'time'
 * - skip {Number} Amount of documents skipped.
 * - limit {Number} Maximum amount of documents retrieved.
 * - All other key/value pairs are considered conditions.
 * @param callback {Function} Receives find() results.
 * - {String} MongoDB driver error message.
 * - {Array} Result set.
 * - {Object} Additional result details.
 *   'nextPage' {Boolean} True if additional documents exist.
 */
MongoDb.prototype.getTimeline = function(params, callback) {
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, mongo.eventCollection, callback, function(err, collection) {
      var options = mongo.extractSortOptions(params);
      mongo.extractFilterOptions(params);
      collection.find(params, options).toArray(function(err, docs) {
        if (err) { mongo.dbClose(err, callback); return; }
        mongo.dbClose();
        var post = mongo.eventPostFind(docs, options);
        callback(err, post.docs, post.info);
      });
    });
  });
};

/**
 * Retrieve the attributes of all logs newer than a given ID/time.
 *
 * @param id {String}
 * @param time {Number} Timestamp in milliseconds.
 * @param params {Object} getTimeline() compatible parameters.
 * @param callback {Function} Receives find() results.
 */
MongoDb.prototype.getTimelineUpdates = function(id, time, params, callback) {
  params._id = {$gt: new BSON.ObjectID(id)};
  params['time-gte'] = time;
  params['sort-attr'] = 'time';
  params['sort-dir'] = 'desc';
  this.getTimeline(params, callback);
};

/**
 * Retrieve the attributes of all logs newer than a given ID.
 *
 * @param job {Object}
 * - name {String} Name of collection to receive results.
 *   - Optionally supply __filename of a job script and the basename will be used.
 * - map {Function}
 * - reduce {Function}
 * - options {Object} (Optional) Collection.mapReduce() options.
 *   out {Object} (Default: {replace: <name>}) Output directive.
 * - return {String} (Optional, Default: none) Callback receives 'cursor' or 'array'.
 * - callback {Function} Fires after success/error.
 *   - If 'return' not set:
 *     err {String}
 *     stats {Object}
 *   - If 'return' set:
 *     err {String}
 *     return {Object|Array}
 *     stats {Object}
 */
MongoDb.prototype.mapReduce = function(job) {
  job.name = diana.extractJobName(job.name);
  var collectionName = job.suffix ? job.name + '_' + job.suffix : job.name;
  var returnAsArray = job.return != 'cursor';

  var mongo = this;
  mongo.dbConnectAndOpen(job.callback, function(err, db) {
    job.options = job.options || {};
    job.options.out = job.options.out || {replace: collectionName};
    mongo.dbCollection(db, mongo.eventCollection, job.callback, function(err, collection) {
      collection.mapReduce(job.map, job.reduce, job.options, function(err, stats) {
        if (err) { mongo.dbClose(); job.callback(err); return; }
        if (job.return) {
          mongo.getMapReduceResults(collectionName, function(err, results) {
            mongo.dbClose();
            job.callback(err, results, stats);
          }, returnAsArray);
        } else {
          mongo.dbClose();
          job.callback(err, stats);
        }
      });
    });
  });
};

/**
 * Time-ranged wrapped for mapReduce().
 *
 * @param startTime {Number} UNIX timestamp in milliseconds.
 * @param endTime {Number} UNIX timestamp in milliseconds.
 * @param job {Object} MongoDb.mapReduce() 'job' argument.
 */
MongoDb.prototype.mapReduceTimeRange = function(startTime, endTime, job) {
  job.options = job.options || {};
  job.options.query = job.options.query || {};
  job.options.query.time = {
    $gte: new Date(parseInt(startTime, 10)),
    $lte: new Date(parseInt(endTime, 10))
  };
  this.mapReduce(job);
};

/**
 * Return all map reduce results from collection.
 *
 * @param name {String}
 * @param callback {Function} Fires after success/error.
 * - asArray = true: Receives an object with _id values as keys and remaining
 *   pairs as values.
 * - asArray = false: Receives a Cursor object.
 * @param asArray {Boolean} (Optional, Default: true)
 */
MongoDb.prototype.getMapReduceResults = function(name, callback, asArray) {
  asArray = _.isUndefined(asArray) ? true : asArray;
  name = diana.extractJobName(name);
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, name, callback, function(err, collection) {
      if (asArray) {
        collection.find().toArray(function(err, docs) {
          mongo.dbClose();
          var results = {};
          _.each(docs, function(doc) {
            results[doc._id] = doc.value;
          })
          callback(err, results);
        });
      } else {
        collection.find(callback);
      }
    });
  });
};

/**
 * Check if a collection exists.
 *
 * @param name {String}
 * @param callback {Function} Fires after success/error.
 * - {Boolean}
 */
MongoDb.prototype.collectionExists = function(name, callback) {
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    db.collections(function(err, results) {
      var names = {};
      _.each(results, function(collection) {
        names[collection.collectionName] = true;
      });
      callback(err, _.has(names, name));
    });
  });
};

/**
 * Retrieve a find() cursor.
 *
 * @param name {String}
 * @param params {Object} find() filters.
 * @param options {Object} find() options.
 * @param callback {Function} Receives a Cursor object.
 */
MongoDb.prototype.getCollectionCursor = function(name, params, options, callback) {
  var mongo = this;
  params = params || {};
  options = options || {};
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, name, callback, function(err, collection) {
      callback(collection.find(params, options));
    });
  });
};

/**
 * Drop a collection.
 *
 * @param name {String}
 * @param callback {Function} Fires on drop completion.
 */
MongoDb.prototype.dropCollection = function(name, callback) {
  var mongo = this;
  mongo.dbConnectAndOpen(callback, function(err, db) {
    mongo.dbCollection(db, name, callback, function(err, collection) {
      collection.drop(function() {
        mongo.dbClose();
        callback();
      });
    });
  });
};
<|endoftext|>