<commit_before>"use strict";

let config = require("../config");
let EventEmitter = require("events");
let moment = require("moment");
let storage = require("./storage");
let util = require("util");

class Result {
  constructor(type, data, status) {
    this.type = type;
    this.data = data;
    this.status = status || 200;
  }

  get collection() {
    return "total" in this.data && "items" in this.data;
  }

  get items() {
    return this.data.items || {};
  }

  get references() {
    return this.data.references || {};
  }
}

exports.Result = Result;

class Searcher {
  constructor() {
    this.storages = new Map;
  }

  searchOne(type, params) {
    return this.search(type, params).then(result => {
      return new Result(result.type, result.items[0]);
    });
  }

  search(type, params) {
    return this.storage(type).search(params).then(result => {
      return new Result(result.type, result);
    });
  }

  addStorage(storage) {
    storage.searcher = this;
    this.storages.set(storage.type, storage);
    return storage;
  }

  storage(type) {
    if (!this.storages.has(type)) {
      throw new Error("No storage for " + type);
    }
    return this.storages.get(type);
  }

  get types() {
    return [...this.storages.keys()];
  }
}

let searcher = exports.searcher = new Searcher;

let dateRange = function(operator, field, value) {
  return {
    [field]: {
      [operator]: value
    }
  };
};

let dateAfter = dateRange.bind(null, "gte");
let dateBefore = dateRange.bind(null, "lte");

let organisations = searcher.addStorage(new storage.ElasticStorage({
  type: "organisation",
  index: config.elastic.index,
  sections: ["services", "accessibility", "phone_numbers", "pictures", "links", "persons", "extra", "mail_address", "meta", "_internal"],
  references: [
    {
      name: "region",
      field: "city.region",
    },
    {
      name: "consortium",
      field: "city.consortium",
    },
    {
      name: "provincial_library",
      field: "city.provincial_library",
    },
    {
      name: "city",
      field: "city",
    }
  ],
  filters: {
    id: {
      query: "ids",
      field: "_id",
      multiple: true,
    },
    parent: {
      query: "terms",
      multiple: true,
    },
    name: {
      query: "match_phrase_prefix",
      multilang: true,
    },
    short_name: {
      query: "match_phrase_prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "slug",
    },
    city: {
      field: "city.id",
      filter: "terms",
      multiple: true,
    },
    "city.name": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.name",
    },
    "city.slug": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.slug",
    },
    branch_type: {
      filter: "terms",
      multiple: true,
    },
    type: {
      filter: "terms",
      multiple: true,
    },
    region: {
      field: "city.region.id",
      filter: "terms",
      multiple: true,
    },
    "region.name": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.region.name",
    },
    "region.slug": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.region.slug",
    },
    consortium: {
      filter: "terms",
      multiple: true,
      field: "_internal.consortium.id"
    },
    "consortium.name": {
      filter: "terms",
      multiple: true,
      multilang: true,
      field: "_internal.consortium.name",
    },
    "consortium.slug": {
      filter: "terms",
      multiple: true,
      multilang: true,
      field: "_internal.consortium.slug",
    },
    service: {
      field: "services.id",
      filter: "terms",
      multiple: true,
    },
    "service.name": {
      field: "services.name",
      query: "match_phrase_prefix",
      multilang: true,
    },
    "service.slug": {
      field: "services.slug",
      query: "terms",
      multilang: true,
    },
    "modified.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "modified.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    "created.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "created.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    geo: {
      query: "function_score",
      callback: (field, value, params) => {
        let distance = util.format("%dkm", params.distance);
        return {
          filter: {
            geo_distance: {
              "address.coordinates": value,
              distance: distance,
            }
          },
          linear: {
            "address.coordinates": {
              origin: value,
              offset: "100m",
              scale: distance
            }
          }
        };
      }
    }
  },
  sorting: {
    modified: "meta.modified",
    created: "meta.created",
    distance: (direction, params) => {
      return "_score:desc";
    },
    name: (direction, params) => {
      return util.format("name.%s.raw:%s", params.lang || "fi", direction);
    },
    short_name: (direction, params) => {
      return util.format("short_name.%s.raw:%s", params.lang || "fi", direction);
    }
  },
}));

organisations.on("result", event => {
  if (event.params.with.indexOf("schedules") != -1) {
    let params = {
      "period.start": event.params["period.start"] || "",
      "period.end": event.params["period.end"] || "",
      sort: "date",
      limit: 9999,
      organisation: event.result.items.map(o => o.id).join(","),
    };

    return searcher.search("opening_time", params).then(result => {
      let organisations = event.result.items;
      let schedules = result.items;
      let cache = new Map;
      schedules.forEach(day => {
        if (!cache.has(day.organisation)) {
          cache.set(day.organisation, []);
        }
        cache.get(day.organisation).push(day);
        delete day.organisation;
      });
      organisations.forEach(organisation => {
        organisation.schedules = cache.get(organisation.id) || [];
      });
    });
  }
});

searcher.addStorage(new storage.ElasticStorage({
  type: "service",
  index: config.elastic.index,
  filters: {
    id: {
      query: "ids",
      field: "_id",
      multiple: true,
    },
    name: {
      query: "match_phrase_prefix",
      multilang: true,
    },
    type: {
      query: "terms",
      multiple: true,
    },
    slug: {
      query: "terms",
      multilang: true,
      multiple: true,
    },
    "modified.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "modified.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    "created.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "created.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    "helmet:type_priority": {
      field: "helmet_type_priority",
      filter: "terms",
      multiple: true,
    }
  },
  sorting: {
    id: true,
    type: true,
    modified: "meta.modified",
    created: "meta.created",
    name: (direction, params) => {
      return util.format("name.%s.raw:%s", params.lang || "fi", direction);
    }
  }
}));

searcher.addStorage(new storage.ElasticStorage({
  type: "person",
  index: config.elastic.index,
  filters: {
    id: {
      query: "ids",
      field: "_id",
      multiple: true,
    },
    organisation: {
      query: "terms",
      multiple: true,
    },
    first_name: {
      query: "prefix",
    },
    last_name: {
      query: "prefix",
    },
    name: {
      query: "bool",
      callback: (field, value, params) => {
        let query = {must:[]};

        value.trim().split(/\s+/).forEach(name => {
          query.must.push({
            bool: {
              should: [
                {
                  prefix: {
                    first_name: name
                  }
                },
                {
                  prefix: {
                    last_name: name
                  }
                }
              ]
            }
          });
        });
        return query;
      }
    },
  },
  sorting: {
    id: true,
    first_name: true,
    last_name: true,
    organisation: true,
    name: (direction, params) => {
      return util.format("name.%s:%s", params.lang || "fi", direction);
    }
  }
}));

let isRelativeDate = function(value) {
  if (!value) {
    return false;
  } else if (value == "today") {
    return true;
  } else {
    return value.search(/^-?\d+[dwm]$/) != -1;
  }
};

let compileRelativeDate = function(value, filter) {
  if (value == "today") {
    return moment();
  }
  let units = new Map([["d", "days"], ["w", "weeks"], ["m", "months"]]);
  let num = parseInt(value, 10);
  let unit = units.get(value.substr(-1));
  let date = moment().add(num, unit);
  return filter ? filter(date, num, unit) : date;
}

let periodDateRange = function(operator, field, value) {
  if (isRelativeDate(value)) {
    let date = compileRelativeDate(value, (date, num, unit) => {
      if (unit == "weeks") {
        operator == "gte" ? date.startOf("isoweek") : date.endOf("isoweek");
      } else if (unit == "months") {
        operator == "gte" ? date.startOf("month") : date.endOf("month");
      }
      return date;
    });

    value = date.format("YYYY-MM-DD");
  }
  return dateRange(operator, field, value);
};

let schedules = searcher.addStorage(new storage.ElasticStorage({
  type: "opening_time",
  index: "libdir_schedules",
  sections: ["meta"],
  filters: {
    "period.start": {
      filter: "range",
      field: "date",
      callback: function(field, value) {
        return periodDateRange("gte", field, value || moment().startOf("isoWeek").format("YYYY-MM-DD"));
      }
    },
    "period.end": {
      filter: "range",
      field: "date",
      callback: function(field, value) {
        return periodDateRange("lte", field, value || moment().endOf("isoWeek").format("YYYY-MM-DD"));
      }
    },
    organisation: {
      filter: "terms",
      field: "organisation",
      multiple: true,
    }
  },
  sorting: {
    organisation: true,
    date: true,
  }
}));

schedules.on("result", event => {
  if (event.params.refs.indexOf("period") != -1) {
    let cache = new Set;

    event.result.items.map(day => {
      // Mobile libraries have no "sections".
      if ("sections" in day) {
        Object.keys(day.sections).map(section => cache.add(day.sections[section].period));
      }
      cache.add(day.period);
    });

    let pids = [...cache.values()].join(",");

    return searcher.search("period", {id: pids, limit: 9999}).then(result => {
      let periods = {};

      result.items.forEach(period => {
        periods[period.id] = period;
      });

      event.result.references = {period: periods};
    });
  }
});

searcher.addStorage(new storage.PostgresStorage({
  type: "city",
  table: "cities",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      query: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
    consortium: {
      field: "consortium_id",
      filter: "terms",
    },
    region: {
      field: "region_id",
      filter: "terms"
    },
    provincial_library: {
      field: "provincial_library_id",
      filter: "terms",
    },
  },
}));

let consortiums = searcher.addStorage(new storage.PostgresStorage({
  type: "consortium",
  table: "consortiums",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
    description: {
      multilang: true
    },
    logo: { }
  }
}));

let periods = searcher.addStorage(new storage.PostgresStorage({
  type: "period",
  table: "periods",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    description: {
      multilang: true
    },
    valid_from: {},
    valid_until: {},
    organisation: {
      field: "organisation_id"
    },
  }
}));

periods.on("query", event => {
  event.query.andWhere("organisation_id", "IS NOT", null);
});

periods.on("load", period => {
  if (period.valid_from) {
    period.valid_from = moment(period.valid_from).format("YYYY-MM-DD");
  }
  if (period.valid_until) {
    period.valid_until = moment(period.valid_until).format("YYYY-MM-DD");
  }
});

consortiums.on("load", consortium => {
  let filename = consortium.logo;

  if (filename) {
    let urls = {};
    ["small", "medium"].forEach(size => {
      urls[size] = util.format("%s/%s/%s", config.api.resource_url, size, filename);
    });
    consortium.logo = urls;
  } else {
    consortium.logo = null;
  }
});

searcher.addStorage(new storage.PostgresStorage({
  type: "region",
  table: "regions",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
  }
}));

searcher.addStorage(new storage.PostgresStorage({
  type: "provincial_library",
  table: "provincial_libraries",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
    province: {
      multilang: true,
    }
  }
}));

exports.storageFor = function(type) {
  return searcher.storage(type);
};

