<commit_before>'use strict';

/*
 * Dependencies
 */
var Promise  = require('bluebird').Promise;
var async    = require('async');
var zoologist = require('../zoologist');

var CHECK_INTERVAL_MS = 5000;
var DEFAULT_BASE_PATH = 'services';

/**
 *  VitalSigns Health Monitor for a Microservice.
 *
 * @public
 */
 module.exports = {
   name: 'microservice',
   report: function() {
     return svcHealth;
   },
   destroy: function() {
     clearInterval(interval);
   }
 };

var svcHealth = null;

/**
 * Peridically check the health of the microservice.
 */
var interval = setInterval(function() {
  var check = checkMicroserviceHealth()

  if (check) {
    check.then(function (health) {
      svcHealth = health;
    });
  }
}, CHECK_INTERVAL_MS);

/**
 * Invoke health check.
 */
function checkMicroserviceHealth() {
  var resolver = Promise.pending();

  if (!zoologist.getServiceDiscovery() || !zoologist.getServiceDiscovery().getData()) {
    resolver.resolve({
      status: (zoologist.getClient().getClient().getState().name === 'SYNC_CONNECTED') ? 'UP' : 'DOWN'
    });

    return resolver.promise;
  }

  var health = {
    id: zoologist.getServiceDiscovery().getData().id,
    status: (zoologist.getClient().getClient().getState().name === 'SYNC_CONNECTED') ? 'UP' : 'DOWN',
    name: zoologist.getServiceInstance().getData().name,
    basePath: zoologist.getServiceInstance().getData().basePath || DEFAULT_BASE_PATH,
    address: zoologist.getServiceInstance().getData().address,
    port: zoologist.getServiceInstance().getData().port,
    registrationTime: new Date(zoologist.getServiceInstance().getData().registrationTimeUTC)
  };

  if (health.status === 'UP' && zoologist.getServiceDependencies() && zoologist.getServiceDependencies().length > 0) {
    checkDependencies(zoologist.getServiceDependencies(), function(err, dependencies) {
      health.dependencies = dependencies;

      // If any dependencies have zero count, set service 'DOWN'
      Object.keys(dependencies).forEach(function(dependency) {
        if (dependencies[dependency] == 0) health.status = 'DOWN';
      });

      resolver.resolve(health);
    });
  } else {
    resolver.resolve(health);
  }

  return resolver.promise;
}

/**
 * Check the the status of any dependencies of this service.
 */
function checkDependencies(dependencies, callback) {
  var checkedDependencies = {};

  async.each(dependencies, function (dependency, callback) {
    zoologist.getServiceDiscovery().queryForInstances(dependency, function(err, instances) {
      checkedDependencies[dependency] = ((instances) ? instances.length : 0);
      callback(err);
    });
  }, function(err) {
    // Ignore error and just return zero'd counts
    callback(null, checkedDependencies);
  });
};
<commit_msg>Update microservice-monitor.js<commit_after>'use strict';

/*
 * Dependencies
 */
var Promise  = require('bluebird').Promise;
var async    = require('async');
var zoologist = require('../zoologist');

var CHECK_INTERVAL_MS = 5000;
var DEFAULT_BASE_PATH = 'services';

/**
 *  VitalSigns Health Monitor for a Microservice.
 *
 * @public
 */
 module.exports = {
   name: 'microservice',
   report: function() {
     return svcHealth;
   },
   destroy: function() {
     clearInterval(interval);
   }
 };

var svcHealth = null;

/**
 * Periodically check the health of the microservice.
 */
var interval = setInterval(function() {
  var check = checkMicroserviceHealth()

  if (check) {
    check.then(function (health) {
      svcHealth = health;
    });
  }
}, CHECK_INTERVAL_MS);

/**
 * Invoke health check.
 */
function checkMicroserviceHealth() {
  var resolver = Promise.pending();

  if (!zoologist.getServiceDiscovery() || !zoologist.getServiceDiscovery().getData()) {
    resolver.resolve({
      status: (zoologist.getClient().getClient().getState().name === 'SYNC_CONNECTED') ? 'UP' : 'DOWN'
    });

    return resolver.promise;
  }

  var health = {
    id: zoologist.getServiceDiscovery().getData().id,
    status: (zoologist.getClient().getClient().getState().name === 'SYNC_CONNECTED') ? 'UP' : 'DOWN',
    name: zoologist.getServiceInstance().getData().name,
    basePath: zoologist.getServiceInstance().getData().basePath || DEFAULT_BASE_PATH,
    address: zoologist.getServiceInstance().getData().address,
    port: zoologist.getServiceInstance().getData().port,
    registrationTime: new Date(zoologist.getServiceInstance().getData().registrationTimeUTC)
  };

  if (health.status === 'UP' && zoologist.getServiceDependencies() && zoologist.getServiceDependencies().length > 0) {
    checkDependencies(zoologist.getServiceDependencies(), function(err, dependencies) {
      health.dependencies = dependencies;

      // If any dependencies have zero count, set service 'DOWN'
      Object.keys(dependencies).forEach(function(dependency) {
        if (dependencies[dependency] == 0) health.status = 'DOWN';
      });

      resolver.resolve(health);
    });
  } else {
    resolver.resolve(health);
  }

  return resolver.promise;
}

/**
 * Check the status of any dependencies of this service.
 */
function checkDependencies(dependencies, callback) {
  var checkedDependencies = {};

  async.each(dependencies, function (dependency, callback) {
    zoologist.getServiceDiscovery().queryForInstances(dependency, function(err, instances) {
      checkedDependencies[dependency] = ((instances) ? instances.length : 0);
      callback(err);
    });
  }, function(err) {
    // Ignore error and just return zero'd counts
    callback(null, checkedDependencies);
  });
};
<|endoftext|>


<commit_before>
utils = require("./../utils.js");

function Channel(channel_name)  {
    /**
     * @class Channel
     * @private
     * Implements a channel, take care of the queue of messages
     */
    this.maxsize = 10;
    this.channel_name = channel_name;
    this.content = new Array();
    this.len = function(){
        return this.content.length;
    };

    this.pushMessage = function(content,count){
        // Remove trailing, old elements (store only the latest messages)
        // count is maintained by the Store object, and allows to identify message with a unique, increasing id.
        while (this.len() >= this.maxsize){
            this.content.shift();
        }
        this.content.push({
            message:content // The backplane message
            , channel_name:this.channel_name
            , id:count}); // Unique ID for the bus, for reference.
    };

    // update the maxsize for this channel and remove potential extra-messages
    this.new_maxsize =  function(size){
        this.maxsize = size;
        while (this.len() > this.maxsize){
            this.content.shift();
        }
    };

    this.getChannelMessages = function(options){
        if(options&&options.since){
            // Return the content of the whole channel until the "since" message
            var res = new Array(); // output
            for (index in this.content){
                var message = this.content[index]
                if (message.id > options.since) {
                    res.push(message)
                }
            }
            return res
        } else {
            // Return the entire channel
            return this.content
        }
    }
}

function Store()  {
    /**
     * @class Store
     * @private
     * Implements the Store. Deals with Bus and Channel construction/checking
     * Retrieve information from Bus/Store
     */
    this.maxsize = 10; // default number of messages stored for all NEW channel
    this.content = {};
    this.count = 0;

    // Add a new bus to the store
    this.addBus = function(bus) {
        if (this.content[bus]) {
            throw {name : "Adding existing bus exception", message : "Attempt to create a new bus, but it exists already (" + bus + ")"}
        } else {
            this.content[bus] = {};
        }
    };

    // Add a new channel to an existing bus
    this.addChannel = function(bus,channel){
        if (!this.content[bus]){
            throw {name : "Bus not valid exception", message : "Attempt to create a new channel on a non-existing bus ("+bus+")"}
        } else {
            if (!this.content[bus][channel]){
                this.content[bus][channel] = new Channel(channel);
            }
            else {
                throw {name : "Adding existing channel exception", message : "Attempt to create a new bus, but it exists already ("+channel+")"}
            }
        }
    };

    this.getChannelMessages = function(bus,channel,options){
        return this.content[bus][channel].getChannelMessages(options)
    }

    this.getBusMessages = function(bus,options){
        if (options && options.since){
            var res = new Array();
            for (var channel in this.content[bus]){
                for (var index in this.content[bus][channel].content){
                    var message = this.content[bus][channel].content[index];
                    if (message.id > options.since) {
                        res.push(message)
                    }
                }
                return res
            }
        } else {
            var res = new Array();
            for (var channel in this.content[bus]){
                for (var index in this.content[bus][channel].content){
                    var message = this.content[bus][channel].content[index];
                    res.push(message);
                }
            }
            return res;
        }

    }

    // Append a message to a given channel. Requires valid/existing bus/channel
    this.pushMessage = function(bus,channel,content){
        this.content[bus][channel].pushMessage(content,this.count);
        this.count += 1;
    };

    this.isValidChannel = function(bus,channel){
        return !!(this.content[bus]) && !!(this.content[bus][channel])
    };
    this.isValidBus = function(bus){
        return !!(this.content[bus]);
    };

    this.delChannel = function(bus,channel){
        if (!this.isValidChannel(bus,channel)){
            throw {name:"Invalid channel exception", message: "Attempt to delete a invalid channel"}
        } else {
            delete this.content[bus][channel]
        }
    };

    this.delBus = function(bus){
        if (!this.isValidBus(bus)){
            throw {name:"Invalid bus exception", message: "Attempt to delete a invalid bus"}
        } else {
            delete this.content[bus]
        }
    };

    this.lsBus = function(){
        // List all the bus stored
        res = new Array();
        for (i in this.content){
            res.push(i);
        }
        return res
    };
    this.lsChannel = function(bus){
        // list all channel for a bus
        if (this.isValid(bus)){
            res = new Array();
            for (i in this.content[bus]){
                res.push(i)
            }
            return res
        } else {
            throw {name: "Invalid bus exception", message: "Attempt to access an invalid bus"}
        }
    }
}

