<commit_before>"use strict";

var moment = require('moment-timezone');
var luid = require('./utils').luid;

moment.tz.setDefault("Asia/Tokyo");
moment.locale('ja', {
    weekdays: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],
    weekdaysShort: ["日","月","火","水","木","金","土"]
});

function ZangyoBot(controller) {
  var zangyo_bot = {};

  zangyo_bot.ranges = {
    today: "今日",
    yesterday: "昨日",
    day_before_yesterday: "一昨日",
    this_week: "今週",
    last_week: "先週",
    past_one_week: "過去一週間"
  };

  zangyo_bot.createApplication = function(err, convo) {
    askApprover(convo);
  };

  function askApprover(convo) {
    convo.ask("プロマネは誰？ [@xxx]", function(response, convo) {
      if (response.text.match(/(cancel|キャンセル)/)) {
        convo.say('キャンセルしたよ！');
        convo.next();
      } else if (!response.text.match(/^\<\@[a-zA-Z0-9]+\>/g)) {
        convo.say('@xxx の形式でユーザーを１人指定してね。');
        convo.repeat();
        convo.next();
      } else {
        var zangyo = {}
        zangyo.id = "Z" + luid();
        zangyo.date = moment().format("YYYY-MM-DD");
        zangyo.applicant = response.user;
        zangyo.approver = response.text.match(/^\<\@[a-zA-Z0-9]+\>/g)[0].slice(2, -1);
        askEndTime(convo, zangyo);
        convo.next();
      }
    });
  };

  function askEndTime(convo, zangyo) {
    convo.ask("何時に終わる？ [HH:MM]", function(response, convo) {
      if (response.text.match(/(cancel|キャンセル)/)) {
        convo.say('キャンセルしたよ！');
        convo.next();
      } else if (!response.text.match(/^([0-2]?[0-9]):([0-5]?[0-9])$/)) {
        convo.say('HH:MM の形式で時間を指定してね。29:59まで指定できるよ。');
        convo.repeat();
        convo.next();
      } else {
        zangyo.end_time = response.text;
        askReason(convo, zangyo);
        convo.next();
      }
    });
  };

  function askReason(convo, zangyo) {
    convo.ask("残業する理由は？", function(response, convo) {
      zangyo.reason = response.text;
      var summary = {
        "text": "残業申請する？",
        "attachments": [
          {
            "callback_id": "apply-" + zangyo.id,
            "fallback": "<@" + zangyo.approver + "> さんへの残業申請",
            "color": "#aaa",
            "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
            "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
            "fields": [
              {
                "title": zangyo.end_time + "まで、以下の理由により残業します。",
                "value": zangyo.reason,
                "short": false
              }
            ],
            "actions": [
              {
                "type": "button",
                "name": "apply",
                "text": "申請",
                "style": "primary"
              },
              {
                "type": "button",
                "name": "redo",
                "text": "やり直し"
              },
              {
                "type": "button",
                "name": "cancel",
                "text": "キャンセル"
              }
            ]
          }
        ]
      }
      controller.storage.teams.get(response.team, function(err, team) {
        if (!team.zangyos) {
          team.zangyos = [];
        }

        team.zangyos.push(zangyo);
        controller.storage.teams.save(team);
      });
      convo.say(summary);
      convo.next();
    });
  };

  zangyo_bot.apply = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos[idx].applied_at = moment().unix();
      controller.storage.teams.save(team);
      var zangyo = team.zangyos[idx];
      var summary = {
        "text": "残業申請したよ。",
        "attachments": [
          {
            "fallback": "<@" + zangyo.approver + "> さんへの残業申請",
            "color": "#aaa",
            "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
            "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
            "fields": [
              {
                "title": zangyo.end_time + "まで、以下の理由により残業します。",
                "value": zangyo.reason,
                "short": false
              }
            ]
          }
        ]
      }
      bot.replyInteractive(message, summary);
      bot.startPrivateConversation({user: zangyo.approver}, function(err,convo) {
        if (err) {
          console.log(err);
        } else {
          var application = {
            "text": "残業申請が来たよ。承認する？",
            "attachments": [
              {
                "callback_id": "approve-" + zangyo.id,
                "fallback": "<@" + zangyo.applicant + "> さんからの残業申請",
                "color": "#aaa",
                "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
                "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
                "fields": [
                  {
                    "title": zangyo.end_time + "まで、以下の理由により残業します。",
                    "value": zangyo.reason,
                    "short": false
                  }
                ],
                "actions": [
                  {
                    "type": "button",
                    "name": "approve",
                    "text": "承認",
                    "style": "primary"
                  },
                  {
                    "type": "button",
                    "name": "reject",
                    "text": "却下",
                    "style": "danger"
                  }
                ]
              }
            ]
          }
          convo.say(application);
          convo.next();
        }
      });
    });
  };

  zangyo_bot.redoApply = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos.splice(idx, 1);
      controller.storage.teams.save(team);
      bot.replyInteractive(message, "最初からやり直し！");
      bot.startConversation(message, zangyo_bot.createApplication);
    });
  };

  zangyo_bot.cancelApply = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos.splice(idx, 1);
      controller.storage.teams.save(team);
      bot.replyInteractive(message, "キャンセルしたよ。さっさと帰ろう！");
    });
  };

  zangyo_bot.approve = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos[idx].approved = true;
      team.zangyos[idx].approved_at = moment().unix();
      controller.storage.teams.save(team);
      var zangyo = team.zangyos[idx];
      bot.replyInteractive(message, getSummary(zangyo, true, true));

      bot.configureIncomingWebhook(team.incoming_webhook);
      bot.sendWebhook(getSummary(zangyo, true, false), function(err,res) {
        if (err) console.log(err);
      });
    });
  };

  zangyo_bot.rejectApprove = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos[idx].approved = false;
      team.zangyos[idx].approved_at = moment().unix();
      controller.storage.teams.save(team);
      var zangyo = team.zangyos[idx];
      bot.replyInteractive(message, getSummary(zangyo, false, true));

      bot.configureIncomingWebhook(team.incoming_webhook);
      bot.sendWebhook(getSummary(zangyo, false, false), function(err,res) {
        if (err) console.log(err);
      });
    });
  };

  function getSummary(zangyo, is_approved, is_reply) {
    var result_word, color, text;

    if (is_approved) {
      result_word = "承認";
      color = "good";
    } else {
      result_word = "却下";
      color = "danger";
    }

    if (is_reply) {
      text = "残業申請を" + result_word + "したよ。";
    }

    var summary = {
      "text": text,
      "attachments": [
        {
          "fallback": "<@" + zangyo.applicant + "> さんの残業申請",
          "color": color,
          "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
          "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
          "fields": [
            {
              "title": zangyo.end_time + "まで、以下の理由により残業します。",
              "value": zangyo.reason,
              "short": false
            }
          ],
          "footer": "<@" + zangyo.approver + "> が" + result_word + "しました。",
          "ts": zangyo.approved_at
        }
      ]
    }
    return summary;
  };

  function getTeamZangyo(team_id, zangyo_id, cb) {
    controller.storage.teams.get(team_id, function(err, team) {
      for (var i = 0; i < team.zangyos.length; i++) {
        if (team.zangyos[i].id == zangyo_id) {
          cb(team, i);
          break;
        }
      }
    });
  };

  zangyo_bot.replyList = function(bot, message, range, is_detailed) {
    if (is_detailed) {
      buildDetailedMessage(bot, message, range);
    } else {
      buildSimplifiedMessage(bot, message, range);
    }
  };

  function buildSimplifiedMessage(bot, message, range) {
    findTargetZangyo(message.team, range, function(zangyo){
      var field = {};
      if (zangyo.approved == true) {
        field.value = ":white_check_mark: <@" + zangyo.applicant + "> - " + zangyo.end_time;
      } else {
        field.value = ":x: <@" + zangyo.applicant + "> - " + zangyo.end_time;
      }
      field.short = false

      return field;
    }, function(fields){
      var reply;
      if (fields.length == 0) {
        reply = range + "は残業申請１件もないっす。。。";
      } else {
        reply = {
          "text": range + "の残業申請一覧",
          "attachments": [
            {
              "color": "#aaa",
              "fields": fields
            }
          ]
        }
      }
      bot.reply(message, reply);
    });
  };

  function buildDetailedMessage(bot, message, range) {
    findTargetZangyo(message.team, range, function(zangyo){
      var attachment = {};
      if (zangyo.approved == true) {
        attachment.color = "good";
        attachment.footer = "<@" + zangyo.approver + "> が承認しました。";
      } else {
        attachment.color = "danger";
        attachment.footer = "<@" + zangyo.approver + "> が却下しました。";
      }
      attachment.title = "<@" + zangyo.applicant + "> - " + zangyo.end_time;
      attachment.text = zangyo.reason;
      attachment.ts = zangyo.approved_at;

      return attachment;
    }, function(attachments){
      var reply;
      if (attachments.length == 0) {
        reply = range + "は残業申請１件もないっす。。。";
      } else {
        reply = {
          "text": range + "の残業申請一覧",
          "attachments": attachments
        }
      }
      bot.reply(message, reply);
    });
  };

  function findTargetZangyo(team_id, range, getMessageItem, replyWholeMessage) {
    var begin_date, end_date;
    switch (range) {
      case zangyo_bot.ranges.today:
        var today = moment().format("YYYY-MM-DD");
        begin_date = today;
        end_date = today
        break;
      case zangyo_bot.ranges.yesterday:
        var yesterday = moment().subtract(1, 'days').format("YYYY-MM-DD");
        begin_date = yesterday;
        end_date = yesterday;
        break;
      case zangyo_bot.ranges.day_before_yesterday:
        var day_before_yesterday = moment().subtract(2, 'days').format("YYYY-MM-DD");
        begin_date = day_before_yesterday;
        end_date = day_before_yesterday;
        break;
      case zangyo_bot.ranges.this_week:
        begin_date = moment().startOf('isoweek').format("YYYY-MM-DD");
        end_date = moment().endOf('isoweek').format("YYYY-MM-DD");
        break;
      case zangyo_bot.ranges.last_week:
        var lastweek = moment().subtract(1, 'weeks');
        begin_date = lastweek.startOf('isoweek').format("YYYY-MM-DD");
        end_date = lastweek.endOf('isoweek').format("YYYY-MM-DD");
        break;
      case zangyo_bot.ranges.past_one_week:
        begin_date = moment().subtract(7, 'days').format("YYYY-MM-DD");
        end_date = moment().subtract(1, 'days').format("YYYY-MM-DD");
        break;
      default:
        var target = range.match(/\d{1,2}/g);
        begin_date = moment().format("YYYY-") + target[0] + '-' + target[1];
        end_date = moment().format("YYYY-") + target[0] + '-' + target[1];
        break;
    }

    controller.storage.teams.get(team_id, function(err, team) {
      var list = [];
      for (var i = 0; i < team.zangyos.length; i++) {
        var zangyo = team.zangyos[i];
        if (begin_date <= zangyo.date && zangyo.date <= end_date) {
          list.push(getMessageItem(zangyo));
        }
      }

      replyWholeMessage(list);
    });
  };

  return zangyo_bot;
}

