<commit_before>var dialects = require('./')
var pync = require('pync')
var PostgresClient = require('./postgres-client')

class PostgresDialect {
  _unquote (str) {
    if (str.substring(0, 1) === '"' && str.substring(str.length - 1) === '"') {
      return str.substring(1, str.length - 1)
    }
    return str
  }

  describeDatabase (options) {
    var schema = { dialect: 'postgres' }
    var client = new PostgresClient(options)
    return client.find('SELECT * FROM pg_tables WHERE schemaname NOT IN ($1, $2, $3)', ['temp', 'pg_catalog', 'information_schema'])
      .then((tables) => (
        pync.map(tables, (table) => {
          var t = {
            name: table.tablename,
            schema: table.schemaname,
            indexes: [],
            constraints: []
          }
          return client.find(`
            SELECT
              table_name,
              table_schema,
              column_name,
              data_type,
              udt_name,
              character_maximum_length,
              is_nullable,
              column_default
            FROM
              INFORMATION_SCHEMA.COLUMNS
            WHERE
              table_name=$1 AND table_schema=$2;`, [table.tablename, table.schemaname])
          .then((columns) => {
            t.columns = columns.map((column) => ({
              name: column.column_name,
              nullable: column.is_nullable === 'YES',
              default_value: column.column_default,
              type: dataType(column)
            }))
            return t
          })
        })
      ))
      .then((tables) => {
        schema.tables = tables
        return client.find(`
          SELECT
            i.relname as indname,
            i.relowner as indowner,
            idx.indrelid::regclass,
            idx.indisprimary,
            idx.indisunique,
            am.amname as indam,
            idx.indkey,
            ARRAY(
              SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
              FROM generate_subscripts(idx.indkey, 1) as k
              ORDER BY k
            ) AS indkey_names,
            idx.indexprs IS NOT NULL as indexprs,
            idx.indpred IS NOT NULL as indpred,
            ns.nspname
          FROM
            pg_index as idx
          JOIN pg_class as i
            ON i.oid = idx.indexrelid
          JOIN pg_am as am
            ON i.relam = am.oid
          JOIN pg_namespace as ns
            ON ns.oid = i.relnamespace
            AND ns.nspname NOT IN ('pg_catalog', 'pg_toast')
          WHERE (NOT idx.indisprimary) AND (NOT idx.indisunique);
        `)
      })
      .then((indexes) => {
        indexes.forEach((index) => {
          var table = schema.tables.find((table) => table.name === index.indrelid && table.schema === index.nspname)
          table.indexes.push({
            name: index.indname,
            schema: table.schema,
            type: index.indam,
            columns: index.indkey_names
          })
        })
        return client.find(`
          SELECT conrelid::regclass AS table_from, n.nspname, contype, conname, pg_get_constraintdef(c.oid) AS description
          FROM   pg_constraint c
          JOIN   pg_namespace n ON n.oid = c.connamespace
          WHERE  contype IN ('f', 'p', 'u')
          ORDER  BY conrelid::regclass::text, contype DESC;
        `)
      })
      .then((constraints) => {
        var types = {
          u: 'unique',
          f: 'foreign',
          p: 'primary'
        }
        constraints.forEach((constraint) => {
          var tableFrom = this._unquote(constraint.table_from)
          var table = schema.tables.find((table) => table.name === tableFrom && table.schema === constraint.nspname)
          var { description } = constraint
          var i = description.indexOf('(')
          var n = description.indexOf(')')
          var m = description.indexOf('REFERENCES')
          var info = {
            name: constraint.conname,
            schema: table.schema,
            type: types[constraint.contype],
            columns: description.substring(i + 1, n).split(',').map((s) => this._unquote(s.trim()))
          }
          table.constraints.push(info)
          if (m > 0) {
            var substr = description.substring(m + 'REFERENCES'.length)
            i = substr.indexOf('(')
            n = substr.indexOf(')')
            info.referenced_table = substr.substring(0, i).trim()
            info.referenced_columns = substr.substring(i + 1, n).split(',').map((s) => this._unquote(s.trim()))
          }
        })
        return client.find('SELECT * FROM information_schema.sequences')
      })
      .then((sequences) => {
        schema.sequences = sequences.map((sequence) => {
          sequence.schema = sequence.sequence_schema
          sequence.name = sequence.sequence_name
          sequence.cycle = sequence.cycle_option === 'YES'
          delete sequence.sequence_name
          delete sequence.sequence_catalog
          delete sequence.sequence_schema
          delete sequence.cycle_option
          return sequence
        })
        return schema
      })
  }
}

function dataType (info) {
  var type
  if (info.data_type === 'ARRAY') {
    type = info.udt_name
    if (type.substring(0, 1) === '_') {
      type = type.substring(1)
    }
    type += '[]'
  } else if (info.data_type === 'USER-DEFINED') {
    type = info.udt_name // hstore for example
  } else {
    type = info.data_type
  }

  if (info.character_maximum_length) {
    type = type + '(' + info.character_maximum_length + ')'
  }
  return type
}

dialects.register('postgres', PostgresDialect)
<commit_msg>fix constraints in dot tables<commit_after>var dialects = require('./')
var pync = require('pync')
var PostgresClient = require('./postgres-client')

class PostgresDialect {
  _unquote (str) {
    if (str.substring(0, 1) === '"' && str.substring(str.length - 1) === '"') {
      return str.substring(1, str.length - 1)
    }
    return str
  }

  describeDatabase (options) {
    var schema = { dialect: 'postgres' }
    var client = new PostgresClient(options)
    return client.find('SELECT * FROM pg_tables WHERE schemaname NOT IN ($1, $2, $3)', ['temp', 'pg_catalog', 'information_schema'])
      .then((tables) => (
        pync.map(tables, (table) => {
          var t = {
            name: table.tablename,
            schema: table.schemaname,
            indexes: [],
            constraints: []
          }
          return client.find(`
            SELECT
              table_name,
              table_schema,
              column_name,
              data_type,
              udt_name,
              character_maximum_length,
              is_nullable,
              column_default
            FROM
              INFORMATION_SCHEMA.COLUMNS
            WHERE
              table_name=$1 AND table_schema=$2;`, [table.tablename, table.schemaname])
          .then((columns) => {
            t.columns = columns.map((column) => ({
              name: column.column_name,
              nullable: column.is_nullable === 'YES',
              default_value: column.column_default,
              type: dataType(column)
            }))
            return t
          })
        })
      ))
      .then((tables) => {
        schema.tables = tables
        return client.find(`
          SELECT
            i.relname as indname,
            i.relowner as indowner,
            idx.indrelid::regclass,
            idx.indisprimary,
            idx.indisunique,
            am.amname as indam,
            idx.indkey,
            ARRAY(
              SELECT pg_get_indexdef(idx.indexrelid, k + 1, true)
              FROM generate_subscripts(idx.indkey, 1) as k
              ORDER BY k
            ) AS indkey_names,
            idx.indexprs IS NOT NULL as indexprs,
            idx.indpred IS NOT NULL as indpred,
            ns.nspname
          FROM
            pg_index as idx
          JOIN pg_class as i
            ON i.oid = idx.indexrelid
          JOIN pg_am as am
            ON i.relam = am.oid
          JOIN pg_namespace as ns
            ON ns.oid = i.relnamespace
            AND ns.nspname NOT IN ('pg_catalog', 'pg_toast')
          WHERE (NOT idx.indisprimary) AND (NOT idx.indisunique);
        `)
      })
      .then((indexes) => {
        indexes.forEach((index) => {
          var table = schema.tables.find((table) => table.name === index.indrelid && table.schema === index.nspname)
          table.indexes.push({
            name: index.indname,
            schema: table.schema,
            type: index.indam,
            columns: index.indkey_names
          })
        })
        return client.find(`
          SELECT conrelid::regclass AS table_from, n.nspname, contype, conname, pg_get_constraintdef(c.oid) AS description
          FROM   pg_constraint c
          JOIN   pg_namespace n ON n.oid = c.connamespace
          WHERE  contype IN ('f', 'p', 'u')
          ORDER  BY conrelid::regclass::text, contype DESC;
        `)
      })
      .then((constraints) => {
        var types = {
          u: 'unique',
          f: 'foreign',
          p: 'primary'
        }
        constraints.forEach((constraint) => {
          var tableFrom = this._unquote(constraint.table_from).split('.').pop()
          var table = schema.tables.find((table) => table.name === tableFrom && table.schema === constraint.nspname)
          var { description } = constraint
          var i = description.indexOf('(')
          var n = description.indexOf(')')
          var m = description.indexOf('REFERENCES')
          var info = {
            name: constraint.conname,
            schema: table.schema,
            type: types[constraint.contype],
            columns: description.substring(i + 1, n).split(',').map((s) => this._unquote(s.trim()))
          }
          table.constraints.push(info)
          if (m > 0) {
            var substr = description.substring(m + 'REFERENCES'.length)
            i = substr.indexOf('(')
            n = substr.indexOf(')')
            info.referenced_table = substr.substring(0, i).trim()
            info.referenced_columns = substr.substring(i + 1, n).split(',').map((s) => this._unquote(s.trim()))
          }
        })
        return client.find('SELECT * FROM information_schema.sequences')
      })
      .then((sequences) => {
        schema.sequences = sequences.map((sequence) => {
          sequence.schema = sequence.sequence_schema
          sequence.name = sequence.sequence_name
          sequence.cycle = sequence.cycle_option === 'YES'
          delete sequence.sequence_name
          delete sequence.sequence_catalog
          delete sequence.sequence_schema
          delete sequence.cycle_option
          return sequence
        })
        return schema
      })
  }
}

