<commit_before>'use strict'
L20n.Parser = function() {
  let content = null;
  let patterns = {
    id: /^([_a-zA-Z]\w*)/,
    value: /^(["'])([^'"]*)(["'])/,
    ws: /^\s+/
  }

  function parse(string) {
    let lol = {type: 'LOL',
               body: []}
    content = string
    get_ws();
    while (content) {
      lol.body.push(get_entry())
      get_ws();
    }
    return lol
  }

  function get_ws() {
    content = content.replace(patterns['ws'], '')
  }

  function get_entry() {
    let entry
    if (content[0] == '<') {
      content = content.substr(1)
      let id = get_identifier()
      if (content[0] == '(') {
        entry = get_macro(id)
      } else if (content[0] == '[') {
        let index = get_index()
        entry = get_entity(id, index)
      } else {
        entry = get_entity(id);
      }
    } else if (content.substr(0,2) == '/*') {
      entry = get_comment();
    } else if (content.substr(0,6) == 'import') {
      entry = get_importstatement()
    } else {
      throw "ParserError at get_entry"
    }
    return entry
  }

  function get_importstatement() {
    content = content.substr(6)
    get_ws()
    if (content[0] != '(') {
      throw "ParserError"
    }
    content = content.substr(1)
    get_ws()
    let uri = get_string()
    get_ws()
    if (content[0] != ')') {
      throw "ParserError"
    }
    content = content.substr(1)
    let impStmt = {
      type: 'ImportStatement',
      uri: uri
    }
    return impStmt
  }

  function get_identifier() {
    if (content[0] == '~') {
      // this expression
    }
    let match = patterns['id'].exec(content)
    if (!match)
      throw "ParserError"
    content = content.substr(match[0].length)
    let identifier = {type: 'Identifier',
                  name: match[0]}
    return identifier
  }

  function get_entity(id, index) {
    let ch = content[0]
    get_ws();
    if (content[0] == '>') {
      // empty entity
    }
    if (!/\s/g.test(ch)) {
      throw "ParserError at get_entity"
    }
    let value = get_value(true)
    get_ws()
    let attrs = get_attributes()
    let entity = {
      type: 'Entity',
      id: id,
      value: value,
      index: index || [],
      attrs: attrs,
      local: (id.name[0] == '_')
    }
    return entity
  }

  function get_value(none) {
    let c = content[0]
    let value
    if (c == '"' || c == "'") {
      let ccc = content.substr(3)
      let quote = (ccc == '"""' || ccc == "'''")?ccc:c
      //let value = get_string()
      value = get_complex_string(quote)
    }
    return value
  }

  function get_string() {
    let match = patterns['value'].exec(content)
    if (!match) {
      throw "ParserError"
    }
    content = content.substr(match[0].length)
    return {type: 'String', content: match[2]}
  }

  function get_complex_string(quote) {
    let str_end = quote[0]
    let literal = new RegExp("^([^\\\{"+str_end+"]+)")
    let obj = []
    let buffer = ''
    content = content.substr(quote.length)
    let i = 0
    while (content.substr(0, quote.length) != quote) {
      i++;
      if (i>20)
        break
      if (content[0] == str_end) {
        buffer += content[0]
        content = content.substr(1)
      }
      if (content[0] == '\\') {
        let jump = content.substr(1, 3) == '{{' ? 3 : 2;
        buffer += content.substr(1, jump)
        content = content.substr(jump)
      }
      if (content.substr(0, 2) == '{{') {
        content = content.substr(2)
        if (buffer) {
          let string = {type: 'String', content: buffer}
          obj.push(string)
          buffer = ''
        }
        get_ws()
        let expr = get_expression()
        obj.push(expr)
        if (content.substr(0, 2) != '}}') {
          throw "ParserError at get_complex_string"
        }
        content = content.substr(2)
      }
      let m = literal.exec(content)
      if (m) {
        buffer = m[1]
        content = content.substr(m[0].length)
      }
    }
    if (buffer) {
      let string = {type: 'String', content: buffer}
      obj.push(string)
    }
    content = content.substr(quote.length)
    if (obj.length == 1 && obj[0].type == 'String') {
      return obj[0]
    }
    let cs = {type: 'ComplexString', content: obj}
    return cs
  }

  function get_kvp(cl) {
    let key = get_identifier()
    get_ws()
    if (content[0] != ':') {
      throw "ParserError"
    }
    content = content.substr(1)
    get_ws()
    let val = get_value()
    let kvp = {type: cl, key: key, value: val}
    return kvp
  }

  function get_attributes() {
    if (content[0] == '>') {
      content = content.substr(1)
      return {}
    }
    let attrs = {}
    while (1) {
      let attr = get_kvp('Attribute')
      attr.local = attr.key.name[0] == '_'
      attrs[attr.key.name] = attr
      let ch = content[0]
      get_ws()
      if (content[0] == '>') {
        content = content.substr(1)
        break
      } else if (!/^\s/.test(ch)) {
        throw "ParserError"
      }
    }
    return attrs
  }

  function get_expression() {
    let exp = get_primary_expression()
    get_ws()
    return exp
  }

  function get_primary_expression() {
    if (content[0] == '$') {
      return get_variable()
    }
    if (content[0] == '@') {
      content = content.substr(1)
      let id = get_identifier()
      let ge = {type: 'GlobalsExpression', id: id}
      return ge
    }
    return get_identifier()
  }

  function get_variable() {
    content = content.substr(1)
    let id = get_identifier()
    let ve = {type: 'VariableExpression', id: id}
    return ve
  }

  return {
    parse: parse,
  }
}
<commit_msg>add index and hash support to parser<commit_after>'use strict'
L20n.Parser = function() {
  let content = null;
  let patterns = {
    id: /^([_a-zA-Z]\w*)/,
    value: /^(["'])([^'"]*)(["'])/,
    ws: /^\s+/
  }

  function parse(string) {
    let lol = {type: 'LOL',
               body: []}
    content = string
    get_ws();
    while (content) {
      lol.body.push(get_entry())
      get_ws();
    }
    return lol
  }

  function get_ws() {
    content = content.replace(patterns['ws'], '')
  }

  function get_entry() {
    let entry
    if (content[0] == '<') {
      content = content.substr(1)
      let id = get_identifier()
      if (content[0] == '(') {
        entry = get_macro(id)
      } else if (content[0] == '[') {
        let index = get_index()
        entry = get_entity(id, index)
      } else {
        entry = get_entity(id);
      }
    } else if (content.substr(0,2) == '/*') {
      entry = get_comment();
    } else if (content.substr(0,6) == 'import') {
      entry = get_importstatement()
    } else {
      throw "ParserError at get_entry"
    }
    return entry
  }

  function get_importstatement() {
    content = content.substr(6)
    get_ws()
    if (content[0] != '(') {
      throw "ParserError"
    }
    content = content.substr(1)
    get_ws()
    let uri = get_string()
    get_ws()
    if (content[0] != ')') {
      throw "ParserError"
    }
    content = content.substr(1)
    let impStmt = {
      type: 'ImportStatement',
      uri: uri
    }
    return impStmt
  }

  function get_identifier() {
    if (content[0] == '~') {
      // this expression
    }
    let match = patterns['id'].exec(content)
    if (!match)
      throw "ParserError"
    content = content.substr(match[0].length)
    let identifier = {type: 'Identifier',
                  name: match[0]}
    return identifier
  }

  function get_entity(id, index) {
    let ch = content[0]
    get_ws();
    if (content[0] == '>') {
      // empty entity
    }
    if (!/\s/g.test(ch)) {
      throw "ParserError at get_entity"
    }
    let value = get_value(true)
    get_ws()
    let attrs = get_attributes()
    let entity = {
      type: 'Entity',
      id: id,
      value: value,
      index: index || [],
      attrs: attrs,
      local: (id.name[0] == '_')
    }
    return entity
  }

  function get_value(none) {
    let c = content[0]
    let value
    if (c == '"' || c == "'") {
      let ccc = content.substr(3)
      let quote = (ccc == '"""' || ccc == "'''")?ccc:c
      //let value = get_string()
      value = get_complex_string(quote)
    } else if (c == '[') {
      value = get_array()
    } else if (c == '{') {
      value = get_hash()
    }
    return value
  }

  function get_string() {
    let match = patterns['value'].exec(content)
    if (!match) {
      throw "ParserError"
    }
    content = content.substr(match[0].length)
    return {type: 'String', content: match[2]}
  }

  function get_complex_string(quote) {
    let str_end = quote[0]
    let literal = new RegExp("^([^\\\{"+str_end+"]+)")
    let obj = []
    let buffer = ''
    content = content.substr(quote.length)
    let i = 0
    while (content.substr(0, quote.length) != quote) {
      i++;
      if (i>20)
        break
      if (content[0] == str_end) {
        buffer += content[0]
        content = content.substr(1)
      }
      if (content[0] == '\\') {
        let jump = content.substr(1, 3) == '{{' ? 3 : 2;
        buffer += content.substr(1, jump)
        content = content.substr(jump)
      }
      if (content.substr(0, 2) == '{{') {
        content = content.substr(2)
        if (buffer) {
          let string = {type: 'String', content: buffer}
          obj.push(string)
          buffer = ''
        }
        get_ws()
        let expr = get_expression()
        obj.push(expr)
        if (content.substr(0, 2) != '}}') {
          throw "ParserError at get_complex_string"
        }
        content = content.substr(2)
      }
      let m = literal.exec(content)
      if (m) {
        buffer = m[1]
        content = content.substr(m[0].length)
      }
    }
    if (buffer) {
      let string = {type: 'String', content: buffer}
      obj.push(string)
    }
    content = content.substr(quote.length)
    if (obj.length == 1 && obj[0].type == 'String') {
      return obj[0]
    }
    let cs = {type: 'ComplexString', content: obj}
    return cs
  }

  function get_hash() {
    content = content.substr(1)
    get_ws()
    if (content[0] == '}') {
      let h = {type: 'Hash', content: []}
      return h
    }
    let hash = []
    while (1) {
      let defitem = false
      if (content[0] == '*') {
        content = content.substr(1)
          defitem = true
      }
      let hi = get_kvp('HashItem')
      hi.default = defitem
      hash.push(hi)
      get_ws()
      if (content[0] == ',') {
        content = content.substr(1)
        get_ws()
      } else if (content[0] == '}') {
        break
      } else {
        throw "ParserError in get_hash"
      }
    }
    content = content.substr(1)
    let h = {type: 'Hash', content: hash}
    return h
  }

  function get_kvp(cl) {
    let key = get_identifier()
    get_ws()
    if (content[0] != ':') {
      throw "ParserError"
    }
    content = content.substr(1)
    get_ws()
    let val = get_value()
    let kvp = {type: cl, key: key, value: val}
    return kvp
  }

  function get_attributes() {
    if (content[0] == '>') {
      content = content.substr(1)
      return {}
    }
    let attrs = {}
    while (1) {
      let attr = get_kvp('Attribute')
      attr.local = attr.key.name[0] == '_'
      attrs[attr.key.name] = attr
      let ch = content[0]
      get_ws()
      if (content[0] == '>') {
        content = content.substr(1)
        break
      } else if (!/^\s/.test(ch)) {
        throw "ParserError"
      }
    }
    return attrs
  }

  function get_index() {
    content = content.substr(1)
    get_ws()
    let index = []
    if (content[0] == ']') {
      content = content.substr(1)
      return index
    }
    while (1) {
      let expression = get_expression()
      index.push(expression)
      get_ws()
      if (content[0] == ',') {
        content = content.substr(1)
      } else if (content[0] == ']') {
        break
      } else {
        throw "ParserError in get_index"
      }
    }
    content = content.substr(1)
    return index
  }

  function get_expression() {
    let exp = get_primary_expression()
    get_ws()
    return exp
  }

  function get_primary_expression() {
    if (content[0] == '$') {
      return get_variable()
    }
    if (content[0] == '@') {
      content = content.substr(1)
      let id = get_identifier()
      let ge = {type: 'GlobalsExpression', id: id}
      return ge
    }
    return get_identifier()
  }

  function get_variable() {
    content = content.substr(1)
    let id = get_identifier()
    let ve = {type: 'VariableExpression', id: id}
    return ve
  }

  return {
    parse: parse,
  }
}
<|endoftext|>


<commit_before>var points = [{
    "latitude": 43.70772,
    "longitude": 7.33437,
    "title": "Beaulieu-sur-Mer, France",
    "content": " "
    }, {
    "latitude": 43.710173,
    "longitude": 7.261953,
    "title": "Nice, France",
    "content": " "
    }, {
    "latitude": 43.296482,
    "longitude": 5.36978,
    "title": "Marseille, France",
    "content": " "
    }, {
    "latitude": 43.433152,
    "longitude": 6.737034,
    "title": "Fréjus, France",
    "content": " "
    }, {
    "latitude": 43.552847,
    "longitude": 7.017369,
    "title": "Cannes, France",
    "content": " "
    }, {
    "latitude": 45.783716,
    "longitude": 4.87168,
    "title": "2 Avenue des Arts, 69100 Villeurbanne, France",
    "content": " "
    }, {
    "latitude": 48.856614,
    "longitude": 2.352222,
    "title": "Paris, France",
    "content": " "
    }, {
    "latitude": 48.404676,
    "longitude": 2.70162,
    "title": "Fontainebleau, France",
    "content": " "
    }, {
    "latitude": 48.573405,
    "longitude": 7.752111,
    "title": "Strasbourg, France",
    "content": " "
    }, {
    "latitude": 50.357113,
    "longitude": 3.518332,
    "title": "Valenciennes, France",
    "content": " "
    }, {
    "latitude": 50.62925,
    "longitude": 3.057256,
    "title": "Lille, France",
    "content": " "
    }, {
    "latitude": 49.417816,
    "longitude": 2.826145,
    "title": "Compiègne, France",
    "content": " "
    }, {
    "latitude": 47.285448,
    "longitude": -2.393236,
    "title": "La Baule-Escoublac, France",
    "content": " "
    }, {
    "latitude": 47.269852,
    "longitude": -2.425372,
    "title": "Le Pouliguen, France",
    "content": " "
    }, {
    "latitude": 47.294319,
    "longitude": -2.511908,
    "title": "Le Croisic, France",
    "content": " "
    }, {
    "latitude": 47.273498,
    "longitude": -2.213848,
    "title": "Saint-Nazaire, France",
    "content": " "
    }, {
    "latitude": 47.328003,
    "longitude": -2.429759,
    "title": "Guérande, France",
    "content": " "
    }, {
    "latitude": 47.263426,
    "longitude": -2.340571,
    "title": "Pornichet, France",
    "content": " "
    }, {
    "latitude": 47.256301,
    "longitude": -2.018918,
    "title": "Saint-Viaud, France",
    "content": " "
    }, {
    "latitude": 46.373565,
    "longitude": 6.477635,
    "title": "Thonon-les-Bains, France",
    "content": " "
    }, {
    "latitude": 46.401488,
    "longitude": 6.590949,
    "title": "Évian-les-Bains, France",
    "content": " "
    }, {
    "latitude": 45.899247,
    "longitude": 6.129384,
    "title": "Annecy, France",
    "content": " "
    }, {
    "latitude": 45.192467,
    "longitude": 5.71409,
    "title": "Grenoble, France",
    "content": " "
    }, {
    "latitude": 45.564601,
    "longitude": 5.917781,
    "title": "Chambéry, France",
    "content": " "
    }, {
    "latitude": 45.923697,
    "longitude": 6.869433,
    "title": "Chamonix-Mont-Blanc, France",
    "content": " "
    }, {
    "latitude": 40.761331,
    "longitude": -73.991087,
    "title": "405 W 46th St, New York, NY 10036, United States",
    "content": " "
    }, {
    "latitude": 51.507351,
    "longitude": -0.127758,
    "title": "London, England",
    "content": " "
    }, {
    "latitude": 55.675984,
    "longitude": 12.574386,
    "title": "Vandkunsten 5, 1467 København K, Danemark",
    "content": " "
    }, {
    "latitude": 55.604981,
    "longitude": 13.003822,
    "title": "Malmö, Suède",
    "content": " "
    }, {
    "latitude": 48.566736,
    "longitude": 13.431947,
    "title": "Passau, Deutschland",
    "content": " "
    }, {
    "latitude": 48.135125,
    "longitude": 11.581981,
    "title": "München, Deutschland",
    "content": " "
    }, {
    "latitude": 50.110922,
    "longitude": 8.682127,
    "title": "Frankfurt, Deutschland",
    "content": " "
    }, {
    "latitude": 50.579608,
    "longitude": 8.662703,
    "title": "Gießen, Deutschland",
    "content": " "
    }, {
    "latitude": 50.93753,
    "longitude": 6.960279,
    "title": "Köln, Deutschland",
    "content": " "
    }, {
    "latitude": 48.544192,
    "longitude": 12.146853,
    "title": "Landshut, Deutschland",
    "content": " "
    }, {
    "latitude": 48.877733,
    "longitude": 12.580154,
    "title": "Straubing, Deutschland",
    "content": " "
    }, {
    "latitude": 49.013407,
    "longitude": 12.101631,
    "title": "Regensburg, Deutschland",
    "content": " "
    }, {
    "latitude": 50.961912,
    "longitude": 14.073166,
    "title": "Bastei, Deutschland",
    "content": " "
    }, {
    "latitude": 51.050409,
    "longitude": 13.737262,
    "title": "Dresden, Deutschland",
    "content": " "
    }, {
    "latitude": 49.45203,
    "longitude": 11.07675,
    "title": "Nürnberg, Deutschland",
    "content": " "
    }, {
    "latitude": 49.898814,
    "longitude": 10.902764,
    "title": "Bamberg, Deutschland",
    "content": " "
    }, {
    "latitude": 49.791304,
    "longitude": 9.953355,
    "title": "Wurtzbourg, Deutschland",
    "content": " "
    }, {
    "latitude": 49.380183,
    "longitude": 10.186739,
    "title": "Rothenburg ob der Tauber, Deutschland",
    "content": " "
    }, {
    "latitude": 48.370545,
    "longitude": 10.89779,
    "title": "Augsbourg, Deutschland",
    "content": " "
    }, {
    "latitude": 47.569648,
    "longitude": 10.700433,
    "title": "Füssen, Deutschland",
    "content": " "
    }, {
    "latitude": 47.577853,
    "longitude": 10.738305,
    "title": "Schwangau, Deutschland",
    "content": " "
    }, {
    "latitude": 47.80949,
    "longitude": 13.05501,
    "title": "Salzburg, Autriche",
    "content": " "
    }, {
    "latitude": 48.262998,
    "longitude": 11.433902,
    "title": "Dachau, Deutschland",
    "content": " "
    }, {
    "latitude": 48.30694,
    "longitude": 14.28583,
    "title": "Linz, Österreich",
    "content": " "
    }, {
    "latitude": 46.240857,
    "longitude": 6.199487,
    "title": "Route de Thonon 63, 1222 Vésenaz, Suisse",
    "content": " "
    }, {
    "latitude": 46.204391,
    "longitude": 6.143158,
    "title": "Genève, Suisse",
    "content": " "
    }, {
    "latitude": 47.376887,
    "longitude": 8.541694,
    "title": "Zurich, Suisse",
    "content": " "
    }, {
    "latitude": 46.519654,
    "longitude": 6.632273,
    "title": "Lausanne, Suisse",
    "content": " "
    }, {
    "latitude": 46.462833,
    "longitude": 6.841919,
    "title": "Vevey, Suisse",
    "content": " "
    }, {
    "latitude": 43.791237,
    "longitude": 7.607586,
    "title": "Ventimiglia, Italia",
    "content": " "
    }, {
    "latitude": 43.780698,
    "longitude": 7.67228,
    "title": "Bordighera, Italia",
    "content": " "
    }, {
    "latitude": 43.849909,
    "longitude": 7.584218,
    "title": "Trucco, Italia",
    "content": " "
    }, {
    "latitude": 44.384477,
    "longitude": 7.542671,
    "title": "Cuneo, Italia",
    "content": " "
    }, {
    "latitude": 44.303156,
    "longitude": 9.209788,
    "title": "Portofino, Italia",
    "content": " "
    }, {
    "latitude": 44.10245,
    "longitude": 9.824083,
    "title": "La Spezia, Italia",
    "content": " "
    }, {
    "latitude": 44.054126,
    "longitude": 9.836628,
    "title": "Portovenere, Italia",
    "content": " "
    }, {
    "latitude": 44.099049,
    "longitude": 9.737485,
    "title": "Riomaggiore, Italia",
    "content": " "
    }, {
    "latitude": 44.111559,
    "longitude": 9.733894,
    "title": "Manarola, Italia",
    "content": " "
    }, {
    "latitude": 44.134963,
    "longitude": 9.684804,
    "title": "Vernazza, Italia",
    "content": " "
    }, {
    "latitude": 44.148772,
    "longitude": 9.654408,
    "title": "Monterosso al Mare, Italia",
    "content": " "
    }, {
    "latitude": 43.815967,
    "longitude": 7.776057,
    "title": "San Remo, Italia",
    "content": " "
    }, {
    "latitude": 44.40565,
    "longitude": 8.946256,
    "title": "Genova, Italia",
    "content": " "
    }, {
    "latitude": 38.115688,
    "longitude": 13.361267,
    "title": "Palermo, Italia",
    "content": " "
    }, {
    "latitude": 50.85034,
    "longitude": 4.35171,
    "title": "Bruxelles, Belgique",
    "content": " "
    }, {
    "latitude": 51.34635,
    "longitude": 3.28586,
    "title": "Knocke-Heist, Belgique",
    "content": " "
    }, {
    "latitude": 51.209348,
    "longitude": 3.2247,
    "title": "Bruges, Belgique",
    "content": " "
    }, {
    "latitude": 50.075538,
    "longitude": 14.4378,
    "title": "Praha, Česká republika",
    "content": " "
    }, {
    "latitude": 48.812735,
    "longitude": 14.317466,
    "title": "Český Krumlov, Česká republika",
    "content": " "
    }
];<commit_msg>Small places update<commit_after>var points = [{
    "latitude": 43.70772,
    "longitude": 7.33437,
    "title": "Beaulieu-sur-Mer, France",
    "content": " "
    }, {
    "latitude": 43.710173,
    "longitude": 7.261953,
    "title": "Nice, France",
    "content": " "
    }, {
    "latitude": 43.296482,
    "longitude": 5.36978,
    "title": "Marseille, France",
    "content": " "
    }, {
    "latitude": 43.433152,
    "longitude": 6.737034,
    "title": "Fréjus, France",
    "content": " "
    }, {
    "latitude": 43.552847,
    "longitude": 7.017369,
    "title": "Cannes, France",
    "content": " "
    }, {
    "latitude": 45.783716,
    "longitude": 4.87168,
    "title": "2 Avenue des Arts, 69100 Villeurbanne, France",
    "content": " "
    }, {
    "latitude": 48.856614,
    "longitude": 2.352222,
    "title": "Paris, France",
    "content": " "
    }, {
    "latitude": 48.404676,
    "longitude": 2.70162,
    "title": "Fontainebleau, France",
    "content": " "
    }, {
    "latitude": 48.573405,
    "longitude": 7.752111,
    "title": "Strasbourg, France",
    "content": " "
    }, {
    "latitude": 50.357113,
    "longitude": 3.518332,
    "title": "Valenciennes, France",
    "content": " "
    }, {
    "latitude": 50.62925,
    "longitude": 3.057256,
    "title": "Lille, France",
    "content": " "
    }, {
    "latitude": 49.417816,
    "longitude": 2.826145,
    "title": "Compiègne, France",
    "content": " "
    }, {
    "latitude": 47.285448,
    "longitude": -2.393236,
    "title": "La Baule-Escoublac, France",
    "content": " "
    }, {
    "latitude": 47.269852,
    "longitude": -2.425372,
    "title": "Le Pouliguen, France",
    "content": " "
    }, {
    "latitude": 47.294319,
    "longitude": -2.511908,
    "title": "Le Croisic, France",
    "content": " "
    }, {
    "latitude": 47.273498,
    "longitude": -2.213848,
    "title": "Saint-Nazaire, France",
    "content": " "
    }, {
    "latitude": 47.328003,
    "longitude": -2.429759,
    "title": "Guérande, France",
    "content": " "
    }, {
    "latitude": 47.263426,
    "longitude": -2.340571,
    "title": "Pornichet, France",
    "content": " "
    }, {
    "latitude": 47.256301,
    "longitude": -2.018918,
    "title": "Saint-Viaud, France",
    "content": " "
    }, {
    "latitude": 46.373565,
    "longitude": 6.477635,
    "title": "Thonon-les-Bains, France",
    "content": " "
    }, {
    "latitude": 46.401488,
    "longitude": 6.590949,
    "title": "Évian-les-Bains, France",
    "content": " "
    }, {
    "latitude": 45.899247,
    "longitude": 6.129384,
    "title": "Annecy, France",
    "content": " "
    }, {
    "latitude": 45.192467,
    "longitude": 5.71409,
    "title": "Grenoble, France",
    "content": " "
    }, {
    "latitude": 45.564601,
    "longitude": 5.917781,
    "title": "Chambéry, France",
    "content": " "
    }, {
    "latitude": 45.923697,
    "longitude": 6.869433,
    "title": "Chamonix-Mont-Blanc, France",
    "content": " "
    }, {
    "latitude": 40.761331,
    "longitude": -73.991087,
    "title": "405 W 46th St, New York, NY 10036, United States",
    "content": " "
    }, {
    "latitude": 51.507351,
    "longitude": -0.127758,
    "title": "London, England",
    "content": " "
    }, {
    "latitude": 55.675984,
    "longitude": 12.574386,
    "title": "Vandkunsten 5, 1467 København K, Danemark",
    "content": " "
    }, {
    "latitude": 55.604981,
    "longitude": 13.003822,
    "title": "Malmö, Suède",
    "content": " "
    }, {
    "latitude": 48.566736,
    "longitude": 13.431947,
    "title": "Passau, Deutschland",
    "content": " "
    }, {
    "latitude": 48.135125,
    "longitude": 11.581981,
    "title": "München, Deutschland",
    "content": " "
    }, {
    "latitude": 50.110922,
    "longitude": 8.682127,
    "title": "Frankfurt, Deutschland",
    "content": " "
    }, {
    "latitude": 50.579608,
    "longitude": 8.662703,
    "title": "Gießen, Deutschland",
    "content": " "
    }, {
    "latitude": 50.93753,
    "longitude": 6.960279,
    "title": "Köln, Deutschland",
    "content": " "
    }, {
    "latitude": 48.544192,
    "longitude": 12.146853,
    "title": "Landshut, Deutschland",
    "content": " "
    }, {
    "latitude": 48.877733,
    "longitude": 12.580154,
    "title": "Straubing, Deutschland",
    "content": " "
    }, {
    "latitude": 49.013407,
    "longitude": 12.101631,
    "title": "Regensburg, Deutschland",
    "content": " "
    }, {
    "latitude": 50.961912,
    "longitude": 14.073166,
    "title": "Bastei, Deutschland",
    "content": " "
    }, {
    "latitude": 51.050409,
    "longitude": 13.737262,
    "title": "Dresden, Deutschland",
    "content": " "
    }, {
    "latitude": 49.45203,
    "longitude": 11.07675,
    "title": "Nürnberg, Deutschland",
    "content": " "
    }, {
    "latitude": 49.898814,
    "longitude": 10.902764,
    "title": "Bamberg, Deutschland",
    "content": " "
    }, {
    "latitude": 49.791304,
    "longitude": 9.953355,
    "title": "Wurtzbourg, Deutschland",
    "content": " "
    }, {
    "latitude": 49.380183,
    "longitude": 10.186739,
    "title": "Rothenburg ob der Tauber, Deutschland",
    "content": " "
    }, {
    "latitude": 48.370545,
    "longitude": 10.89779,
    "title": "Augsbourg, Deutschland",
    "content": " "
    }, {
    "latitude": 47.569648,
    "longitude": 10.700433,
    "title": "Füssen, Deutschland",
    "content": " "
    }, {
    "latitude": 47.577853,
    "longitude": 10.738305,
    "title": "Schwangau, Deutschland",
    "content": " "
    }, {
    "latitude": 47.80949,
    "longitude": 13.05501,
    "title": "Salzburg, Autriche",
    "content": " "
    }, {
    "latitude": 48.262998,
    "longitude": 11.433902,
    "title": "Dachau, Deutschland",
    "content": " "
    }, {
    "latitude": 48.30694,
    "longitude": 14.28583,
    "title": "Linz, Österreich",
    "content": " "
    }, {
    "latitude": 46.240857,
    "longitude": 6.199487,
    "title": "Route de Thonon 63, 1222 Vésenaz, Suisse",
    "content": " "
    }, {
    "latitude": 46.204391,
    "longitude": 6.143158,
    "title": "Genève, Suisse",
    "content": " "
    }, {
    "latitude": 47.376887,
    "longitude": 8.541694,
    "title": "Zurich, Suisse",
    "content": " "
    }, {
    "latitude": 46.519654,
    "longitude": 6.632273,
    "title": "Lausanne, Suisse",
    "content": " "
    }, {
    "latitude": 46.462833,
    "longitude": 6.841919,
    "title": "Vevey, Suisse",
    "content": " "
    }, {
    "latitude": 43.791237,
    "longitude": 7.607586,
    "title": "Ventimiglia, Italia",
    "content": " "
    }, {
    "latitude": 43.780698,
    "longitude": 7.67228,
    "title": "Bordighera, Italia",
    "content": " "
    }, {
    "latitude": 43.849909,
    "longitude": 7.584218,
    "title": "Trucco, Italia",
    "content": " "
    }, {
    "latitude": 44.384477,
    "longitude": 7.542671,
    "title": "Cuneo, Italia",
    "content": " "
    }, {
    "latitude": 44.303156,
    "longitude": 9.209788,
    "title": "Portofino, Italia",
    "content": " "
    }, {
    "latitude": 44.10245,
    "longitude": 9.824083,
    "title": "La Spezia, Italia",
    "content": " "
    }, {
    "latitude": 44.054126,
    "longitude": 9.836628,
    "title": "Portovenere, Italia",
    "content": " "
    }, {
    "latitude": 44.099049,
    "longitude": 9.737485,
    "title": "Riomaggiore, Italia",
    "content": " "
    }, {
    "latitude": 44.111559,
    "longitude": 9.733894,
    "title": "Manarola, Italia",
    "content": " "
    }, {
    "latitude": 44.134963,
    "longitude": 9.684804,
    "title": "Vernazza, Italia",
    "content": " "
    }, {
    "latitude": 44.148772,
    "longitude": 9.654408,
    "title": "Monterosso al Mare, Italia",
    "content": " "
    }, {
    "latitude": 43.815967,
    "longitude": 7.776057,
    "title": "San Remo, Italia",
    "content": " "
    }, {
    "latitude": 44.40565,
    "longitude": 8.946256,
    "title": "Genova, Italia",
    "content": " "
    }, {
    "latitude": 38.115688,
    "longitude": 13.361267,
    "title": "Palermo, Italia",
    "content": " "
    }, {
    "latitude": 50.85034,
    "longitude": 4.35171,
    "title": "Bruxelles, Belgique",
    "content": " "
    }, {
    "latitude": 51.34635,
    "longitude": 3.28586,
    "title": "Knocke-Heist, Belgique",
    "content": " "
    }, {
    "latitude": 51.209348,
    "longitude": 3.2247,
    "title": "Bruges, Belgique",
    "content": " "
    }, {
    "latitude": 50.075538,
    "longitude": 14.4378,
    "title": "Praha, Česká republika",
    "content": " "
    }, {
    "latitude": 48.812735,
    "longitude": 14.317466,
    "title": "Český Krumlov, Česká republika",
    "content": " "
    }, {
    "latitude": -8.505969,
    "longitude": 119.902499,
    "title": "Komodo Boutique Hotel, Labuan Bajo - Indonesia",
    "content": " "
    }, {
    "latitude": 2.808166,
    "longitude": 104.142121,
    "title": "Berjaya Tioman Resort, Pulau Tioman - Malaysia",
    "content": " "
    }, {
    "latitude": 1.352537,
    "longitude": 103.850846,
    "title": "Sky Vue, 15 Bishan Street - Singapore",
    "content": " "
    }
];
<|endoftext|>


<commit_before>var _   = require('lodash');
var fs  = require('fs');
var jsf = require('jsonfile');

/**
 * Object Definition
 */

var Player = function (username) {
  this.username = username;
  this.data     = null;
  this.settings = null;
};

/**
 * Prototypes
 */

Player.prototype.load = function (data) {
  if (fs.existsSync(__dirname + '/../data/save/' + this.username)) {
    this.data = jsf.readFileSync(__dirname + '/../data/save/' + this.username + '/player-data.json');
    this.settings = jsf.readFileSync(__dirname + '/../data/save/' + this.username + '/player-settings.json');
    if (this.settings === undefined) {
      this.settings = {
        spreadsheet: {
          color:  {
            iv:   false,
            move: false
          },
          display: {
            move_1:       true,
            move_2:       false,
            move_energy:  false,
            move_dps:     false,
            level:        true,
            cp:           true,
            hp:           true,
            atk:          false,
            def:          false,
            iva:          true,
            ivd:          true,
            ivs:          true,
            dps:          false,
            piv:          true,
            pcp:          true
          }
        }
      };
    }
    this.update(data);
  } else {
    this.data = data;
    this.settings = {
      spreadsheet: {
        color:  {
          iv:   false,
          move: false
        },
        display: {
          move_1:       true,
          move_2:       false,
          move_energy:  false,
          move_dps:     false,
          level:        true,
          cp:           true,
          hp:           true,
          atk:          false,
          def:          false,
          iva:          true,
          ivd:          true,
          ivs:          true,
          dps:          false,
          piv:          true,
          pcp:          true
        }
      }
    };
  }
};

Player.prototype.save = function () {
  if (!fs.existsSync(__dirname + '/../data/save/' + this.username)) fs.mkdirSync(__dirname + '/../data/save/' + this.username);
  jsf.writeFileSync(__dirname + '/../data/save/' + this.username + '/player-data.json', this.data, {spaces: 2});
  jsf.writeFileSync(__dirname + '/../data/save/' + this.username + '/player-settings.json', this.settings, {spaces: 2});
};

Player.prototype.update = function (data) {
  this.data = data;
};

Player.prototype.toJSON = function () {
  return {
    data: this.data,
    settings: this.settings
  };
};

module.exports = Player;<commit_msg>Fixed settings undefined error once and for all<commit_after>var _   = require('lodash');
var fs  = require('fs');
var jsf = require('jsonfile');

/**
 * Object Definition
 */

var Player = function (username) {
  this.username = username;
  this.data     = null;
  this.settings = null;
};

var defaultSettings = {
  spreadsheet: {
    color:  {
      iv:   false,
      move: false
    },
    display: {
      move_1:       true,
      move_2:       false,
      move_energy:  false,
      move_dps:     false,
      level:        true,
      cp:           true,
      hp:           true,
      atk:          false,
      def:          false,
      iva:          true,
      ivd:          true,
      ivs:          true,
      dps:          false,
      piv:          true,
      pcp:          true
    }
  }
};

/**
 * Prototypes
 */

Player.prototype.load = function (data) {
  if (fs.existsSync(__dirname + '/../data/save/' + this.username)) {
    this.data = jsf.readFileSync(__dirname + '/../data/save/' + this.username + '/player-data.json');
    if (fs.existsSync(__dirname + '/../data/save/' + this.username + '/player-settings.json'))
      this.settings = jsf.readFileSync(__dirname + '/../data/save/' + this.username + '/player-settings.json');
    if (!this.settings) this.settings = _.cloneDeep(defaultSettings);
    if (!this.settings.spreadsheet ||
        !this.settings.spreadsheet.color ||
        !this.settings.spreadsheet.display) this.settings = _.cloneDeep(defaultSettings);
    this.update(data);
  } else {
    this.data = data;
    this.settings = {
      spreadsheet: {
        color:  {
          iv:   false,
          move: false
        },
        display: {
          move_1:       true,
          move_2:       false,
          move_energy:  false,
          move_dps:     false,
          level:        true,
          cp:           true,
          hp:           true,
          atk:          false,
          def:          false,
          iva:          true,
          ivd:          true,
          ivs:          true,
          dps:          false,
          piv:          true,
          pcp:          true
        }
      }
    };
  }
};

Player.prototype.save = function () {
  if (!fs.existsSync(__dirname + '/../data/save/' + this.username)) fs.mkdirSync(__dirname + '/../data/save/' + this.username);
  jsf.writeFileSync(__dirname + '/../data/save/' + this.username + '/player-data.json', this.data, {spaces: 2});
  jsf.writeFileSync(__dirname + '/../data/save/' + this.username + '/player-settings.json', this.settings, {spaces: 2});
};

Player.prototype.update = function (data) {
  this.data = data;
};

Player.prototype.toJSON = function () {
  return {
    data: this.data,
    settings: this.settings
  };
};

module.exports = Player;<|endoftext|>


<commit_before>var path = require('path');
var fs = require('fs');
var semver = require('semver');
var User = require('./user').User;
var version = require('./version');
var log = require("./logger")._system;

'use strict';

module.exports = {
  Plugin: Plugin
}

/**
 * Homebridge Plugin.
 *
 * Allows for discovering and loading installed Homebridge plugins.
 */

function Plugin(pluginPath) {
  this.pluginPath = pluginPath; // like "/usr/local/lib/node_modules/homebridge-lockitron"
  this.initializer; // exported function from the plugin that initializes it
}

Plugin.prototype.name = function() {
  return path.basename(this.pluginPath);
}

Plugin.prototype.load = function(options) {
  options = options || {};

  // does this plugin exist at all?
  if (!fs.existsSync(this.pluginPath)) {
    throw new Error("Plugin " + this.pluginPath + " was not found. Make sure the module '" + this.pluginPath + "' is installed.");
  }

  // attempt to load package.json
  var pjson = Plugin.loadPackageJSON(this.pluginPath);

  // very temporary fix for first wave of plugins
  if (pjson.peerDepdendencies && (!pjson.engines || !pjson.engines.homebridge)) {
    var engines = pjson.engines || {}
    engines.homebridge = pjson.peerDepdendencies.homebridge;
    pjson.engines = engines;
  }

  // pluck out the HomeBridge version requirement
  if (!pjson.engines || !pjson.engines.homebridge) {
    throw new Error("Plugin " + this.pluginPath + " does not contain the 'homebridge' package in 'engines'.");
  }

  var versionRequired = pjson.engines.homebridge;

  // make sure the version is satisfied by the currently running version of HomeBridge
  if (!semver.satisfies(version, versionRequired)) {
    throw new Error("Plugin " + this.pluginPath + " requires a HomeBridge version of " + versionRequired + " which does not satisfy the current HomeBridge version of " + version + ". You may need to upgrade your installation of HomeBridge.");
  }

  // figure out the main module - index.js unless otherwise specified
  var main = pjson.main || "./index.js";

  var mainPath = path.join(this.pluginPath, main);

  // try to require() it and grab the exported initialization hook
  this.initializer = require(mainPath);
}

Plugin.loadPackageJSON = function(pluginPath) {
  // check for a package.json
  var pjsonPath = path.join(pluginPath, "package.json");
  var pjson = null;

  if (!fs.existsSync(pjsonPath)) {
    throw new Error("Plugin " + pluginPath + " does not contain a package.json.");
  }

  try {
    // attempt to parse package.json
    pjson = JSON.parse(fs.readFileSync(pjsonPath));
  }
  catch (err) {
    throw new Error("Plugin " + pluginPath + " contains an invalid package.json. Error: " + err);
  }

  // make sure the name is prefixed with 'homebridge-'
  if (!pjson.name || pjson.name.indexOf('homebridge-') != 0) {
    throw new Error("Plugin " + pluginPath + " does not have a package name that begins with 'homebridge-'.");
  }

  // verify that it's tagged with the correct keyword
  if (!pjson.keywords || pjson.keywords.indexOf("homebridge-plugin") == -1) {
    throw new Error("Plugin " + pluginPath + " package.json does not contain the keyword 'homebridge-plugin'.");
  }

  return pjson;
}

Plugin.getDefaultPaths = function() {
  var win32 = process.platform === 'win32';
  var paths = [];

  // add the paths used by require()
  paths = paths.concat(require.main.paths);

  // THIS SECTION FROM: https://github.com/yeoman/environment/blob/master/lib/resolver.js

  // Adding global npm directories
  // We tried using npm to get the global modules path, but it haven't work out
  // because of bugs in the parseable implementation of `ls` command and mostly
  // performance issues. So, we go with our best bet for now.
  if (process.env.NODE_PATH) {
    paths = process.env.NODE_PATH.split(path.delimiter)
      .filter(function(p) { return !!p; }) // trim out empty values
      .concat(paths);
  } else {
    // Default paths for each system
    if (win32) {
      paths.push(path.join(process.env.APPDATA, 'npm/node_modules'));
    } else {
      paths.push('/usr/local/lib/node_modules');
      paths.push('/usr/lib/node_modules');
      const exec = require('child_process').execSync
      paths.push(exec('/bin/echo -n "$(npm -g prefix)/lib/node_modules"').toString('utf8'));
    }
  }
  return paths;
}

// All search paths we will use to discover installed plugins
Plugin.paths = Plugin.getDefaultPaths();

Plugin.addPluginPath = function(pluginPath) {
  Plugin.paths.unshift(path.resolve(process.cwd(), pluginPath));
}

// Gets all plugins installed on the local system
Plugin.installed = function() {

  var plugins = [];
  var pluginsByName = {}; // don't add duplicate plugins
  var searchedPaths = {}; // don't search the same paths twice

  // search for plugins among all known paths, in order
  for (var index in Plugin.paths) {
    var requirePath = Plugin.paths[index];

    // did we already search this path?
    if (searchedPaths[requirePath])
      continue;

    searchedPaths[requirePath] = true;

    // just because this path is in require.main.paths doesn't mean it necessarily exists!
    if (!fs.existsSync(requirePath))
      continue;

    var names = fs.readdirSync(requirePath);

    // does this path point inside a single plugin and not a directory containing plugins?
    if (fs.existsSync(path.join(requirePath, "package.json")))
      names = [""];

    // read through each directory in this node_modules folder
    for (var index2 in names) {
      var name = names[index2];

      // reconstruct full path
      var pluginPath = path.join(requirePath, name);

      // we only care about directories
      if (!fs.statSync(pluginPath).isDirectory()) continue;

      // does this module contain a package.json?
      var pjson;
      try {
        // throws an Error if this isn't a homebridge plugin
        pjson = Plugin.loadPackageJSON(pluginPath);
      }
      catch (err) {
        // is this "trying" to be a homebridge plugin? if so let you know what went wrong.
        if (!name || name.indexOf('homebridge-') == 0) {
          log.warn(err.message);
        }

        // skip this module
        continue;
      }

      // get actual name if this path points inside a single plugin
      if (!name) name = pjson.name;

      // add it to the return list
      if (!pluginsByName[name]) {
        pluginsByName[name] = pluginPath;
        plugins.push(new Plugin(pluginPath));
      }
      else {
        log.warn("Warning: skipping plugin found at '" + pluginPath + "' since we already loaded the same plugin from '" + pluginsByName[name] + "'.");
      }
    }
  }

  return plugins;
}
<commit_msg>missed ";"<commit_after>var path = require('path');
var fs = require('fs');
var semver = require('semver');
var User = require('./user').User;
var version = require('./version');
var log = require("./logger")._system;

'use strict';

module.exports = {
  Plugin: Plugin
}

/**
 * Homebridge Plugin.
 *
 * Allows for discovering and loading installed Homebridge plugins.
 */

function Plugin(pluginPath) {
  this.pluginPath = pluginPath; // like "/usr/local/lib/node_modules/homebridge-lockitron"
  this.initializer; // exported function from the plugin that initializes it
}

Plugin.prototype.name = function() {
  return path.basename(this.pluginPath);
}

Plugin.prototype.load = function(options) {
  options = options || {};

  // does this plugin exist at all?
  if (!fs.existsSync(this.pluginPath)) {
    throw new Error("Plugin " + this.pluginPath + " was not found. Make sure the module '" + this.pluginPath + "' is installed.");
  }

  // attempt to load package.json
  var pjson = Plugin.loadPackageJSON(this.pluginPath);

  // very temporary fix for first wave of plugins
  if (pjson.peerDepdendencies && (!pjson.engines || !pjson.engines.homebridge)) {
    var engines = pjson.engines || {}
    engines.homebridge = pjson.peerDepdendencies.homebridge;
    pjson.engines = engines;
  }

  // pluck out the HomeBridge version requirement
  if (!pjson.engines || !pjson.engines.homebridge) {
    throw new Error("Plugin " + this.pluginPath + " does not contain the 'homebridge' package in 'engines'.");
  }

  var versionRequired = pjson.engines.homebridge;

  // make sure the version is satisfied by the currently running version of HomeBridge
  if (!semver.satisfies(version, versionRequired)) {
    throw new Error("Plugin " + this.pluginPath + " requires a HomeBridge version of " + versionRequired + " which does not satisfy the current HomeBridge version of " + version + ". You may need to upgrade your installation of HomeBridge.");
  }

  // figure out the main module - index.js unless otherwise specified
  var main = pjson.main || "./index.js";

  var mainPath = path.join(this.pluginPath, main);

  // try to require() it and grab the exported initialization hook
  this.initializer = require(mainPath);
}

Plugin.loadPackageJSON = function(pluginPath) {
  // check for a package.json
  var pjsonPath = path.join(pluginPath, "package.json");
  var pjson = null;

  if (!fs.existsSync(pjsonPath)) {
    throw new Error("Plugin " + pluginPath + " does not contain a package.json.");
  }

  try {
    // attempt to parse package.json
    pjson = JSON.parse(fs.readFileSync(pjsonPath));
  }
  catch (err) {
    throw new Error("Plugin " + pluginPath + " contains an invalid package.json. Error: " + err);
  }

  // make sure the name is prefixed with 'homebridge-'
  if (!pjson.name || pjson.name.indexOf('homebridge-') != 0) {
    throw new Error("Plugin " + pluginPath + " does not have a package name that begins with 'homebridge-'.");
  }

  // verify that it's tagged with the correct keyword
  if (!pjson.keywords || pjson.keywords.indexOf("homebridge-plugin") == -1) {
    throw new Error("Plugin " + pluginPath + " package.json does not contain the keyword 'homebridge-plugin'.");
  }

  return pjson;
}

Plugin.getDefaultPaths = function() {
  var win32 = process.platform === 'win32';
  var paths = [];

  // add the paths used by require()
  paths = paths.concat(require.main.paths);

  // THIS SECTION FROM: https://github.com/yeoman/environment/blob/master/lib/resolver.js

  // Adding global npm directories
  // We tried using npm to get the global modules path, but it haven't work out
  // because of bugs in the parseable implementation of `ls` command and mostly
  // performance issues. So, we go with our best bet for now.
  if (process.env.NODE_PATH) {
    paths = process.env.NODE_PATH.split(path.delimiter)
      .filter(function(p) { return !!p; }) // trim out empty values
      .concat(paths);
  } else {
    // Default paths for each system
    if (win32) {
      paths.push(path.join(process.env.APPDATA, 'npm/node_modules'));
    } else {
      paths.push('/usr/local/lib/node_modules');
      paths.push('/usr/lib/node_modules');
      const exec = require('child_process').execSync;
      paths.push(exec('/bin/echo -n "$(npm -g prefix)/lib/node_modules"').toString('utf8'));
    }
  }
  return paths;
}

// All search paths we will use to discover installed plugins
Plugin.paths = Plugin.getDefaultPaths();

Plugin.addPluginPath = function(pluginPath) {
  Plugin.paths.unshift(path.resolve(process.cwd(), pluginPath));
}

// Gets all plugins installed on the local system
Plugin.installed = function() {

  var plugins = [];
  var pluginsByName = {}; // don't add duplicate plugins
  var searchedPaths = {}; // don't search the same paths twice

  // search for plugins among all known paths, in order
  for (var index in Plugin.paths) {
    var requirePath = Plugin.paths[index];

    // did we already search this path?
    if (searchedPaths[requirePath])
      continue;

    searchedPaths[requirePath] = true;

    // just because this path is in require.main.paths doesn't mean it necessarily exists!
    if (!fs.existsSync(requirePath))
      continue;

    var names = fs.readdirSync(requirePath);

    // does this path point inside a single plugin and not a directory containing plugins?
    if (fs.existsSync(path.join(requirePath, "package.json")))
      names = [""];

    // read through each directory in this node_modules folder
    for (var index2 in names) {
      var name = names[index2];

      // reconstruct full path
      var pluginPath = path.join(requirePath, name);

      // we only care about directories
      if (!fs.statSync(pluginPath).isDirectory()) continue;

      // does this module contain a package.json?
      var pjson;
      try {
        // throws an Error if this isn't a homebridge plugin
        pjson = Plugin.loadPackageJSON(pluginPath);
      }
      catch (err) {
        // is this "trying" to be a homebridge plugin? if so let you know what went wrong.
        if (!name || name.indexOf('homebridge-') == 0) {
          log.warn(err.message);
        }

        // skip this module
        continue;
      }

      // get actual name if this path points inside a single plugin
      if (!name) name = pjson.name;

      // add it to the return list
      if (!pluginsByName[name]) {
        pluginsByName[name] = pluginPath;
        plugins.push(new Plugin(pluginPath));
      }
      else {
        log.warn("Warning: skipping plugin found at '" + pluginPath + "' since we already loaded the same plugin from '" + pluginsByName[name] + "'.");
      }
    }
  }

  return plugins;
}
<|endoftext|>


<commit_before>/*!
 * Pomelo
 * Copyright(c) 2012 xiechengchao <xiecc@163.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var fs = require('fs');
var path = require('path');
var application = require('./application');

/**
 * Expose `createApplication()`.
 *
 * @module
 */

var Pomelo = module.exports = {};

/**
 * Framework version.
 */

Pomelo.version = '0.8';

/**
 * Event definitions that would be emitted by app.event
 */
Pomelo.events = require('./util/events');

/**
 * auto loaded components
 */
Pomelo.components = {};

/**
 * auto loaded filters
 */
Pomelo.filters = {};

/**
 * auto loaded rpc filters
 */
Pomelo.rpcFilters = {};

/**
 * connectors
 */
Pomelo.connectors = {};
Pomelo.connectors.__defineGetter__('sioconnector', function() {
  return require('./connectors/sioconnector');
});

Pomelo.connectors.__defineGetter__('hybridconnector', function() {
  return require('./connectors/hybridconnector');
});

/**
 * pushSchedulers
 */
Pomelo.pushSchedulers = {};
Pomelo.pushSchedulers.__defineGetter__('direct', function() {
  return require('./pushSchedulers/direct');
});

Pomelo.pushSchedulers.__defineGetter__('buffer', function() {
  return require('./pushSchedulers/buffer');
});

var self = this;

/**
 * Create an pomelo application.
 *
 * @return {Application}
 * @memberOf Pomelo
 * @api public
 */
Pomelo.createApp = function (opts) {
  var app = application;
  app.init(opts);
  self.app = app;
  return app;
};

/**
 * Get application
 */
Object.defineProperty(Pomelo, 'app', {
  get:function () {
    return self.app;
  }
});

/**
 * Auto-load bundled components with getters.
 */
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');

  function load() {
    return require('./components/' + name);
  }
  Pomelo.components.__defineGetter__(name, load);
  Pomelo.__defineGetter__(name, load);
});

fs.readdirSync(__dirname + '/filters/handler').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');

  function load() {
    return require('./filters/handler/' + name);
  }
  Pomelo.filters.__defineGetter__(name, load);
  Pomelo.__defineGetter__(name, load);
});

