<commit_before>const EventEmitter = require('events');

const moment = require('moment');
const CouchbaseChannelDisney = require('./couchbaseChannelDisney');
const Facilities = require('./disneyFacilityChannel');
const DebugLog = require('../debugPrint.js');
const Cache = require('../cache');

const DisneyUtil = require('./disneyUtil');

const sParkID = Symbol('Park ID');
const sChannel = Symbol('Channel');
const sFacilities = Symbol('Facilities');
const sBusVersion = Symbol('Bus Version');
const sPollInterval = Symbol('Polling Interval');
const sResorts = Symbol('Specific resorts to sync');
const sInitialiseWithOldData = Symbol('Broadcast old data when starting channel');

class DisneyLiveBusTimes extends EventEmitter {
  constructor(options = {}) {
    super();

    this[sParkID] = options.parkId === undefined ? 'wdw' : options.parkId;
    this[sBusVersion] = options.version === undefined ? '1_0' : options.version;

    // how often to poll for bus time updates (in seconds)
    this[sPollInterval] = options.pollinterval === undefined ? 10 : options.pollinterval;

    // setup our facilities channel, we need this to sync before we get our bus times
    this[sFacilities] = new Facilities({
      resortId: this[sParkID],
    });

    this[sResorts] = options.resorts || undefined;

    this[sInitialiseWithOldData] = !!options.initWithOldData;

    // NOTE - whilst there is a live channel for "Outpost Depot at Disney's Fort Wilderness Resort & Campground"
    //  our list of resorts doesn't return the ID 144943 needed to subscribe
    //  However, channel 80010408 has exactly the same data, so we're getting it via the "The Cabins at Disney's Fort Wilderness Resort" resort instead
  }

  /**
   * Callback for "updated" event from our couchbase channel class
   * @param {Object} doc
   */
  OnDocUpdated(doc) {
    // console.log(JSON.stringify(doc, null, 2));
    const id = DisneyUtil.CleanID(doc.id);
    const {
      lastUpdate,
    } = doc;

    this[sFacilities].GetFacilityName(id).then((name) => {
      if (doc.destinations === undefined) {
        return;
      }

      // add human-readable times and names to each destination
      const destinationPromises = [];
      doc.destinations.forEach((dest) => {
        destinationPromises.push(this.ParseDestination(dest));
      });
      Promise.all(destinationPromises).then((destinations) => {
        // emit events for each updated bus stop
        // console.log(JSON.stringify(destinations, null, 2));

        destinations.forEach((dest) => {
          if (dest.arrivals !== undefined) {
            dest.arrivals.forEach((arrival) => {
              this.emit('busupdate', {
                lastUpdate,
                from: name,
                from_id: Number(id),
                to: dest.name,
                to_id: Number(DisneyUtil.CleanID(dest.id)),
                atStop: arrival.atStop,
                atStopHuman: arrival.atStop_human,
                atDestination: arrival.atDestination,
                atDestinationHuman: arrival.atDestination_human,
                frequency: dest.frequency,
                frequencyHuman: dest.frequency_human,
                transfers: dest.transfers,
                transfersHuman: dest.transfers_human,
              });
            });
          } else {
            // emit event with boring "every 20 minutes" update
            this.emit('busupdate', {
              lastUpdate,
              from: name,
              from_id: Number(id),
              to: dest.name,
              to_id: Number(DisneyUtil.CleanID(dest.id)),
              frequency: dest.frequency,
              frequencyHuman: dest.frequency_human,
              transfers: dest.transfers,
              transfersHuman: dest.transfers_human,
            });
          }
        });
      });
    });
  }

  ParseDestination(destination) {
    return this[sFacilities].GetFacilityName(destination.id).then((name) => {
      // remove superfluous "Disney's" and "Theme Park" from destination names
      destination.name = name.replace(/\s?(?:Theme|Water)?\sPark$/, '').replace(/^Disney's /, '');

      // use moment to make some human-readable times
      const now = moment();

      if (destination.frequency !== undefined) {
        const busFrequency = now.clone().add(destination.frequency, 'seconds');
        destination.frequency_human = `every ${busFrequency.from(now, true)}`;
      }

      // add human-readable strings to each arrival
      if (destination.arrivals !== undefined) {
        destination.arrivals.forEach((arrival) => {
          arrival.atStop_human = moment(arrival.atStop).from(now);
          arrival.atDestination_human = moment(arrival.atDestination).from(now);
        });
      }

      // resolve transfer destination names if they exist
      if (destination.transfers && destination.transfers.length > 0) {
        return Promise.all(destination.transfers.map(x => this.ParseDestination(x))).then((transfers) => {
          destination.transfers = [];
          transfers.forEach((t) => {
            destination.transfers.push(t.name);
          });

          destination.transfers_human = `Transfer at ${(destination.transfers.length > 1 ? destination.transfers.join(', ') : destination.transfers[0])}`;

          return Promise.resolve(destination);
        });
      }

      return Promise.resolve(destination);
    });
  }

  GetCurrentBusState() {
    if (!this[sInitialiseWithOldData]) {
      return Promise.resolve();
    }

    return Cache.DB().then(db => new Promise((resolve, reject) => {
      db.all('SELECT body FROM couchbasesync WHERE dbName = ?', [`${this[sParkID]}_buses`], (err, rows) => {
        if (err) {
          return reject(new Error(`Fetching current bus state: ${err}`));
        }

        // process existing couchbase docs as if they were new
        rows.map(x => JSON.parse(x.body)).forEach(this.OnDocUpdated.bind(this));

        return resolve();
      });
    }));
  }

  /**
   * Start fetching live bus time updates
   */
  Start() {
    if (!this[sFacilities].Synced) {
      // start facilities syncing,
      return this[sFacilities].Start().then(() => {
        if (this[sChannel] === undefined) {
          // set this to something in case multiple callbacks arrive before we set it up
          this[sChannel] = 1;

          // find all our resorts and build channel names
          return this.GetCurrentBusState().then(() => this.GetChannelNames().then((channels) => {
            // create our channel for fetching bus times
            this[sChannel] = new CouchbaseChannelDisney({
              dbName: `${this[sParkID]}_buses`,
              channel: channels.join(','),
              longpollDelay: this[sPollInterval], // delay each longpoll by 10 seconds (to avoid constant flooding of updates)
            });

            // setup puller hooks for document updates
            this[sChannel].on('updated', this.OnDocUpdated.bind(this));
            this[sChannel].on('error', this.Log.bind(this));

            // start the channel fetching documents
            this[sChannel].Start();
          }));
        }

        return Promise.resolve();
      });
    }

    return Promise.resolve();
  }

  GetChannelNames() {
    if (this[sResorts] !== undefined) {
      return Promise.resolve([].concat(this[sResorts]).map(x => `${this[sParkID]}.arrivals.${x};entityType=resort.${this[sBusVersion]}`));
    }

    return Cache.DB().then(db => new Promise((resolve, reject) => {
      db.all('SELECT id, name FROM disneyFacilities WHERE entityType = ? AND resort_code = ?', ['resort', this[sParkID]], (err, rows) => {
        if (err) {
          return reject(err);
        }

        const channels = [];
        rows.forEach((row) => {
          channels.push(`${this[sParkID]}.arrivals.${row.id};entityType=resort.${this[sBusVersion]}`);
        });

        return resolve(channels);
      });
    }));
  }

  /**
   * Debug print a message (when NODE_DEBUG=themeparks is set in environment)
   * @param {...*} ToPrint Objects/strings to print
   * */
  Log(...args) {
    return DebugLog.apply(null, [`${this.constructor.name}:`, ...args]);
  }
}

module.exports = DisneyLiveBusTimes;
<commit_msg>Add alternative services to bus times<commit_after>const EventEmitter = require('events');

const moment = require('moment');
const CouchbaseChannelDisney = require('./couchbaseChannelDisney');
const Facilities = require('./disneyFacilityChannel');
const DebugLog = require('../debugPrint.js');
const Cache = require('../cache');

const DisneyUtil = require('./disneyUtil');

const sParkID = Symbol('Park ID');
const sChannel = Symbol('Channel');
const sFacilities = Symbol('Facilities');
const sBusVersion = Symbol('Bus Version');
const sPollInterval = Symbol('Polling Interval');
const sResorts = Symbol('Specific resorts to sync');
const sInitialiseWithOldData = Symbol('Broadcast old data when starting channel');

class DisneyLiveBusTimes extends EventEmitter {
  constructor(options = {}) {
    super();

    this[sParkID] = options.parkId === undefined ? 'wdw' : options.parkId;
    this[sBusVersion] = options.version === undefined ? '1_0' : options.version;

    // how often to poll for bus time updates (in seconds)
    this[sPollInterval] = options.pollinterval === undefined ? 10 : options.pollinterval;

    // setup our facilities channel, we need this to sync before we get our bus times
    this[sFacilities] = new Facilities({
      resortId: this[sParkID],
    });

    this[sResorts] = options.resorts || undefined;

    this[sInitialiseWithOldData] = !!options.initWithOldData;

    // NOTE - whilst there is a live channel for "Outpost Depot at Disney's Fort Wilderness Resort & Campground"
    //  our list of resorts doesn't return the ID 144943 needed to subscribe
    //  However, channel 80010408 has exactly the same data, so we're getting it via the "The Cabins at Disney's Fort Wilderness Resort" resort instead
  }

