<commit_before>var _ = require("lodash");
var cheerio = require("cheerio");
var Msg = require("../../models/msg");
var request = require("request");
var Helper = require("../../helper");
var es = require('event-stream');

module.exports = function(irc, network) {
	var client = this;
	irc.on("message", function(data) {
		var config = Helper.getConfig();
		if (!config.prefetch) {
			return;
		}

		var links = [];
		var split = data.message.split(" ");
		_.each(split, function(w) {
			var match = w.indexOf("http://") === 0 || w.indexOf("https://") === 0;
			if (match) {
				links.push(w);
			}
		});

		if (links.length === 0) {
			return;
		}

		var self = data.to.toLowerCase() == irc.me.toLowerCase();
		var chan = _.findWhere(network.channels, {name: self ? data.from : data.to});
		if (typeof chan === "undefined") {
			return;
		}

		var msg = new Msg({
			type: Msg.Type.TOGGLE,
			time: ""
		});
		chan.messages.push(msg);
		client.emit("msg", {
			chan: chan.id,
			msg: msg
		});

		var link = links[0];
		fetch(link, function(res) {
			parse(msg, link, res, client);
		});
	});
};

function parse(msg, url, res, client) {
	var toggle = msg.toggle = {
		id: msg.id,
		type: "",
		head: "",
		body: "",
		thumb: "",
		link: url
	};

	switch (res.type) {
	case "text/html":
		var $ = cheerio.load(res.text);
		toggle.type = "link";
		toggle.head = $("title").text();
		toggle.body =
			   $('meta[name=description]').attr('content')
			|| $('meta[property="og:description"]').attr('content')
			|| "No description found.";
		toggle.thumb =
			   $('meta[property="og:image"]').attr('content')
			|| $('meta[name="twitter:image:src"]').attr('content')
			|| "";
		break;

	case "image/png":
	case "image/gif":
	case "image/jpg":
	case "image/jpeg":
		toggle.type = "image";
		break;

	default:
		return;
	}

	client.emit("toggle", toggle);
}

function fetch(url, cb) {
	var req = request.get(url);
	var length = 0;
	var limit = 1024 * 10;
	req
		.on('response', function(res) {
			if (!(/(text\/html|application\/json)/.test(res.headers['content-type']))) {
			  res.req.abort();
			}
		})
		.on('error', function() {})
		.pipe(es.map(function(data, next) {
			length += data.length;
			if (length > limit) {
				req.response.req.abort();
			}
			next(null, data);
		}))
		.pipe(es.wait(function(err, data) {
			if (err) return;
			var body;
			try {
				body = JSON.parse(data);
			} catch(e) {
				body = {};
			}
			data = {
				text: data,
				body: body,
				type: req.response.headers['content-type'].split(/ *; */).shift()
			};
			cb(data);
		}));
}
<commit_msg>Catch undefined content-type headers<commit_after>var _ = require("lodash");
var cheerio = require("cheerio");
var Msg = require("../../models/msg");
var request = require("request");
var Helper = require("../../helper");
var es = require('event-stream');

module.exports = function(irc, network) {
	var client = this;
	irc.on("message", function(data) {
		var config = Helper.getConfig();
		if (!config.prefetch) {
			return;
		}

		var links = [];
		var split = data.message.split(" ");
		_.each(split, function(w) {
			var match = w.indexOf("http://") === 0 || w.indexOf("https://") === 0;
			if (match) {
				links.push(w);
			}
		});

		if (links.length === 0) {
			return;
		}

		var self = data.to.toLowerCase() == irc.me.toLowerCase();
		var chan = _.findWhere(network.channels, {name: self ? data.from : data.to});
		if (typeof chan === "undefined") {
			return;
		}

		var msg = new Msg({
			type: Msg.Type.TOGGLE,
			time: ""
		});
		chan.messages.push(msg);
		client.emit("msg", {
			chan: chan.id,
			msg: msg
		});

		var link = links[0];
		fetch(link, function(res) {
			parse(msg, link, res, client);
		});
	});
};

function parse(msg, url, res, client) {
	var toggle = msg.toggle = {
		id: msg.id,
		type: "",
		head: "",
		body: "",
		thumb: "",
		link: url
	};

	switch (res.type) {
	case "text/html":
		var $ = cheerio.load(res.text);
		toggle.type = "link";
		toggle.head = $("title").text();
		toggle.body =
			   $('meta[name=description]').attr('content')
			|| $('meta[property="og:description"]').attr('content')
			|| "No description found.";
		toggle.thumb =
			   $('meta[property="og:image"]').attr('content')
			|| $('meta[name="twitter:image:src"]').attr('content')
			|| "";
		break;

	case "image/png":
	case "image/gif":
	case "image/jpg":
	case "image/jpeg":
		toggle.type = "image";
		break;

	default:
		return;
	}

	client.emit("toggle", toggle);
}

function fetch(url, cb) {
	var req = request.get(url);
	var length = 0;
	var limit = 1024 * 10;
	req
		.on('response', function(res) {
			if (!(/(text\/html|application\/json)/.test(res.headers['content-type']))) {
			  res.req.abort();
			}
		})
		.on('error', function() {})
		.pipe(es.map(function(data, next) {
			length += data.length;
			if (length > limit) {
				req.response.req.abort();
			}
			next(null, data);
		}))
		.pipe(es.wait(function(err, data) {
			if (err) return;
			var body;
			var type;
			try {
				body = JSON.parse(data);
			} catch(e) {
				body = {};
			}
			try {
				type = req.response.headers['content-type'].split(/ *; */).shift();
			} catch(e) {
				type = {};
			}
			data = {
				text: data,
				body: body,
				type: type
			};
			cb(data);
		}));
}
<|endoftext|>


<commit_before>const QueryString = require('querystring');
const formatNumber = require('format-number')();
const Pagination = require('../pagination');

module.exports = (params, results, config = {}) => {
  const rootUrl = config.rootUrl || '';

  const data = {
    title: 'Search Results',
    page: results.data.length ? 'search' : 'noresults',
    q: params.q,
    results: results.data.map(createResult),
    pagination: createPagination(params, results, rootUrl),
    filters: results.meta.filters,
    links: {
      filterAll: createTypeFilterLink(null, params, rootUrl),
      filterObjects: createTypeFilterLink('objects', params, rootUrl),
      filterPeople: createTypeFilterLink('people', params, rootUrl),
      filterDocuments: createTypeFilterLink('documents', params, rootUrl)
    }
  };

  if (params['filter[type]']) {
    if (params['filter[type]'] === 'objects') {
      data.isFilterObjects = true;
    } else if (params['filter[type]'] === 'people') {
      data.isFilterPeople = true;
    } else if (params['filter[type]'] === 'documents') {
      data.isFilterDocuments = true;
    } else {
      data.isFilterAll = true;
    }
  } else {
    data.isFilterAll = true;
  }

  return data;
};

function createResult (item) {
  return {
    type: item.type,
    title: getTitle(item),
    description: getDescription(item),
    figure: getFigure(item),
    figcaption: getFigCaption(item),
    link: item.links && item.links.self
  };
}

function getTitle (item) {
  return item.attributes.summary_title || getPrimaryValue(item.attributes.title);
}

function getDescription (item) {
  return getPrimaryValue(item.attributes.description);
}

function getFigure (item) {
  if (item.type === 'documents') return '/assets/img/archive.svg';
  return null; // No images in index yet
}

function getFigCaption (item) {
  return getPrimaryValue(item.attributes.description) || getPrimaryValue(item.attributes.title) || item.attributes.summary_title;
}

function getPrimaryValue (array) {
  if (Object.prototype.toString.call(array) === '[object String]') {
    return array;
  }

  if (!Array.isArray(array) || !array.length) {
    return null;
  }

  const primary = array.find((d) => d.primary);
  return primary ? primary.value : array[0].value;
}

function createPagination (params, results, rootUrl) {
  const pageNumber = params['page[number]'] || 0;
  const pageSize = params['page[size]'] || 50;

  const pages = Pagination.pages({
    currentPageNumber: pageNumber,
    totalPages: results.meta.total_pages,
    createPageLink: (page) => {
      if (page) {
        page.pageNumberDisplay = formatNumber(page.pageNumber + 1);
        const pageParams = Object.assign({}, params, { 'page[number]': page.pageNumber });
        return `${rootUrl}/search?${QueryString.stringify(pageParams)}`;
      }
      return page;
    }
  });

  return {
    totalPages: results.meta.total_pages,
    pageNumber: pageNumber + 1,
    isFirst: pageNumber === 0,
    isLast: pageNumber === results.meta.total_pages - 1,
    isPageSize50: pageSize === 50,
    isPageSize100: pageSize === 100,
    links: Object.assign({}, results.links, { pages })
  };
}

function createTypeFilterLink (type, params, rootUrl) {
  params = Object.assign({}, params);
  delete params['filter[type]'];
  type = type ? '/' + type : '';
  return `${rootUrl}/search${type}?${QueryString.stringify(params)}`;
}
<commit_msg>Remove unnecessary guard<commit_after>const QueryString = require('querystring');
const formatNumber = require('format-number')();
const Pagination = require('../pagination');

module.exports = (params, results, config = {}) => {
  const rootUrl = config.rootUrl || '';

  const data = {
    title: 'Search Results',
    page: results.data.length ? 'search' : 'noresults',
    q: params.q,
    results: results.data.map(createResult),
    pagination: createPagination(params, results, rootUrl),
    filters: results.meta.filters,
    links: {
      filterAll: createTypeFilterLink(null, params, rootUrl),
      filterObjects: createTypeFilterLink('objects', params, rootUrl),
      filterPeople: createTypeFilterLink('people', params, rootUrl),
      filterDocuments: createTypeFilterLink('documents', params, rootUrl)
    }
  };

  if (params['filter[type]']) {
    if (params['filter[type]'] === 'objects') {
      data.isFilterObjects = true;
    } else if (params['filter[type]'] === 'people') {
      data.isFilterPeople = true;
    } else if (params['filter[type]'] === 'documents') {
      data.isFilterDocuments = true;
    } else {
      data.isFilterAll = true;
    }
  } else {
    data.isFilterAll = true;
  }

  return data;
};

