<commit_before>import TestContainer from 'mocha-test-container-support';

import {
  act,
  cleanup
} from '@testing-library/preact/pure';

import {
  clearBpmnJS,
  setBpmnJS,
  insertCoreStyles,
  insertBpmnStyles
} from 'test/TestHelper';

import {
  query as domQuery,
  domify
} from 'min-dom';

import Modeler from 'bpmn-js/lib/Modeler';

import BpmnPropertiesPanel from 'src/render';

import BpmnPropertiesProvider from 'src/provider/bpmn';
import CamundaPropertiesProvider from 'src/provider/camunda-platform';
import ZeebePropertiesProvider from 'src/provider/zeebe';

import CamundaModdle from 'camunda-bpmn-moddle/resources/camunda';
import CamundaModdleExtension from 'camunda-bpmn-moddle/lib';

import ZeebeModdle from 'zeebe-bpmn-moddle/resources/zeebe';
import ZeebeModdleExtension from 'zeebe-bpmn-moddle/lib';

import ExamplePropertiesProvider from './extension/ExamplePropertiesProvider';

const singleStart = window.__env__ && window.__env__.SINGLE_START;

insertCoreStyles();
insertBpmnStyles();

describe('<BpmnPropertiesPanelRenderer>', function() {

  let modelerContainer;

  let propertiesContainer;

  afterEach(() => cleanup());

  beforeEach(function() {
    modelerContainer = document.createElement('div');
    modelerContainer.classList.add('modeler-container');

    propertiesContainer = document.createElement('div');
    propertiesContainer.classList.add('properties-container');

    const container = TestContainer.get(this);

    container.appendChild(modelerContainer);
    container.appendChild(propertiesContainer);
  });

  async function createModeler(xml, options = {}) {
    const {
      shouldImport = true,
      additionalModules = [
        ZeebeModdleExtension,
        BpmnPropertiesPanel,
        BpmnPropertiesProvider,
        ZeebePropertiesProvider
      ],
      moddleExtensions = {
        zeebe: ZeebeModdle
      }
    } = options;

    clearBpmnJS();

    const modeler = new Modeler({
      container: modelerContainer,
      keyboard: {
        bindTo: document
      },
      additionalModules,
      moddleExtensions,
      propertiesPanel: {
        parent: propertiesContainer
      },
      ...options
    });

    setBpmnJS(modeler);

    if (!shouldImport) {
      return { modeler };
    }

    try {
      const result = await modeler.importXML(xml);

      return { error: null, warnings: result.warnings, modeler: modeler };
    } catch (err) {
      return { error: err, warnings: err.warnings, modeler: modeler };
    }
  }

  ((singleStart && singleStart === 'cloud') ? it.only : it)('should import simple process (cloud)', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const result = await createModeler(
      diagramXml,
      {
        additionalModules: [
          ZeebeModdleExtension,
          BpmnPropertiesPanel,
          BpmnPropertiesProvider,
          ZeebePropertiesProvider
        ],
        moddleExtensions: {
          zeebe: ZeebeModdle
        }
      }
    );

    // then
    expect(result.error).not.to.exist;
  });

  ((singleStart && singleStart === 'platform') ? it.only : it)('should import simple process (platform)', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const result = await createModeler(
      diagramXml,
      {
        additionalModules: [
          CamundaModdleExtension,
          BpmnPropertiesPanel,
          BpmnPropertiesProvider,
          CamundaPropertiesProvider
        ],
        moddleExtensions: {
          camunda: CamundaModdle
        }
      }
    );

    // then
    expect(result.error).not.to.exist;
  });

  ((singleStart && singleStart === 'bpmn') ? it.only : it)('should import simple process (bpmn)', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const result = await createModeler(
      diagramXml,
      {
        additionalModules: [
          CamundaModdleExtension,
          BpmnPropertiesPanel,
          BpmnPropertiesProvider
        ]
      }
    );

    // then
    expect(result.error).not.to.exist;
  });

  it('should render properties panel when root element was added', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    await createModeler(diagramXml);

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
  });

  it('should remove properties panel when root element was deleted', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    const { modeler } = await createModeler(diagramXml);

    const eventBus = modeler.get('eventBus');

    // when
    eventBus.fire('root.removed');

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;
  });

  it('should allow providing custom entries', async function() {

    // given
    const diagramXml = require('test/fixtures/service-task.bpmn').default;

    const modules = [
      ZeebeModdleExtension,
      BpmnPropertiesPanel,
      BpmnPropertiesProvider,
      ZeebePropertiesProvider,
      ExamplePropertiesProvider
    ];

    // when
    await createModeler(diagramXml, {
      additionalModules: modules
    });

    // then
    expect(getGroup(propertiesContainer, 'foo-group')).to.exist;
  });

  it('should ignore implicit root', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const { modeler } = await createModeler(diagramXml, {
      shouldImport: false,
      propertiesPanel: {}
    });

    const canvas = modeler.get('canvas');
    const rootElement = canvas.getRootElement();

    // assume
    expect(rootElement.isImplicit).to.be.true;

    // when
    const propertiesPanel = modeler.get('propertiesPanel');
    propertiesPanel.attachTo(propertiesContainer);
    propertiesPanel._render(rootElement);

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;
  });

  it('should ignore implicit root - legacy', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const { modeler } = await createModeler(diagramXml, {
      shouldImport: false,
      propertiesPanel: {}
    });

    const implicitRootElement = {
      id: '__implicitroot',
      children: []
    };

    // when
    const propertiesPanel = modeler.get('propertiesPanel');
    propertiesPanel.attachTo(propertiesContainer);
    propertiesPanel._render(implicitRootElement);

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;
  });

  describe('providers', function() {

    const diagramXML = require('test/fixtures/simple.bpmn').default;

    function inject(fn) {

      return async function() {
        const {
          modeler
        } = await createModeler(diagramXML, {
          additionalModules: [
            BpmnPropertiesPanel,
            BpmnPropertiesProvider
          ]
        });

        await modeler.invoke(fn);
      };
    }

    it('should ignore incompatible provider', inject(function(propertiesPanel) {

      // assume
      expect(propertiesPanel._getProviders()).to.have.length(1);

      // given
      const incompatibleProvider = {
        getTabs: function() {
          return [];
        }
      };

      // when
      propertiesPanel.registerProvider(incompatibleProvider);

      // then
      // incompatible provider was not added
      expect(propertiesPanel._getProviders()).to.have.length(1);
    }));

  });

  describe('integration', function() {

    it('should ensure creating + importing -> attaching works', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      // when
      const { modeler } = await createModeler(diagramXml, {
        propertiesPanel: {}
      });

      // assume
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;

      // and when
      const propertiesPanel = modeler.get('propertiesPanel');
      propertiesPanel.attachTo(propertiesContainer);

      // then
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
    });

    it('should ensure creating + attaching -> importing works', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      // when
      const { modeler } = await createModeler(null, {
        shouldImport: false,
        propertiesPanel: {
          parent: propertiesContainer
        }
      });

      // assume
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;

      // and when
      await modeler.importXML(diagramXml);

      // then
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
    });

    it('should ensure creating -> no import -> attaching -> import works', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      const { modeler } = await createModeler(null, {
        shouldImport: false,
        propertiesPanel: {}
      });

      const propertiesPanel = modeler.get('propertiesPanel');

      // when
      propertiesPanel.attachTo(propertiesContainer);

      // assume
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;

      // and when
      await modeler.importXML(diagramXml);

      // then
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
    });

    it('should keep state during detach and attach', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      let modeler;
      await act(async () => {
        const result = await createModeler(diagramXml);
        modeler = result.modeler;
      });

      const elementRegistry = modeler.get('elementRegistry');
      const selection = modeler.get('selection');
      const propertiesPanel = modeler.get('propertiesPanel');

      await act(() => selection.select(elementRegistry.get('StartEvent_1')));

      // assume
      expect(getHeaderName(propertiesContainer)).to.eql('start');

      // when
      propertiesPanel.detach();
      propertiesPanel.attachTo(propertiesContainer);

      // then
      expect(getHeaderName(propertiesContainer)).to.eql('start');
    });

  });

  it('#attachTo', async function() {

    // given
    const container = domify('

');
    TestContainer.get(this).appendChild(container);

    const modules = [
      BpmnPropertiesPanel,
      BpmnPropertiesProvider
    ];

    const diagramXml = require('test/fixtures/simple.bpmn').default;

    const { modeler } = await createModeler(diagramXml, {
      propertiesPanel: {},
      additionalModules: modules
    });

    const propertiesPanel = modeler.get('propertiesPanel');

    // when
    propertiesPanel.attachTo(container);

    // then
    expect(domQuery('.bio-properties-panel', container)).to.exist;
  });

  it('#detach', async function() {

    // given
    const container = domify('

');
    TestContainer.get(this).appendChild(container);

    const modules = [
      BpmnPropertiesPanel,
      BpmnPropertiesProvider
    ];

    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const { modeler } = await createModeler(diagramXml, {
      propertiesPanel: {},
      additionalModules: modules
    });

    const propertiesPanel = modeler.get('propertiesPanel');

    propertiesPanel.attachTo(container);

    // when
    propertiesPanel.detach();

    // then
    expect(domQuery('.bio-properties-panel', container)).to.not.exist;
  });

  describe('event emitting', function() {

    it('should emit <propertiesPanel.attach>', async function() {

      // given
      const spy = sinon.spy();

      const container = domify('

');
      TestContainer.get(this).appendChild(container);

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.attach', spy);

      // when
      propertiesPanel.attachTo(container);

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should emit <propertiesPanel.detach>', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.detach', spy);

      // when
      propertiesPanel.detach();

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should emit <propertiesPanel.rendered>', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.rendered', spy);

      // when
      propertiesPanel._render();

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should NOT emit <propertiesPanel.rendered> on attach', async function() {

      // given
      const spy = sinon.spy();

      const container = domify('

');
      TestContainer.get(this).appendChild(container);

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.rendered', spy);

      // when
      propertiesPanel.attachTo(container);

      // then
      expect(spy).to.not.have.been.called;
    });

    it('should emit <propertiesPanel.destroyed>', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.destroyed', spy);

      // when
      propertiesPanel._destroy();

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should emit keyboard events for undo/redo when editing', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');
      const keyboard = modeler.get('keyboard');
      const inputField = propertiesPanel._container.querySelector('input');

      eventBus.on('keyboard.keydown', 10000, spy);

      // when
      // select all
      keyboard._keyHandler({ key: 'a', ctrlKey: true, target: inputField, preventDefault: () => {} });

      // then
      // use browser default
      expect(spy).to.not.be.called;

      // when
      // undo/redo
      keyboard._keyHandler({ key: 'z', metaKey: true, target: inputField, preventDefault: () => {} });
      keyboard._keyHandler({ key: 'y', ctrlKey: true, target: inputField, preventDefault: () => {} });

      // then
      // fire events
      expect(spy).to.have.been.calledTwice;
    });

  });

});

