<commit_before>import {
	Component,
	PropTypes
} from 'react';

import Radium from 'radium';

import { yellow500 } from 'material-ui/styles/colors';

import Icon from 'components/Icon';
import { Link } from 'react-router';

const styles = {
	container: {
		display: 'inline-block',
		padding: 10
	},
	book: {
		display: 'flex',
		flexDirection: 'column',
		textAlign: 'center'
	},
	link: {
		color: 'white',
		fontSize: 16,
		textDecoration: 'none'
	},
	image: {
		width: 195,
		height: 260
	},
	starButton: {
		fontSize: '40px',
		cursor: 'pointer',
		color: yellow500
	},
	starred: {
		color: yellow500
	},
	starButtonContainer: {
		position: 'absolute',
		marginLeft: '150px'
	}
};

@Radium
export default class ComicBook extends Component {
	static propTypes = {
		coverImage: PropTypes.string,
		latestChapter: PropTypes.string,
		comicName: PropTypes.string,
		starred: PropTypes.bool,
		onStarButtonClick: PropTypes.func,

		showStarButton: PropTypes.bool
	}

	static defaultProps = {
		showStarButton: true
	}

	iconName = () => this.props.starred ? 'star' : 'star_border';

	iconStyle = () => this.props.starred ? styles.starred : { };

	renderStarButton = () => {
		const { onStarButtonClick } = this.props;

		return(
			<Icon
				iconName={this.iconName()}
				style={{
					...styles.starButton,
					...this.iconStyle()
				}}
				onClick={onStarButtonClick}
			/>
		);
	}

	render() {
		const {
			coverImage,
			latestChapter,
			comicName,
			showStarButton
		} = this.props;

		return(

					{ showStarButton ? this.renderStarButton() : null }

					<Link to={`/reader/dm5/${latestChapter}`}>

					</Link>
					<Link to={`/reader/dm5/${latestChapter}`} style={styles.link}>
						{comicName}
					</Link>

		);
	}
}
<commit_msg>Fix comic tiitle width<commit_after>import {
	Component,
	PropTypes
} from 'react';

import Radium from 'radium';

import { yellow500 } from 'material-ui/styles/colors';

import Icon from 'components/Icon';
import { Link } from 'react-router';

const styles = {
	container: {
		display: 'inline-block',
		padding: 10
	},
	book: {
		display: 'flex',
		flexDirection: 'column',
		textAlign: 'center'
	},
	link: {
		color: 'white',
		fontSize: 16,
		width: 195,
		textDecoration: 'none'
	},
	image: {
		width: 195,
		height: 260
	},
	starButton: {
		fontSize: '40px',
		cursor: 'pointer',
		color: yellow500
	},
	starred: {
		color: yellow500
	},
	starButtonContainer: {
		position: 'absolute',
		marginLeft: '150px'
	}
};

@Radium
export default class ComicBook extends Component {
	static propTypes = {
		coverImage: PropTypes.string,
		latestChapter: PropTypes.string,
		comicName: PropTypes.string,
		starred: PropTypes.bool,
		onStarButtonClick: PropTypes.func,

		showStarButton: PropTypes.bool
	}

	static defaultProps = {
		showStarButton: true
	}

	iconName = () => this.props.starred ? 'star' : 'star_border';

	iconStyle = () => this.props.starred ? styles.starred : { };

	renderStarButton = () => {
		const { onStarButtonClick } = this.props;

		return(
			<Icon
				iconName={this.iconName()}
				style={{
					...styles.starButton,
					...this.iconStyle()
				}}
				onClick={onStarButtonClick}
			/>
		);
	}

	render() {
		const {
			coverImage,
			latestChapter,
			comicName,
			showStarButton
		} = this.props;

		return(

					{ showStarButton ? this.renderStarButton() : null }

					<Link to={`/reader/dm5/${latestChapter}`}>

					</Link>
					<Link to={`/reader/dm5/${latestChapter}`} style={styles.link}>
						{comicName}
					</Link>

		);
	}
}
<|endoftext|>


<commit_before>import $ from 'jquery'
import dateFns from 'date-fns'
import InfoPanel from './info-panel'
import React from 'react'
import ReactDOM from 'react-dom'
import {addPete, isKohtaSataa} from './pete'
import {createMap, panTo, showRadarFrame} from './map'

const FRAME_DELAY_MS = 500
const FRAME_LOOP_DELAY_MS = 5000
const FRAME_LIST_RELOAD_MS = 2 * 60 * 1000

class SataakoApp extends React.Component {
  constructor() {
    super()
    this.state = {
      currentFrame: null,
      currentFrameIndex: 0,
      frames: [],
      mapSettings: {
        x: Number(localStorage.getItem('sataako-fi-x')) || 2776307.5078,
        y: Number(localStorage.getItem('sataako-fi-y')) || 8438349.32742,
        zoom: Number(localStorage.getItem('sataako-fi-zoom')) || 7
      }
    }
  }

  componentDidMount() {
    this.map = createMap(this.state.mapSettings)
    this.map.on('moveend', this.onMapMove.bind(this, null))

    this.reloadFramesList()
    this.animateRadar()
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(this.onLocation.bind(this))
    }

    if (isKohtaSataa()) {
      addPete(this.map)
    }
  }

  render() {
    const radarFrameTimestamp = this.state.currentFrame ? dateFns.format(this.state.currentFrame.timestamp, 'D.M. HH:mm') : ''

    return (

{this.renderFrameImages()}

{radarFrameTimestamp}

        <InfoPanel/>

    )
  }

  renderFrameImages() {
    return this.state.frames.map(frame => )
  }

  reloadFramesList() {
    $.get('/frames.json', frames => this.setState({frames}))
    window.setTimeout(this.reloadFramesList.bind(this, null), FRAME_LIST_RELOAD_MS)
  }

  animateRadar() {
    let delayMs = FRAME_DELAY_MS

    if (this.state.frames.length > 0) {
      if (this.state.currentFrameIndex >= this.state.frames.length) {
        this.setState({currentFrameIndex: 0})
        delayMs = FRAME_LOOP_DELAY_MS
      } else {
        const currentFrame = this.state.frames[this.state.currentFrameIndex];
        showRadarFrame(this.map, currentFrame.image)
        this.setState({currentFrame, currentFrameIndex: this.state.currentFrameIndex + 1})
      }
    }

    window.setTimeout(this.animateRadar.bind(this, null), delayMs)
  }

  onLocation(geolocationResponse) {
    panTo(this.map, [geolocationResponse.coords.longitude, geolocationResponse.coords.latitude])
  }

  onMapMove() {
    const [x, y] = this.map.getView().getCenter()
    const zoom = this.map.getView().getZoom()

    localStorage.setItem('sataako-fi-x', x)
    localStorage.setItem('sataako-fi-y', y)
    localStorage.setItem('sataako-fi-zoom', zoom)
  }
}

ReactDOM.render(<SataakoApp />, document.getElementById('app'))
<commit_msg>Fix eslint security warning<commit_after>import $ from 'jquery'
import dateFns from 'date-fns'
import InfoPanel from './info-panel'
import React from 'react'
import ReactDOM from 'react-dom'
import {addPete, isKohtaSataa} from './pete'
import {createMap, panTo, showRadarFrame} from './map'

const FRAME_DELAY_MS = 500
const FRAME_LOOP_DELAY_MS = 5000
const FRAME_LIST_RELOAD_MS = 2 * 60 * 1000

class SataakoApp extends React.Component {
  constructor() {
    super()
    this.state = {
      currentFrame: null,
      currentFrameIndex: 0,
      frames: [],
      mapSettings: {
        x: Number(localStorage.getItem('sataako-fi-x')) || 2776307.5078,
        y: Number(localStorage.getItem('sataako-fi-y')) || 8438349.32742,
        zoom: Number(localStorage.getItem('sataako-fi-zoom')) || 7
      }
    }
  }

  componentDidMount() {
    this.map = createMap(this.state.mapSettings)
    this.map.on('moveend', this.onMapMove.bind(this, null))

    this.reloadFramesList()
    this.animateRadar()
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(this.onLocation.bind(this))
    }

    if (isKohtaSataa()) {
      addPete(this.map)
    }
  }

  render() {
    const radarFrameTimestamp = this.state.currentFrame ? dateFns.format(this.state.currentFrame.timestamp, 'D.M. HH:mm') : ''

    return (

{this.renderFrameImages()}

{radarFrameTimestamp}

        <InfoPanel/>

    )
  }

  renderFrameImages() {
    return this.state.frames.map(frame => )
  }

  reloadFramesList() {
    $.get('/frames.json', frames => this.setState({frames}))
    window.setTimeout(this.reloadFramesList.bind(this, null), FRAME_LIST_RELOAD_MS)
  }

  animateRadar() {
    let delayMs = FRAME_DELAY_MS

    if (this.state.frames.length > 0) {
      if (this.state.currentFrameIndex >= this.state.frames.length) {
        this.setState({currentFrameIndex: 0})
        delayMs = FRAME_LOOP_DELAY_MS
      } else {
        const currentFrame = this.state.frames[this.state.currentFrameIndex];
        showRadarFrame(this.map, currentFrame.image)
        this.setState({currentFrame, currentFrameIndex: this.state.currentFrameIndex + 1})
      }
    }

    window.setTimeout(this.animateRadar.bind(this, null), delayMs)
  }

  onLocation(geolocationResponse) {
    panTo(this.map, [geolocationResponse.coords.longitude, geolocationResponse.coords.latitude])
  }

  onMapMove() {
    const [x, y] = this.map.getView().getCenter()
    const zoom = this.map.getView().getZoom()

    localStorage.setItem('sataako-fi-x', x)
    localStorage.setItem('sataako-fi-y', y)
    localStorage.setItem('sataako-fi-zoom', zoom)
  }
}

ReactDOM.render(<SataakoApp />, document.getElementById('app'))
<|endoftext|>


<commit_before>/**
 * jQuery Mustache Plugin v0.2.3
 *
 * @author Jonny Reeves (http://jonnyreeves.co.uk/)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
 * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*global jQuery, window */