function createResult (item) {
  return {
    type: item.type,
    title: getTitle(item),
    description: getDescription(item),
    figure: getFigure(item),
    figcaption: getFigCaption(item),
    link: item.links && item.links.self
  };
}

function getTitle (item) {
  return item.attributes.summary_title || getPrimaryValue(item.attributes.title);
}

function getDescription (item) {
  return getPrimaryValue(item.attributes.description);
}

function getFigure (item) {
  if (item.type === 'documents') return '/assets/img/archive.svg';
  return null; // No images in index yet
}

function getFigCaption (item) {
  return getPrimaryValue(item.attributes.description) || getPrimaryValue(item.attributes.title) || item.attributes.summary_title;
}

function getPrimaryValue (array) {
  if (Object.prototype.toString.call(array) === '[object String]') {
    return array;
  }

  if (!Array.isArray(array) || !array.length) {
    return null;
  }

  const primary = array.find((d) => d.primary);
  return primary ? primary.value : array[0].value;
}

function createPagination (params, results, rootUrl) {
  const pageNumber = params['page[number]'] || 0;
  const pageSize = params['page[size]'] || 50;

  const pages = Pagination.pages({
    currentPageNumber: pageNumber,
    totalPages: results.meta.total_pages,
    createPageLink: (page) => {
      page.pageNumberDisplay = formatNumber(page.pageNumber + 1);
      const pageParams = Object.assign({}, params, { 'page[number]': page.pageNumber });
      return `${rootUrl}/search?${QueryString.stringify(pageParams)}`;
    }
  });

  return {
    totalPages: results.meta.total_pages,
    pageNumber: pageNumber + 1,
    isFirst: pageNumber === 0,
    isLast: pageNumber === results.meta.total_pages - 1,
    isPageSize50: pageSize === 50,
    isPageSize100: pageSize === 100,
    links: Object.assign({}, results.links, { pages })
  };
}

function createTypeFilterLink (type, params, rootUrl) {
  params = Object.assign({}, params);
  delete params['filter[type]'];
  type = type ? '/' + type : '';
  return `${rootUrl}/search${type}?${QueryString.stringify(params)}`;
}
<|endoftext|>


<commit_before>exports.BattleStatuses = {
	slp: {
		inherit: true,
		onSwitchIn: function(target) {
			this.effectData.time = this.effectData.startTime;
		}
	}
	partiallytrapped: {
		inherit: true,
		onResidual: function(pokemon) {
			if (this.effectData.source && (!this.effectData.source.isActive || this.effectData.source.hp <= 0)) {
				pokemon.removeVolatile('partiallytrapped');
				return;
			}
			if (this.effectData.source.item === 'bindingband') {
				this.damage(pokemon.maxhp/8);
			} else {
				this.damage(pokemon.maxhp/16);
			}
		},
	}
};
<commit_msg>Fix crash from commit 644d9fc720dbcfbc845c46528ac29dfc80e2c39f<commit_after>exports.BattleStatuses = {
	slp: {
		inherit: true,
		onSwitchIn: function(target) {
			this.effectData.time = this.effectData.startTime;
		}
	},
	partiallytrapped: {
		inherit: true,
		onResidual: function(pokemon) {
			if (this.effectData.source && (!this.effectData.source.isActive || this.effectData.source.hp <= 0)) {
				pokemon.removeVolatile('partiallytrapped');
				return;
			}
			if (this.effectData.source.item === 'bindingband') {
				this.damage(pokemon.maxhp/8);
			} else {
				this.damage(pokemon.maxhp/16);
			}
		}
	}
};
<|endoftext|>


<commit_before>
describe('Wand', function () {

  var _elem, wand, opts;
  var expect = chai.expect;
  var elemId = 'wandSpecDiv';
  var nodes = [{"id": 0, 'type': 'pickOne', 'title': "node 0 title", "content": "foo", "triggers": []}];
  var badApiTrigger = [{"callbackFn": "DOESNOTEXIST", "content": "test"}];
  var goodApiTrigger = [{"callbackFn": "aFunction", "content": "test"}];
  var nestedApiTrigger = [{"callbackFn": "nested.aFunction", "content": "test"}];

  var goodPreTrigger = [{ "callbackFn": "aFunction",
                          "preprocessor": "bFunction",
                          "content": "test"}];

  var badPreTrigger = [{ "callbackFn": "aFunction",
                          "preprocessor": "DOESNOTEXIST",
                          "content": "test"}];

  window.aFunction = function(){};
  window.bFunction = function(){};
  window.nested = {};
  window.nested.aFunction = function(){};

  beforeEach(function () {
    opts = {elem: elemId, nodes: nodes};
    var _elem = document.createElement('div');
    _elem.id = elemId;
    document.body.appendChild(_elem);
    wand = Wand;
  });

  afterEach(function() {
    opts = {};
    var node = document.getElementById(elemId);
    if (node.parentNode) {
      node.parentNode.removeChild(node);
    }
  });

  it('should fail without any options', function () {
    expect(function(){ wand.init(); } ).to.throw(Error);
  });

  it('should fail without an html element', function () {
    expect(function(){ wand.init({elem: null}); } ).to.throw(Error);
  });

  it('should fail with a bad html element', function () {
    expect(function(){ wand.init({elem: 'NOELEMENTHERE'}); } ).to.throw(Error);
  });

  it('should fail with duplicate nodes', function() {
    opts.nodes = [{'id': 0}, {'id': 0}];
    expect(function() { wand.init(opts); }).to.throw(Error);
  });

  it('should successfully initialize with a valid options', function () {
    wand.init(opts);
  });

  it('should have a container class of wand-container', function () {
    wand.init(opts);
    expect(document.getElementsByClassName('wand-container').length).to.eq(1);
  });

  describe('Wand API Node', function() {

    beforeEach(function() {
      opts.nodes[0].type = 'api';
    });

    it('should fail to initialize if it cannot find a function in the callbackFns', function() {
      opts.nodes[0].triggers = badApiTrigger;
      expect(function() { wand.init(nodes); }).to.throw(Error);
    });

    it('should initialize properly if the callbackFns are registered', function() {
      opts.nodes[0].triggers = goodApiTrigger;
      wand.init(opts);
    });

    it('should initialize properly even with nested callbacks', function() {
      opts.nodes[0].triggers = nestedApiTrigger;
      wand.init(opts);
    });

  });

});
<commit_msg>Fixed test case 'api' --> 'custom'<commit_after>
describe('Wand', function () {

  var _elem, wand, opts;
  var expect = chai.expect;
  var elemId = 'wandSpecDiv';
  var nodes = [{"id": 0, 'type': 'pickOne', 'title': "node 0 title", "content": "foo", "triggers": []}];
  var badApiTrigger = [{"callbackFn": "DOESNOTEXIST", "content": "test"}];
  var goodApiTrigger = [{"callbackFn": "aFunction", "content": "test"}];
  var nestedApiTrigger = [{"callbackFn": "nested.aFunction", "content": "test"}];

  var goodPreTrigger = [{ "callbackFn": "aFunction",
                          "content": "test"}];

  var badPreTrigger = [{ "callbackFn": "aFunction",
                          "content": "test"}];

  window.aFunction = function(){};
  window.bFunction = function(){};
  window.nested = {};
  window.nested.aFunction = function(){};

  beforeEach(function () {
    opts = {elem: elemId, nodes: nodes};
    var _elem = document.createElement('div');
    _elem.id = elemId;
    document.body.appendChild(_elem);
    wand = Wand;
  });

  afterEach(function() {
    opts = {};
    var node = document.getElementById(elemId);
    if (node.parentNode) {
      node.parentNode.removeChild(node);
    }
  });

  it('should fail without any options', function () {
    expect(function(){ wand.init(); } ).to.throw(Error);
  });

  it('should fail without an html element', function () {
    expect(function(){ wand.init({elem: null}); } ).to.throw(Error);
  });

  it('should fail with a bad html element', function () {
    expect(function(){ wand.init({elem: 'NOELEMENTHERE'}); } ).to.throw(Error);
  });

  it('should fail with duplicate nodes', function() {
    opts.nodes = [{'id': 0}, {'id': 0}];
    expect(function() { wand.init(opts); }).to.throw(Error);
  });

  it('should successfully initialize with a valid options', function () {
    wand.init(opts);
  });

  it('should have a container class of wand-container', function () {
    wand.init(opts);
    expect(document.getElementsByClassName('wand-container').length).to.eq(1);
  });

  describe('Wand API Node', function() {

    beforeEach(function() {
      opts.nodes[0].type = 'custom';
    });

    it('should fail to initialize if it cannot find a function in the callbackFns', function() {
      opts.nodes[0].triggers = badApiTrigger;
      expect(function() { wand.init(nodes); }).to.throw(Error);
    });

    it('should initialize properly if the callbackFns are registered', function() {
      opts.nodes[0].triggers = goodApiTrigger;
      wand.init(opts);
    });

    it('should initialize properly even with nested callbacks', function() {
      opts.nodes[0].triggers = nestedApiTrigger;
      wand.init(opts);
    });

  });

});
<|endoftext|>


<commit_before>"use strict";

var _ = require('lodash');
var bbu = require('blue-button-util');

var json2Json = require('./json2Json');
var rootLogger = require('./logger');
var templates = require('./templates');

var bbuobject = bbu.object;

var logger = rootLogger.child({
    module: 'fhir'
});

var toResourceDictionary = exports.toResourceDictionary = function (bundleEntries) {
    var result = {};
    if (bundleEntries) {
        bundleEntries.forEach(function (bundleEntry) {
            var resource = bundleEntry.resource;
            var id = resource.id;
            result[id] = resource;
        });
    }
    return result;
};

var resourceToModel = exports.resourceToModel = function (resourceDictionary, template, resource) {
    var localResourceDictionary = Object.create(resourceDictionary);
    var contained = resource.contained;
    if (contained) {
        contained.forEach(function (e) {
            var id = e.id;
            localResourceDictionary['#' + id] = e;
        });
    }
    var j2j = json2Json.instance(localResourceDictionary);
    var result = j2j.run(template, resource);
    return result;
};

