<commit_before>'use strict';

var _ = require('lodash');
var moment = require('moment');
var times = require('../times');
var levels = require('../levels');

function init(ctx) {
  var utils = require('../utils')(ctx);

  var sensorState = {
    name: 'xdrip-js'
    , label: 'Sensor Status'
    , pluginType: 'pill-status'
  };

  sensorState.getPrefs = function getPrefs(sbx) {
    return {
      enableAlerts: sbx.extendedSettings.enableAlerts || false
    };
  };

  sensorState.setProperties = function setProperties (sbx) {
    sbx.offerProperty('sensorState', function setProp ( ) {
      return sensorState.getStateString(sbx);
    });
  };

  sensorState.checkNotifications = function checkNotifications(sbx) {

    var info = sbx.properties.sensorState;

    if (info && info.notification) {
      var notification = _.extend({}, info.notification, {
        plugin: sensorState
        , debug: {
          stateString: info.lastStateString
        }
      });

      sbx.notifications.requestNotify(notification);
    }

  };

  sensorState.getStateString = function findLatestState(sbx) {
    var prefs = sensorState.getPrefs(sbx);

    var recentHours = 24;
    var recentMills = sbx.time - times.hours(recentHours).msecs;

    var result = {
      seenDevices: { }
      , latest: null
      , lastDevice: null
      , lastState: null
      , lastStateString: null
      , lastStateStringShort: null
      , lastStateTime: null
      , lastTxId: null
      , lastTxStatus: null
      , lastTxStatusString: null
      , lastTxStatusStringShort: null
      , lastTxActivation: null
      , lastMode: null
      , lastRssi: null
      , lastUnfiltered: null
      , lastFiltered: null
      , lastNoise: null
      , lastNoiseString: null
      , lastSlope: null
      , lastIntercept: null
      , lastCalType: null
      , lastCalibrationDate: null
      , lastBatteryTimestamp: null
      , lastVoltageA: null
      , lastVoltageB: null
      , lastTemperature: null
      , lastResistance: null
    };

    function toMoments (status) {
      return {
        when:  moment(status.mills)
        , timestamp: status.xdripjs && status.xdripjs.timestamp && moment(status.xdripjs.timestamp)
      };
    }

    function getDevice(status) {
      var uri = status.device || 'device';
      var device = result.seenDevices[uri];

      if (!device) {
        device = {
          name: utils.deviceName(uri)
          , uri: uri
        };

        result.seenDevices[uri] = device;
      }
      return device;
    }

    var recentData = _.chain(sbx.data.devicestatus)
      .filter(function (status) {
        return ('xdripjs' in status) && sbx.entryMills(status) <= sbx.time && sbx.entryMills(status) >= recentMills;
      })
      .value( );

    recentData = _.sortBy(recentData, 'xdripjs.timestamp');

    _.forEach(recentData, function eachStatus (status) {
      getDevice(status);

      var moments = toMoments(status);

      if (status.xdripjs && (!result.latest || moments.timestamp && moments.timestamp.isAfter(result.lastStateTime))) {
        result.latest = status;
        result.lastStateTime = moment(status.xdripjs.timestamp);
      }
    });

    var sendNotification = false;
    var sound = 'incoming';
    var message;

    var sensorInfo = result.latest;

    result.level = levels.NONE;

    if (sensorInfo && sensorInfo.xdripjs) {

      if (sensorInfo.xdripjs.state != 0x6) {
        // Send warning notification for all states that are not 'OK'
        sendNotification = true;
        message = 'Sensor state: ' + sensorInfo.xdripjs.stateString;
        result.level = levels.WARN;
      }

      if (sensorInfo.xdripjs.state == 0x7) {
        // Downgrade warning level to info if it is only a Need Cal
        sendNotification = true;
        message = 'Sensor state: ' + sensorInfo.xdripjs.stateString;
        result.level = levels.INFO;
      }

      if (sensorInfo.xdripjs.voltagea && (sensorInfo.xdripjs.voltagea < prefs.warnBatV)) {
        sendNotification = true;
        message = 'G5 Battery A Low Voltage: ' + sensorInfo.xdripjs.voltagea;
        result.level = levels.WARN;
      }

      if (sensorInfo.xdripjs.voltageb && (sensorInfo.xdripjs.voltageb < prefs.warnBatV)) {
        sendNotification = true;
        message = 'G5 Battery B Low Voltage: ' + sensorInfo.xdripjs.voltageb;
        result.level = levels.WARN;
      }

      //allow for 20 minute period after a full hour during which we'll alert the user
      if (prefs.enableAlerts && sendNotification) {
        result.notification = {
          title: 'Sensor State: ' + sensorInfo.xdripjs.stateString
          , message: message
          , pushoverSound: sound
          , level: result.level
          , group: 'xDrip-js'
        };
      }

      result.lastState = sensorInfo.xdripjs.state;
      result.lastStateString = sensorInfo.xdripjs.stateString;
      result.lastStateStringShort = sensorInfo.xdripjs.stateStringShort;
      result.lastTxId = sensorInfo.xdripjs.txId;
      result.lastTxStatus = sensorInfo.xdripjs.txStatus;
      result.lastTxStatusString = sensorInfo.xdripjs.txStatusString;
      result.lastTxStatusStringShort = sensorInfo.xdripjs.txStatusStringShort;
      result.lastTxActivation = sensorInfo.xdripjs.txActivation;
      result.lastMode = sensorInfo.xdripjs.mode;
      result.lastRssi = sensorInfo.xdripjs.rssi;
      result.lastUnfiltered = sensorInfo.xdripjs.unfiltered;
      result.lastFiltered = sensorInfo.xdripjs.filtered;
      result.lastNoise = sensorInfo.xdripjs.noise;
      result.lastNoiseString = sensorInfo.xdripjs.noiseString;
      result.lastSlope = Math.round(sensorInfo.xdripjs.slope * 100) / 100.0;
      result.lastIntercept = Math.round(sensorInfo.xdripjs.intercept * 100) / 100.0;
      result.lastCalType = sensorInfo.xdripjs.calType;
      result.lastCalibrationDate = sensorInfo.xdripjs.lastCalibrationDate;
      result.lastBatteryTimestamp = sensorInfo.xdripjs.batteryTimestamp;
      result.lastVoltageA = sensorInfo.xdripjs.voltagea;
      result.lastVoltageB = sensorInfo.xdripjs.voltageb;
      result.lastTemperature = sensorInfo.xdripjs.temperature;
      result.lastResistance = sensorInfo.xdripjs.resistance;
    }

    return result;
  };

  sensorState.updateVisualisation = function updateVisualisation (sbx) {

    var sensor = sbx.properties.sensorState;
    var info = [];

    _.forIn(sensor.seenDevices, function seenDevice (device) {
      info.push( { label: 'Seen: ', value: device.name } );
    });

    info.push( { label: 'State Time: ', value: (sensor && sensor.lastStateTime && moment().diff(sensor.lastStateTime, 'minutes') + ' minutes ago') || 'Unknown' } );
    info.push( { label: 'Mode: ', value: (sensor && sensor.lastMode) || 'Unknown' } );
    info.push( { label: 'Status: ', value: (sensor && sensor.lastStateString) || 'Unknown' } );
    info.push( { label: 'Tx ID: ', value: (sensor && sensor.lastTxId) || 'Unknown' } );
    info.push( { label: 'Tx Status: ', value: (sensor && sensor.lastTxStatusString) || 'Unknown' } );

    if (sensor) {
      if (sensor.lastTxActivation) {
        info.push( { label: 'Tx Age: ', value: moment().diff(moment(sensor.lastTxActivation), 'days') + ' days' } );
      }

      if (sensor.lastRssi) {
        info.push( { label: 'RSSI: ', value: sensor.lastRssi } );
      }

      if (sensor.lastUnfiltered) {
        info.push( { label: 'Unfiltered: ', value: sensor.lastUnfiltered } );
      }

      if (sensor.lastFiltered) {
        info.push( { label: 'Filtered: ', value: sensor.lastFiltered } );
      }

      if (sensor.lastNoiseString) {
        info.push( { label: 'Noise: ', value: sensor.lastNoiseString } );
      }

      if (sensor.lastSlope) {
        info.push( { label: 'Slope: ', value: sensor.lastSlope } );
      }

      if (sensor.lastIntercept) {
        info.push( { label: 'Intercept: ', value: sensor.lastIntercept } );
      }

      if (sensor.lastCalType) {
        info.push( { label: 'CalType: ', value: sensor.lastCalType } );
      }

      if (sensor.lastCalibrationDate) {
        info.push( { label: 'Calibration: ', value: moment().diff(moment(sensor.lastCalibrationDate), 'hours') + ' hours ago' } );
      }

      if (sensor.lastBatteryTimestamp) {
        info.push( { label: 'Battery: ', value: moment().diff(moment(sensor.lastBatteryTimestamp), 'minutes') + ' minutes ago' } );
      }

      if (sensor.lastVoltageA) {
        info.push( { label: 'VoltageA: ', value: sensor.lastVoltageA } );
      }

      if (sensor.lastVoltageB) {
        info.push( { label: 'VoltageB: ', value: sensor.lastVoltageB } );
      }

      if (sensor.lastTemperature) {
        info.push( { label: 'Temperature: ', value: sensor.lastTemperature } );
      }

      if (sensor.lastResistance) {
        info.push( { label: 'Resistance: ', value: sensor.lastResistance } );
      }

      var statusClass = null;
      if (sensor.level === levels.URGENT) {
          statusClass = 'urgent';
      } else if (sensor.level === levels.WARN) {
        statusClass = 'warn';
      }

      sbx.pluginBase.updatePillText(sensorState, {
        value: (sensor && sensor.lastStateStringShort) || (sensor && sensor.lastStateString) || 'Unknown'
        , label: 'CGM'
        , info: info
        , pillClass: statusClass
      });
    }
  };

  return sensorState;
}

module.exports = init;

<commit_msg>Added warnBatV preference for xdrip-js plugin.<commit_after>'use strict';

var _ = require('lodash');
var moment = require('moment');
var times = require('../times');
var levels = require('../levels');