  /**
   * Callback for "updated" event from our couchbase channel class
   * @param {Object} doc
   */
  OnDocUpdated(doc) {
    // console.log(JSON.stringify(doc, null, 2));
    const id = DisneyUtil.CleanID(doc.id);
    const {
      lastUpdate,
    } = doc;

    this[sFacilities].GetFacilityName(id).then((name) => {
      if (doc.destinations === undefined) {
        return;
      }

      // add human-readable times and names to each destination
      const destinationPromises = [];
      doc.destinations.forEach((dest) => {
        destinationPromises.push(this.ParseDestination(dest));
      });
      Promise.all(destinationPromises).then((destinations) => {
        // emit events for each updated bus stop
        // console.log(JSON.stringify(destinations, null, 2));

        destinations.forEach((dest) => {
          if (dest.arrivals !== undefined) {
            dest.arrivals.forEach((arrival) => {
              this.emit('busupdate', {
                lastUpdate,
                from: name,
                from_id: Number(id),
                to: dest.name,
                to_id: Number(DisneyUtil.CleanID(dest.id)),
                atStop: arrival.atStop,
                atStopHuman: arrival.atStop_human,
                atDestination: arrival.atDestination,
                atDestinationHuman: arrival.atDestination_human,
                frequency: dest.frequency,
                frequencyHuman: dest.frequency_human,
                transfers: dest.transfers,
                transfersHuman: dest.transfers_human,
                alternativeServices: dest.services || [],
              });
            });
          } else {
            // emit event with boring "every 20 minutes" update
            this.emit('busupdate', {
              lastUpdate,
              from: name,
              from_id: Number(id),
              to: dest.name,
              to_id: Number(DisneyUtil.CleanID(dest.id)),
              frequency: dest.frequency,
              frequencyHuman: dest.frequency_human,
              transfers: dest.transfers,
              transfersHuman: dest.transfers_human,
              alternativeServices: dest.services || [],
            });
          }
        });
      });
    });
  }

  ParseDestination(destination) {
    return this[sFacilities].GetFacilityName(destination.id).then((name) => {
      // remove superfluous "Disney's" and "Theme Park" from destination names
      destination.name = name.replace(/\s?(?:Theme|Water)?\sPark$/, '').replace(/^Disney's /, '');

      // use moment to make some human-readable times
      const now = moment();

      if (destination.frequency !== undefined) {
        const busFrequency = now.clone().add(destination.frequency, 'seconds');
        destination.frequency_human = `every ${busFrequency.from(now, true)}`;
      }

      // add human-readable strings to each arrival
      if (destination.arrivals !== undefined) {
        destination.arrivals.forEach((arrival) => {
          arrival.atStop_human = moment(arrival.atStop).from(now);
          arrival.atDestination_human = moment(arrival.atDestination).from(now);
        });
      }

      // resolve transfer destination names if they exist
      if (destination.transfers && destination.transfers.length > 0) {
        return Promise.all(destination.transfers.map(x => this.ParseDestination(x))).then((transfers) => {
          destination.transfers = [];
          transfers.forEach((t) => {
            destination.transfers.push(t.name);
          });

          destination.transfers_human = `Transfer at ${(destination.transfers.length > 1 ? destination.transfers.join(', ') : destination.transfers[0])}`;

          return Promise.resolve(destination);
        });
      }

      return Promise.resolve(destination);
    });
  }

  GetCurrentBusState() {
    if (!this[sInitialiseWithOldData]) {
      return Promise.resolve();
    }

    return Cache.DB().then(db => new Promise((resolve, reject) => {
      db.all('SELECT body FROM couchbasesync WHERE dbName = ?', [`${this[sParkID]}_buses`], (err, rows) => {
        if (err) {
          return reject(new Error(`Fetching current bus state: ${err}`));
        }

        // process existing couchbase docs as if they were new
        rows.map(x => JSON.parse(x.body)).forEach(this.OnDocUpdated.bind(this));

        return resolve();
      });
    }));
  }

  /**
   * Start fetching live bus time updates
   */
  Start() {
    if (!this[sFacilities].Synced) {
      // start facilities syncing,
      return this[sFacilities].Start().then(() => {
        if (this[sChannel] === undefined) {
          // set this to something in case multiple callbacks arrive before we set it up
          this[sChannel] = 1;

          // find all our resorts and build channel names
          return this.GetCurrentBusState().then(() => this.GetChannelNames().then((channels) => {
            // create our channel for fetching bus times
            this[sChannel] = new CouchbaseChannelDisney({
              dbName: `${this[sParkID]}_buses`,
              channel: channels.join(','),
              longpollDelay: this[sPollInterval], // delay each longpoll by 10 seconds (to avoid constant flooding of updates)
            });

            // setup puller hooks for document updates
            this[sChannel].on('updated', this.OnDocUpdated.bind(this));
            this[sChannel].on('error', this.Log.bind(this));

            // start the channel fetching documents
            this[sChannel].Start();
          }));
        }

        return Promise.resolve();
      });
    }

    return Promise.resolve();
  }

  GetChannelNames() {
    if (this[sResorts] !== undefined) {
      return Promise.resolve([].concat(this[sResorts]).map(x => `${this[sParkID]}.arrivals.${x};entityType=resort.${this[sBusVersion]}`));
    }

    return Cache.DB().then(db => new Promise((resolve, reject) => {
      db.all('SELECT id, name FROM disneyFacilities WHERE entityType = ? AND resort_code = ?', ['resort', this[sParkID]], (err, rows) => {
        if (err) {
          return reject(err);
        }

        const channels = [];
        rows.forEach((row) => {
          channels.push(`${this[sParkID]}.arrivals.${row.id};entityType=resort.${this[sBusVersion]}`);
        });

        return resolve(channels);
      });
    }));
  }

  /**
   * Debug print a message (when NODE_DEBUG=themeparks is set in environment)
   * @param {...*} ToPrint Objects/strings to print
   * */
  Log(...args) {
    return DebugLog.apply(null, [`${this.constructor.name}:`, ...args]);
  }
}

module.exports = DisneyLiveBusTimes;
<|endoftext|>


<commit_before>(function() {

var SpatialListener = E2.plugins.audio_spatial_listener = function(core, node)
{
	this.desc = 'Spatial Listener.';

	this.input_slots = [
		{ name: 'position', dt: core.datatypes.VECTOR, desc: 'Position.', def: core.renderer.vector_origin },
		{ name: 'forward', dt: core.datatypes.VECTOR, desc: 'Forward Vector.', def: core.renderer.vector_origin },
		{ name: 'up', dt: core.datatypes.VECTOR, desc: 'Up Vector.', def: core.renderer.vector_origin }
	];

	this.output_slots = [];

	this.position = null;
	this.forward = null;
	this.up = null;

	this.prev_position = new THREE.Vector3();
	this.prev_forward = new THREE.Vector3();
	this.prev_up = new THREE.Vector3();

	this.audioContext = core.audioContext;
	this.first = true;
}

SpatialListener.prototype.reset = function()
{
	this.first = true;
}

SpatialListener.prototype.update_input = function(slot, data)
{
	switch( slot.index )
	{
		case 0: this.position = data;	break;
		case 1:	this.forward = data;	break;
		case 2:	this.up = data;			break;
	}
}

SpatialListener.prototype.update_state = function(uc)
{
	function ramp(param,v0,v1,t)
	{
//		if( v0 != v1 )
			param = v1;
//			param.linearRampToValueAtTime(v0,t);
	}

	var t = this.audioContext.currentTime + this.first ? 0 : uc.delta_t;
	var listener = this.audioContext.listener;

	if( this.position !== null )
	{
		listener.setPosition(this.position.x,this.position.y,this.position.z);
/*		ramp( listener.positionX, this.position.x, this.prev_position.x, t );
		ramp( listener.positionY, this.position.y, this.prev_position.y, t );
		ramp( listener.positionZ, this.position.z, this.prev_position.z, t );*/

		this.prev_position.copy( this.position );
	}

	if( this.forward !== null && this.up !== null)
	{
		listener.setOrientation(
			this.forward.x,this.forward.y,this.forward.z,
			this.up.x,this.up.y,this.up.z
		);
/*		ramp( listener.forwardX, this.forward.x, this.prev_forward.x, t );
		ramp( listener.forwardY, this.forward.y, this.prev_forward.y, t );
		ramp( listener.forwardZ, this.forward.z, this.prev_forward.z, t );

		this.prev_forward.copy( this.forward );
	}

	if( this.up !== null )
	{
		ramp( listener.upX, this.up.x, this.prev_up.x, t );
		ramp( listener.upY, this.up.y, this.prev_up.y, t );
		ramp( listener.upZ, this.up.z, this.prev_up.z, t );

		this.prev_up.copy( this.up );*/
	}

	this.first = false;
}

SpatialListener.prototype.update_output = function(slot)
{
	this.updated = true;
}

})();
<commit_msg>Cleanup<commit_after>(function() {

var SpatialListener = E2.plugins.audio_spatial_listener = function(core, node)
{
	this.desc = 'Spatial Listener.';

	this.input_slots = [
		{ name: 'position', dt: core.datatypes.VECTOR, desc: 'Position.', def: core.renderer.vector_origin },
		{ name: 'forward', dt: core.datatypes.VECTOR, desc: 'Forward Vector.', def: core.renderer.vector_origin },
		{ name: 'up', dt: core.datatypes.VECTOR, desc: 'Up Vector.', def: new THREE.Vector3(0,1,0) /* whatever */ }
	];

	this.output_slots = [];

	this.position = null;
	this.forward = null;
	this.up = null;

	this.audioContext = core.audioContext;
	this.first = true;
}

SpatialListener.prototype.reset = function()
{
	this.first = true;
}

SpatialListener.prototype.update_input = function(slot, data)
{
	switch( slot.index ) {
		case 0: this.position = data;	break;
		case 1:	this.forward = data;	break;
		case 2:	this.up = data;			break;
	}
}

SpatialListener.prototype.update_state = function(uc)
{
	var listener = this.audioContext.listener;

	if( this.position !== null ) {
		listener.setPosition(this.position.x,this.position.y,this.position.z);
	}

	if( this.forward !== null && this.up !== null) {
		listener.setOrientation(
			this.forward.x,this.forward.y,this.forward.z,
			this.up.x,this.up.y,this.up.z
		);
	}

	this.first = false;
}

SpatialListener.prototype.update_output = function(slot)
{
	this.updated = true;
}

})();
<|endoftext|>


<commit_before>import React, { PureComponent as Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import axios from 'axios';
import { Table, Button, Modal, message, Tooltip, Select, Icon } from 'antd';
import AddInterfaceForm from './AddInterfaceForm';
import {
  fetchInterfaceListMenu,
  fetchInterfaceList,
  fetchInterfaceCatList
} from '../../../../reducer/modules/interface.js';
import { getProject } from '../../../../reducer/modules/project.js';
import { Link } from 'react-router-dom';
import variable from '../../../../constants/variable';
import './Edit.scss';
import Label from '../../../../components/Label/Label.js';

const Option = Select.Option;
const limit = 20;

@connect(
  state => {
    return {
      curData: state.inter.curdata,
      curProject: state.project.currProject,
      catList: state.inter.list,
      totalTableList: state.inter.totalTableList,
      catTableList: state.inter.catTableList,
      totalCount: state.inter.totalCount,
      count: state.inter.count
    };
  },
  {
    fetchInterfaceListMenu,
    fetchInterfaceList,
    fetchInterfaceCatList,
    getProject
  }
)
class InterfaceList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      visible: false,
      data: [],
      catid: null,
      total: null,
      current: 1
    };
  }