fs.readdirSync(__dirname + '/filters/rpc').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');

  function load() {
    return require('./filters/rpc/' + name);
  }
  Pomelo.rpcFilters.__defineGetter__(name, load);
  Pomelo.__defineGetter__(name, load);
});<commit_msg>remove rpc filter load pomelo<commit_after>/*!
 * Pomelo
 * Copyright(c) 2012 xiechengchao <xiecc@163.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var fs = require('fs');
var path = require('path');
var application = require('./application');

/**
 * Expose `createApplication()`.
 *
 * @module
 */

var Pomelo = module.exports = {};

/**
 * Framework version.
 */

Pomelo.version = '0.8';

/**
 * Event definitions that would be emitted by app.event
 */
Pomelo.events = require('./util/events');

/**
 * auto loaded components
 */
Pomelo.components = {};

/**
 * auto loaded filters
 */
Pomelo.filters = {};

/**
 * auto loaded rpc filters
 */
Pomelo.rpcFilters = {};

/**
 * connectors
 */
Pomelo.connectors = {};
Pomelo.connectors.__defineGetter__('sioconnector', function() {
  return require('./connectors/sioconnector');
});

Pomelo.connectors.__defineGetter__('hybridconnector', function() {
  return require('./connectors/hybridconnector');
});

/**
 * pushSchedulers
 */
