<commit_before>/** @jsx jsx */

import React, { useEffect } from 'react' //eslint-disable-line
import { jsx, Styled } from 'theme-ui'
import styled from '@emotion/styled'
import PropTypes from 'prop-types'

const Modal = styled.div`
  position: absolute;
  top: 0;
  content: '';
  transition: all 0.3s;
  z-index: 2;
  background: black;
  width: 100%;
  height: 100%;
  opacity: ${props => (props.visible ? '1' : '0')};
  visibility: ${props => (props.visible ? 'visible' : 'hidden')};
  section {
    p {
      line-height: 1.6;
      margin: 0 0 2em 0;
      text-align: center;
    }
  }
`

const Form = styled.form`
  position: relative;
  width: 100%;
  label {
    padding: 0 16px;
  }
  input,
  textarea {
    padding: 12px;
    color: black;
    background: white;
    -webkit-appearance: none;
    border-radius: 0;
    &::placeholder {
      color: ${props => props.theme.colors.shadow};
    }
  }

  &::invalid {
    box-shadow: none;
  }
  &::required {
    box-shadow: none;
  }
  &::optional {
    box-shadow: none;
  }
`

const Input = styled.input`
  margin: 0 0 8px;
  width: 100%;
`
const TextArea = styled.textarea`
  margin: 0 0 8px;
  width: 100%;
`

const Submit = styled.input`
  cursor: pointer;
  transition: 0.2s;
  width: 100%;
  font-weight: bold;
  padding: 12px;
  color: white !important;
  background: black !important;
  border: 1px solid white;
`

const encode = data => {
  return Object.keys(data)
    .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
    .join('&')
}

class CovidForm extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: '',
      email: '',
      restaurant: '',
      address: '',
      notes: '',
      showModal: false,
      showPrompt: true,
    }
  }

  handleInputChange = event => {
    const target = event.target
    const value = target.value
    const email = target.email
    const name = target.name
    const restaurant = target.restaurant
    const address = target.address
    const notes = target.notes
    this.setState({
      [email]: value,
      [name]: value,
      [restaurant]: value,
      [address]: value,
      [notes]: value,
    })
  }

  componentDidMount() {
    this.handleSubmit = event => {
      fetch('/?no-cache=1', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: encode({ 'form-name': 'contact', ...this.state }),
      })
        .then(this.handleSuccess)
        .catch(error => alert(error))
      event.preventDefault()
      this.handleSuccess()
      window.analytics.track('Covid Lander Form Submitted', {})
      window.analytics.identify({
        email: this.state.email,
        name: this.state.name,
        restaurant: this.state.restaurant,
        address: this.state.address,
        notes: this.state.notes,
        tags: [
          {
            name: 'Covid',
          },
        ],
      })
    }
  }

  handleSuccess = () => {
    this.setState({
      email: '',
      name: '',
      restaurant: '',
      address: '',
      notes: '',
      showModal: true,
      showPrompt: false,
    })
  }

  closeModal = () => {
    this.setState({ showModal: false, showPrompt: true })
  }

  render(props) {
    return (
      <>
        <Form
          name="Covid"
          onSubmit={this.handleSubmit}
          overlay={this.state.visible}
          onClick={this.closeModal}
        >
          <Modal visible={this.state.showModal}>

          </Modal>

          <Input
            name="name"
            type="name"
            placeholder="Name*"
            value={this.state.name}
            onChange={this.handleInputChange}
            required
          />
          <Input
            name="email"
            type="email"
            placeholder="Email*"
            value={this.state.email}
            onChange={this.handleInputChange}
            required
          />
          <Input
            name="restaurant"
            type="restaurant"
            placeholder="Restaurant*"
            value={this.state.restaurant}
            onChange={this.handleInputChange}
            required
          />
          <Input
            name="address"
            type="address"
            placeholder="Address*"
            value={this.state.address}
            onChange={this.handleInputChange}
            required
          />
          <TextArea
            name="notes"
            type="notes"
            placeholder="Notes - Number of dishes that need to be photographed, interior / exterior shots, preferred time of day, etc."
            value={this.state.notes}
            onChange={this.handleInputChange}
            required
          />
          <Submit
            className="button"
            name="submit"
            type="submit"
            value="Submit"
          />
        </Form>
      </>
    )
  }
}

CovidForm.propTypes = {
  data: PropTypes.object,
}

export default CovidForm
<commit_msg>Covid response recording<commit_after>/** @jsx jsx */

import React, { useEffect } from 'react' //eslint-disable-line
import { jsx, Styled } from 'theme-ui'
import styled from '@emotion/styled'
import PropTypes from 'prop-types'

const Modal = styled.div`
  position: absolute;
  top: 0;
  content: '';
  transition: all 0.3s;
  z-index: 2;
  background: black;
  width: 100%;
  height: 100%;
  opacity: ${props => (props.visible ? '1' : '0')};
  visibility: ${props => (props.visible ? 'visible' : 'hidden')};
  section {
    p {
      line-height: 1.6;
      margin: 0 0 2em 0;
      text-align: center;
    }
  }
`

const Form = styled.form`
  position: relative;
  width: 100%;
  label {
    padding: 0 16px;
  }
  input,
  textarea {
    padding: 12px;
    color: black;
    background: white;
    -webkit-appearance: none;
    border-radius: 0;
    &::placeholder {
      color: ${props => props.theme.colors.shadow};
    }
  }

  &::invalid {
    box-shadow: none;
  }
  &::required {
    box-shadow: none;
  }
  &::optional {
    box-shadow: none;
  }
`

const Input = styled.input`
  margin: 0 0 8px;
  width: 100%;
`
const TextArea = styled.textarea`
  margin: 0 0 8px;
  width: 100%;
`

const Submit = styled.input`
  cursor: pointer;
  transition: 0.2s;
  width: 100%;
  font-weight: bold;
  padding: 12px;
  color: white !important;
  background: black !important;
  border: 1px solid white;
`

const encode = data => {
  return Object.keys(data)
    .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
    .join('&')
}

class CovidForm extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: '',
      email: '',
      restaurant: '',
      address: '',
      message: '',
      showModal: false,
      showPrompt: true,
    }
  }

  handleInputChange = event => {
    const target = event.target
    const value = target.value
    const email = target.email
    const name = target.name
    const restaurant = target.restaurant
    const address = target.address
    const message = target.message
    this.setState({
      [email]: value,
      [name]: value,
      [restaurant]: value,
      [address]: value,
      [message]: value,
    })
  }

  componentDidMount() {
    this.handleSubmit = event => {
      fetch('/?no-cache=1', {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: encode({ 'form-name': 'contact', ...this.state }),
      })
        .then(this.handleSuccess)
        .catch(error => alert(error))
      event.preventDefault()
      this.handleSuccess()
      window.analytics.track('Covid Lander Form Submitted', {})
      window.analytics.identify({
        email: this.state.email,
        name: this.state.name,
        restaurant: this.state.restaurant,
        address: this.state.address,
        message: this.state.message,
        tags: [
          {
            name: 'Covid',
          },
        ],
      })
    }
  }

  handleSuccess = () => {
    this.setState({
      email: '',
      name: '',
      restaurant: '',
      address: '',
      message: '',
      showModal: true,
      showPrompt: false,
    })
  }

  closeModal = () => {
    this.setState({ showModal: false, showPrompt: true })
  }

  render(props) {
    return (
      <>
        <Form
          name="Covid"
          onSubmit={this.handleSubmit}
          overlay={this.state.visible}
          onClick={this.closeModal}
        >
          <Modal visible={this.state.showModal}>

          </Modal>

          <Input
            name="name"
            type="name"
            placeholder="Name*"
            value={this.state.name}
            onChange={this.handleInputChange}
            required
          />
          <Input
            name="email"
            type="email"
            placeholder="Email*"
            value={this.state.email}
            onChange={this.handleInputChange}
            required
          />
          <Input
            name="restaurant"
            type="restaurant"
            placeholder="Restaurant*"
            value={this.state.restaurant}
            onChange={this.handleInputChange}
            required
          />
          <Input
            name="address"
            type="address"
            placeholder="Address*"
            value={this.state.address}
            onChange={this.handleInputChange}
            required
          />
          <TextArea
            name="message"
            type="message"
            placeholder="Notes - Number of dishes that need to be photographed, interior / exterior shots, preferred time of day, etc."
            value={this.state.message}
            onChange={this.handleInputChange}
            required
          />
          <Submit
            className="button"
            name="submit"
            type="submit"
            value="Submit"
          />
        </Form>
      </>
    )
  }
}

CovidForm.propTypes = {
  data: PropTypes.object,
}

export default CovidForm
<|endoftext|>


<commit_before>DisplaysManager = {

    instances: {},

    get: function(name, iTestCase, display, parent, params) {
        var k = name + iTestCase;
        if(!this.instances[k]) {
            this.instances[k] = window[name](params);
        }
        this.instances[k].init(parent, display);
        return this.instances[k];
    }
}

function ContextCursor(params) {

    var defaults = {
        color: 'rgb(255,128,0)'
    }
    params = Object.assign({}, defaults, params);

    this.position = false;

    this.set = function(x, y) {
        this.position = {
            x: x,
            y: y
        }
    }

    this.reset = function() {
        this.position = false;
    }

    this.render = function(ofs_left, scale) {
        if(!this.position || !params.context2d) {
            return;
        }
        params.context2d.beginPath();
        params.context2d.strokeStyle = params.color;
        params.context2d.lineWidth = scale > 20 ? 2 : 1;
        params.context2d.rect(
            ofs_left + scale * this.position.x + 0.5,
            scale * this.position.y + 0.5,
            scale,
            scale
        );
        params.context2d.stroke();
    }
}

function ContextGrid(params) {

    var defaults = {
        color: '#888',
        min_scale: 6
    }
    params = Object.assign({}, defaults, params);

    this.render = function(ofs_left, scale, cols, rows) {
        if(scale < params.min_scale) {
            return;
        }
        params.context2d.strokeStyle = params.color;
        params.context2d.lineWidth = 1;
        for(var x=1; x<cols; x++) {
            var dx = ofs_left + x * scale + 0.5;
            params.context2d.beginPath();
            params.context2d.moveTo(dx , 0);
            params.context2d.lineTo(dx, rows * scale);
            params.context2d.stroke();
        }
        for(var y=1; y<rows; y++) {
            var dy = y * scale + 0.5;
            params.context2d.beginPath();
            params.context2d.moveTo(ofs_left, dy);
            params.context2d.lineTo(ofs_left + cols * scale, dy);
            params.context2d.stroke();
        }
    }
}