exports.search = function(type, params) {
  return searcher.search(type, params);
};

exports.searchOne = function(type, params) {
  return searcher.searchOne(type, params);
};
<commit_msg>Support period references in organisation query with schedules<commit_after>"use strict";

let config = require("../config");
let EventEmitter = require("events");
let moment = require("moment");
let storage = require("./storage");
let util = require("util");

class Result {
  constructor(type, data, status) {
    this.type = type;
    this.data = data;
    this.status = status || 200;
  }

  get collection() {
    return "total" in this.data && "items" in this.data;
  }

  get items() {
    return this.data.items || {};
  }

  get references() {
    return this.data.references || {};
  }
}

exports.Result = Result;

class Searcher {
  constructor() {
    this.storages = new Map;
  }

  searchOne(type, params) {
    return this.search(type, params).then(result => {
      return new Result(result.type, result.items[0]);
    });
  }

  search(type, params) {
    return this.storage(type).search(params).then(result => {
      return new Result(result.type, result);
    });
  }

  addStorage(storage) {
    storage.searcher = this;
    this.storages.set(storage.type, storage);
    return storage;
  }

  storage(type) {
    if (!this.storages.has(type)) {
      throw new Error("No storage for " + type);
    }
    return this.storages.get(type);
  }

  get types() {
    return [...this.storages.keys()];
  }
}

let searcher = exports.searcher = new Searcher;

let dateRange = function(operator, field, value) {
  return {
    [field]: {
      [operator]: value
    }
  };
};

let dateAfter = dateRange.bind(null, "gte");
let dateBefore = dateRange.bind(null, "lte");

let organisations = searcher.addStorage(new storage.ElasticStorage({
  type: "organisation",
  index: config.elastic.index,
  sections: ["services", "accessibility", "phone_numbers", "pictures", "links", "persons", "extra", "mail_address", "meta", "_internal"],
  references: [
    {
      name: "region",
      field: "city.region",
    },
    {
      name: "consortium",
      field: "city.consortium",
    },
    {
      name: "provincial_library",
      field: "city.provincial_library",
    },
    {
      name: "city",
      field: "city",
    }
  ],
  filters: {
    id: {
      query: "ids",
      field: "_id",
      multiple: true,
    },
    parent: {
      query: "terms",
      multiple: true,
    },
    name: {
      query: "match_phrase_prefix",
      multilang: true,
    },
    short_name: {
      query: "match_phrase_prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "slug",
    },
    city: {
      field: "city.id",
      filter: "terms",
      multiple: true,
    },
    "city.name": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.name",
    },
    "city.slug": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.slug",
    },
    branch_type: {
      filter: "terms",
      multiple: true,
    },
    type: {
      filter: "terms",
      multiple: true,
    },
    region: {
      field: "city.region.id",
      filter: "terms",
      multiple: true,
    },
    "region.name": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.region.name",
    },
    "region.slug": {
      filter: "terms",
      multilang: true,
      multiple: true,
      field: "city.region.slug",
    },
    consortium: {
      filter: "terms",
      multiple: true,
      field: "_internal.consortium.id"
    },
    "consortium.name": {
      filter: "terms",
      multiple: true,
      multilang: true,
      field: "_internal.consortium.name",
    },
    "consortium.slug": {
      filter: "terms",
      multiple: true,
      multilang: true,
      field: "_internal.consortium.slug",
    },
    service: {
      field: "services.id",
      filter: "terms",
      multiple: true,
    },
    "service.name": {
      field: "services.name",
      query: "match_phrase_prefix",
      multilang: true,
    },
    "service.slug": {
      field: "services.slug",
      query: "terms",
      multilang: true,
    },
    "modified.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "modified.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    "created.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "created.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    geo: {
      query: "function_score",
      callback: (field, value, params) => {
        let distance = util.format("%dkm", params.distance);
        return {
          filter: {
            geo_distance: {
              "address.coordinates": value,
              distance: distance,
            }
          },
          linear: {
            "address.coordinates": {
              origin: value,
              offset: "100m",
              scale: distance
            }
          }
        };
      }
    }
  },
  sorting: {
    modified: "meta.modified",
    created: "meta.created",
    distance: (direction, params) => {
      return "_score:desc";
    },
    name: (direction, params) => {
      return util.format("name.%s.raw:%s", params.lang || "fi", direction);
    },
    short_name: (direction, params) => {
      return util.format("short_name.%s.raw:%s", params.lang || "fi", direction);
    }
  },
}));

organisations.on("result", event => {
  if (event.params.with.indexOf("schedules") != -1) {
    let params = {
      "period.start": event.params["period.start"] || "",
      "period.end": event.params["period.end"] || "",
      sort: "date",
      limit: 9999,
      organisation: event.result.items.map(o => o.id).join(","),
      refs: event.params.refs
    };

    return searcher.search("opening_time", params).then(result => {
      let organisations = event.result.items;
      let schedules = result.items;
      let cache = new Map;
      schedules.forEach(day => {
        if (!cache.has(day.organisation)) {
          cache.set(day.organisation, []);
        }
        cache.get(day.organisation).push(day);
        delete day.organisation;
      });
      organisations.forEach(organisation => {
        organisation.schedules = cache.get(organisation.id) || [];
      });
      Object.keys(result.references).forEach(key => {
        event.result.references[key] = result.references[key];
      })
    });
  }
});

searcher.addStorage(new storage.ElasticStorage({
  type: "service",
  index: config.elastic.index,
  filters: {
    id: {
      query: "ids",
      field: "_id",
      multiple: true,
    },
    name: {
      query: "match_phrase_prefix",
      multilang: true,
    },
    type: {
      query: "terms",
      multiple: true,
    },
    slug: {
      query: "terms",
      multilang: true,
      multiple: true,
    },
    "modified.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "modified.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    "created.after": {
      field: "meta.modified",
      filter: "range",
      callback: dateAfter,
    },
    "created.before": {
      field: "meta.modified",
      filter: "range",
      callback: dateBefore,
    },
    "helmet:type_priority": {
      field: "helmet_type_priority",
      filter: "terms",
      multiple: true,
    }
  },
  sorting: {
    id: true,
    type: true,
    modified: "meta.modified",
    created: "meta.created",
    name: (direction, params) => {
      return util.format("name.%s.raw:%s", params.lang || "fi", direction);
    }
  }
}));

searcher.addStorage(new storage.ElasticStorage({
  type: "person",
  index: config.elastic.index,
  filters: {
    id: {
      query: "ids",
      field: "_id",
      multiple: true,
    },
    organisation: {
      query: "terms",
      multiple: true,
    },
    first_name: {
      query: "prefix",
    },
    last_name: {
      query: "prefix",
    },
    name: {
      query: "bool",
      callback: (field, value, params) => {
        let query = {must:[]};

        value.trim().split(/\s+/).forEach(name => {
          query.must.push({
            bool: {
              should: [
                {
                  prefix: {
                    first_name: name
                  }
                },
                {
                  prefix: {
                    last_name: name
                  }
                }
              ]
            }
          });
        });
        return query;
      }
    },
  },
  sorting: {
    id: true,
    first_name: true,
    last_name: true,
    organisation: true,
    name: (direction, params) => {
      return util.format("name.%s:%s", params.lang || "fi", direction);
    }
  }
}));

let isRelativeDate = function(value) {
  if (!value) {
    return false;
  } else if (value == "today") {
    return true;
  } else {
    return value.search(/^-?\d+[dwm]$/) != -1;
  }
};

let compileRelativeDate = function(value, filter) {
  if (value == "today") {
    return moment();
  }
  let units = new Map([["d", "days"], ["w", "weeks"], ["m", "months"]]);
  let num = parseInt(value, 10);
  let unit = units.get(value.substr(-1));
  let date = moment().add(num, unit);
  return filter ? filter(date, num, unit) : date;
}

let periodDateRange = function(operator, field, value) {
  if (isRelativeDate(value)) {
    let date = compileRelativeDate(value, (date, num, unit) => {
      if (unit == "weeks") {
        operator == "gte" ? date.startOf("isoweek") : date.endOf("isoweek");
      } else if (unit == "months") {
        operator == "gte" ? date.startOf("month") : date.endOf("month");
      }
      return date;
    });

    value = date.format("YYYY-MM-DD");
  }
  return dateRange(operator, field, value);
};

let schedules = searcher.addStorage(new storage.ElasticStorage({
  type: "opening_time",
  index: "libdir_schedules",
  sections: ["meta"],
  filters: {
    "period.start": {
      filter: "range",
      field: "date",
      callback: function(field, value) {
        return periodDateRange("gte", field, value || moment().startOf("isoWeek").format("YYYY-MM-DD"));
      }
    },
    "period.end": {
      filter: "range",
      field: "date",
      callback: function(field, value) {
        return periodDateRange("lte", field, value || moment().endOf("isoWeek").format("YYYY-MM-DD"));
      }
    },
    organisation: {
      filter: "terms",
      field: "organisation",
      multiple: true,
    }
  },
  sorting: {
    organisation: true,
    date: true,
  }
}));

schedules.on("result", event => {
  if (event.params.refs.indexOf("period") != -1) {
    let cache = new Set;

    event.result.items.map(day => {
      // Mobile libraries have no "sections".
      if ("sections" in day) {
        Object.keys(day.sections).map(section => cache.add(day.sections[section].period));
      }
      cache.add(day.period);
    });

    if (cache.size > 0) {
      let pids = [...cache.values()].join(",");

      return searcher.search("period", {id: pids, limit: 9999}).then(result => {
        let periods = {};

        result.items.forEach(period => {
          periods[period.id] = period;
        });

        console.log("PERIODS", periods);
        event.result.references = {period: periods};
      });
    } else {
      event.result.references = {period: []};
    }
  }
});

searcher.addStorage(new storage.PostgresStorage({
  type: "city",
  table: "cities",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      query: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
    consortium: {
      field: "consortium_id",
      filter: "terms",
    },
    region: {
      field: "region_id",
      filter: "terms"
    },
    provincial_library: {
      field: "provincial_library_id",
      filter: "terms",
    },
  },
}));

let consortiums = searcher.addStorage(new storage.PostgresStorage({
  type: "consortium",
  table: "consortiums",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
    description: {
      multilang: true
    },
    logo: { }
  }
}));

let periods = searcher.addStorage(new storage.PostgresStorage({
  type: "period",
  table: "periods",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    description: {
      multilang: true
    },
    valid_from: {},
    valid_until: {},
    organisation: {
      field: "organisation_id"
    },
  }
}));

periods.on("query", event => {
  event.query.andWhere("organisation_id", "IS NOT", null);
});

periods.on("load", period => {
  if (period.valid_from) {
    period.valid_from = moment(period.valid_from).format("YYYY-MM-DD");
  }
  if (period.valid_until) {
    period.valid_until = moment(period.valid_until).format("YYYY-MM-DD");
  }
});