Pomelo.pushSchedulers = {};
Pomelo.pushSchedulers.__defineGetter__('direct', function() {
  return require('./pushSchedulers/direct');
});

Pomelo.pushSchedulers.__defineGetter__('buffer', function() {
  return require('./pushSchedulers/buffer');
});

var self = this;

/**
 * Create an pomelo application.
 *
 * @return {Application}
 * @memberOf Pomelo
 * @api public
 */
Pomelo.createApp = function (opts) {
  var app = application;
  app.init(opts);
  self.app = app;
  return app;
};

/**
 * Get application
 */
Object.defineProperty(Pomelo, 'app', {
  get:function () {
    return self.app;
  }
});

/**
 * Auto-load bundled components with getters.
 */
fs.readdirSync(__dirname + '/components').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');

  function load() {
    return require('./components/' + name);
  }
  Pomelo.components.__defineGetter__(name, load);
  Pomelo.__defineGetter__(name, load);
});

fs.readdirSync(__dirname + '/filters/handler').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');

  function load() {
    return require('./filters/handler/' + name);
  }
  Pomelo.filters.__defineGetter__(name, load);
  Pomelo.__defineGetter__(name, load);
});

fs.readdirSync(__dirname + '/filters/rpc').forEach(function (filename) {
  if (!/\.js$/.test(filename)) {
    return;
  }
  var name = path.basename(filename, '.js');

  function load() {
    return require('./filters/rpc/' + name);
  }
  Pomelo.rpcFilters.__defineGetter__(name, load);
});<|endoftext|>


