<commit_before>var DynamicListInput = function() {

  function $it(elementID) {
    return document.getElementById(elementID);
  }

  function deriveRowId(nameBase, index) {
    return nameBase + 'dynamic-list-input-row:' + index + ":";
    }

  function getControl(nameBase, index, extension) {
    var rowid = deriveRowId(nameBase, index);
    var controlid = rowid + extension;
    var control = $it(controlid);
    return control;
    }

  function lastRowInd(existrows) {
    var maxi = -1;
    for (var i in existrows) {
      if (i > maxi) maxi = i;
      }
    return maxi;
    }

  function makeEnabledUpdater(nameBase, existrows, minlength, maxlength) {
    var removeenabled = true;
    var addcontrol = $it(nameBase + 'add-row');
    return function () {
      var makeremoveenabled = existrows.count > minlength;
      if (removeenabled ^ makeremoveenabled) {
        for (var row in existrows) {
          if (row == 'count') continue;
          var removec = getControl(nameBase, row, 'remove');
          removec.disabled = !makeremoveenabled;
          }
        removeenabled = makeremoveenabled;
        }
      var makeaddenabled = existrows.count < maxlength;
      addcontrol.disabled = !makeaddenabled;

      };
    }

  function assignRemoveClick(nameBase, i, existrows, enabledUpdater) {
    existrows[i] = true;
    ++existrows.count;
    var removec = getControl(nameBase, i, 'remove');

    removec.onclick = function () {
      var rowel = getControl(nameBase, i, '');
      rowel.parentNode.removeChild(rowel);
      delete existrows[i];
      --existrows.count;
      enabledUpdater();
      };
    }

  return {
    init_DynamicListInput: function(nameBase, rowcount, minlength, maxlength) {
      var existrows = new Object();
      existrows.count = 0;

      var enabledUpdater = makeEnabledUpdater(nameBase, existrows, minlength, maxlength);

      for (var i = 0; i < rowcount; ++ i) {
        assignRemoveClick(nameBase, i, existrows, enabledUpdater);
      }
      enabledUpdater();

      var sampleid = deriveRowId(nameBase, 0);
      var sampleel = $it(sampleid);
      var addid = nameBase + "add-row";
      var addel = $it(addid);
      var nextrowind = rowcount;
      addel.onclick = function() {
        var nextrowid = deriveRowId(nameBase, nextrowind);
        var lastrowind = lastRowInd(existrows);
        var lastrow = $it(deriveRowId(nameBase, lastrowind));

        var duprow = RSF.duplicateBranch(sampleel, nextrowid, lastrow);

        assignRemoveClick(nameBase, nextrowind, existrows, enabledUpdater);
        $it(nextrowid + 'input').value = "";
        ++nextrowind;
        enabledUpdater();
      };
    }

  };
}();<commit_msg>Fix for Opera compatibility, strings will be coerced with numbers<commit_after>var DynamicListInput = function() {

  function $it(elementID) {
    return document.getElementById(elementID);
  }

  function deriveRowId(nameBase, index) {
    return nameBase + 'dynamic-list-input-row:' + index + ":";
    }

  function getControl(nameBase, index, extension) {
    var rowid = deriveRowId(nameBase, index);
    var controlid = rowid + extension;
    var control = $it(controlid);
    return control;
    }

  function lastRowInd(existrows) {
    var maxi = -1;
    for (var i in existrows) {
      if (i != 'count' && i > maxi) maxi = i;
      }
    return maxi;
    }

  function makeEnabledUpdater(nameBase, existrows, minlength, maxlength) {
    var removeenabled = true;
    var addcontrol = $it(nameBase + 'add-row');
    return function () {
      var makeremoveenabled = existrows.count > minlength;
      if (removeenabled ^ makeremoveenabled) {
        for (var row in existrows) {
          if (row == 'count') continue;
          var removec = getControl(nameBase, row, 'remove');
          removec.disabled = !makeremoveenabled;
          }
        removeenabled = makeremoveenabled;
        }
      var makeaddenabled = existrows.count < maxlength;
      addcontrol.disabled = !makeaddenabled;

      };
    }

  function assignRemoveClick(nameBase, i, existrows, enabledUpdater) {
    existrows[i] = true;
    ++existrows.count;
    var removec = getControl(nameBase, i, 'remove');

    removec.onclick = function () {
      var rowel = getControl(nameBase, i, '');
      rowel.parentNode.removeChild(rowel);
      delete existrows[i];
      --existrows.count;
      enabledUpdater();
      };
    }

  return {
    init_DynamicListInput: function(nameBase, rowcount, minlength, maxlength) {
      var existrows = new Object();
      existrows.count = 0;

      var enabledUpdater = makeEnabledUpdater(nameBase, existrows, minlength, maxlength);

      for (var i = 0; i < rowcount; ++ i) {
        assignRemoveClick(nameBase, i, existrows, enabledUpdater);
      }
      enabledUpdater();

      var sampleid = deriveRowId(nameBase, 0);
      var sampleel = $it(sampleid);
      var addid = nameBase + "add-row";
      var addel = $it(addid);
      var nextrowind = rowcount;
      addel.onclick = function() {
        var nextrowid = deriveRowId(nameBase, nextrowind);
        var lastrowind = lastRowInd(existrows);
        var lastrow = $it(deriveRowId(nameBase, lastrowind));
        alert ("id " + lastrowind + " row " + lastrow);

        var duprow = RSF.duplicateBranch(sampleel, nextrowid, lastrow);

        assignRemoveClick(nameBase, nextrowind, existrows, enabledUpdater);
        $it(nextrowid + 'input').value = "";
        ++nextrowind;
        enabledUpdater();
      };
    }

  };
}();<|endoftext|>


<commit_before>/// <reference types="Cypress" />

context('Login', () => {
  beforeEach(() => {
    cy.visit(`${Cypress.config().baseUrl}ui/`);
  });

  it('Logs in using UI', () => {
    cy.location('hash').should('equal', '#/login');

    // enter valid username and password
    cy.get('[id=email]').type(Cypress.env('username'));
    cy.get('[name=password]').type(Cypress.env('password'));
    cy.contains('button', 'Log in').click();

    // confirm we have logged in successfully
    cy.location('hash')
      .should('equal', '#/')
      .then(() => cy.getCookie('JWT').should('have.property', 'value'));

    // now we can log out
    cy.contains('.header-dropdown', Cypress.env('username')).click({ force: true });
    cy.contains('span', 'Log out').click({ force: true });
    cy.location('hash').should('equal', '#/login');
  });

  it('fails to access unknown resource', () => {
    cy.request({
      url: Cypress.config().baseUrl + '/users',
      failOnStatusCode: false
    })
      .its('status')
      .should('equal', 404);
  });

  it('Does not log in with invalid password', () => {
    cy.clearCookies()
    cy.location('hash').should('equal', '#/login');
    cy.get('[id=email]').type(Cypress.env('username'));
    cy.get('[name=password]').type('lewrongpassword');
    cy.contains('button', 'Log in')
      .click()
      .wait(3000);

    // still on /login page plus an error is displayed
    cy.location('hash').should('equal', '#/login');
    cy.contains('username or password is incorrect').should('be.visible');
  });
});
<commit_msg>aligned e2e failed login test with changed error message<commit_after>/// <reference types="Cypress" />

context('Login', () => {
  beforeEach(() => {
    cy.visit(`${Cypress.config().baseUrl}ui/`);
  });

  it('Logs in using UI', () => {
    cy.location('hash').should('equal', '#/login');

    // enter valid username and password
    cy.get('[id=email]').type(Cypress.env('username'));
    cy.get('[name=password]').type(Cypress.env('password'));
    cy.contains('button', 'Log in').click();

    // confirm we have logged in successfully
    cy.location('hash')
      .should('equal', '#/')
      .then(() => cy.getCookie('JWT').should('have.property', 'value'));

    // now we can log out
    cy.contains('.header-dropdown', Cypress.env('username')).click({ force: true });
    cy.contains('span', 'Log out').click({ force: true });
    cy.location('hash').should('equal', '#/login');
  });

  it('fails to access unknown resource', () => {
    cy.request({
      url: Cypress.config().baseUrl + '/users',
      failOnStatusCode: false
    })
      .its('status')
      .should('equal', 404);
  });

  it('Does not log in with invalid password', () => {
    cy.clearCookies()
    cy.location('hash').should('equal', '#/login');
    cy.get('[id=email]').type(Cypress.env('username'));
    cy.get('[name=password]').type('lewrongpassword');
    cy.contains('button', 'Log in')
      .click()
      .wait(3000);

    // still on /login page plus an error is displayed
    cy.location('hash').should('equal', '#/login');
    cy.contains('There was a problem logging in').should('be.visible');
  });
});
<|endoftext|>


<commit_before>var RawSource = require("webpack-core/lib/RawSource");

function ChunkManifestPlugin(options) {
  options = options || {};
  this.manifestFilename = options.filename || "manifest.json";
  this.manifestVariable = options.manifestVariable || "webpackManifest";
}
module.exports = ChunkManifestPlugin;

ChunkManifestPlugin.prototype.constructor = ChunkManifestPlugin;
ChunkManifestPlugin.prototype.apply = function(compiler) {
  var manifestFilename = this.manifestFilename;
  var manifestVariable = this.manifestVariable;
  var oldChunkFilename;

  compiler.plugin("this-compilation", function(compilation) {
    var mainTemplate = compilation.mainTemplate;
    mainTemplate.plugin("require-ensure", function(_, chunk, hash) {
      var filename = this.outputOptions.chunkFilename || this.outputOptions.filename;
      var chunkManifest;

      if (filename) {
        chunkManifest = [chunk].reduce(function registerChunk(manifest, c) {
          if(c.id in manifest) return manifest;

          if(c.hasRuntime()) {
            manifest[c.id] = undefined;
          } else {
            manifest[c.id] = mainTemplate.applyPluginsWaterfall("asset-path", filename, {
              hash: hash,
              chunk: c
            });
          }
          return c.chunks.reduce(registerChunk, manifest);
        }, {});
        oldChunkFilename = this.outputOptions.chunkFilename;
        this.outputOptions.chunkFilename = "__CHUNK_MANIFEST__";
        // mark as asset for emitting
        compilation.assets[manifestFilename] = new RawSource(JSON.stringify(chunkManifest));
      }

      return _;
    });
  });

  compiler.plugin("compilation", function(compilation) {
    compilation.mainTemplate.plugin("require-ensure", function(_, chunk, hash, chunkIdVar) {
      if (oldChunkFilename) {
        this.outputOptions.chunkFilename = oldChunkFilename;
      }

      return _.replace("\"__CHUNK_MANIFEST__\"",
        "window[\"" + manifestVariable + "\"][" + chunkIdVar + "]");
    });
  });
};
<commit_msg>Add manifest to the current chunk<commit_after>var RawSource = require("webpack-core/lib/RawSource");

