<commit_before>'use strict';

// Node modules
const Cheerio = require('cheerio');
const Fs = require('fs');
const He = require('he');
const Moment = require('moment');
const Path = require('path');
const Striptags = require('striptags');
const Trim = require('trim');
const TruncateHtml = require('truncate-html');

// Local modules
const SignedUrl = require(Path.join(__basedir, 'source/modules/signed_url.js'));
const Slug = require(Path.join(__basedir, 'source/modules/slug.js'));

module.exports = (dust) => {

  //
  // Outputs one or more classes that should be applied to the <body> element.
  //
  // Examples:
  //
  //  {@bodyClass/}
  //
  dust.helpers.bodyClass = (chunk, context) => {
    const locals = context.options.locals;
    let template = context.options.view.name;
    let bodyClass = '';

    // Remove .dust and convert the template name a slug
    template = Slug(template.replace(/\.dust$/, ''));

    // Template class
    bodyClass += 'template-' + template;

    // Homepage class
    if(locals.Request.isHomepage) {
      bodyClass += ' page-homepage';
    }

    // Pagination class
    if(locals.pagination) {
      bodyClass += ' page-' + locals.pagination.currentPage;
    }

    return chunk.write(bodyClass);
  };

  //
  // Outputs the post content. If no post is specified, the current post context will be used.
  //
  // Examples:
  //
  //  {@content/}
  //  {@content editable="true"/}
  //
  dust.helpers.content = (chunk, context, bodies, params) => {
    let isEditor = context.options.locals.isEditor;
    let editable = (context.resolve(params.editable) + '') === 'true';
    let post = context.resolve(params.post);
    let content = post ? post.content : context.get('content') || '';

    // Add editable wrappers when the post is being rendered in the editor
    if(isEditor && editable) {
      content = `
        <div
          data-postleaf-region="content"
          data-postleaf-html="` + He.encode(content, { useNamedReferences: true }) + `"
        >
          ` + content + `
        </div>
      `;
    }

    return chunk.write(content);
  };

  //
  // Generates a signed URL for a dynamic image.
  //
  // Examples:
  //
  //  {@dynamicImage src="/uploads/2017/03/image.jpg" width="100"/}
  //  {@dynamicImage src="/uploads/2017/03/image.jpg" grayscale="true" blur="10" rotate="90"/}
  //
  dust.helpers.dynamicImage = (chunk, context, bodies, params) => {
    let src = context.resolve(params.src);
    let query = [];

    // Don't modify full-qualified URLs or hashes
    if(typeof src === 'string' && src.match(/^(http|https|mailto|#):/i)) {
      return chunk.write(src);
    }

    // Build the query string
    Object.keys(params).forEach((key) => {
      if(key !== 'src') {
        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
      }
    });

    // Generate a signed URL
    return chunk.write(
      SignedUrl.sign(src + '?' + query.join('&'), process.env.AUTH_SECRET)
    );
  };

  //
  // Generates an excerpt. If no content is specified, the current post context will be used.
  //
  // Examples:
  //
  //  {@excerpt/}
  //  {@excerpt content="<p>...</p>" paragraphs="2" words="50" tags="a,em,strong"/}
  //
  dust.helpers.excerpt = (chunk, context, bodies, params) => {
    let content = context.resolve(params.content) || context.get('content') || '';
    let paragraphs = context.resolve(params.paragraphs) || 1;
    let words = parseInt(context.resolve(params.words));
    let tags = context.resolve(params.tags) || 'a,abbr,b,bdi,bdo,blockquote,br,cite,code,data,dd,del,dfn,dl,dt,em,i,ins,kbd,li,mark,ol,p,pre,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,ul,var,wbr';
    let $ = Cheerio.load(content);
    let excerpt = '';

    // Get paragraphs from content (top level matches only)
    let p = $('p:not(* > p)').slice(0, paragraphs);

    // Concatenate matching paragraphs
    $(p).each((index, el) => {
      excerpt += '<p>' + $(el).html() + '</p>';
    });

    // Truncate by words
    if(words) {
      excerpt = TruncateHtml(excerpt, words, {
        byWords: true,
        ellipsis: '…'
      });
    }

    // Strip disallowed tags
    if(tags) {
      excerpt = Striptags(excerpt, tags.split(',').map((val) => Trim(val)));
    }

    return chunk.write(excerpt);
  };

  //
  // Outputs required foot data.
  //
  // Examples:
  //
  //  {@foot/}
  //
  dust.helpers.foot = (chunk, context) => {
    // Async wrapper
    return chunk.map((chunk) => {
      const locals = context.options.locals;
      const MakeUrl = require(Path.join(__basedir, 'source/modules/make_url.js'))(locals.Settings);

      let isEditor = context.options.locals.isEditor;
      let toolbar = Path.join(__basedir, 'source/views/partials/theme_toolbar.dust');
      let script = MakeUrl.raw('assets/js/tinymce.bundle.js');
      let html = '';

      // Editor scripts
      if(isEditor) {
        html += `
          <script
            data-postleaf-editor="scripts"
            src="` + He.encode(script, { useNamedReferences: true }) + `"
          ></script>
        `;
      }

      // Inject foot code
      html += locals.Settings.footCode || '';
      html += '\n';

      // Render the theme toolbar partial
      if(locals.User) {
        chunk.partial(toolbar, context).end();
      }<commit_msg>Remove unused dep<commit_after>'use strict';

// Node modules
const Cheerio = require('cheerio');
const He = require('he');
const Moment = require('moment');
const Path = require('path');
const Striptags = require('striptags');
const Trim = require('trim');
const TruncateHtml = require('truncate-html');

// Local modules
const SignedUrl = require(Path.join(__basedir, 'source/modules/signed_url.js'));
const Slug = require(Path.join(__basedir, 'source/modules/slug.js'));

module.exports = (dust) => {

  //
  // Outputs one or more classes that should be applied to the <body> element.
  //
  // Examples:
  //
  //  {@bodyClass/}
  //
  dust.helpers.bodyClass = (chunk, context) => {
    const locals = context.options.locals;
    let template = context.options.view.name;
    let bodyClass = '';

    // Remove .dust and convert the template name a slug
    template = Slug(template.replace(/\.dust$/, ''));

    // Template class
    bodyClass += 'template-' + template;

    // Homepage class
    if(locals.Request.isHomepage) {
      bodyClass += ' page-homepage';
    }

    // Pagination class
    if(locals.pagination) {
      bodyClass += ' page-' + locals.pagination.currentPage;
    }

    return chunk.write(bodyClass);
  };

  //
  // Outputs the post content. If no post is specified, the current post context will be used.
  //
  // Examples:
  //
  //  {@content/}
  //  {@content editable="true"/}
  //
  dust.helpers.content = (chunk, context, bodies, params) => {
    let isEditor = context.options.locals.isEditor;
    let editable = (context.resolve(params.editable) + '') === 'true';
    let post = context.resolve(params.post);
    let content = post ? post.content : context.get('content') || '';

    // Add editable wrappers when the post is being rendered in the editor
    if(isEditor && editable) {
      content = `
        <div
          data-postleaf-region="content"
          data-postleaf-html="` + He.encode(content, { useNamedReferences: true }) + `"
        >
          ` + content + `
        </div>
      `;
    }

    return chunk.write(content);
  };

  //
  // Generates a signed URL for a dynamic image.
  //
  // Examples:
  //
  //  {@dynamicImage src="/uploads/2017/03/image.jpg" width="100"/}
  //  {@dynamicImage src="/uploads/2017/03/image.jpg" grayscale="true" blur="10" rotate="90"/}
  //
  dust.helpers.dynamicImage = (chunk, context, bodies, params) => {
    let src = context.resolve(params.src);
    let query = [];

    // Don't modify full-qualified URLs or hashes
    if(typeof src === 'string' && src.match(/^(http|https|mailto|#):/i)) {
      return chunk.write(src);
    }

    // Build the query string
    Object.keys(params).forEach((key) => {
      if(key !== 'src') {
        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
      }
    });

    // Generate a signed URL
    return chunk.write(
      SignedUrl.sign(src + '?' + query.join('&'), process.env.AUTH_SECRET)
    );
  };

  //
  // Generates an excerpt. If no content is specified, the current post context will be used.
  //
  // Examples:
  //
  //  {@excerpt/}
  //  {@excerpt content="<p>...</p>" paragraphs="2" words="50" tags="a,em,strong"/}
  //
  dust.helpers.excerpt = (chunk, context, bodies, params) => {
    let content = context.resolve(params.content) || context.get('content') || '';
    let paragraphs = context.resolve(params.paragraphs) || 1;
    let words = parseInt(context.resolve(params.words));
    let tags = context.resolve(params.tags) || 'a,abbr,b,bdi,bdo,blockquote,br,cite,code,data,dd,del,dfn,dl,dt,em,i,ins,kbd,li,mark,ol,p,pre,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,ul,var,wbr';
    let $ = Cheerio.load(content);
    let excerpt = '';

    // Get paragraphs from content (top level matches only)
    let p = $('p:not(* > p)').slice(0, paragraphs);

    // Concatenate matching paragraphs
    $(p).each((index, el) => {
      excerpt += '<p>' + $(el).html() + '</p>';
    });

    // Truncate by words
    if(words) {
      excerpt = TruncateHtml(excerpt, words, {
        byWords: true,
        ellipsis: '…'
      });
    }

    // Strip disallowed tags
    if(tags) {
      excerpt = Striptags(excerpt, tags.split(',').map((val) => Trim(val)));
    }

    return chunk.write(excerpt);
  };

  //
  // Outputs required foot data.
  //
  // Examples:
  //
  //  {@foot/}
  //
  dust.helpers.foot = (chunk, context) => {
    // Async wrapper
    return chunk.map((chunk) => {
      const locals = context.options.locals;
      const MakeUrl = require(Path.join(__basedir, 'source/modules/make_url.js'))(locals.Settings);
      let isEditor = context.options.locals.isEditor;
      let toolbar = Path.join(__basedir, 'source/views/partials/theme_toolbar.dust');
      let script = MakeUrl.raw('assets/js/tinymce.bundle.js');
      let html = '';

      // Editor scripts
      if(isEditor) {
        html += `
          <script
            data-postleaf-editor="scripts"
            src="` + He.encode(script, { useNamedReferences: true }) + `"
          ></script>
        `;
      }

      // Inject foot code
      html += locals.Settings.footCode || '';
      html += '\n';

      // Render the theme toolbar partial
      if(locals.User) {
        chunk.partial(toolbar, context).end();
      }
{'fraction_non_alphanumeric': 0.17241703487825516, 'fraction_numerical': 0.004512550531164802, 'mean_word_length': 2.598782138024357, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 43, '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': '5549980', 'n_tokens_mistral': 3579, 'n_tokens_neox': 3444, 'n_words': 727}