<commit_before>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import { MatrixClientPeg } from '../MatrixClientPeg';
import { accessSecretStorage, AccessCancelledError } from '../CrossSigningManager';
import { PHASE_DONE as VERIF_PHASE_DONE } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";

export const PHASE_INTRO = 0;
export const PHASE_BUSY = 1;
export const PHASE_DONE = 2;    //final done stage, but still showing UX
export const PHASE_CONFIRM_SKIP = 3;
export const PHASE_FINISHED = 4; //UX can be closed

export class SetupEncryptionStore extends EventEmitter {
    static sharedInstance() {
        if (!global.mx_SetupEncryptionStore) global.mx_SetupEncryptionStore = new SetupEncryptionStore();
        return global.mx_SetupEncryptionStore;
    }

    start() {
        if (this._started) {
            return;
        }
        this._started = true;
        this.phase = PHASE_BUSY;
        this.verificationRequest = null;
        this.backupInfo = null;

        // ID of the key that the secrets we want are encrypted with
        this.keyId = null;
        // Descriptor of the key that the secrets we want are encrypted with
        this.keyInfo = null;

        const cli = MatrixClientPeg.get();
        cli.on("crypto.verification.request", this.onVerificationRequest);
        cli.on('userTrustStatusChanged', this._onUserTrustStatusChanged);

        const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId());
        if (requestsInProgress.length) {
            // If there are multiple, we take the most recent. Equally if the user sends another request from
            // another device after this screen has been shown, we'll switch to the new one, so this
            // generally doesn't support multiple requests.
            this._setActiveVerificationRequest(requestsInProgress[requestsInProgress.length - 1]);
        }

        this.fetchKeyInfo();
    }

    stop() {
        if (!this._started) {
            return;
        }
        this._started = false;
        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("crypto.verification.request", this.onVerificationRequest);
            MatrixClientPeg.get().removeListener('userTrustStatusChanged', this._onUserTrustStatusChanged);
        }
    }

    async fetchKeyInfo() {
        const keys = await MatrixClientPeg.get().isSecretStored('m.cross_signing.master', false);
        if (Object.keys(keys).length === 0) {
            this.keyId = null;
            this.keyInfo = null;
        } else {
            // If the secret is stored under more than one key, we just pick an arbitrary one
            this.keyId = Object.keys(keys)[0];
            this.keyInfo = keys[this.keyId];
        }

        this.phase = PHASE_INTRO;
        this.emit("update");
    }

    async usePassPhrase() {
        this.phase = PHASE_BUSY;
        this.emit("update");
        const cli = MatrixClientPeg.get();
        try {
            const backupInfo = await cli.getKeyBackupVersion();
            this.backupInfo = backupInfo;
            this.emit("update");
            // The control flow is fairly twisted here...
            // For the purposes of completing security, we only wait on getting
            // as far as the trust check and then show a green shield.
            // We also begin the key backup restore as well, which we're
            // awaiting inside `accessSecretStorage` only so that it keeps your
            // passphase cached for that work. This dialog itself will only wait
            // on the first trust check, and the key backup restore will happen
            // in the background.
            await new Promise((resolve, reject) => {
                try {
                    accessSecretStorage(async () => {
                        await cli.checkOwnCrossSigningTrust();
                        resolve();
                        if (backupInfo) {
                            // A complete restore can take many minutes for large
                            // accounts / slow servers, so we allow the dialog
                            // to advance before this.
                            await cli.restoreKeyBackupWithSecretStorage(backupInfo);
                        }
                    }).catch(reject);
                } catch (e) {
                    console.error(e);
                    reject(e);
                }
            });

            if (cli.getCrossSigningId()) {
                this.phase = PHASE_DONE;
                this.emit("update");
            }
        } catch (e) {
            if (!(e instanceof AccessCancelledError)) {
                console.log(e);
            }
            // this will throw if the user hits cancel, so ignore
            this.phase = PHASE_INTRO;
            this.emit("update");
        }
    }

    _onUserTrustStatusChanged = async (userId) => {
        if (userId !== MatrixClientPeg.get().getUserId()) return;
        const crossSigningReady = await MatrixClientPeg.get().isCrossSigningReady();
        if (crossSigningReady) {
            this.phase = PHASE_DONE;
            this.emit("update");
        }
    }

    onVerificationRequest = (request) => {
        this._setActiveVerificationRequest(request);
    }

    onVerificationRequestChange = async () => {
        if (this.verificationRequest.cancelled) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            this.emit("update");
        } else if (this.verificationRequest.phase === VERIF_PHASE_DONE) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            // At this point, the verification has finished, we just need to wait for
            // cross signing to be ready to use, so wait for the user trust status to
            // change (or change to DONE if it's already ready).
            const crossSigningReady = await MatrixClientPeg.get().isCrossSigningReady();
            this.phase = crossSigningReady ? PHASE_DONE : PHASE_BUSY;
            this.emit("update");
        }
    }

    skip() {
        this.phase = PHASE_CONFIRM_SKIP;
        this.emit("update");
    }

    skipConfirm() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
    }

    returnAfterSkip() {
        this.phase = PHASE_INTRO;
        this.emit("update");
    }

    done() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
        // async - ask other clients for keys, if necessary
        MatrixClientPeg.get()._crypto.cancelAndResendAllOutgoingKeyRequests();
    }

    async _setActiveVerificationRequest(request) {
        if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return;

        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        this.verificationRequest = request;
        await request.accept();
        request.on("change", this.onVerificationRequestChange);
        this.emit("update");
    }
}
<commit_msg>Add null check<commit_after>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import { MatrixClientPeg } from '../MatrixClientPeg';
import { accessSecretStorage, AccessCancelledError } from '../CrossSigningManager';
import { PHASE_DONE as VERIF_PHASE_DONE } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";

export const PHASE_INTRO = 0;
export const PHASE_BUSY = 1;
export const PHASE_DONE = 2;    //final done stage, but still showing UX
export const PHASE_CONFIRM_SKIP = 3;
export const PHASE_FINISHED = 4; //UX can be closed

export class SetupEncryptionStore extends EventEmitter {
    static sharedInstance() {
        if (!global.mx_SetupEncryptionStore) global.mx_SetupEncryptionStore = new SetupEncryptionStore();
        return global.mx_SetupEncryptionStore;
    }

    start() {
        if (this._started) {
            return;
        }
        this._started = true;
        this.phase = PHASE_BUSY;
        this.verificationRequest = null;
        this.backupInfo = null;

        // ID of the key that the secrets we want are encrypted with
        this.keyId = null;
        // Descriptor of the key that the secrets we want are encrypted with
        this.keyInfo = null;

        const cli = MatrixClientPeg.get();
        cli.on("crypto.verification.request", this.onVerificationRequest);
        cli.on('userTrustStatusChanged', this._onUserTrustStatusChanged);

        const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId());
        if (requestsInProgress.length) {
            // If there are multiple, we take the most recent. Equally if the user sends another request from
            // another device after this screen has been shown, we'll switch to the new one, so this
            // generally doesn't support multiple requests.
            this._setActiveVerificationRequest(requestsInProgress[requestsInProgress.length - 1]);
        }