module.exports = (function(){
    /**
     * The memoryMessageStore class deals with:
     * - storing the message in memory
     * - validating messages
     * - bus and channel management (creation, access)
     * - providing messages (for bus and/or channel)
     * - cleaning outdated messages
     * @class memoryMessageStore
     * @require utils.js
     */

   var my = {};

   my.store = new Store;

   my.save = function(bus,channel,content){
       /**
        * @method save
        * @param {bus,channel,content}
        * @return null
        * bus : must be a valid, existing bus
        * channel : if it does not exist, it will be created before pushing the message (MUST be base64 encoded)
        * content : a valid backplane message
        */
       if (!my.validate(content)){
         throw {name: "Content not valid exception",message:"Attempt to push an invalid message to the channel "+bus+"/"+channel}
       } else {
           if (!my.store.isValidBus(bus)){
               throw {name: "Invalid bus exception", message: "Can't push to non-existing bus"}
           } else if (my.store.isValidChannel(bus,channel)){
                my.store.pushMessage(bus,channel,content)
               } else {
                   my.store.addChannel(bus,channel);
                   my.store.pushMessage(bus,channel,content)
               }
       }
   };

   my.getChannelMessages = function(bus,channel,options,callback){
       /**
        * @method getChannelMessages
        * @param {bus,channel,options,callback}
        * Get messages from a given bus/channel
        * options : to specify a "since" parameter (ONLY supported parameter for now)
        * callback : function(message,error). message: retrieved message from channel, error: potential errors.
        */
       if(!my.store.isValidChannel(bus,channel)){
           callback(null,{ name: "Invalid bus/channel exception", message: "Attempt to read an invalid bus/channel"});
       } else {
           var messages = my.store.getChannelMessages(bus,channel,options);
           callback(messages,null)
       }
   };

   my.getBusMessages = function(bus,options,callback){
       /**
        * @method getBusMessages
        * @param {bus,options,callback}
        * Get messages from all the channel of a given bus
        * options : to specify a "since" parameter (ONLY supported parameter for now)
        * callback : function(message,error). message: retrieved message from channel, error: potential errors.
        */
       if(!my.store.isValidBus(bus)){
           callback(null,{ name: "Invalid bus exception", message: "Attempt to read an invalid bus"});
       } else {
           var messages = my.store.getBusMessages(bus,options);
           callback(messages,null);
       }
   };

   my.validate = function(message){
       /**
        * @method validate
        * @param {message}
        * check that message is a proper Backplane message with source, type and payload
        */
       if(!message.source || !message.payload || !message.type){
           throw {name: "Invalid Message Exception",message:"Specify source, payload and type"}
       } else {
          if  (utils.isValidURL(message.source)) {
              return true
          } else { throw {name: "Invalid Message Exception",message:"source must be a valid URL"} }
       }
   };

   my.delChannel = function(bus,channel){
    /**
     * @method delChannel
     * @param {bus, channel}
     * delete the channel
     * the channel MUST exist
     */
     my.store.delChannel(bus,channel)
   };

   my.delBus = function(bus){
   /**
    * @method delBus
    * @param {bus}
    * delete the bus
    * the bus MUST exist
    */
       my.store.delBus(bus)
    };

   return my;
})();<commit_msg>Fix documentation<commit_after>
utils = require("./../utils.js");

function Channel(channel_name)  {
    /**
     * @class Channel
     * @private
     * Implements a channel, take care of the queue of messages
     */
    this.maxsize = 10;
    this.channel_name = channel_name;
    this.content = new Array();
    this.len = function(){
        return this.content.length;
    };

    this.pushMessage = function(content,count){
        // Remove trailing, old elements (store only the latest messages)
        // count is maintained by the Store object, and allows to identify message with a unique, increasing id.
        while (this.len() >= this.maxsize){
            this.content.shift();
        }
        this.content.push({
            message:content // The backplane message
            , channel_name:this.channel_name
            , id:count}); // Unique ID for the bus, for reference.
    };

    // update the maxsize for this channel and remove potential extra-messages
    this.new_maxsize =  function(size){
        this.maxsize = size;
        while (this.len() > this.maxsize){
            this.content.shift();
        }
    };

    this.getChannelMessages = function(options){
        if(options&&options.since){
            // Return the content of the whole channel until the "since" message
            var res = new Array(); // output
            for (index in this.content){
                var message = this.content[index]
                if (message.id > options.since) {
                    res.push(message)
                }
            }
            return res
        } else {
            // Return the entire channel
            return this.content
        }
    }
}

function Store()  {
    /**
     * @class Store
     * @private
     * Implements the Store. Deals with Bus and Channel construction/checking
     * Retrieve information from Bus/Store
     */
    this.maxsize = 10; // default number of messages stored for all NEW channel
    this.content = {};
    this.count = 0;

    // Add a new bus to the store
    this.addBus = function(bus) {
        if (this.content[bus]) {
            throw {name : "Adding existing bus exception", message : "Attempt to create a new bus, but it exists already (" + bus + ")"}
        } else {
            this.content[bus] = {};
        }
    };

    // Add a new channel to an existing bus
    this.addChannel = function(bus,channel){
        if (!this.content[bus]){
            throw {name : "Bus not valid exception", message : "Attempt to create a new channel on a non-existing bus ("+bus+")"}
        } else {
            if (!this.content[bus][channel]){
                this.content[bus][channel] = new Channel(channel);
            }
            else {
                throw {name : "Adding existing channel exception", message : "Attempt to create a new bus, but it exists already ("+channel+")"}
            }
        }
    };

    this.getChannelMessages = function(bus,channel,options){
        return this.content[bus][channel].getChannelMessages(options)
    }

    this.getBusMessages = function(bus,options){
        if (options && options.since){
            var res = new Array();
            for (var channel in this.content[bus]){
                for (var index in this.content[bus][channel].content){
                    var message = this.content[bus][channel].content[index];
                    if (message.id > options.since) {
                        res.push(message)
                    }
                }
                return res
            }
        } else {
            var res = new Array();
            for (var channel in this.content[bus]){
                for (var index in this.content[bus][channel].content){
                    var message = this.content[bus][channel].content[index];
                    res.push(message);
                }
            }
            return res;
        }

    }

    // Append a message to a given channel. Requires valid/existing bus/channel
    this.pushMessage = function(bus,channel,content){
        this.content[bus][channel].pushMessage(content,this.count);
        this.count += 1;
    };

    this.isValidChannel = function(bus,channel){
        return !!(this.content[bus]) && !!(this.content[bus][channel])
    };
    this.isValidBus = function(bus){
        return !!(this.content[bus]);
    };

    this.delChannel = function(bus,channel){
        if (!this.isValidChannel(bus,channel)){
            throw {name:"Invalid channel exception", message: "Attempt to delete a invalid channel"}
        } else {
            delete this.content[bus][channel]
        }
    };

    this.delBus = function(bus){
        if (!this.isValidBus(bus)){
            throw {name:"Invalid bus exception", message: "Attempt to delete a invalid bus"}
        } else {
            delete this.content[bus]
        }
    };

    this.lsBus = function(){
        // List all the bus stored
        res = new Array();
        for (i in this.content){
            res.push(i);
        }
        return res
    };
    this.lsChannel = function(bus){
        // list all channel for a bus
        if (this.isValid(bus)){
            res = new Array();
            for (i in this.content[bus]){
                res.push(i)
            }
            return res
        } else {
            throw {name: "Invalid bus exception", message: "Attempt to access an invalid bus"}
        }
    }
}

module.exports = (function(){
    /**
     * The memoryMessageStore class deals with:
     * - storing the message in memory
     * - validating messages
     * - bus and channel management (creation, access)
     * - providing messages (for bus and/or channel)
     * - cleaning outdated messages
     * @class memoryMessageStore
     * @require utils.js
     */

   var my = {};

   my.store = new Store;

   my.save = function(bus,channel,content){
       /**
        * @method save
        * @param {bus}: bus where the channel you want to post to lies
        * @param {channel}: channel where to post the message
        * @param {content}: message to post
        * @return null
        * bus : must be a valid, existing bus
        * channel : if it does not exist, it will be created before pushing the message (MUST be base64 encoded)
        * content : a valid backplane message
        */
       if (!my.validate(content)){
         throw {name: "Content not valid exception",message:"Attempt to push an invalid message to the channel "+bus+"/"+channel}
       } else {
           if (!my.store.isValidBus(bus)){
               throw {name: "Invalid bus exception", message: "Can't push to non-existing bus"}
           } else if (my.store.isValidChannel(bus,channel)){
                my.store.pushMessage(bus,channel,content)
               } else {
                   my.store.addChannel(bus,channel);
                   my.store.pushMessage(bus,channel,content)
               }
       }
   };

   my.addBus = function(bus){
       /**
        * @method addBus
        * @param {bus} bus: the bus to be created (string)
        */
       my.store.addBus(bus);
   }

   my.addChannel = function(bus,channel){
       /**
        * @method addChannel
        * @param {bus} bus: bus on top of which to create the channel (string)
        * @param {channel} channel: channel to create (string)
        */
       my.store.addChannel(bus,channel)
   }

   my.getChannelMessages = function(bus,channel,options,callback){
       /**
        * Retrieve messages from a given channel
        * @method getChannelMessages
        * @param {bus}: bus where the channel lies
        * @param {channel}: channel to retrieve the messages from
        * @param {options}: allows to pass a "since" parameter, indicating the id of the last message retrieved before. Method will return new messages published since "since" (unless "since" is too old, in which case it will retrieve all messages in the queue).
        * @param {callback}: callback method. Will be called as callback(message,error)
        */
       if(!my.store.isValidChannel(bus,channel)){
           callback(null,{ name: "Invalid bus/channel exception", message: "Attempt to read an invalid bus/channel"});
       } else {
           var messages = my.store.getChannelMessages(bus,channel,options);
           callback(messages,null)
       }
   };

   my.getBusMessages = function(bus,options,callback){
       /**
        * Retrieve messages from all the channel of a given bus
        * @method getBusMessages
        * @param {bus}: bus from which to retrive messages
        * @param {options}:allows to pass a "since" parameter, indicating the id of the last message retrieved before. Method will return new messages published since "since" (unless "since" is too old, in which case it will retrieve all messages in the queue).
        * @param {callback}: callback method. Will be called as callback(message,error)
        */
       if(!my.store.isValidBus(bus)){
           callback(null,{ name: "Invalid bus exception", message: "Attempt to read an invalid bus"});
       } else {
           var messages = my.store.getBusMessages(bus,options);
           callback(messages,null);
       }
   };

   my.validate = function(message){
       /**
        * @method validate
        * @param {message}: message to be validated
        * check that message is a proper Backplane message with source, type and payload
        */
       if(!message.source || !message.payload || !message.type){
           throw {name: "Invalid Message Exception",message:"Specify source, payload and type"}
       } else {
          if  (utils.isValidURL(message.source)) {
              return true
          } else { throw {name: "Invalid Message Exception",message:"source must be a valid URL"} }
       }
   };

   my.delChannel = function(bus,channel){
    /**
     * @method delChannel
     * @param {bus}: bus where the channel to be deleted lies
     * @param {channel}: channel to remove
     * delete the channel
     * the channel MUST exist
     */
     my.store.delChannel(bus,channel)
   };

   my.delBus = function(bus){
   /**
    * @method delBus
    * @param {bus}: bus to be deleted
    * delete the bus
    * the bus MUST exist
    */
       my.store.delBus(bus)
    };

   return my;
})();<|endoftext|>