  static propTypes = {
    curData: PropTypes.object,
    catList: PropTypes.array,
    match: PropTypes.object,
    curProject: PropTypes.object,
    history: PropTypes.object,
    fetchInterfaceListMenu: PropTypes.func,
    fetchInterfaceList: PropTypes.func,
    fetchInterfaceCatList: PropTypes.func,
    totalTableList: PropTypes.array,
    catTableList: PropTypes.array,
    totalCount: PropTypes.number,
    count: PropTypes.number,
    getProject: PropTypes.func
  };

  handleRequest = async props => {
    const { params } = props.match;
    if (!params.actionId) {
      let projectId = params.id;
      this.setState({
        catid: null
      });
      let option = {
        page: this.state.current,
        limit,
        project_id: projectId
      };
      await this.props.fetchInterfaceList(option);
    } else if (isNaN(params.actionId)) {
      let catid = params.actionId.substr(4);
      this.setState({ catid: +catid });
      let option = {
        page: this.state.current,
        limit,
        catid
      };

      await this.props.fetchInterfaceCatList(option);
    }
  };

  // 更新分类简介
  handleChangeInterfaceCat = (desc, name) => {
    let params = {
      catid: this.state.catid,
      name: name,
      desc: desc
    };

    axios.post('/api/interface/up_cat', params).then(async res => {
      if (res.data.errcode !== 0) {
        return message.error(res.data.errmsg);
      }
      let project_id = this.props.match.params.id;
      await this.props.getProject(project_id);
      await this.props.fetchInterfaceListMenu(project_id);
      message.success('接口集合简介更新成功');
    });
  };
  handleChange = (pagination, filters, sorter) => {
    this.setState({
      sortedInfo: sorter
    });
  };

  componentWillMount() {
    this.actionId = this.props.match.params.actionId;
    this.handleRequest(this.props);
  }

  componentWillReceiveProps(nextProps) {
    let _actionId = nextProps.match.params.actionId;

    if (this.actionId !== _actionId) {
      this.actionId = _actionId;
      this.setState(
        {
          current: 1
        },
        () => this.handleRequest(nextProps)
      );
    }
  }

  handleAddInterface = data => {
    data.project_id = this.props.curProject._id;
    axios.post('/api/interface/add', data).then(res => {
      if (res.data.errcode !== 0) {
        return message.error(`${res.data.errmsg}, 你可以在左侧的接口列表中对接口进行删改`);
      }
      message.success('接口添加成功');
      let interfaceId = res.data.data._id;
      this.props.history.push('/project/' + data.project_id + '/interface/api/' + interfaceId);
      this.props.fetchInterfaceListMenu(data.project_id);
    });
  };

  changeInterfaceCat = async (id, catid) => {
    const params = {
      id: id,
      catid
    };
    let result = await axios.post('/api/interface/up', params);
    if (result.data.errcode === 0) {
      message.success('修改成功');
      this.handleRequest(this.props);
      this.props.fetchInterfaceListMenu(this.props.curProject._id);
    } else {
      message.error(result.data.errmsg);
    }
  };

  changeInterfaceStatus = async value => {
    const params = {
      id: value.split('-')[0],
      status: value.split('-')[1]
    };
    let result = await axios.post('/api/interface/up', params);
    if (result.data.errcode === 0) {
      message.success('修改成功');
      this.handleRequest(this.props);
    } else {
      message.error(result.data.errmsg);
    }
  };

  changePage = current => {
    this.setState(
      {
        current: current
      },
      () => this.handleRequest(this.props)
    );
  };