module.exports = ZangyoBot;
<commit_msg>compare dates<commit_after>"use strict";

var moment = require('moment-timezone');
var luid = require('./utils').luid;

moment.tz.setDefault("Asia/Tokyo");
moment.locale('ja', {
    weekdays: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"],
    weekdaysShort: ["日","月","火","水","木","金","土"]
});

function ZangyoBot(controller) {
  var zangyo_bot = {};

  zangyo_bot.ranges = {
    today: "今日",
    yesterday: "昨日",
    day_before_yesterday: "一昨日",
    this_week: "今週",
    last_week: "先週",
    past_one_week: "過去一週間"
  };

  zangyo_bot.createApplication = function(err, convo) {
    askApprover(convo);
  };

  function askApprover(convo) {
    convo.ask("プロマネは誰？ [@xxx]", function(response, convo) {
      if (response.text.match(/(cancel|キャンセル)/)) {
        convo.say('キャンセルしたよ！');
        convo.next();
      } else if (!response.text.match(/^\<\@[a-zA-Z0-9]+\>/g)) {
        convo.say('@xxx の形式でユーザーを１人指定してね。');
        convo.repeat();
        convo.next();
      } else {
        var zangyo = {}
        zangyo.id = "Z" + luid();
        zangyo.date = moment().format("YYYY-MM-DD");
        zangyo.applicant = response.user;
        zangyo.approver = response.text.match(/^\<\@[a-zA-Z0-9]+\>/g)[0].slice(2, -1);
        askEndTime(convo, zangyo);
        convo.next();
      }
    });
  };

  function askEndTime(convo, zangyo) {
    convo.ask("何時に終わる？ [HH:MM]", function(response, convo) {
      if (response.text.match(/(cancel|キャンセル)/)) {
        convo.say('キャンセルしたよ！');
        convo.next();
      } else if (!response.text.match(/^([0-2]?[0-9]):([0-5]?[0-9])$/)) {
        convo.say('HH:MM の形式で時間を指定してね。29:59まで指定できるよ。');
        convo.repeat();
        convo.next();
      } else {
        zangyo.end_time = response.text;
        askReason(convo, zangyo);
        convo.next();
      }
    });
  };

  function askReason(convo, zangyo) {
    convo.ask("残業する理由は？", function(response, convo) {
      zangyo.reason = response.text;
      var summary = {
        "text": "残業申請する？",
        "attachments": [
          {
            "callback_id": "apply-" + zangyo.id,
            "fallback": "<@" + zangyo.approver + "> さんへの残業申請",
            "color": "#aaa",
            "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
            "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
            "fields": [
              {
                "title": zangyo.end_time + "まで、以下の理由により残業します。",
                "value": zangyo.reason,
                "short": false
              }
            ],
            "actions": [
              {
                "type": "button",
                "name": "apply",
                "text": "申請",
                "style": "primary"
              },
              {
                "type": "button",
                "name": "redo",
                "text": "やり直し"
              },
              {
                "type": "button",
                "name": "cancel",
                "text": "キャンセル"
              }
            ]
          }
        ]
      }
      controller.storage.teams.get(response.team, function(err, team) {
        if (!team.zangyos) {
          team.zangyos = [];
        }

        team.zangyos.push(zangyo);
        controller.storage.teams.save(team);
      });
      convo.say(summary);
      convo.next();
    });
  };

  zangyo_bot.apply = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos[idx].applied_at = moment().unix();
      controller.storage.teams.save(team);
      var zangyo = team.zangyos[idx];
      var summary = {
        "text": "残業申請したよ。",
        "attachments": [
          {
            "fallback": "<@" + zangyo.approver + "> さんへの残業申請",
            "color": "#aaa",
            "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
            "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
            "fields": [
              {
                "title": zangyo.end_time + "まで、以下の理由により残業します。",
                "value": zangyo.reason,
                "short": false
              }
            ]
          }
        ]
      }
      bot.replyInteractive(message, summary);
      bot.startPrivateConversation({user: zangyo.approver}, function(err,convo) {
        if (err) {
          console.log(err);
        } else {
          var application = {
            "text": "残業申請が来たよ。承認する？",
            "attachments": [
              {
                "callback_id": "approve-" + zangyo.id,
                "fallback": "<@" + zangyo.applicant + "> さんからの残業申請",
                "color": "#aaa",
                "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
                "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
                "fields": [
                  {
                    "title": zangyo.end_time + "まで、以下の理由により残業します。",
                    "value": zangyo.reason,
                    "short": false
                  }
                ],
                "actions": [
                  {
                    "type": "button",
                    "name": "approve",
                    "text": "承認",
                    "style": "primary"
                  },
                  {
                    "type": "button",
                    "name": "reject",
                    "text": "却下",
                    "style": "danger"
                  }
                ]
              }
            ]
          }
          convo.say(application);
          convo.next();
        }
      });
    });
  };

  zangyo_bot.redoApply = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos.splice(idx, 1);
      controller.storage.teams.save(team);
      bot.replyInteractive(message, "最初からやり直し！");
      bot.startConversation(message, zangyo_bot.createApplication);
    });
  };

  zangyo_bot.cancelApply = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos.splice(idx, 1);
      controller.storage.teams.save(team);
      bot.replyInteractive(message, "キャンセルしたよ。さっさと帰ろう！");
    });
  };

  zangyo_bot.approve = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos[idx].approved = true;
      team.zangyos[idx].approved_at = moment().unix();
      controller.storage.teams.save(team);
      var zangyo = team.zangyos[idx];
      bot.replyInteractive(message, getSummary(zangyo, true, true));

      bot.configureIncomingWebhook(team.incoming_webhook);
      bot.sendWebhook(getSummary(zangyo, true, false), function(err,res) {
        if (err) console.log(err);
      });
    });
  };

  zangyo_bot.rejectApprove = function(zangyo_id, bot, message) {
    getTeamZangyo(message.team.id, zangyo_id, function(team, idx) {
      team.zangyos[idx].approved = false;
      team.zangyos[idx].approved_at = moment().unix();
      controller.storage.teams.save(team);
      var zangyo = team.zangyos[idx];
      bot.replyInteractive(message, getSummary(zangyo, false, true));

      bot.configureIncomingWebhook(team.incoming_webhook);
      bot.sendWebhook(getSummary(zangyo, false, false), function(err,res) {
        if (err) console.log(err);
      });
    });
  };

  function getSummary(zangyo, is_approved, is_reply) {
    var result_word, color, text;

    if (is_approved) {
      result_word = "承認";
      color = "good";
    } else {
      result_word = "却下";
      color = "danger";
    }

    if (is_reply) {
      text = "残業申請を" + result_word + "したよ。";
    }

    var summary = {
      "text": text,
      "attachments": [
        {
          "fallback": "<@" + zangyo.applicant + "> さんの残業申請",
          "color": color,
          "title": "残業申請 - " + moment(zangyo.date).format("MM月DD日(ddd)"),
          "text": "<@" + zangyo.applicant + ">  >>>  <@" + zangyo.approver + ">",
          "fields": [
            {
              "title": zangyo.end_time + "まで、以下の理由により残業します。",
              "value": zangyo.reason,
              "short": false
            }
          ],
          "footer": "<@" + zangyo.approver + "> が" + result_word + "しました。",
          "ts": zangyo.approved_at
        }
      ]
    }
    return summary;
  };

  function getTeamZangyo(team_id, zangyo_id, cb) {
    controller.storage.teams.get(team_id, function(err, team) {
      for (var i = 0; i < team.zangyos.length; i++) {
        if (team.zangyos[i].id == zangyo_id) {
          cb(team, i);
          break;
        }
      }
    });
  };

  zangyo_bot.replyList = function(bot, message, range, is_detailed) {
    if (is_detailed) {
      buildDetailedMessage(bot, message, range);
    } else {
      buildSimplifiedMessage(bot, message, range);
    }
  };

  function buildSimplifiedMessage(bot, message, range) {
    findTargetZangyo(message.team, range, function(zangyo){
      var field = {};
      if (zangyo.approved == true) {
        field.value = ":white_check_mark: <@" + zangyo.applicant + "> - " + zangyo.end_time;
      } else {
        field.value = ":x: <@" + zangyo.applicant + "> - " + zangyo.end_time;
      }
      field.short = false

      return field;
    }, function(fields){
      var reply;
      if (fields.length == 0) {
        reply = range + "は残業申請１件もないっす。。。";
      } else {
        reply = {
          "text": range + "の残業申請一覧",
          "attachments": [
            {
              "color": "#aaa",
              "fields": fields
            }
          ]
        }
      }
      bot.reply(message, reply);
    });
  };

  function buildDetailedMessage(bot, message, range) {
    findTargetZangyo(message.team, range, function(zangyo){
      var attachment = {};
      if (zangyo.approved == true) {
        attachment.color = "good";
        attachment.footer = "<@" + zangyo.approver + "> が承認しました。";
      } else {
        attachment.color = "danger";
        attachment.footer = "<@" + zangyo.approver + "> が却下しました。";
      }
      attachment.title = "<@" + zangyo.applicant + "> - " + zangyo.end_time;
      attachment.text = zangyo.reason;
      attachment.ts = zangyo.approved_at;

      return attachment;
    }, function(attachments){
      var reply;
      if (attachments.length == 0) {
        reply = range + "は残業申請１件もないっす。。。";
      } else {
        reply = {
          "text": range + "の残業申請一覧",
          "attachments": attachments
        }
      }
      bot.reply(message, reply);
    });
  };

  function findTargetZangyo(team_id, range, getMessageItem, replyWholeMessage) {
    var begin_date, end_date;
    switch (range) {
      case zangyo_bot.ranges.today:
        var today = moment().format("YYYY-MM-DD");
        begin_date = today;
        end_date = today
        break;
      case zangyo_bot.ranges.yesterday:
        var yesterday = moment().subtract(1, 'days').format("YYYY-MM-DD");
        begin_date = yesterday;
        end_date = yesterday;
        break;
      case zangyo_bot.ranges.day_before_yesterday:
        var day_before_yesterday = moment().subtract(2, 'days').format("YYYY-MM-DD");
        begin_date = day_before_yesterday;
        end_date = day_before_yesterday;
        break;
      case zangyo_bot.ranges.this_week:
        begin_date = moment().startOf('isoweek').format("YYYY-MM-DD");
        end_date = moment().endOf('isoweek').format("YYYY-MM-DD");
        break;
      case zangyo_bot.ranges.last_week:
        var lastweek = moment().subtract(1, 'weeks');
        begin_date = lastweek.startOf('isoweek').format("YYYY-MM-DD");
        end_date = lastweek.endOf('isoweek').format("YYYY-MM-DD");
        break;
      case zangyo_bot.ranges.past_one_week:
        begin_date = moment().subtract(7, 'days').format("YYYY-MM-DD");
        end_date = moment().subtract(1, 'days').format("YYYY-MM-DD");
        break;
      default:
        var target = range.match(/\d{1,2}/g);
        begin_date = moment().format("YYYY-") + ('0' + target[0]).slice(-2) + '-' + ('0' + target[1]).slice(-2);
        end_date = moment().format("YYYY-") + ('0' + target[0]).slice(-2) + '-' + ('0' + target[1]).slice(-2);
        break;
    }

    controller.storage.teams.get(team_id, function(err, team) {
      var list = [];
      for (var i = 0; i < team.zangyos.length; i++) {
        var zangyo = team.zangyos[i];
        if (begin_date <= zangyo.date && zangyo.date <= end_date) {
          list.push(getMessageItem(zangyo));
        }
      }

      replyWholeMessage(list);
    });
  };

  return zangyo_bot;
}