<commit_before>import {containerless, bindable} from 'aurelia-framework';
import {Field} from './abstract/field';

/**
 * Optionfield is a {@link Field} that gives certain options to the user as
 * checkboxes or a dropdown depending on the format.
 */
@containerless
export class Optionfield extends Field {
  static TYPE = 'option';
  /**
   * The static choices.
   * @type {Array}
   */
  choices = [];
  /**
   * Sources where to get dynamic choices from.
   * @type {Array}
   */
  dataSources = [];
  /**
   * The type of checkboxes. {@linkplain multi} means multiple checkboxes and
   * output as array with the keys of the selected checkboxes.
   * {@linkplain simple} means a single checkbox and output as boolean that
   * tells whether or not that one checkbox is checked.
   * @type {String}
   */
  checkboxFormat = 'multi';
  /**
   * Whether or not to hide this field if there are no choices.
   * @type {Boolean}
   */
  hideIfNoChoices = true;
  /**
   * The choice that is currently selected. Updated with Aurelia binding.
   */
  @bindable
  selectedChoice = '';

  /**
   * @inheritdoc
   * @param {String[]|Object[]} args.choices     The choices to add.
   * @param {Object[]}          args.dataSources Sources from where to get dynamic choices.
   * @param {Boolean}           args.hideIfNoChoices Whether or not to hide this field if there are no choices.
   */
  init(id = '', args = {}) {
    args = Object.assign({
      choices: [],
      dataSources: [],
      format: 'dropdown',
      checkboxFormat: 'multi',
      hideIfNoChoices: true
    }, args);
    this.hideIfNoChoices = args.hideIfNoChoices;
    this.argChoices = args.choices;
    this.checkboxFormat = args.checkboxFormat;
    this.choices = args.choices;
    this.dataSources = args.dataSources;
    return super.init(id, args);
  }

  /**
   * Check if this Optionfield is empty. An Optionfield is empty either if the
   * value is false or the value array (checkbox-formatted field) is empty.
   */
  isEmpty() {
    const value = this.getValue();
    return !value || (Array.isArray(value) && value.length === 0);
  }

  created() {
    const choices = this.choices;
    this.choices = [];
    for (const choice of choices) {
      if (typeof choice === 'string') {
        // Parse a simple (label-only) choice definition.
        this.choices.push({
          key: choice,
          label: this.i18n(`choices.${choice}`, true),
          selected: false,
          conditionsFulfilled: true
        });
      } else {
        // Parse a full choice definition.
        const choiceParent = this;
        this.choices.push({
          key: choice.key,
          i18nKey: choice.i18nKey,
          label: this.i18n(`choices.${choice.key || choice.i18nKey}`, true),
          selected: false,
          conditions: choice.conditions,
          // A getter function to check whether all the conditions defined in
          // the schema are fulfilled.
          // The HTML templates can't do this complex logic, so we have to do it
          // here.
          get conditionsFulfilled() {
            return Optionfield.conditionsFulfilled(choice.conditions, choiceParent);
          }
        });
      }
    }
    if (this.choices.length === 0 && this.dataSources.length === 0) {
      // We don't want to leave the choices empty, so if there are no choices,
      // make a checkbox with no label.
      this.choices.push({
        key: this.key,
        label: '',
        selected: false,
        conditionsFulfilled: true
      });
      this.checkboxFormat = 'simple';
    }

    const ds = this.dataSources;
    this.dataSources = [];
    for (let dataSource of ds) {
      if (typeof dataSource === 'string') {
        dataSource = {
          source: dataSource,
          key: '\${#}'
        };
      }
      const target = this.resolveRef(dataSource.source);
      dataSource.updateFunc = () => {
        dataSource.choices = [];
        for (const child of target.iterableChildren) {
          const data = {
            key: child.formatReferencePlusField(dataSource.key),
            selected: false,
            get conditionsFulfilled() {
              return Optionfield.conditionsFulfilled(dataSource.localConditions, target)
                  && Optionfield.conditionsFulfilled(dataSource.targetConditions, target);
            }
          };
          if (dataSource.label) {
            data.label = child.formatReferencePlusField(dataSource.label);
          } else {
            data.label = data.key;
          }
          dataSource.choices.push(data);
        }
      };
      target.addChangeListener(dataSource.updateFunc);
      dataSource.updateFunc();
      this.dataSources.push(dataSource);
    }
  }

  shouldDisplay() {
    if (!this.hideIfNoChoices) {
      return super.shouldDisplay();
    }
    for (const choice of this.allChoices) {
      if (choice.conditionsFulfilled) {
        return super.shouldDisplay();
      }
    }
    return false;
  }

  static conditionsFulfilled(conditions, parentField) {
    if (conditions) {
      for (const [fieldPath, expectedValue] of Object.entries(conditions)) {
        const field = parentField.resolveRef(fieldPath);
        const value = field ? field.getValue() : undefined;
        if (Array.isArray(value) && !value.includes(expectedValue)) {
          return false;
        } else if (value !== expectedValue) {
          return false;
        }
      }
    }
    return true;
  }

  get allChoices() {
    if (this.dataSources.length === 0) {
      return this.choices;
    }

    let choices = this.choices;
    for (const dataSource of this.dataSources) {
      if (dataSource.choices) {
        choices = choices.concat(dataSource.choices);
      }
    }
    return choices;
  }

  /**
   * If this field is a dropdown, get the name of the selected choice.
   * If this field is a simple checkbox, get whether or not the checkbox is checked.
   * If this field is a multi-checkbox field, get the names of the checked checkboxes.
   */
  getValue() {
    if (this.format === 'dropdown') {
      // Optionfield is a dropdown, so there's only one selected field.
      // Return the name of the selected field.
      return this.selectedChoice;
    } else if (this.format === 'checkbox') {
      // If this a simple checkbox (only one checkbox), just return whether or
      // not it is selected.
      if (this.checkboxFormat === 'simple') {
        return this.choices[0].selected;
      }
      const chosen = [];
      // Loop through the choices and add their names to the array that is returned.
      for (const choice of this.choices) {
        if (choice.selected) {
          chosen.push(choice.key);
        }
      }
      return chosen;
    }
  }

  /**
   * Get the label(s) of the currently chosen option(s)
   * @return {String|String[]} The label or labels depending on the format of
   *                           this Optionfield.
   */
  getChoiceLabel() {
    if (this.format === 'dropdown') {
      // Optionfield is a dropdown, so there's only one selected field.
      // Loop through the choices to find it and return its label.
      for (const choice of this.choices) {
        if (choice.selected) {
          return choice.label;
        }
      }
      return undefined;
    } else if (this.format === 'checkbox') {
      // If this a simple checkbox (only one checkbox), just return the label
      // of that.
      if (this.checkboxFormat === 'simple') {
        return this.choices[0].label;
      }
      // Loop through the choices and add the labels of the selected choices to
      // the array that is returned.
      const chosen = [];
      for (const choice of this.choices) {
        if (choice.selected) {
          chosen.push(choice.label);
        }
      }
      return chosen;
    }
  }

  /**
   * Set the chosen fields of this Optionfield and unselect all other fields.
   * @param {String|String[]} value The new choice or choices depending on the
   *                                format of this field.
   */
  setValue(value) {
    if (Array.isArray(value) && this.format === 'checkbox') {
      for (const choice of this.choices) {
        if (value.includes(choice.key)) {
          choice.selected = true;
        } else if (choice.selected) {
          choice.selected = false;
        }
      }
    } else {
      for (const choice of this.choices) {
        if (choice.key === value) {
          choice.selected = true;
        } else if (choice.selected) {
          choice.selected = false;
        }
      }

      this.selectedChoice = value;
    }
  }

  /**
   * Called by Aurelia when it sets the current selection to selectedChoice.
   */
  selectedChoiceChanged() {
    this.setValue(this.selectedChoice);
  }

  /**
   * @return {String} The name of the HTML file that displays the choices in the
   *                  format specified by {@link #format}.
   * @private
   */
  getViewStrategy() {
    return `resources/elements/optionfield-${this.format}.html`;
  }
}
<commit_msg>Fix simple checkbox labels<commit_after>import {containerless, bindable} from 'aurelia-framework';
import {Field} from './abstract/field';

