<commit_before>import WMBusReader from "./../../includes/reader/wmbus-reader"
import DataSource from "./../../includes/reader/data-source"
import DataPacket from "./../../includes/buffer/data-packet"
import stream from 'stream'

// Node v0.10+ uses native Transform, else polyfill
const Transform = stream.Transform ||
  require('readable-stream').Transform;

/**
* Wireless M-Bus reader. Provides data source.
*/
class ImstReader extends WMBusReader {

  /**
  * Constructor
  *
  * @param options
  *   source Source file to read data from
  * @param buffer
  *   Data buffer to send data
  */
  constructor(options = {}) {
    super(options);

    this._buffer = options.hasOwnProperty('buffer') ?
      options.buffer : false;

    this._serialPortPath = options.hasOwnProperty('serialPortPath') ?
      options.serialPortPath : false;

    this._done = false;
    this._processing = false;
  }

  /**
  * Implementation of enableSource().
  */
  enableSource() {
    let self = this;
    let telegramStream = new TelegramStrem();

    let SerialPort = require("serialport");

    let serialPort = new SerialPort(this._serialPortPath, {
      baudRate: 57600,
      dataBits: 8,
      stopBits: 1,
      parity: 'none'
    });

    serialPort.on("error", () => {
      console.log(`Unable to connect serial port: ${self._serialPortPath}`);
    });

    serialPort.on("open", () => {
        console.log('Connection opened');

        //setup the wbus reader for Link mode: C1 TF-B,  Device mode:Other, Send RSSI:no, Send Timestamp: no
        var configBuff =  Buffer.alloc(12,"A501030800030007b0000000", "hex");
        serialPort.write(configBuff);
      serialPort.on('data', (data) => {
        // Push data to telegram stream
        telegramStream.write(data);
      });
    });

    telegramStream.on('readable', () => {
      let data = null;

      while (null !== (data = telegramStream.read())) {
         //console.log("Push raw telegram data...");
        self._buffer.push(new DataPacket(data));
      }
    });
  }

  /**
  * Returns boolean value to indicate if source is ready.
  *
  * @return boolean is ready
  */
  isReady() {
    return this._done;
  }
}

/**
* Telegram Stream handles processing of imput data packets.
*/
class TelegramStrem extends Transform {

  /**
  * Constructor.
  *
  * @param options
  */
  constructor(options = {}) {
    // Make sure object mode is enabled
    options.objectMode = true;
    super(options);

    this._frameLength = -1;
    this._bufferedChunk = false;

    // Start byte for Imst Wireless
    this._startByte = Buffer.alloc(1,'A5', 'hex');
  }

  /**
  * Implementation of hook _transform()
  */
  _transform(chunk, enc, cb) {
    let self = this;
    let proceed = true;
    let data = this._bufferedChunk ?
      Buffer.concat([this._bufferedChunk, chunk]) : chunk;

    while (proceed) {
      // Find telegram start by comparing start hex code.
      if (this._frameLength < 0) {
        for (var i = 0; i < (data.length - 2); i++) {
          if (data[i] == self._startByte[0]) {
            // Remove leading bytes, since they are not part of this telegram
            if (i > 0)
              data = data.slice(i);
            // Get telegram length, lengt is 4 longer than the length parameeter due to start byte 2 header bytes and 1 length byte
            this._frameLength = (data[3] + 4);
            break;
          }
        }
      }

      var frameLength = this._frameLength ? this._frameLength : -1;

      if (frameLength > 0 && data.length >= frameLength) {
        // Extract the telegram
          if ((data[1] & 0x80)) {
              var telegramData = data.slice(0, frameLength+2);
          } else {
              var telegramData = data.slice(0, frameLength);
          }
          // Only accept RadioLink messages
          if ((telegramData[1] & 0x0F) == 0x02) {
              // Check if checksum is proviced, if so check it else just accept it
              if ((telegramData[1] & 0x80)) {
                  // This is valid telegram, register and remove
                  if (self.validateChecksum(telegramData.slice(1, frameLength + 2), telegramData.slice(frameLength, frameLength + 2))) {
                      this.push(telegramData.slice(3, frameLength - 1));
                      data = data.slice(frameLength - 4);
                  } else {
                      data = data.slice(1);
                  }

              } else {
                  // This is valid telegram, register and remove
                  this.push(telegramData.slice(3));
                  data = data.slice(frameLength - 4);
              }

        } else {
          // This is not valid telegram, remove leading value
          data = data.slice(1);
        }
        this._frameLength = -1;

      } else {
        this._bufferedChunk = data;
        proceed = false;
      }
    }

    cb();
  }

  /**
  * Implementation of hook_flush()
  */
  _flush(cb) {
    //console.log("Telegram stream flush...");
    this.push(this.digester.digest('hex'));
    cb();
  }

    validateChecksum(data, checksum) {
        const crc = require("node-crc");
        var checkValue = crc.crc(16, true, 0x8408, 0x0000, 0xFFFF, 0x00, 0xffff, 0, data);

        return checkValue[0] == 15 && checkValue[1] == 71;
    }

}

export default ImstReader;<commit_msg>Fixed bug where one byte to little was send to the buffer<commit_after>import WMBusReader from "./../../includes/reader/wmbus-reader"
import DataSource from "./../../includes/reader/data-source"
import DataPacket from "./../../includes/buffer/data-packet"
import stream from 'stream'

// Node v0.10+ uses native Transform, else polyfill
const Transform = stream.Transform ||
  require('readable-stream').Transform;

/**
* Wireless M-Bus reader. Provides data source.
*/
class ImstReader extends WMBusReader {

  /**
  * Constructor
  *
  * @param options
  *   source Source file to read data from
  * @param buffer
  *   Data buffer to send data
  */
  constructor(options = {}) {
    super(options);

    this._buffer = options.hasOwnProperty('buffer') ?
      options.buffer : false;

    this._serialPortPath = options.hasOwnProperty('serialPortPath') ?
      options.serialPortPath : false;

    this._done = false;
    this._processing = false;
  }

  /**
  * Implementation of enableSource().
  */
  enableSource() {
    let self = this;
    let telegramStream = new TelegramStrem();

    let SerialPort = require("serialport");

    let serialPort = new SerialPort(this._serialPortPath, {
      baudRate: 57600,
      dataBits: 8,
      stopBits: 1,
      parity: 'none'
    });

    serialPort.on("error", () => {
      console.log(`Unable to connect serial port: ${self._serialPortPath}`);
    });

    serialPort.on("open", () => {
        console.log('Connection opened');

        //setup the wbus reader for Link mode: C1 TF-B,  Device mode:Other, Send RSSI:no, Send Timestamp: no
        var configBuff =  Buffer.alloc(12,"A501030800030007b0000000", "hex");
        serialPort.write(configBuff);
      serialPort.on('data', (data) => {
        // Push data to telegram stream
        telegramStream.write(data);
      });
    });

    telegramStream.on('readable', () => {
      let data = null;

      while (null !== (data = telegramStream.read())) {
         //console.log("Push raw telegram data...");
        self._buffer.push(new DataPacket(data));
      }
    });
  }

  /**
  * Returns boolean value to indicate if source is ready.
  *
  * @return boolean is ready
  */
  isReady() {
    return this._done;
  }
}

/**
* Telegram Stream handles processing of imput data packets.
*/
class TelegramStrem extends Transform {

  /**
  * Constructor.
  *
  * @param options
  */
  constructor(options = {}) {
    // Make sure object mode is enabled
    options.objectMode = true;
    super(options);

    this._frameLength = -1;
    this._bufferedChunk = false;

    // Start byte for Imst Wireless
    this._startByte = Buffer.alloc(1,'A5', 'hex');
  }

  /**
  * Implementation of hook _transform()
  */
  _transform(chunk, enc, cb) {
    let self = this;
    let proceed = true;
    let data = this._bufferedChunk ?
      Buffer.concat([this._bufferedChunk, chunk]) : chunk;

    while (proceed) {
      // Find telegram start by comparing start hex code.
      if (this._frameLength < 0) {
        for (var i = 0; i < (data.length - 2); i++) {
          if (data[i] == self._startByte[0]) {
            // Remove leading bytes, since they are not part of this telegram
            if (i > 0)
              data = data.slice(i);
            // Get telegram length, lengt is 4 longer than the length parameeter due to start byte 2 header bytes and 1 length byte
            this._frameLength = (data[3] + 4);
            break;
          }
        }
      }

      var frameLength = this._frameLength ? this._frameLength : -1;

      if (frameLength > 0 && data.length >= frameLength) {
        // Extract the telegram
          if ((data[1] & 0x80)) {
              var telegramData = data.slice(0, frameLength+2);
          } else {
              var telegramData = data.slice(0, frameLength);
          }
          // Only accept RadioLink messages
          if ((telegramData[1] & 0x0F) == 0x02) {
              // Check if checksum is proviced, if so check it else just accept it
              if ((telegramData[1] & 0x80)) {
                  // This is valid telegram, register and remove
                  if (self.validateChecksum(telegramData.slice(1, frameLength + 2), telegramData.slice(frameLength, frameLength + 2))) {
                      this.push(telegramData.slice(3, frameLength));
                      data = data.slice(frameLength - 3);
                  } else {
                      data = data.slice(1);
                  }

              } else {
                  // This is valid telegram, register and remove
                  this.push(telegramData.slice(3));
                  data = data.slice(frameLength - 4);
              }

        } else {
          // This is not valid telegram, remove leading value
          data = data.slice(1);
        }
        this._frameLength = -1;

      } else {
        this._bufferedChunk = data;
        proceed = false;
      }
    }

    cb();
  }

  /**
  * Implementation of hook_flush()
  */
  _flush(cb) {
    //console.log("Telegram stream flush...");
    this.push(this.digester.digest('hex'));
    cb();
  }

    validateChecksum(data, checksum) {
        const crc = require("node-crc");
        var checkValue = crc.crc(16, true, 0x8408, 0x0000, 0xFFFF, 0x00, 0xffff, 0, data);

        return checkValue[0] == 15 && checkValue[1] == 71;
    }

}

export default ImstReader;<|endoftext|>


<commit_before>'use strict';

const PushConfig = require('../models/PushConfig');

var updateActiveState = (newPushConfig) => {
    PushConfig.find({ active: true })
        .then(pushConfigs => {
            pushConfigs.forEach((pushConfig) => {
                if (!pushConfig._id.equals(newPushConfig._id)) {
                    pushConfig.active = false;
                    return pushConfig.save()
                        .then(updatedPushConfig => {
                            return res.status(200).json(updatedPushConfig);
                        })
                        .catch(err => {
                            return res.status(500).json({
                                message: 'Error updating active state of push config',
                                error: err
                            });
                        })
                }
            });
        })
        .catch(err => {
            return res.status(404).json({
                message: 'no push configs found',
                error: err
            });
        })
};

exports.create = (req, res) => {
    const pushConfig = new PushConfig(req.body);
    pushConfig.save()
        .then(newPushConfig => {
            if (newPushConfig.active === true) {
                updateActiveState(newPushConfig);
            }
            return res.status(201).json(newPushConfig);
        })
        .catch(err => {
            return res.status(500).json({
                message: 'Error creating push config',
                error: err
            });
        })
};

exports.getOne = (req, res) => {
    PushConfig.findOne({ _id: req.params.id })
        .then(pushConfig => {
            if (pushConfig != null) {
                return res.status(200).json(pushConfig)
            }
        })
        .catch(err => {
            return res.status(404).json({
                message: 'id not found',
                error: err
            });
        })
};