function dataType (info) {
  var type
  if (info.data_type === 'ARRAY') {
    type = info.udt_name
    if (type.substring(0, 1) === '_') {
      type = type.substring(1)
    }
    type += '[]'
  } else if (info.data_type === 'USER-DEFINED') {
    type = info.udt_name // hstore for example
  } else {
    type = info.data_type
  }

  if (info.character_maximum_length) {
    type = type + '(' + info.character_maximum_length + ')'
  }
  return type
}

dialects.register('postgres', PostgresDialect)
<|endoftext|>


<commit_before>/**
 * Copyright 2017, Google, Inc.
 * 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.
 */

'use strict';

const util = require('util');
const fs = require('fs');
const {struct} = require('pb-util');
const {Transform, pipeline} = require('stream');

const pump = util.promisify(pipeline);

function detectTextIntent(projectId, sessionId, queries, languageCode) {
  // [START dialogflow_detect_intent_text]
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  if (!queries || !queries.length) {
    return;
  }

  // The path to identify the agent that owns the created intent.
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  let promise;

  // Detects the intent of the queries.
  for (const query of queries) {
    // The text query request.
    const request = {
      session: sessionPath,
      queryInput: {
        text: {
          text: query,
          languageCode: languageCode,
        },
      },
    };

    if (!promise) {
      // First query.
      console.log(`Sending query "${query}"`);
      promise = sessionClient.detectIntent(request);
    } else {
      promise = promise.then(responses => {
        console.log('Detected intent');
        const response = responses[0];
        logQueryResult(sessionClient, response.queryResult);

        // Use output contexts as input contexts for the next query.
        response.queryResult.outputContexts.forEach(context => {
          // There is a bug in gRPC that the returned google.protobuf.Struct
          // value contains fields with value of null, which causes error
          // when encoding it back. Converting to JSON and back to proto
          // removes those values.
          context.parameters = struct.encode(struct.decode(context.parameters));
        });
        request.queryParams = {
          contexts: response.queryResult.outputContexts,
        };

        console.log(`Sending query "${query}"`);
        return sessionClient.detectIntent(request);
      });
    }
  }

  promise
    .then(responses => {
      console.log('Detected intent');
      logQueryResult(sessionClient, responses[0].queryResult);
    })
    .catch(err => {
      console.error('ERROR:', err);
    });

  // [END dialogflow_detect_intent_text]
}

async function detectEventIntent(
  projectId,
  sessionId,
  eventName,
  languageCode
) {
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  // The path to identify the agent that owns the created intent.
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  // The text query request.
  const request = {
    session: sessionPath,
    queryInput: {
      event: {
        name: eventName,
        parameters: struct.encode({foo: 'bar'}),
        languageCode: languageCode,
      },
    },
  };

  const [response] = await sessionClient.detectIntent(request);
  console.log('Detected intent');
  logQueryResult(sessionClient, response.queryResult);
}

async function detectAudioIntent(
  projectId,
  sessionId,
  filename,
  encoding,
  sampleRateHertz,
  languageCode
) {
  // [START dialogflow_detect_intent_audio]
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  // The path to identify the agent that owns the created intent.
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  // Read the content of the audio file and send it as part of the request.
  const readFile = util.promisify(fs.readFile);
  const inputAudio = await readFile(filename);
  const request = {
    session: sessionPath,
    queryInput: {
      audioConfig: {
        audioEncoding: encoding,
        sampleRateHertz: sampleRateHertz,
        languageCode: languageCode,
      },
    },
    inputAudio: inputAudio,
  };

  // Recognizes the speech in the audio and detects its intent.
  const [response] = await sessionClient.detectIntent(request);

  console.log('Detected intent:');
  logQueryResult(sessionClient, response.queryResult);
  // [END dialogflow_detect_intent_audio]
}

async function streamingDetectIntent(
  projectId,
  sessionId,
  filename,
  encoding,
  sampleRateHertz,
  languageCode
) {
  // [START dialogflow_detect_intent_streaming]
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  // The path to the local file on which to perform speech recognition, e.g.
  // /path/to/audio.raw const filename = '/path/to/audio.raw';

  // The encoding of the audio file, e.g. 'AUDIO_ENCODING_LINEAR_16'
  // const encoding = 'AUDIO_ENCODING_LINEAR_16';

  // The sample rate of the audio file in hertz, e.g. 16000
  // const sampleRateHertz = 16000;

  // The BCP-47 language code to use, e.g. 'en-US'
  // const languageCode = 'en-US';
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  const initialStreamRequest = {
    session: sessionPath,
    queryParams: {
      session: sessionClient.sessionPath(projectId, sessionId),
    },
    queryInput: {
      audioConfig: {
        audioEncoding: encoding,
        sampleRateHertz: sampleRateHertz,
        languageCode: languageCode,
      },
      singleUtterance: true,
    },
  };

  // Create a stream for the streaming request.
  const detectStream = sessionClient
    .streamingDetectIntent()
    .on('error', console.error)
    .on('data', data => {
      if (data.recognitionResult) {
        console.log(
          `Intermediate transcript: ${data.recognitionResult.transcript}`
        );
      } else {
        console.log(`Detected intent:`);
        logQueryResult(sessionClient, data.queryResult);
      }
    });

  // Write the initial stream request to config for audio input.
  detectStream.write(initialStreamRequest);

  // Stream an audio file from disk to the Conversation API, e.g.
  // "./resources/audio.raw"
  await pump(
    fs.createReadStream(filename),
    // Format the audio stream into the request format.
    new Transform({
      objectMode: true,
      transform: (obj, _, next) => {
        next(null, {inputAudio: obj});
      },
    }),
    detectStream
  );
  // [END dialogflow_detect_intent_streaming]
}

function logQueryResult(sessionClient, result) {
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a context client
  const contextClient = new dialogflow.ContextsClient();

  console.log(`  Query: ${result.queryText}`);
  console.log(`  Response: ${result.fulfillmentText}`);
  if (result.intent) {
    console.log(`  Intent: ${result.intent.displayName}`);
  } else {
    console.log(`  No intent matched.`);
  }
  const parameters = JSON.stringify(struct.decode(result.parameters));
  console.log(`  Parameters: ${parameters}`);
  if (result.outputContexts && result.outputContexts.length) {
    console.log(`  Output contexts:`);
    result.outputContexts.forEach(context => {
      const contextId = contextClient.matchContextFromContextName(context.name);
      const contextParameters = JSON.stringify(
        struct.decode(context.parameters)
      );
      console.log(`    ${contextId}`);
      console.log(`      lifespan: ${context.lifespanCount}`);
      console.log(`      parameters: ${contextParameters}`);
    });
  }
}

const cli = require(`yargs`)
  .demand(1)
  .options({
    projectId: {
      alias: 'p',
      default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT,
      description:
        'The Project ID to use. Defaults to the value of the ' +
        'GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.',
      requiresArg: true,
      type: 'string',
    },
    sessionId: {
      alias: 's',
      default: require('uuid/v1')(),
      type: 'string',
      requiresArg: true,
      description:
        'The identifier of the detect session. Defaults to a random UUID.',
    },
    languageCode: {
      alias: 'l',
      default: 'en-US',
      type: 'string',
      requiresArg: true,
      description: 'The language code of the query. Defaults to "en-US".',
    },
    encoding: {
      alias: 'e',
      default: 'AUDIO_ENCODING_LINEAR_16',
      choices: [
        'AUDIO_ENCODING_LINEAR_16',
        'AUDIO_ENCODING_FLAC',
        'AUDIO_ENCODING_MULAW',
        'AUDIO_ENCODING_AMR',
        'AUDIO_ENCODING_AMR_WB',
        'AUDIO_ENCODING_OGG_OPUS',
        'AUDIO_ENCODING_SPEEX_WITH_HEADER_BYTE',
      ],
      requiresArg: true,
      description: 'The encoding of the input audio.',
    },
    sampleRateHertz: {
      alias: 'r',
      type: 'number',
      description:
        'The sample rate in Hz of the input audio. Only ' +
        'required if the input audio is in raw format.',
    },
  })
  .demandOption(
    'projectId',
    "Please provide your Dialogflow agent's project ID with the -p flag or through the GOOGLE_CLOUD_PROJECT env var"
  )
  .command(
    `text`,
    `Detects the intent for text queries.`,
    {
      queries: {
        alias: 'q',
        array: true,
        string: true,
        demandOption: true,
        requiresArg: true,
        description: 'An array of text queries',
      },
    },
    opts =>
      detectTextIntent(
        opts.projectId,
        opts.sessionId,
        opts.queries,
        opts.languageCode
      )
  )
  .command(
    `event <eventName>`,
    `Detects the intent for a client-generated event name.`,
    {},
    opts =>
      detectEventIntent(
        opts.projectId,
        opts.sessionId,
        opts.eventName,
        opts.languageCode
      )
  )
  .command(
    `audio <filename>`,
    `Detects the intent for audio queries in a local file.`,
    {},
    opts =>
      detectAudioIntent(
        opts.projectId,
        opts.sessionId,
        opts.filename,
        opts.encoding,
        opts.sampleRateHertz,
        opts.languageCode
      )
  )
  .command(
    `stream <filename>`,
    `Detects the intent in a local audio file by streaming it to the ` +
      `Conversation API.`,
    {},
    opts =>
      streamingDetectIntent(
        opts.projectId,
        opts.sessionId,
        opts.filename,
        opts.encoding,
        opts.sampleRateHertz,
        opts.languageCode
      )
  )
  .example(
    `node $0 text -q "hello" "book a room" "Mountain View" ` +
      `"today" "230pm" "half an hour" "two people" "A" "yes"`
  )
  .example(`node $0 event order_pizza`)
  .example(`node $0 audio resources/book_a_room.wav -r 16000`)
  .example(`node $0 stream resources/mountain_view.wav -r 16000`)
  .wrap(120)
  .recommendCommands()
  .epilogue(
    `For more information, see https://cloud.google.com/dialogflow-enterprise/docs`
  )
  .help()
  .strict();

