<commit_before>var carto = require('carto');
var _ = require('underscore');
var PSQL = require('../psql');
var Step = require('step');
var torque_reference = require('torque.js').cartocss_reference;

function PSQLAdaptor(psqlClass) {
  var ctor = function(params) {
    this._psql = new psqlClass(params);
  }
  ctor.prototype.query = function(sql, callback) {
    this._psql.query(sql, this._handleResult.bind(this, callback));
  };
  ctor.prototype._handleResult = function(callback, err, result) {
    if ( err ) { callback(err); return; }
    var formatted = {
      fields: this._formatResultFields(result.fields),
      rows: result.rows
    };
    callback(null, formatted);
  };
  ctor.prototype._formatResultFields = function(flds) {
    var nfields = {};
    for (var i=0; i<flds.length; ++i) {
      var f = flds[i];
      var cname = this._psql.typeName(f.dataTypeID);
      var tname;
      if ( ! cname ) {
        tname = 'unknown(' + f.dataTypeID + ')';
      } else {
        if ( cname.match('bool') ) {
          tname = 'boolean';
        }
        else if ( cname.match(/int|float|numeric/) ) {
          tname = 'number';
        }
        else if ( cname.match(/text|char|unknown/) ) {
          tname = 'string';
        }
        else if ( cname.match(/date|time/) ) {
          tname = 'date';
        }
        else {
          tname = cname;
        }
        if ( tname && cname.match(/^_/) ) {
          tname += '[]';
        }
      }
      //console.log('cname:'+cname+' tname:'+tname);
      nfields[f.name] = { type: tname };
    }
    return nfields;
  }

  return ctor;
};

//
// given cartocss return javascript properties
//
// @param required optional array of required properties
//
// @throw Error if required properties are not found
//
function attrsFromCartoCSS(cartocss, required) {
  var attrs_keys = {
    '-torque-frame-count': 'steps',
    '-torque-resolution': 'resolution',
    '-torque-animation-duration': 'animationDuration',
    '-torque-aggregation-function': 'countby',
    '-torque-time-attribute': 'column',
    '-torque-data-aggregation': 'data_aggregation'
  };
  carto.tree.Reference.setData(torque_reference.version.latest);
  var env = {
      benchmark: false,
      validation_data: false,
      effects: [],
      errors: [],
      error: function(e) {
        this.errors.push(e);
      }
  };
  var root = (carto.Parser(env)).parse(cartocss);
  var definitions = root.toList(env);
  var rules = getMapProperties(definitions, env);
  var attrs = {};
  for (var k in attrs_keys) {
    if (rules[k]) {
      attrs[attrs_keys[k]] = rules[k].value.toString();
    } else if ( required && required.indexOf(attrs_keys[k]) != -1 ) {
        throw new Error("Missing required property '" + k + "' in torque layer CartoCSS");
    }
  }
  return attrs;
}

//
// get rules from Map definition
// stores errors in env.error
//
function getMapProperties(definitions, env) {
  var rules = {};
  definitions.filter(function(r) {
      return r.elements.join('') === 'Map';
  }).forEach(function(r) {
      for (var i = 0; i < r.rules.length; i++) {
          var key = r.rules[i].name;
          rules[key] = r.rules[i].ev(env);
      }
  });
  return rules;
}

//
// check layer and raise an exception is some error is found
//
// @throw Error if missing or malformed CartoCSS
//
function _checkLayerAttributes(layerConfig) {
  var cartoCSS = layerConfig.options.cartocss;
  if (!cartoCSS)
    throw Error("cartocss can't be undefined");
  var checks = ['steps', 'resolution', 'column', 'countby'];
  var attrs = attrsFromCartoCSS(cartoCSS, checks);
  return attrs;
}

function Renderer(layer, sql, attrs) {
  this.sql = sql;
  this.attrs = attrs;
  this.layer_sql = layer.options.sql;
}

Renderer.prototype = {
  /// API: renders a tile with the Renderer configuration
  /// @param x tile x coordinate
  /// @param y tile y coordinate
  /// @param z tile zoom
  /// callback: will be called when done using nodejs protocol (err, data)
  getTile: function(x, y, z, callback) {
    _getTileData(this.sql, {x: x, y: y}, z, this.layer_sql, this.attrs, callback);
  },

  /// API: returns metadata for this renderer
  getMetadata: function() {
    var a = this.attrs;
    return {
      start: a.start * 1000,
      end: a.end * 1000,
      data_steps: a.data_steps >> 0,
      columnType: a.is_time ? 'date': 'number'
    };
  }
};

/// API: initializes the renderer, it should be called once
//
/// @param options initialization options.
///     - sqlClass: class used to access postgres, by default is PSQL
///         this addapter should be this interface
///          - constructor(params) where params should contain:
///            host, port, database, user, password.
///            the class is always constructed with dbParams passed to
///            getRender as-is
///
///          - query(sql, callback(err, data))
///            gets an SQL query and return a javascript object with
///            the same structure of a JSON format response from
///            CartoDB-SQL-API, for reference see
///            http://github.com/CartoDB/CartoDB-SQL-API/blob/1.8.2/doc/API.md#json
///
function TorqueFactory(options) {
    this.options = options || {};
    _.defaults(this.options, {
      sqlClass: PSQLAdaptor(PSQL)
    });
    if (this.options.sqlClass) {
      this.sqlClass = this.options.sqlClass;
    }
}

TorqueFactory.prototype = {
  /// API: renderer name, use for information pourposes
  name: 'torque',

  /// API: tile formats this module is able to render
  supported_formats: ['json.torque', 'bin.torque'],

  /// API: creates a renderer
  /// @param mapConfig json object with full mapConfig
  /// @param dbParams json object with database connection params (host, dbname, user and pass)
  /// @param format one of the supported_formats
  /// @param layer layer index inside mapConfig (optional)
  /// @param callback function(err, renderer) called when method finishes
  getRenderer: function(mapConfig, dbParams, format, layer, callback) {
    if (arguments.length === 4) {
      callback = layer;
      callback(new Error("torque renderer only supports a single layer"))
      return;
    }
    if (!_.contains(this.supported_formats, format)) {
      callback(new Error("format not supported: " + format));
      return;
    }
    if (mapConfig.layers[layer] === undefined) {
      callback(new Error("layer index is greater than number of layers"));
      return;
    }
    var pgSQL = sql(dbParams, this.sqlClass);
    fetchMapConfigAttributes(pgSQL, mapConfig, dbParams, layer, function(err, params){
      if (err) {
        callback(err);
        return;
      }
      callback(null, new Renderer(mapConfig.layers[layer], pgSQL, params, layer));
    });
  }

};

module.exports = TorqueFactory;

// returns an array of errors if the mapconfig is not supported or contains errors
// errors is empty is the configuration is ok
// dbParams: host, dbname, user and pass
// layer: optional, if is specified only a layer is checked
function fetchMapConfigAttributes(sql, mapConfig, dbParams, layer_id, callback) {

  // only one layer supported
  var layer = mapConfig.layers[layer_id];

  if (!layer) {
    callback(new Error("mapconfig does not contain any torque layer"));
    return;
  }

  // check attrs
  try { var attrs = _checkLayerAttributes(layer); }
  catch (e) { callback(e); return; }

  // fetch layer attributes to check the query and so on is ok
  var layer_sql = layer.options.sql;
  fetchLayerAttributes(sql, layer_sql, attrs, function(err, layerAttrs) {
    if (err) {
      callback(err);
      return;
    }
    callback(null, _.extend(attrs, layerAttrs));
  });
}

function sql(dbParams, sqlClass) {
  // TODO: cache class instances by dbParams/sqlClass
  return function(query, callback) {
    var pg;
    try {
      pg = new sqlClass(dbParams);
      pg.query(query, callback);
    } catch (err) {
      callback(err);
    }
  };
}

function format(str) {
  for(var i = 1; i < arguments.length; ++i) {
    var attrs = arguments[i];
    for(var attr in attrs) {
      str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]);
    }
  }
  return str;
}

function fetchLayerAttributes(sql, layer_sql, attrs, callback) {
  var is_time;

  Step(

  //
  // first step, fetch the schema to know if torque colum is time column
  //
  function fetchSqlSchema() {
    var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: layer_sql });
    sql(query, this);
  },

  //
  // second step, get time bounds to calculate step
  //
  function fetchProps(err, data) {
    if (err) throw err;
    if ( ! data ) {
      console.log("ERROR: layer query '" + layer_sql + "' returned no data");
      throw new Error("Layer query returned no data");
    }
    if ( ! data.fields.hasOwnProperty(attrs.column) ) {
      console.log("ERROR: layer query " + layer_sql +
        " does not include time-attribute column '" + attrs.column + "'");
      throw new Error(
        "Layer query did not return the requested time-attribute column '"
        + attrs.column + "'");
    }
    is_time = data.fields[attrs.column].type === 'date';
    var column_conv = attrs.column;
    if (is_time){
      max_tmpl = "date_part('epoch', max({column}))";
      min_tmpl = "date_part('epoch', min({column}))";
      column_conv = format("date_part('epoch', {column})", attrs);
    } else {
      max_tmpl = "max({column})";
      min_tmpl = "min({column})";
    }

    max_col = format(max_tmpl, { column: attrs.column });
    min_col = format(min_tmpl, { column: attrs.column });
    var sql_stats = " SELECT " +
        "count(*) as num_steps, " +
        "{max_col} max_date, " +
        "{min_col} min_date FROM  ({sql}) __torque_wrap_sql ";

    var query = format(sql_stats, {
      max_col: max_col,
      min_col: min_col,
      column: column_conv,
      sql: layer_sql
    });
    sql(query, this);
  },

  //
  // prepare metadata needed to render the tiles
  //
  function generateMetadata(err, data) {
    if(err) {
      callback(err);
      return;
    }
    data = data.rows[0];
    var step = (data.max_date - data.min_date)/Math.min(attrs.steps, data.num_steps>>0);
    callback(null, {
      start: data.min_date,
      end: data.max_date,
      step: step || 1,
      data_steps: data.num_steps >> 0,
      is_time: is_time
    });
  });
}

function _getTileData(sql, coord, zoom, layer_sql, attrs, callback) {
    //var prof_fetch_time = Profiler.metric('ProviderJSON:tile_fetch_time').start()

    var column_conv = attrs.column;

    if(attrs.is_time) {
      column_conv = format("date_part('epoch', {column})", attrs);
    }

    //TODO: move to options
    var tile_size = 256;

    function CDB_XYZ_Resolution(z) {
      var earth_circumference = 40075017;
      var full_resolution = earth_circumference/tile_size;
      return full_resolution / Math.pow(2, z);
    }

    function CDB_XYZ_Extent(x, y, z) {
        var initial_resolution = CDB_XYZ_Resolution(0);
        var origin_shift = (initial_resolution * tile_size) / 2.0;

        var pixres = initial_resolution / Math.pow(2,z);
        var tile_geo_size = tile_size * pixres;

        var xmin = -origin_shift + x*tile_geo_size;
        var xmax = -origin_shift + (x+1)*tile_geo_size;

        var ymin = origin_shift - y*tile_geo_size;
        var ymax = origin_shift - (y+1)*tile_geo_size;
        return {
          xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax
        };
    }

    var tile_sql = "" +
      "WITH " +
      "par AS (" +
      "  SELECT ({xyz_resolution})*{resolution} as res" +
      ",  256/{resolution} as tile_size" +
      //", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext "  +
      ", ST_MakeEnvelope({xmin}, {ymin}, {xmax}, {ymax}, 3857) as ext " +
      ")," +
      "cte AS ( "+
      "  SELECT ST_SnapToGrid(i.the_geom_webmercator, p.res) g" +
      ", {countby} c" +
      ", floor(({column_conv} - {start})/{step}) d" +
      "  FROM ({_sql}) i, par p " +
      "  WHERE i.the_geom_webmercator && p.ext " +
      "  GROUP BY g, d" +
      ") " +
      "" +
      "SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " +
      "       (st_y(g)-st_ymin(p.ext))/p.res y__uint8," +
      " array_agg(c) vals__uint8," +
      " array_agg(d) dates__uint16" +
      // the tile_size where are needed because the overlaps query in cte subquery includes the points
      // in the left and bottom borders of the tile
      " FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8";

    var query = format(tile_sql, attrs, {
      zoom: zoom,
      x: coord.x,
      y: coord.y,
      column_conv: column_conv,
      _sql: layer_sql,
      xyz_resolution: CDB_XYZ_Resolution(zoom)
    }, CDB_XYZ_Extent(coord.x, coord.y, zoom));

    sql(query, function (err, data) {
      if (err) callback(new Error("Error running query " + query + ": " + err));
      else callback(null, data.rows);
      //prof_fetch_time.end();
    });
}
<commit_msg>Use variables for geometry column name and srid and for max tile geosize<commit_after>var carto = require('carto');
var _ = require('underscore');
var PSQL = require('../psql');
var Step = require('step');
var torque_reference = require('torque.js').cartocss_reference;