/**
 * Optionfield is a {@link Field} that gives certain options to the user as
 * checkboxes or a dropdown depending on the format.
 */
@containerless
export class Optionfield extends Field {
  static TYPE = 'option';
  /**
   * The static choices.
   * @type {Array}
   */
  choices = [];
  /**
   * Sources where to get dynamic choices from.
   * @type {Array}
   */
  dataSources = [];
  /**
   * The type of checkboxes. {@linkplain multi} means multiple checkboxes and
   * output as array with the keys of the selected checkboxes.
   * {@linkplain simple} means a single checkbox and output as boolean that
   * tells whether or not that one checkbox is checked.
   * @type {String}
   */
  checkboxFormat = 'multi';
  /**
   * Whether or not to hide this field if there are no choices.
   * @type {Boolean}
   */
  hideIfNoChoices = true;
  /**
   * The choice that is currently selected. Updated with Aurelia binding.
   */
  @bindable
  selectedChoice = '';

  /**
   * @inheritdoc
   * @param {String[]|Object[]} args.choices     The choices to add.
   * @param {Object[]}          args.dataSources Sources from where to get dynamic choices.
   * @param {Boolean}           args.hideIfNoChoices Whether or not to hide this field if there are no choices.
   */
  init(id = '', args = {}) {
    args = Object.assign({
      choices: [],
      dataSources: [],
      format: 'dropdown',
      checkboxFormat: 'multi',
      hideIfNoChoices: true
    }, args);
    this.hideIfNoChoices = args.hideIfNoChoices;
    this.argChoices = args.choices;
    this.checkboxFormat = args.checkboxFormat;
    this.choices = args.choices;
    this.dataSources = args.dataSources;
    return super.init(id, args);
  }

  /**
   * Check if this Optionfield is empty. An Optionfield is empty either if the
   * value is false or the value array (checkbox-formatted field) is empty.
   */
  isEmpty() {
    const value = this.getValue();
    return !value || (Array.isArray(value) && value.length === 0);
  }

  created() {
    const choices = this.choices;
    this.choices = [];
    for (const choice of choices) {
      if (typeof choice === 'string') {
        // Parse a simple (label-only) choice definition.
        this.choices.push({
          key: choice,
          label: this.i18n(`choices.${choice}`, true),
          selected: false,
          conditionsFulfilled: true
        });
      } else {
        // Parse a full choice definition.
        const choiceParent = this;
        this.choices.push({
          key: choice.key,
          i18nKey: choice.i18nKey,
          label: this.i18n(`choices.${choice.key || choice.i18nKey}`, true),
          selected: false,
          conditions: choice.conditions,
          // A getter function to check whether all the conditions defined in
          // the schema are fulfilled.
          // The HTML templates can't do this complex logic, so we have to do it
          // here.
          get conditionsFulfilled() {
            return Optionfield.conditionsFulfilled(choice.conditions, choiceParent);
          }
        });
      }
    }
    if (this.choices.length === 0 && this.dataSources.length === 0) {
      // We don't want to leave the choices empty, so if there are no choices,
      // make a checkbox with no label.
      this.choices.push({
        key: this.key,
        i18nKey: 'blank',
        selected: false,
        conditionsFulfilled: true
      });
      this.checkboxFormat = 'simple';
    }

    const ds = this.dataSources;
    this.dataSources = [];
    for (let dataSource of ds) {
      if (typeof dataSource === 'string') {
        dataSource = {
          source: dataSource,
          key: '\${#}'
        };
      }
      const target = this.resolveRef(dataSource.source);
      dataSource.updateFunc = () => {
        dataSource.choices = [];
        for (const child of target.iterableChildren) {
          const data = {
            key: child.formatReferencePlusField(dataSource.key),
            selected: false,
            get conditionsFulfilled() {
              return Optionfield.conditionsFulfilled(dataSource.localConditions, target)
                  && Optionfield.conditionsFulfilled(dataSource.targetConditions, target);
            }
          };
          if (dataSource.label) {
            data.label = child.formatReferencePlusField(dataSource.label);
          } else {
            data.label = data.key;
          }
          dataSource.choices.push(data);
        }
      };
      target.addChangeListener(dataSource.updateFunc);
      dataSource.updateFunc();
      this.dataSources.push(dataSource);
    }
  }

  shouldDisplay() {
    if (!this.hideIfNoChoices) {
      return super.shouldDisplay();
    }
    for (const choice of this.allChoices) {
      if (choice.conditionsFulfilled) {
        return super.shouldDisplay();
      }
    }
    return false;
  }

  static conditionsFulfilled(conditions, parentField) {
    if (conditions) {
      for (const [fieldPath, expectedValue] of Object.entries(conditions)) {
        const field = parentField.resolveRef(fieldPath);
        const value = field ? field.getValue() : undefined;
        if (Array.isArray(value) && !value.includes(expectedValue)) {
          return false;
        } else if (value !== expectedValue) {
          return false;
        }
      }
    }
    return true;
  }

  get allChoices() {
    if (this.dataSources.length === 0) {
      return this.choices;
    }

    let choices = this.choices;
    for (const dataSource of this.dataSources) {
      if (dataSource.choices) {
        choices = choices.concat(dataSource.choices);
      }
    }
    return choices;
  }

  /**
   * If this field is a dropdown, get the name of the selected choice.
   * If this field is a simple checkbox, get whether or not the checkbox is checked.
   * If this field is a multi-checkbox field, get the names of the checked checkboxes.
   */
  getValue() {
    if (this.format === 'dropdown') {
      // Optionfield is a dropdown, so there's only one selected field.
      // Return the name of the selected field.
      return this.selectedChoice;
    } else if (this.format === 'checkbox') {
      // If this a simple checkbox (only one checkbox), just return whether or
      // not it is selected.
      if (this.checkboxFormat === 'simple') {
        return this.choices[0].selected;
      }
      const chosen = [];
      // Loop through the choices and add their names to the array that is returned.
      for (const choice of this.choices) {
        if (choice.selected) {
          chosen.push(choice.key);
        }
      }
      return chosen;
    }
  }

  /**
   * Get the label(s) of the currently chosen option(s)
   * @return {String|String[]} The label or labels depending on the format of
   *                           this Optionfield.
   */
  getChoiceLabel() {
    if (this.format === 'dropdown') {
      // Optionfield is a dropdown, so there's only one selected field.
      // Loop through the choices to find it and return its label.
      for (const choice of this.choices) {
        if (choice.selected) {
          return choice.label;
        }
      }
      return undefined;
    } else if (this.format === 'checkbox') {
      // If this a simple checkbox (only one checkbox), just return the label
      // of that.
      if (this.checkboxFormat === 'simple') {
        return this.choices[0].label;
      }
      // Loop through the choices and add the labels of the selected choices to
      // the array that is returned.
      const chosen = [];
      for (const choice of this.choices) {
        if (choice.selected) {
          chosen.push(choice.label);
        }
      }
      return chosen;
    }
  }

  /**
   * Set the chosen fields of this Optionfield and unselect all other fields.
   * @param {String|String[]} value The new choice or choices depending on the
   *                                format of this field.
   */
  setValue(value) {
    if (Array.isArray(value) && this.format === 'checkbox') {
      for (const choice of this.choices) {
        if (value.includes(choice.key)) {
          choice.selected = true;
        } else if (choice.selected) {
          choice.selected = false;
        }
      }
    } else {
      for (const choice of this.choices) {
        if (choice.key === value) {
          choice.selected = true;
        } else if (choice.selected) {
          choice.selected = false;
        }
      }

      this.selectedChoice = value;
    }
  }

  /**
   * Called by Aurelia when it sets the current selection to selectedChoice.
   */
  selectedChoiceChanged() {
    this.setValue(this.selectedChoice);
  }

  /**
   * @return {String} The name of the HTML file that displays the choices in the
   *                  format specified by {@link #format}.
   * @private
   */
  getViewStrategy() {
    return `resources/elements/optionfield-${this.format}.html`;
  }
}
<|endoftext|>


<commit_before>const common = require('../../../common/api/exports/electron')

// Import common modules.
common.defineProperties(exports)

Object.defineProperties(exports, {
  // Browser side modules, please sort alphabetically alphabet order.
  // Any modules added here must also be added to the browserModules array
  // in remote.js
  app: {
    enumerable: true,
    get: function () {
      return require('../app')
    }
  },
  autoUpdater: {
    enumerable: true,
    get: function () {
      return require('../auto-updater')
    }
  },
  BrowserWindow: {
    enumerable: true,
    get: function () {
      return require('../browser-window')
    }
  },
  contentTracing: {
    enumerable: true,
    get: function () {
      return require('../content-tracing')
    }
  },
  dialog: {
    enumerable: true,
    get: function () {
      return require('../dialog')
    }
  },
  globalShortcut: {
    enumerable: true,
    get: function () {
      return require('../global-shortcut')
    }
  },
  ipcMain: {
    enumerable: true,
    get: function () {
      return require('../ipc-main')
    }
  },
  Menu: {
    enumerable: true,
    get: function () {
      return require('../menu')
    }
  },
  MenuItem: {
    enumerable: true,
    get: function () {
      return require('../menu-item')
    }
  },
  net: {
    enumerable: true,
    get: function () {
      return require('../net')
    }
  },
  powerMonitor: {
    enumerable: true,
    get: function () {
      return require('../power-monitor')
    }
  },
  powerSaveBlocker: {
    enumerable: true,
    get: function () {
      return require('../power-save-blocker')
    }
  },
  protocol: {
    enumerable: true,
    get: function () {
      return require('../protocol')
    }
  },
  screen: {
    enumerable: true,
    get: function () {
      return require('../screen')
    }
  },
  session: {
    enumerable: true,
    get: function () {
      return require('../session')
    }
  },
  systemPreferences: {
    enumerable: true,
    get: function () {
      return require('../system-preferences')
    }
  },
  Tray: {
    enumerable: true,
    get: function () {
      return require('../tray')
    }
  },
  webContents: {
    enumerable: true,
    get: function () {
      return require('../web-contents')
    }
  },

  // The internal modules, invisible unless you know their names.
  NavigationController: {
    get: function () {
      return require('../navigation-controller')
    }
  }
})
<commit_msg>Fix typo in comment<commit_after>const common = require('../../../common/api/exports/electron')

