<commit_before>/**
 * Takes a command line and converts it to something that can
 * be evaluated on the host side.
 *
 * Host commands are defined by creating a method called
 * hostcommand_<command name here>. The command method has the following
 * signature
 *
 * void hostcommand(object token, object[] tokenlist)
 *
 * The first argument contains a token object with keys for
 * "type" and "value". The type is (FIXME: int/enum or string tbd)
 *
 * Value contains the string representation of the token. The token
 * objects are passed by reference, so any changes made to them are
 * propagated back when the string representation of the command line
 * is generated.
 *
 * The "tokenlist" argument contains the entire list of tokens generated
 * from parsing the command line. This array is also passed by reference,
 * so any changes will be seen by all host command methods.
 *
 * Host command methods are expected to directly manipulate the token,
 * and/or the token list. A command handler may ask for the entire
 * token list to be re-processed, by returning true. This may be
 * useful if a handler inserts something that is itself a host command.
 *
 */
function HostCommandTransformer() {
  this.parser = null;
  this.commandTable = {};

  //local copy of token types, local vars have better performance. :
  const
  WHITESPACE = window.cls.SimpleJSParser.WHITESPACE,
  LINETERMINATOR = window.cls.SimpleJSParser.LINETERMINATOR,
  IDENTIFIER = window.cls.SimpleJSParser.IDENTIFIER,
  NUMBER = window.cls.SimpleJSParser.NUMBER,
  STRING = window.cls.SimpleJSParser.STRING,
  PUNCTUATOR = window.cls.SimpleJSParser.PUNCTUATOR,
  DIV_PUNCTUATOR = window.cls.SimpleJSParser.DIV_PUNCTUATOR,
  REG_EXP = window.cls.SimpleJSParser.REG_EXP,
  COMMENT = window.cls.SimpleJSParser.COMMENT;

  this.init = function() {
    // window.simple_js_parser is default location for the parser instance
    // in dragonfly. Use it if it exists.
    this.parser = window.simple_js_parser || new window.cls.SimpleJSParser();

    for (name in this) {
      var commandName = name.split("hostcommand_")[1];

      if (commandName) {
        this.commandTable[commandName] = this[name];
      }
    }
  };

  this.transform = function(source) {
    var types = [];
    var values = [];
    var tokens = [];
    this.parser.parse(source, values, types);

    // make a more straightforward representation of tokens. Command line
    // stuff is small, so the cost of this doesn't matter much.
    tokens = this.zip_tokens(types, values);

    dirty: // we jump back here if we need to re-process all tokens
    for (var n=0, token; token=tokens[n]; n++) {
      if (token.type == IDENTIFIER && token.value in this.commandTable) {
        var fun = this.commandTable[token.value];
        if (fun.call(this, token, tokens)) {
          break dirty;
        }
      }
    }
    return tokens.map(function(e) {return e.value;}).join("");
  };

  this.zip_tokens = function(types, values) {
    var tokens = [];
    for (var n=0; n<types.length; n++) {
      tokens.push({type: types[n], value: values[n]});
    }
    return tokens;
  };

  /**
   * Check if the token at index looks like it's a function/method being
   * called by looking at the following tokens.
   */
  this.is_call = function(tokens, index) {
    if (!tokens[index] || tokens[index].type != IDENTIFIER) {
      return false;
    }

    for (var n=index+1, token; token=tokens[n]; n++) {
      switch (token.type) {
        case WHITESPACE:
          continue;
        case PUNCTUATOR:
        if (token.value == "(") { return true; }
        default:
          return false;
      }
    }
    return false;
  };

  /**
   * Check if the token at index is a property of name
   */
  this.is_property_of = function(name, tokens, index) {
    if (!tokens[index] || tokens[index].type != IDENTIFIER) {
      return false;
    }

    var n = index-1;

    for (var token; token=tokens[n]; n--) {
      switch (token.type) {
        case WHITESPACE:
          continue;
        case PUNCTUATOR:
          if (token.value == ".") { break; }
        default:
          return false;
      }
    }

    for (var token; token=tokens[n]; n--) {
      switch (token.type) {
        case WHITESPACE:
          continue;
        case IDENTIFIER:
          if (token.value == name) { return true; }
        default:
          return false;
      }
    }

    return false;
  };

  // Host commands:

  this.hostcommand_dir = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);
    if (!this.is_call(tokenlist, index) ||
         this.is_property_of("console", tokenlist, index)) {
      return;
    }

    token.value = "console.dir";
  };

  this.hostcommand_dirxml = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);

    if (!this.is_call(tokenlist, index) ||
         this.is_property_of("console", tokenlist, index)) {
      return;
    }

    token.value = "console.dirxml";
  };

  this.hostcommand_$ = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);

    if (this.is_call(tokenlist, index)) {
      token.value = "document.getElementById";
    }
  };

  this.hostcommand_$$ = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);

    if (this.is_call(tokenlist, index)) {
      token.value = "document.querySelectorAll";
    }
  };

  this.hostcommand_$x = function(token, tokenlist) {
    //xpath thingy
  };

  this.hostcommand_keys = function(token, tokenlist) {
    var funstr = "function(o) {var arr=[]; for (key in o) {arr.push(key)}; return arr}";
    token.value = funstr;
  };

  this.hostcommand_values = function(token, tokenlist) {
    var funstr = "function(o) {var arr=[]; for (key in o) {arr.push(o[key])}; return arr}";
    token.value = funstr;
  };

  this.init();
}
<commit_msg>Fixed keys and values functions in command line api<commit_after>/**
 * Takes a command line and converts it to something that can
 * be evaluated on the host side.
 *
 * Host commands are defined by creating a method called
 * hostcommand_<command name here>. The command method has the following
 * signature
 *
 * void hostcommand(object token, object[] tokenlist)
 *
 * The first argument contains a token object with keys for
 * "type" and "value". The type is (FIXME: int/enum or string tbd)
 *
 * Value contains the string representation of the token. The token
 * objects are passed by reference, so any changes made to them are
 * propagated back when the string representation of the command line
 * is generated.
 *
 * The "tokenlist" argument contains the entire list of tokens generated
 * from parsing the command line. This array is also passed by reference,
 * so any changes will be seen by all host command methods.
 *
 * Host command methods are expected to directly manipulate the token,
 * and/or the token list. A command handler may ask for the entire
 * token list to be re-processed, by returning true. This may be
 * useful if a handler inserts something that is itself a host command.
 *
 */
function HostCommandTransformer() {
  this.parser = null;
  this.commandTable = {};

  //local copy of token types, local vars have better performance. :
  const
  WHITESPACE = window.cls.SimpleJSParser.WHITESPACE,
  LINETERMINATOR = window.cls.SimpleJSParser.LINETERMINATOR,
  IDENTIFIER = window.cls.SimpleJSParser.IDENTIFIER,
  NUMBER = window.cls.SimpleJSParser.NUMBER,
  STRING = window.cls.SimpleJSParser.STRING,
  PUNCTUATOR = window.cls.SimpleJSParser.PUNCTUATOR,
  DIV_PUNCTUATOR = window.cls.SimpleJSParser.DIV_PUNCTUATOR,
  REG_EXP = window.cls.SimpleJSParser.REG_EXP,
  COMMENT = window.cls.SimpleJSParser.COMMENT;

  this.init = function() {
    // window.simple_js_parser is default location for the parser instance
    // in dragonfly. Use it if it exists.
    this.parser = window.simple_js_parser || new window.cls.SimpleJSParser();

    for (name in this) {
      var commandName = name.split("hostcommand_")[1];

      if (commandName) {
        this.commandTable[commandName] = this[name];
      }
    }
  };

  this.transform = function(source) {
    var types = [];
    var values = [];
    var tokens = [];
    this.parser.parse(source, values, types);

    // make a more straightforward representation of tokens. Command line
    // stuff is small, so the cost of this doesn't matter much.
    tokens = this.zip_tokens(types, values);

    dirty: // we jump back here if we need to re-process all tokens
    for (var n=0, token; token=tokens[n]; n++) {
      if (token.type == IDENTIFIER && token.value in this.commandTable) {
        var fun = this.commandTable[token.value];
        if (fun.call(this, token, tokens)) {
          break dirty;
        }
      }
    }
    return tokens.map(function(e) {return e.value;}).join("");
  };

  this.zip_tokens = function(types, values) {
    var tokens = [];
    for (var n=0; n<types.length; n++) {
      tokens.push({type: types[n], value: values[n]});
    }
    return tokens;
  };

  /**
   * Check if the token at index looks like it's a function/method being
   * called by looking at the following tokens.
   */
  this.is_call = function(tokens, index) {
    if (!tokens[index] || tokens[index].type != IDENTIFIER) {
      return false;
    }

    for (var n=index+1, token; token=tokens[n]; n++) {
      switch (token.type) {
        case WHITESPACE:
          continue;
        case PUNCTUATOR:
        if (token.value == "(") { return true; }
        default:
          return false;
      }
    }
    return false;
  };

  /**
   * Check if the token at index is a property of name
   */
  this.is_property_of = function(name, tokens, index) {
    if (!tokens[index] || tokens[index].type != IDENTIFIER) {
      return false;
    }

    var n = index-1;

    for (var token; token=tokens[n]; n--) {
      switch (token.type) {
        case WHITESPACE:
          continue;
        case PUNCTUATOR:
          if (token.value == ".") { break; }
        default:
          return false;
      }
    }

    for (var token; token=tokens[n]; n--) {
      switch (token.type) {
        case WHITESPACE:
          continue;
        case IDENTIFIER:
          if (token.value == name) { return true; }
        default:
          return false;
      }
    }

    return false;
  };

  // Host commands:

  this.hostcommand_dir = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);
    if (!this.is_call(tokenlist, index) ||
         this.is_property_of("console", tokenlist, index)) {
      return;
    }

    token.value = "console.dir";
  };

  this.hostcommand_dirxml = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);

    if (!this.is_call(tokenlist, index) ||
         this.is_property_of("console", tokenlist, index)) {
      return;
    }

    token.value = "console.dirxml";
  };

  this.hostcommand_$ = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);

    if (this.is_call(tokenlist, index)) {
      token.value = "document.getElementById";
    }
  };

  this.hostcommand_$$ = function(token, tokenlist) {
    var index = tokenlist.indexOf(token);

    if (this.is_call(tokenlist, index)) {
      token.value = "document.querySelectorAll";
    }
  };

  this.hostcommand_$x = function(token, tokenlist) {
    //xpath thingy
  };

  this.hostcommand_keys = function(token, tokenlist) {
    var funstr = "(function(o) {var arr=[]; for (key in o) {arr.push(key)}; return arr})";
    token.value = funstr;
  };

  this.hostcommand_values = function(token, tokenlist) {
    var funstr = "(function(o) {var arr=[]; for (key in o) {arr.push(o[key])}; return arr})";
    token.value = funstr;
  };

  this.init();
}
<|endoftext|>