// helpers /////////////////////

function getGroup(container, id) {
  return domQuery(`[data-group-id="group-${id}"`, container);
}

function getHeaderName(container) {
  return domQuery('.bio-properties-panel-header-label', container).innerText;
}
<commit_msg>test: simplify singleStart logic<commit_after>import TestContainer from 'mocha-test-container-support';

import {
  act,
  cleanup
} from '@testing-library/preact/pure';

import {
  clearBpmnJS,
  setBpmnJS,
  insertCoreStyles,
  insertBpmnStyles
} from 'test/TestHelper';

import {
  query as domQuery,
  domify
} from 'min-dom';

import Modeler from 'bpmn-js/lib/Modeler';

import BpmnPropertiesPanel from 'src/render';

import BpmnPropertiesProvider from 'src/provider/bpmn';
import CamundaPropertiesProvider from 'src/provider/camunda-platform';
import ZeebePropertiesProvider from 'src/provider/zeebe';

import CamundaModdle from 'camunda-bpmn-moddle/resources/camunda';
import CamundaModdleExtension from 'camunda-bpmn-moddle/lib';

import ZeebeModdle from 'zeebe-bpmn-moddle/resources/zeebe';
import ZeebeModdleExtension from 'zeebe-bpmn-moddle/lib';

import ExamplePropertiesProvider from './extension/ExamplePropertiesProvider';

const singleStart = window.__env__ && window.__env__.SINGLE_START;

insertCoreStyles();
insertBpmnStyles();

describe('<BpmnPropertiesPanelRenderer>', function() {

  let modelerContainer;

  let propertiesContainer;

  afterEach(() => cleanup());

  beforeEach(function() {
    modelerContainer = document.createElement('div');
    modelerContainer.classList.add('modeler-container');

    propertiesContainer = document.createElement('div');
    propertiesContainer.classList.add('properties-container');

    const container = TestContainer.get(this);

    container.appendChild(modelerContainer);
    container.appendChild(propertiesContainer);
  });

  async function createModeler(xml, options = {}) {
    const {
      shouldImport = true,
      additionalModules = [
        ZeebeModdleExtension,
        BpmnPropertiesPanel,
        BpmnPropertiesProvider,
        ZeebePropertiesProvider
      ],
      moddleExtensions = {
        zeebe: ZeebeModdle
      }
    } = options;

    clearBpmnJS();

    const modeler = new Modeler({
      container: modelerContainer,
      keyboard: {
        bindTo: document
      },
      additionalModules,
      moddleExtensions,
      propertiesPanel: {
        parent: propertiesContainer
      },
      ...options
    });

    setBpmnJS(modeler);

    if (!shouldImport) {
      return { modeler };
    }

    try {
      const result = await modeler.importXML(xml);

      return { error: null, warnings: result.warnings, modeler: modeler };
    } catch (err) {
      return { error: err, warnings: err.warnings, modeler: modeler };
    }
  }

  (singleStart === 'cloud' ? it.only : it)('should import simple process (cloud)', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const result = await createModeler(
      diagramXml,
      {
        additionalModules: [
          ZeebeModdleExtension,
          BpmnPropertiesPanel,
          BpmnPropertiesProvider,
          ZeebePropertiesProvider
        ],
        moddleExtensions: {
          zeebe: ZeebeModdle
        }
      }
    );

    // then
    expect(result.error).not.to.exist;
  });

  (singleStart === 'platform' ? it.only : it)('should import simple process (platform)', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const result = await createModeler(
      diagramXml,
      {
        additionalModules: [
          CamundaModdleExtension,
          BpmnPropertiesPanel,
          BpmnPropertiesProvider,
          CamundaPropertiesProvider
        ],
        moddleExtensions: {
          camunda: CamundaModdle
        }
      }
    );

    // then
    expect(result.error).not.to.exist;
  });

  (singleStart === 'bpmn' ? it.only : it)('should import simple process (bpmn)', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const result = await createModeler(
      diagramXml,
      {
        additionalModules: [
          CamundaModdleExtension,
          BpmnPropertiesPanel,
          BpmnPropertiesProvider
        ]
      }
    );

    // then
    expect(result.error).not.to.exist;
  });

  it('should render properties panel when root element was added', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    await createModeler(diagramXml);

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
  });

  it('should remove properties panel when root element was deleted', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    const { modeler } = await createModeler(diagramXml);

    const eventBus = modeler.get('eventBus');

    // when
    eventBus.fire('root.removed');

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;
  });

  it('should allow providing custom entries', async function() {

    // given
    const diagramXml = require('test/fixtures/service-task.bpmn').default;

    const modules = [
      ZeebeModdleExtension,
      BpmnPropertiesPanel,
      BpmnPropertiesProvider,
      ZeebePropertiesProvider,
      ExamplePropertiesProvider
    ];

    // when
    await createModeler(diagramXml, {
      additionalModules: modules
    });

    // then
    expect(getGroup(propertiesContainer, 'foo-group')).to.exist;
  });

  it('should ignore implicit root', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const { modeler } = await createModeler(diagramXml, {
      shouldImport: false,
      propertiesPanel: {}
    });

    const canvas = modeler.get('canvas');
    const rootElement = canvas.getRootElement();

    // assume
    expect(rootElement.isImplicit).to.be.true;

    // when
    const propertiesPanel = modeler.get('propertiesPanel');
    propertiesPanel.attachTo(propertiesContainer);
    propertiesPanel._render(rootElement);

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;
  });

  it('should ignore implicit root - legacy', async function() {

    // given
    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const { modeler } = await createModeler(diagramXml, {
      shouldImport: false,
      propertiesPanel: {}
    });

    const implicitRootElement = {
      id: '__implicitroot',
      children: []
    };

    // when
    const propertiesPanel = modeler.get('propertiesPanel');
    propertiesPanel.attachTo(propertiesContainer);
    propertiesPanel._render(implicitRootElement);

    // then
    expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;
  });

  describe('providers', function() {

    const diagramXML = require('test/fixtures/simple.bpmn').default;

    function inject(fn) {

      return async function() {
        const {
          modeler
        } = await createModeler(diagramXML, {
          additionalModules: [
            BpmnPropertiesPanel,
            BpmnPropertiesProvider
          ]
        });

        await modeler.invoke(fn);
      };
    }

    it('should ignore incompatible provider', inject(function(propertiesPanel) {

      // assume
      expect(propertiesPanel._getProviders()).to.have.length(1);

      // given
      const incompatibleProvider = {
        getTabs: function() {
          return [];
        }
      };

      // when
      propertiesPanel.registerProvider(incompatibleProvider);

      // then
      // incompatible provider was not added
      expect(propertiesPanel._getProviders()).to.have.length(1);
    }));

  });

  describe('integration', function() {

    it('should ensure creating + importing -> attaching works', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      // when
      const { modeler } = await createModeler(diagramXml, {
        propertiesPanel: {}
      });

      // assume
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;

      // and when
      const propertiesPanel = modeler.get('propertiesPanel');
      propertiesPanel.attachTo(propertiesContainer);

      // then
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
    });

    it('should ensure creating + attaching -> importing works', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      // when
      const { modeler } = await createModeler(null, {
        shouldImport: false,
        propertiesPanel: {
          parent: propertiesContainer
        }
      });

      // assume
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;

      // and when
      await modeler.importXML(diagramXml);

      // then
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
    });

    it('should ensure creating -> no import -> attaching -> import works', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      const { modeler } = await createModeler(null, {
        shouldImport: false,
        propertiesPanel: {}
      });

      const propertiesPanel = modeler.get('propertiesPanel');

      // when
      propertiesPanel.attachTo(propertiesContainer);

      // assume
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.not.exist;

      // and when
      await modeler.importXML(diagramXml);

      // then
      expect(domQuery('.bio-properties-panel', propertiesContainer)).to.exist;
    });

    it('should keep state during detach and attach', async function() {

      // given
      const diagramXml = require('test/fixtures/simple.bpmn').default;

      let modeler;
      await act(async () => {
        const result = await createModeler(diagramXml);
        modeler = result.modeler;
      });

      const elementRegistry = modeler.get('elementRegistry');
      const selection = modeler.get('selection');
      const propertiesPanel = modeler.get('propertiesPanel');

      await act(() => selection.select(elementRegistry.get('StartEvent_1')));

      // assume
      expect(getHeaderName(propertiesContainer)).to.eql('start');

      // when
      propertiesPanel.detach();
      propertiesPanel.attachTo(propertiesContainer);

      // then
      expect(getHeaderName(propertiesContainer)).to.eql('start');
    });

  });

  it('#attachTo', async function() {

    // given
    const container = domify('

');
    TestContainer.get(this).appendChild(container);

    const modules = [
      BpmnPropertiesPanel,
      BpmnPropertiesProvider
    ];

    const diagramXml = require('test/fixtures/simple.bpmn').default;

    const { modeler } = await createModeler(diagramXml, {
      propertiesPanel: {},
      additionalModules: modules
    });

    const propertiesPanel = modeler.get('propertiesPanel');

    // when
    propertiesPanel.attachTo(container);

    // then
    expect(domQuery('.bio-properties-panel', container)).to.exist;
  });

  it('#detach', async function() {

    // given
    const container = domify('

');
    TestContainer.get(this).appendChild(container);

    const modules = [
      BpmnPropertiesPanel,
      BpmnPropertiesProvider
    ];

    const diagramXml = require('test/fixtures/simple.bpmn').default;

    // when
    const { modeler } = await createModeler(diagramXml, {
      propertiesPanel: {},
      additionalModules: modules
    });

    const propertiesPanel = modeler.get('propertiesPanel');

    propertiesPanel.attachTo(container);

    // when
    propertiesPanel.detach();

    // then
    expect(domQuery('.bio-properties-panel', container)).to.not.exist;
  });

  describe('event emitting', function() {

    it('should emit <propertiesPanel.attach>', async function() {

      // given
      const spy = sinon.spy();

      const container = domify('

');
      TestContainer.get(this).appendChild(container);

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.attach', spy);

      // when
      propertiesPanel.attachTo(container);

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should emit <propertiesPanel.detach>', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.detach', spy);

      // when
      propertiesPanel.detach();

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should emit <propertiesPanel.rendered>', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.rendered', spy);

      // when
      propertiesPanel._render();

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should NOT emit <propertiesPanel.rendered> on attach', async function() {

      // given
      const spy = sinon.spy();

      const container = domify('

');
      TestContainer.get(this).appendChild(container);

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.rendered', spy);

      // when
      propertiesPanel.attachTo(container);

      // then
      expect(spy).to.not.have.been.called;
    });

    it('should emit <propertiesPanel.destroyed>', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');

      eventBus.on('propertiesPanel.destroyed', spy);

      // when
      propertiesPanel._destroy();

      // then
      expect(spy).to.have.been.calledOnce;
    });

    it('should emit keyboard events for undo/redo when editing', async function() {

      // given
      const spy = sinon.spy();

      const diagramXml = require('test/fixtures/service-task.bpmn').default;

      const { modeler } = await createModeler(diagramXml);

      const eventBus = modeler.get('eventBus');
      const propertiesPanel = modeler.get('propertiesPanel');
      const keyboard = modeler.get('keyboard');
      const inputField = propertiesPanel._container.querySelector('input');

      eventBus.on('keyboard.keydown', 10000, spy);

      // when
      // select all
      keyboard._keyHandler({ key: 'a', ctrlKey: true, target: inputField, preventDefault: () => {} });

      // then
      // use browser default
      expect(spy).to.not.be.called;

      // when
      // undo/redo
      keyboard._keyHandler({ key: 'z', metaKey: true, target: inputField, preventDefault: () => {} });
      keyboard._keyHandler({ key: 'y', ctrlKey: true, target: inputField, preventDefault: () => {} });

      // then
      // fire events
      expect(spy).to.have.been.calledTwice;
    });

  });

});