if (module === require.main) {
  cli.parse(process.argv.slice(2));
}
<commit_msg>docs(samples): remove session option from query params (#427)<commit_after>/**
 * Copyright 2017 Google LLC
 * 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.
 */

'use strict';

const util = require('util');
const fs = require('fs');
const {struct} = require('pb-util');
const {Transform, pipeline} = require('stream');

const pump = util.promisify(pipeline);

function detectTextIntent(projectId, sessionId, queries, languageCode) {
  // [START dialogflow_detect_intent_text]
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  if (!queries || !queries.length) {
    return;
  }

  // The path to identify the agent that owns the created intent.
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  let promise;

  // Detects the intent of the queries.
  for (const query of queries) {
    // The text query request.
    const request = {
      session: sessionPath,
      queryInput: {
        text: {
          text: query,
          languageCode: languageCode,
        },
      },
    };

    if (!promise) {
      // First query.
      console.log(`Sending query "${query}"`);
      promise = sessionClient.detectIntent(request);
    } else {
      promise = promise.then(responses => {
        console.log('Detected intent');
        const response = responses[0];
        logQueryResult(sessionClient, response.queryResult);

        // Use output contexts as input contexts for the next query.
        response.queryResult.outputContexts.forEach(context => {
          // There is a bug in gRPC that the returned google.protobuf.Struct
          // value contains fields with value of null, which causes error
          // when encoding it back. Converting to JSON and back to proto
          // removes those values.
          context.parameters = struct.encode(struct.decode(context.parameters));
        });
        request.queryParams = {
          contexts: response.queryResult.outputContexts,
        };

        console.log(`Sending query "${query}"`);
        return sessionClient.detectIntent(request);
      });
    }
  }

  promise
    .then(responses => {
      console.log('Detected intent');
      logQueryResult(sessionClient, responses[0].queryResult);
    })
    .catch(err => {
      console.error('ERROR:', err);
    });

  // [END dialogflow_detect_intent_text]
}

async function detectEventIntent(
  projectId,
  sessionId,
  eventName,
  languageCode
) {
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  // The path to identify the agent that owns the created intent.
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  // The text query request.
  const request = {
    session: sessionPath,
    queryInput: {
      event: {
        name: eventName,
        parameters: struct.encode({foo: 'bar'}),
        languageCode: languageCode,
      },
    },
  };

  const [response] = await sessionClient.detectIntent(request);
  console.log('Detected intent');
  logQueryResult(sessionClient, response.queryResult);
}

async function detectAudioIntent(
  projectId,
  sessionId,
  filename,
  encoding,
  sampleRateHertz,
  languageCode
) {
  // [START dialogflow_detect_intent_audio]
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  // The path to identify the agent that owns the created intent.
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  // Read the content of the audio file and send it as part of the request.
  const readFile = util.promisify(fs.readFile);
  const inputAudio = await readFile(filename);
  const request = {
    session: sessionPath,
    queryInput: {
      audioConfig: {
        audioEncoding: encoding,
        sampleRateHertz: sampleRateHertz,
        languageCode: languageCode,
      },
    },
    inputAudio: inputAudio,
  };

  // Recognizes the speech in the audio and detects its intent.
  const [response] = await sessionClient.detectIntent(request);

  console.log('Detected intent:');
  logQueryResult(sessionClient, response.queryResult);
  // [END dialogflow_detect_intent_audio]
}

async function streamingDetectIntent(
  projectId,
  sessionId,
  filename,
  encoding,
  sampleRateHertz,
  languageCode
) {
  // [START dialogflow_detect_intent_streaming]
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a session client
  const sessionClient = new dialogflow.SessionsClient();

  // The path to the local file on which to perform speech recognition, e.g.
  // /path/to/audio.raw const filename = '/path/to/audio.raw';

  // The encoding of the audio file, e.g. 'AUDIO_ENCODING_LINEAR_16'
  // const encoding = 'AUDIO_ENCODING_LINEAR_16';

  // The sample rate of the audio file in hertz, e.g. 16000
  // const sampleRateHertz = 16000;

  // The BCP-47 language code to use, e.g. 'en-US'
  // const languageCode = 'en-US';
  const sessionPath = sessionClient.sessionPath(projectId, sessionId);

  const initialStreamRequest = {
    session: sessionPath,
    queryInput: {
      audioConfig: {
        audioEncoding: encoding,
        sampleRateHertz: sampleRateHertz,
        languageCode: languageCode,
      },
      singleUtterance: true,
    },
  };

  // Create a stream for the streaming request.
  const detectStream = sessionClient
    .streamingDetectIntent()
    .on('error', console.error)
    .on('data', data => {
      if (data.recognitionResult) {
        console.log(
          `Intermediate transcript: ${data.recognitionResult.transcript}`
        );
      } else {
        console.log(`Detected intent:`);
        logQueryResult(sessionClient, data.queryResult);
      }
    });

  // Write the initial stream request to config for audio input.
  detectStream.write(initialStreamRequest);

  // Stream an audio file from disk to the Conversation API, e.g.
  // "./resources/audio.raw"
  await pump(
    fs.createReadStream(filename),
    // Format the audio stream into the request format.
    new Transform({
      objectMode: true,
      transform: (obj, _, next) => {
        next(null, {inputAudio: obj});
      },
    }),
    detectStream
  );
  // [END dialogflow_detect_intent_streaming]
}

function logQueryResult(sessionClient, result) {
  // Imports the Dialogflow library
  const dialogflow = require('dialogflow');

  // Instantiates a context client
  const contextClient = new dialogflow.ContextsClient();

  console.log(`  Query: ${result.queryText}`);
  console.log(`  Response: ${result.fulfillmentText}`);
  if (result.intent) {
    console.log(`  Intent: ${result.intent.displayName}`);
  } else {
    console.log(`  No intent matched.`);
  }
  const parameters = JSON.stringify(struct.decode(result.parameters));
  console.log(`  Parameters: ${parameters}`);
  if (result.outputContexts && result.outputContexts.length) {
    console.log(`  Output contexts:`);
    result.outputContexts.forEach(context => {
      const contextId = contextClient.matchContextFromContextName(context.name);
      const contextParameters = JSON.stringify(
        struct.decode(context.parameters)
      );
      console.log(`    ${contextId}`);
      console.log(`      lifespan: ${context.lifespanCount}`);
      console.log(`      parameters: ${contextParameters}`);
    });
  }
}

const cli = require(`yargs`)
  .demand(1)
  .options({
    projectId: {
      alias: 'p',
      default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT,
      description:
        'The Project ID to use. Defaults to the value of the ' +
        'GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.',
      requiresArg: true,
      type: 'string',
    },
    sessionId: {
      alias: 's',
      default: require('uuid/v1')(),
      type: 'string',
      requiresArg: true,
      description:
        'The identifier of the detect session. Defaults to a random UUID.',
    },
    languageCode: {
      alias: 'l',
      default: 'en-US',
      type: 'string',
      requiresArg: true,
      description: 'The language code of the query. Defaults to "en-US".',
    },
    encoding: {
      alias: 'e',
      default: 'AUDIO_ENCODING_LINEAR_16',
      choices: [
        'AUDIO_ENCODING_LINEAR_16',
        'AUDIO_ENCODING_FLAC',
        'AUDIO_ENCODING_MULAW',
        'AUDIO_ENCODING_AMR',
        'AUDIO_ENCODING_AMR_WB',
        'AUDIO_ENCODING_OGG_OPUS',
        'AUDIO_ENCODING_SPEEX_WITH_HEADER_BYTE',
      ],
      requiresArg: true,
      description: 'The encoding of the input audio.',
    },
    sampleRateHertz: {
      alias: 'r',
      type: 'number',
      description:
        'The sample rate in Hz of the input audio. Only ' +
        'required if the input audio is in raw format.',
    },
  })
  .demandOption(
    'projectId',
    "Please provide your Dialogflow agent's project ID with the -p flag or through the GOOGLE_CLOUD_PROJECT env var"
  )
  .command(
    `text`,
    `Detects the intent for text queries.`,
    {
      queries: {
        alias: 'q',
        array: true,
        string: true,
        demandOption: true,
        requiresArg: true,
        description: 'An array of text queries',
      },
    },
    opts =>
      detectTextIntent(
        opts.projectId,
        opts.sessionId,
        opts.queries,
        opts.languageCode
      )
  )
  .command(
    `event <eventName>`,
    `Detects the intent for a client-generated event name.`,
    {},
    opts =>
      detectEventIntent(
        opts.projectId,
        opts.sessionId,
        opts.eventName,
        opts.languageCode
      )
  )
  .command(
    `audio <filename>`,
    `Detects the intent for audio queries in a local file.`,
    {},
    opts =>
      detectAudioIntent(
        opts.projectId,
        opts.sessionId,
        opts.filename,
        opts.encoding,
        opts.sampleRateHertz,
        opts.languageCode
      )
  )
  .command(
    `stream <filename>`,
    `Detects the intent in a local audio file by streaming it to the ` +
      `Conversation API.`,
    {},
    opts =>
      streamingDetectIntent(
        opts.projectId,
        opts.sessionId,
        opts.filename,
        opts.encoding,
        opts.sampleRateHertz,
        opts.languageCode
      )
  )
  .example(
    `node $0 text -q "hello" "book a room" "Mountain View" ` +
      `"today" "230pm" "half an hour" "two people" "A" "yes"`
  )
  .example(`node $0 event order_pizza`)
  .example(`node $0 audio resources/book_a_room.wav -r 16000`)
  .example(`node $0 stream resources/mountain_view.wav -r 16000`)
  .wrap(120)
  .recommendCommands()
  .epilogue(
    `For more information, see https://cloud.google.com/dialogflow-enterprise/docs`
  )
  .help()
  .strict();

if (module === require.main) {
  cli.parse(process.argv.slice(2));
}
<|endoftext|>


<commit_before>var _ = require('underscore'),
    Backbone = require('backbone'),
    async = require('async'),
    ModelStore = require('./store/model_store'),
    CollectionStore = require('./store/collection_store'),
    isServer = (typeof window === 'undefined');

if (!isServer) {
  Backbone.$ = window.$ || require('jquery');
}

module.exports = Fetcher;

function Fetcher(options) {
  this.options = options;
  this.app = this.options.app;
  this.modelUtils = this.app.modelUtils;
  this.modelStore = new ModelStore({
    app: this.app,
    modelUtils: this.modelUtils
  });
  this.collectionStore = new CollectionStore({
    app: this.app,
    modelUtils: this.modelUtils
  });
}

Fetcher.prototype.buildOptions = function(additionalOptions, params) {
  var options = {app: this.app, parse: true};
  _.defaults(options, additionalOptions);
  _.defaults(options, params);
  return options;
};

/**
 * Returns an instance of Model or Collection.
 */
Fetcher.prototype.getModelOrCollectionForSpec = function(spec, attrsOrModels, options, callback) {
  if (spec.model) {
    return this.getModelForSpec(spec, attrsOrModels, options, callback);
  } else {
    return this.getCollectionForSpec(spec, attrsOrModels, options, callback);
  }
};

/**
 * Returns an instance of Collection.
 */
Fetcher.prototype.getCollectionForSpec = function(spec, models, options, callback) {
  var collectionOptions = this.buildOptions(options, _.extend({params: spec.params}, spec.params));
  models = models || [];
  return this.modelUtils.getCollection(spec.collection, models, collectionOptions, callback);
};

/**
 * Returns an instance of Model.
 */
Fetcher.prototype.getModelForSpec = function(spec, attributes, options, callback) {
  var modelOptions = this.buildOptions(options);

  attributes = attributes || {};
  _.defaults(attributes, spec.params);

  return this.modelUtils.getModel(spec.model, attributes, modelOptions, callback);
};

/**
 * map fetchSpecs to models and fetch data in parallel
 */
Fetcher.prototype._retrieve = function(fetchSpecs, options, callback) {
  var batchedRequests = {};

  _.each(fetchSpecs, function(spec, name) {
    batchedRequests[name] = function(cb) {
      var model;

      var readFromCache = options.readFromCache;

      // If present, the individual spec can overwrite the fetchSpec.
      if (!_.isUndefined(spec.readFromCache) && !_.isNull(spec.readFromCache)) {
        readFromCache = spec.readFromCache;
      }

      if (!readFromCache) {
        this.fetchFromApi(spec, options, cb);
      } else {
        model = null;

        // First, see if we have stored the model or collection.
        if (spec.model != null) {

          this._retrieveModel(spec, function(err, model) {
            this._refreshData(spec, model, options, cb);
          }.bind(this));

        } else if (spec.collection != null) {
          this.collectionStore.get(spec.collection, spec.params, function(collection) {
            this._refreshData(spec, collection, options, cb);
          }.bind(this));

        }

      }
    }.bind(this);
  }, this);
  async.parallel(batchedRequests, callback);
};

Fetcher.prototype._refreshData = function(spec, modelOrCollection, options, cb) {

  // If we found the model/collection in the store, then return that.
  if (!this.needsFetch(modelOrCollection, spec)) {
    cb(null, modelOrCollection);
  } else {
    /**
     * Else, fetch anew.
     */
    this.fetchFromApi(spec, options, cb);
  }
}

Fetcher.prototype._retrieveModel = function(spec, callback) {
  var fetcher = this;

  // Attempt to fetch from the modelStore based on the idAttribute
  this.modelUtils.modelIdAttribute(spec.model, function(idAttribute) {
    var model = fetcher.modelStore.get(spec.model, spec.params[idAttribute]);
    if (model) return callback(null, model);

    // if there are no other keys than the id in the params, return null;
    if (_.isEmpty(_.omit(spec.params, idAttribute)))
      return callback(null, null);

    // Attempt to fetch the model in the modelStore based on the other params
    return callback(null, fetcher.modelStore.find(spec.model, spec.params));
  });
};

Fetcher.prototype.needsFetch = function(modelOrCollection, spec) {
  if (modelOrCollection == null) return true;

  if (this.modelUtils.isModel(modelOrCollection) && this.isMissingKeys(modelOrCollection.attributes, spec.ensureKeys)) {
    return true;
  }

  if (spec.needsFetch === true) return true;
  if (typeof spec.needsFetch === 'function' && spec.needsFetch(modelOrCollection)) return true;
  return false;
};

Fetcher.prototype.isMissingKeys = function(modelData, keys) {
  var key;

  if (keys == null) {
    return false;
  }

  if (!_.isArray(keys)) {
    keys = [keys];
  }

  for (var i = 0, len = keys.length; i < len; i++) {
    key = keys[i];
    if (modelData[key] == null) {
      return true;
    }
  }
  return false;
};

Fetcher.prototype.fetchFromApi = function(spec, options, callback) {
  var model = this.getModelOrCollectionForSpec(spec, null, options),
      fetcher = this;

  model.fetch({
    headers: options.headers || {},
    data: spec.params,
    success: function(model, body) {
      callback(null, model);
    },
    error: function(model, resp, options) {
      var body, respOutput, err;

      body = resp.body;
      resp.body = typeof body === 'string' ? body.slice(0, 150) : body;
      respOutput = JSON.stringify(resp);
      err = new Error("ERROR fetching model '" + fetcher.modelUtils.modelName(model.constructor) + "' with options '" + JSON.stringify(options) + "'. Response: " + respOutput);
      err.status = resp.status;
      err.body = body;
      callback(err);
    }
  });
};

Fetcher.prototype.retrieveModelsForCollectionName = function(collectionName, modelIds) {
  var modelName = this.modelUtils.getModelNameForCollectionName(collectionName);
  return this.retrieveModels(modelName, modelIds);
};

Fetcher.prototype.retrieveModels = function(modelName, modelIds) {
  return modelIds.map(function(id) {
    return this.modelStore.get(modelName, id);
  }, this);
};

Fetcher.prototype.summarize = function(modelOrCollection) {
  var summary = {},
      idAttribute;

  if (this.modelUtils.isCollection(modelOrCollection)) {
    idAttribute = modelOrCollection.model.prototype.idAttribute;
    summary = {
      collection: this.modelUtils.modelName(modelOrCollection.constructor),
      ids: modelOrCollection.pluck(idAttribute),
      params: modelOrCollection.params,
      meta: modelOrCollection.meta
    };
  } else if (this.modelUtils.isModel(modelOrCollection)) {
    summary = {
      model: this.modelUtils.modelName(modelOrCollection.constructor),
      id: modelOrCollection.id
    };
  }
  return summary;
};

Fetcher.prototype.storeResults = function(results) {
  _.each(results, function(modelOrCollection) {
    modelOrCollection.store();
  });
};

Fetcher.prototype.bootstrapData = function(modelMap, callback) {
  var results = {},
      fetcher = this;

  async.forEach(_.keys(modelMap), function(name, cb) {
    var map = modelMap[name];
    fetcher.getModelOrCollectionForSpec(map.summary, map.data, _.pick(map.summary, 'params', 'meta'), function(modelOrCollection) {
      results[name] = modelOrCollection;
      cb(null);
    });
  }, function(err) {
    if (_.isFunction(callback)) {
      callback(results);
    }
  });
};

Fetcher.prototype.hydrate = function(summaries, options, callback) {
  var results = {},
      fetcher = this;

  /**
   * Support both (summaries, options, callback)
   * and (summaries, callback).
   */
  if (arguments.length === 2) {
    callback = options;
    options = {};
  } else {
    options = options || {};
  }

  async.forEach(_.keys(summaries), function(name, cb) {
    var summary = summaries[name];
    if (summary.model != null) {
      results[name] = fetcher.modelStore.get(summary.model, summary.id);

      if ((results[name] != null) && (options.app != null)) {
        results[name].app = options.app;
      }

      cb(null);

    } else if (summary.collection != null) {
      // Also support getting all models for a collection.
      fetcher.collectionStore.get(summary.collection, summary.params, function(collection) {
        if (collection == null) {
          throw new Error("Collection of type \"" + summary.collection + "\" not found for params: " + JSON.stringify(summary.params));
        }

        results[name] = collection;

        cb(null);
      });
    }
  }, function(err) {
    callback(err, results);
  });
};

Fetcher.prototype.pendingFetches = 0;

Fetcher.prototype.fetch = function(fetchSpecs, options, callback) {
  var fetcher = this;

  /**
   * Support both (fetchSpecs, options, callback)
   * and (fetchSpecs, callback).
   */
  if (arguments.length === 2) {
    callback = options;
    options = {};
  } else {
    options = options || {};
  }

  // Different defaults for client v server.
  if (isServer) {
    if (options.readFromCache == null) {
      options.readFromCache = false;
    }
    if (options.writeToCache == null) {
      options.writeToCache = false;
    }
  } else {
    if (options.readFromCache == null) {
      options.readFromCache = true;
    }
    if (options.writeToCache == null) {
      options.writeToCache = true;
    }
  }

  this.pendingFetches++;
  this.trigger('fetch:start', fetchSpecs);
  this._retrieve(fetchSpecs, options, function(err, results) {
    fetcher.pendingFetches--;
    fetcher.trigger('fetch:end', fetchSpecs, err, results);
    if (err) return callback(err);
    if (options.writeToCache) {
      fetcher.storeResults(results);
    }
    callback(null, results);
  });
};

// Mixin Backbone.Events for events that work in client & server.
_.extend(Fetcher.prototype, Backbone.Events);
<commit_msg>Support for xhr timeout<commit_after>var _ = require('underscore'),
    Backbone = require('backbone'),
    async = require('async'),
    ModelStore = require('./store/model_store'),
    CollectionStore = require('./store/collection_store'),
    isServer = (typeof window === 'undefined');

if (!isServer) {
  Backbone.$ = window.$ || require('jquery');
}

module.exports = Fetcher;

function Fetcher(options) {
  this.options = options;
  this.app = this.options.app;
  this.modelUtils = this.app.modelUtils;
  this.modelStore = new ModelStore({
    app: this.app,
    modelUtils: this.modelUtils
  });
  this.collectionStore = new CollectionStore({
    app: this.app,
    modelUtils: this.modelUtils
  });
}

Fetcher.prototype.buildOptions = function(additionalOptions, params) {
  var options = {app: this.app, parse: true};
  _.defaults(options, additionalOptions);
  _.defaults(options, params);
  return options;
};

/**
 * Returns an instance of Model or Collection.
 */
Fetcher.prototype.getModelOrCollectionForSpec = function(spec, attrsOrModels, options, callback) {
  if (spec.model) {
    return this.getModelForSpec(spec, attrsOrModels, options, callback);
  } else {
    return this.getCollectionForSpec(spec, attrsOrModels, options, callback);
  }
};

/**
 * Returns an instance of Collection.
 */
Fetcher.prototype.getCollectionForSpec = function(spec, models, options, callback) {
  var collectionOptions = this.buildOptions(options, _.extend({params: spec.params}, spec.params));
  models = models || [];
  return this.modelUtils.getCollection(spec.collection, models, collectionOptions, callback);
};

/**
 * Returns an instance of Model.
 */
Fetcher.prototype.getModelForSpec = function(spec, attributes, options, callback) {
  var modelOptions = this.buildOptions(options);

  attributes = attributes || {};
  _.defaults(attributes, spec.params);

  return this.modelUtils.getModel(spec.model, attributes, modelOptions, callback);
};

/**
 * map fetchSpecs to models and fetch data in parallel
 */
Fetcher.prototype._retrieve = function(fetchSpecs, options, callback) {
  var batchedRequests = {};

  _.each(fetchSpecs, function(spec, name) {
    batchedRequests[name] = function(cb) {
      var model;

      var readFromCache = options.readFromCache;

      // If present, the individual spec can overwrite the fetchSpec.
      if (!_.isUndefined(spec.readFromCache) && !_.isNull(spec.readFromCache)) {
        readFromCache = spec.readFromCache;
      }

      if (!readFromCache) {
        this.fetchFromApi(spec, options, cb);
      } else {
        model = null;

        // First, see if we have stored the model or collection.
        if (spec.model != null) {

          this._retrieveModel(spec, function(err, model) {
            this._refreshData(spec, model, options, cb);
          }.bind(this));

        } else if (spec.collection != null) {
          this.collectionStore.get(spec.collection, spec.params, function(collection) {
            this._refreshData(spec, collection, options, cb);
          }.bind(this));

        }

      }
    }.bind(this);
  }, this);
  async.parallel(batchedRequests, callback);
};

Fetcher.prototype._refreshData = function(spec, modelOrCollection, options, cb) {

  // If we found the model/collection in the store, then return that.
  if (!this.needsFetch(modelOrCollection, spec)) {
    cb(null, modelOrCollection);
  } else {
    /**
     * Else, fetch anew.
     */
    this.fetchFromApi(spec, options, cb);
  }
}

Fetcher.prototype._retrieveModel = function(spec, callback) {
  var fetcher = this;

  // Attempt to fetch from the modelStore based on the idAttribute
  this.modelUtils.modelIdAttribute(spec.model, function(idAttribute) {
    var model = fetcher.modelStore.get(spec.model, spec.params[idAttribute]);
    if (model) return callback(null, model);

    // if there are no other keys than the id in the params, return null;
    if (_.isEmpty(_.omit(spec.params, idAttribute)))
      return callback(null, null);

    // Attempt to fetch the model in the modelStore based on the other params
    return callback(null, fetcher.modelStore.find(spec.model, spec.params));
  });
};

Fetcher.prototype.needsFetch = function(modelOrCollection, spec) {
  if (modelOrCollection == null) return true;

  if (this.modelUtils.isModel(modelOrCollection) && this.isMissingKeys(modelOrCollection.attributes, spec.ensureKeys)) {
    return true;
  }

  if (spec.needsFetch === true) return true;
  if (typeof spec.needsFetch === 'function' && spec.needsFetch(modelOrCollection)) return true;
  return false;
};

Fetcher.prototype.isMissingKeys = function(modelData, keys) {
  var key;

  if (keys == null) {
    return false;
  }

  if (!_.isArray(keys)) {
    keys = [keys];
  }

  for (var i = 0, len = keys.length; i < len; i++) {
    key = keys[i];
    if (modelData[key] == null) {
      return true;
    }
  }
  return false;
};

Fetcher.prototype.fetchFromApi = function(spec, options, callback) {
  var model = this.getModelOrCollectionForSpec(spec, null, options),
      fetcher = this;

  model.fetch({
    headers: options.headers || {},
    timeout: options.timeout || 0,
    data: spec.params,
    success: function(model, body) {
      callback(null, model);
    },
    error: function(model, resp, options) {
      var body, respOutput, err;

      body = resp.body;
      resp.body = typeof body === 'string' ? body.slice(0, 150) : body;
      respOutput = JSON.stringify(resp);
      err = new Error("ERROR fetching model '" + fetcher.modelUtils.modelName(model.constructor) + "' with options '" + JSON.stringify(options) + "'. Response: " + respOutput);
      err.status = resp.status;
      err.body = body;
      callback(err);
    }
  });
};

Fetcher.prototype.retrieveModelsForCollectionName = function(collectionName, modelIds) {
  var modelName = this.modelUtils.getModelNameForCollectionName(collectionName);
  return this.retrieveModels(modelName, modelIds);
};

Fetcher.prototype.retrieveModels = function(modelName, modelIds) {
  return modelIds.map(function(id) {
    return this.modelStore.get(modelName, id);
  }, this);
};

Fetcher.prototype.summarize = function(modelOrCollection) {
  var summary = {},
      idAttribute;

  if (this.modelUtils.isCollection(modelOrCollection)) {
    idAttribute = modelOrCollection.model.prototype.idAttribute;
    summary = {
      collection: this.modelUtils.modelName(modelOrCollection.constructor),
      ids: modelOrCollection.pluck(idAttribute),
      params: modelOrCollection.params,
      meta: modelOrCollection.meta
    };
  } else if (this.modelUtils.isModel(modelOrCollection)) {
    summary = {
      model: this.modelUtils.modelName(modelOrCollection.constructor),
      id: modelOrCollection.id
    };
  }
  return summary;
};

Fetcher.prototype.storeResults = function(results) {
  _.each(results, function(modelOrCollection) {
    modelOrCollection.store();
  });
};

Fetcher.prototype.bootstrapData = function(modelMap, callback) {
  var results = {},
      fetcher = this;

  async.forEach(_.keys(modelMap), function(name, cb) {
    var map = modelMap[name];
    fetcher.getModelOrCollectionForSpec(map.summary, map.data, _.pick(map.summary, 'params', 'meta'), function(modelOrCollection) {
      results[name] = modelOrCollection;
      cb(null);
    });
  }, function(err) {
    if (_.isFunction(callback)) {
      callback(results);
    }
  });
};

Fetcher.prototype.hydrate = function(summaries, options, callback) {
  var results = {},
      fetcher = this;

  /**
   * Support both (summaries, options, callback)
   * and (summaries, callback).
   */
  if (arguments.length === 2) {
    callback = options;
    options = {};
  } else {
    options = options || {};
  }

  async.forEach(_.keys(summaries), function(name, cb) {
    var summary = summaries[name];
    if (summary.model != null) {
      results[name] = fetcher.modelStore.get(summary.model, summary.id);

      if ((results[name] != null) && (options.app != null)) {
        results[name].app = options.app;
      }

      cb(null);

    } else if (summary.collection != null) {
      // Also support getting all models for a collection.
      fetcher.collectionStore.get(summary.collection, summary.params, function(collection) {
        if (collection == null) {
          throw new Error("Collection of type \"" + summary.collection + "\" not found for params: " + JSON.stringify(summary.params));
        }

        results[name] = collection;

        cb(null);
      });
    }
  }, function(err) {
    callback(err, results);
  });
};

Fetcher.prototype.pendingFetches = 0;

Fetcher.prototype.fetch = function(fetchSpecs, options, callback) {
  var fetcher = this;

  /**
   * Support both (fetchSpecs, options, callback)
   * and (fetchSpecs, callback).
   */
  if (arguments.length === 2) {
    callback = options;
    options = {};
  } else {
    options = options || {};
  }

  // Different defaults for client v server.
  if (isServer) {
    if (options.readFromCache == null) {
      options.readFromCache = false;
    }
    if (options.writeToCache == null) {
      options.writeToCache = false;
    }
  } else {
    if (options.readFromCache == null) {
      options.readFromCache = true;
    }
    if (options.writeToCache == null) {
      options.writeToCache = true;
    }
  }

  this.pendingFetches++;
  this.trigger('fetch:start', fetchSpecs);
  this._retrieve(fetchSpecs, options, function(err, results) {
    fetcher.pendingFetches--;
    fetcher.trigger('fetch:end', fetchSpecs, err, results);
    if (err) return callback(err);
    if (options.writeToCache) {
      fetcher.storeResults(results);
    }
    callback(null, results);
  });
};

// Mixin Backbone.Events for events that work in client & server.
_.extend(Fetcher.prototype, Backbone.Events);
<|endoftext|>


<commit_before>import EventEmitter from 'eventemitter3';

describe('(service) AsciiConverter', () => {
  let ascii, Ascii, workerStub, postMessageStub;

  beforeEach(() => {
    postMessageStub = sinon.spy();

    class Worker extends EventEmitter {
      postMessage = postMessageStub;
    }
    workerStub = Worker;

    let inject = require('inject!services/AsciiConverter');
    Ascii = inject({
      'worker-loader!./asciiWorker': workerStub
    }).default;

    ascii = new Ascii();
  });

  it('should have .toAscii method', () => {
    expect(ascii.toAscii).to.be.a('function');
  });

  it('should send data to worker', () => {
    ascii.toAscii('hello');

    expect(postMessageStub).to.have.been.calledWith(['hello']);
  });

  describe('onmessage', () => {
    it('should handle progress events from worker', () => {
      ascii.emit = sinon.spy();
      ascii.toAscii('hello');

      ascii.worker.onmessage({
        data: {
          type: 'progress',
          value: 'hello'
        }
      });

      expect(ascii.emit).to.have.been.calledWith('progress', 'hello');
    });

    it('should handle result events from worker', () => {
      ascii.emit = sinon.spy();
      ascii.toAscii('hello');

      ascii.worker.onmessage({
        data: {
          type: 'result',
          value: 'world'
        }
      });

      expect(ascii.emit).to.have.been.calledWith('result', 'world');
    });
  });
});
<commit_msg>Fixing test<commit_after>import EventEmitter from 'eventemitter3';

describe('(service) AsciiConverter', () => {
  let ascii, Ascii, workerStub, postMessageStub;

  beforeEach(() => {
    postMessageStub = sinon.spy();

    class Worker extends EventEmitter {
      postMessage = postMessageStub;
    }
    workerStub = Worker;

    let inject = require('inject!services/asciiConverter');
    Ascii = inject({
      'worker-loader!./asciiWorker': workerStub
    }).default;

    ascii = new Ascii();
  });

  it('should have .toAscii method', () => {
    expect(ascii.toAscii).to.be.a('function');
  });

  it('should send data to worker', () => {
    ascii.toAscii('hello');

    expect(postMessageStub).to.have.been.calledWith(['hello']);
  });

  describe('onmessage', () => {
    it('should handle progress events from worker', () => {
      ascii.emit = sinon.spy();
      ascii.toAscii('hello');

      ascii.worker.onmessage({
        data: {
          type: 'progress',
          value: 'hello'
        }
      });

      expect(ascii.emit).to.have.been.calledWith('progress', 'hello');
    });

    it('should handle result events from worker', () => {
      ascii.emit = sinon.spy();
      ascii.toAscii('hello');

      ascii.worker.onmessage({
        data: {
          type: 'result',
          value: 'world'
        }
      });

      expect(ascii.emit).to.have.been.calledWith('result', 'world');
    });
  });
});
<|endoftext|>


<commit_before>/** @namespace H5PUpgrades */
var H5PUpgrades = H5PUpgrades || {};

H5PUpgrades['H5P.Agamotto'] = (function ($) {
  return {
    1: {
      3: function (parameters, finished) {
        // Update image items
        parameters.items = parameters.items.map( function (item) {
          // Create new image structure
          var newImage = {
            library: 'H5P.Image 1.0',
            // We avoid using H5P.createUUID since this is an upgrade script and H5P function may change
            subContentId: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) {
              var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
              return newChar.toString(16);
            }),
            params: {
              alt: item.labelText || '',
              contentName: 'Image',
              title: item.labelText || '',
              file: item.image
            }
          };

          // Compose new item
          item = {
            description: item.description,
            image: newImage,
            labelText: item.labelText
          };

          return item;
        });

        finished(null, parameters);
      }
    }
  };
})(H5P.jQuery);
<commit_msg>Fix upgrade script: Check for empty items<commit_after>/** @namespace H5PUpgrades */
var H5PUpgrades = H5PUpgrades || {};