function PSQLAdaptor(psqlClass) {
  var ctor = function(params) {
    this._psql = new psqlClass(params);
  }
  ctor.prototype.query = function(sql, callback) {
    this._psql.query(sql, this._handleResult.bind(this, callback));
  };
  ctor.prototype._handleResult = function(callback, err, result) {
    if ( err ) { callback(err); return; }
    var formatted = {
      fields: this._formatResultFields(result.fields),
      rows: result.rows
    };
    callback(null, formatted);
  };
  ctor.prototype._formatResultFields = function(flds) {
    var nfields = {};
    for (var i=0; i<flds.length; ++i) {
      var f = flds[i];
      var cname = this._psql.typeName(f.dataTypeID);
      var tname;
      if ( ! cname ) {
        tname = 'unknown(' + f.dataTypeID + ')';
      } else {
        if ( cname.match('bool') ) {
          tname = 'boolean';
        }
        else if ( cname.match(/int|float|numeric/) ) {
          tname = 'number';
        }
        else if ( cname.match(/text|char|unknown/) ) {
          tname = 'string';
        }
        else if ( cname.match(/date|time/) ) {
          tname = 'date';
        }
        else {
          tname = cname;
        }
        if ( tname && cname.match(/^_/) ) {
          tname += '[]';
        }
      }
      //console.log('cname:'+cname+' tname:'+tname);
      nfields[f.name] = { type: tname };
    }
    return nfields;
  }

  return ctor;
};

//
// given cartocss return javascript properties
//
// @param required optional array of required properties
//
// @throw Error if required properties are not found
//
function attrsFromCartoCSS(cartocss, required) {
  var attrs_keys = {
    '-torque-frame-count': 'steps',
    '-torque-resolution': 'resolution',
    '-torque-animation-duration': 'animationDuration',
    '-torque-aggregation-function': 'countby',
    '-torque-time-attribute': 'column',
    '-torque-data-aggregation': 'data_aggregation'
  };
  carto.tree.Reference.setData(torque_reference.version.latest);
  var env = {
      benchmark: false,
      validation_data: false,
      effects: [],
      errors: [],
      error: function(e) {
        this.errors.push(e);
      }
  };
  var root = (carto.Parser(env)).parse(cartocss);
  var definitions = root.toList(env);
  var rules = getMapProperties(definitions, env);
  var attrs = {};
  for (var k in attrs_keys) {
    if (rules[k]) {
      attrs[attrs_keys[k]] = rules[k].value.toString();
    } else if ( required && required.indexOf(attrs_keys[k]) != -1 ) {
        throw new Error("Missing required property '" + k + "' in torque layer CartoCSS");
    }
  }
  return attrs;
}

//
// get rules from Map definition
// stores errors in env.error
//
function getMapProperties(definitions, env) {
  var rules = {};
  definitions.filter(function(r) {
      return r.elements.join('') === 'Map';
  }).forEach(function(r) {
      for (var i = 0; i < r.rules.length; i++) {
          var key = r.rules[i].name;
          rules[key] = r.rules[i].ev(env);
      }
  });
  return rules;
}

//
// check layer and raise an exception is some error is found
//
// @throw Error if missing or malformed CartoCSS
//
function _checkLayerAttributes(layerConfig) {
  var cartoCSS = layerConfig.options.cartocss;
  if (!cartoCSS)
    throw Error("cartocss can't be undefined");
  var checks = ['steps', 'resolution', 'column', 'countby'];
  var attrs = attrsFromCartoCSS(cartoCSS, checks);
  return attrs;
}

function Renderer(layer, sql, attrs) {
  this.sql = sql;
  this.attrs = attrs;
  this.layer_sql = layer.options.sql;
}

Renderer.prototype = {
  /// API: renders a tile with the Renderer configuration
  /// @param x tile x coordinate
  /// @param y tile y coordinate
  /// @param z tile zoom
  /// callback: will be called when done using nodejs protocol (err, data)
  getTile: function(x, y, z, callback) {
    _getTileData(this.sql, {x: x, y: y}, z, this.layer_sql, this.attrs, callback);
  },

  /// API: returns metadata for this renderer
  getMetadata: function() {
    var a = this.attrs;
    return {
      start: a.start * 1000,
      end: a.end * 1000,
      data_steps: a.data_steps >> 0,
      columnType: a.is_time ? 'date': 'number'
    };
  }
};

/// API: initializes the renderer, it should be called once
//
/// @param options initialization options.
///     - sqlClass: class used to access postgres, by default is PSQL
///         this addapter should be this interface
///          - constructor(params) where params should contain:
///            host, port, database, user, password.
///            the class is always constructed with dbParams passed to
///            getRender as-is
///
///          - query(sql, callback(err, data))
///            gets an SQL query and return a javascript object with
///            the same structure of a JSON format response from
///            CartoDB-SQL-API, for reference see
///            http://github.com/CartoDB/CartoDB-SQL-API/blob/1.8.2/doc/API.md#json
///
function TorqueFactory(options) {
    this.options = options || {};
    _.defaults(this.options, {
      sqlClass: PSQLAdaptor(PSQL)
    });
    if (this.options.sqlClass) {
      this.sqlClass = this.options.sqlClass;
    }
}

TorqueFactory.prototype = {
  /// API: renderer name, use for information pourposes
  name: 'torque',

  /// API: tile formats this module is able to render
  supported_formats: ['json.torque', 'bin.torque'],

  /// API: creates a renderer
  /// @param mapConfig json object with full mapConfig
  /// @param dbParams json object with database connection params (host, dbname, user and pass)
  /// @param format one of the supported_formats
  /// @param layer layer index inside mapConfig (optional)
  /// @param callback function(err, renderer) called when method finishes
  getRenderer: function(mapConfig, dbParams, format, layer, callback) {
    if (arguments.length === 4) {
      callback = layer;
      callback(new Error("torque renderer only supports a single layer"))
      return;
    }
    if (!_.contains(this.supported_formats, format)) {
      callback(new Error("format not supported: " + format));
      return;
    }
    if (mapConfig.layers[layer] === undefined) {
      callback(new Error("layer index is greater than number of layers"));
      return;
    }
    var pgSQL = sql(dbParams, this.sqlClass);
    fetchMapConfigAttributes(pgSQL, mapConfig, dbParams, layer, function(err, params){
      if (err) {
        callback(err);
        return;
      }
      callback(null, new Renderer(mapConfig.layers[layer], pgSQL, params, layer));
    });
  }

};

module.exports = TorqueFactory;

// returns an array of errors if the mapconfig is not supported or contains errors
// errors is empty is the configuration is ok
// dbParams: host, dbname, user and pass
// layer: optional, if is specified only a layer is checked
function fetchMapConfigAttributes(sql, mapConfig, dbParams, layer_id, callback) {

  // only one layer supported
  var layer = mapConfig.layers[layer_id];

  if (!layer) {
    callback(new Error("mapconfig does not contain any torque layer"));
    return;
  }

  // check attrs
  try { var attrs = _checkLayerAttributes(layer); }
  catch (e) { callback(e); return; }

  // fetch layer attributes to check the query and so on is ok
  var layer_sql = layer.options.sql;
  fetchLayerAttributes(sql, layer_sql, attrs, function(err, layerAttrs) {
    if (err) {
      callback(err);
      return;
    }
    callback(null, _.extend(attrs, layerAttrs));
  });
}

function sql(dbParams, sqlClass) {
  // TODO: cache class instances by dbParams/sqlClass
  return function(query, callback) {
    var pg;
    try {
      pg = new sqlClass(dbParams);
      pg.query(query, callback);
    } catch (err) {
      callback(err);
    }
  };
}

function format(str) {
  for(var i = 1; i < arguments.length; ++i) {
    var attrs = arguments[i];
    for(var attr in attrs) {
      str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]);
    }
  }
  return str;
}

function fetchLayerAttributes(sql, layer_sql, attrs, callback) {
  var is_time;

  Step(

  //
  // first step, fetch the schema to know if torque colum is time column
  //
  function fetchSqlSchema() {
    var query = format("select * from ({sql}) __torque_wrap_sql limit 0", { sql: layer_sql });
    sql(query, this);
  },

  //
  // second step, get time bounds to calculate step
  //
  function fetchProps(err, data) {
    if (err) throw err;
    if ( ! data ) {
      console.log("ERROR: layer query '" + layer_sql + "' returned no data");
      throw new Error("Layer query returned no data");
    }
    if ( ! data.fields.hasOwnProperty(attrs.column) ) {
      console.log("ERROR: layer query " + layer_sql +
        " does not include time-attribute column '" + attrs.column + "'");
      throw new Error(
        "Layer query did not return the requested time-attribute column '"
        + attrs.column + "'");
    }
    is_time = data.fields[attrs.column].type === 'date';
    var column_conv = attrs.column;
    if (is_time){
      max_tmpl = "date_part('epoch', max({column}))";
      min_tmpl = "date_part('epoch', min({column}))";
      column_conv = format("date_part('epoch', {column})", attrs);
    } else {
      max_tmpl = "max({column})";
      min_tmpl = "min({column})";
    }

    max_col = format(max_tmpl, { column: attrs.column });
    min_col = format(min_tmpl, { column: attrs.column });
    var sql_stats = " SELECT " +
        "count(*) as num_steps, " +
        "{max_col} max_date, " +
        "{min_col} min_date FROM  ({sql}) __torque_wrap_sql ";

    var query = format(sql_stats, {
      max_col: max_col,
      min_col: min_col,
      column: column_conv,
      sql: layer_sql
    });
    sql(query, this);
  },

  //
  // prepare metadata needed to render the tiles
  //
  function generateMetadata(err, data) {
    if(err) {
      callback(err);
      return;
    }
    data = data.rows[0];
    var step = (data.max_date - data.min_date)/Math.min(attrs.steps, data.num_steps>>0);
    callback(null, {
      start: data.min_date,
      end: data.max_date,
      step: step || 1,
      data_steps: data.num_steps >> 0,
      is_time: is_time
    });
  });
}

