<commit_before>'use strict'

import React, { Component } from 'react'
import { connect } from 'react-redux'
import Select from 'react-select'
import { style } from 'next/css'
import Slider from 'react-slick'

import fetchAccount from '../actions/fetch-account'
import editUser from './../actions/edit-user'
import addSummoner from './../actions/add-summoner'
import confirmSummoner from '../actions/confirm-summoner'
import { countries, locations } from '../services/places'
import {
  EDIT_USER_SUCCESS,
  EDIT_USER_ERROR,
  ADD_SUMMONER_SUCCESS,
  ADD_SUMMONER_ERROR,
  CONFIRM_SUMMONER_SUCCESS,
  CONFIRM_SUMMONER_ERROR,
  ACCOUNT_SUCCESS,
  ACCOUNT_ERROR
} from './../constants'

const styles = {
  formInput: {
    border: 'none',
    marginBottom: '20px'
  },

  label: {
    display: 'inline-block',
    marginBottom: '10px',
    fontWeight: 600,
    fontSize: '1.15rem',
    color: '#333'
  },

  input: {
    padding: '20px 15px',
    width: '100%',
    border: '1px solid #eee',
    borderRadius: '5px',
    fontSize: '1.1rem',
    outline: 'none',

    ':focus': {
      borderColor: '#ccc'
    }
  },

  title: {
    textAlign: 'center',
    color: '#333',
    fontSize: '2rem',
    fontWeight: '500',
    marginBottom: '10px'
  },

  subtitle: {
    color: '#999',
    fontSize: '1.15rem',
    fontWeight: '400',
    textAlign: 'center',
    marginBottom: '30px',
    lineHeight: '2rem'
  },

  slickImage: {
    display: 'inline-block',
    width: '70%',
    marginTop: '55px',
    marginLeft: 'auto',
    marginRight: 'auto'
  },

  form: {
    maxWidth: '500px',
    marginLeft: 'auto',
    marginRight: 'auto'
  },

  summoner: {
    marginTop: '75px'
  },

  btnPrev: {
    float: 'left',
    outline: 'none',
    color: '#52bdab',
    border: 'none',
    borderRadius: '5px',
    padding: '10px 25px',
    fontSize: '1rem',
    height: '45px',
    marginTop: '30px',
    fontWeight: '500',
    cursor: 'pointer',
    background: 'transparent'
  },

  btnNext: {
    float: 'right',
    outline: 'none',
    color: '#fff',
    border: 'none',
    borderRadius: '5px',
    padding: '10px 25px',
    fontSize: '1rem',
    height: '45px',
    marginTop: '30px',
    fontWeight: '500',
    cursor: 'pointer',
    background: 'linear-gradient(to right, #52bdab 0%,#6BB6D6 100%)'
  },

  codeCase: {
    border: '1px solid #ccc',
    padding: '20px',
    borderRadius: '4px',
    width: '300px',
    position: 'relative',
    margin: '25px auto'
  },

  code: {
    fontSize: '1.5rem',
    color: '#333',
    lineHeight: '1',
    verticalAlign: 'middle'
  },

  codeBtn: {
    height: '60px',
    backgroundColor: 'transparent',
    border: 'none',
    top: '0',
    position: 'absolute',
    right: '0',
    width: '100px',
    fontSize: '1rem',
    borderLeft: '1px solid #ccc',
    outline: 'none',
    color: '#333'
  },

  mastery: {
    maxWidth: '90%',
    borderRadius: '6px'
  },

  stepImage: {
    flexBasis: '100%'
  },

  steps: {
    display: 'flex',
    justifyContent: 'space-between'
  },

  stepList: {
    marginTop: '50px'
  },

  step: {
    marginBottom: '30px'
  },

  stepNumber: {
    display: 'block',
    fontSize: '1.5rem',
    color: '#333',
    marginBottom: '10px'
  },

  stepText: {
    color: '#999'
  }
}

class Onboard extends Component {
  constructor () {
    super()

    this.nextSlide = this.nextSlide.bind(this)
    this.previousSlide = this.previousSlide.bind(this)
    this.submitLocation = this.submitLocation.bind(this)
    this.submitSummoner = this.submitSummoner.bind(this)
    this.handleCountry = this.handleCountry.bind(this)
    this.handleState = this.handleState.bind(this)
    this.handleCity = this.handleCity.bind(this)
    this.confirmSummoner = this.confirmSummoner.bind(this)

    this.state = {
      countryList: countries,
      stateList: null,
      cityList: null,
      country: null,
      state: null,
      city: null,
      code: null,
      account: {}
    }
  }

  nextSlide () {
    this.slider.slickNext()
  }

  previousSlide () {
    this.slider.slickPrev()
  }

  handleCountry (e) {
    this.setState({
      country: e.value,
      stateList: locations[e.value]
    })
  }

  handleState (e) {
    const city = locations[this.state.country].filter(state => state.value === e.value)
    this.setState({
      state: e.label,
      stateParam: e.value,
      cityList: city[0].cities
    })
  }

  handleCity (e) {
    this.setState({city: e.value})
  }

  submitLocation () {
    const userData = {
      country: this.state.country,
      state: this.state.state,
      city: this.state.city
    }

    this.props.editUser(userData)
      .then(({ data, type }) => {
        if (type === EDIT_USER_SUCCESS) {
          this.nextSlide()
        }

        if (type === EDIT_USER_ERROR) {
          this.props.throwError(data)
        }
      })
  }

  submitSummoner () {
    const summoner = {name: this.summoner.value}

    this.props.addSummoner(summoner)
      .then(({ data, type }) => {
        if (type === ADD_SUMMONER_SUCCESS) {
          this.nextSlide()
          this.setState({code: data.code})
          this.props.fetchAccount()
            .then(({ data, type }) => {
              if (type === ACCOUNT_SUCCESS) {
                this.setState({account: data})
              }
            })
        }

        if (type === ADD_SUMMONER_ERROR) {
          this.props.throwError(data)
        }
      })
  }

  confirmSummoner () {
    const summoner = this.state.account.summoners[0].name
    this.props.confirmSummoner(summoner)
      .then(({ data, type }) => {
        if (data) {
          this.props.routing.url.pushTo('/profile')
        } else {
          this.props.throwError('Invocador não confirmado ainda')
        }
      })
  }

  render () {
    const settings = {
      dots: false,
      infinite: false,
      speed: 200,
      slidesToShow: 1,
      slidesToScroll: 1,
      draggable: false
    }

    return (
      <Slider ref={c => this.slider = c } {...settings}>

Bem vindo ao Ritoplz

Vamos ajudar você a configurar sua conta

          <button className={style(styles.btnNext)} onClick={this.nextSlide}>Próximo</button>

Escolha sua localização your location

Essa será sua localização no Rankings

            <fieldset className={style(styles.formInput)}>
              País
              <Select options={this.state.countryList} value={this.state.country} onChange={this.handleCountry}/>
            </fieldset>

            <fieldset className={style(styles.formInput)}>
              Estado
              <Select options={this.state.stateList} value={this.state.stateParam} onChange={this.handleState}/>
            </fieldset>

            <fieldset className={style(styles.formInput)}>
              Cidade
              <Select options={this.state.cityList} value={this.state.city} onChange={this.handleCity}/>
            </fieldset>

          <button className={style(styles.btnPrev)} onClick={this.previousSlide}>Anterios</button>
          <button className={style(styles.btnNext)} onClick={this.submitLocation}>Próximo</button>

Vamos adicionar seu invocador

Nome do seu invocador

              <fieldset className={style(styles.formInput)}>
                Invocador
                <input className={style(styles.input)} type="text" ref={node => this.summoner = node}/>
              </fieldset>

          <button className={style(styles.btnPrev)} onClick={this.previousSlide}>Anterior</button>
          <button className={style(styles.btnNext)} onClick={this.submitSummoner}>Próximo</button>

Confirme seu invocador

Copie o código abaixo para confirmar seu invocador

                Passo 1.
                Crie ou edite uma Página de Talentos ou Página de Runas

                Passo 2.
                Use esse código ("{this.state.code}") como nome da página

                Passo 3.
                Clique em confirmar invocador abaixo

{this.state.code}

            <button className={style(styles.codeBtn)}>Seu código</button>

          <button className={style(styles.btnPrev)} onClick={this.previousSlide}>Anterior</button>
          <button className={style(styles.btnNext)} onClick={this.confirmSummoner}>Confirmar Invocador</button>

      </Slider>
    )
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchAccount: () => dispatch(fetchAccount()),
    editUser: (user) => dispatch(editUser(user)),
    addSummoner: summoner => dispatch(addSummoner(summoner)),
    confirmSummoner: summoner => dispatch(confirmSummoner(summoner))
  }
}

export default connect(null, mapDispatchToProps)(Onboard)
<commit_msg>Update onboard.js<commit_after>'use strict'

import React, { Component } from 'react'
import { connect } from 'react-redux'
import Select from 'react-select'
import { style } from 'next/css'
import Slider from 'react-slick'

import fetchAccount from '../actions/fetch-account'
import editUser from './../actions/edit-user'
import addSummoner from './../actions/add-summoner'
import confirmSummoner from '../actions/confirm-summoner'
import { countries, locations } from '../services/places'
import {
  EDIT_USER_SUCCESS,
  EDIT_USER_ERROR,
  ADD_SUMMONER_SUCCESS,
  ADD_SUMMONER_ERROR,
  CONFIRM_SUMMONER_SUCCESS,
  CONFIRM_SUMMONER_ERROR,
  ACCOUNT_SUCCESS,
  ACCOUNT_ERROR
} from './../constants'