H5PUpgrades['H5P.Agamotto'] = (function ($) {
  return {
    1: {
      3: function (parameters, finished) {
        // Update image items
        if (parameters.items) {
          parameters.items = parameters.items.map( function (item) {
            // Create new image structure
            var newImage = {
              library: 'H5P.Image 1.0',
              // We avoid using H5P.createUUID since this is an upgrade script and H5P function may change
              subContentId: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) {
                var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
                return newChar.toString(16);
              }),
              params: {
                alt: item.labelText || '',
                contentName: 'Image',
                title: item.labelText || '',
                file: item.image
              }
            };

            // Compose new item
            item = {
              description: item.description,
              image: newImage,
              labelText: item.labelText
            };

            return item;
          });
        }

        finished(null, parameters);
      }
    }
  };
})(H5P.jQuery);
<|endoftext|>


<commit_before>/**
 * @fileOverview
 *
 * 保存文件到网盘的功能
 *
 * @author: techird
 * @copyright: Baidu FEX, 2014
 */
KityMinder.registerUI('menu/save/netdisk', function(minder) {
    var $menu = minder.getUI('menu/menu');
    var $save = minder.getUI('menu/save/save');
    var $netdiskfinder = minder.getUI('widget/netdiskfinder');
    var $eve = minder.getUI('eve');
    var $doc = minder.getUI('doc');
    var ret = $eve.setup({});
    var notice = minder.getUI('widget/notice');

    /* extension => protocol */
    var supports = {};

    minder.getSupportedProtocols().forEach(function(protocol) {
        if (protocol.encode && protocol.decode) {
            supports[protocol.fileExtension] = protocol;
        }
    });

    /* 网盘面板 */
    var $panel = $($save.createSub('netdisk', true)).addClass('netdisk-save-panel');

    var $finder = $netdiskfinder.generate($panel, function(file) {
        return supports[file.extension];
    });

    var $selects = $('

')
        .appendTo($panel);

    $('')
        .text(minder.getLang('ui.saveas'))
        .appendTo($selects);

    /* 文件名 */
    var $filename = $('<input>')
        .addClass('fui-widget fui-selectable')
        .attr('type', 'text')
        .attr('placeholder', minder.getLang('ui.filename'))
        .attr('title', minder.getLang('ui.filename'))
        .on('keydown', function(e) {
            if (e.keyCode == 27) $menu.toggle();
            if (e.keyCode == 13) save();
        })
        .appendTo($selects);

    /* 文件格式 */
    var $format = $('<select>')
        .attr('title', minder.getLang('ui.fileformat'))
        .appendTo($selects);

    for (var ext in supports) {
        var protocol = supports[ext];
        if (!protocol.encode) return;
        $('<option>')
            .text(protocol.fileDescription + '(' + protocol.fileExtension + ')')
            .val(ext)
            .appendTo($format);
    }

    $format.val('.km');

    $format.on('change', normalizeFilename);

    /* 保存按钮 */
    var $saveBtn = $('<button></button>')
        .addClass('save-button')
        .text(minder.getLang('ui.save'))
        .click(save)
        .appendTo($selects);

    $menu.on('show', setFilename);

    $finder.on('fileclick', function(file) {
        $finder.select(file.path);
        $filename.val(file.filename);
    });

    ret.quickSave = quickSave;

    window.onbeforeunload = function() {
        var noask = ret.mute || window.location.href.indexOf('noask') > 0;
        if (!$doc.checkSaved(true) && !noask)
            return minder.getLang('ui.unsavedcontent', '* ' + $doc.current().title);
    };

    var autoSaveDuration = minder.getOptions('autoSave');

    if (autoSaveDuration !== false) {
        autoSaveDuration = isNaN(autoSaveDuration) ? 3000 : (autoSaveDuration * 1000);
        autoSave();
    }

    var autoSaveTimer = 0;

    function autoSave() {
        function lazySave(doc) {
            if (doc.saved) return;
            clearTimeout(autoSaveTimer);
            autoSaveTimer = setTimeout(saveCurrent, autoSaveDuration);
        }
        $doc.on('docchange', lazySave);
    }

    // 快速保存
    function quickSave() {
        var doc = $doc.current();
        if (doc.source != 'netdisk' && !$menu.isVisible()) {
            $menu.$tabs.select(2);
            $save.$tabs.select(0);
            return $menu.show();
        } else {
            saveCurrent();
        }
    }

    function saveCurrent() {
        var doc = $doc.current();

        if (doc.source != 'netdisk' || doc.saved ) return Promise.resolve();

        var $title = minder.getUI('topbar/title').$title;
        $filename.val(doc.title);
        return doSave(doc.path, doc.protocol, doc, $title, 'leaveTheMenu');
    }

    function normalizeFilename() {
        var filename = $filename.val();
        var info = fio.file.anlysisPath(filename);
        var ext = info.extension;

        if (ext != $format.val()) {
            if (ext in supports) {
                $filename.val(info.name + $format.val());
            } else {
                $filename.val(filename + $format.val());
            }
            $filename[0].select();
        }

        return $filename.val();
    }

    function getSaveContext() {
        var filename = normalizeFilename();

        var path = $finder.pwd() + filename;
        var doc = $doc.current();
        var protocol = supports[$format.val()];

        var exist = $finder.select(path); // 目标路径存在
        var match = doc.path == path; // 目标路径正是当前文档
        var duplicated = exist && !match;

        return {
            filename: filename,
            path: path,
            doc: doc,
            protocol: protocol,
            exist: exist,
            match: match,
            duplicated: duplicated
        };
    }

    function save() {
        var ctx = getSaveContext();

        if (ctx.match || !ctx.exist || ctx.duplicated && window.confirm(minder.getLang('ui.overrideconfirm', ctx.filename))) {
            doSave(ctx.path, ctx.protocol.name, ctx.doc, $panel);
        }
    }

    var saving = 0;

    function doSave(path, protocol, doc, $mask, leaveTheMenu, msg) {

        if (saving) return;

        saving = true;

        if ($mask) $mask.addClass('loading');

        function upload(data) {
            return fio.file.write({
                path: path,
                content: data,
                ondup: fio.file.DUP_OVERWRITE
            });
        }

        function finish(file) {

            if (!file.modifyTime) throw new Error('File Save Error');

            if (!leaveTheMenu) {
                $menu.hide();
            }

            doc.path = file.path;
            doc.title = file.filename;
            doc.source = 'netdisk';
            doc.protocol = protocol;

            $doc.save(doc);

            notice.info(msg || minder.getLang('ui.save_success', doc.title, file.modifyTime.toLocaleTimeString()));

            setTimeout(function() {
                $finder.list($finder.pwd(), true);
            }, 1499);

        }

        function error(e) {
            notice.error('err_save', e);
        }

        return minder.exportData(protocol).then(upload).then(finish, error).then(function() {
            if ($mask) $mask.removeClass('loading');
            saving = false;
        });
    }

    function setFilename() {
        var doc = $doc.current();

        switch (doc.source) {
            case 'netdisk':
                setFilenameForNetDiskSource(doc);
                break;
            default:
                setFilenameForOtherSource(doc);
                break;
        }

        $filename[0].select();
    }

    function setFilenameInputValue(filename) {
        $filename.val(filename);
        normalizeFilename(filename);
    }

    function setFilenameForNetDiskSource(doc) {
        if (!fio.user.current()) return;

        var path = doc.path;
        var pathInfo = fio.file.anlysisPath(path);

        // 选中当前文件
        if ($finder.pwd() != pathInfo.parentPath) {
            $finder.list(pathInfo.parentPath).then(function() {
                $finder.select(path);
            });
        } else {
            $finder.select(path);
        }

        setFilenameInputValue(pathInfo.filename);
    }

    function setFilenameForOtherSource(doc) {
        setFilenameInputValue(doc.title);
        $finder.select(null);
    }

    return ret;
});<commit_msg>允许手动保存<commit_after>/**
 * @fileOverview
 *
 * 保存文件到网盘的功能
 *
 * @author: techird
 * @copyright: Baidu FEX, 2014
 */
KityMinder.registerUI('menu/save/netdisk', function(minder) {
    var $menu = minder.getUI('menu/menu');
    var $save = minder.getUI('menu/save/save');
    var $netdiskfinder = minder.getUI('widget/netdiskfinder');
    var $eve = minder.getUI('eve');
    var $doc = minder.getUI('doc');
    var ret = $eve.setup({});
    var notice = minder.getUI('widget/notice');

    /* extension => protocol */
    var supports = {};

    minder.getSupportedProtocols().forEach(function(protocol) {
        if (protocol.encode && protocol.decode) {
            supports[protocol.fileExtension] = protocol;
        }
    });

    /* 网盘面板 */
    var $panel = $($save.createSub('netdisk', true)).addClass('netdisk-save-panel');

    var $finder = $netdiskfinder.generate($panel, function(file) {
        return supports[file.extension];
    });

    var $selects = $('

')
        .appendTo($panel);

    $('')
        .text(minder.getLang('ui.saveas'))
        .appendTo($selects);

    /* 文件名 */
    var $filename = $('<input>')
        .addClass('fui-widget fui-selectable')
        .attr('type', 'text')
        .attr('placeholder', minder.getLang('ui.filename'))
        .attr('title', minder.getLang('ui.filename'))
        .on('keydown', function(e) {
            if (e.keyCode == 27) $menu.toggle();
            if (e.keyCode == 13) save();
        })
        .appendTo($selects);

    /* 文件格式 */
    var $format = $('<select>')
        .attr('title', minder.getLang('ui.fileformat'))
        .appendTo($selects);

    for (var ext in supports) {
        var protocol = supports[ext];
        if (!protocol.encode) return;
        $('<option>')
            .text(protocol.fileDescription + '(' + protocol.fileExtension + ')')
            .val(ext)
            .appendTo($format);
    }

    $format.val('.km');

    $format.on('change', normalizeFilename);

    /* 保存按钮 */
    var $saveBtn = $('<button></button>')
        .addClass('save-button')
        .text(minder.getLang('ui.save'))
        .click(save)
        .appendTo($selects);

    $menu.on('show', setFilename);

    $finder.on('fileclick', function(file) {
        $finder.select(file.path);
        $filename.val(file.filename);
    });

    ret.quickSave = quickSave;

    window.onbeforeunload = function() {
        var noask = ret.mute || window.location.href.indexOf('noask') > 0;
        if (!$doc.checkSaved(true) && !noask)
            return minder.getLang('ui.unsavedcontent', '* ' + $doc.current().title);
    };

    var autoSaveDuration = minder.getOptions('autoSave');

    if (autoSaveDuration !== false) {
        autoSaveDuration = isNaN(autoSaveDuration) ? 3000 : (autoSaveDuration * 1000);
        autoSave();
    }

    var autoSaveTimer = 0;

    function autoSave() {
        function lazySave(doc) {
            if (doc.saved) return;
            clearTimeout(autoSaveTimer);
            autoSaveTimer = setTimeout(saveCurrent, autoSaveDuration);
        }
        $doc.on('docchange', lazySave);
    }

    // 快速保存
    function quickSave() {
        var doc = $doc.current();
        if (doc.source != 'netdisk' && !$menu.isVisible()) {
            $menu.$tabs.select(2);
            $save.$tabs.select(0);
            return $menu.show();
        } else {
            saveCurrent();
        }
    }

    function saveCurrent() {
        var doc = $doc.current();

        if (doc.source != 'netdisk') return Promise.resolve();

        var $title = minder.getUI('topbar/title').$title;
        $filename.val(doc.title);
        return doSave(doc.path, doc.protocol, doc, $title, 'leaveTheMenu');
    }

    function normalizeFilename() {
        var filename = $filename.val();
        var info = fio.file.anlysisPath(filename);
        var ext = info.extension;

        if (ext != $format.val()) {
            if (ext in supports) {
                $filename.val(info.name + $format.val());
            } else {
                $filename.val(filename + $format.val());
            }
            $filename[0].select();
        }

        return $filename.val();
    }

    function getSaveContext() {
        var filename = normalizeFilename();

        var path = $finder.pwd() + filename;
        var doc = $doc.current();
        var protocol = supports[$format.val()];

        var exist = $finder.select(path); // 目标路径存在
        var match = doc.path == path; // 目标路径正是当前文档
        var duplicated = exist && !match;

        return {
            filename: filename,
            path: path,
            doc: doc,
            protocol: protocol,
            exist: exist,
            match: match,
            duplicated: duplicated
        };
    }

    function save() {
        var ctx = getSaveContext();

        if (ctx.match || !ctx.exist || ctx.duplicated && window.confirm(minder.getLang('ui.overrideconfirm', ctx.filename))) {
            doSave(ctx.path, ctx.protocol.name, ctx.doc, $panel);
        }
    }

    var saving = 0;

    function doSave(path, protocol, doc, $mask, leaveTheMenu, msg) {

        if (saving) return;

        saving = true;

        if ($mask) $mask.addClass('loading');

        function upload(data) {
            return fio.file.write({
                path: path,
                content: data,
                ondup: fio.file.DUP_OVERWRITE
            });
        }

        function finish(file) {

            if (!file.modifyTime) throw new Error('File Save Error');

            if (!leaveTheMenu) {
                $menu.hide();
            }

            doc.path = file.path;
            doc.title = file.filename;
            doc.source = 'netdisk';
            doc.protocol = protocol;

            $doc.save(doc);

            notice.info(msg || minder.getLang('ui.save_success', doc.title, file.modifyTime.toLocaleTimeString()));

            setTimeout(function() {
                $finder.list($finder.pwd(), true);
            }, 1499);

        }

        function error(e) {
            notice.error('err_save', e);
        }

        return minder.exportData(protocol).then(upload).then(finish, error).then(function() {
            if ($mask) $mask.removeClass('loading');
            saving = false;
        });
    }

    function setFilename() {
        var doc = $doc.current();

        switch (doc.source) {
            case 'netdisk':
                setFilenameForNetDiskSource(doc);
                break;
            default:
                setFilenameForOtherSource(doc);
                break;
        }

        $filename[0].select();
    }

    function setFilenameInputValue(filename) {
        $filename.val(filename);
        normalizeFilename(filename);
    }

    function setFilenameForNetDiskSource(doc) {
        if (!fio.user.current()) return;

        var path = doc.path;
        var pathInfo = fio.file.anlysisPath(path);

        // 选中当前文件
        if ($finder.pwd() != pathInfo.parentPath) {
            $finder.list(pathInfo.parentPath).then(function() {
                $finder.select(path);
            });
        } else {
            $finder.select(path);
        }

        setFilenameInputValue(pathInfo.filename);
    }

    function setFilenameForOtherSource(doc) {
        setFilenameInputValue(doc.title);
        $finder.select(null);
    }

    return ret;
});<|endoftext|>


<commit_before>/** @namespace H5PUpgrades */
var H5PUpgrades = H5PUpgrades || {};

H5PUpgrades['H5P.InteractiveVideo'] = (function ($) {
  return {
    1: {
      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.1.
       *
       * Moves interactions into an assets container to be able to add more
       * properties to the video, i.e. a bookmark list
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      1: function (parameters, finished) {

          // Move interactions into assets container
          parameters.interactiveVideo.assets = {
            interactions: parameters.interactiveVideo.interactions,
            bookmarks: []
          };
          delete parameters.interactiveVideo.interactions;

          // Done
          finished(null, parameters);
      },

      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.3.
       *
       * Wraps summary in a container to be able to add IV specific options
       * to the summary task, i.e. when to display the task.
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      3: function (parameters, finished) {

        // Move summary task into container
        parameters.interactiveVideo.summary = {
          task: parameters.interactiveVideo.summary,
          displayAt: 3
        };

        // Done
        finished(null, parameters);
      },

      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.5.
       *
       * Adds unique identifiers to sub content?
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      5: function (parameters, finished) {
        if (parameters.interactiveVideo && parameters.interactiveVideo.assets && parameters.interactiveVideo.assets.interactions) {
          var interactions = parameters.interactiveVideo.assets.interactions;
          for (var i = 0; i < interactions.length; i++) {
            if (interactions[i].action && interactions[i].action.subContentId === undefined) {
              // NOTE: We avoid using H5P.createUUID since this is an upgrade script and H5P function may change in the
              // future
              interactions[i].action.subContentId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) {
                var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
                return newChar.toString(16);
              });
            }
          }
        }
        finished(null, parameters);
      },

      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.7.
       *
       * Groups all UI text strings to make them eaiser to translate and handle.
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      7: function (parameters, finished) {
        parameters.l10n = {};

        var keys = ['interaction', 'play', 'pause', 'mute', 'quality', 'unmute', 'fullscreen', 'exitFullscreen', 'summary', 'bookmarks', 'defaultAdaptivitySeekLabel'];
        for (var i = 0; i < keys.length; i++) {
          var key = keys[i];
          if (parameters.hasOwnProperty(key)) {
            parameters.l10n[key] = parameters[key];
            delete parameters[key];
          }
        }

        /* Move displayAsButton to displayType  */
        if (parameters.interactiveVideo && parameters.interactiveVideo.assets && parameters.interactiveVideo.assets.interactions) {
          var interactions = parameters.interactiveVideo.assets.interactions;
          for (var i = 0; i < interactions.length; i++) {
            interactions[i].displayType = (interactions[i].displayAsButton === undefined || interactions[i].displayAsButton) ? 'button' : 'poster';
            delete interactions[i].displayAsButton;
          }
        }

        finished(null, parameters);
      },

      /**
       * Upgrade links to always be displayed as poster
       */
      8: function (parameters, finished) {
        console.log("what is parameters ?");
        console.log(parameters);

        if (parameters.interactiveVideo && parameters.interactiveVideo.assets && parameters.interactiveVideo.assets.interactions) {
          var interactions = parameters.interactiveVideo.assets.interactions;
          var i;
          for (i = 0; i < interactions.length; i++) {
            var interaction = interactions[i];

            // Set links displayType to poster
            if (interaction.action && interaction.action.library && interaction.action.library.split(' ')[0] === "H5P.Link") {
              interaction.displayType = "poster";
            }
          }
        }

        finished(null, parameters);
      }
    }
  };
})(H5P.jQuery);
<commit_msg>Removed debugging [HFJ-908]<commit_after>/** @namespace H5PUpgrades */
var H5PUpgrades = H5PUpgrades || {};