// Import common modules.
common.defineProperties(exports)

Object.defineProperties(exports, {
  // Browser side modules, please sort alphabetically.
  // Any modules added here must also be added to the browserModules array
  // in remote.js
  app: {
    enumerable: true,
    get: function () {
      return require('../app')
    }
  },
  autoUpdater: {
    enumerable: true,
    get: function () {
      return require('../auto-updater')
    }
  },
  BrowserWindow: {
    enumerable: true,
    get: function () {
      return require('../browser-window')
    }
  },
  contentTracing: {
    enumerable: true,
    get: function () {
      return require('../content-tracing')
    }
  },
  dialog: {
    enumerable: true,
    get: function () {
      return require('../dialog')
    }
  },
  globalShortcut: {
    enumerable: true,
    get: function () {
      return require('../global-shortcut')
    }
  },
  ipcMain: {
    enumerable: true,
    get: function () {
      return require('../ipc-main')
    }
  },
  Menu: {
    enumerable: true,
    get: function () {
      return require('../menu')
    }
  },
  MenuItem: {
    enumerable: true,
    get: function () {
      return require('../menu-item')
    }
  },
  net: {
    enumerable: true,
    get: function () {
      return require('../net')
    }
  },
  powerMonitor: {
    enumerable: true,
    get: function () {
      return require('../power-monitor')
    }
  },
  powerSaveBlocker: {
    enumerable: true,
    get: function () {
      return require('../power-save-blocker')
    }
  },
  protocol: {
    enumerable: true,
    get: function () {
      return require('../protocol')
    }
  },
  screen: {
    enumerable: true,
    get: function () {
      return require('../screen')
    }
  },
  session: {
    enumerable: true,
    get: function () {
      return require('../session')
    }
  },
  systemPreferences: {
    enumerable: true,
    get: function () {
      return require('../system-preferences')
    }
  },
  Tray: {
    enumerable: true,
    get: function () {
      return require('../tray')
    }
  },
  webContents: {
    enumerable: true,
    get: function () {
      return require('../web-contents')
    }
  },

  // The internal modules, invisible unless you know their names.
  NavigationController: {
    get: function () {
      return require('../navigation-controller')
    }
  }
})
<|endoftext|>


<commit_before>'use strict'

const {BrowserWindow, ipcMain, webContents} = require('electron')
const {isSameOrigin} = process.atomBinding('v8_util')
const parseFeaturesString = require('../common/parse-features-string')

const hasProp = {}.hasOwnProperty
const frameToGuest = {}

// Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) {
  // Check for circular reference.
  if (visited == null) visited = new Set()
  if (visited.has(parent)) return

  visited.add(parent)
  for (const key in parent) {
    if (!hasProp.call(parent, key)) continue
    if (key in child) continue

    const value = parent[key]
    if (typeof value === 'object') {
      child[key] = mergeOptions({}, value, visited)
    } else {
      child[key] = value
    }
  }
  visited.delete(parent)

  return child
}

// Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) {
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }
  if (embedder.browserWindowOptions != null) {
    // Inherit the original options if it is a BrowserWindow.
    mergeOptions(options, embedder.browserWindowOptions)
  } else {
    // Or only inherit web-preferences if it is a webview.
    mergeOptions(options.webPreferences, embedder.getWebPreferences())
  }

  // Disable node integration on child window if disabled on parent window
  if (embedder.getWebPreferences().nodeIntegration === false) {
    options.webPreferences.nodeIntegration = false
  }

  // Enable context isolation on child window if enabled on parent window
  if (embedder.getWebPreferences().contextIsolation === true) {
    options.webPreferences.contextIsolation = true
  }

  // Disable JavaScript on child window if disabled on parent window
  if (embedder.getWebPreferences().javascript === false) {
    options.webPreferences.javascript = false
  }

  // Sets correct openerId here to give correct options to 'new-window' event handler
  options.webPreferences.openerId = embedder.id

  return options
}

// Setup a new guest with |embedder|
const setupGuest = function (embedder, frameName, guest, options) {
  // When |embedder| is destroyed we should also destroy attached guest, and if
  // guest is closed by user then we should prevent |embedder| from double
  // closing guest.
  const guestId = guest.webContents.id
  const closedByEmbedder = function () {
    guest.removeListener('closed', closedByUser)
    guest.destroy()
  }
  const closedByUser = function () {
    embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
    embedder.removeListener('render-view-deleted', closedByEmbedder)
  }
  if (!options.webPreferences.sandbox) {
    // These events should only be handled when the guest window is opened by a
    // non-sandboxed renderer for two reasons:
    //
    // - `render-view-deleted` is emitted when the popup is closed by the user,
    //   and that will eventually result in NativeWindow::NotifyWindowClosed
    //   using a dangling pointer since `destroy()` would have been called by
    //   `closeByEmbedded`
    // - No need to emit `ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_` since
    //   there's no renderer code listening to it.,
    embedder.once('render-view-deleted', closedByEmbedder)
    guest.once('closed', closedByUser)
  }
  if (frameName) {
    frameToGuest[frameName] = guest
    guest.frameName = frameName
    guest.once('closed', function () {
      delete frameToGuest[frameName]
    })
  }
  return guestId
}

// Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, frameName, options, postData) {
  let guest = frameToGuest[frameName]
  if (frameName && (guest != null)) {
    guest.loadURL(url)
    return guest.webContents.id
  }

  // Remember the embedder window's id.
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }

  guest = new BrowserWindow(options)
  if (!options.webContents || url !== 'about:blank') {
    // We should not call `loadURL` if the window was constructed from an
    // existing webContents(window.open in a sandboxed renderer) and if the url
    // is not 'about:blank'.
    //
    // Navigating to the url when creating the window from an existing
    // webContents would not be necessary(it will navigate there anyway), but
    // apparently there's a bug that allows the child window to be scripted by
    // the opener, even when the child window is from another origin.
    //
    // That's why the second condition(url !== "about:blank") is required: to
    // force `OverrideSiteInstanceForNavigation` to be called and consequently
    // spawn a new renderer if the new window is targeting a different origin.
    //
    // If the URL is "about:blank", then it is very likely that the opener just
    // wants to synchronously script the popup, for example:
    //
    //     let popup = window.open()
    //     popup.document.body.write('
hello
')
    //
    // The above code would not work if a navigation to "about:blank" is done
    // here, since the window would be cleared of all changes in the next tick.
    const loadOptions = {}
    if (postData != null) {
      loadOptions.postData = postData
      loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
      if (postData.length > 0) {
        const postDataFront = postData[0].bytes.toString()
        const boundary = /^--.*[^-\r\n]/.exec(postDataFront)
        if (boundary != null) {
          loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`
        }
      }
    }
    guest.loadURL(url, loadOptions)
  }

  return setupGuest(embedder, frameName, guest, options)
}

const getGuestWindow = function (guestContents) {
  let guestWindow = BrowserWindow.fromWebContents(guestContents)
  if (guestWindow == null) {
    const hostContents = guestContents.hostWebContents
    if (hostContents != null) {
      guestWindow = BrowserWindow.fromWebContents(hostContents)
    }
  }
  return guestWindow
}

// Checks whether |sender| can access the |target|:
// 1. Check whether |sender| is the parent of |target|.
// 2. Check whether |sender| has node integration, if so it is allowed to
//    do anything it wants.
// 3. Check whether the origins match.
//
// However it allows a child window without node integration but with same
// origin to do anything it wants, when its opener window has node integration.
// The W3C does not have anything on this, but from my understanding of the
// security model of |window.opener|, this should be fine.
const canAccessWindow = function (sender, target) {
  return (target.getWebPreferences().openerId === sender.id) ||
         (sender.getWebPreferences().nodeIntegration === true) ||
         isSameOrigin(sender.getURL(), target.getURL())
}

// Routed window.open messages with raw options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
  if (url == null || url === '') url = 'about:blank'
  if (frameName == null) frameName = ''
  if (features == null) features = ''

  const options = {}

  const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
  const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload', 'javascript', 'contextIsolation']
  const disposition = 'new-window'

  // Used to store additional features
  const additionalFeatures = []

  // Parse the features
  parseFeaturesString(features, function (key, value) {
    if (value === undefined) {
      additionalFeatures.push(key)
    } else {
      if (webPreferences.includes(key)) {
        if (options.webPreferences == null) {
          options.webPreferences = {}
        }
        options.webPreferences[key] = value
      } else {
        options[key] = value
      }
    }
  })
  if (options.left) {
    if (options.x == null) {
      options.x = options.left
    }
  }
  if (options.top) {
    if (options.y == null) {
      options.y = options.top
    }
  }
  if (options.title == null) {
    options.title = frameName
  }
  if (options.width == null) {
    options.width = 800
  }
  if (options.height == null) {
    options.height = 600
  }

  for (const name of ints) {
    if (options[name] != null) {
      options[name] = parseInt(options[name], 10)
    }
  }

  ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
               url, frameName, disposition, options, additionalFeatures)
})

// Routed window.open messages with fully parsed options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, frameName,
                                                                  disposition, options,
                                                                  additionalFeatures, postData) {
  options = mergeBrowserWindowOptions(event.sender, options)
  event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures)
  const newGuest = event.newGuest
  if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
    if (newGuest !== undefined && newGuest !== null) {
      event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
    } else {
      event.returnValue = null
    }
  } else {
    event.returnValue = createGuest(event.sender, url, frameName, options, postData)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from closing its opener.`)
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) guestWindow.destroy()
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) {
    event.returnValue = guestWindow[method](...args)
  } else {
    event.returnValue = null
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
  if (targetOrigin == null) {
    targetOrigin = '*'
  }

  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  // The W3C does not seem to have word on how postMessage should work when the
  // origins do not match, so we do not do |canAccessWindow| check here since
  // postMessage across origins is useful and not harmful.
  if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
    const sourceId = event.sender.id
    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (canAccessWindow(event.sender, guestContents)) {
    guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (canAccessWindow(event.sender, guestContents)) {
    event.returnValue = guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
  }
})
<commit_msg>Store frame to guests in map<commit_after>'use strict'

const {BrowserWindow, ipcMain, webContents} = require('electron')
const {isSameOrigin} = process.atomBinding('v8_util')
const parseFeaturesString = require('../common/parse-features-string')

const hasProp = {}.hasOwnProperty
const frameToGuest = new Map()

// Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent, visited) {
  // Check for circular reference.
  if (visited == null) visited = new Set()
  if (visited.has(parent)) return

  visited.add(parent)
  for (const key in parent) {
    if (!hasProp.call(parent, key)) continue
    if (key in child) continue

    const value = parent[key]
    if (typeof value === 'object') {
      child[key] = mergeOptions({}, value, visited)
    } else {
      child[key] = value
    }
  }
  visited.delete(parent)

  return child
}

// Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) {
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }
  if (embedder.browserWindowOptions != null) {
    // Inherit the original options if it is a BrowserWindow.
    mergeOptions(options, embedder.browserWindowOptions)
  } else {
    // Or only inherit web-preferences if it is a webview.
    mergeOptions(options.webPreferences, embedder.getWebPreferences())
  }

  // Disable node integration on child window if disabled on parent window
  if (embedder.getWebPreferences().nodeIntegration === false) {
    options.webPreferences.nodeIntegration = false
  }

  // Enable context isolation on child window if enabled on parent window
  if (embedder.getWebPreferences().contextIsolation === true) {
    options.webPreferences.contextIsolation = true
  }

  // Disable JavaScript on child window if disabled on parent window
  if (embedder.getWebPreferences().javascript === false) {
    options.webPreferences.javascript = false
  }

  // Sets correct openerId here to give correct options to 'new-window' event handler
  options.webPreferences.openerId = embedder.id

  return options
}

// Setup a new guest with |embedder|
const setupGuest = function (embedder, frameName, guest, options) {
  // When |embedder| is destroyed we should also destroy attached guest, and if
  // guest is closed by user then we should prevent |embedder| from double
  // closing guest.
  const guestId = guest.webContents.id
  const closedByEmbedder = function () {
    guest.removeListener('closed', closedByUser)
    guest.destroy()
  }
  const closedByUser = function () {
    embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
    embedder.removeListener('render-view-deleted', closedByEmbedder)
  }
  if (!options.webPreferences.sandbox) {
    // These events should only be handled when the guest window is opened by a
    // non-sandboxed renderer for two reasons:
    //
    // - `render-view-deleted` is emitted when the popup is closed by the user,
    //   and that will eventually result in NativeWindow::NotifyWindowClosed
    //   using a dangling pointer since `destroy()` would have been called by
    //   `closeByEmbedded`
    // - No need to emit `ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_` since
    //   there's no renderer code listening to it.,
    embedder.once('render-view-deleted', closedByEmbedder)
    guest.once('closed', closedByUser)
  }
  if (frameName) {
    frameToGuest.set(frameName, guest)
    guest.frameName = frameName
    guest.once('closed', function () {
      frameToGuest.delete(frameName)
    })
  }
  return guestId
}

// Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, frameName, options, postData) {
  let guest = frameToGuest.get(frameName)
  if (frameName && (guest != null)) {
    guest.loadURL(url)
    return guest.webContents.id
  }

  // Remember the embedder window's id.
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }

  guest = new BrowserWindow(options)
  if (!options.webContents || url !== 'about:blank') {
    // We should not call `loadURL` if the window was constructed from an
    // existing webContents(window.open in a sandboxed renderer) and if the url
    // is not 'about:blank'.
    //
    // Navigating to the url when creating the window from an existing
    // webContents would not be necessary(it will navigate there anyway), but
    // apparently there's a bug that allows the child window to be scripted by
    // the opener, even when the child window is from another origin.
    //
    // That's why the second condition(url !== "about:blank") is required: to
    // force `OverrideSiteInstanceForNavigation` to be called and consequently
    // spawn a new renderer if the new window is targeting a different origin.
    //
    // If the URL is "about:blank", then it is very likely that the opener just
    // wants to synchronously script the popup, for example:
    //
    //     let popup = window.open()
    //     popup.document.body.write('
hello
')
    //
    // The above code would not work if a navigation to "about:blank" is done
    // here, since the window would be cleared of all changes in the next tick.
    const loadOptions = {}
    if (postData != null) {
      loadOptions.postData = postData
      loadOptions.extraHeaders = 'content-type: application/x-www-form-urlencoded'
      if (postData.length > 0) {
        const postDataFront = postData[0].bytes.toString()
        const boundary = /^--.*[^-\r\n]/.exec(postDataFront)
        if (boundary != null) {
          loadOptions.extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr(2)}`
        }
      }
    }
    guest.loadURL(url, loadOptions)
  }

  return setupGuest(embedder, frameName, guest, options)
}

const getGuestWindow = function (guestContents) {
  let guestWindow = BrowserWindow.fromWebContents(guestContents)
  if (guestWindow == null) {
    const hostContents = guestContents.hostWebContents
    if (hostContents != null) {
      guestWindow = BrowserWindow.fromWebContents(hostContents)
    }
  }
  return guestWindow
}

// Checks whether |sender| can access the |target|:
// 1. Check whether |sender| is the parent of |target|.
// 2. Check whether |sender| has node integration, if so it is allowed to
//    do anything it wants.
// 3. Check whether the origins match.
//
// However it allows a child window without node integration but with same
// origin to do anything it wants, when its opener window has node integration.
// The W3C does not have anything on this, but from my understanding of the
// security model of |window.opener|, this should be fine.
const canAccessWindow = function (sender, target) {
  return (target.getWebPreferences().openerId === sender.id) ||
         (sender.getWebPreferences().nodeIntegration === true) ||
         isSameOrigin(sender.getURL(), target.getURL())
}

// Routed window.open messages with raw options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
  if (url == null || url === '') url = 'about:blank'
  if (frameName == null) frameName = ''
  if (features == null) features = ''

  const options = {}

  const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
  const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload', 'javascript', 'contextIsolation']
  const disposition = 'new-window'

  // Used to store additional features
  const additionalFeatures = []

  // Parse the features
  parseFeaturesString(features, function (key, value) {
    if (value === undefined) {
      additionalFeatures.push(key)
    } else {
      if (webPreferences.includes(key)) {
        if (options.webPreferences == null) {
          options.webPreferences = {}
        }
        options.webPreferences[key] = value
      } else {
        options[key] = value
      }
    }
  })
  if (options.left) {
    if (options.x == null) {
      options.x = options.left
    }
  }
  if (options.top) {
    if (options.y == null) {
      options.y = options.top
    }
  }
  if (options.title == null) {
    options.title = frameName
  }
  if (options.width == null) {
    options.width = 800
  }
  if (options.height == null) {
    options.height = 600
  }

  for (const name of ints) {
    if (options[name] != null) {
      options[name] = parseInt(options[name], 10)
    }
  }

  ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
               url, frameName, disposition, options, additionalFeatures)
})

// Routed window.open messages with fully parsed options
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, frameName,
                                                                  disposition, options,
                                                                  additionalFeatures, postData) {
  options = mergeBrowserWindowOptions(event.sender, options)
  event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures)
  const newGuest = event.newGuest
  if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
    if (newGuest !== undefined && newGuest !== null) {
      event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
    } else {
      event.returnValue = null
    }
  } else {
    event.returnValue = createGuest(event.sender, url, frameName, options, postData)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from closing its opener.`)
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) guestWindow.destroy()
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) {
    event.returnValue = guestWindow[method](...args)
  } else {
    event.returnValue = null
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
  if (targetOrigin == null) {
    targetOrigin = '*'
  }

  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  // The W3C does not seem to have word on how postMessage should work when the
  // origins do not match, so we do not do |canAccessWindow| check here since
  // postMessage across origins is useful and not harmful.
  if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
    const sourceId = event.sender.id
    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (canAccessWindow(event.sender, guestContents)) {
    guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (canAccessWindow(event.sender, guestContents)) {
    event.returnValue = guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
  }
})
<|endoftext|>


<commit_before>var DomHandler = require("./DomHandler.js"),
    DomUtils = require("./DomUtils.js");

//TODO: make this a streamable handler
function FeedHandler(callback) {
    this.init(callback, { ignoreWhitespace: true });
}

require("util").inherits(FeedHandler, DomHandler);

FeedHandler.prototype.init = DomHandler;

function getElements(what, where, one, recurse) {
    if (one) return DomUtils.getElementsByTagName(what, where, recurse, 1)[0];
    else return DomUtils.getElementsByTagName(what, where, recurse);
}
function fetch(what, where, recurse) {
    var ret = getElements(what, where, true, recurse);
    if (ret && (ret = ret.children) && ret.length !== 0) return ret[0].data;
    else return false;
}

var isValidFeed = function(value) {
    return value === "rss" || value === "feed" || value === "rdf:RDF";
};