const styles = {
  formInput: {
    border: 'none',
    marginBottom: '20px'
  },

  label: {
    display: 'inline-block',
    marginBottom: '10px',
    fontWeight: 600,
    fontSize: '1.15rem',
    color: '#333'
  },

  input: {
    padding: '20px 15px',
    width: '100%',
    border: '1px solid #eee',
    borderRadius: '5px',
    fontSize: '1.1rem',
    outline: 'none',

    ':focus': {
      borderColor: '#ccc'
    }
  },

  title: {
    textAlign: 'center',
    color: '#333',
    fontSize: '2rem',
    fontWeight: '500',
    marginBottom: '10px'
  },

  subtitle: {
    color: '#999',
    fontSize: '1.15rem',
    fontWeight: '400',
    textAlign: 'center',
    marginBottom: '30px',
    lineHeight: '2rem'
  },

  slickImage: {
    display: 'inline-block',
    width: '70%',
    marginTop: '55px',
    marginLeft: 'auto',
    marginRight: 'auto'
  },

  form: {
    maxWidth: '500px',
    marginLeft: 'auto',
    marginRight: 'auto'
  },

  summoner: {
    marginTop: '75px'
  },

  btnPrev: {
    float: 'left',
    outline: 'none',
    color: '#52bdab',
    border: 'none',
    borderRadius: '5px',
    padding: '10px 25px',
    fontSize: '1rem',
    height: '45px',
    marginTop: '30px',
    fontWeight: '500',
    cursor: 'pointer',
    background: 'transparent'
  },

  btnNext: {
    float: 'right',
    outline: 'none',
    color: '#fff',
    border: 'none',
    borderRadius: '5px',
    padding: '10px 25px',
    fontSize: '1rem',
    height: '45px',
    marginTop: '30px',
    fontWeight: '500',
    cursor: 'pointer',
    background: 'linear-gradient(to right, #52bdab 0%,#6BB6D6 100%)'
  },

  codeCase: {
    border: '1px solid #ccc',
    padding: '20px',
    borderRadius: '4px',
    width: '300px',
    position: 'relative',
    margin: '25px auto'
  },

  code: {
    fontSize: '1.5rem',
    color: '#333',
    lineHeight: '1',
    verticalAlign: 'middle'
  },

  codeBtn: {
    height: '60px',
    backgroundColor: 'transparent',
    border: 'none',
    top: '0',
    position: 'absolute',
    right: '0',
    width: '100px',
    fontSize: '1rem',
    borderLeft: '1px solid #ccc',
    outline: 'none',
    color: '#333'
  },

  mastery: {
    maxWidth: '90%',
    borderRadius: '6px'
  },

  stepImage: {
    flexBasis: '100%'
  },

  steps: {
    display: 'flex',
    justifyContent: 'space-between'
  },

  stepList: {
    marginTop: '50px'
  },

  step: {
    marginBottom: '30px'
  },

  stepNumber: {
    display: 'block',
    fontSize: '1.5rem',
    color: '#333',
    marginBottom: '10px'
  },

  stepText: {
    color: '#999'
  }
}

class Onboard extends Component {
  constructor () {
    super()

    this.nextSlide = this.nextSlide.bind(this)
    this.previousSlide = this.previousSlide.bind(this)
    this.submitLocation = this.submitLocation.bind(this)
    this.submitSummoner = this.submitSummoner.bind(this)
    this.handleCountry = this.handleCountry.bind(this)
    this.handleState = this.handleState.bind(this)
    this.handleCity = this.handleCity.bind(this)
    this.confirmSummoner = this.confirmSummoner.bind(this)

    this.state = {
      countryList: countries,
      stateList: null,
      cityList: null,
      country: null,
      state: null,
      city: null,
      code: null,
      account: {}
    }
  }

  nextSlide () {
    this.slider.slickNext()
  }

  previousSlide () {
    this.slider.slickPrev()
  }

  handleCountry (e) {
    this.setState({
      country: e.value,
      stateList: locations[e.value]
    })
  }

  handleState (e) {
    const city = locations[this.state.country].filter(state => state.value === e.value)
    this.setState({
      state: e.label,
      stateParam: e.value,
      cityList: city[0].cities
    })
  }

  handleCity (e) {
    this.setState({city: e.value})
  }

  submitLocation () {
    const userData = {
      country: this.state.country,
      state: this.state.state,
      city: this.state.city
    }

    this.props.editUser(userData)
      .then(({ data, type }) => {
        if (type === EDIT_USER_SUCCESS) {
          this.nextSlide()
        }

        if (type === EDIT_USER_ERROR) {
          this.props.throwError(data)
        }
      })
  }

  submitSummoner () {
    const summoner = {name: this.summoner.value}

    this.props.addSummoner(summoner)
      .then(({ data, type }) => {
        if (type === ADD_SUMMONER_SUCCESS) {
          this.nextSlide()
          this.setState({code: data.code})
          this.props.fetchAccount()
            .then(({ data, type }) => {
              if (type === ACCOUNT_SUCCESS) {
                this.setState({account: data})
              }
            })
        }

        if (type === ADD_SUMMONER_ERROR) {
          this.props.throwError(data)
        }
      })
  }

  confirmSummoner () {
    const summoner = this.state.account.summoners[0].name
    this.props.confirmSummoner(summoner)
      .then(({ data, type }) => {
        if (data) {
          this.props.routing.url.pushTo('/profile')
        } else {
          this.props.throwError('Invocador não confirmado ainda')
        }
      })
  }

  render () {
    const settings = {
      dots: false,
      infinite: false,
      speed: 200,
      slidesToShow: 1,
      slidesToScroll: 1,
      draggable: false
    }

    return (
      <Slider ref={c => this.slider = c } {...settings}>

Bem vindo ao Ritoplz

Vamos ajudar você a configurar sua conta

          <button className={style(styles.btnNext)} onClick={this.nextSlide}>Próximo</button>

Escolha sua localização your location

Essa será sua localização no Rankings

            <fieldset className={style(styles.formInput)}>
              País
              <Select options={this.state.countryList} value={this.state.country} onChange={this.handleCountry}/>
            </fieldset>

            <fieldset className={style(styles.formInput)}>
              Estado
              <Select options={this.state.stateList} value={this.state.stateParam} onChange={this.handleState}/>
            </fieldset>

            <fieldset className={style(styles.formInput)}>
              Cidade
              <Select options={this.state.cityList} value={this.state.city} onChange={this.handleCity}/>
            </fieldset>

          <button className={style(styles.btnPrev)} onClick={this.previousSlide}>Anterior</button>
          <button className={style(styles.btnNext)} onClick={this.submitLocation}>Próximo</button>

Vamos adicionar seu invocador

Nome do seu invocador

              <fieldset className={style(styles.formInput)}>
                Invocador
                <input className={style(styles.input)} type="text" ref={node => this.summoner = node}/>
              </fieldset>

          <button className={style(styles.btnPrev)} onClick={this.previousSlide}>Anterior</button>
          <button className={style(styles.btnNext)} onClick={this.submitSummoner}>Próximo</button>

Confirme seu invocador

Copie o código abaixo para confirmar seu invocador

                Passo 1.
                Crie ou edite uma Página de Talentos ou Página de Runas

                Passo 2.
                Use esse código ("{this.state.code}") como nome da página

                Passo 3.
                Clique em confirmar invocador abaixo

{this.state.code}

            <button className={style(styles.codeBtn)}>Seu código</button>

          <button className={style(styles.btnPrev)} onClick={this.previousSlide}>Anterior</button>
          <button className={style(styles.btnNext)} onClick={this.confirmSummoner}>Confirmar Invocador</button>

      </Slider>
    )
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchAccount: () => dispatch(fetchAccount()),
    editUser: (user) => dispatch(editUser(user)),
    addSummoner: summoner => dispatch(addSummoner(summoner)),
    confirmSummoner: summoner => dispatch(confirmSummoner(summoner))
  }
}

export default connect(null, mapDispatchToProps)(Onboard)
<|endoftext|>


<commit_before>var	allowance_url = 'http://www.systemcontrolcenter.com/stlui/user/allowance_request.html',
	$ = function($) {
		var a = Array.prototype.map.call(document.querySelectorAll($), function(v) {
			return v;
		});

		return a;
	},

	xhr = new XMLHttpRequest(),

	dlzone = new Image(),

	ctx = $('#icon')[0].getContext('2d'),

	highlight = ctx.createLinearGradient(0, 0, 0, 19),
	cylinder = ctx.createLinearGradient(4, 0, 11, 0);

highlight.addColorStop(0, 'rgba(255, 255, 255, .2)');
highlight.addColorStop(1, 'rgba(240, 240, 240, .2)');

cylinder.addColorStop(0, 'rgba(255, 255, 255, .2)');
cylinder.addColorStop(0.4, 'rgba(255, 255, 255, .6)');
cylinder.addColorStop(1, 'rgba(240, 240, 240, .1)');

dlzone.src = '../images/package.png';

chrome.browserAction.onClicked.addListener(function(tab) {
	chrome.tabs.create({ index: tab.index,  url: allowance_url });
});

xhr.onreadystatechange = function() {
	if (xhr.readyState != 4 || xhr.status != 200)
		return;

	$('.hnb')[0].innerHTML = xhr.responseText.replace(/<meta[^>]+>/g, '');

	var offset, hue, t,
	    v = 40,

	    limited = /download speed limited/.test(xhr.responseText),

	    data = $('.hnb table td:nth-child(2) td:nth-child(3)').map(function(e, i) {
		    var t = e.innerHTML.replace(/^\s+|\s+$/g, '');

		    // return first three values in integer form
		    if (i < 3)
		    	return +t;

		    // return time
		    return t;
	    });

	// TODO: expand this to have reboot detection later
	if (typeof data[0] != 'number') {
		chrome.browserAction.setTitle({ title: 'HughesNet Allowance Monitor' });
		chrome.browserAction.setIcon({ path: '../images/error.png' });
	}

	ctx.clearRect(0, 0, 19, 19);

	// background
	ctx.shadowBlur = 2;
	ctx.shadowColor = 'rgba(0, 0, 0, .6)';
	ctx.fillStyle = limited ? '#fdd' : '#eee';
	ctx.fillRect(4, 2, 11, 15);

	ctx.shadowBlur = 0;

	// usage fill
	hue = data[2] < v ? (data[2] * (80 / v)) : 80;

	ctx.fillStyle = 'hsl(' + hue + ', 90%, 47%)';
	ctx.fillRect(4, 17, 11, -((data[2] / 100) * 15));

	// highlight
	ctx.fillStyle = highlight;
	ctx.fillRect(11, 2, 2, 15);

	// cylinder shaping
	ctx.fillStyle = cylinder;
	ctx.fillRect(4, 2, 11, 15);

	// time bar
	t = +data[3].split(/:/g).reduce(function(a, b) { return (+a * 60) + (+b); }) / 86400;
	ctx.fillStyle = '#aaa';
	ctx.fillRect(1, 18, 1, -(17 * (1 - t)));

	// download zone notification
	offset = /DT\)$/.test(t = new Date()) ? 4 : 5;
	if (t.getUTCHours() - offset >= 2 && t.getUTCHours() - offset < 7)
		ctx.drawImage(dlzone, 9, 9);

	chrome.browserAction.setTitle({
		title:	(limited ? 'Download speed limited\n' : '') +
			data[2] + '% (' + data[1] + 'MB/' + data[0] + 'MB)\n' +
			(data[0] / 2) + 'MB refill in: ' + data[3]
	});

	chrome.browserAction.setIcon({ imageData: ctx.getImageData(0, 0, 19, 19) });
};