H5PUpgrades['H5P.InteractiveVideo'] = (function ($) {
  return {
    1: {
      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.1.
       *
       * Moves interactions into an assets container to be able to add more
       * properties to the video, i.e. a bookmark list
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      1: function (parameters, finished) {

          // Move interactions into assets container
          parameters.interactiveVideo.assets = {
            interactions: parameters.interactiveVideo.interactions,
            bookmarks: []
          };
          delete parameters.interactiveVideo.interactions;

          // Done
          finished(null, parameters);
      },

      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.3.
       *
       * Wraps summary in a container to be able to add IV specific options
       * to the summary task, i.e. when to display the task.
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      3: function (parameters, finished) {

        // Move summary task into container
        parameters.interactiveVideo.summary = {
          task: parameters.interactiveVideo.summary,
          displayAt: 3
        };

        // Done
        finished(null, parameters);
      },

      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.5.
       *
       * Adds unique identifiers to sub content?
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      5: function (parameters, finished) {
        if (parameters.interactiveVideo && parameters.interactiveVideo.assets && parameters.interactiveVideo.assets.interactions) {
          var interactions = parameters.interactiveVideo.assets.interactions;
          for (var i = 0; i < interactions.length; i++) {
            if (interactions[i].action && interactions[i].action.subContentId === undefined) {
              // NOTE: We avoid using H5P.createUUID since this is an upgrade script and H5P function may change in the
              // future
              interactions[i].action.subContentId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(char) {
                var random = Math.random()*16|0, newChar = char === 'x' ? random : (random&0x3|0x8);
                return newChar.toString(16);
              });
            }
          }
        }
        finished(null, parameters);
      },

      /**
       * Asynchronous content upgrade hook.
       * Upgrades content parameters to support IV 1.7.
       *
       * Groups all UI text strings to make them eaiser to translate and handle.
       *
       * @params {Object} parameters
       * @params {function} finished
       */
      7: function (parameters, finished) {
        parameters.l10n = {};

        var keys = ['interaction', 'play', 'pause', 'mute', 'quality', 'unmute', 'fullscreen', 'exitFullscreen', 'summary', 'bookmarks', 'defaultAdaptivitySeekLabel'];
        for (var i = 0; i < keys.length; i++) {
          var key = keys[i];
          if (parameters.hasOwnProperty(key)) {
            parameters.l10n[key] = parameters[key];
            delete parameters[key];
          }
        }

        /* Move displayAsButton to displayType  */
        if (parameters.interactiveVideo && parameters.interactiveVideo.assets && parameters.interactiveVideo.assets.interactions) {
          var interactions = parameters.interactiveVideo.assets.interactions;
          for (var i = 0; i < interactions.length; i++) {
            interactions[i].displayType = (interactions[i].displayAsButton === undefined || interactions[i].displayAsButton) ? 'button' : 'poster';
            delete interactions[i].displayAsButton;
          }
        }

        finished(null, parameters);
      },

      /**
       * Upgrade links to always be displayed as poster
       */
      8: function (parameters, finished) {
        if (parameters.interactiveVideo && parameters.interactiveVideo.assets && parameters.interactiveVideo.assets.interactions) {
          var interactions = parameters.interactiveVideo.assets.interactions;
          var i;
          for (i = 0; i < interactions.length; i++) {
            var interaction = interactions[i];

            // Set links displayType to poster
            if (interaction.action && interaction.action.library && interaction.action.library.split(' ')[0] === "H5P.Link") {
              interaction.displayType = "poster";
            }
          }
        }

        finished(null, parameters);
      }
    }
  };
})(H5P.jQuery);
<|endoftext|>