(function ($, window) {
    'use strict';

    var templateMap = {},
		instance = null,
		options = {
			// Should an error be thrown if an attempt is made to render a non-existent template.  If false, the
			// operation will fail silently.
			warnOnMissingTemplates: false,

			// Should an error be thrown if an attempt is made to overwrite a template which has already been added.
			// If true the original template will be overwritten with the new value.
			allowOverwrite: true,

			// The 'type' attribute which you use to denoate a Mustache Template in the DOM; eg:
			// `<script type="text/html" id="my-template"></script>`
			domTemplateType: 'text/html'
		};

	function getMustache() {
		// Lazily retrieve Mustache from the window global if it hasn't been defined by
		// the User.
		if (instance === null) {
			instance = window.Mustache;
			if (typeof (instance) === 'undefined') {
				$.error("Failed to locate Mustache instance, are you sure it has been loaded?");
			}
		}
		return instance;
	}

	/**
	 * Returns true if the supplied templateName has been added.
	 */
	function has(templateName) {
		return typeof (templateMap[templateName]) !== 'undefined';
	}

	/**
	 * Registers a template so that it can be used by $.Mustache.
	 *
	 * @param templateName		A name which uniquely identifies this template.
	 * @param templateHtml		The HTML which makes us the template; this will be rendered by Mustache when render()
	 *							is invoked.
	 * @throws					If options.allowOverwrite is false and the templateName has already been registered.
	 */
	function add(templateName, templateHtml) {
		if (!options.allowOverwrite && has(templateName)) {
			$.error('TemplateName: ' + templateName + ' is already mapped.');
			return;
		}
		templateMap[templateName] = $.trim(templateHtml);
	}

	/**
	 * Adds one or more tempaltes from the DOM using either the supplied templateElementIds or by retrieving all script
	 * tags of the 'domTemplateType'.  Templates added in this fashion will be registered with their elementId value.
	 *
	 * @param templateElementId	ElementId of a script block which contains the template you wish to add
	 */
	function addFromDom() {
		var templateElementIds;

		// If no args are supplied, all script blocks will be read from the document.
		if (arguments.length === 0) {
			templateElementIds = $('body script').filter('[type="' + options.domTemplateType + '"]').map(function () {
				return this.id;
			});
		}
		else {
			templateElementIds = $.makeArray(arguments);
		}

		$.each(templateElementIds, function() {
			var templateHtml = $('#' + this).html();
			if (templateHtml === void 0) {
				$.error('No such elementId: #' + this);
			}
			else {
				add(this, templateHtml);
			}
		});
	}

	/**
	 * Removes a template, the contents of the removed Template will be returned.
	 *
	 * @param templateName		The name of the previously registered Mustache template that you wish to remove.
	 * @returns					String which represents the raw content of the template.
	 */
	function remove(templateName) {
		var result = templateMap[templateName];
		delete templateMap[templateName];
		return result;
	}

	/**
	 * Removes all templates and tells Mustache to flush its cache.
	 */
	function clear() {
		templateMap = {};
		getMustache().clearCache();
	}

	/**
	 * Renders a previously added Mustache template using the supplied templateData object.  Note if the supplied
	 * templateName doesn't exist an empty String will be returned.
	 */
	function render(templateName, templateData) {
		if (!has(templateName)) {
			if (options.warnOnMissingTemplates) {
				$.error('No template registered for: ' + templateName);
			}
			return '';
		}
		return getMustache().to_html(templateMap[templateName], templateData, templateMap);
	}

	/**
	 * Loads the external Mustache templates located at the supplied URL and registers them for later use.  This method
	 * returns a jQuery Promise and also support an `onComplete` callback.
	 *
	 * @param url			URL of the external Mustache template file to load.
	 * @param onComplete	Optional callback function which will be invoked when the templates from the supplied URL
	 *						have been loaded and are ready for use.
	 * @returns				jQuery deferred promise which will complete when the templates have been loaded and are
	 *						ready for use.
	 */
	function load(url, onComplete) {
		return $.Deferred(function (dfd) {
			$.get(url)
				.done(function (templates) {
					$(templates).filter('script').each(function (i, el) {
						add(el.id, $(el).html());
					});

					if ($.isFunction(onComplete)) {
						onComplete();
					}
					dfd.resolve();
				})
				.fail(dfd.reject);
		}).promise();
	}

	/**
	 * Returns an Array of templateNames which have been registered and can be retrieved via
	 * $.Mustache.render() or $(element).mustache().
	 */
	function templates() {
		return $.map(templateMap, function (value, key) {
			return key;
		});
	}

	// Expose the public methods on jQuery.Mustache
	$.Mustache = {
		options: options,
		load: load,
		add: add,
		addFromDom: addFromDom,
		remove: remove,
		clear: clear,
		render: render,
		templates: templates,
		instance: instance
	};

	/**
	 * Renders one or more viewModels into the current jQuery element.
	 *
	 * @param templateName	The name of the Mustache template you wish to render, Note that the
	 *						Template must have 	been previously loaded and / or added.
	 * @param templateData	One or more JavaScript objects which will be used to render the Mustache
	 * 						template.
	 * @param options.method	jQuery method to use when rendering, defaults to 'append'.
	 */
	$.fn.mustache = function (templateName, templateData, options) {
        var settings = $.extend({
			method:	'append'
		}, options);

		var renderTemplate = function (obj, viewModel) {
			$(obj)[settings.method](render(templateName, viewModel));
		};

		return this.each(function () {
			var element = this;

			// Render a collection of viewModels.
			if ($.isArray(templateData)) {
				$.each(templateData, function () {
					renderTemplate(element, this);
				});
			}

			// Render a single viewModel.
			else {
				renderTemplate(element, templateData);
			}
		});
	};

}(jQuery, window));<commit_msg>Consistent undefined check<commit_after>/**
 * jQuery Mustache Plugin v0.2.3
 *
 * @author Jonny Reeves (http://jonnyreeves.co.uk/)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
 * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*global jQuery, window */
(function ($, window) {
    'use strict';

    var templateMap = {},
		instance = null,
		options = {
			// Should an error be thrown if an attempt is made to render a non-existent template.  If false, the
			// operation will fail silently.
			warnOnMissingTemplates: false,

			// Should an error be thrown if an attempt is made to overwrite a template which has already been added.
			// If true the original template will be overwritten with the new value.
			allowOverwrite: true,

			// The 'type' attribute which you use to denoate a Mustache Template in the DOM; eg:
			// `<script type="text/html" id="my-template"></script>`
			domTemplateType: 'text/html'
		};

	function getMustache() {
		// Lazily retrieve Mustache from the window global if it hasn't been defined by
		// the User.
		if (instance === null) {
			instance = window.Mustache;
			if (instance === void 0) {
				$.error("Failed to locate Mustache instance, are you sure it has been loaded?");
			}
		}
		return instance;
	}

	/**
	 * Returns true if the supplied templateName has been added.
	 */
	function has(templateName) {
		return templateMap[templateName] !== void 0;
	}

	/**
	 * Registers a template so that it can be used by $.Mustache.
	 *
	 * @param templateName		A name which uniquely identifies this template.
	 * @param templateHtml		The HTML which makes us the template; this will be rendered by Mustache when render()
	 *							is invoked.
	 * @throws					If options.allowOverwrite is false and the templateName has already been registered.
	 */
	function add(templateName, templateHtml) {
		if (!options.allowOverwrite && has(templateName)) {
			$.error('TemplateName: ' + templateName + ' is already mapped.');
			return;
		}
		templateMap[templateName] = $.trim(templateHtml);
	}

	/**
	 * Adds one or more tempaltes from the DOM using either the supplied templateElementIds or by retrieving all script
	 * tags of the 'domTemplateType'.  Templates added in this fashion will be registered with their elementId value.
	 *
	 * @param templateElementId	ElementId of a script block which contains the template you wish to add
	 */
	function addFromDom() {
		var templateElementIds;

		// If no args are supplied, all script blocks will be read from the document.
		if (arguments.length === 0) {
			templateElementIds = $('body script').filter('[type="' + options.domTemplateType + '"]').map(function () {
				return this.id;
			});
		}
		else {
			templateElementIds = $.makeArray(arguments);
		}

		$.each(templateElementIds, function() {
			var templateHtml = $('#' + this).html();
			if (templateHtml === void 0) {
				$.error('No such elementId: #' + this);
			}
			else {
				add(this, templateHtml);
			}
		});
	}

	/**
	 * Removes a template, the contents of the removed Template will be returned.
	 *
	 * @param templateName		The name of the previously registered Mustache template that you wish to remove.
	 * @returns					String which represents the raw content of the template.
	 */
	function remove(templateName) {
		var result = templateMap[templateName];
		delete templateMap[templateName];
		return result;
	}

	/**
	 * Removes all templates and tells Mustache to flush its cache.
	 */
	function clear() {
		templateMap = {};
		getMustache().clearCache();
	}

	/**
	 * Renders a previously added Mustache template using the supplied templateData object.  Note if the supplied
	 * templateName doesn't exist an empty String will be returned.
	 */
	function render(templateName, templateData) {
		if (!has(templateName)) {
			if (options.warnOnMissingTemplates) {
				$.error('No template registered for: ' + templateName);
			}
			return '';
		}
		return getMustache().to_html(templateMap[templateName], templateData, templateMap);
	}

	/**
	 * Loads the external Mustache templates located at the supplied URL and registers them for later use.  This method
	 * returns a jQuery Promise and also support an `onComplete` callback.
	 *
	 * @param url			URL of the external Mustache template file to load.
	 * @param onComplete	Optional callback function which will be invoked when the templates from the supplied URL
	 *						have been loaded and are ready for use.
	 * @returns				jQuery deferred promise which will complete when the templates have been loaded and are
	 *						ready for use.
	 */
	function load(url, onComplete) {
		return $.Deferred(function (dfd) {
			$.get(url)
				.done(function (templates) {
					$(templates).filter('script').each(function (i, el) {
						add(el.id, $(el).html());
					});

					if ($.isFunction(onComplete)) {
						onComplete();
					}
					dfd.resolve();
				})
				.fail(dfd.reject);
		}).promise();
	}

	/**
	 * Returns an Array of templateNames which have been registered and can be retrieved via
	 * $.Mustache.render() or $(element).mustache().
	 */
	function templates() {
		return $.map(templateMap, function (value, key) {
			return key;
		});
	}

	// Expose the public methods on jQuery.Mustache
	$.Mustache = {
		options: options,
		load: load,
		add: add,
		addFromDom: addFromDom,
		remove: remove,
		clear: clear,
		render: render,
		templates: templates,
		instance: instance
	};

	/**
	 * Renders one or more viewModels into the current jQuery element.
	 *
	 * @param templateName	The name of the Mustache template you wish to render, Note that the
	 *						Template must have 	been previously loaded and / or added.
	 * @param templateData	One or more JavaScript objects which will be used to render the Mustache
	 * 						template.
	 * @param options.method	jQuery method to use when rendering, defaults to 'append'.
	 */
	$.fn.mustache = function (templateName, templateData, options) {
        var settings = $.extend({
			method:	'append'
		}, options);

		var renderTemplate = function (obj, viewModel) {
			$(obj)[settings.method](render(templateName, viewModel));
		};

		return this.each(function () {
			var element = this;

			// Render a collection of viewModels.
			if ($.isArray(templateData)) {
				$.each(templateData, function () {
					renderTemplate(element, this);
				});
			}

			// Render a single viewModel.
			else {
				renderTemplate(element, templateData);
			}
		});
	};

}(jQuery, window));<|endoftext|>


<commit_before>const riot = require('riot');
const route = require('riot-route').default;
const WebSocketEvents = require('./helpers/web-socket-events');
const StreamingRest = require('./helpers/streaming-rest');
const fetchJson = require('./helpers/fetch-json');
const loadTags = require('./helpers/load-tags');

/** サーバから渡されたmetaタグのパラメータを読み込みます */
const loadParams = () => {
	const element = document.getElementsByName('frost-params').item(0);
	return element != null ? JSON.parse(element.content) : {};
};

/** reCAPTCHAが利用可能になるまで待機します */
const waitRecaptcha = (siteKey) => new Promise((resolve) => {
	const t = setInterval(() => {
		if (siteKey == null || typeof grecaptcha != 'undefined') {
			clearInterval(t);
			resolve();
		}
	}, 50);
});

