<commit_before>var fs = require('fs'),
    path = require('path'),
    async = require('async'),
    config = {};

// Attempt to grab the config file
try {
  config = require(path.join(process.cwd(), 'config.jojo.json'));
} catch (e) {
}

// Generate helper object for extension
function objExtend(baseObj, newObj) {
  var retObj = {};

  // Copy the properties from the base object
  Object.getOwnPropertyNames(baseObj).forEach(function (prop) {
    retObj[prop] = baseObj[prop];
  });

  // Copy the properties from the new object
  Object.getOwnPropertyNames(newObj).forEach(function (prop) {
    retObj[prop] = newObj[prop];
  });

  // Return the composition of the objects
  return retObj;
}

// TODO: Switch over to a static file creator and file watching (don't forget about creation of new files -- directory watching? or async setTimeout that watches dir)

// TODO: Make separate methods for base page, article page, and rss which are tied back to jojo (all .use's)

function sendIndex(req, res, next) {
  var app = req.app,
      yoyo = new Yoyo(app),
      indexView = app.settings['jojo index view'] || 'pages/index';

  yoyo.readArticles(null, function (err, articles) {
    // If there is an error, log it and move to next fn
    if (err) {
      console.error(err);
      return next();
    }

    // Otherwise, render
    var renderObj = objExtend(jojo.config, {'articles': articles});
    res.render(indexView, renderObj);
  });
}

function sendArticle(req, res, next) {
  var app = req.app,
      yoyo = new Yoyo(app),
      articleView = settings['jojo article view'] || 'pages/article';

  yoyo.readArticles(null, function (err, articles) {
    // If there is an error, log it and move to next fn
    if (err) {
      console.error(err);
      return next();
    }

    // Otherwise, render
    var renderObj = objExtend(jojo.config, {'articles': articles});
    res.render(indexView, renderObj);
  });
}

function sendRss(req, res, next) {
  var app = req.app,
      yoyo = new Yoyo(app),
      rssView = app.settings['jojo rss view'] || 'xml';

  yoyo.readArticles(null, function (err, articles) {
    // If there is an error, log it and move to next fn
    if (err) {
      console.error(err);
      return next();
    }

    // Otherwise, render
    var renderObj = objExtend(jojo.config, {'layout': false, 'articles': articles});
    res.render(rssView, renderObj);
  });
}

/**
 * jojo - 10 second blog engine for hackers (in javascript)
 * Intended to be use with an express server via .use()
 * @param {Object<ExpressRequest>} req
 * @param {Object<ExpressResponse>} res
 * @param {Function} next
 */
function jojo(req, res, next) {
  // Grab the url, jojoBase
  // TODO: Figure out how to use express route testing
  var url = req.url,
      app = req.app,
      settings = app.settings,
      basepath = settings['jojo basepath'] || '/',
      cwd = process.cwd(),
      articleDir = settings['jojo articles'] || path.join(cwd, 'articles'),
      articleView = settings['jojo article view'] || 'pages/article',
      baseIndex;

  // If we are in the base route
  if (url === basepath) {
    // Generate an index page with articles
    return sendIndex(req, res, next);
  } else {
    baseIndex = url.indexOf(basepath);
    if (baseIndex !== -1) {
      // Load the articles
      // return yoyo.readArticles(articleDir, function (err, articles) {
        // // If there is an error, log it and run the next function
        // if (err) {
          // console.error(err);
          // return next();
        // }

        var articleUrl = url.slice(baseIndex + 1);

        // If we are viewing the index.xml, load it
        if (articleUrl.indexOf('index.xml') !== -1) {
          return sendRss(req, res, next);
        }

        // Otherwise, try to find a matching article
        var i = 0,
            len = articles.length,
            article,
            articleFound = false;
        for (; i < len; i++) {
          article = articles[i];

          // If it matches our article url, save it
          if (articleUrl.indexOf(article.url) !== -1) {
            articleFound = true;
            break;
          }
        }

        // If the article was found, use it
        if (articleFound) {
          // If there is an articleView, render through it
          if (articleView) {
            var renderObj = objExtend(article, {'config': jojo.config});
            res.render(articleView, renderObj);
          } else {
          // Otherwise, send the content
            res.send(article.content);
          }
        } else {
          // Otherwise, call the next method
          next();
        }
      // });
    }
  }

  // Otherwise, call the next method
  next();
}

// Sugar method for creating a server
jojo.createServer = function () {
  // Create a server an express server
  var express = require('express'),
      server = express.createServer.apply(express, arguments);

  // Bind jojo to the server
  server.use(jojo);

  // Return the bound server
  return server;
};

// Expose the config object
jojo.config = config;

// TODO: jojo.static which preloads views and does not dynamically fetch during each request