var classifyResource = exports.classifyResource = (function () {
    var codeMap = {
        '9279-1': "vitals",
        '8867-4': "vitals",
        '2710-2': "vitals",
        '55284-4': "vitals",
        '55418-8': "vitals",
        '8480-6': "vitals",
        '8462-4': "vitals",
        '8310-5': "vitals",
        '8302-2': "vitals",
        '8306-3': "vitals",
        '8287-5': "vitals",
        '3141-9': "vitals",
        '39156-5': "vitals",
        '3140-1': "vitals",
        '8716-3': "vitals",
        '8284-2': "vitals",
        '29463-7': "vitals",
        '35094-2': "vitals",
        '41909-3': 'vitals',
        '3139-2': 'vitals'
    };

    var typeMap = {
        observation: 'results',
        condition: 'problems',
        allergyintolerance: 'allergies'
    };

    return function (resource) {
        var code = bbuobject.deepValue(resource, 'code.coding.0.code');
        var type = codeMap[code];
        if (type) {
            return type;
        }
        type = typeMap[resource.resourceType.toLowerCase()];
        if (type) {
            return type;
        }
        return 'other';
    };
})();

var topBundleEntryInfos = exports.topBundleEntryInfos = (function () {
    var pullRelatedOutOfContention = function (content, r, indexDictionary, doNotAddDictionary) {
        content.related.forEach(function (component) {
            var id = component.target.reference;
            doNotAddDictionary[id] = true;
            var existingIndex = indexDictionary[id];
            if ((existingIndex !== undefined) && (existingIndex !== null)) {
                r[existingIndex] = null;
            }
        });
    };

    var resourceToHandlerKey = {
        Condition: function () {
            return 'condition';
        },
        AllergyIntolerance: function () {
            return 'allergyIntolerance';
        },
        Observation: function (bundleEntry, r, indexDictionary, doNotAddDictionary) {
            var resource = bundleEntry.resource;
            var observationType = classifyResource(resource);
            if (observationType === 'vitals') {
                if (resource.related) {
                    return null;
                } else {
                    return 'observation-vital';
                }
            } else {
                if (resource.related) {
                    pullRelatedOutOfContention(resource, r, indexDictionary, doNotAddDictionary);
                    return 'observation-result';
                } else {
                    return 'observation-result-single';
                }
            }
        },
        MedicationPrescription: function () {
            return 'medicationPrescription';
        },
        MedicationAdministration: function (bundleEntry, r, indexDictionary, doNotAddDictionary) {
            var id = bundleEntry.resource.prescription.reference;
            doNotAddDictionary[id] = true;
            var existingIndex = indexDictionary[id];
            if ((existingIndex !== undefined) && (existingIndex !== null)) {
                r[existingIndex] = null;
            }
            return 'medicationAdministration';
        },
        'MedicationStatement': function () {
            return 'medicationStatement';
        },
        Patient: function () {
            return 'patient';
        }
    };

    return function (bundleEntries) {
        var indexDictionary = {};
        var doNotAddDictionary = {};
        var preliminaryResult = bundleEntries.reduce(function (r, bundleEntry) {
            var resource = bundleEntry.resource;
            var type = resource.resourceType;
            var fn = resourceToHandlerKey[type];
            if (fn) {
                var id = resource.id;
                if (!doNotAddDictionary[id]) {
                    var hk = fn(bundleEntry, r, indexDictionary, doNotAddDictionary);
                    if (hk) {
                        r.push({
                            bundleEntry: bundleEntry,
                            templateKey: hk
                        });
                        indexDictionary[id] = r.length - 1;
                    }
                }
            }
            return r;
        }, []);
        var result = preliminaryResult.filter(function (e) {
            return e;
        });
        return result;
    };
})();

exports.toModel = (function () {
    var addToSection = function (sectionName) {
        return function (data, value) {
            var section = data[sectionName] || (data[sectionName] = []);
            section.push(value);
        };
    };

    var modelUpdate = {
        problem: addToSection('problems'),
        allergy: addToSection('allergies'),
        vital: addToSection('vitals'),
        result: addToSection('results'),
        medication: addToSection('medications'),
        demographics: function (data, value) {
            data.demographics = value;
        }
    };

    return function (bundle) {
        logger.trace('started toModel');

        var bundleEntries = bundle && bundle.entry;
        var resourceDictionary = toResourceDictionary(bundleEntries);

        var fn = function (topBundleEntryInfo) {
            var templateKey = topBundleEntryInfo.templateKey;
            var template = templates[templateKey];
            var value = resourceToModel(resourceDictionary, template, topBundleEntryInfo.bundleEntry.resource);
            return {
                type: template.type,
                value: value
            };
        };

        var ers = topBundleEntryInfos(bundleEntries);
        var results = ers.map(fn);
        var model = {
            meta: {},
            data: {}
        };
        results.forEach(function (result) {
            var type = result.type;
            var f = modelUpdate[type];
            if (f) {
                f(model.data, result.value);
            }
        });
        return model;
    };
})();

exports.resourceToModelEntry = (function () {
    var map = {
        'vitals': 'observation-vital',
        'problems': 'condition',
        'allergies': 'allergyIntolerance',
        'results': 'observation-result-single',
        'medications': 'medicationPrescription',
        'demographics': 'patient'
    };

    return function (resource, type) {
        var templateKey = map[type];
        var template = templates[templateKey];
        var j2j = json2Json.instance();
        var result = j2j.run(template, resource);
        return result;
    };
})();
<commit_msg>handle bad resource type<commit_after>"use strict";

var _ = require('lodash');
var bbu = require('blue-button-util');

var json2Json = require('./json2Json');
var rootLogger = require('./logger');
var templates = require('./templates');

var bbuobject = bbu.object;

var logger = rootLogger.child({
    module: 'fhir'
});

var toResourceDictionary = exports.toResourceDictionary = function (bundleEntries) {
    var result = {};
    if (bundleEntries) {
        bundleEntries.forEach(function (bundleEntry) {
            var resource = bundleEntry.resource;
            var id = resource.id;
            result[id] = resource;
        });
    }
    return result;
};

var resourceToModel = exports.resourceToModel = function (resourceDictionary, template, resource) {
    var localResourceDictionary = Object.create(resourceDictionary);
    var contained = resource.contained;
    if (contained) {
        contained.forEach(function (e) {
            var id = e.id;
            localResourceDictionary['#' + id] = e;
        });
    }
    var j2j = json2Json.instance(localResourceDictionary);
    var result = j2j.run(template, resource);
    return result;
};

var classifyResource = exports.classifyResource = (function () {
    var codeMap = {
        '9279-1': "vitals",
        '8867-4': "vitals",
        '2710-2': "vitals",
        '55284-4': "vitals",
        '55418-8': "vitals",
        '8480-6': "vitals",
        '8462-4': "vitals",
        '8310-5': "vitals",
        '8302-2': "vitals",
        '8306-3': "vitals",
        '8287-5': "vitals",
        '3141-9': "vitals",
        '39156-5': "vitals",
        '3140-1': "vitals",
        '8716-3': "vitals",
        '8284-2': "vitals",
        '29463-7': "vitals",
        '35094-2': "vitals",
        '41909-3': 'vitals',
        '3139-2': 'vitals'
    };

    var typeMap = {
        observation: 'results',
        condition: 'problems',
        allergyintolerance: 'allergies'
    };

    return function (resource) {
        var code = bbuobject.deepValue(resource, 'code.coding.0.code');
        var type = codeMap[code];
        if (type) {
            return type;
        }
        type = resource.resourceType && typeMap[resource.resourceType.toLowerCase()];
        if (type) {
            return type;
        }
        return 'other';
    };
})();

var topBundleEntryInfos = exports.topBundleEntryInfos = (function () {
    var pullRelatedOutOfContention = function (content, r, indexDictionary, doNotAddDictionary) {
        content.related.forEach(function (component) {
            var id = component.target.reference;
            doNotAddDictionary[id] = true;
            var existingIndex = indexDictionary[id];
            if ((existingIndex !== undefined) && (existingIndex !== null)) {
                r[existingIndex] = null;
            }
        });
    };

    var resourceToHandlerKey = {
        Condition: function () {
            return 'condition';
        },
        AllergyIntolerance: function () {
            return 'allergyIntolerance';
        },
        Observation: function (bundleEntry, r, indexDictionary, doNotAddDictionary) {
            var resource = bundleEntry.resource;
            var observationType = classifyResource(resource);
            if (observationType === 'vitals') {
                if (resource.related) {
                    return null;
                } else {
                    return 'observation-vital';
                }
            } else {
                if (resource.related) {
                    pullRelatedOutOfContention(resource, r, indexDictionary, doNotAddDictionary);
                    return 'observation-result';
                } else {
                    return 'observation-result-single';
                }
            }
        },
        MedicationPrescription: function () {
            return 'medicationPrescription';
        },
        MedicationAdministration: function (bundleEntry, r, indexDictionary, doNotAddDictionary) {
            var id = bundleEntry.resource.prescription.reference;
            doNotAddDictionary[id] = true;
            var existingIndex = indexDictionary[id];
            if ((existingIndex !== undefined) && (existingIndex !== null)) {
                r[existingIndex] = null;
            }
            return 'medicationAdministration';
        },
        'MedicationStatement': function () {
            return 'medicationStatement';
        },
        Patient: function () {
            return 'patient';
        }
    };

    return function (bundleEntries) {
        var indexDictionary = {};
        var doNotAddDictionary = {};
        var preliminaryResult = bundleEntries.reduce(function (r, bundleEntry) {
            var resource = bundleEntry.resource;
            var type = resource.resourceType;
            var fn = resourceToHandlerKey[type];
            if (fn) {
                var id = resource.id;
                if (!doNotAddDictionary[id]) {
                    var hk = fn(bundleEntry, r, indexDictionary, doNotAddDictionary);
                    if (hk) {
                        r.push({
                            bundleEntry: bundleEntry,
                            templateKey: hk
                        });
                        indexDictionary[id] = r.length - 1;
                    }
                }
            }
            return r;
        }, []);
        var result = preliminaryResult.filter(function (e) {
            return e;
        });
        return result;
    };
})();

