<commit_before>import React from 'react';
import { convertFromHTML, convertToHTML } from 'draft-convert';
import { Utils } from 'meteor/vulcan:core';
import linkifyIt from 'linkify-it'
const linkify = linkifyIt()

// Convert text to html with links converted to hyperlinks, matches
// linkify-plugin's linkStrategy.
//
// Export for testing
export function autolink(text) {
  const matches = linkify.match(text)
  if (!matches) return `<p>${text}</p>`
  let resultPieces = ['
']
  let lastLinkEndIndex = 0
  for (const match of matches) {
    resultPieces.push(text.substring(lastLinkEndIndex, match.index))
    resultPieces.push(`<a href="${match.url}">${match.text}</a>`)
    lastLinkEndIndex = match.lastIndex
  }
  resultPieces.push(`${text.substring(lastLinkEndIndex, text.length)}</p>`)
  return resultPieces.join('')
}

// This currently only supports our limited subset of semVer
export function extractVersionsFromSemver(semver) {
  semver = semver || "1.0.0"
  const [major, minor, patch] = semver.split(".").map((n) => parseInt(n, 10))
  return { major, minor, patch }
}

export const htmlToDraft = convertFromHTML({
  htmlToEntity: (nodeName, node, createEntity) => {
    if (nodeName === 'img') {
      return createEntity(
        'IMAGE',
        'IMMUTABLE',
        {src: node.src}
      )
    }
    if (nodeName === 'a') {
      return createEntity(
        'LINK',
        'MUTABLE',
        {url: node.href}
      )
    }
    // if (nodeName === 'img') {
    //   console.log("image detected: ", node, node.src)
    //   return createEntity(
    //     'IMAGE',
    //     'IMMUTABLE',
    //     {src: node.src}
    //   )
    // }
  },
  htmlToBlock: (nodeName, node, lastList, inBlock) => {
    if ((nodeName === 'figure' && node.firstChild.nodeName === 'IMG') || (nodeName === 'img' && inBlock !== 'atomic')) {
        return 'atomic';
    }
    // if (nodeName === 'blockquote') {
    //   return {
    //     type: 'blockquote',
    //     data: {}
    //   };
    // }
    if (nodeName === 'hr') { // This currently appears to be broken, sadly. TODO: Fix this
      return {
        type: 'divider',
        data: {},
        text: 'as',
        depth: 0,
        inlineStyleRanges: [ { offset: 0, length: 2, style: 'ITALIC' } ],
      }
    }
  }
})

export const draftToHTML = convertToHTML({
  //eslint-disable-next-line react/display-name
  styleToHTML: (style) => {
    if (style === 'STRIKETHROUGH') {
      return ;
    }
  },
  entityToHTML: (entity, originalText) => {
    if (entity.type === 'image' || entity.type === 'IMAGE') {
      let classNames = 'draft-image '
      if (entity.data.alignment) {
        classNames = classNames + entity.data.alignment;
      }
      let style = "width:" + (entity.data.width || 40) + "%"
      return `<figure><img src="${entity.data.src}" class="${classNames}" style="${style}" /></figure>`;
    }
    if (entity.type === 'LINK') {
      return {
        start: `<a href="${entity.data.url || entity.data.href}">`,
        end: '',
      };
    }
    if (entity.type === 'IMG') {
      const className = 'draft-inline-image';
      return `<img src="${entity.data.src}" class="${className}" alt="${entity.data.alt}"/>`
    }
    if (entity.type === 'INLINETEX') {
      if (entity.data.html) {
        return `${entity.data.css ? `` : ""}${entity.data.html}`
      } else {
        return `<span class="draft-latex-placeholder"> < refresh to render LaTeX > </span>`
      }
    }
    return originalText;
  },
  //eslint-disable-next-line react/display-name
  blockToHTML: (block) => {
    const type = block.type;

    const linkable = [
      'ordered-list-item',
      'unordered-list-item',
      'paragraph',
      'unstyled'
    ]
    if (linkable.includes(type)) {
      return autolink(block.text)
    }
    if (type === 'atomic') {
      if (block.data && block.data.mathjax && block.data.html) {
        return `
${block.data.css ? `` : ""}${block.data.html}
`
      } else if (block.data && block.data.mathjax) {
        return `<div class="draft-latex-placeholder-block"> <refresh to render LaTeX> </div>`
      } else {
        return {start: '', end: ''};
      }
    }
    if (type === 'blockquote') {
      return

    }
    if (type === 'code-block') {
      return {start: '
', end: '
'};
    }
    if (type === 'divider') {
     return

    }
    if (type === 'spoiler') {
     return

    }
    //  return ;
   },
});

Utils.draftToHTML = draftToHTML;
Utils.htmlToDraft = htmlToDraft;
<commit_msg>Deactivate autolink<commit_after>import React from 'react';
import { convertFromHTML, convertToHTML } from 'draft-convert';
import { Utils } from 'meteor/vulcan:core';
import linkifyIt from 'linkify-it'
const linkify = linkifyIt()

// Convert text to html with links converted to hyperlinks, matches
// linkify-plugin's linkStrategy.
//
// Export for testing
export function autolink(text) {
  const matches = linkify.match(text)
  if (!matches) return `<p>${text}</p>`
  let resultPieces = ['
']
  let lastLinkEndIndex = 0
  for (const match of matches) {
    resultPieces.push(text.substring(lastLinkEndIndex, match.index))
    resultPieces.push(`<a href="${match.url}">${match.text}</a>`)
    lastLinkEndIndex = match.lastIndex
  }
  resultPieces.push(`${text.substring(lastLinkEndIndex, text.length)}</p>`)
  return resultPieces.join('')
}

// This currently only supports our limited subset of semVer
export function extractVersionsFromSemver(semver) {
  semver = semver || "1.0.0"
  const [major, minor, patch] = semver.split(".").map((n) => parseInt(n, 10))
  return { major, minor, patch }
}

export const htmlToDraft = convertFromHTML({
  htmlToEntity: (nodeName, node, createEntity) => {
    if (nodeName === 'img') {
      return createEntity(
        'IMAGE',
        'IMMUTABLE',
        {src: node.src}
      )
    }
    if (nodeName === 'a') {
      return createEntity(
        'LINK',
        'MUTABLE',
        {url: node.href}
      )
    }
    // if (nodeName === 'img') {
    //   console.log("image detected: ", node, node.src)
    //   return createEntity(
    //     'IMAGE',
    //     'IMMUTABLE',
    //     {src: node.src}
    //   )
    // }
  },
  htmlToBlock: (nodeName, node, lastList, inBlock) => {
    if ((nodeName === 'figure' && node.firstChild.nodeName === 'IMG') || (nodeName === 'img' && inBlock !== 'atomic')) {
        return 'atomic';
    }
    // if (nodeName === 'blockquote') {
    //   return {
    //     type: 'blockquote',
    //     data: {}
    //   };
    // }
    if (nodeName === 'hr') { // This currently appears to be broken, sadly. TODO: Fix this
      return {
        type: 'divider',
        data: {},
        text: 'as',
        depth: 0,
        inlineStyleRanges: [ { offset: 0, length: 2, style: 'ITALIC' } ],
      }
    }
  }
})

export const draftToHTML = convertToHTML({
  //eslint-disable-next-line react/display-name
  styleToHTML: (style) => {
    if (style === 'STRIKETHROUGH') {
      return ;
    }
  },
  entityToHTML: (entity, originalText) => {
    if (entity.type === 'image' || entity.type === 'IMAGE') {
      let classNames = 'draft-image '
      if (entity.data.alignment) {
        classNames = classNames + entity.data.alignment;
      }
      let style = "width:" + (entity.data.width || 40) + "%"
      return `<figure><img src="${entity.data.src}" class="${classNames}" style="${style}" /></figure>`;
    }
    if (entity.type === 'LINK') {
      return {
        start: `<a href="${entity.data.url || entity.data.href}">`,
        end: '',
      };
    }
    if (entity.type === 'IMG') {
      const className = 'draft-inline-image';
      return `<img src="${entity.data.src}" class="${className}" alt="${entity.data.alt}"/>`
    }
    if (entity.type === 'INLINETEX') {
      if (entity.data.html) {
        return `${entity.data.css ? `` : ""}${entity.data.html}`
      } else {
        return `<span class="draft-latex-placeholder"> < refresh to render LaTeX > </span>`
      }
    }
    return originalText;
  },
  //eslint-disable-next-line react/display-name
  blockToHTML: (block) => {
    const type = block.type;

    const linkable = [
      'ordered-list-item',
      'unordered-list-item',
      'paragraph',
      'unstyled'
    ]
    // if (linkable.includes(type)) {
    //   return autolink(block.text)
    // }
    if (type === 'atomic') {
      if (block.data && block.data.mathjax && block.data.html) {
        return `
${block.data.css ? `` : ""}${block.data.html}
`
      } else if (block.data && block.data.mathjax) {
        return `<div class="draft-latex-placeholder-block"> <refresh to render LaTeX> </div>`
      } else {
        return {start: '', end: ''};
      }
    }
    if (type === 'blockquote') {
      return

    }
    if (type === 'code-block') {
      return {start: '
', end: '
'};
    }
    if (type === 'divider') {
     return

    }
    if (type === 'spoiler') {
     return

    }
    //  return ;
   },
});

Utils.draftToHTML = draftToHTML;
Utils.htmlToDraft = htmlToDraft;
<|endoftext|>


<commit_before>'use strict';

chrome.runtime.onUpdateAvailable.addListener(function onUpdateAvailable() {
  chrome.storage.local.set({
    'updateReady': true
  });
});

chrome.app.runtime.onLaunched.addListener(function onLaunched() {
  chrome.storage.local.get('updateReady', function loadGet(o) {
    if (o.updateReady) {
      chrome.runtime.reload();
      return;
    }

    chrome.app.window.create('index.html', {
      id: 'fimfiction:main',
      frame: 'none',
      innerBounds: {
        width: 1280,
        height: 800,
        minWidth: 375,
        minHeight: 627
      }
    });
  });
});

chrome.runtime.onInstalled.addListener(function onInstalled() {
  chrome.storage.local.set({
    'updateReady': false
  });
});
<commit_msg>prevent potentional hang on update<commit_after>'use strict';

chrome.runtime.onUpdateAvailable.addListener(function onUpdateAvailable() {
  chrome.storage.local.set({
    'updateReady': Date.now()
  });
});

chrome.app.runtime.onLaunched.addListener(function onLaunched() {
  var hang = setTimeout(function preventHang() {
    hang = null;
    chrome.runtime.reload();
  }, 5000);
  chrome.storage.local.get('updateReady', function loadGet(o) {
    if (hang) {
      clearTimeout(hang);
      hang = null;
    }
    if (o.updateReady && (o.updateReady + 86400000) >= Date.now()) {
      chrome.runtime.reload();
      return;
    }

    chrome.app.window.create('index.html', {
      id: 'fimfiction:main',
      frame: 'none',
      innerBounds: {
        width: 1280,
        height: 800,
        minWidth: 375,
        minHeight: 627
      }
    });
  });
});

chrome.runtime.onInstalled.addListener(function onInstalled() {
  chrome.storage.local.set({
    'updateReady': 0
  });
});
<|endoftext|>


<commit_before>import {curry, find, contains, all} from 'lodash'

let isRequiredCourse = curry(function(requiredCourses, checkAgainst) {
	// Takes in a list of required course info, as objects that only have the
	// info needed to match.

	// Find if the current course exists in requiredCourses
	let matchedCourse = find(requiredCourses, {deptnum: checkAgainst.deptnum})

	// Begin the array of results!
	let results = [matchedCourse ? true : false]

	if (!matchedCourse) {
		return false
	}

	if (matchedCourse.name) {
		results.push(contains(checkAgainst.name, matchedCourse.name))
	}

	if (matchedCourse.title) {
		results.push(contains(checkAgainst.title, matchedCourse.title))
	}

	/*console.log('isRequiredCourse',
		'requiredCourses', requiredCourses,
		'checkAgainst', checkAgainst,
		'match', matchedCourse,
		'results', results,
		'result', all(results))*/

	// Only return true if *all* of the checks returned true.
	return all(results)
})

export default isRequiredCourse
<commit_msg>Add JSDoc to isRequiredCourse<commit_after>import {curry, find, contains, all} from 'lodash'

/**
 * Takes a list of required courses and checks if a course matches.
 *
 * @param {Array<Course>} requiredCourses - the required courses.
 * @param {Course} checkAgainst - the course to check.
 * @returns {Boolean} - is the course in the list.
 */
let isRequiredCourse = curry(function(requiredCourses, checkAgainst) {
	// Takes in a list of required course info, as objects that only have the
	// info needed to match.

	// Find if the current course exists in requiredCourses
	let matchedCourse = find(requiredCourses, {deptnum: checkAgainst.deptnum})

	// Begin the array of results!
	let results = [matchedCourse ? true : false]

	if (!matchedCourse) {
		return false
	}

	if (matchedCourse.name) {
		results.push(contains(checkAgainst.name, matchedCourse.name))
	}

	if (matchedCourse.title) {
		results.push(contains(checkAgainst.title, matchedCourse.title))
	}

	/*console.log('isRequiredCourse',
		'requiredCourses', requiredCourses,
		'checkAgainst', checkAgainst,
		'match', matchedCourse,
		'results', results,
		'result', all(results))*/

	// Only return true if *all* of the checks returned true.
	return all(results)
})

export default isRequiredCourse
<|endoftext|>


<commit_before>/**
 * @module  web
 * methods for browsers and business requirement

 * - domready
 * - data storage
 * - getLocation
 * - UA
 */

;(function(K, undef){

// --- method for KM.data ----------------------- *\
function setData(data){
	data && K.mix(stored_data, data);
};

function cloneData(){
	return K.merge({}, stored_data);
};

function getData(name){
	return stored_data[name];
};
// --- /method for KM.data ----------------------- *\

/**
 * get the readable location object of current page
 */
function getCurrentLocation(){
	var L, H;

	// IE may throw an exception when accessing
	// a field from document.location if document.domain has been set
	// ref:
	// stackoverflow.com/questions/1498788/reading-window-location-after-setting-document-domain-in-ie6
	try {
		L = DOC.location;
		H = L.href;
	} catch(e) {

		// Use the href attribute of an A element
		// since IE will modify it given document.location
		H = DOC.createElement('a');
		H.href = EMPTY;
		H = H.href;

		L = parseLocation(H);
	}

	return L;
};

/**
 * parse a link to location object
 * @param {string} H
 * @return {Object} custom location object
 */
function parseLocation(H){
	var E = nullOrEmpty, port;

	H = H.match(REGEX_URL);

	port = H[3];

	return {
		href: 		H[0],
		protocol:	H[1],
		host:		H[2] + (port ? ':' + port : EMPTY),
		hostname:	H[2],
		port:		E( port ),
		pathname:	E( H[4] ),
		search:		E( H[5] ),
		hash:		E( H[6] )
	};
};

function nullOrEmpty(str){
	return str || EMPTY;
};

	// @type {Object}
var stored_data = {},

    is_domready = false,
	is_domready_binded = false,
    is_loaded = false,

    readyList = [],

	// @const
	WIN = K.__HOST,
	DOC = WIN.document,
	EMPTY = '',
	_Browser = Browser,
	ua = navigator.userAgent,
	UA = K.UA,
	REGEX_WEBKIT = /webkit[ \/]([^\s]+)/i,

    /*
    REGEX_URL = /^
    	([\w\+\.\-]+:)		// protocol
    	\/\/
    	([^\/?#:]+)			// domain
    (?::
    	(\d+)				// port
    )?
    	(/[^?#]*)?			// pathname
    	(\?[^?#]*)?			// search
    	(#.*)?				// hash
    $/
    */

    REGEX_URL = /^([\w\+\.\-]+:)\/\/([^\/?#:]+)(?::(\d+))?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/;

K.mix(K, {

	/**
	 * module  data

	 * setter
	 * getter

	 * usage:
	 * 1. KM.data()                     returns the shadow copy of the current data stack
	 * 2. KM.data('abc')                returns the data named 'abc'
	 * 3. KM.data('abc', 123)           set the value of 'abc' as 123
	 * 4. KM.data({abc: 123, edf:456})  batch setter
	 *
	 * @param {all=} value
	 */
	data: function(name, value){
		var type = K._type(name),
			empty_obj = {},
			is_getter, ret;

		if(name === undef){
			ret = cloneData(); // get: return shadow copy
			is_getter = true;

		}else if(type === 'string'){
			if(value === undef){
				ret = getData(name); // get: return the value of name
				is_getter = true;
			}else{
				empty_obj[name] = value;
				setData(empty_obj) // set
			}

		}else if(type === 'object'){
			setData(name); // set
		}

		return is_getter ? ret : K;
	},

	isDomReady: function(){
		return is_domready;
	},

	isLoaded: function(){
		return is_loaded;
	},

	/**
	 * the entire entry for domready event
	 * window.addEvent('domready', fn) has been carried here, and has no more support
	 * @param {function()} fn the function to be executed when dom is ready
	 */
	ready: function(fn){
		// delay the initialization of binding domready, making page render faster
		is_domready_binded || bind_domready();

		if(is_domready){
			fn.call(WIN, this);
		}else{
			readyList.push(fn);
		}
	},

	/**
	 * @param {string} href
	 * @return {Object}
	 *	- if href undefined, returns the current location
	 *	- if href is a string, returns the parsed loaction
	 */
	getLocation: function(href){
		return href ?
			parseLocation(href)
		:	getCurrentLocation();
	}

});

/**
 * user agent

 type {number} KM.UA.<browser> major version of current browser, default to undefined
 - KM.UA.ie
 - KM.UA.firefox
 - KM.UA.opera
 - KM.UA.chrome
 - KM.UA.webkit

 usage:

 	if(KM.UA.ie < 8){...}

 	// always instead of using this:
 	if(Browser.ie && Browser.version < 8){...}

 */
['ie', 'firefox', 'opera', 'chrome'].each(function(name){
	var B = _Browser;

	B[name] && (UA[name] = B.version);
});

K.mix(UA, _Browser.Platform, true, ['mac', 'win', 'linux', 'ios', 'android', 'webos', 'other']);
UA.platform = _Browser.Platform.name;

// UA.fullversion = String(_Browser.version);

(function(UA){
	var m = ua.match(REGEX_WEBKIT);

	if(m){
		UA.webkit = parseInt( m[1] );
	}
})(K.UA);

/**
 * load event
 */
WIN.addEvent('load', function(){
	is_loaded = true;
});

/**
 * fix bugs of mootools
 * ----------------------------------------------------------------------------------------------------------- */

// in mootools 1.3.2, on ie < 8, document.body and html must be 'dollar'ed before the using of most of the methods, esp in Element.dimension
$(document.body);
$(document.documentElement);

/**
 * Custom domready event
 * @private
 * ----------------------------------------------------------------------------------------------------------- */

function domready(){

	// fire domready only once
	if(!is_domready){
		is_domready = true;
		fire_domready();
	}
}

function fire_domready(){
	var self = this,
		r = readyList, fn;

	if(r){
		for(var i = 0, len = r.length; i < len; i ++){
			fn = r[i]
			fn && fn.call(WIN, K);
		}

		r.length = 0;
		readyList = null;
	}
};

function bind_domready(){
	var COMPLETE = 'complete', doc = DOC,
		doScroll = doc.documentElement.doScroll,
		eventType = doScroll ? 'readystatechange' : 'DOMContentLoaded';

	is_domready_binded = true;

	// Catch cases where ready() is called after the
	// browser event has already occurred.
	if(doc.readyState === COMPLETE) return domready();

	function ready(){
		doc.removeListener(eventType, ready).removeListener('load', ready)
		domready();
	}

	doc.addListener(eventType, ready);

	// A fallback to load
	// and make sure that domready event fires before load event registered by user
	doc.addListener('load', ready);

	if(doScroll){
		var not_framed = false;

		try {
			not_framed = win.frameElement == null;
		} catch(e) {}

		if(not_framed){

			// use conditional function declaration, poll_scroll will not be declared in firefox, that it will save memory
			function poll_scroll(){
				try {
					// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
					doScroll('left');
					ready();
				} catch(ex) {
					setTimeout(poll_scroll, 10);
				}
			}
			poll_scroll();
		}
	}
}

})(KM);

/**
 TODO:
 - add more user-agent datas.

 change log:
 2011-07-05  Kael:
 - remove UA.fullversion
 - add adapter for Browser.Platform

 2011-06-12  Kael:
 - fix a bug about the regular expression of location pattern that more than one question mark should be allowed in search query
 - add UA.chrome.

 2011-04-26  Kael:
 - adapt mootools.Browser
 - remove ua.chrome, ua.safari, add ua.webkit

 2011-04-12  Kael Zhang:
 - fix a bug that domready could not be properly fired
 - add KM.getLocation method,
 	1. to fix the bug of ie6, which will cause an exception when fetching the value of window.location if document.domain is already specified
 	2. which can split an specified uri into different parts

 2010-12-31  Kael Zhang:
 - migrate domready event out from mootools to here, and change some way
 - migrate .data and .delay methods from core/lang to here

 */<commit_msg>fix the bug of mootools on ie < 8 that document.body and html don't support most of the method, because they haven't dollared yet<commit_after>/**
 * @module  web
 * methods for browsers and business requirement

 * - domready
 * - data storage
 * - getLocation
 * - UA
 */

;(function(K, undef){

// --- method for KM.data ----------------------- *\
function setData(data){
	data && K.mix(stored_data, data);
};

function cloneData(){
	return K.merge({}, stored_data);
};

function getData(name){
	return stored_data[name];
};
// --- /method for KM.data ----------------------- *\

/**
 * get the readable location object of current page
 */
function getCurrentLocation(){
	var L, H;

	// IE may throw an exception when accessing
	// a field from document.location if document.domain has been set
	// ref:
	// stackoverflow.com/questions/1498788/reading-window-location-after-setting-document-domain-in-ie6
	try {
		L = DOC.location;
		H = L.href;
	} catch(e) {

		// Use the href attribute of an A element
		// since IE will modify it given document.location
		H = DOC.createElement('a');
		H.href = EMPTY;
		H = H.href;

		L = parseLocation(H);
	}

	return L;
};

/**
 * parse a link to location object
 * @param {string} H
 * @return {Object} custom location object
 */
function parseLocation(H){
	var E = nullOrEmpty, port;

	H = H.match(REGEX_URL);

	port = H[3];

	return {
		href: 		H[0],
		protocol:	H[1],
		host:		H[2] + (port ? ':' + port : EMPTY),
		hostname:	H[2],
		port:		E( port ),
		pathname:	E( H[4] ),
		search:		E( H[5] ),
		hash:		E( H[6] )
	};
};

function nullOrEmpty(str){
	return str || EMPTY;
};

	// @type {Object}
var stored_data = {},

    is_domready = false,
	is_domready_binded = false,
    is_loaded = false,

    readyList = [],

	// @const
	WIN = K.__HOST,
	DOC = WIN.document,
	EMPTY = '',
	_Browser = Browser,
	ua = navigator.userAgent,
	UA = K.UA,
	REGEX_WEBKIT = /webkit[ \/]([^\s]+)/i,

    /*
    REGEX_URL = /^
    	([\w\+\.\-]+:)		// protocol
    	\/\/
    	([^\/?#:]+)			// domain
    (?::
    	(\d+)				// port
    )?
    	(/[^?#]*)?			// pathname
    	(\?[^?#]*)?			// search
    	(#.*)?				// hash
    $/
    */

    REGEX_URL = /^([\w\+\.\-]+:)\/\/([^\/?#:]+)(?::(\d+))?(\/[^?#]*)?(\?[^#]*)?(#.*)?$/;

K.mix(K, {

	/**
	 * module  data

	 * setter
	 * getter

	 * usage:
	 * 1. KM.data()                     returns the shadow copy of the current data stack
	 * 2. KM.data('abc')                returns the data named 'abc'
	 * 3. KM.data('abc', 123)           set the value of 'abc' as 123
	 * 4. KM.data({abc: 123, edf:456})  batch setter
	 *
	 * @param {all=} value
	 */
	data: function(name, value){
		var type = K._type(name),
			empty_obj = {},
			is_getter, ret;

		if(name === undef){
			ret = cloneData(); // get: return shadow copy
			is_getter = true;

		}else if(type === 'string'){
			if(value === undef){
				ret = getData(name); // get: return the value of name
				is_getter = true;
			}else{
				empty_obj[name] = value;
				setData(empty_obj) // set
			}

		}else if(type === 'object'){
			setData(name); // set
		}

		return is_getter ? ret : K;
	},

	isDomReady: function(){
		return is_domready;
	},

	isLoaded: function(){
		return is_loaded;
	},

	/**
	 * the entire entry for domready event
	 * window.addEvent('domready', fn) has been carried here, and has no more support
	 * @param {function()} fn the function to be executed when dom is ready
	 */
	ready: function(fn){
		// delay the initialization of binding domready, making page render faster
		is_domready_binded || bind_domready();

		if(is_domready){
			fn.call(WIN, this);
		}else{
			readyList.push(fn);
		}
	},

	/**
	 * @param {string} href
	 * @return {Object}
	 *	- if href undefined, returns the current location
	 *	- if href is a string, returns the parsed loaction
	 */
	getLocation: function(href){
		return href ?
			parseLocation(href)
		:	getCurrentLocation();
	}

});

/**
 * user agent

 type {number} KM.UA.<browser> major version of current browser, default to undefined
 - KM.UA.ie
 - KM.UA.firefox
 - KM.UA.opera
 - KM.UA.chrome
 - KM.UA.webkit

 usage:

 	if(KM.UA.ie < 8){...}

 	// always instead of using this:
 	if(Browser.ie && Browser.version < 8){...}

 */
['ie', 'firefox', 'opera', 'chrome'].each(function(name){
	var B = _Browser;

	B[name] && (UA[name] = B.version);
});

K.mix(UA, _Browser.Platform, true, ['mac', 'win', 'linux', 'ios', 'android', 'webos', 'other']);
UA.platform = _Browser.Platform.name;

// UA.fullversion = String(_Browser.version);

(function(UA){
	var m = ua.match(REGEX_WEBKIT);

	if(m){
		UA.webkit = parseInt( m[1] );
	}
})(K.UA);

/**
 * load event
 */
WIN.addEvent('load', function(){
	is_loaded = true;
});

/**
 * fix bugs of mootools
 * ----------------------------------------------------------------------------------------------------------- */

// in mootools 1.3.2, on ie < 8, document.body and html must be 'dollar'ed before the using of most of the methods, esp in Element.dimension
readyList.push(function(){
	$(document.body);
	$(document.documentElement);
});

/**
 * Custom domready event
 * @private
 * ----------------------------------------------------------------------------------------------------------- */

function domready(){

	// fire domready only once
	if(!is_domready){
		is_domready = true;
		fire_domready();
	}
}

function fire_domready(){
	var self = this,
		r = readyList, fn;

	if(r){
		for(var i = 0, len = r.length; i < len; i ++){
			fn = r[i]
			fn && fn.call(WIN, K);
		}

		r.length = 0;
		readyList = null;
	}
};

function bind_domready(){
	var COMPLETE = 'complete', doc = DOC,
		doScroll = doc.documentElement.doScroll,
		eventType = doScroll ? 'readystatechange' : 'DOMContentLoaded';

	is_domready_binded = true;

	// Catch cases where ready() is called after the
	// browser event has already occurred.
	if(doc.readyState === COMPLETE) return domready();

	function _ready(){
		doc.removeListener(eventType, _ready).removeListener('load', _ready);
		domready();
	}

	doc.addListener(eventType, _ready);

	// A fallback to load
	// and make sure that domready event fires before load event registered by user
	doc.addListener('load', _ready);

	if(doScroll){
		var not_framed = false;

		try {
			not_framed = win.frameElement == null;
		} catch(e) {}

		if(not_framed){

			// use conditional function declaration, poll_scroll will not be declared in firefox, that it will save memory
			function poll_scroll(){
				try {
					// doScroll technique by Diego Perini http://javascript.nwbox.com/IEContentLoaded/
					doScroll('left');
					ready();
				} catch(ex) {
					setTimeout(poll_scroll, 10);
				}
			}
			poll_scroll();
		}
	}
}

})(KM);

/**
 TODO:
 - add more user-agent datas.

 change log:
 2011-07-05  Kael:
 - remove UA.fullversion
 - add adapter for Browser.Platform

 2011-06-12  Kael:
 - fix a bug about the regular expression of location pattern that more than one question mark should be allowed in search query
 - add UA.chrome.

 2011-04-26  Kael:
 - adapt mootools.Browser
 - remove ua.chrome, ua.safari, add ua.webkit

 2011-04-12  Kael Zhang:
 - fix a bug that domready could not be properly fired
 - add KM.getLocation method,
 	1. to fix the bug of ie6, which will cause an exception when fetching the value of window.location if document.domain is already specified
 	2. which can split an specified uri into different parts

 2010-12-31  Kael Zhang:
 - migrate domready event out from mootools to here, and change some way
 - migrate .data and .delay methods from core/lang to here

 */<|endoftext|>


<commit_before>'use strict';
/* global chrome, ACTION_ALLOW, ACTION_DENY, ACTION_SCRUB, ACTION_UNKNOWN,
    BLOCKED_STRINGS */

/**
 * StopLeak namespace.
 * @namespace
 */
var stopleak = stopleak || {};

// Filter out request from the main frame (the
stopleak.requestFilter = {
    urls: ['http://*/*', 'https://*/*']
};

function getBlockedRequests(tabId) {
    var blockedRequests = stopleak.tabCache.getRequests(tabId);
    console.log('[popup] requests for tab ' + tabId + ': ', blockedRequests);
    return blockedRequests;
}

chrome.runtime.onInstalled.addListener(function(details) {
    console.log('previousVersion', details.previousVersion);
});

/**
 * Add message to the list of reasons to block this request.
 *
 * @param {object} request The request to check.
 * @param {string} message Message explaining why this request was blocked.
 */
function addBlockMessage(request, message) {
    request.blockReasons.push(message);
}

/**
 * Checks whether this object contains any PII data and accumulates the found
 * information into the request.
 *
 * @param {object} request The request associated with this object..
 * @param {*} object The object to screen for PII.
 * @returns {boolean} True if the object contains PII data.
 */
function containsPIIdata(request, object) {
    var str = JSON.stringify(object);
    var matched = str.match(stopleak.piiRegex);
    if (matched === null) {
        return false;
    }
    // Save matches
    for (var i = 0; i < matched.length; ++i) {
        request.piiFound[matched[i]] = 1;
    }
    return true;
}

/**
 * Checks whether this request url contains any PII data.
 *
 * @param {object} request The request to check.
 * @returns {boolean} True if the url contains PII data.
 */
function piiInRequestUrl(request) {
    var piiFound = false;
    var url = new URL(request.url);
    var tabURL = stopleak.tabCache.getTabURL(request.tabId);
    if (url.protocol !== 'https:' &&
        (url.password !== '' || url.username !== '')) {
        addBlockMessage(request, 'Sending username or password over an ' +
            'insecure connection.');
        piiFound = true;
    }
    var afterDomain = url.pathname + url.search + url.hash;
    if (tabURL.hostname && afterDomain.indexOf(tabURL.hostname) !== -1) {
        // current hostname info is leaking in this request
        addBlockMessage(request, 'Info about the current domain (' +
            tabURL.hostname + ') is present in this request.');
        piiFound = true;
    }
    // any PII data is leaking in this request
    if (containsPIIdata(request, afterDomain)) {
        piiFound = true;
    }
    return piiFound;
}

/**
 * Returns the request url with PII data replaced with random strings.
 *
 * @param {object} request The request to scrub.
 * @returns {string} The scrubbed url.
 */
function scrubRequestUrl(request) {
    var randLength = stopleak.getRandomIntInclusive(4, 20);
    var psuedoRandomStr = stopleak.getPseudoRandomString(randLength);
    return request.url.replace(stopleak.piiRegex, psuedoRandomStr);
}

/**
 * Checks whether this request body contains any PII data.
 *
 * @param {object} request The request to check.
 * @returns {boolean} True if the body contains PII data.
 */
function piiInRequestBody(request) {
    var requestBody = stopleak.getRequestBody(request);
    return containsPIIdata(request, requestBody);
}

/**
 * Scrubs the HTTP request headers.
 *
 * @param {object} request The request to scrub.
 * @returns {boolean} True if the headers have changed.
 */
function scrubRequestHeaders(request) {
    // TODO: actually scrub headers
    return true;
}

/**
 * Checks whether this request's HTTP headers contain any PII data.
 *
 * @param {object} request The request to check.
 * @returns {boolean} True if the headers contain PII data.
 */
function piiInRequestHeaders(request) {
    var piiFound = false;
    var requestHeaders = request.requestHeaders;
    for (var i = requestHeaders.length - 1; i >= 0; --i) {
        var header = requestHeaders[i];
        var headerValue = stopleak.getHeaderValue(header);
        if (header.name === 'Cookie') {
            // TODO: Is there PII in the Cookie being sent?
            // Does this part even matter? I think we should block cookies
            // that are being sent over http.
            requestHeaders.splice(i, 1);
        } else if (header.name === 'Referer') {
            // TODO: Is there PII in the referrer url, i.e. the current url of
            // this frame.
            requestHeaders.splice(i, 1);
        } else {
            // TODO: just naively check for PII?
            // JavaScript can add arbitrary headers to XMLHttpRequests using
            // the setRequestHeader() method.
            // w3.org/TR/XMLHttpRequest/#the-setrequestheader-method
            if (containsPIIdata(request, header.name + ':' + headerValue)) {
                piiFound = true;
            }
        }
    }
    // Fake the ETag header
    requestHeaders.push({
        name: 'If-None-Match',
        value: stopleak.getPseudoRandomString(32)
    });
    return piiFound;
}

/**
 * Checks whether this from onBeforeRequest request contains any PII data.
 *
 * @param {object} request The request from onBeforeRequest to check.
 * @returns {boolean} True if the request contains PII data.
 */
function piiInRequest(request) {
    // We want to report all leaks in the request
    var url = piiInRequestUrl(request);
    var body = piiInRequestBody(request);
    return url || body;
}

/**
 * This inspects the third party request url and body for PII data.
 * If PII is found then the request will be blocked or redirected
 * based on the user's preferences.
 *
 * @param {object} request The HTTP request before being sent.
 * @param {string} sourceOrigin The source origin.
 * @param {string} destOrigin The destination origin.
 * @return {object} The BlockingResponse to allow or deny this request.
 */
function onBeforeRequest(request, sourceOrigin, destOrigin) {
    var userAction = stopleak.getReqAction(sourceOrigin, destOrigin);
    var saveRequest = false;
    var cancel = false;
    var redirectUrl = '';

    // Reasons why we choose to block this request
    request.blockReasons = [];
    request.piiFound = {};
    // Check if allow, deny, scrub, unknown using the full request
    switch (userAction) {
        case ACTION_ALLOW:
            break;
        case ACTION_SCRUB:
            // TODO: We can only cancel the request OR modify the HTTP headers
            // We can't modify the body of a request, e.g. formData, therefore
            // if there is PII in the requestBody I think we should cancel.
            if (piiInRequestBody(request)) {
                // Chrome
                cancel = true;
            } else {
                // TODO: redirect the url to a scrubbed one
                var scrubbedUrl = scrubRequestUrl(request);
                if (scrubbedUrl !== request.url) {
                    redirectUrl = scrubbedUrl;
                }
            }
            break;
        case ACTION_DENY:
            // Block only if PII is present
            cancel = piiInRequest(request);
            break;
        case ACTION_UNKNOWN:
            if (piiInRequest(request)) {
                // TODO: notify user's tab with content popup
                cancel = saveRequest = true;
            }
            break;
        default:
            console.assert(false, 'Reached unreachable code!');
    }
    // Convert found PII into a list
    request.piiFound = Object.keys(request.piiFound);
    if (redirectUrl) {
        stopleak.tabCache.incBlockCount(request.tabId);
        console.log('[Redirect] ' + destOrigin + ' reasons:', request.blockReasons);
        console.log('[Redirect] PII found:', request.piiFound);
        return {redirectUrl: redirectUrl};
    } else if (cancel) {
        if (saveRequest) {
            stopleak.tabCache.saveRequest(request);
        }
        stopleak.tabCache.incBlockCount(request.tabId);
        console.log('[Cancel] ' + destOrigin + ' reasons:', request.blockReasons);
        console.log('[Cancel] PII found:', request.piiFound);
        return {cancel: true};
    }
    return {cancel: false};
}

/**
 * Modifies or blocks a request if the HTTP headers contain PII data.
 *
 * @param {object} request The HTTP request containing request headers.
 * @param {string} sourceOrigin The source origin.
 * @param {string} destOrigin The destination origin.
 * @return {object} The BlockingResponse to cancel this request or modify
 *     request headers.
 */
function onBeforeSendHeaders(request, sourceOrigin, destOrigin) {
    var userAction = stopleak.getReqAction(sourceOrigin, destOrigin);
    var saveRequest = false;
    var cancel = false;
    var headersChanged = false;

    // Reasons why we choose to block this request
    request.blockReasons = [];
    request.piiFound = {};
    // Check if allow, deny, scrub, unknown using the full request
    switch (userAction) {
        case ACTION_ALLOW:
            break;
        case ACTION_SCRUB:
            headersChanged = scrubRequestHeaders(request);
            break;
        case ACTION_DENY:
            // Block only if PII is present
            cancel = piiInRequestHeaders(request);
            break;
        case ACTION_UNKNOWN:
            if (piiInRequestHeaders(request)) {
                // TODO: notify user's tab with content popup
                cancel = saveRequest = true;
            }
            break;
        default:
            console.assert(false, 'Reached unreachable code!');
    }
    // Convert found PII into a list
    request.piiFound = Object.keys(request.piiFound);
    if (cancel) {
        if (saveRequest) {
            stopleak.tabCache.saveRequest(request);
        }
        stopleak.tabCache.incBlockCount(request.tabId);
        console.log('[Cancel] ' + destOrigin + ' reasons:', request.blockReasons);
        console.log('[Cancel] PII found:', request.piiFound);
        return {cancel: true};
    } else if (headersChanged) {
        return {requestHeaders: request.requestHeaders};
    }
    return {cancel: false};
}

/**
 * Pass cross origin requests to the filter callback.
 *
 * @param {function} onBeforeCallback Either onBeforeSendHeaders or
 *    onBeforeRequest.
 * @return {object} The BlockingResponse to allow or deny this request.
 */
function filterCrossTabOrigin(onBeforeCallback) {
    return function(details) {
        var allow = {cancel: false};
        // Allow requests without tabs or for top-level documents.
        if (details.tabId === chrome.tabs.TAB_ID_NONE ||
            details.type === 'main_frame') {
            return allow;
        }
        var destOrigin = stopleak.extractOrigin(details.url);
        var sourceOrigin = stopleak.tabCache.getTabOrigin(details.tabId);
        // Allow requests going to the same origin.
        if (sourceOrigin === destOrigin) {
            return allow;
        } else {
            return onBeforeCallback(details, sourceOrigin, destOrigin);
        }
    };
}

// Hook network requests

// Fires when a request is about to occur. This event is sent before any TCP
// connection is made and can be used to cancel or redirect requests.
chrome.webRequest.onBeforeRequest.addListener(
    filterCrossTabOrigin(onBeforeRequest),
    stopleak.requestFilter,
    ['blocking', 'requestBody']);

// Fires when a request is about to occur and the initial headers have been
// prepared. The event is intended to allow extensions to add, modify, and
// delete request headers (Some headers are not provided). This event can be
// used to cancel the request.
chrome.webRequest.onBeforeSendHeaders.addListener(
    filterCrossTabOrigin(onBeforeSendHeaders),
    stopleak.requestFilter,
    ['blocking', 'requestHeaders']);
<commit_msg>Fix logging typo.<commit_after>'use strict';
/* global chrome, ACTION_ALLOW, ACTION_DENY, ACTION_SCRUB, ACTION_UNKNOWN,
    BLOCKED_STRINGS */

/**
 * StopLeak namespace.
 * @namespace
 */
var stopleak = stopleak || {};

// Filter out request from the main frame (the
stopleak.requestFilter = {
    urls: ['http://*/*', 'https://*/*']
};

function getBlockedRequests(tabId) {
    var blockedRequests = stopleak.tabCache.getRequests(tabId);
    console.log('[popup] requests for tab ' + tabId + ': ', blockedRequests);
    return blockedRequests;
}

chrome.runtime.onInstalled.addListener(function(details) {
    console.log('previousVersion', details.previousVersion);
});

/**
 * Add message to the list of reasons to block this request.
 *
 * @param {object} request The request to check.
 * @param {string} message Message explaining why this request was blocked.
 */
function addBlockMessage(request, message) {
    request.blockReasons.push(message);
}

/**
 * Checks whether this object contains any PII data and accumulates the found
 * information into the request.
 *
 * @param {object} request The request associated with this object..
 * @param {*} object The object to screen for PII.
 * @returns {boolean} True if the object contains PII data.
 */
function containsPIIdata(request, object) {
    var str = JSON.stringify(object);
    var matched = str.match(stopleak.piiRegex);
    if (matched === null) {
        return false;
    }
    // Save matches
    for (var i = 0; i < matched.length; ++i) {
        request.piiFound[matched[i]] = 1;
    }
    return true;
}

/**
 * Checks whether this request url contains any PII data.
 *
 * @param {object} request The request to check.
 * @returns {boolean} True if the url contains PII data.
 */
function piiInRequestUrl(request) {
    var piiFound = false;
    var url = new URL(request.url);
    var tabURL = stopleak.tabCache.getTabURL(request.tabId);
    if (url.protocol !== 'https:' &&
        (url.password !== '' || url.username !== '')) {
        addBlockMessage(request, 'Sending username or password over an ' +
            'insecure connection.');
        piiFound = true;
    }
    var afterDomain = url.pathname + url.search + url.hash;
    if (tabURL.hostname && afterDomain.indexOf(tabURL.hostname) !== -1) {
        // current hostname info is leaking in this request
        addBlockMessage(request, 'Info about the current domain (' +
            tabURL.hostname + ') is present in this request.');
        piiFound = true;
    }
    // any PII data is leaking in this request
    if (containsPIIdata(request, afterDomain)) {
        piiFound = true;
    }
    return piiFound;
}

/**
 * Returns the request url with PII data replaced with random strings.
 *
 * @param {object} request The request to scrub.
 * @returns {string} The scrubbed url.
 */
function scrubRequestUrl(request) {
    var randLength = stopleak.getRandomIntInclusive(4, 20);
    var psuedoRandomStr = stopleak.getPseudoRandomString(randLength);
    return request.url.replace(stopleak.piiRegex, psuedoRandomStr);
}

/**
 * Checks whether this request body contains any PII data.
 *
 * @param {object} request The request to check.
 * @returns {boolean} True if the body contains PII data.
 */
function piiInRequestBody(request) {
    var requestBody = stopleak.getRequestBody(request);
    return containsPIIdata(request, requestBody);
}

/**
 * Scrubs the HTTP request headers.
 *
 * @param {object} request The request to scrub.
 * @returns {boolean} True if the headers have changed.
 */
function scrubRequestHeaders(request) {
    // TODO: actually scrub headers
    return true;
}

/**
 * Checks whether this request's HTTP headers contain any PII data.
 *
 * @param {object} request The request to check.
 * @returns {boolean} True if the headers contain PII data.
 */
function piiInRequestHeaders(request) {
    var piiFound = false;
    var requestHeaders = request.requestHeaders;
    for (var i = requestHeaders.length - 1; i >= 0; --i) {
        var header = requestHeaders[i];
        var headerValue = stopleak.getHeaderValue(header);
        if (header.name === 'Cookie') {
            // TODO: Is there PII in the Cookie being sent?
            // Does this part even matter? I think we should block cookies
            // that are being sent over http.
            requestHeaders.splice(i, 1);
        } else if (header.name === 'Referer') {
            // TODO: Is there PII in the referrer url, i.e. the current url of
            // this frame.
            requestHeaders.splice(i, 1);
        } else {
            // TODO: just naively check for PII?
            // JavaScript can add arbitrary headers to XMLHttpRequests using
            // the setRequestHeader() method.
            // w3.org/TR/XMLHttpRequest/#the-setrequestheader-method
            if (containsPIIdata(request, header.name + ':' + headerValue)) {
                piiFound = true;
            }
        }
    }
    // Fake the ETag header
    requestHeaders.push({
        name: 'If-None-Match',
        value: stopleak.getPseudoRandomString(32)
    });
    return piiFound;
}

/**
 * Checks whether this from onBeforeRequest request contains any PII data.
 *
 * @param {object} request The request from onBeforeRequest to check.
 * @returns {boolean} True if the request contains PII data.
 */
function piiInRequest(request) {
    // We want to report all leaks in the request
    var url = piiInRequestUrl(request);
    var body = piiInRequestBody(request);
    return url || body;
}

/**
 * This inspects the third party request url and body for PII data.
 * If PII is found then the request will be blocked or redirected
 * based on the user's preferences.
 *
 * @param {object} request The HTTP request before being sent.
 * @param {string} sourceOrigin The source origin.
 * @param {string} destOrigin The destination origin.
 * @return {object} The BlockingResponse to allow or deny this request.
 */
function onBeforeRequest(request, sourceOrigin, destOrigin) {
    var userAction = stopleak.getReqAction(sourceOrigin, destOrigin);
    var saveRequest = false;
    var cancel = false;
    var redirectUrl = '';

    // Reasons why we choose to block this request
    request.blockReasons = [];
    request.piiFound = {};
    // Check if allow, deny, scrub, unknown using the full request
    switch (userAction) {
        case ACTION_ALLOW:
            break;
        case ACTION_SCRUB:
            // TODO: We can only cancel the request OR modify the HTTP headers
            // We can't modify the body of a request, e.g. formData, therefore
            // if there is PII in the requestBody I think we should cancel.
            if (piiInRequestBody(request)) {
                // Chrome
                cancel = true;
            } else {
                // TODO: redirect the url to a scrubbed one
                var scrubbedUrl = scrubRequestUrl(request);
                if (scrubbedUrl !== request.url) {
                    redirectUrl = scrubbedUrl;
                }
            }
            break;
        case ACTION_DENY:
            // Block only if PII is present
            cancel = piiInRequest(request);
            break;
        case ACTION_UNKNOWN:
            if (piiInRequest(request)) {
                // TODO: notify user's tab with content popup
                cancel = saveRequest = true;
            }
            break;
        default:
            console.assert(false, 'Reached unreachable code!');
    }
    // Convert found PII into a list
    request.piiFound = Object.keys(request.piiFound);
    if (redirectUrl) {
        stopleak.tabCache.incBlockCount(request.tabId);
        console.log('[Redirect]', request, 'reasons', request.blockReasons);
        console.log('[Redirect] PII found:', request.piiFound);
        return {redirectUrl: redirectUrl};
    } else if (cancel) {
        if (saveRequest) {
            stopleak.tabCache.saveRequest(request);
        }
        stopleak.tabCache.incBlockCount(request.tabId);
        console.log('[Cancel]', request, 'reasons', request.blockReasons);
        console.log('[Cancel] PII found:', request.piiFound);
        return {cancel: true};
    }
    return {cancel: false};
}

/**
 * Modifies or blocks a request if the HTTP headers contain PII data.
 *
 * @param {object} request The HTTP request containing request headers.
 * @param {string} sourceOrigin The source origin.
 * @param {string} destOrigin The destination origin.
 * @return {object} The BlockingResponse to cancel this request or modify
 *     request headers.
 */
function onBeforeSendHeaders(request, sourceOrigin, destOrigin) {
    var userAction = stopleak.getReqAction(sourceOrigin, destOrigin);
    var saveRequest = false;
    var cancel = false;
    var headersChanged = false;

    // Reasons why we choose to block this request
    request.blockReasons = [];
    request.piiFound = {};
    // Check if allow, deny, scrub, unknown using the full request
    switch (userAction) {
        case ACTION_ALLOW:
            break;
        case ACTION_SCRUB:
            headersChanged = scrubRequestHeaders(request);
            break;
        case ACTION_DENY:
            // Block only if PII is present
            cancel = piiInRequestHeaders(request);
            break;
        case ACTION_UNKNOWN:
            if (piiInRequestHeaders(request)) {
                // TODO: notify user's tab with content popup
                cancel = saveRequest = true;
            }
            break;
        default:
            console.assert(false, 'Reached unreachable code!');
    }
    // Convert found PII into a list
    request.piiFound = Object.keys(request.piiFound);
    if (cancel) {
        if (saveRequest) {
            stopleak.tabCache.saveRequest(request);
        }
        stopleak.tabCache.incBlockCount(request.tabId);
        console.log('[Cancel]', request, 'reasons', request.blockReasons);
        console.log('[Cancel] PII found:', request.piiFound);
        return {cancel: true};
    } else if (headersChanged) {
        return {requestHeaders: request.requestHeaders};
    }
    return {cancel: false};
}

/**
 * Pass cross origin requests to the filter callback.
 *
 * @param {function} onBeforeCallback Either onBeforeSendHeaders or
 *    onBeforeRequest.
 * @return {object} The BlockingResponse to allow or deny this request.
 */
function filterCrossTabOrigin(onBeforeCallback) {
    return function(details) {
        var allow = {cancel: false};
        // Allow requests without tabs or for top-level documents.
        if (details.tabId === chrome.tabs.TAB_ID_NONE ||
            details.type === 'main_frame') {
            return allow;
        }
        var destOrigin = stopleak.extractOrigin(details.url);
        var sourceOrigin = stopleak.tabCache.getTabOrigin(details.tabId);
        // Allow requests going to the same origin.
        if (sourceOrigin === destOrigin) {
            return allow;
        } else {
            return onBeforeCallback(details, sourceOrigin, destOrigin);
        }
    };
}

// Hook network requests

// Fires when a request is about to occur. This event is sent before any TCP
// connection is made and can be used to cancel or redirect requests.
chrome.webRequest.onBeforeRequest.addListener(
    filterCrossTabOrigin(onBeforeRequest),
    stopleak.requestFilter,
    ['blocking', 'requestBody']);

// Fires when a request is about to occur and the initial headers have been
// prepared. The event is intended to allow extensions to add, modify, and
// delete request headers (Some headers are not provided). This event can be
// used to cancel the request.
chrome.webRequest.onBeforeSendHeaders.addListener(
    filterCrossTabOrigin(onBeforeSendHeaders),
    stopleak.requestFilter,
    ['blocking', 'requestHeaders']);
<|endoftext|>


<commit_before>const chai = require('chai');
const expect = chai.expect;
const chaiAsPromised = require("chai-as-promised");
const progress = require("@progress/jsdo-core").progress;
const DataSource = require("../build/progress.data.node").DataSource;
const rxjs = require("rxjs");

chai.use(chaiAsPromised);

// These are smoke tests for the DataSource! :)

describe('Datasource Smoke Tests', function () {
    //options all the info required for creating a jsdo session
    const options = {
        // These services are running in Docker
        serviceURI: "https://172.29.18.125:8894/OEMobileDemoServices",
        catalogURI: "https://172.29.18.125:8894/OEMobileDemoServices/static/CustomerService.json",
        resourceName: "Customer",
        authenticationModel: "anonymous",
        tableRef: "ttCustomer",
        recName: "test9001" //+ (Math.random() * 100)
    };

    //define your variables required for the tests below
    let session,
        jsdo,
        dataSource;

    //before: getSession
    before(function (done) {
        progress.data.getSession(options).then((object) => {
            session = object.jsdosession;
        }).then(() => {
            jsdo = new progress.data.JSDO({ name: options.resourceName });
        }).then(() => done(), () => done());
    });

    describe('DataSource Instantiation Tests', function () {
        it('should successfully create a DataSource', function () {
            dataSource = new DataSource({
                jsdo: jsdo,
                tableRef: options.tableRef
            });

            expect(dataSource).to.be.an.instanceOf(DataSource);
        });
    });

    //1st Read without parameters
    describe('DataSource CRUD tests', function () {
        let data,
            rec;

        describe('DataSource Read tests', function () {
            it('should have an Observable returned by read', function () {
                let read = dataSource.read();
                expect(read).to.be.instanceOf(rxjs.Observable);
            });

            it('should successfully call read()', function () {
                let read = dataSource.read().toPromise().then((mydata) => {
                    data = mydata.data;
                    // console.log(data);
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.read() failed: ' + error);
                });

                return expect(read).to.eventually.be.true;
            });

            it('should have a DataSource with data after the read()', function () {
                return expect(data[0]["CustNum"]).to.be.a("number");
            });
        });

        describe('DataSource Create tests', function () {
            it('should successfully call create()', function () {
                // { CustNum: 1,
                //     Country: 'Mexico',
                //     Name: 'NewItem',
                //     ...
                //     id: '0x0000000000000062',
                //     _id: '1537314778768-1' }

                let testRec = {
                    CustNum: 100000,
                    Name: options.recName,
                    State: 'MA',
                    Balance: 0
                };

                expect(dataSource.create(testRec).Name).to.deep.equal(testRec.Name);
            });

            it('should successfully call saveChanges()', function () {
                // expect(create).to.be.instanceOf(rxjs.Observable);
                let create = dataSource.saveChanges().toPromise().then(() => {
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.create() failed: ' + error);
                });

                return expect(create).to.eventually.be.true;
            });

            it('should successfully create a single record in the backend', function () {
                let count = 0;
                dataSource.getData().forEach(element => {
                    if (element.Name === options.recName) {
                        count += 1;
                    }

                    if (count === 1) {
                        // Save the element for the update test later
                        rec = element;
                    }
                });

                return expect(count).to.equal(1);
            });
        });

        describe('DataSource Update tests', function () {
            it('should successfully call update()', function () {
                rec.Country = "TestCountry";

                expect(dataSource.update(rec).Country).to.deep.equal(rec.Country);
            });

            it('should successfully call saveChanges()', function () {
                // expect(create).to.be.instanceOf(rxjs.Observable);
                let update = dataSource.saveChanges().toPromise().then(() => {
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.create() failed: ' + error);
                });

                return expect(update).to.eventually.be.true;
            });

            it('should successfully update a single record in the backend', function () {
                let count = 0;
                dataSource.getData().forEach(element => {
                    if (element.Name === options.recName
                        && element.Country === "TestCountry") {
                        count += 1;
                    }

                    // Save this element for the delete later
                    if (count === 1) {
                        rec = element;
                    }
                });

                return expect(count).to.equal(1);
            });
        });

        describe('DataSource Remove tests', function () {
            it('should successfully call Remove()', function () {
                let count = 0;

                dataSource.getData().forEach(element => {
                    if (element.Name === options.recName) {
                        if (dataSource.remove(element)) {
                            count += 1;
                        }
                    }
                });

                expect(count).to.equal(1);
            });

            it('should successfully call saveChanges()', function () {
                let remove = dataSource.saveChanges().toPromise().then(() => {
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.create() failed: ' + error);
                });

                return expect(remove).to.eventually.be.true;
            });

            it('should successfully update a single record in the backend', function () {
                let count = 0;
                dataSource.getData().forEach(element => {
                    if (element.Name === options.recName) {
                        count += 1;
                    }
                });

                return expect(count).to.equal(0);
            });
        });

        after(function (done) {
            this.timeout(5000);
            jsdo.ttCustomer.foreach((customer) => {
                if (customer.data.Name === options.recName) {
                    customer.remove();
                }

                jsdo.saveChanges().then((object) => {
                    return jsdo.fill();
                }).then((object) => {
                    console.log(object);
                    let found = 0;
                    jsdo.ttCustomer.foreach((customer) => {
                        if (customer.data.Name === options.recName) {
                            found += 1;
                        }
                    });
                    console.log(found);
                }, () => console.log("jsdo remove"));
            });

            setTimeout(() => {
                session.invalidate().then(() => done());
            }, 1500);
        });
    });
});
<commit_msg>Fixed issues related to Datasource test<commit_after>const chai = require('chai');
const expect = chai.expect;
const chaiAsPromised = require("chai-as-promised");
const progress = require("@progress/jsdo-core").progress;
const DataSource = require("../build/progress.data.node").DataSource;
const rxjs = require("rxjs");

chai.use(chaiAsPromised);

// These are smoke tests for the DataSource! :)

describe('Datasource Smoke Tests', function () {
    //options all the info required for creating a jsdo session
    const options = {
        // These services are running in Docker
        serviceURI: "http://172.29.18.125:8894/OEMobileDemoServices",
        catalogURI: "http://172.29.18.125:8894/OEMobileDemoServices/static/CustomerService.json",
        resourceName: "Customer",
        authenticationModel: "anonymous",
        tableRef: "ttCustomer",
        recName: "test9011" //+ (Math.random() * 100)
    };

    //define your variables required for the tests below
    let session,
        jsdo,
        dataSource;

    //before: getSession
    before(function (done) {
        progress.data.getSession(options).then((object) => {
            session = object.jsdosession;
        }).then(() => {
            jsdo = new progress.data.JSDO({
                name: options.resourceName
            });
        }).then(() => done(), () => done());
    });

    describe('DataSource Instantiation Tests', function () {
        it('should successfully create a DataSource', function () {
            dataSource = new DataSource({
                jsdo: jsdo,
                tableRef: options.tableRef
            });

            expect(dataSource).to.be.an.instanceOf(DataSource);
        });
    });

    //1st Read without parameters
    describe('DataSource CRUD tests', function () {
        let data,
            rec;

        describe('DataSource Read tests', function () {
            it('should have an Observable returned by read', function () {
                let read = dataSource.read();
                expect(read).to.be.instanceOf(rxjs.Observable);
            });

            it('should successfully call read()', function () {
                let read = dataSource.read().toPromise().then((mydata) => {
                    data = mydata.data;
                    // console.log(data);
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.read() failed: ' + error);
                });

                return expect(read).to.eventually.be.true;
            });

            it('should have a DataSource with data after the read()', function () {
                return expect(data[0]["CustNum"]).to.be.a("number");
            });
        });

        describe('DataSource Create tests', function () {
            it('should successfully call create()', function () {

                let testRec = {
                    CustNum: 100000,
                    Name: options.recName,
                    State: 'MA',
                    Balance: 0
                };

                expect(dataSource.create(testRec).Name).to.deep.equal(testRec.Name);
            });

            it('should successfully call saveChanges()', function () {
                // expect(create).to.be.instanceOf(rxjs.Observable);
                let create = dataSource.saveChanges().toPromise().then(() => {
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.create() failed: ' + error);
                });

                return expect(create).to.eventually.be.true;
            });

            it('should successfully create a single record in the backend', function () {
                let count = 0;
                dataSource.getData().forEach(element => {
                    if (element.Name === options.recName) {
                        count += 1;
                    }

                    if (count === 1) {
                        // Save the element for the update test later
                        rec = element;
                    }
                });

                return expect(count).to.equal(1);
            });
        });

        describe('DataSource Update tests', function () {
            it('should successfully call update()', function () {
                // console.log("Before addition", rec);
                rec.Country = "TestCountry";

                // console.log("After addition", rec);

                // expect(dataSource.update(rec).Country).to.deep.equal(rec.Country);
                expect(dataSource.update(rec)).to.deep.equal(true);
            });

            it('should successfully call saveChanges()', function () {
                // expect(create).to.be.instanceOf(rxjs.Observable);
                let update = dataSource.saveChanges().toPromise().then(() => {
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.create() failed: ' + error);
                });

                return expect(update).to.eventually.be.true;
            });

            it('should successfully update a single record in the backend', function () {
                let count = 0;
                dataSource.getData().forEach(element => {
                    if (element.Name === options.recName &&
                        element.Country === "TestCountry") {
                        count += 1;
                    }

                    // Save this element for the delete later
                    if (count === 1) {
                        rec = element;
                    }
                });

                return expect(count).to.equal(1);
            });
        });

        describe('DataSource Remove tests', function () {
            it('should successfully call Remove()', function () {
                let count = 0;

                dataSource.getData().forEach(element => {
                    if (element.Name === options.recName) {
                        if (dataSource.remove(element)) {
                            count += 1;
                        }
                    }
                });

                expect(count).to.equal(1);
            });

            it('should successfully call saveChanges()', function () {
                let remove = dataSource.saveChanges().toPromise().then(() => {
                    return true;
                }, (error) => {
                    return expect.fail(null, null, 'DataSource.create() failed: ' + error);
                });

                return expect(remove).to.eventually.be.true;
            });

            setTimeout(() => {
                it('should successfully remove record in the backend', function () {
                    let count = 0;
                    dataSource.getData().forEach(element => {
                        if (element.Name === options.recName) {
                            count += 1;
                            console.log("Count val: ", count);
                            console.log("elementName", element.Name);
                        }
                    });

                    return expect(count).to.equal(0);
                });
            }, 3000);
        });

        after(function (done) {
            this.timeout(5000);
            // jsdo.ttCustomer.foreach((customer) => {
            //     if (customer.data.Name === options.recName) {
            //         customer.remove();
            //     }

            //     jsdo.saveChanges().then((object) => {
            //         return jsdo.fill();
            //     }).then((object) => {
            //         console.log(object);
            //         let found = 0;
            //         jsdo.ttCustomer.foreach((customer) => {
            //             if (customer.data.Name === options.recName) {
            //                 found += 1;
            //             }
            //         });
            //         console.log(found);
            //     }, () => console.log("jsdo remove"));
            // });

            // session.invalidate();

            setTimeout(() => {
                session.invalidate().then(() => done());
            }, 1500);
        });
    });
});
<|endoftext|>


<commit_before>var faker = require('faker')

module.exports = [
  {
    'id': 'Roadmap-to-HTML',
    'brigade': 'codeforexample',
    'status': 'mvp',
    'thumbnailUrl': 'https://cloud.githubusercontent.com/assets/451510/23977184/d371c3ba-09a9-11e7-971c-f8dc705f0f5a.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'Roadmap to HTML',
    'description': 'Publishing an essential reentry resource on the web',
    'license': 'MIT',
    'homepage': 'https://rootandrebound.github.io/roadmap-to-html/',
    'repository': 'https://github.com/rootandrebound/roadmap-to-html',
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['all', 'homelessness', 'housing', 'infrastructure'],
      'skillsNeeded': ['ms-word','css','html','mongo'],
      'rolesNeeded': ['develop','help', 'initiate']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }
    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'ShelterTech',
    'brigade': 'codeforexample',
    'status': 'alpha',
    'thumbnailUrl': 'https://www.google.com/a/sheltertech.org/images/logo.gif?alpha=1&service=google_default',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'ShelterTech',
    'description': "We're building a suite of products and services for SF's homeless and underserved communities.",
    'license': 'MIT',
    'homepage': 'https://sheltertech.org/',
    'repository': '',
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['homelessness'],
      'skillsNeeded': ['ios', 'css', 'html'],
      'rolesNeeded': ['developer']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }
    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'California-OpenJustice',
    'brigade': 'codeforexample',
    'status': 'beta',
    'thumbnailUrl': 'http://datascience.codeforsanfrancisco.org/wp-content/uploads/2017/03/doj-screenshot.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'California OpenJustice',
    'description': 'Equity & equality in the CA criminal justice system',
    'license': 'MIT',
    'homepage': 'https://getkong.org/install',
    'repository': 'https://github.com/codeforexample/forked-project-kong',
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['criminal-justice'],
      'skillsNeeded': ['optimization-modeling','predictive-modeling','data-science','statistics'],
      'rolesNeeded': ['develop','help']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'Adopt-a-Drain',
    'brigade': 'codeforexample',
    'status': 'live',
    'thumbnailUrl': 'http://i.imgur.com/JUmCJ5L.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'Adopt-a-Drain',
    'description': 'Claim responsibility for cleaning out a storm drain after it rains.',
    'license': 'MIT',
    'homepage': 'http://adoptadrain.sfwater.org/',
    'repository': 'https://github.com/sfbrigade/adopt-a-drain',
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['housing'],
      'skillsNeeded': ['ruby-on-rails','ruby','javascript'],
      'rolesNeeded': ['develop','bug-testing']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'Brigadehub',
    'brigade': 'codeforexample',
    'status': 'mvp',
    'thumbnailUrl': 'https://cdn.rawgit.com/brigadehub/brigadehub/cc51c9ad/assets/rasterized/logo-square-white-blue.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'Brigadehub',
    'description': 'Data portal and CMS for brigades',
    'license': 'MIT',
    'homepage': 'https://brigadehub.github.io',
    'repository': 'https://github.com/brigadehub/brigadehub',
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['civic-participation'],
      'skillsNeeded': ['node','mongo'],
      'rolesNeeded': ['develop']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  }, {
    'id': 'The-Data-Science-Working-Group',
    'brigade': 'codeforexample',
    'status': 'proposed',
    'thumbnailUrl': 'https://github.com/sfbrigade/data-science-wg/raw/master/datascience-wg_header.jpg  ',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'The Data Science Working Group',
    'description': faker.hacker.phrase(),
    'license': 'MIT',
    'homepage': 'http://datascience.codeforsanfrancisco.org/',
    'repository': 'https://github.com/codeforexample/mirrored-project-mirror',
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': [],
      'skillsNeeded': ['python','data-science','visualization','machine-learning'],
      'rolesNeeded': ['develop','learn']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  }
]
<commit_msg>Added more test data to Seed.js - drop db and rebuild<commit_after>var faker = require('faker')

module.exports = [
  {
    'id': 'Roadmap-to-HTML',
    'brigade': 'codeforexample',
    'status': 'mvp',
    'thumbnailUrl': 'https://cloud.githubusercontent.com/assets/451510/23977184/d371c3ba-09a9-11e7-971c-f8dc705f0f5a.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'Roadmap to HTML',
    'description': 'Publishing an essential reentry resource on the web',
    'license': 'MIT',
    'homepage': 'https://rootandrebound.github.io/roadmap-to-html/',
    'repository': 'https://github.com/rootandrebound/roadmap-to-html',
    'contact': [{ 'name': 'Zayne Malik', 'email': 'zm@britpop.com'}],
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['all', 'homelessness', 'housing', 'infrastructure'],
      'skillsNeeded': ['ms-word','css','html','mongo'],
      'rolesNeeded': ['develop','help', 'initiate']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }
    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'ShelterTech',
    'brigade': 'codeforexample',
    'status': 'alpha',
    'thumbnailUrl': 'https://www.google.com/a/sheltertech.org/images/logo.gif?alpha=1&service=google_default',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'ShelterTech',
    'description': "We're building a suite of products and services for SF's homeless and underserved communities.",
    'license': 'MIT',
    'homepage': 'https://sheltertech.org/',
    'repository': '',
    'contact': [{ 'name': 'Zane', 'email': 'z@sheltertech.org'}],
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['homelessness'],
      'skillsNeeded': ['ios', 'css', 'html'],
      'rolesNeeded': ['developer']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }
    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'California-OpenJustice',
    'brigade': 'codeforexample',
    'status': 'beta',
    'thumbnailUrl': 'http://datascience.codeforsanfrancisco.org/wp-content/uploads/2017/03/doj-screenshot.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'California OpenJustice',
    'description': 'Equity & equality in the CA criminal justice system',
    'license': 'MIT',
    'homepage': 'https://getkong.org/install',
    'repository': 'https://github.com/codeforexample/forked-project-kong',
    'contact': [{ 'name': 'Lindsay', 'email': 'linds@caljust.org'}],
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['criminal-justice'],
      'skillsNeeded': ['optimization-modeling','predictive-modeling','data-science','statistics'],
      'rolesNeeded': ['develop','help']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'Adopt-a-Drain',
    'brigade': 'codeforexample',
    'status': 'live',
    'thumbnailUrl': 'http://i.imgur.com/JUmCJ5L.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'Adopt-a-Drain',
    'description': 'Claim responsibility for cleaning out a storm drain after it rains.',
    'license': 'MIT',
    'homepage': 'http://adoptadrain.sfwater.org/',
    'repository': 'https://github.com/sfbrigade/adopt-a-drain',
    'contact': [{ 'name': 'Solon', 'email': 'sol@uwantdrain.guru'}],
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['housing'],
      'skillsNeeded': ['ruby-on-rails','ruby','javascript'],
      'rolesNeeded': ['develop','bug-testing']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  },
  {
    'id': 'Brigadehub',
    'brigade': 'codeforexample',
    'status': 'mvp',
    'thumbnailUrl': 'https://cdn.rawgit.com/brigadehub/brigadehub/cc51c9ad/assets/rasterized/logo-square-white-blue.png',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'Brigadehub',
    'description': 'Data portal and CMS for brigades',
    'license': 'MIT',
    'homepage': 'https://brigadehub.github.io',
    'repository': 'https://github.com/brigadehub/brigadehub',
    'contact': [{ 'name': 'Johnson', 'email': 'jns@brigadehub.c0m'}],
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': ['civic-participation'],
      'skillsNeeded': ['node','mongo'],
      'rolesNeeded': ['develop']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  }, {
    'id': 'The-Data-Science-Working-Group',
    'brigade': 'codeforexample',
    'status': 'proposed',
    'thumbnailUrl': 'https://github.com/sfbrigade/data-science-wg/raw/master/datascience-wg_header.jpg  ',
    'bannerUrl': faker.image.people(),
    'bornAt': 'Code for Example',
    'geography': faker.address.city(),
    'politicalEntity': '',
    'name': 'The Data Science Working Group',
    'description': faker.hacker.phrase(),
    'license': 'MIT',
    'homepage': 'http://datascience.codeforsanfrancisco.org/',
    'repository': 'https://github.com/codeforexample/mirrored-project-mirror',
    'contact': [{ 'name': 'Alvin Toffler', 'email': 'at@thefuture.com'}],
    'links': [],
    'videos': [],
    'matchingConfig': {
      'interestsNeeded': [],
      'skillsNeeded': ['python','data-science','visualization','machine-learning'],
      'rolesNeeded': ['develop','learn']
    },
    'keywords': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'partners': [],
    'team': [
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      },
      {
        avatar: faker.internet.avatar(),
        username: faker.internet.userName()
      }

    ],
    'needs': [
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz(),
      faker.company.bsBuzz()
    ],
    'content': '# This Project! \n\n This is sub info.\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n' +
      faker.lorem.paragraph() +
      '\n\n| Table | Info | Goes | Here |\n| --- | --- | --- | --- |\n| foo | bar | lorem | ipsum |'
  }
]
<|endoftext|>