FeedHandler.prototype.onend = function() {
    var feed = {};
    var feedRoot;
    var tmp, items, childs;

    feedRoot = getElements(isValidFeed, this.dom, true);
    if (feedRoot) {
        if (feedRoot.name === "feed") {
            childs = feedRoot.children;
            items = getElements("entry", childs);
        } else {
            items = getElements("item", feedRoot.children);
            childs = getElements("channel", feedRoot.children, true).children;
        }

        if (feedRoot.name === "feed") {
            feed.type = "atom";
            if ((tmp = fetch("id", childs))) feed.id = tmp;
            if ((tmp = fetch("title", childs))) feed.title = tmp;
            if (
                (tmp = getElements("link", childs, true)) &&
                (tmp = tmp.attribs) &&
                (tmp = tmp.href)
            )
                feed.link = tmp;
            if ((tmp = fetch("subtitle", childs))) feed.description = tmp;
            if ((tmp = fetch("updated", childs))) feed.updated = new Date(tmp);
            if ((tmp = fetch("email", childs, true))) feed.author = tmp;
            feed.items = Array(items.length);
            items.forEach(function(item, i) {
                var entry = {},
                    tmp;
                if ((tmp = fetch("id", item.children))) entry.id = tmp;
                if ((tmp = fetch("title", item.children))) entry.title = tmp;
                if (
                    (tmp = getElements("link", item.children, true)) &&
                    (tmp = tmp.attribs) &&
                    (tmp = tmp.href)
                )
                    entry.link = tmp;
                if ((tmp = fetch("summary", item.children)))
                    entry.description = tmp;
                if ((tmp = fetch("updated", item.children)))
                    entry.pubDate = new Date(tmp);
                feed.items[i] = entry;
            });
        } else {
            feed.type = feedRoot.name;
            feed.id = "";
            if ((tmp = fetch("title", childs))) feed.title = tmp;
            if ((tmp = fetch("link", childs))) feed.link = tmp;
            if ((tmp = fetch("description", childs))) feed.description = tmp;
            if ((tmp = fetch("lastBuildDate", childs)))
                feed.updated = new Date(tmp);
            if ((tmp = fetch("managingEditor", childs))) feed.author = tmp;
            feed.items = Array(items.length);
            items.forEach(function(item, i) {
                var entry = {},
                    tmp;
                if ((tmp = fetch("guid", item.children))) entry.id = tmp;
                if ((tmp = fetch("title", item.children))) entry.title = tmp;
                if ((tmp = fetch("link", item.children))) entry.link = tmp;
                if ((tmp = fetch("description", item.children)))
                    entry.description = tmp;
                if ((tmp = fetch("pubDate", item.children)))
                    entry.pubDate = new Date(tmp);
                feed.items[i] = entry;
            });
        }
    }
    this.dom = feed;
    DomHandler.prototype._handleCallback.call(this);
};

module.exports = FeedHandler;
<commit_msg>Removed unnecessary code inside FeedHandler<commit_after>var DomHandler = require("./DomHandler.js"),
    DomUtils = require("./DomUtils.js");

//TODO: make this a streamable handler
function FeedHandler(callback) {
    this.init(callback, { ignoreWhitespace: true });
}

require("util").inherits(FeedHandler, DomHandler);

FeedHandler.prototype.init = DomHandler;

function getElements(what, where, one, recurse) {
    if (one) return DomUtils.getElementsByTagName(what, where, recurse, 1)[0];
    else return DomUtils.getElementsByTagName(what, where, recurse);
}
function fetch(what, where, recurse) {
    var ret = getElements(what, where, true, recurse);
    if (ret && (ret = ret.children) && ret.length !== 0) return ret[0].data;
    else return false;
}

var isValidFeed = function(value) {
    return value === "rss" || value === "feed" || value === "rdf:RDF";
};

FeedHandler.prototype.onend = function() {
    var feed = {};
    var feedRoot;
    var tmp, items, childs;

    feedRoot = getElements(isValidFeed, this.dom, true);
    if (feedRoot) {
        if (feedRoot.name === "feed") {
            childs = feedRoot.children;
            items = getElements("entry", childs);

            feed.type = "atom";
            if ((tmp = fetch("id", childs))) feed.id = tmp;
            if ((tmp = fetch("title", childs))) feed.title = tmp;
            if (
                (tmp = getElements("link", childs, true)) &&
                (tmp = tmp.attribs) &&
                (tmp = tmp.href)
            )
                feed.link = tmp;
            if ((tmp = fetch("subtitle", childs))) feed.description = tmp;
            if ((tmp = fetch("updated", childs))) feed.updated = new Date(tmp);
            if ((tmp = fetch("email", childs, true))) feed.author = tmp;
            feed.items = items.map(function(item) {
                var entry = {},
                    tmp;
                if ((tmp = fetch("id", item.children))) entry.id = tmp;
                if ((tmp = fetch("title", item.children))) entry.title = tmp;
                if (
                    (tmp = getElements("link", item.children, true)) &&
                    (tmp = tmp.attribs) &&
                    (tmp = tmp.href)
                )
                    entry.link = tmp;
                if ((tmp = fetch("summary", item.children)))
                    entry.description = tmp;
                if ((tmp = fetch("updated", item.children)))
                    entry.pubDate = new Date(tmp);
                return entry;
            });
        } else {
            items = getElements("item", feedRoot.children);
            childs = getElements("channel", feedRoot.children, true).children;

            feed.type = feedRoot.name;
            feed.id = "";
            if ((tmp = fetch("title", childs))) feed.title = tmp;
            if ((tmp = fetch("link", childs))) feed.link = tmp;
            if ((tmp = fetch("description", childs))) feed.description = tmp;
            if ((tmp = fetch("lastBuildDate", childs)))
                feed.updated = new Date(tmp);
            if ((tmp = fetch("managingEditor", childs))) feed.author = tmp;
            feed.items = items.map(function(item) {
                var entry = {},
                    tmp;
                if ((tmp = fetch("guid", item.children))) entry.id = tmp;
                if ((tmp = fetch("title", item.children))) entry.title = tmp;
                if ((tmp = fetch("link", item.children))) entry.link = tmp;
                if ((tmp = fetch("description", item.children)))
                    entry.description = tmp;
                if ((tmp = fetch("pubDate", item.children)))
                    entry.pubDate = new Date(tmp);
                return entry;
            });
        }
    }
    this.dom = feed;
    DomHandler.prototype._handleCallback.call(this);
};

module.exports = FeedHandler;
<|endoftext|>


<commit_before>var fs, path, Link, LinksStructure;

Link = require('./Link.js');
LinksStructure = require('./LinksStructure.js');
fs = require('fs');
path = require('path');

function FilesParser(templates) {
    this.links = new LinksStructure;

    if (templates == null)
        throw 'Undefined templates';

    if (templates.length == null || templates.length === 0)
        throw 'templates is empty';

    this.templates = templates;
    this._done = function() {};
    this._promise = 0;
    this._root = './';

}

FilesParser.prototype = {

    _setRoot: function(dir) {
        this._root = fs.realpathSync(dir);
    },

    _incPromise: function() {
        this._promise++;
    },

    _decPromise: function() {
        this._promise--;
        if (this._promise === 0) {
            this._done(this.links);
        } else if (this._promise < 0) {
            throw 'error Promise increment';
        }
    },

    _setDone: function(fn) {
        this._done = fn;
    },

    read: function(filePath, withRoot, callback) {
        var me = this, rootLink = me._createLink(filePath);

        if (withRoot) {
            me.links.add(rootLink);
        }

        this._setDone(callback);

        this._setRoot(path.dirname(filePath));;

        me._readFile(
            filePath,
            me._getLinks,
            rootLink.setError
        );
    },

    _getLinks: function(content) {
        var me = this;

        me.templates.forEach(function(template) {
            var blanks, maxLevel;

            blanks = template.getBlanks(content);

            if ( blanks.length === 0 ) return;

            maxLevel = me.links.getNumberLevels().max + 1;

            blanks.forEach(function(blank) {
                var link = me._createLink(blank, template, maxLevel);

                me.links.add(link);

                me._readFile(
                    link.path,
                    me._getLinks,
                    link.setError
                );
            });
        });
    },

    _getRealPath: function(filePath) {
        return path.resolve(this._root, filePath);
    },

    _createLink: function(blank, template, level) {

        if (template != null) {
            return new Link(blank, this._getRealPath(template.getPath(blank)), level);
        } else
            return new Link('root', this._getRealPath(blank), 0);  // this blank is path
    },

    _readFile: function(path, success, error) {
        var me = this;

        me._incPromise();

        fs.exists(path, function(exists) {
            if (exists) {
                fs.readFile(path, 'utf8', function (err, content) {

                    if (err != null) {
                        error.call(me, err);
                        return;
                    }

                    success.call(me, content);

                    me._decPromise();
                });

            } else {
                error.call(me, 'Path "' + path + '" not exists.');

                me._decPromise();
            }
        });
     },

    getListLinks: function() {
        return this.links.getList();
    }

};

module.exports = FilesParser;<commit_msg>return end-link-list to done function<commit_after>var fs, path, Link, LinksStructure;

Link = require('./Link.js');
LinksStructure = require('./LinksStructure.js');
fs = require('fs');
path = require('path');

function FilesParser(templates) {
    this.links = new LinksStructure;

    if (templates == null)
        throw 'Undefined templates';

    if (templates.length == null || templates.length === 0)
        throw 'templates is empty';

    this.templates = templates;
    this._done = function() {};
    this._promise = 0;
    this._root = './';

}