function ChunkManifestPlugin(options) {
  options = options || {};
  this.manifestFilename = options.filename || "manifest.json";
  this.manifestVariable = options.manifestVariable || "webpackManifest";
}
module.exports = ChunkManifestPlugin;

ChunkManifestPlugin.prototype.constructor = ChunkManifestPlugin;
ChunkManifestPlugin.prototype.apply = function(compiler) {
  var manifestFilename = this.manifestFilename;
  var manifestVariable = this.manifestVariable;
  var oldChunkFilename;

  compiler.plugin("this-compilation", function(compilation) {
    var mainTemplate = compilation.mainTemplate;
    mainTemplate.plugin("require-ensure", function(_, chunk, hash) {
      var filename = this.outputOptions.chunkFilename || this.outputOptions.filename;
      var chunkManifest;

      if (filename) {
        chunkManifest = [chunk].reduce(function registerChunk(manifest, c) {
          if(c.id in manifest) return manifest;

          if(c.hasRuntime()) {
            manifest[c.id] = undefined;
          } else {
            manifest[c.id] = mainTemplate.applyPluginsWaterfall("asset-path", filename, {
              hash: hash,
              chunk: c
            });
          }
          return c.chunks.reduce(registerChunk, manifest);
        }, {});
        oldChunkFilename = this.outputOptions.chunkFilename;
        this.outputOptions.chunkFilename = "__CHUNK_MANIFEST__";
        // mark as asset for emitting
        compilation.assets[manifestFilename] = new RawSource(JSON.stringify(chunkManifest));
        chunk.files.push(manifestFilename);
      }

      return _;
    });
  });

  compiler.plugin("compilation", function(compilation) {
    compilation.mainTemplate.plugin("require-ensure", function(_, chunk, hash, chunkIdVar) {
      if (oldChunkFilename) {
        this.outputOptions.chunkFilename = oldChunkFilename;
      }

      return _.replace("\"__CHUNK_MANIFEST__\"",
        "window[\"" + manifestVariable + "\"][" + chunkIdVar + "]");
    });
  });
};
<|endoftext|>


<commit_before>/*
 * @author Micael Gallego (micael.gallego@gmail.com)
 * @author Raquel Díaz González
 */

kurento_room.controller('callController', function ($scope, $window, ServiceParticipant, ServiceRoom, Fullscreen, LxNotificationService) {

    $scope.roomName = ServiceRoom.getRoomName();
    $scope.userName = ServiceRoom.getUserName();
    $scope.participants = ServiceParticipant.getParticipants();
    $scope.kurento = ServiceRoom.getKurento();
    $scope.filter = ServiceRoom.getFilterRequestParam();

    $scope.leaveRoom = function () {

        ServiceRoom.closeKurento();

        ServiceParticipant.removeParticipants();

        //redirect to login
        $window.location.href = '#/login';
    };

    window.onbeforeunload = function () {
        //not necessary if not connected
        if (ServiceParticipant.isConnected()) {
            ServiceRoom.closeKurento();
        }
    };

    $scope.$on("$locationChangeStart", function () {
        console.log("Changed location to: " + document.location);
        if (ServiceParticipant.isConnected()) {
            ServiceRoom.closeKurento();
            ServiceParticipant.removeParticipants();
        }
    });

    $scope.goFullscreen = function () {

        if (Fullscreen.isEnabled())
            Fullscreen.cancel();
        else
            Fullscreen.all();

    };

    $scope.disableMainSpeaker = function (value) {

        var element = document.getElementById("buttonMainSpeaker");
        if (element.classList.contains("md-person")) { //on
            element.classList.remove("md-person");
            element.classList.add("md-recent-actors");
            ServiceParticipant.enableMainSpeaker();
        } else { //off
            element.classList.remove("md-recent-actors");
            element.classList.add("md-person");
            ServiceParticipant.disableMainSpeaker();
        }
    }

    $scope.onOffVolume = function () {
        var localStream = ServiceRoom.getLocalStream();
        var element = document.getElementById("buttonVolume");
        if (element.classList.contains("md-volume-off")) { //on
            element.classList.remove("md-volume-off");
            element.classList.add("md-volume-up");
            localStream.audioEnabled = true;
        } else { //off
            element.classList.remove("md-volume-up");
            element.classList.add("md-volume-off");
            localStream.audioEnabled = false;

        }
    };

    $scope.onOffVideocam = function () {
        var localStream = ServiceRoom.getLocalStream();
        var element = document.getElementById("buttonVideocam");
        if (element.classList.contains("md-videocam-off")) {//on
            element.classList.remove("md-videocam-off");
            element.classList.add("md-videocam");
            localStream.videoEnabled = true;
        } else {//off
            element.classList.remove("md-videocam");
            element.classList.add("md-videocam-off");
            localStream.videoEnabled = false;
        }
    };

    $scope.disconnectStream = function () {
        var localStream = ServiceRoom.getLocalStream();
        var participant = ServiceParticipant.getMainParticipant();
        if (!localStream || !participant) {
            LxNotificationService.alert('Error!', "Not connected yet", 'Ok', function (answer) {
            });
            return false;
        }
        ServiceParticipant.disconnectParticipant(participant);
        ServiceRoom.getKurento().disconnectParticipant(participant.getStream());
    }

    //chat
    $scope.message;

    $scope.sendMessage = function () {
        console.log("Sending message", $scope.message);
        var kurento = ServiceRoom.getKurento();
        kurento.sendMessage($scope.roomName, $scope.userName, $scope.message);
        $scope.message = "";
    };

    //open or close chat when click in chat button
    $scope.toggleChat = function () {
        var selectedEffect = "slide";
        // most effect types need no options passed by default
        var options = {direction: "right"};
        if ($("#effect").is(':visible')) {
            $("#content").animate({width: '100%'}, 500);
        } else {
            $("#content").animate({width: '80%'}, 500);
        }
        // run the effect
        $("#effect").toggle(selectedEffect, options, 500);
    };

    var style = {
        hat: {
            "-1": "btn--indigo md-mood",
            "0": "btn--amber md-face-unlock"
        },
        marker: {
            "-1": "btn--indigo md-grid-off",
            "0": "btn--amber md-grid-on",
            "1": "btn--red md-grid-on"
        }
    };

    $scope.filterIndex = "-1"; //off
    $scope.filterState;
    $scope.filterStyle;
    updateFilterValues();

    function updateFilterValues() {
        $scope.filterState = parseInt($scope.filterIndex) < 0 ? "off" : "on";
        $scope.filterStyle = style[$scope.filter][$scope.filterIndex];
    }

    $scope.applyFilter = function () {
        var reqParams = {};
        if ($scope.filter === "marker") {
            reqParams[$scope.filter] = parseInt($scope.filterIndex);
        } else {
            if (parseInt($scope.filterIndex) < 0) { //off -> on
                $scope.filterIndex = "0";
                reqParams[$scope.filter] = true;
            } else { //on -> off
                $scope.filterIndex = "-1";
                reqParams[$scope.filter] = false;
            }
        }

        ServiceRoom.getKurento().sendCustomRequest(reqParams, function (error, response) {
            if (error) {
                console.error("Unable to toggle filter, currently " +
                    $scope.filterState, error);
                LxNotificationService.alert('Error!',
                    "Unable to toggle filter, currently " + $scope.filterState,
                    'Ok', function (answer) {
                    });
                return false;
            } else {
                if ($scope.filter === "marker") {
                    return;
                }

                updateFilterValues();
                console.log("Toggled filter " + $scope.filterState + " (idx " +
                    $scope.filterIndex + ")");
            }
        });
    };

    ServiceParticipant.addEventListener("marker-filter-status-changed", function (status) {
        console.error("Filter status changed", status);
        if ($scope.filter === "marker") {
            $scope.filterIndex = status;
            updateFilterValues();
            $scope.$apply();
        }
    });
});

<commit_msg>[room-demo] Lowers level of debug console msg<commit_after>/*
 * @author Micael Gallego (micael.gallego@gmail.com)
 * @author Raquel Díaz González
 */

