<commit_before>// March 2012, Franziska Hinkelmann, Mike Stillman, and Lars Kastner
//
// This is server-side JavaScript, intended to be run with Node.js.
// This file defines a Node.js server for serving 'tryM2'.
//   run
//      node m2server.js
// in a terminal in this directory.
// Then in a browser, use:
//      http://localhost:8000/
// Required Node.js libraries: cookies.  Install via:
//   npm install cookies, or sudo npm install -g cookies
// Required on path: M2

//
// A message on / : possibly creates a cookie, and serves back index.html
//   and related js/css/png files
// A POST message on /chat: input should be Macaulay2 commands to perform.
// A message on /chat: start an event emitter, which will return the output of
// the M2 process.

var http = require('http');  // NodeJS HTTP server API
var Cookies = require("cookies");

// The HTML file for the chat client. Used below.
var clientui = require('fs').readFileSync("index.html");
var totalUsers = 0;

// An array of Client objects.  Each has an M2 process, and a response object
// It is possible that this.m2 is not defined, and/or that this.response is not
// defined.
var clients = {};

function Client(m2process, resp) {
    this.m2 = m2process;
    this.response = resp;
}

startM2Process = function() {
    var spawn = require('child_process').spawn;
    var m2 = spawn('M2');
    m2.stdout.setEncoding("utf8");
    m2.stderr.setEncoding("utf8");
    console.log('Spawned m2 pid: ' + m2.pid);
    return m2;
};

startM2 = function(client) {
    // client is an object of type Client
    client.m2 = startM2Process();
    console.log("starting emitter");
    var ondata = function(data) {
        console.log('ondata: ' + data);
        message = 'data: ' + data.replace(/\n/g, '\ndata: ') + "\r\n\r\n";
        client.response.write(message);
    };
    client.m2.stdout.on('data', ondata);
    client.m2.stderr.on('data', ondata);
};

restartM2 = function(client) {
    if (client.m2) { client.m2.kill(); }
    startM2(client);
}

// Send a comment to the clients every 20 seconds so they don't
// close the connection and then reconnect
setInterval(function() {
    for (var prop in clients) {
        if (clients.hasOwnProperty(prop)
            && clients[prop]
            && clients[prop].response) {
            clients[prop].response.write(":ping\n");
        }
    }
}, 20000);

startUser = function(cookies) {
    totalUsers = totalUsers + 1;
    var clientID = Math.random()*1000000;
    clientID = Math.floor(clientID);
    clientID = "user" + clientID.toString(10);
    cookies.set( "tryM2", clientID, { httpOnly: false } );
    clients[clientID] = new Client(); // will be populated with a Client in /chat
    return clientID;
}

// Create a new server
var server = new http.Server();