FilesParser.prototype = {

    _setRoot: function(dir) {
        this._root = fs.realpathSync(dir);
    },

    _incPromise: function() {
        this._promise++;
    },

    _decPromise: function() {
        this._promise--;
        if (this._promise === 0) {
            this._done(this.getListLinks());
        } else if (this._promise < 0) {
            throw 'error Promise increment';
        }
    },

    _setDone: function(fn) {
        this._done = fn;
    },

    read: function(filePath, withRoot, callback) {
        var me = this, rootLink = me._createLink(filePath);

        if (withRoot) {
            me.links.add(rootLink);
        }

        this._setDone(callback);

        this._setRoot(path.dirname(filePath));;

        me._readFile(
            filePath,
            me._getLinks,
            rootLink.setError
        );
    },

    _getLinks: function(content) {
        var me = this;

        me.templates.forEach(function(template) {
            var blanks, maxLevel;

            blanks = template.getBlanks(content);

            if ( blanks.length === 0 ) return;

            maxLevel = me.links.getNumberLevels().max + 1;

            blanks.forEach(function(blank) {
                var link = me._createLink(blank, template, maxLevel);

                me.links.add(link);

                me._readFile(
                    link.path,
                    me._getLinks,
                    link.setError
                );
            });
        });
    },

    _getRealPath: function(filePath) {
        return path.resolve(this._root, filePath);
    },

    _createLink: function(blank, template, level) {

        if (template != null) {
            return new Link(blank, this._getRealPath(template.getPath(blank)), level);
        } else
            return new Link('root', this._getRealPath(blank), 0);  // this blank is path
    },

    _readFile: function(path, success, error) {
        var me = this;

        me._incPromise();

        fs.exists(path, function(exists) {
            if (exists) {
                fs.readFile(path, 'utf8', function (err, content) {

                    if (err != null) {
                        error.call(me, err);
                        return;
                    }

                    success.call(me, content);

                    me._decPromise();
                });

            } else {
                error.call(me, 'Path "' + path + '" not exists.');

                me._decPromise();
            }
        });
     },

    getListLinks: function() {
        return this.links.getList();
    }

};

module.exports = FilesParser;<|endoftext|>


<commit_before><commit_msg>Added support to fixed address when associating floating ips<commit_after><|endoftext|>


<commit_before>var fsep = require('fs-extra-promise');
var pg = require('postgres-bluebird');
var copyFrom = require('pg-copy-streams').from;
var config = require('./config');
var throat = require('throat')
var path = require('path');
var fs = require('fs');
var logger = require('./logger')(module);
var Utils = require('./utils');
var Promise = require('bluebird');
var countFundRowsTemplate = handlebars.compile(fs.readFileSync(__dirname + '/sql/countFundRows.hbs').toString());

module.exports.importFilesCmd = function(parentDir, body, year, quarter, fund_number, tableName, concurrency){
	return fsep.readdirAsync(parentDir)
	.then(function(files) {

		files = Utils.filterFiles(files, body, year, quarter, fund_number);
		return module.exports.importFiles(parentDir, files, tableName, concurrency);
	})
	.then(function(res){
		fsep.writeFile("errors.log", res['errors']);
	})
	.catch(function(err){
		logger.error("error:" + err)
	})
}

module.exports.importFileCmd = function(filePath, tableName){

	var filename =  filePath.replace(/^.*(\\|\/|\:)/, '');
	var parentDir = filePath.substr(0, filePath.length - filename.length);

	//process.stdout.cursorTo (0);
	process.stdout.write("Importing: "+filename + "\n");

    return module.exports.importFile(parentDir, filename, tableName)
	.then(function(res){
		if (!res){
			logger.warn("Not imported",res);
		}
		else{
			logger.info("Done importing.");
		}
	})
	.catch(function(err){
		logger.error("error:" +err);
	})
}

/**
 * Import files to db, concurrently --> resolves to
 *
 *
 * result['done'] = [filenames...]
 * result['errors'] = [filenames...]
 */
module.exports.importFiles = function(parentDir, files, tableName, concurrency){

	if ( concurrency == undefined) concurrency = 4;
	var total = files.length;
	var counter = 0;

	return Promise.all(files.map(throat(concurrency, function(filename){

		//process.stdout.cursorTo (0);
		process.stdout.write(++counter+'/'+total + ":"+ filename + "\n");

		if ( filename.substr(-4) !== '.csv' ){
			return;
		}

		return module.exports.importFile(parentDir, filename, tableName);

	})))
	.then(function(resArr){

		//resArr - array of booleans,
		//represents success/fail of corresponding file index

		var result = {}
		result['errors'] = [];
		result['done'] = [];

		for (i in resArr){
			if(resArr[i] === true){
				result['done'].push(files[i]);
			}
			else if (resArr[i] === false){
				result['errors'].push(files[i]);
			}
		}

		return result;
	});
}

//import file to db --> resolves to boolean
module.exports.importFile = function(parentDir, filename, tableName){
	return  pg.connectAsync(config.connection_string)
				.spread(function(client, release) {

		            var filePath = path.join(parentDir, filename);

		            if (!fs.existsSync(filePath)){
			            var absolutePath = path.resolve(filePath);
			            console.log("File not found " + absolutePath);
		            }

					//Check if file already loaded to DB
					var year = filename.split('_')[1];
					var quarter = filename.split('_')[2];
					var fund = filename.split('_')[3].split('.')[0];
					var managing_body = filename.split('_')[0];

					var data = {
						tableName : tableName,
						report_year : year,
						report_quarter : quarter,
						fund : fund,
						managing_body : managing_body
					};

					var countFundSql = countFundRowsTemplate(data);

					return client.queryAsync(countFundSql)
						.then(function(qresult){
							release();
							return new Promise(function(resolve,reject){

								var count = qresult.rows[0].count;

								if (count > 0){ //file in DB, skip file
									logger.info([managing_body, year, quarter, fund].join("_") + " file already loaded to DB	")
									resolve();
									return;
								}

								//File not in DB, copy to table
								var pgstream = client.query(copyFrom('COPY '+ tableName +' FROM STDIN HEADER CSV NULL \'\'' ));
								var sqlStream = fsep.createReadStream(filePath);

							  	sqlStream.on('error',
								  	function(err){
								  		logger.error(" " +err)
										resolve(false);
							  	});
							  	sqlStream.pipe(pgstream)
									.on('end',
									  	function(){
											resolve(true);
								  		})
								  	.on('error',
								  		function(err){
									  		logger.error(" " +err)
											resolve(false);
									  	});

							});

					})
			})
}<commit_msg>Add missing require<commit_after>var fsep = require('fs-extra-promise');
var pg = require('postgres-bluebird');
var copyFrom = require('pg-copy-streams').from;
var config = require('./config');
var throat = require('throat')
var path = require('path');
var fs = require('fs');
var logger = require('./logger')(module);
var Utils = require('./utils');
var Promise = require('bluebird');
var handlebars = require('handlebars');
var countFundRowsTemplate = handlebars.compile(fs.readFileSync(__dirname + '/sql/countFundRows.hbs').toString());

module.exports.importFilesCmd = function(parentDir, body, year, quarter, fund_number, tableName, concurrency){
	return fsep.readdirAsync(parentDir)
	.then(function(files) {

		files = Utils.filterFiles(files, body, year, quarter, fund_number);
		return module.exports.importFiles(parentDir, files, tableName, concurrency);
	})
	.then(function(res){
		fsep.writeFile("errors.log", res['errors']);
	})
	.catch(function(err){
		logger.error("error:" + err)
	})
}

module.exports.importFileCmd = function(filePath, tableName){

	var filename =  filePath.replace(/^.*(\\|\/|\:)/, '');
	var parentDir = filePath.substr(0, filePath.length - filename.length);

	//process.stdout.cursorTo (0);
	process.stdout.write("Importing: "+filename + "\n");

    return module.exports.importFile(parentDir, filename, tableName)
	.then(function(res){
		if (!res){
			logger.warn("Not imported",res);
		}
		else{
			logger.info("Done importing.");
		}
	})
	.catch(function(err){
		logger.error("error:" +err);
	})
}

/**
 * Import files to db, concurrently --> resolves to
 *
 *
 * result['done'] = [filenames...]
 * result['errors'] = [filenames...]
 */
module.exports.importFiles = function(parentDir, files, tableName, concurrency){

	if ( concurrency == undefined) concurrency = 4;
	var total = files.length;
	var counter = 0;

	return Promise.all(files.map(throat(concurrency, function(filename){

		//process.stdout.cursorTo (0);
		process.stdout.write(++counter+'/'+total + ":"+ filename + "\n");

		if ( filename.substr(-4) !== '.csv' ){
			return;
		}

		return module.exports.importFile(parentDir, filename, tableName);

	})))
	.then(function(resArr){

		//resArr - array of booleans,
		//represents success/fail of corresponding file index

		var result = {}
		result['errors'] = [];
		result['done'] = [];

		for (i in resArr){
			if(resArr[i] === true){
				result['done'].push(files[i]);
			}
			else if (resArr[i] === false){
				result['errors'].push(files[i]);
			}
		}

		return result;
	});
}

//import file to db --> resolves to boolean
module.exports.importFile = function(parentDir, filename, tableName){
	return  pg.connectAsync(config.connection_string)
				.spread(function(client, release) {

		            var filePath = path.join(parentDir, filename);

		            if (!fs.existsSync(filePath)){
			            var absolutePath = path.resolve(filePath);
			            console.log("File not found " + absolutePath);
		            }

					//Check if file already loaded to DB
					var year = filename.split('_')[1];
					var quarter = filename.split('_')[2];
					var fund = filename.split('_')[3].split('.')[0];
					var managing_body = filename.split('_')[0];

					var data = {
						tableName : tableName,
						report_year : year,
						report_quarter : quarter,
						fund : fund,
						managing_body : managing_body
					};

					var countFundSql = countFundRowsTemplate(data);

					return client.queryAsync(countFundSql)
						.then(function(qresult){
							release();
							return new Promise(function(resolve,reject){

								var count = qresult.rows[0].count;

								if (count > 0){ //file in DB, skip file
									logger.info([managing_body, year, quarter, fund].join("_") + " file already loaded to DB	")
									resolve();
									return;
								}

								//File not in DB, copy to table
								var pgstream = client.query(copyFrom('COPY '+ tableName +' FROM STDIN HEADER CSV NULL \'\'' ));
								var sqlStream = fsep.createReadStream(filePath);

							  	sqlStream.on('error',
								  	function(err){
								  		logger.error(" " +err)
										resolve(false);
							  	});
							  	sqlStream.pipe(pgstream)
									.on('end',
									  	function(){
											resolve(true);
								  		})
								  	.on('error',
								  		function(err){
									  		logger.error(" " +err)
											resolve(false);
									  	});

							});

					})
			})
}<|endoftext|>