<commit_before>/**
 * Simple webserver with logging. By default, serves whatever files are
 * reachable from the directory where node is running.
 */
var VERSION = "0.2";
var fs = require('fs'),
    pathlib = require('path'),
    uri = require('url'),
    events = require('events'),
    mime = require('./lib/content-type'),
    log = require('./lib/log');

log.level = log.levels.DEBUG;

var settings = {
    "max_bytes_per_read": 1024 * 1024 * 5, // 5MB
    "timeout_milliseconds": 1000 * 30, //30 sec
    "hosts" : [],
    "port" : 8080,
    "default_host" : {
        "root" : "./"
    }
};

try {
    log.debug("Reading/parsing settings.json");
    var custom_settings = JSON.parse(fs.readFileSync('./settings.json'));
    process.mixin(settings, custom_settings);
} catch(e) {
    log.warn("Using default settings: cannot read settings.json.",e);
}

log.info( "Starting server on port", settings.port);
require("http").createServer(function(req,resp) {
    log.info("Request from ",req.connection.remoteAddress,"for",req.url);
    log.info(JSON.stringify(req.headers));
    resp.die = setTimeout(function() {
        finish(resp);
    }, settings.timeout_milliseconds);

    find_local_file_path(req).addListener('success', function(path) {
        try_stream(path, resp);
    });
}).listen(settings.port);