  render() {
    let tag = this.props.curProject.tag;
    let filter = tag.map(item => {
      return { text: item.name, value: item.name };
    });

    const columns = [
      {
        title: '接口名称',
        dataIndex: 'title',
        key: 'title',
        width: 30,
        render: (text, item) => {
          return (
            <Link to={'/project/' + item.project_id + '/interface/api/' + item._id}>
              {text}
            </Link>
          );
        }
      },
      {
        title: '接口路径',
        dataIndex: 'path',
        key: 'path',
        width: 50,
        render: (item, record) => {
          const path = this.props.curProject.basepath + item;
          let methodColor =
            variable.METHOD_COLOR[record.method ? record.method.toLowerCase() : 'get'] ||
            variable.METHOD_COLOR['get'];
          return (

                {record.method}

              <Tooltip title="开放接口" placement="topLeft">
                {record.api_opened && <Icon className="opened" type="eye-o" />}
              </Tooltip>
              <Tooltip title={path} placement="topLeft" overlayClassName="toolTip">
                {path}
              </Tooltip>

          );
        }
      },
      {
        title: '接口分类',
        dataIndex: 'catid',
        key: 'catid',
        width: 28,
        render: (item, record) => {
          return (
            <Select
              value={item + ''}
              className="select path"
              onChange={catid => this.changeInterfaceCat(record._id, catid)}
            >
              {this.props.catList.map(cat => {
                return (
                  <Option key={cat.id + ''} value={cat._id + ''}>
                    {cat.name}
                  </Option>
                );
              })}
            </Select>
          );
        }
      },
      {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        width: 24,
        render: (text, record) => {
          const key = record.key;
          return (
            <Select
              value={key + '-' + text}
              className="select"
              onChange={this.changeInterfaceStatus}
            >
              <Option value={key + '-done'}>
                已完成
              </Option>
              <Option value={key + '-undone'}>
                未完成
              </Option>
            </Select>
          );
        },
        filters: [
          {
            text: '已完成',
            value: 'done'
          },
          {
            text: '未完成',
            value: 'undone'
          }
        ],
        onFilter: (value, record) => record.status.indexOf(value) === 0
      },
      {
        title: 'tag',
        dataIndex: 'tag',
        key: 'tag',
        width: 14,
        render: text => {
          let textMsg = text.length > 0 ? text.join('\n') : '未设置';
          return
{textMsg}
;
        },
        filters: filter,
        onFilter: (value, record) => {
          return record.tag.indexOf(value) >= 0;
        }
      }
    ];
    let intername = '',
      desc = '';
    let cat = this.props.curProject ? this.props.curProject.cat : [];

    if (cat) {
      for (let i = 0; i < cat.length; i++) {
        if (cat[i]._id === this.state.catid) {
          intername = cat[i].name;
          desc = cat[i].desc;
          break;
        }
      }
    }
    // const data = this.state.data ? this.state.data.map(item => {
    //   item.key = item._id;
    //   return item;
    // }) : [];
    let data = [];
    let total = 0;
    const { params } = this.props.match;
    if (!params.actionId) {
      data = this.props.totalTableList;
      total = this.props.totalCount;
    } else if (isNaN(params.actionId)) {
      data = this.props.catTableList;
      total = this.props.count;
    }

    data = data.map(item => {
      item.key = item._id;
      return item;
    }) : null;

    const pageConfig = {
      total: total,
      pageSize: limit,
      current: this.state.current,
      onChange: this.changePage
    };

    const isDisabled = this.props.catList.length === 0;

    // console.log(this.props.curProject.tag)

    return (

          {intername ? intername : '全部接口'}共 ({total}) 个

        <Button
          style={{ float: 'right' }}
          disabled={isDisabled}
          type="primary"
          onClick={() => this.setState({ visible: true })}
        >
          添加接口
        </Button>

           this.handleChangeInterfaceCat(value, intername)} desc={desc} />

        {this.state.visible && (
          <Modal
            title="添加接口"
            visible={this.state.visible}
            onCancel={() => this.setState({ visible: false })}
            footer={null}
            className="addcatmodal"
          >
            <AddInterfaceForm
              catid={this.state.catid}
              catdata={cat}
              onCancel={() => this.setState({ visible: false })}
              onSubmit={this.handleAddInterface}
            />
          </Modal>
        )}

    );
  }
}

export default InterfaceList;
<commit_msg>fix: mergin<commit_after>import React, { PureComponent as Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import axios from 'axios';
import { Table, Button, Modal, message, Tooltip, Select, Icon } from 'antd';
import AddInterfaceForm from './AddInterfaceForm';
import {
  fetchInterfaceListMenu,
  fetchInterfaceList,
  fetchInterfaceCatList
} from '../../../../reducer/modules/interface.js';
import { getProject } from '../../../../reducer/modules/project.js';
import { Link } from 'react-router-dom';
import variable from '../../../../constants/variable';
import './Edit.scss';
import Label from '../../../../components/Label/Label.js';

const Option = Select.Option;
const limit = 20;

@connect(
  state => {
    return {
      curData: state.inter.curdata,
      curProject: state.project.currProject,
      catList: state.inter.list,
      totalTableList: state.inter.totalTableList,
      catTableList: state.inter.catTableList,
      totalCount: state.inter.totalCount,
      count: state.inter.count
    };
  },
  {
    fetchInterfaceListMenu,
    fetchInterfaceList,
    fetchInterfaceCatList,
    getProject
  }
)
class InterfaceList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      visible: false,
      data: [],
      catid: null,
      total: null,
      current: 1
    };
  }

  static propTypes = {
    curData: PropTypes.object,
    catList: PropTypes.array,
    match: PropTypes.object,
    curProject: PropTypes.object,
    history: PropTypes.object,
    fetchInterfaceListMenu: PropTypes.func,
    fetchInterfaceList: PropTypes.func,
    fetchInterfaceCatList: PropTypes.func,
    totalTableList: PropTypes.array,
    catTableList: PropTypes.array,
    totalCount: PropTypes.number,
    count: PropTypes.number,
    getProject: PropTypes.func
  };

  handleRequest = async props => {
    const { params } = props.match;
    if (!params.actionId) {
      let projectId = params.id;
      this.setState({
        catid: null
      });
      let option = {
        page: this.state.current,
        limit,
        project_id: projectId
      };
      await this.props.fetchInterfaceList(option);
    } else if (isNaN(params.actionId)) {
      let catid = params.actionId.substr(4);
      this.setState({ catid: +catid });
      let option = {
        page: this.state.current,
        limit,
        catid
      };

      await this.props.fetchInterfaceCatList(option);
    }
  };

  // 更新分类简介
  handleChangeInterfaceCat = (desc, name) => {
    let params = {
      catid: this.state.catid,
      name: name,
      desc: desc
    };

    axios.post('/api/interface/up_cat', params).then(async res => {
      if (res.data.errcode !== 0) {
        return message.error(res.data.errmsg);
      }
      let project_id = this.props.match.params.id;
      await this.props.getProject(project_id);
      await this.props.fetchInterfaceListMenu(project_id);
      message.success('接口集合简介更新成功');
    });
  };
  handleChange = (pagination, filters, sorter) => {
    this.setState({
      sortedInfo: sorter
    });
  };

  componentWillMount() {
    this.actionId = this.props.match.params.actionId;
    this.handleRequest(this.props);
  }

  componentWillReceiveProps(nextProps) {
    let _actionId = nextProps.match.params.actionId;

    if (this.actionId !== _actionId) {
      this.actionId = _actionId;
      this.setState(
        {
          current: 1
        },
        () => this.handleRequest(nextProps)
      );
    }
  }

  handleAddInterface = data => {
    data.project_id = this.props.curProject._id;
    axios.post('/api/interface/add', data).then(res => {
      if (res.data.errcode !== 0) {
        return message.error(`${res.data.errmsg}, 你可以在左侧的接口列表中对接口进行删改`);
      }
      message.success('接口添加成功');
      let interfaceId = res.data.data._id;
      this.props.history.push('/project/' + data.project_id + '/interface/api/' + interfaceId);
      this.props.fetchInterfaceListMenu(data.project_id);
    });
  };

  changeInterfaceCat = async (id, catid) => {
    const params = {
      id: id,
      catid
    };
    let result = await axios.post('/api/interface/up', params);
    if (result.data.errcode === 0) {
      message.success('修改成功');
      this.handleRequest(this.props);
      this.props.fetchInterfaceListMenu(this.props.curProject._id);
    } else {
      message.error(result.data.errmsg);
    }
  };

  changeInterfaceStatus = async value => {
    const params = {
      id: value.split('-')[0],
      status: value.split('-')[1]
    };
    let result = await axios.post('/api/interface/up', params);
    if (result.data.errcode === 0) {
      message.success('修改成功');
      this.handleRequest(this.props);
    } else {
      message.error(result.data.errmsg);
    }
  };

  changePage = current => {
    this.setState(
      {
        current: current
      },
      () => this.handleRequest(this.props)
    );
  };

  render() {
    let tag = this.props.curProject.tag;
    let filter = tag.map(item => {
      return { text: item.name, value: item.name };
    });

    const columns = [
      {
        title: '接口名称',
        dataIndex: 'title',
        key: 'title',
        width: 30,
        render: (text, item) => {
          return (
            <Link to={'/project/' + item.project_id + '/interface/api/' + item._id}>
              {text}
            </Link>
          );
        }
      },
      {
        title: '接口路径',
        dataIndex: 'path',
        key: 'path',
        width: 50,
        render: (item, record) => {
          const path = this.props.curProject.basepath + item;
          let methodColor =
            variable.METHOD_COLOR[record.method ? record.method.toLowerCase() : 'get'] ||
            variable.METHOD_COLOR['get'];
          return (

                {record.method}

              <Tooltip title="开放接口" placement="topLeft">
                {record.api_opened && <Icon className="opened" type="eye-o" />}
              </Tooltip>
              <Tooltip title={path} placement="topLeft" overlayClassName="toolTip">
                {path}
              </Tooltip>

          );
        }
      },
      {
        title: '接口分类',
        dataIndex: 'catid',
        key: 'catid',
        width: 28,
        render: (item, record) => {
          return (
            <Select
              value={item + ''}
              className="select path"
              onChange={catid => this.changeInterfaceCat(record._id, catid)}
            >
              {this.props.catList.map(cat => {
                return (
                  <Option key={cat.id + ''} value={cat._id + ''}>
                    {cat.name}
                  </Option>
                );
              })}
            </Select>
          );
        }
      },
      {
        title: '状态',
        dataIndex: 'status',
        key: 'status',
        width: 24,
        render: (text, record) => {
          const key = record.key;
          return (
            <Select
              value={key + '-' + text}
              className="select"
              onChange={this.changeInterfaceStatus}
            >
              <Option value={key + '-done'}>
                已完成
              </Option>
              <Option value={key + '-undone'}>
                未完成
              </Option>
            </Select>
          );
        },
        filters: [
          {
            text: '已完成',
            value: 'done'
          },
          {
            text: '未完成',
            value: 'undone'
          }
        ],
        onFilter: (value, record) => record.status.indexOf(value) === 0
      },
      {
        title: 'tag',
        dataIndex: 'tag',
        key: 'tag',
        width: 14,
        render: text => {
          let textMsg = text.length > 0 ? text.join('\n') : '未设置';
          return
{textMsg}
;
        },
        filters: filter,
        onFilter: (value, record) => {
          return record.tag.indexOf(value) >= 0;
        }
      }
    ];
    let intername = '',
      desc = '';
    let cat = this.props.curProject ? this.props.curProject.cat : [];

    if (cat) {
      for (let i = 0; i < cat.length; i++) {
        if (cat[i]._id === this.state.catid) {
          intername = cat[i].name;
          desc = cat[i].desc;
          break;
        }
      }
    }
    // const data = this.state.data ? this.state.data.map(item => {
    //   item.key = item._id;
    //   return item;
    // }) : [];
    let data = [];
    let total = 0;
    const { params } = this.props.match;
    if (!params.actionId) {
      data = this.props.totalTableList;
      total = this.props.totalCount;
    } else if (isNaN(params.actionId)) {
      data = this.props.catTableList;
      total = this.props.count;
    }

    data = data.map(item => {
      item.key = item._id;
      return item;
    });

    const pageConfig = {
      total: total,
      pageSize: limit,
      current: this.state.current,
      onChange: this.changePage
    };

    const isDisabled = this.props.catList.length === 0;

    // console.log(this.props.curProject.tag)

    return (

          {intername ? intername : '全部接口'}共 ({total}) 个

        <Button
          style={{ float: 'right' }}
          disabled={isDisabled}
          type="primary"
          onClick={() => this.setState({ visible: true })}
        >
          添加接口
        </Button>

           this.handleChangeInterfaceCat(value, intername)} desc={desc} />

        {this.state.visible && (
          <Modal
            title="添加接口"
            visible={this.state.visible}
            onCancel={() => this.setState({ visible: false })}
            footer={null}
            className="addcatmodal"
          >
            <AddInterfaceForm
              catid={this.state.catid}
              catdata={cat}
              onCancel={() => this.setState({ visible: false })}
              onSubmit={this.handleAddInterface}
            />
          </Modal>
        )}

    );
  }
}