<commit_before>var gm = require('gm') //.subClass({imageMagick: true});
var http = require("http");
var fs = require("fs");
var path = require("path");
var async = require("async");

// -----------------------------------------------------------------
function log(info) {
	console.log('--------------------------');
	for(var i = 0; i < arguments.length; i++) {
		console.log(arguments[i]);
	}
}

// -----------------------------------------------------------------

function Poster(){

};

module.exports = Poster;
// -----------------------------------------------------------------

function strlen(str){
	var s=0; // 汉字个数
	var  t = 0; // 非汉字个数
	for(var i=0;i<str.length;i++){
		if(str.charAt(i)>'~'){
			s++;
		} else {
			t++;
		}
	}
	return {s: s, t: t};
}

function HandleX(s, t) {
	var posterWidth = 750;
	var posterHeigth = 1334;
	var wordLength = 57;

	var length1 = s * wordLength + Math.floor((t) /2) * wordLength;
	x = (posterWidth - length1) / 2
	return x;
}

// 需要的参数：
// 1. 原始图片
// 2. 用户图片
// 3. 用户名称
// 4. 用户关键字

Poster.prototype.fillWord = function(options, callback) {
	log("fillWord: Hi, I am called!");
	log(options)
	if(!options
		|| !options.hasOwnProperty("imagePath")
		|| !options.hasOwnProperty("username")) {
		log("fillWord: options illegal", options);
	} else {
		var _image = path.join(__dirname, '../img/target/', Math.random().toString(36).substr(2) + ".jpg");

		var imagePath = options.imagePath || "../img/canvas1.jpg";
		var username = options.username || "开业啦！";
		log("imagePath: " + imagePath, "username: " + username)

		var x = 250;
		var y = 440;
		var wordLength = 57;

		var s = strlen(username).s;
		var t = strlen(username).t;

		if((s + t/2) <= 10) {
			x = HandleX(s, t);

			gm(imagePath)
			.stroke("#FFCD5C")
			.fill("#FFCD5C")
			.font("microsoftyahei.ttf", wordLength)
			.drawText(x, y + 70, username)
			.font("microsoftyahei.ttf", wordLength)
			.write(_image, function (err) {
			  if(err) {
			  	log("fillWord: write file error", err);
			  	callback(err, null);
			  } else {
			  	log("fillWord: write file succeed");
			  	callback(err, _image);
			  }
			});
		} else {
			str1 = username.slice(0, 10);
			str2 = username.slice(10,20);

			var s1 = strlen(str1).s;
			var t1 = strlen(str1).t;

			x1 = HandleX(s1, t1);

			var s2 = strlen(str2).s;
			var t2 = strlen(str2).t;
			x2 = HandleX(s2, t2);

			gm(imagePath)
			.stroke("#FFCD5C")
			.fill("#FFCD5C")
			.font("microsoftyahei.ttf", wordLength)
			.drawText(x1, y +70, str1)
			.font("microsoftyahei.ttf", wordLength)
			.drawText(x2, y + 140, str2)
			.write(_image, function (err) {
			  if(err) {
			  	log("fillWord: write file error", err);
			  	callback(err, null);
			  } else {
			  	log("fillWord: write file succeed");
			  	callback(err, _image);
			  }
			});
		}
	}
}