module.exports = ZangyoBot;
<|endoftext|>


<commit_before>(function (ng, page) {
	"use strict";

	/**
	 * Controller for the Project Samples Page.
	 * @param {Object} $scope Angular scope for this controller.
	 * @param {Object} $log Angular logging service.
	 * @param {Object} samplesService Service to handle server calls for samples.
	 * @param {Object} tableService Service to handle rendering the datatable.
	 * @constructor
	 */
	function SamplesController($scope, $log, $uibModal, samplesService, tableService) {
		var vm = this, previousIndex = null,
		    // Which projects to display
		    display = {
			    project: true,
			    local: []
		    };
		vm.selected = [];

		$scope.$on("DATATABLE_UPDATED", function () {
			previousIndex = null;
		});

		// BUTTON STATE
		vm.disabled = {
			lessThanTwo: true,
			lessThanOne: true
		};

		// Hide project name unless multiple displayed.
		vm.showProjectname = false;

		// Create the datatable.
		vm.dtColumnDefs = tableService.createTableColumnDefs();
		vm.dtOptions = tableService.createTableOptions();

		// Get the samples - automatically added to datatable.
		samplesService.fetchSamples().then(function (samples) {
			vm.samples = samples;
		});

		vm.displayProjectsModal = function() {
			var modal = $uibModal.open({
				templateUrl: "associated-projects.modal.html",
				controllerAs: "associatedProjectsCtrl",
				controller: "AssociatedProjectsCtrl",
				resolve: {
					display: function () {
						return display;
					}
				}
			});

			modal.result.then(function (items) {
				// Check to make sure their are updates to the table to process.
				if (!ng.equals(items, display)) {
					display = items;
					samplesService.fetchSamples(items).then(function (samples) {
						// Need to know if the sample should be selected, and remove any that are no longer in the table.;
						var s = [];
						samples.forEach(function (sample) {
							if (vm.selected.find(function (item) {
									// Check to see if the selected item matches the sample and from the right project.
									if (item.sample.identifier === sample.sample.identifier &&
										item.project.identifier === sample.project.identifier) {
										s.push(item);
										return true;
									}
								})) {
								sample.selected = true;
							}
						});
						// Update the samples that are selected and currently in the table.
						vm.selected = s;
						// Determine if the project name needs to be displayed in the table.
						vm.showProjectname = display.local.length > 0;
						// Update the samples;
						vm.samples = samples;
					});
				}
			})
		};

		vm.merge = function () {
			$log.warn("TODO: Implement merge functionality");
		};

		vm.copy = function () {
			$log.warn("TODO: Implement copy functionality");
		};

		vm.move = function () {
			$log.warn("TODO: Implement move functionality");
		};

		vm.delete = function () {
			var ids = [], modal;
			vm.selected.forEach(function (item) {
				ids.push(item.sample.identifier);
			});

			modal = $uibModal.open({
				size        : 'lg',
				templateUrl : page.urls.modals.remove + "?" + $.param({sampleIds: ids}),
				openedClass : 'remove-modal',
				controllerAs: "removeCtrl",
				controller  : ["$uibModalInstance", function RemoveSamplesController($uibModalInstance) {
					var vm = this;

					vm.cancel = function () {
						$uibModalInstance.dismiss();
					};

					vm.remove = function () {
						$uibModalInstance.close();
					};
				}]
			});

			modal.result.then(function () {
				samplesService.removeSamples(vm.selected).then(function () {
					vm.samples = vm.samples.filter(function (sample) {
						return !sample.selected;
					});
					vm.selected = [];
					updateButtons();
				});
			});

		};

		vm.addToCart = function () {
			var selected = vm.samples.filter(function (sample) {
				return sample.selected;
			});
			samplesService.addSamplesToCart(selected);
		};

		// This properly adds the buttons to the table.
		vm.dtInstanceCallback = function(instance) {
			tableService.initTable($scope, instance);
		};

		/**
		 * Responsible for selecting all or none of the samples
		 */
		vm.selectAll = function($event) {
			$event.stopPropagation();
			vm.selected = [];
			vm.samples.forEach(function (sample) {
				sample.selected = vm.allSelected;
				if(vm.allSelected) {
					vm.selected.push(sample)};
			});
			updateButtons();
		};

		/**
		 * Handles user clicking the datatable row.  Updates selected samples
		 * @param $event
		 * @param item
		 */
		vm.rowClick = function($event, $index) {
			$event.stopPropagation();
			var item = vm.samples[$index];

			// Start by selecting or deselecting the item
			if(item.selected) {
				vm.selected.push(item);
			}
			else {
				vm.selected.splice(vm.selected.indexOf(item), 1);
			}

			// Check for multiple selection
			if (!item.selected) {
				// This would be a deselection, and would result in no further actions.
				previousIndex = null;
			} else if (previousIndex !== null && $event.shiftKey) {
				// Multi-select here
				// Get the table rows
				var found = false;
				ng.element('tbody tr').each(function (i, row) {
					var rowIndex = ng.element(row).data("index"),
						rowItem = vm.samples[rowIndex];

					// Check to see if it was the previous clicked row or the currently clicked row.
					// This will mark the beginning or end of the selections.
					if(rowIndex === $index || rowItem === previousIndex) { found = !found; }
					if(found && !rowItem.selected) {
						rowItem.selected = true;
						vm.selected.push(rowItem);
					}
				});
				updateButtons();
			} else {
				previousIndex = item;
			}

			updateButtons();
		};

		/**
		 * Selection of all samples on the current page.
		 */
		vm.selectPage = function () {
			var rows = ng.element("tbody tr");
			rows.each(function(i, row) {
				var index = $(row).data("index"),
					item = vm.samples[index];
				if(!item.selected) {
					item.selected = true;
					vm.selected.push(item);
				}
			})
		};

		/**
		 * Determine how many samples are selected and update the buttons.
		 */
		function updateButtons(){
			var count = vm.selected.length;
			vm.allSelected = vm.samples.length === count;
			vm.disabled = {
				lessThanTwo: count < 2,
				lessThanOne: count < 1
			};
		}
	}

	function AssociatedProjectsCtrl($uibModalInstance, associatedProjectsService, display) {
		var vm = this;
		vm.projects = {};
		vm.display = ng.copy(display);
		vm.local = {};

		// Get the local project
		associatedProjectsService.getLocal().then(function (result) {
			// Check to see if they are already displayed.
			result.data.forEach(function(project) {
				project.selected = vm.display.local.indexOf(project.identifier) > -1;
			});
			vm.projects.local = result.data;
		});

		vm.close = function () {
			$uibModalInstance.dismiss();
		};

		vm.showProjects = function () {
			// Just want the ids
			vm.display.local = [];
			vm.projects.local.forEach(function (project) {
				if (project.selected) {
					vm.display.local.push(project.identifier);
				}
			});

			$uibModalInstance.close(vm.display);
		};
	}

	ng.module("irida.projects.samples.controller", ["irida.projects.samples.service", "ui.bootstrap"])
		.controller("SamplesController", ["$scope", "$log", "$uibModal",  "samplesService", "tableService", SamplesController])
		.controller("AssociatedProjectsCtrl", ["$uibModalInstance", "associatedProjectsService", "display", AssociatedProjectsCtrl])
	;
})(window.angular, window.PAGE);<commit_msg>Injected jQuery into the page.<commit_after>(function (ng, $, page) {
	"use strict";

	/**
	 * Controller for the Project Samples Page.
	 * @param {Object} $scope Angular scope for this controller.
	 * @param {Object} $log Angular logging service.
	 * @param {Object} samplesService Service to handle server calls for samples.
	 * @param {Object} tableService Service to handle rendering the datatable.
	 * @constructor
	 */
	function SamplesController($scope, $log, $uibModal, samplesService, tableService) {
		var vm = this, previousIndex = null,
		    // Which projects to display
		    display = {
			    project: true,
			    local: []
		    };
		vm.selected = [];

		$scope.$on("DATATABLE_UPDATED", function () {
			previousIndex = null;
		});

		// BUTTON STATE
		vm.disabled = {
			lessThanTwo: true,
			lessThanOne: true
		};

		// Hide project name unless multiple displayed.
		vm.showProjectname = false;

		// Create the datatable.
		vm.dtColumnDefs = tableService.createTableColumnDefs();
		vm.dtOptions = tableService.createTableOptions();

		// Get the samples - automatically added to datatable.
		samplesService.fetchSamples().then(function (samples) {
			vm.samples = samples;
		});

		vm.displayProjectsModal = function() {
			var modal = $uibModal.open({
				templateUrl: "associated-projects.modal.html",
				controllerAs: "associatedProjectsCtrl",
				controller: "AssociatedProjectsCtrl",
				resolve: {
					display: function () {
						return display;
					}
				}
			});

			modal.result.then(function (items) {
				// Check to make sure their are updates to the table to process.
				if (!ng.equals(items, display)) {
					display = items;
					samplesService.fetchSamples(items).then(function (samples) {
						// Need to know if the sample should be selected, and remove any that are no longer in the table.;
						var s = [];
						samples.forEach(function (sample) {
							if (vm.selected.find(function (item) {
									// Check to see if the selected item matches the sample and from the right project.
									if (item.sample.identifier === sample.sample.identifier &&
										item.project.identifier === sample.project.identifier) {
										s.push(item);
										return true;
									}
								})) {
								sample.selected = true;
							}
						});
						// Update the samples that are selected and currently in the table.
						vm.selected = s;
						// Determine if the project name needs to be displayed in the table.
						vm.showProjectname = display.local.length > 0;
						// Update the samples;
						vm.samples = samples;
					});
				}
			})
		};

		vm.merge = function () {
			$log.warn("TODO: Implement merge functionality");
		};

		vm.copy = function () {
			$log.warn("TODO: Implement copy functionality");
		};

		vm.move = function () {
			$log.warn("TODO: Implement move functionality");
		};

		vm.delete = function () {
			var ids = [], modal;
			vm.selected.forEach(function (item) {
				ids.push(item.sample.identifier);
			});

			modal = $uibModal.open({
				size        : 'lg',
				templateUrl : page.urls.modals.remove + "?" + $.param({sampleIds: ids}),
				openedClass : 'remove-modal',
				controllerAs: "removeCtrl",
				controller  : ["$uibModalInstance", function RemoveSamplesController($uibModalInstance) {
					var vm = this;

					vm.cancel = function () {
						$uibModalInstance.dismiss();
					};

					vm.remove = function () {
						$uibModalInstance.close();
					};
				}]
			});

			modal.result.then(function () {
				samplesService.removeSamples(vm.selected).then(function () {
					vm.samples = vm.samples.filter(function (sample) {
						return !sample.selected;
					});
					vm.selected = [];
					updateButtons();
				});
			});

		};

		vm.addToCart = function () {
			var selected = vm.samples.filter(function (sample) {
				return sample.selected;
			});
			samplesService.addSamplesToCart(selected);
		};

		// This properly adds the buttons to the table.
		vm.dtInstanceCallback = function(instance) {
			tableService.initTable($scope, instance);
		};

		/**
		 * Responsible for selecting all or none of the samples
		 */
		vm.selectAll = function($event) {
			$event.stopPropagation();
			vm.selected = [];
			vm.samples.forEach(function (sample) {
				sample.selected = vm.allSelected;
				if(vm.allSelected) {
					vm.selected.push(sample)};
			});
			updateButtons();
		};

		/**
		 * Handles user clicking the datatable row.  Updates selected samples
		 * @param $event
		 * @param item
		 */
		vm.rowClick = function($event, $index) {
			$event.stopPropagation();
			var item = vm.samples[$index];

			// Start by selecting or deselecting the item
			if(item.selected) {
				vm.selected.push(item);
			}
			else {
				vm.selected.splice(vm.selected.indexOf(item), 1);
			}

			// Check for multiple selection
			if (!item.selected) {
				// This would be a deselection, and would result in no further actions.
				previousIndex = null;
			} else if (previousIndex !== null && $event.shiftKey) {
				// Multi-select here
				// Get the table rows
				var found = false;
				ng.element('tbody tr').each(function (i, row) {
					var rowIndex = ng.element(row).data("index"),
						rowItem = vm.samples[rowIndex];

					// Check to see if it was the previous clicked row or the currently clicked row.
					// This will mark the beginning or end of the selections.
					if(rowIndex === $index || rowItem === previousIndex) { found = !found; }
					if(found && !rowItem.selected) {
						rowItem.selected = true;
						vm.selected.push(rowItem);
					}
				});
				updateButtons();
			} else {
				previousIndex = item;
			}

			updateButtons();
		};

		/**
		 * Selection of all samples on the current page.
		 */
		vm.selectPage = function () {
			var rows = ng.element("tbody tr");
			rows.each(function(i, row) {
				var index = $(row).data("index"),
					item = vm.samples[index];
				if(!item.selected) {
					item.selected = true;
					vm.selected.push(item);
				}
			})
		};

		/**
		 * Determine how many samples are selected and update the buttons.
		 */
		function updateButtons(){
			var count = vm.selected.length;
			vm.allSelected = vm.samples.length === count;
			vm.disabled = {
				lessThanTwo: count < 2,
				lessThanOne: count < 1
			};
		}
	}

	function AssociatedProjectsCtrl($uibModalInstance, associatedProjectsService, display) {
		var vm = this;
		vm.projects = {};
		vm.display = ng.copy(display);
		vm.local = {};

		// Get the local project
		associatedProjectsService.getLocal().then(function (result) {
			// Check to see if they are already displayed.
			result.data.forEach(function(project) {
				project.selected = vm.display.local.indexOf(project.identifier) > -1;
			});
			vm.projects.local = result.data;
		});

		vm.close = function () {
			$uibModalInstance.dismiss();
		};

		vm.showProjects = function () {
			// Just want the ids
			vm.display.local = [];
			vm.projects.local.forEach(function (project) {
				if (project.selected) {
					vm.display.local.push(project.identifier);
				}
			});

			$uibModalInstance.close(vm.display);
		};
	}

	ng.module("irida.projects.samples.controller", ["irida.projects.samples.service", "ui.bootstrap"])
		.controller("SamplesController", ["$scope", "$log", "$uibModal",  "samplesService", "tableService", SamplesController])
		.controller("AssociatedProjectsCtrl", ["$uibModalInstance", "associatedProjectsService", "display", AssociatedProjectsCtrl])
	;
})(window.angular, window.jQuery, window.PAGE);<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Object.extend({

  /*
   * The main interface to the
   * warp drive. This allows the
   * starship to attain faster
   * than light (FTL) speeds.
   *
   * @method warp
   *
   * @param {Object} options
   *   Warp parameters.
   *
   * @return {Number}
   *   The warp factor.
   */
  warp(options = {}) {
    const { factor } = options;

    this._makeItSo(factor); // Stub this, number one.
    return factor;
  },

  /*
   * Set the state of the shileds.
   * This can be used to bring up
   * or take down shileds in with
   * a series of options.
   *
   * @method shield
   *
   * @param {Object} options
   *   Shield parameters.
   *
   * @return {Object}
   *   The power of the shields (e.g. full) and where to position the shields (e.g. front)
   */
  shields(options = {}) {
    const { power, position } = options;

    this._makeItSo(power, position); // Stub this, number one.
    return options;
  },

  /*
   * Beam down a number of crew
   * members.
   *
   * @method beamDown
   *
   * @param {Object} options
   *   Transporter parameters.
   *
   * @return {Object}
   *   The number of crew members to beam down and their coordinates.
   */
  beamDown(options = {}) {
    const { howMany, coordinates } = options;

    this._makeItSo(howMany, coordinates); // Stub this, number one.
    return options;
  },

  /*
   * Beam up a number of crew
   * members.
   *
   * @method beamUp
   *
   * @param {Object} options
   *   Transporter parameters.
   *
   * @return {Object}
   *   The number of crew memeber to beam up and their coordinates.
   */
  beamUp(options = {}) {
    const { howMany, coordinates } = options;

    this._makeItSo(howMany, coordinates); // Stub this, number one.
    return options;
  },

  /*
   * This is where the captain
   * assets their authority.
   *
   * @method makeItSo
   * @private
   */
  _makeItSo: Ember.K
});
<commit_msg>Replace deprecated `Ember.K` with empty function body<commit_after>import Ember from 'ember';