export default InterfaceList;
<|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 = [];
  if (v.replication === 'no') {
      includeTestTags.push('!org.postgresql.test.Replication');
  }
  if (v.slow_tests === 'no') {
      includeTestTags.push('!org.postgresql.test.SlowTests');
  }

  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>chore: use "untagged tests" filtering expression to allow skipping them<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: '', 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');
  }

  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>'use strict'

/* eslint-env browser, webextensions */

const debug = require('debug')
const log = debug('ipfs-companion:client')
log.error = debug('ipfs-companion:client:error')

const browser = require('webextension-polyfill')
const external = require('./external')
const embedded = require('./embedded')
const embeddedWithChromeSockets = require('./embedded-chromesockets')
const { precache } = require('../precache')

let client

async function initIpfsClient (opts) {
  log('init ipfs client')
  await destroyIpfsClient()
  switch (opts.ipfsNodeType) {
    case 'embedded':
      client = embedded
      break
    case 'embedded:chromesockets':
      client = embeddedWithChromeSockets
      break
    case 'external':
      client = external
      break
    default:
      throw new Error(`Unsupported ipfsNodeType: ${opts.ipfsNodeType}`)
  }

  const instance = await client.init(opts)
  easeApiChanges(instance)
  _reloadIpfsClientDependents(instance) // async (API is present)
  return instance
}

async function destroyIpfsClient () {
  if (client && client.destroy) {
    try {
      await client.destroy()
    } finally {
      client = null
      await _reloadIpfsClientDependents() // sync (API stopped working)
    }
  }
}

function _isWebuiTab (url) {
  const bundled = !url.startsWith('http') && url.includes('/webui/index.html#/')
  const ipns = url.includes('/webui.ipfs.io/#/')
  return bundled || ipns
}

async function _reloadIpfsClientDependents (instance, opts) {
  // online || offline
  if (browser.tabs && browser.tabs.query) {
    const tabs = await browser.tabs.query({})
    if (tabs) {
      tabs.forEach((tab) => {
        // detect bundled webui in any of open tabs
        if (_isWebuiTab(tab.url)) {
          browser.tabs.reload(tab.id)
          log('reloading bundled webui')
        }
      })
    }
  }
  // online only
  if (client && instance) {
    // add important data to local ipfs repo for instant load
    precache(instance)
  }
}

const movedFilesApis = ['add', 'addPullStream', 'addReadableStream', 'cat', 'catPullStream', 'catReadableStream', 'get', 'getPullStream', 'getReadableStream']

// This enables use of dependencies without worrying if they already migrated to the new API.
function easeApiChanges (ipfs) {
  if (!ipfs) return
  // Handle the move of regular files api to top level
  // https://github.com/ipfs/interface-ipfs-core/pull/378
  // https://github.com/ipfs/js-ipfs/releases/tag/v0.34.0-pre.0
  movedFilesApis.forEach(cmd => {
    // Fix old backend (add new methods)
    if (typeof ipfs[cmd] !== 'function' && ipfs.files && typeof ipfs.files[cmd] === 'function') {
      ipfs[cmd] = ipfs.files[cmd]
      // console.log(`[ipfs-companion] fixed missing ipfs.${cmd}: added an alias for ipfs.files.${cmd}`)
    }
    // Fix new backend (add old methods)
    // This ensures ipfs-postmsg-proxy always works and can be migrated later
    if (ipfs.files && typeof ipfs.files[cmd] !== 'function' && typeof ipfs[cmd] === 'function') {
      ipfs.files[cmd] = ipfs[cmd]
      // console.log(`[ipfs-companion] fixed missing ipfs.files.${cmd}: added an alias for ipfs.${cmd}`)
    }
  })
}

exports.movedFilesApis = movedFilesApis
exports.initIpfsClient = initIpfsClient
exports.destroyIpfsClient = destroyIpfsClient
<commit_msg>fix: delay precache for 10s<commit_after>'use strict'

/* eslint-env browser, webextensions */

const debug = require('debug')
const log = debug('ipfs-companion:client')
log.error = debug('ipfs-companion:client:error')

const browser = require('webextension-polyfill')
const external = require('./external')
const embedded = require('./embedded')
const embeddedWithChromeSockets = require('./embedded-chromesockets')
const { precache } = require('../precache')

let client

async function initIpfsClient (opts) {
  log('init ipfs client')
  await destroyIpfsClient()
  switch (opts.ipfsNodeType) {
    case 'embedded':
      client = embedded
      break
    case 'embedded:chromesockets':
      client = embeddedWithChromeSockets
      break
    case 'external':
      client = external
      break
    default:
      throw new Error(`Unsupported ipfsNodeType: ${opts.ipfsNodeType}`)
  }

  const instance = await client.init(opts)
  easeApiChanges(instance)
  _reloadIpfsClientDependents(instance) // async (API is present)
  return instance
}

async function destroyIpfsClient () {
  if (client && client.destroy) {
    try {
      await client.destroy()
    } finally {
      client = null
      await _reloadIpfsClientDependents() // sync (API stopped working)
    }
  }
}

function _isWebuiTab (url) {
  const bundled = !url.startsWith('http') && url.includes('/webui/index.html#/')
  const ipns = url.includes('/webui.ipfs.io/#/')
  return bundled || ipns
}