const fetchUser = async (streamingRest, userId) => {
	const result = await streamingRest.request('get', `/users/${userId}`);
	if (result.statusCode != 200) {
		throw new Error('failed to fetch user');
	}
	return result.response.user;
};

(async () => {
	const mixin = loadParams();
	mixin.config = require('../../.configs/client-config.json');
	mixin.getLoginStatus = () => mixin.userId != null;
	mixin.central = riot.observable();

	// when logged in
	if (mixin.getLoginStatus()) {

		// WebSocket
		const secure = location.protocol == 'https:';
		try {
			const accessToken = localStorage.getItem('accessToken');
			const webSocket = await WebSocketEvents.connect(`${secure ? 'wss' : 'ws'}://${mixin.config.apiHost}?access_token=${accessToken}`);
			webSocket.addEventListener('close', (ev) => { console.log('close:', ev); });
			webSocket.addEventListener('error', (ev) => { console.log('error:', ev); });
			WebSocketEvents.init(webSocket);
			mixin.webSocket = webSocket;
		}
		catch (err) {
			alert('WebSocketの接続に失敗しました');
			console.log(err);
			// logout as a precaution
			await fetchJson('DELETE', '/session', {
				_csrf: mixin.csrf
			});
			return;
		}

		// Streaming REST
		mixin.streamingRest = new StreamingRest(mixin.webSocket);

		try {
			mixin.user = await fetchUser(mixin.streamingRest, mixin.userId);
		}
		catch (err) {
			alert('ユーザー情報の取得に失敗しました');
			console.log(err);
			return;
		}
	}

	// routings

	const changePage = (pageId, params) => {
		mixin.central.trigger('change-page', pageId, params || {});
	};

	route.base('/');

	route('', () =>
		changePage(mixin.getLoginStatus() ? 'home' : 'entrance'));

	route('general', () => {
		if (!mixin.getLoginStatus()) {
			changePage('error', { message: 'forbidden' });
			return;
		}
		changePage('home', { timelineType: 'general' });
	});

	route('dev', () =>
		changePage('dev'));

	route('userlist', () =>
		changePage('userlist'));

	route('users/*', (screenName) =>
		changePage('user', { screenName }));

	route('posts/*', (postId) =>
		changePage('post', { postId }));

	route('oauth/authorize..', () =>
		changePage('appauth'));

	route('*', () =>
		changePage('error', { message: 'page not found' }));

	loadTags();

	await waitRecaptcha(mixin.siteKey);

	// mount riot tags
	riot.mixin(mixin);
	riot.mount('frost-container');

	route.start(true);

	// move to error page by content of error metaData
	if (mixin.error != null) {
		changePage('error', {});
	}

})().catch(err => {
	console.log('何かがおかしいよ');
	console.log(err);
});

require('./styles/page.scss');
<commit_msg>page parameter<commit_after>const riot = require('riot');
const route = require('riot-route').default;
const WebSocketEvents = require('./helpers/web-socket-events');
const StreamingRest = require('./helpers/streaming-rest');
const fetchJson = require('./helpers/fetch-json');
const loadTags = require('./helpers/load-tags');

/** サーバから渡されたmetaタグのパラメータを読み込みます */
const loadParams = () => {
	const element = document.getElementsByName('frost-params').item(0);
	return element != null ? JSON.parse(element.content) : {};
};

/** reCAPTCHAが利用可能になるまで待機します */
const waitRecaptcha = (siteKey) => new Promise((resolve) => {
	const t = setInterval(() => {
		if (siteKey == null || typeof grecaptcha != 'undefined') {
			clearInterval(t);
			resolve();
		}
	}, 50);
});

const fetchUser = async (streamingRest, userId) => {
	const result = await streamingRest.request('get', `/users/${userId}`);
	if (result.statusCode != 200) {
		throw new Error('failed to fetch user');
	}
	return result.response.user;
};

(async () => {
	const mixin = loadParams();
	mixin.config = require('../../.configs/client-config.json');
	mixin.getLoginStatus = () => mixin.userId != null;
	mixin.central = riot.observable();

	// when logged in
	if (mixin.getLoginStatus()) {

		// WebSocket
		const secure = location.protocol == 'https:';
		try {
			const accessToken = localStorage.getItem('accessToken');
			const webSocket = await WebSocketEvents.connect(`${secure ? 'wss' : 'ws'}://${mixin.config.apiHost}?access_token=${accessToken}`);
			webSocket.addEventListener('close', (ev) => { console.log('close:', ev); });
			webSocket.addEventListener('error', (ev) => { console.log('error:', ev); });
			WebSocketEvents.init(webSocket);
			mixin.webSocket = webSocket;
		}
		catch (err) {
			alert('WebSocketの接続に失敗しました');
			console.log(err);
			// logout as a precaution
			await fetchJson('DELETE', '/session', {
				_csrf: mixin.csrf
			});
			return;
		}

		// Streaming REST
		mixin.streamingRest = new StreamingRest(mixin.webSocket);

		try {
			mixin.user = await fetchUser(mixin.streamingRest, mixin.userId);
		}
		catch (err) {
			alert('ユーザー情報の取得に失敗しました');
			console.log(err);
			return;
		}
	}

	// routings

	const changePage = (pageId, params) => {
		mixin.central.trigger('change-page', pageId, params || {});
	};

	route.base('/');

	route('', () =>
		changePage(mixin.getLoginStatus() ? 'home' : 'entrance'));

	route('general', () => {
		if (!mixin.getLoginStatus()) {
			changePage('error', { message: 'forbidden' });
			return;
		}
		changePage('home', { timelineType: 'general' });
	});

	route('dev', () =>
		changePage('dev'));

	route('userlist', () =>
		changePage('userlist'));

	route('users/*', (screenName) =>
		changePage('user', { screenName }));

	route('posts/*', (postId) =>
		changePage('post', { postId }));

	route('oauth/authorize..', () =>
		changePage('appauth'));

	route('*', () =>
		changePage('error', { message: 'page not found' }));

	loadTags();

	await waitRecaptcha(mixin.siteKey);

	// mount riot tags
	riot.mixin(mixin);
	riot.mount('frost-container');

	route.start(true);

	// move to error page by content of error metaData
	if (mixin.error != null) {
		changePage('error');
	}
	// move to target page by content of page metadata
	else if (mixin.page != null) {
		changePage(mixin.page);
	}

})().catch(err => {
	console.log('何かがおかしいよ');
	console.log(err);
});

require('./styles/page.scss');
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import documents from '../../data/documents.json';

export default class Documents extends Component {
    render(){
        return (

Documents/Claims

                            Find all documents/claims for transactions below.

                        <button type="submit" className="btn btn-wide btn-o btn-primary">
                            Sign
                        </button>

                                    Select
                                    Document
                                    Created
                                    From
                                    Signed
                                    Approved

                                    <input type="checkbox"/>

                                            Blanket Authorization Form

                                    04-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Closing Package - Promissory Note

                                    01-01-2017
                                    CCU - 2

                                    <input type="checkbox"/>

                                            Closing Package - Confirmation of Deposits, Employment, Credit History

                                    01-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Closing Package - Compliance Agreement

                                    01-01-2016
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Initial Disclosure

                                    03-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Contract 4

                                    04-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Disclosure 1

                                    04-15-2017
                                    Core Credit Union

                            <button type="submit" className="btn btn-wide btn-o btn-primary">
                                Sign
                            </button>

                { this.renderDocuments() }

        );
    }

    renderDocument (document) {
        return (

{document.title}

        )
    }

    renderDocuments () {
        return (

                { documents.payload.map((document) => this.renderDocument(document)) }

        )
    }

}
<commit_msg>Added security for anchor links<commit_after>import React, { Component } from 'react';

import documents from '../../data/documents.json';

export default class Documents extends Component {
    render(){
        return (

                            Documents/Claims

                            Find all documents/claims for transactions below.

                        <button type="submit" className="btn btn-wide btn-o btn-primary">
                            Sign
                        </button>

                                    Select
                                    Document
                                    Created
                                    From
                                    Signed
                                    Approved

                                    <input type="checkbox"/>

                                            Blanket Authorization Form

                                    04-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Closing Package - Promissory Note

                                    01-01-2017
                                    CCU - 2

                                    <input type="checkbox"/>

                                            Closing Package - Confirmation of Deposits, Employment, Credit History

                                    01-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Closing Package - Compliance Agreement

                                    01-01-2016
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Initial Disclosure

                                    03-01-2017
                                    Core Credit Union

                                    <input type="checkbox"/>

                                            Contract 4

                                    04-01-2017
                                    3rd Party

                                    <input type="checkbox"/>

                                            Disclosure 1

                                    04-15-2017
                                    Core Credit Union

                            <button type="submit" className="btn btn-wide btn-o btn-primary">
                                Sign
                            </button>

                { this.renderDocuments() }

        );
    }

    renderDocument (document) {
        return (

{document.title}

        )
    }

    renderDocuments () {
        return (

                { documents.payload.map((document) => this.renderDocument(document)) }

        )
    }

}
<|endoftext|>


<commit_before>const ChromePreviewProvider = require("addon-chrome/ChromePreviewProvider");
const db = require("addon-chrome/db");
const {BLOCKED_URL, SEARCH_RESULT_REGEX, LOCALHOST_REGEX, BROWSER_RESOURCE_REGEX} = require("addon-chrome/constants");
const _ = require("lodash/collection");