exports.toModel = (function () {
    var addToSection = function (sectionName) {
        return function (data, value) {
            var section = data[sectionName] || (data[sectionName] = []);
            section.push(value);
        };
    };

    var modelUpdate = {
        problem: addToSection('problems'),
        allergy: addToSection('allergies'),
        vital: addToSection('vitals'),
        result: addToSection('results'),
        medication: addToSection('medications'),
        demographics: function (data, value) {
            data.demographics = value;
        }
    };

    return function (bundle) {
        logger.trace('started toModel');

        var bundleEntries = bundle && bundle.entry;
        var resourceDictionary = toResourceDictionary(bundleEntries);

        var fn = function (topBundleEntryInfo) {
            var templateKey = topBundleEntryInfo.templateKey;
            var template = templates[templateKey];
            var value = resourceToModel(resourceDictionary, template, topBundleEntryInfo.bundleEntry.resource);
            return {
                type: template.type,
                value: value
            };
        };

        var ers = topBundleEntryInfos(bundleEntries);
        var results = ers.map(fn);
        var model = {
            meta: {},
            data: {}
        };
        results.forEach(function (result) {
            var type = result.type;
            var f = modelUpdate[type];
            if (f) {
                f(model.data, result.value);
            }
        });
        return model;
    };
})();

exports.resourceToModelEntry = (function () {
    var map = {
        'vitals': 'observation-vital',
        'problems': 'condition',
        'allergies': 'allergyIntolerance',
        'results': 'observation-result-single',
        'medications': 'medicationPrescription',
        'demographics': 'patient'
    };

    return function (resource, type) {
        var templateKey = map[type];
        var template = templates[templateKey];
        var j2j = json2Json.instance();
        var result = j2j.run(template, resource);
        return result;
    };
})();
<|endoftext|>


<commit_before>// Wait until the form has loaded entirely.
var documentReady = new Promise($(document).ready);

// Load the year, month, and items values from localStorage,
// or get the current year and month and the default item set.
var loadFromCookiesOrDefaults = function (){

  return new Promise(function (resolve){
    if (localStorage.getItem('items')){
      resolve({
        items : localStorage.getItem('items'),
        year  : localStorage.getItem('year'),
        month : localStorage.getItem('month')
      });
    } else {
      $.get('default-item-list.txt', function (data) {
        var today = new Date();

        resolve({
          items : data,
          year  : today.getFullYear(),
          month : today.getMonth()
        });
      });
    }
  });

};

// Populate the form from the loaded data,
// generate the TaskPaper month from the items field, and
// store the results in localStorage.
var populateAndProcessAndRememberFormData = function (data){

  var yearField      = $('#year'),
      monthField     = $('#month'),
      itemsField     = $('#items'),
      taskpaperMonth = $('#taskpaper-month');

  var generateTaskPaperMonth = function (){
    // read contents of form, e.g.
    var items          = itemsField.html();
        numberOfDays   = new Date(data.year, data.month, 0).getDate(),
        generatedMonth = '';

    // Build the array of items from the contents of the textarea.
    generatedMonth += items;

    // Loop through each of the days and include the items therein.
    for (var day = 1; day <= numberOfDays; day++) {

      // Add leading zeroes to day numbers under 10.
      day = day < 10 ? '0' + day : day;

      generatedMonth += day !== '01' ? '\n' : '';

      // Start the day.
      generatedMonth += day;

      // If this day contains items, print them.
      // TODO.
    }

    taskpaperMonth.html(generatedMonth);
  };

  // Initial setup:

  // 1. Populate year <select> with current and next year.
  var currentYear = new Date().getFullYear(),
      nextYear    = currentYear + 1,
      years       = [currentYear, nextYear];

  $.each(years, function(key, value) {
    yearField.append(
      $('<option></option>').attr('value', value).text(value)
    );
  });

  // 2. Select the loaded year.

  // TODO

  // 3. Select the loaded month.

  // TODO

  // 4. Populate items field with loaded items.
  // 5. Give items field focus.
  itemsField.html(data.items).focus();

  // 6. Generate TaskPaper month for the first time.
  generateTaskPaperMonth();

  //

  // Regenerate TaskPaper month on subsequent updates.
  itemsField.on('keyup', generateTaskPaperMonth);
};

documentReady
  .then(loadFromCookiesOrDefaults)
  .then(populateAndProcessAndRememberFormData);
<commit_msg>Finished converting functionality to JS.<commit_after>// Wait until the form has loaded entirely.
var documentReady = new Promise($(document).ready);

// Load the year, month, and items values from localStorage,
// or get the current year and month and the default item set.
var loadFromCookiesOrDefaults = function (){

  return new Promise(function (resolve){
    if (localStorage.getItem('items')){
      resolve({
        items : localStorage.getItem('items'),
        year  : localStorage.getItem('year'),
        month : localStorage.getItem('month')
      });
    } else {
      $.get('default-item-list.txt', function (data) {
        var today = new Date();

        resolve({
          items : data,
          year  : today.getFullYear(),
          month : today.getMonth()
        });
      });
    }
  });

};

// Populate the form from the loaded data,
// generate the TaskPaper month from the items field, and
// store the results in localStorage.
var populateAndProcessAndRememberFormData = function (data){

  var yearField      = $('#year'),
      monthField     = $('#month'),
      itemsField     = $('#items'),
      taskpaperMonth = $('#taskpaper-month');

  var generateTaskPaperMonth = function (){

    // Read the form data; prepare to generate a month.
    var itemsText      = itemsField.html(),
        itemsArray     = [],
        items          = {},
        numberOfDays   = new Date(data.year, data.month, 0).getDate(),
        generatedMonth = '';

    // Build an array of items from the contents of the textarea.
    // Split the textarea by digits followed by newlines.
    itemsArray = itemsText.split(/(\d+)\n/m);

    // Hack off the first (empty) array item.
    itemsArray.shift()

    // Create an associative array by iterating through even and odd
    // array items. (Odd are day numbers, even are that day's items.)
    $.each(itemsArray, function(key, value) {
      if (key%2 === 0) {
        items[itemsArray[key]] = itemsArray[key+1];
      }
    });

    // Turn each 'items' value in the object into an array split by newlines.
    $.each(items, function(key, value) {
      items[key] = items[key].trim().split('\n');
    });

    console.log(items);

    // Loop through each of the days and include the items therein.
    for (var day = 0; day <= numberOfDays; day++) {

      // Get the day name.
      var dayName = getDayName(new Date(data.year, data.month, day).getDay());

      // Add the day heading:

      // Add one to the day, since Javascript days are 0-indexed.
      dayNumber = day + 1;

      // Add a newline if we're not on the first day.
      if (dayNumber > 1)
        generatedMonth += '\n';

      // Add a zero for days under 10.
      if (dayNumber < 10)
        generatedMonth += '0'

      // Add the day number itself, a colon, and a newline.
      generatedMonth += dayNumber + ' ' + dayName + ':';

      // If this day contains items, print them.
      if (items[dayNumber]) {
        $.each(items[dayNumber], function(key, value) {
          if (value.substring(0, 2) === '  ' || value.substring(0, 2) === '- ')
            generatedMonth += '\n\t\t' + value
          else
            generatedMonth += '\n\t- ' + value
        });
      }

      // If we're on the last day of the month, generated a link back here!
      if (dayNumber === numberOfDays)
        generatedMonth += '\n\t- generate new Taskpaper month\n\t\thttp://matthewmcvickar.github.io/taskpaper-month-generator';

    }

    taskpaperMonth.html(generatedMonth);
  };

  // // // // // // // // // // // // // // // // // //

  // Initial setup:

  // 1. Populate year <select> with current and next year.
  var currentYear = new Date().getFullYear(),
      nextYear    = currentYear + 1,
      years       = [currentYear, nextYear];

 $.each(years, function (key, value){
    yearField.append(
      $('<option></option>').attr('value', value).text(value)
    );
  });

  // 2. Select the loaded year.

  // TODO

  // 3. Select the loaded month.

  // TODO

  // 4. Populate items field with loaded items.
  // 5. Give items field focus.
  itemsField.html(data.items).focus();

  // 6. Generate TaskPaper month for the first time.
  generateTaskPaperMonth();

  // // // // // // // // // // // // // // // // // //

  // Regenerate TaskPaper month on subsequent updates.
  itemsField.on('keyup', generateTaskPaperMonth);
};

documentReady
  .then(loadFromCookiesOrDefaults)
  .then(populateAndProcessAndRememberFormData);

// Get the name of the day.
function getDayName(day) {
  switch (day) {
    case 0:
      dayName = 'Sunday';
      break;
    case 1:
      dayName = 'Monday';
      break;
    case 2:
      dayName = 'Tuesday';
      break;
    case 3:
      dayName = 'Wednesday';
      break;
    case 4:
      dayName = 'Thursday';
      break;
    case 5:
      dayName = 'Friday';
      break;
    case 6:
      dayName = 'Saturday';
  }

  return dayName;
}<|endoftext|>


<commit_before>var CrossStorageHub = {};

/**
 * Accepts an array of objects with two keys: origin and allow. The value
 * of origin is expected to be a RegExp, and allow, an array of strings.
 * The cross storage hub is then initialized to accept requests from any of
 * the matching origins, allowing access to the associated lists of methods.
 * Methods may include any of: get, set, del, and getKeys. A 'ready' message
 * is sent to the parent window once complete.
 *
 * @example
 * // Subdomain can get, but only root domain can set and del
 * CrossStorageHub.init([
 *   {origin: /\.example.com$/,        allow: ['get']},
 *   {origin: /:(www\.)?example.com$/, allow: ['get', 'set', 'del']}
 * ]);
 *
 * @param {array} permissions An array of objects with origin and allow
 */