exports.getAll = (req, res) => {
    PushConfig.find({}).exec()
        .then(pushConfigs => {
            return res.status(200).json(pushConfigs);
        })
};

exports.update = (req, res) => {
    PushConfig.findOneAndUpdate({ _id: req.params.id }, { $set: req.body }, { 'new': true })
        .then(pushConfig => {
            if (pushConfig != null) {
                return res.status(200).json(pushConfig);
            }
        })
        .catch(err => {
            return res.status(404).json({
                message: 'id not found',
                error: err
            });
        })
};

exports.delete = (req, res) => {
    PushConfig.remove({ _id: req.params.id })
        .then(pushConfig => {
            return res.status(204).json(pushConfig);
        })
        .catch(err => {
            return res.status(404).json({
                message: 'id not found',
                error: err
            });
        });
};
<commit_msg>sonar fixes<commit_after>'use strict';

const PushConfig = require('../models/PushConfig');

var updateActiveState = (newPushConfig) => {
    PushConfig.find({ active: true })
        .then(pushConfigs => {
            pushConfigs.forEach((pushConfig) => {
                if (!pushConfig._id.equals(newPushConfig._id)) {
                    pushConfig.active = false;
                    return pushConfig.save()
                        .then(updatedPushConfig => {
                            return updatedPushConfig;
                        })
                        .catch(err => {
                            return  err;
                        })
                }
            });
        })
        .catch(err => {
            return err;
        })
};

exports.create = (req, res) => {
    const pushConfig = new PushConfig(req.body);
    pushConfig.save()
        .then(newPushConfig => {
            if (newPushConfig.active === true) {
                updateActiveState(newPushConfig);
            }
            return res.status(201).json(newPushConfig);
        })
        .catch(err => {
            return res.status(500).json({
                message: 'Error creating push config',
                error: err
            });
        })
};

exports.getOne = (req, res) => {
    PushConfig.findOne({ _id: req.params.id })
        .then(pushConfig => {
            if (pushConfig != null) {
                return res.status(200).json(pushConfig)
            }
        })
        .catch(err => {
            return res.status(404).json({
                message: 'id not found',
                error: err
            });
        })
};

exports.getAll = (req, res) => {
    PushConfig.find({}).exec()
        .then(pushConfigs => {
            return res.status(200).json(pushConfigs);
        })
};

exports.update = (req, res) => {
    PushConfig.findOneAndUpdate({ _id: req.params.id }, { $set: req.body }, { 'new': true })
        .then(pushConfig => {
            if (pushConfig != null) {
                return res.status(200).json(pushConfig);
            }
        })
        .catch(err => {
            return res.status(404).json({
                message: 'id not found',
                error: err
            });
        })
};

exports.delete = (req, res) => {
    PushConfig.remove({ _id: req.params.id })
        .then(pushConfig => {
            return res.status(204).json(pushConfig);
        })
        .catch(err => {
            return res.status(404).json({
                message: 'id not found',
                error: err
            });
        });
};
<|endoftext|>


<commit_before>var dl = require('datalib'),
    Heap = require('./Heap'),
    ChangeSet = require('./ChangeSet'),
    DataSource = require('./DataSource'),
    Collector = require('./Collector'),
    Tuple = require('./Tuple'),
    Signal = require('./Signal'),
    Deps = require('./Dependencies');

function Graph() {
}

var prototype = Graph.prototype;

prototype.init = function() {
  this._stamp = 0;
  this._rank  = 0;

  this._data = {};
  this._signals = {};

  this.doNotPropagate = {};
};

prototype.rank = function() {
  return ++this._rank;
};

prototype.values = function(type, names, hash) {
  var data = (type === Deps.SIGNALS ? this._signals : this._data),
      n = (names !== undefined ? names : dl.keys(data)),
      vals, i;

  if (Array.isArray(n)) {
    vals = hash || {};
    for (i=0; i<n.length; ++i) {
      vals[n[i]] = data[n[i]].values();
    }
    return vals;
  } else {
    return data[n].values();
  }
};

// Retain for backwards-compatibility
prototype.dataValues = function(names) {
  return this.values(Deps.DATA, names);
};

// Retain for backwards-compatibility
prototype.signalValues = function(names) {
  return this.values(Deps.SIGNALS, names);
};

prototype.data = function(name, pipeline, facet) {
  var db = this._data;
  if (!arguments.length) {
    var all = [], key;
    for (key in db) { all.push(db[key]); }
    return all;
  } else if (arguments.length === 1) {
    return db[name];
  } else {
    return (db[name] = new DataSource(this, name, facet).pipeline(pipeline));
  }
};

prototype.signal = function(name, init) {
  if (arguments.length === 1) {
    var m = this;
    return Array.isArray(name) ?
      name.map(function(n) { return m._signals[n]; }) :
      this._signals[name];
  } else {
    return (this._signals[name] = new Signal(this, name, init));
  }
};

prototype.signalRef = function(ref) {
  if (!Array.isArray(ref)) {
    ref = dl.field(ref);
  }

  var value = this.signal(ref[0]).value();
  if (ref.length > 1) {
    for (var i=1, n=ref.length; i<n; ++i) {
      value = value[ref[i]];
    }
  }
  return value;
};

// Stamp should be specified with caution. It is necessary for inline datasources,
// which need to be populated during the same cycle even though propagation has
// passed that part of the dataflow graph.
prototype.propagate = function(pulse, node, stamp) {
  var pulses = {},
      listeners, next, nplse, tpls, ntpls, i, len;

  // new PQ with each propagation cycle so that we can pulse branches
  // of the dataflow graph during a propagation (e.g., when creating
  // a new inline datasource).
  var pq = new Heap(function(a, b) {
    // Sort on qrank (queue-rank).
    // Rank can change during propagation due to rewiring.
    return a._qrank - b._qrank;
  });

  if (pulse.stamp) throw Error('Pulse already has a non-zero stamp.');

  pulse.stamp = stamp || ++this._stamp;
  pulses[node._id] = pulse;
  pq.push(node.qrank(true));

  while (pq.size() > 0) {
    node  = pq.peek();
    pulse = pulses[node._id];

    if (node.rank() !== node.qrank()) {
      // A node's rank might change during a propagation. Re-queue if so.
      pq.replace(node.qrank(true));
    } else {
      // Evaluate node and propagate pulse.
      pq.pop();
      pulses[node._id] = null;
      listeners = node._listeners;
      pulse = this.evaluate(pulse, node);

      // Propagate the pulse.
      if (pulse !== this.doNotPropagate) {
        // Ensure reflow pulses always send reflow pulses even if skipped.
        if (!pulse.reflow && node.reflows()) {
          pulse = ChangeSet.create(pulse, true);
        }

        for (i=0, len=listeners.length; i<len; ++i) {
          next = listeners[i];

          if ((nplse = pulses[next._id]) !== undefined) {
            if (nplse === null) throw Error('Already propagated to node.');

            // We've already queued this node. Ensure there should be at most one
            // pulse with tuples (add/mod/rem), and the remainder will be reflows.
            tpls  = pulse.add.length || pulse.mod.length || pulse.rem.length;
            ntpls = nplse.add.length || nplse.mod.length || nplse.rem.length;

            if (tpls && ntpls) throw Error('Multiple changeset pulses to same node');

            // Combine reflow and tuples into a single pulse.
            pulses[next._id] = tpls ? pulse : nplse;
            pulses[next._id].reflow = pulse.reflow || nplse.reflow;
          } else {
            // First time we're seeing this node, queue it for propagation.
            pq.push(next.qrank(true));
            pulses[next._id] = pulse;
          }
        }
      }
    }
  }
};

// Process a new branch of the dataflow graph prior to connection:
// (1) Insert new Collector nodes as needed.
// (2) Track + return mutation/routing status of the branch.
prototype.preprocess = function(branch) {
  var graph = this,
      mutates = 0,
      node, router, collector, collects;

  for (var i=0; i<branch.length; ++i) {
    node = branch[i];

    // Batch nodes need access to a materialized dataset.
    if (node.batch() && !node._collector) {
      if (router || !collector) {
        node = new Collector(graph);
        branch.splice(i, 0, node);
        router = false;
      } else {
        node._collector = collector;
      }
    }

    if ((collects = node.collector())) collector = node;
    router  = router  || node.router() && !collects;
    mutates = mutates || node.mutates();

    // A collector needs to be inserted after tuple-producing
    // nodes for correct previous value tracking.
    if (node.produces()) {
      branch.splice(i+1, 0, new Collector(graph));
      router = false;
    }
  }

  return {router: router, collector: collector, mutates: mutates};
};

prototype.connect = function(branch) {
  var collector, node, data, signals, i, n, j, m;

  // connect the pipeline
  for (i=0, n=branch.length; i<n; ++i) {
    node = branch[i];
    if (node.collector()) collector = node;

    data = node.dependency(Deps.DATA);
    for (j=0, m=data.length; j<m; ++j) {
      this.data(data[j]).addListener(collector);
    }

    signals = node.dependency(Deps.SIGNALS);
    for (j=0, m=signals.length; j<m; ++j) {
      this.signal(signals[j]).addListener(collector);
    }

    if (i > 0) branch[i-1].addListener(node);
  }

  return branch;
};

prototype.disconnect = function(branch) {
  var collector, node, data, signals, i, n, j, m;

  for (i=0, n=branch.length; i<n; ++i) {
    node = branch[i];
    if (node.collector()) collector = node;

    data = node.dependency(Deps.DATA);
    for (j=0, m=data.length; j<m; ++j) {
      this.data(data[j]).removeListener(collector);
    }

    signals = node.dependency(Deps.SIGNALS);
    for (j=0, m=signals.length; j<m; ++j) {
      this.signal(signals[j]).removeListener(collector);
    }

    node.disconnect();
  }

  return branch;
};

prototype.synchronize = function(branch) {
  var tids = {}, cids = {},
      node, collector, data, i, n, j, m, d, id;

  for (i=0, n=branch.length; i<n; ++i) {
    node = branch[i];
    collector = node._collector || (node.collector() && node);
    if (cids[collector._id] || !collector) continue;

    for (j=0, data=collector.data(), m=data.length; j<m; ++j) {
      id = (d = data[j])._id;
      if (tids[id]) continue;
      Tuple.prev_update(d);
      tids[id] = 1;
    }

    cids[collector._id] = 1;
  }

  return this;
};

prototype.reevaluate = function(pulse, node) {
  var reflowed = pulse.reflow && node.last() >= pulse.stamp,
      run = node.router() || pulse.add.length || pulse.rem.length;

  return run || !reflowed || node.reevaluate(pulse);
};

prototype.evaluate = function(pulse, node) {
  if (!this.reevaluate(pulse, node)) return pulse;
  pulse = node.evaluate(pulse);
  node.last(pulse.stamp);
  return pulse;
};

module.exports = Graph;
<commit_msg>Cleanup.<commit_after>var dl = require('datalib'),
    Heap = require('./Heap'),
    ChangeSet = require('./ChangeSet'),
    DataSource = require('./DataSource'),
    Collector = require('./Collector'),
    Tuple = require('./Tuple'),
    Signal = require('./Signal'),
    Deps = require('./Dependencies');

function Graph() {
}

var prototype = Graph.prototype;