async function _reloadIpfsClientDependents (instance, opts) {
  // online || offline
  if (browser.tabs && browser.tabs.query) {
    const tabs = await browser.tabs.query({})
    if (tabs) {
      tabs.forEach((tab) => {
        // detect bundled webui in any of open tabs
        if (_isWebuiTab(tab.url)) {
          browser.tabs.reload(tab.id)
          log('reloading bundled webui')
        }
      })
    }
  }
  // online only
  if (client && instance) {
    // add important data to local ipfs repo for instant load
    setTimeout(() => precache(instance), 10000)
  }
}

const movedFilesApis = ['add', 'addPullStream', 'addReadableStream', 'cat', 'catPullStream', 'catReadableStream', 'get', 'getPullStream', 'getReadableStream']

// This enables use of dependencies without worrying if they already migrated to the new API.
function easeApiChanges (ipfs) {
  if (!ipfs) return
  // Handle the move of regular files api to top level
  // https://github.com/ipfs/interface-ipfs-core/pull/378
  // https://github.com/ipfs/js-ipfs/releases/tag/v0.34.0-pre.0
  movedFilesApis.forEach(cmd => {
    // Fix old backend (add new methods)
    if (typeof ipfs[cmd] !== 'function' && ipfs.files && typeof ipfs.files[cmd] === 'function') {
      ipfs[cmd] = ipfs.files[cmd]
      // console.log(`[ipfs-companion] fixed missing ipfs.${cmd}: added an alias for ipfs.files.${cmd}`)
    }
    // Fix new backend (add old methods)
    // This ensures ipfs-postmsg-proxy always works and can be migrated later
    if (ipfs.files && typeof ipfs.files[cmd] !== 'function' && typeof ipfs[cmd] === 'function') {
      ipfs.files[cmd] = ipfs[cmd]
      // console.log(`[ipfs-companion] fixed missing ipfs.files.${cmd}: added an alias for ipfs.${cmd}`)
    }
  })
}

exports.movedFilesApis = movedFilesApis
exports.initIpfsClient = initIpfsClient
exports.destroyIpfsClient = destroyIpfsClient
<|endoftext|>


<commit_before>var cdb = require('internal-carto.js');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var $ = require('jquery');
var _ = require('underscore');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var template = require('./navigation.tpl');

var REQUIRED_OPTS = [
  'configModel',
  'createModel',
  'userModel',
  'routerModel',
  'tablesCollection'
];

/**
 *  Listing datasets navigation.
 *
 *  - 'Filter by' datasets.
 *  - 'Search' any pattern within dataset collection.
 *
 */
module.exports = CoreView.extend({
  events: {
    'submit .js-search-form': '_submitSearch',
    'keydown .js-search-form': '_onSearchKeyDown',
    'click .js-search-form': 'killEvent',
    'click .js-search-link': '_onSearchClick',
    'click .js-clean-search': '_onCleanSearchClick',
    'click .js-shared': '_onSharedClick',
    'click .js-library': '_onLibraryClick',
    'click .js-connect': '_onConnectClick',
    'click .js-datasets': '_onDatasetsClick',
    'click .js-create-empty': '_onCreateEmptyClick'
  },

  initialize: function (opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
    this.model = new Backbone.Model();

    this._preRender();
    this._initBinds();
  },

  // It is necessary to add two static elements because
  // they can't be removed/replaced using render method
  // each time a change (in a model or a collection) happens.
  // This is due to the behaviour of the CSS animations.
  _preRender: function () {
    var $uInner = $('<div>').addClass('u-inner');
    var $filtersInner = $('<div>').addClass('Filters-inner');
    this.$el.append($uInner.append($filtersInner));
  },

  render: function (m, c) {
    this.clearSubViews();

    this.$('.Filters-inner').html(
      template(
        _.extend(
          {
            showDatasets: this._createModel.showDatasets(),
            createModelType: this._createModel.get('type'),
            canCreateDataset: this._userModel.canCreateDatasets(),
            listingType: this._createModel.get('listing'),
            isInsideOrg: this._userModel.isInsideOrg(),
            totalShared: this._tablesCollection.getTotalStat('total_shared'),
            selectedItemsCount: this._selectedItems().length,
            hasDataLibrary: this._configModel.dataLibraryEnabled()
          },
          this._routerModel.attributes
        )
      )
    );

    this._animate();
    if (this._routerModel.isSearching()) {
      this._focusSearchInput();
    }
    return this;
  },

  _initBinds: function () {
    this.listenTo(this.model, 'change:isSearchEnabled', this._onChangeIsSearchEnabled, this);
    this.listenTo(this._createModel, 'change:listing', this.render, this);
    this.listenTo(this._routerModel, 'change', this.render, this);
    this.listenTo(this._tablesCollection, 'sync', this.render, this);
  },

  _onChangeIsSearchEnabled: function (model, isSearchEnabled) {
    this._enableSearchUI(isSearchEnabled);

    if (this._routerModel.isSearching()) {
      this._cleanSearch();
    } else if (isSearchEnabled) {
      this._$searchInput().val('');
      this._focusSearchInput();
    }
  },

  _$searchInput: function () {
    return this.$('.js-search-input');
  },

  _focusSearchInput: function () {
    this._$searchInput()
      .select()
      .focus();
  },

  _onSearchKeyDown: function (e) {
    if (e.keyCode === 27) { // ESC
      this.killEvent(e);
      this._cleanSearch();
    }
  },

  _selectedItems: function () {
    return this._tablesCollection.where({ selected: true });
  },

  _animate: function () {
    this._enableSearchUI(!!this._routerModel.isSearching());

    // Check if user doesn't have any table and it is in library section
    // to remove useless shadow
    var inLibrarySection = this._routerModel.get('library');
    var inDatasetsSection = this._createModel.get('listing') === 'datasets';
    var hasDatasets = this._tablesCollection.getTotalStat('total_user_entries') > 0;
    this.$el.toggleClass('no-shadow', inLibrarySection && !hasDatasets && inDatasetsSection);
  },

  _enableSearchUI: function (enable) {
    this.$('.js-search-field').toggle(enable);
    this.$('.js-links-list').toggleClass('is-hidden', enable);
    this.$('.js-order-list').toggleClass('is-hidden', enable);
  },

  _onDatasetsClick: function () {
    this._routerModel.set({
      shared: 'no',
      library: false,
      page: 1
    });
    this._routerModel.trigger('change');
    this._createModel.set('listing', 'datasets');
  },

  _onSharedClick: function () {
    this._routerModel.set({
      shared: 'only',
      library: false,
      page: 1
    });
    this._routerModel.trigger('change');
    this._createModel.set('listing', 'datasets');
  },

  _onLibraryClick: function () {
    this._routerModel.set({
      shared: 'no',
      library: true,
      page: 1
    });
    this._routerModel.trigger('change');
    this._createModel.set('listing', 'datasets');
  },

  _onConnectClick: function () {
    if (this._userModel.canCreateDatasets()) {
      this._createModel.set('listing', 'import');
    }
  },

  _onCreateEmptyClick: function () {
    if (this._userModel.canCreateDatasets()) {
      this._createModel.createFromScratch();
    }
  },

  // Selection actions
  _onSearchClick: function (e) {
    this.killEvent(e);
    this.model.set('isSearchEnabled', !this.model.get('isSearchEnabled'));
  },

  // Filter actions
  _onCleanSearchClick: function (e) {
    if (e) {
      e.preventDefault();
    }
    this._cleanSearch();
  },

  _cleanSearch: function () {
    this._routerModel.set({
      q: '',
      tag: '',
      shared: 'no',
      library: this._createModel.showLibrary()
    });
    this.model.set('isSearchEnabled', false);
  },

  _submitSearch: function (e) {
    this.killEvent(e);
    var val = cdb.core.sanitize.html(this.$('.js-search-input').val().trim());
    var tag = val.search(':') === 0 ? val.replace(':', '') : '';
    var q = val.search(':') !== 0 ? val : '';

    this._routerModel.set({
      page: 1,
      tag: tag,
      q: q,
      shared: 'yes'
    });

    this._createModel.set('listing', 'datasets');
  }

});
<commit_msg>Pass model to trigger calls<commit_after>var cdb = require('internal-carto.js');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var $ = require('jquery');
var _ = require('underscore');
var checkAndBuildOpts = require('builder/helpers/required-opts');
var template = require('./navigation.tpl');

var REQUIRED_OPTS = [
  'configModel',
  'createModel',
  'userModel',
  'routerModel',
  'tablesCollection'
];

/**
 *  Listing datasets navigation.
 *
 *  - 'Filter by' datasets.
 *  - 'Search' any pattern within dataset collection.
 *
 */