module.exports = class ChromePlacesProvider {
  /**
   * Get a list of top frecent sites from your history
   *
   * @returns {Object} Promise that resolves with a list of top frecent sites
   */
  static topFrecentSites() {
    return new Promise((resolve) => {
      this.getHistory().then((histories) => {
        const topFrecentSites = this._getTopFrecentSites(histories);
        resolve(topFrecentSites);
      });
    });
  }

  /**
   * Get either all the bookmarks or bookmarks before a specified date
   *
   * @param {Object} options - Search options config
   * @param {Number} options.beforeDate - Only get bookmarks before this date, represented in milliseconds
   * @returns {Object} Promise that resolves with the bookmarks sorted ascendingly by the date the bookmark is added
   */
  static recentBookmarks(options) {
    return new Promise((resolve) => {
      this._getBookmarks(options).then((bookmarks) => {
        let rows = bookmarks;
        rows.sort((a, b) => {
          if (a.dateAdded > b.dateAdded) {
            return -1;
          }
          if (a.dateAdded < b.dateAdded) {
            return 1;
          }
          return 0;
        });
        resolve(rows);
      });
    });
  }

  /**
   * Get the recent history items or history items before a specified date
   *
   * @param {Object} options - Search options config
   * @param {Number} options.startTime - Only get history items visited before this date, represented in milliseconds
   * @returns {Object} Promise that resolves with histories
   */
  static recentLinks(options) {
    return new Promise((resolve) => {
      this.getHistory(options).then(resolve);
    });
  }

  /**
   * Get a list of highlights
   *
   * @returns {Object} Promise that resolves with a list of highlight items
   */
  static highlightsLinks() {
    return new Promise((resolve) => {
      this._getHighlights().then(resolve);
    });
  }

  /**
   * Proxy method to ChromePreviewProvider's removeMetadata
   *
   * @param {string} url - Item's url
   */
  static removeMetadata(url) {
    ChromePreviewProvider.removeMetadata(url);
  }

  /**
  * Proxy method to ChromePreviewProvider's cacheMetadata
  *
  * @param {Object} site - Site to get metadata for
  */
  static cacheMetadata(site) {
    ChromePreviewProvider.cacheMetadata(site);
  }

  /**
   * Add blocked urls to db
   *
   * @param {string} url - Url that is marked for block
   * @returns {Object} Promise that resolves when the db is done adding or updating
   */
  static addBlockedUrl(url) {
    return db.addOrUpdateExisting(BLOCKED_URL, {url});
  }

  /**
   * Remove blocked urls from db
   *
   * @returns {Object} Promise that resolves when the db transaction is done removing all
   */
  static unblockAllUrl() {
    return db.removeAll(BLOCKED_URL);
  }

  /**
   * Query Chrome bookmark api for a list of bookmar items with specified config
   *
   * @param {Object} options - Config object for @{link _collectBookmarks}
   *
   * @returns {Object} Promise that resolves with bookmarks
   */
  static _getBookmarks(options) {
    return new Promise((resolve) => {
      chrome.bookmarks.getTree((trees) => {
        let rawBookmarks = [];
        this._collectBookmarks(trees, rawBookmarks, options);
        const bookmarks = rawBookmarks.map(this._transformBookmark);
        this._filterBlockedUrls(bookmarks).then(resolve);
      });
    });
  }

  /**
   * Query Chrome history api for a list of history items with specified config
   *
   * @param {Object} options - Search config
   * @param {String} options.text - Text search for history. Default to empty text
   * @param {Number} options.startTime - Only search for history visited after this time. Default to a year ago
   * @param {Number} options.endTime - Only search for history visited before this time. Default to today
   * @param {Number} options.maxResults - Maximum number of history items to return. Default to 20
   *
   * @returns {Object} Promise that resolves with histories
   */
  static getHistory(options) {
    const today = new Date().getTime();
    const aYear = 365 * 24 * 60 * 60 * 1000;
    const aYearAgo = today - aYear;
    const maxResults = 20;
    const defaultOption = {
      text: "",
      startTime: aYearAgo,
      endTime: today,
      maxResults
    };
    const searchOptions = options ? Object.assign(defaultOption, options) : defaultOption;
    const startTime = searchOptions.startTime;
    const endTime = searchOptions.endTime;
    return new Promise((resolve) => {
      chrome.history.search(searchOptions, (results) => {
        if (startTime && endTime) {
          // api uses start and end time as OR instead of AND
          // so filter it for now or we could just use this ???
          results = results.filter((result) => result.lastVisitTime > startTime && result.lastVisitTime < endTime);
        }
        this._getBookmarks()
          .then((bookmarks) => {
            const histories = results.map((result) => this._transformHistory(result, bookmarks));
            this._filterBlockedUrls(histories).then(resolve);
          });
      });
    });
  }

  /**
   * Rank history items (excluding google searches and localhost sites) by frecency score, then lastVisitTime, then url
   *
   * @param {Array} histories - List of history items
   *
   * @returns {Object} Promise that resolves with list of history items ranked by their frencency, lastVisitTime, url
   */
  static _getTopFrecentSites(histories) {
    return new Promise((resolve) => {
      const searchResultRegex = new RegExp(SEARCH_RESULT_REGEX);
      const localhostRegex = new RegExp(LOCALHOST_REGEX);
      const browserResourceRegex = new RegExp(BROWSER_RESOURCE_REGEX);
      const filterRegex = new RegExp(searchResultRegex.source + "|" + localhostRegex.source + "|" + browserResourceRegex.source);

      const rows = histories
        .filter((hist) => !filterRegex.test(hist.url))
        .map((hist) => {
          const frencency = this._calculateFrecency(hist);
          return Object.assign(hist, {frencency});
        })
        .sort((a, b) => {
          if (a.frencency > b.frencency) {
            return -1;
          }
          if (a.frencency < b.frencency) {
            return 1;
          }
          if (a.lastVisitTime > b.lastVisitTime) {
            return -1;
          }
          if (a.url > b.url) {
            return -1;
          }
          return 0;
        });

      resolve(rows);
    });
  }

  /**
   * Calculate frecency scores a history items base on
   * https://dxr.mozilla.org/mozilla-central/source/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java#124
   * numVisits * max(1, 100 * 225 / (age*age + 225))
   *
   * @param {Object} hist History item to calculate frecency score for
   *
   * @returns {Number} The calculated frecency for the history item
   */
  static _calculateFrecency(hist) {
    const microsecondsPerDay = 86400000000;
    const microsecondsToday = new Date().getTime() * 1000;
    const microsecondsLastVisitDate = hist.lastVisitDate * 1000;
    const age = (microsecondsToday - microsecondsLastVisitDate) / microsecondsPerDay;
    const frencency = hist.visitCount * Math.max(1, 100 * 225 / (age * age + 225));
    return frencency;
  }

  /**
   * Get a list of items from history and bookmarks that is a highlight item
   * Shuffle everytime for higher chance of getting different sets of highlights
   *
   * @returns {Object} Promise that resolves with a list of highlight items
   */
  static _getHighlights() {
    return new Promise((resolve) => {
      const bookmarkPromise = this._getBookmarks();
      const historyPromise = this.getHistory();

      Promise.all([bookmarkPromise, historyPromise]).then((results) => {
        const searchResultRegex = new RegExp(SEARCH_RESULT_REGEX);
        const localhostRegex = new RegExp(LOCALHOST_REGEX);
        const browserResourceRegex = new RegExp(BROWSER_RESOURCE_REGEX);
        const filterRegex = new RegExp(searchResultRegex.source + "|" + localhostRegex.source + "|" + browserResourceRegex.source);

        const bookmarks = results[0].filter((bookmarks) => !filterRegex.test(bookmarks.url));
        const histories = results[1].filter((hist) => !filterRegex.test(hist.url));

        const rows = bookmarks.concat(histories)
          .filter((item) => this._isHighlightItem(item));

        const shuffledRows = _.shuffle(rows);

        ChromePreviewProvider.getLinksMetadataForHighlights(shuffledRows)
          .then(resolve);
      });
    });
  }

  /**
   * Determine whether highlight satifies highlight criteria:
   * 1) Visited at least 30 minutes ago or have been added more than 3 days ago
   * 2) Visited at most 3 times
   *
   * @param {Object} item - Item to check highlight criteria against
   * @returns {Boolean} True if item satisify criteria to be a highlight item, false otherwise
   */
  static _isHighlightItem(item) {
    const threeDays = 3 * 24 * 60 * 60 * 1000;
    const thirtyMinutes = 30 * 60 * 1000;
    const today = new Date().getTime();
    const lastVisitDate = item.lastVisitDate;
    const dateAdded = item.dateAdded;
    let isThreeDaysOrOlder;
    let isThirtyMinutesOrOlder;
    if (dateAdded) {
      isThreeDaysOrOlder = today - dateAdded > threeDays;
    } else if (lastVisitDate) {
      isThirtyMinutesOrOlder = today - lastVisitDate > thirtyMinutes;
    }

    const isVisitCountAtMostThree = (item.visitCount || 0) <= 3;

    return (isThirtyMinutesOrOlder || isThreeDaysOrOlder) && isVisitCountAtMostThree;
  }

  /**
   * Filters out the blocked urls from a list of items
   *
   * @param {Array} items - List of items that needs to be filtered
   * @returns {Object} Promise that resolves with a list of urls that are not in the blocked list
   */
  static _filterBlockedUrls(items) {
    return new Promise((resolve) => {
      db.getAll(BLOCKED_URL)
        .then((blockedUrlItems) => {
          const blockedUrls = blockedUrlItems.map((blocked) => blocked.url);
          const nonBlockedUrls = items.filter((item) => blockedUrls.indexOf(item.url) === -1);
          resolve(nonBlockedUrls);
        });
    });
  }

  /**
   * Transform a raw history bookmark item into activity stream's history item,
   * merge the history item with the bookmark item that shares its url if it hasn't and merge its metadata
   *
   * @param {Object} hist - History item to be transformed
   * @param {Object} bookmarks - List of bookmarks
   * @returns {Object} Promise that resolves with the history item has been transformed
   */
  static _transformHistory(hist, bookmarks) {
    const mergedHist = this._mergeHistoryBookmark(hist, bookmarks);
    Object.assign(mergedHist, {
      favicon_url: "chrome://favicon/" + hist.url,
      lastVisitDate: parseInt(hist.lastVisitTime, 10)
    });
    return mergedHist;
  }

  /**
   * Transform a raw chrome bookmark item into activity stream's bookmark item and merge its metadata
   *
   * @param {Object} bookmark - Bookmark to be transformed
   * @returns {Object} Promise that resolves with the bookmark has been transformed
   */
  static _transformBookmark(bookmark) {
    Object.assign(bookmark, {
      favicon_url: "chrome://favicon/" + bookmark.url,
      bookmarkDateCreated: bookmark.dateAdded,
      bookmarkGuid: bookmark.id
    });
    return bookmark;
  }

  /**
   * Recursively traverse the tree of bookmarks and store only bookmark links in an array
   *
   * @param {Array} trees - Root of a Chrome bookmark tree item
   * @param {Array} bookmarks - List of bookmarks accumulated by traversing the tree
   * @param {Object} options - Config object
   * @param {Number} options.beforeDate - Only get bookmarks before this date, represented in milliseconds. Default to today
   * @param {Number} options.maxResult - Limit to return the number of bookmarks. Default to 20.
   */
  static _collectBookmarks(trees, bookmarks, {beforeDate = new Date().getTime(), maxResults = 20} = {}) {
    trees.forEach((tree) => {
      const hasExceededMaxResults = bookmarks.length >= maxResults;
      if (hasExceededMaxResults) {
        return;
      }

      const isBookmarkLink = !!tree.url;
      const isAddedBeforeDate = tree.dateAdded < beforeDate;
      if (isBookmarkLink && isAddedBeforeDate) {
        bookmarks.push(tree);
      }

      const hasMoreBookmarks = !!tree.children;
      if (hasMoreBookmarks) {
        this._collectBookmarks(tree.children, bookmarks, {beforeDate, maxResults});
      }
    });
  }

  /**
   * Merge a history item and a bookmark item that shares the same url into a single item
   *
   * @param {Object} hist - History item
   * @param {Array} bookmarks - List of bookmarks
   * @returns {Object} The merged history and bookmark item if they share the same url
   */
  static _mergeHistoryBookmark(hist, bookmarks) {
    const bookmarkUrls = bookmarks.map((bookmark) => bookmark.url);
    const index = bookmarkUrls.indexOf(hist.url);
    if (index > -1) {
      Object.assign(hist, {bookmarkGuid: bookmarks[index].bookmarkGuid});
    }
    return hist;
  }
};
<commit_msg>Fix bookmarks<commit_after>const ChromePreviewProvider = require("addon-chrome/ChromePreviewProvider");
const db = require("addon-chrome/db");
const {BLOCKED_URL, SEARCH_RESULT_REGEX, LOCALHOST_REGEX, BROWSER_RESOURCE_REGEX} = require("addon-chrome/constants");
const _ = require("lodash/collection");