setInterval((function() {
	xhr.open('GET', allowance_url, true);
	xhr.send(null);
	return arguments.callee;
})(), 60000);<commit_msg>Switched to strict mode<commit_after>(function() {
	"use strict";

	var	allowance_url = 'http://www.systemcontrolcenter.com/stlui/user/allowance_request.html',
		$ = function($) {
			var a = Array.prototype.map.call(document.querySelectorAll($), function(v) {
				return v;
			});

			return a;
		},

		xhr = new XMLHttpRequest(),

		dlzone = new Image(),

		ctx = $('#icon')[0].getContext('2d'),

		highlight = ctx.createLinearGradient(0, 0, 0, 19),
		cylinder = ctx.createLinearGradient(4, 0, 11, 0);

	highlight.addColorStop(0, 'rgba(255, 255, 255, .2)');
	highlight.addColorStop(1, 'rgba(240, 240, 240, .2)');

	cylinder.addColorStop(0, 'rgba(255, 255, 255, .2)');
	cylinder.addColorStop(0.4, 'rgba(255, 255, 255, .6)');
	cylinder.addColorStop(1, 'rgba(240, 240, 240, .1)');

	dlzone.src = '../images/package.png';

	chrome.browserAction.onClicked.addListener(function(tab) {
		chrome.tabs.create({ index: tab.index,  url: allowance_url });
	});

	xhr.onreadystatechange = function() {
		if (xhr.readyState != 4 || xhr.status != 200)
			return;

		$('.hnb')[0].innerHTML = xhr.responseText.replace(/<meta[^>]+>/g, '');

		var offset, hue, t,
		    v = 40,

		    limited = /download speed limited/.test(xhr.responseText),

		    data = $('.hnb table td:nth-child(2) td:nth-child(3)').map(function(e, i) {
			    var t = e.innerHTML.replace(/^\s+|\s+$/g, '');

			    // return first three values in integer form
			    if (i < 3)
			    	return +t;

			    // return time
			    return t;
		    });

		// TODO: expand this to have reboot detection later
		if (typeof data[0] != 'number') {
			chrome.browserAction.setTitle({ title: 'HughesNet Allowance Monitor' });
			chrome.browserAction.setIcon({ path: '../images/error.png' });
		}

		ctx.clearRect(0, 0, 19, 19);

		// background
		ctx.shadowBlur = 2;
		ctx.shadowColor = 'rgba(0, 0, 0, .6)';
		ctx.fillStyle = limited ? '#fdd' : '#eee';
		ctx.fillRect(4, 2, 11, 15);

		ctx.shadowBlur = 0;

		// usage fill
		hue = data[2] < v ? (data[2] * (80 / v)) : 80;

		ctx.fillStyle = 'hsl(' + hue + ', 90%, 47%)';
		ctx.fillRect(4, 17, 11, -((data[2] / 100) * 15));

		// highlight
		ctx.fillStyle = highlight;
		ctx.fillRect(11, 2, 2, 15);

		// cylinder shaping
		ctx.fillStyle = cylinder;
		ctx.fillRect(4, 2, 11, 15);

		// time bar
		t = +data[3].split(/:/g).reduce(function(a, b) { return (+a * 60) + (+b); }) / 86400;
		ctx.fillStyle = '#aaa';
		ctx.fillRect(1, 18, 1, -(17 * (1 - t)));

		// download zone notification
		offset = /DT\)$/.test(t = new Date()) ? 4 : 5;
		if (t.getUTCHours() - offset >= 2 && t.getUTCHours() - offset < 7)
			ctx.drawImage(dlzone, 9, 9);

		chrome.browserAction.setTitle({
			title:	(limited ? 'Download speed limited\n' : '') +
				data[2] + '% (' + data[1] + 'MB/' + data[0] + 'MB)\n' +
				(data[0] / 2) + 'MB refill in: ' + data[3]
		});

		chrome.browserAction.setIcon({ imageData: ctx.getImageData(0, 0, 19, 19) });
	};

	setInterval((function() {
		xhr.open('GET', allowance_url, true);
		xhr.send(null);
	}).bind(), 60000);
})();<|endoftext|>


<commit_before>/*
 * Copyrights phwang
 * Written by Paul Hwang
 * File name: go_ulink.js
 */

module.exports = {
    malloc: function (root_object_val) {
        return new GoUlinkClass(root_object_val);
    },
};

function GoUlinkClass(root_object_val) {
    "use strict";

    this.init__ = function (root_object_val) {
        this.theRootObject = root_object_val;
        this.debug(true, "init__", "");
    };

    this.objectName = function () {
        return "GoUlinkClass";
    };

    this.rootObject = function () {
        return this.theRootObject;
    };

    this.utilObject = function () {
        return this.rootObject().utilObject();
    };

    this.debug = function (debug_val, str1_val, str2_val) {
        if (debug_val) {
            this.logit(str1_val, str2_val);
        }
    };

    this.logit = function (str1_val, str2_val) {
        this.rootObject().LOG_IT(this.objectName() + "." + str1_val, str2_val);
    };

    this.abend = function (str1_val, str2_val) {
        this.rootObject().ABEND(this.objectName() + "." + str1_val, str2_val);
    };

    this.LOG_IT = function (s1_val, s2_val) {
        this.rootObject().LOG_IT(s1_val, s2_val);
    };

    this.ABEND = function (s1_val, s2_val) {
        this.rootObject().ABEND(s1_val, s2_val);
    };

    this.init__(root_object_val);
}

<commit_msg>matrix<commit_after><|endoftext|>


<commit_before>'use strict'

const NOW = new Date()

const {join, parse} = require('path')

const {readFileSync, readdirSync} = require('fs')

const GitHubAPIs = require('github')

const co = require('co')

const {
  env: {
    RELEASE_GIST,
    GIST_TOKEN,
    GIST_ID,
    GIT_REPO_TAG
  },
  exit,
  stdout,
  stderr
} = require('process')

if (!RELEASE_GIST) {
  stdout.write('Skipped Gist release\n')
  exit(1)
}

if (!GIST_ID || !GIST_TOKEN) {
  stderr.write('Missing environment variables\n')
  exit(1)
}

const github = new GitHubAPIs()
const {gists} = github
const files = {}
const ENCODING = {encoding: 'utf8'}
const CONTAINER = join(__dirname, '..', '..', 'out')
const LIST = readdirSync(CONTAINER)

const filename = item => {
  const {name, ext} = parse(item)
  return name + '-' + GIT_REPO_TAG + ext
}

for (const item of LIST) {
  const content = readFileSync(join(CONTAINER, item), ENCODING)
  files[filename(item)] = {content}
}

github.authenticate({
  type: 'token',
  token: GIST_TOKEN
})

co(main)

function * main () {
  const artifactsResponse = yield gists.edit({
    id: GIST_ID,
    files
  })
  const rfiles = artifactsResponse.files
  const summary = LIST.reduce(
    (prev, item) => {
      const {raw_url: url, type, language} = rfiles[filename(item)]
      return prev + ` * [${item}](${url})\n  - Type: ${type}\n  - Language: ${language}\n\n`
    },
    `# Release ${GIT_REPO_TAG} - ${NOW.toISOString()}\n\n`
  )
  yield gists.edit({
    id: GIST_ID,
    files: {
      [ filename('summary.md') ]: {
        content: summary
      }
    }
  })
}
<commit_msg>Use full path<commit_after>'use strict'

const NOW = new Date()

const {join, parse} = require('path')

const {readFileSync, readdirSync} = require('fs')

const GitHubAPIs = require('github')

const co = require('co')

const {
  env: {
    RELEASE_GIST,
    GIST_TOKEN,
    GIST_ID,
    GIT_REPO_TAG
  },
  exit,
  stdout,
  stderr
} = require('process')

if (!RELEASE_GIST) {
  stdout.write('Skipped Gist release\n')
  exit(1)
}

if (!GIST_ID || !GIST_TOKEN) {
  stderr.write('Missing environment variables\n')
  exit(1)
}

const github = new GitHubAPIs()
const files = {}
const ENCODING = {encoding: 'utf8'}
const CONTAINER = join(__dirname, '..', '..', 'out')
const LIST = readdirSync(CONTAINER)

const filename = item => {
  const {name, ext} = parse(item)
  return name + '-' + GIT_REPO_TAG + ext
}

for (const item of LIST) {
  const content = readFileSync(join(CONTAINER, item), ENCODING)
  files[filename(item)] = {content}
}

github.authenticate({
  type: 'token',
  token: GIST_TOKEN
})

co(main)

function * main () {
  const artifactsResponse = yield github.gists.edit({
    id: GIST_ID,
    files
  })
  const rfiles = artifactsResponse.files
  const summary = LIST.reduce(
    (prev, item) => {
      const {raw_url: url, type, language} = rfiles[filename(item)]
      return prev + ` * [${item}](${url})\n  - Type: ${type}\n  - Language: ${language}\n\n`
    },
    `# Release ${GIT_REPO_TAG} - ${NOW.toISOString()}\n\n`
  )
  yield gists.edit({
    id: GIST_ID,
    files: {
      [ filename('summary.md') ]: {
        content: summary
      }
    }
  })
}
<|endoftext|>


<commit_before>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

(c) 2006-2008: Martin Stubenschrott <stubenschrott@gmx.net>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