module.exports = CoreView.extend({
  events: {
    'submit .js-search-form': '_submitSearch',
    'keydown .js-search-form': '_onSearchKeyDown',
    'click .js-search-form': 'killEvent',
    'click .js-search-link': '_onSearchClick',
    'click .js-clean-search': '_onCleanSearchClick',
    'click .js-shared': '_onSharedClick',
    'click .js-library': '_onLibraryClick',
    'click .js-connect': '_onConnectClick',
    'click .js-datasets': '_onDatasetsClick',
    'click .js-create-empty': '_onCreateEmptyClick'
  },

  initialize: function (opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
    this.model = new Backbone.Model();

    this._preRender();
    this._initBinds();
  },

  // It is necessary to add two static elements because
  // they can't be removed/replaced using render method
  // each time a change (in a model or a collection) happens.
  // This is due to the behaviour of the CSS animations.
  _preRender: function () {
    var $uInner = $('<div>').addClass('u-inner');
    var $filtersInner = $('<div>').addClass('Filters-inner');
    this.$el.append($uInner.append($filtersInner));
  },

  render: function (m, c) {
    this.clearSubViews();

    this.$('.Filters-inner').html(
      template(
        _.extend(
          {
            showDatasets: this._createModel.showDatasets(),
            createModelType: this._createModel.get('type'),
            canCreateDataset: this._userModel.canCreateDatasets(),
            listingType: this._createModel.get('listing'),
            isInsideOrg: this._userModel.isInsideOrg(),
            totalShared: this._tablesCollection.getTotalStat('total_shared'),
            selectedItemsCount: this._selectedItems().length,
            hasDataLibrary: this._configModel.dataLibraryEnabled()
          },
          this._routerModel.attributes
        )
      )
    );

    this._animate();
    if (this._routerModel.isSearching()) {
      this._focusSearchInput();
    }
    return this;
  },

  _initBinds: function () {
    this.listenTo(this.model, 'change:isSearchEnabled', this._onChangeIsSearchEnabled, this);
    this.listenTo(this._createModel, 'change:listing', this.render, this);
    this.listenTo(this._routerModel, 'change', this.render, this);
    this.listenTo(this._tablesCollection, 'sync', this.render, this);
  },

  _onChangeIsSearchEnabled: function (model, isSearchEnabled) {
    this._enableSearchUI(isSearchEnabled);

    if (this._routerModel.isSearching()) {
      this._cleanSearch();
    } else if (isSearchEnabled) {
      this._$searchInput().val('');
      this._focusSearchInput();
    }
  },

  _$searchInput: function () {
    return this.$('.js-search-input');
  },

  _focusSearchInput: function () {
    this._$searchInput()
      .select()
      .focus();
  },

  _onSearchKeyDown: function (e) {
    if (e.keyCode === 27) { // ESC
      this.killEvent(e);
      this._cleanSearch();
    }
  },

  _selectedItems: function () {
    return this._tablesCollection.where({ selected: true });
  },

  _animate: function () {
    this._enableSearchUI(!!this._routerModel.isSearching());

    // Check if user doesn't have any table and it is in library section
    // to remove useless shadow
    var inLibrarySection = this._routerModel.get('library');
    var inDatasetsSection = this._createModel.get('listing') === 'datasets';
    var hasDatasets = this._tablesCollection.getTotalStat('total_user_entries') > 0;
    this.$el.toggleClass('no-shadow', inLibrarySection && !hasDatasets && inDatasetsSection);
  },

  _enableSearchUI: function (enable) {
    this.$('.js-search-field').toggle(enable);
    this.$('.js-links-list').toggleClass('is-hidden', enable);
    this.$('.js-order-list').toggleClass('is-hidden', enable);
  },

  _onDatasetsClick: function () {
    this._routerModel.set({
      shared: 'no',
      library: false,
      page: 1
    });
    this._routerModel.trigger('change', this._routerModel);
    this._createModel.set('listing', 'datasets');
  },

  _onSharedClick: function () {
    this._routerModel.set({
      shared: 'only',
      library: false,
      page: 1
    });
    this._routerModel.trigger('change', this._routerModel);
    this._createModel.set('listing', 'datasets');
  },

  _onLibraryClick: function () {
    this._routerModel.set({
      shared: 'no',
      library: true,
      page: 1
    });
    this._routerModel.trigger('change', this._routerModel);
    this._createModel.set('listing', 'datasets');
  },

  _onConnectClick: function () {
    if (this._userModel.canCreateDatasets()) {
      this._createModel.set('listing', 'import');
    }
  },

  _onCreateEmptyClick: function () {
    if (this._userModel.canCreateDatasets()) {
      this._createModel.createFromScratch();
    }
  },

  // Selection actions
  _onSearchClick: function (e) {
    this.killEvent(e);
    this.model.set('isSearchEnabled', !this.model.get('isSearchEnabled'));
  },

  // Filter actions
  _onCleanSearchClick: function (e) {
    if (e) {
      e.preventDefault();
    }
    this._cleanSearch();
  },

  _cleanSearch: function () {
    this._routerModel.set({
      q: '',
      tag: '',
      shared: 'no',
      library: this._createModel.showLibrary()
    });
    this.model.set('isSearchEnabled', false);
  },

  _submitSearch: function (e) {
    this.killEvent(e);
    var val = cdb.core.sanitize.html(this.$('.js-search-input').val().trim());
    var tag = val.search(':') === 0 ? val.replace(':', '') : '';
    var q = val.search(':') !== 0 ? val : '';

    this._routerModel.set({
      page: 1,
      tag: tag,
      q: q,
      shared: 'yes'
    });

    this._createModel.set('listing', 'datasets');
  }

});
<|endoftext|>


<commit_before>/**
  @module ember-flexberry-gis
*/

import Ember from 'ember';
import layout from '../templates/components/flexberry-table';
import PaginatedControllerMixin from 'ember-flexberry/mixins/paginated-controller';
import SlotsMixin from 'ember-block-slots';

/**
  Flattens an array of arrays and objects.

  @param {Array} a Array to be flatten
  @returns {Array}
*/
let flatten = function (a) {
  return Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
};

/**
  Flexberry table component with [Semantic UI table](https://semantic-ui.com/collections/table) style and paging handling.

  @class FlexberryTableComponent
  @uses PaginatedControllerMixin
  @extends Ember.Component
  @extends Ember block slots
*/
export default Ember.Component.extend(PaginatedControllerMixin, SlotsMixin, {
  /**
    Reference to component's template.
  */
  layout,

  /**
    Count of columns.

    @property _columnCount
    @type Number
    @private
    @readOnly
  */
  _columnCount: Ember.computed('header', {
    get() {
      let additionalColumns = 0;
      let flatStatements = flatten(this.get('layout').raw.statements);

      // consider the added trough yeld-slots columns
      if (flatStatements && flatStatements.length) {
        additionalColumns = flatStatements.filter(function (item) {
          return item && typeof (item) === 'string' && item.indexOf('column-header') !== -1;
        }).length;
      }

      return Object.keys(this.get('header')).length + additionalColumns;
    }
  }),

  actions: {
    /**
      Handles changes in component's {{#crossLink "FlexberryTableComponent/page:property"}}'perPageValue' property{{/crossLink}}.

      @method actions.perPageClick
    */
    perPageClick() {
      this._reload();
    }
  },

  /**
    Observes {{#crossLink "FlexberryTableComponent/page:property"}}'page' property{{/crossLink}} and handles changes in it.

    @method _pageDidChange
    @private
  */
  _pageDidChange: Ember.observer('page', function () {
    this._reload();
  }),

  /**
    Handles changes in component's
    {{#crossLink "FlexberryTableComponent/page:property"}}'page'{{/crossLink}} or
    {{#crossLink "FlexberryTableComponent/perPageValue:property"}}'perPageValue'{{/crossLink}} properties
    Invokes component's {{#crossLink "FlexberryTableComponent/sendingActions.getData:method"}}'getData'{{/crossLink}} action.

    @method _createInnerSettings
    @private
  */
  _reload() {
    let perPageValue = this.get('perPageValue');
    let pageNum = this.get('page');
    this.sendAction('getData', {
      modelName: this.get('modelName'),
      projectionName: this.get('projectionName'),
      top: perPageValue,
      skip: pageNum > 0 ? (pageNum - 1) * perPageValue : 0
    });
  }

  /**
    Component's action invoking when component needs assosiated data to be loaded or reloaded.

    @method sendingActions.getData
    @param {Object} req Action's event object.
  */
});
<commit_msg>Fix variable<commit_after>/**
  @module ember-flexberry-gis
*/