function find_local_file_path(req) {
    var vhost = settings.hosts[req.headers.host] || settings.default_host;
    log.debug("selected vhost",vhost.root);
    //disallow parent directory access
    var clean_path = uri.parse(req.url || '/').pathname.replace(/\.\.\//g);
    var path = pathlib.join(vhost.root, clean_path);

    var emitter = new events.EventEmitter();
    fs.stat(path).addCallback(function (stat) {
        if (stat.isDirectory()) {
            path = pathlib.join(path, "index.html");
        }
        emitter.emit('success', path);
    }).addErrback(function () {
        emitter.emit('success', path);
    });
    return emitter;
}

function try_stream(path, resp) {
    function send_headers(httpstatus, length, content_type, modified_time) {
        var headers = {
            "Content-Type" : content_type || "application/octet-stream",
            "Server" : "Antinode/"+VERSION+" Node.js/"+process.version,
            "Date" : (new Date()).toUTCString(),
            "Content-Length" : length
        };
        if (modified_time) {
            headers["Last-Modified"] = modified_time.toUTCString();
        }
        resp.sendHeader(200, headers);
    }

    fs.stat(path).addCallback(function (stats) {
        if (stats.isFile()) {
            stream_file(path, stats);
        } else {
            file_not_found();
        }
    }).addErrback(file_not_found);
    function stream_file(file, stats) {
        fs.open(file,'r', 0660).addCallback(function(fd) {
            var position = 0;
            log.debug("opened",path,"on fd",fd);
            function read() {
              fs.read(fd,settings.max_bytes_per_read,position, "binary")
                .addCallback(function(data,bytes_read) {
                    log.debug("read",bytes_read,"bytes of",file);
                    if(bytes_read > 0) {
                        resp.write(data, "binary");
                        position += bytes_read;
                        read(); // read more
                    } else {
                        finish(resp);
                        close(fd);
                    }
                }).addErrback(function() {
                    log.error("Error reading from",file,"position:",position,
                        ">",arguments);
                    resp.write("Error reading from " + file);
                    finish(resp);
                    close(fd);
                });
            }
            if(fd) {
                send_headers(200, stats.size, mime.mime_type(file), stats.mtime);
                read();
            } else {
                server_error(file+" couldn't be opened (invalid fd)");
                close(fd);
            }
        }).addErrback(file_not_found);
    }

    function file_not_found() {
        log.debug("404 opening",path,">",arguments);
        var body = "404: " + path + " not found.";
        send_headers(404,body.length,"text/plain");
        resp.write(body);
        finish(resp);
    }

    function server_error(message) {
        log.error("error opening ",path,":",message);
        send_headers(500, message.length, "text/plain");
        resp.write(message);
        finish(resp);
    }
}

function finish(resp) {
    clearTimeout(resp.die);
    resp.finish();
    log.debug("finished response");
}
function close(fd) {
    fs.close(fd);
    log.debug("closed fd",fd);
}
<commit_msg>Check for undefined pathname to prevent crash<commit_after>/**
 * Simple webserver with logging. By default, serves whatever files are
 * reachable from the directory where node is running.
 */
var VERSION = "0.2";
var fs = require('fs'),
    pathlib = require('path'),
    uri = require('url'),
    events = require('events'),
    mime = require('./lib/content-type'),
    log = require('./lib/log');

log.level = log.levels.DEBUG;

var settings = {
    "max_bytes_per_read": 1024 * 1024 * 5, // 5MB
    "timeout_milliseconds": 1000 * 30, //30 sec
    "hosts" : [],
    "port" : 8080,
    "default_host" : {
        "root" : "./"
    }
};

try {
    log.debug("Reading/parsing settings.json");
    var custom_settings = JSON.parse(fs.readFileSync('./settings.json'));
    process.mixin(settings, custom_settings);
} catch(e) {
    log.warn("Using default settings: cannot read settings.json.",e);
}

log.info( "Starting server on port", settings.port);
require("http").createServer(function(req,resp) {
    log.info("Request from ",req.connection.remoteAddress,"for",req.url);
    log.info(JSON.stringify(req.headers));
    resp.die = setTimeout(function() {
        finish(resp);
    }, settings.timeout_milliseconds);

    find_local_file_path(req).addListener('success', function(path) {
        try_stream(path, resp);
    });
}).listen(settings.port);

function find_local_file_path(req) {
    var url = uri.parse(req.url);
    //if the parsed url doesn't have a pathname, default to '/'
    var pathname = (url.pathname || '/');
    //disallow parent directory access
    var clean_pathname = pathname.replace(/\.\.\//g);
    var vhost = settings.hosts[req.headers.host] || settings.default_host;
    log.debug("selected vhost",vhost.root);
    var path = pathlib.join(vhost.root, clean_pathname);
    var emitter = new events.EventEmitter();
    fs.stat(path).addCallback(function (stat) {
        if (stat.isDirectory()) {
            path = pathlib.join(path, "index.html");
        }
        emitter.emit('success', path);
    }).addErrback(function () {
        emitter.emit('success', path);
    });
    return emitter;
}

function try_stream(path, resp) {
    function send_headers(httpstatus, length, content_type, modified_time) {
        var headers = {
            "Content-Type" : content_type || "application/octet-stream",
            "Server" : "Antinode/"+VERSION+" Node.js/"+process.version,
            "Date" : (new Date()).toUTCString(),
            "Content-Length" : length
        };
        if (modified_time) {
            headers["Last-Modified"] = modified_time.toUTCString();
        }
        resp.sendHeader(200, headers);
    }

    fs.stat(path).addCallback(function (stats) {
        if (stats.isFile()) {
            stream_file(path, stats);
        } else {
            file_not_found();
        }
    }).addErrback(file_not_found);
    function stream_file(file, stats) {
        fs.open(file,'r', 0660).addCallback(function(fd) {
            var position = 0;
            log.debug("opened",path,"on fd",fd);
            function read() {
              fs.read(fd,settings.max_bytes_per_read,position, "binary")
                .addCallback(function(data,bytes_read) {
                    log.debug("read",bytes_read,"bytes of",file);
                    if(bytes_read > 0) {
                        resp.write(data, "binary");
                        position += bytes_read;
                        read(); // read more
                    } else {
                        finish(resp);
                        close(fd);
                    }
                }).addErrback(function() {
                    log.error("Error reading from",file,"position:",position,
                        ">",arguments);
                    resp.write("Error reading from " + file);
                    finish(resp);
                    close(fd);
                });
            }
            if(fd) {
                send_headers(200, stats.size, mime.mime_type(file), stats.mtime);
                read();
            } else {
                server_error(file+" couldn't be opened (invalid fd)");
                close(fd);
            }
        }).addErrback(file_not_found);
    }

    function file_not_found() {
        log.debug("404 opening",path,">",arguments);
        var body = "404: " + path + " not found.";
        send_headers(404,body.length,"text/plain");
        resp.write(body);
        finish(resp);
    }

    function server_error(message) {
        log.error("error opening ",path,":",message);
        send_headers(500, message.length, "text/plain");
        resp.write(message);
        finish(resp);
    }
}

function finish(resp) {
    clearTimeout(resp.die);
    resp.finish();
    log.debug("finished response");
}
function close(fd) {
    fs.close(fd);
    log.debug("closed fd",fd);
}
<|endoftext|>


<commit_before>import Controller from '@ember/controller';
import { action, set } from '@ember/object';
import { tracked } from '@glimmer/tracking';

const defaultSize = '24';
const defaultRotate = '0';
const defaultStrokeWidth = '0';
const defaultStrokeLinecap = 'butt';
const defaultStrokeLinejoin = 'miter';
const checkIsShown = function(searchText, meta) {
  if (searchText === '') {
    return true;
  }

  if (meta.name.indexOf(searchText) !== -1) {
    return true;
  }

  for (let i = 0; i < meta.tags.length; i++) {
    if (meta.tags[i].indexOf(searchText) !== -1) {
      return true;
    }
  }

  for (let i = 0; i < meta.aliases.length; i++) {
    if (meta.aliases[i].indexOf(searchText) !== -1) {
      return true;
    }
  }

  return false;
}

export default class ApplicationController extends Controller {
  @tracked selectedIcon = 'heart';
  @tracked size = '120';
  @tracked spin = false;
  @tracked flipH = false;
  @tracked flipV = false;
  @tracked rotate = defaultRotate;
  @tracked fill = null;
  @tracked searchText = '';
  @tracked stroke = null;
  @tracked strokeWidth = defaultStrokeWidth;
  @tracked strokeLinecap = defaultStrokeLinecap;
  @tracked strokeLinecapOptions = Object.freeze(['butt', 'round', 'square']);
  @tracked strokeLinejoin = defaultStrokeLinejoin;
  @tracked strokeLinejoinOptions = Object.freeze(['miter', 'round', 'bevel']);

  get iconHbsCode() {
    let iconHbsCode = `<MdIcon @icon="${this.selectedIcon}"`;

    if (this.size !== defaultSize) {
      iconHbsCode += ` @size=${this.size}`;
    }

    if (this.spin) {
      iconHbsCode += ` @spin=${this.spin}`;
    }

    if (this.flipH) {
      iconHbsCode += ` @flipH=${this.flipH}`;
    }

    if (this.flipV) {
      iconHbsCode += ` @flipH=${this.flipV}`;
    }

    if (this.rotate !== defaultRotate) {
      iconHbsCode += ` @rotate=${this.rotate}`;
    }

    if (this.fill) {
      iconHbsCode += ` @fill="${this.fill}"`;
    }

    if (this.stroke) {
      iconHbsCode += ` @stroke="${this.stroke}"`;
    }

    if (this.strokeWidth !== defaultStrokeWidth) {
      iconHbsCode += ` @strokeWidth="${this.strokeWidth}"`;
    }

    if (this.strokeLinecap !== defaultStrokeLinecap) {
      iconHbsCode += ` @strokeLinecap="${this.strokeLinecap}"`;
    }

    if (this.strokeLinejoin !== defaultStrokeLinejoin) {
      iconHbsCode += ` @strokeLinejoin="${this.strokeLinejoin}"`;
    }

    iconHbsCode += '/>';

    return iconHbsCode;
  }

  @action
  updateStrokeLinecap(event) {
    this.strokeLinecap = event.target.value;
  }

  @action
  updateStrokeLinejoin(event) {
    this.strokeLinejoin = event.target.value;
  }

  @action
  updateSearchText(event) {
    const searchText = event.target.value.toLowerCase();

    for (let i = 0; i < this.model.length; i++) {
      const item = this.model[i];

      set(item, 'isHidden', !checkIsShown(searchText, item));
    }
  }

  @action
  updateSelectedItem(event) {
    const iconWrapper = event.target.closest('.demo-icon');

    if (iconWrapper && iconWrapper.dataset.name) {
      this.selectedIcon = iconWrapper.dataset.name;
    }
  }
};
<commit_msg>Fixed lint warning<commit_after>import Controller from '@ember/controller';
import { action, set } from '@ember/object';
import { tracked } from '@glimmer/tracking';

const defaultSize = '24';
const defaultRotate = '0';
const defaultStrokeWidth = '0';
const defaultStrokeLinecap = 'butt';
const defaultStrokeLinejoin = 'miter';
const checkIsShown = function(searchText, meta) {
  if (searchText === '') {
    return true;
  }

  if (meta.name.indexOf(searchText) !== -1) {
    return true;
  }

  for (let i = 0; i < meta.tags.length; i++) {
    if (meta.tags[i].indexOf(searchText) !== -1) {
      return true;
    }
  }

  for (let i = 0; i < meta.aliases.length; i++) {
    if (meta.aliases[i].indexOf(searchText) !== -1) {
      return true;
    }
  }

  return false;
}

export default class ApplicationController extends Controller {
  @tracked selectedIcon = 'heart';
  @tracked size = '120';
  @tracked spin = false;
  @tracked flipH = false;
  @tracked flipV = false;
  @tracked rotate = defaultRotate;
  @tracked fill = null;
  @tracked searchText = '';
  @tracked stroke = null;
  @tracked strokeWidth = defaultStrokeWidth;
  @tracked strokeLinecap = defaultStrokeLinecap;
  @tracked strokeLinecapOptions = Object.freeze(['butt', 'round', 'square']);
  @tracked strokeLinejoin = defaultStrokeLinejoin;
  @tracked strokeLinejoinOptions = Object.freeze(['miter', 'round', 'bevel']);

  get iconHbsCode() {
    let iconHbsCode = `<MdIcon @icon="${this.selectedIcon}"`;

    if (this.size !== defaultSize) {
      iconHbsCode += ` @size=${this.size}`;
    }

    if (this.spin) {
      iconHbsCode += ` @spin=${this.spin}`;
    }

    if (this.flipH) {
      iconHbsCode += ` @flipH=${this.flipH}`;
    }

    if (this.flipV) {
      iconHbsCode += ` @flipH=${this.flipV}`;
    }

    if (this.rotate !== defaultRotate) {
      iconHbsCode += ` @rotate=${this.rotate}`;
    }

    if (this.fill) {
      iconHbsCode += ` @fill="${this.fill}"`;
    }

    if (this.stroke) {
      iconHbsCode += ` @stroke="${this.stroke}"`;
    }

    if (this.strokeWidth !== defaultStrokeWidth) {
      iconHbsCode += ` @strokeWidth="${this.strokeWidth}"`;
    }

    if (this.strokeLinecap !== defaultStrokeLinecap) {
      iconHbsCode += ` @strokeLinecap="${this.strokeLinecap}"`;
    }

    if (this.strokeLinejoin !== defaultStrokeLinejoin) {
      iconHbsCode += ` @strokeLinejoin="${this.strokeLinejoin}"`;
    }

    iconHbsCode += '/>';

    return iconHbsCode;
  }

  @action
  updateStrokeLinecap(event) {
    this.strokeLinecap = event.target.value;
  }

  @action
  updateStrokeLinejoin(event) {
    this.strokeLinejoin = event.target.value;
  }

  @action
  updateSearchText(event) {
    const searchText = event.target.value.toLowerCase();

    for (let i = 0; i < this.model.length; i++) {
      const item = this.model[i];

      set(item, 'isHidden', !checkIsShown(searchText, item));
    }
  }

  @action
  updateSelectedItem(event) {
    const iconWrapper = event.target.closest('.demo-icon');

    if (iconWrapper && iconWrapper.dataset.name) {
      this.selectedIcon = iconWrapper.dataset.name;
    }
  }
}
<|endoftext|>


<commit_before>const express = require('express');
const app = express();

app.get('/', function (req, res) {
   console.log("Got a GET request for the homepage");
   res.send('Hello GET');
})

var server = app.listen(8081, () => {
    const port = server.address().port;
    console.log(server.address());
    console.log("Server is listening at http://localhost:%s", port);
});<commit_msg>Set up port better<commit_after>const express = require('express');
const app = express();

app.get('/', function (req, res) {
   console.log("Got a GET request for the homepage");
   res.send('Hello GET');
})

var server = app.listen(process.env.PORT || 8081, () => {
    const port = server.address().port;
    console.log(server.address());
    console.log("Server is listening at http://localhost:%s", port);
});<|endoftext|>


<commit_before>/************************************
* scanner.js
* MIT License (c) 2014 David Leonard
* drksephy.github.io
*************************************/

'use strict';

/**********************
* Scanner constructor *
**********************/

function Scanner(){
    this.tokens     = [];
    this.curr_row   = 1 ;
    this.curr_col   = 1 ;
    this.curr_token = '';
    this.curr_val   = '';
    this.comments   = false;

}

// Adds token to list
Scanner.prototype.addToken = function(){

    if(this.curr_token){
        var token = {
            'token': this.curr_token,
            'value': this.curr_val,
            'row'  : this.curr_row,
            'col'  : this.curr_col
        }
        this.tokens.push(token);

        // TODO: Use bootstrap to render these tables
        var table = document.getElementById('list');
        var node = document.createElement('tr');
        for(var prop in token){
            table.appendChild(node);
            var entry = node.appendChild(document.createElement('td'));
            entry.appendChild(document.createTextNode(token[prop]));
        }
    }

    return;
}

// Resets values of token
Scanner.prototype.reset = function(){
    this.curr_val = '';
    this.curr_token = '';
    return;
}

// Returns ascii value of character
Scanner.prototype.toAscii = function(character){
    return character.charCodeAt();
}

// Returns lowercase string
Scanner.prototype.toLower = function(character){
    return character.toLowerCase();
}

// Returns uppercase string
Scanner.prototype.toUpper = function(character){
    return character.toUpperCase();
}

// Handles comments
Scanner.prototype.setComment = function(character){
    // TODO
    return;
}

// Handles EOF
Scanner.prototype.setEOF = function(character){
    // TODO
    return;
}

// Handles numerical characters
Scanner.prototype.setDigit = function(character){
    if(this.curr_token == 'TK_REAL' || this.curr_token == 'TK_INT'){
        this.curr_val += character;
    }
    else{
        this.setToken('TK_INT', character);
    }
    return;
}

// Handles strings
Scanner.prototype.setString = function(character){
    // TODO
    return;
}

// Handles newline characters
Scanner.prototype.setLine = function(ascii_value){
    this.addToken();
    this.reset();
    if(ascii_value == '10'){
        this.curr_row += 1;
        this.curr_col = 0;
    }
    return;
}

// Handles whitespace character
Scanner.prototype.setSpace = function(ascii_value){
    this.addToken();
    this.reset();
    return;
}

// Adds the last token into the list, sets new token
Scanner.prototype.setToken = function(token_type, character){
    this.addToken();
    this.reset();
    this.curr_token = token_type;
    this.curr_val += character;
    return;
}

// Handles characters
Scanner.prototype.setCharacter = function(character){
    if(this.curr_token != 'TK_IDENTIFIER'){
        this.setToken('TK_IDENTIFIER', character);
    }
    else{
        this.curr_val += character;
        if(this.curr_val in keywords){
            this.curr_token = keywords[this.curr_val];
        }
        else if(this.curr_val in operators){
            this.curr_token = operators[this.curr_val];
        }
    }
    return;
}

// Handles operators
Scanner.prototype.setOperator = function(character){
    if(character == '.' && this.curr_token == 'TK_INT'){
        console.log("Hello");
        this.curr_token = 'TK_REAL';
        this.curr_val += character;
    }

    else if(this.curr_val + character in operators){
        this.curr_val += character;
        // Handle all two-symbol operators
        if(this.curr_val in operators){
            this.curr_token = operators[this.curr_val];
        }
    }
    else{
        this.setToken(operators[character], character);
    }
    return;
}

// Main scanner function
function scan(){

    // Get input text from textarea
    var program = document.getElementById("program").value;
    document.getElementById("demo").innerHTML = program;

    // Initalize scanner
    var scanner = new Scanner();

    // Begin scanning input text
    for(var i = 0; i < program.length; i++){

        // Handle case when we close the string
        if(this.curr_token == 'TK_SINGLE_QUOTE' || this.curr_token == 'TK_DOUBLE_QUOTE'){
            scanner.setString(character);
        }

        var character = scanner.toUpper(program[i]);
        var ascii = scanner.toAscii(character);

        // Handle case for newline character
        if(ascii == '10'){
            scanner.setLine(ascii);
        }

        // Handle case for space
        else if(ascii == '32'){
            scanner.setSpace(ascii);
        }

        // Handle case for valid letters
        else if( (ascii >= 97 && ascii <= 122) || (ascii >= 65 && ascii <= 90) ){
            scanner.setCharacter(character);
        }

        // Handle case for operators
        else if(character in operators){
            scanner.setOperator(character);
        }

        // Handle case for numbers
        else if(ascii >= 48 && ascii <= 57){
            scanner.setDigit(character);
        }

        // Handle case for strings composed with single quote
        else if(ascii == 39){
            scanner.setToken('TK_SINGLE_QUOTE', character);
        }

        // Handle case for strings composed with double quotes
        else if(ascii == 34){
            scanner.setToken('TK_DOUBLE_QUOTE', character);
        }

        scanner.curr_col += 1;

    }
    console.log(scanner.tokens);

}

var keywords = {
    'begin'     : 'TK_BEGIN',
    'break'     : 'TK_BREAK',
    'const'     : 'TK_CONST',
    'do'        : 'TK_DO',
    'downto'    : 'TK_DOWNTO',
    'else'      : 'TK_ELSE',
    'end'       : 'TK_END',
    'end.'      : 'TK_END_CODE',
    'for'       : 'TK_FOR',
    'function'  : 'TK_FUNCTION',
    'identifier': 'TK_IDENTIFIER',
    'if'        : 'TK_IF',
    'label'     : 'TK_LABEL',
    'program'   : 'TK_PROGRAM',
    'repeat'    : 'TK_REPEAT',
    'string'    : 'TK_STRING',
    'then'      : 'TK_THEN',
    'to'        : 'TK_TO',
    'type'      : 'TK_TYPE',
    'var'       : 'TK_VAR',
    'until'     : 'TK_UNTIL',
    'while'     : 'TK_WHILE',
    'integer'   : 'TK_ID_INTEGER',
    'real'      : 'TK_ID_REAL',
    'char'      : 'TK_ID_CHAR',
    'boolean'   : 'TK_ID_BOOLEAN',
    'of'        : 'TK_OF'
}

var operators = {
    '+'         : 'TK_PLUS',
    '-'         : 'TK_MINUS',
    '*'         : 'TK_MULT',
    '/'         : 'TK_DIV_FLOAT',
    'div'       : 'TK_DIV',
    'mod'       : 'TK_MOD',
    ':'         : 'TK_COLON',
    '='         : 'TK_EQUALS',
    ':='        : 'TK_ASSIGNMENT',
    '>'         : 'TK_GREATER',
    '<'         : 'TK_LESS',
    '.'         : 'TK_DOT',
    '>='        : 'TK_GREATER_EQUALS',
    '<='        : 'TK_LESS_EQUALS',
    '!'         : 'TK_EXCLAMATION',
    '!='        : 'TK_NOT_EQUALS',
    'and'       : 'TK_AND',
    'xor'       : 'TK_XOR',
    'or'        : 'TK_OR',
    'not'       : 'TK_NOT',
    ';'         : 'TK_SEMICOLON',
    '('         : 'TK_OPEN_PARENTHESIS',
    ')'         : 'TK_CLOSE_PARENTHESIS',
    '\''        : 'TK_SINGLE_QUOTE',
    '"'         : 'TK_DOUBLE_QUOTE',
    '(*'        : 'TK_BEGIN_COMMENT',
    '*)'        : 'TK_END_COMMENT',
    ','         : 'TK_COMMA',
    '~'         : 'TK_RANGE',
    'array'     : 'TK_ARRAY',
    '['         : 'TK_OPEN_BRACKET',
    ']'         : 'TK_CLOSE_BRACKET'
}

var SYSTEM = {
    'writeln'   : 'TK_WRITELN',
    'abs'       : 'TK_ABS'
}<commit_msg>scanner.js: Implemented setString method<commit_after>/************************************
* scanner.js
* MIT License (c) 2014 David Leonard
* drksephy.github.io
*************************************/

'use strict';

/**********************
* Scanner constructor *
**********************/

function Scanner(){
    this.tokens     = [];
    this.curr_row   = 1 ;
    this.curr_col   = 1 ;
    this.curr_token = '';
    this.curr_val   = '';
    this.comments   = false;

}

// Adds token to list
Scanner.prototype.addToken = function(){

    if(this.curr_token){
        var token = {
            'token': this.curr_token,
            'value': this.curr_val,
            'row'  : this.curr_row,
            'col'  : this.curr_col
        }
        this.tokens.push(token);

        // TODO: Use bootstrap to render these tables
        var table = document.getElementById('list');
        var node = document.createElement('tr');
        for(var prop in token){
            table.appendChild(node);
            var entry = node.appendChild(document.createElement('td'));
            entry.appendChild(document.createTextNode(token[prop]));
        }
    }

    return;
}

// Resets values of token
Scanner.prototype.reset = function(){
    this.curr_val = '';
    this.curr_token = '';
    return;
}

// Returns ascii value of character
Scanner.prototype.toAscii = function(character){
    return character.charCodeAt();
}

// Returns lowercase string
Scanner.prototype.toLower = function(character){
    return character.toLowerCase();
}

// Returns uppercase string
Scanner.prototype.toUpper = function(character){
    return character.toUpperCase();
}

// Handles comments
Scanner.prototype.setComment = function(character){
    // TODO
    return;
}

// Handles EOF
Scanner.prototype.setEOF = function(character){
    // TODO
    return;
}

// Handles numerical characters
Scanner.prototype.setDigit = function(character){
    if(this.curr_token == 'TK_REAL' || this.curr_token == 'TK_INT'){
        this.curr_val += character;
    }
    else{
        this.setToken('TK_INT', character);
    }
    return;
}

// Handles strings
Scanner.prototype.setString = function(character){
    this.curr_val += character;
    if((this.curr_token == 'TK_SINGLE_QUOTE' && character == "'") ||
       (this.curr_token == 'TK_DOUBLE_QUOTE' && character == '"')){
        this.buildString();
    }
    return;
}

// Handles newline characters
Scanner.prototype.setLine = function(ascii_value){
    this.addToken();
    this.reset();
    if(ascii_value == '10'){
        this.curr_row += 1;
        this.curr_col = 0;
    }
    return;
}

// Handles whitespace character
Scanner.prototype.setSpace = function(ascii_value){
    this.addToken();
    this.reset();
    return;
}

// Adds the last token into the list, sets new token
Scanner.prototype.setToken = function(token_type, character){
    this.addToken();
    this.reset();
    this.curr_token = token_type;
    this.curr_val += character;
    return;
}

// Handles characters
Scanner.prototype.setCharacter = function(character){
    if(this.curr_token != 'TK_IDENTIFIER'){
        this.setToken('TK_IDENTIFIER', character);
    }
    else{
        this.curr_val += character;
        if(this.curr_val in keywords){
            this.curr_token = keywords[this.curr_val];
        }
        else if(this.curr_val in operators){
            this.curr_token = operators[this.curr_val];
        }
    }
    return;
}

// Handles operators
Scanner.prototype.setOperator = function(character){
    if(character == '.' && this.curr_token == 'TK_INT'){
        console.log("Hello");
        this.curr_token = 'TK_REAL';
        this.curr_val += character;
    }

    else if(this.curr_val + character in operators){
        this.curr_val += character;
        // Handle all two-symbol operators
        if(this.curr_val in operators){
            this.curr_token = operators[this.curr_val];
        }
    }
    else{
        this.setToken(operators[character], character);
    }
    return;
}

// Main scanner function
function scan(){

    // Get input text from textarea
    var program = document.getElementById("program").value;
    document.getElementById("demo").innerHTML = program;

    // Initalize scanner
    var scanner = new Scanner();

    // Begin scanning input text
    for(var i = 0; i < program.length; i++){

        // Handle case when we close the string
        if(this.curr_token == 'TK_SINGLE_QUOTE' || this.curr_token == 'TK_DOUBLE_QUOTE'){
            scanner.setString(character);
        }

        var character = scanner.toUpper(program[i]);
        var ascii = scanner.toAscii(character);

        // Handle case for newline character
        if(ascii == '10'){
            scanner.setLine(ascii);
        }

        // Handle case for space
        else if(ascii == '32'){
            scanner.setSpace(ascii);
        }

        // Handle case for valid letters
        else if( (ascii >= 97 && ascii <= 122) || (ascii >= 65 && ascii <= 90) ){
            scanner.setCharacter(character);
        }

        // Handle case for operators
        else if(character in operators){
            scanner.setOperator(character);
        }

        // Handle case for numbers
        else if(ascii >= 48 && ascii <= 57){
            scanner.setDigit(character);
        }

        // Handle case for strings composed with single quote
        else if(ascii == 39){
            scanner.setToken('TK_SINGLE_QUOTE', character);
        }

        // Handle case for strings composed with double quotes
        else if(ascii == 34){
            scanner.setToken('TK_DOUBLE_QUOTE', character);
        }

        scanner.curr_col += 1;

    }
    console.log(scanner.tokens);

}

var keywords = {
    'begin'     : 'TK_BEGIN',
    'break'     : 'TK_BREAK',
    'const'     : 'TK_CONST',
    'do'        : 'TK_DO',
    'downto'    : 'TK_DOWNTO',
    'else'      : 'TK_ELSE',
    'end'       : 'TK_END',
    'end.'      : 'TK_END_CODE',
    'for'       : 'TK_FOR',
    'function'  : 'TK_FUNCTION',
    'identifier': 'TK_IDENTIFIER',
    'if'        : 'TK_IF',
    'label'     : 'TK_LABEL',
    'program'   : 'TK_PROGRAM',
    'repeat'    : 'TK_REPEAT',
    'string'    : 'TK_STRING',
    'then'      : 'TK_THEN',
    'to'        : 'TK_TO',
    'type'      : 'TK_TYPE',
    'var'       : 'TK_VAR',
    'until'     : 'TK_UNTIL',
    'while'     : 'TK_WHILE',
    'integer'   : 'TK_ID_INTEGER',
    'real'      : 'TK_ID_REAL',
    'char'      : 'TK_ID_CHAR',
    'boolean'   : 'TK_ID_BOOLEAN',
    'of'        : 'TK_OF'
}

var operators = {
    '+'         : 'TK_PLUS',
    '-'         : 'TK_MINUS',
    '*'         : 'TK_MULT',
    '/'         : 'TK_DIV_FLOAT',
    'div'       : 'TK_DIV',
    'mod'       : 'TK_MOD',
    ':'         : 'TK_COLON',
    '='         : 'TK_EQUALS',
    ':='        : 'TK_ASSIGNMENT',
    '>'         : 'TK_GREATER',
    '<'         : 'TK_LESS',
    '.'         : 'TK_DOT',
    '>='        : 'TK_GREATER_EQUALS',
    '<='        : 'TK_LESS_EQUALS',
    '!'         : 'TK_EXCLAMATION',
    '!='        : 'TK_NOT_EQUALS',
    'and'       : 'TK_AND',
    'xor'       : 'TK_XOR',
    'or'        : 'TK_OR',
    'not'       : 'TK_NOT',
    ';'         : 'TK_SEMICOLON',
    '('         : 'TK_OPEN_PARENTHESIS',
    ')'         : 'TK_CLOSE_PARENTHESIS',
    '\''        : 'TK_SINGLE_QUOTE',
    '"'         : 'TK_DOUBLE_QUOTE',
    '(*'        : 'TK_BEGIN_COMMENT',
    '*)'        : 'TK_END_COMMENT',
    ','         : 'TK_COMMA',
    '~'         : 'TK_RANGE',
    'array'     : 'TK_ARRAY',
    '['         : 'TK_OPEN_BRACKET',
    ']'         : 'TK_CLOSE_BRACKET'
}

var SYSTEM = {
    'writeln'   : 'TK_WRITELN',
    'abs'       : 'TK_ABS'
}<|endoftext|>


<commit_before>var Dashboard = React.createClass({
    mixins: [React.addons.LinkedStateMixin],
    getInitialState: function() {
        return {page: "dashboard", query: "", error: null, selected: null, loading: false, willUpdate: true,
                animateOnUpdate: false, sortRowsLabel: "Time", sortRowsAscending: false,
                dashPermalink: null,
                thresholds: {
                    "warning": 30,
                    "danger": 600
                }}
    },
    /*
     * Here we use React's immutable updates to give it some hints on how to update the full DOM
     */
    updateFromRepublish: function(newdata) {
        var self = this;
        _.each(_.values(newdata), function(obj) {
            if (obj == null || obj.Readings == null || obj.uuid == null ) { return; }
            if (self.state.data == null) {return; }
            if (self.state.data[obj.uuid] == null) { return; }
            self.setState(React.addons.update(self.state, {
                data: makeProp(obj.uuid, {
                    latestValue: {$set : obj.Readings[obj.Readings.length-1][1] },
                    latestTime:  {$set : moment.unix(obj.Readings[obj.Readings.length-1][0]) }
                })
            }));
        });
    },
    componentWillMount: function() {
        var permalink = window.location.hash;
        var self= this;
        if (!permalink) { return; }
        permalink = permalink.slice(1,permalink.length);
        console.log("permalink?", permalink);
        get_dash_permalink(permalink,
            function(res) {
                console.log("got permalink!", res);
                //self.refs.queryInput.setValue(res.query);
                self.setState({query: res});
                self.submitQuery();
            },
            function (xhr) {
                self.setState({error: xhr.responseText});
                console.error(xhr.responseText);
            }
        );
        this.submitQuery({});
    },
    handleQueryChange: function(c) {
        this.setChange({query: c});
    },
    /*
     * When a query is submitted we submit it to the archiver, and render any error that we receive.
     * If there is no error, then we set our internal state to a mapping of UUID -> timeseries object.
     * Each timeseries object contains the top level keys Metadata, Properties, Path, uuid and Readings
     */
    submitQuery: function(e) {
        // fetch metadata query
        var query = this.state.query;
        this.setState({error: null, loading: true});
        var self = this;
        run_query(query,
            function(data) {
                var newstate = {};
                _.each(data, function(obj) {
                    newstate[obj.uuid] = obj;
                });
                self.setState({data: newstate});
            },
            function(xhr,status,err) {
                self.setState({error: xhr.responseText});
                console.error(xhr.responseText);
            }
        );

        // fetch most recent data point query
        run_dataquery(query,
            function(data) {
                var newstate = self.state.data;
                if (newstate == null) { return; }
                _.each(data, function(obj) {
                    if (obj == null || obj.Readings == null || obj.uuid == null ) { return; }
                    if (newstate[obj.uuid] == null) { return; }
                    newstate[obj.uuid].latestValue = obj.Readings[obj.Readings.length-1][1];
                    newstate[obj.uuid].latestTime = moment.unix(obj.Readings[obj.Readings.length-1][0]);
                });
                self.setState({data: newstate, loading: false});
            },
            function(xhr,status,err) {
                self.setState({error: xhr.responseText, loading:false});
                console.error(xhr.responseText);
            }
        );

        // set up realtime data updates
        console.log("subscribe", query);
        var socket = io.connect();
        socket.emit('new subscribe', query);
        socket.on(query, function(data) {
            if (self.isMounted()) {
                self.updateFromRepublish(data);
            }
        });
        this.setState({socket: socket});
    },
    showDetail: function(point) {
        this.setState({selected: point});
    },
    changeThreshold: function(e) {
        e.preventDefault();
        var thresholds = this.state.thresholds;
        thresholds.warning = parseInt(this.refs.warningThreshold.getValue());
        thresholds.danger = parseInt(this.refs.dangerThreshold.getValue());
        console.log("new thresholds", thresholds);
        this.setState({thresholds: thresholds});
    },
    savePermalink: function(e) {
        e.preventDefault();
        var self = this;
        save_dash_permalink(this.state.query,
            function (permalink) {
                self.setState({dashPermalink: format_permalink(permalink)});
            },
            function (xhr) {
                self.setState({error: xhr.responseText});
                console.error(xhr.responseText);
            }
        );
    },
    sortRows: function(label) {
        console.log("sort by", label);
        this.setState({sortRowsLabel: label});
        if (this.state.sortRowsAscending == null) {
            this.setState({sortRowsAscending: false});
        } else if (!this.state.sortRowsAscending) {
            this.setState({sortRowsAscending: true});
        } else {
            this.setState({sortRowsAscending: null});
        }
    },
    render: function() {
        var self = this;

        // render error if one occurs during any query
        var error = ();
        if (this.state.error != null) {
            error = (
                <Panel header="Query Error" bsStyle="danger">
                    {this.state.error}
                </Panel>
            );
        }

        // set up the point detail pane on the right
        var detail = ();
        if (this.state.selected != null) {
           detail =  <PointDetail {...this.state.selected} />
        }

        // create the rows resulting from the points
        // thresholds is built off of the global threshold/color values
        // Have a new selement specifically for this list and optimize that shouldComponentUpdate

        var savePermalinkButton = (
            <Button bsStyle="info" onClick={this.savePermalink}>Get Permalink!</Button>
        );

       //

       //

       //        <Input type='checkbox' label='Flash on Update' ref="animateOnUpdate" onChange={this.toggleAnimateOnUpdate} />
       //

       //

        return (

                    <form onSubmit={this.submitQueryForm}>

                            <Input
                                type='text'
                                valueLink={this.linkState("query")}
                                hasFeedback
                            />

                            <Button type="submit" onClick={this.submitQuery} disabled={this.state.loading} bsStyle='success'>
                                {this.state.loading ? "Loading..." : "Query"}
                            </Button>

                    </form>

                {error}

                    <form onSubmit={this.changeThreshold}>

                            <Button type="submit" >Change Thresholds</Button>

                            <Input type='text' maxLength="7" size="4" addonBefore="Warning" bsStyle="warning" defaultValue={this.state.thresholds.warning} ref="warningThreshold" />

                            <Input type='text' maxLength="7" size="4" addonBefore="Danger" bsStyle="error" defaultValue={this.state.thresholds.danger} ref="dangerThreshold" />

                            <Input type='text' maxLength="6" size="6" buttonBefore={savePermalinkButton} value={this.state.dashPermalink} />

                    </form>

                        <ListGroup>
                            <ListGroupItem>

                                    Path

                                    Latest Value

                                    Latest Time

                            </ListGroupItem>
                            <PointList data={this.state.data}
                                       showDetail={this.showDetail.bind(this)}
                                       sortRowsLabel={this.state.sortRowsLabel}
                                       sortRowsAscending={this.state.sortRowsAscending}
                                       thresholds={this.state.thresholds} />
                        </ListGroup>

                        {detail}

        );
    }
});

var PointList = React.createClass({
    getInitialState: function() {
        return {willUpdate: true};
    },
    /*
     * For large dashboards, maintaining a websocket/socket.io connection for each row is expensive. Instead,
     * we use the shouldComponentUpdate method to do an intelligent update. If a query has not yet been
     * submitted, we return the default value of true. If a query *has* been submitted, then at the bottom of
     * the submitQuery method, a timer is executed that sets the value of shouldComponentUpdate to true
     * every second, so the entire component will only be updated then.
     */
    componentDidMount: function() {
        console.log("POINT LIST", this.props);
        var self = this;
        setInterval(function () {
            self.setState({willUpdate: true});
        }, 1000);
    },
    shouldComponentUpdate: function(nextState, nextProps) {
        //if (this.state.query == null) { return true; }
        if (this.state.willUpdate) {
            this.setState({willUpdate: false});
            return true;
        }
        return false;
    },
    render: function() {
        var self = this;
        var rows = _.map(this.props.data, function(point) {
            return (
                <PointRow key={point.uuid} thresholds={self.props.thresholds} onClick={this.props.showDetail.bind(point) } {...point} />
            )
        }, this);
        if (this.props.sortRowsLabel != null) {
            rows = _.sortBy(rows, function(r) {
                switch (self.props.sortRowsLabel) {
                case "Path":
                    return r.props.Path == null ? -Number.MIN_VALUE : r.props.Path
                case "Value":
                    return r.props.latestValue == null ? -Number.MIN_VALUE : r.props.latestValue
                case "Time":
                    return r.props.latestTime == null ? -Number.MIN_VALUE : r.props.latestTime
                }
            });
            if (!this.props.sortRowsAscending) {
                rows.reverse();
            }
        }

        return (

                {rows}

        );
    }
});

var PointRow = React.createClass({
    getInitialState: function() {
        return {loading: false, plotDuration: 'hour', color: "danger"} // default 1 hour
    },
    goToPlot: function() {
        console.log("get permalink for", this.props.uuid);
        var self = this;
        this.setState({loading: true});
        console.log("duration", durationLookup[this.state.plotDuration]);
        get_permalink(this.props.uuid, parseInt(durationLookup[this.state.plotDuration]),
            function(url) {
                console.log(url);
                self.setState({loading: false});
                window.open(url, '_blank');
            },
            function(xhr) {
            }
        );
    },
    changeDuration: function(e) {
        this.setState({plotDuration: this.refs.plotDuration.getValue()});
    },
    componentDidMount: function() {
        if (this.props.latestTime != null) {
            var difference = moment().diff(this.props.latestTime, 'seconds');

            if (difference < this.props.thresholds.warning) {
                this.setState({color: "success"});
            } else if (difference < this.props.thresholds.danger) {
                this.setState({color: "warning"});
            } else {
                this.setState({color: "danger"});
            }
        }
    },
    render: function() {
        //console.log("props", this.props);
        return (
        <ListGroupItem href="#" onClick={this.props.onClick} bsStyle={this.state.color}>

                        {this.props.Path}

                        {this.props.latestValue}

                        {this.props.latestTime == null ? null : this.props.latestTime.format("dddd, MMMM Do YYYY, h:mm:ss a")}

                        {this.props.latestTime == null ? null : this.props.latestTime.from(moment()) }

                            <Button onClick={this.goToPlot}
                                    bsStyle="info"
                                    disabled={this.state.loading}>
                                {this.state.loading ? "Fetching" : "Plot last"}
                            </Button>

                            <Input type='select' onChange={this.changeDuration} ref="plotDuration" value={this.state.plotDuration}>
                              <option value='sec'>sec</option>
                              <option value='min'>min</option>
                              <option value='hour'>hour</option>
                              <option value='day'>day</option>
                            </Input>

        </ListGroupItem>
        );
    }
});

var PointDetail = React.createClass({
    render: function() {
        return (

Detail

{JSON.stringify(this.props, null, 2) }

        );
    }
});
<commit_msg>fix point binding for display metadata<commit_after>var Dashboard = React.createClass({
    mixins: [React.addons.LinkedStateMixin],
    getInitialState: function() {
        return {page: "dashboard", query: "", error: null, selected: null, loading: false, willUpdate: true,
                animateOnUpdate: false, sortRowsLabel: "Time", sortRowsAscending: false,
                dashPermalink: null,
                thresholds: {
                    "warning": 30,
                    "danger": 600
                }}
    },
    /*
     * Here we use React's immutable updates to give it some hints on how to update the full DOM
     */
    updateFromRepublish: function(newdata) {
        var self = this;
        _.each(_.values(newdata), function(obj) {
            if (obj == null || obj.Readings == null || obj.uuid == null ) { return; }
            if (self.state.data == null) {return; }
            if (self.state.data[obj.uuid] == null) { return; }
            self.setState(React.addons.update(self.state, {
                data: makeProp(obj.uuid, {
                    latestValue: {$set : obj.Readings[obj.Readings.length-1][1] },
                    latestTime:  {$set : moment.unix(obj.Readings[obj.Readings.length-1][0]) }
                })
            }));
        });
    },
    componentWillMount: function() {
        var permalink = window.location.hash;
        var self= this;
        if (!permalink) { return; }
        permalink = permalink.slice(1,permalink.length);
        console.log("permalink?", permalink);
        get_dash_permalink(permalink,
            function(res) {
                console.log("got permalink!", res);
                //self.refs.queryInput.setValue(res.query);
                self.setState({query: res});
                self.submitQuery();
            },
            function (xhr) {
                self.setState({error: xhr.responseText});
                console.error(xhr.responseText);
            }
        );
        this.submitQuery({});
    },
    handleQueryChange: function(c) {
        this.setChange({query: c});
    },
    /*
     * When a query is submitted we submit it to the archiver, and render any error that we receive.
     * If there is no error, then we set our internal state to a mapping of UUID -> timeseries object.
     * Each timeseries object contains the top level keys Metadata, Properties, Path, uuid and Readings
     */
    submitQuery: function(e) {
        // fetch metadata query
        var query = this.state.query;
        this.setState({error: null, loading: true});
        var self = this;
        run_query(query,
            function(data) {
                var newstate = {};
                _.each(data, function(obj) {
                    newstate[obj.uuid] = obj;
                });
                self.setState({data: newstate});
            },
            function(xhr,status,err) {
                self.setState({error: xhr.responseText});
                console.error(xhr.responseText);
            }
        );

        // fetch most recent data point query
        run_dataquery(query,
            function(data) {
                var newstate = self.state.data;
                if (newstate == null) { return; }
                _.each(data, function(obj) {
                    if (obj == null || obj.Readings == null || obj.uuid == null ) { return; }
                    if (newstate[obj.uuid] == null) { return; }
                    newstate[obj.uuid].latestValue = obj.Readings[obj.Readings.length-1][1];
                    newstate[obj.uuid].latestTime = moment.unix(obj.Readings[obj.Readings.length-1][0]);
                });
                self.setState({data: newstate, loading: false});
            },
            function(xhr,status,err) {
                self.setState({error: xhr.responseText, loading:false});
                console.error(xhr.responseText);
            }
        );

        // set up realtime data updates
        console.log("subscribe", query);
        var socket = io.connect();
        socket.emit('new subscribe', query);
        socket.on(query, function(data) {
            if (self.isMounted()) {
                self.updateFromRepublish(data);
            }
        });
        this.setState({socket: socket});
    },
    showDetail: function(point) {
        this.setState({selected: point});
    },
    changeThreshold: function(e) {
        e.preventDefault();
        var thresholds = this.state.thresholds;
        thresholds.warning = parseInt(this.refs.warningThreshold.getValue());
        thresholds.danger = parseInt(this.refs.dangerThreshold.getValue());
        console.log("new thresholds", thresholds);
        this.setState({thresholds: thresholds});
    },
    savePermalink: function(e) {
        e.preventDefault();
        var self = this;
        save_dash_permalink(this.state.query,
            function (permalink) {
                self.setState({dashPermalink: format_permalink(permalink)});
            },
            function (xhr) {
                self.setState({error: xhr.responseText});
                console.error(xhr.responseText);
            }
        );
    },
    sortRows: function(label) {
        console.log("sort by", label);
        this.setState({sortRowsLabel: label});
        if (this.state.sortRowsAscending == null) {
            this.setState({sortRowsAscending: false});
        } else if (!this.state.sortRowsAscending) {
            this.setState({sortRowsAscending: true});
        } else {
            this.setState({sortRowsAscending: null});
        }
    },
    render: function() {
        var self = this;

        // render error if one occurs during any query
        var error = ();
        if (this.state.error != null) {
            error = (
                <Panel header="Query Error" bsStyle="danger">
                    {this.state.error}
                </Panel>
            );
        }

        // set up the point detail pane on the right
        var detail = ();
        if (this.state.selected != null) {
           detail =  <PointDetail {...this.state.selected} />
        }

        // create the rows resulting from the points
        // thresholds is built off of the global threshold/color values
        // Have a new selement specifically for this list and optimize that shouldComponentUpdate

        var savePermalinkButton = (
            <Button bsStyle="info" onClick={this.savePermalink}>Get Permalink!</Button>
        );

       //

       //

       //        <Input type='checkbox' label='Flash on Update' ref="animateOnUpdate" onChange={this.toggleAnimateOnUpdate} />
       //

       //

        return (

                    <form onSubmit={this.submitQueryForm}>

                            <Input
                                type='text'
                                valueLink={this.linkState("query")}
                                hasFeedback
                            />

                            <Button type="submit" onClick={this.submitQuery} disabled={this.state.loading} bsStyle='success'>
                                {this.state.loading ? "Loading..." : "Query"}
                            </Button>

                    </form>

                {error}

                    <form onSubmit={this.changeThreshold}>

                            <Button type="submit" >Change Thresholds</Button>

                            <Input type='text' maxLength="7" size="4" addonBefore="Warning" bsStyle="warning" defaultValue={this.state.thresholds.warning} ref="warningThreshold" />

                            <Input type='text' maxLength="7" size="4" addonBefore="Danger" bsStyle="error" defaultValue={this.state.thresholds.danger} ref="dangerThreshold" />

                            <Input type='text' maxLength="6" size="6" buttonBefore={savePermalinkButton} value={this.state.dashPermalink} />

                    </form>

                        <ListGroup>
                            <ListGroupItem>

                                    Path

                                    Latest Value

                                    Latest Time

                            </ListGroupItem>
                            <PointList data={this.state.data}
                                       showDetail={this.showDetail}
                                       sortRowsLabel={this.state.sortRowsLabel}
                                       sortRowsAscending={this.state.sortRowsAscending}
                                       thresholds={this.state.thresholds} />
                        </ListGroup>

                        {detail}

        );
    }
});

var PointList = React.createClass({
    getInitialState: function() {
        return {willUpdate: true};
    },
    /*
     * For large dashboards, maintaining a websocket/socket.io connection for each row is expensive. Instead,
     * we use the shouldComponentUpdate method to do an intelligent update. If a query has not yet been
     * submitted, we return the default value of true. If a query *has* been submitted, then at the bottom of
     * the submitQuery method, a timer is executed that sets the value of shouldComponentUpdate to true
     * every second, so the entire component will only be updated then.
     */
    componentDidMount: function() {
        console.log("POINT LIST", this.props);
        var self = this;
        setInterval(function () {
            self.setState({willUpdate: true});
        }, 1000);
    },
    shouldComponentUpdate: function(nextState, nextProps) {
        //if (this.state.query == null) { return true; }
        if (this.state.willUpdate) {
            this.setState({willUpdate: false});
            return true;
        }
        return false;
    },
    showDetail: function(point) {
        this.props.showDetail(point);
    },
    render: function() {
        var self = this;
        var rows = _.map(this.props.data, function(point) {
            return (
                <PointRow key={point.uuid} thresholds={self.props.thresholds} onClick={this.showDetail.bind(this, point) } {...point} />
            )
        }, this);
        if (this.props.sortRowsLabel != null) {
            rows = _.sortBy(rows, function(r) {
                switch (self.props.sortRowsLabel) {
                case "Path":
                    return r.props.Path == null ? -Number.MIN_VALUE : r.props.Path
                case "Value":
                    return r.props.latestValue == null ? -Number.MIN_VALUE : r.props.latestValue
                case "Time":
                    return r.props.latestTime == null ? -Number.MIN_VALUE : r.props.latestTime
                }
            });
            if (!this.props.sortRowsAscending) {
                rows.reverse();
            }
        }

        return (

                {rows}

        );
    }
});

var PointRow = React.createClass({
    getInitialState: function() {
        return {loading: false, plotDuration: 'hour', color: "danger"} // default 1 hour
    },
    goToPlot: function() {
        console.log("get permalink for", this.props.uuid);
        var self = this;
        this.setState({loading: true});
        console.log("duration", durationLookup[this.state.plotDuration]);
        get_permalink(this.props.uuid, parseInt(durationLookup[this.state.plotDuration]),
            function(url) {
                console.log(url);
                self.setState({loading: false});
                window.open(url, '_blank');
            },
            function(xhr) {
            }
        );
    },
    changeDuration: function(e) {
        this.setState({plotDuration: this.refs.plotDuration.getValue()});
    },
    componentDidMount: function() {
        if (this.props.latestTime != null) {
            var difference = moment().diff(this.props.latestTime, 'seconds');

            if (difference < this.props.thresholds.warning) {
                this.setState({color: "success"});
            } else if (difference < this.props.thresholds.danger) {
                this.setState({color: "warning"});
            } else {
                this.setState({color: "danger"});
            }
        }
    },
    render: function() {
        //console.log("props", this.props);
        return (
        <ListGroupItem href="#" onClick={this.props.onClick} bsStyle={this.state.color}>

                        {this.props.Path}

                        {this.props.latestValue}

                        {this.props.latestTime == null ? null : this.props.latestTime.format("dddd, MMMM Do YYYY, h:mm:ss a")}

                        {this.props.latestTime == null ? null : this.props.latestTime.from(moment()) }

                            <Button onClick={this.goToPlot}
                                    bsStyle="info"
                                    disabled={this.state.loading}>
                                {this.state.loading ? "Fetching" : "Plot last"}
                            </Button>

                            <Input type='select' onChange={this.changeDuration} ref="plotDuration" value={this.state.plotDuration}>
                              <option value='sec'>sec</option>
                              <option value='min'>min</option>
                              <option value='hour'>hour</option>
                              <option value='day'>day</option>
                            </Input>

        </ListGroupItem>
        );
    }
});

var PointDetail = React.createClass({
    render: function() {
        return (

Detail

{JSON.stringify(this.props, null, 2) }

        );
    }
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

(function (root, factory) {

    var MessageFormat = factory();

    // register in -all- the module systems (at once)
    if (typeof define === 'function' && define.amd) {
        define(MessageFormat);
    }

    if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = MessageFormat;
    }

    if (root) {
        root.IntlMessageFormat = MessageFormat;
    }

})(typeof global !== 'undefined' ? global : this, function() {

    "use strict";

    var DEFAULT_LOCALE = null,

        REGEX_WHITE_SPACE         = /\s/g,
        REGEX_STRING_TO_PATTERN   = /\$?\{([^\} ]*)\}/g,
        REGEX_TOKEN_BREAK         = /(\$?\{?[^\$\{\}]*\}?)/gi,
        REGEX_TOKEN_AND_FORMATTER = /\$?\{([-\w]*):?([-\w]*)?\}/i,

        DEFAULT_FORMATTERS = {
            // TYPE: number
            number_integer: function (val, locale) {
                // 20000 -> 20,000
                return (new Intl.NumberFormat(locale)).format(val);
            },
            number_currency: function (val, locale, options) {
                // 20000 -> $20,000.00
                var currencyFormat = new Intl.NumberFormat(locale, {
                    style: 'currency',
                    currency: options.currency || options.CURRENCY || 'USD'
                });
                return currencyFormat.format(val);
            },
            number_percent: function (val, locale) {
                // 20000 -> 200%
                return (new Intl.NumberFormat(locale, { style: 'percent'})).format(val);
            },

            // TYPE: date
            // Date formats
            date_short: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    month: 'numeric',
                    day  : 'numeric',
                    year : '2-digit'
                });

                return dateFormat.format(val);
            },

            date_medium: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    month: 'short',
                    day  : 'numeric',
                    year : 'numeric'
                });

                return dateFormat.format(val);
            },

            date_long: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    month: 'long',
                    day  : 'numeric',
                    year : 'numeric'
                });

                return dateFormat.format(val);
            },

            date_full: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    weekday: 'long',
                    month  : 'long',
                    day    : 'numeric',
                    year   : 'numeric'
                });

                return dateFormat.format(val);
            },

            // TYPE: time
            time_short: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone: options.timeZone || null,
                    hour    : 'numeric',
                    minute  : 'numeric'
                });

                return timeFormat.format(val);
            },

            time_medium: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone: options.timeZone || null,
                    hour    : 'numeric',
                    minute  : 'numeric',
                    second  : 'numeric'
                });

                return timeFormat.format(val);
            },

            time_long: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    hour        : 'numeric',
                    minute      : 'numeric',
                    second      : 'numeric',
                    timeZoneName: 'short'
                });

                return timeFormat.format(val);
            },

            time_full: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    hour        : 'numeric',
                    minute      : 'numeric',
                    second      : 'numeric',
                    timeZoneName: 'short'
                });

                return timeFormat.format(val);
            }
        },

        // localeData registered by __addLocaleData()
        localeData = {};

    /**
     Creates MessageFormat object from a pattern, locale and field formatters.
     String patterns are broken down Arrays. Objects should match the
     following pattern:

     ```
     {
        type: 'plural|gender|select',
        valueName: 'string',
        offset: 1, // consistent offsets for plurals
        options: {}, // keys match options for plurals, gender and selects
        formatter: 'string|function' // strings are matched to internal formatters
     }
     ```

     @constructor

     @param {Array|String} pattern Array or string that serves as formatting pattern.
         Use array for plural and select messages, otherwise use string form.
     @param {LocaleList|String} locale Locale for string formatting.
     @param {Object} optFieldFormatters Holds user defined formatters for each field (Dojo like).
     */
    function MessageFormat (pattern, locale, optFieldFormatters) {
        var chunks,
            matches,
            len,
            i,
            p;

        // default locale to null
        /*jshint expr:true */
        locale || (locale = null);

        if (locale) {
            // strict value checking for locale when provided
            if (
                typeof locale !== 'string' || // make sure we have a string
                locale.replace(REGEX_WHITE_SPACE,'').length < 2 // it's at least two characters
            ) {
                throw new RangeError('Invalid language tag.');
            }
        }

        // store locale
        this.locale = locale;

        // We calculate the pluralization function used for the specific locale.
        // Since this is a bit expensive (if repeated too much) and since the
        // locale can change on us without notice, we need to keep track of
        // which locale was used in choosing the pluralization function.
        // (It's expected that the locale will change very infrequently for
        // each MessageFormat object.)
        this._pluralLocale = undefined;
        this._pluralFunc = undefined;

        // Assume the string passed in is a simple pattern for replacement.
        if (typeof pattern === 'string') {
            // break apart the string into chunks and tokens
            chunks = pattern.match(REGEX_TOKEN_BREAK);

            // Regular expression unfortunately matches an empty string at the end
            if (chunks[chunks.length - 1] === '') {
                chunks.pop();
            }

            // loop through each chunk and replace tokens when found
            for (i = 0, len = chunks.length; i < len; i++) {
                // create an object for the token when found
                matches = chunks[i].match(REGEX_TOKEN_AND_FORMATTER);
                if (matches) {
                    chunks[i] = {
                        // the valuename is the "key" for the token ... ${key}
                        valueName: matches[1],
                        formatter: matches[2],
                    };
                }
            }

            // our pattern should now be the chunked array
            pattern = chunks;
        }

        // save the pattern internally
        this.pattern = pattern;

        // store formatters
        this.formatters = DEFAULT_FORMATTERS;

        if (optFieldFormatters) {
            for (p in optFieldFormatters) {
                if (optFieldFormatters.hasOwnProperty(p) && typeof optFieldFormatters[p] === 'function') {
                    this.formatters[p] = optFieldFormatters[p];
                }
            }
        }
    }

    /**
     Formats pattern with supplied parameters.
     Dates, times and numbers are formatted in locale sensitive way.
     @param {Array|Object} params
     @return {String}
     */
    MessageFormat.prototype.format = function (obj) {

        var pattern = this.pattern,
            tokens,
            key,
            len,
            i;

        // the pattern we have is an array, we need to stitch it together
        // before moving forward
        if (Object.prototype.toString.call(pattern) === '[object Array]') {
            // let's not destroy the local pattern
            pattern = pattern.concat();

            // turn the array into a string
            pattern = this._processArray.call(this, pattern, obj);
        }

        // make sure we have a string
        pattern += '';

        // find tokens and replace with the object
        tokens = pattern.match(REGEX_STRING_TO_PATTERN);

        // if there were any tokens found, we need to replace them with the
        if (tokens) {
            for (i = 0, len = tokens.length; i < len; i++) {
                // extract key out of ${key} or {key}
                key = tokens[i].charAt(0) === '$' ?
                        tokens[i].substr(2) :
                        tokens[i].substr(1);

                // remove trailing }
                key = key.substr(0, key.length - 1);

                // replace the token with obj[key]
                if (obj.hasOwnProperty(key)) {
                    pattern = pattern.replace(tokens[i], obj[key]);
                }
            }
        }

        return pattern;
    };

    /**
     Returns resolved options, in this case supported locale.
     @return {Object}
     */
    MessageFormat.prototype.resolvedOptions = function () {
        // TODO: Figure out what options should be returned for messages
        return {};
    };

    /**
     Normalizes the number to option values for plural identification
     @param {Number} count Number to normalize
     @return {String}
     */
    MessageFormat.prototype._normalizeCount = function (count) {
        var locale = this.locale || DEFAULT_LOCALE,
            data,
            fn,
            parts;

        // if the locale isn't set, and there is no default locale set, throw
        if (
            typeof locale !== 'string' || // make sure we have a string
            locale.replace(/\s/g,'').length < 2 // it's at least two characters
        ) {
            throw new ReferenceError('No locale data has been provided for this object yet.');
        }

        // cache the choice of pluralization function
        if (this._pluralLocale !== locale) {
            if (locale !== DEFAULT_LOCALE) {
                parts = this.locale.toLowerCase().split('-');
                while (parts.length) {
                    data = localeData[parts.join('_')];
                    if (data && data.pluralFunction) {
                        fn = data.pluralFunction;
                        break;
                    }
                    parts.pop();
                }
            }
            if (!fn) {
                // While this seems excessive, it's possible the user has a
                // complex default locale (such as "zh-hans-CN") since the
                // default locale can come from a browser setting.
                parts = DEFAULT_LOCALE.toLowerCase().split('-');
                while (parts.length) {
                    data = localeData[parts.join('_')];
                    if (data && data.pluralFunction) {
                        fn = data.pluralFunction;
                        break;
                    }
                    parts.pop();
                }
            }
            if (!fn) {
                data = localeData[DEFAULT_LOCALE];
                fn = (data && data.pluralFunction) || function() {
                    return 'other';
                };
            }
            this._pluralLocale = locale;
            this._pluralFunc = fn;
        }
        return this._pluralFunc(count) || 'other';
    };

    /**
     Processes an array to return a string back once it's located. Arrays are
     concatenated. Each item is also processed based on whether it is an
     object or an array.
     @param {Array} arr
     @param {Object} obj
     @return {String}
     */
    MessageFormat.prototype._processArray = function (arr, obj) {
        var str = '',
            valType,
            val,
            len,
            i;

        // parse through the array to get the appropriate string value for each index
        for (i = 0, len = arr.length; i < len; i++) {

            val = arr[i];

            // If we don't already have a string, let's try to make it one
            if (typeof val !== 'string') {
                while (typeof val !== 'string') {
                    // let's find out what we are working with in the loop
                    valType = Object.prototype.toString.call(val);

                    if (valType === '[object Array]') {
                        val = this._processArray.call(this, val, obj);
                    } else if (valType === '[object Object]') {
                        val = this._processObject.call(this, val, obj);
                    } else {
                        // not an array or object, let's cast it and move on
                        val += '';
                    }
                }
            }

            // concat our new value to the return string
            str += val;
        }

        return str;
    };

    /**
     Processes the Object based on the lookUp object. Each object should have
     a `valueName` property; this property is used to located the value in the
     lookUp object.

     If the lookUp object returns a string, it will be sandwiched between
     `obj.prefix` and `obj.postfix` if they exist.

     @param {Ojbect} obj
     @param {Object} lookUp
     @return {String|Array|Object}
     */
    MessageFormat.prototype._processObject = function (obj, lookUp) {
        var val = lookUp[obj.valueName],
            valName = val,
            valType,
            formatterFn;

        // our look up object isn't in the provided lookUp object
        if (typeof val === 'undefined' || val === null) {
            throw new ReferenceError('The valueName `' + obj.valueName + '` was not found.');
        }

        // if we are dealing with plurals and we have a number, we need to
        // normalize the number's value based on the locale
        if (obj.type === 'plural' && typeof val === 'number') {
            if (obj.offset) {
                val += obj.offset;
            }

            val = this._normalizeCount(val);
        }

        // if we have an options property, we need the value from this object
        // as it relates to our value
        if (obj.options) {
            // options should always fallback to an "other" option when not found
            val = obj.options[val] || obj.options.other;
        }

        valType = typeof val;

        // if we have a string or number to return, we need to sandwich it
        // with (pre|post)fix
        if (valType === 'string' || valType === 'number') {

            // strings should be checked for hash tokens
            if (valType === 'string') {
                // We need to make sure we aren't doing a context look up `${#}`
                val = val.replace('${#}', valName);
            }

            // process with a formatter if one exists
            if (obj.formatter) {
                formatterFn = (typeof obj.formatter === 'function') ? obj.formatter : this.formatters[obj.formatter];

                if (formatterFn) {
                    val = formatterFn.call(this, val, this.locale);
                }
            }

            // sandwich
            val = (obj.prefix || '') + val + (obj.postfix || '');
        }

        return val;
    };

    /**
     Registers localization data for a particular locale.
     The format is:

     ```
     {
        locale: 'the locale',
        messageformat: {
            // This function takes a number (count) and turns it into a
            // pluralization group (e.g. 'one', 'few', 'many', 'other').
            pluralFunction: function(count) { return 'plural group' }
        }
     }
     ```

     @method __addLocaleData
     @param {Object} The locale data as described above.
     @return {nothing}
     */
    MessageFormat.__addLocaleData = function(data) {

        // if there isn't a default locale set, set it out of the data.locale
        if (DEFAULT_LOCALE === null) {
            DEFAULT_LOCALE = data.locale;
        }

        localeData[data.locale] = data.messageformat;
    };

    return MessageFormat;
});