module.exports = class ChromePlacesProvider {
  /**
   * Get a list of top frecent sites from your history
   *
   * @returns {Object} Promise that resolves with a list of top frecent sites
   */
  static topFrecentSites() {
    return new Promise((resolve) => {
      this.getHistory().then((histories) => {
        const topFrecentSites = this._getTopFrecentSites(histories);
        resolve(topFrecentSites);
      });
    });
  }

  /**
   * Get either all the bookmarks or bookmarks before a specified date
   *
   * @param {Object} options - Search options config
   * @param {Number} options.beforeDate - Only get bookmarks before this date, represented in milliseconds
   * @returns {Object} Promise that resolves with the bookmarks sorted ascendingly by the date the bookmark is added
   */
  static recentBookmarks(options) {
    return new Promise((resolve) => {
      this._getBookmarks(options).then((bookmarks) => {
        let rows = bookmarks;
        rows.sort((a, b) => {
          if (a.dateAdded > b.dateAdded) {
            return -1;
          }
          if (a.dateAdded < b.dateAdded) {
            return 1;
          }
          return 0;
        });
        resolve(rows);
      });
    });
  }

  /**
   * Get the recent history items or history items before a specified date
   *
   * @param {Object} options - Search options config
   * @param {Number} options.startTime - Only get history items visited before this date, represented in milliseconds
   * @returns {Object} Promise that resolves with histories
   */
  static recentLinks(options) {
    return new Promise((resolve) => {
      this.getHistory(options).then(resolve);
    });
  }

  /**
   * Get a list of highlights
   *
   * @returns {Object} Promise that resolves with a list of highlight items
   */
  static highlightsLinks() {
    return new Promise((resolve) => {
      this._getHighlights().then(resolve);
    });
  }

  /**
   * Proxy method to ChromePreviewProvider's removeMetadata
   *
   * @param {string} url - Item's url
   */
  static removeMetadata(url) {
    ChromePreviewProvider.removeMetadata(url);
  }

  /**
  * Proxy method to ChromePreviewProvider's cacheMetadata
  *
  * @param {Object} site - Site to get metadata for
  */
  static cacheMetadata(site) {
    ChromePreviewProvider.cacheMetadata(site);
  }

  /**
   * Add blocked urls to db
   *
   * @param {string} url - Url that is marked for block
   * @returns {Object} Promise that resolves when the db is done adding or updating
   */
  static addBlockedUrl(url) {
    return db.addOrUpdateExisting(BLOCKED_URL, {url});
  }

  /**
   * Remove blocked urls from db
   *
   * @returns {Object} Promise that resolves when the db transaction is done removing all
   */
  static unblockAllUrl() {
    return db.removeAll(BLOCKED_URL);
  }

  /**
   * Query Chrome bookmark api for a list of bookmar items with specified config
   *
   * @param {Object} options - Config object
   * @param {Number} options.beforeDate - Only get bookmarks before this date, represented in milliseconds. Defaults to today
   * @param {Number} options.maxResult - Limit to return the number of bookmarks. Defaults to 20.
   *
   * @returns {Object} Promise that resolves with bookmarks
   */
  static _getBookmarks({beforeDate = new Date().getTime(), maxResults = 20} = {}) {
    return new Promise((resolve) => {
      chrome.bookmarks.getTree((trees) => {
        let rawBookmarks = [];
        this._collectBookmarks(trees, rawBookmarks);
        const bookmarks = rawBookmarks
          .sort((a, b) => {
            if (a.dateAdded > b.dateAdded) {
              return -1;
            }
            if (a.dateAdded < b.dateAdded) {
              return 1;
            }
            return 0;
          })
          .filter((bookmark) => bookmark.dateAdded < beforeDate)
          .slice(0, maxResults)
          .map(this._transformBookmark);
        this._filterBlockedUrls(bookmarks).then(resolve);
      });
    });
  }

  /**
   * Query Chrome history api for a list of history items with specified config
   *
   * @param {Object} options - Search config
   * @param {String} options.text - Text search for history. Default to empty text
   * @param {Number} options.startTime - Only search for history visited after this time. Default to a year ago
   * @param {Number} options.endTime - Only search for history visited before this time. Default to today
   * @param {Number} options.maxResults - Maximum number of history items to return. Default to 20
   *
   * @returns {Object} Promise that resolves with histories
   */
  static getHistory(options) {
    const today = new Date().getTime();
    const aYear = 365 * 24 * 60 * 60 * 1000;
    const aYearAgo = today - aYear;
    const maxResults = 20;
    const defaultOption = {
      text: "",
      startTime: aYearAgo,
      endTime: today,
      maxResults
    };
    const searchOptions = options ? Object.assign(defaultOption, options) : defaultOption;
    const startTime = searchOptions.startTime;
    const endTime = searchOptions.endTime;
    return new Promise((resolve) => {
      chrome.history.search(searchOptions, (results) => {
        if (startTime && endTime) {
          // api uses start and end time as OR instead of AND
          // so filter it for now or we could just use this ???
          results = results.filter((result) => result.lastVisitTime > startTime && result.lastVisitTime < endTime);
        }
        this._getBookmarks()
          .then((bookmarks) => {
            const histories = results.map((result) => this._transformHistory(result, bookmarks));
            this._filterBlockedUrls(histories).then(resolve);
          });
      });
    });
  }

  /**
   * Rank history items (excluding google searches and localhost sites) by frecency score, then lastVisitTime, then url
   *
   * @param {Array} histories - List of history items
   *
   * @returns {Object} Promise that resolves with list of history items ranked by their frencency, lastVisitTime, url
   */
  static _getTopFrecentSites(histories) {
    return new Promise((resolve) => {
      const searchResultRegex = new RegExp(SEARCH_RESULT_REGEX);
      const localhostRegex = new RegExp(LOCALHOST_REGEX);
      const browserResourceRegex = new RegExp(BROWSER_RESOURCE_REGEX);
      const filterRegex = new RegExp(searchResultRegex.source + "|" + localhostRegex.source + "|" + browserResourceRegex.source);

      const rows = histories
        .filter((hist) => !filterRegex.test(hist.url))
        .map((hist) => {
          const frencency = this._calculateFrecency(hist);
          return Object.assign(hist, {frencency});
        })
        .sort((a, b) => {
          if (a.frencency > b.frencency) {
            return -1;
          }
          if (a.frencency < b.frencency) {
            return 1;
          }
          if (a.lastVisitTime > b.lastVisitTime) {
            return -1;
          }
          if (a.url > b.url) {
            return -1;
          }
          return 0;
        });

      resolve(rows);
    });
  }

  /**
   * Calculate frecency scores a history items base on
   * https://dxr.mozilla.org/mozilla-central/source/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java#124
   * numVisits * max(1, 100 * 225 / (age*age + 225))
   *
   * @param {Object} hist History item to calculate frecency score for
   *
   * @returns {Number} The calculated frecency for the history item
   */
  static _calculateFrecency(hist) {
    const microsecondsPerDay = 86400000000;
    const microsecondsToday = new Date().getTime() * 1000;
    const microsecondsLastVisitDate = hist.lastVisitDate * 1000;
    const age = (microsecondsToday - microsecondsLastVisitDate) / microsecondsPerDay;
    const frencency = hist.visitCount * Math.max(1, 100 * 225 / (age * age + 225));
    return frencency;
  }

  /**
   * Get a list of items from history and bookmarks that is a highlight item
   * Shuffle everytime for higher chance of getting different sets of highlights
   *
   * @returns {Object} Promise that resolves with a list of highlight items
   */
  static _getHighlights() {
    return new Promise((resolve) => {
      const bookmarkPromise = this._getBookmarks();
      const historyPromise = this.getHistory();

      Promise.all([bookmarkPromise, historyPromise]).then((results) => {
        const searchResultRegex = new RegExp(SEARCH_RESULT_REGEX);
        const localhostRegex = new RegExp(LOCALHOST_REGEX);
        const browserResourceRegex = new RegExp(BROWSER_RESOURCE_REGEX);
        const filterRegex = new RegExp(searchResultRegex.source + "|" + localhostRegex.source + "|" + browserResourceRegex.source);

        const bookmarks = results[0].filter((bookmarks) => !filterRegex.test(bookmarks.url));
        const histories = results[1].filter((hist) => !filterRegex.test(hist.url));

        const rows = bookmarks.concat(histories)
          .filter((item) => this._isHighlightItem(item));

        const shuffledRows = _.shuffle(rows);

        ChromePreviewProvider.getLinksMetadataForHighlights(shuffledRows)
          .then(resolve);
      });
    });
  }

  /**
   * Determine whether highlight satifies highlight criteria:
   * 1) Visited at least 30 minutes ago or have been added more than 3 days ago
   * 2) Visited at most 3 times
   *
   * @param {Object} item - Item to check highlight criteria against
   * @returns {Boolean} True if item satisify criteria to be a highlight item, false otherwise
   */
  static _isHighlightItem(item) {
    const threeDays = 3 * 24 * 60 * 60 * 1000;
    const thirtyMinutes = 30 * 60 * 1000;
    const today = new Date().getTime();
    const lastVisitDate = item.lastVisitDate;
    const dateAdded = item.dateAdded;
    let isThreeDaysOrOlder;
    let isThirtyMinutesOrOlder;
    if (dateAdded) {
      isThreeDaysOrOlder = today - dateAdded > threeDays;
    } else if (lastVisitDate) {
      isThirtyMinutesOrOlder = today - lastVisitDate > thirtyMinutes;
    }

    const isVisitCountAtMostThree = (item.visitCount || 0) <= 3;

    return (isThirtyMinutesOrOlder || isThreeDaysOrOlder) && isVisitCountAtMostThree;
  }

  /**
   * Filters out the blocked urls from a list of items
   *
   * @param {Array} items - List of items that needs to be filtered
   * @returns {Object} Promise that resolves with a list of urls that are not in the blocked list
   */
  static _filterBlockedUrls(items) {
    return new Promise((resolve) => {
      db.getAll(BLOCKED_URL)
        .then((blockedUrlItems) => {
          const blockedUrls = blockedUrlItems.map((blocked) => blocked.url);
          const nonBlockedUrls = items.filter((item) => blockedUrls.indexOf(item.url) === -1);
          resolve(nonBlockedUrls);
        });
    });
  }

  /**
   * Transform a raw history bookmark item into activity stream's history item,
   * merge the history item with the bookmark item that shares its url if it hasn't and merge its metadata
   *
   * @param {Object} hist - History item to be transformed
   * @param {Object} bookmarks - List of bookmarks
   * @returns {Object} Promise that resolves with the history item has been transformed
   */
  static _transformHistory(hist, bookmarks) {
    const mergedHist = this._mergeHistoryBookmark(hist, bookmarks);
    Object.assign(mergedHist, {
      favicon_url: "chrome://favicon/" + hist.url,
      lastVisitDate: parseInt(hist.lastVisitTime, 10)
    });
    return mergedHist;
  }

  /**
   * Transform a raw chrome bookmark item into activity stream's bookmark item and merge its metadata
   *
   * @param {Object} bookmark - Bookmark to be transformed
   * @returns {Object} Promise that resolves with the bookmark has been transformed
   */
  static _transformBookmark(bookmark) {
    Object.assign(bookmark, {
      favicon_url: "chrome://favicon/" + bookmark.url,
      bookmarkDateCreated: bookmark.dateAdded,
      bookmarkGuid: bookmark.id
    });
    return bookmark;
  }

  /**
   * Recursively traverse the tree of bookmarks and store only bookmark links in an array
   *
   * @param {Array} trees - Root of a Chrome bookmark tree item
   * @param {Array} bookmarks - List of bookmarks accumulated by traversing the tree
   */
  static _collectBookmarks(trees, bookmarks) {
    trees.forEach((tree) => {
      const isBookmarkLink = !!tree.url;
      if (isBookmarkLink) {
        bookmarks.push(tree);
      }

      const hasMoreBookmarks = !!tree.children;
      if (hasMoreBookmarks) {
        this._collectBookmarks(tree.children, bookmarks);
      }
    });
  }

  /**
   * Merge a history item and a bookmark item that shares the same url into a single item
   *
   * @param {Object} hist - History item
   * @param {Array} bookmarks - List of bookmarks
   * @returns {Object} The merged history and bookmark item if they share the same url
   */
  static _mergeHistoryBookmark(hist, bookmarks) {
    const bookmarkUrls = bookmarks.map((bookmark) => bookmark.url);
    const index = bookmarkUrls.indexOf(hist.url);
    if (index > -1) {
      Object.assign(hist, {bookmarkGuid: bookmarks[index].bookmarkGuid});
    }
    return hist;
  }
};
<|endoftext|>


