<commit_before>/**
 * Handles all template-rendering functionality.
 *
 * @module
 */

'use strict';
var uriRoutes,
  log = require('./log'),
  siteService = require('./sites'),
  nunjucks = require('nunjucks-filters')(),
  multiplex = require('multiplex-templates')({nunjucks: nunjucks}),
  references = require('./references'),
  schema = require('./schema'),
  _ = require('lodash'),
  chalk = require('chalk'),
  ignoreDataProperty = 'ignore-data',
  is = require('./assert-is');

/**
 * Add state to result, which adds functionality and information about this request to the templates.
 * @param options
 * @param res
 * @returns {Function}
 */
function applyOptions(options, res) {
  return function (result) {
    var state,
      site = siteService.sites()[res.locals.site],
      locals = res.locals;

    //remove all the properties mentioned here
    if (options[ignoreDataProperty]) {
      //todo: Maybe allow deep-has omiting i.e., ignore-data=story.some.thing,story.thing
      result = _.omit(result, options[ignoreDataProperty]);
    }

    //apply rule:  options.template > result.template
    if (options.template) {
      result.template = options.template;
    }

    //report the end result of what we've done
    log.info(chalk.dim('serving ' + require('util').inspect(result, true, 5)));

    //options are done at this point; now mix with the data and bind into a self-referencing chain of globals
    state = {
      site: site,
      locals: locals,
      getTemplate: references.getTemplate
    };

    //the circle of life;  this is equivalent to what was here before.
    _.assign(state, result);
    state.state = state;

    return state;
  };
}

/**
 * Render a template based on some generic data provided.
 */
function renderTemplate() {
  return function (data) {
    //assertions
    is(data.template, 'data.template');

    return multiplex.render(references.getTemplate(data.template), data);
  };
}

/**
 *
 * @param {{data: {template: string}}} options
 * @param res
 * @returns {Promise}
 */
function renderByConfiguration(options, res) {
  //assertions (Note: since this function isn't an entry point, maybe we can remove these assertions?)
  is(res.locals, 'res.locals');
  is(res.locals.site, 'res.locals.site');
  is.object(options.data, 'options.data');
  is.string(options.data.template, 'options.data.template');

  return schema.resolveDataReferences(options.data)
    .then(applyOptions(options, res))
    .then(renderTemplate());
}

/**
 * Given a component reference of the form /components/<name> or /components/<name>/instances/<id>, render that component.
 *
 * @param {string} componentReference
 * @param res
 * @param {{}} [options]  Optional parameters for rendering
 * @returns {Promise}
 */
function renderComponent(componentReference, res, options) {
  options = options || {};
  
  //assertions
  is(res.locals, 'res.locals');
  is(res.locals.site, 'res.locals.site');
  is(componentReference, 'component reference');
  var componentName = is(references.getComponentName(componentReference), 'component name');

  return references.getComponentData(componentReference, res.locals)
    .then(function (data) {
      //assertions
      is.object(data);

      //apply rule: data.template > componentName
      //  If there is a template mentioned in the data, assume they know what they're doing
      data.template = data.template || componentName;
      options.data = data;
      return renderByConfiguration(options, res);
    });
}

/**
 * Maps strings in arrays of layoutData into the properties of pageData
 * @param {object} pageData
 * @param {object} layoutData
 * @returns {*}
 */
function mapLayoutToPageData(pageData, layoutData) {
  var lists = _.listDeepObjects(layoutData, _.isArray);

  _.each(lists, function (list) {
    _.each(list, function (item, index, list) {
      //if there is a string, try to find a match
      if (_.isString(item)) {
        if (pageData[item]) {
          //if there is a match, assign whatever matches in pageData as a reference
          list[index] = {
            _ref: pageData[item]
          };
        } else {
          //if there is no match, that's a problem with configuration/editing
          log.warn('Missing reference in layout: ', item, pageData, layoutData)
        }
      }
    });
  });
  return layoutData;
}


/**
 * Given a page reference of the form /pages/<pagename>, lookup the alias and render that component
 * @param {string} pageReference
 * @param res<commit_msg>add semi-colon for styling<commit_after>/**
 * Handles all template-rendering functionality.
 *
 * @module
 */

