text:
<!DOCTYPE html>
<html>
<head>
<!-- Image Capture Browser Test -->
</head>
<body>
<script type="text/javascript" src="webrtc_test_utilities.js"></script>
<script>
const WIDTH = 640;
/** @const */ var CONSTRAINTS = { width: { max : WIDTH } };

// Returns a Promise resolved with |object| after a delay of |delayInMs|.
function delayedResolver(object, delayInMs) {
  return new Promise((resolve, reject) => {
    setTimeout(() => { resolve(object); }, delayInMs);
  });
}

// Runs an ImageCapture.getPhotoCapabilities() and expects it to succeed.
function testCreateAndGetPhotoCapabilitiesSucceeds() {
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        let imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        return imageCapturer.getPhotoCapabilities();
      })
      .then(capabilities => {
        // There's nothing to check here since |capabilities| vary per device.
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Runs an ImageCapture.getPhotoSettings() and expects it to succeed.
function testCreateAndGetPhotoSettingsSucceeds() {
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        let imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        return imageCapturer.getPhotoSettings();
      })
      .then(settings => {
        // There's nothing to check here since |settings| vary per device.
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Runs an ImageCapture.takePhoto() and expects it to succeed.
function testCreateAndTakePhotoSucceeds() {
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        let imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        return imageCapturer.takePhoto();
      })
      .then(blob => {
        assertTrue(blob.type != undefined);
        assertNotEquals(0, blob.size);

        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Runs an ImageCapture.takePhoto() and expects it to get rejected.
function testCreateAndTakePhotoIsRejected() {
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        let imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        return imageCapturer.takePhoto();
      })
      .then(blob => {
        failTest('Expected promise to get rejected but it was fulfilled');
      }, err => {
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Runs an ImageCapture.grabFrame() and expects it to succeed.
function testCreateAndGrabFrameSucceeds() {
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        let imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        return imageCapturer.grabFrame();
      })
      .then(imageBitmap => {
        // On Android, depending on the device orientation, |imageBitmap| might
        // appear rotated.
        assertTrue(WIDTH == imageBitmap.width || WIDTH == imageBitmap.height);

        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Runs a MediaStreamTrack.getCapabilities().
function testCreateAndGetTrackCapabilities() {
  var imageCapturer;
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        imageCapturer.track.getCapabilities();
        // There's nothing to check here since |capabilities| vary per device.
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Runs an MediaStreamTrack.getSettings().
function testCreateAndGetTrackSettings() {
  var imageCapturer;
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        imageCapturer.track.getSettings();
        // There's nothing to check here since |settings| vary per device.
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Tries to read, set and read back the pan capability if available.
function testManipulatePan() {
  var newPan = -1;
  var imageCapturer;
  var panTolerance;
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        const trackCapabilities = imageCapturer.track.getCapabilities();
        if (trackCapabilities.pan === undefined) {
          console.log('pan not supported, skipping test');
          reportTestSuccess();
          return;
        }

        const currentPan = imageCapturer.track.getSettings().pan;
        newPan = currentPan + trackCapabilities.pan.step;
        newPan = Math.min(newPan, trackCapabilities.pan.max);
        console.log("Setting pan from " + currentPan + " to " + newPan);
        panTolerance = trackCapabilities.pan.step / 10;

        return imageCapturer.track.applyConstraints({advanced : [{pan : newPan}]});
      })
      .then(() => {
        assertEquals(newPan,
                     imageCapturer.track.getConstraints().advanced[0].pan);
        assertTrue(Math.abs(newPan - imageCapturer.track.getSettings().pan)
            < panTolerance);
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Tries to read, set and read back the tilt capability if available.
function testManipulateTilt() {
  var newTilt = -1;
  var imageCapturer;
  var tiltTolerance;
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        const trackCapabilities = imageCapturer.track.getCapabilities();
        if (trackCapabilities.tilt === undefined) {
          console.log('tilt not supported, skipping test');
          reportTestSuccess();
          return;
        }

        const currentTilt = imageCapturer.track.getSettings().tilt;
        newTilt = currentTilt + trackCapabilities.tilt.step;
        newTilt = Math.min(newTilt, trackCapabilities.tilt.max);
        console.log("Setting tilt from " + currentTilt + " to " + newTilt);
        tiltTolerance = trackCapabilities.tilt.step / 10;

        return imageCapturer.track.applyConstraints({advanced : [{tilt : newTilt}]});
      })
      .then(() => {
        assertEquals(newTilt,
                     imageCapturer.track.getConstraints().advanced[0].tilt);
        assertTrue(Math.abs(newTilt - imageCapturer.track.getSettings().tilt)
            < tiltTolerance);
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Tries to read, set and read back the zoom capability if available.
function testManipulateZoom() {
  var newZoom = -1;
  var imageCapturer;
  var zoomTolerance;
  navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
      .then(stream => {
        assertEquals('video', stream.getVideoTracks()[0].kind);
        imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
        const trackCapabilities = imageCapturer.track.getCapabilities();
        if (trackCapabilities.zoom === undefined) {
          console.log('zoom not supported, skipping test');
          reportTestSuccess();
          return;
        }

        const currentZoom = imageCapturer.track.getSettings().zoom;
        newZoom = currentZoom + trackCapabilities.zoom.step;
        newZoom = Math.min(newZoom, trackCapabilities.zoom.max);
        console.log("Setting zoom from " + currentZoom + " to " + newZoom);
        zoomTolerance = trackCapabilities.zoom.step / 10;

        return imageCapturer.track.applyConstraints({advanced : [{zoom : newZoom}]});
      })
      .then(() => {
        assertEquals(newZoom,
                     imageCapturer.track.getConstraints().advanced[0].zoom);
        assertTrue(Math.abs(newZoom - imageCapturer.track.getSettings().zoom)
            < zoomTolerance);
        reportTestSuccess();
      })
      .catch(err => {
        return failTest(err.toString());
      });
}

// Tries to read, set and read back the exposureTime capability if available.
function testManipulateExposureTime() {
    var newExposureTime = -1;
    var imageCapturer;
    var exposureTimeTolerance;
    navigator.mediaDevices.getUserMedia({"video": CONSTRAINTS})
        .then(stream => {
            assertEquals('video', stream.getVideoTracks()[0].kind);
            imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
            const trackCapabilities = imageCapturer.track.getCapabilities();
            if (trackCapabilities.exposureTime === undefined) {
                console.log('exposureTime not supported, skipping test');
                reportTestSuccess();
                return;
            }

            const currentExposureTime = imageCapturer.track.getSettings().exposureTime;
            newExposureTime = currentExposureTime + trackCapabilities.exposureTime
                .step;
            newExposureTime = Math.min(newExposureTime, trackCapabilities.exposureTime
                .max);
            console.log("Setting exposureTime from " + currentExposureTime +
                " to " +
                newExposureTime);
            exposureTimeTolerance = trackCapabilities.exposureTime.step /
                10;

            currentExposureMode = imageCapturer.track.getSettings().exposureMode;
            console.log(" exposureMode == " + currentExposureMode);
            exposureModeCapabilities = trackCapabilities.exposureMode;
            console.log(" exposureModeCapabilities == " +
                exposureModeCapabilities);

            return imageCapturer.track.applyConstraints({
                advanced: [{
                    exposureMode: "manual",
                    exposureTime: newExposureTime
                }]
            });
        })
        .then(() => {
            assertEquals(newExposureTime,
                imageCapturer.track.getConstraints().advanced[0].exposureTime
            );
            assertTrue(Math.abs(newExposureTime - imageCapturer.track.getSettings()
                    .exposureTime) <
                exposureTimeTolerance);
            reportTestSuccess();
        })
        .catch(err => {
            return failTest(err.toString());
        });
}

// Tries to read, set and read back the focusDistance capability if available.
function testManipulateFocusDistance() {
    var newFocusDistance = -1;
    var imageCapturer;
    var focusDistanceTolerance;
    navigator.mediaDevices.getUserMedia({"video": CONSTRAINTS})
        .then(stream => {
            assertEquals('video', stream.getVideoTracks()[0].kind);
            imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
            const trackCapabilities = imageCapturer.track.getCapabilities();
            if (trackCapabilities.focusDistance === undefined) {
                console.log('focusDistance not supported, skipping test');
                reportTestSuccess();
                return;
            }

            const currentFocusDistance = imageCapturer.track.getSettings().focusDistance;
            newFocusDistance = currentFocusDistance + trackCapabilities.focusDistance
                .step;
            newFocusDistance = Math.min(newFocusDistance, trackCapabilities.focusDistance
                .max);
            console.log("Setting focusDistance from " + currentFocusDistance +
                " to " +
                newFocusDistance);
            focusDistanceTolerance = trackCapabilities.focusDistance.step /
                10;

            currentFocusMode = imageCapturer.track.getSettings().focusMode;
            console.log(" focusMode == " + currentFocusMode);
            focusModeCapabilities = trackCapabilities.focusMode;
            console.log(" focusModeCapabilities == " +
                focusModeCapabilities);

            return imageCapturer.track.applyConstraints({
                advanced: [{
                    focusMode: "manual",
                    focusDistance: newFocusDistance
                }]
            });
        })
        .then(() => {
            assertEquals(newFocusDistance,
                imageCapturer.track.getConstraints().advanced[0].focusDistance
            );
            assertTrue(Math.abs(newFocusDistance - imageCapturer.track.getSettings()
                    .focusDistance) <
                focusDistanceTolerance);
            reportTestSuccess();
        })
        .catch(err => {
            return failTest(err.toString());
        });
}

</script>
</body>
</html>


text:
import { expect } from 'chai';
import * as sinon from 'sinon';
import { setTimeout } from 'timers';

import { LEGACY_HELLO_COMMAND } from '../../../src/constants';
import { ServerType } from '../../../src/sdam/common';
import { ServerHeartbeatFailedEvent, ServerHeartbeatStartedEvent } from '../../../src/sdam/events';
import { Monitor, MonitorInterval } from '../../../src/sdam/monitor';
import { ServerDescription } from '../../../src/sdam/server_description';
import { Topology } from '../../../src/sdam/topology';
import { isHello } from '../../../src/utils';
import * as mock from '../../tools/mongodb-mock/index';
import { createTimerSandbox } from '../timer_sandbox';

class MockServer {
  s: any;
  description: ServerDescription;
  constructor(options) {
    this.s = { pool: { generation: 1 } };
    this.description = new ServerDescription(`${options.host}:${options.port}`);
    this.description.type = ServerType.Unknown;
  }
}

describe('monitoring', function () {
  let mockServer;

  after(() => mock.cleanup());
  beforeEach(function () {
    return mock.createServer().then(server => (mockServer = server));
  });

  // TODO(NODE-3819): Unskip flaky tests.
  it.skip('should record roundTripTime', function (done) {
    mockServer.setMessageHandler(request => {
      const doc = request.document;
      if (isHello(doc)) {
        request.reply(Object.assign({}, mock.HELLO));
      } else if (doc.endSessions) {
        request.reply({ ok: 1 });
      }
    });

    // set `heartbeatFrequencyMS` to 250ms to force a quick monitoring check, and wait 500ms to validate below
    const topology = new Topology(mockServer.hostAddress(), { heartbeatFrequencyMS: 250 } as any);
    topology.connect(err => {
      expect(err).to.not.exist;

      setTimeout(() => {
        expect(topology).property('description').property('servers').to.have.length(1);

        const serverDescription = Array.from(topology.description.servers.values())[0];
        expect(serverDescription).property('roundTripTime').to.be.greaterThan(0);

        topology.close(done as any);
      }, 500);
    });
  }).skipReason = 'TODO(NODE-3819): Unskip flaky tests';

  // TODO(NODE-3600): Unskip flaky test
  it.skip('should recover on error during initial connect', function (done) {
    // This test should take ~1s because initial server selection fails and an immediate check
    // is requested. If the behavior of the immediate check is broken, the test will take ~10s
    // to complete. We want to ensure validation of the immediate check behavior, and therefore
    // hardcode the test timeout to 2s.
    this.timeout(2000);

    let acceptConnections = false;
    mockServer.setMessageHandler(request => {
      if (!acceptConnections) {
        request.connection.destroy();
        return;
      }

      const doc = request.document;
      if (isHello(doc)) {
        request.reply(Object.assign({}, mock.HELLO));
      } else if (doc.endSessions) {
        request.reply({ ok: 1 });
      }
    });

    setTimeout(() => {
      acceptConnections = true;
    }, 250);

    const topology = new Topology(mockServer.hostAddress(), {});
    topology.connect(err => {
      expect(err).to.not.exist;
      expect(topology).property('description').property('servers').to.have.length(1);

      const serverDescription = Array.from(topology.description.servers.values())[0];
      expect(serverDescription).property('roundTripTime').to.be.greaterThan(0);

      topology.close(done);
    });
  }).skipReason = 'TODO(NODE-3600): Unskip flaky tests';

  describe('Monitor', function () {
    let monitor;

    beforeEach(() => {
      monitor = null;
    });

    afterEach(() => {
      if (monitor) {
        monitor.close();
      }
    });

    it('should connect and issue an initial server check', function (done) {
      mockServer.setMessageHandler(request => {
        const doc = request.document;
        if (isHello(doc)) {
          request.reply(Object.assign({}, mock.HELLO));
        }
      });

      const server = new MockServer(mockServer.address());
      monitor = new Monitor(server as any, {} as any);

      monitor.on('serverHeartbeatFailed', () => done(new Error('unexpected heartbeat failure')));
      monitor.on('serverHeartbeatSucceeded', () => {
        expect(monitor.connection.isMonitoringConnection).to.be.true;
        done();
      });
      monitor.connect();
    });

    it('should ignore attempts to connect when not already closed', function (done) {
      mockServer.setMessageHandler(request => {
        const doc = request.document;
        if (isHello(doc)) {
          request.reply(Object.assign({}, mock.HELLO));
        }
      });

      const server = new MockServer(mockServer.address());
      monitor = new Monitor(server as any, {} as any);

      monitor.on('serverHeartbeatFailed', () => done(new Error('unexpected heartbeat failure')));
      monitor.on('serverHeartbeatSucceeded', () => done());
      monitor.connect();
      monitor.connect();
    });

    it('should not initiate another check if one is in progress', function (done) {
      mockServer.setMessageHandler(request => {
        const doc = request.document;
        if (isHello(doc)) {
          setTimeout(() => request.reply(Object.assign({}, mock.HELLO)), 250);
        }
      });

      const server = new MockServer(mockServer.address());
      monitor = new Monitor(server as any, {} as any);

      const startedEvents: ServerHeartbeatStartedEvent[] = [];
      monitor.on('serverHeartbeatStarted', event => startedEvents.push(event));
      monitor.on('close', () => {
        expect(startedEvents).to.have.length(2);
        done();
      });

      monitor.connect();
      monitor.once('serverHeartbeatSucceeded', () => {
        monitor.requestCheck();
        monitor.requestCheck();
        monitor.requestCheck();
        monitor.requestCheck();
        monitor.requestCheck();

        const minHeartbeatFrequencyMS = 500;
        setTimeout(() => {
          // wait for minHeartbeatFrequencyMS, then request a check and verify another check occurred
          monitor.once('serverHeartbeatSucceeded', () => {
            monitor.close();
          });

          monitor.requestCheck();
        }, minHeartbeatFrequencyMS);
      });
    });

    it('should not close the monitor on a failed heartbeat', function (done) {
      let helloCount = 0;
      mockServer.setMessageHandler(request => {
        const doc = request.document;
        if (isHello(doc)) {
          helloCount++;
          if (helloCount === 2) {
            request.reply({ ok: 0, errmsg: 'forced from mock server' });
            return;
          }

          if (helloCount === 3) {
            request.connection.destroy();
            return;
          }

          request.reply(mock.HELLO);
        }
      });

      const server = new MockServer(mockServer.address());
      server.description = new ServerDescription(server.description.hostAddress);
      monitor = new Monitor(
        server as any,
        {
          heartbeatFrequencyMS: 250,
          minHeartbeatFrequencyMS: 50
        } as any
      );

      const events: ServerHeartbeatFailedEvent[] = [];
      monitor.on('serverHeartbeatFailed', event => events.push(event));

      let successCount = 0;
      monitor.on('serverHeartbeatSucceeded', () => {
        if (successCount++ === 2) {
          monitor.close();
        }
      });

      monitor.on('close', () => {
        expect(events).to.have.length(2);
        done();
      });

      monitor.connect();
    });

    it('should upgrade to hello from legacy hello when initial handshake contains helloOk', function (done) {
      const docs: any[] = [];
      mockServer.setMessageHandler(request => {
        const doc = request.document;
        docs.push(doc);
        if (docs.length === 2) {
          expect(docs[0]).to.have.property(LEGACY_HELLO_COMMAND, true);
          expect(docs[0]).to.have.property('helloOk', true);
          expect(docs[1]).to.have.property('hello', true);
          done();
        } else if (isHello(doc)) {
          setTimeout(() => request.reply(Object.assign({ helloOk: true }, mock.HELLO)), 250);
        }
      });

      const server = new MockServer(mockServer.address());
      monitor = new Monitor(server as any, {} as any);

      monitor.connect();
      monitor.once('serverHeartbeatSucceeded', () => {
        const minHeartbeatFrequencyMS = 500;
        setTimeout(() => {
          // wait for minHeartbeatFrequencyMS, then request a check and verify another check occurred
          monitor.once('serverHeartbeatSucceeded', () => {
            monitor.close();
          });

          monitor.requestCheck();
        }, minHeartbeatFrequencyMS);
      });
    });
  });

  describe('class MonitorInterval', function () {
    let timerSandbox, clock, executor, fnSpy;

    beforeEach(function () {
      timerSandbox = createTimerSandbox();
      clock = sinon.useFakeTimers();
      fnSpy = sinon.spy(cb => {
        cb();
      });
    });

    afterEach(function () {
      if (executor) {
        executor.stop();
      }
      clock.restore();
      timerSandbox.restore();
    });

    context('#constructor()', function () {
      context('when the immediate option is provided', function () {
        it('executes the function immediately and schedules the next execution on the interval', function () {
          executor = new MonitorInterval(fnSpy, {
            immediate: true,
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });
          // expect immediate invocation
          expect(fnSpy.calledOnce).to.be.true;
          // advance clock by less than the scheduled interval to ensure we don't execute early
          clock.tick(29);
          expect(fnSpy.calledOnce).to.be.true;
          // advance clock to the interval
          clock.tick(1);
          expect(fnSpy.calledTwice).to.be.true;
        });
      });

      context('when the immediate option is not provided', function () {
        it('executes the function on the provided interval', function () {
          executor = new MonitorInterval(fnSpy, {
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });
          // advance clock by less than the scheduled interval to ensure we don't execute early
          clock.tick(29);
          expect(fnSpy.callCount).to.equal(0);
          // advance clock to the interval
          clock.tick(1);
          expect(fnSpy.calledOnce).to.be.true;
          // advance clock by the interval
          clock.tick(30);
          expect(fnSpy.calledTwice).to.be.true;
        });
      });
    });

    describe('#wake()', function () {
      context('when fn()  has not executed yet', function () {
        beforeEach(function () {
          executor = new MonitorInterval(fnSpy, {
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });

          // tick less than heartbeatFrequencyMS
          clock.tick(5);

          executor.wake();
        });
        it('executes immediately', function () {
          expect(fnSpy.calledOnce).to.be.true;
        });
        it('schedules fn() for heartbeatFrequencyMS away', function () {
          // advance the clock almost heartbeatFrequencyMS away
          clock.tick(29);
          expect(fnSpy.calledOnce).to.be.true;

          // advance it to heartbeatFrequencyMS
          clock.tick(1);
          expect(fnSpy.calledTwice).to.be.true;
        });
      });

      context('when fn() is in progress', function () {
        beforeEach(function () {
          // create an asynchronous spy
          fnSpy = sinon.spy(cb => setTimeout(cb, 5));

          executor = new MonitorInterval(fnSpy, {
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });

          // advance to point of execution
          clock.tick(30);
        });
        it('does not trigger another call to fn()', function () {
          executor.wake();
          executor.wake();
          executor.wake();
          executor.wake();

          expect(fnSpy.calledOnce).to.be.true;
        });
      });

      context('when it has been >=minHeartbeatFrequencyMS since fn() last completed', function () {
        beforeEach(function () {
          executor = new MonitorInterval(fnSpy, {
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });

          // call fn() once
          clock.tick(30);
          expect(fnSpy.calledOnce).to.be.true;

          fnSpy.callCount = 0;

          // advance further than minHeartbeatFrequency
          clock.tick(20);

          executor.wake();
        });
        it('executes fn() immediately', function () {
          expect(fnSpy.calledOnce).to.be.true;
        });
        it('schedules fn() for heartbeatFrequencyMS away', function () {
          clock.tick(29);
          expect(fnSpy.calledOnce).to.be.true;
          clock.tick(1);
          expect(fnSpy.calledTwice).to.be.true;
        });
      });

      context(
        'when it has been < minHeartbeatFrequencyMS and >= 0 since fn() last completed',
        function () {
          beforeEach(function () {
            executor = new MonitorInterval(fnSpy, {
              minHeartbeatFrequencyMS: 10,
              heartbeatFrequencyMS: 30
            });

            // call fn() once
            clock.tick(30);
            expect(fnSpy.calledOnce).to.be.true;

            fnSpy.callCount = 0;

            // advance less than minHeartbeatFrequency
            clock.tick(5);

            executor.wake();
          });

          it('reschedules fn() to minHeartbeatFrequencyMS after the last call', function () {
            expect(fnSpy.callCount).to.equal(0);
            clock.tick(5);
            expect(fnSpy.calledOnce).to.be.true;
          });

          context('when wake() is called more than once', function () {
            it('schedules fn() minHeartbeatFrequencyMS after the last call to fn()', function () {
              executor.wake();
              executor.wake();
              executor.wake();

              expect(fnSpy.callCount).to.equal(0);
              clock.tick(5);
              expect(fnSpy.calledOnce).to.be.true;
            });
          });
        }
      );

      context('when it has been <0 since fn() has last executed', function () {
        beforeEach(function () {
          executor = new MonitorInterval(fnSpy, {
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });

          // negative ticks aren't supported, so manually set execution time
          executor.lastExecutionEnded = Infinity;
          executor.wake();
        });

        it('executes fn() immediately', function () {
          expect(fnSpy.calledOnce).to.be.true;
        });

        it('reschedules fn() to minHeartbeatFrequency away', function () {
          fnSpy.callCount = 0;

          clock.tick(29);
          expect(fnSpy.callCount).to.equal(0);

          clock.tick(1);
          expect(fnSpy.calledOnce).to.be.true;
        });
      });
    });

    describe('#stop()', function () {
      context('when fn() is executing', function () {
        beforeEach(function () {
          // create an asynchronous spy
          fnSpy = sinon.spy(cb => setTimeout(cb, 5));

          executor = new MonitorInterval(fnSpy, {
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });

          // advance to point of execution
          clock.tick(30);

          executor.stop();
        });
        it('does not reschedule fn() after fn() finishes executing', function () {
          // exhaust the spy fn
          clock.tick(5);

          expect(fnSpy.calledOnce).to.be.true;

          // advance heartbeatFrequencyMS
          clock.tick(30);
          expect(fnSpy.calledOnce).to.be.true;
        });
      });
      context('when fn() is not executing', function () {
        beforeEach(function () {
          executor = new MonitorInterval(fnSpy, {
            minHeartbeatFrequencyMS: 10,
            heartbeatFrequencyMS: 30
          });

          executor.stop();

          // advance heartbeatFrequencyMS
          clock.tick(30);
        });
        it('clears any scheduled executions of fn()', function () {
          expect(fnSpy.callCount).to.equal(0);
        });
      });
    });

    context('when fn() returns an error', function () {
      let uncaughtErrors = [];
      beforeEach(function () {
        uncaughtErrors = [];
        process.on('uncaughtException', e => uncaughtErrors.push(e));

        fnSpy = sinon.spy(cb => cb(new Error('ahh')));

        executor = new MonitorInterval(fnSpy, {
          minHeartbeatFrequencyMS: 10,
          heartbeatFrequencyMS: 30
        });

        clock.tick(30);
      });

      afterEach(() => process.removeAllListeners());

      it('no error is thrown by the MonitorInterval', function () {
        expect(uncaughtErrors).to.have.lengthOf(0);
      });
      it('reschedules another call to fn() for heartbeatFrequencyMS in the future', function () {
        clock.tick(30);
        expect(fnSpy.calledTwice).to.be.true;
      });
    });
  });
});

redpajama_set_name: RedPajamaGithub


text:
struct List {
	unsigned int grow_by, num_elems;
	void **items;
};

struct List *lst_init(int grow_by);
int lst_append(struct List *lst, void *elem, int cp_size);

#define RMPG_LST_H
#endif


text:
package org.n52.v3d.worldviz.exception;

/**
 * This exception is thrown during the load process of an ENE XML file when the
 * type of the XML document does not match any type of the
 * {@link GeoReferenceFeatureTypeEnum}. For further information please check
 * {@link DatasetLoader#loadDataset()}.
 *
 * @author Christian Danowski
 *
 */
public class NoValidFeatureTypeException extends Exception {

	private static final long serialVersionUID = 3528367561477962527L;

	public NoValidFeatureTypeException() {
		super(
				"The featureType-attribute of the GeoReference-element of the ENE-dataset has an invalid value!");
	}

	public NoValidFeatureTypeException(String message) {
		super(message);
		// TODO Auto-generated constructor stub
	}

}


text:
'use strict';

module.exports = {
  up: (queryInterface, Sequelize) => {
    /*
      Add altering commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.bulkInsert('Person', [{
        name: 'John Doe',
        isBetaMember: false
      }], {});
    */
  },

  down: (queryInterface, Sequelize) => {
    /*
      Add reverting commands here.
      Return a promise to correctly handle asynchronicity.

      Example:
      return queryInterface.bulkDelete('Person', null, {});
    */
  }
};


text:
%% Initialization
clear ; close all; clc

%% Setup the parameters for this step
input_layer_size  = 784;  % 28x28 Input Images of Digits
num_labels = 10;          % 10 labels, from 1 to 10
                          % (note that we have mapped "0" to label 10)
num_samples = 30000;      % use 25000 training samples

% Load Training Data
fprintf('Loading and Visualizing Data ...\n')

% Read the training data, stored in arrays X, y
[X, y] = loadData('data/train.csv', input_layer_size);

% Randomly choose 100 training samples
rand_indices = randperm(size(X, 1));
X = X(rand_indices(1:num_samples), :);
y = y(rand_indices(1:num_samples), :); %% REMOVE

m = size(X, 1);
fprintf('\nNumber of rows: %d \n', m);

% Display first 100 data points
sel = X(1:100, :);
displayData(sel);

fprintf('\nTraining One-vs-All Logistic Regression...\n')

lambda = 0.1;
[all_theta] = oneVsAll(X, y, num_labels, lambda);

% Save model parameters
fprintf('Save model parameters...\n')
save('all_theta.mat', 'all_theta');

% Predict for One-Vs-All
pred = predictOneVsAll(all_theta, X);

fprintf('\nTraining Set Accuracy: %f\n', mean(double(pred == y)) * 100);


text:
namespace extensions {

using APIBindingBridgeTest = APIBindingTest;

TEST_F(APIBindingBridgeTest, TestUseAfterContextInvalidation) {
  v8::HandleScope handle_scope(isolate());
  v8::Local<v8::Context> context = MainContext();
  v8::Context::Scope context_scope(context);

  std::string extension_id(32, 'a');
  std::string context_type = "context type";
  v8::Local<v8::Object> api_object = v8::Object::New(isolate());

  APIBindingHooks hooks("apiName");
  gin::Handle<APIBindingBridge> bridge_handle = gin::CreateHandle(
      context->GetIsolate(), new APIBindingBridge(&hooks, context, api_object,
                                                  extension_id, context_type));
  v8::Local<v8::Object> bridge_object = bridge_handle.ToV8().As<v8::Object>();

  DisposeContext(context);

  v8::Local<v8::Function> function = FunctionFromString(
      context, "(function(obj) { obj.registerCustomHook(function() {}); })");
  v8::Local<v8::Value> args[] = {bridge_object};
  RunFunctionAndExpectError(function, context, base::size(args), args,
                            "Uncaught Error: Extension context invalidated.");
}

}  // namespace extensions


text:
layout: article
titles:
  # @start locale config
      en      : &EN       Publications
      en-GB   : *EN
      en-US   : *EN
      en-CA   : *EN
      en-AU   : *EN
      zh-Hans : &ZH_HANS
      zh      : *ZH_HANS
      zh-CN   : *ZH_HANS
      zh-SG   : *ZH_HANS
      zh-Hant : &ZH_HANT  發表文章
      zh-TW   : *ZH_HANT
      zh-HK   : *ZH_HANT
      ko      : &KO
      ko-KR   : *KO
      fr      : &FR
      fr-BE   : *FR
      fr-CA   : *FR
      fr-CH   : *FR
      fr-FR   : *FR
      fr-LU   : *FR
  # @end locale config
#key: page-publications
show_title: true
title: Publications
aside:
  toc: true
---

## Conference Paper
1. _Sung-Han Lin_, Marco Paolieri, Cheng-Fu Chou, Leana Golubchik,
__A Model-based Approach to Streamlining Distributed Training for Asynchronous SGD__,
 IEEE International Symposium on the Modeling, Analysis, and Simulation of Computer and Telecommunication Systems (MASCOTS), 2018

    [publisher link (DOI)](https://ieeexplore.ieee.org/abstract/document/8526895){:.button.button--success.button--rounded} [pdf](http://www.mscs.mu.edu/~mascots/Papers/34.pdf){:.button.button--info.button--rounded} [bibtex](#paper5){:.button.button--error.button--rounded}

    @inproceedings{lin2018model,
        title={A model-based approach to streamlining distributed training for asynchronous SGD},
        author={Lin, Sung-Han and Paolieri, Marco and Chou, Cheng-Fu and Golubchik, Leana},
        booktitle={2018 IEEE 26th International Symposium on Modeling, Analysis, and Simulation of Computer and Telecommunication Systems (MASCOTS)},
        pages={306--318},
        year={2018},
        organization={IEEE}
    }

1. _Sung-Han Lin_, Ranjan Pal, Marco Paolieri, Leana Golubchik,
__Performance Driven Resource Sharing Markets for the Small Cloud__,
 IEEE International Conference on Distributed Computing Systems (ICDCS), 2017

    [publisher link (DOI)](http://ieeexplore.ieee.org/document/7979971/){:.button.button--success.button--rounded} [bibtex](#paper4){:.button.button--error.button--rounded}

    @inproceedings{lin2017performance,
        title={Performance driven resource sharing markets for the small cloud},
        author={Lin, Sung-Han and Pal, Ranjan and Paolieri, Marco and Golubchik, Leana},
        booktitle={Distributed Computing Systems (ICDCS), 2017 IEEE 37th International Conference on},
        pages={241--251},
        year={2017},
        organization={IEEE}
    }

1. _Sung-Han Lin_, Ranjan Pal, Bo-Chun Wang, Leana Golubchik,
__Sustaining Ad-Driven P2P Streaming Ecosystems A Market-Based Approach__,
 IEEE/ACM International Symposium on Quality of Service (IWQoS), Jun., 2015

    [publisher link (DOI)](http://ieeexplore.ieee.org/document/7404722/){:.button.button--success.button--rounded} [bibtex](#paper3){:.button.button--error.button--rounded}

    @inproceedings{lin2015sustaining,
        title={Sustaining Ad-driven P2P streaming ecosystems: A market-based approach},
        author={Lin, Sung-Han and Pal, Ranjan and Wang, Bo-Chun and Golubchik, Leana},
        booktitle={Quality of Service (IWQoS), 2015 IEEE 23rd International Symposium on},
        pages={125--134},
        year={2015},
        organization={IEEE}
    }

1. _Sung-Han Lin_, Junn-Yen Hu, Cheng-Fu Chou, Ing-Chau Chang, Chien-Chun Hung,
 __A Novel Social Cluster-based P2P Framework for Integrating VANETs with the Internet__,
 IEEE Wireless Communication and Networking Conference (WCNC), Apr., 2009

    [publisher link (DOI)](http://ieeexplore.ieee.org/document/4917531/){:.button.button--success.button--rounded} [bibtex](#paper2){:.button.button--error.button--rounded}

    @inproceedings{lin2009novel,
        title={A novel social cluster-based p2p framework for integrating vanets with the internet},
        author={Lin, Sung-Han and Hu, Junn-Yen and Chou, Cheng-Fu and Chang, Chau and Hung, Chien-Chun},
        booktitle={Wireless Communications and Networking Conference, 2009. WCNC 2009. IEEE},
        pages={1--6},
        year={2009},
        organization={IEEE}
    }

1. Ching-Ju Lin, _Sung-Han Lin_, Cheng-Fu Chou,
 __Performance Study of Optimal Routing and Channel Assignment in Wireless Mesh Networks__,
 IEEE Global Communications Conference (GLOBECOM), 2007

    [publisher link (DOI)](http://ieeexplore.ieee.org/document/4411824/){:.button.button--success.button--rounded} [bibtex](#paper1){:.button.button--error.button--rounded}

    @inproceedings{lin2007performance,
        title={Performance study of optimal routing and channel assignment in wireless mesh networks},
        author={Lin, Ching-Ju and Lin, Sung-Han and Chou, Cheng-Fu},
        booktitle={Global Telecommunications Conference, 2007. GLOBECOM'07. IEEE},
        pages={4818--4822},
        year={2007},
        organization={IEEE}
    }

## Journal
1. Ranjan Pal, Aditya Ahuja, _Sung-Han Lin_, Abhishek Kumar, Leana Golubchik, Nachikethas A Jagadeesan,
__On the Economic Sustainability of Cloud Sharing Systems Are Dynamic Single Resource Sharing Markets Stable?__,
 ACM SIGMETRICS Performance Evaluation Review, 2019

    [publisher link (DOI)](https://dl.acm.org/doi/abs/10.1145/3372315.3372317){:.button.button--success.button--rounded} [bibtex](#journal3){:.button.button--error.button--rounded}

    @article{pal2019economic,
        title={On the Economic Sustainability of Cloud Sharing Systems Are Dynamic Single Resource Sharing Markets Stable?},
        author={Pal, Ranjan and Ahuja, Aditya and Lin, Sung-Han and Kumar, Abhishek and Golubchik, Leana and Jagadeesan, Nachikethas A},
        journal={ACM SIGMETRICS Performance Evaluation Review},
        volume={46},
        number={4},
        pages={2--10},
        year={2019},
        publisher={ACM New York, NY, USA}
    }

1. Ranjan Pal, Aditya Ahuja, _Sung-Han Lin_, Nachikethas Jagadeesan, Abhishek Kumar, Leana Golubchik,
__Are Federated Cloud Sharing Systems Sustainable?: On Dynamic Sharing Markets and Their Stability__,
 IEEE Transactions on Sustainable Computing, 2019

    [publisher link (DOI)](https://ieeexplore.ieee.org/abstract/document/8910459){:.button.button--success.button--rounded} [bibtex](#journal2){:.button.button--error.button--rounded}

    @article{pal2019federated,
        title={Are Federated Cloud Sharing Systems Sustainable?: On Dynamic Sharing Markets and Their Stability},
        author={Pal, Ranjan and Ahuja, Aditya and Lin, Sung-Han and Jagadeesan, Nachikethas and Kumar, Abhishek and Golubchik, Leana},
        journal={IEEE Transactions on Sustainable Computing},
        year={2019},
        publisher={IEEE}
    }

1. _Sung-Han Lin_, Ranjan Pal, Bo-Chun Wang, Leana Golubchik,
 __On Market-Driven Hybrid-P2P Video Streaming__,
 IEEE Transactions on Multimedia, Issue Date: May.2017, Volume: 19, Issue: 5, pages: 1-15

    [publisher link (DOI)](http://ieeexplore.ieee.org/abstract/document/7797208/){:.button.button--success.button--rounded} [bibtex](#journal1){:.button.button--error.button--rounded}

    @article{lin2017market,
        title={On Market-Driven Hybrid-P2P Video Streaming},
        author={Lin, Sung-Han and Pal, Ranjan and Wang, Bo-Chun and Golubchik, Leana},
        journal={IEEE Transactions on Multimedia},
        volume={19},
        number={5},
        pages={984--998},
        year={2017},
        publisher={IEEE}
    }

## Arxiv
1. Ranjan Pal, _Sung-Han Lin_, Leana Golubchik,
 __The Cloudlet Bazaar Dynamic Markets for the Small Cloud__,
 arXiv preprint arXiv:1704.00845, 2017

    [pdf](https://arxiv.org/pdf/1704.00845){:.button.button--info.button--rounded} [bibtex](#arxiv2){:.button.button--error.button--rounded}

    @article{pal2017cloudlet,
        title={The Cloudlet Bazaar Dynamic Markets for the Small Cloud},
        author={Pal, Ranjan and Lin, Sung-Han and Ahujay, Aditya and Golubchik, Leana},
        journal={arXiv preprint arXiv:1704.00845},
        year={2017}
    }

1. _Sung-Han Lin_, Ranjan Pal, Marco Paolieri, Leana Golubchik,
 __SC-Share: Performance Driven Resource Sharing Markets for the Small Cloud__,
 arXiv preprint arXiv:1703.10318, 2017

    [pdf](https://arxiv.org/pdf/1703.10318){:.button.button--info.button--rounded} [bibtex](#arxiv1){:.button.button--error.button--rounded}

    @article{lin2017sc,
        title={SC-Share: Performance driven resource sharing markets for the small cloud},
        author={Lin, Sung-Han and Pal, Ranjan and Paolieri, Marco and Golubchik, Leana},
        journal={arXiv preprint arXiv:1703.10318},
        year={2017}
    }

## Thesis/Dissertation
1. _Sung-Han Lin_, __Distributed Resource Management for QOS-Aware Service Provision__, Ph.D. Dissertation
1. _Sung-Han Lin_, __On the Design of Vehicular P2P Scheme over Ad Hoc Network and the Internet__, Master Thesis


text:
<?php
namespace MultiRouting\Adapters\JsonRpc\Response;

use MultiRouting\Adapters\JsonRpc\Response\Errors\InternalError;
use MultiRouting\Adapters\JsonRpc\Response\Errors\InvalidParams;
use MultiRouting\Adapters\JsonRpc\Response\Errors\InvalidRequest;
use MultiRouting\Adapters\JsonRpc\Response\Errors\MethodNotFound;
use MultiRouting\Adapters\JsonRpc\Response\Errors\ParseError;
use MultiRouting\Adapters\JsonRpc\Response\Errors\ServerError;

class ErrorFactory
{

    /**
     * @param $data
     * @return ParseError
     */
    public static function parseError($data = null)
    {
        return new ParseError(null, null, $data);
    }

    /**
     * @param $data
     * @return InvalidRequest
     */
    public static function invalidRequest($data = null)
    {
        return new InvalidRequest(null, null, $data);
    }

    /**
     * @param $data
     * @return MethodNotFound
     */
    public static function methodNotFound($data = null)
    {
        return new MethodNotFound(null, null, $data);
    }

    /**
     * @param $data
     * @return InvalidParams
     */
    public static function invalidParams($data = null)
    {
        return new InvalidParams(null, null, $data);
    }

    /**
     * @param $data
     * @return InternalError
     */
    public static function internalError($data = null)
    {
        return new InternalError(null, null, $data);
    }

    /**
     * @param $data
     * @return ServerError
     */
    public static function serverError($data = null)
    {
        return new ServerError(null, null, $data);
    }

    /**
     * @param $code
     * @param $message
     * @param $data
     * @return Error
     */
    public static function applicationError($code, $message, $data = null)
    {
        return new Error($code, $message, $data);
    }
}


text:
package com.google.gerrit.acceptance.rest.change;

import static com.google.common.truth.Truth.assertThat;

import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.RestResponse;
import com.google.gerrit.acceptance.RestSession;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.common.ActionInfo;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gson.reflect.TypeToken;

import org.apache.http.HttpStatus;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;

import java.io.IOException;
import java.util.Map;

public class ActionsIT extends AbstractDaemonTest {
  @ConfigSuite.Config
  public static Config submitWholeTopicEnabled() {
    return submitWholeTopicEnabledConfig();
  }

  @Test
  public void revisionActionsOneChangePerTopicUnapproved() throws Exception {
    String changeId = createChangeWithTopic("foo1").getChangeId();
    Map<String, ActionInfo> actions = getActions(changeId);
    assertThat(actions).containsKey("cherrypick");
    assertThat(actions).containsKey("rebase");
    assertThat(actions).hasSize(2);
  }

  @Test
  public void revisionActionsOneChangePerTopic() throws Exception {
    String changeId = createChangeWithTopic("foo1").getChangeId();
    approve(changeId);
    Map<String, ActionInfo> actions = getActions(changeId);
    commonActionsAssertions(actions);
    // We want to treat a single change in a topic not as a whole topic,
    // so regardless of how submitWholeTopic is configured:
    noSubmitWholeTopicAssertions(actions);
  }

  @Test
  public void revisionActionsTwoChangeChangesInTopic() throws Exception {
    String changeId = createChangeWithTopic("foo2").getChangeId();
    approve(changeId);
    // create another change with the same topic
    createChangeWithTopic("foo2").getChangeId();
    Map<String, ActionInfo> actions = getActions(changeId);
    commonActionsAssertions(actions);
    if (isSubmitWholeTopicEnabled()) {
      ActionInfo info = actions.get("submit");
      assertThat(info.enabled).isNull();
      assertThat(info.label).isEqualTo("Submit whole topic");
      assertThat(info.method).isEqualTo("POST");
      assertThat(info.title).isEqualTo("Other changes in this topic are not ready");
    } else {
      noSubmitWholeTopicAssertions(actions);
    }
  }

  @Test
  public void revisionActionsTwoChangeChangesInTopicReady() throws Exception {
    String changeId = createChangeWithTopic("foo2").getChangeId();
    approve(changeId);
    // create another change with the same topic
    String changeId2 = createChangeWithTopic("foo2").getChangeId();
    approve(changeId2);
    Map<String, ActionInfo> actions = getActions(changeId);
    commonActionsAssertions(actions);
    if (isSubmitWholeTopicEnabled()) {
      ActionInfo info = actions.get("submit");
      assertThat(info.enabled).isTrue();
      assertThat(info.label).isEqualTo("Submit whole topic");
      assertThat(info.method).isEqualTo("POST");
      assertThat(info.title).isEqualTo("Submit all 2 changes of the same topic");
    } else {
      noSubmitWholeTopicAssertions(actions);
    }
  }

  static Map<String, ActionInfo> getActions(RestSession adminSession,
      String changeId) throws IOException {
    return newGson().fromJson(
        adminSession.get("/changes/"
            + changeId
            + "/revisions/1/actions").getReader(),
        new TypeToken<Map<String, ActionInfo>>() {}.getType());
  }

  private Map<String, ActionInfo> getActions(String changeId)
      throws IOException {
    return getActions(adminSession, changeId);
  }

  private void noSubmitWholeTopicAssertions(Map<String, ActionInfo> actions) {
    ActionInfo info = actions.get("submit");
    assertThat(info.enabled).isTrue();
    assertThat(info.label).isEqualTo("Submit");
    assertThat(info.method).isEqualTo("POST");
    assertThat(info.title).isEqualTo("Submit patch set 1 into master");
  }

  private void commonActionsAssertions(Map<String, ActionInfo> actions) {
    assertThat(actions).hasSize(3);
    assertThat(actions).containsKey("cherrypick");
    assertThat(actions).containsKey("submit");
    assertThat(actions).containsKey("rebase");
  }

  private PushOneCommit.Result createChangeWithTopic(String topic)
      throws Exception {
    PushOneCommit push = pushFactory.create(db, admin.getIdent(), testRepo);
    assertThat(topic).isNotEmpty();
    return push.to("refs/for/master/" + topic);
  }

  static void approve(RestSession adminSession, String changeId)
      throws IOException {
    RestResponse r = adminSession.post(
        "/changes/" + changeId + "/revisions/current/review",
        new ReviewInput().label("Code-Review", 2));
    assertThat(r.getStatusCode()).isEqualTo(HttpStatus.SC_OK);
    r.consume();
  }

  private void approve(String changeId) throws IOException {
    approve(adminSession, changeId);
  }
}


text: require('./test.css');


text:
.. _muranopl_extensions:

MuranoPL extension plug-ins
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Murano plug-ins allow extending MuranoPL with new classes. Therefore, using
such plug-ins applications with MuranoPL format, you access some additional
functionality defined in a plug-in. For example, the Magnum plug-in, which
allows murano to deploy applications such as Kubernetes using the capabilities
of the Magnum client.

MuranoPL extension plug-ins can be used for the following purposes:

* Providing interaction with external services.

  For example, you want to interact with the OpenStack Image service to get
  information about images suitable for deployment. A plug-in may request image
  data from glance during deployment, performing any necessary checks.

* Enabling connections between murano applications and external hardware

  For example, you have an external load balancer located on a powerful
  hardware and you want your applications launched in OpenStack to use that
  load balancer. You can write a plug-in that interacts with the load balancer
  API. Once done, add new apps to the pool of your load balancer or make any
  other configurations from within your application definition.

* Extending Core Library class functionality, which is responsible for creating
  networks, interaction with murano-agent, and others

  For example, you want to create networks with special parameters for all of
  your applications. You can just copy the class that is responsible for
  network management from the Murano Core library, make the desired
  modification, and load the new class as a plug-in. Both classes will be
  available, and it is up to you to decide which way to create your networks.

* Optimization of frequently used operations. Plug-in classes are written in
  Python, therefore, the opportunity for improvement is significant.

  Murano provides a number of optimization opportunities depending on the
  improvement needs. For example, classes in the Murano Core Library can be
  rewritten in C and used from Python code to improve their performance in
  particular use cases.

.. _package_type_plugins:

MuranoPL package type plug-ins
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The only package type natively supported by Murano is MuranoPL. However, it is
possible to extend Murano with support for other formats of application
definitions. TOSCA CSARs and HOT templates are the two examples of alternate
ways to define applications.

Package types plug-ins are normal Python packages that can be distributed
through PyPI and installed using :command:`pip` or its alternatives. It is
important that the plug-in be installed to the same Python instance that is
used to run Murano API and Murano Engine. For multi-node Murano deployments,
plug-ins need to be installed on each node.

To associate a plug-in with a particular package format, it needs to have a
special record in `[entry_points]` section of setup.cfg file:

.. code-block:: ini

   io.murano.plugins.packages =
       Name/Version = namespace:Class

For example:

.. code-block:: ini

   [entry_points]
   io.murano.plugins.packages =
       Cloudify.TOSCA/1.0 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage

This declaration maps particular pair of format-name/version to Python class
that implements Package API interface for the package type. It is possible
to specify several different format names or versions and map them to single
or different Python classes. For example, it is possible to specify

.. code-block:: ini

   [entry_points]
   io.murano.plugins.packages =
       Cloudify.TOSCA/1.0 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage
       Cloudify.TOSCA/1.1 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage
       Cloudify.TOSCA/2.0 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage_v2

.. note::

   A single Python plug-in package may contain several Murano plug-ins
   including of different types. For example, it is possible to combine
   MuranoPL extension and package type plug-ins into a single package.

Tooling for package preparation
-------------------------------

Some package formats may require additional tooling to prepare package ZIP
archive of desired structure. In such cases it is expected that those tools
will be provided by plug-in authors either as part of the same Python package
(by exposing additional shell entry points) or as a separate package or
distribution.

The only two exceptions to this rule are native MuranoPL packages and HOT
packages that are built into Murano (there is no need to install additional
plug-ins for them). Tooling for those two formats is a part of
python-muranoclient.

Package API interface reference
-------------------------------

Plug-ins expose API for the rest of Murano to interact with the package
by implementing `murano.packages.package.Package` interface.

Class initializer:

    `def __init__(self, format_name, runtime_version, source_directory, manifest):`

    * **format_name**: name part of the format identifier (string)
    * **runtime_version**: version part of the format identifier (instance of
      semantic_version.Version)
    * **source_directory**: path to the directory where package content was
      extracted (string)
    * **manifest**: contents of the manifest file (string->string dictionary)

    **Note**: implementations must call base class (`Package`) initializer
    passing the first three of these arguments.

Abstract properties that must be implemented by the plug-in:

    `def full_name(self):`

    * Fully qualified name of the package. Must be unique within package
      scope of visibility (string)

    `def version(self):`

    * Package version (not to confuse with format version!). An instance of
      `semantic_version.Version`

    `def classes(self):`

    * List (or tuple) of MuranoPL class names (FQNs) that package contains

    `def requirements(self):`

    * Dictionary of requirements (dependencies on other packages) in a form
      of key-value mapping from required package FQN string to SemVer
      version range specifier (instance of semantic_version.Spec or string
      representation supported by Murano versioning scheme)

    `def package_type(self):`

    * Package type: "Application" or "Library"

    `def display_name(self):`

    * Human-readable name of the package as presented to the user (string)

    `def description(self):`

    * Package description (string or None)

    `def author(self):`

    * Package author (string or None)

    `def supplier(self):`

    * Package supplier (string or None)

    `def tags(self):`

    * List or tags for the package (list of strings)

    `def logo(self):`

    * Package (application) logo file content (str or None)

    `def supplier_logo(self):`

    * Package (application) supplier logo file content (str or None)

    `def ui(self):`

    * YAML-encoded string containing application's form definition (string or
      None)

Abstract methods that must be implemented by the plug-in:

    `def get_class(self, name):`

    * Returns string containing MuranoPL code (YAML-encoded string) for the
      class whose fully qualified name is in "name" parameter (string)

    `def get_resource(self, name):`

    * Returns path for resource file whose name is in "name" parameter (string)

Properties that can be overridden in the plug-in:

    `def format_name(self):`

    * Canonical format name for the plug-in. Usually the same value that was
      passed to class initializer

    `def runtime_version(self):`

    * Format version. Usually the same value that was passed to class
      initializer (semantic_version.Version)

    `def blob(self):`

    * Package file (.zip) content (str)

PackageBase class
-----------------

Usually, there is no need to manually implement all the methods and properties
described. There is a `murano.packages.package.PackageBase` class that provides
typical implementation of most of required properties by obtaining
corresponding value from manifest file.

When inheriting from PackageBase class, plug-in remains responsible for
implementation of:

* `ui` property
* `classes` property
* `get_class` method

This allows plug-in developers to concentrate on dynamic aspects of the package
type plug-in while keeping all static aspects (descriptions, logos and so on)
consistent across all package types (at least those who inherit from
`PackageBase`).


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
Mycologia 68(4): 871 (1976)

#### Original name
Helotium rubroflavum K.S. Thind & Saini

### Remarks
null


text:
require "rails_helper"

describe "Logout" do
  let(:user) { create(:user, admin: true) }

  before do
    login(user)
  end

  it "redirect after logout from basic mode should be correct" do
    check_logout_link_and_redirect
  end

  it "redirect after logout from mission mode should be correct" do
    get("/en/m/#{get_mission.compact_name}")
    check_logout_link_and_redirect
  end

  it "redirect after logout from admin mode should be correct" do
    get("/en/admin")
    check_logout_link_and_redirect
  end

  private

  def check_logout_link_and_redirect
    assert_select('#logout_button[href="/en/logout"]', true)
    delete("/en/logout")
    expect(response).to redirect_to("/en/logged-out")
  end
end


text:
package cache

import (
	"net/http/httptest"
	"net/url"
	"path"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

func parseSelectorOrDie(s string) labels.Selector {
	selector, err := labels.Parse(s)
	if err != nil {
		panic(err)
	}
	return selector
}

// buildResourcePath is a convenience function for knowing if a namespace should be in a path param or not
func buildResourcePath(prefix, namespace, resource string) string {
	base := path.Join("/api", testapi.Version(), prefix)
	if len(namespace) > 0 {
		if !(testapi.Version() == "v1beta1" || testapi.Version() == "v1beta2") {
			base = path.Join(base, "namespaces", namespace)
		}
	}
	return path.Join(base, resource)
}

func getHostFieldLabel() string {
	switch testapi.Version() {
	case "v1beta1", "v1beta2":
		return "DesiredState.Host"
	default:
		return "Status.Host"
	}
}

// buildQueryValues is a convenience function for knowing if a namespace should be in a query param or not
func buildQueryValues(namespace string, query url.Values) url.Values {
	v := url.Values{}
	if query != nil {
		for key, values := range query {
			for _, value := range values {
				v.Add(key, value)
			}
		}
	}
	if testapi.Version() == "v1beta1" || testapi.Version() == "v1beta2" {
		v.Set("namespace", namespace)
	}
	return v
}

func buildLocation(resourcePath string, query url.Values) string {
	return resourcePath + "?" + query.Encode()
}

func TestListWatchesCanList(t *testing.T) {
	table := []struct {
		location      string
		resource      string
		namespace     string
		fieldSelector labels.Selector
	}{
		// Minion
		{
			location:      buildLocation(buildResourcePath("", api.NamespaceAll, "minions"), buildQueryValues(api.NamespaceAll, nil)),
			resource:      "minions",
			namespace:     api.NamespaceAll,
			fieldSelector: parseSelectorOrDie(""),
		},
		// pod with "assigned" field selector.
		{
			location:      buildLocation(buildResourcePath("", api.NamespaceAll, "pods"), buildQueryValues(api.NamespaceAll, url.Values{"fields": []string{getHostFieldLabel() + "="}})),
			resource:      "pods",
			namespace:     api.NamespaceAll,
			fieldSelector: labels.Set{getHostFieldLabel(): ""}.AsSelector(),
		},
		// pod in namespace "foo"
		{
			location:      buildLocation(buildResourcePath("", "foo", "pods"), buildQueryValues("foo", url.Values{"fields": []string{getHostFieldLabel() + "="}})),
			resource:      "pods",
			namespace:     "foo",
			fieldSelector: labels.Set{getHostFieldLabel(): ""}.AsSelector(),
		},
	}
	for _, item := range table {
		handler := util.FakeHandler{
			StatusCode:   500,
			ResponseBody: "",
			T:            t,
		}
		server := httptest.NewServer(&handler)
		defer server.Close()
		client := client.NewOrDie(&client.Config{Host: server.URL, Version: testapi.Version()})
		lw := NewListWatchFromClient(client, item.resource, item.namespace, item.fieldSelector)
		// This test merely tests that the correct request is made.
		lw.List()
		handler.ValidateRequest(t, item.location, "GET", nil)
	}
}

func TestListWatchesCanWatch(t *testing.T) {
	table := []struct {
		rv            string
		location      string
		resource      string
		namespace     string
		fieldSelector labels.Selector
	}{
		// Minion
		{
			location:      buildLocation(buildResourcePath("watch", api.NamespaceAll, "minions"), buildQueryValues(api.NamespaceAll, url.Values{"resourceVersion": []string{""}})),
			rv:            "",
			resource:      "minions",
			namespace:     api.NamespaceAll,
			fieldSelector: parseSelectorOrDie(""),
		},
		{
			location:      buildLocation(buildResourcePath("watch", api.NamespaceAll, "minions"), buildQueryValues(api.NamespaceAll, url.Values{"resourceVersion": []string{"42"}})),
			rv:            "42",
			resource:      "minions",
			namespace:     api.NamespaceAll,
			fieldSelector: parseSelectorOrDie(""),
		},
		// pod with "assigned" field selector.
		{
			location:      buildLocation(buildResourcePath("watch", api.NamespaceAll, "pods"), buildQueryValues(api.NamespaceAll, url.Values{"fields": []string{getHostFieldLabel() + "="}, "resourceVersion": []string{"0"}})),
			rv:            "0",
			resource:      "pods",
			namespace:     api.NamespaceAll,
			fieldSelector: labels.Set{getHostFieldLabel(): ""}.AsSelector(),
		},
		// pod with namespace foo and assigned field selector
		{
			location:      buildLocation(buildResourcePath("watch", "foo", "pods"), buildQueryValues("foo", url.Values{"fields": []string{getHostFieldLabel() + "="}, "resourceVersion": []string{"0"}})),
			rv:            "0",
			resource:      "pods",
			namespace:     "foo",
			fieldSelector: labels.Set{getHostFieldLabel(): ""}.AsSelector(),
		},
	}

	for _, item := range table {
		handler := util.FakeHandler{
			StatusCode:   500,
			ResponseBody: "",
			T:            t,
		}
		server := httptest.NewServer(&handler)
		defer server.Close()
		client := client.NewOrDie(&client.Config{Host: server.URL, Version: testapi.Version()})
		lw := NewListWatchFromClient(client, item.resource, item.namespace, item.fieldSelector)
		// This test merely tests that the correct request is made.
		lw.Watch(item.rv)
		handler.ValidateRequest(t, item.location, "GET", nil)
	}
}


text:
using System;

namespace SimpleContainer.Helpers
{
	public struct ValueWithType
	{
		public object value;
		public Type type;
	}
}


text:
package org.commando.dispatcher.filter.retry;

/**
 * Created by pborbas on 19/02/16.
 */
public interface LimitedRetryCommand {

	String getRetryKey();
	int getMaxRetries();

}


text:
package org.nutz.castor.castor;

import java.text.DateFormat;
import java.text.ParseException;

import org.nutz.lang.Lang;
import org.nutz.lang.Strings;

public class String2SqlDate extends DateTimeCastor<String, java.sql.Date> {

    @Override
    public java.sql.Date cast(String src, Class<?> toType, String... args) {
        if (Strings.isBlank(src))
            return null;
        try {
            return new java.sql.Date(((DateFormat) dateFormat.clone()).parse(src).getTime());
        }
        catch (ParseException e) {
            throw Lang.wrapThrow(e);
        }
    }

}


text:
// This file is licensed to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Linq;
using System.Text;

namespace Spines.Tenhou.Client
{
  /// <summary>
  /// A username as used by tenhou.net.
  /// </summary>
  public class UserName
  {
    /// <summary>
    /// The plain text name.
    /// </summary>
    public string Name
    {
      get { return Decode(EncodedName); }
    }

    private static string Decode(string encodedName)
    {
      var encodedCharacters = encodedName.Split(new[] {'%'}, StringSplitOptions.RemoveEmptyEntries);
      var decodedCharacters = encodedCharacters.Select(c => Convert.ToByte(c, 16)).ToArray();
      return new UTF8Encoding().GetString(decodedCharacters);
    }

    internal UserName(string encodedName)
    {
      EncodedName = encodedName;
    }

    internal string EncodedName { get; }
  }
}


text:
m = Map("luci_statistics",
	translate("Splash Leases Plugin Configuration"),
	translate("The splash leases plugin uses libuci to collect statistics about splash leases."))

s = m:section( NamedSection, "collectd_splash_leases", "luci_statistics" )

enable = s:option( Flag, "enable", translate("Enable this plugin") )
enable.default = 1

return m


text:
class ALM::Constants
  HOST = "localhost"
  PORT = "8080"
  USERNAME = "sa"
  PASSWORD = "C71a04t23"
  DOMAIN = "DEFAULT"
  PROJECT = "Simon"
  VERSIONED = false
end


text:
@interface BRPtouchPrinterData : NSObject
{
	NSArray*	aryPrinterList;
}

- (NSArray*)getPrinterList;

@end


text:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  	xmlns:android="http://schemas.android.com/apk/res/android"
  	android:layout_width="fill_parent"
  	android:layout_height="fill_parent"
  	>

  	<RelativeLayout
  		android:id="@+id/titleArea"

  		android:layout_width="fill_parent"
  		android:layout_height="wrap_content"
  		android:layout_alignParentTop="true"
  		android:layout_alignParentLeft="true"
  		android:layout_alignParentRight="true"

  		android:background="@drawable/titlebackground"
  		>

	  	<ImageView
	  		android:src="@drawable/logo"
	  		android:id="@+id/droidLogo"
	  		android:layout_width="42sp"
	  		android:layout_height="38sp"
	  		android:scaleType="fitXY"

	  		android:paddingLeft="10sp"
	  		android:paddingTop="3sp"
	  		android:paddingBottom="3sp"
	  		android:clickable="true"

	  		android:layout_alignParentTop="true"
	  		android:layout_alignParentLeft="true"
	  		/>

	  	<TextView
	  		android:id="@+id/title"
	  		android:text="@string/listViewTitle"
	  		android:textSize="24sp"
	  		android:textStyle="bold"
	  		android:textColor="#FFF"
	  		android:paddingLeft="5dp"
	  		android:paddingRight="10dp"
	  		android:layout_marginTop="-4dp"
	  		android:shadowColor="#000"
	  		android:shadowDx="1"
	  		android:shadowDy="1"
	  		android:shadowRadius="2"

	  		android:layout_height="wrap_content"
	  		android:layout_width="wrap_content"
	  		android:layout_alignParentTop="true"
	  		android:layout_toRightOf="@id/droidLogo"
	  		/>

		<TextView
			android:id="@+id/lastSynch"
			android:text="Utolsó frissítés: 1 órája"
			android:textSize="10sp"
			android:textStyle="italic"
			android:textColor="#DDD"
			android:gravity="right"
			android:paddingRight="3sp"

			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
	  		android:layout_alignParentRight="true"
	  		android:layout_alignBottom="@id/droidLogo"
			/>

 	  	<ImageView
	  		android:src="@drawable/mini_refresh"
	  		android:layout_width="wrap_content"
	  		android:layout_height="wrap_content"
	  		android:scaleType="fitXY"

	  		android:layout_toLeftOf="@id/lastSynch"
	  		android:layout_alignBottom="@id/droidLogo"
	  		/>

  	</RelativeLayout>

  	<ImageView
  		android:src="@drawable/titleborder"
  		android:id="@+id/titleBorder"
  		android:layout_width="fill_parent"
  		android:layout_height="4sp"

  		android:layout_below="@id/titleArea"
  		android:layout_alignParentLeft="true"
  		android:layout_alignParentRight="true"
  		/>

	<ListView
		android:id="@+id/rssItemList"
	  	android:layout_width="fill_parent"
	  	android:layout_height="fill_parent"

	  	android:layout_below="@id/titleBorder"
	  	/>

	<LinearLayout
  		android:id="@+id/toolbar"
		android:orientation="horizontal"
  		android:layout_width="fill_parent"
  		android:layout_height="wrap_content"
  		android:background="@drawable/toolbarbackground"
  		android:gravity="center_horizontal"

  		android:layout_alignParentBottom="true"
		>

		<ImageButton
			android:id="@+id/toolbarRefresh"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_margin="5dp"
			android:padding="2dp"
			android:background="@drawable/transp_button"

			android:src="@drawable/ic_menu_rotate"
			/>

		<ImageButton
			android:id="@+id/toolbarReadAll"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_margin="5dp"
			android:padding="2dp"
			android:background="@drawable/transp_button"

			android:src="@drawable/ic_menu_agenda"
			/>

		<ImageButton
			android:id="@+id/toolbarPreferences"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_margin="5dp"
			android:padding="2dp"
			android:background="@drawable/transp_button"

			android:src="@drawable/ic_menu_preferences"
			/>

		<ImageButton
			android:id="@+id/toolbarAbout"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:layout_margin="5dp"
			android:padding="2dp"
			android:background="@drawable/transp_button"

			android:src="@drawable/ic_menu_info_details"
			/>

	</LinearLayout>

</RelativeLayout>


text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cats.Models.Security
{
   public class ForgetPasswordRequest
    {
        public int ForgetPasswordRequestID { get; set; }
        public int UserProfileID { get; set; }
        public DateTime GeneratedDate { get; set; }
        public DateTime ExpieryDate { get; set; }
        public bool Completed { get; set; }
        public string RequestKey { get; set; }

    }
}


text:
FM_ID: ''
name: Bank Of America Plaza Farmers Market
address_1: 333 S Hope St
address_2: ''
city: Los Angeles
state: California
zip: '90021'
phone: (818)591-8161
latitude: '34.0533'
longitude: '-118.2529'
website: ''
daycode1: Fri
day1_open: '1000'
day1_close: '1400'
daycode2: ''
day2_open: ''
day2_close: ''
category: Farmers Market
title: 'Bank Of America Plaza Farmers Market, Food Oasis Los Angeles'
uri: /farmers-market/bank-of-america-plaza/
formatted_daycode1: Friday
formatted_day1_open: 10am
formatted_day1_close: 2pm

---


text:
namespace ca {

class Window;

class MessagePumpUI : public nu::MessagePump {
  NU_DELETE_COPY_AND_MOVE(MessagePumpUI);

public:
  explicit MessagePumpUI(Window* window);
  ~MessagePumpUI() override;

  // Override: nu::MessagePump
  void schedule_task() override;
  void run(nu::MessagePump::Delegate* delegate) override;

private:
  Window* window_;
};

}  // namespace ca


text:
module Neography
  class PathTraverser
    include Enumerable

    attr_accessor :depth, :algorithm, :relationships, :get

    def initialize(from, to, algorithm, all=false, types = nil, dir = "all" )
      @from  = from
      @to = to
      @algorithm = algorithm
      @all = all
      @relationships = Array.new
      types.each do |type|
        @relationships << {"type" => type.to_s, "direction" => dir.to_s }
      end unless types.nil?
      @get = ["node","rel"]
      @loaded_nodes = Array.new
      @loaded_rels = Array.new
    end

    def nodes
      @get = ["node"]
      self
    end

    def relationships
      @get = ["rel"]
      self
    end

    alias_method :rels, :relationships

    def both(type)
      @relationships << {"type" => type.to_s, "direction" => "all"}
      self
    end

    def outgoing(type)
      @relationships << {"type" => type.to_s, "direction" => "out"}
      self
    end

    def incoming(type)
      @relationships << {"type" => type.to_s, "direction" => "in"}
      self
    end

    def depth(d)
      d = 2147483647 if d == :all
      @depth = d
      self
    end

    def size
      [*self].size
    end

    alias_method :length, :size

    def each
      iterator.each do |path|
        paths = Array.new

        if @get.include?("node")
          path["nodes"].each_with_index do |n, i|
            @loaded_nodes[get_id(n)] = Neography::Node.load(n) if @loaded_nodes.at(get_id(n)).nil?
            paths[i * 2] =  @loaded_nodes[get_id(n)]
          end
        end

        if @get.include?("rel")
          path["relationships"].each_with_index do |r, i|
            @loaded_rels[get_id(r)] = Neography::Relationship.load(r)  if @loaded_rels.at(get_id(r)).nil?
            paths[i * 2 + 1] =  @loaded_rels[get_id(r)]
          end
        end

        yield paths.compact
      end
    end

    def empty?
      first == nil
    end

    def iterator
      if @all.nil?
        @from.neo_server.get_path(@from, @to, @relationships, @depth, @algorithm)
      else
        @from.neo_server.get_paths(@from, @to, @relationships, @depth, @algorithm)
      end
    end

    private
    def get_id(object)
      object.split('/').last.to_i
    end

  end
end


text:
from __future__ import unicode_literals

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('engine_app', '0002_pissimages_local_filename'),
    ]

    operations = [
        migrations.AddField(
            model_name='pissimages',
            name='qiniu_filename',
            field=models.CharField(default=None, max_length=64, unique=True),
        ),
    ]


text:
(function() {

  window.FlowPayPal = {};

  FlowPayPal.client = {
    sandbox:    'not-defined',
    production: 'not-defined'
  }

  FlowPayPal.bind = function(button_id) {
    paypal.Button.render({
      env: FlowPayPal.opts.environment,

      client: FlowPayPal.opts.client,

      commit: true,

      style: {
        label: 'checkout',
        size:  'responsive',
        shape: 'rect',
        color: 'gold',
      },

      payment: function(resolve, reject) {
        $.post('/flow/paypal_id?order=' + FlowPayPal.opts.order).always(function(response){
          if (!response.paypal || response.state == 500) {
            message = response.responseJSON.message || 'Server error';
            alert(message);
            reject(message);
          } else {
            paypal_id = response.paypal.payment_id

            if (paypal_id.indexOf('PAY-') > -1) {
              resolve(paypal_id);
            } else {
              reject('Invalid PayPal ID')
            }
          }
        })
      },

      onAuthorize: function(data, actions) {
        // console.log(data, actions);
        // alert('onAuthorize called');

        return actions.payment.get().then(function(paymentDetails) {
          console.log(paymentDetails);
          return actions.payment.execute().then(function(paymentData) {
            $.post('/flow/paypal_finish?order=' + FlowPayPal.opts.order, function(response) {
              if (response.order_number) {
                location.href = '/orders/' + response.order_number
              } else {
                alert(response.error);
              }
            });
          });
        });
      },

      onCancel: function() {
        console.log('PayPal: canceled');
        return undefined;
      },

      onError: function(data, msg) {
        console.log('PayPal: error', data);
        return undefined;
      }

    }, button_id);
  }

})();