// -----------------------------------------------------------------

Poster.prototype.handlePhoto = function(options, callback) {
	log("handlePhoto: Hi, I am called!");

	if(!options
		|| !options.hasOwnProperty("image")
		|| !options.hasOwnProperty("userimage")) {
		log("handlePhoto: options illegal", options);
		callback("handlePhoto: options illegal", null);
	} else {
		var image = options.image; // 背景图片
		var userimage = options.userimage; // 用户图像
		// 合成图片
		gm(userimage)
		.resize(250, 250)
		.compress("jpeg")
		.write(userimage, function(err) {
			if(err) {
				log("handlePhoto: resize user image error", err);
				callback("handlePhoto: resize user image error", null);
			} else {
				gm()
				.in('-page', '+0+0')  // Custom place for each of the images
				.in(image)
				.in('-page', '+250+150')
				.in(userimage)
				.quality(100)
				.minify()
				.mosaic()  // Merges the images as a matrix
				.write(image, function (err) {
				    if (err) {
				    	log("handlePhoto: handle photo error", err);
				    	callback(err);
				    } else {
				    	log("handlePhoto: handle photo succeed.");
				    	callback(null);
				    }
				});
			}
		})
	}
}

// -----------------------------------------------------------------

Poster.prototype.getWxUserImg = function(options, callback) {
	log("getWxUserImg: Hi, I am called.");

	if(!options
		|| !options.hasOwnProperty("headimgurl")
		|| !options.hasOwnProperty("openid")) {
		log("getWxUserImg: options illegal", options);
		callback("getWxUserImg: options illegal", null);
	} else {
		var headimgurl = options.headimgurl;
		var openid = options.openid;
		var userimg = path.join(__dirname, '../img/target/', openid + '.jpg');

		// 下载图片
		http.get(headimgurl, function(res){
	    var imgData = "";

	    //一定要设置response的编码为binary否则会下载下来的图片打不开
	    res.setEncoding("binary");

	    res.on("data", function(chunk){
        imgData+=chunk;
	    });

	    res.on("end", function(){
        fs.writeFile(userimg, imgData, "binary", function(err){
          if(err){
            log("getWxUserImg: write file error", err);
            callback(err, null);
          } else {
          	log("getWxUserImg: download file succeed");
          	var userOptions = {
          		userimg: userimg
          	}
          	callback(null, userOptions);
          }
        });
	    });
		});
	}
}