import Ember from 'ember';
import layout from '../templates/components/flexberry-table';
import PaginatedControllerMixin from 'ember-flexberry/mixins/paginated-controller';
import SlotsMixin from 'ember-block-slots';

/**
  Flattens an array of arrays and objects.

  @param {Array} a Array to be flatten
  @returns {Array}
*/
let flatten = function (a) {
  return Array.isArray(a) ? [].concat(...a.map(flatten)) : a;
};

/**
  Flexberry table component with [Semantic UI table](https://semantic-ui.com/collections/table) style and paging handling.

  @class FlexberryTableComponent
  @uses PaginatedControllerMixin
  @extends Ember.Component
  @extends Ember block slots
*/
export default Ember.Component.extend(PaginatedControllerMixin, SlotsMixin, {
  /**
    Reference to component's template.
  */
  layout,

  /**
    Count of columns.

    @property _columnCount
    @type Number
    @private
    @readOnly
  */
  _columnCount: Ember.computed('header', {
    get() {
      let additionalColumns = 0;
      let flatStatements = flatten(this.get('layout.raw.statements'));

      // consider the added trough yeld-slots columns
      if (flatStatements && flatStatements.length) {
        additionalColumns = flatStatements.filter(function (item) {
          return item && typeof (item) === 'string' && item.indexOf('column-header') !== -1;
        }).length;
      }

      return Object.keys(this.get('header')).length + additionalColumns;
    }
  }),

  actions: {
    /**
      Handles changes in component's {{#crossLink "FlexberryTableComponent/page:property"}}'perPageValue' property{{/crossLink}}.

      @method actions.perPageClick
    */
    perPageClick() {
      this._reload();
    }
  },

  /**
    Observes {{#crossLink "FlexberryTableComponent/page:property"}}'page' property{{/crossLink}} and handles changes in it.

    @method _pageDidChange
    @private
  */
  _pageDidChange: Ember.observer('page', function () {
    this._reload();
  }),

  /**
    Handles changes in component's
    {{#crossLink "FlexberryTableComponent/page:property"}}'page'{{/crossLink}} or
    {{#crossLink "FlexberryTableComponent/perPageValue:property"}}'perPageValue'{{/crossLink}} properties
    Invokes component's {{#crossLink "FlexberryTableComponent/sendingActions.getData:method"}}'getData'{{/crossLink}} action.

    @method _createInnerSettings
    @private
  */
  _reload() {
    let perPageValue = this.get('perPageValue');
    let pageNum = this.get('page');
    this.sendAction('getData', {
      modelName: this.get('modelName'),
      projectionName: this.get('projectionName'),
      top: perPageValue,
      skip: pageNum > 0 ? (pageNum - 1) * perPageValue : 0
    });
  }

  /**
    Component's action invoking when component needs assosiated data to be loaded or reloaded.

    @method sendingActions.getData
    @param {Object} req Action's event object.
  */
});
<|endoftext|>


<commit_before>import Ember from 'ember'
import layout from '../../templates/components/frost-viz/scope'
import ScopeProvider from 'ciena-frost-viz/mixins/frost-viz-scope-provider'
// import PropTypesMixin, {PropTypes} from 'ember-prop-types'

const GenericScope = Ember.Object.extend()

const Scope = Ember.Component.extend(ScopeProvider, {
  tagName: '',
  layout,

  // TODO: this surprisingly appears to break all the things
  // propTypes: {
  //   inject: PropTypes.oneOf([
  //     PropTypes.object,
  //     PropTypes.EmberObject
  //   ])
  // },
  // getDefaultProps () {
  //   inject: null
  // },

  childScope: Ember.computed('childScopeBase', 'inject', 'inject.@each', function () {
    const inject = this.get('inject')
    const result = GenericScope.create(this.get('childScopeBase'), inject)
    return result
  })
})

Scope.reopenClass({
  positionalParams: ['scope', 'inject']
})

export default Scope
<commit_msg>Scope component requires scope parameter<commit_after>import Ember from 'ember'
import layout from '../../templates/components/frost-viz/scope'
import ScopeProvider from 'ciena-frost-viz/mixins/frost-viz-scope-provider'
import PropTypesMixin, {PropTypes} from 'ember-prop-types'

const GenericScope = Ember.Object.extend()

const Scope = Ember.Component.extend(ScopeProvider, {
  tagName: '',
  layout,

  // TODO: this surprisingly appears to break all the things
  propTypes: {
  //   inject: PropTypes.oneOf([
  //     PropTypes.object,
  //     PropTypes.EmberObject
  //   ])
    scope: PropTypes.EmberObject.isRequired
  },
  // getDefaultProps () {
  //   inject: null
  // },

  childScope: Ember.computed('childScopeBase', 'inject', 'inject.@each', function () {
    const inject = this.get('inject')
    const result = GenericScope.create(this.get('childScopeBase'), inject)
    return result
  })
})

Scope.reopenClass({
  positionalParams: ['scope', 'inject']
})

export default Scope
<|endoftext|>


<commit_before>import Ember from 'ember'
import computed, {readOnly} from 'ember-computed-decorators'
import moment from 'moment'

import AbstractInput from './abstract-input'
import layout from 'ember-frost-bunsen/templates/components/frost-bunsen-input-datetime'

const {isEmpty} = Ember

const DATE_FORMAT = 'YYYY-MM-DD'
const TIME_FORMAT = 'HH:mm:ss'
const DATE_TIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ'

export default AbstractInput.extend({
  // == Component Properties ===================================================

  classNameBindings: ['value:frost-bunsen-has-value'],

  classNames: [
    'frost-bunsen-input-datetime',
    'frost-field'
  ],

  layout,

  // == Computed Properties ====================================================

  @readOnly
  @computed('cellConfig.renderer.options.defaultToCurrentDateTime')
  defaultToCurrentDateTime (defaultToCurrentDateTime) {
    if (isEmpty(defaultToCurrentDateTime)) {
      return true
    }
    return Boolean(defaultToCurrentDateTime)
  },

  @readOnly
  @computed('value')
  date (value) {
    const date = moment(value).format(DATE_FORMAT)
    return /^\d{4}-\d\d-\d\d$/.test(date) ? date : ''
  },

  @readOnly
  @computed('value')
  time (value) {
    const time = moment(value).format(TIME_FORMAT)
    return /^\d\d:\d\d:\d\d$/.test(time) ? time : ''
  },

  @readOnly
  @computed('defaultToCurrentDateTime', 'value')
  currentValue (defaultToCurrentDateTime, value) {
    if (!defaultToCurrentDateTime || value) {
      return value
    }
    return moment().format(DATE_TIME_FORMAT)
  },

  // == Functions ==============================================================

  parseValue (value) {
    return moment(value).format(DATE_TIME_FORMAT)
  },

  // == Actions ===============================================================

  actions: {
  }
})
<commit_msg>Change mechanism that enforces default value from if condition to default function parameter<commit_after>import computed, {readOnly} from 'ember-computed-decorators'
import moment from 'moment'

import AbstractInput from './abstract-input'
import layout from 'ember-frost-bunsen/templates/components/frost-bunsen-input-datetime'

const DATE_FORMAT = 'YYYY-MM-DD'
const TIME_FORMAT = 'HH:mm:ss'
const DATE_TIME_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ'

export default AbstractInput.extend({
  // == Component Properties ===================================================

  classNameBindings: ['value:frost-bunsen-has-value'],

  classNames: [
    'frost-bunsen-input-datetime',
    'frost-field'
  ],

  layout,

  // == Computed Properties ====================================================

  @readOnly
  @computed('cellConfig.renderer.options.defaultToCurrentDateTime')
  defaultToCurrentDateTime (defaultToCurrentDateTime = true) {
    return Boolean(defaultToCurrentDateTime)
  },

  @readOnly
  @computed('value')
  date (value) {
    const date = moment(value).format(DATE_FORMAT)
    return /^\d{4}-\d\d-\d\d$/.test(date) ? date : ''
  },

  @readOnly
  @computed('value')
  time (value) {
    const time = moment(value).format(TIME_FORMAT)
    return /^\d\d:\d\d:\d\d$/.test(time) ? time : ''
  },

  @readOnly
  @computed('defaultToCurrentDateTime', 'value')
  currentValue (defaultToCurrentDateTime, value) {
    if (!defaultToCurrentDateTime || value) {
      return value
    }
    return moment().format(DATE_TIME_FORMAT)
  },

  // == Functions ==============================================================

  parseValue (value) {
    return moment(value).format(DATE_TIME_FORMAT)
  },

  // == Actions ===============================================================

  actions: {
  }
})
<|endoftext|>