<commit_before>const handleRedirect = ( routes, redirect ) => {
	let currentRoute = FlowRouter.getRouteName();
	if ( routes.indexOf( currentRoute ) > -1 ) {
		FlowRouter.go( redirect );
		return true;
	}
};

Template.default.helpers({
	loggingIn() {
		return Meteor.loggingIn();
	},
	authenticated() {
		return !Meteor.loggingIn() && Meteor.user();
	},
	redirectAuthenticated() {
	 	return handleRedirect([
			'login',
			'signup',
			'recover-password',
			'reset-password'
		], '/' );
	},
	redirectPublic() {
		return handleRedirect([
			'index',
			'dashboard'
		], '/login' );
	}
});
<commit_msg>Protect wish routes.<commit_after>const handleRedirect = ( routes, redirect ) => {
	let currentRoute = FlowRouter.getRouteName();
	if ( routes.indexOf( currentRoute ) > -1 ) {
		FlowRouter.go( redirect );
		return true;
	}
};

Template.default.helpers({
	loggingIn() {
		return Meteor.loggingIn();
	},
	authenticated() {
		return !Meteor.loggingIn() && Meteor.user();
	},
	redirectAuthenticated() {
	 	return handleRedirect([
			'login',
			'signup',
			'recover-password',
			'reset-password'
		], '/' );
	},
	redirectPublic() {
		return handleRedirect([
			'index',
			'dashboard',
      'wishNew',
      'wishDetail'
		], '/login' );
	}
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module Dependencies
 */

var _             = require('underscore');
var Twit          = require('twit');
var config        = require('../config/config');
var async         = require('async');
var cheerio       = require('cheerio');
var request       = require('request');
var graph         = require('fbgraph');
var tumblr        = require('tumblr.js');
var Github        = require('github-api');
var querystring   = require('querystring');
var LastFmNode    = require('lastfm').LastFmNode;
var paypal        = require('paypal-rest-sdk');
var passport      = require('passport');
var passportConf  = require('../config/passport');
var twilio        = require('twilio')(config.twilio.sid, config.twilio.token);
var foursquare    = require('node-foursquare')({ secrets: config.foursquare });

/**
 * API Controller
 */

module.exports.controller = function(app) {

 /**
   * GET /api*
   * *ALL* api routes must be authenticated first
   */

  app.all('/api*', passportConf.isAuthenticated);

  /**
   * GET /api
   * List of API examples.
   */

  app.get('/api', function(req, res) {
    res.render('api/index', {
      url: req.url
    });
  });

  /**
   * GET /api/lastfm
   * Last.fm API example.
   */

  app.get('/api/lastfm', function(req, res, next) {
    var lastfm = new LastFmNode(config.lastfm);
    async.parallel({
        artistInfo: function(done) {
          lastfm.request('artist.getInfo', {
            artist: 'Epica',
            handlers: {
              success: function(data) {
                done(null, data);
              },
              error: function(err) {
                done(err);
              }
            }
          });
        },
        artistTopAlbums: function(done) {
          lastfm.request('artist.getTopAlbums', {
            artist: 'Epica',
            handlers: {
              success: function(data) {
                var albums = [];
                _.each(data.topalbums.album, function(album) {
                  albums.push(album.image.slice( -1 )[0]['#text']);
                });
                done(null, albums.slice(0, 4));
              },
              error: function(err) {
                done(err);
              }
            }
          });
        }
      },
      function(err, results) {
        if (err) {
          return next(err.message);
        }
        var artist = {
          name: results.artistInfo.artist.name,
          image: results.artistInfo.artist.image.slice( -1 )[0]['#text'],
          tags: results.artistInfo.artist.tags.tag,
          bio: results.artistInfo.artist.bio.summary,
          stats: results.artistInfo.artist.stats,
          similar: results.artistInfo.artist.similar.artist,
          topAlbums: results.artistTopAlbums
        };
        res.render('api/lastfm', {
          artist: artist,
          url: '/apiopen'
        });
      });
  });

  /**
   * GET /api/nyt
   * New York Times API example.
   */

  app.get('/api/nyt', function(req, res, next) {
    var query = querystring.stringify({ 'api-key': config.nyt.key, 'list-name': 'young-adult' });
    var url = 'http://api.nytimes.com/svc/books/v2/lists?' + query;
    request.get(url, function(error, request, body) {
      if (request.statusCode === 403) {
        return next(error('Missing or Invalid New York Times API Key'));
      }
      var bestsellers = JSON.parse(body);
      res.render('api/nyt', {
        url: '/apiopen',
        books: bestsellers.results
      });
    });
  });

  /**
   * GET /api/paypal
   * PayPal SDK example.
   */

  app.get('/api/paypal', function(req, res, next) {
    paypal.configure(config.paypal);
    var payment_details = {
      'intent': 'sale',
      'payer': {
        'payment_method': 'paypal'
      },
      'redirect_urls': {
        'return_url': config.paypal.returnUrl,
        'cancel_url': config.paypal.cancelUrl
      },
      'transactions': [
        {
          'description': 'ITEM: Something Awesome!',
          'amount': {
            'currency': 'USD',
            'total': '2.99'
          }
        }
      ]
    };
    paypal.payment.create(payment_details, function(error, payment) {
      if (error) {
        // TODO FIXME
        console.log(error);
      } else {
        req.session.payment_id = payment.id;
        var links = payment.links;
        for (var i = 0; i < links.length; i++) {
          if (links[i].rel === 'approval_url') {
            res.render('api/paypal', {
              url: '/apilocked',
              approval_url: links[i].href
            });
          }
        }
      }
    });
  });

  /**
   * GET /api/paypal/success
   * PayPal SDK example.
   */

  app.get('/api/paypal/success', function(req, res, next) {
    var payment_id = req.session.payment_id;
    var payment_details = { 'payer_id': req.query.PayerID };
    paypal.payment.execute(payment_id, payment_details, function(error, payment) {
      if (error) {
        res.render('api/paypal', {
          url: req.url,
          result: true,
          success: false
        });
      } else {
        res.render('api/paypal', {
          url: '/apilocked',
          result: true,
          success: true
        });
      }
    });
  });

  /**
   * GET /api/paypal/cancel
   * PayPal SDK example.
   */

  app.get('/api/paypal/cancel', function(req, res, next) {
    req.session.payment_id = null;
    res.render('api/paypal', {
      url: '/apilocked',
      result: true,
      canceled: true
    });
  });

  /**
   * GET /api/scraping
   * Web scraping example using Cheerio library.
   */

  app.get('/api/scraping', function(req, res, next) {
    request.get('https://news.ycombinator.com/', function(err, request, body) {
      if (err) {
        return next(err);
      }
      var $ = cheerio.load(body);
      var links = [];
      $('.title a[href^="http"], a[href^="https"]').each(function() {
        links.push($(this));
      });
      res.render('api/scraping', {
        url: '/apiopen',
        links: links
      });
    });
  });

  /**
   * GET /api/twilio
   * Twilio API example.
   */

  app.get('/api/twilio', function(req, res, next) {
    res.render('api/twilio', {
      url: '/apiopen'
    });
  });

  /**
   * POST /api/twilio
   * Twilio API example.
   * @param telephone
   */

  app.post('/api/twilio', function(req, res, next) {
    var message = {
      to: req.body.telephone,
      from: config.twilio.phone,
      body: 'Hello from ' + app.locals.application + '. We are happy you are testing our code!'
    };
    twilio.sendMessage(message, function(err, responseData) {
      if (err) {
        return next(err);
      }
      req.flash('success', { msg: 'Text sent to ' + responseData.to + '.'});
      res.redirect('/api/twilio');
    });
  });

  /**
   * GET /api/foursquare
   * Foursquare API example.
   */

  app.get('/api/foursquare', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res, next) {
    var token = _.findWhere(req.user.tokens, { kind: 'foursquare' });
    async.parallel({
        trendingVenues: function(callback) {
          foursquare.Venues.getTrending('40.7222756', '-74.0022724', { limit: 50 }, token.accessToken, function(err, results) {
            callback(err, results);
          });
        },
        venueDetail: function(callback) {
          foursquare.Venues.getVenue('49da74aef964a5208b5e1fe3', token.accessToken, function(err, results) {
            callback(err, results);
          });
        },
        userCheckins: function(callback) {
          foursquare.Users.getCheckins('self', null, token.accessToken, function(err, results) {
            callback(err, results);
          });
        }
      },
      function(err, results) {
        if (err) {
          return next(err);
        }
        res.render('api/foursquare', {
          url: '/apilocked',
          trendingVenues: results.trendingVenues,
          venueDetail: results.venueDetail,
          userCheckins: results.userCheckins
        });
      });
  });

  /**
   * GET /api/tumblr
   * Tumblr API example.
   */

  app.get('/api/tumblr', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res) {
    var token = _.findWhere(req.user.tokens, { kind: 'tumblr' });
    var client = tumblr.createClient({
      consumer_key: config.tumblr.key,
      consumer_secret: config.tumblr.secret,
      token: token.token,
      token_secret: token.tokenSecret
    });
    client.posts('goddess-of-imaginary-light.tumblr.com', { type: 'photo' }, function(err, data) {
      res.render('api/tumblr', {
        url: '/apilocked',
        blog: data.blog,
        photoset: data.posts[0].photos
      });
    });
  });

  /**
   * GET /api/facebook
   * Facebook API example.
   */

  app.get('/api/facebook', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res, next) {
    var token = _.findWhere(req.user.tokens, { kind: 'facebook' });
    graph.setAccessToken(token.accessToken);
    async.parallel({
        getMe: function(done) {
          graph.get(req.user.facebook, function(err, me) {
            done(err, me);
          });
        },
        getMyFriends: function(done) {
          graph.get(req.user.facebook + '/friends', function(err, friends) {
            done(err, friends.data);
          });
        }
      },
      function(err, results) {
        if (err) {
          return next(err);
        }
        res.render('api/facebook', {
          url: '/apilocked',
          me: results.getMe,
          friends: results.getMyFriends
        });
      });
  });

  /**
   * GET /api/github
   * GitHub API Example.
   */

  app.get('/api/github', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res) {
    var token = _.findWhere(req.user.tokens, { kind: 'github' });
    var github = new Github({ token: token.accessToken });
    var repo = github.getRepo('dstroot', 'skeleton');
    repo.show(function(err, repo) {
      res.render('api/github', {
        url: '/apilocked',
        repo: repo
      });
    });
  });

  /**
   * GET /api/twitter
   * Twiter API example.
   */

  app.get('/api/twitter', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res, next) {
    var token = _.findWhere(req.user.tokens, { kind: 'twitter' });
    var T = new Twit({
      consumer_key: config.twitter.consumerKey,
      consumer_secret: config.twitter.consumerSecret,
      access_token: token.token,
      access_token_secret: token.tokenSecret
    });
    T.get('search/tweets', { q: 'hackathon since:2013-01-01', geocode: '40.71448,-74.00598,5mi', count: 50 }, function(err, reply) {
      if (err) {
        return next(err);
      }
      res.render('api/twitter', {
        url: '/apilocked',
        tweets: reply.statuses
      });
    });
  });

  /**
   * OAuth routes for API examples that require authorization.
   */

  app.get('/auth/foursquare', passport.authorize('foursquare'));
  app.get('/auth/foursquare/callback', passport.authorize('foursquare', { failureRedirect: '/api' }), function(req, res) {
    res.redirect('/api/foursquare');
  });

  app.get('/auth/tumblr', passport.authorize('tumblr'));
  app.get('/auth/tumblr/callback', passport.authorize('tumblr', { failureRedirect: '/api' }), function(req, res) {
    res.redirect('/api/tumblr');
  });

};
<commit_msg>Changed Tumblr Example<commit_after>'use strict';