// helpers /////////////////////

function getGroup(container, id) {
  return domQuery(`[data-group-id="group-${id}"`, container);
}

function getHeaderName(container) {
  return domQuery('.bio-properties-panel-header-label', container).innerText;
}
<|endoftext|>


<commit_before>'use strict';
module.exports = () => {
  return {
    prefix: 'subg-',
    checkVoiceUpdate(client, oldMember, newMember) {
      // Only in Armory and Test Discord
      if (newMember.guild.id !== '84764735832068096' && newMember.guild.id !== '140025699867164673') return;
      const oldVoice = oldMember.voiceChannel;
      const newVoice = newMember.voiceChannel;

      // Left a sub voice channel and is not in a new voice channel
      if (oldVoice && !newVoice && oldVoice.name.includes(this.prefix)) return this.moveMembers(client, false);
      // Joined a sub voice channel and was not in one prior
      if (!oldVoice && newVoice && newVoice.name.includes(this.prefix)) return this.moveMembers(client, false);
      // Moved voice channels
      if (oldVoice && newVoice) {
        // If coming from other voice channel to subg- voice
        if (!oldVoice.name.includes(this.prefix) &&
          newVoice.name.includes(this.prefix)) return this.moveMembers(client, false);
        // If leaving from subg- voice to channel other than streamer lounge
        if (oldVoice.name.includes(this.prefix) &&
          newVoice.name !== 'Streamer Lounge' &&
          newVoice.position > oldVoice.position) return this.moveMembers(client, false);
      }
    },
    moveMembers(client, sendToStreamerLounge) {
      return new Promise((resolve, reject) => {
        // Get Guild
        const guild = client.guilds.get('84764735832068096') || client.guilds.get('140025699867164673');
        if (!guild) return reject('No Guild');
        // Get subg- voice channels
        const voiceChannels = guild.channels
          .filter(x => x.type === 'voice' && x.name.includes(this.prefix))
          .sort((a, b) => {
            const posA = parseInt(a.name.split(this.prefix)[1]);
            const posB = parseInt(b.name.split(this.prefix)[1]);
            return posA - posB;
          });
        if (!voiceChannels || voiceChannels.length === 0) return reject('No Voice Channels');
        // Get streamer lounge voice channel
        const streamerLounge = guild.channels.find('name', 'Streamer Lounge');
        if (!streamerLounge) return reject('No Streamer Lounge');
        const membersInVoiceChannels = [];
        voiceChannels.forEach(x => {
          if (x.members.first()) {
            membersInVoiceChannels.push(x.members.first());
          }
        });
        const voiceChannelIds = voiceChannels.map(x => x.id);
        if (sendToStreamerLounge) {
          voiceChannelIds.unshift(streamerLounge.id);
        }
        const setVoiceArray = [];
        for (let i = 0; i < membersInVoiceChannels.length; i++) {
          // Only set channel if channel is different
          // Reduces api calls
          if (membersInVoiceChannels[i].voiceChannel.id !== voiceChannelIds[i]) {
            setVoiceArray.push(membersInVoiceChannels[i].setVoiceChannel(voiceChannelIds[i]));
          }
        }
        Promise.all(setVoiceArray)
          .then(() => {
            const memberToNotify = sendToStreamerLounge ? membersInVoiceChannels[1] : membersInVoiceChannels[0];
            if (memberToNotify) {
              const anneMLG = client.emojis.get('471888236927516703') || client.emojis.get('471888150281715712');
              memberToNotify.user.send('Hello!\nYou are in Position **#1** in queue for ' + // eslint-disable-line prefer-template, max-len
                'AnneMunition\'s Sub Games.\nIf you are using a microphone, please make sure it is connected and ' +
                'working.\nHave a fun time! ' + (anneMLG || ''))
                .catch(() => {
                  // Do Nothing
                });
            }
            resolve();
          })
          .catch(reject);
      });
    },
  };
};
<commit_msg>add comments<commit_after>'use strict';
module.exports = () => {
  return {
    prefix: 'subg-',
    checkVoiceUpdate(client, oldMember, newMember) {
      // Only in Armory and Test Discord
      if (newMember.guild.id !== '84764735832068096' && newMember.guild.id !== '140025699867164673') return;
      const oldVoice = oldMember.voiceChannel;
      const newVoice = newMember.voiceChannel;

      // Left a sub voice channel and is not in a new voice channel
      if (oldVoice && !newVoice && oldVoice.name.includes(this.prefix)) return this.moveMembers(client, false);
      // Joined a sub voice channel and was not in one prior
      if (!oldVoice && newVoice && newVoice.name.includes(this.prefix)) return this.moveMembers(client, false);
      // Moved voice channels
      if (oldVoice && newVoice) {
        // If coming from other voice channel to subg- voice
        if (!oldVoice.name.includes(this.prefix) &&
          newVoice.name.includes(this.prefix)) return this.moveMembers(client, false);
        // If leaving from subg- voice to channel other than streamer lounge
        if (oldVoice.name.includes(this.prefix) &&
          newVoice.name !== 'Streamer Lounge' &&
          // Only process subg- to subg- moves if moving down in position
          newVoice.position > oldVoice.position) return this.moveMembers(client, false);
      }
    },
    moveMembers(client, sendToStreamerLounge) {
      return new Promise((resolve, reject) => {
        // Get Guild
        const guild = client.guilds.get('84764735832068096') || client.guilds.get('140025699867164673');
        if (!guild) return reject('No Guild');
        // Get subg- voice channels
        const voiceChannels = guild.channels
          .filter(x => x.type === 'voice' && x.name.includes(this.prefix))
          .sort((a, b) => {
            const posA = parseInt(a.name.split(this.prefix)[1]);
            const posB = parseInt(b.name.split(this.prefix)[1]);
            return posA - posB;
          });
        if (!voiceChannels || voiceChannels.length === 0) return reject('No Voice Channels');
        // Get streamer lounge voice channel
        const streamerLounge = guild.channels.find('name', 'Streamer Lounge');
        if (!streamerLounge) return reject('No Streamer Lounge');

        // Array to squeeze together members
        const membersInVoiceChannels = [];
        voiceChannels.forEach(x => {
          if (x.members.first()) {
            membersInVoiceChannels.push(x.members.first());
          }
        });
        // Array of sub voice channel ids
        const voiceChannelIds = voiceChannels.map(x => x.id);
        // If next command was run, add streamer lounge to the beginning of the voice channel array
        if (sendToStreamerLounge) {
          voiceChannelIds.unshift(streamerLounge.id);
        }
        // Promise array of Discord.js calls to setVoiceChannel
        const setVoiceArray = [];
        for (let i = 0; i < membersInVoiceChannels.length; i++) {
          // Only set channel if channel is different
          // Reduces api calls
          if (membersInVoiceChannels[i].voiceChannel.id !== voiceChannelIds[i]) {
            setVoiceArray.push(membersInVoiceChannels[i].setVoiceChannel(voiceChannelIds[i]));
          }
        }
        // setVoiceChannel multi request
        Promise.all(setVoiceArray)
          .then(() => {
            // Get the member that would be in position 1
            const memberToNotify = sendToStreamerLounge ? membersInVoiceChannels[1] : membersInVoiceChannels[0];
            // Notify that member if they exist
            if (memberToNotify) {
              const anneMLG = client.emojis.get('471888236927516703') || client.emojis.get('471888150281715712');
              memberToNotify.user.send('Hello!\nYou are in Position **#1** in queue for ' + // eslint-disable-line prefer-template, max-len
                'AnneMunition\'s Sub Games.\nIf you are using a microphone, please make sure it is connected and ' +
                'working.\nHave a fun time! ' + (anneMLG || ''))
                .catch(() => {
                  // Do Nothing
                  // Don't error if we cannot send the member a DM. They might have the bot blocked
                });
            }
            resolve();
          })
          .catch(reject);
      });
    },
  };
};
<|endoftext|>