// -----------------------------------------------------------------

Poster.prototype.MergeImage = function(options, callback) {
	log("MergeImage: I am called.");
	if(!options
		|| !options.hasOwnProperty("openid") // wx user openid
		|| !options.hasOwnProperty("headimgurl") // wx user head image url
		|| !options.hasOwnProperty("username") // wx user name
		|| !options.hasOwnProperty("imagePath")) { //background image path
		log("MergeImage: options illegal", options);
		callback("MergeImage: options illegal", null);
	} else {
		var self = this;

		var openid =options.openid;
		var username = options.username;
		var imagePath = options.imagePath;
		var headimgurl = options.headimgurl;

		var wxImgOptions = {
			openid: openid,
			headimgurl: headimgurl
		}

		// 获取用户图片等信息
		self.getWxUserImg(wxImgOptions, function(err, imgInfo) {
			if(err) {
				log("MergeImage: get user wximage headimg error", err);
				callback(err, null);
			} else {
				var userimg = imgInfo.userimg;

				var fillWordOptions = {
					imagePath: imagePath,
					username: username
				};
				// 图片水印
				self.fillWord(fillWordOptions, function(err, image) {
					if(err) {
						log("MergeImage: fillword error", err);
						callback(err, null);
					} else {
						handlePhotoOptions = {
							image: image,
							userimage:userimg
						}
						// 合成用户图片和背景图片
						self.handlePhoto(handlePhotoOptions, function(err) {
							if(err) {
								log("MergeImage: handle photo error", err);
								callback(err, null);
							} else {
								log("MergeImage: handle photo succeed.");
								callback(null, {
									image: image
								})
							}
						})
					}
				})
			}
		})
	}
}
// -----------------------------------------------------------------

Poster.prototype.RemoveUserImg = function(options, callback) {
	log("RemoveUserImg: Hi, I am called!");

	if(!options
		|| !options.hasOwnProperty("imgLists")
		|| !(options.imgLists instanceof Array)) {
		log("RemoveUserImg: options illegal", options);
		callback("RemoveUserImg: options illegal");
	} else {
		var imgLists = options.imgLists;
		log(imgLists);

		async.each(imgLists, function(list) {
			fs.unlink(list, function(err) {
				if(err) {
					log("RemoveUserImg: error remove file " + list, err);
					done(err);
				} else {
					log("RemoveUserImg: succeed remove file " + list);
					done();
				}
			})
		}, function(err) {
			if(err) {
				log("RemoveUserImg: remove file error", err);
				callback(err);
			} else {
				log("RemoveUserImg: remove file succeed.");
				callback(null);
			}
		})
	}
}

// -----------------------------------------------------------------
<commit_msg>change<commit_after>var gm = require('gm') //.subClass({imageMagick: true});
var http = require("http");
var fs = require("fs");
var path = require("path");
var async = require("async");

// -----------------------------------------------------------------
function log(info) {
	console.log('--------------------------');
	for(var i = 0; i < arguments.length; i++) {
		console.log(arguments[i]);
	}
}

// -----------------------------------------------------------------

function Poster(){

};

module.exports = Poster;
// -----------------------------------------------------------------

function strlen(str){
	var s=0; // 汉字个数
	var  t = 0; // 非汉字个数
	for(var i=0;i<str.length;i++){
		if(str.charAt(i)>'~'){
			s++;
		} else {
			t++;
		}
	}
	return {s: s, t: t};
}

function HandleX(s, t) {
	var posterWidth = 750;
	var posterHeigth = 1334;
	var wordLength = 57;

	var length1 = s * wordLength + Math.floor((t) /2) * wordLength;
	x = (posterWidth - length1) / 2
	return x;
}

// 需要的参数：
// 1. 原始图片
// 2. 用户图片
// 3. 用户名称
// 4. 用户关键字

Poster.prototype.fillWord = function(options, callback) {
	log("fillWord: Hi, I am called!");
	log(options)
	if(!options
		|| !options.hasOwnProperty("imagePath")
		|| !options.hasOwnProperty("username")) {
		log("fillWord: options illegal", options);
	} else {
		var _image = path.join(__dirname, '../img/target/', Math.random().toString(36).substr(2) + ".jpg");

		var imagePath = options.imagePath || "../img/canvas1.jpg";
		var username = options.username || "开业啦！";
		log("imagePath: " + imagePath, "username: " + username)

		var x = 250;
		var y = 440;
		var wordLength = 57;

		var s = strlen(username).s;
		var t = strlen(username).t;

		if((s + t/2) <= 10) {
			x = HandleX(s, t);

			gm(imagePath)
			.stroke("#FFCD5C")
			.fill("#FFCD5C")
			.font("microsoftyahei.ttf", wordLength)
			.drawText(x, y + 70, username)
			.write(_image, function (err) {
			  if(err) {
			  	log("fillWord: write file error", err);
			  	callback(err, null);
			  } else {
			  	log("fillWord: write file succeed");
			  	callback(err, _image);
			  }
			});
		} else {
			str1 = username.slice(0, 10);
			str2 = username.slice(10,20);

			var s1 = strlen(str1).s;
			var t1 = strlen(str1).t;

			x1 = HandleX(s1, t1);

			var s2 = strlen(str2).s;
			var t2 = strlen(str2).t;
			x2 = HandleX(s2, t2);

			gm(imagePath)
			.stroke("#FFCD5C")
			.fill("#FFCD5C")
			.font("microsoftyahei.ttf", wordLength)
			.drawText(x1, y +70, str1)
			.font("microsoftyahei.ttf", wordLength)
			.drawText(x2, y + 140, str2)
			.write(_image, function (err) {
			  if(err) {
			  	log("fillWord: write file error", err);
			  	callback(err, null);
			  } else {
			  	log("fillWord: write file succeed");
			  	callback(err, _image);
			  }
			});
		}
	}
}

// -----------------------------------------------------------------

Poster.prototype.handlePhoto = function(options, callback) {
	log("handlePhoto: Hi, I am called!");

	if(!options
		|| !options.hasOwnProperty("image")
		|| !options.hasOwnProperty("userimage")) {
		log("handlePhoto: options illegal", options);
		callback("handlePhoto: options illegal", null);
	} else {
		var image = options.image; // 背景图片
		var userimage = options.userimage; // 用户图像
		// 合成图片
		gm(userimage)
		.resize(250, 250)
		.compress("jpeg")
		.write(userimage, function(err) {
			if(err) {
				log("handlePhoto: resize user image error", err);
				callback("handlePhoto: resize user image error", null);
			} else {
				gm()
				.in('-page', '+0+0')  // Custom place for each of the images
				.in(image)
				.in('-page', '+250+150')
				.in(userimage)
				.quality(100)
				.minify()
				.mosaic()  // Merges the images as a matrix
				.write(image, function (err) {
				    if (err) {
				    	log("handlePhoto: handle photo error", err);
				    	callback(err);
				    } else {
				    	log("handlePhoto: handle photo succeed.");
				    	callback(null);
				    }
				});
			}
		})
	}
}

// -----------------------------------------------------------------

Poster.prototype.getWxUserImg = function(options, callback) {
	log("getWxUserImg: Hi, I am called.");

	if(!options
		|| !options.hasOwnProperty("headimgurl")
		|| !options.hasOwnProperty("openid")) {
		log("getWxUserImg: options illegal", options);
		callback("getWxUserImg: options illegal", null);
	} else {
		var headimgurl = options.headimgurl;
		var openid = options.openid;
		var userimg = path.join(__dirname, '../img/target/', openid + '.jpg');

		// 下载图片
		http.get(headimgurl, function(res){
	    var imgData = "";

	    //一定要设置response的编码为binary否则会下载下来的图片打不开
	    res.setEncoding("binary");

	    res.on("data", function(chunk){
        imgData+=chunk;
	    });

	    res.on("end", function(){
        fs.writeFile(userimg, imgData, "binary", function(err){
          if(err){
            log("getWxUserImg: write file error", err);
            callback(err, null);
          } else {
          	log("getWxUserImg: download file succeed");
          	var userOptions = {
          		userimg: userimg
          	}
          	callback(null, userOptions);
          }
        });
	    });
		});
	}
}

// -----------------------------------------------------------------

Poster.prototype.MergeImage = function(options, callback) {
	log("MergeImage: I am called.");
	if(!options
		|| !options.hasOwnProperty("openid") // wx user openid
		|| !options.hasOwnProperty("headimgurl") // wx user head image url
		|| !options.hasOwnProperty("username") // wx user name
		|| !options.hasOwnProperty("imagePath")) { //background image path
		log("MergeImage: options illegal", options);
		callback("MergeImage: options illegal", null);
	} else {
		var self = this;

		var openid =options.openid;
		var username = options.username;
		var imagePath = options.imagePath;
		var headimgurl = options.headimgurl;

		var wxImgOptions = {
			openid: openid,
			headimgurl: headimgurl
		}

		// 获取用户图片等信息
		self.getWxUserImg(wxImgOptions, function(err, imgInfo) {
			if(err) {
				log("MergeImage: get user wximage headimg error", err);
				callback(err, null);
			} else {
				var userimg = imgInfo.userimg;

				var fillWordOptions = {
					imagePath: imagePath,
					username: username
				};
				// 图片水印
				self.fillWord(fillWordOptions, function(err, image) {
					if(err) {
						log("MergeImage: fillword error", err);
						callback(err, null);
					} else {
						handlePhotoOptions = {
							image: image,
							userimage:userimg
						}
						// 合成用户图片和背景图片
						self.handlePhoto(handlePhotoOptions, function(err) {
							if(err) {
								log("MergeImage: handle photo error", err);
								callback(err, null);
							} else {
								log("MergeImage: handle photo succeed.");
								callback(null, {
									image: image
								})
							}
						})
					}
				})
			}
		})
	}
}
// -----------------------------------------------------------------

Poster.prototype.RemoveUserImg = function(options, callback) {
	log("RemoveUserImg: Hi, I am called!");

	if(!options
		|| !options.hasOwnProperty("imgLists")
		|| !(options.imgLists instanceof Array)) {
		log("RemoveUserImg: options illegal", options);
		callback("RemoveUserImg: options illegal");
	} else {
		var imgLists = options.imgLists;
		log(imgLists);

		async.each(imgLists, function(list) {
			fs.unlink(list, function(err) {
				if(err) {
					log("RemoveUserImg: error remove file " + list, err);
					done(err);
				} else {
					log("RemoveUserImg: succeed remove file " + list);
					done();
				}
			})
		}, function(err) {
			if(err) {
				log("RemoveUserImg: remove file error", err);
				callback(err);
			} else {
				log("RemoveUserImg: remove file succeed.");
				callback(null);
			}
		})
	}
}

// -----------------------------------------------------------------
<|endoftext|>


<commit_before>// pub-sub functions for team interaction
/*jslint node: true */

'use strict';

var indexer = require('../lib/indexer');
var utils = require('../lib/utils');
var faye = require('faye'), fs = require('fs');
var bayeux = new faye.NodeAdapter({mount: GLOBAL.config.FAYEMOUNT, timeout: 45});
var fayeClient = new faye.Client(GLOBAL.config.FAYEHOST);
var annotations = require('./annotations.js'), contentLib = require('./content'), searchLib = require('./search.js');

exports.requestAnnotate = requestAnnotate;
exports.start = start;
exports.publish = publish;