function _getTileData(sql, coord, zoom, layer_sql, attrs, callback) {
    //var prof_fetch_time = Profiler.metric('ProviderJSON:tile_fetch_time').start()

    var column_conv = attrs.column;

    if(attrs.is_time) {
      column_conv = format("date_part('epoch', {column})", attrs);
    }

    //TODO: move to options, allow to write in MapConfig
    var tile_size = 256;
    var tile_max_geosize = 40075017; // earth circumference in webmercator 3857
    var geom_column = 'the_geom_webmercator';
    var geom_column_srid = 3857;

    function CDB_XYZ_Resolution(z) {
      var full_resolution = tile_max_geosize / tile_size;
      return full_resolution / Math.pow(2, z);
    }

    function CDB_XYZ_Extent(x, y, z) {
        var initial_resolution = CDB_XYZ_Resolution(0);
        var origin_shift = (initial_resolution * tile_size) / 2.0;

        var pixres = initial_resolution / Math.pow(2,z);
        var tile_geo_size = tile_size * pixres;

        var xmin = -origin_shift + x*tile_geo_size;
        var xmax = -origin_shift + (x+1)*tile_geo_size;

        var ymin = origin_shift - y*tile_geo_size;
        var ymax = origin_shift - (y+1)*tile_geo_size;
        return {
          xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax
        };
    }

    var tile_sql = "" +
      "WITH " +
      "par AS (" +
      "  SELECT ({xyz_resolution})*{resolution} as res" +
      ",  256/{resolution} as tile_size" +
      //", CDB_XYZ_Extent({x}, {y}, {zoom}) as ext "  +
      ", ST_MakeEnvelope({xmin}, {ymin}, {xmax}, {ymax}, " +
      geom_column_srid + ") as ext )," +
      "cte AS ( "+
      "  SELECT ST_SnapToGrid(i." + geom_column + ", p.res) g" +
      ", {countby} c" +
      ", floor(({column_conv} - {start})/{step}) d" +
      "  FROM ({_sql}) i, par p " +
      "  WHERE i." + geom_column + " && p.ext " +
      "  GROUP BY g, d" +
      ") " +
      "" +
      "SELECT (st_x(g)-st_xmin(p.ext))/p.res x__uint8, " +
      "       (st_y(g)-st_ymin(p.ext))/p.res y__uint8," +
      " array_agg(c) vals__uint8," +
      " array_agg(d) dates__uint16" +
      // the tile_size where are needed because the overlaps query in cte subquery includes the points
      // in the left and bottom borders of the tile
      " FROM cte, par p where (st_y(g)-st_ymin(p.ext))/p.res < tile_size and (st_x(g)-st_xmin(p.ext))/p.res < tile_size GROUP BY x__uint8, y__uint8";

    var query = format(tile_sql, attrs, {
      zoom: zoom,
      x: coord.x,
      y: coord.y,
      column_conv: column_conv,
      _sql: layer_sql,
      xyz_resolution: CDB_XYZ_Resolution(zoom)
    }, CDB_XYZ_Extent(coord.x, coord.y, zoom));

    sql(query, function (err, data) {
      if (err) callback(new Error("Error running query " + query + ": " + err));
      else callback(null, data.rows);
      //prof_fetch_time.end();
    });
}
<|endoftext|>


<commit_before>define(["Ti/_/declare", "Ti/_/lang", "Ti/_/UI/Widget", "Ti/_/dom", "Ti/_/style", "Ti/Locale", "Ti/UI", "Ti/UI/ActivityIndicatorStyle"],
	function(declare, lang, Widget, dom, style, Locale, UI, ActivityIndicatorStyle) {

	var opacity = 0.3,
		setStyle = style.set;

	return declare("Ti.UI.ActivityIndicator", Widget, {

		constructor: function() {
			var contentContainer = this._contentContainer = UI.createView({
					layout: UI._LAYOUT_CONSTRAINING_HORIZONTAL,
					width: UI.SIZE,
					height: UI.SIZE
				});
			this._add(contentContainer);
			contentContainer.hide();

			contentContainer._add(this._indicatorIndicator = UI.createView());
			contentContainer._add(this._indicatorMessage = UI.createLabel());

			this._createProngs();
		},

		_createProngs: function() {

			var i = 0,
				prongs = this._prongs = [],
				indicator = this._indicatorIndicator,
				indicatorDomNode = indicator.domNode,
				backgroundColor = this.indicatorColor,
				diameter = this.indicatorDiameter,
				scale = diameter / 36,
				prongContainer;

			// Set the container size
			indicator.width = indicator.height = diameter;

			// Remove any old children
			while (indicatorDomNode.firstChild) {
				indicatorDomNode.removeChild(indicatorDomNode.firstChild);
			}

			// Add the prong container
			prongContainer = dom.create("div", {
				className: "TiUIActivityIndicatorProngContainer",
				style: {
					transformOrigin: "0px 0px",
					transform: "scale(" + scale + ")"
				}
			}, indicatorDomNode)

			// Add the new prongs
			for (; i < 12; i++) {
				prongs.push(dom.create("div", {
					className: "TiUIActivityIndicatorProng",
					style: {
						transform: "translate(16px,0px) rotate(" + i * 30 + "deg)",
						transformOrigin: "2px 18px",
						opacity: opacity,
						backgroundColor: backgroundColor
					}
				}, prongContainer));
			}
		},

		show: function() {
			if (!this._visible) {
				this._contentContainer.show();
				this._timer = setInterval(lang.hitch(this, "_animate"), 100);
				this._visible = 1;
			}
		},

		hide: function() {
			clearTimeout(this._timer);
			if (this._visible) {
				this._contentContainer.hide();
				this._visible = 0;
			}
		},

		_currentProng: 0,

		_animate: function() {
			var prong = this._prongs[this._currentProng];
			++this._currentProng == 12 && (this._currentProng = 0);
			setStyle(prong, "transition", "");
			setTimeout(function() {
				setStyle(prong, "opacity", 1);
				setTimeout(function() {
					setStyle(prong, "transition", "opacity 500ms linear 0ms");
					setTimeout(function() {
						setStyle(prong, "opacity", opacity);
					}, 1);
				}, 1);
			}, 1);
		},

		_defaultWidth: UI.SIZE,

		_defaultHeight: UI.SIZE,

		_messagePadding: 0,

		properties: {
			color: {
				set: function(value) {
					return this._indicatorMessage.color = value;
				}
			},
			font: {
				set: function(value) {
					return this._indicatorMessage.font = value;
				}
			},
			indicatorColor: {
				post: "_createProngs",
				value: "#fff"
			},
			indicatorDiameter: {
				post: "_createProngs",
				value: 36
			},
			message: {
				set: function(value) {
					var indicatorMessage = this._indicatorMessage;
					indicatorMessage.left = value ? 5 : 0;
					return indicatorMessage.text = value;
				}
			},
			messageid: {
				set: function(value) {
					var indicatorMessage = this._indicatorMessage;
					indicatorMessage.left = value ? 5 : 0;
					return indicatorMessage.textid = value;
				}
			},
			style: {
				set: function(value) {
					if (~[ActivityIndicatorStyle.DARK, ActivityIndicatorStyle.BIG_DARK].indexOf(value)) {
						this.indicatorColor = "#444";
					} else {
						this.indicatorColor = "#fff";
					}
					if (~[ActivityIndicatorStyle.BIG, ActivityIndicatorStyle.BIG_DARK].indexOf(value)) {
						this.indicatorDiameter = 72;
					} else {
						this.indicatorDiameter = 36;
					}
				}
			}
		}

	});

});<commit_msg>call hide/show on widget super class<commit_after>define(["Ti/_/declare", "Ti/_/lang", "Ti/_/UI/Widget", "Ti/_/dom", "Ti/_/style", "Ti/Locale", "Ti/UI", "Ti/UI/ActivityIndicatorStyle"],
	function(declare, lang, Widget, dom, style, Locale, UI, ActivityIndicatorStyle) {

	var opacity = 0.3,
		setStyle = style.set;

	return declare("Ti.UI.ActivityIndicator", Widget, {

		constructor: function() {
			var contentContainer = this._contentContainer = UI.createView({
					layout: UI._LAYOUT_CONSTRAINING_HORIZONTAL,
					width: UI.SIZE,
					height: UI.SIZE
				});
			this._add(contentContainer);

			Widget.prototype.hide.call(this);

			contentContainer._add(this._indicatorIndicator = UI.createView());
			contentContainer._add(this._indicatorMessage = UI.createLabel());

			this._createProngs();
		},

		_createProngs: function() {

			var i = 0,
				prongs = this._prongs = [],
				indicator = this._indicatorIndicator,
				indicatorDomNode = indicator.domNode,
				backgroundColor = this.indicatorColor,
				diameter = this.indicatorDiameter,
				scale = diameter / 36,
				prongContainer;

			// Set the container size
			indicator.width = indicator.height = diameter;

			// Remove any old children
			while (indicatorDomNode.firstChild) {
				indicatorDomNode.removeChild(indicatorDomNode.firstChild);
			}

			// Add the prong container
			prongContainer = dom.create("div", {
				className: "TiUIActivityIndicatorProngContainer",
				style: {
					transformOrigin: "0px 0px",
					transform: "scale(" + scale + ")"
				}
			}, indicatorDomNode)

			// Add the new prongs
			for (; i < 12; i++) {
				prongs.push(dom.create("div", {
					className: "TiUIActivityIndicatorProng",
					style: {
						transform: "translate(16px,0px) rotate(" + i * 30 + "deg)",
						transformOrigin: "2px 18px",
						opacity: opacity,
						backgroundColor: backgroundColor
					}
				}, prongContainer));
			}
		},

		show: function() {
			if (!this._visible) {
				Widget.prototype.show.call(this);
				this._timer = setInterval(lang.hitch(this, "_animate"), 100);
				this._visible = 1;
			}
		},

		hide: function() {
			clearTimeout(this._timer);
			if (this._visible) {
				Widget.prototype.hide.call(this);
				this._visible = 0;
			}
		},

		_currentProng: 0,

		_animate: function() {
			var prong = this._prongs[this._currentProng];
			++this._currentProng == 12 && (this._currentProng = 0);
			setStyle(prong, "transition", "");
			setTimeout(function() {
				setStyle(prong, "opacity", 1);
				setTimeout(function() {
					setStyle(prong, "transition", "opacity 500ms linear 0ms");
					setTimeout(function() {
						setStyle(prong, "opacity", opacity);
					}, 1);
				}, 1);
			}, 1);
		},

		_defaultWidth: UI.SIZE,

		_defaultHeight: UI.SIZE,

		_messagePadding: 0,

		properties: {
			color: {
				set: function(value) {
					return this._indicatorMessage.color = value;
				}
			},
			font: {
				set: function(value) {
					return this._indicatorMessage.font = value;
				}
			},
			indicatorColor: {
				post: "_createProngs",
				value: "#fff"
			},
			indicatorDiameter: {
				post: "_createProngs",
				value: 36
			},
			message: {
				set: function(value) {
					var indicatorMessage = this._indicatorMessage;
					indicatorMessage.left = value ? 5 : 0;
					return indicatorMessage.text = value;
				}
			},
			messageid: {
				set: function(value) {
					var indicatorMessage = this._indicatorMessage;
					indicatorMessage.left = value ? 5 : 0;
					return indicatorMessage.textid = value;
				}
			},
			style: {
				set: function(value) {
					if (~[ActivityIndicatorStyle.DARK, ActivityIndicatorStyle.BIG_DARK].indexOf(value)) {
						this.indicatorColor = "#444";
					} else {
						this.indicatorColor = "#fff";
					}
					if (~[ActivityIndicatorStyle.BIG, ActivityIndicatorStyle.BIG_DARK].indexOf(value)) {
						this.indicatorDiameter = 72;
					} else {
						this.indicatorDiameter = 36;
					}
				}
			}
		}

	});

});<|endoftext|>


<commit_before>/**
 * 弹出层
 */
onkeydown(document, function (e) {
    if (e.which === 27 && rootElements.length) {
        remove(rootElements[rootElements.length - 1]);
    }
});
var setPosition = function (element) {
    css(element, {
        marginLeft: fromPixel(-element.offsetWidth / 2),
        marginTop: fromPixel(-element.offsetHeight / 2),
        left: "50%",
        top: "50%"
    });
};
var windowFactory = function () {
    var window = document.createElement("div");
    css(window, `height:100px;width:100px;position:absolute;background-color:#fff;left:${rootElements.length << 4}px;top:${rootElements.length << 4}px;`);
    return window;
};
var loadingFactory = function () {
    var element = document.createElement("div");
    css(element, `position:absolute;background-color:#fff;`);
    return element;
}
/**
 * 弹出子窗口的函数
 * @param {string} path 窗口路径的唯一索引
 * @param {any} parameters 给窗口的作域预先赋值
 * @param {object} style 自定义样式，主要是width,height等位置参数
 * @returns {Element} 给调用弹窗的命令暴露的可选方法
 */