CrossStorageHub.init = function(permissions) {
  var available = true;

  // Return if localStorage is unavailable, or third party
  // access is disabled
  try {
    if (!window.localStorage) available = false;
  } catch (e) {
    available = false;
  }

  if (!available) {
    return window.parent.postMessage('unavailable', '*');
  }

  CrossStorageHub._permissions = permissions || [];
  CrossStorageHub._installListener();
  window.parent.postMessage('ready', '*');
};

/**
 * Installs the necessary listener for the window message event. Accommodates
 * IE8 and up.
 *
 * @private
 */
CrossStorageHub._installListener = function() {
  var listener = CrossStorageHub._listener;
  if (window.addEventListener) {
    window.addEventListener('message', listener, false);
  } else {
    window.attachEvent('onmessage', listener);
  }
};

/**
 * The message handler for all requests posted to the window. It ignores any
 * messages having an origin that does not match the originally supplied
 * pattern. Given a JSON object with one of get, set, del or getKeys as the
 * method, the function performs the requested action and returns its result.
 *
 * @param {MessageEvent} message A message to be processed
 */
CrossStorageHub._listener = function(message) {
  var uri, available, request, error, result, response;

  // Handle polling for a ready message
  if (message.data === 'poll') {
    return window.parent.postMessage('ready', message.origin);
  }

  // Ignore the ready message when viewing the hub directly
  if (message.data === 'ready') return;
  request = JSON.parse(message.data);

  if (!CrossStorageHub._permitted(message.origin, request.method)) {
    error = 'Invalid permissions for ' + request.method;
  } else {
    try {
      result = CrossStorageHub['_' + request.method](request.params);
    } catch (err) {
      error = err.message;
    }
  }

  response = JSON.stringify({
    id: request.id,
    error: error,
    result: result
  });

  window.parent.postMessage(response, message.origin);
};

/**
 * Returns a boolean indicating whether or not the requested method is
 * permitted for the given origin. The argument passed to method is expected
 * to be one of 'get', 'set', 'del' or 'getKeys'.
 *
 * @param   {string} origin The origin for which to determine permissions
 * @param   {string} method Requested action
 * @returns {bool}   Whether or not the request is permitted
 */
CrossStorageHub._permitted = function(origin, method) {
  var available, i, entry, match;

  available = ['get', 'set', 'del', 'clear', 'getKeys'];
  if (!CrossStorageHub._inArray(method, available)) {
    return false;
  }

  for (i = 0; i < CrossStorageHub._permissions.length; i++) {
    entry = CrossStorageHub._permissions[i];
    if (!(entry.origin instanceof RegExp) || !(entry.allow instanceof Array)) {
      continue;
    }

    match = entry.origin.test(origin);
    if (match && CrossStorageHub._inArray(method, entry.allow)) {
      return true;
    }
  }

  return false;
};

/**
 * Sets a key to the specified value. If a ttl is provided, an expiration
 * timestamp is added to the object to be stored, prior to serialization.
 *
 * @param {object} params An object with key, value and optional ttl
 */
CrossStorageHub._set = function(params) {
  var ttl, item;

  ttl = params.ttl;
  if (ttl && parseInt(ttl, 10) !== ttl) {
    throw new Error('ttl must be a number');
  }

  item = {value:  params.value};
  if (ttl) {
    item.expire = CrossStorageHub._now() + ttl;
  }

  window.localStorage.setItem(params.key, JSON.stringify(item));
};

/**
 * Accepts an object with an array of keys for which to retrieve their values.
 * Returns a single value if only one key was supplied, otherwise it returns
 * an array. Any keys not set, or expired, result in a null element in the
 * resulting array.
 *
 * @param   {object} params An object with an array of keys
 * @returns {*|*[]}  Either a single value, or an array
 */
CrossStorageHub._get = function(params) {
  var storage, result, i, item, key;

  storage = window.localStorage;
  result = [];

  for (i = 0; i < params.keys.length; i++) {
    key = params.keys[i];
    item = JSON.parse(storage.getItem(key));

    if (item === null) {
      result.push(null);
    } else if (item.expire && item.expire < CrossStorageHub._now()) {
      storage.removeItem(key);
      result.push(null);
    } else {
      result.push(item.value);
    }
  }

  return (result.length > 1) ? result : result[0];
};

/**
 * Deletes all keys specified in the array found at params.keys.
 *
 * @param {object} params An object with an array of keys
 */
CrossStorageHub._del = function(params) {
  for (var i = 0; i < params.keys.length; i++) {
    window.localStorage.removeItem(params.keys[i]);
  }
};

/**
 * Clears localStorage.
 */
CrossStorageHub._clear = function() {
  window.localStorage.clear();
};

/**
 * Returns an array of all keys stored in localStorage.
 *
 * @returns {string[]} The array of keys
 */
CrossStorageHub._getKeys = function(params) {
  var i, length, keys;

  keys = [];
  length = window.localStorage.length;

  for (i = 0; i < length; i++) {
    keys.push(window.localStorage.key(i));
  }

  return keys;
};

/**
 * Returns whether or not a value is present in the array. Consists of an
 * alternative to extending the array prototype for indexOf, since it's
 * unavailable for IE8.
 *
 * @param   {*}    value The value to find
 * @parma   {[]*}  array The array in which to search
 * @returns {bool} Whether or not the value was found
 */
CrossStorageHub._inArray = function(value, array) {
  for (var i = 0; i < array.length; i++) {
    if (value === array[i]) return true;
  }

  return false;
};

/**
 * A cross-browser version of Date.now compatible with IE8 that avoids
 * modifying the Date object.
 *
 * @return {int} The current timestamp in milliseconds
 */
CrossStorageHub._now = function() {
  if (typeof Date.now === 'function') {
    return Date.now();
  }

  return new Date().getTime();
};
<commit_msg>Wrap unavailable postMessage in try<commit_after>var CrossStorageHub = {};

/**
 * Accepts an array of objects with two keys: origin and allow. The value
 * of origin is expected to be a RegExp, and allow, an array of strings.
 * The cross storage hub is then initialized to accept requests from any of
 * the matching origins, allowing access to the associated lists of methods.
 * Methods may include any of: get, set, del, and getKeys. A 'ready' message
 * is sent to the parent window once complete.
 *
 * @example
 * // Subdomain can get, but only root domain can set and del
 * CrossStorageHub.init([
 *   {origin: /\.example.com$/,        allow: ['get']},
 *   {origin: /:(www\.)?example.com$/, allow: ['get', 'set', 'del']}
 * ]);
 *
 * @param {array} permissions An array of objects with origin and allow
 */
CrossStorageHub.init = function(permissions) {
  var available = true;

  // Return if localStorage is unavailable, or third party
  // access is disabled
  try {
    if (!window.localStorage) available = false;
  } catch (e) {
    available = false;
  }

  if (!available) {
    try {
      return window.parent.postMessage('unavailable', '*');
    } catch (e) {
      return;
    }
  }

  CrossStorageHub._permissions = permissions || [];
  CrossStorageHub._installListener();
  window.parent.postMessage('ready', '*');
};

/**
 * Installs the necessary listener for the window message event. Accommodates
 * IE8 and up.
 *
 * @private
 */
CrossStorageHub._installListener = function() {
  var listener = CrossStorageHub._listener;
  if (window.addEventListener) {
    window.addEventListener('message', listener, false);
  } else {
    window.attachEvent('onmessage', listener);
  }
};

/**
 * The message handler for all requests posted to the window. It ignores any
 * messages having an origin that does not match the originally supplied
 * pattern. Given a JSON object with one of get, set, del or getKeys as the
 * method, the function performs the requested action and returns its result.
 *
 * @param {MessageEvent} message A message to be processed
 */
CrossStorageHub._listener = function(message) {
  var uri, available, request, error, result, response;

  // Handle polling for a ready message
  if (message.data === 'poll') {
    return window.parent.postMessage('ready', message.origin);
  }

  // Ignore the ready message when viewing the hub directly
  if (message.data === 'ready') return;
  request = JSON.parse(message.data);

  if (!CrossStorageHub._permitted(message.origin, request.method)) {
    error = 'Invalid permissions for ' + request.method;
  } else {
    try {
      result = CrossStorageHub['_' + request.method](request.params);
    } catch (err) {
      error = err.message;
    }
  }

  response = JSON.stringify({
    id: request.id,
    error: error,
    result: result
  });

  window.parent.postMessage(response, message.origin);
};

/**
 * Returns a boolean indicating whether or not the requested method is
 * permitted for the given origin. The argument passed to method is expected
 * to be one of 'get', 'set', 'del' or 'getKeys'.
 *
 * @param   {string} origin The origin for which to determine permissions
 * @param   {string} method Requested action
 * @returns {bool}   Whether or not the request is permitted
 */
CrossStorageHub._permitted = function(origin, method) {
  var available, i, entry, match;

  available = ['get', 'set', 'del', 'clear', 'getKeys'];
  if (!CrossStorageHub._inArray(method, available)) {
    return false;
  }

  for (i = 0; i < CrossStorageHub._permissions.length; i++) {
    entry = CrossStorageHub._permissions[i];
    if (!(entry.origin instanceof RegExp) || !(entry.allow instanceof Array)) {
      continue;
    }

    match = entry.origin.test(origin);
    if (match && CrossStorageHub._inArray(method, entry.allow)) {
      return true;
    }
  }

  return false;
};

/**
 * Sets a key to the specified value. If a ttl is provided, an expiration
 * timestamp is added to the object to be stored, prior to serialization.
 *
 * @param {object} params An object with key, value and optional ttl
 */
CrossStorageHub._set = function(params) {
  var ttl, item;

  ttl = params.ttl;
  if (ttl && parseInt(ttl, 10) !== ttl) {
    throw new Error('ttl must be a number');
  }

  item = {value:  params.value};
  if (ttl) {
    item.expire = CrossStorageHub._now() + ttl;
  }

  window.localStorage.setItem(params.key, JSON.stringify(item));
};

/**
 * Accepts an object with an array of keys for which to retrieve their values.
 * Returns a single value if only one key was supplied, otherwise it returns
 * an array. Any keys not set, or expired, result in a null element in the
 * resulting array.
 *
 * @param   {object} params An object with an array of keys
 * @returns {*|*[]}  Either a single value, or an array
 */
