<commit_before>var request = require('request');

var representBaseUrl = 'http://represent.opennorth.ca';

module.exports.get = function(url, callback) {
  request(representBaseUrl + url , function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.boundarySet = function(boundary, callback) {
  request(representBaseUrl + '/boundary-sets/' + boundary + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.boundary = function(boundary, callback) {
  request(representBaseUrl + '/boundaries/' + boundary + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.boundaryLatLon = function(lat, lon, callback) {
  request(representBaseUrl + '/boundaries/?contains=' + lat + ',' + lon + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.postCode = function(postCode, callback) {
  request(representBaseUrl + '/postcodes/' + postCode + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}
<commit_msg>Add representatives by lat/lon<commit_after>var request = require('request');

var representBaseUrl = 'http://represent.opennorth.ca';

module.exports.get = function(url, callback) {
  request(representBaseUrl + url , function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.boundarySet = function(boundary, callback) {
  request(representBaseUrl + '/boundary-sets/' + boundary + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.boundary = function(boundary, callback) {
  request(representBaseUrl + '/boundaries/' + boundary + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.boundaryLatLon = function(lat, lon, callback) {
  request(representBaseUrl + '/boundaries/?contains=' + lat + ',' + lon + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.postCode = function(postCode, callback) {
  request(representBaseUrl + '/postcodes/' + postCode + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}

module.exports.representativesLatLon = function(lat, lon, callback) {
  request(representBaseUrl + '/representatives/?point=' + lat + ',' + lon + '/', function (error, response, body) {
    if (!error && response.statusCode == 200) {
      callback(JSON.parse(body));
      return;
    }
    callback(null);
  });
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux';
import { withRouter } from 'react-router'
import PropTypes from 'prop-types'
import { fetchInterfaceColList, fetchInterfaceCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
import { autobind } from 'core-decorators';
import axios from 'axios';
import { Input, Icon, Tag, Modal, message, Tooltip, Tree, Dropdown, Menu, Form } from 'antd';

const TreeNode = Tree.TreeNode;
const FormItem = Form.Item;
const confirm = Modal.confirm;

import './InterfaceColMenu.scss'

const ColModalForm = Form.create()((props) => {
  const { visible, onCancel, onCreate, form, title } = props;
  const { getFieldDecorator } = form;
  return (
    <Modal
      visible={visible}
      title={title}
      onCancel={onCancel}
      onOk={onCreate}
    >
      <Form layout="vertical">
        <FormItem label="集合名">
          {getFieldDecorator('colName', {
            rules: [{ required: true, message: '请输入集合命名！' }]
          })(
            <Input />
          )}
        </FormItem>
        <FormItem label="简介">
          {getFieldDecorator('colDesc')(<Input type="textarea" />)}
        </FormItem>
      </Form>
    </Modal>
  )
});

@connect(
  state => {
    return {
      interfaceColList: state.interfaceCol.interfaceColList,
      currColId: state.interfaceCol.currColId,
      currCaseId: state.interfaceCol.currCaseId,
      isShowCol: state.interfaceCol.isShowCol
    }
  },
  {
    fetchInterfaceColList,
    fetchInterfaceCaseList,
    setColData
  }
)
@withRouter
export default class InterfaceColMenu extends Component {

  static propTypes = {
    match: PropTypes.object,
    interfaceColList: PropTypes.array,
    fetchInterfaceColList: PropTypes.func,
    fetchInterfaceCaseList: PropTypes.func,
    setColData: PropTypes.func,
    history: PropTypes.object,
    currColId: PropTypes.number,
    currCaseId: PropTypes.number,
    isShowCol: PropTypes.bool
  }

  state = {
    expandedKeys: [],
    colModalType: '',
    colModalVisible: false,
    editColId: 0
  }

  constructor(props) {
    super(props)
  }

  async componentWillMount() {
    const { isShowCol, currColId, currCaseId } = this.props;
    const action = isShowCol ? 'col' : 'case';
    const actionId = isShowCol ? currColId : currCaseId;
    this.setState({expandedKeys: [action+'_'+actionId]})
  }

  async componentWillReceiveProps(nextProps) {
    const { currColId } = nextProps;
    let expandedKeys = this.state.expandedKeys;
    if (expandedKeys.indexOf('col_'+currColId) === -1) {
      expandedKeys = expandedKeys.concat(['col_'+currColId])
    }
    this.setState({expandedKeys})
  }

  @autobind
  async addorEditCol() {
    const { colName: name, colDesc: desc } = this.form.getFieldsValue();
    const { colModalType, editColId: col_id } = this.state;
    const project_id = this.props.match.params.id;
    let res = {};
    if (colModalType === 'add') {
      res = await axios.post('/api/col/add_col', { name, desc, project_id })
    } else if (colModalType === 'edit') {
      res = await axios.post('/api/col/up_col', { name, desc, col_id })
    }
    if (!res.data.errcode) {
      this.setState({
        colModalVisible: false
      });
      message.success(colModalType === 'edit' ? '修改集合成功' : '添加集合成功');
      await this.props.fetchInterfaceColList(project_id);
    } else {
      message.error(res.data.errmsg);
    }
  }

  onExpand = (keys) => {
    this.setState({expandedKeys: keys})
  }

  onSelect = (keys) => {
    if (keys.length) {
      const type = keys[0].split('_')[0];
      const id = keys[0].split('_')[1];
      const project_id = this.props.match.params.id
      if (type === 'col') {
        this.props.setColData({
          isShowCol: true,
          currColId: +id
        })
        this.props.history.push('/project/' + project_id + '/interface/col/' + id)
      } else {
        this.props.setColData({
          isShowCol: false,
          currCaseId: +id
        })
        this.props.history.push('/project/' + project_id + '/interface/case/' + id)
      }
    }
  }
  showDelColConfirm = (colId) => {
    let that = this;
    confirm({
      title: '您确认删除此测试集合',
      content: '温馨提示：该操作会删除该集合下所有测试用例，用例删除后无法恢复',
      async onOk() {
        const res = await axios.get('/api/col/del_col?col_id=' + colId)
        if (!res.data.errcode) {
          message.success('删除集合成功');
          await that.props.fetchInterfaceColList(that.props.match.params.id);
        } else {
          message.error(res.data.errmsg);
        }
      }
    });
  }
  showDelCaseConfirm = (caseId) => {
    let that = this;
    confirm({
      title: '您确认删除此测试用例',
      content: '温馨提示：用例删除后无法恢复',
      async onOk() {
        const res = await axios.get('/api/col/del_case?caseid=' + caseId)
        if (!res.data.errcode) {
          message.success('删除用例成功');
          await that.props.fetchInterfaceColList(that.props.match.params.id);
        } else {
          message.error(res.data.errmsg);
        }
      }
    });
  }
  showColModal = (type, col) => {
    const editCol = type === 'edit' ? {colName: col.name, colDesc: col.desc} : {colName: '', colDesc: ''};
    this.setState({
      colModalVisible: true,
      colModalType: type || 'add',
      editColId: col && col._id
    })
    this.form.setFieldsValue(editCol)
  }
  saveFormRef = (form) => {
    this.form = form;
  }

  render() {
    const { currColId, currCaseId, isShowCol } = this.props;
    const { colModalType, colModalVisible } = this.state;

    const menu = (col) => {
      return (
        <Menu>
          <Menu.Item>
             this.showColModal('edit', col)}>修改集合
          </Menu.Item>
          <Menu.Item>
             {
              this.showDelColConfirm(col._id)
            }}>删除集合
          </Menu.Item>
        </Menu>
      )
    };

    return (

          <Input placeholder="Filter by name" style={{ width: "70%" }} />
          <Tooltip placement="bottom" title="添加集合">
            <Tag color="#108ee9" style={{ marginLeft: "15px" }} onClick={() => this.showColModal('add')} ><Icon type="plus" /></Tag>
          </Tooltip>

        <Tree
          className="col-list-tree"
          expandedKeys={this.state.expandedKeys}
          selectedKeys={[isShowCol ? 'col_'+currColId : 'case_'+currCaseId]}
          onSelect={this.onSelect}
          autoExpandParent
          onExpand={this.onExpand}
        >
          {
            this.props.interfaceColList.map((col) => (
              <TreeNode
                key={'col_' + col._id}
                title={

                    <Icon type="folder-open" style={{marginRight: 5}} />{col.name}
                    <Dropdown overlay={menu(col)}>
                      <Icon type='bars'/>
                    </Dropdown>

                }
              >
                {
                  col.caseList && col.caseList.map((interfaceCase) => (
                    <TreeNode
                      style={{width: '100%'}}
                      key={'case_' + interfaceCase._id}
                      title={

                          {interfaceCase.casename}
                          <Icon type='delete' className="case-delete-icon" onClick={() => { this.showDelCaseConfirm(interfaceCase._id) }} />

                      }
                    ></TreeNode>
                  ))
                }
              </TreeNode>
            ))
          }
        </Tree>
        <ColModalForm
          ref={this.saveFormRef}
          type={colModalType}
          visible={colModalVisible}
          onCancel={() => { this.setState({ colModalVisible: false }) }}
          onCreate={this.addorEditCol}
        ></ColModalForm>

    )
  }
}
<commit_msg>feat: col filter<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux';
import { withRouter } from 'react-router'
import PropTypes from 'prop-types'
import { fetchInterfaceColList, fetchInterfaceCaseList, setColData } from '../../../../reducer/modules/interfaceCol'
import { autobind } from 'core-decorators';
import axios from 'axios';
import { Input, Icon, Tag, Modal, message, Tooltip, Tree, Dropdown, Menu, Form } from 'antd';

const TreeNode = Tree.TreeNode;
const FormItem = Form.Item;
const confirm = Modal.confirm;

import './InterfaceColMenu.scss'

const ColModalForm = Form.create()((props) => {
  const { visible, onCancel, onCreate, form, title } = props;
  const { getFieldDecorator } = form;
  return (
    <Modal
      visible={visible}
      title={title}
      onCancel={onCancel}
      onOk={onCreate}
    >
      <Form layout="vertical">
        <FormItem label="集合名">
          {getFieldDecorator('colName', {
            rules: [{ required: true, message: '请输入集合命名！' }]
          })(
            <Input />
          )}
        </FormItem>
        <FormItem label="简介">
          {getFieldDecorator('colDesc')(<Input type="textarea" />)}
        </FormItem>
      </Form>
    </Modal>
  )
});

@connect(
  state => {
    return {
      interfaceColList: state.interfaceCol.interfaceColList,
      currColId: state.interfaceCol.currColId,
      currCaseId: state.interfaceCol.currCaseId,
      isShowCol: state.interfaceCol.isShowCol
    }
  },
  {
    fetchInterfaceColList,
    fetchInterfaceCaseList,
    setColData
  }
)
@withRouter
export default class InterfaceColMenu extends Component {

  static propTypes = {
    match: PropTypes.object,
    interfaceColList: PropTypes.array,
    fetchInterfaceColList: PropTypes.func,
    fetchInterfaceCaseList: PropTypes.func,
    setColData: PropTypes.func,
    history: PropTypes.object,
    currColId: PropTypes.number,
    currCaseId: PropTypes.number,
    isShowCol: PropTypes.bool
  }

  state = {
    expandedKeys: [],
    colModalType: '',
    colModalVisible: false,
    editColId: 0,
    filterValue: ''
  }

  constructor(props) {
    super(props)
  }

  async componentWillMount() {
    const { isShowCol, currColId, currCaseId } = this.props;
    const action = isShowCol ? 'col' : 'case';
    const actionId = isShowCol ? currColId : currCaseId;
    this.setState({expandedKeys: [action+'_'+actionId]})
  }

  async componentWillReceiveProps(nextProps) {
    const { currColId } = nextProps;
    let expandedKeys = this.state.expandedKeys;
    if (expandedKeys.indexOf('col_'+currColId) === -1) {
      expandedKeys = expandedKeys.concat(['col_'+currColId])
    }
    this.setState({expandedKeys})
  }

  @autobind
  async addorEditCol() {
    const { colName: name, colDesc: desc } = this.form.getFieldsValue();
    const { colModalType, editColId: col_id } = this.state;
    const project_id = this.props.match.params.id;
    let res = {};
    if (colModalType === 'add') {
      res = await axios.post('/api/col/add_col', { name, desc, project_id })
    } else if (colModalType === 'edit') {
      res = await axios.post('/api/col/up_col', { name, desc, col_id })
    }
    if (!res.data.errcode) {
      this.setState({
        colModalVisible: false
      });
      message.success(colModalType === 'edit' ? '修改集合成功' : '添加集合成功');
      await this.props.fetchInterfaceColList(project_id);
    } else {
      message.error(res.data.errmsg);
    }
  }

  onExpand = (keys) => {
    this.setState({expandedKeys: keys})
  }

  onSelect = (keys) => {
    if (keys.length) {
      const type = keys[0].split('_')[0];
      const id = keys[0].split('_')[1];
      const project_id = this.props.match.params.id
      if (type === 'col') {
        this.props.setColData({
          isShowCol: true,
          currColId: +id
        })
        this.props.history.push('/project/' + project_id + '/interface/col/' + id)
      } else {
        this.props.setColData({
          isShowCol: false,
          currCaseId: +id
        })
        this.props.history.push('/project/' + project_id + '/interface/case/' + id)
      }
    }
  }
  showDelColConfirm = (colId) => {
    let that = this;
    confirm({
      title: '您确认删除此测试集合',
      content: '温馨提示：该操作会删除该集合下所有测试用例，用例删除后无法恢复',
      async onOk() {
        const res = await axios.get('/api/col/del_col?col_id=' + colId)
        if (!res.data.errcode) {
          message.success('删除集合成功');
          await that.props.fetchInterfaceColList(that.props.match.params.id);
        } else {
          message.error(res.data.errmsg);
        }
      }
    });
  }
  showDelCaseConfirm = (caseId) => {
    let that = this;
    confirm({
      title: '您确认删除此测试用例',
      content: '温馨提示：用例删除后无法恢复',
      async onOk() {
        const res = await axios.get('/api/col/del_case?caseid=' + caseId)
        if (!res.data.errcode) {
          message.success('删除用例成功');
          await that.props.fetchInterfaceColList(that.props.match.params.id);
        } else {
          message.error(res.data.errmsg);
        }
      }
    });
  }
  showColModal = (type, col) => {
    const editCol = type === 'edit' ? {colName: col.name, colDesc: col.desc} : {colName: '', colDesc: ''};
    this.setState({
      colModalVisible: true,
      colModalType: type || 'add',
      editColId: col && col._id
    })
    this.form.setFieldsValue(editCol)
  }
  saveFormRef = (form) => {
    this.form = form;
  }

  filterCol = (e) => {
    const value = e.target.value;
    this.setState({filterValue: value})
  }

  render() {
    const { currColId, currCaseId, isShowCol } = this.props;
    const { colModalType, colModalVisible, filterValue } = this.state;

    const menu = (col) => {
      return (
        <Menu>
          <Menu.Item>
             this.showColModal('edit', col)}>修改集合
          </Menu.Item>
          <Menu.Item>
             {
              this.showDelColConfirm(col._id)
            }}>删除集合
          </Menu.Item>
        </Menu>
      )
    };

    return (

          <Input placeholder="Filter by name" style={{ width: "70%" }} onChange={this.filterCol} />
          <Tooltip placement="bottom" title="添加集合">
            <Tag color="#108ee9" style={{ marginLeft: "15px" }} onClick={() => this.showColModal('add')} ><Icon type="plus" /></Tag>
          </Tooltip>

        <Tree
          className="col-list-tree"
          expandedKeys={this.state.expandedKeys}
          selectedKeys={[isShowCol ? 'col_'+currColId : 'case_'+currCaseId]}
          onSelect={this.onSelect}
          autoExpandParent
          onExpand={this.onExpand}
        >
          {
            this.props.interfaceColList.filter(col => col.name.indexOf(filterValue) !== -1).map((col) => (
              <TreeNode
                key={'col_' + col._id}
                title={

                    <Icon type="folder-open" style={{marginRight: 5}} />{col.name}
                    <Dropdown overlay={menu(col)}>
                      <Icon type='bars'/>
                    </Dropdown>

                }
              >
                {
                  col.caseList && col.caseList.map((interfaceCase) => (
                    <TreeNode
                      style={{width: '100%'}}
                      key={'case_' + interfaceCase._id}
                      title={

                          {interfaceCase.casename}
                          <Icon type='delete' className="case-delete-icon" onClick={() => { this.showDelCaseConfirm(interfaceCase._id) }} />

                      }
                    ></TreeNode>
                  ))
                }
              </TreeNode>
            ))
          }
        </Tree>
        <ColModalForm
          ref={this.saveFormRef}
          type={colModalType}
          visible={colModalVisible}
          onCancel={() => { this.setState({ colModalVisible: false }) }}
          onCreate={this.addorEditCol}
        ></ColModalForm>

    )
  }
}
<|endoftext|>


<commit_before>const electron = require('electron');
const config = require('./config');
const elementReady = require('element-ready');

const ipcRenderer = electron.ipcRenderer;
const $ = document.querySelector.bind(document);

const selectors = {
  root: '#react-root ._onabe',
  loginButton: '#react-root ._fcn8k'
};

ipcRenderer.on('toggle-dark-mode', () => {
  config.set('darkMode', !config.get('darkMode'));
  setDarkMode();
});

ipcRenderer.on('navigate-home', () => {
  const home = $('._n7q2c ._r1svv:nth-child(1) a');
  if (home) {
    home.click();
  }
});

ipcRenderer.on('navigate-discover', () => {
  const discover = $('._n7q2c ._r1svv:nth-child(2) a');
  if (discover) {
    discover.click();
  }
});

ipcRenderer.on('navigate-notifications', () => {
  const notifications = $('._n7q2c ._r1svv:nth-child(3) a');
  if (notifications) {
    notifications.click();
  }
});

ipcRenderer.on('navigate-profile', () => {
  const profile = $('._n7q2c ._r1svv:nth-child(4) a');
  if (profile) {
    profile.click();
  }
});

function backButton() {
  const body = $('body');
  const link = document.createElement('a');
  const element = document.createElement('div');
  link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.84 17.39">'
    + '<polygon points="22.84 8.22 1.82 8.22 9.37 0.67 8.7 0 0 8.7 8.7 17.39 '
    + '9.37 16.72 1.82 9.17 22.84 9.17 22.84 8.22"/>'
    + '</svg>';
  element.classList.add('back-btn', 'inactive');
  element.appendChild(link);
  body.appendChild(element);

  link.addEventListener('click', () => {
    ipcRenderer.send('back');
  });

  ipcRenderer.on('set-button-state', (event, enabled) => {
    if (enabled) {
      element.classList.remove('inactive');
    } else {
      element.classList.add('inactive');
    }
  });
}

function login(elm) {
  elm.addEventListener('click', () => {
    elm.classList.toggle('goback');
    process.nextTick(() => {
      if (elm.classList.contains('goback')) {
        elm.innerText = 'Go back';
      } else {
        elm.innerText = 'Log In';
      }
    });
  });
}

function setDarkMode() {
  document.documentElement.classList.toggle('dark-mode', config.get('darkMode'));
}

function init() {
  backButton();
  setDarkMode();

  // Prevent flash of white on startup when in dark mode
  // TODO: Find solution to this with pure css
  if (config.get('darkMode')) {
    document.documentElement.style.backgroundColor = '#192633';
  }
}

document.addEventListener('DOMContentLoaded', () => {
  // enable OS specific styles
  document.documentElement.classList.add(`os-${process.platform}`);

  elementReady(selectors.root).then(init);
  elementReady(selectors.loginButton).then(login);
});

window.addEventListener('load', () => {
  document.getElementById('react-root').addEventListener('contextmenu', (e) => {
    const targetName = e.target.className;
    if (targetName === '_ovg3g') {
      picCover(e);
    }
    if (targetName === '_c2kdw') {
      videoCover(e);
    }
  });
});

const removeCover = (div) => {
  document.body.removeChild(div);
  document.body.style.overflow = 'auto';
};

const videoCover = (e) => {
  const target = e.target;
  const trueTarget = target.previousSibling;
  const src = trueTarget.querySelectorAll('video')[0].src;
  const video = document.createElement('video');
  video.src = src;
  video.style.width = '90%';
  video.setAttribute('autoplay', true);
  video.setAttribute('controls', true);
  const div = document.createElement('div');
  div.appendChild(video);
  div.className = 'electron_pic_cover';
  document.body.style.overflow = 'hidden';
  video.addEventListener('click', () => removeCover(div));
  document.body.appendChild(div);
};

const picCover = (e) => {
  const trueTarget = e.target.previousSibling.previousSibling;
  const src = trueTarget.querySelectorAll('img._icyx7')[0].src;
  const img = document.createElement('img');
  img.src = src;
  img.style.width = '90%';
  img.style.cursor = 'pointer';
  const div = document.createElement('div');
  div.appendChild(img);
  div.className = 'electron_pic_cover';
  document.body.style.overflow = 'hidden';
  img.addEventListener('click', () => removeCover(div));
  document.body.appendChild(div);
};
<commit_msg>save pic and video<commit_after>const https = require('https');
const fs = require('fs');
const electron = require('electron');
const { dialog } = require('electron').remote;
const config = require('./config');
const elementReady = require('element-ready');

const ipcRenderer = electron.ipcRenderer;
const $ = document.querySelector.bind(document);

const selectors = {
  root: '#react-root ._onabe',
  loginButton: '#react-root ._fcn8k'
};

ipcRenderer.on('toggle-dark-mode', () => {
  config.set('darkMode', !config.get('darkMode'));
  setDarkMode();
});

ipcRenderer.on('navigate-home', () => {
  const home = $('._n7q2c ._r1svv:nth-child(1) a');
  if (home) {
    home.click();
  }
});

ipcRenderer.on('navigate-discover', () => {
  const discover = $('._n7q2c ._r1svv:nth-child(2) a');
  if (discover) {
    discover.click();
  }
});

ipcRenderer.on('navigate-notifications', () => {
  const notifications = $('._n7q2c ._r1svv:nth-child(3) a');
  if (notifications) {
    notifications.click();
  }
});

ipcRenderer.on('navigate-profile', () => {
  const profile = $('._n7q2c ._r1svv:nth-child(4) a');
  if (profile) {
    profile.click();
  }
});

function backButton() {
  const body = $('body');
  const link = document.createElement('a');
  const element = document.createElement('div');
  link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22.84 17.39">'
    + '<polygon points="22.84 8.22 1.82 8.22 9.37 0.67 8.7 0 0 8.7 8.7 17.39 '
    + '9.37 16.72 1.82 9.17 22.84 9.17 22.84 8.22"/>'
    + '</svg>';
  element.classList.add('back-btn', 'inactive');
  element.appendChild(link);
  body.appendChild(element);

  link.addEventListener('click', () => {
    ipcRenderer.send('back');
  });

  ipcRenderer.on('set-button-state', (event, enabled) => {
    if (enabled) {
      element.classList.remove('inactive');
    } else {
      element.classList.add('inactive');
    }
  });
}

function login(elm) {
  elm.addEventListener('click', () => {
    elm.classList.toggle('goback');
    process.nextTick(() => {
      if (elm.classList.contains('goback')) {
        elm.innerText = 'Go back';
      } else {
        elm.innerText = 'Log In';
      }
    });
  });
}

function setDarkMode() {
  document.documentElement.classList.toggle('dark-mode', config.get('darkMode'));
}

function init() {
  backButton();
  setDarkMode();

  // Prevent flash of white on startup when in dark mode
  // TODO: Find solution to this with pure css
  if (config.get('darkMode')) {
    document.documentElement.style.backgroundColor = '#192633';
  }
}

document.addEventListener('DOMContentLoaded', () => {
  // enable OS specific styles
  document.documentElement.classList.add(`os-${process.platform}`);

  elementReady(selectors.root).then(init);
  elementReady(selectors.loginButton).then(login);
});

window.addEventListener('load', () => {
  document.getElementById('react-root').addEventListener('contextmenu', (e) => {
    const targetName = e.target.className;
    if (targetName === '_ovg3g') {
      picCover(e);
    }
    if (targetName === '_c2kdw') {
      videoCover(e);
    }
  });
});

const removeCover = (div) => {
  document.body.removeChild(div);
  document.body.style.overflow = 'auto';
};

const savePicOrVideo = (src, fileType) => {
  const title = `save ${fileType}`;
  const options = {
    title,
    defaultPath: __dirname,
  };
  const choosePath = dialog.showSaveDialog(options);
  if (choosePath) {
    download(src, `${choosePath}.${fileType}`)
      .then((done, error) => {
        let body;
        if (done) {
          body = done;
        }
        if (error) {
          body = done;
        }
        const notificationOptions = {
          title,
          body,
          icon: './static/icon.png'
        };
        const notification = new Notification(title, notificationOptions);
        notification();
      });
  }
};

const concat1 = (buf1, buf2) => {
  const tmp = new Buffer(buf1.length + buf2.length);
  buf1.copy(tmp, 0);
  buf2.copy(tmp, buf1.length);
  return tmp;
};

const download = (url, path) => new Promise((resolve, reject) => {
  https.get(url, (res) => {
    let picData = new Buffer(0);
    res.on('data', (chunk) => {
      picData = concat1(picData, chunk);
    });
    res.on('end', () => {
      fs.writeFile(path, picData, (err) => {
        if (err) {
          reject(err);
        } else {
          resolve('save pictures is done');
        }
      });
    });
  }).on('error', (e) => {
    reject(e.message);
  });
});

const videoCover = (e) => {
  const target = e.target;
  const trueTarget = target.previousSibling;
  const src = trueTarget.querySelectorAll('video')[0].src;
  const video = document.createElement('video');
  video.src = src;
  video.style.width = '90%';
  video.setAttribute('autoplay', true);
  video.setAttribute('controls', true);
  const div = document.createElement('div');
  div.appendChild(video);
  div.className = 'electron_pic_cover';
  document.body.style.overflow = 'hidden';
  video.addEventListener('click', () => removeCover(div));
  video.addEventListener('contextmenu', () => savePicOrVideo(src, 'mp4'));
  document.body.appendChild(div);
};

const picCover = (e) => {
  const trueTarget = e.target.previousSibling.previousSibling;
  const src = trueTarget.querySelectorAll('img._icyx7')[0].src;
  const img = document.createElement('img');
  img.src = src;
  img.style.width = '90%';
  img.style.cursor = 'pointer';
  const div = document.createElement('div');
  div.appendChild(img);
  div.className = 'electron_pic_cover';
  document.body.style.overflow = 'hidden';
  img.addEventListener('click', () => removeCover(div));
  img.addEventListener('contextmenu', () => savePicOrVideo(src, 'jpg'));
  document.body.appendChild(div);
};
<|endoftext|>


<commit_before>// The script generates a random subset of valid jdk, os, timezone, and other axes.
// You can preview the results by running "node matrix.js"
// See https://github.com/vlsi/github-actions-random-matrix
let {MatrixBuilder} = require('./matrix_builder');
const matrix = new MatrixBuilder();

// Some of the filter conditions might become unsatisfiable, and by default
// the matrix would ignore that.
// For instance, SCRAM requires PostgreSQL 10+, so if you ask
// matrix.generateRow({pg_version: '9.0'}), then it won't generate a row
// That behaviour is useful for PR testing. For instance, if you want to test only SCRAM=yes
// cases, then just comment out "value: no" in SCRAM axis, and the matrix would yield the matching
// parameters.
// However, if you add new testing parameters, you might want un-comment the following line
// to notice if you accidentally introduce unsatisfiable conditions.
// matrix.failOnUnsatisfiableFilters(true);

matrix.addAxis({
  name: 'java_distribution',
  values: [
    'zulu',
    'temurin',
    'liberica',
    'microsoft',
  ]
});

// TODO: support different JITs (see https://github.com/actions/setup-java/issues/279)
matrix.addAxis({name: 'jit', title: '', values: ['hotspot']});

matrix.addAxis({
  name: 'java_version',
  title: x => 'Java ' + x,
  // Strings allow versions like 18-ea
  values: [
    '8',
    '11',
    // '17',
  ]
});

matrix.addAxis({
  name: 'pg_version',
  title: x => 'PG ' + x,
  // Strings allow versions like 18-ea
  values: [
    '8.4',
    '9.0',
    '9.1',
    '9.2',
    '9.3',
    '9.4',
    '9.5',
    '9.6',
    '10',
    '11',
    '12',
    '13',
    '14',
  ]
});

matrix.addAxis({
  name: 'tz',
  title: x => 'client_tz ' + x,
  values: [
    'America/New_York',
    'Pacific/Chatham',
    'UTC'
  ]
});

matrix.addAxis({
  name: 'server_tz',
  title: x => 'server_tz ' + x,
  values: [
    'America/New_York',
    'Pacific/Chatham',
    'UTC'
  ]
});

matrix.addAxis({
  name: 'os',
  title: x => x.replace('-latest', ''),
  values: [
    'ubuntu-latest',
    // We use docker-compose for launching PostgreSQL
    // 'windows-latest',
    // 'macos-latest',
    ...(process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc' ? ['self-hosted'] : [])
  ]
});

// Test cases when Object#hashCode produces the same results
// It allows capturing cases when the code uses hashCode as a unique identifier
matrix.addAxis({
  name: 'hash',
  values: [
    {value: 'regular', title: '', weight: 42},
    {value: 'same', title: 'same hashcode', weight: 1}
  ]
});
matrix.addAxis({
  name: 'locale',
  title: x => x.language + '_' + x.country,
  values: [
    {language: 'de', country: 'DE'},
    {language: 'fr', country: 'FR'},
    {language: 'ru', country: 'RU'},
    {language: 'tr', country: 'TR'},
  ]
});

matrix.addAxis({
  name: 'query_mode',
  values: [
    {value: 'simple', title: 'simple query', weight: 2},
    {value: 'extendedForPrepared', title: '', weight: 42},
    {value: 'extendedCacheEverything', title: 'always extended query', weight: 2}
  ]
});

matrix.addAxis({
  name: 'replication',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'replication',
  values: [
    {value: 'yes', weight: 10},
// Replication requires PG 9.6+
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'slow_tests',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'slow_tests',
  values: [
    {value: 'yes', weight: 1},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'scram',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'scram',
  values: [
    {value: 'yes', weight: 10},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'ssl',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'ssl',
  values: [
    {value: 'yes', weight: 10},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'gss',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'gss',
  values: [
    {value: 'yes', weight: 2},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'xa',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'xa',
  values: [
    {value: 'yes', weight: 10},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'check_anorm_sbt',
  values: [
    // See https://github.com/pgjdbc/pgjdbc/issues/2537
    // {value: 'yes', title: 'check_anorm_sbt', weight: 30},
    {value: 'no', title: '', weight: 10},
  ]
});

function isLessThan(pg_version, minVersion){
    return Number(pg_version) < Number(minVersion);
}

matrix.setNamePattern([
    'java_version', 'java_distribution', 'pg_version', 'query_mode', 'scram', 'ssl', 'hash', 'os',
    'server_tz', 'tz', 'locale',
    'check_anorm_sbt', 'gss', 'replication', 'slow_tests',
]);

matrix.exclude(row => row.ssl.value === 'yes' && isLessThan(row.pg_version, '9.3'));
matrix.exclude(row => row.scram.value === 'yes' && isLessThan(row.pg_version, '10'));
matrix.exclude(row => row.replication.value === 'yes' && isLessThan(row.pg_version, '9.6'));

// Microsoft Java has no distribution for 8
matrix.exclude({java_distribution: 'microsoft', java_version: '8'});
matrix.exclude({gss: {value: 'yes'}, os: ['windows-latest', 'macos-latest', 'self-hosted']})
if (process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc') {
    // PG images below 9.3 are x86_64 only
    matrix.exclude(row => row.os === 'self-hosted' && isLessThan(row.pg_version, '9.3'));
}

// The most rare features should be generated the first
// For instance, we have a lot of PostgreSQL versions, so we generate the minimal the first
// It would have to generate other parameters, and it might happen it would cover "most recent Java" automatically
// Ensure at least one job with "same" hashcode exists
matrix.generateRow({hash: {value: 'same'}});
matrix.generateRow({scram: {value: 'yes'}});
// Ensure we have a job with the minimal and maximal PostgreSQL versions
matrix.generateRow({pg_version: matrix.axisByName.pg_version.values[0]});
matrix.generateRow({pg_version: matrix.axisByName.pg_version.values.slice(-1)[0]});
//Ensure at least one job with "simple" query_mode exists
matrix.generateRow({query_mode: {value: 'simple'}});
// Ensure there will be at least one job with minimal supported Java
matrix.generateRow({java_version: matrix.axisByName.java_version.values[0]});
// Ensure there will be at least one job with the latest Java
matrix.generateRow({java_version: matrix.axisByName.java_version.values.slice(-1)[0]});
matrix.generateRow({ssl: {value: 'yes'}});
// Ensure at least one Windows and at least one Linux job is present (macOS is almost the same as Linux)
// matrix.generateRow({os: 'windows-latest'});
matrix.generateRow({os: 'ubuntu-latest'});
if (process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc') {
  matrix.generateRow({os: 'self-hosted'});
}
const include = matrix.generateRows(process.env.MATRIX_JOBS || 5);
if (include.length === 0) {
  throw new Error('Matrix list is empty');
}
include.sort((a, b) => a.name.localeCompare(b.name, undefined, {numeric: true}));
include.forEach(v => {
  let jvmArgs = [];
  v.replication = v.replication.value;
  v.slow_tests = v.slow_tests.value;
  v.xa = v.xa.value;
  v.gss = v.gss.value;
  v.ssl = v.ssl.value;
  v.scram = v.scram.value;
  v.check_anorm_sbt = v.check_anorm_sbt.value;
  v.query_mode = v.query_mode.value;

  let includeTestTags = [];
  // See https://junit.org/junit5/docs/current/user-guide/#running-tests-tag-expressions
  includeTestTags.push('none()'); // untagged tests

  if (v.replication === 'yes') {
      includeTestTags.push('org.postgresql.test.Replication');
  }
  if (v.slow_tests === 'yes') {
      includeTestTags.push('org.postgresql.test.SlowTests');
  }
  if (v.xa === 'yes') {
      includeTestTags.push('org.postgresql.test.XaTests');
  }

  v.includeTestTags = includeTestTags.join(' | ');

  if (v.gss === 'yes' || v.check_anorm_sbt === 'yes') {
      v.deploy_to_maven_local = true
  }
  if (v.hash.value === 'same') {
    jvmArgs.push('-XX:+UnlockExperimentalVMOptions', '-XX:hashCode=2');
  }
  // Gradle does not work in tr_TR locale, so pass locale to test only: https://github.com/gradle/gradle/issues/17361
  jvmArgs.push(`-Duser.country=${v.locale.country}`);
  jvmArgs.push(`-Duser.language=${v.locale.language}`);
  if (v.jit === 'hotspot' && Math.random() > 0.5) {
    // The following options randomize instruction selection in JIT compiler
    // so it might reveal missing synchronization in TestNG code
    v.name += ', stress JIT';
    jvmArgs.push('-XX:+UnlockDiagnosticVMOptions');
    if (v.java_version >= 8) {
      // Randomize instruction scheduling in GCM
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressGCM');
      // Randomize instruction scheduling in LCM
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressLCM');
    }
    if (v.java_version >= 16) {
      // Randomize worklist traversal in IGVN
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressIGVN');
    }
    if (v.java_version >= 17) {
      // Randomize worklist traversal in CCP
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressCCP');
    }
  }
  v.testExtraJvmArgs = jvmArgs.join(' ');
  delete v.hash;
});

console.log(include);
console.log('::set-output name=matrix::' + JSON.stringify({include}));
<commit_msg>test: add query_mode=extended as it is the default value<commit_after>// The script generates a random subset of valid jdk, os, timezone, and other axes.
// You can preview the results by running "node matrix.js"
// See https://github.com/vlsi/github-actions-random-matrix
let {MatrixBuilder} = require('./matrix_builder');
const matrix = new MatrixBuilder();

// Some of the filter conditions might become unsatisfiable, and by default
// the matrix would ignore that.
// For instance, SCRAM requires PostgreSQL 10+, so if you ask
// matrix.generateRow({pg_version: '9.0'}), then it won't generate a row
// That behaviour is useful for PR testing. For instance, if you want to test only SCRAM=yes
// cases, then just comment out "value: no" in SCRAM axis, and the matrix would yield the matching
// parameters.
// However, if you add new testing parameters, you might want un-comment the following line
// to notice if you accidentally introduce unsatisfiable conditions.
// matrix.failOnUnsatisfiableFilters(true);

matrix.addAxis({
  name: 'java_distribution',
  values: [
    'zulu',
    'temurin',
    'liberica',
    'microsoft',
  ]
});

// TODO: support different JITs (see https://github.com/actions/setup-java/issues/279)
matrix.addAxis({name: 'jit', title: '', values: ['hotspot']});

matrix.addAxis({
  name: 'java_version',
  title: x => 'Java ' + x,
  // Strings allow versions like 18-ea
  values: [
    '8',
    '11',
    // '17',
  ]
});

matrix.addAxis({
  name: 'pg_version',
  title: x => 'PG ' + x,
  // Strings allow versions like 18-ea
  values: [
    '8.4',
    '9.0',
    '9.1',
    '9.2',
    '9.3',
    '9.4',
    '9.5',
    '9.6',
    '10',
    '11',
    '12',
    '13',
    '14',
  ]
});

matrix.addAxis({
  name: 'tz',
  title: x => 'client_tz ' + x,
  values: [
    'America/New_York',
    'Pacific/Chatham',
    'UTC'
  ]
});

matrix.addAxis({
  name: 'server_tz',
  title: x => 'server_tz ' + x,
  values: [
    'America/New_York',
    'Pacific/Chatham',
    'UTC'
  ]
});

matrix.addAxis({
  name: 'os',
  title: x => x.replace('-latest', ''),
  values: [
    'ubuntu-latest',
    // We use docker-compose for launching PostgreSQL
    // 'windows-latest',
    // 'macos-latest',
    ...(process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc' ? ['self-hosted'] : [])
  ]
});

// Test cases when Object#hashCode produces the same results
// It allows capturing cases when the code uses hashCode as a unique identifier
matrix.addAxis({
  name: 'hash',
  values: [
    {value: 'regular', title: '', weight: 42},
    {value: 'same', title: 'same hashcode', weight: 1}
  ]
});
matrix.addAxis({
  name: 'locale',
  title: x => x.language + '_' + x.country,
  values: [
    {language: 'de', country: 'DE'},
    {language: 'fr', country: 'FR'},
    {language: 'ru', country: 'RU'},
    {language: 'tr', country: 'TR'},
  ]
});

matrix.addAxis({
  name: 'query_mode',
  values: [
    {value: 'simple', title: 'simple query', weight: 2},
    {value: 'extendedForPrepared', title: 'extendedForPrepared', weight: 2},
    {value: 'extended', title: '', weight: 10},
    {value: 'extendedCacheEverything', title: 'extendedCacheEverything', weight: 2}
  ]
});

matrix.addAxis({
  name: 'replication',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'replication',
  values: [
    {value: 'yes', weight: 10},
// Replication requires PG 9.6+
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'slow_tests',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'slow_tests',
  values: [
    {value: 'yes', weight: 1},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'scram',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'scram',
  values: [
    {value: 'yes', weight: 10},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'ssl',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'ssl',
  values: [
    {value: 'yes', weight: 10},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'gss',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'gss',
  values: [
    {value: 'yes', weight: 2},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'xa',
  title: x => (x.value === 'yes' ? '' : 'no_') + 'xa',
  values: [
    {value: 'yes', weight: 10},
    {value: 'no', weight: 10},
  ]
});

matrix.addAxis({
  name: 'check_anorm_sbt',
  values: [
    // See https://github.com/pgjdbc/pgjdbc/issues/2537
    // {value: 'yes', title: 'check_anorm_sbt', weight: 30},
    {value: 'no', title: '', weight: 10},
  ]
});

function isLessThan(pg_version, minVersion){
    return Number(pg_version) < Number(minVersion);
}

matrix.setNamePattern([
    'java_version', 'java_distribution', 'pg_version', 'query_mode', 'scram', 'ssl', 'hash', 'os',
    'server_tz', 'tz', 'locale',
    'check_anorm_sbt', 'gss', 'replication', 'slow_tests',
]);

matrix.exclude(row => row.ssl.value === 'yes' && isLessThan(row.pg_version, '9.3'));
matrix.exclude(row => row.scram.value === 'yes' && isLessThan(row.pg_version, '10'));
matrix.exclude(row => row.replication.value === 'yes' && isLessThan(row.pg_version, '9.6'));

// Microsoft Java has no distribution for 8
matrix.exclude({java_distribution: 'microsoft', java_version: '8'});
matrix.exclude({gss: {value: 'yes'}, os: ['windows-latest', 'macos-latest', 'self-hosted']})
if (process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc') {
    // PG images below 9.3 are x86_64 only
    matrix.exclude(row => row.os === 'self-hosted' && isLessThan(row.pg_version, '9.3'));
}

// The most rare features should be generated the first
// For instance, we have a lot of PostgreSQL versions, so we generate the minimal the first
// It would have to generate other parameters, and it might happen it would cover "most recent Java" automatically
// Ensure at least one job with "same" hashcode exists
matrix.generateRow({hash: {value: 'same'}});
matrix.generateRow({scram: {value: 'yes'}});
// Ensure we have a job with the minimal and maximal PostgreSQL versions
matrix.generateRow({pg_version: matrix.axisByName.pg_version.values[0]});
matrix.generateRow({pg_version: matrix.axisByName.pg_version.values.slice(-1)[0]});
//Ensure at least one job with "simple" query_mode exists
matrix.generateRow({query_mode: {value: 'simple'}});
// Ensure there will be at least one job with minimal supported Java
matrix.generateRow({java_version: matrix.axisByName.java_version.values[0]});
// Ensure there will be at least one job with the latest Java
matrix.generateRow({java_version: matrix.axisByName.java_version.values.slice(-1)[0]});
matrix.generateRow({ssl: {value: 'yes'}});
// Ensure at least one Windows and at least one Linux job is present (macOS is almost the same as Linux)
// matrix.generateRow({os: 'windows-latest'});
matrix.generateRow({os: 'ubuntu-latest'});
if (process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc') {
  matrix.generateRow({os: 'self-hosted'});
}
const include = matrix.generateRows(process.env.MATRIX_JOBS || 5);
if (include.length === 0) {
  throw new Error('Matrix list is empty');
}
include.sort((a, b) => a.name.localeCompare(b.name, undefined, {numeric: true}));
include.forEach(v => {
  let jvmArgs = [];
  v.replication = v.replication.value;
  v.slow_tests = v.slow_tests.value;
  v.xa = v.xa.value;
  v.gss = v.gss.value;
  v.ssl = v.ssl.value;
  v.scram = v.scram.value;
  v.check_anorm_sbt = v.check_anorm_sbt.value;
  v.query_mode = v.query_mode.value;

  let includeTestTags = [];
  // See https://junit.org/junit5/docs/current/user-guide/#running-tests-tag-expressions
  includeTestTags.push('none()'); // untagged tests

  if (v.replication === 'yes') {
      includeTestTags.push('org.postgresql.test.Replication');
  }
  if (v.slow_tests === 'yes') {
      includeTestTags.push('org.postgresql.test.SlowTests');
  }
  if (v.xa === 'yes') {
      includeTestTags.push('org.postgresql.test.XaTests');
  }

  v.includeTestTags = includeTestTags.join(' | ');

  if (v.gss === 'yes' || v.check_anorm_sbt === 'yes') {
      v.deploy_to_maven_local = true
  }
  if (v.hash.value === 'same') {
    jvmArgs.push('-XX:+UnlockExperimentalVMOptions', '-XX:hashCode=2');
  }
  // Gradle does not work in tr_TR locale, so pass locale to test only: https://github.com/gradle/gradle/issues/17361
  jvmArgs.push(`-Duser.country=${v.locale.country}`);
  jvmArgs.push(`-Duser.language=${v.locale.language}`);
  if (v.jit === 'hotspot' && Math.random() > 0.5) {
    // The following options randomize instruction selection in JIT compiler
    // so it might reveal missing synchronization in TestNG code
    v.name += ', stress JIT';
    jvmArgs.push('-XX:+UnlockDiagnosticVMOptions');
    if (v.java_version >= 8) {
      // Randomize instruction scheduling in GCM
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressGCM');
      // Randomize instruction scheduling in LCM
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressLCM');
    }
    if (v.java_version >= 16) {
      // Randomize worklist traversal in IGVN
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressIGVN');
    }
    if (v.java_version >= 17) {
      // Randomize worklist traversal in CCP
      // share/opto/c2_globals.hpp
      jvmArgs.push('-XX:+StressCCP');
    }
  }
  v.testExtraJvmArgs = jvmArgs.join(' ');
  delete v.hash;
});

console.log(include);
console.log('::set-output name=matrix::' + JSON.stringify({include}));
<|endoftext|>


<commit_before>/**
 * This is the web browser implementation of `debug()`.
 *
 * Expose `debug()` as the module.
 */

exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = 'undefined' != typeof chrome
               && 'undefined' != typeof chrome.storage
                  ? chrome.storage.local
                  : localstorage();

/**
 * Colors.
 */

exports.colors = [
  'lightseagreen',
  'forestgreen',
  'goldenrod',
  'dodgerblue',
  'darkorchid',
  'crimson'
];

/**
 * Currently only WebKit-based Web Inspectors, Firefox >= v31,
 * and the Firebug extension (any Firefox version) are known
 * to support "%c" CSS customizations.
 *
 * TODO: add a `localStorage` variable to explicitly enable/disable colors
 */

function useColors() {
  // is webkit? http://stackoverflow.com/a/16459606/376773
  // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
  return (typeof document !== 'undefined' && 'WebkitAppearance' in document.documentElement.style) ||
    // is firebug? http://stackoverflow.com/a/398120/376773
    (typeof window !== 'undefined' && window.console && (console.firebug || (console.exception && console.table))) ||
    // is firefox >= v31?
    // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
    (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
    // double check webkit in userAgent just in case we are in a worker
    (navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}

/**
 * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
 */

exports.formatters.j = function(v) {
  try {
    return JSON.stringify(v);
  } catch (err) {
    return '[UnexpectedJSONParseError]: ' + err.message;
  }
};

/**
 * Colorize log arguments if enabled.
 *
 * @api public
 */

function formatArgs(args) {
  var useColors = this.useColors;

  args[0] = (useColors ? '%c' : '')
    + this.namespace
    + (useColors ? ' %c' : ' ')
    + args[0]
    + (useColors ? '%c ' : ' ')
    + '+' + exports.humanize(this.diff);

  if (!useColors) return;

  var c = 'color: ' + this.color;
  args.splice(1, 0, c, 'color: inherit')

  // the final "%c" is somewhat tricky, because there could be other
  // arguments passed either before or after the %c, so we need to
  // figure out the correct index to insert the CSS into
  var index = 0;
  var lastC = 0;
  args[0].replace(/%[a-z%]/g, function(match) {
    if ('%%' === match) return;
    index++;
    if ('%c' === match) {
      // we only are interested in the *last* %c
      // (the user may have provided their own)
      lastC = index;
    }
  });

  args.splice(lastC, 0, c);
}

/**
 * Invokes `console.log()` when available.
 * No-op when `console.log` is not a "function".
 *
 * @api public
 */

function log() {
  // this hackery is required for IE8/9, where
  // the `console.log` function doesn't have 'apply'
  return 'object' === typeof console
    && console.log
    && Function.prototype.apply.call(console.log, console, arguments);
}

/**
 * Save `namespaces`.
 *
 * @param {String} namespaces
 * @api private
 */

function save(namespaces) {
  try {
    if (null == namespaces) {
      exports.storage.removeItem('debug');
    } else {
      exports.storage.debug = namespaces;
    }
  } catch(e) {}
}

/**
 * Load `namespaces`.
 *
 * @return {String} returns the previously persisted debug modes
 * @api private
 */

function load() {
  var r;
  try {
    return exports.storage.debug;
  } catch(e) {}

  // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
  if (typeof process !== 'undefined' && 'env' in process) {
    return process.env.DEBUG;
  }
}

/**
 * Enable namespaces listed in `localStorage.debug` initially.
 */

exports.enable(load());

/**
 * Localstorage attempts to return the localstorage.
 *
 * This is necessary because safari throws
 * when a user disables cookies/localstorage
 * and you attempt to access it.
 *
 * @return {LocalStorage}
 * @api private
 */

function localstorage() {
  try {
    return window.localStorage;
  } catch (e) {}
}
<commit_msg>Fix #363 (#364)<commit_after>/**
 * This is the web browser implementation of `debug()`.
 *
 * Expose `debug()` as the module.
 */

exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = 'undefined' != typeof chrome
               && 'undefined' != typeof chrome.storage
                  ? chrome.storage.local
                  : localstorage();

/**
 * Colors.
 */

exports.colors = [
  'lightseagreen',
  'forestgreen',
  'goldenrod',
  'dodgerblue',
  'darkorchid',
  'crimson'
];

/**
 * Currently only WebKit-based Web Inspectors, Firefox >= v31,
 * and the Firebug extension (any Firefox version) are known
 * to support "%c" CSS customizations.
 *
 * TODO: add a `localStorage` variable to explicitly enable/disable colors
 */

function useColors() {
  // is webkit? http://stackoverflow.com/a/16459606/376773
  // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
  return (typeof document !== 'undefined' && 'WebkitAppearance' in document.documentElement.style) ||
    // is firebug? http://stackoverflow.com/a/398120/376773
    (typeof window !== 'undefined' && window.console && (console.firebug || (console.exception && console.table))) ||
    // is firefox >= v31?
    // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
    (navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
    // double check webkit in userAgent just in case we are in a worker
    (navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}

/**
 * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
 */

exports.formatters.j = function(v) {
  try {
    return JSON.stringify(v);
  } catch (err) {
    return '[UnexpectedJSONParseError]: ' + err.message;
  }
};

/**
 * Colorize log arguments if enabled.
 *
 * @api public
 */

function formatArgs(args) {
  var useColors = this.useColors;

  args[0] = (useColors ? '%c' : '')
    + this.namespace
    + (useColors ? ' %c' : ' ')
    + args[0]
    + (useColors ? '%c ' : ' ')
    + '+' + exports.humanize(this.diff);

  if (!useColors) return;

  var c = 'color: ' + this.color;
  args.splice(1, 0, c, 'color: inherit')

  // the final "%c" is somewhat tricky, because there could be other
  // arguments passed either before or after the %c, so we need to
  // figure out the correct index to insert the CSS into
  var index = 0;
  var lastC = 0;
  args[0].replace(/%[a-z%]/g, function(match) {
    if ('%%' === match) return;
    index++;
    if ('%c' === match) {
      // we only are interested in the *last* %c
      // (the user may have provided their own)
      lastC = index;
    }
  });

  args.splice(lastC, 0, c);
}

/**
 * Invokes `console.log()` when available.
 * No-op when `console.log` is not a "function".
 *
 * @api public
 */

function log() {
  // this hackery is required for IE8/9, where
  // the `console.log` function doesn't have 'apply'
  return 'object' === typeof console
    && console.log
    && Function.prototype.apply.call(console.log, console, arguments);
}

/**
 * Save `namespaces`.
 *
 * @param {String} namespaces
 * @api private
 */

function save(namespaces) {
  try {
    if (null == namespaces) {
      exports.storage.removeItem('debug');
    } else {
      exports.storage.debug = namespaces;
    }
  } catch(e) {}
}

/**
 * Load `namespaces`.
 *
 * @return {String} returns the previously persisted debug modes
 * @api private
 */

function load() {
  var r;
  try {
    return exports.storage.debug;
  } catch(e) {}

  // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
  if (typeof process !== 'undefined' && 'env' in process) {
    return process.env.DEBUG;
  }
}

/**
 * Enable namespaces listed in `localStorage.debug` initially.
 */

exports.enable(load());

/**
 * Localstorage attempts to return the localstorage.
 *
 * This is necessary because safari throws
 * when a user disables cookies/localstorage
 * and you attempt to access it.
 *
 * @return {LocalStorage}
 * @api private
 */

function localstorage() {
  try {
    return window.localStorage;
  } catch (e) {}
}
<|endoftext|>


<commit_before>define(function(require, exports, module) {
    main.consumes = [
        "Previewer", "preview", "vfs", "tabManager", "remote.PostMessage",
        "CSSDocument", "HTMLDocument"
    ];
    main.provides = ["preview.browser"];
    return main;

    function main(options, imports, register) {
        var Previewer    = imports.Previewer;
        var tabManager   = imports.tabManager;
        var preview      = imports.preview;
        var PostMessage  = imports["remote.PostMessage"];
        var CSSDocument  = imports.CSSDocument;
        var HTMLDocument = imports.HTMLDocument;
        // var JSDocument   = imports.JSDocument;

        // var join        = require("path").join;
        // var dirname     = require("path").dirname;

        /***** Initialization *****/

        var plugin = new Previewer("Ajax.org", main.consumes, {
            caption  : "Browser",
            index    : 10,
            divider  : true,
            selector : function(path){
                return path.match(/(?:\.html|\.htm|\.xhtml)$|^https?\:\/\//);
            }
        });

        var BASEPATH = preview.previewUrl;
        var counter  = 0;

        /***** Methods *****/

        function calcRootedPath(url){
            if (url.substr(0, BASEPATH.length) == BASEPATH)
                return url.substr(BASEPATH.length);
            return url;
        }

        function cleanIframeSrc(src){
            return src
                .replace(/_c9_id=\w+\&_c9_host=.*?(?:\&|$)/, "")
                .replace(/[\?\&]$/, "");
        }

        /***** Lifecycle *****/

        plugin.on("load", function(){
        });
        plugin.on("documentLoad", function(e){
            var doc     = e.doc;
            var session = doc.getSession();
            var tab     = doc.tab;
            var editor  = e.editor;

            if (session.iframe) {
                session.editor = editor;
                editor.container.appendChild(session.iframe);
                return;
            }

            var iframe = document.createElement("iframe");
            // iframe.setAttribute("nwfaketop", true);
            iframe.setAttribute("nwdisable", true);

            iframe.style.width  = "100%";
            iframe.style.height = "100%";
            iframe.style.border = 0;
            iframe.style.backgroundColor = "rgba(255, 255, 255, 0.88)";

            iframe.addEventListener("load", function(){
                if (!iframe.src) return;

                var path = calcRootedPath(cleanIframeSrc(iframe.src));

                tab.title   =
                tab.tooltip = "[B] " + path;
                session.lastSrc  = iframe.src;

                if (options.local) {
                    var url = cleanIframeSrc(iframe.contentWindow.location.href);
                    editor.setLocation(url);
                    session.currentLocation = url;
                }
                else {
                    editor.setLocation(path);
                }

                tab.className.remove("loading");
            });

            session.id        = "livepreview" + counter++;
            session.iframe    = iframe;
            session.editor    = editor;
            session.transport = new PostMessage(iframe, session.id);

            session.transport.on("ready", function(){
                session.transport.getSources(function(err, sources){
                    session.styleSheets = sources.styleSheets.map(function(path){
                        return new CSSDocument(path).addTransport(session.transport);
                    });
                    // session.scripts = sources.scripts.map(function(path){
                    //     return new JSDocument(path).addTransport(session.transport);
                    // });
                    session.html = new HTMLDocument(sources.html)
                        .addTransport(session.transport);
                });
            }, doc);
            session.transport.on("focus", function(){
                tabManager.focusTab(doc.tab);
            }, doc);

            session.destroy = function(){
                session.transport.unload();
                delete session.editor;
                delete session.transport;
                delete session.iframe;
                delete session.id;
            }

            // Lets only destroy when the doc is destroyed
            doc.addOther(function(){ session.destroy() });

            editor.container.appendChild(session.iframe);
        });
        plugin.on("documentUnload", function(e){
            var doc     = e.doc;
            var session = doc.getSession();
            var iframe  = session.iframe;

            iframe.parentNode.removeChild(iframe);

            doc.tab.className.remove("loading");
        });
        plugin.on("documentActivate", function(e){
            var session = e.doc.getSession();
            var path = calcRootedPath(cleanIframeSrc(session.iframe.src));

            session.iframe.style.display = "block";
            session.editor.setLocation(path, true);
            session.editor.setButtonStyle("Browser", "page_white.png");
        });
        plugin.on("documentDeactivate", function(e){
            var session = e.doc.getSession();
            session.iframe.style.display = "none";
        });
        plugin.on("navigate", function(e){
            var tab     = plugin.activeDocument.tab;
            var session = plugin.activeSession;
            var iframe  = session.iframe;
            var url = e.url.match(/^[a-z]\w{1,4}\:\/\//)
                ? e.url
                : BASEPATH + e.url;
            session.url = url;

            tab.className.add("loading");
            iframe.src = url + (~url.indexOf("?") ? "&" : "?")
                + "_c9_id=" + session.id
                + "&_c9_host=" + (options.local ? "local" : location.origin);

            var path = calcRootedPath(url);
            tab.title   =
            tab.tooltip = "[B] " + path;

            plugin.activeSession.editor.setLocation(path, true);
        });
        plugin.on("update", function(e){
            // var iframe = plugin.activeSession.iframe;
            // if (e.saved)
            //     iframe.src = iframe.src;
        });
        plugin.on("reload", function(){
            var iframe = plugin.activeSession.iframe;
            var tab    = plugin.activeDocument.tab;
            tab.className.add("loading");
            iframe.src = iframe.src;
        });
        plugin.on("popout", function(){
            var src = plugin.activeSession.iframe.src;
            window.open(src);
        });
        plugin.on("getState", function(e){
            var session = e.doc.getSession();
            var state   = e.state;

            state.currentLocation = session.currentLocation;
        });
        plugin.on("setState", function(e){
            var session = e.doc.getSession();

            if (e.state.currentLocation) {
                if (session.initPath)
                    session.initPath = e.state.currentLocation;
                // else
                //     plugin.navigate({ url: e.state.currentLocation, doc: e.doc });
            }
        });
        plugin.on("unload", function(){
        });

        /***** Register and define API *****/

        /**
         * Previewer for content that the browser can display natively.
         **/
        plugin.freezePublicAPI({

        });

        register(null, {
            "preview.browser": plugin
        });
    }
});<commit_msg>add script to compress folder and use it for makestatic.sh<commit_after>define(function(require, exports, module) {
    main.consumes = [
        "Previewer", "preview", "vfs", "tabManager", "remote.PostMessage",
        "CSSDocument", "HTMLDocument"
    ];
    main.provides = ["preview.browser"];
    return main;

    function main(options, imports, register) {
        var Previewer    = imports.Previewer;
        var tabManager   = imports.tabManager;
        var preview      = imports.preview;
        var PostMessage  = imports["remote.PostMessage"];
        var CSSDocument  = imports.CSSDocument;
        var HTMLDocument = imports.HTMLDocument;
        // var JSDocument   = imports.JSDocument;

        // var join        = require("path").join;
        // var dirname     = require("path").dirname;

        /***** Initialization *****/

        var plugin = new Previewer("Ajax.org", main.consumes, {
            caption  : "Browser",
            index    : 10,
            divider  : true,
            selector : function(path){
                return path.match(/(?:\.html|\.htm|\.xhtml)$|^https?\:\/\//);
            }
        });

        var BASEPATH = preview.previewUrl;
        var counter  = 0;

        /***** Methods *****/

        function calcRootedPath(url){
            if (url.substr(0, BASEPATH.length) == BASEPATH)
                return url.substr(BASEPATH.length);
            return url;
        }

        function cleanIframeSrc(src){
            return src
                .replace(/_c9_id=\w+\&_c9_host=.*?(?:\&|$)/, "")
                .replace(/[\?\&]$/, "");
        }

        /***** Lifecycle *****/

        plugin.on("load", function(){
        });
        plugin.on("documentLoad", function(e){
            var doc     = e.doc;
            var session = doc.getSession();
            var tab     = doc.tab;
            var editor  = e.editor;

            if (session.iframe) {
                session.editor = editor;
                editor.container.appendChild(session.iframe);
                return;
            }

            var iframe = document.createElement("iframe");
            // iframe.setAttribute("nwfaketop", true);
            iframe.setAttribute("nwdisable", true);

            iframe.style.width  = "100%";
            iframe.style.height = "100%";
            iframe.style.border = 0;
            iframe.style.backgroundColor = "rgba(255, 255, 255, 0.88)";

            iframe.addEventListener("load", function(){
                if (!iframe.src) return;

                var path = calcRootedPath(cleanIframeSrc(iframe.src));

                tab.title   =
                tab.tooltip = "[B] " + path;
                session.lastSrc  = iframe.src;

                if (options.local) {
                    var url = cleanIframeSrc(iframe.contentWindow.location.href);
                    editor.setLocation(url);
                    session.currentLocation = url;
                }
                else {
                    editor.setLocation(path);
                }

                tab.className.remove("loading");
            });

            session.id        = "livepreview" + counter++;
            session.iframe    = iframe;
            session.editor    = editor;
            session.transport = new PostMessage(iframe, session.id);

            session.transport.on("ready", function(){
                session.transport.getSources(function(err, sources){
                    session.styleSheets = sources.styleSheets.map(function(path){
                        return new CSSDocument(path).addTransport(session.transport);
                    });
                    // session.scripts = sources.scripts.map(function(path){
                    //     return new JSDocument(path).addTransport(session.transport);
                    // });
                    session.html = new HTMLDocument(sources.html)
                        .addTransport(session.transport);
                });
            }, doc);
            session.transport.on("focus", function(){
                tabManager.focusTab(doc.tab);
            }, doc);

            session.destroy = function(){
                if (session.transport)
                    session.transport.unload();
                delete session.editor;
                delete session.transport;
                delete session.iframe;
                delete session.id;
            };

            // Lets only destroy when the doc is destroyed
            doc.addOther(function(){ session.destroy() });

            editor.container.appendChild(session.iframe);
        });
        plugin.on("documentUnload", function(e){
            var doc     = e.doc;
            var session = doc.getSession();
            var iframe  = session.iframe;

            iframe.parentNode.removeChild(iframe);

            doc.tab.className.remove("loading");
        });
        plugin.on("documentActivate", function(e){
            var session = e.doc.getSession();
            var path = calcRootedPath(cleanIframeSrc(session.iframe.src));

            session.iframe.style.display = "block";
            session.editor.setLocation(path, true);
            session.editor.setButtonStyle("Browser", "page_white.png");
        });
        plugin.on("documentDeactivate", function(e){
            var session = e.doc.getSession();
            session.iframe.style.display = "none";
        });
        plugin.on("navigate", function(e){
            var tab     = plugin.activeDocument.tab;
            var session = plugin.activeSession;
            var iframe  = session.iframe;
            var url = e.url.match(/^[a-z]\w{1,4}\:\/\//)
                ? e.url
                : BASEPATH + e.url;
            session.url = url;

            tab.className.add("loading");
            iframe.src = url + (~url.indexOf("?") ? "&" : "?")
                + "_c9_id=" + session.id
                + "&_c9_host=" + (options.local ? "local" : location.origin);

            var path = calcRootedPath(url);
            tab.title   =
            tab.tooltip = "[B] " + path;

            plugin.activeSession.editor.setLocation(path, true);
        });
        plugin.on("update", function(e){
            // var iframe = plugin.activeSession.iframe;
            // if (e.saved)
            //     iframe.src = iframe.src;
        });
        plugin.on("reload", function(){
            var iframe = plugin.activeSession.iframe;
            var tab    = plugin.activeDocument.tab;
            tab.className.add("loading");
            iframe.src = iframe.src;
        });
        plugin.on("popout", function(){
            var src = plugin.activeSession.iframe.src;
            window.open(src);
        });
        plugin.on("getState", function(e){
            var session = e.doc.getSession();
            var state   = e.state;

            state.currentLocation = session.currentLocation;
        });
        plugin.on("setState", function(e){
            var session = e.doc.getSession();

            if (e.state.currentLocation) {
                if (session.initPath)
                    session.initPath = e.state.currentLocation;
                // else
                //     plugin.navigate({ url: e.state.currentLocation, doc: e.doc });
            }
        });
        plugin.on("unload", function(){
        });

        /***** Register and define API *****/

        /**
         * Previewer for content that the browser can display natively.
         **/
        plugin.freezePublicAPI({

        });

        register(null, {
            "preview.browser": plugin
        });
    }
});<|endoftext|>


<commit_before>module.exports = (function() {

  'use strict';

  const url = require('url');
  const Template = require('./template.js');
  const Model = require('./model.js');
  const API = require('./api.js');

  /**
  * A new Controller instance is created by the Router when its associated route is hit, handles all business logic
  * @class
  */
  class Controller {

    /**
    * @param {http.ClientRequest} request Incoming HTTP Request
    * @param {http.ServerResponse} response Outgoing HTTP Response
    * @param {Object} params HTTP request parameters containing: location (route), query (GET), body (POST), and id values
    * @param {Nodal.Application} app Parent application object
    */
    constructor(request, response, params, app) {

      this._initializeTime = (new Date()).valueOf();
      this._request = request;
      this._response = response;
      this._path = url.parse(this._request.url, true).pathname;
      this._status = null;
      this._headers = {};

      Object.defineProperty(this, 'app', {enumerable: true, value: app});
      Object.defineProperty(this, 'params', {enumerable: true, value: params});

    }

    /**
    * Run the given method
    * @private
    */
    __run__(method) {

      this.app.middleware.exec(this, (err) => {

        if (err) {
          return this.error(err);
        }

        this[method](this, this.params, this.app);

      });

    }

    /**
    * Specifies CORS (cross origin resource sharing) headers.
    * @param {string} value Use '*' for a generic API service that accepts requests from anywhere, otherwise specific a domain.
    * @return {this}
    */
    allowOrigin(value) {

      this.setHeader('Access-Control-Allow-Origin', value);
      this.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
      this.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');

      return this;

    }

    /**
    * Intended to be overwritten when inherited. Used in AuthorizationController class.
    * @param {string} permissionName The permission name (and everything below) you'd like to allow.
    * @param {function({Error} err)} callback Function to execute upon authorization check.
    * @return {this}
    */
    authorize(permissionName, callback) {

      callback(null);
      return this;

    }

    /**
    * Return a copy of an object with only the specified fields allowed.
    * @experimental
    * @param {Object} obj The object to copy from
    * @param {Array} fields The keys you wish to keep
    * @return {Object}
    */
    filterParams(obj, fields) {

      let filterObj = {};

      fields
        .map(k => ((filterObj[k] = obj[k]), k))
        .filter(k => filterObj[k] === undefined)
        .forEach(k => delete filterObj[k]);

      return filterObj;

    }

    /**
    * Return the HTTP Request object
    * @return {http.ClientRequest}
    */
    request() {
      return this._request;
    }

    /**
    * The pathname used by the router to instantiate this controller
    * @return {string}
    */
    path() {
      return this._path;
    }

    /**
    * The current HTTP status code expected to be used by the outgoing http.ServerResponse
    * @return {number}
    */
    getStatus() {
      return this._status;
    }

    /**
    * Set the HTTP status code to be used by the outgoing http.ServerResponse
    * @param {number} value Outgoing HTTP status code
    * @return {number}
    */
    status(value) {
      this._status = value | 0;
      return this._status;
    }

    /**
    * Set HTTP headers to be used by the outgoing http.ServerResponse
    * @param {Object} object Object containing key-value pairs for HTTP headers
    * @return {Object} The headers object created
    */
    setHeaders(object) {
      let keys = Object.keys(object);
      let headers = {};
      for(let i = 0, len = keys.length; i < len; i++) {
        headers[keys[i]] = object[keys[i]];
      }
      this._headers = headers;
      return headers;
    }

    /**
    * Set HTTP header key-value pairs individually for the outgoing http.ServerResponse
    * @param {string} key HTTP header name
    * @param {string} value HTTP header value
    * @return {string} HTTP header value
    */
    setHeader(key, value) {

      key = key.split('-').map(function(v) {
        return v[0].toUpperCase() + v.substr(1);
      }).join('-');

      if (key === 'Content-Type' && value.indexOf(';') === -1 && (
        value === 'application/javascript' ||
        value === 'application/json' ||
        value.indexOf('text/') === 0
      )) {
        value = value + '; charset=utf-8';
      }

      this._headers[key] = value;
      return value;

    }

    /**
    * Return the value of an outgoing expected HTTP header
    * @param {string} key HTTP header value
    * @param {optional string} defaultValue The value to be returned if header is not set
    * @return {string}
    */
    getHeader(key, defaultValue) {
      return this._headers.hasOwnProperty(key) ? this._headers[key] : defaultValue;
    }

    /**
    * Creates a 302 redirect to the desired location
    * @param {string} location
    */
    redirect(location) {
      this.status(302);
      this.setHeader('Location', location);
      this.render();
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there was a Bad Request (400)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    badRequest(msg, details) {
      this.status(400);
      this.render(API.error(msg || 'Bad Request', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there was an Unauthorized request (401)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    unauthorized(msg, details) {
      this.status(401);
      this.render(API.error(msg || 'Unauthorized', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating the requested resource was Not Found (404)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    notFound(msg, details) {
      this.status(404);
      this.render(API.error(msg || 'Not Found', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there were Too Many Requests (429) (i.e. the client is being rate limited)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    tooManyRequests(msg, details) {
      this.status(429);
      this.render(API.error(msg || 'Too Many Requests', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there was an Internal Server Error (500)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    error(msg, details) {
      this.status(500);
      this.render(API.error(msg || 'Internal Server Error', details));
      return true;
    }

    /**
    * Using API formatting, generate an error or respond with model / object data.
    * @param {Error|Object|Array|Nodal.Model|Nodal.ModelArray} data Object to be formatted for API response
    * @param {optional Array} The interface to use for the data being returned, if not an error.
    * @param {Object} options Options object to send to Model#toObject
    * @return {boolean}
    */
    respond(data, arrInterface, options) {

      if (data instanceof Error) {

        if (data.notFound) {
          return this.notFound(data.message, data.details);
        }

        return this.badRequest(data.message, data.details);

      }

      this.render(API.format(data, arrInterface, options));
      return true;

    }

    /**
    * Send an http.ServerResponse back to the client with data to render
    * @param {string|Buffer|Object} data Data to be returned to the client. Buffers will be written as binary, Objects will be JSON-serialized, and strings will be output as-is.
    * @return {boolean}
    */
    render(data) {

      if (!data) { data = ''; }

      if (data instanceof Buffer) {

        this.getHeader('Content-Type') || this.setHeader('Content-Type', 'text/html');

      } else if (typeof data === 'object') {

        this.getHeader('Content-Type') || this.setHeader('Content-Type', 'application/json');

        try {
          data = JSON.stringify(data);
        } catch(e) {
          data = {};
        }

      } else {

        this.getHeader('Content-Type') || this.setHeader('Content-Type', 'text/html');
        data = data + '';

      }

      this.getStatus() || this.status(200);
      this.getHeader('X-Powered-By') || this.setHeader('X-Powered-By', 'Nodal');

      this.app.renderware.exec(this, data, (e, data) => {

        if (e) {
          this.setHeader('Content-Type', 'text/plain');
          this.status(500);
          this.write(e.message || 'Unresolved error');
          return;
        }

        if (data instanceof Buffer) {
          this.write(data, 'binary');
        } else {
          this.write(data);
        }

      });

      return true;

    }

    /**
    * Use Controller#render instead (which calls this method). Ends the http.ServerResponse by sending data.
    * @param {string|Buffer} data Response to send to client
    * @return {boolean}
    */
    write(data) {

      this._response.writeHead(this._status, this._headers);
      this._response.end(data);

      console.log(this._request.url + ' loaded in: ' + ((new Date()).valueOf() - this._initializeTime) + 'ms');

      return true;

    }

    /**
    * Method called when a route is hit with a GET request and no "id" parameter. Intended to be overwritten when inherited. Defaults to calling Controller#get.
    */
    index() {
      this.get.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a GET request and an "id" parameter. Intended to be overwritten when inherited. Defaults to calling Controller#get.
    */
    show() {
      this.get.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a POST request. Intended to be overwritten when inherited. Defaults to calling Controller#post.
    */
    create() {
      this.post.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a PUT request. Intended to be overwritten when inherited. Defaults to calling Controller#put.
    */
    update() {
      this.put.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a DELETE request. Intended to be overwritten when inherited. Defaults to calling Controller#post.
    */
    destroy() {
      this.del.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a GET request, if not first intercepted by custom Controller#index or Controller#show methods. Intended to be overwritten when inherited.
    */
    get() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with a PUT request, if not first intercepted by custom Controller#update method. Intended to be overwritten when inherited.
    */
    put() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with a POST request, if not first intercepted by custom Controller#create method. Intended to be overwritten when inherited.
    */
    post() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with a DELETE request, if not first intercepted by custom Controller#destroy method. Intended to be overwritten when inherited.
    */
    del() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with an OPTIONS request. Typically unused, exists for CORS purposes.
    */
    options() {
      this.status(200);
      this.render();
    }

  }

  return Controller;

})();
<commit_msg>Simplified logic in filter fields<commit_after>module.exports = (function() {

  'use strict';

  const url = require('url');
  const Template = require('./template.js');
  const Model = require('./model.js');
  const API = require('./api.js');

  /**
  * A new Controller instance is created by the Router when its associated route is hit, handles all business logic
  * @class
  */
  class Controller {

    /**
    * @param {http.ClientRequest} request Incoming HTTP Request
    * @param {http.ServerResponse} response Outgoing HTTP Response
    * @param {Object} params HTTP request parameters containing: location (route), query (GET), body (POST), and id values
    * @param {Nodal.Application} app Parent application object
    */
    constructor(request, response, params, app) {

      this._initializeTime = (new Date()).valueOf();
      this._request = request;
      this._response = response;
      this._path = url.parse(this._request.url, true).pathname;
      this._status = null;
      this._headers = {};

      Object.defineProperty(this, 'app', {enumerable: true, value: app});
      Object.defineProperty(this, 'params', {enumerable: true, value: params});

    }

    /**
    * Run the given method
    * @private
    */
    __run__(method) {

      this.app.middleware.exec(this, (err) => {

        if (err) {
          return this.error(err);
        }

        this[method](this, this.params, this.app);

      });

    }

    /**
    * Specifies CORS (cross origin resource sharing) headers.
    * @param {string} value Use '*' for a generic API service that accepts requests from anywhere, otherwise specific a domain.
    * @return {this}
    */
    allowOrigin(value) {

      this.setHeader('Access-Control-Allow-Origin', value);
      this.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
      this.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');

      return this;

    }

    /**
    * Intended to be overwritten when inherited. Used in AuthorizationController class.
    * @param {string} permissionName The permission name (and everything below) you'd like to allow.
    * @param {function({Error} err)} callback Function to execute upon authorization check.
    * @return {this}
    */
    authorize(permissionName, callback) {

      callback(null);
      return this;

    }

    /**
    * Return a copy of an object with only the specified fields allowed.
    * @experimental
    * @param {Object} obj The object to copy from
    * @param {Array} fields The keys you wish to keep
    * @return {Object}
    */
    filterParams(obj, fields) {

      return fields
        .filter(f => obj[f] !== undefined)
        .reduce((prev, f) => {
          prev[f] = obj[f];
          return prev;
        }, {});
    }

    /**
    * Return the HTTP Request object
    * @return {http.ClientRequest}
    */
    request() {
      return this._request;
    }

    /**
    * The pathname used by the router to instantiate this controller
    * @return {string}
    */
    path() {
      return this._path;
    }

    /**
    * The current HTTP status code expected to be used by the outgoing http.ServerResponse
    * @return {number}
    */
    getStatus() {
      return this._status;
    }

    /**
    * Set the HTTP status code to be used by the outgoing http.ServerResponse
    * @param {number} value Outgoing HTTP status code
    * @return {number}
    */
    status(value) {
      this._status = value | 0;
      return this._status;
    }

    /**
    * Set HTTP headers to be used by the outgoing http.ServerResponse
    * @param {Object} object Object containing key-value pairs for HTTP headers
    * @return {Object} The headers object created
    */
    setHeaders(object) {
      let keys = Object.keys(object);
      let headers = {};
      for(let i = 0, len = keys.length; i < len; i++) {
        headers[keys[i]] = object[keys[i]];
      }
      this._headers = headers;
      return headers;
    }

    /**
    * Set HTTP header key-value pairs individually for the outgoing http.ServerResponse
    * @param {string} key HTTP header name
    * @param {string} value HTTP header value
    * @return {string} HTTP header value
    */
    setHeader(key, value) {

      key = key.split('-').map(function(v) {
        return v[0].toUpperCase() + v.substr(1);
      }).join('-');

      if (key === 'Content-Type' && value.indexOf(';') === -1 && (
        value === 'application/javascript' ||
        value === 'application/json' ||
        value.indexOf('text/') === 0
      )) {
        value = value + '; charset=utf-8';
      }

      this._headers[key] = value;
      return value;

    }

    /**
    * Return the value of an outgoing expected HTTP header
    * @param {string} key HTTP header value
    * @param {optional string} defaultValue The value to be returned if header is not set
    * @return {string}
    */
    getHeader(key, defaultValue) {
      return this._headers.hasOwnProperty(key) ? this._headers[key] : defaultValue;
    }

    /**
    * Creates a 302 redirect to the desired location
    * @param {string} location
    */
    redirect(location) {
      this.status(302);
      this.setHeader('Location', location);
      this.render();
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there was a Bad Request (400)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    badRequest(msg, details) {
      this.status(400);
      this.render(API.error(msg || 'Bad Request', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there was an Unauthorized request (401)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    unauthorized(msg, details) {
      this.status(401);
      this.render(API.error(msg || 'Unauthorized', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating the requested resource was Not Found (404)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    notFound(msg, details) {
      this.status(404);
      this.render(API.error(msg || 'Not Found', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there were Too Many Requests (429) (i.e. the client is being rate limited)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    tooManyRequests(msg, details) {
      this.status(429);
      this.render(API.error(msg || 'Too Many Requests', details));
      return true;
    }

    /**
    * Using API formatting, send an http.ServerResponse indicating there was an Internal Server Error (500)
    * @param {string} msg Error message to send
    * @param {Object} details Any additional details for the error (must be serializable)
    * @return {boolean}
    */
    error(msg, details) {
      this.status(500);
      this.render(API.error(msg || 'Internal Server Error', details));
      return true;
    }

    /**
    * Using API formatting, generate an error or respond with model / object data.
    * @param {Error|Object|Array|Nodal.Model|Nodal.ModelArray} data Object to be formatted for API response
    * @param {optional Array} The interface to use for the data being returned, if not an error.
    * @param {Object} options Options object to send to Model#toObject
    * @return {boolean}
    */
    respond(data, arrInterface, options) {

      if (data instanceof Error) {

        if (data.notFound) {
          return this.notFound(data.message, data.details);
        }

        return this.badRequest(data.message, data.details);

      }

      this.render(API.format(data, arrInterface, options));
      return true;

    }

    /**
    * Send an http.ServerResponse back to the client with data to render
    * @param {string|Buffer|Object} data Data to be returned to the client. Buffers will be written as binary, Objects will be JSON-serialized, and strings will be output as-is.
    * @return {boolean}
    */
    render(data) {

      if (!data) { data = ''; }

      if (data instanceof Buffer) {

        this.getHeader('Content-Type') || this.setHeader('Content-Type', 'text/html');

      } else if (typeof data === 'object') {

        this.getHeader('Content-Type') || this.setHeader('Content-Type', 'application/json');

        try {
          data = JSON.stringify(data);
        } catch(e) {
          data = {};
        }

      } else {

        this.getHeader('Content-Type') || this.setHeader('Content-Type', 'text/html');
        data = data + '';

      }

      this.getStatus() || this.status(200);
      this.getHeader('X-Powered-By') || this.setHeader('X-Powered-By', 'Nodal');

      this.app.renderware.exec(this, data, (e, data) => {

        if (e) {
          this.setHeader('Content-Type', 'text/plain');
          this.status(500);
          this.write(e.message || 'Unresolved error');
          return;
        }

        if (data instanceof Buffer) {
          this.write(data, 'binary');
        } else {
          this.write(data);
        }

      });

      return true;

    }

    /**
    * Use Controller#render instead (which calls this method). Ends the http.ServerResponse by sending data.
    * @param {string|Buffer} data Response to send to client
    * @return {boolean}
    */
    write(data) {

      this._response.writeHead(this._status, this._headers);
      this._response.end(data);

      console.log(this._request.url + ' loaded in: ' + ((new Date()).valueOf() - this._initializeTime) + 'ms');

      return true;

    }

    /**
    * Method called when a route is hit with a GET request and no "id" parameter. Intended to be overwritten when inherited. Defaults to calling Controller#get.
    */
    index() {
      this.get.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a GET request and an "id" parameter. Intended to be overwritten when inherited. Defaults to calling Controller#get.
    */
    show() {
      this.get.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a POST request. Intended to be overwritten when inherited. Defaults to calling Controller#post.
    */
    create() {
      this.post.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a PUT request. Intended to be overwritten when inherited. Defaults to calling Controller#put.
    */
    update() {
      this.put.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a DELETE request. Intended to be overwritten when inherited. Defaults to calling Controller#post.
    */
    destroy() {
      this.del.apply(this, arguments);
    }

    /**
    * Method called when a route is hit with a GET request, if not first intercepted by custom Controller#index or Controller#show methods. Intended to be overwritten when inherited.
    */
    get() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with a PUT request, if not first intercepted by custom Controller#update method. Intended to be overwritten when inherited.
    */
    put() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with a POST request, if not first intercepted by custom Controller#create method. Intended to be overwritten when inherited.
    */
    post() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with a DELETE request, if not first intercepted by custom Controller#destroy method. Intended to be overwritten when inherited.
    */
    del() {
      this.status(501);
      this.setHeader('Content-Type', 'text/plain');
      this.render('501 - Not Implemented');
    }

    /**
    * Method called when a route is hit with an OPTIONS request. Typically unused, exists for CORS purposes.
    */
    options() {
      this.status(200);
      this.render();
    }

  }

  return Controller;

})();
<|endoftext|>


<commit_before><commit_msg>Remove mistakenly added license header<commit_after><|endoftext|>