export default Ember.Object.extend({

  /*
   * The main interface to the
   * warp drive. This allows the
   * starship to attain faster
   * than light (FTL) speeds.
   *
   * @method warp
   *
   * @param {Object} options
   *   Warp parameters.
   *
   * @return {Number}
   *   The warp factor.
   */
  warp(options = {}) {
    const { factor } = options;

    this._makeItSo(factor); // Stub this, number one.
    return factor;
  },

  /*
   * Set the state of the shileds.
   * This can be used to bring up
   * or take down shileds in with
   * a series of options.
   *
   * @method shield
   *
   * @param {Object} options
   *   Shield parameters.
   *
   * @return {Object}
   *   The power of the shields (e.g. full) and where to position the shields (e.g. front)
   */
  shields(options = {}) {
    const { power, position } = options;

    this._makeItSo(power, position); // Stub this, number one.
    return options;
  },

  /*
   * Beam down a number of crew
   * members.
   *
   * @method beamDown
   *
   * @param {Object} options
   *   Transporter parameters.
   *
   * @return {Object}
   *   The number of crew members to beam down and their coordinates.
   */
  beamDown(options = {}) {
    const { howMany, coordinates } = options;

    this._makeItSo(howMany, coordinates); // Stub this, number one.
    return options;
  },

  /*
   * Beam up a number of crew
   * members.
   *
   * @method beamUp
   *
   * @param {Object} options
   *   Transporter parameters.
   *
   * @return {Object}
   *   The number of crew memeber to beam up and their coordinates.
   */
  beamUp(options = {}) {
    const { howMany, coordinates } = options;

    this._makeItSo(howMany, coordinates); // Stub this, number one.
    return options;
  },

  /*
   * This is where the captain
   * assets their authority.
   *
   * @method makeItSo
   * @private
   */
  _makeItSo() {}
});
<|endoftext|>


<commit_before>'use strict';

/*
Module dependencies
 */
var mongoose = require('mongoose');
var config = require('./../../../config/config')();

// Connect to MongoDB
var db = mongoose.connect(config.db.test.uri, config.db.test.options, function (err) {
  if (err) {
    console.error('Could not connect to MongoDB.');
  }
});

var FileModel = require('./../app/files/models/file.model');

/*
Globals
 */
var file;
describe('File Model Unit Tests: ', function () {

  describe('Create File Objects', function () {

    beforeEach(function (done) {
      file = new FileModel({
        filename: 'A random file',
        created_at: new Date()
      });
      done();
    });

    it('should be able to save without problems', function (done) {
      file.save(function (err, file) {
        expect(err).toBeNull();
        done();
      });
    });

    it('should throw an error when saved without a filename', function (done) {
      file.filename = '';

      file.save(function (err, file) {
        expect(err).not.toBeNull();
        done();
      });
    });

    it('should create a file object', function (done) {
      file.filename = "image.jpg";

      file.save(function (err, file) {

        FileModel.findOne(file, function (err, file) {
          expect(file).not.toBeNull();
          done();
        });
      });
    });

    afterEach(function (done) {
      file.remove(function (err, file) {
        if (err) {
          console.log(err);
        }
      });
      done();
    });

  });

  describe('Edit File Objects: ', function () {

    beforeEach(function (done) {
      file = new FileModel({
        filename: "funny.gif",
        created_at: new Date()
      });
      done();
    });

    it('should update the file object', function (done) {
      file.save(function (err, file) {
        FileModel.findOne(file, function (err, found_file) {
          found_file.filename = "not_funny.gif";

          found_file.save(function (err, file) {
            FileModel.findOne(file, function (err, file) {
              expect(file).not.toBeNull();
              done();
            });
          });
        });
      });
    });

    afterEach(function (done) {
      file.remove(function (err, file) {
        if (err) {
          console.log(err);
        }
      });
      done();
    });
  });

  describe('Delete File Objects: ', function () {

    beforeEach(function (done) {
      file = new FileModel({
        filename: "funny.gif",
        created_at: new Date()
      });
      done();
    });

    it('should delete the file object', function (done) {
      file.save(function (err, file) {
        file.remove(function (err, file) {
          FileModel.findOne(file, function (err, file) {
            expect(file).toBeNull();
            done();
          });
        });
      });
    });

    afterEach(function (done) {
      file.remove(function (err, file) {
        if (err) {
          console.log(err);
        }
      });
      done();
    });

  });

});
<commit_msg>Redefine test instance of File model.<commit_after>'use strict';