// When the server gets a new request, run this function
server.on("request", function (request, response) {
    console.log( "got on");
    // Parse the requested URL
    var cookies = new Cookies(request, response);
    var url = require('url').parse(request.url);

    if(url.pathname === "/admin") {
        // authorization
        response.writeHead(200, {"Content-Type": "text/html"});
        var currentUsers = 0;
        for( var c in clients) {
            if(clients.hasOwnProperty(c))
                    currentUsers = currentUsers + 1;
        }
        response.write('<link rel="stylesheet" href="m2.css" type="text/css" media="screen">' );
        response.write("There are currently " + currentUsers + " users using M2.
" );
        response.write("In total, there were " + totalUsers + " users since the server started.
");
        response.write("Enjoy M2!");
        response.end();
        return;
    }

    // If the request was for "/", send the client-side chat UI.
    if (url.pathname === "/" || url.pathname === "/index.html") {  // A request for the chat UI
        startUser(cookies);
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write(clientui);
        response.end();
        return;
    }
    if (/\.css/.test(url.pathname) ) {
        response.writeHead(200, {'Content-Type': 'text/css'});
        var u = url.pathname.replace("/", '');
        response.write(require('fs').readFileSync(u));
        response.end();
        return;
    }
    if (url.pathname === "/chat") {
        // If the request was a post, then a client is posting a new message
        var clientID = cookies.get("tryM2");
        console.log("cookie value from client: " + clientID);

        if (!clients[clientID]) {
            // somehow the user sent M2 commands without sending "/" first
            console.log("startUser");
            clientID = startUser(cookies);
        }
        if (request.method === "POST") {
            console.log( " got post");
            request.setEncoding("utf8");

            //   for (e in request.headers) {
            //       console.log(e+": "+request.headers[e]);
            //   }

            var body = "";
            // When we get a chunk of data, add it to the body
            request.on("data", function(chunk) { body += chunk; });

            request.on("end", function() {
                // Send 'body' to M2
                try {
                    console.log("Send M2 output: " + body);
                    clients[clientID].m2.stdin.write(body);
                }
                catch (err) {
                    console.log("socket was closed for clients[" + clientID + "]");
                    // send error back to user so user restart eventSource
                    response.writeHead(200, {
                      'notEventSourceError': 'No socket for client...' });
                    response.end();
                    return;
                }
                response.writeHead(200);   // Respond to the request
                response.end();
            });
            return;
        } else {
            console.log( " got get");

            response.writeHead(200, {'Content-Type': "text/event-stream" });
            if (!clients[clientID].response) {
                clients[clientID].response = response;
                startM2(clients[clientID]);
            }

            // If the client closes the connection, remove the corresponding
            // response object from the array of active clients
            request.connection.on("end", function() {
                console.log("close connection: clients[" + clientID + "]");
                clients[clientID].m2.kill();
                clients[clientID].m2 = null;
                clients[clientID].response = null;
                response.end();
            });

            return;
        }
    }
    if (url.pathname === "/restart") {
        var clientID = cookies.get("tryM2");
        restartM2(clients[clientID]);
        console.log("received: /restart from " + clientID);
        return;
    }
    if (url.pathname === "/interrupt") {
        var clientID = cookies.get("tryM2");
        if (clients[clientID] && clients[clientID].m2) {
            clients[clientID].m2.kill('SIGINT');
        }
        console.log("received: /interrupt from " + clientID);
        return;
    }
    // Send file (e.g. images and tutorial) or 404 if not found
    if (url.pathname !== "/chat") {

        var u = url.pathname.replace("/", '');
        try {
            data = require('fs').readFileSync(u);
            response.writeHead(200, {"Content-Type": "text/html"});
            response.write(data);
        }
        catch (err) {
            console.error("There was an error opening the file:");
            console.log(err);
            response.writeHead(404,{"Content-Type": "text/html"});
            response.write( '
Page not found. Return to TryM2
');
        }

        response.end();
        return;
    }
});

// Run the server on port 8000. Connect to http://localhost:8000/ to use it.
console.log("Listening on port 8000...");
server.listen(8000);<commit_msg>delete users that have decided to leave us<commit_after>// March 2012, Franziska Hinkelmann, Mike Stillman, and Lars Kastner
//
// This is server-side JavaScript, intended to be run with Node.js.
// This file defines a Node.js server for serving 'tryM2'.
//   run
//      node m2server.js
// in a terminal in this directory.
// Then in a browser, use:
//      http://localhost:8000/
// Required Node.js libraries: cookies.  Install via:
//   npm install cookies, or sudo npm install -g cookies
// Required on path: M2

//
// A message on / : possibly creates a cookie, and serves back index.html
//   and related js/css/png files
// A POST message on /chat: input should be Macaulay2 commands to perform.
// A message on /chat: start an event emitter, which will return the output of
// the M2 process.

var http = require('http');  // NodeJS HTTP server API
var Cookies = require("cookies");

// The HTML file for the chat client. Used below.
var clientui = require('fs').readFileSync("index.html");
var totalUsers = 0;

// An array of Client objects.  Each has an M2 process, and a response object
// It is possible that this.m2 is not defined, and/or that this.response is not
// defined.
var clients = {};

function Client(m2process, resp) {
    this.m2 = m2process;
    this.response = resp;
}

startM2Process = function() {
    var spawn = require('child_process').spawn;
    var m2 = spawn('M2');
    m2.stdout.setEncoding("utf8");
    m2.stderr.setEncoding("utf8");
    console.log('Spawned m2 pid: ' + m2.pid);
    return m2;
};

startM2 = function(client) {
    // client is an object of type Client
    client.m2 = startM2Process();
    console.log("starting emitter");
    var ondata = function(data) {
        console.log('ondata: ' + data);
        message = 'data: ' + data.replace(/\n/g, '\ndata: ') + "\r\n\r\n";
        client.response.write(message);
    };
    client.m2.stdout.on('data', ondata);
    client.m2.stderr.on('data', ondata);
};

restartM2 = function(client) {
    if (client.m2) { client.m2.kill(); }
    startM2(client);
}

// Send a comment to the clients every 20 seconds so they don't
// close the connection and then reconnect
setInterval(function() {
    for (var prop in clients) {
        if (clients.hasOwnProperty(prop)
            && clients[prop]
            && clients[prop].response) {
            clients[prop].response.write(":ping\n");
        }
    }
}, 20000);

startUser = function(cookies) {
    totalUsers = totalUsers + 1;
    var clientID = Math.random()*1000000;
    clientID = Math.floor(clientID);
    clientID = "user" + clientID.toString(10);
    cookies.set( "tryM2", clientID, { httpOnly: false } );
    clients[clientID] = new Client(); // will be populated with a Client in /chat
    return clientID;
}

// Create a new server
var server = new http.Server();

var stats = function(response) {
    // to do: authorization
     response.writeHead(200, {"Content-Type": "text/html"});
     var currentUsers = 0;
     for( var c in clients) {
         if(clients.hasOwnProperty(c))
                 currentUsers = currentUsers + 1;
     }
     response.write('<link rel="stylesheet" href="m2.css" type="text/css" media="screen">' );
     response.write('
Macaulay2 User Statistics
');
     response.write("There are currently " + currentUsers + " users using M2.
" );
     response.write("In total, there were " + totalUsers + " users since the server started.
");
     response.write("Enjoy M2!");
     response.end();
}

// When the server gets a new request, run this function
server.on("request", function (request, response) {
    console.log( "got on");
    // Parse the requested URL
    var cookies = new Cookies(request, response);
    var url = require('url').parse(request.url);

    if(url.pathname === "/admin") {
        stats(response);
        return;
    }

    // If the request was for "/", send the client-side chat UI.
    if (url.pathname === "/" || url.pathname === "/index.html") {  // A request for the chat UI
        startUser(cookies);
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write(clientui);
        response.end();
        return;
    }
    if (/\.css/.test(url.pathname) ) {
        response.writeHead(200, {'Content-Type': 'text/css'});
        var u = url.pathname.replace("/", '');
        response.write(require('fs').readFileSync(u));
        response.end();
        return;
    }
    if (url.pathname === "/chat") {
        // If the request was a post, then a client is posting a new message
        var clientID = cookies.get("tryM2");
        console.log("cookie value from client: " + clientID);

        if (!clients[clientID]) {
            // somehow the user sent M2 commands without sending "/" first
            console.log("startUser");
            clientID = startUser(cookies);
        }
        if (request.method === "POST") {
            console.log( " got post");
            request.setEncoding("utf8");

            //   for (e in request.headers) {
            //       console.log(e+": "+request.headers[e]);
            //   }

            var body = "";
            // When we get a chunk of data, add it to the body
            request.on("data", function(chunk) { body += chunk; });

            request.on("end", function() {
                // Send 'body' to M2
                try {
                    console.log("Send M2 output: " + body);
                    clients[clientID].m2.stdin.write(body);
                }
                catch (err) {
                    console.log("socket was closed for clients[" + clientID + "]");
                    // send error back to user so user restart eventSource
                    response.writeHead(200, {
                      'notEventSourceError': 'No socket for client...' });
                    response.end();
                    return;
                }
                response.writeHead(200);   // Respond to the request
                response.end();
            });
            return;
        } else {
            console.log( " got get");

            response.writeHead(200, {'Content-Type': "text/event-stream" });
            if (!clients[clientID].response) {
                clients[clientID].response = response;
                startM2(clients[clientID]);
            }

            // If the client closes the connection, remove the corresponding
            // response object from the array of active clients
            request.connection.on("end", function() {
                console.log("close connection: clients[" + clientID + "]");
                clients[clientID].m2.kill();
                delete clients[clientID];
                response.end();
            });

            return;
        }
    }
    if (url.pathname === "/restart") {
        var clientID = cookies.get("tryM2");
        restartM2(clients[clientID]);
        console.log("received: /restart from " + clientID);
        return;
    }
    if (url.pathname === "/interrupt") {
        var clientID = cookies.get("tryM2");
        if (clients[clientID] && clients[clientID].m2) {
            clients[clientID].m2.kill('SIGINT');
        }
        console.log("received: /interrupt from " + clientID);
        return;
    }
    // Send file (e.g. images and tutorial) or 404 if not found
    if (url.pathname !== "/chat") {

        var u = url.pathname.replace("/", '');
        try {
            data = require('fs').readFileSync(u);
            response.writeHead(200, {"Content-Type": "text/html"});
            response.write(data);
        }
        catch (err) {
            console.error("There was an error opening the file:");
            console.log(err);
            response.writeHead(404,{"Content-Type": "text/html"});
            response.write( '
Page not found. Return to TryM2
');
        }

        response.end();
        return;
    }
});

// Run the server on port 8000. Connect to http://localhost:8000/ to use it.
console.log("Listening on port 8000...");
server.listen(8000);<|endoftext|>


<commit_before>/* global PIXI */
import 'pixi.js';
import { hsvToRgb, hueToRgbString } from 'util/hsvToRgb';
import BaseOverlay from 'components/Layers/BaseOverlay';
import {
  TIMELINE_MAX_STEPS,
  VESSELS_HEATMAP_STYLE_ZOOM_THRESHOLD,
  VESSELS_BASE_RADIUS,
  VESSELS_HEATMAP_BLUR_FACTOR,
  VESSELS_HUES_INCREMENTS_NUM,
  VESSELS_HUES_INCREMENT
} from 'constants';

const MAX_SPRITES_FACTOR = 0.002;

export default class VesselsLayerOverlay extends BaseOverlay {

  constructor(flag, viewportWidth, viewportHeight) {
    super();

    this.viewportWidth = viewportWidth;
    this.viewportHeight = viewportHeight;
    this._build();
    this.setFlag(flag);
  }

  setFlag(flag) {
    if (flag !== '') {
      this.flag = parseInt(flag, 10);
    } else {
      this.flag = null;
    }
  }

  onAdd() {
    // Add the element to the "overlayLayer" pane.
    const panes = this.getPanes();
    panes.overlayLayer.appendChild(this.container);
  }

  onRemove() {}

  _build() {
    this.container = document.createElement('div');
    this.container.style.position = 'absolute';

    this.renderer = new PIXI.WebGLRenderer(this.viewportWidth, this.viewportHeight, { transparent: true });

    this.canvas = this.renderer.view;
    this.canvas.style.position = 'absolute';

    // this.stage = new PIXI.Container();
    // the ParticleContainer is a fastest version of the PIXI sprite container
    const maxSprites = this._getSpritesPerStep() * TIMELINE_MAX_STEPS;
    this.stage = new PIXI.particles.ParticleContainer(maxSprites, {
      scale: true,
      alpha: true,
      position: true,
      uvs: true
    });
    this.stage.blendMode = PIXI.BLEND_MODES.SCREEN;

    this.container.appendChild(this.canvas);

    const baseTextureCanvas = this._getVesselTexture(VESSELS_BASE_RADIUS, VESSELS_HEATMAP_BLUR_FACTOR);
    const baseTexture = PIXI.Texture.fromCanvas(baseTextureCanvas);
    const initialTextureFrame = new PIXI.Rectangle(0, 0, VESSELS_BASE_RADIUS * 2, VESSELS_BASE_RADIUS * 2);
    this.mainVesselTexture = new PIXI.Texture(baseTexture, initialTextureFrame);

    // uncomment to debug spritesheet
    // this.container.appendChild(baseTextureCanvas);

    this.spritesPool = [];
    this.timeIndexDelta = 0;
  }

  // builds a texture spritesheet containing both the heatmap style (radial gradient)
  // and the circle style that is used at higher zoom levels, as well as a number of hues for each
  // in a grid.
  // Then, only the texture frame is modified depending on the zoom level,
  // in order not to have to recreate sprites
  _getVesselTexture(radius, blurFactor) {
    const tplCanvas = document.createElement('canvas');
    const tplCtx = tplCanvas.getContext('2d');
    const diameter = radius * 2;
    tplCanvas.width = diameter * 2 + 1; // tiny offset between 2 frames
    tplCanvas.height = diameter * VESSELS_HUES_INCREMENTS_NUM + VESSELS_HUES_INCREMENTS_NUM;

    for (let hueIncrement = 0; hueIncrement < VESSELS_HUES_INCREMENTS_NUM; hueIncrement++) {
      const y = diameter * hueIncrement + hueIncrement;
      const yCenter = y + radius;

      // heatmap style
      let x = radius;
      const gradient = tplCtx.createRadialGradient(x, yCenter, radius * blurFactor, x, yCenter, radius);
      const hue = hueIncrement * VESSELS_HUES_INCREMENT;
      const rgbString = hueToRgbString(hue);
      gradient.addColorStop(0, rgbString);

      const rgbOuter = hsvToRgb(Math.min(360, hue + 30), 80, 100);
      gradient.addColorStop(1, `rgba(${rgbOuter.r}, ${rgbOuter.g}, ${rgbOuter.b}, 0)`);

      tplCtx.fillStyle = gradient;
      tplCtx.fillRect(0, y, diameter, diameter);

      // circle style
      x += diameter + 1; // tiny offset between 2 frames
      tplCtx.beginPath();
      tplCtx.arc(x, yCenter, radius, 0, 2 * Math.PI, false);
      tplCtx.fillStyle = rgbString;
      tplCtx.fill();
    }

    return tplCanvas;
  }

  /**
   * Updates the main texture frame offset to show different brush styles and hues
   * Both args are optional, if one is omitted, previous value is used
   * @heatmapStyle bool whether to use heatmap style or solid circle style
   * @hue number hue value between 0 and 360
   */
  _setTextureFrame(useHeatmapStyle = null, hue = null) {
    const textureFrame = this.mainVesselTexture.frame.clone();

    if (useHeatmapStyle !== null) {
      // one diameter + tiny offset between 2 frames
      textureFrame.x = (useHeatmapStyle) ? 0 : VESSELS_BASE_RADIUS * 2 + 1;
    }

    if (hue !== null) {
      // 0 - 360 -> 0 -> 10
      let hueIncrement = hue / VESSELS_HUES_INCREMENT;
      if (hueIncrement === VESSELS_HUES_INCREMENTS_NUM) {
        hueIncrement = 0;
      }
      textureFrame.y = hueIncrement * VESSELS_BASE_RADIUS * 2;
      if (hueIncrement > 0) {
        textureFrame.y += hueIncrement;
      }
    }

    this.mainVesselTexture.frame = textureFrame;
    this.mainVesselTexture.update();
  }

  repositionCanvas() {
    if (!this.container) return;
    const offset = super.getRepositionOffset(this.viewportWidth, this.viewportHeight);
    this.container.style.left = `${offset.x}px`;
    this.container.style.top = `${offset.y}px`;
    this.renderer.resize(this.viewportWidth, this.viewportHeight);
    this.canvas.style.width = `${this.viewportWidth}px`;
    this.canvas.style.height = `${this.viewportHeight}px`;
  }

  updateViewportSize(viewportWidth, viewportHeight) {
    this.viewportWidth = viewportWidth;
    this.viewportHeight = viewportHeight;
    this._resizeSpritesPool();
    this.repositionCanvas();
  }

  draw() {
    this.repositionCanvas();
  }

  show() {
    this.hidden = false;
    this.container.style.display = 'block';
  }

  hide() {
    this.hidden = true;
    this.container.style.display = 'none';
    this._clear(true);
  }

  setZoom(zoom) {
    this._setTextureFrame(zoom < VESSELS_HEATMAP_STYLE_ZOOM_THRESHOLD);
  }

  setOpacity(opacity) {
    this.canvas.style.opacity = opacity;
  }

  setHue(hue) {
    this._setTextureFrame(null, hue);
  }

  render(tiles, startIndex, endIndex) {
    if (!this.stage || this.hidden) return;

    const newTimeIndexDelta = endIndex - startIndex;

    if (this.timeIndexDelta !== newTimeIndexDelta) {
      const delta = newTimeIndexDelta - this.timeIndexDelta;
      // because of the way dates are rounded, the range length can vary of one day even if the user didnt change range
      // in that case skip resizing sprites pool, avoiding doing this in the middle of on animation
      if (Math.abs(delta) !== 1) {
        this.timeIndexDelta = newTimeIndexDelta;
        this._resizeSpritesPool();
      }
    }

    this.numSprites = 0;

    tiles.forEach(tile => {
      const bounds = tile.getBoundingClientRect();
      if (!document.body.contains(tile)) {
        console.warn('rendering tile that doesnt exist in the DOM', tile);
      }

      if (bounds.left === 0 && bounds.top === 0) {
        console.warn('tile at 0,0');
      }

      if (tile.ready === true) {
        this.numSprites += this._dumpTileVessels(startIndex, endIndex, tile.data, bounds.left, bounds.top, tile.error);
      }
    });

    // hide unused sprites
    for (let i = this.numSprites, poolSize = this.spritesPool.length; i < poolSize; i++) {
      this.spritesPool[i].x = -100;
    }

    this.renderer.render(this.stage);
  }

  _dumpTileVessels(startIndex, endIndex, data, offsetX, offsetY) {
    if (!data) {
      return 0;
    }

    let numSprites = 0;

    for (let timeIndex = startIndex; timeIndex < endIndex; timeIndex ++) {
      const frame = data[timeIndex];

      if (!frame) continue;

      for (let index = 0, len = frame.x.length; index < len; index++) {
        if (this.flag && this.flag !== frame.category[index]) {
          continue;
        }
        numSprites++;
        const sprite = this.spritesPool[this.numSprites];

        sprite.position.x = offsetX + frame.x[index];
        sprite.position.y = offsetY + frame.y[index];
        sprite.alpha = frame.opacity[index];
        sprite.scale.set(frame.radius[index]);

        this.numSprites++;
      }
    }

    return numSprites;
  }

  _resizeSpritesPool() {
    // compute final pool size
    const finalPoolSize = this.timeIndexDelta * this._getSpritesPerStep();
    const currentPoolSize = this.spritesPool.length;
    const poolDelta = finalPoolSize - currentPoolSize;

    if (poolDelta > 0) {
      this._addSprites(poolDelta);
    } else {
      const startRemovingAt = currentPoolSize - poolDelta;
      for (let i = startRemovingAt; i < currentPoolSize; i++) {
        // this is actually insanely costly - keep this in RAM and be done with it ?
        // this.stage.removeChild(this.spritesPool[i]);
      }
      // this.spritesPool.splice(- deltaSprites);
    }

    // disable all sprites and let render take it from there
    this._clear();
  }

  _addSprites(num) {
    // console.log('add sprites: ', num);
    for (let i = 0; i < num; i++) {
      const vessel = new PIXI.Sprite(this.mainVesselTexture);
      vessel.anchor.x = vessel.anchor.y = 0.5;
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      vessel.x = -100;
      // vessel.blendMode = PIXI.BLEND_MODES.SCREEN;
      // vessel.filters=  [new PIXI.filters.BlurFilter(10,10)]
      this.spritesPool.push(vessel);
      this.stage.addChild(vessel);
    }
  }

  _getSpritesPerStep() {
    return Math.round(this.viewportWidth * this.viewportHeight * MAX_SPRITES_FACTOR);
  }

  _clear(render = false) {
    for (let i = 0, poolSize = this.spritesPool.length; i < poolSize; i++) {
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      this.spritesPool[i].x = -100;
    }
    if (render) {
      this.renderer.render(this.stage);
    }
  }
}
<commit_msg>Fix filter for Andorra case<commit_after>/* global PIXI */
import 'pixi.js';
import { hsvToRgb, hueToRgbString } from 'util/hsvToRgb';
import BaseOverlay from 'components/Layers/BaseOverlay';
import {
  TIMELINE_MAX_STEPS,
  VESSELS_HEATMAP_STYLE_ZOOM_THRESHOLD,
  VESSELS_BASE_RADIUS,
  VESSELS_HEATMAP_BLUR_FACTOR,
  VESSELS_HUES_INCREMENTS_NUM,
  VESSELS_HUES_INCREMENT
} from 'constants';

const MAX_SPRITES_FACTOR = 0.002;

export default class VesselsLayerOverlay extends BaseOverlay {

  constructor(flag, viewportWidth, viewportHeight) {
    super();

    this.viewportWidth = viewportWidth;
    this.viewportHeight = viewportHeight;
    this._build();
    this.setFlag(flag);
  }

  setFlag(flag) {
    if (flag !== '') {
      this.flag = parseInt(flag, 10);
    } else {
      this.flag = null;
    }
  }

  onAdd() {
    // Add the element to the "overlayLayer" pane.
    const panes = this.getPanes();
    panes.overlayLayer.appendChild(this.container);
  }

  onRemove() {}

  _build() {
    this.container = document.createElement('div');
    this.container.style.position = 'absolute';

    this.renderer = new PIXI.WebGLRenderer(this.viewportWidth, this.viewportHeight, { transparent: true });

    this.canvas = this.renderer.view;
    this.canvas.style.position = 'absolute';

    // this.stage = new PIXI.Container();
    // the ParticleContainer is a fastest version of the PIXI sprite container
    const maxSprites = this._getSpritesPerStep() * TIMELINE_MAX_STEPS;
    this.stage = new PIXI.particles.ParticleContainer(maxSprites, {
      scale: true,
      alpha: true,
      position: true,
      uvs: true
    });
    this.stage.blendMode = PIXI.BLEND_MODES.SCREEN;

    this.container.appendChild(this.canvas);

    const baseTextureCanvas = this._getVesselTexture(VESSELS_BASE_RADIUS, VESSELS_HEATMAP_BLUR_FACTOR);
    const baseTexture = PIXI.Texture.fromCanvas(baseTextureCanvas);
    const initialTextureFrame = new PIXI.Rectangle(0, 0, VESSELS_BASE_RADIUS * 2, VESSELS_BASE_RADIUS * 2);
    this.mainVesselTexture = new PIXI.Texture(baseTexture, initialTextureFrame);

    // uncomment to debug spritesheet
    // this.container.appendChild(baseTextureCanvas);

    this.spritesPool = [];
    this.timeIndexDelta = 0;
  }

  // builds a texture spritesheet containing both the heatmap style (radial gradient)
  // and the circle style that is used at higher zoom levels, as well as a number of hues for each
  // in a grid.
  // Then, only the texture frame is modified depending on the zoom level,
  // in order not to have to recreate sprites
  _getVesselTexture(radius, blurFactor) {
    const tplCanvas = document.createElement('canvas');
    const tplCtx = tplCanvas.getContext('2d');
    const diameter = radius * 2;
    tplCanvas.width = diameter * 2 + 1; // tiny offset between 2 frames
    tplCanvas.height = diameter * VESSELS_HUES_INCREMENTS_NUM + VESSELS_HUES_INCREMENTS_NUM;

    for (let hueIncrement = 0; hueIncrement < VESSELS_HUES_INCREMENTS_NUM; hueIncrement++) {
      const y = diameter * hueIncrement + hueIncrement;
      const yCenter = y + radius;

      // heatmap style
      let x = radius;
      const gradient = tplCtx.createRadialGradient(x, yCenter, radius * blurFactor, x, yCenter, radius);
      const hue = hueIncrement * VESSELS_HUES_INCREMENT;
      const rgbString = hueToRgbString(hue);
      gradient.addColorStop(0, rgbString);

      const rgbOuter = hsvToRgb(Math.min(360, hue + 30), 80, 100);
      gradient.addColorStop(1, `rgba(${rgbOuter.r}, ${rgbOuter.g}, ${rgbOuter.b}, 0)`);

      tplCtx.fillStyle = gradient;
      tplCtx.fillRect(0, y, diameter, diameter);

      // circle style
      x += diameter + 1; // tiny offset between 2 frames
      tplCtx.beginPath();
      tplCtx.arc(x, yCenter, radius, 0, 2 * Math.PI, false);
      tplCtx.fillStyle = rgbString;
      tplCtx.fill();
    }

    return tplCanvas;
  }

  /**
   * Updates the main texture frame offset to show different brush styles and hues
   * Both args are optional, if one is omitted, previous value is used
   * @heatmapStyle bool whether to use heatmap style or solid circle style
   * @hue number hue value between 0 and 360
   */
  _setTextureFrame(useHeatmapStyle = null, hue = null) {
    const textureFrame = this.mainVesselTexture.frame.clone();

    if (useHeatmapStyle !== null) {
      // one diameter + tiny offset between 2 frames
      textureFrame.x = (useHeatmapStyle) ? 0 : VESSELS_BASE_RADIUS * 2 + 1;
    }

    if (hue !== null) {
      // 0 - 360 -> 0 -> 10
      let hueIncrement = hue / VESSELS_HUES_INCREMENT;
      if (hueIncrement === VESSELS_HUES_INCREMENTS_NUM) {
        hueIncrement = 0;
      }
      textureFrame.y = hueIncrement * VESSELS_BASE_RADIUS * 2;
      if (hueIncrement > 0) {
        textureFrame.y += hueIncrement;
      }
    }

    this.mainVesselTexture.frame = textureFrame;
    this.mainVesselTexture.update();
  }

  repositionCanvas() {
    if (!this.container) return;
    const offset = super.getRepositionOffset(this.viewportWidth, this.viewportHeight);
    this.container.style.left = `${offset.x}px`;
    this.container.style.top = `${offset.y}px`;
    this.renderer.resize(this.viewportWidth, this.viewportHeight);
    this.canvas.style.width = `${this.viewportWidth}px`;
    this.canvas.style.height = `${this.viewportHeight}px`;
  }

  updateViewportSize(viewportWidth, viewportHeight) {
    this.viewportWidth = viewportWidth;
    this.viewportHeight = viewportHeight;
    this._resizeSpritesPool();
    this.repositionCanvas();
  }

  draw() {
    this.repositionCanvas();
  }

  show() {
    this.hidden = false;
    this.container.style.display = 'block';
  }

  hide() {
    this.hidden = true;
    this.container.style.display = 'none';
    this._clear(true);
  }

  setZoom(zoom) {
    this._setTextureFrame(zoom < VESSELS_HEATMAP_STYLE_ZOOM_THRESHOLD);
  }

  setOpacity(opacity) {
    this.canvas.style.opacity = opacity;
  }

  setHue(hue) {
    this._setTextureFrame(null, hue);
  }

  render(tiles, startIndex, endIndex) {
    if (!this.stage || this.hidden) return;

    const newTimeIndexDelta = endIndex - startIndex;

    if (this.timeIndexDelta !== newTimeIndexDelta) {
      const delta = newTimeIndexDelta - this.timeIndexDelta;
      // because of the way dates are rounded, the range length can vary of one day even if the user didnt change range
      // in that case skip resizing sprites pool, avoiding doing this in the middle of on animation
      if (Math.abs(delta) !== 1) {
        this.timeIndexDelta = newTimeIndexDelta;
        this._resizeSpritesPool();
      }
    }

    this.numSprites = 0;

    tiles.forEach(tile => {
      const bounds = tile.getBoundingClientRect();
      if (!document.body.contains(tile)) {
        console.warn('rendering tile that doesnt exist in the DOM', tile);
      }

      if (bounds.left === 0 && bounds.top === 0) {
        console.warn('tile at 0,0');
      }

      if (tile.ready === true) {
        this.numSprites += this._dumpTileVessels(startIndex, endIndex, tile.data, bounds.left, bounds.top, tile.error);
      }
    });

    // hide unused sprites
    for (let i = this.numSprites, poolSize = this.spritesPool.length; i < poolSize; i++) {
      this.spritesPool[i].x = -100;
    }

    this.renderer.render(this.stage);
  }

  _dumpTileVessels(startIndex, endIndex, data, offsetX, offsetY) {
    if (!data) {
      return 0;
    }

    let numSprites = 0;

    for (let timeIndex = startIndex; timeIndex < endIndex; timeIndex ++) {
      const frame = data[timeIndex];

      if (!frame) continue;

      for (let index = 0, len = frame.x.length; index < len; index++) {
        if (Number.isInteger(this.flag) && this.flag !== frame.category[index]) {
          continue;
        }
        numSprites++;
        const sprite = this.spritesPool[this.numSprites];

        sprite.position.x = offsetX + frame.x[index];
        sprite.position.y = offsetY + frame.y[index];
        sprite.alpha = frame.opacity[index];
        sprite.scale.set(frame.radius[index]);

        this.numSprites++;
      }
    }

    return numSprites;
  }

  _resizeSpritesPool() {
    // compute final pool size
    const finalPoolSize = this.timeIndexDelta * this._getSpritesPerStep();
    const currentPoolSize = this.spritesPool.length;
    const poolDelta = finalPoolSize - currentPoolSize;

    if (poolDelta > 0) {
      this._addSprites(poolDelta);
    } else {
      const startRemovingAt = currentPoolSize - poolDelta;
      for (let i = startRemovingAt; i < currentPoolSize; i++) {
        // this is actually insanely costly - keep this in RAM and be done with it ?
        // this.stage.removeChild(this.spritesPool[i]);
      }
      // this.spritesPool.splice(- deltaSprites);
    }

    // disable all sprites and let render take it from there
    this._clear();
  }

  _addSprites(num) {
    // console.log('add sprites: ', num);
    for (let i = 0; i < num; i++) {
      const vessel = new PIXI.Sprite(this.mainVesselTexture);
      vessel.anchor.x = vessel.anchor.y = 0.5;
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      vessel.x = -100;
      // vessel.blendMode = PIXI.BLEND_MODES.SCREEN;
      // vessel.filters=  [new PIXI.filters.BlurFilter(10,10)]
      this.spritesPool.push(vessel);
      this.stage.addChild(vessel);
    }
  }

  _getSpritesPerStep() {
    return Math.round(this.viewportWidth * this.viewportHeight * MAX_SPRITES_FACTOR);
  }

  _clear(render = false) {
    for (let i = 0, poolSize = this.spritesPool.length; i < poolSize; i++) {
      // ParticlesContainer does not support .visible, so we just move the sprite out of the viewport
      this.spritesPool[i].x = -100;
    }
    if (render) {
      this.renderer.render(this.stage);
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'backbone',
    'underscore',
    'moment',
    'find/app/configuration'
], function(Backbone, _, moment, configuration) {

    'use strict';

    const MEDIA_TYPES = ['audio', 'image', 'video'];
    const WEB_TYPES = ['text/html', 'text/xhtml'];
    const isUrlRegex = /^(?:https?|ftp):\/\/|\\\\\S+/;

    function isURL(reference) {
        return isUrlRegex.test(reference);
    }

    const fieldTypeParsers = {
        STRING: function (valueWrapper) {
            return valueWrapper.displayValue
        },
        NUMBER: function (valueWrapper) {
            return valueWrapper.value
        },
        /**
         * @return {boolean}
         */
        BOOLEAN: function (valueWrapper) {
            return valueWrapper.value.toLowerCase() === 'true';
        },
        DATE: function (valueWrapper) {
            return moment(valueWrapper.value).format('LLLL');
        }
    };

    function getMediaType(contentType) {
        return contentType && _.find(MEDIA_TYPES, function(mediaType) {
            return contentType.indexOf(mediaType) === 0;
        });
    }

    function getFieldValues(fieldData) {
        if (fieldData && fieldData.values.length) {
            return _.map(fieldData.values, fieldTypeParsers[fieldData.type]);
        }

        return [];
    }

    const getFieldValue = _.compose(_.first, getFieldValues);

    // Model representing a document in an HOD text index
    return Backbone.Model.extend({
        url: 'api/public/search/get-document-content',

        defaults: {
            authors: [],
            fields: []
        },

        parse: function(response) {
            if (!response.title) {
                // If there is no title, use the last part of the reference (assuming the reference is a file path)
                // C:\Documents\file.txt -> file.txt
                // /home/user/another-file.txt -> another-file.txt
                const splitReference = response.reference.split(/\/|\\/);
                const lastPart = _.last(splitReference);

                if (/\S/.test(lastPart)) {
                    // Use the "file name" if it contains a non whitespace character
                    response.title = lastPart;
                } else {
                    response.title = response.reference;
                }
            }

            if (response.date) {
                response.date = moment(response.date);
            }

            response.thumbnail = getFieldValue(response.fieldMap.thumbnail);
            response.thumbnailUrl = getFieldValue(response.fieldMap.thumbnailUrl);
            response.contentType = getFieldValue(response.fieldMap.contentType);
            response.offset = getFieldValue(response.fieldMap.offset);
            response.mmapEventSourceType = getFieldValue(response.fieldMap.mmapEventSourceType);
            response.mmapEventSourceName = getFieldValue(response.fieldMap.mmapEventSourceName);
            response.mmapEventTime = getFieldValue(response.fieldMap.mmapEventTime);
            response.mmapUrl = getFieldValue(response.fieldMap.mmapUrl);
            response.sourceType = getFieldValue(response.fieldMap.sourceType);
            response.transcript = getFieldValue(response.fieldMap.transcript);

            response.url = getFieldValue(response.fieldMap.url) || (isURL(response.reference) ? response.reference : '');

            if (configuration().map.enabled) {
                response.locations = _.chain(configuration().map.locationFields)
                    .map(function (field) {
                        const latitudes = getFieldValues(response.fieldMap[field.latitudeField]);
                        const longitudes = getFieldValues(response.fieldMap[field.longitudeField]);

                        const locations = [];
                        for (let i = 0; i < latitudes.length && i < longitudes.length; i++) {
                            locations.push({
                                displayName: field.displayName,
                                latitude: latitudes[i],
                                longitude: longitudes[i],
                                iconName: field.iconName,
                                iconColor: field.iconColor,
                                markerColor: field.markerColor
                            })
                        }
                        return locations;
                    })
                    .flatten()
                    .groupBy('displayName')
                    .value()
            }

            response.media = getMediaType(response.contentType);

            response.authors = getFieldValues(response.fieldMap.authors);

            response.fields = _.chain(response.fieldMap)
                .map(function(fieldData) {
                    return _.defaults({
                        values: getFieldValues(fieldData)
                    }, fieldData);
                })
                .sortBy('displayName')
                .value();

            delete response.fieldMap;
            return response;
        },

        isMedia: function() {
            return Boolean(this.get('media') && this.get('url'));
        },

        isWebType: function() {
            const contentType = this.get('contentType');

            return contentType && _.contains(WEB_TYPES, contentType.toLowerCase());
        }
    });

});
<commit_msg>Use _.zip to better iterate over location array (FIND-1074)<commit_after>/*
 * Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
 * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
 */

define([
    'backbone',
    'underscore',
    'moment',
    'find/app/configuration'
], function(Backbone, _, moment, configuration) {

    'use strict';

    const MEDIA_TYPES = ['audio', 'image', 'video'];
    const WEB_TYPES = ['text/html', 'text/xhtml'];
    const isUrlRegex = /^(?:https?|ftp):\/\/|\\\\\S+/;

    function isURL(reference) {
        return isUrlRegex.test(reference);
    }

    const fieldTypeParsers = {
        STRING: function (valueWrapper) {
            return valueWrapper.displayValue
        },
        NUMBER: function (valueWrapper) {
            return valueWrapper.value
        },
        /**
         * @return {boolean}
         */
        BOOLEAN: function (valueWrapper) {
            return valueWrapper.value.toLowerCase() === 'true';
        },
        DATE: function (valueWrapper) {
            return moment(valueWrapper.value).format('LLLL');
        }
    };

    function getMediaType(contentType) {
        return contentType && _.find(MEDIA_TYPES, function(mediaType) {
            return contentType.indexOf(mediaType) === 0;
        });
    }

    function getFieldValues(fieldData) {
        if (fieldData && fieldData.values.length) {
            return _.map(fieldData.values, fieldTypeParsers[fieldData.type]);
        }

        return [];
    }

    const getFieldValue = _.compose(_.first, getFieldValues);

    // Model representing a document in an HOD text index
    return Backbone.Model.extend({
        url: 'api/public/search/get-document-content',

        defaults: {
            authors: [],
            fields: []
        },

        parse: function(response) {
            if (!response.title) {
                // If there is no title, use the last part of the reference (assuming the reference is a file path)
                // C:\Documents\file.txt -> file.txt
                // /home/user/another-file.txt -> another-file.txt
                const splitReference = response.reference.split(/\/|\\/);
                const lastPart = _.last(splitReference);

                if (/\S/.test(lastPart)) {
                    // Use the "file name" if it contains a non whitespace character
                    response.title = lastPart;
                } else {
                    response.title = response.reference;
                }
            }

            if (response.date) {
                response.date = moment(response.date);
            }

            response.thumbnail = getFieldValue(response.fieldMap.thumbnail);
            response.thumbnailUrl = getFieldValue(response.fieldMap.thumbnailUrl);
            response.contentType = getFieldValue(response.fieldMap.contentType);
            response.offset = getFieldValue(response.fieldMap.offset);
            response.mmapEventSourceType = getFieldValue(response.fieldMap.mmapEventSourceType);
            response.mmapEventSourceName = getFieldValue(response.fieldMap.mmapEventSourceName);
            response.mmapEventTime = getFieldValue(response.fieldMap.mmapEventTime);
            response.mmapUrl = getFieldValue(response.fieldMap.mmapUrl);
            response.sourceType = getFieldValue(response.fieldMap.sourceType);
            response.transcript = getFieldValue(response.fieldMap.transcript);

            response.url = getFieldValue(response.fieldMap.url) || (isURL(response.reference) ? response.reference : '');

            if (configuration().map.enabled) {
                response.locations = _.chain(configuration().map.locationFields)
                    .map(function (field) {
                        const latitudes = getFieldValues(response.fieldMap[field.latitudeField]);
                        const longitudes = getFieldValues(response.fieldMap[field.longitudeField]);

                        return _.zip(latitudes, longitudes).map(function (coordinates) {
                            return {
                                displayName: field.displayName,
                                latitude: coordinates[0],
                                longitude: coordinates[1],
                                iconName: field.iconName,
                                iconColor: field.iconColor,
                                markerColor: field.markerColor
                            }
                        });
                    })
                    .flatten()
                    .groupBy('displayName')
                    .value()
            }

            response.media = getMediaType(response.contentType);

            response.authors = getFieldValues(response.fieldMap.authors);

            response.fields = _.chain(response.fieldMap)
                .map(function(fieldData) {
                    return _.defaults({
                        values: getFieldValues(fieldData)
                    }, fieldData);
                })
                .sortBy('displayName')
                .value();

            delete response.fieldMap;
            return response;
        },

        isMedia: function() {
            return Boolean(this.get('media') && this.get('url'));
        },

        isWebType: function() {
            const contentType = this.get('contentType');

            return contentType && _.contains(WEB_TYPES, contentType.toLowerCase());
        }
    });

});
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const { URL } = require('url');
const hapi = require('@hapi/hapi');
const h2o2 = require('@hapi/h2o2');
const hapiPluginWebsocket = require('hapi-plugin-websocket');
const debugLog = require('./debugLog');
const LambdaContext = require('./LambdaContext.js');
const functionHelper = require('./functionHelper');
const { createUniqueId, parseQueryStringParameters } = require('./utils');
const authFunctionNameExtractor = require('./authFunctionNameExtractor');
const wsHelpers = require('./websocketHelpers');

module.exports = class ApiGatewayWebSocket {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.service = serverless.service;
    this.serverlessLog = serverless.cli.log.bind(serverless.cli);
    this.options = options;
    this.exitCode = 0;
    this.clients = new Map();
    this.actions = {};
    this.websocketsApiRouteSelectionExpression =
      serverless.service.provider.websocketsApiRouteSelectionExpression ||
      '$request.body.action';
  }

  printBlankLine() {
    console.log();
  }

  _createWebSocket() {
    // start COPY PASTE FROM HTTP SERVER CODE
    const serverOptions = {
      host: this.options.host,
      port: this.options.websocketPort,
      router: {
        stripTrailingSlash: !this.options.preserveTrailingSlash, // removes trailing slashes on incoming paths.
      },
    };

    const httpsDir = this.options.httpsProtocol;

    // HTTPS support
    if (typeof httpsDir === 'string' && httpsDir.length > 0) {
      serverOptions.tls = {
        key: fs.readFileSync(path.resolve(httpsDir, 'key.pem'), 'ascii'),
        cert: fs.readFileSync(path.resolve(httpsDir, 'cert.pem'), 'ascii'),
      };
    }

    serverOptions.state = this.options.enforceSecureCookies
      ? {
          isHttpOnly: true,
          isSameSite: false,
          isSecure: true,
        }
      : {
          isHttpOnly: false,
          isSameSite: false,
          isSecure: false,
        };

    // Hapijs server creation
    this.wsServer = hapi.server(serverOptions);

    this.wsServer.register(h2o2).catch((err) => err && this.serverlessLog(err));

    // Enable CORS preflight response
    this.wsServer.ext('onPreResponse', (request, h) => {
      if (request.headers.origin) {
        const response = request.response.isBoom
          ? request.response.output
          : request.response;

        response.headers['access-control-allow-origin'] =
          request.headers.origin;
        response.headers['access-control-allow-credentials'] = 'true';

        if (request.method === 'options') {
          response.statusCode = 200;
          response.headers['access-control-expose-headers'] =
            'content-type, content-length, etag';
          response.headers['access-control-max-age'] = 60 * 10;

          if (request.headers['access-control-request-headers']) {
            response.headers['access-control-allow-headers'] =
              request.headers['access-control-request-headers'];
          }

          if (request.headers['access-control-request-method']) {
            response.headers['access-control-allow-methods'] =
              request.headers['access-control-request-method'];
          }
        }
      }

      return h.continue;
    });
    // end COPY PASTE FROM HTTP SERVER CODE

    this.wsServer
      .register(hapiPluginWebsocket)
      .catch((err) => err && this.serverlessLog(err));

    const doAction = (ws, connectionId, name, event, doDefaultAction) => {
      const sendError = (err) => {
        if (ws.readyState === /* OPEN */ 1) {
          ws.send(
            JSON.stringify({
              message: 'Internal server error',
              connectionId,
              requestId: '1234567890',
            }),
          );
        }

        // mimic AWS behaviour (close connection) when the $connect action handler throws
        if (name === '$connect') {
          ws.close();
        }

        debugLog(`Error in handler of action ${action}`, err);
      };

      let action = this.actions[name];

      if (!action && doDefaultAction) action = this.actions.$default;
      if (!action) return;

      function cb(err) {
        if (!err) return;
        sendError(err);
      }

      // TEMP
      const func = {
        ...action.fun,
        name,
      };
      const context = new LambdaContext(func, this.service.provider, cb);

      let p = null;

      try {
        p = action.handler(event, context, cb);
      } catch (err) {
        sendError(err);
      }

      if (p) {
        p.catch((err) => {
          sendError(err);
        });
      }
    };

    this.wsServer.route({
      method: 'POST',
      path: '/',
      config: {
        payload: {
          output: 'data',
          parse: true,
          allow: 'application/json',
        },
        plugins: {
          websocket: {
            only: true,
            initially: false,
            connect: ({ ws, req }) => {
              const { searchParams } = new URL(req.url, `ws://${req.headers.host}`);
              const queryStringParameters = parseQueryStringParameters(
                searchParams,
              );
              const connection = {
                connectionId: createUniqueId(),
                connectionTime: Date.now(),
              };

              debugLog(`connect:${connection.connectionId}`);

              this.clients.set(ws, connection);

              let event = wsHelpers.createConnectEvent(
                '$connect',
                'CONNECT',
                connection,
                this.options,
              );

              if (Object.keys(queryStringParameters).length > 0) {
                event = { queryStringParameters, ...event };
              }

              doAction(ws, connection.connectionId, '$connect', event, false);
            },
            disconnect: ({ ws }) => {
              const connection = this.clients.get(ws);

              debugLog(`disconnect:${connection.connectionId}`);

              this.clients.delete(ws);

              const event = wsHelpers.createDisconnectEvent(
                '$disconnect',
                'DISCONNECT',
                connection,
              );

              doAction(
                ws,
                connection.connectionId,
                '$disconnect',
                event,
                false,
              );
            },
          },
        },
      },
      handler: (request, h) => {
        const { initially, ws } = request.websocket();

        if (!request.payload || initially) {
          return h.response().code(204);
        }

        const connection = this.clients.get(ws);
        let actionName = null;

        if (
          this.websocketsApiRouteSelectionExpression.startsWith(
            '$request.body.',
          )
        ) {
          actionName = request.payload;

          if (typeof actionName === 'object') {
            this.websocketsApiRouteSelectionExpression
              .replace('$request.body.', '')
              .split('.')
              .forEach((key) => {
                if (actionName) {
                  actionName = actionName[key];
                }
              });
          } else actionName = null;
        }

        if (typeof actionName !== 'string') {
          actionName = null;
        }

        const action = actionName || '$default';

        debugLog(`action:${action} on connection=${connection.connectionId}`);

        const event = wsHelpers.createEvent(
          action,
          'MESSAGE',
          connection,
          request.payload,
        );

        doAction(ws, connection.connectionId, action, event, true);

        return h.response().code(204);
      },
    });

    this.wsServer.route({
      method: 'GET',
      path: '/{path*}',
      handler: (request, h) => h.response().code(426),
    });

    this.wsServer.route({
      method: 'POST',
      path: '/@connections/{connectionId}',
      config: { payload: { parse: false } },
      handler: (request, h) => {
        debugLog(`got POST to ${request.url}`);

        const getByConnectionId = (map, searchValue) => {
          for (const [key, connection] of map.entries()) {
            if (connection.connectionId === searchValue) return key;
          }

          return undefined;
        };

        const ws = getByConnectionId(this.clients, request.params.connectionId);

        if (!ws) return h.response().code(410);
        if (!request.payload) return '';

        ws.send(request.payload.toString());

        debugLog(`sent data to connection:${request.params.connectionId}`);

        return '';
      },
    });

    this.wsServer.route({
      method: 'DELETE',
      path: '/@connections/{connectionId}',
      config: { payload: { parse: false } },
      handler: (request, h) => {
        debugLog(`got DELETE to ${request.url}`);

        const getByConnectionId = (map, searchValue) => {
          for (const [key, connection] of map.entries()) {
            if (connection.connectionId === searchValue) return key;
          }

          return undefined;
        };

        const ws = getByConnectionId(this.clients, request.params.connectionId);

        if (!ws) return h.response().code(410);

        ws.close();

        debugLog(`closed connection:${request.params.connectionId}`);

        return '';
      },
    });
  }

  _createWsAction(fun, funName, servicePath, funOptions, event) {
    let handler; // The lambda function
    Object.assign(process.env, this.originalEnvironment);

    try {
      if (this.options.noEnvironment) {
        // This evict errors in server when we use aws services like ssm
        const baseEnvironment = {
          AWS_REGION: 'dev',
        };

        if (!process.env.AWS_PROFILE) {
          baseEnvironment.AWS_ACCESS_KEY_ID = 'dev';
          baseEnvironment.AWS_SECRET_ACCESS_KEY = 'dev';
        }

        process.env = Object.assign(baseEnvironment, process.env);
      } else {
        Object.assign(
          process.env,
          { AWS_REGION: this.service.provider.region },
          this.service.provider.environment,
          this.service.functions[funName].environment,
        );
      }

      process.env._HANDLER = fun.handler;
      handler = functionHelper.createHandler(funOptions, this.options);
    } catch (error) {
      return this.serverlessLog(`Error while loading ${funName}`, error);
    }

    const actionName = event.websocket.route;
    const action = { funName, fun, funOptions, servicePath, handler };

    this.actions[actionName] = action;
    this.serverlessLog(`Action '${event.websocket.route}'`);
  }

  _extractAuthFunctionName(endpoint) {
    const result = authFunctionNameExtractor(endpoint, this.serverlessLog);

    return result.unsupportedAuth ? null : result.authorizerName;
  }

  // All done, we can listen to incomming requests
  async _listen() {
    try {
      await this.wsServer.start();
    } catch (error) {
      console.error(
        `Unexpected error while starting serverless-offline websocket server on port ${this.options.websocketPort}:`,
        error,
      );
      process.exit(1);
    }

    this.printBlankLine();
    this.serverlessLog(
      `Offline [websocket] listening on ws${
        this.options.httpsProtocol ? 's' : ''
      }://${this.options.host}:${this.options.websocketPort}`,
    );
  }
};
<commit_msg>Use constant for base url placeholder<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const { URL } = require('url');
const hapi = require('@hapi/hapi');
const h2o2 = require('@hapi/h2o2');
const hapiPluginWebsocket = require('hapi-plugin-websocket');
const debugLog = require('./debugLog');
const LambdaContext = require('./LambdaContext.js');
const functionHelper = require('./functionHelper');
const { createUniqueId, parseQueryStringParameters } = require('./utils');
const authFunctionNameExtractor = require('./authFunctionNameExtractor');
const wsHelpers = require('./websocketHelpers');

// dummy placeholder url for the WHATWG URL constructor
// https://github.com/nodejs/node/issues/12682
// TODO move to common constants file
const BASE_URL_PLACEHOLDER = 'http://example';

module.exports = class ApiGatewayWebSocket {
  constructor(serverless, options) {
    this.serverless = serverless;
    this.service = serverless.service;
    this.serverlessLog = serverless.cli.log.bind(serverless.cli);
    this.options = options;
    this.exitCode = 0;
    this.clients = new Map();
    this.actions = {};
    this.websocketsApiRouteSelectionExpression =
      serverless.service.provider.websocketsApiRouteSelectionExpression ||
      '$request.body.action';
  }

  printBlankLine() {
    console.log();
  }

  _createWebSocket() {
    // start COPY PASTE FROM HTTP SERVER CODE
    const serverOptions = {
      host: this.options.host,
      port: this.options.websocketPort,
      router: {
        stripTrailingSlash: !this.options.preserveTrailingSlash, // removes trailing slashes on incoming paths.
      },
    };

    const httpsDir = this.options.httpsProtocol;

    // HTTPS support
    if (typeof httpsDir === 'string' && httpsDir.length > 0) {
      serverOptions.tls = {
        key: fs.readFileSync(path.resolve(httpsDir, 'key.pem'), 'ascii'),
        cert: fs.readFileSync(path.resolve(httpsDir, 'cert.pem'), 'ascii'),
      };
    }

    serverOptions.state = this.options.enforceSecureCookies
      ? {
          isHttpOnly: true,
          isSameSite: false,
          isSecure: true,
        }
      : {
          isHttpOnly: false,
          isSameSite: false,
          isSecure: false,
        };

    // Hapijs server creation
    this.wsServer = hapi.server(serverOptions);

    this.wsServer.register(h2o2).catch((err) => err && this.serverlessLog(err));

    // Enable CORS preflight response
    this.wsServer.ext('onPreResponse', (request, h) => {
      if (request.headers.origin) {
        const response = request.response.isBoom
          ? request.response.output
          : request.response;

        response.headers['access-control-allow-origin'] =
          request.headers.origin;
        response.headers['access-control-allow-credentials'] = 'true';

        if (request.method === 'options') {
          response.statusCode = 200;
          response.headers['access-control-expose-headers'] =
            'content-type, content-length, etag';
          response.headers['access-control-max-age'] = 60 * 10;

          if (request.headers['access-control-request-headers']) {
            response.headers['access-control-allow-headers'] =
              request.headers['access-control-request-headers'];
          }

          if (request.headers['access-control-request-method']) {
            response.headers['access-control-allow-methods'] =
              request.headers['access-control-request-method'];
          }
        }
      }

      return h.continue;
    });
    // end COPY PASTE FROM HTTP SERVER CODE

    this.wsServer
      .register(hapiPluginWebsocket)
      .catch((err) => err && this.serverlessLog(err));

    const doAction = (ws, connectionId, name, event, doDefaultAction) => {
      const sendError = (err) => {
        if (ws.readyState === /* OPEN */ 1) {
          ws.send(
            JSON.stringify({
              message: 'Internal server error',
              connectionId,
              requestId: '1234567890',
            }),
          );
        }

        // mimic AWS behaviour (close connection) when the $connect action handler throws
        if (name === '$connect') {
          ws.close();
        }

        debugLog(`Error in handler of action ${action}`, err);
      };

      let action = this.actions[name];

      if (!action && doDefaultAction) action = this.actions.$default;
      if (!action) return;

      function cb(err) {
        if (!err) return;
        sendError(err);
      }

      // TEMP
      const func = {
        ...action.fun,
        name,
      };
      const context = new LambdaContext(func, this.service.provider, cb);

      let p = null;

      try {
        p = action.handler(event, context, cb);
      } catch (err) {
        sendError(err);
      }

      if (p) {
        p.catch((err) => {
          sendError(err);
        });
      }
    };

    this.wsServer.route({
      method: 'POST',
      path: '/',
      config: {
        payload: {
          output: 'data',
          parse: true,
          allow: 'application/json',
        },
        plugins: {
          websocket: {
            only: true,
            initially: false,
            connect: ({ ws, req }) => {
              const { searchParams } = new URL(req.url, BASE_URL_PLACEHOLDER);
              const queryStringParameters = parseQueryStringParameters(
                searchParams,
              );
              const connection = {
                connectionId: createUniqueId(),
                connectionTime: Date.now(),
              };

              debugLog(`connect:${connection.connectionId}`);

              this.clients.set(ws, connection);

              let event = wsHelpers.createConnectEvent(
                '$connect',
                'CONNECT',
                connection,
                this.options,
              );

              if (Object.keys(queryStringParameters).length > 0) {
                event = { queryStringParameters, ...event };
              }

              doAction(ws, connection.connectionId, '$connect', event, false);
            },
            disconnect: ({ ws }) => {
              const connection = this.clients.get(ws);

              debugLog(`disconnect:${connection.connectionId}`);

              this.clients.delete(ws);

              const event = wsHelpers.createDisconnectEvent(
                '$disconnect',
                'DISCONNECT',
                connection,
              );

              doAction(
                ws,
                connection.connectionId,
                '$disconnect',
                event,
                false,
              );
            },
          },
        },
      },
      handler: (request, h) => {
        const { initially, ws } = request.websocket();

        if (!request.payload || initially) {
          return h.response().code(204);
        }

        const connection = this.clients.get(ws);
        let actionName = null;

        if (
          this.websocketsApiRouteSelectionExpression.startsWith(
            '$request.body.',
          )
        ) {
          actionName = request.payload;

          if (typeof actionName === 'object') {
            this.websocketsApiRouteSelectionExpression
              .replace('$request.body.', '')
              .split('.')
              .forEach((key) => {
                if (actionName) {
                  actionName = actionName[key];
                }
              });
          } else actionName = null;
        }

        if (typeof actionName !== 'string') {
          actionName = null;
        }

        const action = actionName || '$default';

        debugLog(`action:${action} on connection=${connection.connectionId}`);

        const event = wsHelpers.createEvent(
          action,
          'MESSAGE',
          connection,
          request.payload,
        );

        doAction(ws, connection.connectionId, action, event, true);

        return h.response().code(204);
      },
    });

    this.wsServer.route({
      method: 'GET',
      path: '/{path*}',
      handler: (request, h) => h.response().code(426),
    });

    this.wsServer.route({
      method: 'POST',
      path: '/@connections/{connectionId}',
      config: { payload: { parse: false } },
      handler: (request, h) => {
        debugLog(`got POST to ${request.url}`);

        const getByConnectionId = (map, searchValue) => {
          for (const [key, connection] of map.entries()) {
            if (connection.connectionId === searchValue) return key;
          }

          return undefined;
        };

        const ws = getByConnectionId(this.clients, request.params.connectionId);

        if (!ws) return h.response().code(410);
        if (!request.payload) return '';

        ws.send(request.payload.toString());

        debugLog(`sent data to connection:${request.params.connectionId}`);

        return '';
      },
    });

    this.wsServer.route({
      method: 'DELETE',
      path: '/@connections/{connectionId}',
      config: { payload: { parse: false } },
      handler: (request, h) => {
        debugLog(`got DELETE to ${request.url}`);

        const getByConnectionId = (map, searchValue) => {
          for (const [key, connection] of map.entries()) {
            if (connection.connectionId === searchValue) return key;
          }

          return undefined;
        };

        const ws = getByConnectionId(this.clients, request.params.connectionId);

        if (!ws) return h.response().code(410);

        ws.close();

        debugLog(`closed connection:${request.params.connectionId}`);

        return '';
      },
    });
  }

  _createWsAction(fun, funName, servicePath, funOptions, event) {
    let handler; // The lambda function
    Object.assign(process.env, this.originalEnvironment);

    try {
      if (this.options.noEnvironment) {
        // This evict errors in server when we use aws services like ssm
        const baseEnvironment = {
          AWS_REGION: 'dev',
        };

        if (!process.env.AWS_PROFILE) {
          baseEnvironment.AWS_ACCESS_KEY_ID = 'dev';
          baseEnvironment.AWS_SECRET_ACCESS_KEY = 'dev';
        }

        process.env = Object.assign(baseEnvironment, process.env);
      } else {
        Object.assign(
          process.env,
          { AWS_REGION: this.service.provider.region },
          this.service.provider.environment,
          this.service.functions[funName].environment,
        );
      }

      process.env._HANDLER = fun.handler;
      handler = functionHelper.createHandler(funOptions, this.options);
    } catch (error) {
      return this.serverlessLog(`Error while loading ${funName}`, error);
    }

    const actionName = event.websocket.route;
    const action = { funName, fun, funOptions, servicePath, handler };

    this.actions[actionName] = action;
    this.serverlessLog(`Action '${event.websocket.route}'`);
  }

  _extractAuthFunctionName(endpoint) {
    const result = authFunctionNameExtractor(endpoint, this.serverlessLog);

    return result.unsupportedAuth ? null : result.authorizerName;
  }

  // All done, we can listen to incomming requests
  async _listen() {
    try {
      await this.wsServer.start();
    } catch (error) {
      console.error(
        `Unexpected error while starting serverless-offline websocket server on port ${this.options.websocketPort}:`,
        error,
      );
      process.exit(1);
    }

    this.printBlankLine();
    this.serverlessLog(
      `Offline [websocket] listening on ws${
        this.options.httpsProtocol ? 's' : ''
      }://${this.options.host}:${this.options.websocketPort}`,
    );
  }
};
<|endoftext|>


<commit_before>'use strict';

var colorNarrator = new Narrator ();
var colorUser = new User();

var colorPhaseOne = new Phase (
    'Welcome to a world of color! Black and white can be very appealing, but color can also add a lot to a page. Let\'s experiment! First, choose a color for the background of your page.',
    '<input type="submit" value="Onward!">',
    '<input type="radio" value="grey">  Grey',
    '<input type="radio" value="lightblue">  Light Blue'
);

var colorPhaseTwo = new Phase (
    'Next, choose a color for your header.',
    '<input type="submit" value="Onward!">',
    '<input type="radio" value="darkgreen">  Dark Green',
    '<input type="radio" value="maroon">  Maroon'
);

var colorPhaseThree = new Phase (
    'Lastly, choose a color for the text in your paragraph.',
    '<input type="submit" value="Onward!">',
    '<input type="radio" value="darkblue">  Dark Blue',
    '<input type="radio" value="red">  Red'
);

var colorPhaseFour = new Phase (
    'What do you think? If you\'re happy with it, submit it to see your changes, so far.',
    '<input type="submit" value="Onward!"'
);

Phase.prototype.executePhase = function(){
        colorNarrator.talkLoc.innerText = this.talkAt;
        colorNarrator.buttonOne.innerHTML = this.buttonOne;

if (this.inputOne) {
    colorNarrator.listenLocOne.innerHTML = this.inputOne;
}

if (this.inputTwo) {
    colorNarrator.listenLocTwo.innerHTML = this.inputTwo;
}

if (this.buttonTwo) {
    colorNarrator.buttonTwo.innerHTML = this.buttonTwo;
}
    };

colorNarrator.saveGuest = function() {
    this.guest = colorUser;
};

//  give the user a page to view

var onloadPage = function() {
    colorPhaseOne.executePhase();
// colorNarrator.talkLoc.innerText = colorPhaseOne.talkAt;
// colorNarrator.buttonOne.innerHTML = colorPhaseOne.buttonOne;

// if (colorPhaseOne.inputOne) {
//     colorNarrator.listenLocOne.innerHTML = colorPhaseOne.inputOne;
// }

// if (colorPhaseOne.inputTwo) {
//     colorNarrator.listenLocTwo.innerHTML = colorPhaseOne.inputTwo;
// }

// if (colorPhaseOne.buttonTwo) {
//     colorNarrator.buttonTwo.innerHTML = colorPhaseOne.buttonTwo;
// }

};

onloadPage();

var submitButton = document.getElementById('buttonOne');
submitButton.addEventListener('click', submitHandler);

function submitHandler() {
    event.preventDefault();
    // colorUser.userSubmission();
    colorNarrator.saveGuest();
    colorNarrator.locallyStoreUser();
    window.location.href= './roughdraft.html';
}

// var inputSpotOne = document.getElementById('inputOne');
// inputSpotOne.innerHTML = '<input type="radio" value="grey">';
<commit_msg>creating phases for each page<commit_after>'use strict';

var colorNarrator = new Narrator();
var colorUser = new User();
var currentPhase = 1;

var colorPhaseOne = new Phase(
    'Welcome to a world of color! Black and white can be very appealing, but color can also add a lot to a page. Let\'s experiment! First, choose a color for the background of your page.',
    '<input type="submit" value="Onward!">',
    '<input type="radio" name ="colorId" value="grey">  Grey',
    '<input type="radio" name ="colorId" value="lightblue">  Light Blue'
);

var colorPhaseTwo = new Phase(
    'Next, choose a color for your header.',
    '<input type="submit" value="Onward!">',
    '<input type="radio"  name ="colorId"value="darkgreen">  Dark Green',
    '<input type="radio" name ="colorId" value="maroon">  Maroon'
);

var colorPhaseThree = new Phase(
    'Lastly, choose a color for the text in your paragraph.',
    '<input type="submit" value="Onward!">',
    '<input type="radio" name ="colorId" value="darkblue">  Dark Blue',
    '<input type="radio" name ="colorId" value="red">  Red'
);

var colorPhaseFour = new Phase(
    'What do you think? If you\'re happy with it, submit it to see your changes, so far.',
    '<input type="submit" value="Onward!"'
);

Phase.prototype.executePhase = function () {
	colorNarrator.talkLoc.innerText = this.talkAt;
	colorNarrator.buttonOne.innerHTML = this.buttonOne;

	if (this.inputOne) {
		colorNarrator.listenLocOne.innerHTML = this.inputOne;
	}

	if (this.inputTwo) {
		colorNarrator.listenLocTwo.innerHTML = this.inputTwo;
	}

	if (this.buttonTwo) {
		colorNarrator.buttonTwo.innerHTML = this.buttonTwo;
	}
};

colorNarrator.saveGuest = function () {
	this.guest = colorUser;
};

//  give the user a page to view

colorPhaseOne.executePhase();

var submitButton = document.getElementById('buttonOne');
submitButton.addEventListener('click', submitHandler);

function submitHandler() {
	event.preventDefault();
	if (currentPhase === 1) {
		var colorIds = document.getElementsByName('colorId');
		var selectedColor = [];
		for (var i = 0; i < colorIds.length; i++) {
			if (colorIds[i].checked) {
				selectedColor.push(colorIds[i].value);
			}
		}
		colorUser.color1 = selectedColor;
        document.getElementsByTagName('body')[0].setAttribute('class',selectedColor);
		colorPhaseTwo.executePhase();
		currentPhase += 1;
	}

	colorPhaseThree.executePhase();
	colorPhaseFour.executePhase();

    //save to localstorage and move to next page
	colorNarrator.saveGuest();
	colorNarrator.locallyStoreUser();
	window.location.href = './roughdraft.html';
}

// var inputSpotOne = document.getElementById('inputOne');
// inputSpotOne.innerHTML = '<input type="radio" value="grey">';
<|endoftext|>


<commit_before>var smocks = require('../lib');

// we always have to set a mock server id
smocks.id('example');

// fixture demonstrating user config, handling state and multiple variants
smocks.route({
    id: 'counter',
    label: 'Counter and Message', // label is optional
    path: '/api/counter',
    method: 'GET', // method is optional if it is GET

    config: {
      countBy: {
        label: 'Count by',
        type: 'text',
        defaultValue: '1'
      }
    },

    handler: function (req, reply) {
    // routes can have a default handler but do not have to
      respond.call(this, 'default message', reply);
    }
  })

  // add a separate handler for this fixture
  .variant({
    id: 'hello_world',
    label: 'hello world',
    handler: function (req, reply) {
      respond.call(this, 'hello world', reply);
    }
  })

  // and we can have as many variants as we want
  .variant({
    id: 'hello_universe',
    label: 'hello universe',
    handler: function (req, reply) {
      respond.call(this, 'hello universe', reply);
    }
  });

function respond (message, reply) {
  // context is "this" from the route handler

  // this is how you get the user config value
  var countBy = this.config('countBy');
  countBy = parseInt(countBy, 10) || 1;

  // this is how you get state values
  var count = this.state('counter') || 0;
  count = count + countBy;

  // this is how you set a state value
  this.state('counter', count);

  reply({
    message: message,
    count: count
  });
}

// now start the server
require('smocks/hapi').start({
  port: 8000,
  host: 'localhost'
});
<commit_msg>update smocks reference<commit_after>var smocks = require('smocks');

// we always have to set a mock server id
smocks.id('example');

// fixture demonstrating user config, handling state and multiple variants
smocks.route({
    id: 'counter',
    label: 'Counter and Message', // label is optional
    path: '/api/counter',
    method: 'GET', // method is optional if it is GET

    config: {
      countBy: {
        label: 'Count by',
        type: 'text',
        defaultValue: '1'
      }
    },

    handler: function (req, reply) {
    // routes can have a default handler but do not have to
      respond.call(this, 'default message', reply);
    }
  })

  // add a separate handler for this fixture
  .variant({
    id: 'hello_world',
    label: 'hello world',
    handler: function (req, reply) {
      respond.call(this, 'hello world', reply);
    }
  })

  // and we can have as many variants as we want
  .variant({
    id: 'hello_universe',
    label: 'hello universe',
    handler: function (req, reply) {
      respond.call(this, 'hello universe', reply);
    }
  });

function respond (message, reply) {
  // context is "this" from the route handler

  // this is how you get the user config value
  var countBy = this.config('countBy');
  countBy = parseInt(countBy, 10) || 1;

  // this is how you get state values
  var count = this.state('counter') || 0;
  count = count + countBy;

  // this is how you set a state value
  this.state('counter', count);

  reply({
    message: message,
    count: count
  });
}

// now start the server
require('smocks/hapi').start({
  port: 8000,
  host: 'localhost'
});
<|endoftext|>


<commit_before> 'use strict';

 L.Control.Angular = L.Control.extend({
    options: {
         position: 'bottomleft',
         template: ''
     },
     onAdd: function onAdd (map) {
         var that = this;
         this._container = L.DomUtil.create('div', 'angular-control-leaflet');

         L.DomEvent
            .on(this._container, 'dblclick', L.DomEvent.stop)
            .on(this._container, 'click', L.DomEvent.stop);

         angular.element(document).ready(function() {

             var $injector = angular.element(document).injector();

             if (!$injector) {
                 $injector = angular.element(document.querySelectorAll('[ng-app]')).injector();
             }

             if (!$injector) {
                 throw "L.Control.Angular can't find your Angular app";
             }

             var $rootScope = $injector.get('$rootScope'),
                 $compile = $injector.get('$compile'),
                 $controller = $injector.get('$controller');

             that._scope = $rootScope.$new(true);

             var element = angular.element(that._container);
             element.html(that.options.template);

             if (that.options.controller) {
                 var controller = $controller(that.options.controller, {
                     '$map': map,
                     '$scope': that._scope,
                     '$element': element,
                     '$options': that.options
                 });

                 if (that.options.controllerAs) {
                     that._scope[that.options.controllerAs] = controller;
                 }

                 element.data('$ngControllerController', controller);
                 element.children().data('$ngControllerController', controller);
             }

             $compile(element)(that._scope);
             that._scope.$apply();
         });
         return this._container;
     },
     onRemove: function(){
        if(this._scope){
            this._scope.$destroy();
        }

        L.DomEvent
            .off(this._container, 'dblclick', L.DomEvent.stop)
            .off(this._container, 'click', L.DomEvent.stop);
     }
 });

 L.control.angular = function(options) {
     return new L.Control.Angular(options);
 };<commit_msg>Disabled click and scroll events propagation<commit_after> 'use strict';

 L.Control.Angular = L.Control.extend({
    options: {
         position: 'bottomleft',
         template: ''
     },
     onAdd: function onAdd (map) {
         var that = this;
         this._container = L.DomUtil.create('div', 'angular-control-leaflet');

         if (!L.Browser.touch) {
             L.DomEvent
               .disableClickPropagation(this._container)
               .disableScrollPropagation(this._container);
         } else {
             L.DomEvent.disableClickPropagation(this._container);
         }

         angular.element(document).ready(function() {
             var $injector = angular.element(document).injector();

             if (!$injector) {
                 $injector = angular.element(document.querySelectorAll('[ng-app]')).injector();
             }

             if (!$injector) {
                 throw "L.Control.Angular can't find your Angular app";
             }

             var $rootScope = $injector.get('$rootScope'),
                 $compile = $injector.get('$compile'),
                 $controller = $injector.get('$controller');

             that._scope = $rootScope.$new(true);

             var element = angular.element(that._container);
             element.html(that.options.template);

             if (that.options.controller) {
                 var controller = $controller(that.options.controller, {
                     '$map': map,
                     '$scope': that._scope,
                     '$element': element,
                     '$options': that.options
                 });

                 if (that.options.controllerAs) {
                     that._scope[that.options.controllerAs] = controller;
                 }

                 element.data('$ngControllerController', controller);
                 element.children().data('$ngControllerController', controller);
             }

             $compile(element)(that._scope);
             that._scope.$apply();
         });
         return this._container;
     },
     onRemove: function(){
        if(this._scope){
            this._scope.$destroy();
        }
     }
 });

 L.control.angular = function(options) {
     return new L.Control.Angular(options);
 };<|endoftext|>


<commit_before>var win = Titanium.UI.currentWindow;
win.backgroundColor = 'black';

var minDate = new Date()
minDate.setFullYear(2009);
minDate.setMonth(0);
minDate.setDate(1)

var maxDate = new Date()
maxDate.setFullYear(2009);
maxDate.setMonth(11);
maxDate.setDate(31)

var value = new Date();
value.setFullYear(2009);
value.setMonth(0);
value.setDate(1);

var picker = Ti.UI.createPicker({
	type:Ti.UI.PICKER_TYPE_DATE,
	minDate:minDate,
	maxDate:maxDate,
	value:value
});

// turn on the selection indicator (off by default)
picker.selectionIndicator = true;

win.add(picker);

var label = Ti.UI.createLabel({
	text:'Choose a date',
	top:6,
	width:'auto',
	height:'auto',
	textAlign:'center',
	color:'white'
});
win.add(label);

picker.addEventListener('change',function(e)
{
	label.text = e.value;
});
<commit_msg>[#1068 state:open] Picker test for setting locale.  Should work when Apple fixes this.<commit_after>var win = Titanium.UI.currentWindow;
win.backgroundColor = 'black';

var minDate = new Date()
minDate.setFullYear(2009);
minDate.setMonth(0);
minDate.setDate(1)

var maxDate = new Date()
maxDate.setFullYear(2009);
maxDate.setMonth(11);
maxDate.setDate(31)

var value = new Date();
value.setFullYear(2009);
value.setMonth(0);
value.setDate(1);

var picker = Ti.UI.createPicker({
	type:Ti.UI.PICKER_TYPE_DATE,
	minDate:minDate,
	maxDate:maxDate,
	value:value,
});

// turn on the selection indicator (off by default)
picker.selectionIndicator = true;

win.add(picker);

var label = Ti.UI.createLabel({
	text:'Choose a date',
	top:6,
	width:'auto',
	height:'auto',
	textAlign:'center',
	color:'white'
});
win.add(label);

picker.addEventListener('change',function(e)
{
	label.text = e.value;
});

var locale = false
var localebutton = Ti.UI.createButton({
	title:'Change locale',
	bottom:20,
	width:200,
	height:40
});
localebutton.addEventListener('click', function() {
	if (!locale) {
		picker.setLocale('ru');
		locale = true;
	}
	else {
		locale = false;
		picker.setLocale(Titanium.Platform.locale);
	}
});
win.add(localebutton);<|endoftext|>


<commit_before>import React, {Component} from 'react';
import axios from 'axios';

class Papers extends Component {
    state = {
        article: {},
        attachments: []
    }
    getProps = () => {
        return this.props.location.params;
    }
    getArticleData = () => {
        const hash = new Buffer(`${this.getProps().userToken}:anything`).toString('base64')
        axios({
                method: 'GET',
                url: this.getProps().apiPath+'/api/articles/'+this.getProps().articleId,
                headers: {
                    'Authorization': `Basic ${hash}`
                }
            }).then((response) => {
                console.log(response);
                var attachments = [];
                if ( response.data.attachments.length > 0 ) {
                    response.data.attachments.forEach((item) => {
                       attachments.push( this.getAttachment(item.attachmentId) );
                    })

                }
                this.setState({
                    article: response.data,
                    attachments: attachments
                })
            })
    }
    getAttachment = (attachmentId) => {
        const hash = new Buffer(`${this.getProps().userToken}:anything`).toString('base64');
        axios({
                method: 'GET',
                url: this.getProps().apiPath+'/api/articles/attachments/'+attachmentId,
                headers: {
                    'Authorization': `Basic ${hash}`
                }
            }).then((response) => {
                console.log(response);
                return response.data;
            })
    }
    postAttachment = (attachment) => {
        console.log(attachment);
        var datas = new FormData();
        datas.append('foo', 'bar');
        datas.append('file', attachment);
        const hash = new Buffer(`${this.getProps().userToken}:anything`).toString('base64')
        axios({
            method: 'post',
            url: this.getProps().apiPath+'/api/articles/'+this.getProps().articleId+'/attachments',
            headers: {
                'Content-Type': 'multipart/form-data',
                'Authorization': `Basic ${hash}`
            },
            data: datas
        }).then((response) => {
            console.log(response);
            this.getArticleData();
            return true;
        });
        return false;
    }
    handleFileUpload = (event) => {
        event.preventDefault();
        this.postAttachment(event.target[0].files[0])
    }
    componentWillMount = () => {
        this.componentWillReceiveProps();
    }
    componentWillReceiveProps = () => {
        //console.log(this.props);
        this.getArticleData();
    }
    render() {
        //console.log(this.state.attachments);
        var attachments="";
        if (this.state.article.attachments) {
            attachments =

                    {this.state.article.attachments.map( attachment =>

{attachment.name}

)}

        }
        return (

{this.state.article.name}

{this.state.article.theme}

{this.state.article.description}

{this.state.article.label}

{this.state.article.text}

                {attachments}
                <form onSubmit={this.handleFileUpload}>
                Attach file to this article

                <input type="file" id="addFileToArticle" name="files" accept="*/*" />
                <input type="submit" value="Submit your file" />
                </form>

        )
    }
}

export default Papers;

/*
<BrowserRouter>

                            <NavLink exact strict activeClassName="active" className="managePaperLink" to={pathTo} >Manage paper</NavLink>

                            <Route path='/users/paperManage/:articleId/manage' exact component={() => (<PaperManage articleName={this.props.articleName}/>)} />

                </BrowserRouter>
*/<commit_msg>quick fix<commit_after>import React, {Component} from 'react';
import axios from 'axios';

class Papers extends Component {
    state = {
        article: {},
        attachments: []
    }
    getProps = () => {
        return this.props.location.params;
    }
    getArticleData = () => {
        const hash = new Buffer(`${this.getProps().userToken}:anything`).toString('base64')
        axios({
                method: 'GET',
                url: this.getProps().apiPath+'/api/articles/'+this.getProps().articleId,
                headers: {
                    'Authorization': `Basic ${hash}`
                }
            }).then((response) => {
                //console.log(response);
                var attachments = [];
                if ( response.data.attachments.length > 0 ) {
                    response.data.attachments.forEach((item) => {
                       attachments.push( this.getAttachment(item.attachmentId) );
                    })

                }
                this.setState({
                    article: response.data,
                    attachments: attachments
                })
            })
    }
    getAttachment = (attachmentId) => {
        const hash = new Buffer(`${this.getProps().userToken}:anything`).toString('base64');
        axios({
                method: 'GET',
                url: this.getProps().apiPath+'/api/articles/attachments/'+attachmentId,
                headers: {
                    'Authorization': `Basic ${hash}`
                }
            }).then((response) => {
                //console.log(response);
                return response.data;
            })
    }
    postAttachment = (attachment) => {
        //console.log(attachment);
        var datas = new FormData();
        datas.append('foo', 'bar');
        datas.append('file', attachment);
        const hash = new Buffer(`${this.getProps().userToken}:anything`).toString('base64')
        axios({
            method: 'post',
            url: this.getProps().apiPath+'/api/articles/'+this.getProps().articleId+'/attachments',
            headers: {
                'Content-Type': 'multipart/form-data',
                'Authorization': `Basic ${hash}`
            },
            data: datas
        }).then((response) => {
            //console.log(response);
            this.getArticleData();
            return true;
        });
        return false;
    }
    handleFileUpload = (event) => {
        event.preventDefault();
        this.postAttachment(event.target[0].files[0])
    }
    componentWillMount = () => {
        this.componentWillReceiveProps();
    }
    componentWillReceiveProps = () => {
        //console.log(this.props);
        this.getArticleData();
    }
    render() {
        //console.log(this.state.attachments);
        var attachments="";
        if (this.state.article.attachments) {
            attachments =

                    {this.state.article.attachments.map( attachment =>

{attachment.name}

)}

        }
        return (

{this.state.article.name}

{this.state.article.theme}

{this.state.article.description}

{this.state.article.label}

{this.state.article.text}

                {attachments}
                <form onSubmit={this.handleFileUpload}>
                Attach file to this article

                <input type="file" id="addFileToArticle" name="files" accept="*/*" />
                <input type="submit" value="Submit your file" />
                </form>

        )
    }
}

export default Papers;

/*
<BrowserRouter>

                            <NavLink exact strict activeClassName="active" className="managePaperLink" to={pathTo} >Manage paper</NavLink>

                            <Route path='/users/paperManage/:articleId/manage' exact component={() => (<PaperManage articleName={this.props.articleName}/>)} />

                </BrowserRouter>
*/<|endoftext|>


<commit_before>'use strict';

/**
 * @param {{}} user_options
 * @param {number} user_options.count
 * @param {number} user_options.start
 *
 * @returns {{}}
 */
function getQueryString( user_options ) {
  var query_string = {};
  var options = user_options || {};

  if ( typeof options.count === 'number' && !isNaN( options.count ) ) {
    query_string.count = options.count;
  }

  if ( typeof options.start === 'number' && !isNaN( options.start ) ) {
    query_string.start = options.start;
  }

  return query_string;
}

module.exports = getQueryString;
<commit_msg>added URI encoding to query string<commit_after>'use strict';

/**
 * @param {{}} user_options
 * @param {number} user_options.count
 * @param {number} user_options.start
 *
 * @returns {{}}
 */
function getQueryString( user_options ) {
  var query_string = {};
  var options = user_options || {};

  if ( typeof options.count === 'number' && !isNaN( options.count ) ) {
    query_string.count = encodeURIComponent( String( options.count ) );
  }

  if ( typeof options.start === 'number' && !isNaN( options.start ) ) {
    query_string.start = encodeURIComponent( String( options.start ) );
  }

  return query_string;
}

module.exports = getQueryString;
<|endoftext|>