<commit_msg>Use prototype chain instead of object looping.<commit_after>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

(function (root, factory) {

    var MessageFormat = factory();

    // register in -all- the module systems (at once)
    if (typeof define === 'function' && define.amd) {
        define(MessageFormat);
    }

    if (typeof module === 'object' && typeof module.exports === 'object') {
        module.exports = MessageFormat;
    }

    if (root) {
        root.IntlMessageFormat = MessageFormat;
    }

})(typeof global !== 'undefined' ? global : this, function() {

    "use strict";

    var DEFAULT_LOCALE = null,

        REGEX_WHITE_SPACE         = /\s/g,
        REGEX_STRING_TO_PATTERN   = /\$?\{([^\} ]*)\}/g,
        REGEX_TOKEN_BREAK         = /(\$?\{?[^\$\{\}]*\}?)/gi,
        REGEX_TOKEN_AND_FORMATTER = /\$?\{([-\w]*):?([-\w]*)?\}/i,

        DEFAULT_FORMATTERS = {
            // TYPE: number
            number_integer: function (val, locale) {
                // 20000 -> 20,000
                return (new Intl.NumberFormat(locale)).format(val);
            },
            number_currency: function (val, locale, options) {
                // 20000 -> $20,000.00
                var currencyFormat = new Intl.NumberFormat(locale, {
                    style: 'currency',
                    currency: options.currency || options.CURRENCY || 'USD'
                });
                return currencyFormat.format(val);
            },
            number_percent: function (val, locale) {
                // 20000 -> 200%
                return (new Intl.NumberFormat(locale, { style: 'percent'})).format(val);
            },

            // TYPE: date
            // Date formats
            date_short: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    month: 'numeric',
                    day  : 'numeric',
                    year : '2-digit'
                });

                return dateFormat.format(val);
            },

            date_medium: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    month: 'short',
                    day  : 'numeric',
                    year : 'numeric'
                });

                return dateFormat.format(val);
            },

            date_long: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    month: 'long',
                    day  : 'numeric',
                    year : 'numeric'
                });

                return dateFormat.format(val);
            },

            date_full: function (val, locale, options) {
                var dateFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    weekday: 'long',
                    month  : 'long',
                    day    : 'numeric',
                    year   : 'numeric'
                });

                return dateFormat.format(val);
            },

            // TYPE: time
            time_short: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone: options.timeZone || null,
                    hour    : 'numeric',
                    minute  : 'numeric'
                });

                return timeFormat.format(val);
            },

            time_medium: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone: options.timeZone || null,
                    hour    : 'numeric',
                    minute  : 'numeric',
                    second  : 'numeric'
                });

                return timeFormat.format(val);
            },

            time_long: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    hour        : 'numeric',
                    minute      : 'numeric',
                    second      : 'numeric',
                    timeZoneName: 'short'
                });

                return timeFormat.format(val);
            },

            time_full: function (val, locale, options) {
                var timeFormat = new Intl.DateTimeFormat(locale, {
                    timeZone    : options.timeZone || null,
                    hour        : 'numeric',
                    minute      : 'numeric',
                    second      : 'numeric',
                    timeZoneName: 'short'
                });

                return timeFormat.format(val);
            }
        },

        // localeData registered by __addLocaleData()
        localeData = {};

    /**
     Creates MessageFormat object from a pattern, locale and field formatters.
     String patterns are broken down Arrays. Objects should match the
     following pattern:

     ```
     {
        type: 'plural|gender|select',
        valueName: 'string',
        offset: 1, // consistent offsets for plurals
        options: {}, // keys match options for plurals, gender and selects
        formatter: 'string|function' // strings are matched to internal formatters
     }
     ```

     @constructor

     @param {Array|String} pattern Array or string that serves as formatting pattern.
         Use array for plural and select messages, otherwise use string form.
     @param {LocaleList|String} locale Locale for string formatting.
     @param {Object} optFieldFormatters Holds user defined formatters for each field (Dojo like).
     */
    function MessageFormat (pattern, locale, optFieldFormatters) {
        var chunks,
            matches,
            len,
            i,
            p;

        // default locale to null
        /*jshint expr:true */
        locale || (locale = null);

        if (locale) {
            // strict value checking for locale when provided
            if (
                typeof locale !== 'string' || // make sure we have a string
                locale.replace(REGEX_WHITE_SPACE,'').length < 2 // it's at least two characters
            ) {
                throw new RangeError('Invalid language tag.');
            }
        }

        // store locale
        this.locale = locale;

        // We calculate the pluralization function used for the specific locale.
        // Since this is a bit expensive (if repeated too much) and since the
        // locale can change on us without notice, we need to keep track of
        // which locale was used in choosing the pluralization function.
        // (It's expected that the locale will change very infrequently for
        // each MessageFormat object.)
        this._pluralLocale = undefined;
        this._pluralFunc = undefined;

        // Assume the string passed in is a simple pattern for replacement.
        if (typeof pattern === 'string') {
            // break apart the string into chunks and tokens
            chunks = pattern.match(REGEX_TOKEN_BREAK);

            // Regular expression unfortunately matches an empty string at the end
            if (chunks[chunks.length - 1] === '') {
                chunks.pop();
            }

            // loop through each chunk and replace tokens when found
            for (i = 0, len = chunks.length; i < len; i++) {
                // create an object for the token when found
                matches = chunks[i].match(REGEX_TOKEN_AND_FORMATTER);
                if (matches) {
                    chunks[i] = {
                        // the valuename is the "key" for the token ... ${key}
                        valueName: matches[1],
                        formatter: matches[2],
                    };
                }
            }

            // our pattern should now be the chunked array
            pattern = chunks;
        }

        // save the pattern internally
        this.pattern = pattern;

        // store formatters
        this.formatters = optFieldFormatters || {};
        this.formatters.__proto__ = DEFAULT_FORMATTERS;
    }

    /**
     Formats pattern with supplied parameters.
     Dates, times and numbers are formatted in locale sensitive way.
     @param {Array|Object} params
     @return {String}
     */
    MessageFormat.prototype.format = function (obj) {

        var pattern = this.pattern,
            tokens,
            key,
            len,
            i;

        // the pattern we have is an array, we need to stitch it together
        // before moving forward
        if (Object.prototype.toString.call(pattern) === '[object Array]') {
            // let's not destroy the local pattern
            pattern = pattern.concat();

            // turn the array into a string
            pattern = this._processArray.call(this, pattern, obj);
        }

        // make sure we have a string
        pattern += '';

        // find tokens and replace with the object
        tokens = pattern.match(REGEX_STRING_TO_PATTERN);

        // if there were any tokens found, we need to replace them with the
        if (tokens) {
            for (i = 0, len = tokens.length; i < len; i++) {
                // extract key out of ${key} or {key}
                key = tokens[i].charAt(0) === '$' ?
                        tokens[i].substr(2) :
                        tokens[i].substr(1);

                // remove trailing }
                key = key.substr(0, key.length - 1);

                // replace the token with obj[key]
                if (obj.hasOwnProperty(key)) {
                    pattern = pattern.replace(tokens[i], obj[key]);
                }
            }
        }

        return pattern;
    };

    /**
     Returns resolved options, in this case supported locale.
     @return {Object}
     */
    MessageFormat.prototype.resolvedOptions = function () {
        // TODO: Figure out what options should be returned for messages
        return {};
    };

    /**
     Normalizes the number to option values for plural identification
     @param {Number} count Number to normalize
     @return {String}
     */
    MessageFormat.prototype._normalizeCount = function (count) {
        var locale = this.locale || DEFAULT_LOCALE,
            data,
            fn,
            parts;

        // if the locale isn't set, and there is no default locale set, throw
        if (
            typeof locale !== 'string' || // make sure we have a string
            locale.replace(/\s/g,'').length < 2 // it's at least two characters
        ) {
            throw new ReferenceError('No locale data has been provided for this object yet.');
        }

        // cache the choice of pluralization function
        if (this._pluralLocale !== locale) {
            if (locale !== DEFAULT_LOCALE) {
                parts = this.locale.toLowerCase().split('-');
                while (parts.length) {
                    data = localeData[parts.join('_')];
                    if (data && data.pluralFunction) {
                        fn = data.pluralFunction;
                        break;
                    }
                    parts.pop();
                }
            }
            if (!fn) {
                // While this seems excessive, it's possible the user has a
                // complex default locale (such as "zh-hans-CN") since the
                // default locale can come from a browser setting.
                parts = DEFAULT_LOCALE.toLowerCase().split('-');
                while (parts.length) {
                    data = localeData[parts.join('_')];
                    if (data && data.pluralFunction) {
                        fn = data.pluralFunction;
                        break;
                    }
                    parts.pop();
                }
            }
            if (!fn) {
                data = localeData[DEFAULT_LOCALE];
                fn = (data && data.pluralFunction) || function() {
                    return 'other';
                };
            }
            this._pluralLocale = locale;
            this._pluralFunc = fn;
        }
        return this._pluralFunc(count) || 'other';
    };

    /**
     Processes an array to return a string back once it's located. Arrays are
     concatenated. Each item is also processed based on whether it is an
     object or an array.
     @param {Array} arr
     @param {Object} obj
     @return {String}
     */
    MessageFormat.prototype._processArray = function (arr, obj) {
        var str = '',
            valType,
            val,
            len,
            i;

        // parse through the array to get the appropriate string value for each index
        for (i = 0, len = arr.length; i < len; i++) {

            val = arr[i];

            // If we don't already have a string, let's try to make it one
            if (typeof val !== 'string') {
                while (typeof val !== 'string') {
                    // let's find out what we are working with in the loop
                    valType = Object.prototype.toString.call(val);

                    if (valType === '[object Array]') {
                        val = this._processArray.call(this, val, obj);
                    } else if (valType === '[object Object]') {
                        val = this._processObject.call(this, val, obj);
                    } else {
                        // not an array or object, let's cast it and move on
                        val += '';
                    }
                }
            }

            // concat our new value to the return string
            str += val;
        }

        return str;
    };

    /**
     Processes the Object based on the lookUp object. Each object should have
     a `valueName` property; this property is used to located the value in the
     lookUp object.

     If the lookUp object returns a string, it will be sandwiched between
     `obj.prefix` and `obj.postfix` if they exist.

     @param {Ojbect} obj
     @param {Object} lookUp
     @return {String|Array|Object}
     */
    MessageFormat.prototype._processObject = function (obj, lookUp) {
        var val = lookUp[obj.valueName],
            valName = val,
            valType,
            formatterFn;

        // our look up object isn't in the provided lookUp object
        if (typeof val === 'undefined' || val === null) {
            throw new ReferenceError('The valueName `' + obj.valueName + '` was not found.');
        }

        // if we are dealing with plurals and we have a number, we need to
        // normalize the number's value based on the locale
        if (obj.type === 'plural' && typeof val === 'number') {
            if (obj.offset) {
                val += obj.offset;
            }

            val = this._normalizeCount(val);
        }

        // if we have an options property, we need the value from this object
        // as it relates to our value
        if (obj.options) {
            // options should always fallback to an "other" option when not found
            val = obj.options[val] || obj.options.other;
        }

        valType = typeof val;

        // if we have a string or number to return, we need to sandwich it
        // with (pre|post)fix
        if (valType === 'string' || valType === 'number') {

            // strings should be checked for hash tokens
            if (valType === 'string') {
                // We need to make sure we aren't doing a context look up `${#}`
                val = val.replace('${#}', valName);
            }

            // process with a formatter if one exists
            if (obj.formatter) {
                formatterFn = (typeof obj.formatter === 'function') ? obj.formatter : this.formatters[obj.formatter];

                if (formatterFn) {
                    val = formatterFn.call(this, val, this.locale);
                }
            }

            // sandwich
            val = (obj.prefix || '') + val + (obj.postfix || '');
        }

        return val;
    };

    /**
     Registers localization data for a particular locale.
     The format is:

     ```
     {
        locale: 'the locale',
        messageformat: {
            // This function takes a number (count) and turns it into a
            // pluralization group (e.g. 'one', 'few', 'many', 'other').
            pluralFunction: function(count) { return 'plural group' }
        }
     }
     ```

     @method __addLocaleData
     @param {Object} The locale data as described above.
     @return {nothing}
     */
    MessageFormat.__addLocaleData = function(data) {

        // if there isn't a default locale set, set it out of the data.locale
        if (DEFAULT_LOCALE === null) {
            DEFAULT_LOCALE = data.locale;
        }

        localeData[data.locale] = data.messageformat;
    };

    return MessageFormat;
});