function CanvasTooltip(params) {

    this.element = null;
    this.bounds = null;

    this.render = function() {
        if(this.element) {
            return;
        }
        this.element = $('
')
            .css('position', 'fixed')
            .css('z-index', '10000')
            .css('background', '#4A90E2')
            .css('color', '#FFF')
            .css('padding', '10px')
            .css('border-radius', '5px');
        $(document.body).append(this.element);
    }

    this.setBounds = function(ofs_left, scale, cols, rows) {
        this.bounds = {
            ofs_left: ofs_left,
            scale: scale,
            cols: cols,
            rows: rows
        }
    }

    this.show = function(e) {
        if(!this.bounds) {
            return;
        }
        var canvas_offset = params.canvas.offset();
        var x = e.pageX - canvas_offset.left - this.bounds.ofs_left;
        var y = e.pageY - canvas_offset.top;
        if(x < 0 || x > this.bounds.cols * this.bounds.scale) {
            this.hide();
            return;
        }

        var col = Math.floor(x / this.bounds.scale);
        var row = Math.floor(y / this.bounds.scale);
        var luminocity = params.getPixelLuminosity(col, row);

        this.render();
        this.element.show();
        this.element.css({
            left: e.pageX + 10,
            top: e.pageY + 10
        });
        var str = params.strings.messages.tooltip;
        str = str.replace('%1', col).replace('%2', row).replace('%3', luminocity);
        this.element.text(str);
    }

    this.hide = function() {
        this.element && this.element.hide();
    }

    params.canvas.mouseleave(this.hide.bind(this));
    params.canvas.mousemove(this.show.bind(this));
}

function BarcodeDisplay(params) {

    var parent;
    var canvas;
    var context2d;
    var cursor;
    var grid;
    var tooltip;

    function init(new_parent, new_display) {
        parent = new_parent;
        display = new_display;
        if(!display) {
            return;
        }
        canvas = $('<canvas>');
        parent.append(canvas);
        context2d = canvas[0].getContext('2d');
        cursor = new ContextCursor({
            context2d: context2d
        });
        grid = new ContextGrid({
            context2d: context2d
        });
        tooltip = new CanvasTooltip({
            canvas: canvas,
            strings: params.strings,
            getPixelLuminosity: calculatePixelLuminosity
        });
        render();
    }

    var image_data = '';
    var image;
    var image_loaded;
    var image_canvas;
    var image_context2d;

    function render() {
        if(!image || !display) {
            return;
        }
        var w = canvas[0].width = Math.floor(parent.width());
        var h = canvas[0].height = Math.floor(parent.height() * 0.5 - 10);
        if(w == 0) {
            return;
        }
        var scale = Math.min(Math.floor(w / image.width), Math.floor(h / image.height));
        var image_w = Math.floor(image.width * scale);
        var image_h = Math.floor(image.height * scale);
        var ofs_left = Math.floor(0.5 * (w - image_w));

        context2d.imageSmoothingEnabled = false;
        //context2d.mozImageSmoothingEnabled = false;

        context2d.clearRect(0, 0, w, h);
        context2d.drawImage(image, ofs_left, 0, image_w, image_h);

        grid.render(ofs_left, scale, image.width, image.height);
        tooltip.setBounds(ofs_left, scale, image.width, image.height);
        cursor.render(ofs_left, scale);
    }

    function setImage(data, callback) {
        if(image_data === data) {
            return;
        }
        image = false;
        image_loaded = false;
        image_data = data;
        cursor.reset();
        if(!image_canvas) {
            image_canvas = document.createElement('canvas');
            image_context2d = image_canvas.getContext('2d');
        }
        loadImage(callback);
    }

    function loadImage(callback) {
        if(image_loaded) {
            return callback && callback();
        }
        image = new Image();
        image.onload = function() {
            image_context2d.drawImage(image, 0, 0);
            image_loaded = true;
            render();
            callback && callback();
        }
        image.src = image_data;
    }

    function calculatePixelLuminosity(x, y) {
        var d = image_context2d.getImageData(x, y, 1, 1).data;
        // ITU BT.601
        var l = 0.299 * d[0] + 0.587 * d[1] + 0.114 * d[2];
        return Math.round(l);
    }

    return {

        init: init,

        setImage: setImage,

        render: render,

        getSize: function(callback) {
            loadImage(function() {
                callback({
                    width: image ? image.width : 0,
                    height: image ? image.height : 0
                })
            });
        },

        getPixelLuminosity: function(x, y, callback) {
            loadImage(function() {
                cursor.set(x, y);
                render();
                callback(calculatePixelLuminosity(x, y));
            })
        }

    }
}

function UserDisplay(params) {

    var pixels = [];
    var canvas, context2d;
    var data_size;
    var parent;
    var cursor;
    var grid;
    var tooltip;

    function init(new_parent, new_display) {
        parent = new_parent;
        display = new_display;
        if(!display) {
            return;
        }
        canvas = $('<canvas>');
        parent.append(canvas);
        context2d = canvas[0].getContext('2d');
        cursor = new ContextCursor({
            context2d: context2d
        });
        grid = new ContextGrid({
            context2d: context2d
        });
        tooltip = new CanvasTooltip({
            canvas: canvas,
            strings: params.strings,
            getPixelLuminosity: getPixelLuminosity
        });
        render();
    }

    function getPixelLuminosity(x, y) {
        if(!data_size) {
            return 0;
        }
        var ofs = y * data_size.width + x;
        return pixels[ofs];
    }

    function render(valid_result) {
        if(!pixels || w == 0 || !data_size) {
            return;
        }

        if(display) {
            context2d.imageSmoothingEnabled = false;
            //context2d.mozImageSmoothingEnabled = false;

            var w = canvas[0].width = Math.floor(parent.width());
            var h = canvas[0].height = Math.floor(parent.height() * 0.5 - 10);
            if(w == 0) {
                return;
            }
            var scale = Math.min(Math.floor(w / data_size.width), Math.floor(h / data_size.height));
            var ofs_left = Math.floor(0.5 * (w - data_size.width * scale));
            context2d.clearRect(0, 0, w, h);

            var i=0;
            for(var y=0; y<data_size.height; y++) {
                for(var x=0; x<data_size.width; x++) {
                    if(pixels[i] != 255) {
                        context2d.fillStyle = 'rgb(' + pixels[i] + ',' + pixels[i] + ',' + pixels[i] + ')';
                        context2d.fillRect(
                            ofs_left + x * scale,
                            y * scale,
                            scale,
                            scale
                        );
                    }
                    i++;
                }
            }

            grid.render(ofs_left, scale, data_size.width, data_size.height);
            cursor.render(ofs_left, scale);
            tooltip.setBounds(ofs_left, scale, data_size.width, data_size.height);
        }

        if(valid_result && 'data' in valid_result) {
            var i=0;
            var threshold = valid_result.threshold || 0;
            for(var y=0; y<data_size.height; y++) {
                for(var x=0; x<data_size.width; x++) {
                    if(Math.abs(pixels[i] - valid_result.data[y][x]) > threshold) {
                        if(display) {
                            context2d.beginPath();
                            context2d.strokeStyle = '#F00';
                            context2d.lineWidth = scale > 20 ? 2 : 1;
                            context2d.rect(
                                ofs_left + x * scale,
                                y * scale,
                                scale,
                                scale
                            );
                            context2d.stroke();
                        }

                        var msg = params.strings.messages.mistake_pixel;
                        msg = msg.replace('%1', pixels[i]).replace('%2', valid_result.data[y][x]);
                        return {
                            success: false,
                            message: msg
                        }
                    }
                    i++;
                }
            }
            return {
                success: true,
                message: params.strings.messages.success
            }
        }
    }

    return {

        init: init,

        setPixelLuminosity: function(x, y, v) {
            cursor.set(x, y);
            var v = Math.max(0, Math.min(v, 255));
            pixels[y * data_size.width + x] = v;
            render();
        },

        setSize: function(new_data_size) {
            data_size = new_data_size;
            pixels = new Array(data_size.width * data_size.height).fill(255)
            render();
        },

        clear: function() {
            cursor.reset();
            data_size = null;
            pixels = [];
            context2d.clearRect(0, 0, canvas[0].width, canvas[0].height);
        },

        render: render,

        diff: function(valid_result) {
            return render(valid_result);
        }
    }
}

function StringDisplay(params) {

    var data = '';
    var diff = '';
    var element;
    var wrapper;
    var display;

    function init(parent, new_display) {
        display = new_display;
        data = '';
        if(!display) {
            return;
        }
        element = $('')
        wrapper = $('
' + params.strings.messages.result + '
');
        wrapper.append(element).hide();
        parent.append(wrapper);
    }

    function isEmpty() {
        return data == '';
    }

    function render(html) {
        if(!display) {
            return;
        }
        wrapper.toggle(data != '');
        element.html(html);
    }

    return {

        init: init,

        setData: function(str) {
            str = '' + str;
            data = str;
            render(str);
        },

        diff: function(valid_result) {
            diff = '';
            var valid = true;

            var l = Math.max(valid_result.data.length, data.length);
            for(var i=0; i<l; i++) {
                if(valid_result.data[i] !== data[i]) {
                    valid = false;
                    if(i < data.length) {
                        diff += '' + data[i] + '';
                    }
                } else {
                    diff += data[i];
                }
            }
            render(diff);
            return {
                success: valid,
                message: valid ? params.strings.messages.success : params.strings.messages.mistake_digit
            }
        }
    }

}<commit_msg>barcode<commit_after>DisplaysManager = {

    instances: {},

    get: function(name, iTestCase, display, parent, params) {
        var k = name + iTestCase;
        if(!this.instances[k]) {
            this.instances[k] = window[name](params);
        }
        this.instances[k].init(parent, display);
        return this.instances[k];
    }
}

function ContextCursor(params) {

    var defaults = {
        color: 'rgb(255,128,0)'
    }
    params = Object.assign({}, defaults, params);

    this.position = false;

    this.set = function(x, y) {
        this.position = {
            x: x,
            y: y
        }
    }

    this.reset = function() {
        this.position = false;
    }

    this.render = function(ofs_left, scale) {
        if(!this.position || !params.context2d) {
            return;
        }
        params.context2d.beginPath();
        params.context2d.strokeStyle = params.color;
        params.context2d.lineWidth = scale > 5 ? 3 : 1;
        params.context2d.rect(
            ofs_left + scale * this.position.x + 0.5,
            scale * this.position.y + 0.5,
            scale,
            scale
        );
        params.context2d.stroke();
    }
}

function ContextGrid(params) {

    var defaults = {
        color: '#888',
        min_scale: 6
    }
    params = Object.assign({}, defaults, params);

    this.render = function(ofs_left, scale, cols, rows) {
        if(scale < params.min_scale) {
            return;
        }
        params.context2d.strokeStyle = params.color;
        params.context2d.lineWidth = 1;
        for(var x=1; x<cols; x++) {
            var dx = ofs_left + x * scale + 0.5;
            params.context2d.beginPath();
            params.context2d.moveTo(dx , 0);
            params.context2d.lineTo(dx, rows * scale);
            params.context2d.stroke();
        }
        for(var y=1; y<rows; y++) {
            var dy = y * scale + 0.5;
            params.context2d.beginPath();
            params.context2d.moveTo(ofs_left, dy);
            params.context2d.lineTo(ofs_left + cols * scale, dy);
            params.context2d.stroke();
        }
    }
}

function CanvasTooltip(params) {

    this.element = null;
    this.bounds = null;

    this.render = function() {
        if(this.element) {
            return;
        }
        this.element = $('
')
            .css('position', 'fixed')
            .css('z-index', '10000')
            .css('background', '#4A90E2')
            .css('color', '#FFF')
            .css('padding', '10px')
            .css('border-radius', '5px');
        $(document.body).append(this.element);
    }

    this.setBounds = function(ofs_left, scale, cols, rows) {
        this.bounds = {
            ofs_left: ofs_left,
            scale: scale,
            cols: cols,
            rows: rows
        }
    }

    this.show = function(e) {
        if(!this.bounds) {
            return;
        }
        var canvas_offset = params.canvas.offset();
        var x = e.pageX - canvas_offset.left - this.bounds.ofs_left;
        var y = e.pageY - canvas_offset.top;
        if(x < 0 || x > this.bounds.cols * this.bounds.scale) {
            this.hide();
            return;
        }

        var col = Math.floor(x / this.bounds.scale);
        var row = Math.floor(y / this.bounds.scale);
        var luminocity = params.getPixelLuminosity(col, row);

        this.render();
        this.element.show();
        this.element.css({
            left: e.pageX + 10,
            top: e.pageY + 10
        });
        var str = params.strings.messages.tooltip;
        str = str.replace('%1', col).replace('%2', row).replace('%3', luminocity);
        this.element.text(str);
    }

    this.hide = function() {
        this.element && this.element.hide();
    }

    params.canvas.mouseleave(this.hide.bind(this));
    params.canvas.mousemove(this.show.bind(this));
}

function BarcodeDisplay(params) {

    var parent;
    var canvas;
    var context2d;
    var cursor;
    var grid;
    var tooltip;

    function init(new_parent, new_display) {
        parent = new_parent;
        display = new_display;
        if(!display) {
            return;
        }
        canvas = $('<canvas>');
        parent.append(canvas);
        context2d = canvas[0].getContext('2d');
        cursor = new ContextCursor({
            context2d: context2d
        });
        grid = new ContextGrid({
            context2d: context2d
        });
        tooltip = new CanvasTooltip({
            canvas: canvas,
            strings: params.strings,
            getPixelLuminosity: calculatePixelLuminosity
        });
        render();
    }

    var image_data = '';
    var image;
    var image_loaded;
    var image_canvas;
    var image_context2d;

    function render() {
        if(!image || !display) {
            return;
        }
        var w = canvas[0].width = Math.floor(parent.width());
        var h = canvas[0].height = Math.floor(parent.height() * 0.5 - 10);
        if(w == 0) {
            return;
        }
        var scale = Math.min(Math.floor(w / image.width), Math.floor(h / image.height));
        var image_w = Math.floor(image.width * scale);
        var image_h = Math.floor(image.height * scale);
        var ofs_left = Math.floor(0.5 * (w - image_w));

        context2d.imageSmoothingEnabled = false;
        //context2d.mozImageSmoothingEnabled = false;

        context2d.clearRect(0, 0, w, h);
        context2d.drawImage(image, ofs_left, 0, image_w, image_h);

        grid.render(ofs_left, scale, image.width, image.height);
        tooltip.setBounds(ofs_left, scale, image.width, image.height);
        cursor.render(ofs_left, scale);
    }

    function setImage(data, callback) {
        if(image_data === data) {
            return;
        }
        image = false;
        image_loaded = false;
        image_data = data;
        cursor && cursor.reset();
        if(!image_canvas) {
            image_canvas = document.createElement('canvas');
            image_context2d = image_canvas.getContext('2d');
        }
        loadImage(callback);
    }

    function loadImage(callback) {
        if(image_loaded) {
            return callback && callback();
        }
        image = new Image();
        image.onload = function() {
            image_context2d.drawImage(image, 0, 0);
            image_loaded = true;
            render();
            callback && callback();
        }
        image.src = image_data;
    }

    function calculatePixelLuminosity(x, y) {
        var d = image_context2d.getImageData(x, y, 1, 1).data;
        // ITU BT.601
        var l = 0.299 * d[0] + 0.587 * d[1] + 0.114 * d[2];
        return Math.round(l);
    }

    return {

        init: init,

        setImage: setImage,

        render: render,

        getSize: function(callback) {
            loadImage(function() {
                callback({
                    width: image ? image.width : 0,
                    height: image ? image.height : 0
                })
            });
        },

        getPixelLuminosity: function(x, y, callback) {
            loadImage(function() {
                cursor && cursor.set(x, y);
                render();
                callback(calculatePixelLuminosity(x, y));
            })
        }

    }
}

function UserDisplay(params) {

    var pixels = [];
    var canvas, context2d;
    var data_size;
    var parent;
    var cursor;
    var grid;
    var tooltip;

    function init(new_parent, new_display) {
        parent = new_parent;
        display = new_display;
        if(!display) {
            return;
        }
        canvas = $('<canvas>');
        parent.append(canvas);
        context2d = canvas[0].getContext('2d');
        cursor = new ContextCursor({
            context2d: context2d
        });
        grid = new ContextGrid({
            context2d: context2d
        });
        tooltip = new CanvasTooltip({
            canvas: canvas,
            strings: params.strings,
            getPixelLuminosity: getPixelLuminosity
        });
        render();
    }

    function getPixelLuminosity(x, y) {
        if(!data_size) {
            return 0;
        }
        var ofs = y * data_size.width + x;
        return pixels[ofs];
    }

    function render(valid_result) {
        if(!pixels || w == 0 || !data_size) {
            return;
        }

        if(display) {
            context2d.imageSmoothingEnabled = false;
            //context2d.mozImageSmoothingEnabled = false;

            var w = canvas[0].width = Math.floor(parent.width());
            var h = canvas[0].height = Math.floor(parent.height() * 0.5 - 10);
            if(w == 0) {
                return;
            }
            var scale = Math.min(Math.floor(w / data_size.width), Math.floor(h / data_size.height));
            var ofs_left = Math.floor(0.5 * (w - data_size.width * scale));
            context2d.clearRect(0, 0, w, h);

            var i=0;
            for(var y=0; y<data_size.height; y++) {
                for(var x=0; x<data_size.width; x++) {
                    if(pixels[i] != 255) {
                        context2d.fillStyle = 'rgb(' + pixels[i] + ',' + pixels[i] + ',' + pixels[i] + ')';
                        context2d.fillRect(
                            ofs_left + x * scale,
                            y * scale,
                            scale,
                            scale
                        );
                    }
                    i++;
                }
            }

            grid.render(ofs_left, scale, data_size.width, data_size.height);
            cursor.render(ofs_left, scale);
            tooltip.setBounds(ofs_left, scale, data_size.width, data_size.height);
        }

        if(valid_result && 'data' in valid_result) {
            var i=0;
            var threshold = valid_result.threshold || 0;
            for(var y=0; y<data_size.height; y++) {
                for(var x=0; x<data_size.width; x++) {
                    if(Math.abs(pixels[i] - valid_result.data[y][x]) > threshold) {
                        if(display) {
                            context2d.beginPath();
                            context2d.strokeStyle = '#F00';
                            context2d.lineWidth = scale > 20 ? 2 : 1;
                            context2d.rect(
                                ofs_left + x * scale,
                                y * scale,
                                scale,
                                scale
                            );
                            context2d.stroke();
                        }

                        var msg = params.strings.messages.mistake_pixel;
                        msg = msg.replace('%1', pixels[i]).replace('%2', valid_result.data[y][x]);
                        return {
                            success: false,
                            message: msg
                        }
                    }
                    i++;
                }
            }
            return {
                success: true,
                message: params.strings.messages.success
            }
        }
    }

    return {

        init: init,

        setPixelLuminosity: function(x, y, v) {
            cursor && cursor.set(x, y);
            var v = Math.max(0, Math.min(v, 255));
            pixels[y * data_size.width + x] = v;
            render();
        },

        setSize: function(new_data_size) {
            data_size = new_data_size;
            pixels = new Array(data_size.width * data_size.height).fill(255)
            render();
        },

        clear: function() {
            cursor && cursor.reset();
            data_size = null;
            pixels = [];
            context2d.clearRect(0, 0, canvas[0].width, canvas[0].height);
        },

        render: render,

        diff: function(valid_result) {
            return render(valid_result);
        }
    }
}

function StringDisplay(params) {

    var data = '';
    var diff = '';
    var element;
    var wrapper;
    var display;

    function init(parent, new_display) {
        display = new_display;
        data = '';
        if(!display) {
            return;
        }
        element = $('')
        wrapper = $('
' + params.strings.messages.result + '
');
        wrapper.append(element).hide();
        parent.append(wrapper);
    }

    function isEmpty() {
        return data == '';
    }

    function render(html) {
        if(!display) {
            return;
        }
        wrapper.toggle(data != '');
        element.html(html);
    }

    return {

        init: init,

        setData: function(str) {
            str = '' + str;
            data = str;
            render(str);
        },

        diff: function(valid_result) {
            diff = '';
            var valid = true;

            var l = Math.max(valid_result.data.length, data.length);
            for(var i=0; i<l; i++) {
                if(valid_result.data[i] !== data[i]) {
                    valid = false;
                    if(i < data.length) {
                        diff += '' + data[i] + '';
                    }
                } else {
                    diff += data[i];
                }
            }
            render(diff);
            return {
                success: valid,
                message: valid ? params.strings.messages.success : params.strings.messages.mistake_digit
            }
        }
    }

}<|endoftext|>


<commit_before>import { useMutation, useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core'
import gql from 'graphql-tag'
import React, { useState } from 'react'

import { SupportLinkButton, Button } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import { P, H4 } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer'
import schema from 'src/pages/Services/schemas'

import styles from './Shared.styles'

const useStyles = makeStyles({
  ...styles,
  radioGroup: styles.radioGroup,
  radioLabel: {
    ...styles.radioLabel,
    width: 200
  }
})

const GET_CONFIG = gql`
  {
    accounts
  }
`
const SAVE_ACCOUNTS = gql`
  mutation SaveAccountsBC($accounts: JSONObject) {
    saveAccounts(accounts: $accounts)
  }
`

const options = [
  {
    code: 'enable',
    display: 'I will enable cash-out'
  },
  {
    code: 'disable',
    display: "I won't enable cash-out"
  }
]

const Blockcypher = ({ addData }) => {
  const classes = useStyles()

  const { data } = useQuery(GET_CONFIG)
  const [saveConfig] = useMutation(SAVE_ACCOUNTS, {
    onCompleted: () => addData({ zeroConf: 'blockcypher' })
  })

  const [selected, setSelected] = useState(null)
  const [error, setError] = useState(false)

  const accounts = data?.accounts ?? []

  const onSelect = e => {
    setSelected(e.target.value)
    setError(false)
  }

  const save = blockcypher => {
    const accounts = { blockcypher }
    return saveConfig({ variables: { accounts } })
  }

  return (
    <>

Blockcypher

        If you are enabling cash-out services, create a Blockcypher account.

      <SupportLinkButton
        link="https://support.lamassu.is/hc/en-us/articles/115001209472-Blockcypher"
        label="Configuring Blockcypher"
      />
      <RadioGroup
        labelClassName={classes.radioLabel}
        className={classes.radioGroup}
        options={options}
        value={selected}
        onChange={onSelect}
      />

        {selected === 'disable' && (
          <Button
            size="lg"
            onClick={() => addData({ zeroConf: 'none', zeroConfLimit: 0 })}
            className={classes.button}>
            Continue
          </Button>
        )}
        {selected === 'enable' && (
          <FormRenderer
            value={accounts.blockcypher}
            save={save}
            elements={schema.blockcypher.elements}
            validationSchema={schema.blockcypher.validationSchema}
            buttonLabel={'Continue'}
            buttonClass={classes.formButton}
          />
        )}

    </>
  )
}

export default Blockcypher
<commit_msg>fix: validate Blockcypher settings on initial wizard<commit_after>import { useMutation, useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core'
import gql from 'graphql-tag'
import React, { useState } from 'react'

import { SupportLinkButton, Button } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import { P, H4 } from 'src/components/typography'
import FormRenderer from 'src/pages/Services/FormRenderer'
import schema from 'src/pages/Services/schemas'

import styles from './Shared.styles'

const useStyles = makeStyles({
  ...styles,
  radioGroup: styles.radioGroup,
  radioLabel: {
    ...styles.radioLabel,
    width: 200
  }
})

const GET_CONFIG = gql`
  {
    accounts
  }
`
const SAVE_ACCOUNTS = gql`
  mutation SaveAccountsBC($accounts: JSONObject) {
    saveAccounts(accounts: $accounts)
  }
`

const options = [
  {
    code: 'enable',
    display: 'I will enable cash-out'
  },
  {
    code: 'disable',
    display: "I won't enable cash-out"
  }
]

const Blockcypher = ({ addData }) => {
  const classes = useStyles()

  const { data } = useQuery(GET_CONFIG)
  const [saveConfig] = useMutation(SAVE_ACCOUNTS, {
    onCompleted: () => addData({ zeroConf: 'blockcypher' })
  })

  const [selected, setSelected] = useState(null)
  const [error, setError] = useState(false)

  const accounts = data?.accounts ?? []

  const onSelect = e => {
    setSelected(e.target.value)
    setError(false)
  }

  const save = blockcypher => {
    const accounts = { blockcypher }
    return saveConfig({ variables: { accounts } })
  }

  return (
    <>

Blockcypher

        If you are enabling cash-out services, create a Blockcypher account.

      <SupportLinkButton
        link="https://support.lamassu.is/hc/en-us/articles/115001209472-Blockcypher"
        label="Configuring Blockcypher"
      />
      <RadioGroup
        labelClassName={classes.radioLabel}
        className={classes.radioGroup}
        options={options}
        value={selected}
        onChange={onSelect}
      />

        {selected === 'disable' && (
          <Button
            size="lg"
            onClick={() => addData({ zeroConf: 'none', zeroConfLimit: 0 })}
            className={classes.button}>
            Continue
          </Button>
        )}
        {selected === 'enable' && (
          <FormRenderer
            value={accounts.blockcypher}
            save={save}
            elements={schema.blockcypher.elements}
            validationSchema={schema.blockcypher.getValidationSchema}
            buttonLabel={'Continue'}
            buttonClass={classes.formButton}
          />
        )}

    </>
  )
}

export default Blockcypher
<|endoftext|>


<commit_before>var downloadMenu = [
	{ link: 'https://github.com/ThorQin/tui/archive/v1.0.8.zip', value: '下载工程源码' },
	{ link: 'https://github.com/ThorQin/tui/releases/download/v1.0.8/tui-1.0.8.zip', value: '下载开发包', icon:'fa-download' }];

var detailItemMenu = [
	{ key: "moveUp", value: "上移" ,icon:"fa-arrow-up"},
	{ key: "moveDown", value: "下移" ,icon:"fa-arrow-down" },
	{ value: "-" },
	{ key: "delete", value: "删除条目", icon: "fa-trash-o" }
];

var docTableCol = [
	{ name: "名称", key: 0, width: 80, _important: true, format: tui.ctrl.Grid.textEditor() },
	{ name: "类型", key: 1, width: 80, _important: true, format: tui.ctrl.Grid.textEditor() },
	{ name: "描述", key: 2, format: tui.ctrl.Grid.textEditor() },
	{
		name: "", format: tui.ctrl.Grid.menu(detailItemMenu, function (item, data) {
			var array = data.grid.data().src();
			if (item.key === "delete") {
				array.splice(data.rowIndex, 1);
				data.grid.data(array);
			} else if (item.key === "moveUp") {
				if (data.rowIndex > 0) {
					var tmp = array[data.rowIndex - 1];
					array[data.rowIndex - 1] = array[data.rowIndex];
					array[data.rowIndex] = tmp;
					data.grid.data(array);
				}
			} else if (item.key === "moveDown") {
				if (data.rowIndex < array.length - 1) {
					var tmp = array[data.rowIndex + 1];
					array[data.rowIndex + 1] = array[data.rowIndex];
					array[data.rowIndex] = tmp;
					data.grid.data(array);
				}
			}

		}),	width: 32, fixed:true
	}
];

function setupCatalogSize() {
	var size = tui.windowSize();
	var docIndex = document.getElementById("docIndex");
	if (!docIndex)
		return;
	var g = docIndex._ctrl;
	if (g instanceof tui.ctrl.AccordionGroup)
		g.maxHeight(size.height - 240);
	else if (g) {
		g[0].style.minHeight = size.height - 140 + "px";
		g.refresh();
	}
}

$(window).resize(function () {
	setupCatalogSize();
});<commit_msg>* Upgrade to 1.0.9<commit_after>var downloadMenu = [
	{ link: 'https://github.com/ThorQin/tui/archive/v1.0.9.zip', value: '下载工程源码' },
	{ link: 'https://github.com/ThorQin/tui/releases/download/v1.0.9/tui-1.0.9.zip', value: '下载开发包', icon:'fa-download' }];

var detailItemMenu = [
	{ key: "moveUp", value: "上移" ,icon:"fa-arrow-up"},
	{ key: "moveDown", value: "下移" ,icon:"fa-arrow-down" },
	{ value: "-" },
	{ key: "delete", value: "删除条目", icon: "fa-trash-o" }
];

var docTableCol = [
	{ name: "名称", key: 0, width: 80, _important: true, format: tui.ctrl.Grid.textEditor() },
	{ name: "类型", key: 1, width: 80, _important: true, format: tui.ctrl.Grid.textEditor() },
	{ name: "描述", key: 2, format: tui.ctrl.Grid.textEditor() },
	{
		name: "", format: tui.ctrl.Grid.menu(detailItemMenu, function (item, data) {
			var array = data.grid.data().src();
			if (item.key === "delete") {
				array.splice(data.rowIndex, 1);
				data.grid.data(array);
			} else if (item.key === "moveUp") {
				if (data.rowIndex > 0) {
					var tmp = array[data.rowIndex - 1];
					array[data.rowIndex - 1] = array[data.rowIndex];
					array[data.rowIndex] = tmp;
					data.grid.data(array);
				}
			} else if (item.key === "moveDown") {
				if (data.rowIndex < array.length - 1) {
					var tmp = array[data.rowIndex + 1];
					array[data.rowIndex + 1] = array[data.rowIndex];
					array[data.rowIndex] = tmp;
					data.grid.data(array);
				}
			}

		}),	width: 32, fixed:true
	}
];

function setupCatalogSize() {
	var size = tui.windowSize();
	var docIndex = document.getElementById("docIndex");
	if (!docIndex)
		return;
	var g = docIndex._ctrl;
	if (g instanceof tui.ctrl.AccordionGroup)
		g.maxHeight(size.height - 240);
	else if (g) {
		g[0].style.minHeight = size.height - 140 + "px";
		g.refresh();
	}
}

$(window).resize(function () {
	setupCatalogSize();
});<|endoftext|>


<commit_before>/*
* Copyright (c) 2013 DataTorrent, 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
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
exports = module.exports = {
    //wsRoot: 'http://localhost:3000',
    alertUrlRoot: '/alerts',
    version: 'v1',
    maxAlertActions: 3,
    statusOrder: ['RUNNING','FAILED','FINISHED','KILLED'],
    visibility_timeout: 30000,
    urls: {

        Application              :'/ws/:v/applications',
        ClusterMetrics           :'/ws/:v/cluster/metrics',
        LogicalPlan              :'/ws/:v/applications/:appId/logicalPlan',
        PhysicalPlan             :'/ws/:v/applications/:appId/physicalPlan',
        Operator                 :'/ws/:v/applications/:appId/physicalPlan/operators',
        Port                     :'/ws/:v/applications/:appid/physicalPlan/operators/:operatorId/ports',
        Container                :'/ws/:v/applications/:appId/physicalPlan/containers',
        Alert                    :'/ws/:v/applications/:appId/alerts',
        AppRecordings            :'/ws/:v/applications/:appId/recordings',
        Recording                :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings',
        RecordingTuples          :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings/:startTime/tuples',
        AlertTemplate            :'/ws/:v/alertTemplates',
        OpProperties             :'/ws/:v/applications/:appId/logicalPlan/operators/:operatorName/properties',
        Jar                      :'/ws/:v/jars',
        JarApps                  :'/ws/:v/jars/:fileName/applications',
        JarDependencies          :'/ws/:v/jars/:fileName/dependencyJars',
        DependencyJar            :'/ws/:v/dependencyJars',
        License                  :'/ws/:v/licenses/files/default',
        LicenseAgent             :'/ws/:v/licenses/agents',
        GatewayInfo              :'/ws/:v/about'

    },

    actions: {
        startOpRecording         :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings/start',
        stopOpRecording          :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings/stop',
        startPortRecording       :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/ports/:portName/recordings/start',
        stopPortRecording        :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/ports/:portName/recordings/stop',
        shutdownApp              :'/ws/:v/applications/:appId/shutdown',
        killApp                  :'/ws/:v/applications/:appId/kill',
        launchApp                :'/ws/:v/jars/:fileName/applications/:appName/launch',
        specifyDepJars           :'/ws/:v/jars/:fileName/dependencyJars'
    },

    topics: {

        ClusterMetrics           :'cluster.metrics',
        Applications             :'applications',
        Application              :'applications.:appId',
        Operators                :'applications.:appId.operators',
        Containers               :'applications.:appId.containers',
        TupleRecorder            :'tupleRecorder.:startTime'

    },

    dag: {
        edges: {
            NONE: {
                displayName: 'NOT ASSIGNED',
                dasharray: '5,2'
            },
            THREAD_LOCAL: {
                dasharray: null
            },
            CONTAINER_LOCAL: {
                dasharray: '1,1'
            },
            NODE_LOCAL: {
                dasharray: '1,3'
            },
            RACK_LOCAL: {
                dasharray: '1,5'
            }
        }
    },

    interpolateParams: function(string, params) {
        return string.replace(/:(\w+)/g, function(match, paramName) {
            return encodeURIComponent(params[paramName]);
        });
    }
};<commit_msg>changed visibility timeout to 20 seconds<commit_after>/*
* Copyright (c) 2013 DataTorrent, 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
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
exports = module.exports = {
    //wsRoot: 'http://localhost:3000',
    alertUrlRoot: '/alerts',
    version: 'v1',
    maxAlertActions: 3,
    statusOrder: ['RUNNING','FAILED','FINISHED','KILLED'],
    visibility_timeout: 20000,
    urls: {

        Application              :'/ws/:v/applications',
        ClusterMetrics           :'/ws/:v/cluster/metrics',
        LogicalPlan              :'/ws/:v/applications/:appId/logicalPlan',
        PhysicalPlan             :'/ws/:v/applications/:appId/physicalPlan',
        Operator                 :'/ws/:v/applications/:appId/physicalPlan/operators',
        Port                     :'/ws/:v/applications/:appid/physicalPlan/operators/:operatorId/ports',
        Container                :'/ws/:v/applications/:appId/physicalPlan/containers',
        Alert                    :'/ws/:v/applications/:appId/alerts',
        AppRecordings            :'/ws/:v/applications/:appId/recordings',
        Recording                :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings',
        RecordingTuples          :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings/:startTime/tuples',
        AlertTemplate            :'/ws/:v/alertTemplates',
        OpProperties             :'/ws/:v/applications/:appId/logicalPlan/operators/:operatorName/properties',
        Jar                      :'/ws/:v/jars',
        JarApps                  :'/ws/:v/jars/:fileName/applications',
        JarDependencies          :'/ws/:v/jars/:fileName/dependencyJars',
        DependencyJar            :'/ws/:v/dependencyJars',
        License                  :'/ws/:v/licenses/files/default',
        LicenseAgent             :'/ws/:v/licenses/agents',
        GatewayInfo              :'/ws/:v/about'

    },

    actions: {
        startOpRecording         :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings/start',
        stopOpRecording          :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/recordings/stop',
        startPortRecording       :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/ports/:portName/recordings/start',
        stopPortRecording        :'/ws/:v/applications/:appId/physicalPlan/operators/:operatorId/ports/:portName/recordings/stop',
        shutdownApp              :'/ws/:v/applications/:appId/shutdown',
        killApp                  :'/ws/:v/applications/:appId/kill',
        launchApp                :'/ws/:v/jars/:fileName/applications/:appName/launch',
        specifyDepJars           :'/ws/:v/jars/:fileName/dependencyJars'
    },

    topics: {

        ClusterMetrics           :'cluster.metrics',
        Applications             :'applications',
        Application              :'applications.:appId',
        Operators                :'applications.:appId.operators',
        Containers               :'applications.:appId.containers',
        TupleRecorder            :'tupleRecorder.:startTime'

    },

    dag: {
        edges: {
            NONE: {
                displayName: 'NOT ASSIGNED',
                dasharray: '5,2'
            },
            THREAD_LOCAL: {
                dasharray: null
            },
            CONTAINER_LOCAL: {
                dasharray: '1,1'
            },
            NODE_LOCAL: {
                dasharray: '1,3'
            },
            RACK_LOCAL: {
                dasharray: '1,5'
            }
        }
    },

    interpolateParams: function(string, params) {
        return string.replace(/:(\w+)/g, function(match, paramName) {
            return encodeURIComponent(params[paramName]);
        });
    }
};<|endoftext|>


<commit_before>'use strict';

var i              = require('es5-ext/function/i')
  , isFunction     = require('es5-ext/function/is-function')
  , mixin          = require('es5-ext/object/mixin')
  , setPrototypeOf = require('es5-ext/object/set-prototype-of')
  , d              = require('d/d')
  , DbjsError      = require('../error')
  , Event          = require('../event')
  , getIdent       = require('../utils/get-ident')
  , turnPrototype  = require('../utils/propagate-prototype-turn').object
  , serialize      = require('../utils/serialize')
  , getMessage     = require('../utils/get-sub-error-message')
  , updateEnum     = require('../utils/update-enumerability')
  , unserialize    = require('../unserialize/value')

  , push = Array.prototype.push, slice = Array.prototype.slice
  , create = Object.create, defineProperties = Object.defineProperties
  , defineProperty = Object.defineProperty
  , getPrototypeOf = Object.getPrototypeOf
  , isValidTypeName = RegExp.prototype.test.bind(/^[A-Z][0-9a-zA-Z]*$/)
  , updateObjEnum;

updateObjEnum = function (obj) {
	var sKey, descs = obj.__descriptors__, desc;
	for (sKey in descs) {
		desc = descs[sKey];
		if (desc._reverse_) continue;
		if (desc.nested || desc.multiple) updateEnum(obj, sKey, true);
	}
	return obj;
};

module.exports = function (db, createObj, object) {
	var Base, existingIds = db.objects.__setData__;

	Base = module.exports = function Self(value) {
		if (Self.is(value)) return value;
		return Self.create.apply(Self, arguments);
	};
	setPrototypeOf(Base, object);
	try { mixin(Base, Function.prototype); } catch (ignore) {}

	defineProperties(Base, {
		__id__: d('', 'Base'),
		__object__: d('', Base),
		extend: d(function (name) {
			return this._extendAndInitialize_.apply(this,
				this._validateExtend_.apply(this, arguments));
		}),
		_validateExtend_: d(function (name) {
			if (!isValidTypeName(name)) {
				throw new DbjsError(name + " is not valid type name",
					'INVALID_TYPE_NAME');
			}
			if (existingIds[name]) {
				throw new DbjsError(name + " type is already created", 'TYPE_EXISTS');
			}
			return [name].concat(this._validateExtendInitialize_.apply(this,
				slice.call(arguments, 1)));
		}),
		_validateExtendInitialize_: d(function (initialize, nsProps, objProps) {
			var errors;
			if (!isFunction(initialize)) {
				objProps = nsProps;
				nsProps = initialize;
				initialize = null;
			} else if (!nsProps) {
				nsProps = { _initialize_: { value: initialize } };
			} else {
				nsProps._initialize_ = { value: initialize };
			}

			if (nsProps) {
				try {
					nsProps = create(this)._validateDefineProperties_(nsProps);
				} catch (e) {
					if (e.name !== 'DbjsError') throw e;
					if (e.errors) errors = e.errors;
					else errors = [e];
				}
			}
			if (objProps) {
				try {
					objProps =
						create(this.prototype)._validateDefineProperties_(objProps);
				} catch (e) {
					if (e.name !== 'DbjsError') throw e;
					if (errors) {
						if (e.errors) push.apply(errors, e.errors);
						else errors.push(e);
					} else {
						if (e.errors) errors = e.errors;
						else errors = [e];
					}
				}
			}
			if (!errors) return [nsProps, objProps];
			throw new DbjsError("Invalid properties:\n\t" +
				errors.map(getMessage).join('\t\n'), 'TYPE_CONSTRUCTION_ERROR',
				{ errors: errors });
		}),
		_extendAndInitialize_: d(function (name) {
			var constructor = this._extend_(name);
			new Event(constructor, this); //jslint: skip
			constructor._initialize_.apply(constructor, slice.call(arguments, 1));
			return constructor;
		}),
		_initialize_: d(function (nsProps, objProps) {
			if (nsProps) this._defineProperties_(nsProps);
			if (objProps) this.prototype._defineProperties_(objProps);
		}),
		is: d(function (value) { return Boolean(serialize(value)); }),
		normalize: d(function (value) { return this.is(value) ? value : null; }),
		validate: d(function (value) {
			if (!serialize(value)) {
				throw new DbjsError(value + " cannot be handled by dbjs",
					'NOT_SUPPORTED_VALUE');
			}
			return value;
		}),
		_extend_: d(function (name) {
			var constructor = function Self(value) {
				if (Self.is(value)) return value;
				return Self.create.apply(Self, arguments);
			};
			setPrototypeOf(constructor, this);
			defineProperties(constructor, {
				__id__: d('', name),
				__object__: d('', constructor)
			});
			constructor.prototype = create(this.prototype, {
				constructor: d(constructor),
				__id__: d('', name + '#')
			});
			defineProperty(constructor.prototype, '__object__', d('',
				constructor.prototype));
			db.objects._add(constructor);
			db.objects._add(constructor.prototype);
			this._descendants_._add(constructor);
			this.prototype._descendants_._add(constructor.prototype);
			if (!(name in db)) db[name] = constructor;
			updateObjEnum(constructor);
			updateObjEnum(constructor.prototype);
			return constructor;
		}),
		_setValue_: d(function (nu, dbEvent) {
			var old = getPrototypeOf(this), postponed;
			if (!nu) nu = Base;
			if (old === nu) return;
			postponed = turnPrototype(this, nu, dbEvent);
			db._release_(turnPrototype(this.prototype, nu.prototype,
				dbEvent, postponed));
		}),
		create: d(function () {
			return this._createAndInitialize_.apply(this,
				this._validateCreate_.apply(this, arguments));
		}),
		_createNested_: d(function (object, sKey) {
			var nested;
			if (!this._keys_[sKey]) this._serialize_(unserialize(sKey, db.objects));
			nested = this._create_(object.__id__ + '/' +
				getIdent(this._keys_[sKey], sKey), object.__master__);
			return defineProperties(nested, {
				__parent__: d('', object),
				__sKey__: d('', sKey)
			});
		}),
		_validateCreate_: d(function (value) { return [this.validate(value)]; }),
		_createAndInitialize_: d(i),
		_create_: d(function (id, master) {
			return updateObjEnum(createObj(this.prototype, id, null, master));
		}),
		find: d(function (key, value) {
			var sKey = this._serialize_(key), sValue;
			if (sKey == null) {
				throw new DbjsError(key + " is invalid key", 'INVALID_KEY');
			}
			if (value == null) {
				throw new DbjsError(value + " is not a value", 'INVALID_VALUE');
			}
			sValue = serialize(value);
			if (sValue == null) {
				throw new DbjsError(value + " is invalid database value",
					'NOT_SUPPORTED_VALUE');
			}
			return this.prototype._getReverseMap_(sKey)._getMultiple_(sValue, value);
		})
	});
	db.objects._add(Base);
	db.Base = Base;
	Base.prototype = object;

	defineProperties(object, {
		constructor: d(Base),
		_setValue_: d(function (nu, dbEvent) {
			var old;
			if (this.constructor.prototype === this) {
				// Sanity check
				throw new DbjsError("Turn of prototype of the prototypes is forbidden",
					'PROTOTYPE_TURN');
			}
			old = getPrototypeOf(this);
			if (!nu) nu = object;
			if (old === nu) return;
			db._release_(turnPrototype(this, nu, dbEvent));
		})
	});
};
<commit_msg>Expose `ident` on nested object.<commit_after>'use strict';

var i              = require('es5-ext/function/i')
  , isFunction     = require('es5-ext/function/is-function')
  , mixin          = require('es5-ext/object/mixin')
  , setPrototypeOf = require('es5-ext/object/set-prototype-of')
  , d              = require('d/d')
  , DbjsError      = require('../error')
  , Event          = require('../event')
  , getIdent       = require('../utils/get-ident')
  , turnPrototype  = require('../utils/propagate-prototype-turn').object
  , serialize      = require('../utils/serialize')
  , getMessage     = require('../utils/get-sub-error-message')
  , updateEnum     = require('../utils/update-enumerability')
  , unserialize    = require('../unserialize/value')

  , push = Array.prototype.push, slice = Array.prototype.slice
  , create = Object.create, defineProperties = Object.defineProperties
  , defineProperty = Object.defineProperty
  , getPrototypeOf = Object.getPrototypeOf
  , isValidTypeName = RegExp.prototype.test.bind(/^[A-Z][0-9a-zA-Z]*$/)
  , updateObjEnum;

updateObjEnum = function (obj) {
	var sKey, descs = obj.__descriptors__, desc;
	for (sKey in descs) {
		desc = descs[sKey];
		if (desc._reverse_) continue;
		if (desc.nested || desc.multiple) updateEnum(obj, sKey, true);
	}
	return obj;
};

module.exports = function (db, createObj, object) {
	var Base, existingIds = db.objects.__setData__;

	Base = module.exports = function Self(value) {
		if (Self.is(value)) return value;
		return Self.create.apply(Self, arguments);
	};
	setPrototypeOf(Base, object);
	try { mixin(Base, Function.prototype); } catch (ignore) {}

	defineProperties(Base, {
		__id__: d('', 'Base'),
		__object__: d('', Base),
		extend: d(function (name) {
			return this._extendAndInitialize_.apply(this,
				this._validateExtend_.apply(this, arguments));
		}),
		_validateExtend_: d(function (name) {
			if (!isValidTypeName(name)) {
				throw new DbjsError(name + " is not valid type name",
					'INVALID_TYPE_NAME');
			}
			if (existingIds[name]) {
				throw new DbjsError(name + " type is already created", 'TYPE_EXISTS');
			}
			return [name].concat(this._validateExtendInitialize_.apply(this,
				slice.call(arguments, 1)));
		}),
		_validateExtendInitialize_: d(function (initialize, nsProps, objProps) {
			var errors;
			if (!isFunction(initialize)) {
				objProps = nsProps;
				nsProps = initialize;
				initialize = null;
			} else if (!nsProps) {
				nsProps = { _initialize_: { value: initialize } };
			} else {
				nsProps._initialize_ = { value: initialize };
			}

			if (nsProps) {
				try {
					nsProps = create(this)._validateDefineProperties_(nsProps);
				} catch (e) {
					if (e.name !== 'DbjsError') throw e;
					if (e.errors) errors = e.errors;
					else errors = [e];
				}
			}
			if (objProps) {
				try {
					objProps =
						create(this.prototype)._validateDefineProperties_(objProps);
				} catch (e) {
					if (e.name !== 'DbjsError') throw e;
					if (errors) {
						if (e.errors) push.apply(errors, e.errors);
						else errors.push(e);
					} else {
						if (e.errors) errors = e.errors;
						else errors = [e];
					}
				}
			}
			if (!errors) return [nsProps, objProps];
			throw new DbjsError("Invalid properties:\n\t" +
				errors.map(getMessage).join('\t\n'), 'TYPE_CONSTRUCTION_ERROR',
				{ errors: errors });
		}),
		_extendAndInitialize_: d(function (name) {
			var constructor = this._extend_(name);
			new Event(constructor, this); //jslint: skip
			constructor._initialize_.apply(constructor, slice.call(arguments, 1));
			return constructor;
		}),
		_initialize_: d(function (nsProps, objProps) {
			if (nsProps) this._defineProperties_(nsProps);
			if (objProps) this.prototype._defineProperties_(objProps);
		}),
		is: d(function (value) { return Boolean(serialize(value)); }),
		normalize: d(function (value) { return this.is(value) ? value : null; }),
		validate: d(function (value) {
			if (!serialize(value)) {
				throw new DbjsError(value + " cannot be handled by dbjs",
					'NOT_SUPPORTED_VALUE');
			}
			return value;
		}),
		_extend_: d(function (name) {
			var constructor = function Self(value) {
				if (Self.is(value)) return value;
				return Self.create.apply(Self, arguments);
			};
			setPrototypeOf(constructor, this);
			defineProperties(constructor, {
				__id__: d('', name),
				__object__: d('', constructor)
			});
			constructor.prototype = create(this.prototype, {
				constructor: d(constructor),
				__id__: d('', name + '#')
			});
			defineProperty(constructor.prototype, '__object__', d('',
				constructor.prototype));
			db.objects._add(constructor);
			db.objects._add(constructor.prototype);
			this._descendants_._add(constructor);
			this.prototype._descendants_._add(constructor.prototype);
			if (!(name in db)) db[name] = constructor;
			updateObjEnum(constructor);
			updateObjEnum(constructor.prototype);
			return constructor;
		}),
		_setValue_: d(function (nu, dbEvent) {
			var old = getPrototypeOf(this), postponed;
			if (!nu) nu = Base;
			if (old === nu) return;
			postponed = turnPrototype(this, nu, dbEvent);
			db._release_(turnPrototype(this.prototype, nu.prototype,
				dbEvent, postponed));
		}),
		create: d(function () {
			return this._createAndInitialize_.apply(this,
				this._validateCreate_.apply(this, arguments));
		}),
		_createNested_: d(function (object, sKey) {
			var nested, ident;
			if (!this._keys_[sKey]) this._serialize_(unserialize(sKey, db.objects));
			ident = getIdent(this._keys_[sKey], sKey);
			nested = this._create_(object.__id__ + '/' + ident, object.__master__);
			return defineProperties(nested, {
				__parent__: d('', object),
				__sKey__: d('', sKey),
				__ident__: d('', ident)
			});
		}),
		_validateCreate_: d(function (value) { return [this.validate(value)]; }),
		_createAndInitialize_: d(i),
		_create_: d(function (id, master) {
			return updateObjEnum(createObj(this.prototype, id, null, master));
		}),
		find: d(function (key, value) {
			var sKey = this._serialize_(key), sValue;
			if (sKey == null) {
				throw new DbjsError(key + " is invalid key", 'INVALID_KEY');
			}
			if (value == null) {
				throw new DbjsError(value + " is not a value", 'INVALID_VALUE');
			}
			sValue = serialize(value);
			if (sValue == null) {
				throw new DbjsError(value + " is invalid database value",
					'NOT_SUPPORTED_VALUE');
			}
			return this.prototype._getReverseMap_(sKey)._getMultiple_(sValue, value);
		})
	});
	db.objects._add(Base);
	db.Base = Base;
	Base.prototype = object;

	defineProperties(object, {
		constructor: d(Base),
		_setValue_: d(function (nu, dbEvent) {
			var old;
			if (this.constructor.prototype === this) {
				// Sanity check
				throw new DbjsError("Turn of prototype of the prototypes is forbidden",
					'PROTOTYPE_TURN');
			}
			old = getPrototypeOf(this);
			if (!nu) nu = object;
			if (old === nu) return;
			db._release_(turnPrototype(this, nu, dbEvent));
		})
	});
};
<|endoftext|>


<commit_before>var async = require("async"),
    utility = require('./utility'),
    matches = utility.matches,
    players = utility.players,
    winston = require('winston'),
    jobs = utility.jobs,
    trackedPlayers = {},
    transports = [new(winston.transports.Console)(),
        new(winston.transports.File)({
            filename: 'backend.log',
            level: 'info'
        })
    ],
    logger = new(winston.Logger)({transports: transports})
    untrack_interval = process.env.UNTRACK_INTERVAL || 3;

async.series([
    function(cb) {
        utility.clearActiveJobs('api', function(err) {
            cb(err)
        })
    },
    function(cb) {
        //parse unparsed matches
        matches.find({
            parse_status: 0
        }, function(err, docs) {
            docs.forEach(function(match) {
                utility.queueReq("parse", match)
            })
        })
        cb(null)
    },
    function(cb) {
        if (process.env.START_SEQ_NUM === "AUTO") {
            utility.getData(utility.api_url + "/GetMatchHistory/V001/?key=" + process.env.STEAM_API_KEY, function(err, data) {
                getMatches(data.result.matches[0].match_seq_num)
                cb(null)
            })
        }
        else if (process.env.START_SEQ_NUM) {
            getMatches(process.env.START_SEQ_NUM);
            cb(null);
        }
        else {
            //start at highest id in db
            matches.findOne({}, {
                sort: {
                    match_seq_num: -1
                }
            }, function(err, doc) {
                getMatches(doc ? doc.match_seq_num + 1 : 0)
                cb(null)
            })
        }
    }
], function(err) {
    jobs.process('api', function(job, done) {
        setTimeout(function() {
            apiRequest(job, done)
        }, 1000)
    })
})

function getMatches(seq_num) {
    setTimeout(function() {
        players.find({
            track: 1
        }, function(err, docs) {
            //rebuild set of tracked players before every check
            trackedPlayers = {}
            docs.forEach(function(player) {
                trackedPlayers[player.account_id] = true
            })
            var url = utility.api_url + "/GetMatchHistoryBySequenceNum/V001/?key=" + process.env.STEAM_API_KEY + "&start_at_match_seq_num=" + seq_num;
            utility.getData(url, function(err, data) {
                if (data.result.error || data.result.status == 2) {
                    logger.info(data)
                    return getMatches(seq_num)
                }
                var resp = data.result.matches
                logger.info("[API] seq_num: %s, found %s matches", seq_num, resp.length)
                async.mapSeries(resp, insertMatch, function(err) {
                    if (resp.length > 0) {
                        seq_num = resp[resp.length - 1].match_seq_num + 1
                    }
                    return getMatches(seq_num)
                })
            })
        })
    }, 1000)
}

function apiRequest(job, cb) {
    //process an api request
    var payload = job.data.payload;
    if (!job.data.url) {
        logger.info(job);
        cb("no url");
    }
    utility.getData(job.data.url, function(err, data) {
        if (data.response) {
            //summaries response
            async.map(data.response.players, insertPlayer, function(err) {
                cb(err);
            });
        }
        else if (data.result.status === 15 || data.result.error === "Practice matches are not available via GetMatchDetails") {
            //user does not have stats enabled
            //or attempting to get private match
            //don't retry
            logger.info(data);
            return cb(null);
        }
        else if (data.result.error || data.result.status === 2) {
            //error response from dota api
            logger.info(data);
            return cb(data);
        }
        else if (payload.match_id) {
            //response for single match details
            var match = data.result;
            match.parsed_data = payload.parsed_data;
            insertMatch(match, function(err) {
                cb(err);
            });
        }
        else if (payload.account_id) {
            //response for match history for single player
            var resp = data.result.matches;
            async.map(resp, function(match, cb2) {
                utility.requestDetails(match, function(err) {
                    cb2(err);
                });
            }, function(err) {
                cb(err);
            });
        }
    });
}

function insertMatch(match, cb) {
    var track = match.players.some(function(element) {
        return (element.account_id in trackedPlayers);
    });
    //queued or untracked
    match.parse_status = (track ? 0 : 3);
    if (track) {
        var summaries = {
            summaries_id: new Date()
        }
        var steamids = []
        match.players.forEach(function(player) {
            steamids.push(utility.convert32to64(player.account_id).toString())
        })
        summaries.query = steamids.join();
        //queue for player names
        utility.queueReq("api", summaries);
        //parse if unparsed
        if (match.parsed_data) {
            match.parse_status = 2;
        }
        else {
            utility.queueReq("parse", match);
        }
        matches.update({
                match_id: match.match_id
            }, {
                $set: match
            }, {
                upsert: true
            },
            function(err) {
                cb(err);
            });
    }
    else {
        cb(null)
    }
}

function insertPlayer(player, cb) {
    //Inserts/updates a player in the database

    var account_id = Number(utility.convert64to32(player.steamid));
    players.update({
        account_id: account_id
    }, {
        $set: player
    }, {
        upsert: true
    }, function(err) {
        cb(err);
    });
}

function untrackPlayers() {
    players.update({
        last_visited: {
            $lte: Date.now() - 86400 * untrack_interval
        }
    }, {
        track : 0;
    })
}

untrackPlayers();
setInterval(untrackPlayers(), 60*60*1000);<commit_msg>update more than one...<commit_after>var async = require("async"),
    utility = require('./utility'),
    matches = utility.matches,
    players = utility.players,
    winston = require('winston'),
    jobs = utility.jobs,
    trackedPlayers = {},
    transports = [new(winston.transports.Console)(),
        new(winston.transports.File)({
            filename: 'backend.log',
            level: 'info'
        })
    ],
    logger = new(winston.Logger)({transports: transports})
    untrack_interval = process.env.UNTRACK_INTERVAL || 3;

async.series([
    function(cb) {
        utility.clearActiveJobs('api', function(err) {
            cb(err)
        })
    },
    function(cb) {
        //parse unparsed matches
        matches.find({
            parse_status: 0
        }, function(err, docs) {
            docs.forEach(function(match) {
                utility.queueReq("parse", match)
            })
        })
        cb(null)
    },
    function(cb) {
        if (process.env.START_SEQ_NUM === "AUTO") {
            utility.getData(utility.api_url + "/GetMatchHistory/V001/?key=" + process.env.STEAM_API_KEY, function(err, data) {
                getMatches(data.result.matches[0].match_seq_num)
                cb(null)
            })
        }
        else if (process.env.START_SEQ_NUM) {
            getMatches(process.env.START_SEQ_NUM);
            cb(null);
        }
        else {
            //start at highest id in db
            matches.findOne({}, {
                sort: {
                    match_seq_num: -1
                }
            }, function(err, doc) {
                getMatches(doc ? doc.match_seq_num + 1 : 0)
                cb(null)
            })
        }
    }
], function(err) {
    jobs.process('api', function(job, done) {
        setTimeout(function() {
            apiRequest(job, done)
        }, 1000)
    })
})

function getMatches(seq_num) {
    setTimeout(function() {
        players.find({
            track: 1
        }, function(err, docs) {
            //rebuild set of tracked players before every check
            trackedPlayers = {}
            docs.forEach(function(player) {
                trackedPlayers[player.account_id] = true
            })
            var url = utility.api_url + "/GetMatchHistoryBySequenceNum/V001/?key=" + process.env.STEAM_API_KEY + "&start_at_match_seq_num=" + seq_num;
            utility.getData(url, function(err, data) {
                if (data.result.error || data.result.status == 2) {
                    logger.info(data)
                    return getMatches(seq_num)
                }
                var resp = data.result.matches
                logger.info("[API] seq_num: %s, found %s matches", seq_num, resp.length)
                async.mapSeries(resp, insertMatch, function(err) {
                    if (resp.length > 0) {
                        seq_num = resp[resp.length - 1].match_seq_num + 1
                    }
                    return getMatches(seq_num)
                })
            })
        })
    }, 1000)
}

function apiRequest(job, cb) {
    //process an api request
    var payload = job.data.payload;
    if (!job.data.url) {
        logger.info(job);
        cb("no url");
    }
    utility.getData(job.data.url, function(err, data) {
        if (data.response) {
            //summaries response
            async.map(data.response.players, insertPlayer, function(err) {
                cb(err);
            });
        }
        else if (data.result.status === 15 || data.result.error === "Practice matches are not available via GetMatchDetails") {
            //user does not have stats enabled
            //or attempting to get private match
            //don't retry
            logger.info(data);
            return cb(null);
        }
        else if (data.result.error || data.result.status === 2) {
            //error response from dota api
            logger.info(data);
            return cb(data);
        }
        else if (payload.match_id) {
            //response for single match details
            var match = data.result;
            match.parsed_data = payload.parsed_data;
            insertMatch(match, function(err) {
                cb(err);
            });
        }
        else if (payload.account_id) {
            //response for match history for single player
            var resp = data.result.matches;
            async.map(resp, function(match, cb2) {
                utility.requestDetails(match, function(err) {
                    cb2(err);
                });
            }, function(err) {
                cb(err);
            });
        }
    });
}

function insertMatch(match, cb) {
    var track = match.players.some(function(element) {
        return (element.account_id in trackedPlayers);
    });
    //queued or untracked
    match.parse_status = (track ? 0 : 3);
    if (track) {
        var summaries = {
            summaries_id: new Date()
        }
        var steamids = []
        match.players.forEach(function(player) {
            steamids.push(utility.convert32to64(player.account_id).toString())
        })
        summaries.query = steamids.join();
        //queue for player names
        utility.queueReq("api", summaries);
        //parse if unparsed
        if (match.parsed_data) {
            match.parse_status = 2;
        }
        else {
            utility.queueReq("parse", match);
        }
        matches.update({
                match_id: match.match_id
            }, {
                $set: match
            }, {
                upsert: true
            },
            function(err) {
                cb(err);
            });
    }
    else {
        cb(null)
    }
}

function insertPlayer(player, cb) {
    //Inserts/updates a player in the database

    var account_id = Number(utility.convert64to32(player.steamid));
    players.update({
        account_id: account_id
    }, {
        $set: player
    }, {
        upsert: true
    }, function(err) {
        cb(err);
    });
}

function untrackPlayers() {
    logger.info("[UNTRACK] Untracking users...");
    players.update({
        last_visited: {
            $lte: Date.now() - 86400 * untrack_interval
        }
    }, {
        $set: {
            track: 0
        }
    }, {
        multi: true
    },  function(err, num){
        logger.info("[UNTRACK] Untracked " + num + " users.")
    })
}

setInterval(untrackPlayers(), 60*60*1000);<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

chromeSpec(function(runningInBackground) {
  describe('chrome.storage', function() {
    it('should contain definitions', function() {
      expect(chrome.storage).toBeDefined();
      expect(chrome.storage.local).toBeDefined();
      expect(chrome.storage.sync).toBeDefined();
      expect(chrome.storage.onChanged).toBeDefined();

      [chrome.storage.local, chrome.storage.sync].forEach(function(store) {
        expect(store.get).toBeDefined();
        expect(store.getBytesInUse).toBeDefined();
        expect(store.set).toBeDefined();
        expect(store.remove).toBeDefined();
        expect(store.clear).toBeDefined();
      });
    });

    it('should have properties', function() {
      expect(chrome.storage.sync.QUOTA_BYTES).toEqual(102400);
      expect(chrome.storage.sync.QUOTA_BYTES_PER_ITEM).toEqual(4096);
      expect(chrome.storage.sync.MAX_ITEMS).toEqual(512);
      expect(chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR).toEqual(1000);
      expect(chrome.storage.sync.MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE).toEqual(10);

      expect(chrome.storage.local.QUOTA_BYTES).toEqual(5242880);
    });

    it('onChanged() Event', function() {
      // TODO
    });

    function test_storagearea(type, storagearea) {
      var obj = {
        'a': 1,
        'b': 2.345,
        'c': 'test',
        'd': [1,2,3],
        'e': {'a':1,'b':'2','c':3.456}
      };

      describe('chrome.storage.' + type, function() {
        describe('testing set', function() {
          beforeEach(function() {
            storagearea.clear();
          });

          Object.keys(obj).forEach(function(key) {
            it('set(object) with single value of type: ' + typeof obj[key], function() {
              var temp = {};
              temp[key] = obj[key];
              storagearea.set(temp);
              storagearea.get(key, waitUntilCalled(function(items) {
                expect(items[key]).toEqual(obj[key]);

                storagearea.get(waitUntilCalled(function(items) {
                  expect(Object.keys(items).length).toEqual(1);
                }));
              }));
            });
          });

          it('set(object) with multiple values', function() {
            storagearea.set(obj);
            storagearea.get(Object.keys(obj), waitUntilCalled(function(items) {
              expect(items).toEqual(obj);

              storagearea.get(waitUntilCalled(function(items) {
                expect(Object.keys(items).length).toBeGreaterThan(0);
              }));
            }));
          });
        });

        describe('testing get', function() {
          beforeEach(function() {
            storagearea.clear();
            storagearea.set(obj);
          });

          it('get() should return all items', function() {
            storagearea.get(waitUntilCalled(function(items) {
              expect(items).toEqual(obj);
            }));
          });

          it('get(null) should return all items', function() {
            storagearea.get(null, waitUntilCalled(function(items) {
              expect(items).toEqual(obj);
            }));
          });

          it('get(string) should return item with that key', function() {
            storagearea.get('a', waitUntilCalled(function(items) {
              expect(items).toEqual({'a': obj.a});
            }));
          });

          it('get(object) should return all items with those keys, or items with default values provided', function() {
            var request = {
              'a': 'should ignore',
              'b': 'also ignore',
              'z': 'should not ignore'
            };
            var expected = {
              'a': obj.a,
              'b': obj.b,
              'z': request.z
            };
            storagearea.get(request, waitUntilCalled(function(items) {
              expect(items).toEqual(expected);
            }));
          });

          it('get([string, ...]) should return items with those keys, ignoring keys that arent found', function() {
            storagearea.get(['a','b','c','d','e','x','y','z'], waitUntilCalled(function(items) {
              expect(items).toEqual(obj);
            }));
            storagearea.get(['a','b','c'], waitUntilCalled(function(items) {
              expect(items).toNotEqual(obj);
            }));
            storagearea.get([], waitUntilCalled(function(items) {
              expect(items).toEqual({});
            }));
          });
        });

        describe('testing clear', function() {
          beforeEach(function() {
            storagearea.clear();
          });

          it('should delete all items', function() {
            storagearea.set(obj);
            storagearea.get(waitUntilCalled(function(items) {
              expect(Object.keys(items).length).toEqual(Object.keys(obj).length);

              storagearea.clear();
              storagearea.get(waitUntilCalled(function(items) {
                expect(Object.keys(items).length).toEqual(0);
              }));
            }));
          });
        });

        describe('testing remove', function() {
          beforeEach(function() {
            storagearea.clear();
            storagearea.set(obj);
          });

          it('remove(string) should remove item with key', function() {
            storagearea.remove('a');
            var expected = {
              'b': obj.b,
              'c': obj.c,
              'd': obj.d,
              'e': obj.e
            };
            storagearea.get(waitUntilCalled(function(items) {
              expect(items).not.toEqual(obj);
              expect(items).toEqual(expected);
            }));
          });

          it('remove(string) with invalid key should be ignored', function() {
            storagearea.remove('z');
            storagearea.get(waitUntilCalled(function(items) {
              expect(items).toEqual(obj);
            }));
          });

          it('remove([string, ...]) should remove item with key, ignoring invalid keys', function() {
            storagearea.remove(['a','b','z']);
            var expected = {
              'c': obj.c,
              'd': obj.d,
              'e': obj.e
            };
            storagearea.get(waitUntilCalled(function(items) {
              expect(items).toEqual(expected);
            }));
          });
        });

        describe('testing getBytesInUse', function() {
          // TODO
          it('getBytesInUse()', function() {
          });

          it('getBytesInUse(null)', function() {
          });

          it('getBytesInUse(string)', function() {
          });

          it('getBytesInUse([string, ...])', function() {
            // note: dont forget that [] should return 0
          });
        });
      });
    }

    test_storagearea('local', chrome.storage.local);
    test_storagearea('sync', chrome.storage.sync);
  });
});
<commit_msg>Add tests to chrome.storage for special-case values.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

chromeSpec(function(runningInBackground) {
  describe('chrome.storage', function() {
    it('should contain definitions', function() {
      expect(chrome.storage).toBeDefined();
      expect(chrome.storage.local).toBeDefined();
      expect(chrome.storage.sync).toBeDefined();
      expect(chrome.storage.onChanged).toBeDefined();

      [chrome.storage.local, chrome.storage.sync].forEach(function(store) {
        expect(store.get).toBeDefined();
        expect(store.getBytesInUse).toBeDefined();
        expect(store.set).toBeDefined();
        expect(store.remove).toBeDefined();
        expect(store.clear).toBeDefined();
      });
    });

    it('should have properties', function() {
      expect(chrome.storage.sync.QUOTA_BYTES).toEqual(102400);
      expect(chrome.storage.sync.QUOTA_BYTES_PER_ITEM).toEqual(4096);
      expect(chrome.storage.sync.MAX_ITEMS).toEqual(512);
      expect(chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR).toEqual(1000);
      expect(chrome.storage.sync.MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE).toEqual(10);

      expect(chrome.storage.local.QUOTA_BYTES).toEqual(5242880);
    });

    it('onChanged() Event', function() {
      // TODO
    });

    function test_storagearea(type, storagearea) {
      var obj = {
        'int': 1,
        'double': 2.345,
        'string': 'test',
        'String': Object('test'),
        'Array': [1,2,3],
        'object': {'a':1,'b':'2','c':3.456},
        'object_with_toJSON': {'a':1, 'toJSON': function() { return 'foo'; }},
        'Window': window,
        'RegExp': /hello/,
        'date': new Date(1000),
        'undefined': undefined, // This gets ignored by .set().
        'null': null,
        'function': function hello() { return 1; },
        'DivElement': document.createElement('div'),
        'Document': document
      };

      var expected = {
        'int': 1,
        'double': 2.345,
        'string': 'test',
        'String': {'0': 't', '1': 'e', '2': 's', '3': 't'},
        'Array': [1,2,3],
        'object': {'a':1,'b':'2','c':3.456},
        'object_with_toJSON': {'a':1, 'toJSON': {}},
        'Window': {},
        'RegExp': {},
        'date': {},
        'null': null,
        'function': {},
        'DivElement': {},
        'Document': {}
      };

      describe('chrome.storage.' + type, function() {
        describe('testing set', function() {
          beforeEach(function() {
            storagearea.clear();
          });

          Object.keys(obj).forEach(function(key) {
            it('set(object) with single value of type: ' + key, function() {
              var temp = {};
              temp[key] = obj[key];
              storagearea.set(temp);
              storagearea.get(key, waitUntilCalled(function(items) {
                expect(items[key]).toEqual(expected[key]);

                storagearea.get(waitUntilCalled(function(items) {
                  expect(Object.keys(items).length).toEqual(+(key in expected));
                }));
              }));
            });
          });

          it('set(object) with multiple values', function() {
            storagearea.set(obj);
            storagearea.get(Object.keys(obj), waitUntilCalled(function(items) {
              expect(items).toEqual(expected);

              storagearea.get(waitUntilCalled(function(items) {
                expect(items).toEqual(expected);
              }));
            }));
          });
        });

        describe('testing get', function() {
          beforeEach(function() {
            storagearea.clear();
            storagearea.set(obj);
          });

          it('get() should return all items', function() {
            storagearea.get(waitUntilCalled(function(items) {
              expect(items).toEqual(expected);
            }));
          });

          it('get(null) should return all items', function() {
            storagearea.get(null, waitUntilCalled(function(items) {
              expect(items).toEqual(expected);
            }));
          });

          it('get(string) should return item with that key', function() {
            storagearea.get('int', waitUntilCalled(function(items) {
              expect(items).toEqual({'int': expected.int});
            }));
          });

          it('get(object) should return all items with those keys, or items with default values provided', function() {
            var request = {
              'int': 'should ignore',
              'double': 'also ignore',
              'z': 'should not ignore'
            };
            var answer = {
              'int': expected.int,
              'double': expected.double,
              'z': request.z
            };
            storagearea.get(request, waitUntilCalled(function(items) {
              expect(items).toEqual(answer);
            }));
          });

          it('get([string, ...]) should return items with those keys, ignoring keys that arent found', function() {
            var answer = {
              'int': expected.int,
              'double': expected.double
            };
            storagearea.get(['int','double','x','y','z'], waitUntilCalled(function(items) {
              expect(items).toEqual(answer);
            }));
            storagearea.get([], waitUntilCalled(function(items) {
              expect(items).toEqual({});
            }));
          });
        });

        describe('testing clear', function() {
          beforeEach(function() {
            storagearea.clear();
          });

          it('should delete all items', function() {
            storagearea.set(obj);
            storagearea.get(waitUntilCalled(function(items) {
              expect(Object.keys(items).length).toEqual(Object.keys(expected).length);

              storagearea.clear();
              storagearea.get(waitUntilCalled(function(items) {
                expect(Object.keys(items).length).toEqual(0);
              }));
            }));
          });
        });

        describe('testing remove', function() {
          beforeEach(function() {
            storagearea.clear();
            storagearea.set(obj);
          });

          it('remove(string) should remove item with key', function() {
            storagearea.remove('int');
            storagearea.get(waitUntilCalled(function(items) {
              expect(items.int).toBeUndefined();
              expect(Object.keys(items).length).toEqual(Object.keys(expected).length - 1);
            }));
          });

          it('remove(string) with invalid key should be ignored', function() {
            storagearea.remove('z');
            storagearea.get(waitUntilCalled(function(items) {
              expect(items).toEqual(expected);
            }));
          });

          it('remove([string, ...]) should remove item with key, ignoring invalid keys', function() {
            storagearea.remove(['int','string','z']);
            storagearea.get(waitUntilCalled(function(items) {
              expect(items.int).toBeUndefined();
              expect(items.string).toBeUndefined();
              expect(Object.keys(items).length).toEqual(Object.keys(expected).length - 2);
            }));
          });
        });

        describe('testing getBytesInUse', function() {
          // TODO
          it('getBytesInUse()', function() {
          });

          it('getBytesInUse(null)', function() {
          });

          it('getBytesInUse(string)', function() {
          });

          it('getBytesInUse([string, ...])', function() {
            // note: dont forget that [] should return 0
          });
        });
      });
    }

    test_storagearea('local', chrome.storage.local);
    test_storagearea('sync', chrome.storage.sync);
  });
});
<|endoftext|>