CrossStorageHub._get = function(params) {
  var storage, result, i, item, key;

  storage = window.localStorage;
  result = [];

  for (i = 0; i < params.keys.length; i++) {
    key = params.keys[i];
    item = JSON.parse(storage.getItem(key));

    if (item === null) {
      result.push(null);
    } else if (item.expire && item.expire < CrossStorageHub._now()) {
      storage.removeItem(key);
      result.push(null);
    } else {
      result.push(item.value);
    }
  }

  return (result.length > 1) ? result : result[0];
};

/**
 * Deletes all keys specified in the array found at params.keys.
 *
 * @param {object} params An object with an array of keys
 */
CrossStorageHub._del = function(params) {
  for (var i = 0; i < params.keys.length; i++) {
    window.localStorage.removeItem(params.keys[i]);
  }
};

/**
 * Clears localStorage.
 */
CrossStorageHub._clear = function() {
  window.localStorage.clear();
};

/**
 * Returns an array of all keys stored in localStorage.
 *
 * @returns {string[]} The array of keys
 */
CrossStorageHub._getKeys = function(params) {
  var i, length, keys;

  keys = [];
  length = window.localStorage.length;

  for (i = 0; i < length; i++) {
    keys.push(window.localStorage.key(i));
  }

  return keys;
};

/**
 * Returns whether or not a value is present in the array. Consists of an
 * alternative to extending the array prototype for indexOf, since it's
 * unavailable for IE8.
 *
 * @param   {*}    value The value to find
 * @parma   {[]*}  array The array in which to search
 * @returns {bool} Whether or not the value was found
 */
CrossStorageHub._inArray = function(value, array) {
  for (var i = 0; i < array.length; i++) {
    if (value === array[i]) return true;
  }

  return false;
};

/**
 * A cross-browser version of Date.now compatible with IE8 that avoids
 * modifying the Date object.
 *
 * @return {int} The current timestamp in milliseconds
 */
CrossStorageHub._now = function() {
  if (typeof Date.now === 'function') {
    return Date.now();
  }

  return new Date().getTime();
};
<|endoftext|>


<commit_before>// Internal hub for ofirehose.com
//
// Copyright 2012 StatusNet Inc.
//
// 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.

var _ = require('underscore'),
    url = require('url'),
    qs = require('querystring'),
    http = require('http'),
    databank = require('databank'),
    crypto = require('crypto'),
    NoSuchThingError = databank.NoSuchThingError;

var MAX_LEASE     = 60 * 60 * 24 * 365;
var MIN_LEASE     = 60 * 60 * 24 * 1;
var DEFAULT_LEASE = 60 * 60 * 24 * 7;

var Hub = function(server, db) {
    this.server = server;
    this.db = db;
};

Hub.schema = {
    subscription: {
        pkey: 'callback',
        fields: ['callback', 'created', 'topic', 'lease_seconds'],
        indices: ['topic']
    }
};

Hub.prototype.ourTopic = function(topic) {
    var parts = url.parse(topic);

    return (parts.hostname === this.server &&
            parts.pathname === '/feed.json');
};

Hub.prototype.subscribe = function(params, cb) {

    var hub = this;

    this.validate(params, function(err) {
        var sub, callback;
        if (err) {
            cb(err);
        } else {
            callback = params.hub.callback;
            sub = {
                callback: callback,
                created: Date.now(),
                lease_seconds: Math.min(DEFAULT_LEASE, parseInt(params.hub.lease_seconds, 10))
            };

            if (_(params.hub).has('secret')) {
                sub.secret = params.hub.secret;
            }

            hub.db.save('subscription', callback, sub, function(err, value) {
                if (err) {
                    cb(new Error("Error saving subscription."));
                } else {
                    cb(null, value);
                }
            });
        }
    });
};

Hub.prototype.unsubscribe = function(params, cb) {

    var hub = this;

    this.validate(params, function(err) {
        var sub, callback;
        if (err) {
            cb(err);
        } else {
            callback = params.hub.callback;

            hub.db.del('subscription', callback, function(err) {
                if (err && !(err instanceof NoSuchThingError)) {
                    cb(new Error("Error deleting subscription."));
                } else {
                    cb(null);
                }
            });
        }
    });
};

Hub.prototype.validate = function(params, cb) {

    var callback, topic;

    callback = params.hub.callback;

    if (!callback) {
        cb(new Error("No callback provided."));
        return;
    }

    topic = params.hub.topic;

    if (!topic) {
        cb(new Error("No topic provided."));
        return;
    }

    if (!this.ourTopic(topic)) {
        cb(new Error("We don't support that topic."));
        return;
    }

    if (params.hub.verify !== 'sync') {
        cb(new Error("We only support sync verification."));
        return;
    }

    this.verify(params, function(err) {
        var sub;
        if (err) {
            cb(err);
        } else {
            cb(null);
        }
    });
};