'use strict';
var uriRoutes,
  log = require('./log'),
  siteService = require('./sites'),
  nunjucks = require('nunjucks-filters')(),
  multiplex = require('multiplex-templates')({nunjucks: nunjucks}),
  references = require('./references'),
  schema = require('./schema'),
  _ = require('lodash'),
  chalk = require('chalk'),
  ignoreDataProperty = 'ignore-data',
  is = require('./assert-is');

/**
 * Add state to result, which adds functionality and information about this request to the templates.
 * @param options
 * @param res
 * @returns {Function}
 */
function applyOptions(options, res) {
  return function (result) {
    var state,
      site = siteService.sites()[res.locals.site],
      locals = res.locals;

    //remove all the properties mentioned here
    if (options[ignoreDataProperty]) {
      //todo: Maybe allow deep-has omiting i.e., ignore-data=story.some.thing,story.thing
      result = _.omit(result, options[ignoreDataProperty]);
    }

    //apply rule:  options.template > result.template
    if (options.template) {
      result.template = options.template;
    }

    //report the end result of what we've done
    log.info(chalk.dim('serving ' + require('util').inspect(result, true, 5)));

    //options are done at this point; now mix with the data and bind into a self-referencing chain of globals
    state = {
      site: site,
      locals: locals,
      getTemplate: references.getTemplate
    };

    //the circle of life;  this is equivalent to what was here before.
    _.assign(state, result);
    state.state = state;

    return state;
  };
}

/**
 * Render a template based on some generic data provided.
 */
function renderTemplate() {
  return function (data) {
    //assertions
    is(data.template, 'data.template');

    return multiplex.render(references.getTemplate(data.template), data);
  };
}

/**
 *
 * @param {{data: {template: string}}} options
 * @param res
 * @returns {Promise}
 */
function renderByConfiguration(options, res) {
  //assertions (Note: since this function isn't an entry point, maybe we can remove these assertions?)
  is(res.locals, 'res.locals');
  is(res.locals.site, 'res.locals.site');
  is.object(options.data, 'options.data');
  is.string(options.data.template, 'options.data.template');

  return schema.resolveDataReferences(options.data)
    .then(applyOptions(options, res))
    .then(renderTemplate());
}

/**
 * Given a component reference of the form /components/<name> or /components/<name>/instances/<id>, render that component.
 *
 * @param {string} componentReference
 * @param res
 * @param {{}} [options]  Optional parameters for rendering
 * @returns {Promise}
 */
function renderComponent(componentReference, res, options) {
  options = options || {};
  
  //assertions
  is(res.locals, 'res.locals');
  is(res.locals.site, 'res.locals.site');
  is(componentReference, 'component reference');
  var componentName = is(references.getComponentName(componentReference), 'component name');

  return references.getComponentData(componentReference, res.locals)
    .then(function (data) {
      //assertions
      is.object(data);

      //apply rule: data.template > componentName
      //  If there is a template mentioned in the data, assume they know what they're doing
      data.template = data.template || componentName;
      options.data = data;
      return renderByConfiguration(options, res);
    });
}

/**
 * Maps strings in arrays of layoutData into the properties of pageData
 * @param {object} pageData
 * @param {object} layoutData
 * @returns {*}
 */
function mapLayoutToPageData(pageData, layoutData) {
  var lists = _.listDeepObjects(layoutData, _.isArray);

  _.each(lists, function (list) {
    _.each(list, function (item, index, list) {
      //if there is a string, try to find a match
      if (_.isString(item)) {
        if (pageData[item]) {
          //if there is a match, assign whatever matches in pageData as a reference
          list[index] = {
            _ref: pageData[item]
          };
        } else {
          //if there is no match, that's a problem with configuration/editing
          log.warn('Missing reference in layout: ', item, pageData, layoutData);
        }
      }
    });
  });
  return layoutData;
}


/**
 * Given a page reference of the form /pages/<pagename>, lookup the alias and render that component
 * @param {string} pageReference
 * @param res
{'fraction_non_alphanumeric': 0.13139818302681144, 'fraction_numerical': 0.00022158209616662973, 'mean_word_length': 3.3129479216435738, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5750087', 'n_tokens_mistral': 2633, 'n_tokens_neox': 2593, 'n_words': 776}