function init(ctx) {
  var utils = require('../utils')(ctx);

  var sensorState = {
    name: 'xdrip-js'
    , label: 'Sensor Status'
    , pluginType: 'pill-status'
  };

  sensorState.getPrefs = function getPrefs(sbx) {
    return {
      enableAlerts: sbx.extendedSettings.enableAlerts || false
      , warnBatV: sbx.extendedSettings.warnBatV || 0
    };
  };

  sensorState.setProperties = function setProperties (sbx) {
    sbx.offerProperty('sensorState', function setProp ( ) {
      return sensorState.getStateString(sbx);
    });
  };

  sensorState.checkNotifications = function checkNotifications(sbx) {

    var info = sbx.properties.sensorState;

    if (info && info.notification) {
      var notification = _.extend({}, info.notification, {
        plugin: sensorState
        , debug: {
          stateString: info.lastStateString
        }
      });

      sbx.notifications.requestNotify(notification);
    }

  };

  sensorState.getStateString = function findLatestState(sbx) {
    var prefs = sensorState.getPrefs(sbx);

    var recentHours = 24;
    var recentMills = sbx.time - times.hours(recentHours).msecs;

    var result = {
      seenDevices: { }
      , latest: null
      , lastDevice: null
      , lastState: null
      , lastStateString: null
      , lastStateStringShort: null
      , lastStateTime: null
      , lastTxId: null
      , lastTxStatus: null
      , lastTxStatusString: null
      , lastTxStatusStringShort: null
      , lastTxActivation: null
      , lastMode: null
      , lastRssi: null
      , lastUnfiltered: null
      , lastFiltered: null
      , lastNoise: null
      , lastNoiseString: null
      , lastSlope: null
      , lastIntercept: null
      , lastCalType: null
      , lastCalibrationDate: null
      , lastBatteryTimestamp: null
      , lastVoltageA: null
      , lastVoltageB: null
      , lastTemperature: null
      , lastResistance: null
    };

    function toMoments (status) {
      return {
        when:  moment(status.mills)
        , timestamp: status.xdripjs && status.xdripjs.timestamp && moment(status.xdripjs.timestamp)
      };
    }

    function getDevice(status) {
      var uri = status.device || 'device';
      var device = result.seenDevices[uri];

      if (!device) {
        device = {
          name: utils.deviceName(uri)
          , uri: uri
        };

        result.seenDevices[uri] = device;
      }
      return device;
    }

    var recentData = _.chain(sbx.data.devicestatus)
      .filter(function (status) {
        return ('xdripjs' in status) && sbx.entryMills(status) <= sbx.time && sbx.entryMills(status) >= recentMills;
      })
      .value( );

    recentData = _.sortBy(recentData, 'xdripjs.timestamp');

    _.forEach(recentData, function eachStatus (status) {
      getDevice(status);

      var moments = toMoments(status);

      if (status.xdripjs && (!result.latest || moments.timestamp && moments.timestamp.isAfter(result.lastStateTime))) {
        result.latest = status;
        result.lastStateTime = moment(status.xdripjs.timestamp);
      }
    });

    var sendNotification = false;
    var sound = 'incoming';
    var message;

    var sensorInfo = result.latest;

    result.level = levels.NONE;

    if (sensorInfo && sensorInfo.xdripjs) {

      if (sensorInfo.xdripjs.state != 0x6) {
        // Send warning notification for all states that are not 'OK'
        sendNotification = true;
        message = 'Sensor state: ' + sensorInfo.xdripjs.stateString;
        result.level = levels.WARN;
      }

      if (sensorInfo.xdripjs.state == 0x7) {
        // Downgrade warning level to info if it is only a Need Cal
        sendNotification = true;
        message = 'Sensor state: ' + sensorInfo.xdripjs.stateString;
        result.level = levels.INFO;
      }

      if (sensorInfo.xdripjs.voltagea && (sensorInfo.xdripjs.voltagea < prefs.warnBatV)) {
        sendNotification = true;
        message = 'G5 Battery A Low Voltage: ' + sensorInfo.xdripjs.voltagea;
        result.level = levels.WARN;
      }

      if (sensorInfo.xdripjs.voltageb && (sensorInfo.xdripjs.voltageb < prefs.warnBatV)) {
        sendNotification = true;
        message = 'G5 Battery B Low Voltage: ' + sensorInfo.xdripjs.voltageb;
        result.level = levels.WARN;
      }

      //allow for 20 minute period after a full hour during which we'll alert the user
      if (prefs.enableAlerts && sendNotification) {
        result.notification = {
          title: 'Sensor State: ' + sensorInfo.xdripjs.stateString
          , message: message
          , pushoverSound: sound
          , level: result.level
          , group: 'xDrip-js'
        };
      }

      result.lastState = sensorInfo.xdripjs.state;
      result.lastStateString = sensorInfo.xdripjs.stateString;
      result.lastStateStringShort = sensorInfo.xdripjs.stateStringShort;
      result.lastTxId = sensorInfo.xdripjs.txId;
      result.lastTxStatus = sensorInfo.xdripjs.txStatus;
      result.lastTxStatusString = sensorInfo.xdripjs.txStatusString;
      result.lastTxStatusStringShort = sensorInfo.xdripjs.txStatusStringShort;
      result.lastTxActivation = sensorInfo.xdripjs.txActivation;
      result.lastMode = sensorInfo.xdripjs.mode;
      result.lastRssi = sensorInfo.xdripjs.rssi;
      result.lastUnfiltered = sensorInfo.xdripjs.unfiltered;
      result.lastFiltered = sensorInfo.xdripjs.filtered;
      result.lastNoise = sensorInfo.xdripjs.noise;
      result.lastNoiseString = sensorInfo.xdripjs.noiseString;
      result.lastSlope = Math.round(sensorInfo.xdripjs.slope * 100) / 100.0;
      result.lastIntercept = Math.round(sensorInfo.xdripjs.intercept * 100) / 100.0;
      result.lastCalType = sensorInfo.xdripjs.calType;
      result.lastCalibrationDate = sensorInfo.xdripjs.lastCalibrationDate;
      result.lastBatteryTimestamp = sensorInfo.xdripjs.batteryTimestamp;
      result.lastVoltageA = sensorInfo.xdripjs.voltagea;
      result.lastVoltageB = sensorInfo.xdripjs.voltageb;
      result.lastTemperature = sensorInfo.xdripjs.temperature;
      result.lastResistance = sensorInfo.xdripjs.resistance;
    }

    return result;
  };

  sensorState.updateVisualisation = function updateVisualisation (sbx) {

    var sensor = sbx.properties.sensorState;
    var info = [];

    _.forIn(sensor.seenDevices, function seenDevice (device) {
      info.push( { label: 'Seen: ', value: device.name } );
    });

    info.push( { label: 'State Time: ', value: (sensor && sensor.lastStateTime && moment().diff(sensor.lastStateTime, 'minutes') + ' minutes ago') || 'Unknown' } );
    info.push( { label: 'Mode: ', value: (sensor && sensor.lastMode) || 'Unknown' } );
    info.push( { label: 'Status: ', value: (sensor && sensor.lastStateString) || 'Unknown' } );
    info.push( { label: 'Tx ID: ', value: (sensor && sensor.lastTxId) || 'Unknown' } );
    info.push( { label: 'Tx Status: ', value: (sensor && sensor.lastTxStatusString) || 'Unknown' } );

    if (sensor) {
      if (sensor.lastTxActivation) {
        info.push( { label: 'Tx Age: ', value: moment().diff(moment(sensor.lastTxActivation), 'days') + ' days' } );
      }

      if (sensor.lastRssi) {
        info.push( { label: 'RSSI: ', value: sensor.lastRssi } );
      }

      if (sensor.lastUnfiltered) {
        info.push( { label: 'Unfiltered: ', value: sensor.lastUnfiltered } );
      }

      if (sensor.lastFiltered) {
        info.push( { label: 'Filtered: ', value: sensor.lastFiltered } );
      }

      if (sensor.lastNoiseString) {
        info.push( { label: 'Noise: ', value: sensor.lastNoiseString } );
      }

      if (sensor.lastSlope) {
        info.push( { label: 'Slope: ', value: sensor.lastSlope } );
      }

      if (sensor.lastIntercept) {
        info.push( { label: 'Intercept: ', value: sensor.lastIntercept } );
      }

      if (sensor.lastCalType) {
        info.push( { label: 'CalType: ', value: sensor.lastCalType } );
      }

      if (sensor.lastCalibrationDate) {
        info.push( { label: 'Calibration: ', value: moment().diff(moment(sensor.lastCalibrationDate), 'hours') + ' hours ago' } );
      }

      if (sensor.lastBatteryTimestamp) {
        info.push( { label: 'Battery: ', value: moment().diff(moment(sensor.lastBatteryTimestamp), 'minutes') + ' minutes ago' } );
      }

      if (sensor.lastVoltageA) {
        info.push( { label: 'VoltageA: ', value: sensor.lastVoltageA } );
      }

      if (sensor.lastVoltageB) {
        info.push( { label: 'VoltageB: ', value: sensor.lastVoltageB } );
      }

      if (sensor.lastTemperature) {
        info.push( { label: 'Temperature: ', value: sensor.lastTemperature } );
      }

      if (sensor.lastResistance) {
        info.push( { label: 'Resistance: ', value: sensor.lastResistance } );
      }

      var statusClass = null;
      if (sensor.level === levels.URGENT) {
          statusClass = 'urgent';
      } else if (sensor.level === levels.WARN) {
        statusClass = 'warn';
      }

      sbx.pluginBase.updatePillText(sensorState, {
        value: (sensor && sensor.lastStateStringShort) || (sensor && sensor.lastStateString) || 'Unknown'
        , label: 'CGM'
        , info: info
        , pillClass: statusClass
      });
    }
  };

  return sensorState;
}

module.exports = init;

<|endoftext|>


<commit_before>const $ = require('jquery');
const React = require('react');
const ReactDOM = require('react-dom');

const common = require('../common/common.js');
const constants = require('../common/constants.js');
const ga = require('../common/ga.js');
const DashboardHistoryButton = require('../components/DashboardHistoryButton.jsx');
const CampaignHistoryTab = require('../components/CampaignHistoryTab.jsx');
const tabber = require('../components/tabber.js');

const twoWeeks = 15 * constants.timespan.day;
const startTimestamp = Date.now() - twoWeeks;
const charts = [
    { column: 6, label: "Impressions / day", metric: 'impressions' },
    { column: 7, label: "Clicks / day", metric: 'clicks' },
    { column: 8, label: "Avg CPC", metric: 'avgCpc' },
    { column: 9, label: "Spend / day", metric: 'spend' },
    { column: 10, label: "Sales ($) / day", metric: 'salesValue' },
    { column: 11, label: "ACOS", metric: 'acos' },
];
const deltaConfig = { rate: 'day', chunk: 'day', startTimestamp };

window.setInterval(ga.mcatch(() => {
    let tableRows = $('#campaignTable tbody tr');
    addChartButtons(tableRows);

    let wrapper = $('#campaignTable_wrapper');
    addTabs(wrapper);
}), 100);