<commit_before>
describe('Directive: ivhMultiSelect', function() {
  'use strict';

  beforeEach(module('ivh.multiSelect'));

  var collect;

  beforeEach(inject(function(ivhMultiSelectCollectFilter) {
    collect = ivhMultiSelectCollectFilter;
  }));

  it('should gather collect selected item ids', function() {
    var list = []
      , item = {selected: true, id: 'foo'};
    collect(list, item);
    expect(list.length).toBe(1);
    expect(list[0]).toBe('foo');
  });

  it('should remove deselected item ids', function() {
    var list = ['foo', 'bar']
      , item = {selected: false, id: 'foo'};
    collect(list, item);
    expect(list.length).toBe(1);
    expect(list[0]).toBe('bar');
  });
});

<commit_msg>test: Fix describe label for ivhMultiSelectCollect tests<commit_after>
describe('Filter: ivhMultiSelectCollect', function() {
  'use strict';

  beforeEach(module('ivh.multiSelect'));

  var collect;

  beforeEach(inject(function(ivhMultiSelectCollectFilter) {
    collect = ivhMultiSelectCollectFilter;
  }));

  it('should gather collect selected item ids', function() {
    var list = []
      , item = {selected: true, id: 'foo'};
    collect(list, item);
    expect(list.length).toBe(1);
    expect(list[0]).toBe('foo');
  });

  it('should remove deselected item ids', function() {
    var list = ['foo', 'bar']
      , item = {selected: false, id: 'foo'};
    collect(list, item);
    expect(list.length).toBe(1);
    expect(list[0]).toBe('bar');
  });
});

<|endoftext|>


<commit_before>/* jshint quotmark: false */
'use strict';

require('colors');

var FS = require('fs'),
    PATH = require('path'),
    SVGO = require('../svgo.js'),
    YAML = require('js-yaml'),
    PKG = require('../../package.json'),
    mkdirp = require('mkdirp'),
    encodeSVGDatauri = require('./tools.js').encodeSVGDatauri,
    decodeSVGDatauri = require('./tools.js').decodeSVGDatauri,
    regSVGFile = /\.svg$/;

/**
 * Command-Option-Argument.
 *
 * @see https://github.com/veged/coa
 */
module.exports = require('coa').Cmd()
    .helpful()
    .name(PKG.name)
    .title(PKG.description)
    .opt()
        .name('version').title('Version')
        .short('v').long('version')
        .only()
        .flag()
        .act(function() {
            // output the version to stdout instead of stderr if returned
            process.stdout.write(PKG.version + '\n');
            // coa will run `.toString` on the returned value and send it to stderr
            return '';
        })
        .end()
    .opt()
        .name('input').title('Input file, "-" for STDIN')
        .short('i').long('input')
        .val(function(val) {
            return val || this.reject("Option '--input' must have a value.");
        })
        .end()
    .opt()
        .name('string').title('Input SVG data string')
        .short('s').long('string')
        .end()
    .opt()
        .name('folder').title('Input folder, optimize and rewrite all *.svg files')
        .short('f').long('folder')
        .val(function(val) {
            return val || this.reject("Option '--folder' must have a value.");
        })
        .end()
    .opt()
        .name('output').title('Output file or folder (by default the same as the input), "-" for STDOUT')
        .short('o').long('output')
        .val(function(val) {
            return val || this.reject("Option '--output' must have a value.");
        })
        .end()
    .opt()
        .name('precision').title('Set number of digits in the fractional part, overrides plugins params')
        .short('p').long('precision')
        .val(function(val) {
            return !isNaN(val) ? val : this.reject("Option '--precision' must be an integer number");
        })
        .end()
    .opt()
        .name('config').title('Config file or JSON string to extend or replace default')
        .long('config')
        .val(function(val) {
            return val || this.reject("Option '--config' must have a value.");
        })
        .end()
    .opt()
        .name('disable').title('Disable plugin by name')
        .long('disable')
        .arr()
        .val(function(val) {
            return val || this.reject("Option '--disable' must have a value.");
        })
        .end()
    .opt()
        .name('enable').title('Enable plugin by name')
        .long('enable')
        .arr()
        .val(function(val) {
            return val || this.reject("Option '--enable' must have a value.");
        })
        .end()
    .opt()
        .name('datauri').title('Output as Data URI string (base64, URI encoded or unencoded)')
        .long('datauri')
        .val(function(val) {
            return val || this.reject("Option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'");
        })
        .end()
    .opt()
        .name('multipass').title('Enable multipass')
        .long('multipass')
        .flag()
        .end()
    .opt()
        .name('pretty').title('Make SVG pretty printed')
        .long('pretty')
        .flag()
        .end()
    .opt()
        .name('indent').title('Indent number when pretty printing SVGs')
        .long('indent')
        .val(function(val) {
            return !isNaN(val) ? val : this.reject("Option '--indent' must be an integer number");
        })
        .end()
    .opt()
        .name('quiet').title('Only output error messages, not regular status messages')
        .short('q').long('quiet')
        .flag()
        .end()
    .opt()
        .name('show-plugins').title('Show available plugins and exit')
        .long('show-plugins')
        .flag()
        .end()
    .arg()
        .name('input').title('Alias to --input')
        .end()
    .arg()
        .name('output').title('Alias to --output')
        .end()
    .act(function(opts, args) {
        var input = args && args.input ? args.input : opts.input,
            output = args && args.output ? args.output : opts.output,
            config = {};

        // --show-plugins
        if (opts['show-plugins']) {
            showAvailablePlugins();
            process.exit(0);
        }

        // w/o anything
        if (
            (!input || input === '-') &&
            !opts.string &&
            !opts.stdin &&
            !opts.folder &&
            process.stdin.isTTY !== false
        ) return this.usage();

        if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) {
            var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
            if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
                console.error(`Error: ${PKG.name} requires Node.js version ${nodeVersion} or higher.`);
                process.exit(1);
            }
        }

        // --config
        if (opts.config) {
            // string
            if (opts.config.charAt(0) === '{') {
                try {
                    config = JSON.parse(opts.config);
                } catch (e) {
                    console.error("Error: Couldn't parse config JSON.");
                    console.error(String(e));
                    return;
                }
            // external file
            } else {
                var configPath = PATH.resolve(opts.config);
                try {
                    // require() adds some weird output on YML files
                    config = JSON.parse(FS.readFileSync(configPath, 'utf8'));
                } catch (err) {
                    if (err.code === 'ENOENT') {
                        console.error(`Error: couldn't find config file '${opts.config}'.`);
                        return;
                    } else if (err.code === 'EISDIR') {
                        console.error(`Error: directory '${opts.config}' is not a config file.`);
                        return;
                    }
                    config = YAML.safeLoad(FS.readFileSync(configPath, 'utf8'));

                    if (!config || Array.isArray(config)) {
                        console.error(`Error: invalid config file '${opts.config}'.`);
                        return;
                    }
                }
            }
        }

        // --quiet
        if (opts.quiet) {
            config.quiet = opts.quiet;
        }

        // --precision
        if (opts.precision) {
            config.floatPrecision = Math.max(0, parseInt(opts.precision));
        }

        // --disable
        if (opts.disable) {
            changePluginsState(opts.disable, false, config);
        }

        // --enable
        if (opts.enable) {
            changePluginsState(opts.enable, true, config);
        }

        // --multipass
        if (opts.multipass) {
            config.multipass = true;
        }

        // --pretty
        if (opts.pretty) {
            config.js2svg = config.js2svg || {};
            config.js2svg.pretty = true;
            if (opts.indent) {
                config.js2svg.indent = parseInt(opts.indent, 10);
            }
        }

        // --output
        if (opts.output) {
            config.output = opts.output;
        }

        // --folder
        if (opts.folder) {
            optimizeFolder(opts.folder, config, output);

            return;
        }

        // --input
        if (input) {
            // STDIN
            if (input === '-') {
                var data = '';

                process.stdin.pause();

                process.stdin
                    .on('data', function(chunk) {
                        data += chunk;
                    })
                    .once('end', function() {
                        optimizeFromString(data, config, opts.datauri, input, output);
                    })
                    .resume();
            // file
            } else {
                FS.readFile(input, 'utf8', function(err, data) {
                    if (err) {
                        if (err.code === 'EISDIR')
                            optimizeFolder(input, config, output);
                        else if (err.code === 'ENOENT')
                            console.error(`Error: no such file or directory '${input}'.`);
                        else
                            console.error(err);
                        return;
                    }
                    optimizeFromString(data, config, opts.datauri, input, output);
                });
            }

        // --string
        } else if (opts.string) {
            opts.string = decodeSVGDatauri(opts.string);

            optimizeFromString(opts.string, config, opts.datauri, input, output);
        }
    });