// State (and sanity) preserver for jojo
function Yoyo(app) {
  // Fallback app<commit_msg>Article method working<commit_after>var fs = require('fs'),
    path = require('path'),
    async = require('async'),
    config = {};

// Attempt to grab the config file
try {
  config = require(path.join(process.cwd(), 'config.jojo.json'));
} catch (e) {
}

// Generate helper object for extension
function objExtend(baseObj, newObj) {
  var retObj = {};

  // Copy the properties from the base object
  Object.getOwnPropertyNames(baseObj).forEach(function (prop) {
    retObj[prop] = baseObj[prop];
  });

  // Copy the properties from the new object
  Object.getOwnPropertyNames(newObj).forEach(function (prop) {
    retObj[prop] = newObj[prop];
  });

  // Return the composition of the objects
  return retObj;
}

// TODO: Switch over to a static file creator and file watching (don't forget about creation of new files -- directory watching? or async setTimeout that watches dir)

// TODO: Make separate methods for base page, article page, and rss which are tied back to jojo (all .use's)

function sendIndex(req, res, next) {
  var app = req.app,
      yoyo = new Yoyo(app),
      indexView = app.settings['jojo index view'] || 'pages/index';

  yoyo.readArticles(null, function (err, articles) {
    // If there is an error, log it and move to next fn
    if (err) {
      console.error(err);
      return next();
    }

    // Otherwise, render
    var renderObj = objExtend(jojo.config, {'articles': articles});
    res.render(indexView, renderObj);
  });
}

function sendArticle(req, res, next) {
  var app = req.app,
      yoyo = new Yoyo(app),
      url = req.url,
      settings = app.settings,
      basepath = settings['jojo basepath'] || '/',
      articleView = settings['jojo article view'] || 'pages/article';

  yoyo.readArticles(null, function (err, articles) {
    // If there is an error, log it and move to next fn
    if (err) {
      console.error(err);
      return next();
    }
    
    // Try to find a matching article
    var articleUrl = url.slice(url.indexOf(basepath) + basepath.length),
        i = 0,
        len = articles.length,
        article,
        articleFound = false;
    for (; i < len; i++) {
      article = articles[i];

      // If it matches our article url, save it
      if (articleUrl.indexOf(article.url) !== -1) {
        articleFound = true;
        break;
      }
    }

    // If the article was found, use it
    if (articleFound) {
      // If there is an articleView, render through it
      var renderObj = objExtend(article, {'config': jojo.config});
      res.render(articleView, renderObj);
    } else {
      // Otherwise, call the next method
      next();
    }
  });
}

function sendRss(req, res, next) {
  var app = req.app,
      yoyo = new Yoyo(app),
      rssView = app.settings['jojo rss view'] || 'xml';

  yoyo.readArticles(null, function (err, articles) {
    // If there is an error, log it and move to next fn
    if (err) {
      console.error(err);
      return next();
    }

    // Otherwise, render
    var renderObj = objExtend(jojo.config, {'layout': false, 'articles': articles});
    res.render(rssView, renderObj);
  });
}

/**
 * jojo - 10 second blog engine for hackers (in javascript)
 * Intended to be use with an express server via .use()
 * @param {Object<ExpressRequest>} req
 * @param {Object<ExpressResponse>} res
 * @param {Function} next
 */
function jojo(req, res, next) {
  // Grab the url, jojoBase
  // TODO: Figure out how to use express route testing
  var url = req.url,
      app = req.app,
      basepath = app.settings['jojo basepath'] || '/';

  // If we are in the base route
  if (url === basepath) {
    // Generate an index page with articles
    return sendIndex(req, res, next);
  } else if (url.indexOf(basepath) !== -1) {
    // If we are viewing the index.xml, load it
    if (url.indexOf('index.xml') !== -1) {
      return sendRss(req, res, next);
    } else {
    // Otherwise, attempt to send an article
      return sendArticle(req, res, next);
    }
  }

  // Otherwise, call the next method
  next();
}

// Sugar method for creating a server
jojo.createServer = function () {
  // Create a server an express server
  var express = require('express'),
      server = express.createServer.apply(express, arguments);

  // Bind jojo to the server
  server.use(jojo);

  // Return the bound server
  return server;
};

// Expose the config object
jojo.config = config;

// TODO: jojo.static which preloads views and does not dynamically fetch during each request

// State (and sanity) preserver for jojo
function Yoyo(app) {
  // Fallback app
{'fraction_non_alphanumeric': 0.12660028449502134, 'fraction_numerical': 0.0013208697419223735, 'mean_word_length': 2.5663043478260867, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 6}, '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': '5083086', 'n_tokens_mistral': 3012, 'n_tokens_neox': 2927, 'n_words': 1000}