consortiums.on("load", consortium => {
  let filename = consortium.logo;

  if (filename) {
    let urls = {};
    ["small", "medium"].forEach(size => {
      urls[size] = util.format("%s/%s/%s", config.api.resource_url, size, filename);
    });
    consortium.logo = urls;
  } else {
    consortium.logo = null;
  }
});

searcher.addStorage(new storage.PostgresStorage({
  type: "region",
  table: "regions",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
  }
}));

searcher.addStorage(new storage.PostgresStorage({
  type: "provincial_library",
  table: "provincial_libraries",
  translated: true,
  schema: {
    id: {
      filter: "terms",
    },
    name: {
      filter: "prefix",
      multilang: true,
    },
    slug: {
      filter: "terms",
      multilang: true,
    },
    province: {
      multilang: true,
    }
  }
}));

exports.storageFor = function(type) {
  return searcher.storage(type);
};

exports.search = function(type, params) {
  return searcher.search(type, params);
};

exports.searchOne = function(type, params) {
  return searcher.searchOne(type, params);
};
<|endoftext|>


<commit_before>var net = require("net"),
    msgpack = require("msgpack2");

var socket = null;
var pid = null;
connect();

function connect() {
    socket = null;
    pid = process.pid.toString();
    var conn = net.createConnection('/tmp/gd.agent.sock', function() {
        console.log("gd.agent.sync connected");
        socket = conn;
    });
    conn.on("error", function(err) {
        console.log("gd.agent.sync error: ", err);
        socket = null;
    });
}

function write(item, retry) {
    try {
        if(!socket && !retry) {
            connect();
            setTimeout(function() { write(item, true); }, 0);
        }
        if(socket) {
            var msg = msgpack.pack(item);
            var length = msg.length;

            var bytes = new Array(4)
            bytes[0] = length >> 24
            bytes[1] = length >> 16
            bytes[2] = length >> 8
            bytes[3] = length

            //for(var i = 0; i < 100; i++){
            socket.write(new Buffer(bytes));
            //socket.write(msg, function() { console.log("gd.agent.sync data written")});
            socket.write(msg);
            //}
        }
    }
    catch(ex) {
        console.log("gd.agent.sync exception: ", ex);
        socket = null;
    }
}

exports.push = function(options, payload) {
    //var data = [];

    if(payload.samples) {
        payload.samples.forEach(function(sample) {
            if(sample.Type === 'HTTP') {
                var item = {
                    type:"Sample",
                    route:sample.Method + ":" + sample.URL + ":" + pid,
                    responsetime:sample._ms,
                    timestamp:sample._ts,
                    cputime:sample["CPU time (ms)"],
                    intimeoperations:sample["In-time operations"]
                };

                //console.log(JSON.stringify(sample));
                //console.log("--------\n", JSON.stringify(sample["In-time operations"]));
                //console.log("\n\n");

                write(item);
            }
        });
    }

    /*
    if(data.length > 0) {
        write(data);
        console.log("gdSync: data written");
    } else {
        console.log("gdSync: no samples");
    }
    */
}
<commit_msg>Less verbosity on socket error<commit_after>var net = require("net"),
    msgpack = require("msgpack2");

var socket = null;
var pid = null;
var lastWasError = false;
connect();

function connect() {
    socket = null;
    pid = process.pid.toString();
    var conn = net.createConnection('/tmp/gd.agent.sock', function() {
        socket = conn;
        lastWasError = false;
        console.log("gd.agent.sync connected");
    });
    conn.on("error", function(err) {
        socket = null;
        if(!lastWasError)
            console.error("gd.agent.sync error: ", err);
        lastWasError = true;
    });
}

function write(item, retry) {
    try {
        if(!socket && !retry) {
            connect();
            setTimeout(function() { write(item, true); }, 0);
        }
        if(socket) {
            var msg = msgpack.pack(item);
            var length = msg.length;

            var bytes = new Array(4)
            bytes[0] = length >> 24
            bytes[1] = length >> 16
            bytes[2] = length >> 8
            bytes[3] = length

            socket.write(new Buffer(bytes));
            //socket.write(msg, function() { console.log("gd.agent.sync data written")});
            //socket.write(msg);
        }
    }
    catch(ex) {
        console.error("gd.agent.sync exception: ", ex);
        socket = null;
    }
}

exports.push = function(options, payload) {
    if(payload.samples) {
        payload.samples.forEach(function(sample) {
            if(sample.Type === 'HTTP') {
                var item = {
                    type:"Sample",
                    route:sample.Method + ":" + sample.URL + ":" + pid,
                    responsetime:sample._ms,
                    timestamp:sample._ts,
                    cputime:sample["CPU time (ms)"],
                    intimeoperations:sample["In-time operations"]
                };

                //console.log(JSON.stringify(sample));
                //console.log("--------\n", JSON.stringify(sample["In-time operations"]));
                //console.log("\n\n");

                write(item);
            }
        });
    }
}
<|endoftext|>