function optimizeFromString(svgstr, config, datauri, input, output) {
    var startTime = Date.now(config),
        time,
        inBytes = Buffer.byteLength(svgstr, 'utf8'),
        outBytes,
        svgo = new SVGO(config);

    svgo.optimize(svgstr, function(result) {
        if (result.error) {
            console.error(result.error);
            return;
        }

        if (datauri) {
            result.data = encodeSVGDatauri(result.data, datauri);
        }

        outBytes = Buffer.byteLength(result.data, 'utf8');
        time = Date.now() - startTime;

        // stdout
        if (output === '-' || (!input || input === '-') && !output) {
            process.stdout.write(result.data + '\n');
        // file
        } else {
            // overwrite input file if there is no output
            if (!output && input) {
                output = input;
            }

            if (!config.quiet) {
                console.log('\r');
            }

            saveFileAndPrintInfo(config, result.data, output, inBytes, outBytes, time);
        }
    });
}

function saveFileAndPrintInfo(config, data, path, inBytes, outBytes, time) {
    FS.writeFile(path, data, 'utf8', function() {
        if (config.quiet) {
            return;
        }

        // print time info
        printTimeInfo(time);

        // print optimization profit info
        printProfitInfo(inBytes, outBytes);

    });
}

function printTimeInfo(time) {
    console.log(`Done in ${time} ms!`);
}

function printProfitInfo(inBytes, outBytes) {
    var profitPercents = 100 - outBytes * 100 / inBytes;

    console.log(
        (Math.round((inBytes / 1024) * 1000) / 1000) + ' KiB' +
        (profitPercents < 0 ? ' + ' : ' - ') +
        String(Math.abs((Math.round(profitPercents * 10) / 10)) + '%').green + ' = ' +
        (Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB\n'
    );
}

/**
 * Change plugins state by names array.
 *
 * @param {Array} names plugins names
 * @param {Boolean} state active state
 * @param {Object} config original config
 * @return {Object} changed config
 */
function changePluginsState(names, state, config) {
    // extend config
    if (config.plugins) {
        for (var name of names) {
            var matched = false,
                key;

            for (var plugin of config.plugins) {
                // get plugin name
                if (typeof plugin === 'object') {
                    key = Object.keys(plugin)[0];
                } else {
                    key = plugin;
                }

                // if there is such a plugin name
                if (key === name) {
                    // don't replace plugin's params with true
                    if (typeof plugin[key] !== 'object' || !state) {
                        plugin[key] = state;
                    }
                    // mark it as matched
                    matched = true;
                }
            }

            // if not matched and current config is not full
            if (!matched && !config.full) {
                // push new plugin Object
                config.plugins.push({ [name]: state });
                matched = true;
            }
        }
    // just push
    } else {
        config.plugins = names.map(name => ({ [name]: state }));
    }
    return config;
}

function optimizeFolder(dir, config, output) {
    var svgo = new SVGO(config);

    if (!config.quiet) {
        console.log(`Processing directory '${dir}':\n`);
    }

    // absoluted folder path
    var path = PATH.resolve(dir);

    // list folder content
    FS.readdir(path, function(err, files) {
        if (err) {
            console.error(err);
            return;
        }

        if (!files.length) {
            console.log(`Directory '${dir}' is empty.`);
            return;
        }

        var i = 0,
            found = false;

        function optimizeFile(file) {
            // absoluted file path
            var filepath = PATH.resolve(path, file);
            var outfilepath = output ? PATH.resolve(output, file) : filepath;

            // check if file name matches *.svg
            if (regSVGFile.test(filepath)) {
                found = true;
                FS.readFile(filepath, 'utf8', function(err, data) {
                    if (err) {
                        console.error(err);
                        return;
                    }

                    var startTime = Date.now(),
                        time,
                        inBytes = Buffer.byteLength(data, 'utf8'),
                        outBytes;

                    svgo.optimize(data, function(result) {
                        if (result.error) {
                            console.error(result.error);
                            return;
                        }

                        outBytes = Buffer.byteLength(result.data, 'utf8');
                        time = Date.now() - startTime;

                        writeOutput();

                        function writeOutput() {
                            FS.writeFile(outfilepath, result.data, 'utf8', report);
                        }

                        function report(err) {
                            if (err) {
                                if (err.code === 'ENOENT') {
                                    mkdirp(output, writeOutput);
                                    return;
                                } else if (err.code === 'ENOTDIR') {
                                    console.error(`Error: output '${output}' is not a directory.`);
                                    return;
                                }
                                console.error(err);
                                return;
                            }

                            if (!config.quiet) {
                                console.log(file + ':');

                                // print time info
                                printTimeInfo(time);

                                // print optimization profit info
                                printProfitInfo(inBytes, outBytes);
                            }

                            //move on to the next file
                            if (++i < files.length) {
                                optimizeFile(files[i]);
                            }
                        }
                    });
                });
            }
            //move on to the next file
            else if (++i < files.length) {
                optimizeFile(files[i]);
            } else if (!found) {
                console.log('No SVG files have been found.');
            }
        }

        optimizeFile(files[i]);
    });
}

function showAvailablePlugins() {
    var svgo = new SVGO(),
        // Flatten an array of plugins grouped per type and sort alphabetically
        list = [];

    console.log('Currently available plugins:');
    list = list.concat.apply(list, svgo.config.plugins).sort((a, b) => a.name.localeCompare(b.name));

    for (var plugin of list) {
        console.log(` [ ${plugin.name.green} ] ${plugin.description}`);
    };
};
<commit_msg>Lint fixes<commit_after>/* jshint quotmark: false */
'use strict';

require('colors');

var FS = require('fs'),
    PATH = require('path'),
    SVGO = require('../svgo.js'),
    YAML = require('js-yaml'),
    PKG = require('../../package.json'),
    mkdirp = require('mkdirp'),
    encodeSVGDatauri = require('./tools.js').encodeSVGDatauri,
    decodeSVGDatauri = require('./tools.js').decodeSVGDatauri,
    regSVGFile = /\.svg$/;

/**
 * Command-Option-Argument.
 *
 * @see https://github.com/veged/coa
 */
module.exports = require('coa').Cmd()
    .helpful()
    .name(PKG.name)
    .title(PKG.description)
    .opt()
        .name('version').title('Version')
        .short('v').long('version')
        .only()
        .flag()
        .act(function() {
            // output the version to stdout instead of stderr if returned
            process.stdout.write(PKG.version + '\n');
            // coa will run `.toString` on the returned value and send it to stderr
            return '';
        })
        .end()
    .opt()
        .name('input').title('Input file, "-" for STDIN')
        .short('i').long('input')
        .val(function(val) {
            return val || this.reject("Option '--input' must have a value.");
        })
        .end()
    .opt()
        .name('string').title('Input SVG data string')
        .short('s').long('string')
        .end()
    .opt()
        .name('folder').title('Input folder, optimize and rewrite all *.svg files')
        .short('f').long('folder')
        .val(function(val) {
            return val || this.reject("Option '--folder' must have a value.");
        })
        .end()
    .opt()
        .name('output').title('Output file or folder (by default the same as the input), "-" for STDOUT')
        .short('o').long('output')
        .val(function(val) {
            return val || this.reject("Option '--output' must have a value.");
        })
        .end()
    .opt()
        .name('precision').title('Set number of digits in the fractional part, overrides plugins params')
        .short('p').long('precision')
        .val(function(val) {
            return !isNaN(val) ? val : this.reject("Option '--precision' must be an integer number");
        })
        .end()
    .opt()
        .name('config').title('Config file or JSON string to extend or replace default')
        .long('config')
        .val(function(val) {
            return val || this.reject("Option '--config' must have a value.");
        })
        .end()
    .opt()
        .name('disable').title('Disable plugin by name')
        .long('disable')
        .arr()
        .val(function(val) {
            return val || this.reject("Option '--disable' must have a value.");
        })
        .end()
    .opt()
        .name('enable').title('Enable plugin by name')
        .long('enable')
        .arr()
        .val(function(val) {
            return val || this.reject("Option '--enable' must have a value.");
        })
        .end()
    .opt()
        .name('datauri').title('Output as Data URI string (base64, URI encoded or unencoded)')
        .long('datauri')
        .val(function(val) {
            return val || this.reject("Option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'");
        })
        .end()
    .opt()
        .name('multipass').title('Enable multipass')
        .long('multipass')
        .flag()
        .end()
    .opt()
        .name('pretty').title('Make SVG pretty printed')
        .long('pretty')
        .flag()
        .end()
    .opt()
        .name('indent').title('Indent number when pretty printing SVGs')
        .long('indent')
        .val(function(val) {
            return !isNaN(val) ? val : this.reject("Option '--indent' must be an integer number");
        })
        .end()
    .opt()
        .name('quiet').title('Only output error messages, not regular status messages')
        .short('q').long('quiet')
        .flag()
        .end()
    .opt()
        .name('show-plugins').title('Show available plugins and exit')
        .long('show-plugins')
        .flag()
        .end()
    .arg()
        .name('input').title('Alias to --input')
        .end()
    .arg()
        .name('output').title('Alias to --output')
        .end()
    .act(function(opts, args) {
        var input = args && args.input ? args.input : opts.input,
            output = args && args.output ? args.output : opts.output,
            config = {};

        // --show-plugins
        if (opts['show-plugins']) {
            showAvailablePlugins();
            process.exit(0);
        }

        // w/o anything
        if (
            (!input || input === '-') &&
            !opts.string &&
            !opts.stdin &&
            !opts.folder &&
            process.stdin.isTTY !== false
        ) return this.usage();

        if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) {
            var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
            if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
                console.error(`Error: ${PKG.name} requires Node.js version ${nodeVersion} or higher.`);
                process.exit(1);
            }
        }

        // --config
        if (opts.config) {
            // string
            if (opts.config.charAt(0) === '{') {
                try {
                    config = JSON.parse(opts.config);
                } catch (e) {
                    console.error("Error: Couldn't parse config JSON.");
                    console.error(String(e));
                    return;
                }
            // external file
            } else {
                var configPath = PATH.resolve(opts.config);
                try {
                    // require() adds some weird output on YML files
                    config = JSON.parse(FS.readFileSync(configPath, 'utf8'));
                } catch (err) {
                    if (err.code === 'ENOENT') {
                        console.error(`Error: couldn't find config file '${opts.config}'.`);
                        return;
                    } else if (err.code === 'EISDIR') {
                        console.error(`Error: directory '${opts.config}' is not a config file.`);
                        return;
                    }
                    config = YAML.safeLoad(FS.readFileSync(configPath, 'utf8'));

                    if (!config || Array.isArray(config)) {
                        console.error(`Error: invalid config file '${opts.config}'.`);
                        return;
                    }
                }
            }
        }

        // --quiet
        if (opts.quiet) {
            config.quiet = opts.quiet;
        }

        // --precision
        if (opts.precision) {
            config.floatPrecision = Math.max(0, parseInt(opts.precision));
        }

        // --disable
        if (opts.disable) {
            changePluginsState(opts.disable, false, config);
        }

        // --enable
        if (opts.enable) {
            changePluginsState(opts.enable, true, config);
        }

        // --multipass
        if (opts.multipass) {
            config.multipass = true;
        }

        // --pretty
        if (opts.pretty) {
            config.js2svg = config.js2svg || {};
            config.js2svg.pretty = true;
            if (opts.indent) {
                config.js2svg.indent = parseInt(opts.indent, 10);
            }
        }

        // --output
        if (opts.output) {
            config.output = opts.output;
        }

        // --folder
        if (opts.folder) {
            optimizeFolder(opts.folder, config, output);

            return;
        }

        // --input
        if (input) {
            // STDIN
            if (input === '-') {
                var data = '';

                process.stdin.pause();

                process.stdin
                    .on('data', function(chunk) {
                        data += chunk;
                    })
                    .once('end', function() {
                        optimizeFromString(data, config, opts.datauri, input, output);
                    })
                    .resume();
            // file
            } else {
                FS.readFile(input, 'utf8', function(err, data) {
                    if (err) {
                        if (err.code === 'EISDIR')
                            optimizeFolder(input, config, output);
                        else if (err.code === 'ENOENT')
                            console.error(`Error: no such file or directory '${input}'.`);
                        else
                            console.error(err);
                        return;
                    }
                    optimizeFromString(data, config, opts.datauri, input, output);
                });
            }

        // --string
        } else if (opts.string) {
            opts.string = decodeSVGDatauri(opts.string);

            optimizeFromString(opts.string, config, opts.datauri, input, output);
        }
    });