<|endoftext|>


<commit_before>var http = require('http');
var git = require('./lib/git');
var app = require('./lib/app');
var discovery = require('./lib/discovery');

var config = require('./lib/config').get();
var gitPort = process.env.GIT_PORT || config.server.gitPort || 8000;

var gitServer = http.createServer(git);
gitServer.listen(gitPort, function() {
  console.log("Public Git server listening on http://" + gitServer.address().address + ":" + gitServer.address().port);
});

var appServer = http.createServer(app);
var io = require('socket.io').listen(appServer);

appServer.listen(app.port, '0.0.0.0', function() {
  console.log("Private Express server listening on http://" + appServer.address().address + ":" + appServer.address().port);
});

var sockets = {};
io.sockets.on('connection', function(socket) {
  sockets[socket.id] = socket;
  socket.on('disconnect', function() {
    delete sockets[socket.id];
  });
});

[
  'repoAvailable',
  'repoUnavailable',
  'repoNotification'
].forEach(function(event) {
  discovery.on(event, function(data) {
    Object.keys(sockets).forEach(function(key) {
      var socket = sockets[key];
      socket.emit(event, data);
    });
  });
});

config.repos.forEach(function(repo) {
  if (repo.shared) {
    discovery.repoAvailable(config.server.description, gitPort, repo);
  }
});