function start(server) {
  bayeux.attach(server);

// user functions

  fayeClient.subscribe('/team/list', function(data) {
    GLOBAL.info('team/list'.yellow, data);
    fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
  });

  fayeClient.subscribe('/team/remove', function(data) {
    GLOBAL.info('team/remove'.yellow, data);
    for (var i = 0; i < GLOBAL.config.users.length; i++) {
      if (GLOBAL.config.users[i].username == data.username) {
        GLOBAL.config.users.splice(i, 1);
        saveUsers();
        fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
      }
    }
  });

  fayeClient.subscribe('/team/save', function(data) {
    GLOBAL.info('team/save'.yellow, data);
    for (var i = 0; i < GLOBAL.config.users.length; i++) {
      if (GLOBAL.config.users[i].username == data.username) {
        GLOBAL.config.users[i] = data;
        saveUsers();
        fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
      }
    }
  });

  fayeClient.subscribe('/team/add', function(data) {
    GLOBAL.info('team/add'.yellow, data);
    if (data.name && data.type) {
      saveUsers(function() {
        GLOBAL.config.users.push({id : new Date().getTime(), active: new Date(), username : data.name, type: data.type});
      });
      fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
    }
  });

// FIXME log signouts
  fayeClient.subscribe('/logout', function(data) {
    console.log('/logout', data);
  });

// annotations

// retrieve annotations for an individual uri
  fayeClient.subscribe('/annotate', function(data) {
    GLOBAL.info('/annotate'.yellow, data);
    // FIXME deal with anchors
    indexer.retrieveByURI(data.uri.replace(/#.*$/, ''), function(err, res) {
      if (err) {
        GLOBAL.error('/annotate error' + err);
      } else {
        fayeClient.publish('/annotations/' + data.clientID, { uri: data.uri, annotations: res._source.annotations});
      }
    });
  });

  // index incoming annotations, adding validated state
  fayeClient.subscribe('/saveAnnotations', function(combo) {
    GLOBAL.info('saveAnnotations'.yellow, combo);
    var uris = combo.uris || [combo.uri];
    try {
      uris.forEach(function(uri) {
        var annos = [];
    // apply validation state based on user
        combo.annotations.forEach(function(p) {
          var by = p.annotatedBy || combo.annotatedBy;
          p.state = utils.states.annotations.unvalidated;
          GLOBAL.config.users.forEach(function(u) {
            if (u.username === by) {
              p.annotatedBy = by;
              if (!u.needsValidation) {
                p.state = utils.states.annotations.validated;
              }
            }
          });
          p.hasTarget = uri;
          var a = annotations.createAnnotation(p);
          annos.push(a);
        });

        GLOBAL.config.indexer.saveAnnotations(uri, annos, function(err, res, cItem) {
          if (cItem) {
            fayeClient.publish('/annotations/' + combo.clientID, { uri: uri, annotationSummary: cItem.annotationSummary, annotations: cItem.annotations});
          } else {
            GLOBAL.error('missing cItem');
          }
        });
      });
    } catch (err) {
      console.log('saveAnnotations failed', err);
    }
  });

  // form query
  fayeClient.subscribe('/query', function(data) {
    GLOBAL.info('/query'.yellow, data.clientID);
    GLOBAL.config.indexer.formQuery(data, function(err, results) {
      if (err) {
        GLOBAL.error('/query query failed', JSON.stringify((results || {}).query, null, 2));
        return;
      }
      if (results.hits) {
        // create annotation overview
        if (data.annotations) {
          var root = { name : 'Annotations', size: 0, children: [], items: [] };
          // build a size of hierarchical annotations
          results.hits.hits.forEach(function(hit) {
            (hit._source.annotations || []).forEach(function(anno) {
              // for each start at the root
              var last = root;
              // iterate through its annotations
              if (!anno.position) {
                GLOBAL.error('missing anno position', anno);
              } else {
                anno.position.forEach(function(p) {
                  var cur;
                  // find its parent
                  last.children.forEach(function(c) {
                    if (c.name === p) {
                      cur = c;
                      return;
                    }
                  });
                  // or create it
                  if (!cur) {
                    last.children.push({name: p, size: 0, children: [], items: []});
                    cur = last.children[last.children.length - 1];
                  }
                  // increment its instances
                  cur.size += 1;
                  // add uri for item selecting
                  if (cur.items.indexOf(hit._source.uri) < 0) {
                    cur.items.push(hit._source.uri);
                  }
                  // use it as the basis for the cur up
                  last = cur;
                });
              }
              // remove raw annotations
              delete hit._source.annotations;
            });
          });
          results.annotationOverview = root;
        }
        GLOBAL.info('queryResults'.yellow, results.hits.hits.length);
        fayeClient.publish('/queryResults/' + data.clientID, results);
      } else {
        fayeClient.publish('/queryResults/' + data.clientID, []);
        GLOBAL.debug('no query results');
      }
    }, data.annotations ? ({ sourceFields : indexer.sourceFields.concat(['annotations.' + data.annotations])}) : null);
  });

  // form cluster
  fayeClient.subscribe('/cluster', function(data) {
    GLOBAL.info('/cluster'.yellow, data);
    GLOBAL.config.indexer.formCluster(data, function(err, results) {
      if (results) {
        GLOBAL.info('  /clusterResults'.yellow, err, results.hits.hits.length);
        fayeClient.publish('/clusterResults/' + data.clientID, results);
      } else {
        fayeClient.publish('/clusterResults/' + data.clientID, []);
        GLOBAL.debug('no cluster results');
      }
    });
  });

  // update content of existing item by uri
  fayeClient.subscribe('/updateContent', function(desc) {
    GLOBAL.info('/updateContent'.yellow, desc.uri);
    contentLib.indexContentItem(desc, {member: desc.clientID.split(':')[0], isHTML: true});
  });

  // retrieve an existing content item by uri
  fayeClient.subscribe('/getContentItem', function(uri) {
    GLOBAL.info('/getContentItem'.yellow, uri);
    indexer.retrieveByURI(uri, function(err, result) {
      fayeClient.publish('/updateItem', result);
    });
  });

// delete a document
  fayeClient.subscribe('/delete', function(cItemURIs) {
    GLOBAL.info('/delete'.yellow, cItemURIs);
    indexer.deleteContentItems(cItemURIs.selected, function(err, res) {
      if (err) {
        GLOBAL.error('/delete', err, res);
      } else {
        fayeClient.publish('/deletedItem', {_id: res._id});
      }
    });
  });

// query moreLikeThis on document
  fayeClient.subscribe('/moreLikeThis', function(data) {
    GLOBAL.info('/moreLikeThis'.yellow, data);
    GLOBAL.config.indexer.moreLikeThis(data.uri, function(err, results) {
      if (results) {
        GLOBAL.info('moreLikeThisResults', err, results.hits.hits.length);
        fayeClient.publish('/queryResults/' + data.clientID, results);
      } else {
        fayeClient.publish('/queryResults/' + data.clientID, []);
        GLOBAL.debug('NO RESULTS');
      }
    });
  });

// find existing searches
  fayeClient.subscribe('/search/retrieve', function(data) {
    GLOBAL.info('/search/retrieve'.yellow, data);
    publishRetrievedSearches();
  });

// save a search
  fayeClient.subscribe('/search/save', function(search) {
    GLOBAL.info('/search/save'.yellow, search);
    indexer.saveSearch(search, function(err, res) {
    publishRetrievedSearches();
    });
  });

// remove a search
  fayeClient.subscribe('/search/remove', function(data) {
    GLOBAL.info('/search/remove'.yellow, data);
  });

// subscribe to query requests
  fayeClient.subscribe('/search/queue', function(data) {
    console.log('/search/queue'.yellow, data);
    searchLib.queueSearcher(data);
  });
}

// Publish an arbitary message
function publish(channel, message) {
  fayeClient.publish(channel, message);
}

// updates clients with a content item
exports.updateItem = function(cItem) {
  fayeClient.publish('/updateItem', cItem);
};

// requests annotators to annotate. pass plain text or it will be extracted from cItem
function requestAnnotate(cItem) {
  GLOBAL.info('selector', cItem.uri, cItem.selector, cItem.content ? cItem.content.length : 'nocontent');
  fayeClient.publish('/requestAnnotate', cItem);
}

function publishRetrievedSearches(filter) {
  indexer.retrieveSearches(filter, function(err, results) {
    fayeClient.publish('/search/results', results);
  });
}

function saveUsers(istep) {
  fs.writeFileSync('versions/local-users.' + new Date().getTime() + '.json', JSON.stringify({ logins : GLOBAL.config.users}, null, 2));
  if (istep) { istep(); }
  fs.writeFileSync('local-users.json', JSON.stringify({ logins : GLOBAL.config.users}, null, 2));
}
<commit_msg>watch for missing clientID<commit_after>// pub-sub functions for team interaction
/*jslint node: true */

'use strict';

var indexer = require('../lib/indexer');
var utils = require('../lib/utils');
var faye = require('faye'), fs = require('fs');
var bayeux = new faye.NodeAdapter({mount: GLOBAL.config.FAYEMOUNT, timeout: 45});
var fayeClient = new faye.Client(GLOBAL.config.FAYEHOST);
var annotations = require('./annotations.js'), contentLib = require('./content'), searchLib = require('./search.js');

exports.requestAnnotate = requestAnnotate;
exports.start = start;
exports.publish = publish;