        this.fetchKeyInfo();
    }

    stop() {
        if (!this._started) {
            return;
        }
        this._started = false;
        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("crypto.verification.request", this.onVerificationRequest);
            MatrixClientPeg.get().removeListener('userTrustStatusChanged', this._onUserTrustStatusChanged);
        }
    }

    async fetchKeyInfo() {
        const keys = await MatrixClientPeg.get().isSecretStored('m.cross_signing.master', false);
        if (keys === null || Object.keys(keys).length === 0) {
            this.keyId = null;
            this.keyInfo = null;
        } else {
            // If the secret is stored under more than one key, we just pick an arbitrary one
            this.keyId = Object.keys(keys)[0];
            this.keyInfo = keys[this.keyId];
        }

        this.phase = PHASE_INTRO;
        this.emit("update");
    }

    async usePassPhrase() {
        this.phase = PHASE_BUSY;
        this.emit("update");
        const cli = MatrixClientPeg.get();
        try {
            const backupInfo = await cli.getKeyBackupVersion();
            this.backupInfo = backupInfo;
            this.emit("update");
            // The control flow is fairly twisted here...
            // For the purposes of completing security, we only wait on getting
            // as far as the trust check and then show a green shield.
            // We also begin the key backup restore as well, which we're
            // awaiting inside `accessSecretStorage` only so that it keeps your
            // passphase cached for that work. This dialog itself will only wait
            // on the first trust check, and the key backup restore will happen
            // in the background.
            await new Promise((resolve, reject) => {
                try {
                    accessSecretStorage(async () => {
                        await cli.checkOwnCrossSigningTrust();
                        resolve();
                        if (backupInfo) {
                            // A complete restore can take many minutes for large
                            // accounts / slow servers, so we allow the dialog
                            // to advance before this.
                            await cli.restoreKeyBackupWithSecretStorage(backupInfo);
                        }
                    }).catch(reject);
                } catch (e) {
                    console.error(e);
                    reject(e);
                }
            });

            if (cli.getCrossSigningId()) {
                this.phase = PHASE_DONE;
                this.emit("update");
            }
        } catch (e) {
            if (!(e instanceof AccessCancelledError)) {
                console.log(e);
            }
            // this will throw if the user hits cancel, so ignore
            this.phase = PHASE_INTRO;
            this.emit("update");
        }
    }

    _onUserTrustStatusChanged = async (userId) => {
        if (userId !== MatrixClientPeg.get().getUserId()) return;
        const crossSigningReady = await MatrixClientPeg.get().isCrossSigningReady();
        if (crossSigningReady) {
            this.phase = PHASE_DONE;
            this.emit("update");
        }
    }

    onVerificationRequest = (request) => {
        this._setActiveVerificationRequest(request);
    }

    onVerificationRequestChange = async () => {
        if (this.verificationRequest.cancelled) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            this.emit("update");
        } else if (this.verificationRequest.phase === VERIF_PHASE_DONE) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            // At this point, the verification has finished, we just need to wait for
            // cross signing to be ready to use, so wait for the user trust status to
            // change (or change to DONE if it's already ready).
            const crossSigningReady = await MatrixClientPeg.get().isCrossSigningReady();
            this.phase = crossSigningReady ? PHASE_DONE : PHASE_BUSY;
            this.emit("update");
        }
    }

    skip() {
        this.phase = PHASE_CONFIRM_SKIP;
        this.emit("update");
    }

    skipConfirm() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
    }

    returnAfterSkip() {
        this.phase = PHASE_INTRO;
        this.emit("update");
    }

    done() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
        // async - ask other clients for keys, if necessary
        MatrixClientPeg.get()._crypto.cancelAndResendAllOutgoingKeyRequests();
    }

    async _setActiveVerificationRequest(request) {
        if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return;

        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        this.verificationRequest = request;
        await request.accept();
        request.on("change", this.onVerificationRequestChange);
        this.emit("update");
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow
import type {
  CategoryList,
  Marker,
  MarkerIndex,
  MarkerTiming,
  MarkerTimingAndBuckets,
} from 'firefox-profiler/types';

// Arbitrarily set an upper limit for adding marker depths, avoiding an infinite loop.
const MAX_STACKING_DEPTH = 300;

/**
 * This function computes the timing information for laying out the markers in the
 * MarkerChart component. Each marker is put into a single row based on its name. In
 * addition they are grouped by "buckets", which is based off of their category.
 * This structure is a simple array, as it makes it very easy to loop through the
 * fixed height rows in the canvas, and draw only what is in view.
 *
 * e.g. An array of 15 markers named either "A", "B", or "C" would be translated into
 *      something that looks like:
 *
 *  [
 *    "DOM", // The bucket.
 *    {
 *      name: "A",
 *      start: [0, 23, 35, 65, 75],
 *      end: [1, 25, 37, 67, 77],
 *      index: [0, 2, 5, 6, 8],
 *      label: ["Aye", "Aye", "Aye", "Aye", "Aye"]
 *      bucket: "DOM"
 *    }
 *    {
 *      name: "B",
 *      start: [1, 28, 39, 69, 70],
 *      end: [2, 29, 49, 70, 77],
 *      index: [1, 3, 7, 9, 10],
 *      label: ["Bee", "Bee", "Bee", "Bee", "Bee"]
 *      bucket: "DOM"
 *    }
 *    "Other", // The bucket.
 *    {
 *      name: "C",
 *      start: [10, 33, 45, 75, 85],
 *      end: [11, 35, 47, 77, 87],
 *      index: [4, 11, 12, 13, 14],
 *      label: ["Sea", "Sea", "Sea", "Sea", "Sea"]
 *      bucket: "Other"
 *    }
 *  ]
 *
 * If a marker of a name has timings that overlap in a single row, then it is broken
 * out into multiple rows, with the overlapping timings going in the next rows. The
 * getMarkerTiming tests show the behavior of how this works in practice.
 *
 * This structure allows the markers to easily be laid out like this example below:
 *    ____________________________________________
 *   |              | GC/CC                       | <- Bucket, represented as a `string`
 *   | GCMajor      | *---------------------*     | <- MarkerTimingRow
 *   | GCMinor      | *--*       *--*        *--* | <- MarkerTimingRow
 *   |              | DOM                         | <- Bucket
 *   | User Timings |    *----------------*       | <- MarkerTimingRow
 *   | User Timings |       *------------*        | <- MarkerTimingRow
 *   | User Timings |       *--*     *---*        | <- MarkerTimingRow
 *   |______________|_____________________________|
 */
export function getMarkerTimingAndBuckets(
  getMarker: (MarkerIndex) => Marker,
  markerIndexes: MarkerIndex[],
  // Categories can be null for things like Network Markers, where we don't care to
  // break things up by category.
  getLabel: (MarkerIndex) => string,
  categories: ?CategoryList
): MarkerTimingAndBuckets {
  // Each marker type will have it's own timing information, later collapse these into
  // a single array.
  const markerTimingsMap: Map<string, MarkerTiming[]> = new Map();

  // Go through all of the markers.
  for (const markerIndex of markerIndexes) {
    const marker = getMarker(markerIndex);

    // We want to group all network requests in the same line. Indeed they all
    // have different names and they'd end up with one single request in each
    // line without this special handling.
    const markerLineName =
      marker.data && marker.data.type === 'Network'
        ? 'Network Requests'
        : marker.name;
    let markerTimingsByName = markerTimingsMap.get(markerLineName);
    if (markerTimingsByName === undefined) {
      markerTimingsByName = [];
      markerTimingsMap.set(markerLineName, markerTimingsByName);
    }

    // Place the marker in the closest row that is empty.
    for (let i = 0; i < MAX_STACKING_DEPTH; i++) {
      const bucketName = categories ? categories[marker.category].name : 'None';

      // Get or create a row for marker timings.
      let markerTiming = markerTimingsByName[i];
      if (!markerTiming) {
        markerTiming = {
          start: [],
          end: [],
          index: [],
          label: [],
          name: markerLineName,
          bucket: bucketName,
          length: 0,
        };
        markerTimingsByName.push(markerTiming);
      }

      // Since the markers are sorted, look at the last added marker in this row. If
      // the new marker fits, go ahead and insert it.
      const otherEnd = markerTiming.end[markerTiming.length - 1];
      if (otherEnd === undefined || otherEnd <= marker.start) {
        markerTiming.start.push(marker.start);
        markerTiming.end.push(
          // If this is an instant marker, the start time and end time will match.
          // The chart will then be responsible for drawing this as a dot.
          marker.end === null ? marker.start : marker.end
        );
        markerTiming.label.push(getLabel(markerIndex));
        markerTiming.index.push(markerIndex);
        markerTiming.length++;
        break;
      }
    }
  }

  // Flatten out the map into a single array.
  const allMarkerTimings = [].concat(...markerTimingsMap.values());

  // Sort all the marker timings in place, first by the bucket, then by their names.
  allMarkerTimings.sort((a, b) => {
    if (a.bucket !== b.bucket) {
      // Sort by buckets first.
      // Show the 'Test' category first. Test markers are almost guaranteed to
      // be the most relevant when they exist.
      if (a.bucket === 'Test') {
        return -1;
      }
      if (b.bucket === 'Test') {
        return 1;
      }
      // Put the 'Other' category last.
      if (a.bucket === 'Other') {
        return 1;
      }
      if (b.bucket === 'Other') {
        return -1;
      }
      // Sort alphabetically for the remaining categories.
      return a.bucket > b.bucket ? 1 : -1;
    }
    if (a.name === b.name) {
      // Keep the original ordering if the names are the same.
      return 0;
    }

    // Put network requests at the end of the Network category.
    if (a.bucket === 'Network') {
      if (a.name === 'Network Requests') {
        return 1;
      }
      if (b.name === 'Network Requests') {
        return -1;
      }
    }

    // Sort by names second
    return a.name > b.name ? 1 : -1;
  });

  // Interleave the bucket names in between the marker timings.
  const markerTimingsAndBuckets: MarkerTimingAndBuckets = [];
  let prevBucket;
  for (const markerTiming of allMarkerTimings) {
    if (markerTiming.bucket !== prevBucket) {
      markerTimingsAndBuckets.push(markerTiming.bucket);
      prevBucket = markerTiming.bucket;
    }
    markerTimingsAndBuckets.push(markerTiming);
  }

  return markerTimingsAndBuckets;
}

export function getMarkerTiming(
  getMarker: (MarkerIndex) => Marker,
  markerIndexes: MarkerIndex[],
  getLabel: (MarkerIndex) => string
): MarkerTiming[] {
  // Flow didn't understand the filter operation here, so filter out bucket names
  // imperatively.
  const onlyTiming = [];
  for (const timingOrString of getMarkerTimingAndBuckets(
    getMarker,
    markerIndexes,
    getLabel
  )) {
    if (typeof timingOrString !== 'string') {
      onlyTiming.push(timingOrString);
    }
  }
  return onlyTiming;
}
<commit_msg>Small refactor of marker timings, where we don't interleave bucket names when not needed<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @flow
import type {
  CategoryList,
  Marker,
  MarkerIndex,
  MarkerTiming,
  MarkerTimingAndBuckets,
} from 'firefox-profiler/types';

// Arbitrarily set an upper limit for adding marker depths, avoiding an infinite loop.
const MAX_STACKING_DEPTH = 300;

/**
 * This function computes the timing information for laying out the markers in the
 * MarkerChart component. Each marker is put into a single row based on its name. In
 * addition they are grouped by "buckets", which is based off of their category.
 * This structure is a simple array, as it makes it very easy to loop through the
 * fixed height rows in the canvas, and draw only what is in view.
 *
 * e.g. An array of 15 markers named either "A", "B", or "C" would be translated into
 *      something that looks like:
 *
 *  [
 *    {
 *      name: "A",
 *      start: [0, 23, 35, 65, 75],
 *      end: [1, 25, 37, 67, 77],
 *      index: [0, 2, 5, 6, 8],
 *      label: ["Aye", "Aye", "Aye", "Aye", "Aye"],
 *      bucket: "DOM",
 *    },
 *    {
 *      name: "B",
 *      start: [1, 28, 39, 69, 70],
 *      end: [2, 29, 49, 70, 77],
 *      index: [1, 3, 7, 9, 10],
 *      label: ["Bee", "Bee", "Bee", "Bee", "Bee"],
 *      bucket: "DOM",
 *    },
 *    {
 *      name: "C",
 *      start: [10, 33, 45, 75, 85],
 *      end: [11, 35, 47, 77, 87],
 *      index: [4, 11, 12, 13, 14],
 *      label: ["Sea", "Sea", "Sea", "Sea", "Sea"],
 *      bucket: "Other",
 *    },
 *  ]
 *
 * If a marker of a name has timings that overlap in a single row, then it is broken
 * out into multiple rows, with the overlapping timings going in the next rows. The
 * getMarkerTiming tests show the behavior of how this works in practice.
 *
 * This structure allows the markers to easily be laid out like this example below:
 *    ____________________________________________
 *   |              | GC/CC                       | <- Bucket, represented as a `string`
 *   | GCMajor      | *---------------------*     | <- MarkerTimingRow
 *   | GCMinor      | *--*       *--*        *--* | <- MarkerTimingRow
 *   |              | DOM                         | <- Bucket
 *   | User Timings |    *----------------*       | <- MarkerTimingRow
 *   | User Timings |       *------------*        | <- MarkerTimingRow
 *   | User Timings |       *--*     *---*        | <- MarkerTimingRow
 *   |______________|_____________________________|
 *
 * Note that the bucket names aren't present as a result of this function,
 * they're inserted in `getMarkerTimingAndBuckets`.
 */
export function getMarkerTiming(
  getMarker: (MarkerIndex) => Marker,
  markerIndexes: MarkerIndex[],
  // Categories can be null for things like Network Markers, where we don't care to
  // break things up by category.
  getLabel: (MarkerIndex) => string,
  categories: ?CategoryList
): MarkerTiming[] {
  // Each marker type will have it's own timing information, later collapse these into
  // a single array.
  const markerTimingsMap: Map<string, MarkerTiming[]> = new Map();

  // Go through all of the markers.
  for (const markerIndex of markerIndexes) {
    const marker = getMarker(markerIndex);

    // We want to group all network requests in the same line. Indeed they all
    // have different names and they'd end up with one single request in each
    // line without this special handling.
    const markerLineName =
      marker.data && marker.data.type === 'Network'
        ? 'Network Requests'
        : marker.name;
    let markerTimingsByName = markerTimingsMap.get(markerLineName);
    if (markerTimingsByName === undefined) {
      markerTimingsByName = [];
      markerTimingsMap.set(markerLineName, markerTimingsByName);
    }

    // Place the marker in the closest row that is empty.
    for (let i = 0; i < MAX_STACKING_DEPTH; i++) {
      const bucketName = categories ? categories[marker.category].name : 'None';

      // Get or create a row for marker timings.
      let markerTiming = markerTimingsByName[i];
      if (!markerTiming) {
        markerTiming = {
          start: [],
          end: [],
          index: [],
          label: [],
          name: markerLineName,
          bucket: bucketName,
          length: 0,
        };
        markerTimingsByName.push(markerTiming);
      }

      // Since the markers are sorted, look at the last added marker in this row. If
      // the new marker fits, go ahead and insert it.
      const otherEnd = markerTiming.end[markerTiming.length - 1];
      if (otherEnd === undefined || otherEnd <= marker.start) {
        markerTiming.start.push(marker.start);
        markerTiming.end.push(
          // If this is an instant marker, the start time and end time will match.
          // The chart will then be responsible for drawing this as a dot.
          marker.end === null ? marker.start : marker.end
        );
        markerTiming.label.push(getLabel(markerIndex));
        markerTiming.index.push(markerIndex);
        markerTiming.length++;
        break;
      }
    }
  }

  // Flatten out the map into a single array.
  const allMarkerTimings = [].concat(...markerTimingsMap.values());

  // Sort all the marker timings in place, first by the bucket, then by their names.
  allMarkerTimings.sort((a, b) => {
    if (a.bucket !== b.bucket) {
      // Sort by buckets first.
      // Show the 'Test' category first. Test markers are almost guaranteed to
      // be the most relevant when they exist.
      if (a.bucket === 'Test') {
        return -1;
      }
      if (b.bucket === 'Test') {
        return 1;
      }
      // Put the 'Other' category last.
      if (a.bucket === 'Other') {
        return 1;
      }
      if (b.bucket === 'Other') {
        return -1;
      }
      // Sort alphabetically for the remaining categories.
      return a.bucket > b.bucket ? 1 : -1;
    }
    if (a.name === b.name) {
      // Keep the original ordering if the names are the same.
      return 0;
    }

    // Put network requests at the end of the Network category.
    if (a.bucket === 'Network') {
      if (a.name === 'Network Requests') {
        return 1;
      }
      if (b.name === 'Network Requests') {
        return -1;
      }
    }

    // Sort by names second
    return a.name > b.name ? 1 : -1;
  });

  return allMarkerTimings;
}

/**
 * This function builds on `getMarkerTiming` above, inserting the bucket names
 * that are plain strings as items in the result array.
 *
 * Reusing the previous example of an array of 15 markers named either "A", "B",
 * or "C", this is the result we'd get:
 *
 *  [
 *    "DOM", // The bucket, inserted by this function after `getMarkerTiming`.
 *    {
 *      name: "A",
 *      start: [0, 23, 35, 65, 75],
 *      end: [1, 25, 37, 67, 77],
 *      index: [0, 2, 5, 6, 8],
 *      label: ["Aye", "Aye", "Aye", "Aye", "Aye"],
 *      bucket: "DOM",
 *    },
 *    {
 *      name: "B",
 *      start: [1, 28, 39, 69, 70],
 *      end: [2, 29, 49, 70, 77],
 *      index: [1, 3, 7, 9, 10],
 *      label: ["Bee", "Bee", "Bee", "Bee", "Bee"],
 *      bucket: "DOM",
 *    },
 *    "Other", // The bucket.
 *    {
 *      name: "C",
 *      start: [10, 33, 45, 75, 85],
 *      end: [11, 35, 47, 77, 87],
 *      index: [4, 11, 12, 13, 14],
 *      label: ["Sea", "Sea", "Sea", "Sea", "Sea"],
 *      bucket: "Other",
 *    },
 *  ]
 */
export function getMarkerTimingAndBuckets(
  getMarker: (MarkerIndex) => Marker,
  markerIndexes: MarkerIndex[],
  // Categories can be null for things like Network Markers, where we don't care to
  // break things up by category.
  getLabel: (MarkerIndex) => string,
  categories: ?CategoryList
): MarkerTimingAndBuckets {
  const allMarkerTimings = getMarkerTiming(
    getMarker,
    markerIndexes,
    getLabel,
    categories
  );

  // Interleave the bucket names in between the marker timings.
  const markerTimingsAndBuckets: MarkerTimingAndBuckets = [];
  let prevBucket;
  for (const markerTiming of allMarkerTimings) {
    if (markerTiming.bucket !== prevBucket) {
      markerTimingsAndBuckets.push(markerTiming.bucket);
      prevBucket = markerTiming.bucket;
    }
    markerTimingsAndBuckets.push(markerTiming);
  }

  return markerTimingsAndBuckets;
}
<|endoftext|>


<commit_before>'use strict';

import coverImageTransformer from './neoCoverImageTransform';
import openSearchWorkTransformer from './neoOpenSearchWorkTransformer';
import searchTransformer from './opensearchSearch';
import {requestType, makeTypeID} from '../requestTypeIdentifier';
import _ from 'lodash';

let filePath = __dirname + '/../../doc/work-context.jsonld';
let typeId = makeTypeID(filePath);

let requestMethod = {
  MOREINFO: 'moreinfo',
  SEARCH: 'search',
  GETOBJECT: 'getobject'
};

function isGetObject(field) {
  return (typeId.isType(field, requestType.DKABM)
  || typeId.isType(field, requestType.BRIEFDISPLAY)
  || typeId.isType(field, requestType.RELATIONS));
}

export function workRequest(request, context) { // eslint-disable-line no-unused-vars

  let state = {};
  if (_.has(request, 'pids') && request.pids.length > 0) {
    state.pid = request.pids[0]; // saves pid for use in responder.
    state.pids = request.pids;
  }
  let transformedRequests = {};
  if (_.has(request, 'fields')) {
    // Only call clients which can contribute the given fields.
    let fields = request.fields;
    // Determine which OpenSearch-method to use:
    // If the collection-field is given, then the OpenSearch request should
    // be given to the search method. Else it should be given to the getObject method.
    if (fields.some(field => typeId.isType(field, requestType.COLLECTION))) {
      // A collection is found.
      // Restructure this request as a Search request for retrieving collection only!
      transformedRequests[requestMethod.SEARCH] = [];
      for (let i=0; i<request.pids.length; i++) {
        transformedRequests[requestMethod.SEARCH].push({
          q: 'rec.id=' + request.pids[i],
          fields: ['collection'],
          offset: 0,
          limit: 1
        });
      }
    }
    if (fields.some(field => isGetObject(field))) {
      // send this as a getObjectRequest
      transformedRequests[requestMethod.GETOBJECT] = request;
    }

    if (fields.some(field => typeId.isType(field, requestType.MOREINFO))) {
      // send this to the coverurl transformer.
      transformedRequests[requestMethod.MOREINFO] = {pids: request.pids};
    }
  } else { // eslint-disable-line brace-style
    // Default:
    // Return dkabm, briefdisplay and relations from getObject
    // This should be default behaviour for getObject with no fields!
    transformedRequests.getobject = request;
  }
  return {transformedRequest: transformedRequests, state: state};
}

export function workResponse(response, context, state) { // eslint-disable-line no-unused-vars
  let envelope = {
    statusCode: 200
    // data: []
  };
  // loop over at most three promises (getObject, search and moreInfo):
  for (let i = 0; i < response.length; i++) {
    let resp = response[i];
    // evaluate the statusCode for getObject and moreInfo
    if (resp.statusCode && resp.statusCode !== 200) {
      // Setting error envelope if found!
      envelope = resp;
      break;
    }
    // evaluate statusCode for list of responses from search
    if (resp.length) {
      let error = false;
      for (let x=0; x<resp.length; x++) {
        if (resp[x].statusCode !== 200) {
          envelope = resp[i];
          error = true;
          break;
        }
      }
      if (error) {
        break;
      }
    }
    // Set number of data elements if not done in previous iteration
    if (!envelope.data) {
      // Damn, this feel hacked.
      // But if you just do 'let x = Array(3).fill({})', you will get an array with the same
      // object three times. So if you add to x[0], you will also add to x[1] and x[2].
      // Not exactly what I expected.
      if (resp.data) {
        envelope.data = Array(resp.data.length).fill(1).map(x => { // eslint-disable-line no-unused-vars
          return {};
        });
      }

    }

    // Where am I?????
    // I need to collect the data from the different services.
    // I have an assumption, that there will be equally many results in each.
    // Search is currently not implemented to return more than one result.
    //   - I'll have to make a loop with X promises.
    switch (state.services[i]) {
      case requestMethod.MOREINFO:
        // TODO: Check that pids corresponds.
        for (let x = 0; x < resp.data.length; x++) {
          let respData = resp.data[x];
          if (respData.pid) {
            delete (respData.pid); // remove the pid.
          }
          _.extend(envelope.data[x], respData);
        }
        break;
      case requestMethod.GETOBJECT:
        for (let x = 0; x<resp.data.length; x++) {
          _.extend(envelope.data[x], resp.data[x]);
        }
        break;
      case requestMethod.SEARCH:
        for (let x = 0; x<resp.length; x++) {
          let coll = {
            collection: resp[x].data[0].collection
          };
          _.extend(envelope.data[x], coll);
        }
        break;
      default:
      // We should never get here.
    }
  }

  return envelope;
}

export default (request, context) => {
  let {transformedRequest: params, state: state} = workRequest(request, context);

  let services = []; // state-data for knowing which servies is called and in which order.
  let promises = [];
  if (_.has(params, requestMethod.MOREINFO)) {
    // query moreinfo through its transformer.
    let moreInfoPromise = coverImageTransformer(params.moreinfo, context);
    promises.push(moreInfoPromise);
    services.push(requestMethod.MOREINFO);
  }
  if (_.has(params, requestMethod.GETOBJECT)) {
    // query opensearch through getObject method
    let getObjectPromise = openSearchWorkTransformer(params.getobject, context);
    promises.push(getObjectPromise);
    services.push(requestMethod.GETOBJECT);
  }
  if (_.has(params, requestMethod.SEARCH)) {
    // query opensearch through search method
    let searchPromises = [];
    for (let i = 0; i < params.search.length; i++) {
      let prom = searchTransformer(params.search[i], context);
      searchPromises.push(prom);
    }
    promises.push(Promise.all(searchPromises));
    services.push(requestMethod.SEARCH);
  }
  state.services = services;

  return Promise.all(promises).then(body => {
    return workResponse(body, context, state);
  });
};
<commit_msg>Added use of collectionDetails to work-endpoint (#227)<commit_after>'use strict';

import coverImageTransformer from './neoCoverImageTransform';
import openSearchWorkTransformer from './neoOpenSearchWorkTransformer';
import searchTransformer from './opensearchSearch';
import {requestType, makeTypeID} from '../requestTypeIdentifier';
import _ from 'lodash';

let filePath = __dirname + '/../../doc/work-context.jsonld';
let typeId = makeTypeID(filePath);

let requestMethod = {
  MOREINFO: 'moreinfo',
  SEARCH: 'search',
  GETOBJECT: 'getobject'
};

function isGetObject(field) {
  return (typeId.isType(field, requestType.DKABM)
  || typeId.isType(field, requestType.BRIEFDISPLAY)
  || typeId.isType(field, requestType.RELATIONS));
}

export function workRequest(request, context) { // eslint-disable-line no-unused-vars

  let state = {};
  if (_.has(request, 'pids') && request.pids.length > 0) {
    state.pid = request.pids[0]; // saves pid for use in responder.
    state.pids = request.pids;
  }
  let transformedRequests = {};
  if (_.has(request, 'fields')) {
    // Only call clients which can contribute the given fields.
    let fields = request.fields;
    // Determine which OpenSearch-method to use:
    // If the collection-field is given, then the OpenSearch request should
    // be given to the search method. Else it should be given to the getObject method.
    if (fields.some(field => typeId.isType(field, requestType.COLLECTION))) {
      // A collection is found.
      // Restructure this request as a Search request for retrieving collection only!
      transformedRequests[requestMethod.SEARCH] = [];
      for (let i=0; i<request.pids.length; i++) {
        let searchRequest = {
          q: 'rec.id=' + request.pids[i],
          fields: ['collection'],
          offset: 0,
          limit: 1
        };
        if (_.contains(fields, 'collectionDetails')) {
          searchRequest.fields.push('collectionDetails');
        }
        transformedRequests[requestMethod.SEARCH].push(searchRequest);
      }
    }
    if (fields.some(field => isGetObject(field))) {
      // send this as a getObjectRequest
      transformedRequests[requestMethod.GETOBJECT] = request;
    }

    if (fields.some(field => typeId.isType(field, requestType.MOREINFO))) {
      // send this to the coverurl transformer.
      transformedRequests[requestMethod.MOREINFO] = {pids: request.pids};
    }
  } else { // eslint-disable-line brace-style
    // Default:
    // Return dkabm, briefdisplay and relations from getObject
    // This should be default behaviour for getObject with no fields!
    transformedRequests.getobject = request;
  }
  return {transformedRequest: transformedRequests, state: state};
}

export function workResponse(response, context, state) { // eslint-disable-line no-unused-vars
  let envelope = {
    statusCode: 200
    // data: []
  };
  // loop over at most three promises (getObject, search and moreInfo):
  for (let i = 0; i < response.length; i++) {
    let resp = response[i];
    // evaluate the statusCode for getObject and moreInfo
    if (resp.statusCode && resp.statusCode !== 200) {
      // Setting error envelope if found!
      envelope = resp;
      break;
    }
    // evaluate statusCode for list of responses from search
    if (resp.length) {
      let error = false;
      for (let x=0; x<resp.length; x++) {
        if (resp[x].statusCode !== 200) {
          envelope = resp[i];
          error = true;
          break;
        }
      }
      if (error) {
        break;
      }
    }
    // Set number of data elements if not done in previous iteration
    if (!envelope.data) {
      // Damn, this feel hacked.
      // But if you just do 'let x = Array(3).fill({})', you will get an array with the same
      // object three times. So if you add to x[0], you will also add to x[1] and x[2].
      // Not exactly what I expected.
      if (resp.data) {
        envelope.data = Array(resp.data.length).fill(1).map(x => { // eslint-disable-line no-unused-vars
          return {};
        });
      }

    }

    // Where am I?????
    // I need to collect the data from the different services.
    // I have an assumption, that there will be equally many results in each.
    // Search is currently not implemented to return more than one result.
    //   - I'll have to make a loop with X promises.
    switch (state.services[i]) {
      case requestMethod.MOREINFO:
        // TODO: Check that pids corresponds.
        for (let x = 0; x < resp.data.length; x++) {
          let respData = resp.data[x];
          if (respData.pid) {
            delete (respData.pid); // remove the pid.
          }
          _.extend(envelope.data[x], respData);
        }
        break;
      case requestMethod.GETOBJECT:
        for (let x = 0; x<resp.data.length; x++) {
          _.extend(envelope.data[x], resp.data[x]);
        }
        break;
      case requestMethod.SEARCH:
        for (let x = 0; x<resp.length; x++) {
          let coll = {
            collection: resp[x].data[0].collection
          };
          if (resp[x].data[0].collectionDetails) {
            coll.collectionDetails = resp[x].data[0].collectionDetails;
          }
          _.extend(envelope.data[x], coll);
        }
        break;
      default:
      // We should never get here.
    }
  }

  return envelope;
}

export default (request, context) => {
  let {transformedRequest: params, state: state} = workRequest(request, context);

  let services = []; // state-data for knowing which servies is called and in which order.
  let promises = [];
  if (_.has(params, requestMethod.MOREINFO)) {
    // query moreinfo through its transformer.
    let moreInfoPromise = coverImageTransformer(params.moreinfo, context);
    promises.push(moreInfoPromise);
    services.push(requestMethod.MOREINFO);
  }
  if (_.has(params, requestMethod.GETOBJECT)) {
    // query opensearch through getObject method
    let getObjectPromise = openSearchWorkTransformer(params.getobject, context);
    promises.push(getObjectPromise);
    services.push(requestMethod.GETOBJECT);
  }
  if (_.has(params, requestMethod.SEARCH)) {
    // query opensearch through search method
    let searchPromises = [];
    for (let i = 0; i < params.search.length; i++) {
      let prom = searchTransformer(params.search[i], context);
      searchPromises.push(prom);
    }
    promises.push(Promise.all(searchPromises));
    services.push(requestMethod.SEARCH);
  }
  state.services = services;

  return Promise.all(promises).then(body => {
    return workResponse(body, context, state);
  });
};
<|endoftext|>


<commit_before>define(['knockout'], function (ko) {

    var data;
    var playlist;
    var mediaUrl = ko.observable();
    var mediaImage;
    var isplaying = false;
    var duration = ko.observable(0);
    var position = ko.observable(0);

    var STATE_INIT = 0;
    var STATE_GETDURATION = 1;
    var STATE_DURATIONOK = 2;
    var STATE_READY = 3;
    var state = STATE_INIT;

    var onTimeCallback;

    function createPlaylist() {

        playlist = [];

        if (data == null) {
            playlist.push(
                       {
                           file: "rtmp://ec2-79-125-71-117.eu-west-1.compute.amazonaws.com/vods3/mp4:amazons3/chaosdata/LARM/LARMFM_demo/logical_h264_5min.mp4",
                           fileduration: 4123,
                           start: 0,
                           end: 4123
                       });
            return;
        }

        // Parse FileInfos
        // <Larm.FileInfos><Larm.FileInfo><StartOffSetMS>3840000</StartOffSetMS><EndOffSetMS>0</EndOffSetMS><FileName>P2_1800_2000_890121_001.mp3</FileName><Index>0</Index></Larm.FileInfo><Larm.FileInfo><StartOffSetMS>0</StartOffSetMS><EndOffSetMS>1320000</EndOffSetMS><FileName>P2_2000_2200_890121_001.mp3</FileName><Index>1</Index></Larm.FileInfo></Larm.FileInfos>
        var fileinfo = [];
        for (var i = 0; i < data.Metadatas.length; i++) {
            var md = data.Metadatas[i];
            if (md.MetadataSchemaGuid == Settings.Object.FileInfosSchemaGuid) {
                var xml = md.MetadataXml;
                xml = xml.replace(/\./g, '_');
                var x2js = new X2JS({ arrayAccessFormPaths: ['Larm_FileInfos.Larm_FileInfo'] });
                var json = x2js.xml_str2json(xml);
                for (var j = 0; j < json.Larm_FileInfos.Larm_FileInfo.length; j++) {
                    var info = json.Larm_FileInfos.Larm_FileInfo[j];
                    var index = parseInt(info.Index, 10);
                    var start = parseInt(info.StartOffSetMS, 10) / 1000;    // Should be in seconds for jwplayer.
                    var end = parseInt(info.EndOffSetMS, 10) / 1000;
                    fileinfo[index] = { start: start, end: end };
                }
                break;
            }
        }

        // Parse Files
        if (data.Files.length > 0) {
            for (var i = 0; i < data.Files.length; i++) {
                var ft = data.Files[i].FormatType;
                if (ft == "Audio") {
                    var fi = fileinfo[i];
                    if (fi === undefined)
                        return;
                    playlist.push(
                        {
                            file: data.Files[i].URL,
                            fileduration: 0,
                            start: fi.start,
                            end: fi.end
                        }
                        );
                }
                else if (ft == "Image") {
                    mediaImage = data.Files[i].URL;
                }

            }
        }
    }

    function setupPlayer() {

        createPlaylist();

        mediaUrlsIdx = 0;

        state = STATE_GETDURATION;

        var mu = "";
        for (var i = 0; i < playlist.length; i++) {
            if(i > 0)
                mu += "
" + playlist[i].file;
            else
                mu += playlist[i].file;
        }
        mediaUrl(mu);

        var w = 1;
        var h = 1;
        var hascontrols = true;

        if (data == null){
            mediaImage = "";
            w = 533;
            h = 300;
            hascontrols = false;
        }

        jwplayer("larmplayer").setup({
            playlist: playlist,
            width: w,
            height: h,
            image: mediaImage,
            controls: hascontrols
        });

        jwplayer().onTime(onTime);
        jwplayer().onPlay(onPlay);
        jwplayer().onPause(onPause);
        jwplayer().play(true); // Play
    }

    function onPlay() {

    }

    function onPause() {
    }

    function onTime(e) {
        // e.duration, e.position
        if (state == STATE_READY) {
            var s = jwplayer().getState();
            if (!isplaying && s == "PLAYING")
                jwplayer().play(false);

            var idx = jwplayer().getPlaylistIndex();
            if (e.position < playlist[idx].start) {
                jwplayer().seek(playlist[idx].start);
            }
            else if (e.position > playlist[idx].end) {

                if (idx + 1 == playlist.length)
                    isplaying = false;
                else
                    jwplayer().playlistItem(idx + 1);
            }
            else {
                // Calculate position
                var pos = e.position - playlist[idx].start;
                if (idx == 1)
                    pos += playlist[0].end - playlist[0].start;
                position(pos);
            }
        }
        else if (state == STATE_DURATIONOK) {
            jwplayer().play(isplaying);
            state = STATE_READY;
        }
        else if (state == STATE_GETDURATION) {
            var idx = jwplayer().getPlaylistIndex();
            var item = playlist[idx];
            if(item.fileduration == 0)
                item.fileduration = e.duration;

            // Duration missing?
            var dur = 0;
            for (var i = 0; i < playlist.length; i++) {
                if (playlist[i].fileduration == 0) {
                    jwplayer().playlistItem(i);
                    return;
                }

                if (playlist[i].end == 0)
                    playlist[i].end = playlist[i].fileduration;

                dur += playlist[i].end - playlist[i].start;
            }

            if (idx != 0) {
                jwplayer().playlistItem(0);
            }
            duration(dur);
            state = STATE_DURATIONOK;
        }
    }

    // Returns program time in seconds.
    function getProgramTimeFromFileTime(fileTimeInSeconds) {
        var filetime = fileTimeInSeconds;
        var fileduracc = 0; // filedurationaccumulated
        var progtmacc = 0; // program time accumulated
        for (var i = 0; i < playlist.length; i++) {
            var pl = playlist[i];
            if (filetime <= fileduracc + pl.fileduration) {
                var localfiletime = filetime - fileduracc;
                var reltime = Math.max(0, localfiletime - pl.start);
                return progtmacc + reltime;
            }
            fileduracc += pl.fileduration;
            progtmacc += pl.end - pl.start;
        }
        return 0;
    }

    // Returns file time in seconds
    function getFileTimeFromProgramTime(programTimeInSeconds) {
        var pt = programTimeInSeconds;
        var ptacc = 0;
        var ftacc = 0;
        for (var i = 0; i < playlist.length; i++) {
            var pl = playlist[i];
            var programduration = pl.end - pl.start;
            if (pt > programduration + ptacc) {
                ptacc += programduration;
                ftacc += pl.fileduration;
            }
            else {
                return (pt - ptacc) + ftacc + pl.start;
            }
        }

        return 0;
    }

    /*
    public double GetGlobalFilePosition(double visualposition)
		{
			visualposition = System.Math.Max(0, visualposition);
			visualposition = System.Math.Min(_uiwidth, visualposition);

			double ratio = visualposition / _uiwidth;

			double visualdurationacc = 0;
			foreach (var f in _program.Files)
				visualdurationacc += (f.OffsetEndMaximum - f.OffsetBeginMinimum);

			double relativepos = visualdurationacc * ratio;
			double fileduration = 0;
			double filedurationacc = 0;
			double visualduration = 0;
			visualdurationacc = 0;
			foreach (var f in _program.Files)
			{
				fileduration = f.Duration;
				visualduration = f.OffsetEndMaximum - f.OffsetBeginMinimum;
				if (relativepos <= (visualdurationacc + visualduration))
				{
					return filedurationacc + (relativepos - visualdurationacc) + f.OffsetBeginMinimum;
				}
				visualdurationacc += visualduration;
				filedurationacc += fileduration;
			}

			return 0;
		}
    */

    function isReady() {
        return duration() !== 0;
    }

    return {
        duration: duration,
        position: position,
        mediaUrl: mediaUrl,
        init: function (objectdata) {
            data = objectdata;

            if (data == null) {
                setupPlayer();
                return;
            }

            if (data == undefined)
                return;

            setupPlayer();
        },
        isReady: isReady,
        play: function () {
            isplaying = true;
            jwplayer().play(true);
        },
        pause: function () {
            isplaying = false;
            jwplayer().play(false);
        },
        getProgramTimeFromFileTime: getProgramTimeFromFileTime,
        getFileTimeFromProgramTime: getFileTimeFromProgramTime
    };
});
<commit_msg>Changed video url to new stream with full video<commit_after>define(['knockout'], function (ko) {

    var data;
    var playlist;
    var mediaUrl = ko.observable();
    var mediaImage;
    var isplaying = false;
    var duration = ko.observable(0);
    var position = ko.observable(0);

    var STATE_INIT = 0;
    var STATE_GETDURATION = 1;
    var STATE_DURATIONOK = 2;
    var STATE_READY = 3;
    var state = STATE_INIT;

    var onTimeCallback;

    function createPlaylist() {

        playlist = [];

        if (data == null) {
            playlist.push(
                       {
                           file: "rtmp://ec2-79-125-71-117.eu-west-1.compute.amazonaws.com/vods3/mp4:amazons3/chaosdata/LARM/LARMFM_demo/logical_h264_2.mp4",
                           fileduration: 4123,
                           start: 0,
                           end: 4123
                       });
            return;
        }

        // Parse FileInfos
        // <Larm.FileInfos><Larm.FileInfo><StartOffSetMS>3840000</StartOffSetMS><EndOffSetMS>0</EndOffSetMS><FileName>P2_1800_2000_890121_001.mp3</FileName><Index>0</Index></Larm.FileInfo><Larm.FileInfo><StartOffSetMS>0</StartOffSetMS><EndOffSetMS>1320000</EndOffSetMS><FileName>P2_2000_2200_890121_001.mp3</FileName><Index>1</Index></Larm.FileInfo></Larm.FileInfos>
        var fileinfo = [];
        for (var i = 0; i < data.Metadatas.length; i++) {
            var md = data.Metadatas[i];
            if (md.MetadataSchemaGuid == Settings.Object.FileInfosSchemaGuid) {
                var xml = md.MetadataXml;
                xml = xml.replace(/\./g, '_');
                var x2js = new X2JS({ arrayAccessFormPaths: ['Larm_FileInfos.Larm_FileInfo'] });
                var json = x2js.xml_str2json(xml);
                for (var j = 0; j < json.Larm_FileInfos.Larm_FileInfo.length; j++) {
                    var info = json.Larm_FileInfos.Larm_FileInfo[j];
                    var index = parseInt(info.Index, 10);
                    var start = parseInt(info.StartOffSetMS, 10) / 1000;    // Should be in seconds for jwplayer.
                    var end = parseInt(info.EndOffSetMS, 10) / 1000;
                    fileinfo[index] = { start: start, end: end };
                }
                break;
            }
        }

        // Parse Files
        if (data.Files.length > 0) {
            for (var i = 0; i < data.Files.length; i++) {
                var ft = data.Files[i].FormatType;
                if (ft == "Audio") {
                    var fi = fileinfo[i];
                    if (fi === undefined)
                        return;
                    playlist.push(
                        {
                            file: data.Files[i].URL,
                            fileduration: 0,
                            start: fi.start,
                            end: fi.end
                        }
                        );
                }
                else if (ft == "Image") {
                    mediaImage = data.Files[i].URL;
                }

            }
        }
    }

    function setupPlayer() {

        createPlaylist();

        mediaUrlsIdx = 0;

        state = STATE_GETDURATION;

        var mu = "";
        for (var i = 0; i < playlist.length; i++) {
            if(i > 0)
                mu += "
" + playlist[i].file;
            else
                mu += playlist[i].file;
        }
        mediaUrl(mu);

        var w = 1;
        var h = 1;
        var hascontrols = true;

        if (data == null){
            mediaImage = "";
            w = 533;
            h = 300;
            hascontrols = false;
        }

        jwplayer("larmplayer").setup({
            playlist: playlist,
            width: w,
            height: h,
            image: mediaImage,
            controls: hascontrols
        });

        jwplayer().onTime(onTime);
        jwplayer().onPlay(onPlay);
        jwplayer().onPause(onPause);
        jwplayer().play(true); // Play
    }

    function onPlay() {

    }

    function onPause() {
    }

    function onTime(e) {
        // e.duration, e.position
        if (state == STATE_READY) {
            var s = jwplayer().getState();
            if (!isplaying && s == "PLAYING")
                jwplayer().play(false);

            var idx = jwplayer().getPlaylistIndex();
            if (e.position < playlist[idx].start) {
                jwplayer().seek(playlist[idx].start);
            }
            else if (e.position > playlist[idx].end) {

                if (idx + 1 == playlist.length)
                    isplaying = false;
                else
                    jwplayer().playlistItem(idx + 1);
            }
            else {
                // Calculate position
                var pos = e.position - playlist[idx].start;
                if (idx == 1)
                    pos += playlist[0].end - playlist[0].start;
                position(pos);
            }
        }
        else if (state == STATE_DURATIONOK) {
            jwplayer().play(isplaying);
            state = STATE_READY;
        }
        else if (state == STATE_GETDURATION) {
            var idx = jwplayer().getPlaylistIndex();
            var item = playlist[idx];
            if(item.fileduration == 0)
                item.fileduration = e.duration;

            // Duration missing?
            var dur = 0;
            for (var i = 0; i < playlist.length; i++) {
                if (playlist[i].fileduration == 0) {
                    jwplayer().playlistItem(i);
                    return;
                }

                if (playlist[i].end == 0)
                    playlist[i].end = playlist[i].fileduration;

                dur += playlist[i].end - playlist[i].start;
            }

            if (idx != 0) {
                jwplayer().playlistItem(0);
            }
            duration(dur);
            state = STATE_DURATIONOK;
        }
    }

    // Returns program time in seconds.
    function getProgramTimeFromFileTime(fileTimeInSeconds) {
        var filetime = fileTimeInSeconds;
        var fileduracc = 0; // filedurationaccumulated
        var progtmacc = 0; // program time accumulated
        for (var i = 0; i < playlist.length; i++) {
            var pl = playlist[i];
            if (filetime <= fileduracc + pl.fileduration) {
                var localfiletime = filetime - fileduracc;
                var reltime = Math.max(0, localfiletime - pl.start);
                return progtmacc + reltime;
            }
            fileduracc += pl.fileduration;
            progtmacc += pl.end - pl.start;
        }
        return 0;
    }

    // Returns file time in seconds
    function getFileTimeFromProgramTime(programTimeInSeconds) {
        var pt = programTimeInSeconds;
        var ptacc = 0;
        var ftacc = 0;
        for (var i = 0; i < playlist.length; i++) {
            var pl = playlist[i];
            var programduration = pl.end - pl.start;
            if (pt > programduration + ptacc) {
                ptacc += programduration;
                ftacc += pl.fileduration;
            }
            else {
                return (pt - ptacc) + ftacc + pl.start;
            }
        }

        return 0;
    }

    /*
    public double GetGlobalFilePosition(double visualposition)
		{
			visualposition = System.Math.Max(0, visualposition);
			visualposition = System.Math.Min(_uiwidth, visualposition);

			double ratio = visualposition / _uiwidth;

			double visualdurationacc = 0;
			foreach (var f in _program.Files)
				visualdurationacc += (f.OffsetEndMaximum - f.OffsetBeginMinimum);

			double relativepos = visualdurationacc * ratio;
			double fileduration = 0;
			double filedurationacc = 0;
			double visualduration = 0;
			visualdurationacc = 0;
			foreach (var f in _program.Files)
			{
				fileduration = f.Duration;
				visualduration = f.OffsetEndMaximum - f.OffsetBeginMinimum;
				if (relativepos <= (visualdurationacc + visualduration))
				{
					return filedurationacc + (relativepos - visualdurationacc) + f.OffsetBeginMinimum;
				}
				visualdurationacc += visualduration;
				filedurationacc += fileduration;
			}

			return 0;
		}
    */

    function isReady() {
        return duration() !== 0;
    }

    return {
        duration: duration,
        position: position,
        mediaUrl: mediaUrl,
        init: function (objectdata) {
            data = objectdata;

            if (data == null) {
                setupPlayer();
                return;
            }

            if (data == undefined)
                return;

            setupPlayer();
        },
        isReady: isReady,
        play: function () {
            isplaying = true;
            jwplayer().play(true);
        },
        pause: function () {
            isplaying = false;
            jwplayer().play(false);
        },
        getProgramTimeFromFileTime: getProgramTimeFromFileTime,
        getFileTimeFromProgramTime: getFileTimeFromProgramTime
    };
});
<|endoftext|>


<commit_before>/* @flow strict-local */
import type { Reducer, Store, Dispatch } from 'redux';

import { REHYDRATE } from '../actionConstants';
import type { Action, GlobalState as State } from '../types';
import * as logging from '../utils/logging';

const processKey = key => {
  const int = parseInt(key, 10);
  if (Number.isNaN(int)) {
    throw new Error('redux-persist-migrate: migrations must be keyed with integer values');
  }
  return int;
};

type InnerStoreCreator<S, A, D> = (Reducer<S, A>, S | void) => Store<S, A, D>;
type StoreEnhancer<S, A, D> = (InnerStoreCreator<S, A, D>) => InnerStoreCreator<S, A, D>;

/* eslint-disable no-use-before-define */

export default function createMigration(
  manifest: { [string]: (State) => State },
  versionSelector: string | (State => number | string | void),
  versionSetter?: (State, number) => State,
): StoreEnhancer<State, Action, Dispatch<Action>> {
  if (typeof versionSelector === 'string') {
    const reducerKey = versionSelector;
    const realVersionSelector = state => state && state[reducerKey] && state[reducerKey].version;
    const realVersionSetter = (state, version) => {
      if (['undefined', 'object'].indexOf(typeof state[reducerKey]) === -1) {
        logging.error(
          'redux-persist-migrate: state for versionSetter key must be an object or undefined',
        );
        return state;
      }
      state[reducerKey] = state[reducerKey] || {};
      state[reducerKey].version = version;
      return state;
    };
    return createMigrationImpl(manifest, realVersionSelector, realVersionSetter);
  }

  if (versionSetter === undefined) {
    throw new Error('createMigration: bad arguments');
  }

  return createMigrationImpl(manifest, versionSelector, versionSetter);
}

export function createMigrationImpl(
  manifest: { [string]: (State) => State },
  versionSelector: State => number | string | void,
  versionSetter: (State, number) => State,
): StoreEnhancer<State, Action, Dispatch<Action>> {
  const versionKeys = Object.keys(manifest)
    .map(processKey)
    .sort((a, b) => a - b);
  let currentVersion = versionKeys[versionKeys.length - 1];
  if (!currentVersion && currentVersion !== 0) {
    currentVersion = -1;
  }

  const migrate = (state, version) => {
    let newState = state;
    versionKeys
      .filter(v => v > version || version === null)
      .forEach(v => {
        newState = manifest[v.toString()](newState);
      });

    newState = versionSetter(newState, currentVersion);
    return newState;
  };

  const migrationDispatch = next => (action: Action) => {
    if (versionSetter === undefined || typeof versionSelector === 'string') {
      throw new Error('createMigration: bad arguments');
    }
    if (action.type === REHYDRATE) {
      // $FlowMigrationFudge this really is a lie -- and kind of central to migration
      const incomingState: State = action.payload;
      const incomingVersion = parseInt(versionSelector(incomingState), 10);
      if (Number.isNaN(incomingVersion)) {
        // first launch after install, so incoming state is empty object
        // migration not required, just update version
        const payload = versionSetter(incomingState, currentVersion);
        return next({ ...action, type: REHYDRATE, payload });
      }

      if (incomingVersion !== currentVersion) {
        const migratedState = migrate(incomingState, incomingVersion);
        action.payload = migratedState;
      }
    }
    return next(action);
  };

  return next => (reducer, initialState) => {
    const store = next(reducer, initialState);
    return {
      ...store,
      dispatch: migrationDispatch(store.dispatch),
    };
  };
}
<commit_msg>migration: add context when logging migration error<commit_after>/* @flow strict-local */
import type { Reducer, Store, Dispatch } from 'redux';

import { REHYDRATE } from '../actionConstants';
import type { Action, GlobalState as State } from '../types';
import * as logging from '../utils/logging';

const processKey = key => {
  const int = parseInt(key, 10);
  if (Number.isNaN(int)) {
    throw new Error('redux-persist-migrate: migrations must be keyed with integer values');
  }
  return int;
};

type InnerStoreCreator<S, A, D> = (Reducer<S, A>, S | void) => Store<S, A, D>;
type StoreEnhancer<S, A, D> = (InnerStoreCreator<S, A, D>) => InnerStoreCreator<S, A, D>;

/* eslint-disable no-use-before-define */

export default function createMigration(
  manifest: { [string]: (State) => State },
  versionSelector: string | (State => number | string | void),
  versionSetter?: (State, number) => State,
): StoreEnhancer<State, Action, Dispatch<Action>> {
  if (typeof versionSelector === 'string') {
    const reducerKey = versionSelector;
    const realVersionSelector = state => state && state[reducerKey] && state[reducerKey].version;
    const realVersionSetter = (state, version) => {
      if (['undefined', 'object'].indexOf(typeof state[reducerKey]) === -1) {
        logging.error(
          'redux-persist-migrate: state for versionSetter key must be an object or undefined',
          { version, reducerKey, 'actual-state': state[reducerKey] },
        );
        return state;
      }
      state[reducerKey] = state[reducerKey] || {};
      state[reducerKey].version = version;
      return state;
    };
    return createMigrationImpl(manifest, realVersionSelector, realVersionSetter);
  }

  if (versionSetter === undefined) {
    throw new Error('createMigration: bad arguments');
  }

  return createMigrationImpl(manifest, versionSelector, versionSetter);
}

export function createMigrationImpl(
  manifest: { [string]: (State) => State },
  versionSelector: State => number | string | void,
  versionSetter: (State, number) => State,
): StoreEnhancer<State, Action, Dispatch<Action>> {
  const versionKeys = Object.keys(manifest)
    .map(processKey)
    .sort((a, b) => a - b);
  let currentVersion = versionKeys[versionKeys.length - 1];
  if (!currentVersion && currentVersion !== 0) {
    currentVersion = -1;
  }

  const migrate = (state, version) => {
    let newState = state;
    versionKeys
      .filter(v => v > version || version === null)
      .forEach(v => {
        newState = manifest[v.toString()](newState);
      });

    newState = versionSetter(newState, currentVersion);
    return newState;
  };

  const migrationDispatch = next => (action: Action) => {
    if (versionSetter === undefined || typeof versionSelector === 'string') {
      throw new Error('createMigration: bad arguments');
    }
    if (action.type === REHYDRATE) {
      // $FlowMigrationFudge this really is a lie -- and kind of central to migration
      const incomingState: State = action.payload;
      const incomingVersion = parseInt(versionSelector(incomingState), 10);
      if (Number.isNaN(incomingVersion)) {
        // first launch after install, so incoming state is empty object
        // migration not required, just update version
        const payload = versionSetter(incomingState, currentVersion);
        return next({ ...action, type: REHYDRATE, payload });
      }

      if (incomingVersion !== currentVersion) {
        const migratedState = migrate(incomingState, incomingVersion);
        action.payload = migratedState;
      }
    }
    return next(action);
  };

  return next => (reducer, initialState) => {
    const store = next(reducer, initialState);
    return {
      ...store,
      dispatch: migrationDispatch(store.dispatch),
    };
  };
}
<|endoftext|>


<commit_before>var DOM = require('../../util/dom'),
    Handler = require('../Handler'),
    marks = require('./marks');

function CanvasHandler() {
  Handler.call(this);
  this._down = null;
  this._touch = null;
  this._first = true;
}

var base = Handler.prototype;
var prototype = (CanvasHandler.prototype = Object.create(base));
prototype.constructor = CanvasHandler;

prototype.initialize = function(el, pad, obj) {
  // add event listeners
  var canvas = this._canvas = DOM.find(el, 'canvas');
  if (canvas) {
    var that = this;
    this.events.forEach(function(type) {
      canvas.addEventListener(type, function(evt) {
        if (prototype[type]) {
          prototype[type].call(that, evt);
        } else {
          that.fire(type, evt);
        }
      });
    });
  }

  return base.initialize.call(this, el, pad, obj);
};

prototype.canvas = function() {
  return this._canvas;
};

// retrieve the current canvas context
prototype.context = function() {
  return this._canvas.getContext('2d');
};

// supported events
prototype.events = [
  'keydown',
  'keypress',
  'keyup',
  'dragenter',
  'dragleave',
  'dragover',
  'mousedown',
  'mouseup',
  'mousemove',
  'mouseout',
  'mouseover',
  'click',
  'dblclick',
  'wheel',
  'mousewheel',
  'touchstart',
  'touchmove',
  'touchend'
];

// to keep firefox happy
prototype.DOMMouseScroll = function(evt) {
  this.fire('mousewheel', evt);
};

function move(move, over, out) {
  return function(evt) {
    var a = this._active,
        p = this.pickEvent(evt);

    if (p === a) {
      // active item and picked item are the same
      this.fire(move, evt); // fire move
    } else {
      // active item and picked item are different
      this.fire(out, evt);  // fire out for prior active item
      this._active = p;            // set new active item
      this.fire(over, evt); // fire over for new active item
      this.fire(move, evt); // fire move for new active item
    }
  }
}

function inactive(type) {
  return function(evt) {
    this.fire(type, evt);
    this._active = null;
  }
}

prototype.mousemove = move('mousemove', 'mouseover', 'mouseout');
prototype.dragover  = move('dragover', 'dragenter', 'dragleave');

prototype.mouseout  = inactive('mouseout');
prototype.dragleave = inactive('dragleave');

prototype.mousedown = function(evt) {
  this._down = this._active;
  this.fire('mousedown', evt);
};

prototype.click = function(evt) {
  if (this._down === this._active) {
    this.fire('click', evt);
    this._down = null;
  }
};

prototype.touchstart = function(evt) {
  this._touch = this.pickEvent(evt.changedTouches[0]);

  if (this._first) {
    this._active = this._touch;
    this._first = false;
  }

  this.fire('touchstart', evt, true);
};

prototype.touchmove = function(evt) {
  this.fire('touchmove', evt, true);
};

prototype.touchend = function(evt) {
  this.fire('touchend', evt, true);
  this._touch = null;
};

// fire an event
prototype.fire = function(type, evt, touch) {
  var a = touch ? this._touch : this._active,
      h = this._handlers[type], i, len;
  if (h) {
    evt.vegaType = type;
    for (i=0, len=h.length; i<len; ++i) {
      h[i].handler.call(this._obj, evt, a);
    }
  }
};

// add an event handler
prototype.on = function(type, handler) {
  var name = this.eventName(type),
      h = this._handlers;
  (h[name] || (h[name] = [])).push({
    type: type,
    handler: handler
  });
  return this;
};

// remove an event handler
prototype.off = function(type, handler) {
  var name = this.eventName(type),
      h = this._handlers[name], i;
  if (!h) return;
  for (i=h.length; --i>=0;) {
    if (h[i].type !== type) continue;
    if (!handler || h[i].handler === handler) h.splice(i, 1);
  }
  return this;
};

prototype.pickEvent = function(evt) {
  var rect = this._canvas.getBoundingClientRect(),
      pad = this._padding, x, y;
  return this.pick(this._scene,
    x = (evt.clientX - rect.left),
    y = (evt.clientY - rect.top),
    x - pad.left, y - pad.top);
};

// find the scenegraph item at the current mouse position
// x, y -- the absolute x, y mouse coordinates on the canvas element
// gx, gy -- the relative coordinates within the current group
prototype.pick = function(scene, x, y, gx, gy) {
  var g = this.context(),
      mark = marks[scene.marktype];
  return mark.pick.call(this, g, scene, x, y, gx, gy);
};

module.exports = CanvasHandler;
<commit_msg>Fix linter errors.<commit_after>var DOM = require('../../util/dom'),
    Handler = require('../Handler'),
    marks = require('./marks');

function CanvasHandler() {
  Handler.call(this);
  this._down = null;
  this._touch = null;
  this._first = true;
}

var base = Handler.prototype;
var prototype = (CanvasHandler.prototype = Object.create(base));
prototype.constructor = CanvasHandler;

prototype.initialize = function(el, pad, obj) {
  // add event listeners
  var canvas = this._canvas = DOM.find(el, 'canvas');
  if (canvas) {
    var that = this;
    this.events.forEach(function(type) {
      canvas.addEventListener(type, function(evt) {
        if (prototype[type]) {
          prototype[type].call(that, evt);
        } else {
          that.fire(type, evt);
        }
      });
    });
  }

  return base.initialize.call(this, el, pad, obj);
};

prototype.canvas = function() {
  return this._canvas;
};

// retrieve the current canvas context
prototype.context = function() {
  return this._canvas.getContext('2d');
};

// supported events
prototype.events = [
  'keydown',
  'keypress',
  'keyup',
  'dragenter',
  'dragleave',
  'dragover',
  'mousedown',
  'mouseup',
  'mousemove',
  'mouseout',
  'mouseover',
  'click',
  'dblclick',
  'wheel',
  'mousewheel',
  'touchstart',
  'touchmove',
  'touchend'
];

// to keep firefox happy
prototype.DOMMouseScroll = function(evt) {
  this.fire('mousewheel', evt);
};

function move(moveEvent, overEvent, outEvent) {
  return function(evt) {
    var a = this._active,
        p = this.pickEvent(evt);

    if (p === a) {
      // active item and picked item are the same
      this.fire(moveEvent, evt); // fire move
    } else {
      // active item and picked item are different
      this.fire(outEvent, evt);  // fire out for prior active item
      this._active = p;            // set new active item
      this.fire(overEvent, evt); // fire over for new active item
      this.fire(moveEvent, evt); // fire move for new active item
    }
  };
}

function inactive(type) {
  return function(evt) {
    this.fire(type, evt);
    this._active = null;
  };
}

prototype.mousemove = move('mousemove', 'mouseover', 'mouseout');
prototype.dragover  = move('dragover', 'dragenter', 'dragleave');

prototype.mouseout  = inactive('mouseout');
prototype.dragleave = inactive('dragleave');

prototype.mousedown = function(evt) {
  this._down = this._active;
  this.fire('mousedown', evt);
};

prototype.click = function(evt) {
  if (this._down === this._active) {
    this.fire('click', evt);
    this._down = null;
  }
};

prototype.touchstart = function(evt) {
  this._touch = this.pickEvent(evt.changedTouches[0]);

  if (this._first) {
    this._active = this._touch;
    this._first = false;
  }

  this.fire('touchstart', evt, true);
};

prototype.touchmove = function(evt) {
  this.fire('touchmove', evt, true);
};

prototype.touchend = function(evt) {
  this.fire('touchend', evt, true);
  this._touch = null;
};

// fire an event
prototype.fire = function(type, evt, touch) {
  var a = touch ? this._touch : this._active,
      h = this._handlers[type], i, len;
  if (h) {
    evt.vegaType = type;
    for (i=0, len=h.length; i<len; ++i) {
      h[i].handler.call(this._obj, evt, a);
    }
  }
};

// add an event handler
prototype.on = function(type, handler) {
  var name = this.eventName(type),
      h = this._handlers;
  (h[name] || (h[name] = [])).push({
    type: type,
    handler: handler
  });
  return this;
};

// remove an event handler
prototype.off = function(type, handler) {
  var name = this.eventName(type),
      h = this._handlers[name], i;
  if (!h) return;
  for (i=h.length; --i>=0;) {
    if (h[i].type !== type) continue;
    if (!handler || h[i].handler === handler) h.splice(i, 1);
  }
  return this;
};

prototype.pickEvent = function(evt) {
  var rect = this._canvas.getBoundingClientRect(),
      pad = this._padding, x, y;
  return this.pick(this._scene,
    x = (evt.clientX - rect.left),
    y = (evt.clientY - rect.top),
    x - pad.left, y - pad.top);
};

// find the scenegraph item at the current mouse position
// x, y -- the absolute x, y mouse coordinates on the canvas element
// gx, gy -- the relative coordinates within the current group
prototype.pick = function(scene, x, y, gx, gy) {
  var g = this.context(),
      mark = marks[scene.marktype];
  return mark.pick.call(this, g, scene, x, y, gx, gy);
};

module.exports = CanvasHandler;
<|endoftext|>


<commit_before>'use strict';
const yeoman = require('yeoman-generator'),
  chalk = require('chalk'),
  yosay = require('yosay'),
  bakery = require('../lib/bakery'),
  feedback = require('../lib/feedback'),
  debug = require('debug')('bakery:generator:index'),
  mkdirp = require('mkdirp'),
  path = require('path'),
  _ = require('lodash');

const CM_SOURCE_DIRECTORY = 'Local Directory';
const CM_SOURCE_FORK = 'Fork From SCM';
const CM_SOURCE_GENERATE = 'Use Generator'
const CM_SOURCES = [ CM_SOURCE_DIRECTORY, CM_SOURCE_FORK, CM_SOURCE_GENERATE ];

var BakeryGenerator = yeoman.Base.extend({

  constructor: function() {
    yeoman.Base.apply(this, arguments);
    this._options.help.desc = 'Show this help';

    /** @property {object} answers - prompt answers */
    this.answers = {};
    this.existingProject = false;

    this.argument('projectname', {
      type: String,
      required: false
    });

    this.option('awsprofile', {
      type: String,
      alias: 'p',
      desc: 'Name of the AWS profile to use when calling the AWS api for value validation',
      defaults: 'default'
    });
  },

  initializing: function() {
    let default_config = {
      source: CM_SOURCE_DIRECTORY,
      projectname: this.projectname
    }
    this.config.defaults(default_config);

    var configFound = this.baseName !== undefined && this.applicationType !== undefined;
    if (configFound) {
      this.existingProject = true;
    }
  },

  prompting: function() {
    process.env.AWS_PROFILE = this.options.awsprofile;
    // Have Yeoman greet the user.
    this.log(yosay(
      'Welcome to the super-excellent ' + chalk.red('bakery') + ' generator!'
    ));

    let prompts = [{
      name: "projectname",
      type: "input",
      message: "Project name",
      when: function() { this.projectname == undefined },
      default: this.config.get('projectname'),
      required: true
    },
    {
      name: "source",
      type: "list",
      choices: CM_SOURCES,
      message: "Choose source of configuration management code",
      default: this.config.get('source'),
      required: true
    }];

    return this.prompt(prompts).then(function(props) {
      this.config.set('projectname', props.projectname || this.projectname);
      this.config.set('source', props.source);
      this.config.save();

      let projectname = this.config.get('projectname');
      let args = { arguments: [ projectname ] };

      switch(props.source) {
        case CM_SOURCE_GENERATE:
          this.composeWith('bakery:cm', args);
          break;
        case CM_SOURCE_FORK:
          this.composeWith('bakery:cm-source-fork');
          break;
        case CM_SOURCE_DIRECTORY:
          this.composeWith('bakery:cm-source-local');
          break;
      }
      this.composeWith('bakery:scm');
      this.composeWith('bakery:ci');

      args.options = { awsprofile: this.options.awsprofile };
      this.composeWith('bakery:bake', args);
    }.bind(this));
  },

  configuring: function() {
    let projectname = this.config.get('projectname');
    if (path.basename(this.destinationPath()) !== projectname) {
      mkdirp(projectname);
      this.destinationRoot(this.destinationPath(projectname));
    }
  },

  writing: function() {},

  install: function() {
    this.installDependencies();
  }
});

module.exports = BakeryGenerator;
<commit_msg>fix projectname argument handling<commit_after>'use strict';
const yeoman = require('yeoman-generator'),
  chalk = require('chalk'),
  yosay = require('yosay'),
  bakery = require('../lib/bakery'),
  feedback = require('../lib/feedback'),
  debug = require('debug')('bakery:generator:index'),
  mkdirp = require('mkdirp'),
  path = require('path'),
  _ = require('lodash');

const CM_SOURCE_DIRECTORY = 'Local Directory';
const CM_SOURCE_FORK = 'Fork From SCM';
const CM_SOURCE_GENERATE = 'Use Generator'
const CM_SOURCES = [ CM_SOURCE_DIRECTORY, CM_SOURCE_FORK, CM_SOURCE_GENERATE ];

var BakeryGenerator = yeoman.Base.extend({

  constructor: function() {
    yeoman.Base.apply(this, arguments);
    this._options.help.desc = 'Show this help';

    /** @property {object} answers - prompt answers */
    this.answers = {};
    this.existingProject = false;

    this.argument('projectname', {
      type: String,
      required: false,
      defaults: ''
    });

    this.option('awsprofile', {
      type: String,
      alias: 'p',
      desc: 'Name of the AWS profile to use when calling the AWS api for value validation',
      defaults: 'default'
    });
  },

  initializing: function() {
    let default_config = {
      source: CM_SOURCE_DIRECTORY,
      projectname: this.projectname
    }
    this.config.defaults(default_config);

    var configFound = this.baseName !== undefined && this.applicationType !== undefined;
    if (configFound) {
      this.existingProject = true;
    }
  },

<<<<<<< HEAD
=======
  default: {
    function() {
    },
  },

>>>>>>> cf5cf57... fix projectname argument handling
  prompting: function() {
    process.env.AWS_PROFILE = this.options.awsprofile;
    // Have Yeoman greet the user.
    this.log(yosay(
      'Welcome to the super-excellent ' + chalk.red('bakery') + ' generator!'
    ));

    let prompts = [{
      name: "projectname",
      type: "input",
      message: "Project name",
      when: () => { return (this.projectname.length < 1); },
      default: this.config.get('projectname'),
      required: true
    },
    {
      name: "source",
      type: "list",
      choices: CM_SOURCES,
      message: "Choose source of configuration management code",
      default: this.config.get('source'),
      required: true
    }];

    return this.prompt(prompts).then(function(props) {
      this.config.set('projectname', props.projectname || this.config.get('projectname'));
      this.config.set('source', props.source);
      this.config.save();

      let projectname = this.config.get('projectname');
      let args = { arguments: [ projectname ] };

      switch(props.source) {
        case CM_SOURCE_GENERATE:
          this.composeWith('bakery:cm', args);
          break;
        case CM_SOURCE_FORK:
          this.composeWith('bakery:cm-source-fork');
          break;
        case CM_SOURCE_DIRECTORY:
          this.composeWith('bakery:cm-source-local');
          break;
      }
      this.composeWith('bakery:scm');
      this.composeWith('bakery:ci');

      args.options = { awsprofile: this.options.awsprofile };
      this.composeWith('bakery:bake', args);
    }.bind(this));
  },

  configuring: function() {
    let projectname = this.config.get('projectname');
    if (path.basename(this.destinationPath()) !== projectname) {
      mkdirp(projectname);
      this.destinationRoot(this.destinationPath(projectname));
    }
  },

  writing: function() {},

  install: function() {
    this.installDependencies();
  }
});

module.exports = BakeryGenerator;
<|endoftext|>