/**
 * Module Dependencies
 */

var _             = require('underscore');
var Twit          = require('twit');
var config        = require('../config/config');
var async         = require('async');
var cheerio       = require('cheerio');
var request       = require('request');
var graph         = require('fbgraph');
var tumblr        = require('tumblr.js');
var Github        = require('github-api');
var querystring   = require('querystring');
var LastFmNode    = require('lastfm').LastFmNode;
var paypal        = require('paypal-rest-sdk');
var passport      = require('passport');
var passportConf  = require('../config/passport');
var twilio        = require('twilio')(config.twilio.sid, config.twilio.token);
var foursquare    = require('node-foursquare')({ secrets: config.foursquare });

/**
 * API Controller
 */

module.exports.controller = function(app) {

 /**
   * GET /api*
   * *ALL* api routes must be authenticated first
   */

  app.all('/api*', passportConf.isAuthenticated);

  /**
   * GET /api
   * List of API examples.
   */

  app.get('/api', function(req, res) {
    res.render('api/index', {
      url: req.url
    });
  });

  /**
   * GET /api/lastfm
   * Last.fm API example.
   */

  app.get('/api/lastfm', function(req, res, next) {
    var lastfm = new LastFmNode(config.lastfm);
    async.parallel({
        artistInfo: function(done) {
          lastfm.request('artist.getInfo', {
            artist: 'Epica',
            handlers: {
              success: function(data) {
                done(null, data);
              },
              error: function(err) {
                done(err);
              }
            }
          });
        },
        artistTopAlbums: function(done) {
          lastfm.request('artist.getTopAlbums', {
            artist: 'Epica',
            handlers: {
              success: function(data) {
                var albums = [];
                _.each(data.topalbums.album, function(album) {
                  albums.push(album.image.slice( -1 )[0]['#text']);
                });
                done(null, albums.slice(0, 4));
              },
              error: function(err) {
                done(err);
              }
            }
          });
        }
      },
      function(err, results) {
        if (err) {
          return next(err.message);
        }
        var artist = {
          name: results.artistInfo.artist.name,
          image: results.artistInfo.artist.image.slice( -1 )[0]['#text'],
          tags: results.artistInfo.artist.tags.tag,
          bio: results.artistInfo.artist.bio.summary,
          stats: results.artistInfo.artist.stats,
          similar: results.artistInfo.artist.similar.artist,
          topAlbums: results.artistTopAlbums
        };
        res.render('api/lastfm', {
          artist: artist,
          url: '/apiopen'
        });
      });
  });

  /**
   * GET /api/nyt
   * New York Times API example.
   */

  app.get('/api/nyt', function(req, res, next) {
    var query = querystring.stringify({ 'api-key': config.nyt.key, 'list-name': 'young-adult' });
    var url = 'http://api.nytimes.com/svc/books/v2/lists?' + query;
    request.get(url, function(error, request, body) {
      if (request.statusCode === 403) {
        return next(error('Missing or Invalid New York Times API Key'));
      }
      var bestsellers = JSON.parse(body);
      res.render('api/nyt', {
        url: '/apiopen',
        books: bestsellers.results
      });
    });
  });

  /**
   * GET /api/paypal
   * PayPal SDK example.
   */

  app.get('/api/paypal', function(req, res, next) {
    paypal.configure(config.paypal);
    var payment_details = {
      'intent': 'sale',
      'payer': {
        'payment_method': 'paypal'
      },
      'redirect_urls': {
        'return_url': config.paypal.returnUrl,
        'cancel_url': config.paypal.cancelUrl
      },
      'transactions': [
        {
          'description': 'ITEM: Something Awesome!',
          'amount': {
            'currency': 'USD',
            'total': '2.99'
          }
        }
      ]
    };
    paypal.payment.create(payment_details, function(error, payment) {
      if (error) {
        // TODO FIXME
        console.log(error);
      } else {
        req.session.payment_id = payment.id;
        var links = payment.links;
        for (var i = 0; i < links.length; i++) {
          if (links[i].rel === 'approval_url') {
            res.render('api/paypal', {
              url: '/apilocked',
              approval_url: links[i].href
            });
          }
        }
      }
    });
  });

  /**
   * GET /api/paypal/success
   * PayPal SDK example.
   */

  app.get('/api/paypal/success', function(req, res, next) {
    var payment_id = req.session.payment_id;
    var payment_details = { 'payer_id': req.query.PayerID };
    paypal.payment.execute(payment_id, payment_details, function(error, payment) {
      if (error) {
        res.render('api/paypal', {
          url: req.url,
          result: true,
          success: false
        });
      } else {
        res.render('api/paypal', {
          url: '/apilocked',
          result: true,
          success: true
        });
      }
    });
  });

  /**
   * GET /api/paypal/cancel
   * PayPal SDK example.
   */

  app.get('/api/paypal/cancel', function(req, res, next) {
    req.session.payment_id = null;
    res.render('api/paypal', {
      url: '/apilocked',
      result: true,
      canceled: true
    });
  });

  /**
   * GET /api/scraping
   * Web scraping example using Cheerio library.
   */

  app.get('/api/scraping', function(req, res, next) {
    request.get('https://news.ycombinator.com/', function(err, request, body) {
      if (err) {
        return next(err);
      }
      var $ = cheerio.load(body);
      var links = [];
      $('.title a[href^="http"], a[href^="https"]').each(function() {
        links.push($(this));
      });
      res.render('api/scraping', {
        url: '/apiopen',
        links: links
      });
    });
  });

  /**
   * GET /api/twilio
   * Twilio API example.
   */

  app.get('/api/twilio', function(req, res, next) {
    res.render('api/twilio', {
      url: '/apiopen'
    });
  });

  /**
   * POST /api/twilio
   * Twilio API example.
   * @param telephone
   */

  app.post('/api/twilio', function(req, res, next) {
    var message = {
      to: req.body.telephone,
      from: config.twilio.phone,
      body: 'Hello from ' + app.locals.application + '. We are happy you are testing our code!'
    };
    twilio.sendMessage(message, function(err, responseData) {
      if (err) {
        return next(err);
      }
      req.flash('success', { msg: 'Text sent to ' + responseData.to + '.'});
      res.redirect('/api/twilio');
    });
  });

  /**
   * GET /api/foursquare
   * Foursquare API example.
   */

  app.get('/api/foursquare', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res, next) {
    var token = _.findWhere(req.user.tokens, { kind: 'foursquare' });
    async.parallel({
        trendingVenues: function(callback) {
          foursquare.Venues.getTrending('40.7222756', '-74.0022724', { limit: 50 }, token.accessToken, function(err, results) {
            callback(err, results);
          });
        },
        venueDetail: function(callback) {
          foursquare.Venues.getVenue('49da74aef964a5208b5e1fe3', token.accessToken, function(err, results) {
            callback(err, results);
          });
        },
        userCheckins: function(callback) {
          foursquare.Users.getCheckins('self', null, token.accessToken, function(err, results) {
            callback(err, results);
          });
        }
      },
      function(err, results) {
        if (err) {
          return next(err);
        }
        res.render('api/foursquare', {
          url: '/apilocked',
          trendingVenues: results.trendingVenues,
          venueDetail: results.venueDetail,
          userCheckins: results.userCheckins
        });
      });
  });

  /**
   * GET /api/tumblr
   * Tumblr API example.
   */

  app.get('/api/tumblr', passportConf.isAuthenticated, passportConf.isAuthorized, function (req, res) {
    var token = _.findWhere(req.user.tokens, { kind: 'tumblr' });
    var client = tumblr.createClient({
      consumer_key: config.tumblr.key,
      consumer_secret: config.tumblr.secret,
      token: token.token,
      token_secret: token.tokenSecret
    });
    client.posts('danielmoyerdesign.tumblr.com', { type: 'photo' }, function (err, data) {
      console.log('data: ' + JSON.stringify(data));
      res.render('api/tumblr', {
        url: '/apilocked',
        blog: data.blog,
        photoset: data.posts[0].photos
      });
    });
  });

  /**
   * GET /api/facebook
   * Facebook API example.
   */

  app.get('/api/facebook', passportConf.isAuthenticated, passportConf.isAuthorized, function (req, res, next) {
    var token = _.findWhere(req.user.tokens, { kind: 'facebook' });
    graph.setAccessToken(token.accessToken);
    async.parallel({
        getMe: function(done) {
          graph.get(req.user.facebook, function(err, me) {
            done(err, me);
          });
        },
        getMyFriends: function(done) {
          graph.get(req.user.facebook + '/friends', function(err, friends) {
            done(err, friends.data);
          });
        }
      },
      function(err, results) {
        if (err) {
          return next(err);
        }
        res.render('api/facebook', {
          url: '/apilocked',
          me: results.getMe,
          friends: results.getMyFriends
        });
      });
  });

  /**
   * GET /api/github
   * GitHub API Example.
   */

  app.get('/api/github', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res) {
    var token = _.findWhere(req.user.tokens, { kind: 'github' });
    var github = new Github({ token: token.accessToken });
    var repo = github.getRepo('dstroot', 'skeleton');
    repo.show(function(err, repo) {
      res.render('api/github', {
        url: '/apilocked',
        repo: repo
      });
    });
  });

  /**
   * GET /api/twitter
   * Twiter API example.
   */

  app.get('/api/twitter', passportConf.isAuthenticated, passportConf.isAuthorized, function(req, res, next) {
    var token = _.findWhere(req.user.tokens, { kind: 'twitter' });
    var T = new Twit({
      consumer_key: config.twitter.consumerKey,
      consumer_secret: config.twitter.consumerSecret,
      access_token: token.token,
      access_token_secret: token.tokenSecret
    });
    T.get('search/tweets', { q: 'hackathon since:2013-01-01', geocode: '40.71448,-74.00598,5mi', count: 50 }, function(err, reply) {
      if (err) {
        return next(err);
      }
      res.render('api/twitter', {
        url: '/apilocked',
        tweets: reply.statuses
      });
    });
  });

  /**
   * OAuth routes for API examples that require authorization.
   */

  app.get('/auth/foursquare', passport.authorize('foursquare'));
  app.get('/auth/foursquare/callback', passport.authorize('foursquare', { failureRedirect: '/api' }), function(req, res) {
    res.redirect('/api/foursquare');
  });

  app.get('/auth/tumblr', passport.authorize('tumblr'));
  app.get('/auth/tumblr/callback', passport.authorize('tumblr', { failureRedirect: '/api' }), function(req, res) {
    res.redirect('/api/tumblr');
  });

};
<|endoftext|>