<commit_before>/*
    The Conflict of Interest (COI) module of Kuali Research
    Copyright © 2005-2016 Kuali, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

import {camelizeJson, snakeizeJson} from './json-utils';
import {
  populateTemplateData,
  handleTemplates
} from '../services/notification-service/notification-service';
import { NOTIFICATIONS_MODE, LANES } from '../../coi-constants';
import Log from '../log';

const cachedConfigs = {};

let getNotificationsInfo;
let lane;
try {
  const extensions = require('research-extensions').default;
  getNotificationsInfo = extensions.getNotificationsInfo;
  lane = extensions.config.lane;
}
catch (err) {
  if (err.code !== 'MODULE_NOT_FOUND') {
    Log.error(err);
  }
  lane = process.env.LANE || LANES.PRODUCTION;
  getNotificationsInfo = () => {
    return {
      notificationsMode: (
        process.env.NOTIFICATIONS_MODE || NOTIFICATIONS_MODE.OFF
      )
    };
  };
}

const SCREENING_QUESTIONNAIRE_TYPE_CD = 1;
const FIN_ENTITY_QUESTIONNAIRE_TYPE_CD = 2;

async function createDeleteQueries(knex, collection, tableProps) {
  let sel = knex(tableProps.table).select(tableProps.pk);
  if (tableProps.where) {
    sel = sel.where(tableProps.where);
  }

  const results = await sel;

  await Promise.all(
    results.filter(result => {
      const match = collection.find(item => {
        return (
          item[tableProps.pk] &&
          item[tableProps.pk] === result[tableProps.pk]
        );
      });
      return !match;
    }).map(result => {
      return knex(tableProps.table)
        .update({active: false})
        .where(tableProps.pk, result[tableProps.pk]);
    })
  );
}

async function createInsertQueries(knex, collection, tableProps) {
  return await Promise.all(
    collection.map(async (line) => {
      const tmpId = line.tmpId;
      delete line.tmpId;
      delete line.tmp_id;
      line.active = true;
      if (tableProps.parent) {
        line[tableProps.parent.key] = tableProps.parent.value;
      }
      const result = await knex(tableProps.table).insert(line, tableProps.pk);

      line[tableProps.pk] = result[0];
      line.tmpId = tmpId;
      return line;
    })
  );
}

function createUpdateQueries(knex, collection, tableProps) {
  return Promise.all(
    collection.map(line => {
      delete line.tmpId;
      delete line.tmp_id;
      return knex(tableProps.table)
        .update(line)
        .where(tableProps.pk, line[tableProps.pk]);
    })
  );
}

async function createCollectionQueries(knex, collection, tableProps) {
  const updates = [];
  const inserts = [];
  collection.forEach(line => {
    if (line[tableProps.pk] === undefined) {
      inserts.push(line);
    } else {
      updates.push(line);
    }
  });

  const results = await Promise.all([
    createDeleteQueries(knex, collection, tableProps),
    createInsertQueries(knex, inserts, tableProps),
    createUpdateQueries(knex, updates, tableProps)
  ]);

  const insertResults = results[1];
  return collection.map(line => {
    if (insertResults) {
      const insert = insertResults.find(
        result => result.tmpId === line.tmpId
      );
      if (insert) {
        line.id = insert.id;
      }
    }
    return line;
  });
}

function convertQuestionFormat(questions, questionnaireId) {
  return questions.map(question => {
    question.question = JSON.stringify(question.question);
    question.questionnaire_id = questionnaireId;
    if (isNaN(question.id)) {
      question.tmpId = question.id;
      delete question.id;
    }
    return question;
  });
}

async function getNotificationTemplates(knex, dbInfo, hostname, notificationsMode) {
  if (notificationsMode > NOTIFICATIONS_MODE.OFF) {
    const templates = await knex.select(
        'template_id as templateId',
        'description',
        'type',
        'active',
        'core_template_id as coreTemplateId',
        'value',
        'period'
      )
      .from('notification_template');

    try {
      return await populateTemplateData(dbInfo, hostname, templates);
    } catch (err) {
      return templates.map(template => {
        template.error = true;
        return template;
      });
    }
  }
  return [];
}

async function createMatrixTypes(knex) {
  const categories = await knex
    .select('*')
    .from('relationship_category_type');
  const relationshipTypes = await knex
    .select('*')
    .from('relationship_type')
    .where('active', true);
  const amountTypes = await knex
    .select('*')
    .from('relationship_amount_type')
    .where('active', true);
  return categories.map(type => {
    type.typeOptions = relationshipTypes.filter(relationType => {
      return relationType.relationship_cd === type.type_cd;
    });
    type.amountOptions = amountTypes.filter(amountType => {
      return amountType.relationship_cd === type.type_cd;
    });
    return type;
  });
}

async function getQuestionnaireQuestions(knex, typeCd) {
  const questionnaire = await knex
    .first('id')
    .from('questionnaire')
    .where('type_cd', typeCd)
    .orderBy('version', 'desc');

  if (questionnaire) {
    const questions = await knex
      .select('*')
      .from('questionnaire_question as qq')
      .where({questionnaire_id: questionnaire.id, active: true});
    return questions.map(question => {
      question.question = JSON.parse(question.question);
      return question;
    });
  }
}

async function getScreeningQuestions(knex) {
  return await getQuestionnaireQuestions(
    knex,
    SCREENING_QUESTIONNAIRE_TYPE_CD
  );
}

async function getEntityQuestions(knex) {
  return await getQuestionnaireQuestions(
    knex,
    FIN_ENTITY_QUESTIONNAIRE_TYPE_CD
  );
}

async function getQuestions(knex) {
  const screening = await getScreeningQuestions(knex);
  const entities = await getEntityQuestions(knex);
  return {
    screening,
    entities
  };
}

export async function getLatestConfigsId(knex) {
  const record = await knex('config').max('id as id');
  return record[0].id;
}

export async function getGeneralConfig(knex) {
  const mostRecentId = await getLatestConfigsId(knex);

  if (cachedConfigs[mostRecentId]) {
    return {
      config: cachedConfigs[mostRecentId].general,
      id: mostRecentId
    };
  }

  const generalConfigRecords = await knex
    .first('config')
    .from('config')
    .where({id: mostRecentId});

  if (!generalConfigRecords) {
    return {
      config: {},
      id: mostRecentId
    };
  }

  const parsedConfig = JSON.parse(generalConfigRecords.config);
  cachedConfigs[mostRecentId] = parsedConfig;

  return {
    config: parsedConfig.general,
    id: mostRecentId
  };
}

export async function getConfig(dbInfo, knex, hostname) {
  try {
    let config = {};
    const notificationsMode = getNotificationsInfo(dbInfo).notificationsMode;

    config.matrixTypes = await createMatrixTypes(knex);
    config.relationshipPersonTypes = await knex
      .select('*')
      .from('relationship_person_type')
      .where('active', true);

    config.declarationTypes = await knex
      .select('*')
      .from('declaration_type')
      .orderBy('order');

    config.dispositionTypes = await knex
      .select('*')
      .from('disposition_type')
      .orderBy('order');

    config.disclosureTypes = await knex.select('*').from('disclosure_type');
    config.questions = await getQuestions(knex);
    config.disclosureStatus = await knex.select('*').from('disclosure_status');
    config.projectTypes = await knex.select('*').from('project_type');
    config.projectRoles = await knex.select('*').from('project_role');
    config.projectStatuses = await knex.select('*').from('project_status');
    config.notificationTemplates = await getNotificationTemplates(
      knex,
      dbInfo,
      hostname,
      notificationsMode
    );
    config.notificationsMode = notificationsMode;
    config.lane = lane;
    config = camelizeJson(config);
    const generalConfig = await getGeneralConfig(knex);
    config.id = generalConfig.id;
    config.general = generalConfig.config;
    return Promise.resolve(config);
  } catch (err) {
    Promise.reject(err);
  }
}

function updateParentIdOnChildren(children, updatedParents) {
  return children.map(question => {
    if (isNaN(question.parent)) {
      const parent = updatedParents.find(p => {
        return p.tmpId === question.parent;
      });

      if (parent) {
        question.parent = parent.id;
      }
    }
    return question;
  });
}

export async function saveScreeningQuestionnaire(knex, questions) {
  const questionnaire = await knex
    .first('id')
    .from('questionnaire')
    .where('type_cd', 1)
    .orderBy('version', 'desc');

  const convertedQuestions = convertQuestionFormat(
    questions,
    questionnaire.id
  );
  const parents = convertedQuestions.filter(question => !question.parent);
  const children = convertedQuestions.filter(question => question.parent);

  const updatedParents = await createCollectionQueries(knex, parents, {
    pk: 'id',
    table: 'questionnaire_question',
    where() {
      this
        .whereNull('parent')
        .andWhere({
          questionnaire_id: questionnaire.id
        });
    }
  });

  await createCollectionQueries(
    knex,
    updateParentIdOnChildren(children, updatedParents),
    {
      pk: 'id',
      table: 'questionnaire_question',
      where() {
        this
          .whereNotNull('parent')
          .andWhere({
            questionnaire_id: questionnaire.id}
          );
      }
    }
  );
}

export async function saveEntityQuestionnaire(knex, questions) {
  const result = await knex
    .first('*')
    .from('questionnaire')
    .where('type_cd', 2)
    .orderBy('version', 'desc');

  let idToUse;
  if (result) {
    idToUse = result.id;
  }
  else {
    const id = await knex('questionnaire')
      .insert({
        version: 1,
        type_cd: 2
      }, 'id');

    idToUse = id[0];
  }

  return await createCollectionQueries(
    knex,
    convertQuestionFormat(questions, idToUse),
    {
      pk: 'id',
      table: 'questionnaire_question',
      where: {questionnaire_id: idToUse}
    }
  );
}

export async function setConfig(dbInfo, knex, userId, body, hostname) {
  const config = snakeizeJson(body);

  if (config.disposition_types && Array.isArray(config.disposition_types)) {
    const tooLong = config.disposition_types.some(dispositionType => {
      return (
        dispositionType.description &&
        dispositionType.description.length > 50
      );
    });

    if (tooLong) {
      throw Error('disposition type description is too long');
    }
  }

  const notificationsMode = getNotificationsInfo(dbInfo).notificationsMode;
  const queries = [];

  config.matrix_types.forEach(type => {
    queries.push(
      knex('relationship_category_type')
        .update({
          enabled: type.enabled,
          type_enabled: type.type_enabled,
          amount_enabled: type.amount_enabled,
          destination_enabled: type.destination_enabled,
          date_enabled: type.date_enabled,
          reason_enabled: type.reason_enabled
        })
        .where('type_cd', type.type_cd)
    );

    queries.push(
      createCollectionQueries(knex, type.type_options, {
        pk: 'type_cd',
        table: 'relationship_type',
        where: {relationship_cd: type.type_cd}
      })
    );

    queries.push(
      createCollectionQueries(knex, type.amount_options, {
        pk: 'type_cd',
        table: 'relationship_amount_type',
        where: {relationship_cd: type.type_cd}
      })
    );
  });

  queries.push(
    createCollectionQueries(
      knex,
      config.declaration_types,
      {pk: 'type_cd', table: 'declaration_type'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.disposition_types,
      {pk: 'type_cd', table: 'disposition_type'}
    )
  );

  queries.push(
    createCollectionQueries(knex, config.relationship_person_types, {
      pk: 'type_cd',
      table: 'relationship_person_type'
    })
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.disclosure_types,
      {pk: 'type_cd', table: 'disclosure_type'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.project_types,
      {pk: 'type_cd', table: 'project_type'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.project_roles,
      {pk: 'type_cd', table: 'project_role'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.project_statuses,
      {pk: 'type_cd', table: 'project_status'}
    )
  );

  await saveScreeningQuestionnaire(knex, config.questions.screening);
  await saveEntityQuestionnaire(knex, config.questions.entities);

  if (notificationsMode > NOTIFICATIONS_MODE.OFF) {
    const results = await handleTemplates(
      dbInfo,
      hostname,
      config.notification_templates
    );
    const templates = await Promise.all(results);
    queries.push(
      createCollectionQueries(
        knex,
        templates,
        {pk: 'template_id', table: 'notification_template'}
      )
    );
  }

  await Promise.all(queries);
  return camelizeJson(config);
}

export function archiveConfig(knex, userId, userName, config) {
  return knex('config').insert({
    config: JSON.stringify(config),
    user_id: userId,
    user_name: userName,
    updated_date: new Date()
  }, 'id');
}

export async function getArchivedConfig(knex, id) {
  if (cachedConfigs[id]) {
    return cachedConfigs[id];
  }

  const result = await knex('config')
    .first(
      'config',
      'id'
    )
    .where('id', id);

  const config = JSON.parse(result.config);
  config.lane = lane;
  config.id = result.id;

  cachedConfigs[id] = config;
  return config;
}

export function getRequiredProjectRoles(knex) {
  return knex('project_role')
    .select('source_role_cd as sourceRoleCd','project_type_cd as projectTypeCd')
    .where({req_disclosure: true});
}

export function getRequiredProjectStatuses(knex) {
  return knex('project_status')
    .select(
      'source_status_cd as sourceStatusCd',
      'project_type_cd as projectTypeCd'
    )
    .where({req_disclosure: true});
}

export function getRequiredProjectTypes(knex) {
  return knex('project_type')
    .select('type_cd as typeCd')
    .where({req_disclosure: true});
}

export async function getProjectTypeDescription(knex, typeCd) {
  const projectType = await knex('project_type')
    .first('description')
    .where({type_cd: typeCd});

  return projectType.description;
}

export async function getCoreTemplateIdByTemplateId(knex, templateId) {
  const template = await knex('notification_template')
    .first('core_template_id as coreTemplateId', 'active')
    .where({template_id: templateId});

  return template;
}
<commit_msg>RESCOI-900: Increase the allowed length of disposition description<commit_after>/*
    The Conflict of Interest (COI) module of Kuali Research
    Copyright © 2005-2016 Kuali, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

import {camelizeJson, snakeizeJson} from './json-utils';
import {
  populateTemplateData,
  handleTemplates
} from '../services/notification-service/notification-service';
import { NOTIFICATIONS_MODE, LANES } from '../../coi-constants';
import Log from '../log';

const cachedConfigs = {};

let getNotificationsInfo;
let lane;
try {
  const extensions = require('research-extensions').default;
  getNotificationsInfo = extensions.getNotificationsInfo;
  lane = extensions.config.lane;
}
catch (err) {
  if (err.code !== 'MODULE_NOT_FOUND') {
    Log.error(err);
  }
  lane = process.env.LANE || LANES.PRODUCTION;
  getNotificationsInfo = () => {
    return {
      notificationsMode: (
        process.env.NOTIFICATIONS_MODE || NOTIFICATIONS_MODE.OFF
      )
    };
  };
}

const SCREENING_QUESTIONNAIRE_TYPE_CD = 1;
const FIN_ENTITY_QUESTIONNAIRE_TYPE_CD = 2;

async function createDeleteQueries(knex, collection, tableProps) {
  let sel = knex(tableProps.table).select(tableProps.pk);
  if (tableProps.where) {
    sel = sel.where(tableProps.where);
  }

  const results = await sel;

  await Promise.all(
    results.filter(result => {
      const match = collection.find(item => {
        return (
          item[tableProps.pk] &&
          item[tableProps.pk] === result[tableProps.pk]
        );
      });
      return !match;
    }).map(result => {
      return knex(tableProps.table)
        .update({active: false})
        .where(tableProps.pk, result[tableProps.pk]);
    })
  );
}

async function createInsertQueries(knex, collection, tableProps) {
  return await Promise.all(
    collection.map(async (line) => {
      const tmpId = line.tmpId;
      delete line.tmpId;
      delete line.tmp_id;
      line.active = true;
      if (tableProps.parent) {
        line[tableProps.parent.key] = tableProps.parent.value;
      }
      const result = await knex(tableProps.table).insert(line, tableProps.pk);

      line[tableProps.pk] = result[0];
      line.tmpId = tmpId;
      return line;
    })
  );
}

function createUpdateQueries(knex, collection, tableProps) {
  return Promise.all(
    collection.map(line => {
      delete line.tmpId;
      delete line.tmp_id;
      return knex(tableProps.table)
        .update(line)
        .where(tableProps.pk, line[tableProps.pk]);
    })
  );
}

async function createCollectionQueries(knex, collection, tableProps) {
  const updates = [];
  const inserts = [];
  collection.forEach(line => {
    if (line[tableProps.pk] === undefined) {
      inserts.push(line);
    } else {
      updates.push(line);
    }
  });

  const results = await Promise.all([
    createDeleteQueries(knex, collection, tableProps),
    createInsertQueries(knex, inserts, tableProps),
    createUpdateQueries(knex, updates, tableProps)
  ]);

  const insertResults = results[1];
  return collection.map(line => {
    if (insertResults) {
      const insert = insertResults.find(
        result => result.tmpId === line.tmpId
      );
      if (insert) {
        line.id = insert.id;
      }
    }
    return line;
  });
}

function convertQuestionFormat(questions, questionnaireId) {
  return questions.map(question => {
    question.question = JSON.stringify(question.question);
    question.questionnaire_id = questionnaireId;
    if (isNaN(question.id)) {
      question.tmpId = question.id;
      delete question.id;
    }
    return question;
  });
}

async function getNotificationTemplates(knex, dbInfo, hostname, notificationsMode) {
  if (notificationsMode > NOTIFICATIONS_MODE.OFF) {
    const templates = await knex.select(
        'template_id as templateId',
        'description',
        'type',
        'active',
        'core_template_id as coreTemplateId',
        'value',
        'period'
      )
      .from('notification_template');

    try {
      return await populateTemplateData(dbInfo, hostname, templates);
    } catch (err) {
      return templates.map(template => {
        template.error = true;
        return template;
      });
    }
  }
  return [];
}

async function createMatrixTypes(knex) {
  const categories = await knex
    .select('*')
    .from('relationship_category_type');
  const relationshipTypes = await knex
    .select('*')
    .from('relationship_type')
    .where('active', true);
  const amountTypes = await knex
    .select('*')
    .from('relationship_amount_type')
    .where('active', true);
  return categories.map(type => {
    type.typeOptions = relationshipTypes.filter(relationType => {
      return relationType.relationship_cd === type.type_cd;
    });
    type.amountOptions = amountTypes.filter(amountType => {
      return amountType.relationship_cd === type.type_cd;
    });
    return type;
  });
}

async function getQuestionnaireQuestions(knex, typeCd) {
  const questionnaire = await knex
    .first('id')
    .from('questionnaire')
    .where('type_cd', typeCd)
    .orderBy('version', 'desc');

  if (questionnaire) {
    const questions = await knex
      .select('*')
      .from('questionnaire_question as qq')
      .where({questionnaire_id: questionnaire.id, active: true});
    return questions.map(question => {
      question.question = JSON.parse(question.question);
      return question;
    });
  }
}

async function getScreeningQuestions(knex) {
  return await getQuestionnaireQuestions(
    knex,
    SCREENING_QUESTIONNAIRE_TYPE_CD
  );
}

async function getEntityQuestions(knex) {
  return await getQuestionnaireQuestions(
    knex,
    FIN_ENTITY_QUESTIONNAIRE_TYPE_CD
  );
}

async function getQuestions(knex) {
  const screening = await getScreeningQuestions(knex);
  const entities = await getEntityQuestions(knex);
  return {
    screening,
    entities
  };
}

export async function getLatestConfigsId(knex) {
  const record = await knex('config').max('id as id');
  return record[0].id;
}

export async function getGeneralConfig(knex) {
  const mostRecentId = await getLatestConfigsId(knex);

  if (cachedConfigs[mostRecentId]) {
    return {
      config: cachedConfigs[mostRecentId].general,
      id: mostRecentId
    };
  }

  const generalConfigRecords = await knex
    .first('config')
    .from('config')
    .where({id: mostRecentId});

  if (!generalConfigRecords) {
    return {
      config: {},
      id: mostRecentId
    };
  }

  const parsedConfig = JSON.parse(generalConfigRecords.config);
  cachedConfigs[mostRecentId] = parsedConfig;

  return {
    config: parsedConfig.general,
    id: mostRecentId
  };
}

export async function getConfig(dbInfo, knex, hostname) {
  try {
    let config = {};
    const notificationsMode = getNotificationsInfo(dbInfo).notificationsMode;

    config.matrixTypes = await createMatrixTypes(knex);
    config.relationshipPersonTypes = await knex
      .select('*')
      .from('relationship_person_type')
      .where('active', true);

    config.declarationTypes = await knex
      .select('*')
      .from('declaration_type')
      .orderBy('order');

    config.dispositionTypes = await knex
      .select('*')
      .from('disposition_type')
      .orderBy('order');

    config.disclosureTypes = await knex.select('*').from('disclosure_type');
    config.questions = await getQuestions(knex);
    config.disclosureStatus = await knex.select('*').from('disclosure_status');
    config.projectTypes = await knex.select('*').from('project_type');
    config.projectRoles = await knex.select('*').from('project_role');
    config.projectStatuses = await knex.select('*').from('project_status');
    config.notificationTemplates = await getNotificationTemplates(
      knex,
      dbInfo,
      hostname,
      notificationsMode
    );
    config.notificationsMode = notificationsMode;
    config.lane = lane;
    config = camelizeJson(config);
    const generalConfig = await getGeneralConfig(knex);
    config.id = generalConfig.id;
    config.general = generalConfig.config;
    return Promise.resolve(config);
  } catch (err) {
    Promise.reject(err);
  }
}

function updateParentIdOnChildren(children, updatedParents) {
  return children.map(question => {
    if (isNaN(question.parent)) {
      const parent = updatedParents.find(p => {
        return p.tmpId === question.parent;
      });

      if (parent) {
        question.parent = parent.id;
      }
    }
    return question;
  });
}

export async function saveScreeningQuestionnaire(knex, questions) {
  const questionnaire = await knex
    .first('id')
    .from('questionnaire')
    .where('type_cd', 1)
    .orderBy('version', 'desc');

  const convertedQuestions = convertQuestionFormat(
    questions,
    questionnaire.id
  );
  const parents = convertedQuestions.filter(question => !question.parent);
  const children = convertedQuestions.filter(question => question.parent);

  const updatedParents = await createCollectionQueries(knex, parents, {
    pk: 'id',
    table: 'questionnaire_question',
    where() {
      this
        .whereNull('parent')
        .andWhere({
          questionnaire_id: questionnaire.id
        });
    }
  });

  await createCollectionQueries(
    knex,
    updateParentIdOnChildren(children, updatedParents),
    {
      pk: 'id',
      table: 'questionnaire_question',
      where() {
        this
          .whereNotNull('parent')
          .andWhere({
            questionnaire_id: questionnaire.id}
          );
      }
    }
  );
}

export async function saveEntityQuestionnaire(knex, questions) {
  const result = await knex
    .first('*')
    .from('questionnaire')
    .where('type_cd', 2)
    .orderBy('version', 'desc');

  let idToUse;
  if (result) {
    idToUse = result.id;
  }
  else {
    const id = await knex('questionnaire')
      .insert({
        version: 1,
        type_cd: 2
      }, 'id');

    idToUse = id[0];
  }

  return await createCollectionQueries(
    knex,
    convertQuestionFormat(questions, idToUse),
    {
      pk: 'id',
      table: 'questionnaire_question',
      where: {questionnaire_id: idToUse}
    }
  );
}

export async function setConfig(dbInfo, knex, userId, body, hostname) {
  const config = snakeizeJson(body);

  if (config.disposition_types && Array.isArray(config.disposition_types)) {
    const tooLong = config.disposition_types.some(dispositionType => {
      return (
        dispositionType.description &&
        dispositionType.description.length > 60
      );
    });

    if (tooLong) {
      throw Error('disposition type description is too long');
    }
  }

  const notificationsMode = getNotificationsInfo(dbInfo).notificationsMode;
  const queries = [];

  config.matrix_types.forEach(type => {
    queries.push(
      knex('relationship_category_type')
        .update({
          enabled: type.enabled,
          type_enabled: type.type_enabled,
          amount_enabled: type.amount_enabled,
          destination_enabled: type.destination_enabled,
          date_enabled: type.date_enabled,
          reason_enabled: type.reason_enabled
        })
        .where('type_cd', type.type_cd)
    );

    queries.push(
      createCollectionQueries(knex, type.type_options, {
        pk: 'type_cd',
        table: 'relationship_type',
        where: {relationship_cd: type.type_cd}
      })
    );

    queries.push(
      createCollectionQueries(knex, type.amount_options, {
        pk: 'type_cd',
        table: 'relationship_amount_type',
        where: {relationship_cd: type.type_cd}
      })
    );
  });

  queries.push(
    createCollectionQueries(
      knex,
      config.declaration_types,
      {pk: 'type_cd', table: 'declaration_type'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.disposition_types,
      {pk: 'type_cd', table: 'disposition_type'}
    )
  );

  queries.push(
    createCollectionQueries(knex, config.relationship_person_types, {
      pk: 'type_cd',
      table: 'relationship_person_type'
    })
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.disclosure_types,
      {pk: 'type_cd', table: 'disclosure_type'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.project_types,
      {pk: 'type_cd', table: 'project_type'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.project_roles,
      {pk: 'type_cd', table: 'project_role'}
    )
  );

  queries.push(
    createCollectionQueries(
      knex,
      config.project_statuses,
      {pk: 'type_cd', table: 'project_status'}
    )
  );

  await saveScreeningQuestionnaire(knex, config.questions.screening);
  await saveEntityQuestionnaire(knex, config.questions.entities);

  if (notificationsMode > NOTIFICATIONS_MODE.OFF) {
    const results = await handleTemplates(
      dbInfo,
      hostname,
      config.notification_templates
    );
    const templates = await Promise.all(results);
    queries.push(
      createCollectionQueries(
        knex,
        templates,
        {pk: 'template_id', table: 'notification_template'}
      )
    );
  }

  await Promise.all(queries);
  return camelizeJson(config);
}

export function archiveConfig(knex, userId, userName, config) {
  return knex('config').insert({
    config: JSON.stringify(config),
    user_id: userId,
    user_name: userName,
    updated_date: new Date()
  }, 'id');
}

export async function getArchivedConfig(knex, id) {
  if (cachedConfigs[id]) {
    return cachedConfigs[id];
  }

  const result = await knex('config')
    .first(
      'config',
      'id'
    )
    .where('id', id);

  const config = JSON.parse(result.config);
  config.lane = lane;
  config.id = result.id;

  cachedConfigs[id] = config;
  return config;
}

export function getRequiredProjectRoles(knex) {
  return knex('project_role')
    .select('source_role_cd as sourceRoleCd','project_type_cd as projectTypeCd')
    .where({req_disclosure: true});
}

export function getRequiredProjectStatuses(knex) {
  return knex('project_status')
    .select(
      'source_status_cd as sourceStatusCd',
      'project_type_cd as projectTypeCd'
    )
    .where({req_disclosure: true});
}

export function getRequiredProjectTypes(knex) {
  return knex('project_type')
    .select('type_cd as typeCd')
    .where({req_disclosure: true});
}

export async function getProjectTypeDescription(knex, typeCd) {
  const projectType = await knex('project_type')
    .first('description')
    .where({type_cd: typeCd});

  return projectType.description;
}

export async function getCoreTemplateIdByTemplateId(knex, templateId) {
  const template = await knex('notification_template')
    .first('core_template_id as coreTemplateId', 'active')
    .where({template_id: templateId});

  return template;
}
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var BaseSoundManager = require('../BaseSoundManager');
var Class = require('../../utils/Class');
var EventEmitter = require('eventemitter3');
var NoAudioSound = require('./NoAudioSound');
var NOOP = require('../../utils/NOOP');

/**
 * @classdesc
 * No audio implementation of the sound manager. It is used if audio has been
 * disabled in the game config or the device doesn't support any audio.
 *
 * It represents a graceful degradation of sound manager logic that provides
 * minimal functionality and prevents Phaser projects that use audio from
 * breaking on devices that don't support any audio playback technologies.
 *
 * @class NoAudioSoundManager
 * @extends Phaser.Sound.BaseSoundManager
 * @memberOf Phaser.Sound
 * @constructor
 * @author Pavle Goloskokovic <pgoloskokovic@gmail.com> (http://prunegames.com)
 * @since 3.0.0
 *
 * @param {Phaser.Game} game - Reference to the current game instance.
 */