prototype.init = function() {
  this._stamp = 0;
  this._rank  = 0;

  this._data = {};
  this._signals = {};

  this.doNotPropagate = {};
};

prototype.rank = function() {
  return ++this._rank;
};

prototype.values = function(type, names, hash) {
  var data = (type === Deps.SIGNALS ? this._signals : this._data),
      n = (names !== undefined ? names : dl.keys(data)),
      vals, i;

  if (Array.isArray(n)) {
    vals = hash || {};
    for (i=0; i<n.length; ++i) {
      vals[n[i]] = data[n[i]].values();
    }
    return vals;
  } else {
    return data[n].values();
  }
};

// Retain for backwards-compatibility
prototype.dataValues = function(names) {
  return this.values(Deps.DATA, names);
};

// Retain for backwards-compatibility
prototype.signalValues = function(names) {
  return this.values(Deps.SIGNALS, names);
};

prototype.data = function(name, pipeline, facet) {
  var db = this._data;
  if (!arguments.length) {
    var all = [], key;
    for (key in db) { all.push(db[key]); }
    return all;
  } else if (arguments.length === 1) {
    return db[name];
  } else {
    return (db[name] = new DataSource(this, name, facet).pipeline(pipeline));
  }
};

prototype.signal = function(name, init) {
  if (arguments.length === 1) {
    var m = this;
    return Array.isArray(name) ?
      name.map(function(n) { return m._signals[n]; }) :
      this._signals[name];
  } else {
    return (this._signals[name] = new Signal(this, name, init));
  }
};

prototype.signalRef = function(ref) {
  if (!Array.isArray(ref)) {
    ref = dl.field(ref);
  }

  var value = this.signal(ref[0]).value();
  if (ref.length > 1) {
    for (var i=1, n=ref.length; i<n; ++i) {
      value = value[ref[i]];
    }
  }
  return value;
};

// Stamp should be specified with caution. It is necessary for inline datasources,
// which need to be populated during the same cycle even though propagation has
// passed that part of the dataflow graph.
prototype.propagate = function(pulse, node, stamp) {
  var pulses = {},
      listeners, next, nplse, tpls, ntpls, i, len;

  // new PQ with each propagation cycle so that we can pulse branches
  // of the dataflow graph during a propagation (e.g., when creating
  // a new inline datasource).
  var pq = new Heap(function(a, b) {
    // Sort on qrank (queue-rank).
    // Rank can change during propagation due to rewiring.
    return a._qrank - b._qrank;
  });

  if (pulse.stamp) throw Error('Pulse already has a non-zero stamp.');

  pulse.stamp = stamp || ++this._stamp;
  pulses[node._id] = pulse;
  pq.push(node.qrank(true));

  while (pq.size() > 0) {
    node  = pq.peek();
    pulse = pulses[node._id];

    if (node.rank() !== node.qrank()) {
      // A node's rank might change during a propagation. Re-queue if so.
      pq.replace(node.qrank(true));
    } else {
      // Evaluate node and propagate pulse.
      pq.pop();
      pulses[node._id] = null;
      listeners = node._listeners;
      pulse = this.evaluate(pulse, node);

      // Propagate the pulse.
      if (pulse !== this.doNotPropagate) {
        // Ensure reflow pulses always send reflow pulses even if skipped.
        if (!pulse.reflow && node.reflows()) {
          pulse = ChangeSet.create(pulse, true);
        }

        for (i=0, len=listeners.length; i<len; ++i) {
          next = listeners[i];

          if ((nplse = pulses[next._id]) !== undefined) {
            if (nplse === null) throw Error('Already propagated to node.');

            // We've already queued this node. Ensure there should be at most one
            // pulse with tuples (add/mod/rem), and the remainder will be reflows.
            tpls  = pulse.add.length || pulse.mod.length || pulse.rem.length;
            ntpls = nplse.add.length || nplse.mod.length || nplse.rem.length;

            if (tpls && ntpls) throw Error('Multiple changeset pulses to same node');

            // Combine reflow and tuples into a single pulse.
            pulses[next._id] = tpls ? pulse : nplse;
            pulses[next._id].reflow = pulse.reflow || nplse.reflow;
          } else {
            // First time we're seeing this node, queue it for propagation.
            pq.push(next.qrank(true));
            pulses[next._id] = pulse;
          }
        }
      }
    }
  }
};

// Process a new branch of the dataflow graph prior to connection:
// (1) Insert new Collector nodes as needed.
// (2) Track + return mutation/routing status of the branch.
prototype.preprocess = function(branch) {
  var graph = this,
      mutates = 0,
      node, router, collector, collects;

  for (var i=0; i<branch.length; ++i) {
    node = branch[i];

    // Batch nodes need access to a materialized dataset.
    if (node.batch() && !node._collector) {
      if (router || !collector) {
        node = new Collector(graph);
        branch.splice(i, 0, node);
        router = false;
      } else {
        node._collector = collector;
      }
    }

    if ((collects = node.collector())) collector = node;
    router  = router  || node.router() && !collects;
    mutates = mutates || node.mutates();

    // A collector needs to be inserted after tuple-producing
    // nodes for correct previous value tracking.
    if (node.produces()) {
      branch.splice(i+1, 0, new Collector(graph));
      router = false;
    }
  }

  return {router: router, collector: collector, mutates: mutates};
};

prototype.connect = function(branch) {
  var collector, node, data, signals, i, n, j, m;

  // connect the pipeline
  for (i=0, n=branch.length; i<n; ++i) {
    node = branch[i];
    if (node.collector()) collector = node;

    data = node.dependency(Deps.DATA);
    for (j=0, m=data.length; j<m; ++j) {
      this.data(data[j]).addListener(collector);
    }

    signals = node.dependency(Deps.SIGNALS);
    for (j=0, m=signals.length; j<m; ++j) {
      this.signal(signals[j]).addListener(collector);
    }

    if (i > 0) branch[i-1].addListener(node);
  }

  return branch;
};

prototype.disconnect = function(branch) {
  var collector, node, data, signals, i, n, j, m;

  for (i=0, n=branch.length; i<n; ++i) {
    node = branch[i];
    if (node.collector()) collector = node;

    data = node.dependency(Deps.DATA);
    for (j=0, m=data.length; j<m; ++j) {
      this.data(data[j]).removeListener(collector);
    }

    signals = node.dependency(Deps.SIGNALS);
    for (j=0, m=signals.length; j<m; ++j) {
      this.signal(signals[j]).removeListener(collector);
    }

    node.disconnect();
  }

  return branch;
};

prototype.synchronize = function(branch) {
  var ids = {},
      node, data, i, n, j, m, d, id;

  for (i=0, n=branch.length; i<n; ++i) {
    node = branch[i];
    if (!node.collector()) continue;

    for (j=0, data=node.data(), m=data.length; j<m; ++j) {
      id = (d = data[j])._id;
      if (ids[id]) continue;
      Tuple.prev_update(d);
      ids[id] = 1;
    }
  }

  return this;
};

prototype.reevaluate = function(pulse, node) {
  var reflowed = pulse.reflow && node.last() >= pulse.stamp,
      run = node.router() || pulse.add.length || pulse.rem.length;

  return run || !reflowed || node.reevaluate(pulse);
};

prototype.evaluate = function(pulse, node) {
  if (!this.reevaluate(pulse, node)) return pulse;
  pulse = node.evaluate(pulse);
  node.last(pulse.stamp);
  return pulse;
};

module.exports = Graph;
<|endoftext|>


<commit_before>/**
 * @fileoverview This file provides a measure directive. This directive is used
 * to create a measure panel in the page.
 *
 * Example:
 *
 * <app-measure app-measure-map="::mainCtrl.map"></app-measure>
 *
 * Note the use of the one-time binding operator (::) in the map expression.
 * One-time binding is used because we know the map is not going to change
 * during the lifetime of the application.
 */
goog.provide('app.measureDirective');

goog.require('app');
goog.require('app.profileDirective');
goog.require('ngeo.DecorateInteraction');
goog.require('ngeo.btngroupDirective');
goog.require('ngeo.interaction.MeasureArea');
goog.require('ngeo.interaction.MeasureAzimut');
goog.require('ngeo.interaction.MeasureLength');
goog.require('ol.Object');
goog.require('ol.format.GeoJSON');
goog.require('ol.source.Vector');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');

/**
 * @param {angular.$compile} $compile The compile provider.
 * @param {string} appMeasureTemplateUrl Url to measure template
 * @return {angular.Directive} The Directive Definition Object.
 * @ngInject
 */
app.measureDirective = function($compile, appMeasureTemplateUrl) {
  return {
    restrict: 'E',
    scope: {
      'map': '=appMeasureMap',
      'active': '=appMeasureActive'
    },
    controller: 'AppMeasureController',
    controllerAs: 'ctrl',
    bindToController: true,
    templateUrl: appMeasureTemplateUrl
  };
};

app.module.directive('appMeasure', app.measureDirective);

/**
 * @param {angular.Scope} $scope Scope.
 * @param {angular.$q} $q
 * @param {angular.$http} $http Angular http service.
 * @param {ngeo.DecorateInteraction} ngeoDecorateInteraction Decorate
 *     interaction service.
 * @param {string} elevationServiceUrl the url of the service
 * @param {string} profilejsonUrl The URL to the profile webservice.
 * @constructor
 * @export
 * @ngInject
 */