/*
Module dependencies
 */
var mongoose = require('mongoose');
var config = require('./../../../config/config')();

// Connect to MongoDB
var db = mongoose.connect(config.db.test.uri, config.db.test.options, function (err) {
  if (err) {
    console.error('Could not connect to MongoDB.');
  }
});

var FileModel = require('./../app/files/models/file.model');

/*
Globals
 */
var file;
describe('File Model Unit Tests: ', function () {

  describe('Create File Objects', function () {

    beforeEach(function (done) {
      file = new FileModel({
        downloadUrl: 'https://www.google.com',
        modified: 'today',
        size: '234KB',
        is_dir: true,
        path: '/avatar.png'
      });
      done();
    });

    it('should be able to save without problems', function (done) {
      file.save(function (err, file) {
        expect(err).toBeNull();
        done();
      });
    });

    it('should throw an error when saved without a filename', function (done) {
      file.filename = '';

      file.save(function (err, file) {
        expect(err).not.toBeNull();
        done();
      });
    });

    it('should create a file object', function (done) {
      file.filename = "image.jpg";

      file.save(function (err, file) {

        FileModel.findOne(file, function (err, file) {
          expect(file).not.toBeNull();
          done();
        });
      });
    });

    afterEach(function (done) {
      file.remove(function (err, file) {
        if (err) {
          console.log(err);
        }
      });
      done();
    });

  });

  describe('Edit File Objects: ', function () {

    beforeEach(function (done) {
      file = new FileModel({
        filename: "funny.gif",
        created_at: new Date()
      });
      done();
    });

    it('should update the file object', function (done) {
      file.save(function (err, file) {
        FileModel.findOne(file, function (err, found_file) {
          found_file.filename = "not_funny.gif";

          found_file.save(function (err, file) {
            FileModel.findOne(file, function (err, file) {
              expect(file).not.toBeNull();
              done();
            });
          });
        });
      });
    });

    afterEach(function (done) {
      file.remove(function (err, file) {
        if (err) {
          console.log(err);
        }
      });
      done();
    });
  });

  describe('Delete File Objects: ', function () {

    beforeEach(function (done) {
      file = new FileModel({
        filename: "funny.gif",
        created_at: new Date()
      });
      done();
    });

    it('should delete the file object', function (done) {
      file.save(function (err, file) {
        file.remove(function (err, file) {
          FileModel.findOne(file, function (err, file) {
            expect(file).toBeNull();
            done();
          });
        });
      });
    });

    afterEach(function (done) {
      file.remove(function (err, file) {
        if (err) {
          console.log(err);
        }
      });
      done();
    });

  });

});
<|endoftext|>


<commit_before>require('./check-versions')()

var config = require('../config')
if (!process.env.NODE_ENV) {
  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}

var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = require('./webpack.dev.conf')

// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  quiet: true
})

var hotMiddleware = require('webpack-hot-middleware')(compiler, {
  log: () => {}
})
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(options.filter || context, options))
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

var uri = 'http://localhost:' + port

devMiddleware.waitUntilValid(function () {
  console.log('> Listening at ' + uri + '\n')
})

module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }

  // when env is testing, don't need open it
  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
    opn(uri)
  }
})
<commit_msg>udpate<commit_after>require('./check-versions')()

var config = require('../config')
if (!process.env.NODE_ENV) {
  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}

var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = require('./webpack.dev.conf')

// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable

var app = express()
var compiler = webpack(webpackConfig)

var devMiddleware = require('webpack-dev-middleware')(compiler, {
  publicPath: webpackConfig.output.publicPath,
  quiet: true
})

var hotMiddleware = require('webpack-hot-middleware')(compiler, {
  log: function() {}
})
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
    hotMiddleware.publish({ action: 'reload' })
    cb()
  })
})

// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
  var options = proxyTable[context]
  if (typeof options === 'string') {
    options = { target: options }
  }
  app.use(proxyMiddleware(options.filter || context, options))
})

// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())

// serve webpack bundle output
app.use(devMiddleware)

// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)

// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))

var uri = 'http://localhost:' + port

devMiddleware.waitUntilValid(function () {
  console.log('> Listening at ' + uri + '\n')
})

module.exports = app.listen(port, function (err) {
  if (err) {
    console.log(err)
    return
  }

  // when env is testing, don't need open it
  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
    opn(uri)
  }
})
<|endoftext|>


<commit_before>
import {validateEntityClass, setPrimaryKey} from "./utils"

/**
 * Private field and method symbols.
 *
 * @type {Object<string, symbol>}
 */
const PRIVATE = Object.freeze({
  transaction: Symbol("transaction"),
  manageEntity: Symbol("manageEntity"),
  entityManager: Symbol("entityManager")
})

/**
 * The Write Operations Provider is a shared implementation of entity
 * modification operations.
 */
export default class WriteOperationsProvider {
  /**
   * Initializes the provider of the read-write operations for the persistence
   * context.
   *
   * @param {Transaction} transaction The current IndexedDB.ES6 read-write
   *        transaction to use to perform the read-write operations.
   * @param {function(function(new: AbstractEntity, Object<string, *>), (string|string[]), Object<string, *>): AbstractEntity} manageEntity
   *        A callback, provided by the entity manager, used to create a
   *        managed entity out of a record, or to retrieve an already managed
   *        entity representing the record.
   * @param {EntityManager} entityManager The entity manager representing the
   *        persistence context within which this write operations provider
   *        will be used.
   */
  constructor(transaction, manageEntity, entityManager) {
    /**
     * The current IndexedDB.ES6 read-write transaction to use to perform the
     * read-write operations.
     *
     * @type {Transaction}
     */
    this[PRIVATE.transaction] = transaction

    /**
     * A callback, provided by the entity manager, used to create a managed
     * entity out of a record, or to retrieve an already managed entity
     * representing the record.
     *
     * @type {function(function(new:AbstractEntity, Object.<string, *>), (string|string[]), Object.<string, *>): AbstractEntity}
     */
    this[PRIVATE.manageEntity] = manageEntity

    /**
     * The entity manager representing the persistence context within which
     * this write operations provider will be used.
     *
     * @type {EntityManager}
     */
    this[PRIVATE.entityManager] = entityManager
  }

  /**
   * Creates the specified entity in the storage. If the entity does not have
   * its primary key set and the storage has the {@code autoIncrement} flag
   * set, the entity will have its primary key generated and set after this
   * operation completes.
   *
   * @param {AbstractEntity} entity The entity to persist in the storage.
   * @return {Promise<AbstractEntity>} A promise resolved when the entity has
   *         been persisted. The result is the persisted entity with its
   *         primary key set.
   */
  persist(entity) {
    validateEntityClass(entity.constructor)

    let objectStoreName = entity.constructor.objectStore
    let objectStore = this[PRIVATE.transaction].getObjectStore(objectStoreName)
    let keyPath = objectStore.keyPath

    return new Promise((resolve, reject) => {
      objectStore.add(entity).then((primaryKey) => {
        setPrimaryKey(entity, keyPath, primaryKey)
        return entity
      }).then(resolve).catch(reject)
    })
  }

  /**
   * Deletes the specified entity. If the specified entity is present in the
   * persistence context, it will be detached after being deleted.
   *
   * @param {function(new: AbstractEntity, Object<string, *>)} entityClass The
   *        entity class specifying the type of the entity to delete.
   * @param {(number|string|Date|Array)} primaryKey The primary key identifying
   *        the entity to delete.
   * @return {Promise<undefined>} A promise that resolves when the record has
   *         been deleted.
   */
  remove(entityClass, primaryKey) {
    validateEntityClass(entityClass)

    let objectStoreName = entityClass.objectStore
    let objectStore = this[PRIVATE.transaction].getObjectStore(objectStoreName)

    return new Promise((resolve, reject) => {
      objectStore.delete(primaryKey).then(resolve).catch(reject)
    }).then(() => {
      let entityManager = this[PRIVATE.entityManager]
      if (entityManager.containsByPrimaryKey(entityClass, primaryKey)) {
        let entity = entityManager.find(entityClass, primaryKey)
        entityManager.detach(entity)
      }
    })
  }

  /**
   * Executes an update query for batch update of entities matched by the
   * specified query.
   *
   * Note that the entities matched by this query will become managed by the
   * entity managed owning this transaction, unless they are already managed by
   * it.
   *
   * @template {T} extends AbstractEntity
   * @param {function(new: T, Object<string, *>)} entityClass The entity class
   *        specifying the type of entity to match by the query.
   * @param {?(undefined|number|string|Date|Array|IDBKeyRange|Object<string, (number|string|Date|Array|IDBKeyRange)>|function(*, (number|string|Date|Array)): boolean)=} filter
   *        The filter, restricting the entities updated by this method. If a
   *        function is provided, the first argument will be set to the record
   *        and the second argument will be set to the primary key of the
   *        record.
   * @param {?(CursorDirection|string|string[]|function(*, *): number)} order
   *        How the records should be sorted.
   * @param {number} offset The index of the first record to delete. The
   *        records are numbered from {@code 0}, the offset must be a
   *        non-negative integer.
   * @param {?number} limit The maximum number of records to delete. The limit
   *        must be a positive integer, or {@code null} if no limit should be
   *        imposed.
   * @param {function(T)} entityCallback A callback that will be executed on
   *        each entity matched by this query. The modifications made by the
   *        callback will be saved.
   * @return {Promise<number>} A promise that will resolve once all entities
   *         have been processed. The promise resolves to the number of updated
   *         entities.
   */
  updateQuery(entityClass, filter, order, offset, limit, entityCallback) {
    validateEntityClass(entityClass)

    let objectStoreName = entityClass.objectStore
    let objectStore = this[PRIVATE.transaction].getObjectStore(objectStoreName)
    let keyPath = objectStore.keyPath

    return new Promise((resolve, reject) => {
      objectStore.updateQuery(filter, order, offset, limit)((record) => {
        let entity = this[PRIVATE.manageEntity](
          entityClass,
          keyPath,
          record
        )

        entityCallback(entity)

        return entity
      }).then(resolve).catch(reject)
    })
  }