<commit_before>window.__ref__ = (function(){
  if (typeof WeakMap !== 'function') {
    console.info('basisjs-tools-instrumenter is not available');
    return function(info, ref){
      return ref;
    };
  }

  var map = new WeakMap();
  var result = function(loc, ref){
    map.set(ref, loc);
    return ref;
  };

  result.set = result;
  result.get = function(ref) {
    return (ref && map.get(ref)) || undefined;
  };

  result.fixSourceOffset = function(source, offset) {
    if (offset && typeof btoa == 'function') {
      offset = String(offset || 0);
                              //{"version":3,"sections":[{"offset":{"line:0,"
      source = source.replace(/(eyJ2ZXJzaW9uIjozLCJzZWN0aW9ucyI6W3sib2Zmc2V0Ijp7ImxpbmUi)OjAs/, '$1' + btoa(':' + offset + ','));
    }

    return source;
  };

  return result;
})();
<commit_msg>get rid of base64 padding<commit_after>window.__ref__ = (function(){
  if (typeof WeakMap !== 'function') {
    console.info('basisjs-tools-instrumenter is not available');
    return function(info, ref){
      return ref;
    };
  }

  var map = new WeakMap();
  var result = function(loc, ref){
    map.set(ref, loc);
    return ref;
  };

  result.set = result;
  result.get = function(ref) {
    return (ref && map.get(ref)) || undefined;
  };

  result.fixSourceOffset = function(source, offset) {
    if (offset && typeof btoa == 'function') {
      offset = ':' + (offset || 0) + ',';
      while (offset.length % 3) offset += ' ';  // get rid of base64 padding
      //{"version":3,"sections":[{"offset":{"line:0,"
      source = source.replace(/(eyJ2ZXJzaW9uIjozLCJzZWN0aW9ucyI6W3sib2Zmc2V0Ijp7ImxpbmUi)OjAs/, '$1' + btoa(offset));
    }

    return source;
  };

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


<commit_before>"use strict";
var assert = require('assert');
var R = require('ramda');
var Q = require('q');
var Mailer = function Mailer() {};
var sendGridLib = require('sendgrid');
var helper = require('sendgrid').mail;
/* eslint-disable no-unused-vars */
var sendgridInstance = {};
var sendgridMailSettings = {};
var sendgridTrackingSettings = {};
/* eslint-enable no-unused-vars */

var SendgridConnector = function SendgridConnector(settings) {
  assert(typeof settings === 'object', 'cannot init connector without settings');
  assert(typeof settings.api_key === 'string', 'cannot init connector without api key');
  if (settings.api_key){
    this.sendgrid = sendGridLib(settings.api_key); //eslint-disable-line
  }

  this.sendgridMailSettings = settings.mail_settings;
  this.sendgridTrackingSettings = settings.tracking_settings;
  sendgridMailSettings = this.sendgridMailSettings;
  sendgridTrackingSettings = this.sendgridTrackingSettings;
  sendgridInstance = this.sendgrid;
};

SendgridConnector.initialize = function (dataSource, callback) {
  dataSource.connector = new SendgridConnector(dataSource.settings);
  callback();
};

SendgridConnector.prototype.DataAccessObject = Mailer;

/**
 * Send transactional email with options
 * Full list of options are available here: https://www.npmjs.com/package/sendgrid#available-params
 *
 * @param {Object} options
 * {
 *   from: { name: "John", email: "john@cellarise.com" },
 *   to: "mail@cellarise.com",
 *   subject: "Test mail",
 *   text: "Plain text message",
 *   html: "Html messages here"
 * }
 *
 * @param {Function} cb callback
 * @returns {Function} deferred promise
 */
Mailer.send = function (options, cb) { // eslint-disable-line
  var dataSource = this.dataSource,
    connector = dataSource.connector,
    deferred = Q.defer(),
    sendgridMessage = {},
    sendgridEmail,
    request;

  var fn = function (err, result) {
    if (err) {
      deferred.reject(err);
    } else {
      deferred.resolve(result);
    }
    return cb && cb(err, result);
  };

  if (options.__data) {
    options = R.clone(options.__data);
  } else {
    options = R.clone(options);
  }

  assert(connector, 'Cannot send mail without a connector!');

  if (connector.sendgrid) {

    //email from param
    if (R.is(String, options.from)) {
      sendgridMessage.from = options.from;
    } else if (R.is(Object, options.from)) {
      sendgridMessage.fromname = options.from.name;
      if (options.from.hasOwnProperty('address')) {
        sendgridMessage.from = options.from.address;
      } else if (options.from.hasOwnProperty('email')) {
        sendgridMessage.from = options.from.email;
      }
    } else {
      if (options.from_name) {
        sendgridMessage.fromname = options.from_name || '';
      }
      if (options.from_email) {
        sendgridMessage.from = options.from_email || '';
      }
    }
    delete options.from;

    //email to param
    if (R.is(String, options.to)) {
      options.to = R.map(
        function unwrapEmailString(email) {
          return {"email": email};
        },
        options.to.split([',', ';'])
      );
    }

    if (R.is(Object, options.to) && !R.is(Array, options.to)) {
      options.to = [options.to];
    }

    if (R.is(Array, options.to)) {
      sendgridMessage.to = R.map(
        function unwrapArrayString(recipient) {
          if (R.is(String, recipient)) {
            recipient = {"email": recipient};
          }
          return new helper.Email(recipient.email, recipient.name);
        },
        options.to
      );
    } else {
      throw new Error('invalid format for "to"');
    }

    delete options.to;

    //basic sendgrid email
    sendgridEmail = new helper.Mail(
      new helper.Email(sendgridMessage.from, sendgridMessage.fromname),
      options.subject,
      sendgridMessage.to[0], // needs major refactoring to send to each 'to'
      new helper.Content("text/plain", options.text || '')
    );
    if (options.html) {
      sendgridEmail.addContent(new helper.Content("text/html", options.html));
    }

    if (R.is(Array, sendgridEmail.personalizations) && sendgridEmail.personalizations.length > 0) {
      if (options.cc) {
        sendgridEmail.personalizations[0].addCc(options.cc);
      }
      if (options.bcc) {
        sendgridEmail.personalizations[0].addBcc(options.bcc);
      }
    }

    //attachments
    if (R.is(Array, options.attachments)) {
      R.forEach(function eachFile(fileConfig){
        sendgridEmail.addAttachment(fileConfig);
      }, options.attachments);
    }
    if (options.files) {
      R.forEach(function eachFile(fileConfig){
        sendgridEmail.addAttachment(fileConfig);
      }, options.files);
    }

    //sendgrid mail settings
    if (sendgridMailSettings) {
      sendgridEmail.addMailSettings(sendgridMailSettings);
    }
    //trackingSettings
    if (sendgridTrackingSettings) {
      sendgridEmail.addTrackingSettings(sendgridTrackingSettings);
    }
    //Sendgrid options
    if (options.sendGridConfig) {
      //personalizations
      if (R.is(Array, options.sendGridConfig.personalizations)) {
        sendgridEmail.personalizations = options.sendGridConfig.personalizations;
      }
      //sections
      if (R.is(Array, options.sendGridConfig.sections)) {
        R.forEach(function eachSec(section){
          sendgridEmail.addSection(section);
        }, options.sendGridConfig.sections);
      }
      //headers
      if (R.is(Array, options.sendGridConfig.headers)) {
        R.forEach(function eachHeader(header){
          sendgridEmail.addHeader(header);
        }, options.sendGridConfig.headers);
      }
      //templateId
      if (options.sendGridConfig.templateId) {
        sendgridEmail.setTemplateId(options.sendGridConfig.templateId);
      }
      //sendAt
      if (options.sendGridConfig.sendAt) {
        sendgridEmail.setSendAt(options.sendGridConfig.sendAt);
      }
      //batchId
      if (options.sendGridConfig.batchId) {
        sendgridEmail.setBatchId(options.sendGridConfig.batchId);
      }
      //asm
      if (options.sendGridConfig.asm) {
        sendgridEmail.setAsm(options.sendGridConfig.asm);
      }
      //ipPoolName
      if (options.sendGridConfig.ipPoolName) {
        sendgridEmail.setIpPoolName(options.sendGridConfig.ipPoolName);
      }
      //replyTo
      if (options.sendGridConfig.replyTo) {
        sendgridEmail.setReplyTo(options.sendGridConfig.replyTo);
      }
      //categories
      if (R.is(Array, options.sendGridConfig.categories)) {
        R.forEach(function eachCategory(category){
          sendgridEmail.addCategory(category);
        }, options.sendGridConfig.categories);
      }
      //unique_args
      if (R.is(Array, options.sendGridConfig.unique_args)) {
        R.forEach(function eachUniqueArg(arg){
          sendgridEmail.addUniqueArg(arg);
        }, options.sendGridConfig.unique_args);
      }

    }
    request = connector.sendgrid.emptyRequest();
    request.method = 'POST';
    request.path = '/v3/mail/send';
    request.body = sendgridEmail.toJSON();
    connector.sendgrid.API(request, function sgCb(response){ // eslint-disable-line
      var code = response && response.statusCode;
      if (code && (code.toString() === "200" || code.toString() === "202")) {
        fn(null, response);
      } else {
        fn(response);
      }
    });
  } else {
    process.nextTick(function nextTick() { // eslint-disable-line
      fn(null, options);
    });
  }
  return deferred.promise;
};

/**
 * Send an email instance using instance
 */

Mailer.prototype.send = function protoSend(fn) {
  return this.constructor.send(this, fn);
};

/**
 * Export the connector class
 */
module.exports = SendgridConnector;
<commit_msg>Bugfix: renamed addUniqueArg to addCustomArg<commit_after>"use strict";
var assert = require('assert');
var R = require('ramda');
var Q = require('q');
var Mailer = function Mailer() {};
var sendGridLib = require('sendgrid');
var helper = require('sendgrid').mail;
/* eslint-disable no-unused-vars */
var sendgridInstance = {};
var sendgridMailSettings = {};
var sendgridTrackingSettings = {};
/* eslint-enable no-unused-vars */

var SendgridConnector = function SendgridConnector(settings) {
  assert(typeof settings === 'object', 'cannot init connector without settings');
  assert(typeof settings.api_key === 'string', 'cannot init connector without api key');
  if (settings.api_key){
    this.sendgrid = sendGridLib(settings.api_key); //eslint-disable-line
  }

  this.sendgridMailSettings = settings.mail_settings;
  this.sendgridTrackingSettings = settings.tracking_settings;
  sendgridMailSettings = this.sendgridMailSettings;
  sendgridTrackingSettings = this.sendgridTrackingSettings;
  sendgridInstance = this.sendgrid;
};

SendgridConnector.initialize = function (dataSource, callback) {
  dataSource.connector = new SendgridConnector(dataSource.settings);
  callback();
};

SendgridConnector.prototype.DataAccessObject = Mailer;

/**
 * Send transactional email with options
 * Full list of options are available here: https://www.npmjs.com/package/sendgrid#available-params
 *
 * @param {Object} options
 * {
 *   from: { name: "John", email: "john@cellarise.com" },
 *   to: "mail@cellarise.com",
 *   subject: "Test mail",
 *   text: "Plain text message",
 *   html: "Html messages here"
 * }
 *
 * @param {Function} cb callback
 * @returns {Function} deferred promise
 */
Mailer.send = function (options, cb) { // eslint-disable-line
  var dataSource = this.dataSource,
    connector = dataSource.connector,
    deferred = Q.defer(),
    sendgridMessage = {},
    sendgridEmail,
    request;

  var fn = function (err, result) {
    if (err) {
      deferred.reject(err);
    } else {
      deferred.resolve(result);
    }
    return cb && cb(err, result);
  };

  if (options.__data) {
    options = R.clone(options.__data);
  } else {
    options = R.clone(options);
  }

  assert(connector, 'Cannot send mail without a connector!');

  if (connector.sendgrid) {

    //email from param
    if (R.is(String, options.from)) {
      sendgridMessage.from = options.from;
    } else if (R.is(Object, options.from)) {
      sendgridMessage.fromname = options.from.name;
      if (options.from.hasOwnProperty('address')) {
        sendgridMessage.from = options.from.address;
      } else if (options.from.hasOwnProperty('email')) {
        sendgridMessage.from = options.from.email;
      }
    } else {
      if (options.from_name) {
        sendgridMessage.fromname = options.from_name || '';
      }
      if (options.from_email) {
        sendgridMessage.from = options.from_email || '';
      }
    }
    delete options.from;

    //email to param
    if (R.is(String, options.to)) {
      options.to = R.map(
        function unwrapEmailString(email) {
          return {"email": email};
        },
        options.to.split([',', ';'])
      );
    }

    if (R.is(Object, options.to) && !R.is(Array, options.to)) {
      options.to = [options.to];
    }

    if (R.is(Array, options.to)) {
      sendgridMessage.to = R.map(
        function unwrapArrayString(recipient) {
          if (R.is(String, recipient)) {
            recipient = {"email": recipient};
          }
          return new helper.Email(recipient.email, recipient.name);
        },
        options.to
      );
    } else {
      throw new Error('invalid format for "to"');
    }

    delete options.to;

    //basic sendgrid email
    sendgridEmail = new helper.Mail(
      new helper.Email(sendgridMessage.from, sendgridMessage.fromname),
      options.subject,
      sendgridMessage.to[0], // needs major refactoring to send to each 'to'
      new helper.Content("text/plain", options.text || '')
    );
    if (options.html) {
      sendgridEmail.addContent(new helper.Content("text/html", options.html));
    }

    if (R.is(Array, sendgridEmail.personalizations) && sendgridEmail.personalizations.length > 0) {
      if (options.cc) {
        sendgridEmail.personalizations[0].addCc(options.cc);
      }
      if (options.bcc) {
        sendgridEmail.personalizations[0].addBcc(options.bcc);
      }
    }

    //attachments
    if (R.is(Array, options.attachments)) {
      R.forEach(function eachFile(fileConfig){
        sendgridEmail.addAttachment(fileConfig);
      }, options.attachments);
    }
    if (options.files) {
      R.forEach(function eachFile(fileConfig){
        sendgridEmail.addAttachment(fileConfig);
      }, options.files);
    }

    //sendgrid mail settings
    if (sendgridMailSettings) {
      sendgridEmail.addMailSettings(sendgridMailSettings);
    }
    //trackingSettings
    if (sendgridTrackingSettings) {
      sendgridEmail.addTrackingSettings(sendgridTrackingSettings);
    }
    //Sendgrid options
    if (options.sendGridConfig) {
      //personalizations
      if (R.is(Array, options.sendGridConfig.personalizations)) {
        sendgridEmail.personalizations = options.sendGridConfig.personalizations;
      }
      //sections
      if (R.is(Array, options.sendGridConfig.sections)) {
        R.forEach(function eachSec(section){
          sendgridEmail.addSection(section);
        }, options.sendGridConfig.sections);
      }
      //headers
      if (R.is(Array, options.sendGridConfig.headers)) {
        R.forEach(function eachHeader(header){
          sendgridEmail.addHeader(header);
        }, options.sendGridConfig.headers);
      }
      //templateId
      if (options.sendGridConfig.templateId) {
        sendgridEmail.setTemplateId(options.sendGridConfig.templateId);
      }
      //sendAt
      if (options.sendGridConfig.sendAt) {
        sendgridEmail.setSendAt(options.sendGridConfig.sendAt);
      }
      //batchId
      if (options.sendGridConfig.batchId) {
        sendgridEmail.setBatchId(options.sendGridConfig.batchId);
      }
      //asm
      if (options.sendGridConfig.asm) {
        sendgridEmail.setAsm(options.sendGridConfig.asm);
      }
      //ipPoolName
      if (options.sendGridConfig.ipPoolName) {
        sendgridEmail.setIpPoolName(options.sendGridConfig.ipPoolName);
      }
      //replyTo
      if (options.sendGridConfig.replyTo) {
        sendgridEmail.setReplyTo(options.sendGridConfig.replyTo);
      }
      //categories
      if (R.is(Array, options.sendGridConfig.categories)) {
        R.forEach(function eachCategory(category){
          sendgridEmail.addCategory(category);
        }, options.sendGridConfig.categories);
      }
      //unique_args
      if (R.is(Array, options.sendGridConfig.unique_args)) {
        R.forEach(function eachUniqueArg(arg){
          sendgridEmail.addCustomArg(arg);
        }, options.sendGridConfig.unique_args);
      }

    }
    request = connector.sendgrid.emptyRequest();
    request.method = 'POST';
    request.path = '/v3/mail/send';
    request.body = sendgridEmail.toJSON();
    connector.sendgrid.API(request, function sgCb(response){ // eslint-disable-line
      var code = response && response.statusCode;
      if (code && (code.toString() === "200" || code.toString() === "202")) {
        fn(null, response);
      } else {
        fn(response);
      }
    });
  } else {
    process.nextTick(function nextTick() { // eslint-disable-line
      fn(null, options);
    });
  }
  return deferred.promise;
};

/**
 * Send an email instance using instance
 */

Mailer.prototype.send = function protoSend(fn) {
  return this.constructor.send(this, fn);
};

/**
 * Export the connector class
 */
module.exports = SendgridConnector;
<|endoftext|>


<commit_before>/**
 * @file DropdownCalculation vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import Dom from './Dom';

function isAbove(dropdownEl, triggerEl, popupEl) {

    if (!dropdownEl || !triggerEl || !popupEl) {
        return false;
    }

    const {top} = Dom.getOffset(dropdownEl),
        scrollTop = Dom.getScrollTop();

    if (top + triggerEl.clientHeight + popupEl.clientHeight - scrollTop > window.innerHeight) {
        return true;
    }

    return false;

}

export default {
    isAbove
};<commit_msg>optimize DropdownCalculation<commit_after>/**
 * @file DropdownCalculation vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import Dom from './Dom';

function isAbove(dropdownEl, triggerEl, popupEl) {

    if (!dropdownEl || !triggerEl || !popupEl) {
        return false;
    }

    const {top} = Dom.getOffset(dropdownEl),
        scrollTop = Dom.getScrollTop();

    if (
        top + triggerEl.clientHeight + popupEl.clientHeight - scrollTop > window.innerHeight // bottom overflow
        &&
        top - popupEl.clientHeight - scrollTop >= 0 // no top overflow
    ) {
        return true;
    }

    return false;

}

export default {
    isAbove
};<|endoftext|>


<commit_before>/* jshint node: true */

'use strict';

var http = require('http');
var https = require('https');
var packageInfo = require('../package.json');
var url = require('url');

module.exports = GitHubClient;

function GitHubClient(config) {
  this.user = config.githubUser;
  this.timeout = config.githubTimeout;
  this.baseurl = url.parse(config.githubApiBaseUrl ||
    GitHubClient.API_BASE_URL);
}

GitHubClient.API_BASE_URL = 'https://api.github.com/';

GitHubClient.prototype.fileNewIssue = function(metadata, repository) {
  return makeApiCall(this, metadata, repository);
};

function getHttpOptions(client, repository, paramsStr) {
  var baseurl = client.baseurl;
  return {
    protocol: baseurl.protocol,
    host: baseurl.hostname,
    port: baseurl.port,
    path: baseurl.pathname + 'repos/' + client.user + '/' + repository +
      '/issues',
    method: 'POST',
    headers: {
      'Accept': 'application/vnd.github.v3+json',
      'Authorization': 'token ' + process.env.HUBOT_GITHUB_TOKEN,
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(paramsStr, 'utf8'),
      'User-Agent': packageInfo.name + '/' + packageInfo.version
    }
  };
}

function makeApiCall(client, metadata, repository) {
  var requestFactory = (client.protocol === 'https:') ? https : http,
      paramsStr = JSON.stringify({
        title: metadata.title,
        body: metadata.url
      });
  return new Promise(function(resolve, reject) {
    var httpOptions = getHttpOptions(client, repository, paramsStr),
        req = requestFactory.request(httpOptions, function(res) {
          handleResponse(res, resolve, reject);
        });

    req.setTimeout(client.timeout);
    req.on('error', function(err) {
      reject(new Error('failed to make GitHub API request: ' + err.message));
    });
    req.end(paramsStr);
  });
}

function handleResponse(res, resolve, reject) {
  var result = '';

  res.setEncoding('utf8');
  res.on('data', function(chunk) {
    result = result + chunk;
  });
  res.on('end', function() {
    if (res.statusCode >= 200 && res.statusCode < 300) {
      try {
        resolve(JSON.parse(result).html_url);  // jshint ignore:line
      } catch (err) {
        reject(new Error('could not parse JSON response from GitHub API: ' +
          result));
      }
    } else {
      reject(new Error('received ' + res.statusCode +
        ' response from GitHub API: ' + result));
    }
  });
}
<commit_msg>Check for client.baseurl.protocol in GitHubClient<commit_after>/* jshint node: true */

'use strict';

var http = require('http');
var https = require('https');
var packageInfo = require('../package.json');
var url = require('url');

module.exports = GitHubClient;

function GitHubClient(config) {
  this.user = config.githubUser;
  this.timeout = config.githubTimeout;
  this.baseurl = url.parse(config.githubApiBaseUrl ||
    GitHubClient.API_BASE_URL);
}

GitHubClient.API_BASE_URL = 'https://api.github.com/';

GitHubClient.prototype.fileNewIssue = function(metadata, repository) {
  return makeApiCall(this, metadata, repository);
};

function getHttpOptions(client, repository, paramsStr) {
  var baseurl = client.baseurl;
  return {
    protocol: baseurl.protocol,
    host: baseurl.hostname,
    port: baseurl.port,
    path: baseurl.pathname + 'repos/' + client.user + '/' + repository +
      '/issues',
    method: 'POST',
    headers: {
      'Accept': 'application/vnd.github.v3+json',
      'Authorization': 'token ' + process.env.HUBOT_GITHUB_TOKEN,
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(paramsStr, 'utf8'),
      'User-Agent': packageInfo.name + '/' + packageInfo.version
    }
  };
}

function makeApiCall(client, metadata, repository) {
  var requestFactory = (client.baseurl.protocol === 'https:') ? https : http,
      paramsStr = JSON.stringify({
        title: metadata.title,
        body: metadata.url
      });
  return new Promise(function(resolve, reject) {
    var httpOptions = getHttpOptions(client, repository, paramsStr),
        req = requestFactory.request(httpOptions, function(res) {
          handleResponse(res, resolve, reject);
        });

    req.setTimeout(client.timeout);
    req.on('error', function(err) {
      reject(new Error('failed to make GitHub API request: ' + err.message));
    });
    req.end(paramsStr);
  });
}

function handleResponse(res, resolve, reject) {
  var result = '';

  res.setEncoding('utf8');
  res.on('data', function(chunk) {
    result = result + chunk;
  });
  res.on('end', function() {
    if (res.statusCode >= 200 && res.statusCode < 300) {
      try {
        resolve(JSON.parse(result).html_url);  // jshint ignore:line
      } catch (err) {
        reject(new Error('could not parse JSON response from GitHub API: ' +
          result));
      }
    } else {
      reject(new Error('received ' + res.statusCode +
        ' response from GitHub API: ' + result));
    }
  });
}
<|endoftext|>


<commit_before>/**
 * Wikipedia Read Mode, inject.js
 * This script will be injected to the wikipedia page
 * And it will function as a wiki script rather than a content
 * Script
 */

//==================================================================
// loading properties from localStorage
//==================================================================

var ewprops = {
	enabled: true,
	font: "Calibri",
	size: 30
}

// For ajax search capability
var xhr = new XMLHttpRequest();
var xhr_timeout;
// timeout for searching, and showing slow search message
var timeout_freq = 3000;

if (localStorage['ew_enabled']
	&& localStorage['ew_enabled'] == "false") {
	ewprops.enabled = false;
}
if (localStorage['ew_font']) {
	ewprops.font = localStorage['ew_font'];
}
if (localStorage['ew_size']) {
	ewprops.size = localStorage['ew_size'];
}

//==================================================================
// Methods that will be called by eventlisteners
//==================================================================

//function to refref the properties
function refreshProperty() {
	if (localStorage['ew_font']) {
		ewprops.font = localStorage['ew_font'];
	}
	if (localStorage['ew_size']) {
		ewprops.size = localStorage['ew_size'];
	}
}

//add a function to reset the modifications
function setModification() {
	//refresh property first
	refreshProperty();

	try {
		document.getElementsByClassName('infobox')[0].style.display = 'none';
	} catch (err) { /** do we need to do something about this error? **/ }

	try {
		document.getElementById('mw-panel').style.display = 'none';
	} catch (err) { /** do we need to do something about this error? **/ }

	document.getElementById('content').style.marginLeft = '50px';
	document.getElementById('content').style.marginRight = '50px';
	document.getElementById('content').style.fontSize = ewprops.size +'px';
	document.getElementById('content').style.fontFamily = ewprops.font;
}

//reset modifications done by the set function
function resetModification() {
	try {
		document.getElementsByClassName('infobox')[0].style.display = '';
	} catch (err) {
		/** do we need to do something about this error? **/
	}

	try {
		document.getElementById('mw-panel').style.display = '';
	} catch (err) { /** do we need to do something about this error? **/ }

	document.getElementById('content').style.marginLeft = '12em';
	document.getElementById('content').style.marginRight = '';
	document.getElementById('content').style.fontSize = '';
	document.getElementById('content').style.fontFamily = '';
}

//called by checkbox, for readmode in UI
function checkChange() {
	if (document.getElementById('ewcheckbox').checked) setModification();
	else resetModification();
}

/**
 * Code implement, scroll up/down of the EW menu, with document scroll
 */
var y1 = -50;var y2 = 2;
var currentPos = document.body.scrollTop;
function monitor(event){
	var dy = (currentPos - document.body.scrollTop) / 2;
	if (document.getElementById('easywiki').style.top == '')
		document.getElementById('easywiki').style.top = '2px';
	var y = parseInt(document.getElementById('easywiki').style.top) + dy;
	if (y > y2) y = y2;
	if (y < y1) y = y1;
	currentPos = document.body.scrollTop;
	document.getElementById('easywiki').style.top = y + 'px';

	//hide menu
	//hideSubMenu();
	hideContentMenu();
}
document.body.onscroll = monitor;

/**
 * Adding search capability to easywiki
 */
function searchew(event) {
	var source = document.getElementById('ewsearch');

	if (event.which == 13 && source.value.length) {
		window.location.href = "http://en.wikipedia.org/wiki/w/index.php?search=" +source.value;
	} else if (source.value.length){
		clearTimeout(xhr_timeout);

        var url = "http://en.wikipedia.org/wiki/w/index.php?search=" +source.value;
        // Abort existing operation
        xhr.abort();
        xhr.open('GET', url);
        //Add a property to xhr element
        xhr.url = url;
        xhr.searchKey = source.value;
        xhr.send();

        // call the timeout for slow search
        var lasturl = url;
        xhr_timeout = setTimeout(function() {showtimeoutmessageXHR(lasturl);}, timeout_freq);

        document.getElementById('searchsuggestions').style.display = 'block';
        document.getElementsByClassName('ew_search_title')[0].innerHTML = 'Searching: ' +source.value;
    }
}

// Code to initiate the search suggestion feature
xhr.onreadystatechange = XHRReadyStateChangeFunction;
function XHRReadyStateChangeFunction() {
    document.getElementsByClassName('ew_search_title')[0].innerHTML = 'Result: ' +this.searchKey;
    if (this.readyState == 4 && this.status == 200) {
    	clearTimeout(xhr_timeout);

        if (this.url == this.responseURL) {
            var c = document.createElement('html');
            c.innerHTML = this.responseText;
            var dom = c.getElementsByClassName('mw-search-results');
            document.getElementsByClassName('ew_ss_results')[0].innerHTML = dom[0].outerHTML;
            document.getElementById('searchsuggestions').style.display = 'block';
        } else {
            document.getElementsByClassName('ew_ss_results')[0].innerHTML = '

Direct match found for: ' +this.searchKey
                +', press on search button or click here to go to this content!

';
            document.getElementById('searchsuggestions').style.display = 'block';
        }
    }
}

function showtimeoutmessageXHR(url) {
	document.getElementsByClassName('ew_ss_results')[0].innerHTML = '
Search is taking little longer than usual, click here to go directly!
';
}

// Attach click listener to close button on search suggestion
document.getElementsByClassName('ew_ss_close')[0].onclick = function() {
    document.getElementById('searchsuggestions').style.display = 'none';
}

// Attach listener to enter button press on search input
document.getElementById('ewsearch').onkeyup = searchew;

// Trigger search when user clicks on search button
document.getElementById('ewsearch').onclick = function(event) {
    var parent_left = this.parentNode.offsetLeft;
    var parent_top = this.parentNode.offsetTop;
    if (event.x >= (parent_left + this.offsetLeft +this.offsetWidth - 10)
        && event.x <= (parent_left + this.offsetLeft +this.offsetWidth)) {
        if (event.y >= (parent_top + this.offsetTop)
        && event.y <= (parent_left + this.offsetLeft +this.offsetHeight)) {
            searchew({which: 13});
        }
    }
};

function hideContentMenu() {
	var target = document.getElementById('toc_');
	if (target != null) {
		target.style.display = 'none';
		document.getElementsByClassName('ewcmenu')[0].setAttribute('state', 'inactive');
	}
}

// Code to enable Move to top functionality
document.getElementsByClassName('movetotop')[0].addEventListener('click', function() {
	document.body.scrollTop = 0;
});

function addEWContextMenu() {
	var content = document.getElementById('toc');
	if (typeof content != undefined
		&& content != null) {
		var contentObj = document.createElement('div');
		contentObj.className = "toc";
		contentObj.setAttribute("id", "toc_");
		contentObj.innerHTML = content.innerHTML;

		// Remove the content menu from actual UI
		content.parentNode.removeChild(content);

		// Modify the new content menu to suit our need
		// Insert to DOM
		document.body.appendChild(contentObj);

		// now change the position according to the position of
		// main easywiki box
		contentObj.style.left = document.getElementById('easywiki').offsetLeft +'px';
        contentObj.style.top = document.getElementById('easywiki').offsetHeight
                        + document.getElementById('easywiki').offsetTop +'px';

		//Now change directly
		var contentObj = document.getElementById('toc_');

		// Remove title
		var temp = document.getElementById('toctitle');
		temp.parentNode.removeChild(temp);

		// Add listeners to internal links
	    var links = document.getElementById('toc_').getElementsByTagName('a');
	    var i;
	    for (i = 0; i < links.length; i++) {
	        if (links[i].href.indexOf('#') !== -1) {
	            links[i].addEventListener('click', function() {
	                setTimeout(function() {
	                    document.getElementById('easywiki').style.top =  '2px';
	                }, 300);
	            });
	        }
	    }
		return true;
	}
	return false;
}

var isMenuClickEventAttached = false;
function addEventListenertoMenuImg() {
	// Add listener to cmenu img
    document.getElementsByClassName('ewcmenu')[0].addEventListener('click', function() {
        var source = document.getElementsByClassName('ewcmenu')[0];
        var target = document.getElementById('toc_');
        if (typeof target != undefined
            && target != null) {
            var state = source.getAttribute('state');
            if (typeof state == undefined)
                state = 'inactive';
            if (state == 'inactive') {
                // Need to show
                target.style.display = 'block';
                source.setAttribute('state', 'active');
            } else {
                target.style.display = 'none';
                source.setAttribute('state', 'inactive');
            }
            //hideSubMenu();
        } else {
        	addEWContextMenu();
        }
    });
    isMenuClickEventAttached = true;
}

//==================================================================
// Get Content section from the wiki and add it as a context menu
//==================================================================
window.onload = function() {
	addEWContextMenu();
    addEventListenertoMenuImg();

    // adding event listener to shift + R button to minimize/maximize the read mode
    document.onkeypress = function(e) {
        if (e.shiftKey && e.which == 82) {
            document.getElementById('ewcheckbox').click();
        }
    };

};

setTimeout(function() {
	addEWContextMenu();
	// ^ reattempt incase previos attempt failed
	// after 3 seconds
	// adding event listener to shift + R button to minimize/maximize the read mode
    document.onkeypress = function(e) {
        if (e.shiftKey && e.which == 82) {
            document.getElementById('ewcheckbox').click();
        }
    };
	if (!isMenuClickEventAttached) addEventListenertoMenuImg();
}, 1000);

<commit_msg>Menu position auto adjust on window resize<commit_after>/**
 * Wikipedia Read Mode, inject.js
 * This script will be injected to the wikipedia page
 * And it will function as a wiki script rather than a content
 * Script
 */

//==================================================================
// loading properties from localStorage
//==================================================================

var ewprops = {
	enabled: true,
	font: "Calibri",
	size: 30
}

// For ajax search capability
var xhr = new XMLHttpRequest();
var xhr_timeout;
// timeout for searching, and showing slow search message
var timeout_freq = 3000;

if (localStorage['ew_enabled']
	&& localStorage['ew_enabled'] == "false") {
	ewprops.enabled = false;
}
if (localStorage['ew_font']) {
	ewprops.font = localStorage['ew_font'];
}
if (localStorage['ew_size']) {
	ewprops.size = localStorage['ew_size'];
}

//==================================================================
// Methods that will be called by eventlisteners
//==================================================================

//function to refref the properties
function refreshProperty() {
	if (localStorage['ew_font']) {
		ewprops.font = localStorage['ew_font'];
	}
	if (localStorage['ew_size']) {
		ewprops.size = localStorage['ew_size'];
	}
}

//add a function to reset the modifications
function setModification() {
	//refresh property first
	refreshProperty();

	try {
		document.getElementsByClassName('infobox')[0].style.display = 'none';
	} catch (err) { /** do we need to do something about this error? **/ }

	try {
		document.getElementById('mw-panel').style.display = 'none';
	} catch (err) { /** do we need to do something about this error? **/ }

	document.getElementById('content').style.marginLeft = '50px';
	document.getElementById('content').style.marginRight = '50px';
	document.getElementById('content').style.fontSize = ewprops.size +'px';
	document.getElementById('content').style.fontFamily = ewprops.font;
}

//reset modifications done by the set function
function resetModification() {
	try {
		document.getElementsByClassName('infobox')[0].style.display = '';
	} catch (err) {
		/** do we need to do something about this error? **/
	}

	try {
		document.getElementById('mw-panel').style.display = '';
	} catch (err) { /** do we need to do something about this error? **/ }

	document.getElementById('content').style.marginLeft = '12em';
	document.getElementById('content').style.marginRight = '';
	document.getElementById('content').style.fontSize = '';
	document.getElementById('content').style.fontFamily = '';
}

//called by checkbox, for readmode in UI
function checkChange() {
	if (document.getElementById('ewcheckbox').checked) setModification();
	else resetModification();
}

/**
 * Code implement, scroll up/down of the EW menu, with document scroll
 */
var y1 = -50;var y2 = 2;
var currentPos = document.body.scrollTop;
function monitor(event){
	var dy = (currentPos - document.body.scrollTop) / 2;
	if (document.getElementById('easywiki').style.top == '')
		document.getElementById('easywiki').style.top = '2px';
	var y = parseInt(document.getElementById('easywiki').style.top) + dy;
	if (y > y2) y = y2;
	if (y < y1) y = y1;
	currentPos = document.body.scrollTop;
	document.getElementById('easywiki').style.top = y + 'px';

	//hide menu
	//hideSubMenu();
	hideContentMenu();
}
document.body.onscroll = monitor;

/**
 * Adding search capability to easywiki
 */
function searchew(event) {
	var source = document.getElementById('ewsearch');

	if (event.which == 13 && source.value.length) {
		window.location.href = "http://en.wikipedia.org/wiki/w/index.php?search=" +source.value;
	} else if (source.value.length){
		clearTimeout(xhr_timeout);

        var url = "http://en.wikipedia.org/wiki/w/index.php?search=" +source.value;
        // Abort existing operation
        xhr.abort();
        xhr.open('GET', url);
        //Add a property to xhr element
        xhr.url = url;
        xhr.searchKey = source.value;
        xhr.send();

        // call the timeout for slow search
        var lasturl = url;
        xhr_timeout = setTimeout(function() {showtimeoutmessageXHR(lasturl);}, timeout_freq);

        document.getElementById('searchsuggestions').style.display = 'block';
        document.getElementsByClassName('ew_search_title')[0].innerHTML = 'Searching: ' +source.value;
    }
}

// Code to initiate the search suggestion feature
xhr.onreadystatechange = XHRReadyStateChangeFunction;
function XHRReadyStateChangeFunction() {
    document.getElementsByClassName('ew_search_title')[0].innerHTML = 'Result: ' +this.searchKey;
    if (this.readyState == 4 && this.status == 200) {
    	clearTimeout(xhr_timeout);

        if (this.url == this.responseURL) {
            var c = document.createElement('html');
            c.innerHTML = this.responseText;
            var dom = c.getElementsByClassName('mw-search-results');
            document.getElementsByClassName('ew_ss_results')[0].innerHTML = dom[0].outerHTML;
            document.getElementById('searchsuggestions').style.display = 'block';
        } else {
            document.getElementsByClassName('ew_ss_results')[0].innerHTML = '

Direct match found for: ' +this.searchKey
                +', press on search button or click here to go to this content!

';
            document.getElementById('searchsuggestions').style.display = 'block';
        }
    }
}

function showtimeoutmessageXHR(url) {
	document.getElementsByClassName('ew_ss_results')[0].innerHTML = '
Search is taking little longer than usual, click here to go directly!
';
}

// Attach click listener to close button on search suggestion
document.getElementsByClassName('ew_ss_close')[0].onclick = function() {
    document.getElementById('searchsuggestions').style.display = 'none';
}

// Attach listener to enter button press on search input
document.getElementById('ewsearch').onkeyup = searchew;

// Trigger search when user clicks on search button
document.getElementById('ewsearch').onclick = function(event) {
    var parent_left = this.parentNode.offsetLeft;
    var parent_top = this.parentNode.offsetTop;
    if (event.x >= (parent_left + this.offsetLeft +this.offsetWidth - 10)
        && event.x <= (parent_left + this.offsetLeft +this.offsetWidth)) {
        if (event.y >= (parent_top + this.offsetTop)
        && event.y <= (parent_left + this.offsetLeft +this.offsetHeight)) {
            searchew({which: 13});
        }
    }
};

function hideContentMenu() {
	var target = document.getElementById('toc_');
	if (target != null) {
		target.style.display = 'none';
		document.getElementsByClassName('ewcmenu')[0].setAttribute('state', 'inactive');
	}
}

// Code to enable Move to top functionality
document.getElementsByClassName('movetotop')[0].addEventListener('click', function() {
	document.body.scrollTop = 0;
});

function addEWContextMenu() {
	var content = document.getElementById('toc');
	if (typeof content != undefined
		&& content != null) {
		var contentObj = document.createElement('div');
		contentObj.className = "toc";
		contentObj.setAttribute("id", "toc_");
		contentObj.innerHTML = content.innerHTML;

		// Remove the content menu from actual UI
		content.parentNode.removeChild(content);

		// Modify the new content menu to suit our need
		// Insert to DOM
		document.body.appendChild(contentObj);

		// now change the position according to the position of
		// main easywiki box
		contentObj.style.left = document.getElementById('easywiki').offsetLeft +'px';
        contentObj.style.top = document.getElementById('easywiki').offsetHeight
                        + document.getElementById('easywiki').offsetTop +'px';

		//Now change directly
		var contentObj = document.getElementById('toc_');

		// Remove title
		var temp = document.getElementById('toctitle');
		temp.parentNode.removeChild(temp);

		// Add listeners to internal links
	    var links = document.getElementById('toc_').getElementsByTagName('a');
	    var i;
	    for (i = 0; i < links.length; i++) {
	        if (links[i].href.indexOf('#') !== -1) {
	            links[i].addEventListener('click', function() {
	                setTimeout(function() {
	                    document.getElementById('easywiki').style.top =  '2px';
	                }, 300);
	            });
	        }
	    }
		return true;
	}
	return false;
}

var isMenuClickEventAttached = false;
function addEventListenertoMenuImg() {
	// Add listener to cmenu img
    document.getElementsByClassName('ewcmenu')[0].addEventListener('click', function() {
        var source = document.getElementsByClassName('ewcmenu')[0];
        var target = document.getElementById('toc_');
        if (typeof target != undefined
            && target != null) {
            var state = source.getAttribute('state');
            if (typeof state == undefined)
                state = 'inactive';
            if (state == 'inactive') {
                // Need to show
                target.style.display = 'block';
                source.setAttribute('state', 'active');
            } else {
                target.style.display = 'none';
                source.setAttribute('state', 'inactive');
            }
            //hideSubMenu();
        } else {
        	addEWContextMenu();
        }
    });
    isMenuClickEventAttached = true;
}

//==================================================================
// Get Content section from the wiki and add it as a context menu
//==================================================================
window.onload = function() {
	addEWContextMenu();
    addEventListenertoMenuImg();

    // adding event listener to shift + R button to minimize/maximize the read mode
    document.onkeypress = function(e) {
        if (e.shiftKey && e.which == 82) {
            document.getElementById('ewcheckbox').click();
        }
    };

};

setTimeout(function() {
	addEWContextMenu();
	// ^ reattempt incase previos attempt failed
	// after 3 seconds
	// adding event listener to shift + R button to minimize/maximize the read mode
    document.onkeypress = function(e) {
        if (e.shiftKey && e.which == 82) {
            document.getElementById('ewcheckbox').click();
        }
    };
	if (!isMenuClickEventAttached) addEventListenertoMenuImg();
}, 1000);

// Adjust menu position on window resize
window.onresize = function() {
	document.getElementById('toc_').style.left = document.getElementById('easywiki').offsetLeft +'px';
};

<|endoftext|>


<commit_before>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = function() {
  return Promise.all([
    getChannelURL('release'),
    getChannelURL('beta'),
    getChannelURL('canary')
  ]).then((urls) => {
    return {
      useYarn: true,
      scenarios: [
        {
          name: 'ember-lts-2.18',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1',
              'ember-source': '~2.18.0'
            }
          }
        },
        {
          name: 'ember-lts-3.4',
          npm: {
            devDependencies: {
              'ember-source': '~3.4.0'
            }
          }
        },
        {
          name: 'ember-release',
          npm: {
            devDependencies: {
              'ember-source': urls[0]
            }
          },
          env: {
            FAIL_ON_DEPRECATION: true
          }
        },
        {
          name: 'ember-beta',
          npm: {
            devDependencies: {
              'ember-source': urls[1]
            }
          },
          env: {
            FAIL_ON_DEPRECATION: true
          }
        },
        {
          name: 'ember-canary',
          npm: {
            devDependencies: {
              'ember-source': urls[2]
            }
          },
          env: {
            FAIL_ON_DEPRECATION: true
          }
        },
        // The default `.travis.yml` runs this scenario via `npm test`,
        // not via `ember try`. It's still included here so that running
        // `ember try:each` manually or from a customized CI config will run it
        // along with all the other scenarios.
        {
          name: 'ember-default',
          npm: {
            devDependencies: {}
          }
        },
        {
          name: 'ember-default-with-jquery',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({
              'jquery-integration': true
            })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1'
            }
          }
        },
        {
          name: 'fastboot-addon-tests',
          command: 'DEBUG=ember-cli-addon-tests ember fastboot:test --ember-data-version=3.10.0',
          npm: {
            devDependencies: {}
          }
        },
        {
          name: 'node-tests',
          command: 'yarn run nodetest'
        }
      ]
    };
  });
};
<commit_msg>Don't fail on deprecations for Ember Beta<commit_after>'use strict';