function addTabs(wrapper) {
    if (wrapper.hasClass('a-tab-container'))
        return;
    const detachedChildren = wrapper.children().detach();

    const tabs = $('

');
    wrapper.append(tabs);
    wrapper.addClass('a-tab-container');

    let {container} = tabber(tabs, { label: 'Dashboard', active: true });
    container.append(detachedChildren);

    tabber(tabs, {
        label: 'Aggregate History',
        activate: (entityId, historyContainer) => {
            common.getCampaignSummary(entityId).then(summary => {
                const allowedPromises = summary.map(s => common.getCampaignAllowed(entityId, s.campaignId));
                Promise.all(allowedPromises).then(allowed => {
                    const summaryAllowed = summary.filter((item, index) => allowed[index]);
                    let aggContent = React.createElement(CampaignHistoryTab, {
                        allowed: true,
                        anonymous: false,
                        downloadHref: '',
                        loadData: cb => {
                            const historyPromises = summaryAllowed.map(s => common.getCampaignHistory(entityId, s.campaignId));
                            Promise.all(historyPromises).then(series => {
                                const deltas = series.map(s => common.convertSnapshotsToDeltas(s, { rate: 'day', chunk: 'day' }));
                                const aggSeries = common.aggregateSeries(deltas, { chunk: 'day' });
                                cb(aggSeries);
                            });
                        },
                    });
                    ReactDOM.render(aggContent, historyContainer[0]);
                });
            });
        },
    });
}

function addChartButtons(rows) {
    for (let row of rows) {
        if ($(row).find(`.${DashboardHistoryButton.chartClass}`).length)
            continue;

        let cells = $(row).children();
        let link = $(cells[1]).find('a')[0];
        if (!link)
            continue;

        let href = link.href;
        let campaignId = common.getCampaignId(href);

        const renderButtons = (allowed, anonymous) => {
            for (let chart of charts) {
                let target = cells[chart.column];
                if (!target)
                    continue;

                const loadData = onComplete => {
                    if (!allowed)
                        return onComplete(formatParallelData({}, chart.metric));

                    return common.getCampaignHistory(common.getEntityId(), campaignId).then(data => {
                        const deltas = common.convertSnapshotsToDeltas(data, deltaConfig);
                        const campaignData = common.parallelizeSeries(deltas);
                        onComplete(formatParallelData(campaignData, chart.metric));
                    });
                };

                let container = $(target).find('.machete-dash-container');
                if (!container.length) {
                    container = $('');
                    $(target).append(container);
                }

                let btn = React.createElement(DashboardHistoryButton, {
                    allowed,
                    anonymous,
                    metric: chart.metric,
                    title: chart.label,
                    loadData,
                });
                ReactDOM.render(btn, container[0]);
            }
        };

        renderButtons(false, true);

        Promise.all([common.getCampaignAllowed(common.getEntityId(), campaignId), common.getUser()])
        .then(results => {
            const [allowed, user] = results;
            renderButtons(allowed, user.isAnon);
        });
    }
}

function formatParallelData(data, name) {
    return {
        timestamp: data.timestamp || [],
        data: data[name] || [],
        name,
    };
}
<commit_msg>reworked dashboard aggregation renderer to use lots of promises<commit_after>const $ = require('jquery');
const React = require('react');
const ReactDOM = require('react-dom');
const co = require('co');

const common = require('../common/common.js');
const constants = require('../common/constants.js');
const ga = require('../common/ga.js');
const DashboardHistoryButton = require('../components/DashboardHistoryButton.jsx');
const CampaignHistoryTab = require('../components/CampaignHistoryTab.jsx');
const tabber = require('../components/tabber.js');

const twoWeeks = 15 * constants.timespan.day;
const startTimestamp = Date.now() - twoWeeks;
const charts = [
    { column: 6, label: "Impressions / day", metric: 'impressions' },
    { column: 7, label: "Clicks / day", metric: 'clicks' },
    { column: 8, label: "Avg CPC", metric: 'avgCpc' },
    { column: 9, label: "Spend / day", metric: 'spend' },
    { column: 10, label: "Sales ($) / day", metric: 'salesValue' },
    { column: 11, label: "ACOS", metric: 'acos' },
];
const deltaConfig = { rate: 'day', chunk: 'day', startTimestamp };

window.setInterval(ga.mcatch(() => {
    let tableRows = $('#campaignTable tbody tr');
    addChartButtons(tableRows);

    let wrapper = $('#campaignTable_wrapper');
    addTabs(wrapper);
}), 100);

const campaignSummaryPromise = co(function*() {
    const entityId = common.getEntityId();
    const summaries = yield common.getCampaignSummary(entityId);
    const allowed = yield Promise.all(summaries.map(s => common.getCampaignAllowed(entityId, s.campaignId)));
    const summaryAllowed = summaries.filter((item, index) => allowed[index]);
    return summaryAllowed;
});

function addTabs(wrapper) {
    if (wrapper.hasClass('a-tab-container'))
        return;
    const detachedChildren = wrapper.children().detach();

    const tabs = $('

');
    wrapper.append(tabs);
    wrapper.addClass('a-tab-container');

    let {container} = tabber(tabs, { label: 'Dashboard', active: true });
    container.append(detachedChildren);

    tabber(tabs, {
        label: 'Aggregate History',
        activate: (entityId, historyContainer) => {
            let aggContent = React.createElement(CampaignHistoryTab, {
                allowed: true,
                anonymous: false,
                downloadHref: '',
                loadData: cb => co(function*() {
                    const summaries = yield campaignSummaryPromise;
                    const histories = yield Promise.all(summaries.map(s => common.getCampaignHistory(entityId, s.campaignId)));
                    const deltas = histories.map(h => common.convertSnapshotsToDeltas(h, { rate: 'day', chunk: 'day' }));
                    const aggSeries = common.aggregateSeries(deltas, { chunk: 'day' });
                    cb(aggSeries);
                }),
            });
            ReactDOM.render(aggContent, historyContainer[0]);
        },
    });
}

function addChartButtons(rows) {
    for (let row of rows) {
        if ($(row).find(`.${DashboardHistoryButton.chartClass}`).length)
            continue;

        let cells = $(row).children();
        let link = $(cells[1]).find('a')[0];
        if (!link)
            continue;

        let href = link.href;
        let campaignId = common.getCampaignId(href);

        const renderButtons = (allowed, anonymous) => {
            for (let chart of charts) {
                let target = cells[chart.column];
                if (!target)
                    continue;

                const loadData = onComplete => {
                    if (!allowed)
                        return onComplete(formatParallelData({}, chart.metric));

                    return common.getCampaignHistory(common.getEntityId(), campaignId).then(data => {
                        const deltas = common.convertSnapshotsToDeltas(data, deltaConfig);
                        const campaignData = common.parallelizeSeries(deltas);
                        onComplete(formatParallelData(campaignData, chart.metric));
                    });
                };

                let container = $(target).find('.machete-dash-container');
                if (!container.length) {
                    container = $('');
                    $(target).append(container);
                }

                let btn = React.createElement(DashboardHistoryButton, {
                    allowed,
                    anonymous,
                    metric: chart.metric,
                    title: chart.label,
                    loadData,
                });
                ReactDOM.render(btn, container[0]);
            }
        };

        renderButtons(false, true);

        Promise.all([common.getCampaignAllowed(common.getEntityId(), campaignId), common.getUser()])
        .then(results => {
            const [allowed, user] = results;
            renderButtons(allowed, user.isAnon);
        });
    }
}

function formatParallelData(data, name) {
    return {
        timestamp: data.timestamp || [],
        data: data[name] || [],
        name,
    };
}
<|endoftext|>


<commit_before>'use strict'

const child_process = require('child_process')
const debug = require('debug')('attempt-backport')
const request = require('request')
const node_repo = require('../lib/node-repo')
const updatePrWithLabels = node_repo.updatePrWithLabels
// const removeLabelFromPR = node_repo.removeLabelFromPR

const enabledRepos = ['node']
const nodeVersions = [
  { version: 7 },
  { version: 6, lts: true },
  { version: 4, lts: true }
]
const queue = []
let inProgress = false

module.exports = function (app) {
  if (!global._node_repo_dir) return

  app.on('pull_request.opened', handlePrUpdate)
  // Pull Request updates
  app.on('pull_request.synchronize', handlePrUpdate)

  function handlePrUpdate (event, owner, repo) {
    if (!~enabledRepos.indexOf(repo)) return

    if (event.pull_request.base.ref !== 'master') return

    const prId = event.number
    const options = { owner, repo, prId, logger: event.logger }

    debug(`/${owner}/${repo}/pull/${prId} sync`)
    for (const node of nodeVersions) {
      queueAttemptBackport(options, node.version, !!node.lts)
    }

    if (!inProgress) processNextBackport()
  }

  // to trigger polling manually
  app.get('/attempt-backport/pr/:owner/:repo/:id', (req, res) => {
    const owner = req.params.owner
    const repo = req.params.repo
    const prId = parseInt(req.params.id, 10)
    const options = { owner, repo, prId, logger: req.log }

    if (~enabledRepos.indexOf(repo)) {
      for (const node of nodeVersions) {
        queueAttemptBackport(options, node.version, !!node.lts)
      }
    }

    if (!inProgress) processNextBackport()

    res.end()
  })
}

function processNextBackport () {
  const item = queue.shift()
  if (!item) return

  if (typeof item !== 'function') {
    debug(`item was not a function! - queue size: ${queue.length}`)
    return
  } else if (inProgress) {
    debug(`was still in progress! - queue size: ${queue.length}`)
    return
  }
  item()
}

function queueAttemptBackport (options, version, isLTS) {
  queue.push(function () {
    options.logger.debug(`processing a new backport to v${version}`)
    attemptBackport(options, version, isLTS, processNextBackport)
  })
}

function attemptBackport (options, version, isLTS, cb) {
  // Start
  gitAmAbort()

  function wrapCP (cmd, args, opts, callback) {
    let exited = false

    if (arguments.length === 3) {
      callback = opts
      opts = {}
    }

    opts.cwd = global._node_repo_dir

    const cp = child_process.spawn(cmd, args, opts)
    const argsString = [cmd, ...args].join(' ')

    cp.on('error', function (err) {
      debug(`child_process err: ${err}`)
      if (!exited) onError()
    })
    cp.on('exit', function (code) {
      exited = true
      if (!cb) {
        debug(`error before exit, code: ${code}, on '${argsString}'`)
        return
      } else if (code > 0) {
        debug(`exit code > 0: ${code}, on '${argsString}'`)
        onError()
        return
      }
      callback()
    })
    // Useful when debugging.

    cp.stdout.on('data', (data) => {
      options.logger.debug(data.toString())
    })
    cp.stderr.on('data', (data) => {
      options.logger.debug(data.toString())
    })

    return cp
  }

  function onError () {
    if (!cb) return
    const _cb = cb
    setImmediate(() => {
      options.logger.debug(`backport to ${version} failed`)
      if (!isLTS) updatePrWithLabels(options, [`dont-land-on-v${version}.x`])
      setImmediate(() => {
        inProgress = false
        _cb()
      })
    })
    cb = null
  }

  function gitAmAbort () {
    // TODO(Fishrock123): this should probably just merge into wrapCP
    let exited = false
    options.logger.debug('aborting any previous backport attempt...')

    const cp = child_process.spawn('git', ['am', '--abort'], { cwd: global._node_repo_dir })
    const argsString = 'git am --abort'

    cp.on('error', function (err) {
      debug(`child_process err: ${err}`)
      if (!exited) onError()
    })
    cp.on('exit', function (code) {
      exited = true
      if (!cb) {
        debug(`error before exit, code: ${code}, on '${argsString}'`)
        return
      }
      gitResetBefore()
    })
  }

  function gitResetBefore () {
    options.logger.debug(`resetting origin/v${version}.x-staging...`)
    wrapCP('git', ['reset', `origin/v${version}.x-staging`, '--hard'], gitRemoteUpdate)
  }

  function gitRemoteUpdate () {
    options.logger.debug('updating git remotes...')
    wrapCP('git', ['remote', 'update', '-p'], gitCheckout)
  }

  function gitCheckout () {
    options.logger.debug(`checking out origin/v${version}.x-staging...`)
    wrapCP('git', ['checkout', `origin/v${version}.x-staging`], gitReset)
  }

  function gitReset () {
    options.logger.debug(`resetting origin/v${version}.x-staging...`)
    wrapCP('git', ['reset', `origin/v${version}.x-staging`, '--hard'], fetchDiff)
  }

  function fetchDiff () {
    options.logger.debug(`fetching diff from pr ${options.prId}...`)

    const url = `https://patch-diff.githubusercontent.com/raw/${options.owner}/${options.repo}/pull/${options.prId}.patch`

    const req = request(url)

    req.on('error', function (err) {
      debug(`request err: ${err}`)
      return onError()
    })
    req.on('response', function (response) {
      if (response.statusCode !== 200) {
        debug(`request non-200 status: ${response.statusCode}`)
        return onError()
      }
    })

    gitAttemptBackport(req)
  }

  function gitAttemptBackport (req) {
    options.logger.debug(`attempting a backport to v${version}...`)
    const cp = wrapCP('git', ['am'], { stdio: 'pipe' }, function done () {
      // Success!
      if (isLTS) {
        updatePrWithLabels(options, [`lts-watch-v${version}.x`])
      }// else {
        // TODO(Fishrock123): Re-enable this, but do a check first
        // to make sure the label was set by the bot only.
        // removeLabelFromPR(options, `dont-land-on-v${version}.x`)
      // }

      setImmediate(() => {
        options.logger.debug(`backport to v${version} successful`)
        inProgress = false
        cb()
      })
    })

    req.pipe(cp.stdin)
  }
}
<commit_msg>attempt-backport: git clean -fd after checkout<commit_after>'use strict'

const child_process = require('child_process')
const debug = require('debug')('attempt-backport')
const request = require('request')
const node_repo = require('../lib/node-repo')
const updatePrWithLabels = node_repo.updatePrWithLabels
// const removeLabelFromPR = node_repo.removeLabelFromPR

const enabledRepos = ['node']
const nodeVersions = [
  { version: 7 },
  { version: 6, lts: true },
  { version: 4, lts: true }
]
const queue = []
let inProgress = false

module.exports = function (app) {
  if (!global._node_repo_dir) return

  app.on('pull_request.opened', handlePrUpdate)
  // Pull Request updates
  app.on('pull_request.synchronize', handlePrUpdate)

  function handlePrUpdate (event, owner, repo) {
    if (!~enabledRepos.indexOf(repo)) return

    if (event.pull_request.base.ref !== 'master') return

    const prId = event.number
    const options = { owner, repo, prId, logger: event.logger }

    debug(`/${owner}/${repo}/pull/${prId} sync`)
    for (const node of nodeVersions) {
      queueAttemptBackport(options, node.version, !!node.lts)
    }

    if (!inProgress) processNextBackport()
  }

  // to trigger polling manually
  app.get('/attempt-backport/pr/:owner/:repo/:id', (req, res) => {
    const owner = req.params.owner
    const repo = req.params.repo
    const prId = parseInt(req.params.id, 10)
    const options = { owner, repo, prId, logger: req.log }

    if (~enabledRepos.indexOf(repo)) {
      for (const node of nodeVersions) {
        queueAttemptBackport(options, node.version, !!node.lts)
      }
    }

    if (!inProgress) processNextBackport()

    res.end()
  })
}

function processNextBackport () {
  const item = queue.shift()
  if (!item) return

  if (typeof item !== 'function') {
    debug(`item was not a function! - queue size: ${queue.length}`)
    return
  } else if (inProgress) {
    debug(`was still in progress! - queue size: ${queue.length}`)
    return
  }
  item()
}

function queueAttemptBackport (options, version, isLTS) {
  queue.push(function () {
    options.logger.debug(`processing a new backport to v${version}`)
    attemptBackport(options, version, isLTS, processNextBackport)
  })
}

function attemptBackport (options, version, isLTS, cb) {
  // Start
  gitAmAbort()

  function wrapCP (cmd, args, opts, callback) {
    let exited = false

    if (arguments.length === 3) {
      callback = opts
      opts = {}
    }

    opts.cwd = global._node_repo_dir

    const cp = child_process.spawn(cmd, args, opts)
    const argsString = [cmd, ...args].join(' ')

    cp.on('error', function (err) {
      debug(`child_process err: ${err}`)
      if (!exited) onError()
    })
    cp.on('exit', function (code) {
      exited = true
      if (!cb) {
        debug(`error before exit, code: ${code}, on '${argsString}'`)
        return
      } else if (code > 0) {
        debug(`exit code > 0: ${code}, on '${argsString}'`)
        onError()
        return
      }
      callback()
    })
    // Useful when debugging.

    cp.stdout.on('data', (data) => {
      options.logger.debug(data.toString())
    })
    cp.stderr.on('data', (data) => {
      options.logger.debug(data.toString())
    })

    return cp
  }

  function onError () {
    if (!cb) return
    const _cb = cb
    setImmediate(() => {
      options.logger.debug(`backport to ${version} failed`)
      if (!isLTS) updatePrWithLabels(options, [`dont-land-on-v${version}.x`])
      setImmediate(() => {
        inProgress = false
        _cb()
      })
    })
    cb = null
  }

  function gitAmAbort () {
    // TODO(Fishrock123): this should probably just merge into wrapCP
    let exited = false
    options.logger.debug('aborting any previous backport attempt...')

    const cp = child_process.spawn('git', ['am', '--abort'], { cwd: global._node_repo_dir })
    const argsString = 'git am --abort'

    cp.on('error', function (err) {
      debug(`child_process err: ${err}`)
      if (!exited) onError()
    })
    cp.on('exit', function (code) {
      exited = true
      if (!cb) {
        debug(`error before exit, code: ${code}, on '${argsString}'`)
        return
      }
      gitResetBefore()
    })
  }

  function gitResetBefore () {
    options.logger.debug(`resetting origin/v${version}.x-staging...`)
    wrapCP('git', ['reset', `origin/v${version}.x-staging`, '--hard'], gitRemoteUpdate)
  }

  function gitRemoteUpdate () {
    options.logger.debug('updating git remotes...')
    wrapCP('git', ['remote', 'update', '-p'], gitCheckout)
  }

  function gitCheckout () {
    options.logger.debug(`checking out origin/v${version}.x-staging...`)
    wrapCP('git', ['checkout', `origin/v${version}.x-staging`], gitClean_fd)
  }

  function gitClean_fd () {
    wrapCP('git', ['clean', '-fd'], gitReset)
  }

  function gitReset () {
    options.logger.debug(`resetting origin/v${version}.x-staging...`)
    wrapCP('git', ['reset', `origin/v${version}.x-staging`, '--hard'], fetchDiff)
  }

  function fetchDiff () {
    options.logger.debug(`fetching diff from pr ${options.prId}...`)

    const url = `https://patch-diff.githubusercontent.com/raw/${options.owner}/${options.repo}/pull/${options.prId}.patch`

    const req = request(url)

    req.on('error', function (err) {
      debug(`request err: ${err}`)
      return onError()
    })
    req.on('response', function (response) {
      if (response.statusCode !== 200) {
        debug(`request non-200 status: ${response.statusCode}`)
        return onError()
      }
    })

    gitAttemptBackport(req)
  }

  function gitAttemptBackport (req) {
    options.logger.debug(`attempting a backport to v${version}...`)
    const cp = wrapCP('git', ['am'], { stdio: 'pipe' }, function done () {
      // Success!
      if (isLTS) {
        updatePrWithLabels(options, [`lts-watch-v${version}.x`])
      }// else {
        // TODO(Fishrock123): Re-enable this, but do a check first
        // to make sure the label was set by the bot only.
        // removeLabelFromPR(options, `dont-land-on-v${version}.x`)
      // }

      setImmediate(() => {
        options.logger.debug(`backport to v${version} successful`)
        inProgress = false
        cb()
      })
    })

    req.pipe(cp.stdin)
  }
}
<|endoftext|>


<commit_before>/* eslint-disable no-console, spaced-comment */

//==============================================================
// This is example of polling (reading) Holding Registers
// on a regular scan interval with timeouts enabled.
// For robust behaviour, the next action is not activated
// until the previous action is completed (callback served).
//==============================================================

"use strict"

//==============================================================
// create an empty modbus client
var ModbusRTU   = require ('modbus-serial');
var client      = new ModbusRTU();

var mbsStatus   = 'Initializing...';    // holds a status of Modbus

// Modbus 'state' constants
var MBS_STATE_INIT          = 'State init';
var MBS_STATE_IDLE          = 'State idle';
var MBS_STATE_NEXT          = 'State next';
var MBS_STATE_GOOD_READ     = 'State good (read)';
var MBS_STATE_FAIL_READ     = 'State fail (read)';
var MBS_STATE_GOOD_CONNECT  = 'State good (port)';
var MBS_STATE_FAIL_CONNECT  = 'State fail (port)';

// Modbus configuration values
var mbsId       = 1;
var mbsScan     = 1000;
var mbsTimeout  = 5000;
var mbsState    = MBS_STATE_INIT;
var mbsBufData;

//==============================================================
var connectClient = function()
{
    // set requests parameters
    client.setID      (mbsId);
    client.setTimeout (mbsTimeout);

    // try to connect
    client.connectRTUBuffered ("COM9", { baudRate: 9600, parity: 'even', dataBits:8, stopBits:1 })
        .then(function()
        {
            mbsState  = MBS_STATE_GOOD_CONNECT;
            mbsStatus = 'Connected, wait for reading...';
            console.log(mbsStatus);
        })
        .catch(function(e)
        {
            mbsState  = MBS_STATE_FAIL_CONNECT;
            mbsStatus = e.message;
            console.log(e);
        });
}

//==============================================================
var readModbusData = function()
{
    // try to read data
    client.readHoldingRegisters (5, 1)
        .then(function(data)
        {
            mbsState   = MBS_STATE_GOOD_READ;
            mbsStatus  = 'success';
            mbsBufData = data.buffer.swap16();
            console.log(data.buffer);
        })
        .catch(function(e)
        {
            mbsState  = MBS_STATE_FAIL_READ;
            mbsStatus = e.message;
            console.log(e);
        });
}

//==============================================================
var runModbus = function()
{
    var nextAction;

    switch (mbsState)
    {
        case MBS_STATE_INIT:
            nextAction = connectClient;
            break;

        case MBS_STATE_NEXT:
            nextAction = readModbusData;
            break;

        case MBS_STATE_GOOD_CONNECT:
            nextAction = readModbusData;
            break;

        case MBS_STATE_FAIL_CONNECT:
            nextAction = connectClient;
            break;

        case MBS_STATE_GOOD_READ:
            nextAction = readModbusData;
            break;

        case MBS_STATE_FAIL_READ:
            if (client.isOpen)  { mbsState = MBS_STATE_NEXT;  }
            else                { nextAction = connectClient; }
            break;

        default:
            // nothing to do, keep scanning until actionable case
    }

    console.log();
    console.log(nextAction);

    // execute "next action" function if defined
    if (nextAction != undefined)
    {
        nextAction();
        mbsState = MBS_STATE_IDLE;
    }

    // set for next run
    setTimeout (runModbus, mbsScan);
}

//==============================================================
runModbus();
<commit_msg>Modbus holding registers polling<commit_after>/* eslint-disable no-console, spaced-comment */

//==============================================================
// This is an example of polling (reading) Holding Registers
// on a regular scan interval with timeouts enabled.
// For robust behaviour, the next action is not activated
// until the previous action is completed (callback served).
//==============================================================

"use strict"

//==============================================================
// create an empty modbus client
var ModbusRTU   = require ('modbus-serial');
var client      = new ModbusRTU();

var mbsStatus   = 'Initializing...';    // holds a status of Modbus

// Modbus 'state' constants
var MBS_STATE_INIT          = 'State init';
var MBS_STATE_IDLE          = 'State idle';
var MBS_STATE_NEXT          = 'State next';
var MBS_STATE_GOOD_READ     = 'State good (read)';
var MBS_STATE_FAIL_READ     = 'State fail (read)';
var MBS_STATE_GOOD_CONNECT  = 'State good (port)';
var MBS_STATE_FAIL_CONNECT  = 'State fail (port)';

// Modbus configuration values
var mbsId       = 1;
var mbsScan     = 1000;
var mbsTimeout  = 5000;
var mbsState    = MBS_STATE_INIT;
var mbsBufData;

//==============================================================
var connectClient = function()
{
    // set requests parameters
    client.setID      (mbsId);
    client.setTimeout (mbsTimeout);

    // try to connect
    client.connectRTUBuffered ("COM9", { baudRate: 9600, parity: 'even', dataBits:8, stopBits:1 })
        .then(function()
        {
            mbsState  = MBS_STATE_GOOD_CONNECT;
            mbsStatus = 'Connected, wait for reading...';
            console.log(mbsStatus);
        })
        .catch(function(e)
        {
            mbsState  = MBS_STATE_FAIL_CONNECT;
            mbsStatus = e.message;
            console.log(e);
        });
}

//==============================================================
var readModbusData = function()
{
    // try to read data
    client.readHoldingRegisters (5, 1)
        .then(function(data)
        {
            mbsState   = MBS_STATE_GOOD_READ;
            mbsStatus  = 'success';
            mbsBufData = data.buffer.swap16();
            console.log(data.buffer);
        })
        .catch(function(e)
        {
            mbsState  = MBS_STATE_FAIL_READ;
            mbsStatus = e.message;
            console.log(e);
        });
}

//==============================================================
var runModbus = function()
{
    var nextAction;

    switch (mbsState)
    {
        case MBS_STATE_INIT:
            nextAction = connectClient;
            break;

        case MBS_STATE_NEXT:
            nextAction = readModbusData;
            break;

        case MBS_STATE_GOOD_CONNECT:
            nextAction = readModbusData;
            break;

        case MBS_STATE_FAIL_CONNECT:
            nextAction = connectClient;
            break;

        case MBS_STATE_GOOD_READ:
            nextAction = readModbusData;
            break;

        case MBS_STATE_FAIL_READ:
            if (client.isOpen)  { mbsState = MBS_STATE_NEXT;  }
            else                { nextAction = connectClient; }
            break;

        default:
            // nothing to do, keep scanning until actionable case
    }

    console.log();
    console.log(nextAction);

    // execute "next action" function if defined
    if (nextAction != undefined)
    {
        nextAction();
        mbsState = MBS_STATE_IDLE;
    }

    // set for next run
    setTimeout (runModbus, mbsScan);
}

//==============================================================
runModbus();
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.INTERFACE({
  refines: 'foam.mlang.Expr',

  methods: [
    {
      name: 'f',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        }
      ],
      javaReturns: 'Object'
    },
    {
      name: 'partialEval',
      javaReturns: 'foam.mlang.Expr'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.ExprProperty',

  properties: [
    ['javaType', 'foam.mlang.Expr'],
    ['javaJSONParser', 'foam.lib.json.ExprParser']
  ]
});

debugger;
foam.INTERFACE({
  refines: 'foam.mlang.predicate.Predicate',

  methods: [
    {
      name: 'f',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        }
      ],
      javaReturns: 'boolean'
    },
    {
      name: 'partialEval',
      javaReturns: 'foam.mlang.predicate.Predicate'
    },
    {
      name: 'toIndex',
      javaSupport: false,
      args: [
        {
          name: 'tail',
          javaType: 'foam.dao.index.Index'
        }
      ],
      javaReturns: 'foam.dao.index.Index'
    },
    {
      name: 'toDisjunctiveNormalForm',
      javaSupport: false,
      javaReturns: 'foam.mlang.predicate.Predicate'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.PredicateProperty',

  properties: [
    ['javaType', 'foam.mlang.predicate.Predicate']
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.PredicateArray',

  properties: [
    ['javaType', 'foam.mlang.predicate.Predicate[]']
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.AbstractPredicate',

  methods: [
    {
      // TODO: This is a duplicate of the method in Predicate,
      // but it's necessary because when we refine Predicate, it doesn't
      // update classes that copied their axioms in from Predicate as a trait.
      // If we were more careful about the ordering of classes this wouldn't be
      // necessary.
      name: 'f',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        }
      ],
      javaCode: 'return false;',
      javaReturns: 'boolean'
    },
    {
      // TODO: Same TODO as .f method above
      name: 'toIndex',
      javaSupport: false
    },
    {
      // TODO: Same TODO as .f
      name: 'toDisjunctiveNormalForm',
      javaSupport: false,
      javaReturns: 'foam.mlang.predicate.Predicate'
    },
    {
      name: 'partialEval',
      javaCode: 'return this;',
      javaReturns: 'foam.mlang.predicate.Predicate'
    },
    {
      name: 'toString',
      javaCode: 'return classInfo_.getId();'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.AbstractExpr',

  methods: [
    {
      name: 'partialEval',
      javaCode: 'return this;',
      // Remove this javaReturns when it is inherited properly. (Traits are fixed).
      javaReturns: 'foam.mlang.Expr'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.True',

  methods: [
    {
      name: 'f',
      javaCode: 'return true;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.False',

  methods: [
    {
      name: 'f',
      javaCode: 'return false;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Or',

  methods: [
    {
      name: 'f',
      javaCode: 'for ( int i = 0 ; i < getArgs().length ; i++ ) {\n'
                + '  if ( getArgs()[i].f(obj) ) return true;\n'
                + '}\n'
                + 'return false;\n'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.And',

  methods: [
    {
      name: 'f',
      javaCode: 'for ( int i = 0 ; i < getArgs().length ; i++ ) {\n'
                + '  if ( ! getArgs()[i].f(obj) ) return false;\n'
                + '}\n'
                + 'return true;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Contains',

  methods: [
    {
      name: 'f',
      javaCode: 'String s1 = (String)getArg1().f(obj);\n'
                + 'String s2 = (String)getArg2().f(obj);\n'
                + 'return s1 != null ? s1.indexOf(s2) != -1 : false;\n'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.StartsWithIC',

  methods: [
    {
      name: 'f',
      javaCode: 'String arg2 = ((String)getArg2().f(obj)).toUpperCase();\n'
                + 'Object arg1 = getArg1().f(obj);\n'
                + 'if ( arg1 instanceof Object[] ) {\n'
                + '  Object[] values = (Object[])arg1;\n'
                + '  for ( int i = 0 ; i < values.length ; i++ ) {\n'
                + '    if ( ((String)values[i]).toUpperCase().startsWith(arg2) ) {\n'
                + '      return true;\n'
                + '    }\n'
                + '  }\n'
                + '  return false;'
                + '}'
                + 'String value = (String)arg1;\n'
                + 'return value.toUpperCase().startsWith(arg2);\n'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.Constant',

  methods: [
    {
      name: 'f',
      javaCode: 'return getValue();'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Eq',

  methods: [
    {
      name: 'f',
      // TODO(adamvy): Is there a better option than all the Comparable casts?
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) == 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Neq',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) != 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Lt',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) < 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Lte',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) <= 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Gt',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) > 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Gte',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) >= 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Not',

  methods: [
    {
      name: 'f',
      javaCode: 'return ! getArg1().f(obj);'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Has',

  methods: [
    {
      name: 'f',
      javaCode: `Object value = getArg1().f(obj);
        return ! (value == null ||
          (value instanceof String && ((String)value).length() == 0) ||
          (value.getClass().isArray() && java.lang.reflect.Array.getLength(value) == 0));`
    }
  ]
});

foam.INTERFACE({
  refines: 'foam.mlang.order.Comparator',

  methods: [
    {
      name: 'compare',
      args: [
        {
          name: 'o1',
          javaType: 'Object'
        },
        {
          name: 'o2',
          javaType: 'Object'
        }
      ]
    },
    {
      name: 'toIndex',
      javaSupport: false,
      args: [
        {
          name: 'tail'
        }
      ]
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    {
      name: 'orderTail',
      javaCode: 'return null;'
    },
    {
      name: 'orderPrimaryProperty',
      javaCode: 'return this;'
    },
    {
      name: 'orderDirection',
      javaCode: 'return 1;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.order.Desc',

  methods: [
    {
      name: 'compare',
      javaCode: 'return -1 * getArg1().compare(o1, o2);'
    },
    {
      name: 'toString',
      javaCode: 'return "DESC(" + getArg1().toString() + ")";'
    },
    {
      name: 'orderTail',
      javaCode: 'return null;'
    },
    {
      name: 'orderPrimaryProperty',
      javaCode: 'return getArg1();'
    },
    {
      name: 'orderDirection',
      javaCode: 'Object ret = getArg1().orderDirection().reverse();' +
        'ret.setSrcOrder(this); return ret;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.order.CustomComparator',

  methods: [
    {
      name: 'orderTail',
      javaCode: 'return null;'
    },
    {
      name: 'orderPrimaryProperty',
      javaCode: 'return null;'
    },
    {
      name: 'orderDirection',
      javaCode: 'return 1;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.sink.Count',

  methods: [
    {
      name: 'put',
      javaReturns: 'void',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        },
        {
          name: 'sub',
          javaType: 'foam.core.Detachable'
        }
      ],
      javaCode: 'setValue(this.getValue() + 1);'
    }
  ]
});
<commit_msg>Added a TODO.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.INTERFACE({
  refines: 'foam.mlang.Expr',

  methods: [
    {
      name: 'f',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        }
      ],
      javaReturns: 'Object'
    },
    {
      name: 'partialEval',
      javaReturns: 'foam.mlang.Expr'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.ExprProperty',

  properties: [
    ['javaType', 'foam.mlang.Expr'],
    ['javaJSONParser', 'foam.lib.json.ExprParser']
  ]
});

debugger;
foam.INTERFACE({
  refines: 'foam.mlang.predicate.Predicate',

  methods: [
    {
      name: 'f',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        }
      ],
      javaReturns: 'boolean'
    },
    {
      name: 'partialEval',
      javaReturns: 'foam.mlang.predicate.Predicate'
    },
    {
      name: 'toIndex',
      javaSupport: false,
      args: [
        {
          name: 'tail',
          javaType: 'foam.dao.index.Index'
        }
      ],
      javaReturns: 'foam.dao.index.Index'
    },
    {
      name: 'toDisjunctiveNormalForm',
      javaSupport: false,
      javaReturns: 'foam.mlang.predicate.Predicate'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.PredicateProperty',

  properties: [
    ['javaType', 'foam.mlang.predicate.Predicate']
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.PredicateArray',

  properties: [
    ['javaType', 'foam.mlang.predicate.Predicate[]']
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.AbstractPredicate',

  methods: [
    {
      // TODO: This is a duplicate of the method in Predicate,
      // but it's necessary because when we refine Predicate, it doesn't
      // update classes that copied their axioms in from Predicate as a trait.
      // If we were more careful about the ordering of classes this wouldn't be
      // necessary.
      name: 'f',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        }
      ],
      javaCode: 'return false;',
      javaReturns: 'boolean'
    },
    {
      // TODO: Same TODO as .f method above
      name: 'toIndex',
      javaSupport: false
    },
    {
      // TODO: Same TODO as .f
      name: 'toDisjunctiveNormalForm',
      javaSupport: false,
      javaReturns: 'foam.mlang.predicate.Predicate'
    },
    {
      name: 'partialEval',
      javaCode: 'return this;',
      javaReturns: 'foam.mlang.predicate.Predicate'
    },
    {
      name: 'toString',
      javaCode: 'return classInfo_.getId();'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.AbstractExpr',

  methods: [
    {
      name: 'partialEval',
      javaCode: 'return this;',
      // Remove this javaReturns when it is inherited properly. (Traits are fixed).
      javaReturns: 'foam.mlang.Expr'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.True',

  methods: [
    {
      name: 'f',
      javaCode: 'return true;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.False',

  methods: [
    {
      name: 'f',
      javaCode: 'return false;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Or',

  methods: [
    {
      name: 'f',
      javaCode: 'for ( int i = 0 ; i < getArgs().length ; i++ ) {\n'
                + '  if ( getArgs()[i].f(obj) ) return true;\n'
                + '}\n'
                + 'return false;\n'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.And',

  methods: [
    {
      name: 'f',
      javaCode: 'for ( int i = 0 ; i < getArgs().length ; i++ ) {\n'
                + '  if ( ! getArgs()[i].f(obj) ) return false;\n'
                + '}\n'
                + 'return true;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Contains',

  methods: [
    {
      name: 'f',
      javaCode: 'String s1 = (String)getArg1().f(obj);\n'
                + 'String s2 = (String)getArg2().f(obj);\n'
                + 'return s1 != null ? s1.indexOf(s2) != -1 : false;\n'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.StartsWithIC',

  methods: [
    {
      name: 'f',
      javaCode: 'String arg2 = ((String)getArg2().f(obj)).toUpperCase();\n'
                + 'Object arg1 = getArg1().f(obj);\n'
                + 'if ( arg1 instanceof Object[] ) {\n'
                + '  Object[] values = (Object[])arg1;\n'
                + '  for ( int i = 0 ; i < values.length ; i++ ) {\n'
                + '    if ( ((String)values[i]).toUpperCase().startsWith(arg2) ) {\n'
                + '      return true;\n'
                + '    }\n'
                + '  }\n'
                + '  return false;'
                + '}'
                + 'String value = (String)arg1;\n'
                + 'return value.toUpperCase().startsWith(arg2);\n'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.Constant',

  methods: [
    {
      name: 'f',
      javaCode: 'return getValue();'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Eq',

  methods: [
    {
      name: 'f',
      // TODO(adamvy): Is there a better option than all the Comparable casts?
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) == 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Neq',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) != 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Lt',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) < 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Lte',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) <= 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Gt',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) > 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Gte',

  methods: [
    {
      name: 'f',
      javaCode: 'return ((Comparable)getArg1().f(obj)).compareTo((Comparable)getArg2().f(obj)) >= 0;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Not',

  methods: [
    {
      name: 'f',
      javaCode: 'return ! getArg1().f(obj);'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.predicate.Has',

  methods: [
    {
      name: 'f',
      // TODO(kgr): Instead of checking type, use polymorphims and add a
      // type-specific has() method to the Property.
      javaCode: `Object value = getArg1().f(obj);
        return ! (value == null ||
          (value instanceof String && ((String)value).length() == 0) ||
          (value.getClass().isArray() && java.lang.reflect.Array.getLength(value) == 0));`
    }
  ]
});

foam.INTERFACE({
  refines: 'foam.mlang.order.Comparator',

  methods: [
    {
      name: 'compare',
      args: [
        {
          name: 'o1',
          javaType: 'Object'
        },
        {
          name: 'o2',
          javaType: 'Object'
        }
      ]
    },
    {
      name: 'toIndex',
      javaSupport: false,
      args: [
        {
          name: 'tail'
        }
      ]
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Property',

  methods: [
    {
      name: 'orderTail',
      javaCode: 'return null;'
    },
    {
      name: 'orderPrimaryProperty',
      javaCode: 'return this;'
    },
    {
      name: 'orderDirection',
      javaCode: 'return 1;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.order.Desc',

  methods: [
    {
      name: 'compare',
      javaCode: 'return -1 * getArg1().compare(o1, o2);'
    },
    {
      name: 'toString',
      javaCode: 'return "DESC(" + getArg1().toString() + ")";'
    },
    {
      name: 'orderTail',
      javaCode: 'return null;'
    },
    {
      name: 'orderPrimaryProperty',
      javaCode: 'return getArg1();'
    },
    {
      name: 'orderDirection',
      javaCode: 'Object ret = getArg1().orderDirection().reverse();' +
        'ret.setSrcOrder(this); return ret;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.order.CustomComparator',

  methods: [
    {
      name: 'orderTail',
      javaCode: 'return null;'
    },
    {
      name: 'orderPrimaryProperty',
      javaCode: 'return null;'
    },
    {
      name: 'orderDirection',
      javaCode: 'return 1;'
    }
  ]
});

foam.CLASS({
  refines: 'foam.mlang.sink.Count',

  methods: [
    {
      name: 'put',
      javaReturns: 'void',
      args: [
        {
          name: 'obj',
          javaType: 'foam.core.FObject'
        },
        {
          name: 'sub',
          javaType: 'foam.core.Detachable'
        }
      ],
      javaCode: 'setValue(this.getValue() + 1);'
    }
  ]
});
<|endoftext|>


<commit_before>/*
Geodesic extension to Leaflet library, by Fragger
https://github.com/Fragger/Leaflet.Geodesic
Version from master branch, dated Apr 26, 2013
*/
(function () {
  function geodesicPoly(Klass, fill) {
    return Klass.extend({
      initialize: function (latlngs, options) {
        Klass.prototype.initialize.call(this, L.geodesicConvertLines(latlngs, fill), options);
        this._latlngsinit = this._convertLatLngs(latlngs);
      },
      getLatLngs: function () {
        return this._latlngsinit;
      },
      setLatLngs: function (latlngs) {
        this._latlngsinit = this._convertLatLngs(latlngs);
        this._latlngs = this._convertLatLngs(L.geodesicConvertLines(this._latlngsinit, fill));
        return this.redraw();
      },
      addLatLng: function (latlng) {
        this._latlngsinit.push(L.latLng(latlng));
        this._latlngs = this._convertLatLngs(L.geodesicConvertLines(this._latlngsinit, fill));
        return this.redraw();
      },
      spliceLatLngs: function () { // (Number index, Number howMany)
        var removed = [].splice.apply(this._latlngsinit, arguments);
        this._convertLatLngs(this._latlngsinit);
        this._latlngs = this._convertLatLngs(L.geodesicConvertLines(this._latlngsinit, fill));
        this.redraw();
        return removed;
      }
    });
  }

  function geodesicConvertLine(startLatlng, endLatlng, convertedPoints) {
    var i,
      R = 6378137, // earth radius in meters (doesn't have to be exact)
      maxlength = 5000, // meters before splitting
      d2r = L.LatLng.DEG_TO_RAD,
      r2d = L.LatLng.RAD_TO_DEG,
      lat1, lat2, lng1, lng2, dLng, d, segments,
      f, A, B, x, y, z, fLat, fLng;

    dLng = Math.abs(endLatlng.lng - startLatlng.lng) * d2r;
    lat1 = startLatlng.lat * d2r;
    lat2 = endLatlng.lat * d2r;
    lng1 = startLatlng.lng * d2r;
    lng2 = endLatlng.lng * d2r;

    // http://en.wikipedia.org/wiki/Great-circle_distance
    d = Math.atan2(Math.sqrt( Math.pow(Math.cos(lat2) * Math.sin(dLng), 2) + Math.pow(Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng), 2) ), Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLng));

    segments = Math.ceil(d * R / maxlength);
    for (i = 1; i <= segments; i++) {
      // http://williams.best.vwh.net/avform.htm#Intermediate
      f = i / segments;
      A = Math.sin((1-f)*d) / Math.sin(d);
      B = Math.sin(f*d) / Math.sin(d);
      x = A * Math.cos(lat1) * Math.cos(lng1) + B * Math.cos(lat2) * Math.cos(lng2);
      y = A * Math.cos(lat1) * Math.sin(lng1) + B * Math.cos(lat2) * Math.sin(lng2);
      z = A * Math.sin(lat1)                  + B * Math.sin(lat2);
      fLat = r2d * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
      fLng = r2d * Math.atan2(y, x);

      convertedPoints.push(L.latLng([fLat, fLng]));
    }
  }

  L.geodesicConvertLines = function (latlngs, fill) {
    var i, j, len, geodesiclatlngs = [];
    for (i = 0, len = latlngs.length; i < len; i++) {
      if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
        return;
      }
      latlngs[i] = L.latLng(latlngs[i]);
    }

    if(!fill) {
      geodesiclatlngs.push(latlngs[0]);
    }
    for (i = 0, len = latlngs.length - 1; i < len; i++) {
      geodesicConvertLine(latlngs[i], latlngs[i+1], geodesiclatlngs);
    }
    if(fill) {
      geodesicConvertLine(latlngs[len], latlngs[0], geodesiclatlngs);
    }
    return geodesiclatlngs;
  }

  L.GeodesicPolyline = geodesicPoly(L.Polyline, 0);
  L.GeodesicPolygon = geodesicPoly(L.Polygon, 1);

  //L.GeodesicMultiPolyline = createMulti(L.GeodesicPolyline);
  //L.GeodesicMultiPolygon = createMulti(L.GeodesicPolygon);

  /*L.GeodesicMultiPolyline = L.MultiPolyline.extend({
    initialize: function (latlngs, options) {
      L.MultiPolyline.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  /*L.GeodesicMultiPolygon = L.MultiPolygon.extend({
    initialize: function (latlngs, options) {
      L.MultiPolygon.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  L.geodesicPolyline = function (latlngs, options) {
    return new L.GeodesicPolyline(latlngs, options);
  };

  L.geodesicPolygon = function (latlngs, options) {
    return new L.GeodesicPolygon(latlngs, options);
  };

  /*
  L.geodesicMultiPolyline = function (latlngs, options) {
    return new L.GeodesicMultiPolyline(latlngs, options);
  };

  L.geodesicMultiPolygon = function (latlngs, options) {
    return new L.GeodesicMultiPolygon(latlngs, options);
  };

  */
}());
<commit_msg>Extended redraw() method to fix behavior during editing<commit_after>/*
Geodesic extension to Leaflet library, by Fragger
https://github.com/Fragger/Leaflet.Geodesic
Version from master branch, dated Apr 26, 2013
Modified by qnstie 2013-07-17 to maintain compatibility with Leaflet.draw
*/
(function () {
  function geodesicPoly(Klass, fill) {
    return Klass.extend({
      initialize: function (latlngs, options) {
        Klass.prototype.initialize.call(this, L.geodesicConvertLines(latlngs, fill), options);
        this._latlngsinit = this._convertLatLngs(latlngs);
      },
      getLatLngs: function () {
        return this._latlngsinit;
      },
      setLatLngs: function (latlngs) {
        this._latlngsinit = this._convertLatLngs(latlngs);
        return this.redraw();
      },
      addLatLng: function (latlng) {
        this._latlngsinit.push(L.latLng(latlng));
        return this.redraw();
      },
      spliceLatLngs: function () { // (Number index, Number howMany)
        var removed = [].splice.apply(this._latlngsinit, arguments);
        this._convertLatLngs(this._latlngsinit);
        this.redraw();
        return removed;
      },
      redraw: function() {
        this._latlngs = this._convertLatLngs(L.geodesicConvertLines(this._latlngsinit, fill));
        return Klass.prototype.redraw.call(this);
      }
    });
  }

  function geodesicConvertLine(startLatlng, endLatlng, convertedPoints) {
    var i,
      R = 6378137, // earth radius in meters (doesn't have to be exact)
      maxlength = 5000, // meters before splitting
      d2r = L.LatLng.DEG_TO_RAD,
      r2d = L.LatLng.RAD_TO_DEG,
      lat1, lat2, lng1, lng2, dLng, d, segments,
      f, A, B, x, y, z, fLat, fLng;

    dLng = Math.abs(endLatlng.lng - startLatlng.lng) * d2r;
    lat1 = startLatlng.lat * d2r;
    lat2 = endLatlng.lat * d2r;
    lng1 = startLatlng.lng * d2r;
    lng2 = endLatlng.lng * d2r;

    // http://en.wikipedia.org/wiki/Great-circle_distance
    d = Math.atan2(Math.sqrt( Math.pow(Math.cos(lat2) * Math.sin(dLng), 2) + Math.pow(Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLng), 2) ), Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(dLng));

    segments = Math.ceil(d * R / maxlength);
    for (i = 1; i <= segments; i++) {
      // http://williams.best.vwh.net/avform.htm#Intermediate
      f = i / segments;
      A = Math.sin((1-f)*d) / Math.sin(d);
      B = Math.sin(f*d) / Math.sin(d);
      x = A * Math.cos(lat1) * Math.cos(lng1) + B * Math.cos(lat2) * Math.cos(lng2);
      y = A * Math.cos(lat1) * Math.sin(lng1) + B * Math.cos(lat2) * Math.sin(lng2);
      z = A * Math.sin(lat1)                  + B * Math.sin(lat2);
      fLat = r2d * Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
      fLng = r2d * Math.atan2(y, x);

      convertedPoints.push(L.latLng([fLat, fLng]));
    }
  }

  L.geodesicConvertLines = function (latlngs, fill) {
    var i, j, len, geodesiclatlngs = [];
    for (i = 0, len = latlngs.length; i < len; i++) {
      if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') {
        return;
      }
      latlngs[i] = L.latLng(latlngs[i]);
    }

    if(!fill) {
      geodesiclatlngs.push(latlngs[0]);
    }
    for (i = 0, len = latlngs.length - 1; i < len; i++) {
      geodesicConvertLine(latlngs[i], latlngs[i+1], geodesiclatlngs);
    }
    if(fill) {
      geodesicConvertLine(latlngs[len], latlngs[0], geodesiclatlngs);
    }
    return geodesiclatlngs;
  }

  L.GeodesicPolyline = geodesicPoly(L.Polyline, 0);
  L.GeodesicPolygon = geodesicPoly(L.Polygon, 1);

  //L.GeodesicMultiPolyline = createMulti(L.GeodesicPolyline);
  //L.GeodesicMultiPolygon = createMulti(L.GeodesicPolygon);

  /*L.GeodesicMultiPolyline = L.MultiPolyline.extend({
    initialize: function (latlngs, options) {
      L.MultiPolyline.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  /*L.GeodesicMultiPolygon = L.MultiPolygon.extend({
    initialize: function (latlngs, options) {
      L.MultiPolygon.prototype.initialize.call(this, L.geodesicConvertLines(latlngs), options);
    }
  });*/

  L.geodesicPolyline = function (latlngs, options) {
    return new L.GeodesicPolyline(latlngs, options);
  };

  L.geodesicPolygon = function (latlngs, options) {
    return new L.GeodesicPolygon(latlngs, options);
  };

  /*
  L.geodesicMultiPolyline = function (latlngs, options) {
    return new L.GeodesicMultiPolyline(latlngs, options);
  };

  L.geodesicMultiPolygon = function (latlngs, options) {
    return new L.GeodesicMultiPolygon(latlngs, options);
  };

  */
}());
<|endoftext|>


<commit_before>const
	Processor = require("./"),
	levelup = require('levelup'),
	leveldown = require('leveldown'),
	Semaphore = require('../semaphore'),
	logger = require('../logger'),
	Path = require('path'),
	extend = require("extend"),
	Watermark = require("../watermark"),
	jsexpr = require("jsexpr");

const TTL_IVAL = 5000;
const MODE = {
	accept : "accept",
	reject : "reject",
	every : "every"
}

const DEF_CONF = {
	buffer : 1000,
	mode : MODE.accept,
	filter : "true",
	key : "'all'",
	every : 1,
	first : true,
	ttl : 0,
	output : "aggregate",
	aggregate : {
		"count" : 1
	}
}

class FilterProcessor extends Processor {
	constructor(id) {
		super(id);
		this.buffer = {};
		this.blen = 0;
		this.ttls = {};
		this.sem = new Semaphore(1);
		this.ttlival = null;
	}

	async configure(config,callback) {
		let spath = Path.resolve(config.$datadir,"filter",`${this.id}_store}`);
		let tpath = Path.resolve(config.$datadir,"filter",`${this.id}_ttl}`);

		this.config = extend({},DEF_CONF,config);
		this.mode = MODE[this.config.mode] || MODE.accept;
		this.filter = jsexpr.eval(this.config.filter);
		this.key = jsexpr.expr(this.config.key);
		this.ttl = Math.abs(parseInt(this.config.ttl)) || DEF_CONF.ttl;
		this.every = parseInt(this.config.every) || DEF_CONF.every;
		this.output = jsexpr.assign(this.config.output);
		this.aggregate = jsexpr.expr(this.config.aggregate);
		this.first = this.config.first;
		this.maxbuff = this.config.buffer;
		this.store = new Watermark(spath);
		this.ttlstore = new Watermark(tpath);

		callback();
	}

	async purgeMemory() {
		if(this.blen>this.maxbuff) {
			await this.store.saveall(this.buffer);
			this.buffer = {};
			this.blen = 0;
		}
	}

	async getItem(key,entry) {
		if(!this.buffer[key]) {
			let item = await this.store.get(key);
			item = extend({first:this.first,data:{},size:0},item);
			item.entry = item.entry || entry;
			this.buffer[key] = item;
			this.blen++;
		}
		return this.buffer[key];
	}

	async updateTTL(buff) {
		var ttl = null;
		var key = buff.ttl;
		var isnew = false;

		await this.sem.take();

		// Round to 1 second
		if(!key) {
			key = buff.ttl = `${Math.floor(Date.now() / 1000.0) * 1000}`;

			if(!this.ttls[key]) {
				ttl = await this.ttlstore.get(key);
				this.ttls[key] = ttl;
			}
			else {
				ttl = this.ttls[key];
			}

			if(!ttl.items) ttl.items = [];
			ttl.items.push(buff._id);
		}

		this.sem.leave();
	}

	async loopTTL() {
		let ttls = [];
		let ids = [];
		let since = Date.now()-this.ttl;

		await this.sem.take();

		try {
			await this.ttlstore.saveall(this.ttls);
			await this.ttlstore.readStream({lt:`${since}`},(key,value)=>{
				ttls.push(key);
				ids = ids.concat(value.items||[]);
			});
			let buffs = await Promise.all(
				ids.map(id=>this.buffer[id] || this.store.get(id))
			);
			await this.ttlstore.removeall(ttls);
			await this.store.removeall(ids);
			ids.forEach(id=>delete this.buffer[id]);
			buffs.forEach(buff=>{
				let entry = buff.entry;
				this.output(entry,buff.data);
				this.push(entry);
			});
		}catch(err) {
			logger.error(err);
		}

		this.ttls = {};
		this.sem.leave();
		this.ttlival = setTimeout(()=>this.loopTTL(),TTL_IVAL);
	}

	async start(callback) {
		if(this.ttl) {
			this.ttlival = setTimeout(()=>this.loopTTL(),TTL_IVAL);
		}
		callback();
	}

	async stop(callback) {
		if(this.ttlival)
			clearInterval(this.ttlival);
		callback();
	}

	async process(entry,callback) {
		let test = this.filter(entry);
		let key = this.key(entry);

		await this.sem.take();
		await this.purgeMemory();
		this.sem.leave();

		switch(this.mode) {
			case MODE.reject :
				return callback(null, test? null : entry);
			case 'every' :
				if(!test) return callback(null);
				let aggr = this.aggregate(entry);
				let buff = await this.getItem(key,entry);

				buff.size++;
				Object.keys(aggr).forEach(k=>{
					buff.data[k] = buff.data[k]!==undefined? (buff.data[k]+aggr[k]) : aggr[k];
				});

				if(buff.first || buff.size>=this.every) {
					buff.first = false;
					this.output(entry,buff.data);
					buff.data = {};
					buff.size = 0;
					return callback(null,entry);
				}
				else {
					if(this.ttl)
						await this.updateTTL(buff);
					return callback(null);
				}

				break;
			case 'accept' :
			default :
				return callback(null, test? entry : null);
		}
	}
}

module.exports = FilterProcessor;
<commit_msg>filter window dts<commit_after>const
	Processor = require("./"),
	levelup = require('levelup'),
	leveldown = require('leveldown'),
	Semaphore = require('../semaphore'),
	logger = require('../logger'),
	Path = require('path'),
	extend = require("extend"),
	Watermark = require("../watermark"),
	jsexpr = require("jsexpr");

const TTL_IVAL = 5000;
const MODE = {
	accept : "accept",
	reject : "reject",
	every : "every"
}

const DEF_CONF = {
	buffer : 1000,
	mode : MODE.accept,
	filter : "true",
	key : "'all'",
	every : 1,
	first : true,
	ttl : 0,
	output : "aggregate",
	aggregate : {
		"count" : 1
	}
}

class FilterProcessor extends Processor {
	constructor(id) {
		super(id);
		this.buffer = {};
		this.blen = 0;
		this.ttls = {};
		this.sem = new Semaphore(1);
		this.ttlival = null;
	}

	async configure(config,callback) {
		let spath = Path.resolve(config.$datadir,"filter",`${this.id}_store}`);
		let tpath = Path.resolve(config.$datadir,"filter",`${this.id}_ttl}`);

		this.config = extend({},DEF_CONF,config);
		this.mode = MODE[this.config.mode] || MODE.accept;
		this.filter = jsexpr.eval(this.config.filter);
		this.key = jsexpr.expr(this.config.key);
		this.ttl = Math.abs(parseInt(this.config.ttl)) || DEF_CONF.ttl;
		this.every = parseInt(this.config.every) || DEF_CONF.every;
		this.output = jsexpr.assign(this.config.output);
		this.aggregate = jsexpr.expr(this.config.aggregate);
		this.first = this.config.first;
		this.maxbuff = this.config.buffer;
		this.store = new Watermark(spath);
		this.ttlstore = new Watermark(tpath);

		callback();
	}

	async purgeMemory() {
		if(this.blen>this.maxbuff) {
			await this.store.saveall(this.buffer);
			this.buffer = {};
			this.blen = 0;
		}
	}

	async getItem(key,entry) {
		if(!this.buffer[key]) {
			let item = await this.store.get(key);
			item = extend({first:this.first,data:{},size:0,ts:Date.now()},item);
			item.entry = item.entry || entry;
			this.buffer[key] = item;
			this.blen++;
		}
		return this.buffer[key];
	}

	async updateTTL(buff) {
		var ttl = null;
		var key = buff.ttl;
		var isnew = false;

		await this.sem.take();

		// Round to 1 second
		if(!key) {
			key = buff.ttl = `${Math.floor(Date.now() / 1000.0) * 1000}`;

			if(!this.ttls[key]) {
				ttl = await this.ttlstore.get(key);
				this.ttls[key] = ttl;
			}
			else {
				ttl = this.ttls[key];
			}

			if(!ttl.items) ttl.items = [];
			ttl.items.push(buff._id);
		}

		this.sem.leave();
	}

	async loopTTL() {
		let ttls = [];
		let ids = [];
		let since = Date.now()-this.ttl;

		await this.sem.take();

		try {
			await this.ttlstore.saveall(this.ttls);
			await this.ttlstore.readStream({lt:`${since}`},(key,value)=>{
				ttls.push(key);
				ids = ids.concat(value.items||[]);
			});
			let buffs = await Promise.all(
				ids.map(id=>this.buffer[id] || this.store.get(id))
			);
			await this.ttlstore.removeall(ttls);
			await this.store.removeall(ids);
			ids.forEach(id=>delete this.buffer[id]);
			buffs.forEach(buff=>{
				let entry = buff.entry;
				let dts = Math.floor((Date.now() - buff.timestamp) / 1000) * 1000;
				extend(buff.data,{$dts:dts});
				this.output(entry,buff.data);
				this.push(entry);
			});
		}catch(err) {
			logger.error(err);
		}

		this.ttls = {};
		this.sem.leave();
		this.ttlival = setTimeout(()=>this.loopTTL(),TTL_IVAL);
	}

	async start(callback) {
		if(this.ttl) {
			this.ttlival = setTimeout(()=>this.loopTTL(),TTL_IVAL);
		}
		callback();
	}

	async stop(callback) {
		if(this.ttlival)
			clearInterval(this.ttlival);
		callback();
	}

	async process(entry,callback) {
		let test = this.filter(entry);
		let key = this.key(entry);

		await this.sem.take();
		await this.purgeMemory();
		this.sem.leave();

		switch(this.mode) {
			case MODE.reject :
				return callback(null, test? null : entry);
			case 'every' :
				if(!test) return callback(null);
				let aggr = this.aggregate(entry);
				let buff = await this.getItem(key,entry);

				buff.size++;
				Object.keys(aggr).forEach(k=>{
					buff.data[k] = buff.data[k]!==undefined? (buff.data[k]+aggr[k]) : aggr[k];
				});

				if(buff.first || buff.size>=this.every) {
					let dts = Math.floor((Date.now() - buff.timestamp) / 1000) * 1000;
					buff.first = false;
					extend(buff.data,{$dts:dts});
					this.output(entry,buff.data);
					buff.data = {};
					buff.size = 0;
					return callback(null,entry);
				}
				else {
					if(this.ttl)
						await this.updateTTL(buff);
					return callback(null);
				}

				break;
			case 'accept' :
			default :
				return callback(null, test? entry : null);
		}
	}
}

module.exports = FilterProcessor;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.auth',
  name: 'User',

  implements: [
    'foam.nanos.auth.EnabledAware',
    'foam.nanos.auth.LastModifiedAware',
    'foam.nanos.auth.LastModifiedByAware'
  ],

  documentation: '',

  tableColumns: [
    'id', 'enabled', 'firstName', 'lastName', 'organization', 'lastModified'
  ],

  properties: [
    {
      class: 'Long',
      name: 'id',
      displayWidth: 30,
      width: 100
    },
    {
      class: 'String',
      // class: 'SPID',
      label: 'Service Provider',
      name: 'spid',
      documentation: "User's service provider."
    },
    {
      class: 'DateTime',
      name: 'lastLogin'
    },
    {
      class: 'String',
      name: 'firstName'
    },
    {
      class: 'String',
      name: 'middleName'
    },
    {
      class: 'String',
      name: 'lastName'
    },
    {
      class: 'String',
      name: 'organization'
    },
    {
      class: 'String',
      name: 'department'
    },
    {
      class: 'String',
      // class: 'Email',
      name: 'email'
    },
    {
      class: 'String',
      // class: 'Phone',
      name: 'phone'
    },
    {
      class: 'String',
      // class: 'Phone',
      name: 'mobile'
    },
    {
      class: 'String',
      name: 'type'
    },
    {
      class: 'DateTime',
      name: 'birthday'
    },
    {
      class: 'String',
      name: 'profilePicture'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.auth.Address',
      name: 'address'
    },
    {
      class: 'FObjectArray',
      of: 'foam.core.FObject',
      name: 'accounts'
    },
    {
      class: 'Reference',
      name: 'language',
      of: 'foam.nanos.auth.Language',
      value: 'en'
    },
    {
      class: 'String',
      name: 'timeZone'
      // TODO: create custom view or DAO
    },
    {
      class: 'Password',
      name: 'password',
      displayWidth: 30,
      width: 100
    },
    {
      class: 'Password',
      name: 'previousPassword',
      displayWidth: 30,
      width: 100
    },
    {
      class: 'DateTime',
      name: 'passwordLastModified'
    },
    // TODO: startDate, endDate,
    // TODO: do we want to replace 'note' with a simple ticket system?
    {
      class: 'String',
      name: 'note',
      displayWidth: 70,
      displayHeight: 10
    },
    // TODO: remove after demo
    {
      class: 'String',
      name: 'businessName',
      documentation: 'Name of the business'
    },
    {
      class: 'String',
      name: 'businessIdentificationNumber',
      name: 'Business Identification Number (BIN)'
    },
    {
      class: 'String',
      name: 'bankIdentificationCode',
      documentation: 'Bank Identification Code (BIC)'
    }
  ]
});
<commit_msg>Changed name to documentation<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.auth',
  name: 'User',

  implements: [
    'foam.nanos.auth.EnabledAware',
    'foam.nanos.auth.LastModifiedAware',
    'foam.nanos.auth.LastModifiedByAware'
  ],

  documentation: '',

  tableColumns: [
    'id', 'enabled', 'firstName', 'lastName', 'organization', 'lastModified'
  ],

  properties: [
    {
      class: 'Long',
      name: 'id',
      displayWidth: 30,
      width: 100
    },
    {
      class: 'String',
      // class: 'SPID',
      label: 'Service Provider',
      name: 'spid',
      documentation: "User's service provider."
    },
    {
      class: 'DateTime',
      name: 'lastLogin'
    },
    {
      class: 'String',
      name: 'firstName'
    },
    {
      class: 'String',
      name: 'middleName'
    },
    {
      class: 'String',
      name: 'lastName'
    },
    {
      class: 'String',
      name: 'organization'
    },
    {
      class: 'String',
      name: 'department'
    },
    {
      class: 'String',
      // class: 'Email',
      name: 'email'
    },
    {
      class: 'String',
      // class: 'Phone',
      name: 'phone'
    },
    {
      class: 'String',
      // class: 'Phone',
      name: 'mobile'
    },
    {
      class: 'String',
      name: 'type'
    },
    {
      class: 'DateTime',
      name: 'birthday'
    },
    {
      class: 'String',
      name: 'profilePicture'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.auth.Address',
      name: 'address'
    },
    {
      class: 'FObjectArray',
      of: 'foam.core.FObject',
      name: 'accounts'
    },
    {
      class: 'Reference',
      name: 'language',
      of: 'foam.nanos.auth.Language',
      value: 'en'
    },
    {
      class: 'String',
      name: 'timeZone'
      // TODO: create custom view or DAO
    },
    {
      class: 'Password',
      name: 'password',
      displayWidth: 30,
      width: 100
    },
    {
      class: 'Password',
      name: 'previousPassword',
      displayWidth: 30,
      width: 100
    },
    {
      class: 'DateTime',
      name: 'passwordLastModified'
    },
    // TODO: startDate, endDate,
    // TODO: do we want to replace 'note' with a simple ticket system?
    {
      class: 'String',
      name: 'note',
      displayWidth: 70,
      displayHeight: 10
    },
    // TODO: remove after demo
    {
      class: 'String',
      name: 'businessName',
      documentation: 'Name of the business'
    },
    {
      class: 'String',
      name: 'businessIdentificationNumber',
      documentation: 'Business Identification Number (BIN)'
    },
    {
      class: 'String',
      name: 'bankIdentificationCode',
      documentation: 'Bank Identification Code (BIC)'
    }
  ]
});
<|endoftext|>