app.MeasureController = function($scope, $q, $http, ngeoDecorateInteraction,
    elevationServiceUrl, profilejsonUrl) {

  /**
   * @type {angular.$http}
   * @private
   */
  this.$http_ = $http;

  /**
   * @type {string}
   * @private
   */
  this.elevationServiceUrl_ = elevationServiceUrl;

  /**
   * @type {string}
   * @private
   */
  this.profilejsonUrl_ = profilejsonUrl;

  var sketchStyle = new ol.style.Style({
    fill: new ol.style.Fill({
      color: 'rgba(255, 255, 255, 0.4)'
    }),
    stroke: new ol.style.Stroke({
      color: 'rgba(0, 0, 0, 0.5)',
      lineDash: [10, 10],
      width: 2
    }),
    image: new ol.style.Circle({
      radius: 5,
      stroke: new ol.style.Stroke({
        color: 'rgba(0, 0, 0, 0.7)'
      }),
      fill: new ol.style.Fill({
        color: 'rgba(255, 255, 255, 0.4)'
      })
    })
  });

  var style = new ol.style.Style({
    fill: new ol.style.Fill({
      color: 'rgba(255, 204, 51, 0.3)'
    }),
    stroke: new ol.style.Stroke({
      color: 'rgba(255, 204, 51, 1)',
      width: 2
    }),
    image: new ol.style.Circle({
      radius: 7,
      fill: new ol.style.Fill({
        color: 'rgba(255, 204, 51, 0.3)'
      })
    })
  });

  /**
   * @type {ol.Map}
   * @private
   */
  this.map_ = this['map'];

  var measureProfile = new ngeo.interaction.MeasureLength({
    sketchStyle: sketchStyle
  });

  /**
   * @type {app.interaction.MeasureProfile}
   */
  this['measureProfile'] = measureProfile;
  measureProfile.setActive(false);
  ngeoDecorateInteraction(measureProfile);
  this.map_.addInteraction(measureProfile);

  var measureLength = new ngeo.interaction.MeasureLength({
    sketchStyle: sketchStyle
  });

  /**
   * @type {array<object>}
   */
  this['profileData'] = undefined;

  /**
   * @type {ngeo.interaction.MeasureLength}
   */
  this['measureLength'] = measureLength;

  measureLength.setActive(false);
  ngeoDecorateInteraction(measureLength);
  this.map_.addInteraction(measureLength);

  var measureArea = new ngeo.interaction.MeasureArea({
    sketchStyle: sketchStyle,
    style: style
  });

  /**
   * @type {ngeo.interaction.MeasureArea}
   */
  this['measureArea'] = measureArea;

  measureArea.setActive(false);
  ngeoDecorateInteraction(measureArea);
  this.map_.addInteraction(measureArea);

  /** @type {ngeo.interaction.MeasureAzimut} */
  var measureAzimut = new ngeo.interaction.MeasureAzimut({
    sketchStyle: sketchStyle,
    style: style
  });

  /**
   * @type {ngeo.interaction.MeasureAzimut}
   */
  this['measureAzimut'] = measureAzimut;

  measureAzimut.setActive(false);
  ngeoDecorateInteraction(measureAzimut);
  this.map_.addInteraction(measureAzimut);

  goog.events.listen(measureAzimut, ngeo.MeasureEventType.MEASUREEND,
      goog.bind(function(evt) {
        var geometryCollection =
            /** @type {ol.geom.GeometryCollection} */
            (evt.feature.getGeometry());

        var radius =
            /** @type {ol.geom.LineString} */
            (geometryCollection.getGeometries()[0]);
        var radiusCoordinates = radius.getCoordinates();
        $q.all([this.getElevation_(radiusCoordinates[0]),
              this.getElevation_(radiusCoordinates[1])]
        ).then(goog.bind(function(data) {
          if (data[0].data['dhm'] >= 0 && data[1].data['dhm'] >= 0) {
            var el = evt.target.getTooltipElement();
            var elevationOffset = data[1].data['dhm'] - data[0].data['dhm'];
            el.innerHTML += '
 Δh : ' +
                parseInt(elevationOffset / 100, 0) + 'm';
          }
        }, this));
      }, this));

  goog.events.listen(measureProfile, ngeo.MeasureEventType.MEASUREEND,
      /**
       * @param {ngeo.MeasureEvent} evt Measure event.
       */
      function(evt) {
        var geom = /** @type {ol.geom.Geometry} */ (evt.feature.getGeometry());
        var encOpt = {
          dataProjection: 'EPSG:2169',
          featureProjection: this['map'].getView().getProjection()
        };
        var req = $.param({
          'geom': new ol.format.GeoJSON().writeGeometry(geom, encOpt),
          'nbPoints': 100,
          'layers': 'dhm'
        });
        var config = {
          headers: {'Content-Type': 'application/x-www-form-urlencoded'}
        };
        $http.post(this.profilejsonUrl_, req, config).then(
            goog.bind(function(resp) {
              this['profileData'] = resp.data['profile'];
            }, this));
      }, undefined, this);

  goog.events.listen(measureProfile, ol.Object.getChangeEventType('active'),
      /**
       * @param {ol.ObjectEvent} evt Change active event.
       */
      function(evt) {
        if (!measureProfile.getActive()) {
          this['profileData'] = undefined;
          $scope.$applyAsync();
        }
      }, undefined, this);

  // Watch the "active" property, and disable the measure interactions
  // when "active" gets set to false.
  $scope.$watch(goog.bind(function() {
    return this['active'];
  }, this), goog.bind(function(newVal) {
    if (newVal === false) {
      this['measureLength'].setActive(false);
      this['measureArea'].setActive(false);
      this['measureAzimut'].setActive(false);
      this['measureProfile'].setActive(false);
    }
  }, this));
};

/**
 * @param {ol.Coordinate} coordinates
 * @return {angular.$q.Promise}
 * @private
 */
app.MeasureController.prototype.getElevation_ = function(coordinates) {
  var eastnorth =
      /** @type {ol.Coordinate} */ (ol.proj.transform(
      coordinates,
      this.map_.getView().getProjection(),
      'EPSG:2169'));

  return this.$http_.get(this.elevationServiceUrl_, {
    params: {'lon': eastnorth[0], 'lat': eastnorth[1]}
  });
};

app.module.controller('AppMeasureController', app.MeasureController);
<commit_msg>Measure help message are now translatable<commit_after>/**
 * @fileoverview This file provides a measure directive. This directive is used
 * to create a measure panel in the page.
 *
 * Example:
 *
 * <app-measure app-measure-map="::mainCtrl.map"></app-measure>
 *
 * Note the use of the one-time binding operator (::) in the map expression.
 * One-time binding is used because we know the map is not going to change
 * during the lifetime of the application.
 */
goog.provide('app.measureDirective');

goog.require('app');
goog.require('app.profileDirective');
goog.require('goog.dom');
goog.require('ngeo.DecorateInteraction');
goog.require('ngeo.btngroupDirective');
goog.require('ngeo.interaction.MeasureArea');
goog.require('ngeo.interaction.MeasureAzimut');
goog.require('ngeo.interaction.MeasureLength');
goog.require('ol.Object');
goog.require('ol.format.GeoJSON');
goog.require('ol.source.Vector');
goog.require('ol.style.Circle');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');

/**
 * @param {angular.$compile} $compile The compile provider.
 * @param {string} appMeasureTemplateUrl Url to measure template
 * @return {angular.Directive} The Directive Definition Object.
 * @ngInject
 */
app.measureDirective = function($compile, appMeasureTemplateUrl) {
  return {
    restrict: 'E',
    scope: {
      'map': '=appMeasureMap',
      'active': '=appMeasureActive'
    },
    controller: 'AppMeasureController',
    controllerAs: 'ctrl',
    bindToController: true,
    templateUrl: appMeasureTemplateUrl
  };
};

app.module.directive('appMeasure', app.measureDirective);

/**
 * @param {angular.Scope} $scope Scope.
 * @param {angular.$q} $q
 * @param {angular.$http} $http Angular http service.
 * @param {angular.$compile} $compile The compile provider.
 * @param {gettext} gettext Gettext service.
 * @param {ngeo.DecorateInteraction} ngeoDecorateInteraction Decorate
 *     interaction service.
 * @param {string} elevationServiceUrl the url of the service
 * @param {string} profilejsonUrl The URL to the profile webservice.
 * @constructor
 * @export
 * @ngInject
 */