function optimizeFromString(svgstr, config, datauri, input, output) {
    var startTime = Date.now(config),
        time,
        inBytes = Buffer.byteLength(svgstr, 'utf8'),
        outBytes,
        svgo = new SVGO(config);

    svgo.optimize(svgstr, function(result) {
        if (result.error) {
            console.error(result.error);
            return;
        }

        if (datauri) {
            result.data = encodeSVGDatauri(result.data, datauri);
        }

        outBytes = Buffer.byteLength(result.data, 'utf8');
        time = Date.now() - startTime;

        // stdout
        if (output === '-' || (!input || input === '-') && !output) {
            process.stdout.write(result.data + '\n');
        // file
        } else {
            // overwrite input file if there is no output
            if (!output && input) {
                output = input;
            }

            if (!config.quiet) {
                console.log('\r');
            }

            saveFileAndPrintInfo(config, result.data, output, inBytes, outBytes, time);
        }
    });
}

function saveFileAndPrintInfo(config, data, path, inBytes, outBytes, time) {
    FS.writeFile(path, data, 'utf8', function() {
        if (config.quiet) {
            return;
        }

        // print time info
        printTimeInfo(time);

        // print optimization profit info
        printProfitInfo(inBytes, outBytes);

    });
}

function printTimeInfo(time) {
    console.log(`Done in ${time} ms!`);
}