kurento_room.controller('callController', function ($scope, $window, ServiceParticipant, ServiceRoom, Fullscreen, LxNotificationService) {

    $scope.roomName = ServiceRoom.getRoomName();
    $scope.userName = ServiceRoom.getUserName();
    $scope.participants = ServiceParticipant.getParticipants();
    $scope.kurento = ServiceRoom.getKurento();
    $scope.filter = ServiceRoom.getFilterRequestParam();

    $scope.leaveRoom = function () {

        ServiceRoom.closeKurento();

        ServiceParticipant.removeParticipants();

        //redirect to login
        $window.location.href = '#/login';
    };

    window.onbeforeunload = function () {
        //not necessary if not connected
        if (ServiceParticipant.isConnected()) {
            ServiceRoom.closeKurento();
        }
    };

    $scope.$on("$locationChangeStart", function () {
        console.log("Changed location to: " + document.location);
        if (ServiceParticipant.isConnected()) {
            ServiceRoom.closeKurento();
            ServiceParticipant.removeParticipants();
        }
    });

    $scope.goFullscreen = function () {

        if (Fullscreen.isEnabled())
            Fullscreen.cancel();
        else
            Fullscreen.all();

    };

    $scope.disableMainSpeaker = function (value) {

        var element = document.getElementById("buttonMainSpeaker");
        if (element.classList.contains("md-person")) { //on
            element.classList.remove("md-person");
            element.classList.add("md-recent-actors");
            ServiceParticipant.enableMainSpeaker();
        } else { //off
            element.classList.remove("md-recent-actors");
            element.classList.add("md-person");
            ServiceParticipant.disableMainSpeaker();
        }
    }

    $scope.onOffVolume = function () {
        var localStream = ServiceRoom.getLocalStream();
        var element = document.getElementById("buttonVolume");
        if (element.classList.contains("md-volume-off")) { //on
            element.classList.remove("md-volume-off");
            element.classList.add("md-volume-up");
            localStream.audioEnabled = true;
        } else { //off
            element.classList.remove("md-volume-up");
            element.classList.add("md-volume-off");
            localStream.audioEnabled = false;

        }
    };

    $scope.onOffVideocam = function () {
        var localStream = ServiceRoom.getLocalStream();
        var element = document.getElementById("buttonVideocam");
        if (element.classList.contains("md-videocam-off")) {//on
            element.classList.remove("md-videocam-off");
            element.classList.add("md-videocam");
            localStream.videoEnabled = true;
        } else {//off
            element.classList.remove("md-videocam");
            element.classList.add("md-videocam-off");
            localStream.videoEnabled = false;
        }
    };

    $scope.disconnectStream = function () {
        var localStream = ServiceRoom.getLocalStream();
        var participant = ServiceParticipant.getMainParticipant();
        if (!localStream || !participant) {
            LxNotificationService.alert('Error!', "Not connected yet", 'Ok', function (answer) {
            });
            return false;
        }
        ServiceParticipant.disconnectParticipant(participant);
        ServiceRoom.getKurento().disconnectParticipant(participant.getStream());
    }

    //chat
    $scope.message;

    $scope.sendMessage = function () {
        console.log("Sending message", $scope.message);
        var kurento = ServiceRoom.getKurento();
        kurento.sendMessage($scope.roomName, $scope.userName, $scope.message);
        $scope.message = "";
    };

    //open or close chat when click in chat button
    $scope.toggleChat = function () {
        var selectedEffect = "slide";
        // most effect types need no options passed by default
        var options = {direction: "right"};
        if ($("#effect").is(':visible')) {
            $("#content").animate({width: '100%'}, 500);
        } else {
            $("#content").animate({width: '80%'}, 500);
        }
        // run the effect
        $("#effect").toggle(selectedEffect, options, 500);
    };

    var style = {
        hat: {
            "-1": "btn--indigo md-mood",
            "0": "btn--amber md-face-unlock"
        },
        marker: {
            "-1": "btn--indigo md-grid-off",
            "0": "btn--amber md-grid-on",
            "1": "btn--red md-grid-on"
        }
    };

    $scope.filterIndex = "-1"; //off
    $scope.filterState;
    $scope.filterStyle;
    updateFilterValues();

    function updateFilterValues() {
        $scope.filterState = parseInt($scope.filterIndex) < 0 ? "off" : "on";
        $scope.filterStyle = style[$scope.filter][$scope.filterIndex];
    }

    $scope.applyFilter = function () {
        var reqParams = {};
        if ($scope.filter === "marker") {
            reqParams[$scope.filter] = parseInt($scope.filterIndex);
        } else {
            if (parseInt($scope.filterIndex) < 0) { //off -> on
                $scope.filterIndex = "0";
                reqParams[$scope.filter] = true;
            } else { //on -> off
                $scope.filterIndex = "-1";
                reqParams[$scope.filter] = false;
            }
        }

        ServiceRoom.getKurento().sendCustomRequest(reqParams, function (error, response) {
            if (error) {
                console.error("Unable to toggle filter, currently " +
                    $scope.filterState, error);
                LxNotificationService.alert('Error!',
                    "Unable to toggle filter, currently " + $scope.filterState,
                    'Ok', function (answer) {
                    });
                return false;
            } else {
                if ($scope.filter === "marker") {
                    return;
                }

                updateFilterValues();
                console.log("Toggled filter " + $scope.filterState + " (idx " +
                    $scope.filterIndex + ")");
            }
        });
    };

    ServiceParticipant.addEventListener("marker-filter-status-changed", function (status) {
        console.log("Filter status changed", status);
        if ($scope.filter === "marker") {
            $scope.filterIndex = status;
            updateFilterValues();
            $scope.$apply();
        }
    });
});

<|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.cron',
  name: 'Cron',
  extends: 'foam.nanos.script.Script',

  imports: [ 'cronDAO as scriptDAO' ],

  javaImports: [
    'foam.dao.DAO',
    'foam.nanos.notification.Notification',
    'foam.util.SafetyUtil',
    'java.util.Date'
  ],

  documentation: 'FOAM class that models a Cron script',

  tableColumns: [
    'id', 'enabled', 'server', 'description', 'lastDuration', 'status', 'run'
  ],

  searchColumns: ['id', 'description'],

  properties: [
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.cron.Schedule',
      name: 'schedule',
      hidden: true,
      javaFactory: `return new NeverSchedule.Builder().build();`
    },
    {
      class: 'DateTime',
      name: 'scheduledTime',
      documentation: `Scheduled time to run Cron script.`,
      hidden: true,
      javaFactory: 'return getNextScheduledTime();'
    },
    {
      class: 'Boolean',
      name: 'enabled',
      value: false
    }
  ],

  methods: [
    {
      name: 'runScript',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      type: 'Void',
      javaCode:
`DAO notification = (DAO) x.get("notificationDAO");

Notification cronStartNotify = new Notification();
cronStartNotify.setBody("Cron STARTED - " + this.getId() + " " + this.getDescription());
notification.put(cronStartNotify);

super.runScript(x);

Notification cronEndNotify = new Notification();
cronEndNotify.setBody("Cron ENDED - " + this.getId() + " " + this.getDescription());
notification.put(cronEndNotify);

setScheduledTime(getNextScheduledTime());`
    },
    {
      name: 'getNextScheduledTime',
      type: 'Date',
      javaCode:
`
return getSchedule().getNextScheduledTime(
  new Date(System.currentTimeMillis())
);
`
    }
  ]
});
<commit_msg>Fix up creation of NeverSchedule<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.cron',
  name: 'Cron',
  extends: 'foam.nanos.script.Script',

  imports: [ 'cronDAO as scriptDAO' ],

  javaImports: [
    'foam.dao.DAO',
    'foam.nanos.notification.Notification',
    'foam.util.SafetyUtil',
    'java.util.Date'
  ],

  documentation: 'FOAM class that models a Cron script',

  tableColumns: [
    'id', 'enabled', 'server', 'description', 'lastDuration', 'status', 'run'
  ],

  searchColumns: ['id', 'description'],

  properties: [
    {
      class: 'FObjectProperty',
      of: 'foam.nanos.cron.Schedule',
      name: 'schedule',
      hidden: true,
      javaFactory: `return new NeverSchedule.Builder(getX()).build();`
    },
    {
      class: 'DateTime',
      name: 'scheduledTime',
      documentation: `Scheduled time to run Cron script.`,
      hidden: true,
      javaFactory: 'return getNextScheduledTime();'
    },
    {
      class: 'Boolean',
      name: 'enabled',
      value: false
    }
  ],

  methods: [
    {
      name: 'runScript',
      args: [
        {
          name: 'x',
          type: 'Context'
        }
      ],
      type: 'Void',
      javaCode:
`DAO notification = (DAO) x.get("notificationDAO");

Notification cronStartNotify = new Notification();
cronStartNotify.setBody("Cron STARTED - " + this.getId() + " " + this.getDescription());
notification.put(cronStartNotify);

super.runScript(x);

Notification cronEndNotify = new Notification();
cronEndNotify.setBody("Cron ENDED - " + this.getId() + " " + this.getDescription());
notification.put(cronEndNotify);

setScheduledTime(getNextScheduledTime());`
    },
    {
      name: 'getNextScheduledTime',
      type: 'Date',
      javaCode:
`
return getSchedule().getNextScheduledTime(
  new Date(System.currentTimeMillis())
);
`
    }
  ]
});
<|endoftext|>


<commit_before>var Client = require('node-rest-client').Client;
var client = new Client();

// Keys are stringified userId-questionId pairs
// Values are the last activity time
var watcher = {};

var url = 'https://api.stackexchange.com/2.2/';
var questionAnswerUrlAppend = '?site=stackoverflow';

function getLastActivityTime(questionId) {
	var questionUrl = url + '/questions/' + questionId + questionAnswerUrlAppend;

	console.log(questionUrl);

	var lastActivityDate = 0;

    client.get(questionUrl, function (data, response) {
    	lastActivityDate = data.items[0].last_activity_date;
        console.log("Last Activity Date - " + lastActivityDate);
    });

    return lastActivityDate;
}

function checkForUpdates(callback) {
	console.log("Checking for updates");

	console.log(watcher);

	for(toWatchObj in watcher) {
		currentTime = watcher[toWatchObj];

		console.log(toWatchObj);

		var questionId = toWatchObj.split(",")[1];
		var userId = toWatchObj.split(",")[0];

		console.log("Question id: " + questionId);

		var newTime = getLastActivityTime(questionId);

		console.log("Current known last activity time: " + currentTime);

		if (newTime + 10 > currentTime){
			console.log("Sending message to bot")
			event = {'userId': userId}
			callback(userId, questionId);
			watcher[questionId] =  newTime;
		}
	}
}

module.exports = {

	getWatchList: function(userId){
		console.log("Inside getWatchList");
		var map = {};
		for(toWatchObj in watcher) {
			currentTime = watcher[toWatchObj];

			console.log(toWatchObj);

			var questionId = toWatchObj.split(",")[1];
			var userId = toWatchObj.split(",")[0];

			if(!(userId in map)){
				map[userId]=[];
			}
			map[userId].push(questionId);

		}
		return map;
	},
	addWatcher: function (userId, questionId) {
		console.log("Now watching for userId = " + userId + ", questionId = " + questionId);
		watcher[[userId, questionId]] = getLastActivityTime(questionId);
	},

	startWatcher: function (callback) {
		console.log("Starting watcher");
		setInterval(function(){checkForUpdates(callback);}, 300000);
	}
};<commit_msg>Fix mocking in watcher<commit_after>var Client = require('node-rest-client').Client;
var client = new Client();

// Keys are stringified userId-questionId pairs
// Values are the last activity time
var watcher = {};

var url = 'https://api.stackexchange.com/2.2/';
var questionAnswerUrlAppend = '?site=stackoverflow';

function getLastActivityTime(questionId) {
	var questionUrl = url + '/questions/' + questionId + questionAnswerUrlAppend;

	console.log(questionUrl);

	var lastActivityDate = 0;

    client.get(questionUrl, function (data, response) {
    	lastActivityDate = data.items[0].last_activity_date;
        console.log("Last Activity Date - " + lastActivityDate);
    });

    return lastActivityDate;
}