function start(server) {
  bayeux.attach(server);

// user functions

  fayeClient.subscribe('/team/list', function(data) {
    GLOBAL.info('team/list'.yellow, data);
    fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
  });

  fayeClient.subscribe('/team/remove', function(data) {
    GLOBAL.info('team/remove'.yellow, data);
    for (var i = 0; i < GLOBAL.config.users.length; i++) {
      if (GLOBAL.config.users[i].username == data.username) {
        GLOBAL.config.users.splice(i, 1);
        saveUsers();
        fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
      }
    }
  });

  fayeClient.subscribe('/team/save', function(data) {
    GLOBAL.info('team/save'.yellow, data);
    for (var i = 0; i < GLOBAL.config.users.length; i++) {
      if (GLOBAL.config.users[i].username == data.username) {
        GLOBAL.config.users[i] = data;
        saveUsers();
        fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
      }
    }
  });

  fayeClient.subscribe('/team/add', function(data) {
    GLOBAL.info('team/add'.yellow, data);
    if (data.name && data.type) {
      saveUsers(function() {
        GLOBAL.config.users.push({id : new Date().getTime(), active: new Date(), username : data.name, type: data.type});
      });
      fayeClient.publish('/teamList/' + data.clientID, GLOBAL.config.users);
    }
  });

// FIXME log signouts
  fayeClient.subscribe('/logout', function(data) {
    console.log('/logout', data);
  });

// annotations

// retrieve annotations for an individual uri
  fayeClient.subscribe('/annotate', function(data) {
    GLOBAL.info('/annotate'.yellow, data);
    // FIXME deal with anchors
    indexer.retrieveByURI(data.uri.replace(/#.*$/, ''), function(err, res) {
      if (err) {
        GLOBAL.error('/annotate error' + err);
      } else {
        fayeClient.publish('/annotations/' + data.clientID, { uri: data.uri, annotations: res._source.annotations});
      }
    });
  });

  // index incoming annotations, adding validated state
  fayeClient.subscribe('/saveAnnotations', function(combo) {
    GLOBAL.info('saveAnnotations'.yellow, combo);
    var uris = combo.uris || [combo.uri];
    try {
      uris.forEach(function(uri) {
        var annos = [];
    // apply validation state based on user
        combo.annotations.forEach(function(p) {
          var by = p.annotatedBy || combo.annotatedBy;
          p.state = utils.states.annotations.unvalidated;
          GLOBAL.config.users.forEach(function(u) {
            if (u.username === by) {
              p.annotatedBy = by;
              if (!u.needsValidation) {
                p.state = utils.states.annotations.validated;
              }
            }
          });
          p.hasTarget = uri;
          var a = annotations.createAnnotation(p);
          annos.push(a);
        });

        GLOBAL.config.indexer.saveAnnotations(uri, annos, function(err, res, cItem) {
          if (cItem) {
            fayeClient.publish('/annotations/' + combo.clientID, { uri: uri, annotationSummary: cItem.annotationSummary, annotations: cItem.annotations});
          } else {
            GLOBAL.error('missing cItem');
          }
        });
      });
    } catch (err) {
      console.log('saveAnnotations failed', err);
    }
  });

  // form query
  fayeClient.subscribe('/query', function(data) {
    GLOBAL.info('/query'.yellow, data.clientID);
    GLOBAL.config.indexer.formQuery(data, function(err, results) {
      if (err) {
        GLOBAL.error('/query query failed', JSON.stringify((results || {}).query, null, 2));
        return;
      }
      if (results.hits) {
        // create annotation overview
        if (data.annotations) {
          var root = { name : 'Annotations', size: 0, children: [], items: [] };
          // build a size of hierarchical annotations
          results.hits.hits.forEach(function(hit) {
            (hit._source.annotations || []).forEach(function(anno) {
              // for each start at the root
              var last = root;
              // iterate through its annotations
              if (!anno.position) {
                GLOBAL.error('missing anno position', anno);
              } else {
                anno.position.forEach(function(p) {
                  var cur;
                  // find its parent
                  last.children.forEach(function(c) {
                    if (c.name === p) {
                      cur = c;
                      return;
                    }
                  });
                  // or create it
                  if (!cur) {
                    last.children.push({name: p, size: 0, children: [], items: []});
                    cur = last.children[last.children.length - 1];
                  }
                  // increment its instances
                  cur.size += 1;
                  // add uri for item selecting
                  if (cur.items.indexOf(hit._source.uri) < 0) {
                    cur.items.push(hit._source.uri);
                  }
                  // use it as the basis for the cur up
                  last = cur;
                });
              }
              // remove raw annotations
              delete hit._source.annotations;
            });
          });
          results.annotationOverview = root;
        }
        GLOBAL.info('queryResults'.yellow, results.hits.hits.length);
        fayeClient.publish('/queryResults/' + data.clientID, results);
      } else {
        fayeClient.publish('/queryResults/' + data.clientID, []);
        GLOBAL.debug('no query results');
      }
    }, data.annotations ? ({ sourceFields : indexer.sourceFields.concat(['annotations.' + data.annotations])}) : null);
  });

  // form cluster
  fayeClient.subscribe('/cluster', function(data) {
    GLOBAL.info('/cluster'.yellow, data);
    GLOBAL.config.indexer.formCluster(data, function(err, results) {
      if (results) {
        GLOBAL.info('  /clusterResults'.yellow, err, results.hits.hits.length);
        fayeClient.publish('/clusterResults/' + data.clientID, results);
      } else {
        fayeClient.publish('/clusterResults/' + data.clientID, []);
        GLOBAL.debug('no cluster results');
      }
    });
  });

  // update content of existing item by uri
  fayeClient.subscribe('/updateContent', function(desc) {
    if (!desc.clientID) {
      GLOBAL.error('missing clientID', desc);
      return;
    }
    GLOBAL.info('/updateContent'.yellow, desc.uri);
    contentLib.indexContentItem(desc, {member: desc.clientID.split(':')[0], isHTML: true});
  });

  // retrieve an existing content item by uri
  fayeClient.subscribe('/getContentItem', function(uri) {
    GLOBAL.info('/getContentItem'.yellow, uri);
    indexer.retrieveByURI(uri, function(err, result) {
      fayeClient.publish('/updateItem', result);
    });
  });

// delete a document
  fayeClient.subscribe('/delete', function(cItemURIs) {
    GLOBAL.info('/delete'.yellow, cItemURIs);
    indexer.deleteContentItems(cItemURIs.selected, function(err, res) {
      if (err) {
        GLOBAL.error('/delete', err, res);
      } else {
        fayeClient.publish('/deletedItem', {_id: res._id});
      }
    });
  });

// query moreLikeThis on document
  fayeClient.subscribe('/moreLikeThis', function(data) {
    GLOBAL.info('/moreLikeThis'.yellow, data);
    GLOBAL.config.indexer.moreLikeThis(data.uri, function(err, results) {
      if (results) {
        GLOBAL.info('moreLikeThisResults', err, results.hits.hits.length);
        fayeClient.publish('/queryResults/' + data.clientID, results);
      } else {
        fayeClient.publish('/queryResults/' + data.clientID, []);
        GLOBAL.debug('NO RESULTS');
      }
    });
  });

// find existing searches
  fayeClient.subscribe('/search/retrieve', function(data) {
    GLOBAL.info('/search/retrieve'.yellow, data);
    publishRetrievedSearches();
  });

// save a search
  fayeClient.subscribe('/search/save', function(search) {
    GLOBAL.info('/search/save'.yellow, search);
    indexer.saveSearch(search, function(err, res) {
    publishRetrievedSearches();
    });
  });

// remove a search
  fayeClient.subscribe('/search/remove', function(data) {
    GLOBAL.info('/search/remove'.yellow, data);
  });

// subscribe to query requests
  fayeClient.subscribe('/search/queue', function(data) {
    console.log('/search/queue'.yellow, data);
    searchLib.queueSearcher(data);
  });
}

// Publish an arbitary message
function publish(channel, message) {
  fayeClient.publish(channel, message);
}

// updates clients with a content item
exports.updateItem = function(cItem) {
  fayeClient.publish('/updateItem', cItem);
};

// requests annotators to annotate. pass plain text or it will be extracted from cItem
function requestAnnotate(cItem) {
  GLOBAL.info('selector', cItem.uri, cItem.selector, cItem.content ? cItem.content.length : 'nocontent');
  fayeClient.publish('/requestAnnotate', cItem);
}

function publishRetrievedSearches(filter) {
  indexer.retrieveSearches(filter, function(err, results) {
    fayeClient.publish('/search/results', results);
  });
}

function saveUsers(istep) {
  fs.writeFileSync('versions/local-users.' + new Date().getTime() + '.json', JSON.stringify({ logins : GLOBAL.config.users}, null, 2));
  if (istep) { istep(); }
  fs.writeFileSync('local-users.json', JSON.stringify({ logins : GLOBAL.config.users}, null, 2));
}
<|endoftext|>


<commit_before>module.exports = (function() {
  var crypto = require('crypto');
  var request = require('request');

  var Pusher = function(options) {
    this.options = options;

    // support appKey being provided instead of key for legacy support
    if( this.options.appKey ) {
      this.options.key = this.options.appKey;
      delete this.options.appKey;
    }

    return this;
  };

  /**
   * @private
   * Used for testing so that modules can be overidden.
   */
  Pusher.modules = {
    request: request,
    crypto: crypto
  };

  Pusher.prototype = {
    domain: 'api.pusherapp.com',
    scheme: 'http',
    port: 80
  };

  Pusher.prototype.auth = function(socketId, channel, channelData) {
    var returnHash = {}
    var channelDataStr = ''
    if (channelData) {
      channelData = JSON.stringify(channelData);
      channelDataStr = ':' + channelData;
      returnHash['channel_data'] = channelData;
    }
    var stringToSign = socketId + ':' + channel + channelDataStr;
    returnHash['auth'] = this.options.key + ':' + crypto.createHmac('sha256', this.options.secret).update(stringToSign).digest('hex');
    return(returnHash);
  }

  /**
   * Legacy supporting function for fetching a channel object.
   */
  Pusher.prototype.channel = function( channelName ) {
    return new Channel( channelName, this );
  }

  Pusher.prototype.trigger = function(channel, event, message, socketId, callback) {

    var timestamp = parseInt(new Date().getTime() / 1000);
    var requestBody = JSON.stringify(message);
    var hash = crypto.createHash('md5').update(new Buffer(requestBody).toString('binary')).digest('hex');

    var params = [
      'auth_key=', this.options.key,
      '&auth_timestamp=', timestamp,
      '&auth_version=', '1.0',
      '&body_md5=', hash,
      '&name=', event
    ];
    if (socketId) {
      params.push('&socket_id=', socketId);
    }
    var queryString = params.join('');

    var path = '/apps/' + this.options.appId + '/channels/' + channel + '/events';
    var signData = ['POST', path, queryString].join('\n');
    var signature = Pusher.modules.crypto.createHmac('sha256', this.options.secret).update(signData).digest('hex');
    path = path + '?' + queryString + '&auth_signature=' + signature;
    var url = this.scheme + '://' + this.domain + ( this.port === 80? '' : ':' + this.port ) + path;
    Pusher.modules.request.post({
        url: url,
        headers: {
          'content-type': 'application/json'
        },
        body: requestBody
    }, function( err, res, body ) {
      // although using request module the callback signature
      // needs to be maintained
      if( typeof callback === 'function' ) {
        callback( err, this.req, res );
      }
    });

    return this;
  }

  /**
   * Channel object used for legacy support. Should not be extended.
   */
  function Channel( channelName, pusher ) {
    this._channelName = channelName;
    this._pusher = pusher;
  }
  /**
   * Trigger an event on a channel.
   */
  Channel.prototype.trigger = function( event, message, callback ) {
    var socketId = null;
    this._pusher.trigger( this.channelName, event, message, socketId, callback );
  };

  return Pusher;
})();
<commit_msg>whitespace/semi-colon cleanup<commit_after>module.exports = (function() {
  var crypto = require('crypto');
  var request = require('request');

  var Pusher = function(options) {
    this.options = options;

    // support appKey being provided instead of key for legacy support
    if( this.options.appKey ) {
      this.options.key = this.options.appKey;
      delete this.options.appKey;
    }

    return this;
  };

  /**
   * @private
   * Used for testing so that modules can be overidden.
   */
  Pusher.modules = {
    request: request,
    crypto: crypto
  };

  Pusher.prototype = {
    domain: 'api.pusherapp.com',
    scheme: 'http',
    port: 80
  };

  Pusher.prototype.auth = function(socketId, channel, channelData) {
    var returnHash = {}
    var channelDataStr = ''
    if (channelData) {
      channelData = JSON.stringify(channelData);
      channelDataStr = ':' + channelData;
      returnHash['channel_data'] = channelData;
    }
    var stringToSign = socketId + ':' + channel + channelDataStr;
    returnHash['auth'] = this.options.key + ':' + crypto.createHmac('sha256', this.options.secret).update(stringToSign).digest('hex');
    return(returnHash);
  };

  /**
   * Legacy supporting function for fetching a channel object.
   */
  Pusher.prototype.channel = function( channelName ) {
    return new Channel( channelName, this );
  };

  Pusher.prototype.trigger = function(channel, event, message, socketId, callback) {

    var timestamp = parseInt(new Date().getTime() / 1000);
    var requestBody = JSON.stringify(message);
    var hash = crypto.createHash('md5').update(new Buffer(requestBody).toString('binary')).digest('hex');

    var params = [
      'auth_key=', this.options.key,
      '&auth_timestamp=', timestamp,
      '&auth_version=', '1.0',
      '&body_md5=', hash,
      '&name=', event
    ];
    if (socketId) {
      params.push('&socket_id=', socketId);
    }
    var queryString = params.join('');

    var path = '/apps/' + this.options.appId + '/channels/' + channel + '/events';
    var signData = ['POST', path, queryString].join('\n');
    var signature = Pusher.modules.crypto.createHmac('sha256', this.options.secret).update(signData).digest('hex');
    path = path + '?' + queryString + '&auth_signature=' + signature;
    var url = this.scheme + '://' + this.domain + ( this.port === 80? '' : ':' + this.port ) + path;
    Pusher.modules.request.post({
      url: url,
      headers: {
        'content-type': 'application/json'
      },
      body: requestBody
    }, function( err, res, body ) {
      // although using request module the callback signature
      // needs to be maintained
      if( typeof callback === 'function' ) {
        callback( err, this.req, res );
      }
    });

    return this;
  };

  /**
   * Channel object used for legacy support. Should not be extended.
   */
  function Channel( channelName, pusher ) {
    this._channelName = channelName;
    this._pusher = pusher;
  }

  /**
   * Trigger an event on a channel.
   */
  Channel.prototype.trigger = function( event, message, callback ) {
    var socketId = null;
    this._pusher.trigger( this.channelName, event, message, socketId, callback );
  };

  return Pusher;
})();
<|endoftext|>