const getChannelURL = require('ember-source-channel-url');

module.exports = function() {
  return Promise.all([
    getChannelURL('release'),
    getChannelURL('beta'),
    getChannelURL('canary')
  ]).then((urls) => {
    return {
      useYarn: true,
      scenarios: [
        {
          name: 'ember-lts-2.18',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1',
              'ember-source': '~2.18.0'
            }
          }
        },
        {
          name: 'ember-lts-3.4',
          npm: {
            devDependencies: {
              'ember-source': '~3.4.0'
            }
          }
        },
        {
          name: 'ember-release',
          npm: {
            devDependencies: {
              'ember-source': urls[0]
            }
          },
          env: {
            FAIL_ON_DEPRECATION: true
          }
        },
        {
          name: 'ember-beta',
          npm: {
            devDependencies: {
              'ember-source': urls[1]
            }
          }
        },
        {
          name: 'ember-canary',
          npm: {
            devDependencies: {
              'ember-source': urls[2]
            }
          },
          env: {
            FAIL_ON_DEPRECATION: true
          }
        },
        // The default `.travis.yml` runs this scenario via `npm test`,
        // not via `ember try`. It's still included here so that running
        // `ember try:each` manually or from a customized CI config will run it
        // along with all the other scenarios.
        {
          name: 'ember-default',
          npm: {
            devDependencies: {}
          }
        },
        {
          name: 'ember-default-with-jquery',
          env: {
            EMBER_OPTIONAL_FEATURES: JSON.stringify({
              'jquery-integration': true
            })
          },
          npm: {
            devDependencies: {
              '@ember/jquery': '^0.5.1'
            }
          }
        },
        {
          name: 'fastboot-addon-tests',
          command: 'DEBUG=ember-cli-addon-tests ember fastboot:test --ember-data-version=3.10.0',
          npm: {
            devDependencies: {}
          }
        },
        {
          name: 'node-tests',
          command: 'yarn run nodetest'
        }
      ]
    };
  });
};
<|endoftext|>