function checkForUpdates(callback) {
	console.log("Checking for updates");

	console.log(watcher);

	for(toWatchObj in watcher) {
		currentTime = watcher[toWatchObj];

		console.log(toWatchObj);

		var questionId = toWatchObj.split(",")[1];
		var userId = toWatchObj.split(",")[0];

		console.log("Question id: " + questionId);

		var newTime = getLastActivityTime(questionId);

		console.log("Current known last activity time: " + currentTime);

		if (newTime > currentTime){
			console.log("Sending message to bot")
			event = {'userId': userId}
			callback(userId, questionId);
			watcher[questionId] =  newTime;
		}
	}
}

module.exports = {

	getWatchList: function(userId){
		console.log("Inside getWatchList");
		var map = {};
		for(toWatchObj in watcher) {
			currentTime = watcher[toWatchObj];

			console.log(toWatchObj);

			var questionId = toWatchObj.split(",")[1];
			var userId = toWatchObj.split(",")[0];

			if(!(userId in map)){
				map[userId]=[];
			}
			map[userId].push(questionId);

		}
		return map;
	},
	addWatcher: function (userId, questionId) {
		console.log("Now watching for userId = " + userId + ", questionId = " + questionId);
		watcher[[userId, questionId]] = getLastActivityTime(questionId);
	},

	startWatcher: function (callback) {
		console.log("Starting watcher");
		setInterval(function(){checkForUpdates(callback);}, 300000);
	}
};<|endoftext|>


<commit_before>var spyApp = angular.module('spyApp', []);

spyApp.run(['$http', function ($http) {
  $http.defaults.headers.common.Accept = 'application/json';
}]);

spyApp.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) {

  $scope.host = 'landregistry.local';

  $scope.apps = [
    {"name": 'system-of-record', "href": "http://system-of-record." + $scope.host, "path": "/titles/"},
    {"name": 'property-frontend', "href": "http://property-frontend." + $scope.host, "path": "/property/" },
    {"name": 'search-api', "href": "http://search-api." + $scope.host, "path": "/titles/" },
    {"name": 'service-frontend', "href": "http://service-frontend." + $scope.host, "path": "/property/" },
    {"name": 'historian', "href": "http://historian." + $scope.host, "path": "/titles/" }
  ];

  $scope.results = {};
  $scope.show = function (key) {
    return $scope.results[key.replace('-','')] || 'dontknow';
  }

  $scope.spy = function() {
    for (var j=0; j<$scope.apps.length; j++) {
      (function (i) {
        var app = $scope.apps[i];
        var url = app.href + app.path + $scope.title;
        var key = app.name.replace('-', '');
        try {
          if (app.name === 'service-frontend') {
            $scope.results[key] = 'needslogin';
          } else {
            $http.get(url).
              success(function(data, status, headers, config) {
                $scope.results[key] = 'good';
              }).
              error(function(data, status, headers, config) {
                $scope.results[key] = 'bad';
              });
          }
        } catch (err) {
          console.log("fatal", err);
        }
      })(j);
    }
  };
}]);
<commit_msg>auth search api too<commit_after>var spyApp = angular.module('spyApp', []);

spyApp.run(['$http', function ($http) {
  $http.defaults.headers.common.Accept = 'application/json';
}]);

spyApp.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) {

  $scope.host = 'landregistry.local';

  $scope.apps = [
    {"name": 'system-of-record', "href": "http://system-of-record." + $scope.host, "path": "/titles/"},
    {"name": 'property-frontend', "href": "http://property-frontend." + $scope.host, "path": "/property/" },
    {"name": 'search-api', "href": "http://search-api." + $scope.host, "path": "/titles/" },
    {"name": 'search-api', "href": "http://search-api." + $scope.host, "path": "/auth/titles/" },
    {"name": 'service-frontend', "href": "http://service-frontend." + $scope.host, "path": "/property/" },
    {"name": 'historian', "href": "http://historian." + $scope.host, "path": "/titles/" }
  ];

  $scope.results = {};
  $scope.show = function (key) {
    return $scope.results[key.replace('-','')] || 'dontknow';
  }

  $scope.spy = function() {
    for (var j=0; j<$scope.apps.length; j++) {
      (function (i) {
        var app = $scope.apps[i];
        var url = app.href + app.path + $scope.title;
        var key = app.name.replace('-', '');
        try {
          if (app.name === 'service-frontend') {
            $scope.results[key] = 'needslogin';
          } else {
            $http.get(url).
              success(function(data, status, headers, config) {
                $scope.results[key] = 'good';
              }).
              error(function(data, status, headers, config) {
                $scope.results[key] = 'bad';
              });
          }
        } catch (err) {
          console.log("fatal", err);
        }
      })(j);
    }
  };
}]);
<|endoftext|>


<commit_before>'use strict';

var logger = require('log4js').getLogger('Unit-Test');
var filter = require(__dirname + '/../ecplurkbot/lib/contentfilter');

exports['Test verifiyKeyword'] = {

	'Test keywords found(One keywords in list)': function(test) {

		var list = [{"Hello":"World!"}];

		var content = "Hello";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(result !== null, 'It should return a string! ');

		test.equal(result, 'World!', 'It should return World!' );

		test.done();
	},

	'Test keywords found(Two keywords in list)': function(test) {

		var list = [{"Hello":"World!","Kagero":"Love"}];

		var content = "Kagero";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(result !== null, 'It should return a string! ');

		test.equal(result, 'Love', 'It should return Love!' );

		test.done();
	},

	'Test keywords not found(no keywords in list)': function(test) {

		var list = [];

		var content = "Kagero";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(!result, 'It should return undefined! ');

		test.done();
	},

	'Test keywords not found(one keywords in list)': function(test) {

		var list = [{"Hello":"World!"}];

		var content = "Kagero";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(!result, 'It should return undefined! ');

		test.done();
	},

	'Test keywords not found(two keywords in list)': function(test) {

		var list = [{"Hello":"World!","Kagero":"Love"}];

		var content = "Kuroshio";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(!result, 'It should return undefined! ');

		test.done();
	}
}
<commit_msg>Fix test case error<commit_after>'use strict';

var logger = require('log4js').getLogger('Unit-Test');
var filter = require(__dirname + '/../ecplurkbot/lib/contentfilter');

exports['Test verifiyKeyword'] = {

	'Test keywords found(One keywords in list)': function(test) {

		var list = [{"Hello":"World!"}];

		var content = "Hello";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(result !== null, 'It should return a string! ');

		test.equal(result, 'World!', 'It should return World!' );

		test.done();
	},

	'Test keywords found(Two keywords in list)': function(test) {

		var list = [{"Hello":"World!"},{"Kagero":"Love"}];

		var content = "Kagero";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(result !== null, 'It should return a string! ');

		test.equal(result, 'Love', 'It should return Love!' );

		test.done();
	},

	'Test keywords not found(no keywords in list)': function(test) {

		var list = [];

		var content = "Kagero";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(!result, 'It should return undefined! ');

		test.done();
	},

	'Test keywords not found(one keywords in list)': function(test) {

		var list = [{"Hello":"World!"}];

		var content = "Kagero";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(!result, 'It should return undefined! ');

		test.done();
	},

	'Test keywords not found(two keywords in list)': function(test) {

		var list = [{"Hello":"World!"},{"Kagero":"Love"}];

		var content = "Kuroshio";

		var result = filter.verifiyKeyword(list,content);

		logger.debug('Keywords:' + content);

		logger.debug('Response: ' + result);

		test.ok(!result, 'It should return undefined! ');

		test.done();
	}
}
<|endoftext|>


<commit_before>import domCompare from 'dom-compare';
import diagramsRenderer from 'src/logic/renderer/diagrams';
import documentPackager from 'src/logic/documentPackager';
import diagramsInput from './reference/diagrams/diagramsInput.txt';
import seqDiagramsInput from './reference/diagrams/seqDiagramsInput.txt';
import seqDiagramsOutput from './reference/diagrams/seqDiagramsOutput.txt';
import flowChartsInput from './reference/diagrams/flowChartsInput.txt';
import flowChartsOutput from './reference/diagrams/flowChartsOutput.txt';
import graphvizInput from './reference/diagrams/graphvizInput.txt';
import graphvizOutput from './reference/diagrams/graphvizOutput.txt';
import mermaidInput from './reference/diagrams/mermaidInput.txt';
import mermaidOutput from './reference/diagrams/mermaidOutput.txt';
import diagramsRenderedOutput from './reference/diagrams/diagramsRenderedOutput.txt';

// Parse markdown content for test use
const fullHtmlData = documentPackager.getHtmlData(diagramsInput);
const seqHtmlData = documentPackager.getHtmlData(seqDiagramsInput);
const flowHtmlData = documentPackager.getHtmlData(flowChartsInput);
const vizHtmlData = documentPackager.getHtmlData(graphvizInput);
const mermaidHtmlData = documentPackager.getHtmlData(mermaidInput);

// Use iframe as a psuedo browser for testing
const iframe = document.createElement('IFRAME');
let iframeDoc;
let toRender;