function printProfitInfo(inBytes, outBytes) {
    var profitPercents = 100 - outBytes * 100 / inBytes;

    console.log(
        (Math.round((inBytes / 1024) * 1000) / 1000) + ' KiB' +
        (profitPercents < 0 ? ' + ' : ' - ') +
        String(Math.abs((Math.round(profitPercents * 10) / 10)) + '%').green + ' = ' +
        (Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB\n'
    );
}

/**
 * Change plugins state by names array.
 *
 * @param {Array} names plugins names
 * @param {Boolean} state active state
 * @param {Object} config original config
 * @return {Object} changed config
 */
function changePluginsState(names, state, config) {
    // extend config
    if (config.plugins) {
        for (var name of names) {
            var matched = false,
                key;

            for (var plugin of config.plugins) {
                // get plugin name
                if (typeof plugin === 'object') {
                    key = Object.keys(plugin)[0];
                } else {
                    key = plugin;
                }

                // if there is such a plugin name
                if (key === name) {
                    // don't replace plugin's params with true
                    if (typeof plugin[key] !== 'object' || !state) {
                        plugin[key] = state;
                    }
                    // mark it as matched
                    matched = true;
                }
            }

            // if not matched and current config is not full
            if (!matched && !config.full) {
                // push new plugin Object
                config.plugins.push({ [name]: state });
                matched = true;
            }
        }
    // just push
    } else {
        config.plugins = names.map(name => ({ [name]: state }));
    }
    return config;
}

function optimizeFolder(dir, config, output) {
    var svgo = new SVGO(config);

    if (!config.quiet) {
        console.log(`Processing directory '${dir}':\n`);
    }

    // absoluted folder path
    var path = PATH.resolve(dir);

    // list folder content
    FS.readdir(path, function(err, files) {
        if (err) {
            console.error(err);
            return;
        }

        if (!files.length) {
            console.log(`Directory '${dir}' is empty.`);
            return;
        }

        var i = 0,
            found = false;

        function optimizeFile(file) {
            // absoluted file path
            var filepath = PATH.resolve(path, file);
            var outfilepath = output ? PATH.resolve(output, file) : filepath;

            // check if file name matches *.svg
            if (regSVGFile.test(filepath)) {
                found = true;
                FS.readFile(filepath, 'utf8', function(err, data) {
                    if (err) {
                        console.error(err);
                        return;
                    }

                    var startTime = Date.now(),
                        time,
                        inBytes = Buffer.byteLength(data, 'utf8'),
                        outBytes;

                    svgo.optimize(data, function(result) {
                        if (result.error) {
                            console.error(result.error);
                            return;
                        }

                        outBytes = Buffer.byteLength(result.data, 'utf8');
                        time = Date.now() - startTime;

                        writeOutput();

                        function writeOutput() {
                            FS.writeFile(outfilepath, result.data, 'utf8', report);
                        }

                        function report(err) {
                            if (err) {
                                if (err.code === 'ENOENT') {
                                    mkdirp(output, writeOutput);
                                    return;
                                } else if (err.code === 'ENOTDIR') {
                                    console.error(`Error: output '${output}' is not a directory.`);
                                    return;
                                }
                                console.error(err);
                                return;
                            }

                            if (!config.quiet) {
                                console.log(file + ':');

                                // print time info
                                printTimeInfo(time);

                                // print optimization profit info
                                printProfitInfo(inBytes, outBytes);
                            }

                            //move on to the next file
                            if (++i < files.length) {
                                optimizeFile(files[i]);
                            }
                        }
                    });
                });
            }
            //move on to the next file
            else if (++i < files.length) {
                optimizeFile(files[i]);
            } else if (!found) {
                console.log('No SVG files have been found.');
            }
        }

        optimizeFile(files[i]);
    });
}

function showAvailablePlugins() {
    var svgo = new SVGO(),
        // Flatten an array of plugins grouped per type and sort alphabetically
        list = [];

    console.log('Currently available plugins:');
    list = list.concat.apply(list, svgo.config.plugins).sort((a, b) => a.name.localeCompare(b.name));

    for (var plugin of list) {
        console.log(` [ ${plugin.name.green} ] ${plugin.description}`);
    }
}
<|endoftext|>


<commit_before>'use strict';
var chumblRequest=require("./chumbl-request.js");
module.exports = {
    getLibraryChomps: function (options, callback) {
        options.verb = "get";
        var url = "/{chumblr}/libraryChomps";
        options.url = queryBuilder(url, options);
        options.errorIfNoResult=true;
        chumblRequest(options, callback);
    },
    getLibraryChomp: function (options, callback) {
        options.verb = "get";
        options.url = "/{chumblr}/libraryChomps/{libraryChompId}";
        options.errorIfNoResult=true;
        chumblRequest(options, callback);
    },
    addLibraryChomp: function (options, callback) {
        options.verb = "post";
        options.url = "/{chumblr}/libraryChomps";
        options.data = options.libraryChomp;
        options.errorIfNoResult=true;
        chumblRequest(options, callback);
    },
    updateLibraryChomp: function (options, callback) {
        options.verb = "put";
        options.url = "/{chumblr}/libraryChomps/{libraryChompId}";
        options.data = options.libraryChomp;
        chumblRequest(options, callback);
    }
};

<commit_msg>fixing lint error<commit_after>'use strict';
var chumblRequest=require("./chumbl-request.js");
var queryBuilder = require('./query-builder');
module.exports = {
    getLibraryChomps: function (options, callback) {
        options.verb = "get";
        var url = "/{chumblr}/libraryChomps";
        options.url = queryBuilder(url, options);
        options.errorIfNoResult=true;
        chumblRequest(options, callback);
    },
    getLibraryChomp: function (options, callback) {
        options.verb = "get";
        options.url = "/{chumblr}/libraryChomps/{libraryChompId}";
        options.errorIfNoResult=true;
        chumblRequest(options, callback);
    },
    addLibraryChomp: function (options, callback) {
        options.verb = "post";
        options.url = "/{chumblr}/libraryChomps";
        options.data = options.libraryChomp;
        options.errorIfNoResult=true;
        chumblRequest(options, callback);
    },
    updateLibraryChomp: function (options, callback) {
        options.verb = "put";
        options.url = "/{chumblr}/libraryChomps/{libraryChompId}";
        options.data = options.libraryChomp;
        chumblRequest(options, callback);
    }
};

<|endoftext|>


<commit_before>/*
 *
 * Date.addLocale(<code>) adds this locale to Sugar.
 * To set the locale globally, simply call:
 *
 * Date.setLocale('zh-CN');
 *
 * var locale = Date.getLocale(<code>) will return this object, which
 * can be tweaked to change the behavior of parsing/formatting in the locales.
 *
 * locale.addFormat adds a date format (see this file for examples).
 * Special tokens in the date format will be parsed out into regex tokens:
 *
 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
 * {unit} is a reference to all units. Output: (day|week|month|...)
 * {unit3} is a reference to a specific unit. Output: (hour)
 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
 * {unit?} "?" makes that token optional. Output: (day|week|month)?
 *
 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
 *
 * All spaces are optional and will be converted to "\s*"
 *
 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
 * all entries in the modifiers array follow a special format indicated by a colon:
 *
 * minute:|s  = minute|minutes
 * thicke:n|r = thicken|thicker
 *
 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
 *
 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
 *
 * When matched, the index will be found using:
 *
 * units.indexOf(match) % 7;
 *
 * Resulting in the correct index with any number of alternates for that entry.
 *
 */

Date.addLocale('zh-CN', {
  'variant': true,
  'monthSuffix': '月',
  'weekdays': '星期日|周日,星期一|周一,星期二|周二,星期三|周三,星期四|周四,星期五|周五,星期六|周六',
  'units': '毫秒,秒钟,分钟,小时,天,个星期|周,个月,年',
  'tokens': '日|号',
  'short':'{yyyy}年{M}月{d}日',
  'long': '{yyyy}年{M}月{d}日 {tt}{h}:{mm}',
  'full': '{yyyy}年{M}月{d}日 {weekday} {tt}{h}:{mm}:{ss}',
  'past': '{num}{unit}{sign}',
  'future': '{num}{unit}{sign}',
  'duration': '{num}{unit}',
  'timeSuffixes': '点|时,分钟?,秒',
  'ampm': '上午,下午',
  'modifiers': [
    { 'name': 'day', 'src': '前天', 'value': -2 },
    { 'name': 'day', 'src': '昨天', 'value': -1 },
    { 'name': 'day', 'src': '今天', 'value': 0 },
    { 'name': 'day', 'src': '明天', 'value': 1 },
    { 'name': 'day', 'src': '后天', 'value': 2 },
    { 'name': 'sign', 'src': '前', 'value': -1 },
    { 'name': 'sign', 'src': '后', 'value':  1 },
    { 'name': 'shift', 'src': '上|去', 'value': -1 },
    { 'name': 'shift', 'src': '这', 'value':  0 },
    { 'name': 'shift', 'src': '下|明', 'value':  1 }
  ],
  'dateParse': [
    '{num}{unit}{sign}',
    '{shift}{unit=5-7}'
  ],
  'timeParse': [
    '{shift}{weekday}',
    '{year}年{month?}月?{date?}{0?}',
    '{month}月{date?}{0?}',
    '{date}[日号]'
  ]
});

<commit_msg>Update zh-CN.js<commit_after>/*
 *
 * Date.addLocale(<code>) adds this locale to Sugar.
 * To set the locale globally, simply call:
 *
 * Date.setLocale('zh-CN');
 *
 * var locale = Date.getLocale(<code>) will return this object, which
 * can be tweaked to change the behavior of parsing/formatting in the locales.
 *
 * locale.addFormat adds a date format (see this file for examples).
 * Special tokens in the date format will be parsed out into regex tokens:
 *
 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
 * {unit} is a reference to all units. Output: (day|week|month|...)
 * {unit3} is a reference to a specific unit. Output: (hour)
 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
 * {unit?} "?" makes that token optional. Output: (day|week|month)?
 *
 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
 *
 * All spaces are optional and will be converted to "\s*"
 *
 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
 * all entries in the modifiers array follow a special format indicated by a colon:
 *
 * minute:|s  = minute|minutes
 * thicke:n|r = thicken|thicker
 *
 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
 *
 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
 *
 * When matched, the index will be found using:
 *
 * units.indexOf(match) % 7;
 *
 * Resulting in the correct index with any number of alternates for that entry.
 *
 */

Date.addLocale('zh-CN', {
  'variant': true,
  'monthSuffix': '月',
  'weekdays': '星期日|周日|星期天,星期一|周一,星期二|周二,星期三|周三,星期四|周四,星期五|周五,星期六|周六',
  'units': '毫秒,秒钟,分钟,小时,天,个星期|周,个月,年',
  'tokens': '日|号',
  'short':'{yyyy}年{M}月{d}日',
  'long': '{yyyy}年{M}月{d}日 {tt}{h}:{mm}',
  'full': '{yyyy}年{M}月{d}日 {weekday} {tt}{h}:{mm}:{ss}',
  'past': '{num}{unit}{sign}',
  'future': '{num}{unit}{sign}',
  'duration': '{num}{unit}',
  'timeSuffixes': '点|时,分钟?,秒',
  'ampm': '上午,下午',
  'modifiers': [
    { 'name': 'day', 'src': '大前天', 'value': -3 },
    { 'name': 'day', 'src': '前天', 'value': -2 },
    { 'name': 'day', 'src': '昨天', 'value': -1 },
    { 'name': 'day', 'src': '今天', 'value': 0 },
    { 'name': 'day', 'src': '明天', 'value': 1 },
    { 'name': 'day', 'src': '后天', 'value': 2 },
    { 'name': 'day', 'src': '大后天', 'value': 3 },
    { 'name': 'sign', 'src': '前', 'value': -1 },
    { 'name': 'sign', 'src': '后', 'value':  1 },
    { 'name': 'shift', 'src': '上|去', 'value': -1 },
    { 'name': 'shift', 'src': '这', 'value':  0 },
    { 'name': 'shift', 'src': '下|明', 'value':  1 }
  ],
  'dateParse': [
    '{num}{unit}{sign}',
    '{shift}{unit=5-7}'
  ],
  'timeParse': [
    '{shift}{weekday}',
    '{year}年{month?}月?{date?}{0?}',
    '{month}月{date?}{0?}',
    '{date}[日号]'
  ]
});

<|endoftext|>


<commit_before>'use strict'

let cli = require('heroku-cli-util')
let EventSource = require('eventsource')
let url = require('url')
let liner = require('../lib/line_transform')
const colorize = require('./colorize')

function readLogs (logplexURL) {
  let u = url.parse(logplexURL)
  if (u.query && u.query.includes('srv')) {
    return readLogsV1(logplexURL)
  } else {
    return readLogsV2(logplexURL)
  }
}

function readLogsV1 (logplexURL) {
  return new Promise(function (resolve, reject) {
    let res = cli.got.stream(logplexURL)
    res.setEncoding('utf8')
    liner.setEncoding('utf8')
    res.pipe(liner)
    liner.on('data', line => cli.log(colorize(line)))
    res.on('end', resolve)
    res.on('error', reject)
  })
}

function readLogsV2 (logplexURL) {
  return new Promise(function (resolve, reject) {
    const u = url.parse(logplexURL, true)
    const isTail = u.query.tail && u.query.tail === 'true'
    const userAgent = process.env.HEROKU_DEBUG_USER_AGENT || 'heroku-run'
    const proxy = process.env.https_proxy || process.env.HTTPS_PROXY
    const es = new EventSource(logplexURL, {
      proxy,
      headers: {
        'User-Agent': userAgent
      }
    })

    es.onerror = function (err) {
      if (!isTail) {
        resolve()
        es.close()
      }

      if (err && (err.status === 404 || err.status === 403)) {
        reject(new Error('Log stream timed out. Please try again.'))
        es.close()
      }
    }

    es.onmessage = function (e) {
      e.data.trim().split(/\n+/).forEach((line) => {
        cli.log(colorize(line))
      })
    }
  })
}

async function logDisplayer (heroku, options) {
  process.stdout.on('error', err => {
    if (err.code === 'EPIPE') {
      process.exit(0)
    } else {
      console.error(err.stack)
      process.exit(1)
    }
  })
  const response = await heroku.request({
    path: `/apps/${options.app}/log-sessions`,
    method: 'POST',
    body: {
      tail: options.tail,
      dyno: options.dyno,
      source: options.source,
      lines: options.lines
    }
  })
  return readLogs(response.logplex_url)
}

module.exports = logDisplayer
<commit_msg>use http-call instead of got<commit_after>'use strict'

let cli = require('heroku-cli-util')
let EventSource = require('eventsource')
let url = require('url')
let liner = require('../lib/line_transform')
const colorize = require('./colorize')
const HTTP = require('http-call')

function readLogs (logplexURL) {
  let u = url.parse(logplexURL)
  if (u.query && u.query.includes('srv')) {
    return readLogsV1(logplexURL)
  } else {
    return readLogsV2(logplexURL)
  }
}

async function readLogsV1 (logplexURL) {
  let {response} = await HTTP.stream(logplexURL)
  return new Promise(function (resolve, reject) {
    response.setEncoding('utf8')
    liner.setEncoding('utf8')
    response.pipe(liner)
    liner.on('data', line => cli.log(colorize(line)))
    response.on('end', resolve)
    response.on('error', reject)
  })
}

function readLogsV2 (logplexURL) {
  return new Promise(function (resolve, reject) {
    const u = url.parse(logplexURL, true)
    const isTail = u.query.tail && u.query.tail === 'true'
    const userAgent = process.env.HEROKU_DEBUG_USER_AGENT || 'heroku-run'
    const proxy = process.env.https_proxy || process.env.HTTPS_PROXY
    const es = new EventSource(logplexURL, {
      proxy,
      headers: {
        'User-Agent': userAgent
      }
    })

    es.onerror = function (err) {
      if (!isTail) {
        resolve()
        es.close()
      }

      if (err && (err.status === 404 || err.status === 403)) {
        reject(new Error('Log stream timed out. Please try again.'))
        es.close()
      }
    }

    es.onmessage = function (e) {
      e.data.trim().split(/\n+/).forEach((line) => {
        cli.log(colorize(line))
      })
    }
  })
}

async function logDisplayer (heroku, options) {
  process.stdout.on('error', err => {
    if (err.code === 'EPIPE') {
      process.exit(0)
    } else {
      console.error(err.stack)
      process.exit(1)
    }
  })
  const response = await heroku.request({
    path: `/apps/${options.app}/log-sessions`,
    method: 'POST',
    body: {
      tail: options.tail,
      dyno: options.dyno,
      source: options.source,
      lines: options.lines
    }
  })
  return readLogs(response.logplex_url)
}

module.exports = logDisplayer
<|endoftext|>


<commit_before>//Template for generating final code

var template = (function () {

//TEMPLATE START ##
/**
 * Modules combined by SingleJS
 */
;(function (m, a, s, g) { s(m, a, g); })({
//===============
//    Modules
//===============
//#var path,code; for (path in codes) { code=codes[path];
'{{path}}': function module(require, exports) {
{{code}}
},///{{path}}
//#}
'single.js': function module(require, exports) {
//default module 'single'
exports.version = '{{version}}';
}///single.js
//===============
//  Modules end
//===============
}, {

//===========
//   Alias
//===========
//#var path,alias; for (alias in aliases) { path=aliases[alias];
'{{alias}}': '{{path}}',
//#}
'singlejs': 'single' //default alias for single
//===========
// Alias end
//===========

/*!
 * SingleJS
 */
}, function (modules, alias, g, undefined) {
    //bind global
    g.require = require;

    //TODO if main, if module module.exports = main, else g[main] = main

    //core
    function require(path) {
        path = resolvePath(path, (require.caller || arguments.callee.caller).id || '');

        if (typeof path !== 'string') return; //TODO throw Error accroding to CommonJS spec

        var module = modules[path];
        if (!('exports' in module)) {
            module.id = path;
            module.exports = {};
            module(require, module.exports);
        }

        return module.exports;
    }

    //resolve module's real path
    function resolvePath(path, rpath) {
        //path is alias
        if (alias[path]) {
            return alias[path];
        }

        //split path
        var parts = path.split('/');

        //whether path is relative to rpath
        if (parts[0] === '.' || parts[0] === '..') {
            parts = rpath.split('/').slice(0, -1).concat(parts);
        }

        //resolve relative dir
        var updir = 0, newParts = [];
        for (var i=parts.length, part; i--;) {
            part=parts[i];

            if (part === '.') continue; //clear current dir
            if (part === '..') { updir++; continue; } //store updir
            if (updir) { updir--; continue; } //resolve updir

            newParts.push(part);
        }
        //restore unresolved updir
        while (updir--) {
            newParts.push('..');
        }

        //join normalized path
        path = newParts.reverse().join('/');

        //default extension '.js' check
        if ((path in modules) || ((path +='.js') in modules)) {
            return path;
        }

        return null;
    }
}, window);
//(°ω°〃)single ## TEMPLATE END

}).toString().replace(/^[\w\W]*##\n?([\w\W]+)##[\w\W]*$/, '$1'); //extract code as string

//compile template
exports.render = new Function('codes', 'aliases', 'version',
    "var S = '"+ template
        .replace(/\\/g, "\\\\") //escape \
        .replace(/'/g, "\\'") //escape '
        .replace(/\{\{(\w+)\}\}/g, "' +$1 +'") //variables
        .replace(/\/\/#(.+)\n/g, "'; $1 S+='") //statements
        .replace(/\n/g, "\\n")+ //escape cr
    "'; return S;"
);
<commit_msg>fix :fish: aliased path is unormalized or without js ext<commit_after>//Template for generating final code

var template = (function () {

//TEMPLATE START ##
/**
 * Modules combined by SingleJS
 */
;(function (m, a, s, g) { s(m, a, g); })({
//===============
//    Modules
//===============
//#var path,code; for (path in codes) { code=codes[path];
'{{path}}': function module(require, exports) {
{{code}}
},///{{path}}
//#}
'single.js': function module(require, exports) {
//default module 'single'
exports.version = '{{version}}';
}///single.js
//===============
//  Modules end
//===============
}, {

//===========
//   Alias
//===========
//#var path,alias; for (alias in aliases) { path=aliases[alias];
'{{alias}}': '{{path}}',
//#}
'singlejs': 'single' //default alias for single
//===========
// Alias end
//===========

/*!
 * SingleJS
 */
}, function (modules, alias, g, undefined) {
    //bind global
    g.require = require;

    //TODO if main, if module module.exports = main, else g[main] = main

    //core
    function require(path) {
        path = resolvePath(path, (require.caller || arguments.callee.caller).id || '');

        if (typeof path !== 'string') return; //TODO throw Error accroding to CommonJS spec

        var module = modules[path];
        if (!('exports' in module)) {
            module.id = path;
            module.exports = {};
            module(require, module.exports);
        }

        return module.exports;
    }

    //resolve module's real path
    function resolvePath(path, rpath) {
        //get aliased module path
        path = alias[path] || path;

        //split path
        var parts = path.split('/');

        //whether path is relative to rpath
        if (parts[0] === '.' || parts[0] === '..') {
            parts = rpath.split('/').slice(0, -1).concat(parts);
        }

        //resolve relative dir
        var updir = 0, newParts = [];
        for (var i=parts.length, part; i--;) {
            part=parts[i];

            if (part === '.') continue; //clear current dir
            if (part === '..') { updir++; continue; } //store updir
            if (updir) { updir--; continue; } //resolve updir

            newParts.push(part);
        }
        //restore unresolved updir
        while (updir--) {
            newParts.push('..');
        }

        //join normalized path
        path = newParts.reverse().join('/');

        //default extension '.js' check
        if ((path in modules) || ((path +='.js') in modules)) {
            return path;
        }

        return null;
    }
}, window);
//(°ω°〃)single ## TEMPLATE END

}).toString().replace(/^[\w\W]*##\n?([\w\W]+)##[\w\W]*$/, '$1'); //extract code as string

//compile template
exports.render = new Function('codes', 'aliases', 'version',
    "var S = '"+ template
        .replace(/\\/g, "\\\\") //escape \
        .replace(/'/g, "\\'") //escape '
        .replace(/\{\{(\w+)\}\}/g, "' +$1 +'") //variables
        .replace(/\/\/#(.+)\n/g, "'; $1 S+='") //statements
        .replace(/\n/g, "\\n")+ //escape cr
    "'; return S;"
);
<|endoftext|>