  /**
   * Executes a delete query, deleting all entities matched by the specified
   * query.
   *
   * @param {function(new: AbstractEntity, Object<string, *>)} entityClass The
   *        entity class specifying the type of entity to match by the query.
   * @param {?(undefined|number|string|Date|Array|IDBKeyRange|Object<string, (number|string|Date|Array|IDBKeyRange)>|function(*, (number|string|Date|Array)): boolean)=} filter
   *        The filter, restricting the records deleted by this method. If a
   *        function is provided, the first argument will be set to the record
   *        and the second argument will be set to the primary key of the
   *        record.
   * @param {?(CursorDirection|string|string[]|function(*, *): number)} order
   *        How the records should be sorted.
   * @param {number} offset The index of the first record to delete. The
   *        records are numbered from {@code 0}, the offset must be a
   *        non-negative integer.
   * @param {?number} limit The maximum number of records to delete. The limit
   *        must be a positive integer, or {@code null} if no limit should be
   *        imposed.
   * @return {Promise<number>} A promise that will resolve after the records
   *         have been deleted. The promise will resolve to the number of
   *         deleted records.
   */
  deleteQuery(entityClass, filter, order, offset, limit) {
    validateEntityClass(entityClass)

    let objectStoreName = entityClass.objectStore
    let objectStore = transaction.getObjectStore(objectStoreName)
    let keyPath = objectStore.keyPath

    return new Promise((resolve, reject) => {
      objectStore.query(filter, order, offset, limit).then((records) => {
        return Promise.all(records.map((record) => {
          let primaryKey = getPrimaryKey(record, keyPath)
          return objectStore.delete(primaryKey).then(() => {
            this[PRIVATE.entityManager].detach(entityClass, primaryKey)
          })
        }))
      }).then(resolve).catch(reject)
    })
  }
}
<commit_msg>fixed the reference to the indexed-db.es6 library in the documentation<commit_after>
import {validateEntityClass, setPrimaryKey} from "./utils"

/**
 * Private field and method symbols.
 *
 * @type {Object<string, symbol>}
 */
const PRIVATE = Object.freeze({
  transaction: Symbol("transaction"),
  manageEntity: Symbol("manageEntity"),
  entityManager: Symbol("entityManager")
})

/**
 * The Write Operations Provider is a shared implementation of entity
 * modification operations.
 */
export default class WriteOperationsProvider {
  /**
   * Initializes the provider of the read-write operations for the persistence
   * context.
   *
   * @param {Transaction} transaction The current indexed-db.es6 read-write
   *        transaction to use to perform the read-write operations.
   * @param {function(function(new: AbstractEntity, Object<string, *>), (string|string[]), Object<string, *>): AbstractEntity} manageEntity
   *        A callback, provided by the entity manager, used to create a
   *        managed entity out of a record, or to retrieve an already managed
   *        entity representing the record.
   * @param {EntityManager} entityManager The entity manager representing the
   *        persistence context within which this write operations provider
   *        will be used.
   */
  constructor(transaction, manageEntity, entityManager) {
    /**
     * The current indexed-db.es6 read-write transaction to use to perform the
     * read-write operations.
     *
     * @type {Transaction}
     */
    this[PRIVATE.transaction] = transaction

    /**
     * A callback, provided by the entity manager, used to create a managed
     * entity out of a record, or to retrieve an already managed entity
     * representing the record.
     *
     * @type {function(function(new:AbstractEntity, Object.<string, *>), (string|string[]), Object.<string, *>): AbstractEntity}
     */
    this[PRIVATE.manageEntity] = manageEntity

    /**
     * The entity manager representing the persistence context within which
     * this write operations provider will be used.
     *
     * @type {EntityManager}
     */
    this[PRIVATE.entityManager] = entityManager
  }

  /**
   * Creates the specified entity in the storage. If the entity does not have
   * its primary key set and the storage has the {@code autoIncrement} flag
   * set, the entity will have its primary key generated and set after this
   * operation completes.
   *
   * @param {AbstractEntity} entity The entity to persist in the storage.
   * @return {Promise<AbstractEntity>} A promise resolved when the entity has
   *         been persisted. The result is the persisted entity with its
   *         primary key set.
   */
  persist(entity) {
    validateEntityClass(entity.constructor)

    let objectStoreName = entity.constructor.objectStore
    let objectStore = this[PRIVATE.transaction].getObjectStore(objectStoreName)
    let keyPath = objectStore.keyPath

    return new Promise((resolve, reject) => {
      objectStore.add(entity).then((primaryKey) => {
        setPrimaryKey(entity, keyPath, primaryKey)
        return entity
      }).then(resolve).catch(reject)
    })
  }

  /**
   * Deletes the specified entity. If the specified entity is present in the
   * persistence context, it will be detached after being deleted.
   *
   * @param {function(new: AbstractEntity, Object<string, *>)} entityClass The
   *        entity class specifying the type of the entity to delete.
   * @param {(number|string|Date|Array)} primaryKey The primary key identifying
   *        the entity to delete.
   * @return {Promise<undefined>} A promise that resolves when the record has
   *         been deleted.
   */
  remove(entityClass, primaryKey) {
    validateEntityClass(entityClass)

    let objectStoreName = entityClass.objectStore
    let objectStore = this[PRIVATE.transaction].getObjectStore(objectStoreName)

    return new Promise((resolve, reject) => {
      objectStore.delete(primaryKey).then(resolve).catch(reject)
    }).then(() => {
      let entityManager = this[PRIVATE.entityManager]
      if (entityManager.containsByPrimaryKey(entityClass, primaryKey)) {
        let entity = entityManager.find(entityClass, primaryKey)
        entityManager.detach(entity)
      }
    })
  }

  /**
   * Executes an update query for batch update of entities matched by the
   * specified query.
   *
   * Note that the entities matched by this query will become managed by the
   * entity managed owning this transaction, unless they are already managed by
   * it.
   *
   * @template {T} extends AbstractEntity
   * @param {function(new: T, Object<string, *>)} entityClass The entity class
   *        specifying the type of entity to match by the query.
   * @param {?(undefined|number|string|Date|Array|IDBKeyRange|Object<string, (number|string|Date|Array|IDBKeyRange)>|function(*, (number|string|Date|Array)): boolean)=} filter
   *        The filter, restricting the entities updated by this method. If a
   *        function is provided, the first argument will be set to the record
   *        and the second argument will be set to the primary key of the
   *        record.
   * @param {?(CursorDirection|string|string[]|function(*, *): number)} order
   *        How the records should be sorted.
   * @param {number} offset The index of the first record to delete. The
   *        records are numbered from {@code 0}, the offset must be a
   *        non-negative integer.
   * @param {?number} limit The maximum number of records to delete. The limit
   *        must be a positive integer, or {@code null} if no limit should be
   *        imposed.
   * @param {function(T)} entityCallback A callback that will be executed on
   *        each entity matched by this query. The modifications made by the
   *        callback will be saved.
   * @return {Promise<number>} A promise that will resolve once all entities
   *         have been processed. The promise resolves to the number of updated
   *         entities.
   */
  updateQuery(entityClass, filter, order, offset, limit, entityCallback) {
    validateEntityClass(entityClass)

    let objectStoreName = entityClass.objectStore
    let objectStore = this[PRIVATE.transaction].getObjectStore(objectStoreName)
    let keyPath = objectStore.keyPath

    return new Promise((resolve, reject) => {
      objectStore.updateQuery(filter, order, offset, limit)((record) => {
        let entity = this[PRIVATE.manageEntity](
          entityClass,
          keyPath,
          record
        )

        entityCallback(entity)

        return entity
      }).then(resolve).catch(reject)
    })
  }

  /**
   * Executes a delete query, deleting all entities matched by the specified
   * query.
   *
   * @param {function(new: AbstractEntity, Object<string, *>)} entityClass The
   *        entity class specifying the type of entity to match by the query.
   * @param {?(undefined|number|string|Date|Array|IDBKeyRange|Object<string, (number|string|Date|Array|IDBKeyRange)>|function(*, (number|string|Date|Array)): boolean)=} filter
   *        The filter, restricting the records deleted by this method. If a
   *        function is provided, the first argument will be set to the record
   *        and the second argument will be set to the primary key of the
   *        record.
   * @param {?(CursorDirection|string|string[]|function(*, *): number)} order
   *        How the records should be sorted.
   * @param {number} offset The index of the first record to delete. The
   *        records are numbered from {@code 0}, the offset must be a
   *        non-negative integer.
   * @param {?number} limit The maximum number of records to delete. The limit
   *        must be a positive integer, or {@code null} if no limit should be
   *        imposed.
   * @return {Promise<number>} A promise that will resolve after the records
   *         have been deleted. The promise will resolve to the number of
   *         deleted records.
   */
  deleteQuery(entityClass, filter, order, offset, limit) {
    validateEntityClass(entityClass)

    let objectStoreName = entityClass.objectStore
    let objectStore = transaction.getObjectStore(objectStoreName)
    let keyPath = objectStore.keyPath

    return new Promise((resolve, reject) => {
      objectStore.query(filter, order, offset, limit).then((records) => {
        return Promise.all(records.map((record) => {
          let primaryKey = getPrimaryKey(record, keyPath)
          return objectStore.delete(primaryKey).then(() => {
            this[PRIVATE.entityManager].detach(entityClass, primaryKey)
          })
        }))
      }).then(resolve).catch(reject)
    })
  }
}
<|endoftext|>


<commit_before>// FIXME - make non global
var infoWindows = [];

function loadGoogleMapsScript() {
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = 'https://maps.googleapis.com/maps/api/js?' +
      '&sensor=false&callback=loadedGoogleMapsAPI&hl=en';
  document.body.appendChild(script);
}
window.addEventListener ?
        window.addEventListener("load",loadGoogleMapsScript,false) :
window.attachEvent && window.attachEvent("onload",loadGoogleMapsScript);

/**
 * Create a google map pin from data provided
 * @param  {GoogleMap} map             Instance of a google map
 * @param  {Number} lat                Latitude of pin
 * @param  {Number} lng                Longitude of pin
 * @param  {String} html               HTML for information window
 * @param  {String} icon               URL of alternative map icon, or blank for default
 * @param  {boolean} useClusterer      Whether or not to use clusterer
 * @param  {boolean} enableWindowZoom  Whether or not to enable zoom on the rendered map
 * @param  {boolean} defaultHideMarker Whether or not to hide markers initially
 * @return {MapMarker}                 Google map pin object
 */
function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom,
	defaultHideMarker) {

	mapId = map.getDiv().getAttribute('id');

	var marker = new google.maps.Marker();
	marker.setPosition(new google.maps.LatLng(lat, lng));
	marker.mycategory = category;

	if (icon && icon !== '') {
		var image = new google.maps.MarkerImage(icon);
		marker.setIcon(image);
	}

	if (!useClusterer) {
		marker.setMap(map);
	}

	google.maps.event.addListener(marker, "click", function() {
		if (enableWindowZoom) {
			map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom);
		}
		var infoWindow = infoWindows[mapId];
		infoWindow.setContent(html);
		infoWindow.open(map, this);
	});

	//FIXME gmarkers[mapId].push(marker);

	if (defaultHideMarker) {
		marker.hide();
	}
	return marker;
}

/**
 * Get the current latitude
 * @return {float} Current latitude
 */
function getCurrentLat() {
	return current_lat;
}

/**
 * Get the current longitude
 * @return {float} The current longitude
 */
function getCurrentLng() {
	return current_lng;
}