<commit_before>import _ from 'lodash'
import PropTypes from 'prop-types'
import React, { Component } from 'react'

import {
  customPropTypes,
  eventStack,
  getElementType,
  getUnhandledProps,
  META,
} from '../../lib'

/**
 * Responsive can control visibility of content.
 */
export default class Responsive extends Component {
  static propTypes = {
    /** An element type to render as (string or function). */
    as: customPropTypes.as,

    /** Primary content. */
    children: PropTypes.node,

    /** The maximum width at which content will be displayed. */
    maxWidth: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.string,
    ]),

    /** The minimum width at which content will be displayed. */
    minWidth: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.string,
    ]),

    /**
     * Called on update.
     *
     * @param {SyntheticEvent} event - The React SyntheticEvent object
     * @param {object} data - All props and the event value.
     */
    onUpdate: PropTypes.func,
  }

  static _meta = {
    name: 'Responsive',
    type: META.TYPES.ADDON,
  }

  static onlyMobile = { minWidth: 320, maxWidth: 767 }
  static onlyTablet = { minWidth: 768, maxWidth: 991 }
  static onlyComputer = { minWidth: 992 }
  static onlyLargeScreen = { minWidth: 1200, maxWidth: 1919 }
  static onlyWidescreen = { minWidth: 1920 }

  constructor(...args) {
    super(...args)
    this.state = { width: window.innerWidth }
  }

  componentDidMount() {
    eventStack.sub('resize', this.handleResize, { target: 'window' })
  }

  componentWillUnmount() {
    eventStack.unsub('resize', this.handleResize, { target: 'window' })
  }

  // ----------------------------------------
  // Helpers
  // ----------------------------------------

  fitsMaxWidth = () => {
    const { maxWidth } = this.props
    const { width } = this.state

    return _.isNil(maxWidth) ? true : width <= maxWidth
  }

  fitsMinWidth = () => {
    const { minWidth } = this.props
    const { width } = this.state

    return _.isNil(minWidth) ? true : width >= minWidth
  }

  isVisible = () => this.fitsMinWidth() && this.fitsMaxWidth()

  // ----------------------------------------
  // Event handlers
  // ----------------------------------------

  handleResize = (e) => {
    if (this.ticking) return

    this.ticking = true
    requestAnimationFrame(() => this.handleUpdate(e))
  }

  handleUpdate = (e) => {
    this.ticking = false
    const width = window.innerWidth

    this.setState({ width })
    _.invoke(this.props, 'onUpdate', e, { ...this.props, width })
  }

  // ----------------------------------------
  // Render
  // ----------------------------------------

  render() {
    const { children } = this.props

    const ElementType = getElementType(Responsive, this.props)
    const rest = getUnhandledProps(Responsive, this.props)

    if (this.isVisible()) return <ElementType {...rest}>{children}</ElementType>
    return null
  }
}
<commit_msg>fix(Responsive): fix usage of `window` for SSR (#2209)<commit_after>import _ from 'lodash'
import PropTypes from 'prop-types'
import React, { Component } from 'react'

import {
  customPropTypes,
  eventStack,
  getElementType,
  getUnhandledProps,
  isBrowser,
  META,
} from '../../lib'

/**
 * Responsive can control visibility of content.
 */
export default class Responsive extends Component {
  static propTypes = {
    /** An element type to render as (string or function). */
    as: customPropTypes.as,

    /** Primary content. */
    children: PropTypes.node,

    /** The maximum width at which content will be displayed. */
    maxWidth: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.string,
    ]),

    /** The minimum width at which content will be displayed. */
    minWidth: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.string,
    ]),

    /**
     * Called on update.
     *
     * @param {SyntheticEvent} event - The React SyntheticEvent object
     * @param {object} data - All props and the event value.
     */
    onUpdate: PropTypes.func,
  }

  static _meta = {
    name: 'Responsive',
    type: META.TYPES.ADDON,
  }

  static onlyMobile = { minWidth: 320, maxWidth: 767 }
  static onlyTablet = { minWidth: 768, maxWidth: 991 }
  static onlyComputer = { minWidth: 992 }
  static onlyLargeScreen = { minWidth: 1200, maxWidth: 1919 }
  static onlyWidescreen = { minWidth: 1920 }

  constructor(...args) {
    super(...args)

    this.state = { width: isBrowser ? window.innerWidth : 0 }
  }

  componentDidMount() {
    eventStack.sub('resize', this.handleResize, { target: 'window' })
  }

  componentWillUnmount() {
    eventStack.unsub('resize', this.handleResize, { target: 'window' })
  }

  // ----------------------------------------
  // Helpers
  // ----------------------------------------

  fitsMaxWidth = () => {
    const { maxWidth } = this.props
    const { width } = this.state

    return _.isNil(maxWidth) ? true : width <= maxWidth
  }

  fitsMinWidth = () => {
    const { minWidth } = this.props
    const { width } = this.state

    return _.isNil(minWidth) ? true : width >= minWidth
  }

  isVisible = () => this.fitsMinWidth() && this.fitsMaxWidth()

  // ----------------------------------------
  // Event handlers
  // ----------------------------------------

  handleResize = (e) => {
    if (this.ticking) return

    this.ticking = true
    requestAnimationFrame(() => this.handleUpdate(e))
  }

  handleUpdate = (e) => {
    this.ticking = false
    const width = window.innerWidth

    this.setState({ width })
    _.invoke(this.props, 'onUpdate', e, { ...this.props, width })
  }

  // ----------------------------------------
  // Render
  // ----------------------------------------

  render() {
    const { children } = this.props

    const ElementType = getElementType(Responsive, this.props)
    const rest = getUnhandledProps(Responsive, this.props)

    if (this.isVisible()) return <ElementType {...rest}>{children}</ElementType>
    return null
  }
}
<|endoftext|>