process.on('exit', function() {
  config.repos.forEach(function(repo) {
    discovery.repoUnavailable(gitPort, repo);
  });
});
<commit_msg>Reannounce every 2 minutes<commit_after>var http = require('http');
var git = require('./lib/git');
var app = require('./lib/app');
var discovery = require('./lib/discovery');

var config = require('./lib/config').get();
var gitPort = process.env.GIT_PORT || config.server.gitPort || 8000;

var gitServer = http.createServer(git);
gitServer.listen(gitPort, function() {
  console.log("Public Git server listening on http://" + gitServer.address().address + ":" + gitServer.address().port);
});

var appServer = http.createServer(app);
var io = require('socket.io').listen(appServer);

appServer.listen(app.port, '0.0.0.0', function() {
  console.log("Private Express server listening on http://" + appServer.address().address + ":" + appServer.address().port);
});

var sockets = {};
io.sockets.on('connection', function(socket) {
  sockets[socket.id] = socket;
  socket.on('disconnect', function() {
    delete sockets[socket.id];
  });
});

[
  'repoAvailable',
  'repoUnavailable',
  'repoNotification'
].forEach(function(event) {
  discovery.on(event, function(data) {
    Object.keys(sockets).forEach(function(key) {
      var socket = sockets[key];
      socket.emit(event, data);
    });
  });
});

announceAll();
setTimeout(announceAll, 2 * 1000);

function announceAll() {
  config.repos.forEach(function(repo) {
    if (repo.shared) {
      discovery.repoAvailable(config.server.description, gitPort, repo);
    }
  });
}

process.on('exit', function() {
  config.repos.forEach(function(repo) {
    discovery.repoUnavailable(gitPort, repo);
  });
});
<|endoftext|>