/**
 * Convert JSON point data into google map markers
 * @param {GoogleMap} map             Google Map instance
 * @param {array} markers            point data loaded from JSON
 * @param {boolean} useClusterer      Whether or not to use the clusterer
 * @param {boolean} enableWindowZoom  Whether or not zoom is enabled
 * @param {boolean} defaultHideMarker Whether or not to hide markers
 * * @return array of Google map markers converted from the JSON data
 */
function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) {

	// these will be altered by adding markers
	map.minLat = 1000000;
	map.minLng = 1000000;
	map.maxLat = -1000000;
	map.maxLng = -1000000;

	var allmarkers = [];
	for (var i = 0; i < markers.length; i++) {
		var markerinfo = markers[i];
		var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html,
			markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom,
			defaultHideMarker);

		var latitude = parseFloat(markerinfo.latitude);
		var longitude = parseFloat(markerinfo.longitude);

		// update lat,lon min to lat,lon max
		if (latitude < map.minLat) {
			map.minLat = latitude;

		}
		if (latitude > map.maxLat) {
			map.maxLat = latitude;
		}

		if (longitude > map.maxLng) {
			map.maxLng = longitude;

		}

		if (longitude < map.minLng) {
			map.minLng = longitude;
		}

		allmarkers.push(marker);
	}

	var centreCoordinates = [];
	centreCoordinates.lat = (parseFloat(map.minLat)+parseFloat(map.maxLat))/2;
	centreCoordinates.lng = (parseFloat(map.minLng)+parseFloat(map.maxLng))/2;
	map.centreCoordinates = centreCoordinates;
	return allmarkers;
}

/**
 * Add lines to a Google map
 * @param {googleMap} map   Google map instance
 * @param {array} lines Line data loaded from json, lat1,lon1 to lat2,lon2
 */
function addLines(map, lines) {
	for (i = 0; i < lines.length; i++) {
		var line = lines[i];
		var point1 = new google.maps.LatLng(line.lat1, line.lon1);
		var point2 = new google.maps.LatLng(line.lat2, line.lon2);
		var points = [point1, point2];
		var pl = new google.maps.Polyline({
			path: points,
			strokeColor: line.color,
			strokeWeight: 4,
			strokeOpacity: 0.8
		});
		pl.setMap(map);
	}
}

/**
 * Add one or more (max of 25) KML files to a Google map
 * @param {GoogleMap} map      A Google Map instance
 * @param {array} kmlFiles array of URLs for KML files
 */
function addKmlFiles(map, kmlFiles) {
	for (var i = 0; i < kmlFiles.length; i++) {
		var kmlFile = kmlFiles[i];
		var kmlLayer = new google.maps.KmlLayer(kmlFile, {
			suppressInfoWindows: true,
			map: map
		});

	}
}

/**
 * Convert a map type name (road,satellite,hybrid,terrain) to Google map types
 * @param  String mapTypeName  		generic name of the map type
 * @return google.maps.MapTypeId 	map type in Google format
 */
function convertMapType(mapTypeName) {
	var result = google.maps.MapTypeId.ROADMAP;
	switch (mapTypeName) {
		case 'aerial':
			result = google.maps.MapTypeId.SATELLITE;
			break;
		case 'hybrid':
			result = google.maps.MapTypeId.HYBRID;
			break;
		case 'terrain':
			result = google.maps.MapTypeId.TERRAIN;
			break;
	}
	return result;
}

/**
 * After the Google Maps API has been loaded init the relevant Google maps
 */
function loadShortCodeStreetView() {

	var svs = $('div[data-streetview]');

	svs.each(function(index) {
		var svnode = $(this);
		var svnodeid = svnode.attr('id');
		var lat = parseFloat(svnode.attr('data-latitude'));
		var lon = parseFloat(svnode.attr('data-longitude'));
		var zoom = parseInt(svnode.attr('data-zoom'));
		var heading = parseFloat(svnode.attr('data-heading'));
		var pitch = parseFloat(svnode.attr('data-pitch'));

		var location = new google.maps.LatLng(lat,lon);

		  var panoramaOptions = {
		    position: location,
		    pov: {
		      heading: heading,
		      pitch: pitch
		    },
		    zoom: zoom
		  };

		  var domNode = document.getElementById(svnodeid);
		  var pano = new google.maps.StreetViewPanorama(
		      domNode,
		      panoramaOptions);
		  pano.setVisible(true);
	});

}

/**
 * After the Google Maps API has been loaded init the relevant Google maps
 */
function loadShortCodeMaps() {

	var scms = $('div[data-shortcode-map]');

	scms.each(function(index) {
		var scmnode = $(this);
		var scmnodeid = scmnode.attr('id');
		var maptype = convertMapType(scmnode.attr('data-maptype'));
		var lat = parseFloat(scmnode.attr('data-latitude'));
		var lng = parseFloat(scmnode.attr('data-longitude'));
		var zoom = parseInt(scmnode.attr('data-zoom'));
		var allowfullscreen = parseInt(scmnode.attr('data-allowfullscreen'));

		// Google Maps API has already been loaded, so init Google Map
		var mapOptions = {
			center: { lat:lat, lng: lng},
			zoom: zoom,
			mapTypeId: maptype
		};

		var gmap = new google.maps.Map(document.getElementById(scmnodeid),
			mapOptions);
		if (allowfullscreen == 1) {
			gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push(
				FullScreenControl(gmap, "Full Screen", "Original Size")
			);
		}
	});

}

/**
 * Callback function after the Google Maps API has been loaded - renders the maps along with
 * associated points of interest and layers
 */
function loadedGoogleMapsAPI() {
	loadShortCodeMaps();
	loadShortCodeStreetView();

	var maps = $('div[data-map]');

	maps.each(function(index) {
		var mapnode = $(this);
		mapdomid = mapnode.attr('id');
		var map = new google.maps.Map(document.getElementById(mapdomid));

		// initialise geocoder
		geocoder = new google.maps.Geocoder();

		// default of [] renders google maps as per normal
		if (mapnode.attr('data-mapstyles')) {
			var json=$.parseJSON(mapnode.attr('data-mapstyles'));
			map.setOptions({styles: json});
		};

		if (mapnode.data['data-allowfullscreen']) {
			map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
				FullScreenControl(map, "Full Screen", "Original Size")
			);
		}

		var markerjson = $.parseJSON(mapnode.attr('data-mapmarkers'));
		var useClusterer = mapnode.attr('data-useclusterer');
		var enableAutomaticCenterZoom = mapnode.attr('data-enableautocentrezoom');
		var defaultHideMarker = mapnode.attr('data-defaulthidemarker');
		var markers = addAllMarkers(map, markerjson, useClusterer,
			enableAutomaticCenterZoom, defaultHideMarker);
		var allowfullscreen = parseInt(mapnode.attr('data-allowfullscreen'));

		if (enableAutomaticCenterZoom == '1') {
			centre = $.parseJSON(mapnode.attr('data-centre'));
			map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));

			var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map.minLat, map.minLng),
				new google.maps.LatLng(map.maxLat, map.maxLng));
			map.fitBounds(bds);
		} else {
			centre = $.parseJSON(mapnode.attr('data-centre'));
			map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));
			var zoom = parseInt(mapnode.attr('data-zoom'));
			map.setZoom(zoom);
		}

		if (allowfullscreen == 1) {
			map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
				FullScreenControl(map, "Full Screen", "Original Size")
			);
		}

		var googlemaptype = convertMapType(mapnode.attr('data-maptype'));
		map.setMapTypeId(googlemaptype);

		if (useClusterer) {
			// ensure zoom and grid size are integers by prefixing with unary plus
			var clustererGridSize = parseInt(mapnode.attr('data-clusterergridsize'));
			var clustererMaxZoom = parseInt(mapnode.attr('data-clusterermaxzoom'));
			var mcOptions = {gridSize: clustererGridSize, maxZoom: clustererMaxZoom};
			var markerCluster = new MarkerClusterer(map,markers,mcOptions);
		}

		var lines =$.parseJSON(mapnode.attr('data-lines'));
		addLines(map, lines);

		var kmlFiles =$.parseJSON(mapnode.attr('data-kmlfiles'));
		addKmlFiles(map, kmlFiles);

		var infoWindow = new google.maps.InfoWindow({
			content: ''
		});
		infoWindows[mapdomid] = infoWindow;

		// trigger an event now that the map has been initialised
		// Use this for example to add listeners to the map from another JS file
		mapnode.trigger( "mapInitialised", [ map ] );
	});
}

function nearestPOIs() {

	// normally will only be one
	var nears = $('div[data-nearest-poi]');

	nears.each(function(index) {
		var nearnode = $(this);
		var layerID = nearnode.attr('data-layer-id');

		if(geoPosition.init()){  // Geolocation Initialisation
            geoPosition.getCurrentPosition(success_callback,error_callback,{enableHighAccuracy:true});
	    }else{
	        alert('your location is not available');
	    }
	    //geoPositionSimulator.init();
	});
}

function success_callback(p){
	console.log(p.coords);
        // p.latitude : latitude value
        // p.longitude : longitude value
        //
        var url = window.location;
        url = url + 'find?lat='+p.coords.latitude+'&lng='+p.coords.longitude;
        window.location = url;
}

 function error_callback(p){
 	alert('error)');
 };
<commit_msg>FIX: Ensure Google maps is initialised correctly for the default Simple theme<commit_after>var primeMap;