var popup = function (path) {
    if (isString(path)) {
        return popup_path.apply(null, arguments);
    }
    if (isNode(path)) {
        return popup_view.apply(null, arguments);
    }
    throw new Error(`path isn't valid:${path}`);
};
var popup_path = function (path = "", parameters, target) {
    if (!popup.go || !popup.prepare) throw new Error("当前环境无法使用");
    // 3 has mask has view control
    var element;
    if (/^#/.test(path)) {
        // mask
        element = windowFactory();
        path = path.replace(/^#/, "");
        popup.go(path, parameters, element);
        popup_with_mask(element, target);
    }
    // 2 has view control has no mask
    else if (/^@/.test(path)) {
        element = windowFactory();
        path = path.replace(/^@/, "");
        popup.go(path, parameters, element);
        popup_as_single(element);
    }
    // 1 has mask has no control
    else if (/^!/.test(path)) {
        element = loadingFactory();
        path = path.replace(/^!/, "");
        popup.go(path, parameters, element);
        popup_with_mask(element);
    }
    // 0 has no mask no control
    else {
        element = loadingFactory();
        popup.go(path, parameters, element);
        popup_as_single(element);
    }
    element.style.opacity = 0;
    popup.prepare(path, function () {
        setTimeout(function () {
            setPosition(element);
            element.style.opacity = 1;
        });
    });
    return element;
};

var popup_view = function (element, target) {
    if (isNode(target)) {
        if (target.isMask) {
            popup_with_mask(element, target);
            setPosition(element);
            return element;
        }
        return popup_as_extra(element, target);
    }
    if (target) {
        popup_with_mask(element);
        setPosition(element);
    }
    popup_as_single(element);
    setPosition(element);
    return element;
};
var createMask = function () {
    var mask = document.createElement("div");
    css(mask, `position:absolute;position:fixed;z-index:${zIndex()};left:0;right:0;bottom:0;top:0;width:auto;height:auto;background-color:rgba(0,0,0,0.2)`);
    mask.className = "popup-factory mask";
    mask.isMask = true;
    return mask;
};
var popup_with_mask = function (element, mask = createMask()) {
    if (!mask.with) {
        mask.with = [element];
    } else if (mask.with instanceof Array) {
        mask.with.push(element);
    } else {
        mask.with = [mask.with, element];
    }
    if (!mask.clean) {
        mask.clean = new Cleanup(mask.with);
    }
    element.mask = mask;
    onremove(element, mask.clean);
    css(element, `z-index:${zIndex()};`);
    if (mask.isMounted) {
        global(element);
    } else {
        global(mask);
    }
    return element;
};
var popup_as_extra = function (element, target) {
    var { offsetParent, nextSibling, previousSibling } = target;
    if (
        nextSibling
        && (
            nextSibling.offsetLeft - target.offsetLeft >= target.offsetWidth / 2
            || target.offsetLeft - nextSibling.offsetLeft >= nextSibling.offsetWidth / 2
        )
        || previousSibling
        && (
            previousSibling.offsetLeft - target.offsetLeft >= target.offsetWidth / 2
            || target.offsetLeft - previousSibling.offsetLeft >= previousSibling.offsetWidth / 2
        )
    ) {
        popup_as_yextra(global, element, target);
    } else if (offsetParent) {
        if (innerHeight - target.offsetHeight > innerWidth - target.offsetWidth && offsetParent.clientWidth !== target.offsetWidth) {
            popup_as_yextra(global, element, target);
        } else {
            popup_as_xextra(global, element, target);
        }
    } else if (/inline|cell/i.test(getComputedStyle(target).display)) {
        popup_as_yextra(global, element, target);
    } else {
        popup_as_xextra(global, element, target);
    }
};
var popup_as_yextra = function (global, element, target) {
    if (!element.origin) {
        element.origin = {
            height: element.style.height,
            width: element.style.width,
            minWidth: element.style.minWidth,
            display: element.style.display
        };
    }
    css(element, `position:absolute;`);
    var release1 = onremove(target, function () {
        remove(element);
    });
    var release2 = onremove(element, function () {
        release1();
        release2();
        if (document.removeEventListener) {
            document.removeEventListener("scroll", reshape, true);
            document.removeEventListener("resize", reshape);
        }
    });
    global(element);
    var reshape = function () {
        extend(element.style, element.origin);
        var position = getScreenPosition(target);
        var viewrect = getScreenPosition(element.offsetParent);
        var maxHeight = Math.max(position.top, innerHeight - position.top - position.height);
        var maxWidth = Math.max(position.left + position.width, innerWidth - position.left);
        var height = element.offsetHeight;
        //如果高度超出可视区，调整高度
        if (height > maxHeight) {
            css(element, `height:${maxHeight}px;`);
        }
        css(element, `min-width:auto;`);
        var aimedWidth = element.offsetWidth;
        //如果宽度不足其附着元素的宽度
        if (aimedWidth < target.offsetWidth) {
            aimedWidth = target.offsetWidth;
        }

        //如果宽度超出可视区，调整宽度
        if (aimedWidth > maxWidth) {
            aimedWidth = maxWidth;
        }
        if (aimedWidth !== element.offsetWidth) {
            css(element, `width:${aimedWidth}px`);
        }
        if (position.top + element.offsetHeight + position.height > innerHeight) {
            css(element, `bottom:${viewrect.height - position.top + viewrect.top}px;top:auto;`);
        } else {
            css(element, `top:${position.top - viewrect.top + position.height}px;bottom:auto;`);
        }
        if (position.left + element.offsetWidth > innerWidth) {
            css(element, `right:${viewrect.width + viewrect.left - position.left - position.width}px;left:auto;`);
        } else {
            css(element, `left:${position.left - viewrect.left}px;right:auto;`);
        }
        var offsetParent = target.offsetParent;
        if (offsetParent) {
            if (overlap(position, offsetParent) > 0) {
                element.style.display = element.origin.display;
            } else {
                element.style.display = "none";
            };
        }

    }
    if (document.addEventListener) {
        document.addEventListener("scroll", reshape, true);
        document.addEventListener("resize", reshape);
    }
    reshape();

};
var allArgumentsNames = arguments[arguments.length - 1];
var popup_as_xextra = arriswise(popup_as_yextra, allArgumentsNames.concat([].slice.call(arguments, 0)));
var popup_as_single = function (element) {
    css(element, `z-index:${zIndex()};`);
    global(element);
};
var global = function (element) {
    once("remove")(element, cleanup);
    popup.global ? popup.global(element, true) : appendChild(document.body, element);
}
var cleanup = new Cleanup(rootElements);<commit_msg>弹出已初始化对象时不再自动居中<commit_after>/**
 * 弹出层
 */
onkeydown(document, function (e) {
    if (e.which === 27 && rootElements.length) {
        remove(rootElements[rootElements.length - 1]);
    }
});
var setInitialStyle = function (element) {
    element.initialStyle = {
        opacity: 0,
        transform: "scaleZ(2)"
    };
    // element.style.transform = "scaleZ(1)";
    element.style.transition = "opacity 3s, transform 3s ease-out";
};
var setPosition = function (element) {
    css(element, {
        marginLeft: fromPixel(-element.offsetWidth / 2),
        marginTop: fromPixel(-element.offsetHeight / 2),
        left: "50%",
        top: "50%"
    });
};
var windowFactory = function () {
    var window = document.createElement("div");
    css(window, `height:100px;width:100px;position:absolute;background-color:#fff;left:${rootElements.length << 4}px;top:${rootElements.length << 4}px;`);
    return window;
};
var loadingFactory = function () {
    var element = document.createElement("div");
    css(element, `position:absolute;background-color:#fff;`);
    return element;
}
/**
 * 弹出子窗口的函数
 * @param {string} path 窗口路径的唯一索引
 * @param {any} parameters 给窗口的作域预先赋值
 * @param {object} style 自定义样式，主要是width,height等位置参数
 * @returns {Element} 给调用弹窗的命令暴露的可选方法
 */
var popup = function (path) {
    if (isString(path)) {
        return popup_path.apply(null, arguments);
    }
    if (isNode(path)) {
        return popup_view.apply(null, arguments);
    }
    throw new Error(`path isn't valid:${path}`);
};
var popup_path = function (path = "", parameters, target) {
    if (!popup.go || !popup.prepare) throw new Error("当前环境无法使用");
    // 3 has mask has view control
    var element;
    if (/^#/.test(path)) {
        // mask
        element = windowFactory();
        path = path.replace(/^#/, "");
        popup.go(path, parameters, element);
        setInitialStyle(element);
        popup_with_mask(element, target);
    }
    // 2 has view control has no mask
    else if (/^@/.test(path)) {
        element = windowFactory();
        path = path.replace(/^@/, "");
        popup.go(path, parameters, element);
        setInitialStyle(element);
        popup_as_single(element);
    }
    // 1 has mask has no control
    else if (/^!/.test(path)) {
        element = loadingFactory();
        path = path.replace(/^!/, "");
        popup.go(path, parameters, element);
        setInitialStyle(element);
        popup_with_mask(element);
    }
    // 0 has no mask no control
    else {
        element = loadingFactory();
        popup.go(path, parameters, element);
        setInitialStyle(element);
        popup_as_single(element);
    }
    element.style.opacity = 0;
    popup.prepare(path, function () {
        setTimeout(function () {
            setPosition(element);
            element.style.opacity = 1;
        });
    });
    return element;
};

var popup_view = function (element, target) {
    if (isNode(target)) {
        if (target.isMask) {
            popup_with_mask(element, target);
            return element;
        }
        return popup_as_extra(element, target);
    }
    if (target) {
        popup_with_mask(element);
    } else {
        popup_as_single(element);
    }
    return element;
};
var createMask = function () {
    var mask = document.createElement("div");
    css(mask, `position:absolute;position:fixed;z-index:${zIndex()};left:0;right:0;bottom:0;top:0;width:auto;height:auto;background-color:rgba(0,0,0,0.2)`);
    mask.className = "popup-factory mask";
    mask.isMask = true;
    return mask;
};
var popup_with_mask = function (element, mask = createMask()) {
    if (!mask.with) {
        mask.with = [element];
    } else if (mask.with instanceof Array) {
        mask.with.push(element);
    } else {
        mask.with = [mask.with, element];
    }
    if (!mask.clean) {
        mask.clean = new Cleanup(mask.with);
    }
    element.mask = mask;
    onremove(element, mask.clean);
    css(element, `z-index:${zIndex()};`);
    if (mask.isMounted) {
        global(element);
    } else {
        global(mask);
    }
    return element;
};
var popup_as_extra = function (element, target) {
    var { offsetParent, nextSibling, previousSibling } = target;
    if (
        nextSibling
        && (
            nextSibling.offsetLeft - target.offsetLeft >= target.offsetWidth / 2
            || target.offsetLeft - nextSibling.offsetLeft >= nextSibling.offsetWidth / 2
        )
        || previousSibling
        && (
            previousSibling.offsetLeft - target.offsetLeft >= target.offsetWidth / 2
            || target.offsetLeft - previousSibling.offsetLeft >= previousSibling.offsetWidth / 2
        )
    ) {
        popup_as_yextra(global, element, target);
    } else if (offsetParent) {
        if (innerHeight - target.offsetHeight > innerWidth - target.offsetWidth && offsetParent.clientWidth !== target.offsetWidth) {
            popup_as_yextra(global, element, target);
        } else {
            popup_as_xextra(global, element, target);
        }
    } else if (/inline|cell/i.test(getComputedStyle(target).display)) {
        popup_as_yextra(global, element, target);
    } else {
        popup_as_xextra(global, element, target);
    }
};
var popup_as_yextra = function (global, element, target) {
    if (!element.origin) {
        element.origin = {
            height: element.style.height,
            width: element.style.width,
            minWidth: element.style.minWidth,
            display: element.style.display
        };
    }
    css(element, `position:absolute;`);
    var release1 = onremove(target, function () {
        remove(element);
    });
    var release2 = onremove(element, function () {
        release1();
        release2();
        if (document.removeEventListener) {
            document.removeEventListener("scroll", reshape, true);
            document.removeEventListener("resize", reshape);
        }
    });
    global(element);
    var reshape = function () {
        extend(element.style, element.origin);
        var position = getScreenPosition(target);
        var viewrect = getScreenPosition(element.offsetParent);
        var maxHeight = Math.max(position.top, innerHeight - position.top - position.height);
        var maxWidth = Math.max(position.left + position.width, innerWidth - position.left);
        var height = element.offsetHeight;
        //如果高度超出可视区，调整高度
        if (height > maxHeight) {
            css(element, `height:${maxHeight}px;`);
        }
        css(element, `min-width:auto;`);
        var aimedWidth = element.offsetWidth;
        //如果宽度不足其附着元素的宽度
        if (aimedWidth < target.offsetWidth) {
            aimedWidth = target.offsetWidth;
        }

        //如果宽度超出可视区，调整宽度
        if (aimedWidth > maxWidth) {
            aimedWidth = maxWidth;
        }
        if (aimedWidth !== element.offsetWidth) {
            css(element, `width:${aimedWidth}px`);
        }
        if (position.top + element.offsetHeight + position.height > innerHeight) {
            css(element, `bottom:${viewrect.height - position.top + viewrect.top}px;top:auto;`);
        } else {
            css(element, `top:${position.top - viewrect.top + position.height}px;bottom:auto;`);
        }
        if (position.left + element.offsetWidth > innerWidth) {
            css(element, `right:${viewrect.width + viewrect.left - position.left - position.width}px;left:auto;`);
        } else {
            css(element, `left:${position.left - viewrect.left}px;right:auto;`);
        }
        var offsetParent = target.offsetParent;
        if (offsetParent) {
            if (overlap(position, offsetParent) > 0) {
                element.style.display = element.origin.display;
            } else {
                element.style.display = "none";
            };
        }

    }
    if (document.addEventListener) {
        document.addEventListener("scroll", reshape, true);
        document.addEventListener("resize", reshape);
    }
    reshape();

};
var allArgumentsNames = arguments[arguments.length - 1];
var popup_as_xextra = arriswise(popup_as_yextra, allArgumentsNames.concat([].slice.call(arguments, 0)));
var popup_as_single = function (element) {
    css(element, `z-index:${zIndex()};`);
    global(element);
};
var global = function (element) {
    once("remove")(element, cleanup);
    popup.global ? popup.global(element, true) : appendChild(document.body, element);
}
var cleanup = new Cleanup(rootElements);<|endoftext|>


<commit_before>$('.select-course table tbody tr .sectionNumber').each(function () {
  var nameElem = $(this).next().next().next();
  getPolyratingData(nameElem);
});

function getPolyratingData(nameElem) {
  if (nameElem[0].innerText === 'STAFF') {
    return;
  }
  var names = nameElem[0].innerText.split(',');
  var lastName = removeAllSingleLetters(names[0].trim());
  var firstName = onlyFirstName(removeAllSingleLetters(names[1].trim()));
  var name = firstName + ' ' + lastName;
  chrome.runtime.sendMessage(
    {
      method: 'GET',
      action: 'xhttp',
      url: 'http://polyratings.com/search.php?type=ProfName&terms=' + name.replace(/ /g, '+') + '&format=long&sort=name',
      data: '',
    },
    function (response) {
      if (response != 'error') {
        var polyratingPage = $($.parseHTML(response));
        try {
          var rating = polyratingPage.find('.hidden-xs span .text-primary')[0].innerText;
          var profId = response.match(/profid=(\d+)/)[1];
          var href = 'http://polyratings.com/eval.php?profid=' + profId;
          var numericalRating = parseFloat(rating, 10);
          nameElem.css('background', calculateBgRGBA(numericalRating));
          nameElem.html('' + nameElem.html() + '
' + rating + '');

        } catch (e) {
          getPolyratingData2ndAttempt(nameElem, lastName);
        }
      } else {
        getPolyratingData2ndAttempt(nameElem, lastName);
      }
    }
  );
}

function getPolyratingData2ndAttempt(nameElem, lastName) {
  chrome.runtime.sendMessage(
    {
      method: 'GET',
      action: 'xhttp',
      url: 'http://polyratings.com/search.php?type=ProfName&terms=' + lastName + '&format=long&sort=name',
      data: '',
    },
    function (response) {
      if (response != 'error') {
        var polyratingPage = $($.parseHTML(response));
        try {
          var rating = polyratingPage.find('.hidden-xs span .text-primary')[0].innerText;
          var profId = response.match(/profid=(\d+)/)[1];
          var href = 'http://polyratings.com/eval.php?profid=' + profId;
          var numericalRating = parseFloat(rating, 10);
          nameElem.css('background', calculateBgRGBA(numericalRating));
          nameElem.html('' + nameElem.html() + '
' + rating + '');
        } catch (e) {
          addLinkToSearchPage(nameElem, lastName);
        }
      } else {
        addLinkToSearchPage(nameElem, lastName);
      }
    }
  );
}

function onlyFirstName(name) {
  var spaceIndex = name.indexOf(' ');
  return spaceIndex > -1 ? name.substr(0, spaceIndex) : name;
}

function removeAllSingleLetters(name) {
  var modified = '';
  var parts = name.split(' ');
  parts.forEach(function (part) {
    if (/[a-z]{2,}/.test(part)) {
      modified += ' ' + part;
    }
  });
  return modified.trim();
}

function calculateBgRGBA(rating) {
  var r = rating < 3 ? 255 : 255 * (4 - rating) / 4;
  var g = rating < 3 ? 255 * rating / 3 : 255;
  return 'rgba(' + Math.round(r) + ',' + Math.round(g) + ',0,0.6)';
}

function addLinkToSearchPage(nameElem, lastName) {
  var href = 'http://polyratings.com/search.php?type=ProfName&terms=' + lastName + '&format=long&sort=name';
  nameElem.html('' + nameElem.html() + '');
}
<commit_msg>add evals count<commit_after>$('.select-course table tbody tr .sectionNumber').each(function () {
  var nameElem = $(this).next().next().next();
  getPolyratingData(nameElem);
});

function getPolyratingData(nameElem) {
  if (nameElem[0].innerText === 'STAFF') {
    return;
  }
  var names = nameElem[0].innerText.split(',');
  var lastName = removeAllSingleLetters(names[0].trim());
  var firstName = onlyFirstName(removeAllSingleLetters(names[1].trim()));
  var name = firstName + ' ' + lastName;
  chrome.runtime.sendMessage(
    {
      method: 'GET',
      action: 'xhttp',
      url: 'http://polyratings.com/search.php?type=ProfName&terms=' + name.replace(/ /g, '+') + '&format=long&sort=name',
      data: '',
    },
    function (response) {
      if (response != 'error') {
        try {
          var polyratingPage = $($.parseHTML(response));
          var ratingElem = polyratingPage.find('.hidden-xs span .text-primary');
          var rating = ratingElem[0].innerText;
          var evals = ratingElem.next()[0].innerText.replace('uation', '');
          var profId = response.match(/profid=(\d+)/)[1];
          var href = 'http://polyratings.com/eval.php?profid=' + profId;
          var numericalRating = parseFloat(rating, 10);
          nameElem.css('background', calculateBgRGBA(numericalRating));
          nameElem.html('' + nameElem.html() + '
' + rating + ' (' + evals + ')');

        } catch (e) {
          getPolyratingData2ndAttempt(nameElem, lastName);
        }
      } else {
        getPolyratingData2ndAttempt(nameElem, lastName);
      }
    }
  );
}

function getPolyratingData2ndAttempt(nameElem, lastName) {
  chrome.runtime.sendMessage(
    {
      method: 'GET',
      action: 'xhttp',
      url: 'http://polyratings.com/search.php?type=ProfName&terms=' + lastName + '&format=long&sort=name',
      data: '',
    },
    function (response) {
      if (response != 'error') {
        try {
          var polyratingPage = $($.parseHTML(response));
          var ratingElem = polyratingPage.find('.hidden-xs span .text-primary');
          var rating = ratingElem[0].innerText;
          var profId = response.match(/profid=(\d+)/)[1];
          var evals = ratingElem.next()[0].innerText.replace('uation', '');
          var href = 'http://polyratings.com/eval.php?profid=' + profId;
          var numericalRating = parseFloat(rating, 10);
          nameElem.css('background', calculateBgRGBA(numericalRating));
          nameElem.html('' + nameElem.html() + '
' + rating + ' (' + evals + ')');
        } catch (e) {
          addLinkToSearchPage(nameElem, lastName);
        }
      } else {
        addLinkToSearchPage(nameElem, lastName);
      }
    }
  );
}

function onlyFirstName(name) {
  var spaceIndex = name.indexOf(' ');
  return spaceIndex > -1 ? name.substr(0, spaceIndex) : name;
}

function removeAllSingleLetters(name) {
  var modified = '';
  var parts = name.split(' ');
  parts.forEach(function (part) {
    if (/[a-z]{2,}/.test(part)) {
      modified += ' ' + part;
    }
  });
  return modified.trim();
}

function calculateBgRGBA(rating) {
  var r = rating < 3 ? 255 : 255 * (4 - rating) / 4;
  var g = rating < 3 ? 255 * rating / 3 : 255;
  return 'rgba(' + Math.round(r) + ',' + Math.round(g) + ',0,0.6)';
}

function addLinkToSearchPage(nameElem, lastName) {
  var href = 'http://polyratings.com/search.php?type=ProfName&terms=' + lastName + '&format=long&sort=name';
  nameElem.html('' + nameElem.html() + '');
}
<|endoftext|>


<commit_before>var pkg = require('./package.json');
var config = require('./config.js');
var log = require('yalm');

log(pkg.name + ' ' + pkg.version + ' starting');
var Mqtt = require('mqtt');

log.info('loading HomeKit to MQTT mapping file');
var mapping = require(config.m);

var mqttStatus = {};
var mqttCallbacks = {};

var mqttConnected;

log.info('mqtt trying to connect', config.url);
var mqtt = Mqtt.connect(config.url, {will: {topic: config.name + '/connected', payload: '0'}});

mqtt.on('connect', function () {
    mqttConnected = true;
    log.info('mqtt connected ' + config.url);
    mqtt.publish(config.name + '/connected', '2');
    log.info('mqtt subscribe', config.name + '/set/#');
    mqtt.subscribe(config.name + '/set/#');
});

mqtt.on('close', function () {
    if (mqttConnected) {
        mqttConnected = false;
        log.info('mqtt closed ' + config.url);
    }
});

mqtt.on('error', function () {
    log.error('mqtt error ' + config.url);
});

mqtt.on('message', function (topic, payload) {
    payload = payload.toString();
    var state;
    try {
        state = JSON.parse(payload).val;
    } catch (e) {
        if (payload === 'true') {
            state = true;
        } else if (payload === 'false') {
            state = false;
        } else if (!isNaN(payload)) {
            state = parseFloat(payload);
        } else {
            state = payload;
        }
    }
    console.log('< mqtt', topic, state);
    mqttStatus[topic] = state;
    if (mqttCallbacks[topic]) {
        mqttCallbacks[topic].forEach(function (cb) {
            cb(state);
        });
    }
});

function mqttSub(topic, callback) {
    if (!mqttCallbacks[topic]) {
        mqttCallbacks[topic] = [callback];
    } else {
        mqttCallbacks[topic].push(callback);
    }
}

console.log("HAP-NodeJS starting...");
var HAP =               require('HAP-NodeJS');
var uuid =              HAP.uuid;
var Bridge =            HAP.Bridge;
var Accessory =         HAP.Accessory;
var Service =           HAP.Service;
var Characteristic =    HAP.Characteristic;

var storage =   require('HAP-NodeJS/node_modules/node-persist');
var types =     require('HAP-NodeJS/accessories/types');

// Initialize our storage system
storage.initSync();

// Start by creating our Bridge which will host all loaded Accessories
var bridge = new Bridge('MQTT Bridge', uuid.generate("MQTT Bridge"));

// Listen for bridge identification event
bridge.on('identify', function (paired, callback) {
    console.log("Node Bridge identify", paired);
    callback(); // success
});

function identify(settings, paired, callback) {
    console.log('< hap', settings.name, 'identify', paired);
    if (settings.topic.identify) {
        console.log('> mqtt', settings.topic.identify, settings.payload.identify);
        mqtt.publish(settings.topic.identify, settings.payload.identify);
    }
    callback();
}

function setInfos(acc, settings) {
    if (settings.manufacturer || settings.model || settings.serial) {
        acc.getService(Service.AccessoryInformation)
            .setCharacteristic(Characteristic.Manufacturer, settings.manufacturer || "-")
            .setCharacteristic(Characteristic.Model, settings.model || "-" )
            .setCharacteristic(Characteristic.SerialNumber, settings.serial || "-");
    }
}

var createAccessory = {
    LockMechanism: function createAccessory_LockMechanism(settings) {

        var lockUUID = uuid.generate('hap-nodejs:accessories:lock:' + settings.topic.setLock);

        var lock = new Accessory(settings.name, lockUUID);

        setInfos(lock, settings);

        lock.on('identify', function (paired, callback) {
            identify(settings, paired, callback);
        });

        lock.addService(Service.LockMechanism, settings.name)
            .getCharacteristic(Characteristic.LockTargetState)
            .on('set', function(value, callback) {
                console.log('< hap', settings.name, 'set', 'LockTargetState', value);

                if (value == Characteristic.LockTargetState.UNSECURED) {
                    console.log('> mqtt publish', settings.topic.setLock, settings.payload.lockUnsecured);
                    mqtt.publish(settings.topic.setLock, settings.payload.lockUnsecured);

                    callback();

                } else if (value == Characteristic.LockTargetState.SECURED) {

                    console.log('> mqtt publish', settings.topic.setLock, settings.payload.lockSecured);
                    mqtt.publish(settings.topic.setLock, settings.payload.lockSecured);

                    callback();

                }
            });

        if (settings.topic.statusLock) {

            log.info('> mqtt subscribe', settings.topic.statusLock);
            mqttSub(settings.topic.statusLock, function (val) {

                if (val === settings.payload.lockSecured) {
                    lock.getService(Service.LockMechanism)
                        .setCharacteristic(Characteristic.LockCurrentState, Characteristic.LockCurrentState.SECURED);
                }
                if (val === settings.payload.lockUnsecured) {
                    lock.getService(Service.LockMechanism)
                        .setCharacteristic(Characteristic.LockCurrentState, Characteristic.LockCurrentState.UNSECURED);
                }

            });

            lock.getService(Service.LockMechanism)
                .getCharacteristic(Characteristic.LockCurrentState)
                .on('get', function(callback) {
                    console.log('< hap', settings.name, 'get', 'LockCurrentState');

                    if (mqttStatus[settings.topic.statusLock] === settings.payload.lockSecured) {
                        console.log('> hap', settings.name, 'LockCurrentState.SECURED');
                        callback(null, Characteristic.LockCurrentState.SECURED);
                    } else {
                        console.log('> hap', settings.name, 'LockCurrentState.UNSECURED');
                        callback(null, Characteristic.LockCurrentState.UNSECURED);
                    }
                });

        }

        return lock;

    },
    TemperatureSensor: function createAccessory_TemperatureSensor(settings) {

        var sensorUUID = uuid.generate('hap-nodejs:accessories:temperature-sensor:' + settings.topic.statusTemperature);
        var sensor = new Accessory(settings.name, sensorUUID);
        setInfos(sensor, settings);

        log.info('> mqtt subscribe', settings.topic.statusTemperature);
        mqtt.subscribe(settings.topic.statusTemperature);

        sensor.addService(Service.TemperatureSensor)
            .getCharacteristic(Characteristic.CurrentTemperature)
            .on('get', function(callback) {
                console.log('< hap', settings.name, 'get', 'TemperatureSensor', 'CurrentTemperature');
                console.log('> hap', settings.name, mqttStatus[settings.topic.statusTemperature]);
                callback(null, mqttStatus[settings.topic.statusTemperature]);
            });

        return sensor;
    },
    Lightbulb: function createAccessory_Lightbulb(settings) {

        var lightUUID = uuid.generate('hap-nodejs:accessories:light:' + settings.topic.setOn);
        var light = new Accessory(settings.name, lightUUID);
        setInfos(light, settings);

        light.on('identify', function (paired, callback) {
            identify(settings, paired, callback);
        });

        light.addService(Service.Lightbulb, settings.name)
            .getCharacteristic(Characteristic.On)
            .on('set', function(value, callback) {
                console.log('< hap', settings.name, 'set', 'On', value);
                var on = value ? settings.payload.onTrue : settings.payload.onFalse;
                console.log('> mqtt', settings.topic.setOn, on);
                mqtt.publish(settings.topic.setOn, '' + on);
                callback();
            });

        light.getService(Service.Lightbulb)
            .getCharacteristic(Characteristic.On)
            .on('get', function (callback) {
                console.log('< hap', settings.name, 'get', 'On');
                var on = mqttStatus[settings.topic.statusOn] === settings.payload.onTrue;

                console.log('> hap', settings.name, on);
                callback(null, on);
            });

        if (settings.topic.setBrightness) {
            light.getService(Service.Lightbulb)
                .addCharacteristic(Characteristic.Brightness)
                .on('set', function (value, callback) {
                    console.log('< hap', settings.name, 'set', 'Brightness', value);
                    var bri = (value * (settings.payload.brightnessFactor || 1));
                    console.log('> mqtt', settings.topic.setBrightness, bri);
                    mqtt.publish(settings.topic.setBrightness, '' + bri);
                    callback();
                });

            if (settings.topic.statusBrightness) {
                console.log('mqtt subscribe', settings.topic.statusBrightness);
                mqtt.subscribe(settings.topic.statusBrightness);
                light.getService(Service.Lightbulb)
                    .getCharacteristic(Characteristic.Brightness)
                    .on('get', function (callback) {
                        console.log('< hap', settings.name, 'get', 'Brightness');
                        var brightness = mqttStatus[settings.topic.statusBrightness] / settings.payload.brightnessFactor;

                        console.log('> hap', settings.name, brightness);
                        callback(null, brightness);
                    });

            }

        }

        if (settings.topic.setHue) {
            light.getService(Service.Lightbulb)
                .addCharacteristic(Characteristic.Hue)
                .on('set', function (value, callback) {
                    console.log('< hap', settings.name, 'set', 'Hue', value);
                    console.log('> mqtt', settings.topic.setHue, '' + (value * (settings.payload.hueFactor || 1)));
                    mqtt.publish(settings.topic.setHue, '' + (value * (settings.payload.hueFactor || 1)));
                    callback();
                });
            if (settings.topic.statusHue) {
                console.log('mqtt subscribe', settings.topic.statusHue);
                mqtt.subscribe(settings.topic.statusHue);
                light.getService(Service.Lightbulb)
                    .getCharacteristic(Characteristic.Hue)
                    .on('get', function (callback) {
                        console.log('< hap', settings.name, 'get', 'Hue');
                        var hue = mqttStatus[settings.topic.statusHue] / settings.payload.hueFactor;

                        console.log('> hap', settings.name, hue);
                        callback(null, hue);
                    });

            }
        }

        if (settings.topic.setSaturation) {
            light.getService(Service.Lightbulb)
                .addCharacteristic(Characteristic.Saturation)
                 .on('set', function (value, callback) {
                    console.log('< hap', settings.name, 'set', 'Saturation', value);
                    var sat = (value * (settings.payload.saturationFactor || 1));
                    console.log('> mqtt', settings.topic.setSaturation, sat);
                    mqtt.publish(settings.topic.setSaturation, '' + sat);
                    callback();
                });
            if (settings.topic.statusSaturation) {
                console.log('mqtt subscribe', settings.topic.statusSaturation);
                mqtt.subscribe(settings.topic.statusSaturation);
                light.getService(Service.Lightbulb)
                    .getCharacteristic(Characteristic.Saturation)
                    .on('get', function (callback) {
                        console.log('< hap', settings.name, 'get', 'Saturation');
                        var saturation = mqttStatus[settings.topic.statusSaturation] / settings.payload.saturationFactor;

                        console.log('> hap', settings.name, saturation);
                        callback(null, saturation);
                    });

            }
        }

        return light;

    },
    Switch: function createAccessory_Switch(settings) {

        var switchUUID = uuid.generate('hap-nodejs:accessories:switch:' + settings.topic.setOn);
        var sw = new Accessory(settings.name, switchUUID);
        setInfos(sw, settings);

        sw.on('identify', function (paired, callback) {
            identify(settings, paired, callback);
        });

        sw.addService(Service.Switch, settings.name)
            .getCharacteristic(Characteristic.On)
            .on('set', function(value, callback) {
                console.log('< hap', settings.name, 'set', 'On', value);
                var on = value ? settings.payload.onTrue : settings.payload.onFalse;
                console.log('> mqtt', settings.topic.setOn, on);
                mqtt.publish(settings.topic.setOn, '' + on);
                powerOn = value;
                callback();
            });

        if (settings.topic.statusOn) {
            console.log('mqtt subscribe', settings.topic.statusOn);
            mqtt.subscribe(settings.topic.statusOn);
            sw.getService(Service.Switch)
                .getCharacteristic(Characteristic.On)
                .on('get', function (callback) {
                    console.log('< hap', settings.name, 'get', 'On');
                    var on = mqttStatus[settings.topic.statusOn] === settings.payload.onTrue;
                    console.log('> hap', settings.name, on);
                    callback(null, on);
                });

        }

        return sw;

    }
};

for (var id in mapping) {
    var a = mapping[id];

    if (createAccessory[a.service]) {
        bridge.addBridgedAccessory(createAccessory[a.service](a));
    } else {
        log.err('unknown service', a.service, id);
    }
}

// Publish the Bridge on the local network.
bridge.publish({
    username: "CC:22:3D:E3:CE:F6",
    port: 51826,
    pincode: "031-45-154",
    category: Accessory.Categories.OTHER
});

<commit_msg>added services WindowCovering and ContactSensor<commit_after>var pkg = require('./package.json');
var config = require('./config.js');
var log = require('yalm');

log(pkg.name + ' ' + pkg.version + ' starting');
var Mqtt = require('mqtt');

log.info('loading HomeKit to MQTT mapping file');
var mapping = require(config.m);

var mqttStatus = {};
var mqttCallbacks = {};

var mqttConnected;

log.info('mqtt trying to connect', config.url);
var mqtt = Mqtt.connect(config.url, {will: {topic: config.name + '/connected', payload: '0'}});

mqtt.on('connect', function () {
    mqttConnected = true;
    log.info('mqtt connected ' + config.url);
    mqtt.publish(config.name + '/connected', '2');
    log.info('mqtt subscribe', config.name + '/set/#');
    mqtt.subscribe(config.name + '/set/#');
});

mqtt.on('close', function () {
    if (mqttConnected) {
        mqttConnected = false;
        log.info('mqtt closed ' + config.url);
    }
});

mqtt.on('error', function () {
    log.error('mqtt error ' + config.url);
});

mqtt.on('message', function (topic, payload) {
    payload = payload.toString();
    var state;
    try {
        state = JSON.parse(payload).val;
    } catch (e) {
        if (payload === 'true') {
            state = true;
        } else if (payload === 'false') {
            state = false;
        } else if (!isNaN(payload)) {
            state = parseFloat(payload);
        } else {
            state = payload;
        }
    }
    console.log('< mqtt', topic, state);
    mqttStatus[topic] = state;
    if (mqttCallbacks[topic]) {
        mqttCallbacks[topic].forEach(function (cb) {
            cb(state);
        });
    }
});

function mqttSub(topic, callback) {
    if (!mqttCallbacks[topic]) {
        mqttCallbacks[topic] = [callback];
    } else {
        mqttCallbacks[topic].push(callback);
    }
}

console.log("HAP-NodeJS starting...");
var HAP =               require('HAP-NodeJS');
var uuid =              HAP.uuid;
var Bridge =            HAP.Bridge;
var Accessory =         HAP.Accessory;
var Service =           HAP.Service;
var Characteristic =    HAP.Characteristic;

var storage =   require('HAP-NodeJS/node_modules/node-persist');
var types =     require('HAP-NodeJS/accessories/types');

// Initialize our storage system
storage.initSync();

// Start by creating our Bridge which will host all loaded Accessories
var bridge = new Bridge('MQTT Bridge', uuid.generate("MQTT Bridge"));

// Listen for bridge identification event
bridge.on('identify', function (paired, callback) {
    console.log("Node Bridge identify", paired);
    callback(); // success
});

function identify(settings, paired, callback) {
    console.log('< hap', settings.name, 'identify', paired);
    if (settings.topic.identify) {
        console.log('> mqtt', settings.topic.identify, settings.payload.identify);
        mqtt.publish(settings.topic.identify, settings.payload.identify);
    }
    callback();
}

function setInfos(acc, settings) {
    if (settings.manufacturer || settings.model || settings.serial) {
        acc.getService(Service.AccessoryInformation)
            .setCharacteristic(Characteristic.Manufacturer, settings.manufacturer || "-")
            .setCharacteristic(Characteristic.Model, settings.model || "-" )
            .setCharacteristic(Characteristic.SerialNumber, settings.serial || "-");
    }
}

var createAccessory = {

    WindowCovering: function createAccessory_WindowCovering(settings) {
        var shutterUUID = uuid.generate('hap-nodejs:accessories:windowCovering:' + settings.topic.setTargetPosition);
        var shutter = new Accessory(settings.name, shutterUUID);
        setInfos(shutter, settings);

        shutter.addService(Service.WindowCovering, settings.name)
            .getCharacteristic(Characteristic.TargetPosition)
            .on('set', function (value, callback) {
                console.log('< hap', settings.name, 'set', 'TargetPosition', value);
                value = (value * (settings.payload.targetPositionFactor || 1));
                console.log('> mqtt', settings.topic.setTargetPosition, value);
                mqtt.publish(settings.topic.setTargetPosition, '' + value);
                callback();
            });

        if (settings.topic.statusTargetPosition) {
            console.log('mqtt subscribe', settings.topic.statusTargetPosition);
            mqtt.subscribe(settings.topic.statusTargetPosition);
            shutter.getService(Service.WindowCovering)
                .getCharacteristic(Characteristic.TargetPosition)
                .on('get', function (callback) {
                    console.log('< hap', settings.name, 'get', 'TargetPosition');
                    var position = mqttStatus[settings.topic.statusTargetPosition] / (settings.payload.targetPositionFactor || 1);

                    console.log('> hap', settings.name, position);
                    callback(null, position);
                });

        }

        if (settings.topic.statusCurrentPosition) {
            console.log('mqtt subscribe', settings.topic.statusCurrentPosition);
            mqtt.subscribe(settings.topic.statusCurrentPosition);
            shutter.getService(Service.WindowCovering)
                .getCharacteristic(Characteristic.CurrentPosition)
                .on('get', function (callback) {
                    console.log('< hap', settings.name, 'get', 'CurrentPosition');
                    var position = mqttStatus[settings.topic.statusCurrentPosition] / (settings.payload.currentPositionFactor || 1);

                    console.log('> hap', settings.name, position);
                    callback(null, position);
                });

        }

        shutter.getService(Service.WindowCovering)
            .getCharacteristic(Characteristic.CurrentPosition)
            .on('set', function (value, callback) {
                console.log('< hap', settings.name, 'set', 'CurrentPosition', value);
                value = (value * (settings.payload.currentPositionFactor || 1));
                console.log('> mqtt', settings.topic.setCurrentPosition, value);
                mqtt.publish(settings.topic.setCurrentPosition, '' + value);
                callback();
            });

        if (settings.topic.statusPositionStatus) {
            console.log('mqtt subscribe', settings.topic.statusPositionStatus);
            mqtt.subscribe(settings.topic.statusPositionStatus);
            shutter.getService(Service.WindowCovering)
                .getCharacteristic(Characteristic.PositionState)
                .on('get', function (callback) {
                    console.log('< hap', settings.name, 'get', 'PositionState');

                    if (mqttStatus[settings.topic.statusPositionState] === settings.payload.positionStatusDecreasing) {
                        console.log('> hap', settings.name, 'PositionState.DECREASING');
                        callback(null, Characteristic.PositionState.DECREASING);
                    } else if (mqttStatus[settings.topic.statusPositionState] === settings.payload.positionStatusIncreasing) {
                        console.log('> hap', settings.name, 'PositionState.INCREASING');
                        callback(null,  Characteristic.PositionState.INCREASING);
                    } else {
                        console.log('> hap', settings.name, 'PositionState.STOPPED');
                        callback(null, Characteristic.PositionState.STOPPED);
                    }

                });

        }

        return shutter;

    },
    LockMechanism: function createAccessory_LockMechanism(settings) {

        var lockUUID = uuid.generate('hap-nodejs:accessories:lock:' + settings.topic.setLock);

        var lock = new Accessory(settings.name, lockUUID);

        setInfos(lock, settings);

        lock.on('identify', function (paired, callback) {
            identify(settings, paired, callback);
        });

        lock.addService(Service.LockMechanism, settings.name)
            .getCharacteristic(Characteristic.LockTargetState)
            .on('set', function(value, callback) {
                console.log('< hap', settings.name, 'set', 'LockTargetState', value);

                if (value == Characteristic.LockTargetState.UNSECURED) {
                    console.log('> mqtt publish', settings.topic.setLock, settings.payload.lockUnsecured);
                    mqtt.publish(settings.topic.setLock, settings.payload.lockUnsecured);

                    callback();

                } else if (value == Characteristic.LockTargetState.SECURED) {

                    console.log('> mqtt publish', settings.topic.setLock, settings.payload.lockSecured);
                    mqtt.publish(settings.topic.setLock, settings.payload.lockSecured);

                    callback();

                }
            });

        if (settings.topic.statusLock) {

            log.info('> mqtt subscribe', settings.topic.statusLock);
            mqttSub(settings.topic.statusLock, function (val) {

                if (val === settings.payload.lockSecured) {
                    lock.getService(Service.LockMechanism)
                        .setCharacteristic(Characteristic.LockCurrentState, Characteristic.LockCurrentState.SECURED);
                }
                if (val === settings.payload.lockUnsecured) {
                    lock.getService(Service.LockMechanism)
                        .setCharacteristic(Characteristic.LockCurrentState, Characteristic.LockCurrentState.UNSECURED);
                }

            });

            lock.getService(Service.LockMechanism)
                .getCharacteristic(Characteristic.LockCurrentState)
                .on('get', function(callback) {
                    console.log('< hap', settings.name, 'get', 'LockCurrentState');

                    if (mqttStatus[settings.topic.statusLock] === settings.payload.lockSecured) {
                        console.log('> hap', settings.name, 'LockCurrentState.SECURED');
                        callback(null, Characteristic.LockCurrentState.SECURED);
                    } else {
                        console.log('> hap', settings.name, 'LockCurrentState.UNSECURED');
                        callback(null, Characteristic.LockCurrentState.UNSECURED);
                    }
                });

        }

        return lock;

    },
    TemperatureSensor: function createAccessory_TemperatureSensor(settings) {

        var sensorUUID = uuid.generate('hap-nodejs:accessories:temperature-sensor:' + settings.topic.statusTemperature);
        var sensor = new Accessory(settings.name, sensorUUID);
        setInfos(sensor, settings);

        log.info('> mqtt subscribe', settings.topic.statusTemperature);
        mqtt.subscribe(settings.topic.statusTemperature);

        sensor.addService(Service.TemperatureSensor)
            .getCharacteristic(Characteristic.CurrentTemperature)
            .on('get', function(callback) {
                console.log('< hap', settings.name, 'get', 'TemperatureSensor', 'CurrentTemperature');
                console.log('> hap', settings.name, mqttStatus[settings.topic.statusTemperature]);
                callback(null, mqttStatus[settings.topic.statusTemperature]);
            });

        return sensor;
    },
    Lightbulb: function createAccessory_Lightbulb(settings) {

        var lightUUID = uuid.generate('hap-nodejs:accessories:light:' + settings.topic.setOn);
        var light = new Accessory(settings.name, lightUUID);
        setInfos(light, settings);

        light.on('identify', function (paired, callback) {
            identify(settings, paired, callback);
        });

        light.addService(Service.Lightbulb, settings.name)
            .getCharacteristic(Characteristic.On)
            .on('set', function(value, callback) {
                console.log('< hap', settings.name, 'set', 'On', value);
                var on = value ? settings.payload.onTrue : settings.payload.onFalse;
                console.log('> mqtt', settings.topic.setOn, on);
                mqtt.publish(settings.topic.setOn, '' + on);
                callback();
            });

        light.getService(Service.Lightbulb)
            .getCharacteristic(Characteristic.On)
            .on('get', function (callback) {
                console.log('< hap', settings.name, 'get', 'On');
                var on = mqttStatus[settings.topic.statusOn] === settings.payload.onTrue;

                console.log('> hap', settings.name, on);
                callback(null, on);
            });

        if (settings.topic.setBrightness) {
            light.getService(Service.Lightbulb)
                .addCharacteristic(Characteristic.Brightness)
                .on('set', function (value, callback) {
                    console.log('< hap', settings.name, 'set', 'Brightness', value);
                    var bri = (value * (settings.payload.brightnessFactor || 1));
                    console.log('> mqtt', settings.topic.setBrightness, bri);
                    mqtt.publish(settings.topic.setBrightness, '' + bri);
                    callback();
                });

            if (settings.topic.statusBrightness) {
                console.log('mqtt subscribe', settings.topic.statusBrightness);
                mqtt.subscribe(settings.topic.statusBrightness);
                light.getService(Service.Lightbulb)
                    .getCharacteristic(Characteristic.Brightness)
                    .on('get', function (callback) {
                        console.log('< hap', settings.name, 'get', 'Brightness');
                        var brightness = mqttStatus[settings.topic.statusBrightness] / settings.payload.brightnessFactor;

                        console.log('> hap', settings.name, brightness);
                        callback(null, brightness);
                    });

            }

        }

        if (settings.topic.setHue) {
            light.getService(Service.Lightbulb)
                .addCharacteristic(Characteristic.Hue)
                .on('set', function (value, callback) {
                    console.log('< hap', settings.name, 'set', 'Hue', value);
                    console.log('> mqtt', settings.topic.setHue, '' + (value * (settings.payload.hueFactor || 1)));
                    mqtt.publish(settings.topic.setHue, '' + (value * (settings.payload.hueFactor || 1)));
                    callback();
                });
            if (settings.topic.statusHue) {
                console.log('mqtt subscribe', settings.topic.statusHue);
                mqtt.subscribe(settings.topic.statusHue);
                light.getService(Service.Lightbulb)
                    .getCharacteristic(Characteristic.Hue)
                    .on('get', function (callback) {
                        console.log('< hap', settings.name, 'get', 'Hue');
                        var hue = mqttStatus[settings.topic.statusHue] / settings.payload.hueFactor;

                        console.log('> hap', settings.name, hue);
                        callback(null, hue);
                    });

            }
        }

        if (settings.topic.setSaturation) {
            light.getService(Service.Lightbulb)
                .addCharacteristic(Characteristic.Saturation)
                 .on('set', function (value, callback) {
                    console.log('< hap', settings.name, 'set', 'Saturation', value);
                    var sat = (value * (settings.payload.saturationFactor || 1));
                    console.log('> mqtt', settings.topic.setSaturation, sat);
                    mqtt.publish(settings.topic.setSaturation, '' + sat);
                    callback();
                });
            if (settings.topic.statusSaturation) {
                console.log('mqtt subscribe', settings.topic.statusSaturation);
                mqtt.subscribe(settings.topic.statusSaturation);
                light.getService(Service.Lightbulb)
                    .getCharacteristic(Characteristic.Saturation)
                    .on('get', function (callback) {
                        console.log('< hap', settings.name, 'get', 'Saturation');
                        var saturation = mqttStatus[settings.topic.statusSaturation] / settings.payload.saturationFactor;

                        console.log('> hap', settings.name, saturation);
                        callback(null, saturation);
                    });

            }
        }

        return light;

    },
    Switch: function createAccessory_Switch(settings) {

        var switchUUID = uuid.generate('hap-nodejs:accessories:switch:' + settings.topic.setOn);
        var sw = new Accessory(settings.name, switchUUID);
        setInfos(sw, settings);

        sw.addService(Service.Switch, settings.name)
            .getCharacteristic(Characteristic.On)
            .on('set', function(value, callback) {
                console.log('< hap', settings.name, 'set', 'On', value);
                var on = value ? settings.payload.onTrue : settings.payload.onFalse;
                console.log('> mqtt', settings.topic.setOn, on);
                mqtt.publish(settings.topic.setOn, '' + on);
                powerOn = value;
                callback();
            });

        if (settings.topic.statusOn) {
            console.log('mqtt subscribe', settings.topic.statusOn);
            mqtt.subscribe(settings.topic.statusOn);
            sw.getService(Service.Switch)
                .getCharacteristic(Characteristic.On)
                .on('get', function (callback) {
                    console.log('< hap', settings.name, 'get', 'On');
                    var on = mqttStatus[settings.topic.statusOn] === settings.payload.onTrue;
                    console.log('> hap', settings.name, on);
                    callback(null, on);
                });

        }

        return sw;

    },
    ContactSensor: function createAccessory_Switch(settings) {

        var switchUUID = uuid.generate('hap-nodejs:accessories:contactSensor:' + settings.topic.statusContactSensorState);
        var sensor = new Accessory(settings.name, switchUUID);
        setInfos(sensor, settings);

        sensor.addService(Service.ContactSensor, settings.name)
            .getCharacteristic(Characteristic.ContactSensorState)
            .on('get', function (callback) {
                console.log('< hap', settings.name, 'get', 'ContactSensorState');
                var contact =
                    mqttStatus[settings.topic.statusContactSensorState] === settings.payload.onContactDetected ?
                        Characteristic.ContactSensorState.CONTACT_DETECTED :
                        Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
                console.log('> hap', settings.name, contact);
                callback(null, contact);
            });

        return sensor;

    }
};

for (var id in mapping) {
    var a = mapping[id];

    if (createAccessory[a.service]) {
        bridge.addBridgedAccessory(createAccessory[a.service](a));
    } else {
        log.err('unknown service', a.service, id);
    }
}

// Publish the Bridge on the local network.
bridge.publish({
    username: "CC:22:3D:E3:CE:F6",
    port: 51826,
    pincode: "031-45-154",
    category: Accessory.Categories.OTHER
});

<|endoftext|>


<commit_before>'use strict';

var AWS = require("aws-sdk");

let channel_id;

var docClient = new AWS.DynamoDB.DocumentClient();

Array.prototype.countBy = function (key) {
    return this.reduce(function (rv, x) {
        rv[x[key]] = (rv[x[key]] || 0) + 1;
        return rv;
    }, {});
};

Date.prototype.getWeek = function () {
    var onejan = new Date(this.getFullYear(), 0, 1);
    return Math.ceil((((this - onejan) / 86400000) + onejan.getDay() + 1) / 7);
};

Date.prototype.previousWeek = function () {
    return new Date(this.getTime() - (7 * 24 * 60 * 60 * 1000));
};

function forWeek(week) {
    var expression = "#wk = :week";
    var attrs = { ":week": week };
    if (channel_id) {
        expression += " and channel_id = :chan";
        attrs[":chan"] = channel_id;
    }

    return {
        TableName: "MoodResponses",
        IndexName: 'week-channel_id-index',
        KeyConditionExpression: expression,
        ExpressionAttributeNames: {
            "#wk": "week"
        },
        ExpressionAttributeValues: attrs
    };
}

function handleError(err, callback) {
    console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
    callback(null, { "error": JSON.stringify(err, null, 2) });
}

function happinessMetric(moods) {
    var good = moods.good || 0;
    var bad = moods.bad || 0;
    var awesome = moods.awesome || 0;
    var terrible = moods.terrible || 0;

    var score = good + (bad * -1) + (awesome * 5) + (terrible * -5);
    var count = good + bad + awesome + terrible + (moods.meh || 0);

    return Math.round(score * 100 / count);
}

function overallMood(happiness) {
    if (happiness > 200)
        return "awesome";
    else if (happiness < -200)
        return "terrible";
    else if (happiness > 50)
        return "good";
    else if (happiness < -50)
        return "bad";
    else
        return "meh";
}

function handleData(data, callback, week) {
    console.log("Query succeeded.");

    var moods = data.Items.countBy("mood");
    var happiness = happinessMetric(moods);
    var results = {
        week: week,
        moods: moods,
        happiness: happiness,
        teamMood: overallMood(happiness),
        channel_id: channel_id
    };

    callback(null, results);
}

function runFor(date, tries, callback) {
    var week = date.getWeek();
    console.log('Fetching mood results for week: ' + week);

    docClient.query(forWeek(week), function (err, data) {
        if (err) {
            handleError(err, callback);
        } else if (data.Items.length > 0 || tries <= 0) {
            handleData(data, callback, week);
        } else {
            runFor(date.previousWeek(), tries - 1, callback);
        }
    });
}

exports.handler = function (event, context, callback) {
    channel_id = event.channel_id;

    runFor(new Date(), 1, callback);
};<commit_msg>Changed query to scan to get around timeout issue in lambda. No idea why that works<commit_after>'use strict';

var AWS = require("aws-sdk");

let channel_id;

var docClient = new AWS.DynamoDB.DocumentClient();

Array.prototype.countBy = function(key) {
  return this.reduce(function(rv, x) {
    rv[x[key]] = (rv[x[key]] || 0) + 1;
    return rv;
  }, {});
};

Date.prototype.getWeek = function() {
    var onejan = new Date(this.getFullYear(), 0, 1);
    return Math.ceil((((this - onejan) / 86400000) + onejan.getDay() + 1) / 7);
};

Date.prototype.previousWeek = function() {
    return new Date(this.getTime() - (7 * 24 * 60 * 60 * 1000));
};

function forWeek(week) {
    var expression = "#wk = :week";
    var attrs = { ":week": week };
    if(channel_id) {
        expression += " and channel_id = :chan";
        attrs[":chan"] = channel_id;
    }

    return {
        TableName : "MoodResponses",
        IndexName: 'week-channel_id-index',
        FilterExpression: expression,
        ExpressionAttributeNames:{
            "#wk": "week"
        },
        ExpressionAttributeValues: attrs
    };
}

function handleError(err, callback) {
    console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
    callback(null, {"error": JSON.stringify(err, null, 2)});
}

function happinessMetric(moods) {
    var good = moods.good || 0;
    var bad = moods.bad || 0;
    var awesome = moods.awesome || 0;
    var terrible = moods.terrible || 0;

    var score = good + (bad * -1) + (awesome * 5) + (terrible * -5);
    var count = good + bad + awesome + terrible + (moods.meh || 0);

    return Math.round(score * 100 / count);
}

function overallMood(happiness) {
    if(happiness > 200)
        return "awesome";
    else if(happiness < -200)
        return "terrible";
    else if(happiness > 50)
        return "good";
    else if(happiness < -50)
        return "bad";
    else
        return "meh";
}

function handleData(data, callback, week) {
    console.log("Query succeeded.");

    var moods = data.Items.countBy("mood");
    var happiness = happinessMetric(moods);
    var results = {
        week: week,
        moods: moods,
        happiness: happiness,
        teamMood: overallMood(happiness),
        channel_id: channel_id
    };

    callback(null, results);
}

function runFor(date, tries, callback) {
    var week = date.getWeek();
    console.log('Fetching mood results for week: ' + week);

    docClient.scan(forWeek(week), function(err, data) {
        if (err) {
            handleError(err, callback);
        } else if(data.Items.length > 0 || tries <= 0) {
            handleData(data, callback, week);
        } else {
            runFor(date.previousWeek(), tries - 1, callback);
        }
    });
}

exports.handler = function(event, context, callback) {
    channel_id = event.channel_id;

    runFor(new Date(), 1, callback);
};<|endoftext|>


<commit_before>var inquirer = require('inquirer')
  , Promise = require('bluebird')
  , i18n = require('i18n')
  , _ = require('lodash')

module.exports = function(opts) {
  return new Promise(function(resolve, reject) {

    inquirer.prompt([
      {
        type      : 'list',
        name      : 'role',
        message   :  i18n.__('Role?'),
        default   : 'user',
        choices   : ['admin','user'],
        when      : function() {
          return opts.role === undefined
        }
      },
      {
        type      : 'password',
        name      : 'password',
        message   : i18n.__('Password?'),
        validate  : function(val) {
          return val.length > 0
        },
        when      : function() {
          return opts.password === undefined
        }
      },
      {
        type: 'list',
        name: 'client',
        message: i18n.__('Which client?'),
        choices: ['rtorrent', 'transmission', 'none', ''],
        when: function() {
          return opts.client === undefined
        },
        filter: function() {
          return opts.client == 'none' ? '' : opts.client
        }
      }
    ], function(answers) {
      resolve(_.extend(opts, answers))
    })
  })
}
<commit_msg>fix(useradd): Inquirer client filter<commit_after>var inquirer = require('inquirer')
  , Promise = require('bluebird')
  , i18n = require('i18n')
  , _ = require('lodash')

module.exports = function(opts) {
  return new Promise(function(resolve, reject) {

    inquirer.prompt([
      {
        type      : 'list',
        name      : 'role',
        message   :  i18n.__('Role?'),
        default   : 'user',
        choices   : ['admin','user'],
        when      : function() {
          return opts.role === undefined
        }
      },
      {
        type      : 'password',
        name      : 'password',
        message   : i18n.__('Password?'),
        validate  : function(val) {
          return val.length > 0
        },
        when      : function() {
          return opts.password === undefined
        }
      },
      {
        type: 'list',
        name: 'client',
        message: i18n.__('Which client?'),
        choices: ['rtorrent', 'transmission', 'none', ''],
        when: function() {
          return opts.client === undefined
        },
        filter: function(client) {
          return client == 'none' ? '' : client
        }
      }
    ], function(answers) {
      resolve(_.extend(opts, answers))
    })
  })
}
<|endoftext|>