<commit_before>/*
 Copyright 2012 - $Date $ by PeopleWare n.v.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

define(["dojo/_base/declare", "dojo/Stateful", "ppwcode-util-contracts/_Mixin", "ppwcode-util-oddsAndEnds/bindingChains", "module"],
    function(declare, Stateful, _ContractMixin, bindingChains, module) {

      // lang.getObject and lang.setObject don't use get and set
      function getProp(context, /*String[]*/ nameChain) {
        // nameChain:
        //   Note that this array will be changed by this method. On return, it will be empty.

        if (!nameChain || nameChain.length <= 0) {
          return context;
        }
        // there is more chain
        if (!context) {
          return undefined;
        }
        var first = nameChain.shift();
        var nextContext = context.get ? context.get(first) : context[first];
        return getProp(nextContext, nameChain);
      }

      var _DelegationMixing = declare([Stateful, _ContractMixin], {
        // summary:
        //   Mixin that adds the ability to create virtual properties that delegate to other properties.
        //   Only makes sense with Stateful.

        // TODO created to save code that is ultimately not used; untested; DO NOT USE IN THIS STATE!

        _c_invar: [
        ],

        postscript: function() {
          var self = this;

          function dependentPropertyChanged(dependentPropertyName) {
            return function(propName, previousValue, newValue) {
              if(self._watchCallbacks && newValue !== previousValue) {
                // fake the events
                self._watchCallbacks(dependentPropertyName, previousValue, newValue);
              }
            };
          }

          self.inherited(arguments);
          for (var delegatedPropertyName in self["-delegated-"]) {
            //noinspection JSUnfilteredForInLoop
            self["-delegated-"][delegatedPropertyName].fullNameChain = self["-delegated-"][delegatedPropertyName].path.split(".");
            //noinspection JSUnfilteredForInLoop
            self["-delegated-"][delegatedPropertyName].lastContextNameChain = self["-delegated-"][delegatedPropertyName].fullNameChain.slice(0);
            //noinspection JSUnfilteredForInLoop
            self["-delegated-"][delegatedPropertyName].lastName = self["-delegated-"][delegatedPropertyName].lastContextNameChain.pop();
            //noinspection JSUnfilteredForInLoop
            self["-delegated-"][delegatedPropertyName].changed = dependentPropertyChanged(delegatedPropertyName);
            //noinspection JSUnfilteredForInLoop
            self["-delegated-"][delegatedPropertyName].chain = bindingChains(
              self,
              //noinspection JSUnfilteredForInLoop
              [self["-delegated-"][delegatedPropertyName].path],
              //noinspection JSUnfilteredForInLoop
              self["-delegated-"][delegatedPropertyName].changed
            );
          }
        },

        get: function(/*String*/ propName) {
          // also get the delegated properties
          if (this["-delegated-"][propName]) {
            return getProp(this, this["-delegated-"][propName].nameChain);
          }
          return this.inherited(arguments);
        },

        set: function(/*String*/ propName, value) {
          // also get the delegated properties
          if (this["-delegated-"][propName]) {
            if (this["-delegated-"][propName].readOnly) {
              throw "ERROR: " + propName + " is read-only";
            }
            var lastContext = getProp(this, this["-delegated-"][propName].lastContextNameChain);
            if (!lastContext) {
              throw "ERROR: no context to set " + this["-delegated-"][propName].fullNameChain + " for " + propName;
            }
            if (lastContext.set) {
              return lastContext.set(this["-delegated-"][propName].lastName, value);
            }
            else {
              lastContext[this["-delegated-"][propName].lastName] = value;
              return value;
            }
          }
          return this.inherited(arguments);
        },

        "-delegated-": {
          something: {path: "one.two.three", readOnly: true} // TODO change and document, take union over inheritance
        },

        _stateToString: function(/*Array of String*/ toStrings) {
          // tags:
          //   protected extension

          // TODO; inherit from PpwCodeObject?
        }

      });

      _DelegationMixing.mid = module.id;

      return _DelegationMixing;
    }
);
<commit_msg>Code inspection. Ignore warning. Code reformat.<commit_after>/*
 Copyright 2012 - $Date $ by PeopleWare n.v.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

define(["dojo/_base/declare", "dojo/Stateful", "ppwcode-util-contracts/_Mixin",
        "ppwcode-util-oddsAndEnds/bindingChains", "module"],
  function(declare, Stateful, _ContractMixin, bindingChains, module) {

    // lang.getObject and lang.setObject don't use get and set
    function getProp(context, /*String[]*/ nameChain) {
      // nameChain:
      //   Note that this array will be changed by this method. On return, it will be empty.

      if (!nameChain || nameChain.length <= 0) {
        return context;
      }
      // there is more chain
      if (!context) {
        return undefined;
      }
      var first = nameChain.shift();
      var nextContext = context.get ? context.get(first) : context[first];
      return getProp(nextContext, nameChain);
    }

    //noinspection LocalVariableNamingConventionJS
    var _DelegationMixing = declare([Stateful, _ContractMixin], {
      // summary:
      //   Mixin that adds the ability to create virtual properties that delegate to other properties.
      //   Only makes sense with Stateful.

      // TODO created to save code that is ultimately not used; untested; DO NOT USE IN THIS STATE!

      _c_invar: [],

      postscript: function() {
        var self = this;

        function dependentPropertyChanged(dependentPropertyName) {
          return function(propName, previousValue, newValue) {
            if (self._watchCallbacks && newValue !== previousValue) {
              // fake the events
              self._watchCallbacks(dependentPropertyName, previousValue, newValue);
            }
          };
        }

        self.inherited(arguments);
        for (var delegatedPropertyName in self["-delegated-"]) {
          //noinspection JSUnfilteredForInLoop
          self["-delegated-"][delegatedPropertyName].fullNameChain =
          self["-delegated-"][delegatedPropertyName].path.split(".");
          //noinspection JSUnfilteredForInLoop
          self["-delegated-"][delegatedPropertyName].lastContextNameChain =
          self["-delegated-"][delegatedPropertyName].fullNameChain.slice(0);
          //noinspection JSUnfilteredForInLoop
          self["-delegated-"][delegatedPropertyName].lastName =
          self["-delegated-"][delegatedPropertyName].lastContextNameChain.pop();
          //noinspection JSUnfilteredForInLoop
          self["-delegated-"][delegatedPropertyName].changed = dependentPropertyChanged(delegatedPropertyName);
          //noinspection JSUnfilteredForInLoop
          self["-delegated-"][delegatedPropertyName].chain = bindingChains(
            self,
            //noinspection JSUnfilteredForInLoop
            [self["-delegated-"][delegatedPropertyName].path],
            //noinspection JSUnfilteredForInLoop
            self["-delegated-"][delegatedPropertyName].changed
          );
        }
      },

      get: function(/*String*/ propName) {
        // also get the delegated properties
        if (this["-delegated-"][propName]) {
          return getProp(this, this["-delegated-"][propName].nameChain);
        }
        return this.inherited(arguments);
      },

      set: function(/*String*/ propName, value) {
        // also get the delegated properties
        if (this["-delegated-"][propName]) {
          if (this["-delegated-"][propName].readOnly) {
            throw "ERROR: " + propName + " is read-only";
          }
          var lastContext = getProp(this, this["-delegated-"][propName].lastContextNameChain);
          if (!lastContext) {
            throw "ERROR: no context to set " + this["-delegated-"][propName].fullNameChain + " for " + propName;
          }
          if (lastContext.set) {
            return lastContext.set(this["-delegated-"][propName].lastName, value);
          }
          else {
            lastContext[this["-delegated-"][propName].lastName] = value;
            return value;
          }
        }
        return this.inherited(arguments);
      },

      "-delegated-": {
        something: {path: "one.two.three", readOnly: true} // TODO change and document, take union over inheritance
      },

      _stateToString: function(/*Array of String*/ toStrings) {
        // tags:
        //   protected extension

        // TODO; inherit from PpwCodeObject?
      }

    });

    _DelegationMixing.mid = module.id;

    return _DelegationMixing;
  }
);
<|endoftext|>