liberator.config = { //{{{
    /*** required options, no checks done if they really exist, so be careful ***/
    name: "Vimperator",
    hostApplication: "Firefox",

    /*** optional options, there are checked for existance and a fallback provided  ***/
    features: ["bookmarks", "hints", "history", "marks", "quickmarks", "session", "tabs", "windows"],
    defaults: { guioptions: "" },
    guioptions: { m: ["toolbar-menubar"], T: ["nav-bar"], b: ["PersonalToolbar"] },

    get visualbellWindow() { return getBrowser().mPanelContainer; },

    autocommands: [["LocationChange", "Triggered when changing tabs or when naviagtion to a new location"],
                   ["PageLoadPre",    "Triggered after a page load is initiated"],
                   ["PageLoad",       "Triggered when a page gets (re)loaded/opened"],
                   ["Quit",           "Triggered before exiting Firefox"],
                   ["Startup",        "Triggered after Firefox starts"]],

    dialogs: [
        ["about",            "About Firefox",
            function () { openDialog("chrome://browser/content/aboutDialog.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
        ["addbookmark",      "Add bookmark for the current page",
            function () { PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksRootId); }],
        ["addons",           "Manage Add-ons",
            function () { BrowserOpenAddonsMgr(); }],
        ["bookmarks",        "List your bookmarks",
            function () { openDialog("chrome://browser/content/bookmarks/bookmarksPanel.xul", "Bookmarks", "dialog,centerscreen,width=600,height=600"); }],
        ["checkupdates",     "Check for updates",
            function () { checkForUpdates(); }],
        ["cleardata",        "Clear private data",
            function () { Cc[GLUE_CID].getService(Ci.nsIBrowserGlue).sanitize(window || null); }],
        ["console",          "JavaScript console",
            function () { toJavaScriptConsole(); }],
        ["customizetoolbar", "Customize the Toolbar",
            function () { BrowserCustomizeToolbar(); }],
        ["dominspector",     "DOM Inspector",
            function () { inspectDOMDocument(content.document); }],
        ["downloads",        "Manage Downloads",
            function () { toOpenWindowByType('Download:Manager', 'chrome://mozapps/content/downloads/downloads.xul', 'chrome,dialog=no,resizable'); }],
        ["history",          "List your history",
            function () { openDialog("chrome://browser/content/history/history-panel.xul", "History", "dialog,centerscreen,width=600,height=600"); }],
        ["import",           "Import Preferences, Bookmarks, History, etc. from other browsers",
            function () { BrowserImport(); }],
        ["openfile",         "Open the file selector dialog",
            function () { BrowserOpenFileWindow(); }],
        ["pageinfo",         "Show information about the current page",
            function () { BrowserPageInfo(); }],
        ["pagesource",       "View page source",
            function () { BrowserViewSourceOfDocument(content.document); }],
        ["places",           "Places Organizer: Manage your bookmarks and history",
            function () { PlacesCommandHook.showPlacesOrganizer(ORGANIZER_ROOT_BOOKMARKS); }],
        ["preferences",      "Show Firefox preferences dialog",
            function () { openPreferences(); }],
        ["printpreview",     "Preview the page before printing",
            function () { PrintUtils.printPreview(onEnterPrintPreview, onExitPrintPreview); }],
        ["printsetup",       "Setup the page size and orientation before printing",
            function () { PrintUtils.showPageSetup(); }],
        ["print",            "Show print dialog",
            function () { PrintUtils.print(); }],
        ["saveframe",        "Save frame to disk",
            function () { saveFrameDocument(); }],
        ["savepage",         "Save page to disk",
            function () { saveDocument(window.content.document); }],
        ["searchengines",    "Manage installed search engines",
            function () { openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
        ["selectionsource",  "View selection source",
            function () { liberator.buffer.viewSelectionSource(); }]
    ],

    // they are sorted by relevance, not alphabetically
    helpFiles: ["intro.html", "tutorial.html", "starting.html",
        "browsing.html", "buffer.html", "pattern.html", "options.html",
        "tabs.html", "hints.html", "map.html", "eval.html", "marks.html",
        "repeat.html", "autocommands.html", "print.html", "developer.html",
        "various.html"
    ],

    init: function ()
    {
        function incrementURL(count)
        {
            var url = liberator.buffer.URL;
            var regex = /(.*?)(\d+)(\D*)$/;

            var matches = url.match(regex);
            if (!matches || !matches[2]) // no number to increment
                return liberator.beep();

            var newNum = parseInt(matches[2], 10) + count + ""; // "" to make sure its a string
            var nums = newNum.match(/^(-?)(\d+)$/);
            var oldLength = matches[2].replace(/-/, "").length, newLength = nums[2].length;
            newNum = nums[1] || "";
            for (let i = 0; i < oldLength - newLength; i++)
                newNum += "0"; // keep leading zeros
            newNum += nums[2];

            liberator.open(matches[1] + newNum + matches[3]);
        }

        // load Vimperator specific modules
        liberator.loadModule("search",     liberator.Search);
        liberator.loadModule("bookmarks",  liberator.Bookmarks);
        liberator.loadModule("history",    liberator.History);
        liberator.loadModule("tabs",       liberator.Tabs);
        liberator.loadModule("marks",      liberator.Marks);
        liberator.loadModule("quickmarks", liberator.QuickMarks);
        liberator.loadModule("hints",      liberator.Hints);

        ////////////////////////////////////////////////////////////////////////////////
        ////////////////////// MAPPINGS ////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////{{{

        liberator.mappings.add([liberator.modes.NORMAL],
            ["y"], "Yank current location to the clipboard",
            function () { liberator.util.copyToClipboard(liberator.buffer.URL, true); });

        // opening websites
        liberator.mappings.add([liberator.modes.NORMAL],
            ["o"], "Open one or more URLs",
            function () { liberator.commandline.open(":", "open ", liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL], ["O"],
            "Open one or more URLs, based on current location",
            function () { liberator.commandline.open(":", "open " + liberator.buffer.URL, liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL], ["t"],
            "Open one or more URLs in a new tab",
            function () { liberator.commandline.open(":", "tabopen ", liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL], ["T"],
            "Open one or more URLs in a new tab, based on current location",
            function () { liberator.commandline.open(":", "tabopen " + liberator.buffer.URL, liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL],
            ["<C-a>"], "Increment last number in URL",
            function (count) { incrementURL(count > 1 ? count : 1); },
            { flags: liberator.Mappings.flags.COUNT });

        liberator.mappings.add([liberator.modes.NORMAL],
            ["<C-x>"], "Decrement last number in URL",
            function (count) { incrementURL(-(count > 1 ? count : 1)); },
            { flags: liberator.Mappings.flags.COUNT });

        liberator.mappings.add([liberator.modes.NORMAL], ["~"],
            "Open home directory",
            function () { liberator.open("~"); });

        liberator.mappings.add([liberator.modes.NORMAL], ["gh"],
            "Open homepage",
            function () { BrowserHome(); });

        liberator.mappings.add([liberator.modes.NORMAL], ["gH"],
            "Open homepage in a new tab",
            function ()
            {
                var homepages = gHomeButton.getHomePage();
                liberator.open(homepages, /\bhomepage\b/.test(liberator.options["activate"]) ?
                        liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB);
            });

        liberator.mappings.add([liberator.modes.NORMAL], ["gu"],
            "Go to parent directory",
            function (count)
            {
                function isDirectory(url)
                {
                    if (/^file:\/|^\//.test(url))
                    {
                        //var strippedFilename = url.replace(/^(file:\/\/)?(.*)/, "$2");
                        var file = liberator.io.getFile(url);
                        if (!file.exists() || !file.isDirectory())
                            return false;
                        else
                            return true;
                    }

                    // for all other locations just check if the URL ends with /
                    return /\/$/.test(url);
                }

                if (count < 1)
                    count = 1;

                var url = liberator.buffer.URL;
                for (var i = 0; i < count; i++)
                {
                    if (isDirectory(url))
                        url = url.replace(/^(.*?:)(.*?)([^\/]+\/*)$/, "$1$2/");
                    else
                        url = url.replace(/^(.*?:)(.*?)(\/+[^\/]+)$/, "$1$2/");
                }
                url = url.replace(/^(.*:\/+.*?)\/+$/, "$1/"); // get rid of more than 1 / at the end

                if (url == liberator.buffer.URL)
                {
                    liberator.beep();
                    return;
                }
                liberator.open(url);
            },
            { flags: liberator.Mappings.flags.COUNT });

        liberator.mappings.add([liberator.modes.NORMAL], ["gU"],
            "Go to the root of the website",
            function ()
            {
                var uri = content.document.location;
                if (/(about|mailto):/.test(uri.protocol)) // exclude these special protocols for now
                {
                    liberator.beep();
                    return;
                }
                liberator.open(uri.protocol + "//" + (uri.host || "") + "/");
            });

        liberator.mappings.add([liberator.modes.NORMAL], ["<C-l>"],
            "Redraw the screen",
            function () { liberator.commands.get("redraw").execute(); });

        /////////////////////////////////////////////////////////////////////////////}}}
        ////////////////////// COMMANDS ////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////{{{

        liberator.commands.add(["downl[oads]", "dl"],
            "Show progress of current downloads",
            function ()
            {
                liberator.open("chrome://mozapps/content/downloads/downloads.xul",
                    (liberator.options["newtab"] == "all" || liberator.options["newtab"].split(",").indexOf("downloads") != -1) ?
                        liberator.NEW_TAB : liberator.CURRENT_TAB);
            });

        liberator.commands.add(["o[pen]", "e[dit]"],
            "Open one or more URLs in the current tab",
            function (args, special)
            {
                if (args)
                {
                    liberator.open(args);
                }
                else
                {
                    if (special)
                        BrowserReloadSkipCache();
                    else
                        BrowserReload();
                }
            },
            {
                completer: function (filter) { return liberator.completion.url(filter); }
            });

        liberator.commands.add(["redr[aw]"],
            "Redraw the screen",
            function ()
            {
                var wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
                                getInterface(Components.interfaces.nsIDOMWindowUtils);
                wu.redraw();
            });

        // TODO: move sidebar commands to ui.js?
        liberator.commands.add(["sbcl[ose]"],
            "Close the sidebar window",
            function (args)
            {
                if (args)
                {
                    liberator.echoerr("E488: Trailing characters");
                    return;
                }

                if (document.getElementById("sidebar-box").hidden == false)
                    toggleSidebar();
            });

        liberator.commands.add(["sideb[ar]", "sb[ar]", "sbope[n]"],
            "Open the sidebar window",
            function (args)
            {
                if (!args)
                {
                    liberator.echoerr("E471: Argument required");
                    return;
                }

                // do nothing if the requested sidebar is already open
                if (document.getElementById("sidebar-title").value == args)
                {
                    document.getElementById("sidebar-box").contentWindow.focus();
                    return;
                }

                var menu = document.getElementById("viewSidebarMenu");

                for (var i = 0; i < menu.childNodes.length; i++)
                {
                    if (menu.childNodes[i].label == args)
                    {
                        eval(menu.childNodes[i].getAttribute("oncommand"));
                        break;
                    }
                }
            },
            {
                completer: function (filter)
                {
                    var menu = document.getElementById("viewSidebarMenu");
                    var nodes = [];

                    for (var i = 0; i < menu.childNodes.length; i++)
                        nodes.push([menu.childNodes[i].label, ""]);

                    return [0, liberator.completion.filter(nodes, filter)];
                }
            });

        liberator.commands.add(["winc[lose]", "wc[lose]"],
            "Close window",
            function (args) { window.close(); });

        liberator.commands.add(["wino[pen]", "wo[pen]", "wine[dit]"],
            "Open one or more URLs in a new window",
            function (args)
            {
                if (args)
                    liberator.open(args, liberator.NEW_WINDOW);
                else
                    liberator.open("about:blank", liberator.NEW_WINDOW);
            },
            {
                completer: function (filter) { return liberator.completion.url(filter); }
            });

        /////////////////////////////////////////////////////////////////////////////}}}
        ////////////////////// OPTIONS /////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////{{{

        liberator.options.add(["online"],
            "Set the 'work offline' option",
            "boolean", true,
            {
                setter: function (value)
                {
                    var ioService = Components.classes['@mozilla.org/network/io-service;1'].
                                    getService(Components.interfaces.nsIIOService2);

                    ioService.offline = !value;
                    gPrefService.setBoolPref("browser.offline", ioService.offline);
                },

                getter: function ()
                {
                    return Components.classes['@mozilla.org/network/io-service;1'].
                           getService(Components.interfaces.nsIIOService2).offline;
                }
            });

        liberator.options.add(["titlestring"],
            "Change the title of the window",
            "string", "Vimperator",
            {
                setter: function (value)
                {
                    try
                    {
                        var id = liberator.config.mainWindowID || "main-window";
                        document.getElementById(id).setAttribute("titlemodifier", value);
                        if (window.content.document.title.length > 0)
                            document.title = window.content.document.title + " - " + value;
                        else
                            document.title = value;
                    }
                    catch (e)
                    {
                        liberator.log("Couldn't set titlestring", 3);
                    }
                }
            });

        liberator.options.add(["urlseparator"],
            "Set the separator regexp used to separate multiple URL args",
            "string", ",\\s");
    }
    //}}}
}; //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<commit_msg>remove FF2 workaround and call doCommand() on sidebar menu items<commit_after>/***** BEGIN LICENSE BLOCK ***** {{{
Version: MPL 1.1/GPL 2.0/LGPL 2.1

The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.

(c) 2006-2008: Martin Stubenschrott <stubenschrott@gmx.net>

Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
}}} ***** END LICENSE BLOCK *****/

liberator.config = { //{{{
    /*** required options, no checks done if they really exist, so be careful ***/
    name: "Vimperator",
    hostApplication: "Firefox",

    /*** optional options, there are checked for existance and a fallback provided  ***/
    features: ["bookmarks", "hints", "history", "marks", "quickmarks", "session", "tabs", "windows"],
    defaults: { guioptions: "" },
    guioptions: { m: ["toolbar-menubar"], T: ["nav-bar"], b: ["PersonalToolbar"] },

    get visualbellWindow() { return getBrowser().mPanelContainer; },

    autocommands: [["LocationChange", "Triggered when changing tabs or when naviagtion to a new location"],
                   ["PageLoadPre",    "Triggered after a page load is initiated"],
                   ["PageLoad",       "Triggered when a page gets (re)loaded/opened"],
                   ["Quit",           "Triggered before exiting Firefox"],
                   ["Startup",        "Triggered after Firefox starts"]],

    dialogs: [
        ["about",            "About Firefox",
            function () { openDialog("chrome://browser/content/aboutDialog.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
        ["addbookmark",      "Add bookmark for the current page",
            function () { PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksRootId); }],
        ["addons",           "Manage Add-ons",
            function () { BrowserOpenAddonsMgr(); }],
        ["bookmarks",        "List your bookmarks",
            function () { openDialog("chrome://browser/content/bookmarks/bookmarksPanel.xul", "Bookmarks", "dialog,centerscreen,width=600,height=600"); }],
        ["checkupdates",     "Check for updates",
            function () { checkForUpdates(); }],
        ["cleardata",        "Clear private data",
            function () { Cc[GLUE_CID].getService(Ci.nsIBrowserGlue).sanitize(window || null); }],
        ["console",          "JavaScript console",
            function () { toJavaScriptConsole(); }],
        ["customizetoolbar", "Customize the Toolbar",
            function () { BrowserCustomizeToolbar(); }],
        ["dominspector",     "DOM Inspector",
            function () { inspectDOMDocument(content.document); }],
        ["downloads",        "Manage Downloads",
            function () { toOpenWindowByType('Download:Manager', 'chrome://mozapps/content/downloads/downloads.xul', 'chrome,dialog=no,resizable'); }],
        ["history",          "List your history",
            function () { openDialog("chrome://browser/content/history/history-panel.xul", "History", "dialog,centerscreen,width=600,height=600"); }],
        ["import",           "Import Preferences, Bookmarks, History, etc. from other browsers",
            function () { BrowserImport(); }],
        ["openfile",         "Open the file selector dialog",
            function () { BrowserOpenFileWindow(); }],
        ["pageinfo",         "Show information about the current page",
            function () { BrowserPageInfo(); }],
        ["pagesource",       "View page source",
            function () { BrowserViewSourceOfDocument(content.document); }],
        ["places",           "Places Organizer: Manage your bookmarks and history",
            function () { PlacesCommandHook.showPlacesOrganizer(ORGANIZER_ROOT_BOOKMARKS); }],
        ["preferences",      "Show Firefox preferences dialog",
            function () { openPreferences(); }],
        ["printpreview",     "Preview the page before printing",
            function () { PrintUtils.printPreview(onEnterPrintPreview, onExitPrintPreview); }],
        ["printsetup",       "Setup the page size and orientation before printing",
            function () { PrintUtils.showPageSetup(); }],
        ["print",            "Show print dialog",
            function () { PrintUtils.print(); }],
        ["saveframe",        "Save frame to disk",
            function () { saveFrameDocument(); }],
        ["savepage",         "Save page to disk",
            function () { saveDocument(window.content.document); }],
        ["searchengines",    "Manage installed search engines",
            function () { openDialog("chrome://browser/content/search/engineManager.xul", "_blank", "chrome,dialog,modal,centerscreen"); }],
        ["selectionsource",  "View selection source",
            function () { liberator.buffer.viewSelectionSource(); }]
    ],

    // they are sorted by relevance, not alphabetically
    helpFiles: ["intro.html", "tutorial.html", "starting.html",
        "browsing.html", "buffer.html", "pattern.html", "options.html",
        "tabs.html", "hints.html", "map.html", "eval.html", "marks.html",
        "repeat.html", "autocommands.html", "print.html", "developer.html",
        "various.html"
    ],

    init: function ()
    {
        function incrementURL(count)
        {
            var url = liberator.buffer.URL;
            var regex = /(.*?)(\d+)(\D*)$/;

            var matches = url.match(regex);
            if (!matches || !matches[2]) // no number to increment
                return liberator.beep();

            var newNum = parseInt(matches[2], 10) + count + ""; // "" to make sure its a string
            var nums = newNum.match(/^(-?)(\d+)$/);
            var oldLength = matches[2].replace(/-/, "").length, newLength = nums[2].length;
            newNum = nums[1] || "";
            for (let i = 0; i < oldLength - newLength; i++)
                newNum += "0"; // keep leading zeros
            newNum += nums[2];

            liberator.open(matches[1] + newNum + matches[3]);
        }

        // load Vimperator specific modules
        liberator.loadModule("search",     liberator.Search);
        liberator.loadModule("bookmarks",  liberator.Bookmarks);
        liberator.loadModule("history",    liberator.History);
        liberator.loadModule("tabs",       liberator.Tabs);
        liberator.loadModule("marks",      liberator.Marks);
        liberator.loadModule("quickmarks", liberator.QuickMarks);
        liberator.loadModule("hints",      liberator.Hints);

        ////////////////////////////////////////////////////////////////////////////////
        ////////////////////// MAPPINGS ////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////{{{

        liberator.mappings.add([liberator.modes.NORMAL],
            ["y"], "Yank current location to the clipboard",
            function () { liberator.util.copyToClipboard(liberator.buffer.URL, true); });

        // opening websites
        liberator.mappings.add([liberator.modes.NORMAL],
            ["o"], "Open one or more URLs",
            function () { liberator.commandline.open(":", "open ", liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL], ["O"],
            "Open one or more URLs, based on current location",
            function () { liberator.commandline.open(":", "open " + liberator.buffer.URL, liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL], ["t"],
            "Open one or more URLs in a new tab",
            function () { liberator.commandline.open(":", "tabopen ", liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL], ["T"],
            "Open one or more URLs in a new tab, based on current location",
            function () { liberator.commandline.open(":", "tabopen " + liberator.buffer.URL, liberator.modes.EX); });

        liberator.mappings.add([liberator.modes.NORMAL],
            ["<C-a>"], "Increment last number in URL",
            function (count) { incrementURL(count > 1 ? count : 1); },
            { flags: liberator.Mappings.flags.COUNT });

        liberator.mappings.add([liberator.modes.NORMAL],
            ["<C-x>"], "Decrement last number in URL",
            function (count) { incrementURL(-(count > 1 ? count : 1)); },
            { flags: liberator.Mappings.flags.COUNT });

        liberator.mappings.add([liberator.modes.NORMAL], ["~"],
            "Open home directory",
            function () { liberator.open("~"); });

        liberator.mappings.add([liberator.modes.NORMAL], ["gh"],
            "Open homepage",
            function () { BrowserHome(); });

        liberator.mappings.add([liberator.modes.NORMAL], ["gH"],
            "Open homepage in a new tab",
            function ()
            {
                var homepages = gHomeButton.getHomePage();
                liberator.open(homepages, /\bhomepage\b/.test(liberator.options["activate"]) ?
                        liberator.NEW_TAB : liberator.NEW_BACKGROUND_TAB);
            });

        liberator.mappings.add([liberator.modes.NORMAL], ["gu"],
            "Go to parent directory",
            function (count)
            {
                function isDirectory(url)
                {
                    if (/^file:\/|^\//.test(url))
                    {
                        //var strippedFilename = url.replace(/^(file:\/\/)?(.*)/, "$2");
                        var file = liberator.io.getFile(url);
                        if (!file.exists() || !file.isDirectory())
                            return false;
                        else
                            return true;
                    }

                    // for all other locations just check if the URL ends with /
                    return /\/$/.test(url);
                }

                if (count < 1)
                    count = 1;

                var url = liberator.buffer.URL;
                for (var i = 0; i < count; i++)
                {
                    if (isDirectory(url))
                        url = url.replace(/^(.*?:)(.*?)([^\/]+\/*)$/, "$1$2/");
                    else
                        url = url.replace(/^(.*?:)(.*?)(\/+[^\/]+)$/, "$1$2/");
                }
                url = url.replace(/^(.*:\/+.*?)\/+$/, "$1/"); // get rid of more than 1 / at the end

                if (url == liberator.buffer.URL)
                {
                    liberator.beep();
                    return;
                }
                liberator.open(url);
            },
            { flags: liberator.Mappings.flags.COUNT });

        liberator.mappings.add([liberator.modes.NORMAL], ["gU"],
            "Go to the root of the website",
            function ()
            {
                var uri = content.document.location;
                if (/(about|mailto):/.test(uri.protocol)) // exclude these special protocols for now
                {
                    liberator.beep();
                    return;
                }
                liberator.open(uri.protocol + "//" + (uri.host || "") + "/");
            });

        liberator.mappings.add([liberator.modes.NORMAL], ["<C-l>"],
            "Redraw the screen",
            function () { liberator.commands.get("redraw").execute(); });

        /////////////////////////////////////////////////////////////////////////////}}}
        ////////////////////// COMMANDS ////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////{{{

        liberator.commands.add(["downl[oads]", "dl"],
            "Show progress of current downloads",
            function ()
            {
                liberator.open("chrome://mozapps/content/downloads/downloads.xul",
                    (liberator.options["newtab"] == "all" || liberator.options["newtab"].split(",").indexOf("downloads") != -1) ?
                        liberator.NEW_TAB : liberator.CURRENT_TAB);
            });

        liberator.commands.add(["o[pen]", "e[dit]"],
            "Open one or more URLs in the current tab",
            function (args, special)
            {
                if (args)
                {
                    liberator.open(args);
                }
                else
                {
                    if (special)
                        BrowserReloadSkipCache();
                    else
                        BrowserReload();
                }
            },
            {
                completer: function (filter) { return liberator.completion.url(filter); }
            });

        liberator.commands.add(["redr[aw]"],
            "Redraw the screen",
            function ()
            {
                var wu = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
                                getInterface(Components.interfaces.nsIDOMWindowUtils);
                wu.redraw();
            });

        // TODO: move sidebar commands to ui.js?
        liberator.commands.add(["sbcl[ose]"],
            "Close the sidebar window",
            function (args)
            {
                if (args)
                {
                    liberator.echoerr("E488: Trailing characters");
                    return;
                }

                if (document.getElementById("sidebar-box").hidden == false)
                    toggleSidebar();
            });

        liberator.commands.add(["sideb[ar]", "sb[ar]", "sbope[n]"],
            "Open the sidebar window",
            function (args)
            {
                if (!args)
                {
                    liberator.echoerr("E471: Argument required");
                    return;
                }

                // do nothing if the requested sidebar is already open
                if (document.getElementById("sidebar-title").value == args)
                {
                    document.getElementById("sidebar-box").contentWindow.focus();
                    return;
                }

                var menu = document.getElementById("viewSidebarMenu");

                for (var i = 0; i < menu.childNodes.length; i++)
                {
                    if (menu.childNodes[i].label == args)
                    {
                        menu.childNodes[i].doCommand();
                        break;
                    }
                }
            },
            {
                completer: function (filter)
                {
                    var menu = document.getElementById("viewSidebarMenu");
                    var nodes = [];

                    for (var i = 0; i < menu.childNodes.length; i++)
                        nodes.push([menu.childNodes[i].label, ""]);

                    return [0, liberator.completion.filter(nodes, filter)];
                }
            });

        liberator.commands.add(["winc[lose]", "wc[lose]"],
            "Close window",
            function (args) { window.close(); });

        liberator.commands.add(["wino[pen]", "wo[pen]", "wine[dit]"],
            "Open one or more URLs in a new window",
            function (args)
            {
                if (args)
                    liberator.open(args, liberator.NEW_WINDOW);
                else
                    liberator.open("about:blank", liberator.NEW_WINDOW);
            },
            {
                completer: function (filter) { return liberator.completion.url(filter); }
            });

        /////////////////////////////////////////////////////////////////////////////}}}
        ////////////////////// OPTIONS /////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////{{{

        liberator.options.add(["online"],
            "Set the 'work offline' option",
            "boolean", true,
            {
                setter: function (value)
                {
                    var ioService = Components.classes['@mozilla.org/network/io-service;1'].
                                    getService(Components.interfaces.nsIIOService2);

                    ioService.offline = !value;
                    gPrefService.setBoolPref("browser.offline", ioService.offline);
                },

                getter: function ()
                {
                    return Components.classes['@mozilla.org/network/io-service;1'].
                           getService(Components.interfaces.nsIIOService2).offline;
                }
            });

        liberator.options.add(["titlestring"],
            "Change the title of the window",
            "string", "Vimperator",
            {
                setter: function (value)
                {
                    try
                    {
                        var id = liberator.config.mainWindowID || "main-window";
                        document.getElementById(id).setAttribute("titlemodifier", value);
                        if (window.content.document.title.length > 0)
                            document.title = window.content.document.title + " - " + value;
                        else
                            document.title = value;
                    }
                    catch (e)
                    {
                        liberator.log("Couldn't set titlestring", 3);
                    }
                }
            });

        liberator.options.add(["urlseparator"],
            "Set the separator regexp used to separate multiple URL args",
            "string", ",\\s");
    }
    //}}}
}; //}}}

// vim: set fdm=marker sw=4 ts=4 et:
<|endoftext|>


<commit_before>/*
 * Simple jQuery plugin for use with django-file-picker
 * Author: Caktus Consulting Group, LLC (http://www.caktusgroup.com/)
 * Source: https://github.com/caktus/django-file-picker
 *
 * Copyright (C) 2011 by Caktus Consulting Group, LLC
 *
 * 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.
 */

jQuery(document).ready(function($) {

    var FILE_PICKER_ROOT = '/file-picker/';

    function install_file_picker(el, picker_names, urls) {
        var pickers = {};
        $.each(picker_names, function(type, name) {
            pickers[type] = urls[name];
        });
        var overlay = $('<div>').addClass('file-picker-overlay').overlay({
            effect: 'apple',
            speed: 'fast'
        }).filePicker({
            onImageClick: function(e, insert) {
                insertAtCaret(el.id, insert);
            }
        }).insertBefore($(el));

        if (pickers.image) {
            var anchor = $('<a>').text('Insert Image').attr({
                'name': 'filepicker-image',
                'title': 'Insert Image',
                'href': '#'
            }).click(function(e) {
                e.preventDefault();
                var conf = $(overlay).data('filePicker').getConf();
                conf.url = pickers.image;
                $(overlay).data('overlay').load();
            }).prependTo('.form-row.body');
        }
        if (pickers.file) {
            var anchor = $('<a>').text('Insert File').attr({
                'name': 'filepicker-file',
                'title': 'Insert File',
                'href': '#'
            }).click(function(e) {
                e.preventDefault();
                var conf = $(overlay).data('filePicker').getConf();
                conf.url = pickers.file;
                $(overlay).data('overlay').load();
            }).prependTo('.form-row.body');
        }
    }

    $('textarea.filepicker').each(function(idx, el) {
        var picker_names = get_file_picker_types(el);
        if (picker_names) {
            var names = [];
            $.each(picker_names, function(key, val) { names.push(val); });
            $.getJSON(FILE_PICKER_ROOT, {'pickers': names}, function(response) {
                install_file_picker(el, picker_names, response.pickers);
            });
        }
    });
});

<commit_msg>format anchors<commit_after>/*
 * Simple jQuery plugin for use with django-file-picker
 * Author: Caktus Consulting Group, LLC (http://www.caktusgroup.com/)
 * Source: https://github.com/caktus/django-file-picker
 *
 * Copyright (C) 2011 by Caktus Consulting Group, LLC
 *
 * 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.
 */

jQuery(document).ready(function($) {

    var FILE_PICKER_ROOT = '/file-picker/';

    function install_file_picker(el, picker_names, urls) {
        var pickers = {};
        $.each(picker_names, function(type, name) {
            pickers[type] = urls[name];
        });
        var overlay = $('<div>').addClass('file-picker-overlay').overlay({
            effect: 'apple',
            speed: 'fast'
        }).filePicker({
            onImageClick: function(e, insert) {
                insertAtCaret(el.id, insert);
            }
        }).insertBefore($(el));

        if (pickers.image) {
            var anchor = $('<a>').text('Insert Image').attr({
                'name': 'filepicker-image',
                'title': 'Insert Image',
                'href': '#'
            }).css('display', 'block').click(function(e) {
                e.preventDefault();
                var conf = $(overlay).data('filePicker').getConf();
                conf.url = pickers.image;
                $(overlay).data('overlay').load();
            }).prependTo('.form-row.body');
        }
        if (pickers.file) {
            var anchor = $('<a>').text('Insert File').attr({
                'name': 'filepicker-file',
                'title': 'Insert File',
                'href': '#'
            }).css('display', 'block').click(function(e) {
                e.preventDefault();
                var conf = $(overlay).data('filePicker').getConf();
                conf.url = pickers.file;
                $(overlay).data('overlay').load();
            }).prependTo('.form-row.body');
        }
    }

    $('textarea.filepicker').each(function(idx, el) {
        var picker_names = get_file_picker_types(el);
        if (picker_names) {
            var names = [];
            $.each(picker_names, function(key, val) { names.push(val); });
            $.getJSON(FILE_PICKER_ROOT, {'pickers': names}, function(response) {
                install_file_picker(el, picker_names, response.pickers);
            });
        }
    });
});

<|endoftext|>


<commit_before>/*
 * @license
 * Copyright 2019 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const { Fido2Lib } = require('fido2-lib');
const { coerceToBase64Url,
        coerceToArrayBuffer
      } = require('fido2-lib/lib/utils');
const fs = require('fs');

const low = require('lowdb');

try {
  fs.readFileSync('./.data/db.json');
} catch (e) {
  fs.mkdirSync('./.data');
  fs.writeFileSync('./.data/db.json', '');
}

const FileSync = require('lowdb/adapters/FileSync');
const adapter = new FileSync('.data/db.json');
const db = low(adapter);

router.use(express.json());

const f2l = new Fido2Lib({
    timeout: 30*1000*60,
    rpId: "webauthn-codelab-resolution.glitch.me", // TODO: Auto generate
    rpName: "WebAuthn Codelab",
    challengeSize: 32,
    cryptoParams: [-7]
});

db.defaults({
  users: []
}).write();

const csrfCheck = (req, res, next) => {
  if (req.header('X-Requested-With') != 'XMLHttpRequest') {
    res.status(400).json({error: 'invalid access.'});
    return;
  }
  next();
};

/**
 * Checks CSRF protection using custom header `X-Requested-With`
 * If cookie doesn't contain `username`, consider the user is not authenticated.
 **/
const sessionCheck = (req, res, next) => {
  if (!req.cookies['signed-in']) {
    res.status(401).json({error: 'not signed in.'});
    return;
  }
  next();
};

/**
 * Check username, create a new account if it doesn't exist.
 * Set a `username` cookie.
 **/
router.post('/username', (req, res) => {
  const username = req.body.username;
  // Only check username, no need to check password as this is a mock
  if (!username) {
    res.status(400).send({ error: 'Bad request' });
    return;
  } else {
    // See if account already exists
    let user = db.get('users')
      .find({ username: username })
      .value();
    // If user entry is not created yet, create one
    if (!user) {
      user = {
        username: username,
        id: coerceToBase64Url(crypto.randomBytes(32), 'user.id'),
        credentials: []
      }
      db.get('users')
        .push(user)
        .write();
    }
    // Set username cookie
    res.cookie('username', username);
    // If sign-in succeeded, redirect to `/home`.
    res.json(user);
  }
});

/**
 * Verifies user credential and let the user sign-in.
 * No preceding registration required.
 * This only checks if `username` is not empty string and ignores the password.
 **/
router.post('/password', (req, res) => {
  if (!req.body.password) {
    res.status(401).json({error: 'Enter at least one random letter.'});
    return;
  }
  const user = db.get('users')
    .find({ username: req.cookies.username })
    .value();

  if (!user) {
    res.status(401).json({error: 'Enter username first.'});
    return;
  }

  res.cookie('signed-in', 'yes');
  res.json(user);
});

router.get('/signout', (req, res) => {
  // Remove cookies
  res.clearCookie('username');
  res.clearCookie('signed-in');
  // Redirect to `/`
  res.redirect(302, '/');
});

/**
 * Returns a credential id
 * (This server only stores one key per username.)
 * Response format:
 * ```{
 *   username: String,
 *   credentials: [Credential]
 * }```

 Credential
 ```
 {
   credId: String,
   publicKey: String,
   aaguid: ??,
   prevCounter: Int
 };
 ```
 **/
router.post('/getKeys', csrfCheck, sessionCheck, (req, res) => {
  const user = db.get('users')
    .find({ username: req.cookies.username })
    .value();
  res.json(user || {});
});

/**
 * Removes a credential id attached to the user
 * Responds with empty JSON `{}`
 **/
router.post('/removeKey', csrfCheck, sessionCheck, (req, res) => {
  const credId = req.query.credId;
  const username = req.cookies.username;
  const user = db.get('users')
    .find({ username: username })
    .value();

  const newCreds = user.credentials.filter(cred => {
    // Leave credential ids that do not match
    return cred.credId !== credId;
  });

  db.get('users')
    .find({ username: username })
    .assign({ credentials: newCreds })
    .write();

  res.json({});
});

router.get('/resetDB', (req, res) => {
  db.set('users', []).write();
  const users = db.get('users').value();
  res.json(users);
});

/**
 * Respond with required information to call navigator.credential.create()
 * Input is passed via `req.body` with similar format as output
 * Output format:
 * ```{
     rp: {
       id: String,
       name: String
     },
     user: {
       displayName: String,
       id: String,
       name: String
     },
     publicKeyCredParams: [{  // @herrjemand
       type: 'public-key', alg: -7
     }],
     timeout: Number,
     challenge: String,
     excludeCredentials: [{
       id: String,
       type: 'public-key',
       transports: [('ble'|'nfc'|'usb'|'internal'), ...]
     }, ...],
     authenticatorSelection: {
       authenticatorAttachment: ('platform'|'cross-platform'),
       requireResidentKey: Boolean,
       userVerification: ('required'|'preferred'|'discouraged')
     },
     attestation: ('none'|'indirect'|'direct')
 * }```
 **/
router.post('/registerRequest', csrfCheck, sessionCheck, async (req, res) => {
  const username = req.cookies.username;
  const user = db.get('users')
    .find({ username: username })
    .value();
  try {
    const response = await f2l.attestationOptions();
    response.user = {
      displayName: 'No name',
      id: user.id,
      name: user.username
    };
    response.challenge = coerceToBase64Url(response.challenge, 'challenge');
    res.cookie('challenge', response.challenge);
    response.excludeCredentials = [];
    if (user.credentials.length > 0) {
      for (let cred of user.credentials) {
        response.excludeCredentials.push({
          id: cred.credId,
          type: 'public-key',
          transports: ['internal']
        });
      }
    }
    const as = {}; // authenticatorSelection
    const aa = req.body.authenticatorSelection.authenticatorAttachment;
    const rr = req.body.authenticatorSelection.requireResidentKey;
    const uv = req.body.authenticatorSelection.userVerification;
    const cp = req.body.attestation; // attestationConveyancePreference
    let asFlag = false;

    if (aa && (aa == 'platform' || aa == 'cross-platform')) {
      asFlag = true;
      as.authenticatorAttachment = aa;
    }
    if (rr && typeof rr == 'boolean') {
      asFlag = true;
      as.requireResidentKey = rr;
    }
    if (uv && (uv == 'required' || uv == 'preferred' || uv == 'discouraged')) {
      asFlag = true;
      as.userVerification = uv;
    }
    if (asFlag) {
      response.authenticatorSelection = as;
    }
    if (cp && (cp == 'none' || cp == 'indirect' || cp == 'direct')) {
      response.attestation = cp;
    }

    res.json(response);
  } catch (e) {
    res.status(400).send({ error: e });
  }
});

/**
 * Register user credential.
 * Input format:
 * ```{
     id: String,
     type: 'public-key',
     rawId: String,
     response: {
       clientDataJSON: String,
       attestationObject: String,
       signature: String,
       userHandle: String
     }
 * }```
 **/
router.post('/registerResponse', csrfCheck, sessionCheck, async (req, res) => {
  const username = req.cookies.username;
  const challenge = coerceToArrayBuffer(req.cookies.challenge, 'challenge');
  const credId = req.body.id;
  const type = req.body.type;

  try {
    const clientAttestationResponse = { response: {} };
    clientAttestationResponse.rawId =
      coerceToArrayBuffer(req.body.rawId, "rawId");
    clientAttestationResponse.response.clientDataJSON =
      coerceToArrayBuffer(req.body.response.clientDataJSON, "clientDataJSON");
    clientAttestationResponse.response.attestationObject =
      coerceToArrayBuffer(req.body.response.attestationObject, "attestationObject");

    let origin = '';
    if (req.get('User-Agent').indexOf('okhttp') > -1) {
      const octArray = process.env.ANDROID_SHA256HASH.split(':').map(h => parseInt(h, 16));
      const androidHash = coerceToBase64Url(octArray, 'Android Hash');
      origin = `android:apk-key-hash:${androidHash}`; // TODO: Generate
    } else {
      origin = `https://${req.get('host')}`;
    }

    const attestationExpectations = {
      challenge: challenge,
      origin: origin,
      factor: "either"
    };

    const regResult = await f2l.attestationResult(clientAttestationResponse, attestationExpectations);

    const credential = {
      credId: coerceToBase64Url(regResult.authnrData.get("credId"), 'credId'),
      publicKey: regResult.authnrData.get("credentialPublicKeyPem"),
      aaguid: coerceToBase64Url(regResult.authnrData.get("aaguid"), 'aaguid'),
      prevCounter: regResult.authnrData.get("counter")
    };

    const user = db.get('users')
      .find({ username: username })
      .value();

    user.credentials.push(credential);

    db.get('users')
      .find({ username: username })
      .assign(user)
      .write();

    res.clearCookie('challenge');

    // Respond with user info
    res.json(user);
  } catch (e) {
    res.clearCookie('challenge');
    res.status(400).send({ error: e.message });
  }
});

/**
 * Respond with required information to call navigator.credential.get()
 * Input is passed via `req.body` with similar format as output
 * Output format:
 * ```{
     challenge: String,
     userVerification: ('required'|'preferred'|'discouraged'),
     allowCredentials: [{
       id: String,
       type: 'public-key',
       transports: [('ble'|'nfc'|'usb'|'internal'), ...]
     }, ...]
 * }```
 **/
router.post('/signinRequest', csrfCheck, async (req, res) => {
  try {
    const user = db.get('users')
      .find({ username: req.cookies.username })
      .value();

    if (!user) {
      // Send empty response if user is not registered yet.
      res.json({error: 'User not found.'});
      return;
    }

    const credId = req.query.credId;

    const response = await f2l.assertionOptions();

    // const response = {};
    response.userVerification = req.body.userVerification || 'preferred';
    response.challenge = coerceToBase64Url(response.challenge, 'challenge');
    res.cookie('challenge', response.challenge);

    response.allowCredentials = [];

    if (credId) {
      for (let cred of user.credentials) {
        if (cred.credId == credId) {
          response.allowCredentials.push({
            id: cred.credId,
            type: 'public-key',
            transports: ['internal']
          });
        }
      }
    }

    res.json(response);
  } catch (e) {
    res.status(400).json({ error: e });
  }
});

/**
 * Authenticate the user.
 * Input format:
 * ```{
     id: String,
     type: 'public-key',
     rawId: String,
     response: {
       clientDataJSON: String,
       authenticatorData: String,
       signature: String,
       userHandle: String
     }
 * }```
 **/
router.post('/signinResponse', csrfCheck, async (req, res) => {
  // Query the user
  const user = db.get('users')
    .find({ username: req.cookies.username })
    .value();

  let credential = null;
  for (let cred of user.credentials) {
    if (cred.credId === req.body.id) {
      credential = cred;
    }
  }

  try {
    if (!credential) {
      throw 'Authenticating credential not found.';
    }

    const challenge = coerceToArrayBuffer(req.cookies.challenge, 'challenge');
    const origin = `https://${req.get('host')}`; // TODO: Temporary work around for scheme

    const clientAssertionResponse = { response: {} };
    clientAssertionResponse.rawId =
      coerceToArrayBuffer(req.body.rawId, "rawId");
    clientAssertionResponse.response.clientDataJSON =
      coerceToArrayBuffer(req.body.response.clientDataJSON, "clientDataJSON");
    clientAssertionResponse.response.authenticatorData =
      coerceToArrayBuffer(req.body.response.authenticatorData, "authenticatorData");
    clientAssertionResponse.response.signature =
      coerceToArrayBuffer(req.body.response.signature, "signature");
    clientAssertionResponse.response.userHandle =
      coerceToArrayBuffer(req.body.response.userHandle, "userHandle");
    const assertionExpectations = {
      challenge: challenge,
      origin: origin,
      factor: "either",
      publicKey: credential.publicKey,
      prevCounter: credential.prevCounter,
      userHandle: coerceToArrayBuffer(user.id, 'userHandle')
    };
    const result = await f2l.assertionResult(clientAssertionResponse, assertionExpectations);

    credential.prevCounter = result.authnrData.get("counter");

    db.get('users')
      .find({ id: req.cookies.id })
      .assign(user)
      .write();

    res.clearCookie('challenge');
    res.cookie('signed-in', 'yes');
    res.json(user);
  } catch (e) {
    res.clearCookie('challenge');
    res.status(400).json({ error: e });
  }
});

module.exports = router;
<commit_msg>Dynamic hostname<commit_after>/*
 * @license
 * Copyright 2019 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const { Fido2Lib } = require('fido2-lib');
const { coerceToBase64Url,
        coerceToArrayBuffer
      } = require('fido2-lib/lib/utils');
const fs = require('fs');

const low = require('lowdb');

try {
  fs.readFileSync('./.data/db.json');
} catch (e) {
  fs.mkdirSync('./.data');
  fs.writeFileSync('./.data/db.json', '');
}

const FileSync = require('lowdb/adapters/FileSync');
const adapter = new FileSync('.data/db.json');
const db = low(adapter);

router.use(express.json());

const f2l = new Fido2Lib({
    timeout: 30*1000*60,
    rpId: process.env.HOSTNAME,
    rpName: "WebAuthn Codelab",
    challengeSize: 32,
    cryptoParams: [-7]
});

db.defaults({
  users: []
}).write();

const csrfCheck = (req, res, next) => {
  if (req.header('X-Requested-With') != 'XMLHttpRequest') {
    res.status(400).json({error: 'invalid access.'});
    return;
  }
  next();
};

/**
 * Checks CSRF protection using custom header `X-Requested-With`
 * If cookie doesn't contain `username`, consider the user is not authenticated.
 **/
const sessionCheck = (req, res, next) => {
  if (!req.cookies['signed-in']) {
    res.status(401).json({error: 'not signed in.'});
    return;
  }
  next();
};

/**
 * Check username, create a new account if it doesn't exist.
 * Set a `username` cookie.
 **/
router.post('/username', (req, res) => {
  const username = req.body.username;
  // Only check username, no need to check password as this is a mock
  if (!username) {
    res.status(400).send({ error: 'Bad request' });
    return;
  } else {
    // See if account already exists
    let user = db.get('users')
      .find({ username: username })
      .value();
    // If user entry is not created yet, create one
    if (!user) {
      user = {
        username: username,
        id: coerceToBase64Url(crypto.randomBytes(32), 'user.id'),
        credentials: []
      }
      db.get('users')
        .push(user)
        .write();
    }
    // Set username cookie
    res.cookie('username', username);
    // If sign-in succeeded, redirect to `/home`.
    res.json(user);
  }
});

/**
 * Verifies user credential and let the user sign-in.
 * No preceding registration required.
 * This only checks if `username` is not empty string and ignores the password.
 **/
router.post('/password', (req, res) => {
  if (!req.body.password) {
    res.status(401).json({error: 'Enter at least one random letter.'});
    return;
  }
  const user = db.get('users')
    .find({ username: req.cookies.username })
    .value();

  if (!user) {
    res.status(401).json({error: 'Enter username first.'});
    return;
  }

  res.cookie('signed-in', 'yes');
  res.json(user);
});

router.get('/signout', (req, res) => {
  // Remove cookies
  res.clearCookie('username');
  res.clearCookie('signed-in');
  // Redirect to `/`
  res.redirect(302, '/');
});

/**
 * Returns a credential id
 * (This server only stores one key per username.)
 * Response format:
 * ```{
 *   username: String,
 *   credentials: [Credential]
 * }```

 Credential
 ```
 {
   credId: String,
   publicKey: String,
   aaguid: ??,
   prevCounter: Int
 };
 ```
 **/
router.post('/getKeys', csrfCheck, sessionCheck, (req, res) => {
  const user = db.get('users')
    .find({ username: req.cookies.username })
    .value();
  res.json(user || {});
});

/**
 * Removes a credential id attached to the user
 * Responds with empty JSON `{}`
 **/
router.post('/removeKey', csrfCheck, sessionCheck, (req, res) => {
  const credId = req.query.credId;
  const username = req.cookies.username;
  const user = db.get('users')
    .find({ username: username })
    .value();

  const newCreds = user.credentials.filter(cred => {
    // Leave credential ids that do not match
    return cred.credId !== credId;
  });

  db.get('users')
    .find({ username: username })
    .assign({ credentials: newCreds })
    .write();

  res.json({});
});

router.get('/resetDB', (req, res) => {
  db.set('users', []).write();
  const users = db.get('users').value();
  res.json(users);
});

/**
 * Respond with required information to call navigator.credential.create()
 * Input is passed via `req.body` with similar format as output
 * Output format:
 * ```{
     rp: {
       id: String,
       name: String
     },
     user: {
       displayName: String,
       id: String,
       name: String
     },
     publicKeyCredParams: [{  // @herrjemand
       type: 'public-key', alg: -7
     }],
     timeout: Number,
     challenge: String,
     excludeCredentials: [{
       id: String,
       type: 'public-key',
       transports: [('ble'|'nfc'|'usb'|'internal'), ...]
     }, ...],
     authenticatorSelection: {
       authenticatorAttachment: ('platform'|'cross-platform'),
       requireResidentKey: Boolean,
       userVerification: ('required'|'preferred'|'discouraged')
     },
     attestation: ('none'|'indirect'|'direct')
 * }```
 **/
router.post('/registerRequest', csrfCheck, sessionCheck, async (req, res) => {
  const username = req.cookies.username;
  const user = db.get('users')
    .find({ username: username })
    .value();
  try {
    const response = await f2l.attestationOptions();
    response.user = {
      displayName: 'No name',
      id: user.id,
      name: user.username
    };
    response.challenge = coerceToBase64Url(response.challenge, 'challenge');
    res.cookie('challenge', response.challenge);
    response.excludeCredentials = [];
    if (user.credentials.length > 0) {
      for (let cred of user.credentials) {
        response.excludeCredentials.push({
          id: cred.credId,
          type: 'public-key',
          transports: ['internal']
        });
      }
    }
    const as = {}; // authenticatorSelection
    const aa = req.body.authenticatorSelection.authenticatorAttachment;
    const rr = req.body.authenticatorSelection.requireResidentKey;
    const uv = req.body.authenticatorSelection.userVerification;
    const cp = req.body.attestation; // attestationConveyancePreference
    let asFlag = false;

    if (aa && (aa == 'platform' || aa == 'cross-platform')) {
      asFlag = true;
      as.authenticatorAttachment = aa;
    }
    if (rr && typeof rr == 'boolean') {
      asFlag = true;
      as.requireResidentKey = rr;
    }
    if (uv && (uv == 'required' || uv == 'preferred' || uv == 'discouraged')) {
      asFlag = true;
      as.userVerification = uv;
    }
    if (asFlag) {
      response.authenticatorSelection = as;
    }
    if (cp && (cp == 'none' || cp == 'indirect' || cp == 'direct')) {
      response.attestation = cp;
    }

    res.json(response);
  } catch (e) {
    res.status(400).send({ error: e });
  }
});

/**
 * Register user credential.
 * Input format:
 * ```{
     id: String,
     type: 'public-key',
     rawId: String,
     response: {
       clientDataJSON: String,
       attestationObject: String,
       signature: String,
       userHandle: String
     }
 * }```
 **/
router.post('/registerResponse', csrfCheck, sessionCheck, async (req, res) => {
  const username = req.cookies.username;
  const challenge = coerceToArrayBuffer(req.cookies.challenge, 'challenge');
  const credId = req.body.id;
  const type = req.body.type;

  try {
    const clientAttestationResponse = { response: {} };
    clientAttestationResponse.rawId =
      coerceToArrayBuffer(req.body.rawId, "rawId");
    clientAttestationResponse.response.clientDataJSON =
      coerceToArrayBuffer(req.body.response.clientDataJSON, "clientDataJSON");
    clientAttestationResponse.response.attestationObject =
      coerceToArrayBuffer(req.body.response.attestationObject, "attestationObject");

    let origin = '';
    if (req.get('User-Agent').indexOf('okhttp') > -1) {
      const octArray = process.env.ANDROID_SHA256HASH.split(':').map(h => parseInt(h, 16));
      const androidHash = coerceToBase64Url(octArray, 'Android Hash');
      origin = `android:apk-key-hash:${androidHash}`; // TODO: Generate
    } else {
      origin = `https://${req.get('host')}`;
    }

    const attestationExpectations = {
      challenge: challenge,
      origin: origin,
      factor: "either"
    };

    const regResult = await f2l.attestationResult(clientAttestationResponse, attestationExpectations);

    const credential = {
      credId: coerceToBase64Url(regResult.authnrData.get("credId"), 'credId'),
      publicKey: regResult.authnrData.get("credentialPublicKeyPem"),
      aaguid: coerceToBase64Url(regResult.authnrData.get("aaguid"), 'aaguid'),
      prevCounter: regResult.authnrData.get("counter")
    };

    const user = db.get('users')
      .find({ username: username })
      .value();

    user.credentials.push(credential);

    db.get('users')
      .find({ username: username })
      .assign(user)
      .write();

    res.clearCookie('challenge');

    // Respond with user info
    res.json(user);
  } catch (e) {
    res.clearCookie('challenge');
    res.status(400).send({ error: e.message });
  }
});

/**
 * Respond with required information to call navigator.credential.get()
 * Input is passed via `req.body` with similar format as output
 * Output format:
 * ```{
     challenge: String,
     userVerification: ('required'|'preferred'|'discouraged'),
     allowCredentials: [{
       id: String,
       type: 'public-key',
       transports: [('ble'|'nfc'|'usb'|'internal'), ...]
     }, ...]
 * }```
 **/
router.post('/signinRequest', csrfCheck, async (req, res) => {
  try {
    const user = db.get('users')
      .find({ username: req.cookies.username })
      .value();

    if (!user) {
      // Send empty response if user is not registered yet.
      res.json({error: 'User not found.'});
      return;
    }

    const credId = req.query.credId;

    const response = await f2l.assertionOptions();

    // const response = {};
    response.userVerification = req.body.userVerification || 'preferred';
    response.challenge = coerceToBase64Url(response.challenge, 'challenge');
    res.cookie('challenge', response.challenge);

    response.allowCredentials = [];

    if (credId) {
      for (let cred of user.credentials) {
        if (cred.credId == credId) {
          response.allowCredentials.push({
            id: cred.credId,
            type: 'public-key',
            transports: ['internal']
          });
        }
      }
    }

    res.json(response);
  } catch (e) {
    res.status(400).json({ error: e });
  }
});

/**
 * Authenticate the user.
 * Input format:
 * ```{
     id: String,
     type: 'public-key',
     rawId: String,
     response: {
       clientDataJSON: String,
       authenticatorData: String,
       signature: String,
       userHandle: String
     }
 * }```
 **/
router.post('/signinResponse', csrfCheck, async (req, res) => {
  // Query the user
  const user = db.get('users')
    .find({ username: req.cookies.username })
    .value();

  let credential = null;
  for (let cred of user.credentials) {
    if (cred.credId === req.body.id) {
      credential = cred;
    }
  }

  try {
    if (!credential) {
      throw 'Authenticating credential not found.';
    }

    const challenge = coerceToArrayBuffer(req.cookies.challenge, 'challenge');
    const origin = `https://${req.get('host')}`; // TODO: Temporary work around for scheme

    const clientAssertionResponse = { response: {} };
    clientAssertionResponse.rawId =
      coerceToArrayBuffer(req.body.rawId, "rawId");
    clientAssertionResponse.response.clientDataJSON =
      coerceToArrayBuffer(req.body.response.clientDataJSON, "clientDataJSON");
    clientAssertionResponse.response.authenticatorData =
      coerceToArrayBuffer(req.body.response.authenticatorData, "authenticatorData");
    clientAssertionResponse.response.signature =
      coerceToArrayBuffer(req.body.response.signature, "signature");
    clientAssertionResponse.response.userHandle =
      coerceToArrayBuffer(req.body.response.userHandle, "userHandle");
    const assertionExpectations = {
      challenge: challenge,
      origin: origin,
      factor: "either",
      publicKey: credential.publicKey,
      prevCounter: credential.prevCounter,
      userHandle: coerceToArrayBuffer(user.id, 'userHandle')
    };
    const result = await f2l.assertionResult(clientAssertionResponse, assertionExpectations);

    credential.prevCounter = result.authnrData.get("counter");

    db.get('users')
      .find({ id: req.cookies.id })
      .assign(user)
      .write();

    res.clearCookie('challenge');
    res.cookie('signed-in', 'yes');
    res.json(user);
  } catch (e) {
    res.clearCookie('challenge');
    res.status(400).json({ error: e });
  }
});

module.exports = router;
<|endoftext|>