app.MeasureController = function($scope, $q, $http, $compile, gettext,
    ngeoDecorateInteraction, elevationServiceUrl, profilejsonUrl) {

  /**
   * @type {angular.$http}
   * @private
   */
  this.$http_ = $http;

  /**
   * @type {string}
   * @private
   */
  this.elevationServiceUrl_ = elevationServiceUrl;

  /**
   * @type {string}
   * @private
   */
  this.profilejsonUrl_ = profilejsonUrl;

  var sketchStyle = new ol.style.Style({
    fill: new ol.style.Fill({
      color: 'rgba(255, 255, 255, 0.4)'
    }),
    stroke: new ol.style.Stroke({
      color: 'rgba(0, 0, 0, 0.5)',
      lineDash: [10, 10],
      width: 2
    }),
    image: new ol.style.Circle({
      radius: 5,
      stroke: new ol.style.Stroke({
        color: 'rgba(0, 0, 0, 0.7)'
      }),
      fill: new ol.style.Fill({
        color: 'rgba(255, 255, 255, 0.4)'
      })
    })
  });

  var style = new ol.style.Style({
    fill: new ol.style.Fill({
      color: 'rgba(255, 204, 51, 0.3)'
    }),
    stroke: new ol.style.Stroke({
      color: 'rgba(255, 204, 51, 1)',
      width: 2
    }),
    image: new ol.style.Circle({
      radius: 7,
      fill: new ol.style.Fill({
        color: 'rgba(255, 204, 51, 0.3)'
      })
    })
  });

  /**
   * @type {ol.Map}
   * @private
   */
  this.map_ = this['map'];

  var helpMsg = gettext('Click to start drawing profile');
  var contMsg = gettext('Click to continue drawing the line
' +
      'Double-click or click last point to finish');
  var measureProfile = new ngeo.interaction.MeasureLength({
    startMsg: $compile('
' + helpMsg + '
')($scope)[0],
    continueMsg: $compile('
' + contMsg + '
')($scope)[0],
    sketchStyle: sketchStyle
  });

  /**
   * @type {app.interaction.MeasureProfile}
   */
  this['measureProfile'] = measureProfile;
  measureProfile.setActive(false);
  ngeoDecorateInteraction(measureProfile);
  this.map_.addInteraction(measureProfile);

  helpMsg = gettext('Click to start drawing length');
  var measureLength = new ngeo.interaction.MeasureLength({
    startMsg: $compile('
' + helpMsg + '
')($scope)[0],
    continueMsg: $compile('
' + contMsg + '
')($scope)[0],
    sketchStyle: sketchStyle
  });

  /**
   * @type {array<object>}
   */
  this['profileData'] = undefined;

  /**
   * @type {ngeo.interaction.MeasureLength}
   */
  this['measureLength'] = measureLength;

  measureLength.setActive(false);
  ngeoDecorateInteraction(measureLength);
  this.map_.addInteraction(measureLength);

  helpMsg = gettext('Click to start drawing area');
  contMsg = gettext('Click to continue drawing the polygon
' +
      'Double-click or click last point to finish');
  var measureArea = new ngeo.interaction.MeasureArea({
    startMsg: $compile('
' + helpMsg + '
')($scope)[0],
    continueMsg: $compile('
' + contMsg + '
')($scope)[0],
    sketchStyle: sketchStyle,
    style: style
  });

  /**
   * @type {ngeo.interaction.MeasureArea}
   */
  this['measureArea'] = measureArea;

  measureArea.setActive(false);
  ngeoDecorateInteraction(measureArea);
  this.map_.addInteraction(measureArea);

  helpMsg = gettext('Click to start drawing azimut');
  contMsg = gettext('Click to finish');
  /** @type {ngeo.interaction.MeasureAzimut} */
  var measureAzimut = new ngeo.interaction.MeasureAzimut({
    startMsg: $compile('
' + helpMsg + '
')($scope)[0],
    continueMsg: $compile('
' + contMsg + '
')($scope)[0],
    sketchStyle: sketchStyle,
    style: style
  });

  /**
   * @type {ngeo.interaction.MeasureAzimut}
   */
  this['measureAzimut'] = measureAzimut;

  measureAzimut.setActive(false);
  ngeoDecorateInteraction(measureAzimut);
  this.map_.addInteraction(measureAzimut);

  goog.events.listen(measureAzimut, ngeo.MeasureEventType.MEASUREEND,
      goog.bind(function(evt) {
        var geometryCollection =
            /** @type {ol.geom.GeometryCollection} */
            (evt.feature.getGeometry());

        var radius =
            /** @type {ol.geom.LineString} */
            (geometryCollection.getGeometries()[0]);
        var radiusCoordinates = radius.getCoordinates();
        $q.all([this.getElevation_(radiusCoordinates[0]),
              this.getElevation_(radiusCoordinates[1])]
        ).then(goog.bind(function(data) {
          if (data[0].data['dhm'] >= 0 && data[1].data['dhm'] >= 0) {
            var el = evt.target.getTooltipElement();
            var elevationOffset = data[1].data['dhm'] - data[0].data['dhm'];
            el.innerHTML += '
 Δh : ' +
                parseInt(elevationOffset / 100, 0) + 'm';
          }
        }, this));
      }, this));

  goog.events.listen(measureProfile, ngeo.MeasureEventType.MEASUREEND,
      /**
       * @param {ngeo.MeasureEvent} evt Measure event.
       */
      function(evt) {
        var geom = /** @type {ol.geom.Geometry} */ (evt.feature.getGeometry());
        var encOpt = {
          dataProjection: 'EPSG:2169',
          featureProjection: this['map'].getView().getProjection()
        };
        var req = $.param({
          'geom': new ol.format.GeoJSON().writeGeometry(geom, encOpt),
          'nbPoints': 100,
          'layers': 'dhm'
        });
        var config = {
          headers: {'Content-Type': 'application/x-www-form-urlencoded'}
        };
        $http.post(this.profilejsonUrl_, req, config).then(
            goog.bind(function(resp) {
              this['profileData'] = resp.data['profile'];
            }, this));
      }, undefined, this);

  goog.events.listen(measureProfile, ol.Object.getChangeEventType('active'),
      /**
       * @param {ol.ObjectEvent} evt Change active event.
       */
      function(evt) {
        if (!measureProfile.getActive()) {
          this['profileData'] = undefined;
          $scope.$applyAsync();
        }
      }, undefined, this);

  // Watch the "active" property, and disable the measure interactions
  // when "active" gets set to false.
  $scope.$watch(goog.bind(function() {
    return this['active'];
  }, this), goog.bind(function(newVal) {
    if (newVal === false) {
      this['measureLength'].setActive(false);
      this['measureArea'].setActive(false);
      this['measureAzimut'].setActive(false);
      this['measureProfile'].setActive(false);
    }
  }, this));
};

/**
 * @param {ol.Coordinate} coordinates
 * @return {angular.$q.Promise}
 * @private
 */
app.MeasureController.prototype.getElevation_ = function(coordinates) {
  var eastnorth =
      /** @type {ol.Coordinate} */ (ol.proj.transform(
      coordinates,
      this.map_.getView().getProjection(),
      'EPSG:2169'));

  return this.$http_.get(this.elevationServiceUrl_, {
    params: {'lon': eastnorth[0], 'lat': eastnorth[1]}
  });
};

app.module.controller('AppMeasureController', app.MeasureController);
<|endoftext|>


<commit_before>if (!Object.create || !Object.defineProperty || !Object.defineProperties) alert("Example will fail because your browser does not support ECMAScript 5. Try with another browser!");
var __filename = "" + window.location;

function require(str) {
    if (str == "streamline/lib/util/flows") return StreamlineFlows;
    else if (str == "streamline/lib/callbacks/runtime") return StreamlineRuntime;
    else if (str == "streamline/lib/globals") return StreamlineRuntime.globals;
    else alert("cannot require " + str)
}<commit_msg>fixed test stubs<commit_after>if (!Object.create || !Object.defineProperty || !Object.defineProperties) alert("Example will fail because your browser does not support ECMAScript 5. Try with another browser!");
var __filename = "" + window.location;

function require(str) {
    if (str == "streamline/lib/util/flows") return StreamlineFlows;
    else if (str == "streamline/lib/callbacks/runtime") return StreamlineRuntime;
    else if (str == "streamline/lib/callbacks/transform") return Streamline;
    else if (str == "streamline/lib/globals") return StreamlineRuntime.globals;
    else alert("cannot require " + str)
}<|endoftext|>


<commit_before>/*! @license ©2015 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University */

/** An TimegateController responds to timegate requests */

var Controller = require('./Controller'),
    fs = require('fs'),
    path = require('path'),
    mime = require('mime'),
    _ = require('lodash'),
    url = require('url'),
    Util = require('../Util');

// Creates a new TimegateController
function TimegateController(options) {
  if (!(this instanceof TimegateController))
    return new TimegateController(options);
  options = options || {};
  Controller.call(this, options);

  //TODO: check wether datasources for timegates exist
  //var datasources = options.datasources || {};

  // Settings for timegate
  var timegates = options.timegates || {};
  this._timemaps = _.mapValues(timegates.mementos, function (mementos) {
    return _.assign(mementos, {
      timemap: sortTimemap(_.map(_.pick(options.datasources, mementos.versions),
        function (datasource, key) {
          return _.assign(datasource.memento, {
            dataSource: key,
            interval: (datasource.memento.interval || [0, 0]).map(toDate),
          });
        }))
      });
  });
  this._routers = options.routers || [];

  // Set up path matching
  this._timegatePath = timegates.baseUrl  || '/timegate/',
  this._matcher = new RegExp('^' + Util.toRegExp(this._timegatePath) + '((.+?)\\?|(.+))');
}
Controller.extend(TimegateController);

// Tries to serve the requested Timegate
TimegateController.prototype._handleRequest = function (request, response, next) {
  var self = this, timegateMatch = this._matcher && this._matcher.exec(request.url),
  datasource = timegateMatch && (timegateMatch[2] || timegateMatch[1]);
  if (datasource && this._timemaps[datasource]) {
    // retrieve Accept-Datetime & construct memento link
    var acceptDatetime = toDate(request.headers['accept-datetime']), // If no datetime is present, return most recent one
        memento = this._getClosestMemento(this._timemaps[datasource].timemap, acceptDatetime);

    // memento invalid, go to next
    if (!memento) next();

    // Construct memento and Original url
    var mementoUrl = url.format(_.assign(request.parsedUrl, { pathname: memento.dataSource})),
        parsedOriginalUrl = this._timemaps[datasource].originalBaseURL ? // If originalBaseURL is present, the original is external
                            _.assign(url.parse(this._timemaps[datasource].originalBaseURL), { query: request.parsedUrl.query }) :
                            _.defaults({ pathname: datasource }, request.parsedUrl),
        originalUrl = url.format(parsedOriginalUrl);

    response.writeHead(303, {
      'Location': mementoUrl,
      'Vary': 'Accept, Accept-Datetime',
      'Link': '<' + originalUrl + '>;rel="original", <' + mementoUrl + '>;rel="memento";datetime="' + memento.interval[0].toUTCString() + '"'
    });
    response.end();
  }
  else
    next();
};

/*
   * @param timemap: [{"dataSource": "data source name", "interval": [start, end]}, ...]
                   the start, end values can either be Date objects, or ISO 8601 string.
   * @param accept_datetime: the requested datetime value as Date object, or ISO 8601 string.
   * @param sorted: bool. If not sorted, the timemap will be sorted using the start time in the interval.

   * eg:
    var timemap = [
      {"dataSource": "dbpedia_2012", "interval": ["2011-10-20T12:22:24Z", new Date("2012-10-19T12:22:24Z")]},
      {"dataSource": "dbpedia_2015", "interval": ["2014-10-20T12:22:24Z", ""]},
      {"dataSource": "dbpedia_2013", "interval": [new Date("2012-10-20T12:22:24Z"), new Date("2013-10-19T12:22:24Z")]},
      {"dataSource": "dbpedia_2014", "interval": ["2013-10-20T12:22:24Z", "2014-10-19T12:22:24Z"]}
    ];
    get_closest_memento(timemap, "2011-10-20T12:22:24Z", false);
*/
TimegateController.prototype._getClosestMemento = function (timemap, acceptDatetime, unsorted) {
    // NOTE: assuming that the interval is always specified as [start_date, end_date]
    // empty timemap can't give any mementos
    if (timemap.length === 0)
      return null;

    // convert accept datetime to timestamp
    acceptDatetime = toDate(acceptDatetime).getTime();

    // If accept datetime is invalid, exit
    if (isNaN(acceptDatetime)) return null;
    // Sort timemap first if it is not sorted
    if (unsorted) sortTimemap(timemap);

    var firstMemento = timemap[0],
        firstMementoDatetime = firstMemento.interval[0].getTime();

    // if the accept_datetime is less than the first memento, return first memento.
    if (acceptDatetime <= firstMementoDatetime) return firstMemento;

    // return the latest memento if the accept datetime is after it
    var lastMemento = timemap[timemap.length-1];
    var lastMementoDatetime;
    if (!lastMemento.interval[1]) {
        lastMementoDatetime = new Date().getTime();
    } else if (typeof(lastMemento.interval[1]) == "string") {
        lastMementoDatetime = new Date(lastMemento.interval[1]).getTime();
    } else {
        lastMementoDatetime = lastMemento.interval[1].getTime();
    }

    if (acceptDatetime >= lastMementoDatetime) return lastMemento;

    // check if the accept datetime falls within any intervals defined in the data sources.
    for (var i = 0, memento; memento = timemap[i]; i++) {
        var dataSource = memento.dataSource,
            startTime = memento.interval[0].getTime(),
            endTime   = memento.interval[1].getTime();

        if (isNaN(startTime) || isNaN(endTime)) continue;
        if (startTime > acceptDatetime) return timemap[i - 1];
        if (startTime <= acceptDatetime && endTime >= acceptDatetime) return memento;
    }
    return null;
};

// Sort the timemap by interval start date
function sortTimemap(timemap) {
  return timemap.sort(function (a, b) {
    return a.interval[0].getTime() - b.interval[0].getTime();
  });
}

// Convert the value to a date
function toDate(value) {
  return typeof value === "string" ? new Date(value) : (value || new Date());
}

module.exports = TimegateController;
<commit_msg>Fix time gate paths with trailing slash.<commit_after>/*! @license ©2015 Miel Vander Sande - Multimedia Lab / iMinds / Ghent University */

/** An TimegateController responds to timegate requests */

var Controller = require('./Controller'),
    fs = require('fs'),
    path = require('path'),
    mime = require('mime'),
    _ = require('lodash'),
    url = require('url'),
    Util = require('../Util');

// Creates a new TimegateController
function TimegateController(options) {
  if (!(this instanceof TimegateController))
    return new TimegateController(options);
  options = options || {};
  Controller.call(this, options);

  //TODO: check wether datasources for timegates exist
  //var datasources = options.datasources || {};

  // Settings for timegate
  var timegates = options.timegates || {};
  this._timemaps = _.mapValues(timegates.mementos, function (mementos) {
    return _.assign(mementos, {
      timemap: sortTimemap(_.map(_.pick(options.datasources, mementos.versions),
        function (datasource, key) {
          return _.assign(datasource.memento, {
            dataSource: key,
            interval: (datasource.memento.interval || [0, 0]).map(toDate),
          });
        }))
      });
  });
  this._routers = options.routers || [];

  // Set up path matching
  this._timegatePath = timegates.baseUrl || '/timegate/',
  this._matcher = new RegExp('^' + Util.toRegExp(this._timegatePath) + '(.+?)\/?$');
}
Controller.extend(TimegateController);

// Tries to serve the requested Timegate
TimegateController.prototype._handleRequest = function (request, response, next) {
  var self = this, timegateMatch = this._matcher && this._matcher.exec(request.url),
      datasource = timegateMatch && timegateMatch[1];
  if (datasource && this._timemaps[datasource]) {
    // retrieve Accept-Datetime & construct memento link
    var acceptDatetime = toDate(request.headers['accept-datetime']), // If no datetime is present, return most recent one
        memento = this._getClosestMemento(this._timemaps[datasource].timemap, acceptDatetime);

    // memento invalid, go to next
    if (!memento) next();

    // Construct memento and Original url
    var mementoUrl = url.format(_.assign(request.parsedUrl, { pathname: memento.dataSource})),
        parsedOriginalUrl = this._timemaps[datasource].originalBaseURL ? // If originalBaseURL is present, the original is external
                            _.assign(url.parse(this._timemaps[datasource].originalBaseURL), { query: request.parsedUrl.query }) :
                            _.defaults({ pathname: datasource }, request.parsedUrl),
        originalUrl = url.format(parsedOriginalUrl);

    response.writeHead(303, {
      'Location': mementoUrl,
      'Vary': 'Accept, Accept-Datetime',
      'Link': '<' + originalUrl + '>;rel="original", <' + mementoUrl + '>;rel="memento";datetime="' + memento.interval[0].toUTCString() + '"'
    });
    response.end();
  }
  else
    next();
};

/*
   * @param timemap: [{"dataSource": "data source name", "interval": [start, end]}, ...]
                   the start, end values can either be Date objects, or ISO 8601 string.
   * @param accept_datetime: the requested datetime value as Date object, or ISO 8601 string.
   * @param sorted: bool. If not sorted, the timemap will be sorted using the start time in the interval.

   * eg:
    var timemap = [
      {"dataSource": "dbpedia_2012", "interval": ["2011-10-20T12:22:24Z", new Date("2012-10-19T12:22:24Z")]},
      {"dataSource": "dbpedia_2015", "interval": ["2014-10-20T12:22:24Z", ""]},
      {"dataSource": "dbpedia_2013", "interval": [new Date("2012-10-20T12:22:24Z"), new Date("2013-10-19T12:22:24Z")]},
      {"dataSource": "dbpedia_2014", "interval": ["2013-10-20T12:22:24Z", "2014-10-19T12:22:24Z"]}
    ];
    get_closest_memento(timemap, "2011-10-20T12:22:24Z", false);
*/
TimegateController.prototype._getClosestMemento = function (timemap, acceptDatetime, unsorted) {
    // NOTE: assuming that the interval is always specified as [start_date, end_date]
    // empty timemap can't give any mementos
    if (timemap.length === 0)
      return null;

    // convert accept datetime to timestamp
    acceptDatetime = toDate(acceptDatetime).getTime();

    // If accept datetime is invalid, exit
    if (isNaN(acceptDatetime)) return null;
    // Sort timemap first if it is not sorted
    if (unsorted) sortTimemap(timemap);

    var firstMemento = timemap[0],
        firstMementoDatetime = firstMemento.interval[0].getTime();

    // if the accept_datetime is less than the first memento, return first memento.
    if (acceptDatetime <= firstMementoDatetime) return firstMemento;

    // return the latest memento if the accept datetime is after it
    var lastMemento = timemap[timemap.length-1];
    var lastMementoDatetime;
    if (!lastMemento.interval[1]) {
        lastMementoDatetime = new Date().getTime();
    } else if (typeof(lastMemento.interval[1]) == "string") {
        lastMementoDatetime = new Date(lastMemento.interval[1]).getTime();
    } else {
        lastMementoDatetime = lastMemento.interval[1].getTime();
    }

    if (acceptDatetime >= lastMementoDatetime) return lastMemento;

    // check if the accept datetime falls within any intervals defined in the data sources.
    for (var i = 0, memento; memento = timemap[i]; i++) {
        var dataSource = memento.dataSource,
            startTime = memento.interval[0].getTime(),
            endTime   = memento.interval[1].getTime();

        if (isNaN(startTime) || isNaN(endTime)) continue;
        if (startTime > acceptDatetime) return timemap[i - 1];
        if (startTime <= acceptDatetime && endTime >= acceptDatetime) return memento;
    }
    return null;
};

// Sort the timemap by interval start date
function sortTimemap(timemap) {
  return timemap.sort(function (a, b) {
    return a.interval[0].getTime() - b.interval[0].getTime();
  });
}

// Convert the value to a date
function toDate(value) {
  return typeof value === "string" ? new Date(value) : (value || new Date());
}

module.exports = TimegateController;
<|endoftext|>


<commit_before>define('lyria/serialize', ['jquery'], function($) {

  // TODO: Use JSON.parse + reviver instead
  /**
   *
   * @param {Object} anyObject
   *
   * @returns {String}
   */
  var serialize = function(anyObject) {
    if (( typeof anyObject !== 'object') || ( anyObject instanceof $)) {
      return;
    }

    var str = '{';

    for (var p in anyObject) {
      if (anyObject.hasOwnProperty(p)) {
        if (anyObject[p] instanceof $) {
          continue;
        }

        var objKeys = Object.keys(anyObject);
        var commaStr = (objKeys.indexOf(p) === (objKeys.length - 1)) ? '' : ',';

        switch (typeof anyObject[p]) {
          case 'object':
            {
              str += p + ': ' + serialize(anyObject[p]) + commaStr + '\n';
            }
            break;
          case 'string':
            {
              str += p + ': "' + anyObject[p] + '"' + commaStr + '\n';
            }
            break;
          default:
            {
              str += p + ': ' + anyObject[p] + commaStr + '\n';
            }
            break;
        }
      }
    }
    str += '}';

    return str;
  };

  return serialize;
});
<commit_msg>Fixes faulty serialize function<commit_after>define('lyria/serialize', ['jquery'], function($) {

  // TODO: Use JSON.parse + reviver instead
  /**
   *
   * @param {Object} anyObject
   *
   * @returns {String}
   */
  var serialize = function(anyObject) {
    if (( typeof anyObject !== 'object') || ( anyObject instanceof $)) {
      return;
    }

    return JSON.stringify(anyObject, function(key, value) {
      if (value instanceof $) {
        return null;
      }

      if (typeof value === 'function') {
        value = value.toString();
      }

      return value;
    });
  };

  return serialize;
});
<|endoftext|>


<commit_before>var mongoose   = require('mongoose');
var config     = require('config');
var semver     = require('semver');

// configure mongodb
mongoose.connect(config.mongodb.connectionString || 'mongodb://' + config.mongodb.user + ':' + config.mongodb.password + '@' + config.mongodb.server +'/' + config.mongodb.database);
mongoose.connection.on('error', function (err) {
  console.error('MongoDB error: ' + err.message);
  console.error('Make sure a mongoDB server is running and accessible by this application');
  process.exit(1);
});
mongoose.connection.on('open', function (err) {
  mongoose.connection.db.admin().serverStatus(function(err, data) {
    if (err) {
      if (err.name === "MongoError" && (err.errmsg === 'need to login' || err.errmsg === 'unauthorized') && !config.mongodb.connectionString) {
        console.log('Forcing MongoDB authentication');
        mongoose.connection.db.authenticate(config.mongodb.user, config.mongodb.password, function(err) {
          if (!err) return;
          console.error(err);
          process.exit(1);
        });
        return;
      } else {
        console.error(err);
        process.exit(1);
      }
    }
    if (!semver.satisfies(data.version, '>=2.1.0')) {
      console.error('Error: Uptime requires MongoDB v2.1 minimum. The current MongoDB server uses only '+ data.version);
      process.exit(1);
    }
  });
});

module.exports = mongoose;
<commit_msg>Enable us to connect to mongo even though we dont specify user and pwd.<commit_after>var mongoose   = require('mongoose');
var config     = require('config');
var semver     = require('semver');
var util       = require('util')

var connectionString = config.mongodb.connectionString
if (!connectionString) {
	if(config.mongodb.user && config.mongodb.password) {
		connectionString = util.format('mongodb://%s:%s@%s/%s', config.mongodb.user, config.mongodb.password, config.mongodb.server, config.mongodb.database)
	} else {
		connectionString = util.format('mongodb://%s/%s', config.mongodb.server, config.mongodb.database)
	}
}

mongoose.connect(connectionString)
mongoose.connection.on('error', function (err) {
  console.error('MongoDB error: ' + err.message);
  console.error('Make sure a mongoDB server is running and accessible by this application');
  process.exit(1);
});

mongoose.connection.on('open', function (err) {
  mongoose.connection.db.admin().serverStatus(function(err, data) {
    if (err) {
      if (err.name === "MongoError" && (err.errmsg === 'need to login' || err.errmsg === 'unauthorized') && !config.mongodb.connectionString) {
        console.log('Forcing MongoDB authentication');
        mongoose.connection.db.authenticate(config.mongodb.user, config.mongodb.password, function(err) {
          if (!err) return;
          console.error(err);
          process.exit(1);
        });
        return;
      } else {
        console.error(err);
        process.exit(1);
      }
    }
    if (!semver.satisfies(data.version, '>=2.1.0')) {
      console.error('Error: Uptime requires MongoDB v2.1 minimum. The current MongoDB server uses only '+ data.version);
      process.exit(1);
    }
  });
});

module.exports = mongoose;
<|endoftext|>


<commit_before>var format = require('util').format;

var steps = {
  start: {
    state: 'onboarding.start',
  },
  seeds: {
    state: 'onboarding.seeds',
  },
  community: {
    state: 'community.seeds'
  },
  profile: {
    state: 'profile'
  }
};

var stepOrder = ['start', 'seeds', 'community', 'profile', 'done'];

var factory = function($timeout, $resource, $rootScope, $state, $analytics, Overlay) {

  var OnboardingResource = $resource('/noo/user/:userId/onboarding', {userId: '@userId'});

  var Onboarding = function(user) {
    // this is used in templates
    this.community = user.memberships[0].community;

    // for internal use only
    this._user = user;
    this._status = user.onboarding.status;

    // TESTING
    var params = require('querystring').parse(location.search.replace(/^\?/, ''));
    if (params.obs) {
      _.merge(this._status, {step: params.obs});
      this.allowBack = true;
    }

    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
      $rootScope.$emit('announcer:hide');

      if (this.currentStep() === 'community' && toState.name === 'profile' && toParams.id === this._user.id) {
        if (this._announcerDelay) {
          $timeout.cancel(this._announcerDelay);
        }
        this._setStep('profile', true);
      }

    }.bind(this));

  };

  _.extend(Onboarding.prototype, {
    isComplete: function() {
      return this._status.step === 'done';
    },
    canSkipSeedForm: function() {
      return !!this._status.can_skip_seed_form;
    },
    markSeedCreated: function(type) {
      this._track('Add Seed', {type: type});
      this._status.seed_created = true;
      this.goNext();
    },
    isSeedCreated: function() {
      return !!this._status.seed_created;
    },
    currentStep: function() {
      if (!_.contains(stepOrder, this._status.step))
        return 'community';

      return this._status.step;
    },
    showOverlay: function(name) {
      var scope = {
        overlay: 'onboarding.' + name,
        onboarding: this
      };
      if (name === 'community') {
        scope.conversationStep = 1;
      }
      Overlay.show(scope);
    },
    continue: function(data) {
      switch (this.currentStep()) {
        case 'community':

          var cleanup = function(string) {
            if (!string) return;

            return _.chain(string.split(','))
              .map(function(x) { return x.trim(); })
              .reject(function(x) { return x == '' })
              .uniq().value();
          }, input = {
            skills: cleanup(data.skills),
            organizations: cleanup(data.organizations)
          };

          if (_.isEmpty(input.skills)) {
            delete input.skills;
          } else {
            this._track('Add Skills');
          }

          if (_.isEmpty(input.organizations)) {
            delete input.organizations;
          } else {
            this._track('Add Affiliations');
          }

          if (!_.isEmpty(input)) this._user.update(input);

          this._announcerDelay = $timeout(function() {
            $rootScope.$emit('announcer:show', {
              text: "When you're ready, click here to visit your profile.",
              onclick: function() {
                self.goNext();
              },
              className: 'point-to-profile'
            });
          }, 3000);
          break;

        case 'profile':
          this._status.step = 'done';
          OnboardingResource.save({userId: this._user.id, step: 'done'});
          break;
      }
    },
    resume: function() {
      // TODO maybe only do this if within the first few steps?
      // i.e. once we get to community step, we can allow open-ended
      // exploration and only continue onboarding through non-modal prompts
      if (this.currentStep() === 'done' || $state.$current.name === steps[this.currentStep()].state)
        return;

      this._track('Resume', {from: $state.$current.name, to: steps[this.currentStep()].state});
      this._goDelta(0);
    },
    goNext: function() {
      this._goDelta(1);
    },
    goBack: function() {
      this._goDelta(-1);
    },
    jump: function(name) {
      this._go(name, true);
    },
    _goDelta: function(delta) {
      var next = stepOrder[_.indexOf(stepOrder, this.currentStep()) + delta];
      this._go(next, delta != 0);
    },
    _go: function(name, update) {
      this._setStep(name, update);

      var params;
      // FIXME code smell
      if (_.include(['newSeed', 'community'], name)) {
        params = {community: this.community.slug};
      } else if (_.include(['profile'], name)) {
        params = {id: this._user.id};
      } else {
        params = {};
      }
      return $state.go(steps[name].state, params);
    },
    _setStep: function(name, update) {
      this._track('Step: ' + name);
      this._status.step = name;

      if (update) {
        OnboardingResource.save({userId: this._user.id, step: name});
      }
    },
    _track: function(name, params) {
      $analytics.eventTrack('Onboarding: ' + name, _.merge({
        user_id: this._user.id,
        new_user: this._status.new_user
      }, params));
    }
  });

  return Onboarding;
};

module.exports = function(angularModule) {
  angularModule.factory('Onboarding', factory);
};<commit_msg>fix regression in onboarding<commit_after>var format = require('util').format;

var steps = {
  start: {
    state: 'onboarding.start',
  },
  seeds: {
    state: 'onboarding.seeds',
  },
  community: {
    state: 'community.seeds'
  },
  profile: {
    state: 'profile'
  }
};

var stepOrder = ['start', 'seeds', 'community', 'profile', 'done'];

var factory = function($timeout, $resource, $rootScope, $state, $analytics, Overlay) {

  var OnboardingResource = $resource('/noo/user/:userId/onboarding', {userId: '@userId'});

  var Onboarding = function(user) {
    // this is used in templates
    this.community = user.memberships[0].community;

    // for internal use only
    this._user = user;
    this._status = user.onboarding.status;

    // TESTING
    var params = require('querystring').parse(location.search.replace(/^\?/, ''));
    if (params.obs) {
      _.merge(this._status, {step: params.obs});
      this.allowBack = true;
    }

    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
      $rootScope.$emit('announcer:hide');

      if (this.currentStep() === 'community' && toState.name === 'profile' && toParams.id === this._user.id) {
        if (this._announcerDelay) {
          $timeout.cancel(this._announcerDelay);
        }
        this._setStep('profile', true);
      }

    }.bind(this));

  };

  _.extend(Onboarding.prototype, {
    isComplete: function() {
      return this._status.step === 'done';
    },
    canSkipSeedForm: function() {
      return !!this._status.can_skip_seed_form;
    },
    markSeedCreated: function(type) {
      this._track('Add Seed', {type: type});
      this._status.seed_created = true;
      this.goNext();
    },
    isSeedCreated: function() {
      return !!this._status.seed_created;
    },
    currentStep: function() {
      if (!_.contains(stepOrder, this._status.step))
        return 'community';

      return this._status.step;
    },
    showOverlay: function(name) {
      var scope = {
        overlay: 'onboarding.' + name,
        onboarding: this
      };
      if (name === 'community') {
        scope.conversationStep = 1;
      }
      Overlay.show(scope);
    },
    continue: function(data) {
      var self = this;

      switch (this.currentStep()) {
        case 'community':

          var cleanup = function(string) {
            if (!string) return;

            return _.chain(string.split(','))
              .map(function(x) { return x.trim(); })
              .reject(function(x) { return x == '' })
              .uniq().value();
          }, input = {
            skills: cleanup(data.skills),
            organizations: cleanup(data.organizations)
          };

          if (_.isEmpty(input.skills)) {
            delete input.skills;
          } else {
            this._track('Add Skills');
          }

          if (_.isEmpty(input.organizations)) {
            delete input.organizations;
          } else {
            this._track('Add Affiliations');
          }

          if (!_.isEmpty(input)) this._user.update(input);

          this._announcerDelay = $timeout(function() {
            $rootScope.$emit('announcer:show', {
              text: "When you're ready, click here to visit your profile.",
              onclick: function() {
                self.goNext();
              },
              className: 'point-to-profile'
            });
          }, 3000);
          break;

        case 'profile':
          this._status.step = 'done';
          OnboardingResource.save({userId: this._user.id, step: 'done'});
          break;
      }
    },
    resume: function() {
      // TODO maybe only do this if within the first few steps?
      // i.e. once we get to community step, we can allow open-ended
      // exploration and only continue onboarding through non-modal prompts
      if (this.currentStep() === 'done' || $state.$current.name === steps[this.currentStep()].state)
        return;

      this._track('Resume', {from: $state.$current.name, to: steps[this.currentStep()].state});
      this._goDelta(0);
    },
    goNext: function() {
      this._goDelta(1);
    },
    goBack: function() {
      this._goDelta(-1);
    },
    jump: function(name) {
      this._go(name, true);
    },
    _goDelta: function(delta) {
      var next = stepOrder[_.indexOf(stepOrder, this.currentStep()) + delta];
      this._go(next, delta != 0);
    },
    _go: function(name, update) {
      this._setStep(name, update);

      var params;
      // FIXME code smell
      if (_.include(['newSeed', 'community'], name)) {
        params = {community: this.community.slug};
      } else if (_.include(['profile'], name)) {
        params = {id: this._user.id};
      } else {
        params = {};
      }
      return $state.go(steps[name].state, params);
    },
    _setStep: function(name, update) {
      this._track('Step: ' + name);
      this._status.step = name;

      if (update) {
        OnboardingResource.save({userId: this._user.id, step: name});
      }
    },
    _track: function(name, params) {
      $analytics.eventTrack('Onboarding: ' + name, _.merge({
        user_id: this._user.id,
        new_user: this._status.new_user
      }, params));
    }
  });

  return Onboarding;
};

module.exports = function(angularModule) {
  angularModule.factory('Onboarding', factory);
};<|endoftext|>


<commit_before>import { DefaultDOMElement as DOM, prettyPrintXML } from '../dom'
import { camelCase } from '../util'
import DocumentSchema from './DocumentSchema'
import NextDocument from './NextDocument'
import _createValidator from './_createValidator'
import _createDefinition from './_createDefinition'
import _createXmlConverterFactory from './_createXmlConverterFactory'

export default class NextDocumentSchema {
  constructor (version, rootType, issuer, nodes, actions) {
    this.name = rootType[0].toUpperCase() + camelCase(rootType).slice(1)
    this.rootType = rootType
    this.version = version
    this.issuer = issuer
    this.nodes = nodes
    this.publicId = this._getPublicId(version)
    this.dtd = this._getDtd(version)

    // keep the schema definition here so that we can use it later on for XML validation and im-/export
    this._actions = actions

    // generating validators, importers and exporters lazily
    this._definitions = new Map()
    this._validators = new Map()

    const _definition = this._getDefinition(this.version)

    this._xmlConverterFactory = _createXmlConverterFactory(rootType, _definition)

    // legacy
    this._documentSchema = new DocumentSchema({
      // TODO: we should allow to override NextDocument
      // e.g. to provide a getTitle() implementation, etc.
      DocumentClass: NextDocument,
      nodes: Array.from(nodes.values()),
      definition: _definition
    })
  }

  createDocumentInstance () {
    return new NextDocument(this._documentSchema, this)
  }

  importDocumentFromXml (doc, xmlStr, context) {
    const xmlDom = DOM.parseXML(xmlStr)
    const xmlSchemaId = xmlDom.getDoctype().publicId
    // identify version
    let version
    for (let v = this.version; v > 0; v--) {
      if (xmlSchemaId === this._getPublicId(v)) {
        version = v
        break
      }
    }
    if (!version) throw new Error(`Unknown xml schema ${xmlSchemaId}`)
    const validator = this._getValidator(version)
    const result = validator.validate(xmlDom)
    if (!result.ok) {
      console.error(result.errors)
      throw new Error('Invalid xml.')
    }
    if (version !== this.version) {
      console.error('TODO: implement migrations')
    }
    const importer = this._xmlConverterFactory.createImporter(doc, context)
    importer.importIntoDocument(xmlDom)
  }

  exportDocumentToXml (doc, context = {}, options = {}) {
    const exporter = this._xmlConverterFactory.createExporter(context)
    const dom = exporter.convertNode(doc.root)
    let xmlStr
    if (options.prettyPrint) {
      xmlStr = prettyPrintXML(dom)
    } else {
      xmlStr = dom.serialize()
    }
    return [
      '<?xml version="1.0" encoding="UTF-8"?>',
      `<!DOCTYPE ${this.rootType} PUBLIC "${this.publicId}" "${this.dtd}">`,
      xmlStr
    ].join('\n')
  }

  _getPublicId (version) {
    // TODO: until we introduce minor versions we just use '0' for minor
    return `-//${this.issuer.toUpperCase()}//DTD ${this.name} v${version}.0//EN`
  }

  _getDtd (version) {
    return `${this.name}-${version}.dtd`
  }

  _getValidator (version) {
    if (!this._validators.has(version)) {
      this._validators.set(version, _createValidator(this.rootType, this._getDefinition(version)))
    }
    return this._validators.get(version)
  }

  _getDefinition (version) {
    if (!this._definitions.has(version)) {
      this._definitions.set(version, _createDefinition(version, this._actions))
    }
    return this._definitions.get(version)
  }
}
<commit_msg>Expose more of the existing doc schema API.<commit_after>import { DefaultDOMElement as DOM, prettyPrintXML } from '../dom'
import { camelCase } from '../util'
import DocumentSchema from './DocumentSchema'
import NextDocument from './NextDocument'
import _createValidator from './_createValidator'
import _createDefinition from './_createDefinition'
import _createXmlConverterFactory from './_createXmlConverterFactory'

export default class NextDocumentSchema {
  constructor (version, rootType, issuer, nodes, actions) {
    this.name = rootType[0].toUpperCase() + camelCase(rootType).slice(1)
    this.rootType = rootType
    this.version = version
    this.issuer = issuer
    this.nodes = nodes
    this.publicId = this._getPublicId(version)
    this.dtd = this._getDtd(version)

    // keep the schema definition here so that we can use it later on for XML validation and im-/export
    this._actions = actions

    // generating validators, importers and exporters lazily
    this._definitions = new Map()
    this._validators = new Map()

    const _definition = this._getDefinition(this.version)

    this._xmlConverterFactory = _createXmlConverterFactory(rootType, _definition)

    // legacy
    this._documentSchema = new DocumentSchema({
      // TODO: we should allow to override NextDocument
      // e.g. to provide a getTitle() implementation, etc.
      DocumentClass: NextDocument,
      nodes: Array.from(nodes.values()),
      definition: _definition
    })
  }

  createDocumentInstance () {
    return new NextDocument(this._documentSchema, this)
  }

  importDocumentFromXml (doc, xmlStr, context) {
    const xmlDom = DOM.parseXML(xmlStr)
    const xmlSchemaId = xmlDom.getDoctype().publicId
    // identify version
    let version
    for (let v = this.version; v > 0; v--) {
      if (xmlSchemaId === this._getPublicId(v)) {
        version = v
        break
      }
    }
    if (!version) throw new Error(`Unknown xml schema ${xmlSchemaId}`)
    const validator = this._getValidator(version)
    const result = validator.validate(xmlDom)
    if (!result.ok) {
      console.error(result.errors)
      throw new Error('Invalid xml.')
    }
    if (version !== this.version) {
      console.error('TODO: implement migrations')
    }
    const importer = this._xmlConverterFactory.createImporter(doc, context)
    importer.importIntoDocument(xmlDom)
  }

  exportDocumentToXml (doc, context = {}, options = {}) {
    const exporter = this._xmlConverterFactory.createExporter(context)
    const dom = exporter.convertNode(doc.root)
    let xmlStr
    if (options.prettyPrint) {
      xmlStr = prettyPrintXML(dom)
    } else {
      xmlStr = dom.serialize()
    }
    return [
      '<?xml version="1.0" encoding="UTF-8"?>',
      `<!DOCTYPE ${this.rootType} PUBLIC "${this.publicId}" "${this.dtd}">`,
      xmlStr
    ].join('\n')
  }

  getNodeSchema (type) {
    return this._documentSchema.getNodeSchema(type)
  }

  getNodeClass (type) {
    return this._documentSchema.getNodeClass(type)
  }

  _getPublicId (version) {
    // TODO: until we introduce minor versions we just use '0' for minor
    return `-//${this.issuer.toUpperCase()}//DTD ${this.name} v${version}.0//EN`
  }

  _getDtd (version) {
    return `${this.name}-${version}.dtd`
  }

  _getValidator (version) {
    if (!this._validators.has(version)) {
      this._validators.set(version, _createValidator(this.rootType, this._getDefinition(version)))
    }
    return this._validators.get(version)
  }

  _getDefinition (version) {
    if (!this._definitions.has(version)) {
      this._definitions.set(version, _createDefinition(version, this._actions))
    }
    return this._definitions.get(version)
  }
}
<|endoftext|>


<commit_before>"use strict";

import $ from 'jquery';

// Core Foundation Utilities, utilized in a number of places.

  /**
   * Returns a boolean for RTL support
   */
function rtl() {
  return $('html').attr('dir') === 'rtl';
}

/**
 * returns a random base-36 uid with namespacing
 * @function
 * @param {Number} length - number of random base-36 digits desired. Increase for more random strings.
 * @param {String} namespace - name of plugin to be incorporated in uid, optional.
 * @default {String} '' - if no plugin name is provided, nothing is appended to the uid.
 * @returns {String} - unique id
 */
function GetYoDigits(length, namespace){
  length = length || 6;
  return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1) + (namespace ? `-${namespace}` : '');
}

/**
 * Escape a string so it can be used as a regexp pattern
 * @function
 * @see https://stackoverflow.com/a/9310752/4317384
 *
 * @param {String} str - string to escape.
 * @returns {String} - escaped string
 */
function RegExpEscape(str){
  return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

function transitionend($elem){
  var transitions = {
    'transition': 'transitionend',
    'WebkitTransition': 'webkitTransitionEnd',
    'MozTransition': 'transitionend',
    'OTransition': 'otransitionend'
  };
  var elem = document.createElement('div'),
      end;

  for (var t in transitions){
    if (typeof elem.style[t] !== 'undefined'){
      end = transitions[t];
    }
  }
  if(end){
    return end;
  }else{
    end = setTimeout(function(){
      $elem.triggerHandler('transitionend', [$elem]);
    }, 1);
    return 'transitionend';
  }
}

/**
 * Return an event type to listen for window load.
 *
 * If `$elem` is passed, an event will be triggered on `$elem`. If window is already loaded, the event will still be triggered.
 * If `handler` is passed, attach it to the event on `$elem`.
 * Calling `onLoad` without handler allows you to get the event type that will be triggered before attaching the handler by yourself.
 * @function
 *
 * @param {Object} [] $elem - jQuery element on which the event will be triggered if passed.
 * @param {Function} [] handler - function to attach to the event.
 * @returns {String} - event type that should or will be triggered.
 */
function onLoad($elem, handler) {
  const didLoad = document.readyState === 'complete';
  const eventType = (didLoad ? '_didLoad' : 'load') + '.zf.util.onLoad';
  const cb = () => $elem.triggerHandler(eventType);

  if ($elem) {
    if (handler) $elem.one(eventType, handler);

    if (didLoad)
      setTimeout(cb);
    else
      $(window).one('load', cb);
  }

  return eventType;
}

/**
 * Retuns an handler for the `mouseleave` that ignore disappeared mouses.
 *
 * If the mouse "disappeared" from the document (like when going on a browser UI element, See https://git.io/zf-11410),
 * the event is ignored.
 * - If the `ignoreLeaveWindow` is `true`, the event is ignored when the user actually left the window
 *   (like by switching to an other window with [Alt]+[Tab]).
 * - If the `ignoreReappear` is `true`, the event will be ignored when the mouse will reappear later on the document
 *   outside of the element it left.
 *
 * @function
 *
 * @param {Function} [] handler - handler for the filtered `mouseleave` event to watch.
 * @param {Object} [] options - object of options:
 * - {Boolean} [false] ignoreLeaveWindow - also ignore when the user switched windows.
 * - {Boolean} [false] ignoreReappear - also ignore when the mouse reappeared outside of the element it left.
 * @returns {Function} - filtered handler to use to listen on the `mouseleave` event.
 */
function ignoreMousedisappear(handler, { ignoreLeaveWindow = false, ignoreReappear = false } = {}) {
  return function leaveEventHandler(eEnter, ...rest) {
    const callback = handler.bind(this, eEnter, ...rest);
    setTimeout(function leaveEventDebouncer() {

      if (eEnter.relatedTarget !== null
      // - if the user switched to an other window (and we don't ignore it)
        || !ignoreLeaveWindow && document.hasFocus && !document.hasFocus()) {
        callback();
      }

      else if (!ignoreReappear) {
        $(document).one('mouseenter', function reenterEventHandler(eReenter) {
          if (!$(eEnter.currentTarget).has(eReenter.target).length) {

            eEnter.relatedTarget = eReenter.target;
            callback();

          }
        });
      }
    });

  };
}

export {rtl, GetYoDigits, RegExpEscape, transitionend, onLoad, ignoreMousedisappear};
<commit_msg>docs: add some doc in the magic mouseleave utility "ignoreMousedisappear"<commit_after>"use strict";

import $ from 'jquery';

// Core Foundation Utilities, utilized in a number of places.

  /**
   * Returns a boolean for RTL support
   */
function rtl() {
  return $('html').attr('dir') === 'rtl';
}

/**
 * returns a random base-36 uid with namespacing
 * @function
 * @param {Number} length - number of random base-36 digits desired. Increase for more random strings.
 * @param {String} namespace - name of plugin to be incorporated in uid, optional.
 * @default {String} '' - if no plugin name is provided, nothing is appended to the uid.
 * @returns {String} - unique id
 */
function GetYoDigits(length, namespace){
  length = length || 6;
  return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1) + (namespace ? `-${namespace}` : '');
}

/**
 * Escape a string so it can be used as a regexp pattern
 * @function
 * @see https://stackoverflow.com/a/9310752/4317384
 *
 * @param {String} str - string to escape.
 * @returns {String} - escaped string
 */
function RegExpEscape(str){
  return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

function transitionend($elem){
  var transitions = {
    'transition': 'transitionend',
    'WebkitTransition': 'webkitTransitionEnd',
    'MozTransition': 'transitionend',
    'OTransition': 'otransitionend'
  };
  var elem = document.createElement('div'),
      end;

  for (var t in transitions){
    if (typeof elem.style[t] !== 'undefined'){
      end = transitions[t];
    }
  }
  if(end){
    return end;
  }else{
    end = setTimeout(function(){
      $elem.triggerHandler('transitionend', [$elem]);
    }, 1);
    return 'transitionend';
  }
}

/**
 * Return an event type to listen for window load.
 *
 * If `$elem` is passed, an event will be triggered on `$elem`. If window is already loaded, the event will still be triggered.
 * If `handler` is passed, attach it to the event on `$elem`.
 * Calling `onLoad` without handler allows you to get the event type that will be triggered before attaching the handler by yourself.
 * @function
 *
 * @param {Object} [] $elem - jQuery element on which the event will be triggered if passed.
 * @param {Function} [] handler - function to attach to the event.
 * @returns {String} - event type that should or will be triggered.
 */
function onLoad($elem, handler) {
  const didLoad = document.readyState === 'complete';
  const eventType = (didLoad ? '_didLoad' : 'load') + '.zf.util.onLoad';
  const cb = () => $elem.triggerHandler(eventType);

  if ($elem) {
    if (handler) $elem.one(eventType, handler);

    if (didLoad)
      setTimeout(cb);
    else
      $(window).one('load', cb);
  }

  return eventType;
}

/**
 * Retuns an handler for the `mouseleave` that ignore disappeared mouses.
 *
 * If the mouse "disappeared" from the document (like when going on a browser UI element, See https://git.io/zf-11410),
 * the event is ignored.
 * - If the `ignoreLeaveWindow` is `true`, the event is ignored when the user actually left the window
 *   (like by switching to an other window with [Alt]+[Tab]).
 * - If the `ignoreReappear` is `true`, the event will be ignored when the mouse will reappear later on the document
 *   outside of the element it left.
 *
 * @function
 *
 * @param {Function} [] handler - handler for the filtered `mouseleave` event to watch.
 * @param {Object} [] options - object of options:
 * - {Boolean} [false] ignoreLeaveWindow - also ignore when the user switched windows.
 * - {Boolean} [false] ignoreReappear - also ignore when the mouse reappeared outside of the element it left.
 * @returns {Function} - filtered handler to use to listen on the `mouseleave` event.
 */
function ignoreMousedisappear(handler, { ignoreLeaveWindow = false, ignoreReappear = false } = {}) {
  return function leaveEventHandler(eEnter, ...rest) {
    const callback = handler.bind(this, eEnter, ...rest);

    // In firefox if the user switched between windows, the window sill have the focus by the time
    // the event is triggered. We have to debounce the event to test this case.
    setTimeout(function leaveEventDebouncer() {

      // The mouse left: call the given callback...
      // - if the mouse entered elsewhere (`relatedTarget` is where)
      if (eEnter.relatedTarget !== null
      // - if the user switched to an other window (and we don't ignore it)
        || !ignoreLeaveWindow && document.hasFocus && !document.hasFocus()) {
        callback();
      }

      // Otherwise, when the mouse will reenter (and if we don't ignore it), call the given callback...
      else if (!ignoreReappear) {
        $(document).one('mouseenter', function reenterEventHandler(eReenter) {
          // - if the mouse reentered outside of the element it left.
          if (!$(eEnter.currentTarget).has(eReenter.target).length) {

            // Fill where the mouse finally entered.
            eEnter.relatedTarget = eReenter.target;
            callback();

          }
        });
      }
    });

  };
}

export {rtl, GetYoDigits, RegExpEscape, transitionend, onLoad, ignoreMousedisappear};
<|endoftext|>