var NoAudioSoundManager = new Class({

    Extends: EventEmitter,

    initialize:

    function NoAudioSoundManager (game)
    {
        EventEmitter.call(this);

        /**
         * Reference to the current game instance.
         *
         * @name Phaser.Sound.NoAudioSoundManager#game
         * @type {Phaser.Game}
         * @since 3.0.0
         */
        this.game = game;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#sounds
         * @type {array}
         * @default []
         * @since 3.0.0
         */
        this.sounds = [];

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#mute
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.mute = false;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#volume
         * @type {number}
         * @default 1
         * @since 3.0.0
         */
        this.volume = 1;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#rate
         * @type {number}
         * @default 1
         * @since 3.0.0
         */
        this.rate = 1;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#detune
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.detune = 0;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#pauseOnBlur
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.pauseOnBlur = true;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#locked
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.locked = false;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#add
     * @since 3.0.0
     *
     * @param {string} key - Asset key for the sound.
     * @param {ISoundConfig} [config] - An optional config object containing default sound settings.
     *
     * @return {ISound} The new sound instance.
     */
    add: function (key, config)
    {
        var sound = new NoAudioSound(this, key, config);

        this.sounds.push(sound);

        return sound;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#addAudioSprite
     * @since 3.0.0
     *
     * @param {string} key - Asset key for the sound.
     * @param {ISoundConfig} [config] - An optional config object containing default sound settings.
     *
     * @return {IAudioSpriteSound} The new audio sprite sound instance.
     */
    addAudioSprite: function (key, config)
    {
        var sound = this.add(key, config);
        sound.spritemap = {};
        return sound;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#play
     * @since 3.0.0
     *
     * @return {boolean} No Audio methods always return `false`.
     */
    play: function ()
    {
        return false;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#playAudioSprite
     * @since 3.0.0
     *
     * @return {boolean} No Audio methods always return `false`.
     */
    playAudioSprite: function ()
    {
        return false;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#remove
     * @since 3.0.0
     *
     * @param {ISound} sound - The sound object to remove.
     *
     * @return {boolean} True if the sound was removed successfully, otherwise false.
     */
    remove: function (sound)
    {
        return BaseSoundManager.prototype.remove.call(this, sound);
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#removeByKey
     * @since 3.0.0
     *
     * @param {string} key - The key to match when removing sound objects.
     *
     * @return {number} The number of matching sound objects that were removed.
     */
    removeByKey: function (key)
    {
        return BaseSoundManager.prototype.removeByKey.call(this, key);
    },

    pauseAll: NOOP,

    resumeAll: NOOP,

    stopAll: NOOP,

    update: NOOP,

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#destroy
     * @since 3.0.0
     */
    destroy: function ()
    {
        BaseSoundManager.prototype.destroy.call(this);
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#forEachActiveSound
     * @since 3.0.0
     *
     * @param {function} callbackfn - Callback function. (sound: ISound, index: number, array: ISound[]) => void
     * @param [scope] - Callback context.
     */
    forEachActiveSound: function (callbackfn, scope)
    {
        BaseSoundManager.prototype.forEachActiveSound.call(this, callbackfn, scope);
    }

});

module.exports = NoAudioSoundManager;
<commit_msg>Removed redundant docs<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2018 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var BaseSoundManager = require('../BaseSoundManager');
var Class = require('../../utils/Class');
var EventEmitter = require('eventemitter3');
var NoAudioSound = require('./NoAudioSound');
var NOOP = require('../../utils/NOOP');

/**
 * @classdesc
 * No audio implementation of the sound manager. It is used if audio has been
 * disabled in the game config or the device doesn't support any audio.
 *
 * It represents a graceful degradation of sound manager logic that provides
 * minimal functionality and prevents Phaser projects that use audio from
 * breaking on devices that don't support any audio playback technologies.
 *
 * @class NoAudioSoundManager
 * @extends Phaser.Sound.BaseSoundManager
 * @memberOf Phaser.Sound
 * @constructor
 * @author Pavle Goloskokovic <pgoloskokovic@gmail.com> (http://prunegames.com)
 * @since 3.0.0
 *
 * @param {Phaser.Game} game - Reference to the current game instance.
 */
var NoAudioSoundManager = new Class({

    Extends: EventEmitter,

    initialize:

    function NoAudioSoundManager (game)
    {
        EventEmitter.call(this);
        this.game = game;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#sounds
         * @type {array}
         * @default []
         * @since 3.0.0
         */
        this.sounds = [];

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#mute
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.mute = false;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#volume
         * @type {number}
         * @default 1
         * @since 3.0.0
         */
        this.volume = 1;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#rate
         * @type {number}
         * @default 1
         * @since 3.0.0
         */
        this.rate = 1;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#detune
         * @type {number}
         * @default 0
         * @since 3.0.0
         */
        this.detune = 0;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#pauseOnBlur
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.pauseOnBlur = true;

        /**
         * [description]
         *
         * @name Phaser.Sound.NoAudioSoundManager#locked
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.locked = false;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#add
     * @since 3.0.0
     *
     * @param {string} key - Asset key for the sound.
     * @param {ISoundConfig} [config] - An optional config object containing default sound settings.
     *
     * @return {ISound} The new sound instance.
     */
    add: function (key, config)
    {
        var sound = new NoAudioSound(this, key, config);

        this.sounds.push(sound);

        return sound;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#addAudioSprite
     * @since 3.0.0
     *
     * @param {string} key - Asset key for the sound.
     * @param {ISoundConfig} [config] - An optional config object containing default sound settings.
     *
     * @return {IAudioSpriteSound} The new audio sprite sound instance.
     */
    addAudioSprite: function (key, config)
    {
        var sound = this.add(key, config);
        sound.spritemap = {};
        return sound;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#play
     * @since 3.0.0
     *
     * @return {boolean} No Audio methods always return `false`.
     */
    play: function ()
    {
        return false;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#playAudioSprite
     * @since 3.0.0
     *
     * @return {boolean} No Audio methods always return `false`.
     */
    playAudioSprite: function ()
    {
        return false;
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#remove
     * @since 3.0.0
     *
     * @param {ISound} sound - The sound object to remove.
     *
     * @return {boolean} True if the sound was removed successfully, otherwise false.
     */
    remove: function (sound)
    {
        return BaseSoundManager.prototype.remove.call(this, sound);
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#removeByKey
     * @since 3.0.0
     *
     * @param {string} key - The key to match when removing sound objects.
     *
     * @return {number} The number of matching sound objects that were removed.
     */
    removeByKey: function (key)
    {
        return BaseSoundManager.prototype.removeByKey.call(this, key);
    },

    pauseAll: NOOP,

    resumeAll: NOOP,

    stopAll: NOOP,

    update: NOOP,

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#destroy
     * @since 3.0.0
     */
    destroy: function ()
    {
        BaseSoundManager.prototype.destroy.call(this);
    },

    /**
     * [description]
     *
     * @method Phaser.Sound.NoAudioSoundManager#forEachActiveSound
     * @since 3.0.0
     *
     * @param {function} callbackfn - Callback function. (sound: ISound, index: number, array: ISound[]) => void
     * @param [scope] - Callback context.
     */
    forEachActiveSound: function (callbackfn, scope)
    {
        BaseSoundManager.prototype.forEachActiveSound.call(this, callbackfn, scope);
    }

});

module.exports = NoAudioSoundManager;
<|endoftext|>


<commit_before>'use strict';

var aFrom        = require('es5-ext/array/from')
  , compact      = require('es5-ext/array/#/compact')
  , isCopy       = require('es5-ext/array/#/is-copy')
  , copy         = require('es5-ext/object/copy')
  , assign       = require('es5-ext/object/assign')
  , isObject     = require('es5-ext/object/is-object')
  , callable     = require('es5-ext/object/valid-callable')
  , toArray      = require('es6-iterator/to-array')
  , d            = require('d/d')
  , memoize      = require('memoizee/lib/regular')
  , memPrimitive = require('memoizee/lib/primitive')
  , makeEl       = require('dom-ext/document/#/make-element')
  , append       = require('dom-ext/element/#/append')
  , clear        = require('dom-ext/element/#/clear')
  , remove       = require('dom-ext/element/#/remove')
  , replaceCont  = require('dom-ext/element/#/replace-content')
  , dispatchEvnt = require('dom-ext/html-element/#/dispatch-event-2')
  , DOMInput     = require('../_controls/input')
  , eventOpts    = require('../_event-options')
  , setup        = require('../')

  , isArray = Array.isArray, map = Array.prototype.map
  , defineProperty = Object.defineProperty
  , defineProperties = Object.defineProperties
  , getName = Object.getOwnPropertyDescriptor(DOMInput.prototype, 'name').get
  , Input, render, renderItem;

require('memoizee/lib/ext/method');

render = function (options) {
	var el = this.make;
	return el('div', { class: 'dbjs-file' },
		this.valueDOM = this.multiple ? el('ul') : el('span'),
		el('label', { class: 'dbjs-file-input' }, options.label,
			this.control = el('input', { type: 'file' })));
};

renderItem = function (file) {
	var el = this.make, data = {};
	data.dom = el(this.multiple ? 'li' : 'span', { 'data-id': file._id_ });
	append.call(data.dom,
		el('a', { href: file._url, target: '_blank' }, file._name), " ",
		data.control = el('input', { type: 'hidden', name: this.name,
			value: file._id_ }),
		el('a', { class: 'clear', onclick: this.removeItem.bind(this, data.dom) },
			"x"));
	return data;
};

Input = function (document, type/*, options*/) {
	var options = Object(arguments[2]);
	this.make = makeEl.bind(document);
	this.controls = [];
	if (options.multiple) this.multiple = true;
	((options.render == null) || callable(options.render));
	defineProperty(this, 'renderItem', d((options.renderItem == null) ?
			renderItem : callable(options.renderItem)));
	DOMInput.call(this, document, type, options);
	if (this.multiple) this.control.setAttribute('multiple', 'multiple');
	this.control.setAttribute('accept', toArray(type.accept).join(','));
};

Input.prototype = Object.create(DOMInput.prototype, assign({
	constructor: d(Input),
	multiple: d(false),
	_value: d(null),
	controlAttributes: d(assign(copy(DOMInput.prototype.controlAttributes),
		{ required: true })),
	dbAttributes: d(assign(copy(DOMInput.prototype.dbAttributes),
		{ required: true })),
	_render: d(function (options) {
		this.dom = (options.render || render).call(this, options);
		this.controls.push(this.control);
	}),
	name: d.gs(getName, function (name) {
		this._name = name;
		name = this.name;
		this.controls.forEach(function (input) { input.name = name; });
	}),
	inputValue: d.gs(function () {
		var value;
		if (!this.multiple) {
			if (this.control.files[0]) return this.control.files[0];
			if (!this.valueDOM.firstChild) return null;
			return this.valueDOM.firstChild.getAttribute('data-id');
		}
		value = map.call(this.valueDOM.childNodes, function (node) {
			var id = node.getAttribute('data-id');
			if (!id) {
				throw new TypeError("Missing id (data-id attribute) on file item");
			}
			return id;
		}).concat(aFrom(this.control.files));
		return value.length ? value : null;
	}, function (nu) {
		var old = this.inputValue, changed;
		this._value = nu;
		if (this.multiple) {
			if (nu) {
				if (!old || !isCopy.call(nu, old)) {
					this.control.value = null;
					replaceCont.call(this.valueDOM, nu.map(this._renderItem));
					changed = true;
				}
			} else {
				clear.call(this.valueDOM);
			}
		} else if (nu !== old) {
			this.control.value = null;
			if (nu) {
				replaceCont.call(this.valueDOM, this._renderItem(nu));
				this.dom.classList.add('filled');
			} else {
				clear.call(this.valueDOM);
				this.dom.classList.remove('filled');
			}
			changed = true;
		}
		if (changed) {
			try {
				dispatchEvnt.call(this.control, 'change', eventOpts);
			} catch (ignore) {}
		} else {
			this.onChange();
		}
	}),
	value: d.gs(function () {
		var value = this.inputValue;
		if (value == null) return null;
		if (this.multiple) return value.map(this.type.toInputValue, this.type);
		return this.type.toInputValue(value);
	}, function (value) {
		if (value == null) {
			value = null;
		} else if (this.multiple) {
			value = toArray(value);
			if (!value.length) value = null;
			else value = compact.call(value.map(this.type.toInputValue, this.type));
		} else {
			value = this.type.toInputValue(value);
		}

		this.inputValue = value;
	}),
	onChange: d(function () {
		var value, changed, valid, emitChanged, emitValid;
		value = this.inputValue;
		changed = (this.multiple && (this._value != null) && (value != null)) ?
				isCopy.call(value, this._value) : (value !== this._value);
		valid = this.required ? (value != null) : true;

		if (this.changed !== changed) {
			this.changed = changed;
			emitChanged = true;
		}
		if (this.valid !== valid) {
			this.valid = valid;
			emitValid = true;
		}

		this.emit('change', value);
		if (emitChanged) this.emit('change:changed', this.changed);
		if (emitValid) this.emit('change:valid', this.valid);
	}),
	removeItem: d(function (dom) {
		remove.call(dom);
		if (!this.multiple) this.dom.classList.remove('filled');
		dispatchEvnt.call(this.control, 'change', eventOpts);
	})
}, memPrimitive(function (file) {
	var data;
	file = this.type.getById(file);
	data = this.renderItem(file);
	this.controls.push(data.control);
	return data.dom;
}, { method: '_renderItem' })));

module.exports = memoize(function (db) {
	defineProperties(setup(db).File, {
		fromInputValue: d(function (value) {
			if (value == null) return null;
			if (isArray(value) && (value.length === 2)) {
				if (typeof value[0] === 'string') {
					if (isObject(value[1])) return value[1];
				} else if (typeof value[1] === 'string') {
					if (isObject(value[0])) return value[0];
				}
				return value;
			}
			if (isObject(value)) return value;
			value = value.trim();
			if (!value) return null;
			if (!this.propertyIsEnumerable(value)) return null;
			if (this[value].__id__ !== value) return null;
			return this[value];
		}),
		DOMInput: d(Input),
		toDOMInput: d(function (document/*, options*/) {
			var options = Object(arguments[1]);
			if (options.multiple || (options.type !== 'checkbox')) {
				return new this.DOMInput(document, this, options);
			}
			return db.Object.toDOMInput(document, options);
		})
	});
});
<commit_msg>Consistent value resolution<commit_after>'use strict';

var aFrom        = require('es5-ext/array/from')
  , compact      = require('es5-ext/array/#/compact')
  , isCopy       = require('es5-ext/array/#/is-copy')
  , copy         = require('es5-ext/object/copy')
  , assign       = require('es5-ext/object/assign')
  , isObject     = require('es5-ext/object/is-object')
  , callable     = require('es5-ext/object/valid-callable')
  , toArray      = require('es6-iterator/to-array')
  , d            = require('d/d')
  , memoize      = require('memoizee/lib/regular')
  , memPrimitive = require('memoizee/lib/primitive')
  , makeEl       = require('dom-ext/document/#/make-element')
  , append       = require('dom-ext/element/#/append')
  , clear        = require('dom-ext/element/#/clear')
  , remove       = require('dom-ext/element/#/remove')
  , replaceCont  = require('dom-ext/element/#/replace-content')
  , dispatchEvnt = require('dom-ext/html-element/#/dispatch-event-2')
  , DOMInput     = require('../_controls/input')
  , eventOpts    = require('../_event-options')
  , setup        = require('../')

  , isArray = Array.isArray, map = Array.prototype.map
  , defineProperty = Object.defineProperty
  , defineProperties = Object.defineProperties
  , getName = Object.getOwnPropertyDescriptor(DOMInput.prototype, 'name').get
  , Input, render, renderItem;

require('memoizee/lib/ext/method');

render = function (options) {
	var el = this.make;
	return el('div', { class: 'dbjs-file' },
		this.valueDOM = this.multiple ? el('ul') : el('span'),
		el('label', { class: 'dbjs-file-input' }, options.label,
			this.control = el('input', { type: 'file' })));
};

renderItem = function (file) {
	var el = this.make, data = {};
	data.dom = el(this.multiple ? 'li' : 'span', { 'data-id': file._id_ });
	append.call(data.dom,
		el('a', { href: file._url, target: '_blank' }, file._name), " ",
		data.control = el('input', { type: 'hidden', name: this.name,
			value: file._id_ }),
		el('a', { class: 'clear', onclick: this.removeItem.bind(this, data.dom) },
			"x"));
	return data;
};

Input = function (document, type/*, options*/) {
	var options = Object(arguments[2]);
	this.make = makeEl.bind(document);
	this.controls = [];
	if (options.multiple) this.multiple = true;
	((options.render == null) || callable(options.render));
	defineProperty(this, 'renderItem', d((options.renderItem == null) ?
			renderItem : callable(options.renderItem)));
	DOMInput.call(this, document, type, options);
	if (this.multiple) this.control.setAttribute('multiple', 'multiple');
	this.control.setAttribute('accept', toArray(type.accept).join(','));
};

Input.prototype = Object.create(DOMInput.prototype, assign({
	constructor: d(Input),
	multiple: d(false),
	_value: d(null),
	controlAttributes: d(assign(copy(DOMInput.prototype.controlAttributes),
		{ required: true })),
	dbAttributes: d(assign(copy(DOMInput.prototype.dbAttributes),
		{ required: true })),
	_render: d(function (options) {
		this.dom = (options.render || render).call(this, options);
		this.controls.push(this.control);
	}),
	name: d.gs(getName, function (name) {
		this._name = name;
		name = this.name;
		this.controls.forEach(function (input) { input.name = name; });
	}),
	inputValue: d.gs(function () {
		var value;
		if (!this.multiple) {
			if (this.control.files[0]) return this.control.files[0];
			if (!this.valueDOM.firstChild) return null;
			return this.valueDOM.firstChild.getAttribute('data-id');
		}
		value = map.call(this.valueDOM.childNodes, function (node) {
			var id = node.getAttribute('data-id');
			if (!id) {
				throw new TypeError("Missing id (data-id attribute) on file item");
			}
			return id;
		}).concat(aFrom(this.control.files));
		return value.length ? value : null;
	}, function (nu) {
		var old = this.inputValue, changed;
		this._value = nu;
		if (this.multiple) {
			if (nu) {
				if (!old || !isCopy.call(nu, old)) {
					this.control.value = null;
					replaceCont.call(this.valueDOM, nu.map(this._renderItem));
					changed = true;
				}
			} else {
				clear.call(this.valueDOM);
			}
		} else if (nu !== old) {
			this.control.value = null;
			if (nu) {
				replaceCont.call(this.valueDOM, this._renderItem(nu));
				this.dom.classList.add('filled');
			} else {
				clear.call(this.valueDOM);
				this.dom.classList.remove('filled');
			}
			changed = true;
		}
		if (changed) {
			try {
				dispatchEvnt.call(this.control, 'change', eventOpts);
			} catch (ignore) {}
		} else {
			this.onChange();
		}
	}),
	value: d.gs(function () {
		var value = this.inputValue;
		if (value == null) return null;
		if (this.multiple) return value.map(this.type.toInputValue, this.type);
		return this.type.toInputValue(value);
	}, function (value) {
		if (value == null) {
			value = null;
		} else if (this.multiple) {
			value = toArray(value);
			if (value.length) {
				value = compact.call(value.map(this.type.toInputValue, this.type));
			}
			if (!value.length) value = null;
		} else {
			value = this.type.toInputValue(value);
		}

		this.inputValue = value;
	}),
	onChange: d(function () {
		var value, changed, valid, emitChanged, emitValid;
		value = this.inputValue;
		changed = (this.multiple && (this._value != null) && (value != null)) ?
				isCopy.call(value, this._value) : (value !== this._value);
		valid = this.required ? (value != null) : true;

		if (this.changed !== changed) {
			this.changed = changed;
			emitChanged = true;
		}
		if (this.valid !== valid) {
			this.valid = valid;
			emitValid = true;
		}

		this.emit('change', value);
		if (emitChanged) this.emit('change:changed', this.changed);
		if (emitValid) this.emit('change:valid', this.valid);
	}),
	removeItem: d(function (dom) {
		remove.call(dom);
		if (!this.multiple) this.dom.classList.remove('filled');
		dispatchEvnt.call(this.control, 'change', eventOpts);
	})
}, memPrimitive(function (file) {
	var data;
	file = this.type.getById(file);
	data = this.renderItem(file);
	this.controls.push(data.control);
	return data.dom;
}, { method: '_renderItem' })));

module.exports = memoize(function (db) {
	defineProperties(setup(db).File, {
		fromInputValue: d(function (value) {
			if (value == null) return null;
			if (isArray(value) && (value.length === 2)) {
				if (typeof value[0] === 'string') {
					if (isObject(value[1])) return value[1];
				} else if (typeof value[1] === 'string') {
					if (isObject(value[0])) return value[0];
				}
				return value;
			}
			if (isObject(value)) return value;
			value = value.trim();
			if (!value) return null;
			if (!this.propertyIsEnumerable(value)) return null;
			if (this[value].__id__ !== value) return null;
			return this[value];
		}),
		DOMInput: d(Input),
		toDOMInput: d(function (document/*, options*/) {
			var options = Object(arguments[1]);
			if (options.multiple || (options.type !== 'checkbox')) {
				return new this.DOMInput(document, this, options);
			}
			return db.Object.toDOMInput(document, options);
		})
	});
});
<|endoftext|>


<commit_before>import path from 'path';
import glob from 'glob';
import fs from 'fs';
import async from 'async';

import { MonicError } from './error';
import { FileStructure } from './file';
import { SourceMapConsumer } from 'source-map';

/**
 * Parser class
 */
export default class Parser {
	/**
	 * @param {string} nl - EOL symbol
	 * @param {!Array} replacers - an array of transform functions
	 * @param {boolean} sourceMaps - if is true, then will be enabled support for source maps
	 */
	constructor({nl, replacers, sourceMaps}) {
		this.nl = nl;
		this.replacers = replacers;
		this.sourceMaps = sourceMaps;
		this.realpathCache = {};
		this.cache = {};
	}

	/**
	 * Checks a file for existence
	 * and returns an absolute path to it
	 *
	 * @param {string} file - the file path
	 * @param {function(Error, string=)} callback - a callback function
	 */
	normalizePath(file, callback) {
		file = path.normalize(path.resolve(file));

		if (this.realpathCache[file]) {
			callback(null, file);

		} else {
			async.waterfall([
				(next) => fs.stat(file, next),
				(stat, next) => {
					if (!stat.isFile()) {
						return next(new Error(`"${file}" is not a file`));
					}

					this.realpathCache[file] = true;
					next(null, file);
				}

			], callback);
		}
	}

	parseUrl(file, url, callback) {
		const
			parts = url.split('::'),
			dirname = path.dirname(file);

		glob(path.join(dirname, parts[0]), null, (err, files) => {
			if (err) {
				return callback(err);
			}

			callback(
				null,

				$C(files).reduce((res, el) => {
					parts[0] = path.relative(dirname, el);
					res.push(parts.slice());
					return res;
				}, [])

			);
		});
	}

	/**
	 * Parses a file and returns it structure
	 *
	 * @param {string} file - the file path
	 * @param {function(Error, FileStructure=, string=)} callback - a callback function
	 */
	parseFile(file, callback) {
		async.waterfall([
			(next) => this.normalizePath(file, next),

			(src, next) => {
				if (this.cache[src]) {
					return next(null, src, this.cache[src]);
				}

				fs.readFile(src, (err, content) => {
					next(err, src, String(content));
				});
			},

			(src, content, next) => {
				if (typeof content !== 'string') {
					return next(null, content, src);
				}

				this.parse(src, content, next);
			}

		], callback);
	}

	/**
	 * Parses a text and returns it structure
	 *
	 * @param {string} file - a path to a file
	 * @param {string} content - the source text
	 * @param {function(Error, FileStructure=, string=)} callback - a callback function
	 */
	parse(file, content, callback) {
		if (this.cache[file]) {
			return callback(null, this.cache[file], file);
		}

		const
			actions = [];

		$C(this.replacers).forEach((replacer) => {
			actions.push((next) => {
				if (replacer.length > 2) {
					replacer(content, file, (err, res) => next(err, err ? undefined : content = res));

				} else {
					content = replacer(content, file);
					next();
				}
			});
		});

		let sourceMap;
		if (this.sourceMaps) {
			content = content.replace(/\/\/# sourceMappingURL=(.*?)\s*$/, (sstr, url) => {
				actions.push((next) => {
					fs.readFile(path.normalize(path.resolve(path.dirname(file), url)), (err, str) => {
						try {
							sourceMap = new SourceMapConsumer(JSON.parse(str));
							content = content.replace(sstr, this.nl);

						} catch (ignore) {

						} finally {
							next();
						}
					});
				});

				return sstr;
			});
		}

		async.series(actions, (err) => {
			if (err) {
				return callback(err);
			}

			const
				fileStructure = new FileStructure({file, nl: this.nl}),
				lines = content.split(/\r?\n|\r/);

			this.cache[file] = fileStructure;
			const parseLines = (start) => {
				const
					errors = [];

				let
					info,
					i;

				function appendError(err) {
					const
						msg = err.message;

					errors.push(new MonicError(msg, file, i + 1));
					fileStructure.error(msg, info);
				}

				function asyncParseCallback(err) {
					if (err) {
						appendError(err);
					}

					parseLines(i + 1);
				}

				let original;
				if (sourceMap) {
					let
						originalMap = [];

					sourceMap.eachMapping((el) => {
						originalMap.push({
							generated: {
								line: el.generatedLine,
								column: el.generatedColumn
							},

							original: {
								line: el.originalLine,
								column: el.originalColumn
							},

							source: el.source,
							name: el.name
						});
					});

					if (sourceMap.sourcesContent) {
						$C(sourceMap.sourcesContent).forEach((content, i) => {
							const
								src = sourceMap.sources[i];

							$C(originalMap).forEach((el) => {
								if (el.source === src) {
									el.sourcesContent = content;
								}
							});
						});
					}

					original = $C(originalMap).group('generated > line');
				}

				for (i = start; i < lines.length; i++) {
					const
						line = lines[i],
						val = line + (i < lines.length - 1 ? this.nl : '');

					if (this.sourceMaps) {
						if (original) {
							info = original[i] || {ignore: true};

						} else {
							info = {
								generated: {
									column: 0
								},

								original: {
									line: i + 1,
									column: 0
								},

								source: file,
								sourcesContent: content,
								line
							};
						}
					}

					if (line.match(/^\s*\/\/#(.*)/)) {
						if (RegExp.$1) {
							const
								command = RegExp.$1.split(' '),
								dir = String(command.shift());

							const
								key = '_' + dir,
								params = command.join(' ');

							if (/^(?:include|without)$/.test(dir)) {
								return this[key](fileStructure, params, asyncParseCallback);

							} else if (this[key]) {
								try {
									this[key](fileStructure, params);

								} catch (err) {
									appendError(err);
								}

							} else {
								fileStructure.addCode(val, info);
							}
						}

					} else {
						fileStructure.addCode(val, info);
					}
				}

				callback(null, fileStructure, file);
			};

			parseLines(0);
		});
	}

	/**
	 * Directive #include
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 * @param {function(Error=)} callback - a callback function
	 */
	_include(file, value, callback) {
		this.parseUrl(file.file, value, (err, arr) => {
			if (err) {
				return callback(err);
			}

			const
				actions = [];

			$C(arr).forEach((paramsParts) => {
				actions.push((next) => action(paramsParts, next));
			});

			async.series(actions, callback);
		});

		function action(paramsParts, next) {
			const
				includeFileName = paramsParts.shift();

			paramsParts = $C(paramsParts).reduce((res, el) => {
				res[el] = true;
				return res;
			}, {});

			if (includeFileName) {
				this.parseFile(file.getRelativePathOf(includeFileName), (err, includeFile) => {
					if (err) {
						return next(err);
					}

					file.addInclude(includeFile, paramsParts);
					next();
				});

			} else {
				$C(paramsParts).forEach((el, key) => {
					file.root.labels[key] = true;
				});

				next();
			}
		}
	}

	/**
	 * Directive #without
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 * @param {function(Error=)} callback - a callback function
	 */
	_without(file, value, callback) {
		this.parseUrl(file.file, value, (err, arr) => {
			if (err) {
				return callback(err);
			}

			const
				actions = [];

			$C(arr).forEach((paramsParts) => {
				actions.push((next) => action(paramsParts, next));
			});

			async.series(actions, callback);
		});

		function action(paramsParts, next) {
			const
				includedFile = file.getRelativePathOf(paramsParts.shift());

			paramsParts = $C(paramsParts).reduce((res, el) => {
				res[el] = true;
				return res;
			}, {});

			this.parseFile(includedFile, (err, includeFile) => {
				if (err) {
					return next(err);
				}

				file.addWithout(includeFile, paramsParts);
				next();
			});
		}
	}

	/**
	 * Directive #label
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_label(file, value) {
		file.beginLabel(value);
	}

	/**
	 * Directive #endlabel
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 */
	_endlabel(file) {
		file.endLabel();
	}

	/**
	 * Directive #if
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_if(file, value) {
		value = value.trim();

		if (!value) {
			throw new Error('Bad "if" directive');
		}

		const
			args = value.split(/\s+/);

		let res = true;
		if (args.length > 1 && args[0] === 'not') {
			res = false;
			args.shift();
		}

		file.beginIf(args[0], res);
	}

	/**
	 * Directive #endif
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 */
	_endif(file) {
		file.endIf();
	}

	/**
	 * Directive #set
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_set(file, value) {
		value = value.trim();

		if (!value) {
			throw new Error('Bad "set" directive');
		}

		file.addSet(value);
	}

	/**
	 * Directive #unset
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_unset(file, value) {
		value = value.trim();

		if (!value) {
			throw new Error('Bad "unset" directive');
		}

		file.addUnset(value);
	}
}
<commit_msg>Added description to method parseURL<commit_after>import path from 'path';
import glob from 'glob';
import fs from 'fs';
import async from 'async';

import { MonicError } from './error';
import { FileStructure } from './file';
import { SourceMapConsumer } from 'source-map';

/**
 * Parser class
 */
export default class Parser {
	/**
	 * @param {string} nl - EOL symbol
	 * @param {!Array} replacers - an array of transform functions
	 * @param {boolean} sourceMaps - if is true, then will be enabled support for source maps
	 */
	constructor({nl, replacers, sourceMaps}) {
		this.nl = nl;
		this.replacers = replacers;
		this.sourceMaps = sourceMaps;
		this.realpathCache = {};
		this.cache = {};
	}

	/**
	 * Checks a file for existence
	 * and returns an absolute path to it
	 *
	 * @param {string} file - the file path
	 * @param {function(Error, string=)} callback - a callback function
	 */
	normalizePath(file, callback) {
		file = path.normalize(path.resolve(file));

		if (this.realpathCache[file]) {
			callback(null, file);

		} else {
			async.waterfall([
				(next) => fs.stat(file, next),
				(stat, next) => {
					if (!stat.isFile()) {
						return next(new Error(`"${file}" is not a file`));
					}

					this.realpathCache[file] = true;
					next(null, file);
				}

			], callback);
		}
	}

	/**
	 * Parses URL string with glob
	 *
	 * @param {string} base - a path to a base file
	 * @param {string} url - a path to a file
	 * @param {function(Error, Array=)} callback - a callback function
	 */
	parseURL(base, url, callback) {
		const
			parts = url.split('::'),
			dirname = path.dirname(base);

		glob(path.join(dirname, parts[0]), null, (err, files) => {
			if (err) {
				return callback(err);
			}

			callback(
				null,

				$C(files).reduce((res, el) => {
					parts[0] = path.relative(dirname, el);
					res.push(parts.slice());
					return res;
				}, [])

			);
		});
	}

	/**
	 * Parses a file and returns it structure
	 *
	 * @param {string} file - the file path
	 * @param {function(Error, FileStructure=, string=)} callback - a callback function
	 */
	parseFile(file, callback) {
		async.waterfall([
			(next) => this.normalizePath(file, next),

			(src, next) => {
				if (this.cache[src]) {
					return next(null, src, this.cache[src]);
				}

				fs.readFile(src, (err, content) => {
					next(err, src, String(content));
				});
			},

			(src, content, next) => {
				if (typeof content !== 'string') {
					return next(null, content, src);
				}

				this.parse(src, content, next);
			}

		], callback);
	}

	/**
	 * Parses a text and returns it structure
	 *
	 * @param {string} file - a path to a file
	 * @param {string} content - the source text
	 * @param {function(Error, FileStructure=, string=)} callback - a callback function
	 */
	parse(file, content, callback) {
		if (this.cache[file]) {
			return callback(null, this.cache[file], file);
		}

		const
			actions = [];

		$C(this.replacers).forEach((replacer) => {
			actions.push((next) => {
				if (replacer.length > 2) {
					replacer(content, file, (err, res) => next(err, err ? undefined : content = res));

				} else {
					content = replacer(content, file);
					next();
				}
			});
		});

		let sourceMap;
		if (this.sourceMaps) {
			content = content.replace(/\/\/# sourceMappingURL=(.*?)\s*$/, (sstr, url) => {
				actions.push((next) => {
					fs.readFile(path.normalize(path.resolve(path.dirname(file), url)), (err, str) => {
						try {
							sourceMap = new SourceMapConsumer(JSON.parse(str));
							content = content.replace(sstr, this.nl);

						} catch (ignore) {

						} finally {
							next();
						}
					});
				});

				return sstr;
			});
		}

		async.series(actions, (err) => {
			if (err) {
				return callback(err);
			}

			const
				fileStructure = new FileStructure({file, nl: this.nl}),
				lines = content.split(/\r?\n|\r/);

			this.cache[file] = fileStructure;
			const parseLines = (start) => {
				const
					errors = [];

				let
					info,
					i;

				function appendError(err) {
					const
						msg = err.message;

					errors.push(new MonicError(msg, file, i + 1));
					fileStructure.error(msg, info);
				}

				function asyncParseCallback(err) {
					if (err) {
						appendError(err);
					}

					parseLines(i + 1);
				}

				let original;
				if (sourceMap) {
					let
						originalMap = [];

					sourceMap.eachMapping((el) => {
						originalMap.push({
							generated: {
								line: el.generatedLine,
								column: el.generatedColumn
							},

							original: {
								line: el.originalLine,
								column: el.originalColumn
							},

							source: el.source,
							name: el.name
						});
					});

					if (sourceMap.sourcesContent) {
						$C(sourceMap.sourcesContent).forEach((content, i) => {
							const
								src = sourceMap.sources[i];

							$C(originalMap).forEach((el) => {
								if (el.source === src) {
									el.sourcesContent = content;
								}
							});
						});
					}

					original = $C(originalMap).group('generated > line');
				}

				for (i = start; i < lines.length; i++) {
					const
						line = lines[i],
						val = line + (i < lines.length - 1 ? this.nl : '');

					if (this.sourceMaps) {
						if (original) {
							info = original[i] || {ignore: true};

						} else {
							info = {
								generated: {
									column: 0
								},

								original: {
									line: i + 1,
									column: 0
								},

								source: file,
								sourcesContent: content,
								line
							};
						}
					}

					if (line.match(/^\s*\/\/#(.*)/)) {
						if (RegExp.$1) {
							const
								command = RegExp.$1.split(' '),
								dir = String(command.shift());

							const
								key = '_' + dir,
								params = command.join(' ');

							if (/^(?:include|without)$/.test(dir)) {
								return this[key](fileStructure, params, asyncParseCallback);

							} else if (this[key]) {
								try {
									this[key](fileStructure, params);

								} catch (err) {
									appendError(err);
								}

							} else {
								fileStructure.addCode(val, info);
							}
						}

					} else {
						fileStructure.addCode(val, info);
					}
				}

				callback(null, fileStructure, file);
			};

			parseLines(0);
		});
	}

	/**
	 * Directive #include
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 * @param {function(Error=)} callback - a callback function
	 */
	_include(file, value, callback) {
		this.parseURL(file.file, value, (err, arr) => {
			if (err) {
				return callback(err);
			}

			const
				actions = [];

			$C(arr).forEach((paramsParts) => {
				actions.push((next) => action(paramsParts, next));
			});

			async.series(actions, callback);
		});

		function action(paramsParts, next) {
			const
				includeFileName = paramsParts.shift();

			paramsParts = $C(paramsParts).reduce((res, el) => {
				res[el] = true;
				return res;
			}, {});

			if (includeFileName) {
				this.parseFile(file.getRelativePathOf(includeFileName), (err, includeFile) => {
					if (err) {
						return next(err);
					}

					file.addInclude(includeFile, paramsParts);
					next();
				});

			} else {
				$C(paramsParts).forEach((el, key) => {
					file.root.labels[key] = true;
				});

				next();
			}
		}
	}

	/**
	 * Directive #without
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 * @param {function(Error=)} callback - a callback function
	 */
	_without(file, value, callback) {
		this.parseURL(file.file, value, (err, arr) => {
			if (err) {
				return callback(err);
			}

			const
				actions = [];

			$C(arr).forEach((paramsParts) => {
				actions.push((next) => action(paramsParts, next));
			});

			async.series(actions, callback);
		});

		function action(paramsParts, next) {
			const
				includedFile = file.getRelativePathOf(paramsParts.shift());

			paramsParts = $C(paramsParts).reduce((res, el) => {
				res[el] = true;
				return res;
			}, {});

			this.parseFile(includedFile, (err, includeFile) => {
				if (err) {
					return next(err);
				}

				file.addWithout(includeFile, paramsParts);
				next();
			});
		}
	}

	/**
	 * Directive #label
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_label(file, value) {
		file.beginLabel(value);
	}

	/**
	 * Directive #endlabel
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 */
	_endlabel(file) {
		file.endLabel();
	}

	/**
	 * Directive #if
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_if(file, value) {
		value = value.trim();

		if (!value) {
			throw new Error('Bad "if" directive');
		}

		const
			args = value.split(/\s+/);

		let res = true;
		if (args.length > 1 && args[0] === 'not') {
			res = false;
			args.shift();
		}

		file.beginIf(args[0], res);
	}

	/**
	 * Directive #endif
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 */
	_endif(file) {
		file.endIf();
	}

	/**
	 * Directive #set
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_set(file, value) {
		value = value.trim();

		if (!value) {
			throw new Error('Bad "set" directive');
		}

		file.addSet(value);
	}

	/**
	 * Directive #unset
	 *
	 * @private
	 * @param {!FileStructure} file - a file structure
	 * @param {string} value - a value of the directive
	 */
	_unset(file, value) {
		value = value.trim();

		if (!value) {
			throw new Error('Bad "unset" directive');
		}

		file.addUnset(value);
	}
}
<|endoftext|>


<commit_before>'use strict';

import oo from './oo'

/*
  An iterator for arrays.
  @class
  @param {Array} arr
 */
function ArrayIterator(arr) {
  this.arr = arr;
  this.pos = -1;
}

ArrayIterator.Prototype = function() {

  this._isArrayIterator = true;

  /**
    @returns {Boolean} true if there is another child node left.
   */
  this.hasNext = function() {
    return this.pos < this.arr.length - 1;
  };

  /**
    Increments the iterator providing the next child node.

    @returns {HTMLElement} The next child node.
   */
  this.next = function() {
    this.pos += 1;
    var next = this.arr[this.pos];
    return next;
  };

  /**
    Decrements the iterator.
   */
  this.back = function() {
    if (this.pos >= 0) {
      this.pos -= 1;
    }
    return this;
  };

};

oo.initClass(ArrayIterator);

export default ArrayIterator;
<commit_msg>ES6 ArrayIterator.<commit_after>/*
  An iterator for arrays.

  @class
  @param {Array} arr
 */
class ArrayIterator {

  constructor(arr) {
    this.arr = arr
    this.pos = -1
  }

  get _isArrayIterator() {
    return true
  }

  /**
    @returns {Boolean} true if there is another child node left.
   */
  hasNext() {
    return this.pos < this.arr.length - 1
  }

  /**
    Increments the iterator providing the next child node.

    @returns {HTMLElement} The next child node.
   */
  next() {
    this.pos += 1
    var next = this.arr[this.pos]
    return next
  }

  /**
    Decrements the iterator.
   */
  back() {
    if (this.pos >= 0) {
      this.pos -= 1
    }
    return this
  }
}

export default ArrayIterator;
<|endoftext|>