// Regex helper function
function regexHelper(diff) {
  const ignoredAttr = ['id', 'marker-end', 'x1', 'x2', 'x', 'y1', 'y2', 'y',
    'width', 'height', 'd', 'dy', 'r', 'style', 'viewBox', 'transform', 'points'];
  const errorArray = [];
  const missedArray = [];
  diff.forEach((line) => {
    const regex1 = /(.*) '(.*)':.* '(.*)'.*'(.*)'/g;
    const regex2 = /(.*) '(.*)' (is missed)/g;
    const regex3 = /Extra attribute '(.*)'/g;
    const regex4 = /Expected text '(.*)' instead of '(.*)'/g;
    const arr1 = regex1.exec(line.message);
    const arr2 = regex2.exec(line.message);
    const arr3 = regex3.exec(line.message);
    const arr4 = regex4.exec(line.message);
    if (arr1 !== null) {
      // ignore expected value error
      if (arr1[1] === 'Attribute') {
        if (arr1[2] === 'font-family') {
          const expected = arr1[3].replace(/'/g, '');
          const actual = arr1[4].replace(/"/g, '');
          if (expected !== actual) {
            console.log(line.message);
            errorArray.push(line);
          }
        } else if (ignoredAttr.indexOf(arr1[2]) === -1) {
          if (arr1[3] !== arr1[4]) {
            console.log(line.message);
            errorArray.push(line);
          }
        }
      }
    } else if (arr2 !== null) {
      // ignore missing attribute error
      if (arr2[2].match(/xlink:/g) !== null) {
        missedArray.push(arr2[2].replace(/xlink:/g, ''));
      } else {
        console.log(line.message);
        errorArray.push(line);
      }
    } else if (arr3 !== null) {
      // ignore extra attribute error
      if (missedArray.length !== 0) {
        const pos = missedArray.indexOf(arr3[1]);
        if (pos !== -1) {
          missedArray.splice(pos, 1);
        } else {
          console.log(line.message);
          errorArray.push(line);
        }
      }
    } else if (arr4 !== null) {
      // check the node the error is thrown and concantate the strings!
      console.log(line);
    } else {
      // add into error array
      errorArray.push(line);
      console.log(line.message);
    }
  });
  return errorArray;
}

describe('Renderer', () => {
  before(() => {
    document.body.appendChild(iframe);
    iframe.onload = function() {
      iframeDoc = iframe.contentWindow.document;
      iframeDoc.write('

');
    };
    iframe.onload();
  });

  describe('Diagrams Renderer', () => {
    it('should handle empty data', (done) => {
      toRender = iframeDoc.getElementsByTagName('div')[0];
      diagramsRenderer(toRender)
      .then((output) => {
        expect(toRender.innerHTML).to.equal('');
        done();
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of sequence diagrams', (done) => {
      // Retrieve HTML string from getHtmlData
      seqHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // const renderedContent = toRender.innerHTML.replace(/"/g, '\'');
          const outputContent = seqDiagramsOutput.replace(/(\r\n)/g, '\n');
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of flow charts', (done) => {
      flowHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // We are comparing length because the draw function (using Raphaël) will input a different id value for
          // certain tags on different calls, but content length will always be the same.
          // let renderedContent = toRender.innerHTML.replace(/>/g, '>');
          // renderedContent = renderedContent.replace(/"/g, '\'');
          const outputContent = flowChartsOutput.replace(/(\r\n)/g, '\n');
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of graphviz diagrams', (done) => {
      vizHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // const renderedContent = toRender.innerHTML.replace(/>/g, '>');
          const outputContent = graphvizOutput.replace(/(\r\n)/g, '\n');
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of mermaid diagrams', (done) => {
      mermaidHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          const renderedContent = toRender.innerHTML.replace(/>/g, '>');
          const outputContent = mermaidOutput.replace(/(\r\n)/g, '\n');
          // We are comparing length here also because mermaid diagrams being drawn cannot compare directly due to mermaidAPI
          // setting custom numbered/tagged attribute values on different runs of the test, however content length will always be the same.
          // e.g. 1st run: <text dy="1em" y="3" x="0" fill="#000" stroke="none" font-size="10" style="text-anchor: middle;">
          // 2nd run could be: y attribute value can be 4 instead of 3 etc
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of all types of diagrams together', (done) => {
      fullHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // Replace special characters that have been converted into ampersands
          console.log(toRender);
          let renderedContent = toRender.innerHTML.replace(/>/g, '>');
          renderedContent = renderedContent.replace(/(\\')/g, '\'');
          renderedContent = renderedContent.replace(/"/g, '"');
          renderedContent = renderedContent.replace(/-/g, '-');
          const outputContent = diagramsRenderedOutput.replace(/(\r\n)/g, '\n');
          // We are comparing length here again because flowCharts being drawn cannot compare directly because the draw function (using Raphaël)
          // will input a different id value for certain tags on different calls, but content length will always be the same.
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });
  });
});
<commit_msg>Debug text difference error<commit_after>import domCompare from 'dom-compare';
import diagramsRenderer from 'src/logic/renderer/diagrams';
import documentPackager from 'src/logic/documentPackager';
import diagramsInput from './reference/diagrams/diagramsInput.txt';
import seqDiagramsInput from './reference/diagrams/seqDiagramsInput.txt';
import seqDiagramsOutput from './reference/diagrams/seqDiagramsOutput.txt';
import flowChartsInput from './reference/diagrams/flowChartsInput.txt';
import flowChartsOutput from './reference/diagrams/flowChartsOutput.txt';
import graphvizInput from './reference/diagrams/graphvizInput.txt';
import graphvizOutput from './reference/diagrams/graphvizOutput.txt';
import mermaidInput from './reference/diagrams/mermaidInput.txt';
import mermaidOutput from './reference/diagrams/mermaidOutput.txt';
import diagramsRenderedOutput from './reference/diagrams/diagramsRenderedOutput.txt';

// Parse markdown content for test use
const fullHtmlData = documentPackager.getHtmlData(diagramsInput);
const seqHtmlData = documentPackager.getHtmlData(seqDiagramsInput);
const flowHtmlData = documentPackager.getHtmlData(flowChartsInput);
const vizHtmlData = documentPackager.getHtmlData(graphvizInput);
const mermaidHtmlData = documentPackager.getHtmlData(mermaidInput);

// Use iframe as a psuedo browser for testing
const iframe = document.createElement('IFRAME');
let iframeDoc;
let toRender;

// Regex helper function
function regexHelper(diff) {
  const ignoredAttr = ['id', 'marker-end', 'x1', 'x2', 'x', 'y1', 'y2', 'y',
    'width', 'height', 'd', 'dy', 'r', 'style', 'viewBox', 'transform', 'points'];
  const missedArray = [];
  let errorArray = [];
  let textExpected = new Map();
  let textActual = new Map();
  diff.forEach((line) => {
    const regex1 = /(.*) '(.*)':.* '(.*)'.*'(.*)'/g;
    const regex2 = /(.*) '(.*)' (is missed)/g;
    const regex3 = /Extra attribute '(.*)'/g;
    const regex4 = /Expected text '(.*)' instead of '(.*)'/g;
    const arr1 = regex1.exec(line.message);
    const arr2 = regex2.exec(line.message);
    const arr3 = regex3.exec(line.message);
    const arr4 = regex4.exec(line.message);
    if (arr1 !== null) {
      // ignore expected value error
      if (arr1[1] === 'Attribute') {
        if (arr1[2] === 'font-family') {
          const expected = arr1[3].replace(/'/g, '');
          const actual = arr1[4].replace(/"/g, '');
          if (expected !== actual) {
            console.log(line.message);
            errorArray.push(line);
          }
        } else if (ignoredAttr.indexOf(arr1[2]) === -1) {
          if (arr1[3] !== arr1[4]) {
            console.log(line.message);
            errorArray.push(line);
          }
        }
      }
    } else if (arr2 !== null) {
      // ignore missing attribute error
      if (arr2[2].match(/xlink:/g) !== null) {
        missedArray.push(arr2[2].replace(/xlink:/g, ''));
      } else {
        console.log(line.message);
        errorArray.push(line);
      }
    } else if (arr3 !== null) {
      // ignore extra attribute error
      if (missedArray.length !== 0) {
        const pos = missedArray.indexOf(arr3[1]);
        if (pos !== -1) {
          missedArray.splice(pos, 1);
        } else {
          console.log(line.message);
          errorArray.push(line);
        }
      }
    } else if (arr4 !== null) {
      // check the node the error is thrown and concantate the strings!
      const regexNode = /(.*)\//g;
      const arrNode = regexNode.exec(line.node);
      const key = arrNode[1];
      errorArray.push(line);
      console.log(line);
      if (textExpected.has(key) && textActual.has(key)) {
        const currExpectedText = textExpected.get(key);
        const currActualText = textActual.get(key);
        textExpected.set(key, currExpectedText + arr4[1]);
        textActual.set(key, currActualText + arr4[2]);
      } else {
        textExpected.set(key, arr4[1]);
        textActual.set(key, arr4[2]);
      }

      if (textExpected.get(key) === textActual.get(key)) {
        errorArray = errorArray.filter((errorLine) => {
          return errorLine.node === arrNode[1]+arrNode[2];
        });
      }
    } else {
      // add into error array
      errorArray.push(line);
      console.log(line.message);
    }
  });
  return errorArray;
}

describe('Renderer', () => {
  before(() => {
    document.body.appendChild(iframe);
    iframe.onload = function() {
      iframeDoc = iframe.contentWindow.document;
      iframeDoc.write('

');
    };
    iframe.onload();
  });

  describe('Diagrams Renderer', () => {
    it('should handle empty data', (done) => {
      toRender = iframeDoc.getElementsByTagName('div')[0];
      diagramsRenderer(toRender)
      .then((output) => {
        expect(toRender.innerHTML).to.equal('');
        done();
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of sequence diagrams', (done) => {
      // Retrieve HTML string from getHtmlData
      seqHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // const renderedContent = toRender.innerHTML.replace(/"/g, '\'');
          const outputContent = seqDiagramsOutput.replace(/(\r\n)/g, '\n');
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of flow charts', (done) => {
      flowHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // We are comparing length because the draw function (using Raphaël) will input a different id value for
          // certain tags on different calls, but content length will always be the same.
          // let renderedContent = toRender.innerHTML.replace(/>/g, '>');
          // renderedContent = renderedContent.replace(/"/g, '\'');
          const outputContent = flowChartsOutput.replace(/(\r\n)/g, '\n');
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of graphviz diagrams', (done) => {
      vizHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // const renderedContent = toRender.innerHTML.replace(/>/g, '>');
          const outputContent = graphvizOutput.replace(/(\r\n)/g, '\n');
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of mermaid diagrams', (done) => {
      mermaidHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          const renderedContent = toRender.innerHTML.replace(/>/g, '>');
          const outputContent = mermaidOutput.replace(/(\r\n)/g, '\n');
          // We are comparing length here also because mermaid diagrams being drawn cannot compare directly due to mermaidAPI
          // setting custom numbered/tagged attribute values on different runs of the test, however content length will always be the same.
          // e.g. 1st run: <text dy="1em" y="3" x="0" fill="#000" stroke="none" font-size="10" style="text-anchor: middle;">
          // 2nd run could be: y attribute value can be 4 instead of 3 etc
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });

    it('should handle drawing of all types of diagrams together', (done) => {
      fullHtmlData.then((output) => {
        toRender.innerHTML = output;
        diagramsRenderer(toRender)
        .then((out) => {
          // Replace special characters that have been converted into ampersands
          let renderedContent = toRender.innerHTML.replace(/>/g, '>');
          renderedContent = renderedContent.replace(/(\\')/g, '\'');
          renderedContent = renderedContent.replace(/"/g, '"');
          renderedContent = renderedContent.replace(/-/g, '-');
          const outputContent = diagramsRenderedOutput.replace(/(\r\n)/g, '\n');
          // We are comparing length here again because flowCharts being drawn cannot compare directly because the draw function (using Raphaël)
          // will input a different id value for certain tags on different calls, but content length will always be the same.
          const eleOutput = iframeDoc.createElement('DIV');
          eleOutput.innerHTML = outputContent;
          const results = domCompare.compare(eleOutput, toRender);
          const diff = results.getDifferences();
          const errorArray = regexHelper(diff);
          expect(errorArray.length).to.equal(0);
          done();
        })
        .catch((error) => {
          done(error);
        });
      })
      .catch((error) => {
        done(error);
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

var assign = require('core-js/modules/$.object-assign');
var isShallowEqual = require('is-equal-shallow');

var uriInfo = require('./uri-info');

var states = {
  ACTIVE:   'active',
  INACTIVE: 'inactive',
  ERRORED:  'errored',
};

/** The default H state for a new browser tab */
var DEFAULT_STATE = {
  /** Whether or not H is active on the page */
  state: states.INACTIVE,
  /** The count of annotations on the page visible to the user,
   * as returned by the badge API
   */
  annotationCount: 0,
  /** Whether or not the H sidebar has been installed onto the page by
   * the extension
   */
  extensionSidebarInstalled: false,
  /** Whether the tab is loaded and ready for the sidebar to be installed. */
  ready: false,
  /** The error for the current tab. */
  error: undefined,
};

/** TabState stores the H state for a tab. This state includes:
 *
 * - Whether the extension has been activated on a tab
 * - Whether the sidebar is currently installed on a tab
 * - The count of annotations visible to the user on the URL currently
 *   displayed in the tab.
 *
 * The H state for a tab is updated via the setState() method and
 * retrieved via getState().
 *
 * When the H state for a tab changes, the `onchange()` callback will
 * be triggered with the tab ID and current and previous states.
 *
 * initialState - An Object of tabId/state keys. Used when loading state
 *   from a persisted store such as localStorage. This will be merged with
 *   the default state for a tab.
 * onchange     - A function that recieves onchange(tabId, current).
 */
function TabState(initialState, onchange) {
  var _this = this;
  var currentState;

  this.onchange = onchange || null;

  /** Replaces the H state for all tabs with the state data
   * from `newState`.
   *
   * @param newState - A dictionary mapping tab ID to tab state objects.
   *                   The provided state will be merged with the default
   *                   state for a tab.
   */
  this.load = function (newState) {
    var newCurrentState = {};
    Object.keys(newState).forEach(function (tabId) {
      newCurrentState[tabId] = assign({}, DEFAULT_STATE, newState[tabId]);
    });
    currentState = newCurrentState;
  };

  this.activateTab = function (tabId) {
    this.setState(tabId, {state: states.ACTIVE});
  };

  this.deactivateTab = function (tabId) {
    this.setState(tabId, {state: states.INACTIVE});
  };

  this.errorTab = function (tabId, error) {
    this.setState(tabId, {
      state: states.ERRORED,
      error: error,
    });
  };

  this.clearTab = function (tabId) {
    this.setState(tabId, null);
  };

  this.getState = function (tabId) {
    if (!currentState[tabId]) {
      return DEFAULT_STATE;
    }
    return currentState[tabId];
  };

  this.annotationCount = function(tabId) {
    return this.getState(tabId).annotationCount;
  }

  this.isTabActive = function (tabId) {
    return this.getState(tabId).state === states.ACTIVE;
  };

  this.isTabInactive = function (tabId) {
    return this.getState(tabId).state === states.INACTIVE;
  };

  this.isTabErrored = function (tabId) {
    return this.getState(tabId).state === states.ERRORED;
  };

  /**
   * Updates the H state for a tab.
   *
   * @param tabId - The ID of the tab being updated
   * @param stateUpdate - A dictionary of {key:value} properties for
   *                      state properties to update or null if the
   *                      state should be removed.
   */
  this.setState = function (tabId, stateUpdate) {
    var newState;
    if (stateUpdate) {
      newState = assign({}, this.getState(tabId), stateUpdate);
      if (newState.state !== states.ERRORED) {
        newState.error = undefined;
      }
    }

    if (isShallowEqual(newState, currentState[tabId])) {
      return;
    }

    currentState[tabId] = newState;

    if (_this.onchange) {
      _this.onchange(tabId, newState);
    }
  }

  /**
   * Query the server for the annotation count for a URL
   * and update the annotation count for the tab accordingly.
   *
   * @method
   * @param {integer} tabId The id of the tab.
   * @param {string} tabUrl The URL of the tab.
   * @param {string} apiUrl The URL of the Hypothesis API.
   */
  this.updateAnnotationCount = function(tabId, tabUrl, apiUrl) {
    var self = this;
    return uriInfo.query(tabUrl).then(function (result) {
      self.setState(tabId, { annotationCount: result.total });
    }).catch(function (err) {
      self.setState(tabId, { annotationCount: 0 });
      console.error('Failed to fetch annotation count for %s: %s', tabUrl, err)
    });
  };

  this.load(initialState || {});
}

TabState.states = states;

module.exports = TabState;
<commit_msg>Remove unused function parameter<commit_after>'use strict';

var assign = require('core-js/modules/$.object-assign');
var isShallowEqual = require('is-equal-shallow');

var uriInfo = require('./uri-info');

var states = {
  ACTIVE:   'active',
  INACTIVE: 'inactive',
  ERRORED:  'errored',
};

/** The default H state for a new browser tab */
var DEFAULT_STATE = {
  /** Whether or not H is active on the page */
  state: states.INACTIVE,
  /** The count of annotations on the page visible to the user,
   * as returned by the badge API
   */
  annotationCount: 0,
  /** Whether or not the H sidebar has been installed onto the page by
   * the extension
   */
  extensionSidebarInstalled: false,
  /** Whether the tab is loaded and ready for the sidebar to be installed. */
  ready: false,
  /** The error for the current tab. */
  error: undefined,
};

/** TabState stores the H state for a tab. This state includes:
 *
 * - Whether the extension has been activated on a tab
 * - Whether the sidebar is currently installed on a tab
 * - The count of annotations visible to the user on the URL currently
 *   displayed in the tab.
 *
 * The H state for a tab is updated via the setState() method and
 * retrieved via getState().
 *
 * When the H state for a tab changes, the `onchange()` callback will
 * be triggered with the tab ID and current and previous states.
 *
 * initialState - An Object of tabId/state keys. Used when loading state
 *   from a persisted store such as localStorage. This will be merged with
 *   the default state for a tab.
 * onchange     - A function that recieves onchange(tabId, current).
 */
function TabState(initialState, onchange) {
  var _this = this;
  var currentState;

  this.onchange = onchange || null;

  /** Replaces the H state for all tabs with the state data
   * from `newState`.
   *
   * @param newState - A dictionary mapping tab ID to tab state objects.
   *                   The provided state will be merged with the default
   *                   state for a tab.
   */
  this.load = function (newState) {
    var newCurrentState = {};
    Object.keys(newState).forEach(function (tabId) {
      newCurrentState[tabId] = assign({}, DEFAULT_STATE, newState[tabId]);
    });
    currentState = newCurrentState;
  };

  this.activateTab = function (tabId) {
    this.setState(tabId, {state: states.ACTIVE});
  };

  this.deactivateTab = function (tabId) {
    this.setState(tabId, {state: states.INACTIVE});
  };

  this.errorTab = function (tabId, error) {
    this.setState(tabId, {
      state: states.ERRORED,
      error: error,
    });
  };

  this.clearTab = function (tabId) {
    this.setState(tabId, null);
  };

  this.getState = function (tabId) {
    if (!currentState[tabId]) {
      return DEFAULT_STATE;
    }
    return currentState[tabId];
  };

  this.annotationCount = function(tabId) {
    return this.getState(tabId).annotationCount;
  }

  this.isTabActive = function (tabId) {
    return this.getState(tabId).state === states.ACTIVE;
  };

  this.isTabInactive = function (tabId) {
    return this.getState(tabId).state === states.INACTIVE;
  };

  this.isTabErrored = function (tabId) {
    return this.getState(tabId).state === states.ERRORED;
  };

  /**
   * Updates the H state for a tab.
   *
   * @param tabId - The ID of the tab being updated
   * @param stateUpdate - A dictionary of {key:value} properties for
   *                      state properties to update or null if the
   *                      state should be removed.
   */
  this.setState = function (tabId, stateUpdate) {
    var newState;
    if (stateUpdate) {
      newState = assign({}, this.getState(tabId), stateUpdate);
      if (newState.state !== states.ERRORED) {
        newState.error = undefined;
      }
    }

    if (isShallowEqual(newState, currentState[tabId])) {
      return;
    }

    currentState[tabId] = newState;

    if (_this.onchange) {
      _this.onchange(tabId, newState);
    }
  }

  /**
   * Query the server for the annotation count for a URL
   * and update the annotation count for the tab accordingly.
   *
   * @method
   * @param {integer} tabId The id of the tab.
   * @param {string} tabUrl The URL of the tab.
   */
  this.updateAnnotationCount = function(tabId, tabUrl) {
    var self = this;
    return uriInfo.query(tabUrl).then(function (result) {
      self.setState(tabId, { annotationCount: result.total });
    }).catch(function (err) {
      self.setState(tabId, { annotationCount: 0 });
      console.error('Failed to fetch annotation count for %s: %s', tabUrl, err)
    });
  };

  this.load(initialState || {});
}

TabState.states = states;

module.exports = TabState;
<|endoftext|>


<commit_before>export function fetchIPs (context) {
  context.commit('setFetchingState');
  return new Promise((resolve, reject) => {
    context.rootState.client.directDBConnection().getIPs(function (err, _, data) {
      if (err) {
        const error = data.responseJSON && data.responseJSON.errors ||
          { message: data.responseText || data.statusText };
        context.commit('setIPsRequestError', error);
        return reject(error);
      }

      const ips = data.ips || [];
      context.commit('setIPs', ips);
      resolve(ips);
    });
  });
}

export function setIPs (context, ips) {
  return new Promise((resolve, reject) => {
    context.rootState.client.directDBConnection().setIPs(ips,
      function (err, _, data) {
        if (err) {
          const errorMessage =
            (data.responseJSON && data.responseJSON.errors && data.responseJSON.errors.ips.join('. ')) ||
            (data.responseJSON && data.responseJSON.errors) ||
            data.responseText ||
            data.statusText;

          context.commit('setIPsRequestError', errorMessage);
          return reject(errorMessage);
        }

        const ips = data.ips || [];
        context.commit('setIPs', ips);
        resolve(ips);
      }
    );
  });
}

export function fetchCertificates (context) {
  context.commit('setFetchingState');

  context.rootState.client.directDBConnection().getCertificates(function (err, _, data) {
    if (err) {
      const error = data.responseJSON && data.responseJSON.errors ||
        { message: data.responseText || data.statusText };
      context.commit('setCertificatesRequestError', error);
      return;
    }

    context.commit('setCertificates', data);
  });
}

export function revokeCertificate (context, certificateId) {
  return new Promise((resolve, reject) => {
    context.rootState.client.directDBConnection()
      .revokeCertificate(certificateId, (err, _, data) => {
        if (err) {
          const error = data.responseJSON && data.responseJSON.errors ||
            { message: data.responseText || data.statusText };
          return reject(error);
        }

        resolve();
      });
  });
}
<commit_msg>Make code consistent across direct db actions<commit_after>export function fetchIPs (context) {
  context.commit('setFetchingState');
  return new Promise((resolve, reject) => {
    context.rootState.client.directDBConnection().getIPs(function (err, _, data) {
      if (err) {
        const errorMessage =
          (data.responseJSON && data.responseJSON.errors) ||
          data.responseText ||
          data.statusText;

        context.commit('setIPsRequestError', errorMessage);
        return reject(errorMessage);
      }

      const ips = data.ips || [];
      context.commit('setIPs', ips);
      resolve(ips);
    });
  });
}

export function setIPs (context, ips) {
  return new Promise((resolve, reject) => {
    context.rootState.client.directDBConnection().setIPs(ips,
      function (err, _, data) {
        if (err) {
          const errorMessage =
            (data.responseJSON && data.responseJSON.errors && data.responseJSON.errors.ips.join('. ')) ||
            (data.responseJSON && data.responseJSON.errors) ||
            data.responseText ||
            data.statusText;

          context.commit('setIPsRequestError', errorMessage);
          return reject(errorMessage);
        }

        const ips = data.ips || [];
        context.commit('setIPs', ips);
        resolve(ips);
      }
    );
  });
}

export function fetchCertificates (context) {
  context.commit('setFetchingState');

  context.rootState.client.directDBConnection().getCertificates(function (err, _, data) {
    if (err) {
      const error = data.responseJSON && data.responseJSON.errors ||
        { message: data.responseText || data.statusText };
      context.commit('setCertificatesRequestError', error);
      return;
    }

    context.commit('setCertificates', data);
  });
}

export function revokeCertificate (context, certificateId) {
  return new Promise((resolve, reject) => {
    context.rootState.client.directDBConnection()
      .revokeCertificate(certificateId, (err, _, data) => {
        if (err) {
          const error = data.responseJSON && data.responseJSON.errors ||
            { message: data.responseText || data.statusText };
          return reject(error);
        }

        resolve();
      });
  });
}
<|endoftext|>


<commit_before>/* global Notification: true */
export default Notification = {};
import '/imports/collections/Log.js';

Notification.Event = {};

/** Record the intent to send event notifications
  *
  * @param      {ID} eventID   - event to announce
  * @param {Boolean} isNew     - whether the event is a new one
  */
Notification.Event.record = function(eventId, isNew, addNotificationMessage) {
	check(eventId, String);
	check(isNew, Boolean);
	var event = Events.findOne(eventId);
	if (!event) throw new Meteor.Error("No event for" + eventId);

	// What do we do when we receive an event which is not attached to a course?
	// For now when we don't have a course we just go through the motions but
	// the recipient list will be empty.
	var course = false;
	if (event.courseId) course = Courses.findOne(event.courseId);

	var entry = {};
	entry.new = isNew;
	entry.eventId = event._id;
	entry.additionalMessage = addNotificationMessage;

	// The list of recipients is built right away so that only course members
	// at the time of event creation will get the notice even if sending is
	// delayed.
	entry.recipients = [];
	if (course) {
		entry.recipients = _.pluck(course.members, 'user');
		entry.courseId = course._id;
	}

	Log.record('Notification.Event', [course._id], entry);
};

/** Handle event notification
  *
  * @param entry Notification.Event log entry to process
  */
Notification.Event.handler = function(entry) {
	// Find out for which recipients sending has already been attempted.
	var concluded = {};

	Log.find(
		{ tr: 'Notification.EventResult'
		, rel: entry._id
		}
	).forEach(function(result) {
		concluded[result.body.recipient] = true;
	});

	var event = Events.findOne(entry.body.eventId);
	var course = false;
	if (event && event.courseId) {
		course = Courses.findOne(event.courseId);
	}
	var region = false;
	if (event && event.region) {
	    region = Regions.findOne(event.region);
	}

	_.each(entry.body.recipients, function(recipient) {
		if (!concluded[recipient]) {
			var mail = null;
			var unsubToken = null;
			var userId = null;

			try {
				if (!event) throw "Event does not exist (0.o)";
				if (!course) throw "Course does not exist (0.o)";
				if (!region) throw "Region does not exist (0.o)";

				var user = Meteor.users.findOne(recipient);
				userId = user._id;

				if (user.profile.notifications === false) {
					throw "User wishes to not receive notifications";
				}

				if (!user.emails || !user.emails[0] || !user.emails[0].address) {
					throw "Recipient has no email address registered";
				}

				var	email = user.emails[0];
				var address = email.address;

				// Show dates in local time and in users locale
				var regionZone = LocalTime.zone(event.region);
				var userLocale = user.profile && user.profile.locale || 'en';

				var startMoment = regionZone.at(event.start);
				startMoment.locale(userLocale);

				var endMoment = regionZone.at(event.end);
				endMoment.locale(userLocale);

				var subjectvars =
					{ TITLE: event.title.substr(0,30)
					, DATE: startMoment.format('LL')
					};

				var subjectPrefix = '['+Accounts.emailTemplates.siteName+'] ';
				var subject;
				if (entry.new) {
					subject = mf('notification.event.mail.subject.new', subjectvars, "On {DATE}: {TITLE}");
				} else {
					subject = mf('notification.event.mail.subject.changed', subjectvars, "Fixed {DATE}: {TITLE}");
				}

				unsubToken = Random.secret();

				var vars =
					{ event: event
					, course: course
					, username: user.username
					, eventDate: startMoment.format('LL')
					, eventStart: startMoment.format('LT')
					, eventEnd: endMoment.format('LT')
					, regionName: region.name
					, timeZone: endMoment.format('z') // Ignoring the possibility that event start could have a different offset like when going from CET to CEST
					, locale: userLocale
					, eventLink: Router.url('showEvent', event)
					, calLink: Router.url('calEvent', event)
					, unsubLink: Router.url('profile.unsubscribe', { token: unsubToken })
					, new: entry.body.new
					, additionalMessage: entry.body.additionalMessage
					};

				var message = SSR.render("notificationEventMail", vars);

				mail =
					{ from: Accounts.emailTemplates.from
					, to: address
					, subject: subjectPrefix + subject
					, html: message
					};

				Email.send(mail);

				Notification.EventResult.record(entry, unsubToken, true, recipient, mail, "success");
			}
			catch(e) {
				var reason = e;
				if (typeof e == 'object' && 'toJSON' in e) reason = e.toJSON();
				Notification.EventResult.record(entry, unsubToken, false, recipient, mail, reason);
			}

		}
	});
};

Notification.EventResult = {};

/** Record the result of a notification delivery attempt
  * @param  {object} note      - notification log-entry
  * @param      {ID} unsubToken - token that can be used to unsubscribe from
  *                               further notices
  * @param {Boolean} sent      - whether the notification was sent
  * @param      {ID} recipient - recipient user ID
  * @param  {String} message   - generated message (or null if we didn't get
  *                              that far)
  * @param  {String} reason    - why this log entry was recorded
  */
Notification.EventResult.record = function(note, unsubToken, sent, recipient, message, reason) {
	check(sent, Boolean);
	check(unsubToken, Match.Maybe(String));
	check(recipient, String);
	check(message, Match.Maybe(Object));
	var entry = {
		sent: sent,
		recipient: recipient,
		message: message,
		reason: reason,
		unsubToken: unsubToken
	};

	var rel = [ note._id ];
	if (unsubToken) rel.push(unsubToken);

	Log.record('Notification.EventResult', rel, entry);
};
<commit_msg>standardize var name<commit_after>/* global Notification: true */
export default Notification = {};
import '/imports/collections/Log.js';

Notification.Event = {};

/** Record the intent to send event notifications
  *
  * @param      {ID} eventID   - event to announce
  * @param {Boolean} isNew     - whether the event is a new one
  * @param {String}  additionalMessage - custom message
  */
Notification.Event.record = function(eventId, isNew, additionalMessage) {
	check(eventId, String);
	check(isNew, Boolean);
	var event = Events.findOne(eventId);
	if (!event) throw new Meteor.Error("No event for" + eventId);

	// What do we do when we receive an event which is not attached to a course?
	// For now when we don't have a course we just go through the motions but
	// the recipient list will be empty.
	var course = false;
	if (event.courseId) course = Courses.findOne(event.courseId);

	var entry = {};
	entry.new = isNew;
	entry.eventId = event._id;
	entry.additionalMessage = addtitionalMessage;

	// The list of recipients is built right away so that only course members
	// at the time of event creation will get the notice even if sending is
	// delayed.
	entry.recipients = [];
	if (course) {
		entry.recipients = _.pluck(course.members, 'user');
		entry.courseId = course._id;
	}

	Log.record('Notification.Event', [course._id], entry);
};

/** Handle event notification
  *
  * @param entry Notification.Event log entry to process
  */
Notification.Event.handler = function(entry) {
	// Find out for which recipients sending has already been attempted.
	var concluded = {};

	Log.find(
		{ tr: 'Notification.EventResult'
		, rel: entry._id
		}
	).forEach(function(result) {
		concluded[result.body.recipient] = true;
	});

	var event = Events.findOne(entry.body.eventId);
	var course = false;
	if (event && event.courseId) {
		course = Courses.findOne(event.courseId);
	}
	var region = false;
	if (event && event.region) {
	    region = Regions.findOne(event.region);
	}

	_.each(entry.body.recipients, function(recipient) {
		if (!concluded[recipient]) {
			var mail = null;
			var unsubToken = null;
			var userId = null;

			try {
				if (!event) throw "Event does not exist (0.o)";
				if (!course) throw "Course does not exist (0.o)";
				if (!region) throw "Region does not exist (0.o)";

				var user = Meteor.users.findOne(recipient);
				userId = user._id;

				if (user.profile.notifications === false) {
					throw "User wishes to not receive notifications";
				}

				if (!user.emails || !user.emails[0] || !user.emails[0].address) {
					throw "Recipient has no email address registered";
				}

				var	email = user.emails[0];
				var address = email.address;

				// Show dates in local time and in users locale
				var regionZone = LocalTime.zone(event.region);
				var userLocale = user.profile && user.profile.locale || 'en';

				var startMoment = regionZone.at(event.start);
				startMoment.locale(userLocale);

				var endMoment = regionZone.at(event.end);
				endMoment.locale(userLocale);

				var subjectvars =
					{ TITLE: event.title.substr(0,30)
					, DATE: startMoment.format('LL')
					};

				var subjectPrefix = '['+Accounts.emailTemplates.siteName+'] ';
				var subject;
				if (entry.new) {
					subject = mf('notification.event.mail.subject.new', subjectvars, "On {DATE}: {TITLE}");
				} else {
					subject = mf('notification.event.mail.subject.changed', subjectvars, "Fixed {DATE}: {TITLE}");
				}

				unsubToken = Random.secret();

				var vars =
					{ event: event
					, course: course
					, username: user.username
					, eventDate: startMoment.format('LL')
					, eventStart: startMoment.format('LT')
					, eventEnd: endMoment.format('LT')
					, regionName: region.name
					, timeZone: endMoment.format('z') // Ignoring the possibility that event start could have a different offset like when going from CET to CEST
					, locale: userLocale
					, eventLink: Router.url('showEvent', event)
					, calLink: Router.url('calEvent', event)
					, unsubLink: Router.url('profile.unsubscribe', { token: unsubToken })
					, new: entry.body.new
					, additionalMessage: entry.body.additionalMessage
					};

				var message = SSR.render("notificationEventMail", vars);

				mail =
					{ from: Accounts.emailTemplates.from
					, to: address
					, subject: subjectPrefix + subject
					, html: message
					};

				Email.send(mail);

				Notification.EventResult.record(entry, unsubToken, true, recipient, mail, "success");
			}
			catch(e) {
				var reason = e;
				if (typeof e == 'object' && 'toJSON' in e) reason = e.toJSON();
				Notification.EventResult.record(entry, unsubToken, false, recipient, mail, reason);
			}

		}
	});
};

Notification.EventResult = {};

/** Record the result of a notification delivery attempt
  * @param  {object} note      - notification log-entry
  * @param      {ID} unsubToken - token that can be used to unsubscribe from
  *                               further notices
  * @param {Boolean} sent      - whether the notification was sent
  * @param      {ID} recipient - recipient user ID
  * @param  {String} message   - generated message (or null if we didn't get
  *                              that far)
  * @param  {String} reason    - why this log entry was recorded
  */
Notification.EventResult.record = function(note, unsubToken, sent, recipient, message, reason) {
	check(sent, Boolean);
	check(unsubToken, Match.Maybe(String));
	check(recipient, String);
	check(message, Match.Maybe(Object));
	var entry = {
		sent: sent,
		recipient: recipient,
		message: message,
		reason: reason,
		unsubToken: unsubToken
	};

	var rel = [ note._id ];
	if (unsubToken) rel.push(unsubToken);

	Log.record('Notification.EventResult', rel, entry);
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import ReactDOM from 'react-dom';

export default function (WrappedComponent) {
  var portalNodes = {};
  return class TooltipPortal extends Component {
    static defaultProps = {
      active: false,
      group: 'main',
    };

    componentDidMount() {
      if (!this.props.active || typeof window === 'undefined') {
        return;
      }

      this.renderPortal(this.props);
    }

    /*componentWillReceiveProps(nextProps) {
      const { active, group } = this.props;

      if ((!portalNodes[group] && !nextProps.active) ||
        (!active && !nextProps.active)) {
        return;
      }

      if (portalNodes[group] && portalNodes[group].timeout) {
        clearTimeout(portalNodes[group].timeout);
      }

      if (active && !nextProps.active) {
        portalNodes[group].timeout = setTimeout(() => {
          this.renderPortal({ ...nextProps, active: false });
        }, 500);

        return this.renderPortal({ ...nextProps, active: true });
      }

      this.renderPortal(nextProps);
    }*/

    removeThisGroupTip(){
      const { group } = this.props;
      portalNodes[group].tips = portalNodes[group].tips.filter(tip => {
        if (this.props.uid) {
          return tip.props.uid !== this.props.uid;
        } else if (this.props.parent) {
          return tip.props.parent !== this.props.parent;
        } return tip !== this;
      });
      return portalNodes[group].tips;
    }

    componentWillUnmount() {
      const { group } = this.props;
      if (portalNodes[group]) {

        if (portalNodes[group].tips.length > 1) {
          this.removeThisGroupTip();
          // Other tooltip was active, switching
          const item = portalNodes[group].tips[portalNodes[group].tips.length - 1];
          return item.renderPortal(item.props);
        }

        // No other tooltip, fading away..
        this.renderPortal({ ...this.props, active: false });

        // Timeout to switch to next tooltip if necessary
        portalNodes[group].timeout = setTimeout(() => {
          if (portalNodes[group].tips.length === 1) {
            ReactDOM.unmountComponentAtNode(portalNodes[group].el);
            delete portalNodes[group];
          } else {
            this.removeThisGroupTip();
            const item = portalNodes[group].tips[portalNodes[group].tips.length - 1];
            item.renderPortal(item.props);
          }
        }, 500);
      }
    }

    createPortal(group) {
      portalNodes[group] = {
        tips: [],
        el: document.createElement('div'),
        timeout: false,
      };

      document.body.appendChild(portalNodes[group].el);
    }

    renderPortal(props) {
      const { group, clearAll, ...other } = props;

      if (!portalNodes[group]) {
        this.createPortal(group);
      } else if (portalNodes[group].timeout) {
        clearTimeout(portalNodes[group].timeout);
        portalNodes[group].timeout = null;
      }

      portalNodes[group].tips = clearAll ? [this] : [
        ...this.removeThisGroupTip(),
        this,
      ];

      ReactDOM.render(<WrappedComponent {...other} />, portalNodes[group].el);
    }

    shouldComponentUpdate() {
      return false;
    }

    render() {
      return null;
    }
  };
}
<commit_msg>Improve tooltip behaviour<commit_after>import React, { Component } from 'react';
import ReactDOM from 'react-dom';

export default function (WrappedComponent) {
  var portalNodes = {};
  return class TooltipPortal extends Component {
    static defaultProps = {
      active: false,
      group: 'main',
    };

    componentDidMount() {
      if (!this.props.active || typeof window === 'undefined') {
        return;
      }

      this.renderPortal(this.props);
    }

    /*componentWillReceiveProps(nextProps) {
      const { active, group } = this.props;

      if ((!portalNodes[group] && !nextProps.active) ||
        (!active && !nextProps.active)) {
        return;
      }

      if (portalNodes[group] && portalNodes[group].timeout) {
        clearTimeout(portalNodes[group].timeout);
      }

      if (active && !nextProps.active) {
        portalNodes[group].timeout = setTimeout(() => {
          this.renderPortal({ ...nextProps, active: false });
        }, 500);

        return this.renderPortal({ ...nextProps, active: true });
      }

      this.renderPortal(nextProps);
    }*/

    removeThisGroupTip(){
      const { group } = this.props;
      portalNodes[group].tips = portalNodes[group].tips.filter(tip => {
        if (this.props.uid) {
          return tip.props.uid !== this.props.uid;
        } else if (this.props.parent) {
          return tip.props.parent !== this.props.parent;
        } return tip !== this;
      });
      return portalNodes[group].tips;
    }

    componentWillUnmount() {
      const { group } = this.props;
      if (portalNodes[group]) {

        // Other tooltip was active, switching
        if (portalNodes[group].tips.length > 1) {
          this.removeThisGroupTip();
          const item = portalNodes[group].tips[portalNodes[group].tips.length - 1];
          return item.renderPortal(item.props);
        }

        // No other tooltip, fading away..
        this.renderPortal({ ...this.props, active: false });

        // Timeout to switch to next tooltip if necessary
        portalNodes[group].timeout = setTimeout(() => {
          if (portalNodes[group].tips.length === 0) {
            ReactDOM.unmountComponentAtNode(portalNodes[group].el);
            delete portalNodes[group];
          } else {
            const item = portalNodes[group].tips[portalNodes[group].tips.length - 1];
            item.renderPortal(item.props);
          }
        }, 500);
      }
    }

    createPortal(group) {
      portalNodes[group] = {
        tips: [],
        el: document.createElement('div'),
        timeout: false,
      };

      document.body.appendChild(portalNodes[group].el);
    }

    renderPortal(props) {
      const { group, clearAll, ...other } = props;

      if (!portalNodes[group]) {
        this.createPortal(group);
      } else if (portalNodes[group].timeout) {
        clearTimeout(portalNodes[group].timeout);
        portalNodes[group].timeout = null;
      }

      if (props.active) {
        portalNodes[group].tips = clearAll ? [this] : [
          ...this.removeThisGroupTip(),
          this,
        ];
      } else {
        this.removeThisGroupTip();
      }

      ReactDOM.render(<WrappedComponent {...other} />, portalNodes[group].el);
    }

    shouldComponentUpdate() {
      return false;
    }

    render() {
      return null;
    }
  };
}
<|endoftext|>