(function($) {

	// FIXME - make non global
	var infoWindows = [];

	/**
	 * Create a google map pin from data provided
	 * @param  {GoogleMap} map             Instance of a google map
	 * @param  {Number} lat                Latitude of pin
	 * @param  {Number} lng                Longitude of pin
	 * @param  {String} html               HTML for information window
	 * @param  {String} icon               URL of alternative map icon, or blank for default
	 * @param  {boolean} useClusterer      Whether or not to use clusterer
	 * @param  {boolean} enableWindowZoom  Whether or not to enable zoom on the rendered map
	 * @param  {boolean} defaultHideMarker Whether or not to hide markers initially
	 * @return {MapMarker}                 Google map pin object
	 */
	function createMarker(map, lat, lng, html, category, icon, useClusterer, enableWindowZoom,
		defaultHideMarker) {

		mapId = map.getDiv().getAttribute('id');

		var marker = new google.maps.Marker();
		marker.setPosition(new google.maps.LatLng(lat, lng));
		marker.mycategory = category;

		if (icon && icon !== '') {
			var image = new google.maps.MarkerImage(icon);
			marker.setIcon(image);
		}

		if (!useClusterer) {
			marker.setMap(map);
		}

		google.maps.event.addListener(marker, "click", function() {
			if (enableWindowZoom) {
				map.setCenter(new google.maps.LatLng(lat, lng), 12); // $InfoWindowZoom);
			}
			var infoWindow = infoWindows[mapId];
			infoWindow.setContent(html);
			infoWindow.open(map, this);
		});

		//FIXME gmarkers[mapId].push(marker);

		if (defaultHideMarker) {
			marker.hide();
		}
		return marker;
	}

	/**
	 * Get the current latitude
	 * @return {float} Current latitude
	 */
	function getCurrentLat() {
		return current_lat;
	}

	/**
	 * Get the current longitude
	 * @return {float} The current longitude
	 */
	function getCurrentLng() {
		return current_lng;
	}

	/**
	 * Convert JSON point data into google map markers
	 * @param {GoogleMap} map             Google Map instance
	 * @param {array} markers            point data loaded from JSON
	 * @param {boolean} useClusterer      Whether or not to use the clusterer
	 * @param {boolean} enableWindowZoom  Whether or not zoom is enabled
	 * @param {boolean} defaultHideMarker Whether or not to hide markers
	 * * @return array of Google map markers converted from the JSON data
	 */
	function addAllMarkers(map, markers, useClusterer, enableWindowZoom, defaultHideMarker) {

		// these will be altered by adding markers
		map.minLat = 1000000;
		map.minLng = 1000000;
		map.maxLat = -1000000;
		map.maxLng = -1000000;

		var allmarkers = [];
		for (var i = 0; i < markers.length; i++) {
			var markerinfo = markers[i];
			var marker = createMarker(map, markerinfo.latitude, markerinfo.longitude, markerinfo.html,
				markerinfo.category, markerinfo.icon, useClusterer, enableWindowZoom,
				defaultHideMarker);

			var latitude = parseFloat(markerinfo.latitude);
			var longitude = parseFloat(markerinfo.longitude);

			// update lat,lon min to lat,lon max
			if (latitude < map.minLat) {
				map.minLat = latitude;

			}
			if (latitude > map.maxLat) {
				map.maxLat = latitude;
			}

			if (longitude > map.maxLng) {
				map.maxLng = longitude;

			}

			if (longitude < map.minLng) {
				map.minLng = longitude;
			}

			allmarkers.push(marker);
		}

		var centreCoordinates = [];
		centreCoordinates.lat = (parseFloat(map.minLat) + parseFloat(map.maxLat)) / 2;
		centreCoordinates.lng = (parseFloat(map.minLng) + parseFloat(map.maxLng)) / 2;
		map.centreCoordinates = centreCoordinates;
		return allmarkers;
	}

	/**
	 * Add lines to a Google map
	 * @param {googleMap} map   Google map instance
	 * @param {array} lines Line data loaded from json, lat1,lon1 to lat2,lon2
	 */
	function addLines(map, lines) {
		for (i = 0; i < lines.length; i++) {
			var line = lines[i];
			var point1 = new google.maps.LatLng(line.lat1, line.lon1);
			var point2 = new google.maps.LatLng(line.lat2, line.lon2);
			var points = [point1, point2];
			var pl = new google.maps.Polyline({
				path: points,
				strokeColor: line.color,
				strokeWeight: 4,
				strokeOpacity: 0.8
			});
			pl.setMap(map);
		}
	}

	/**
	 * Add one or more (max of 25) KML files to a Google map
	 * @param {GoogleMap} map      A Google Map instance
	 * @param {array} kmlFiles array of URLs for KML files
	 */
	function addKmlFiles(map, kmlFiles) {
		for (var i = 0; i < kmlFiles.length; i++) {
			var kmlFile = kmlFiles[i];
			var kmlLayer = new google.maps.KmlLayer(kmlFile, {
				suppressInfoWindows: true,
				map: map
			});

		}
	}

	/**
	 * Convert a map type name (road,satellite,hybrid,terrain) to Google map types
	 * @param  String mapTypeName  		generic name of the map type
	 * @return google.maps.MapTypeId 	map type in Google format
	 */
	function convertMapType(mapTypeName) {
		var result = google.maps.MapTypeId.ROADMAP;
		switch (mapTypeName) {
			case 'aerial':
				result = google.maps.MapTypeId.SATELLITE;
				break;
			case 'hybrid':
				result = google.maps.MapTypeId.HYBRID;
				break;
			case 'terrain':
				result = google.maps.MapTypeId.TERRAIN;
				break;
		}
		return result;
	}

	/**
	 * After the Google Maps API has been loaded init the relevant Google maps
	 */
	function loadShortCodeStreetView() {

		var svs = $('div[data-streetview]');

		svs.each(function(index) {
			var svnode = $(this);
			var svnodeid = svnode.attr('id');
			var lat = parseFloat(svnode.attr('data-latitude'));
			var lon = parseFloat(svnode.attr('data-longitude'));
			var zoom = parseInt(svnode.attr('data-zoom'));
			var heading = parseFloat(svnode.attr('data-heading'));
			var pitch = parseFloat(svnode.attr('data-pitch'));

			var location = new google.maps.LatLng(lat, lon);

			var panoramaOptions = {
				position: location,
				pov: {
					heading: heading,
					pitch: pitch
				},
				zoom: zoom
			};

			var domNode = document.getElementById(svnodeid);
			var pano = new google.maps.StreetViewPanorama(
				domNode,
				panoramaOptions);
			pano.setVisible(true);
		});

	}

	/**
	 * After the Google Maps API has been loaded init the relevant Google maps
	 */
	function loadShortCodeMaps() {

		var scms = $('div[data-shortcode-map]');

		scms.each(function(index) {
			var scmnode = $(this);
			var scmnodeid = scmnode.attr('id');
			var maptype = convertMapType(scmnode.attr('data-maptype'));
			var lat = parseFloat(scmnode.attr('data-latitude'));
			var lng = parseFloat(scmnode.attr('data-longitude'));
			var zoom = parseInt(scmnode.attr('data-zoom'));
			var allowfullscreen = parseInt(scmnode.attr('data-allowfullscreen'));

			// Google Maps API has already been loaded, so init Google Map
			var mapOptions = {
				center: {
					lat: lat,
					lng: lng
				},
				zoom: zoom,
				mapTypeId: maptype
			};

			var gmap = new google.maps.Map(document.getElementById(scmnodeid),
				mapOptions);
			if (allowfullscreen == 1) {
				gmap.controls[google.maps.ControlPosition.TOP_RIGHT].push(
					FullScreenControl(gmap, "Full Screen", "Original Size")
				);
			}
		});

	}

	/**
	 * Callback function after the Google Maps API has been loaded - renders the maps along with
	 * associated points of interest and layers
	 */
	primeMap = function loadedGoogleMapsAPI() {
		loadShortCodeMaps();
		loadShortCodeStreetView();

		var maps = $('div[data-map]');

		maps.each(function(index) {
			var mapnode = $(this);
			mapdomid = mapnode.attr('id');
			var map = new google.maps.Map(document.getElementById(mapdomid));

			// initialise geocoder
			geocoder = new google.maps.Geocoder();

			// default of [] renders google maps as per normal
			if (mapnode.attr('data-mapstyles')) {
				var json = $.parseJSON(mapnode.attr('data-mapstyles'));
				map.setOptions({
					styles: json
				});
			}

			if (mapnode.data['data-allowfullscreen']) {
				map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
					FullScreenControl(map, "Full Screen", "Original Size")
				);
			}

			var markerjson = $.parseJSON(mapnode.attr('data-mapmarkers'));
			var useClusterer = mapnode.attr('data-useclusterer');
			var enableAutomaticCenterZoom = mapnode.attr('data-enableautocentrezoom');
			var defaultHideMarker = mapnode.attr('data-defaulthidemarker');
			var markers = addAllMarkers(map, markerjson, useClusterer,
				enableAutomaticCenterZoom, defaultHideMarker);
			var allowfullscreen = parseInt(mapnode.attr('data-allowfullscreen'));

			if (enableAutomaticCenterZoom == '1') {
				centre = $.parseJSON(mapnode.attr('data-centre'));
				map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));

				var bds = new google.maps.LatLngBounds(new google.maps.LatLng(map.minLat, map.minLng),
					new google.maps.LatLng(map.maxLat, map.maxLng));
				map.fitBounds(bds);
			} else {
				centre = $.parseJSON(mapnode.attr('data-centre'));
				map.setCenter(new google.maps.LatLng(centre.lat, centre.lng));
				var zoom = parseInt(mapnode.attr('data-zoom'));
				map.setZoom(zoom);
			}

			if (allowfullscreen == 1) {
				map.controls[google.maps.ControlPosition.TOP_RIGHT].push(
					FullScreenControl(map, "Full Screen", "Original Size")
				);
			}

			var googlemaptype = convertMapType(mapnode.attr('data-maptype'));
			map.setMapTypeId(googlemaptype);

			if (useClusterer) {
				// ensure zoom and grid size are integers by prefixing with unary plus
				var clustererGridSize = parseInt(mapnode.attr('data-clusterergridsize'));
				var clustererMaxZoom = parseInt(mapnode.attr('data-clusterermaxzoom'));
				var mcOptions = {
					gridSize: clustererGridSize,
					maxZoom: clustererMaxZoom
				};
				var markerCluster = new MarkerClusterer(map, markers, mcOptions);
			}

			var lines = $.parseJSON(mapnode.attr('data-lines'));
			addLines(map, lines);

			var kmlFiles = $.parseJSON(mapnode.attr('data-kmlfiles'));
			addKmlFiles(map, kmlFiles);

			var infoWindow = new google.maps.InfoWindow({
				content: ''
			});
			infoWindows[mapdomid] = infoWindow;

			// trigger an event now that the map has been initialised
			// Use this for example to add listeners to the map from another JS file
			mapnode.trigger("mapInitialised", [map]);
		});
	};

	function nearestPOIs() {

		// normally will only be one
		var nears = $('div[data-nearest-poi]');

		nears.each(function(index) {
			var nearnode = $(this);
			var layerID = nearnode.attr('data-layer-id');

			if (geoPosition.init()) { // Geolocation Initialisation
				geoPosition.getCurrentPosition(success_callback, error_callback, {
					enableHighAccuracy: true
				});
			} else {
				alert('your location is not available');
			}
			//geoPositionSimulator.init();
		});
	}

	function success_callback(p) {
		console.log(p.coords);
		// p.latitude : latitude value
		// p.longitude : longitude value
		//
		var url = window.location;
		url = url + 'find?lat=' + p.coords.latitude + '&lng=' + p.coords.longitude;
		window.location = url;
	}

	function error_callback(p) {
		alert('error)');
	}

	window.loadGoogleMapsScript = function() {
		var script = document.createElement('script');
		script.type = 'text/javascript';
		script.src = 'https://maps.googleapis.com/maps/api/js?' +
			'&sensor=false&callback=loadedGoogleMapsAPI&hl=en';
		document.body.appendChild(script);
	};

	window.addEventListener ?
		window.addEventListener("load", loadGoogleMapsScript, false) :
		window.attachEvent && window.attachEvent("onload", loadGoogleMapsScript);
})(jQuery);

function loadedGoogleMapsAPI() {
	primeMap();
}
<|endoftext|>