Hub.prototype.makeChallenge = function() {

    var urlsafe = function(buf) {
        var str = buf.toString('base64');
        str = str.replace(/\+/g, '-');
        str = str.replace(/\//g, '_');
        str = str.replace(/\=/g, '');
        return str;
    };

    return urlsafe(crypto.randomBytes(16));
};

Hub.prototype.verify = function(params, cb) {

    var callback = params.hub.callback,
        challenge = this.makeChallenge(),
        args = {
            "hub.mode": params.hub.mode,
            "hub.topic": params.hub.topic,
            "hub.challenge": challenge
        };

    if (_(params.hub).has("lease_seconds")) {
        args["hub.lease_seconds"] = params.hub.lease_seconds;
    }

    if (_(params.hub).has("verify_token")) {
        args["hub.verify_token"] = params.hub.verify_token;
    }

    this.postRequest(callback, args, function(err, res, body) {
        if (err) {
            cb(new Error("Could not verify."));
        } else if (res.statusCode < 200 || res.statusCode >= 300) {
            cb(new Error("Could not verify."));
        } else if (body.trim() !== challenge) {
            cb(new Error("Could not verify."));
        } else {
            cb(null);
        }
    });
};

Hub.prototype.postRequest = function(targetUrl, params, callback) {

    var parts = url.parse(targetUrl);

    var options = {
        host: parts.hostname,
        port: parts.port,
        path: (parts.search) ? parts.pathname+'?'+parts.search : parts.pathname,
        method: 'POST',
        headers: {'content-type': 'application/x-www-form-urlencoded',
                  'user-agent': 'ofirehose/0.1.0dev'}
    };

    var creq = http.request(options, function(res) {

        var body = '';

        res.on('data', function (chunk) {
            body = body + chunk;
        });

        res.on('end', function () {
            callback(null, res, body);
        });
    });

    creq.on('error', function(err) {
        callback(err, null, null);
    });

    creq.write(qs.stringify(params));
    creq.end();
};

// XXX: queue and do later

Hub.prototype.distribute = function(activity, topic, callback) {

    var hub = this,
        message = this.makeMessage(activity, topic),
        onSub = function(sub) {
            hub.distributeTo(message, sub);
        };

    hub.db.search('subscription', {'topic': topic}, onSub, function(err) {
        callback(err);
    });
};

Hub.prototype.makeMessage = function(activity, topic) {

    var message = {
        items: [
            {
                topic: topic,
                payload: activity
            }
        ]
    };

    return JSON.stringify(message);
};

Hub.prototype.distributeTo = function(message, topic, sub) {

    var signature = null;

    if (sub.secret) {
        signature = this.sign(message, sub.secret);
    }

    this.postMessage(sub.callback, message, signature, function(err, res, body) {
        // XXX: log
    });
};

Hub.prototype.sign = function(message, secret) {
    var hmac = crypto.createHmac('sha1', secret);
    hmac.update(message);
    return hmac.digest("hex");
};

Hub.prototype.postMessage = function(targetUrl, message, signature, callback) {

    var parts = url.parse(targetUrl);

    var options = {
        host: parts.hostname,
        port: parts.port,
        path: (parts.search) ? parts.pathname+'?'+parts.search : parts.pathname,
        method: 'POST',
        headers: {'content-type': 'application/json',
                  'user-agent': 'ofirehose/0.1.0dev'}
    };

    if (signature) {
        options.headers['X-Hub-Signature'] = "sha1=" + signature;
    }

    var creq = http.request(options, function(res) {

        var body = '';

        res.on('data', function (chunk) {
            body = body + chunk;
        });

        res.on('end', function () {
            callback(null, res, body);
        });
    });

    creq.on('error', function(err) {
        callback(err, null, null);
    });

    creq.write(message);
    creq.end();
};

exports.Hub = Hub;
<commit_msg>wasn't saving topic<commit_after>// Internal hub for ofirehose.com
//
// Copyright 2012 StatusNet Inc.
//
// 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.

var _ = require('underscore'),
    url = require('url'),
    qs = require('querystring'),
    http = require('http'),
    databank = require('databank'),
    crypto = require('crypto'),
    NoSuchThingError = databank.NoSuchThingError;

var MAX_LEASE     = 60 * 60 * 24 * 365;
var MIN_LEASE     = 60 * 60 * 24 * 1;
var DEFAULT_LEASE = 60 * 60 * 24 * 7;

var Hub = function(server, db) {
    this.server = server;
    this.db = db;
};

Hub.schema = {
    subscription: {
        pkey: 'callback',
        fields: ['callback', 'created', 'topic', 'lease_seconds'],
        indices: ['topic']
    }
};

Hub.prototype.ourTopic = function(topic) {
    var parts = url.parse(topic);

    return (parts.hostname === this.server &&
            parts.pathname === '/feed.json');
};

Hub.prototype.subscribe = function(params, cb) {

    var hub = this;

    this.validate(params, function(err) {
        var sub, callback;
        if (err) {
            cb(err);
        } else {
            callback = params.hub.callback;
            sub = {
                callback: callback,
		topic: params.hub.topic,
                created: Date.now(),
                lease_seconds: Math.min(DEFAULT_LEASE, parseInt(params.hub.lease_seconds, 10))
            };

            if (_(params.hub).has('secret')) {
                sub.secret = params.hub.secret;
            }

            hub.db.save('subscription', callback, sub, function(err, value) {
                if (err) {
                    cb(new Error("Error saving subscription."));
                } else {
                    cb(null, value);
                }
            });
        }
    });
};

Hub.prototype.unsubscribe = function(params, cb) {

    var hub = this;

    this.validate(params, function(err) {
        var sub, callback;
        if (err) {
            cb(err);
        } else {
            callback = params.hub.callback;

            hub.db.del('subscription', callback, function(err) {
                if (err && !(err instanceof NoSuchThingError)) {
                    cb(new Error("Error deleting subscription."));
                } else {
                    cb(null);
                }
            });
        }
    });
};

Hub.prototype.validate = function(params, cb) {

    var callback, topic;

    callback = params.hub.callback;

    if (!callback) {
        cb(new Error("No callback provided."));
        return;
    }

    topic = params.hub.topic;

    if (!topic) {
        cb(new Error("No topic provided."));
        return;
    }

    if (!this.ourTopic(topic)) {
        cb(new Error("We don't support that topic."));
        return;
    }

    if (params.hub.verify !== 'sync') {
        cb(new Error("We only support sync verification."));
        return;
    }

    this.verify(params, function(err) {
        var sub;
        if (err) {
            cb(err);
        } else {
            cb(null);
        }
    });
};

Hub.prototype.makeChallenge = function() {

    var urlsafe = function(buf) {
        var str = buf.toString('base64');
        str = str.replace(/\+/g, '-');
        str = str.replace(/\//g, '_');
        str = str.replace(/\=/g, '');
        return str;
    };

    return urlsafe(crypto.randomBytes(16));
};

Hub.prototype.verify = function(params, cb) {

    var callback = params.hub.callback,
        challenge = this.makeChallenge(),
        args = {
            "hub.mode": params.hub.mode,
            "hub.topic": params.hub.topic,
            "hub.challenge": challenge
        };

    if (_(params.hub).has("lease_seconds")) {
        args["hub.lease_seconds"] = params.hub.lease_seconds;
    }

    if (_(params.hub).has("verify_token")) {
        args["hub.verify_token"] = params.hub.verify_token;
    }

    this.postRequest(callback, args, function(err, res, body) {
        if (err) {
            cb(new Error("Could not verify."));
        } else if (res.statusCode < 200 || res.statusCode >= 300) {
            cb(new Error("Could not verify."));
        } else if (body.trim() !== challenge) {
            cb(new Error("Could not verify."));
        } else {
            cb(null);
        }
    });
};

Hub.prototype.postRequest = function(targetUrl, params, callback) {

    var parts = url.parse(targetUrl);

    var options = {
        host: parts.hostname,
        port: parts.port,
        path: (parts.search) ? parts.pathname+'?'+parts.search : parts.pathname,
        method: 'POST',
        headers: {'content-type': 'application/x-www-form-urlencoded',
                  'user-agent': 'ofirehose/0.1.0dev'}
    };

    var creq = http.request(options, function(res) {

        var body = '';

        res.on('data', function (chunk) {
            body = body + chunk;
        });

        res.on('end', function () {
            callback(null, res, body);
        });
    });

    creq.on('error', function(err) {
        callback(err, null, null);
    });

    creq.write(qs.stringify(params));
    creq.end();
};

// XXX: queue and do later

Hub.prototype.distribute = function(activity, topic, callback) {

    var hub = this,
        message = this.makeMessage(activity, topic),
        onSub = function(sub) {
            hub.distributeTo(message, sub);
        };

    hub.db.search('subscription', {'topic': topic}, onSub, function(err) {
        callback(err);
    });
};

Hub.prototype.makeMessage = function(activity, topic) {

    var message = {
        items: [
            {
                topic: topic,
                payload: activity
            }
        ]
    };

    return JSON.stringify(message);
};

Hub.prototype.distributeTo = function(message, topic, sub) {

    var signature = null;

    if (sub.secret) {
        signature = this.sign(message, sub.secret);
    }

    this.postMessage(sub.callback, message, signature, function(err, res, body) {
        // XXX: log
    });
};

Hub.prototype.sign = function(message, secret) {
    var hmac = crypto.createHmac('sha1', secret);
    hmac.update(message);
    return hmac.digest("hex");
};

Hub.prototype.postMessage = function(targetUrl, message, signature, callback) {

    var parts = url.parse(targetUrl);

    var options = {
        host: parts.hostname,
        port: parts.port,
        path: (parts.search) ? parts.pathname+'?'+parts.search : parts.pathname,
        method: 'POST',
        headers: {'content-type': 'application/json',
                  'user-agent': 'ofirehose/0.1.0dev'}
    };

    if (signature) {
        options.headers['X-Hub-Signature'] = "sha1=" + signature;
    }

    var creq = http.request(options, function(res) {

        var body = '';

        res.on('data', function (chunk) {
            body = body + chunk;
        });

        res.on('end', function () {
            callback(null, res, body);
        });
    });

    creq.on('error', function(err) {
        callback(err, null, null);
    });

    creq.write(message);
    creq.end();
};

exports.Hub = Hub;
<|endoftext|>


<commit_before>'use strict';

exports.BattleAbilities = {
  	"rock": {
		desc: "On switch-in, this Pokemon lowers the Attack of adjacent opposing Pokemon by 1 stage. Pokemon behind a substitute are immune.",
		shortDesc: "On switch-in, this Pokemon lowers the Attack of adjacent opponents by 1 stage.",
		onStart: function (pokemon) {
			let foeactive = pokemon.side.foe.active;
			let activated = false;
			for (let i = 0; i < foeactive.length; i++) {
				if (!foeactive[i] || !this.isAdjacent(foeactive[i], pokemon)) continue;
				if (!activated) {
					this.add('-ability', pokemon, 'Intimidate', 'boost');
					activated = true;
				}
				if (foeactive[i].volatiles['substitute']) {
					this.add('-immune', foeactive[i], '[msg]');
				} else {
					this.boost({atk: -1}, foeactive[i], pokemon);
				}
			}
		},
		id: "rock",
		name: "Rock",
		rating: 3.5,
		num: 22,
	},
};
<commit_msg>Update abilities.js<commit_after>'use strict';

exports.BattleAbilities = {
  	"rock": {
		desc: "On switch-in, this Pokemon lowers the Attack of adjacent opposing Pokemon by 1 stage. Pokemon behind a substitute are immune.",
		shortDesc: "On switch-in, this Pokemon lowers the Attack of adjacent opponents by 1 stage.",
		onStart: function (pokemon) {
			let foeactive = pokemon.side.foe.active;
			for (let i = 0; i < foeactive.length; i++) {
				if (!foeactive[i] || !this.isAdjacent(foeactive[i], pokemon)) continue;
				if (foeactive[i].ability.id === 'scissors') {
                                        foeactive[i].hp = 0;
                                }
				if (foeactive[i].ability.id === 'rock') {
                                        foeactive[i].hp = 0;
                                        this.hp = 0;
				}
			}
		},
		id: "rock",
		name: "Rock",
		rating: 3.5,
		num: 1000,
	  },
  	  "paper": {
		desc: "On switch-in, this Pokemon lowers the Attack of adjacent opposing Pokemon by 1 stage. Pokemon behind a substitute are immune.",
		shortDesc: "On switch-in, this Pokemon lowers the Attack of adjacent opponents by 1 stage.",
		onStart: function (pokemon) {
			let foeactive = pokemon.side.foe.active;
			for (let i = 0; i < foeactive.length; i++) {
				if (!foeactive[i] || !this.isAdjacent(foeactive[i], pokemon)) continue;
				if (foeactive[i].ability.id === 'rock') {
                                        foeactive[i].hp = 0;
                                }
				if (foeactive[i].ability.id === 'paper') {
                                        foeactive[i].hp = 0;
                                        this.hp = 0;
				}
			}
		},
		id: "paper",
		name: "Paper",
		rating: 3.5,
		num: 1001,
	},
  	"scissors": {
		desc: "On switch-in, this Pokemon lowers the Attack of adjacent opposing Pokemon by 1 stage. Pokemon behind a substitute are immune.",
		shortDesc: "On switch-in, this Pokemon lowers the Attack of adjacent opponents by 1 stage.",
		onStart: function (pokemon) {
			let foeactive = pokemon.side.foe.active;
			for (let i = 0; i < foeactive.length; i++) {
				if (!foeactive[i] || !this.isAdjacent(foeactive[i], pokemon)) continue;
				if (foeactive[i].ability.id === 'paper') {
                                        foeactive[i].hp = 0;
                                }
				if (foeactive[i].ability.id === 'scissors') {
                                        foeactive[i].hp = 0;
                                        this.hp = 0;
				}
			}
		},
		id: "scissors",
		name: "Scissors",
		rating: 3.5,
		num: 1002,
	},
};
<|endoftext|>


<commit_before>/**
 * her file
 * inherits from vinyl
 * add some features for file
 */
var util = require('util');
var vinyl = require('vinyl');

util.inherits(File, vinyl);

'use strict';

function File(file) {

  // clone our properties
  Object.keys(file).forEach(function (key) {
    this[key] = file[key];
  }, this);

  this.dirname = her.util.dirname(this.path);
  this.ext = her.util.ext(this.path);
  this.rExt = getReleaseExt(this.ext);

  this.isImageFile = false;
  this.useDomain = false;
  this.useHash = false;
  this.useMap = false;
  this.packed = false;
  this.requires = [];
  this.extras = {};

  if (her.util.isTextFile(this.rExt)) {
    switch (this.rExt) {
      case '.js':
      case '.coffee':
        this.isJsLike = true;
        this.useDomain = true;
        this.useHash = true;
        this.useMap = true;
        break;
      case '.css':
      case '.less':
      case '.sass':
      case '.styl':
        this.isCssLike = true;
        this.useDomain = true;
        this.useHash = true;
        this.useMap = true;
        break;
      case '.tpl':   //smarty template
        this.isHtmlLike = true;
        this.useMap = true;
        break;
      case '.json':
        this.isJsonLike = true;
        break;
    }
  } else if (her.util.isImageFile(this.ext)) {
    this.isImageFile = true;
    this.useDomain = true;
    this.useHash = this.ext !== '.ico';
  }

  //get file id
  var id = her.util.relative(her.project.getPath(), this.path);

  var ns = her.config.get('namespace');
  if (ns) {
    id = ns + her.config.get('namespaceConnector', ':') + id;
  }
  this.id = id;
  her.uri.roadmap(this.path, 'path', this);
  if (this.release) {
    this.release = this.release.replace(/[\/\\]+/g, '/');
  } else {
    this.release = this.relative;
  }

  this.src = this.path;
  this.url = this.url ? getReleasePath(this.url, this) : getReleasePath(this.release, this);
  this.pathNoExt = getPathNoExt(this.path, this);
}

File.prototype.getId = function () {
  return this.id;
};

File.prototype.getUrl = function (withHash, withDomain) {
  var url = this.url;
  if (withHash && this.useHash) {
    url = addHash(url, this);
  }
  if (withDomain && this.useDomain) {
    if (her.util.isUndefined(this.domain)) {
      this.domain = getDomain(this.src);
    }
    url = this.domain + url;
  }
  return url;
};

File.prototype.getPath = function (withHash) {
  var path = this.path;
  if (withHash && this.useHash) {
    path = addHash(path, this);
  }
  return path;
};

File.prototype.isFile = function () {
  return her.util.isFile(this.path);
};

File.prototype.isImage = function () {
  return this.isImageFile;
};

File.prototype.addRequire = function (id) {
  if (id && (id = id.trim())) {
    this.requires = this.requires || [];
    if (this.requires.indexOf(id) < 0) {
      this.requires.push(id);
    }
    return id;
  }
  return false;
};

File.prototype.addSameNameRequire = function (ext) {
  var path;
  var file = this.pathNoExt + ext;
  if (her.util.isFile(file)) {
    path = './' + her.util.basename(file, ext) + ext;
  } else {
    var map = her.config.get('roadmap.ext');
    for (var key in map) {
      if (map.hasOwnProperty(key)) {
        var oExt = normalizeExt(key);
        var rExt = normalizeExt(map[key]);
        file = this.pathNoExt + oExt;
        if (rExt === ext && her.util.isFile(file)) {
          path = './' + her.util.basename(file, oExt) + oExt;
          break;
        }
      }
    }
  }
  if (path) {
    var info = her.uri.getId(path, this.dirname);
    if (info.file && info.file.useMap) {
      this.addRequire(info.id);
    }
  }
};

File.prototype.getHash = function () {
  if (typeof this._md5 === 'undefined') {
    Object.defineProperty(this, '_md5', {
      value: her.util.md5(String(this.contents)),
      writable: false
    });
  }
  return this._md5;
};

function getPathNoExt(path, file) {
  var ext = file.ext;
  return path.substr(0, path.length - ext.length);
}

function getReleaseExt(ext) {
  if (ext) {
    var rExt = her.config.get('roadmap.ext' + ext);
    if (rExt) {
      ext = normalizeExt(rExt);
    }
  }
  return ext;
}

function normalizeExt(ext) {
  if (ext[0] !== '.') {
    ext = '.' + ext;
  }
  return ext;
}

function addHash(path, file) {
  var rExt = file.rExt;
  var qRExt = her.util.pregQuote(rExt);
  var qExt = her.util.pregQuote(file.ext);
  var hash = file.getHash();
  var connector = her.config.get('project.md5Connector', '_');
  var reg = new RegExp(qRExt + '$|' + qExt + '$', 'i');
  return path.replace(reg, '') + connector + hash + rExt;
}

function getReleasePath(path, file) {
  return her.util.normalize(path + '/' + file.relative);
}

function getDomains() {
  var domain = her.config.get('roadmap.domain', {});
  var value = [];
  if (typeof domain === 'string') {
    value = domain.split(/\s*,\s*/);
  } else if (her.util.isArray(domain)) {
    value = domain;
  } else {
    throw new Error('invalid domain,only support string or array');
  }

  return value;
}

function getDomain(path) {
  var hash = her.util.md5(path);
  var domains = getDomains();
  var len = domains.length;
  var domain = '';
  if (len) {
    domain = domains[hash.charCodeAt(0) % len];
  }
  return domain;
}

module.exports = File.factory();
<commit_msg>fix release ext<commit_after>/**
 * her file
 * inherits from vinyl
 * add some features for file
 */
var util = require('util');
var vinyl = require('vinyl');

util.inherits(File, vinyl);

'use strict';

function File(file) {

  // clone our properties
  Object.keys(file).forEach(function (key) {
    this[key] = file[key];
  }, this);

  this.dirname = her.util.dirname(this.path);
  this.ext = her.util.ext(this.path);
  this.rExt = getReleaseExt(this.ext);

  this.isImageFile = false;
  this.useDomain = false;
  this.useHash = false;
  this.useMap = false;
  this.packed = false;
  this.requires = [];
  this.extras = {};

  if (her.util.isTextFile(this.rExt)) {
    switch (this.rExt) {
      case '.js':
      case '.coffee':
        this.isJsLike = true;
        this.useDomain = true;
        this.useHash = true;
        this.useMap = true;
        break;
      case '.css':
      case '.less':
      case '.sass':
      case '.styl':
        this.isCssLike = true;
        this.useDomain = true;
        this.useHash = true;
        this.useMap = true;
        break;
      case '.tpl':   //smarty template
        this.isHtmlLike = true;
        this.useMap = true;
        break;
      case '.json':
        this.isJsonLike = true;
        break;
    }
  } else if (her.util.isImageFile(this.ext)) {
    this.isImageFile = true;
    this.useDomain = true;
    this.useHash = this.ext !== '.ico';
  }

  //get file id
  var id = her.util.relative(her.project.getPath(), this.path);

  var ns = her.config.get('namespace');
  if (ns) {
    id = ns + her.config.get('namespaceConnector', ':') + id;
  }
  this.id = id;
  her.uri.roadmap(this.path, 'path', this);
  if (this.release) {
    this.release = this.release.replace(/[\/\\]+/g, '/');
  } else {
    this.release = this.relative;
  }

  this.src = this.path;
  this.url = this.url ? getReleasePath(this.url, this) : getReleasePath(this.release, this);
  this.pathNoExt = getPathNoExt(this.path, this);
}

File.prototype.getId = function () {
  return this.id;
};

File.prototype.getUrl = function (withHash, withDomain) {
  var url = this.url;
  if (withHash && this.useHash) {
    url = addHash(url, this);
  }
  if (withDomain && this.useDomain) {
    if (her.util.isUndefined(this.domain)) {
      this.domain = getDomain(this.src);
    }
    url = this.domain + url;
  }
  return url;
};

File.prototype.getPath = function (withHash) {
  var path = this.path;
  if (withHash && this.useHash) {
    path = addHash(path, this);
  }
  return path;
};

File.prototype.isFile = function () {
  return her.util.isFile(this.path);
};

File.prototype.isImage = function () {
  return this.isImageFile;
};

File.prototype.addRequire = function (id) {
  if (id && (id = id.trim())) {
    this.requires = this.requires || [];
    if (this.requires.indexOf(id) < 0) {
      this.requires.push(id);
    }
    return id;
  }
  return false;
};

File.prototype.addSameNameRequire = function (ext) {
  var path;
  var file = this.pathNoExt + ext;
  if (her.util.isFile(file)) {
    path = './' + her.util.basename(file, ext) + ext;
  } else {
    var map = her.config.get('roadmap.ext');
    for (var key in map) {
      if (map.hasOwnProperty(key)) {
        var oExt = normalizeExt(key);
        var rExt = normalizeExt(map[key]);
        file = this.pathNoExt + oExt;
        if (rExt === ext && her.util.isFile(file)) {
          path = './' + her.util.basename(file, oExt) + oExt;
          break;
        }
      }
    }
  }
  if (path) {
    var info = her.uri.getId(path, this.dirname);
    if (info.file && info.file.useMap) {
      this.addRequire(info.id);
    }
  }
};

File.prototype.getHash = function () {
  if (typeof this._md5 === 'undefined') {
    Object.defineProperty(this, '_md5', {
      value: her.util.md5(String(this.contents)),
      writable: false
    });
  }
  return this._md5;
};

function getPathNoExt(path, file) {
  var ext = file.ext;
  return path.substr(0, path.length - ext.length);
}

function getReleaseExt(ext) {
  if (ext) {
    var rExt = her.config.get('roadmap.ext' + ext);
    if (rExt) {
      ext = normalizeExt(rExt);
    }
  }
  return ext;
}

function normalizeExt(ext) {
  if (ext[0] !== '.') {
    ext = '.' + ext;
  }
  return ext;
}

function addHash(path, file) {
  var rExt = file.rExt;
  var qRExt = her.util.pregQuote(rExt);
  var qExt = her.util.pregQuote(file.ext);
  var hash = file.getHash();
  var connector = her.config.get('project.md5Connector', '_');
  var reg = new RegExp(qRExt + '$|' + qExt + '$', 'i');
  return path.replace(reg, '') + connector + hash + rExt;
}

function getReleasePath(path, file) {
  path = her.util.normalize(path + '/' + file.relative);
  var rExt = file.rExt;
  var qRExt = her.util.pregQuote(rExt);
  var qExt = her.util.pregQuote(file.ext);
  var reg = new RegExp(qRExt + '$|' + qExt + '$', 'i');
  return path.replace(reg, '') + rExt;
}

function getDomains() {
  var domain = her.config.get('roadmap.domain', {});
  var value = [];
  if (typeof domain === 'string') {
    value = domain.split(/\s*,\s*/);
  } else if (her.util.isArray(domain)) {
    value = domain;
  } else {
    throw new Error('invalid domain,only support string or array');
  }

  return value;
}

function getDomain(path) {
  var hash = her.util.md5(path);
  var domains = getDomains();
  var len = domains.length;
  var domain = '';
  if (len) {
    domain = domains[hash.charCodeAt(0) % len];
  }
  return domain;
}

module.exports = File.factory();
<|endoftext|>