<commit_before>'use strict';

/**
 * Wouldn't it be great if you or a CI system were notified properly
 * that you aren't using the right version of node.js, npm or Python?
 *
 * @see https://github.com/atom/atom/blob/master/script/utils/verify-requirements.js
 */
const Promise = require('bluebird');
const _ = require('lodash');
const GitHub = require('github');
const github = new GitHub({version: '3.0.0', 'User-Agent': 'hadron-build'});
const cli = require('mongodb-js-cli')('hadron-build:upload');
const abortIfError = cli.abortIfError.bind(cli);

const config = require('../lib/config');
const downloadCenter = require('../lib/download-center');

let createGitHubRelease = (CONFIG) => {
  const p = Promise.defer();
  var opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo,
    draft: true,
    tag_name: `v${CONFIG.version}`,
    name: CONFIG.version,
    target_commitish: CONFIG.target_commitish,
    body: `### Notable Changes
    * Something new
    `
  };

  cli.debug('Creating release', opts);

  github.releases.createRelease(opts, function(err, res) {
    if (err) p.reject(err);

    cli.debug('Created release', res);
    p.resolve(res);
  });
  return p.promise;
};

let getOrCreateGitHubRelease = (CONFIG) => {
  const p = Promise.defer();
  const opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo
  };
  github.releases.listReleases(opts, (err, releases) => {
    if (err) {
      return p.reject(err);
    }

    let existing = _.find(releases, (release) => release.name === CONFIG.version);
    if (existing) {
      cli.debug(`Found existsing release for ${CONFIG.version}`, existing);
      return p.resolve(existing);
    }

    cli.debug(`Creating new draft release for ${CONFIG.version}`);
    createGitHubRelease(CONFIG)
      .then( (release) => p.resolve(release))
      .catch( (_err) => p.reject(_err));
  });
  return p.promise;
};

let removeGitHubReleaseAssetIfExists = (CONFIG, release, asset) => {
  let existing = _.chain(release.assets)
    .filter((a) => a.name === asset.name)
    .first()
    .value();

  if (!existing) {
    return Promise.resolve(false);
  }

  cli.debug(`Removing existing asset ${asset.name}`);
  const opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo,
    id: existing.id
  };

  const p = Promise.defer();
  github.releases.deleteAsset(opts, (err, res) => {
    if (err) {
      return p.reject(err);
    }
    cli.debug('Asset deleted', res);
    return p.resolve(true);
  });
  return p.promise;
};

let doGitHubReleaseAssetUpload = (CONFIG, release, asset) => {
  const opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo,
    id: release.id,
    name: asset.name,
    filePath: asset.path
  };

  cli.spinner(`Uploading ${asset.name}`);

  const p = Promise.defer();
  github.releases.uploadAsset(opts, function(err, res) {
    if (err) {
      err.stack = err.stack || '<no stacktrace>';
      cli.error(`Failed to upload ${asset.name}`);
      return p.reject(err);
    }
    cli.debug('Asset upload returned', res);
    cli.ok(`Uploaded ${asset.name}`);
    p.resolve(asset);
  });
  return p.promise;
};

let uploadGitHubReleaseAsset = (CONFIG, release, asset) => {
  if (release.draft === true) {
    return removeGitHubReleaseAssetIfExists(CONFIG, release, asset)
      .then(() => doGitHubReleaseAssetUpload(CONFIG, release, asset));
  }

  let existing = _.chain(release.assets)
    .filter((a) => a.name === asset.name)
    .first()
    .value();

  if (existing) {
    cli.debug('Asset already exists and release is currently not a draft.  skipping.', existing);
    return Promise.resolve(existing);
  }

  return doGitHubReleaseAssetUpload(CONFIG, release, asset);
};

let maybePublishGitHubRelease = (CONFIG) => {
  if (CONFIG.channel === 'dev') {
    cli.info('Skipping publish GitHub release for dev channel.');
    return Promise.resolve();
  }

  if (!CONFIG.github_token) {
    cli.warn('Skipping publish release because github_token not set.');
    return Promise.resolve();
  }

  github.authenticate({
    token: CONFIG.github_token,
    type: 'oauth'
  });

  return getOrCreateGitHubRelease(CONFIG)
    .then((release) => {
      return CONFIG.assets.map((asset) => uploadGitHubReleaseAsset(CONFIG, release, asset));
    });
};

exports.command = 'upload [options]';

exports.describe = 'Upload assets from `release`.';

exports.builder = {};
_.assign(exports.builder, config.options);

exports.handler = function(argv) {
  cli.argv = argv;
  var CONFIG = config.get(cli);
  maybePublishGitHubRelease(CONFIG)
    .then( () => downloadCenter.maybeUpload(CONFIG))
    .catch(abortIfError);
};
<commit_msg>Fix Github upload (#39)<commit_after>'use strict';

/**
 * Wouldn't it be great if you or a CI system were notified properly
 * that you aren't using the right version of node.js, npm or Python?
 *
 * @see https://github.com/atom/atom/blob/master/script/utils/verify-requirements.js
 */
const Promise = require('bluebird');
const _ = require('lodash');
const GitHub = require('github');
const github = new GitHub({version: '3.0.0', 'User-Agent': 'hadron-build'});
const cli = require('mongodb-js-cli')('hadron-build:upload');
const abortIfError = cli.abortIfError.bind(cli);

const config = require('../lib/config');
const downloadCenter = require('../lib/download-center');

let createGitHubRelease = (CONFIG) => {
  const p = Promise.defer();
  var opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo,
    draft: true,
    tag_name: `v${CONFIG.version}`,
    name: CONFIG.version,
    target_commitish: CONFIG.target_commitish,
    body: `### Notable Changes
    * Something new
    `
  };

  cli.debug('Creating release', opts);

  github.repos.createRelease(opts, function(err, res) {
    if (err) p.reject(err);

    cli.debug('Created release', res);
    p.resolve(res);
  });
  return p.promise;
};

let getOrCreateGitHubRelease = (CONFIG) => {
  const p = Promise.defer();
  const opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo
  };
  github.repos.getReleases(opts, (err, releases) => {
    if (err) {
      return p.reject(err);
    }

    let existing = _.find(releases, (release) => release.name === CONFIG.version);
    if (existing) {
      cli.debug(`Found existsing release for ${CONFIG.version}`, existing);
      return p.resolve(existing);
    }

    cli.debug(`Creating new draft release for ${CONFIG.version}`);
    createGitHubRelease(CONFIG)
      .then( (release) => p.resolve(release))
      .catch( (_err) => p.reject(_err));
  });
  return p.promise;
};

let removeGitHubReleaseAssetIfExists = (CONFIG, release, asset) => {
  let existing = _.chain(release.assets)
    .filter((a) => a.name === asset.name)
    .first()
    .value();

  if (!existing) {
    return Promise.resolve(false);
  }

  cli.debug(`Removing existing asset ${asset.name}`);
  const opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo,
    id: existing.id
  };

  const p = Promise.defer();
  github.repos.deleteAsset(opts, (err, res) => {
    if (err) {
      return p.reject(err);
    }
    cli.debug('Asset deleted', res);
    return p.resolve(true);
  });
  return p.promise;
};

let doGitHubReleaseAssetUpload = (CONFIG, release, asset) => {
  const opts = {
    owner: CONFIG.github_owner,
    repo: CONFIG.github_repo,
    id: release.id,
    name: asset.name,
    filePath: asset.path
  };

  cli.spinner(`Uploading ${asset.name}`);

  const p = Promise.defer();
  github.repos.uploadAsset(opts, function(err, res) {
    if (err) {
      err.stack = err.stack || '<no stacktrace>';
      cli.error(`Failed to upload ${asset.name}`);
      return p.reject(err);
    }
    cli.debug('Asset upload returned', res);
    cli.ok(`Uploaded ${asset.name}`);
    p.resolve(asset);
  });
  return p.promise;
};

let uploadGitHubReleaseAsset = (CONFIG, release, asset) => {
  if (release.draft === true) {
    return removeGitHubReleaseAssetIfExists(CONFIG, release, asset)
      .then(() => doGitHubReleaseAssetUpload(CONFIG, release, asset));
  }

  let existing = _.chain(release.assets)
    .filter((a) => a.name === asset.name)
    .first()
    .value();

  if (existing) {
    cli.debug('Asset already exists and release is currently not a draft.  skipping.', existing);
    return Promise.resolve(existing);
  }

  return doGitHubReleaseAssetUpload(CONFIG, release, asset);
};

let maybePublishGitHubRelease = (CONFIG) => {
  if (CONFIG.channel === 'dev') {
    cli.info('Skipping publish GitHub release for dev channel.');
    return Promise.resolve();
  }

  if (!CONFIG.github_token) {
    cli.warn('Skipping publish release because github_token not set.');
    return Promise.resolve();
  }

  github.authenticate({
    token: CONFIG.github_token,
    type: 'oauth'
  });

  return getOrCreateGitHubRelease(CONFIG)
    .then((release) => {
      return CONFIG.assets.map((asset) => uploadGitHubReleaseAsset(CONFIG, release, asset));
    });
};

exports.command = 'upload [options]';

exports.describe = 'Upload assets from `release`.';

exports.builder = {};
_.assign(exports.builder, config.options);

exports.handler = function(argv) {
  cli.argv = argv;
  var CONFIG = config.get(cli);
  maybePublishGitHubRelease(CONFIG)
    .then( () => downloadCenter.maybeUpload(CONFIG))
    .catch(abortIfError);
};
<|endoftext|>