<commit_before>const path = require('path');
const os = require('os');
const multer = require('multer');
const fs = require('fs-extra');
const errors = require('@tryghost/errors');
const config = require('../../../../shared/config');
const i18n = require('../../../../shared/i18n');
const logging = require('@tryghost/logging');

const upload = {
    enabledClear: config.get('uploadClear') || true,
    multer: multer({dest: os.tmpdir()})
};

const deleteSingleFile = file => fs.unlink(file.path).catch(err => logging.error(err));

const single = name => (req, res, next) => {
    const singleUpload = upload.multer.single(name);
    singleUpload(req, res, (err) => {
        if (err) {
            return next(err);
        }
        if (upload.enabledClear) {
            const deleteFiles = () => {
                res.removeListener('finish', deleteFiles);
                res.removeListener('close', deleteFiles);
                if (!req.disableUploadClear) {
                    if (req.files) {
                        return req.files.forEach(deleteSingleFile);
                    }

                    if (req.file) {
                        return deleteSingleFile(req.file);
                    }
                }
            };
            if (!req.disableUploadClear) {
                res.on('finish', deleteFiles);
                res.on('close', deleteFiles);
            }
        }
        next();
    });
};

const checkFileExists = (fileData) => {
    return !!(fileData.mimetype && fileData.path);
};

const checkFileIsValid = (fileData, types, extensions) => {
    const type = fileData.mimetype;

    if (types.includes(type) && extensions.includes(fileData.ext)) {
        return true;
    }

    return false;
};

const validation = function (options) {
    const type = options.type;

    // if we finish the data/importer logic, we forward the request to the specified importer
    return function uploadValidation(req, res, next) {
        const extensions = (config.get('uploads')[type] && config.get('uploads')[type].extensions) || [];
        const contentTypes = (config.get('uploads')[type] && config.get('uploads')[type].contentTypes) || [];

        req.file = req.file || {};
        req.file.name = req.file.originalname;
        req.file.type = req.file.mimetype;

        // Check if a file was provided
        if (!checkFileExists(req.file)) {
            return next(new errors.ValidationError({
                message: i18n.t(`errors.api.${type}.missingFile`)
            }));
        }

        req.file.ext = path.extname(req.file.name).toLowerCase();

        // Check if the file is valid
        if (!checkFileIsValid(req.file, contentTypes, extensions)) {
            return next(new errors.UnsupportedMediaTypeError({
                message: i18n.t(`errors.api.${type}.invalidFile`, {extensions: extensions})
            }));
        }

        next();
    };
};

module.exports = {
    single,
    validation
};

// Exports for testing only
module.exports._test = {
    checkFileExists,
    checkFileIsValid
};
<commit_msg>Replaced i18n.t w/ tpl in upload middleware<commit_after>const path = require('path');
const os = require('os');
const multer = require('multer');
const fs = require('fs-extra');
const errors = require('@tryghost/errors');
const config = require('../../../../shared/config');
const tpl = require('@tryghost/tpl');
const logging = require('@tryghost/logging');

const messages = {
    db: {
        missingFile: 'Please select a database file to import.',
        invalidFile: 'Unsupported file. Please try any of the following formats: {extensions}'
    },
    redirects: {
        missingFile: 'Please select a JSON file.',
        invalidFile: 'Please select a valid JSON file to import.'
    },
    routes: {
        missingFile: 'Please select a YAML file.',
        invalidFile: 'Please select a valid YAML file to import.'
    },
    themes: {
        missingFile: 'Please select a theme.',
        invalidFile: 'Please select a valid zip file.'
    },
    images: {
        missingFile: 'Please select an image.',
        invalidFile: 'Please select a valid image.'
    },
    icons: {
        missingFile: 'Please select an icon.',
        invalidFile: 'Icon must be a square .ico or .png file between 60px – 1,000px, under 100kb.'
    }
};

const upload = {
    enabledClear: config.get('uploadClear') || true,
    multer: multer({dest: os.tmpdir()})
};

const deleteSingleFile = file => fs.unlink(file.path).catch(err => logging.error(err));

const single = name => (req, res, next) => {
    const singleUpload = upload.multer.single(name);
    singleUpload(req, res, (err) => {
        if (err) {
            return next(err);
        }
        if (upload.enabledClear) {
            const deleteFiles = () => {
                res.removeListener('finish', deleteFiles);
                res.removeListener('close', deleteFiles);
                if (!req.disableUploadClear) {
                    if (req.files) {
                        return req.files.forEach(deleteSingleFile);
                    }

                    if (req.file) {
                        return deleteSingleFile(req.file);
                    }
                }
            };
            if (!req.disableUploadClear) {
                res.on('finish', deleteFiles);
                res.on('close', deleteFiles);
            }
        }
        next();
    });
};

const checkFileExists = (fileData) => {
    return !!(fileData.mimetype && fileData.path);
};

const checkFileIsValid = (fileData, types, extensions) => {
    const type = fileData.mimetype;

    if (types.includes(type) && extensions.includes(fileData.ext)) {
        return true;
    }

    return false;
};

const validation = function (options) {
    const type = options.type;

    // if we finish the data/importer logic, we forward the request to the specified importer
    return function uploadValidation(req, res, next) {
        const extensions = (config.get('uploads')[type] && config.get('uploads')[type].extensions) || [];
        const contentTypes = (config.get('uploads')[type] && config.get('uploads')[type].contentTypes) || [];

        req.file = req.file || {};
        req.file.name = req.file.originalname;
        req.file.type = req.file.mimetype;

        // Check if a file was provided
        if (!checkFileExists(req.file)) {
            return next(new errors.ValidationError({
                message: tpl(messages[type].missingFile)
            }));
        }

        req.file.ext = path.extname(req.file.name).toLowerCase();

        // Check if the file is valid
        if (!checkFileIsValid(req.file, contentTypes, extensions)) {
            return next(new errors.UnsupportedMediaTypeError({
                message: tpl(messages[type].invalidFile, {extensions: extensions})
            }));
        }

        next();
    };
};

module.exports = {
    single,
    validation
};

// Exports for testing only
module.exports._test = {
    checkFileExists,
    checkFileIsValid
};
<|endoftext|>


<commit_before>const fs = require('fs');

let settings = {};

function main()
{
    let args = getArguments();
    if (args.length < 1)
    {
        printHelp();
    }
    else
    {
        if (args[0] == '-h' || args[0] == '-help' || args[0] == '--help')
        {
            printHelp();
        }
        else if (args[0] == '-V' || args[0] == '-version' || args[0] == '--version')
        {
            printVersion();
        }
        else
        {
            let file = readFile(args[0]);
            if (file != null)
            {
                parseSettings(file);
                let pages = parsePages();
                for (index in pages)
                {
                    console.log(pages);

                    let page = pages[index];
                    let template = getTemplate(page.template);
                    let html = generateHTML(template, page);

                    //@@TODO: write html out
                    console.log(html);
                }
            }
        }
    }
}

//
// Get the command line arguments.
//
function getArguments()
{
    return process.argv.slice(2);
}

//
// Read in a utf8-encoded file.
//
function readFile(path)
{
    let data = '';
    try
    {
        data = fs.readFileSync(path, 'utf8');
        return data;
    }
    catch (err)
    {
        if (err.errno == -2)
        {
            console.log("File not found: " + path);
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Write out a ut8-encoded file.
//
function writeFile(path, data)
{
    fs.writeFileSync(path, data);
}

//
// Get the names of files in a folder
//
function getFilenames(path)
{
    try {
        return fs.readdirSync(path);
    }
    catch (err) {
        if (err.errno == -2)
        {
            console.log("Path not found: " + path);
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Parse the settings JSON file.
//
function parseSettings(data)
{
    let json = JSON.parse(data);
    const properties = {
        // paths
        input_path: ".",
        output_path: ".",
        pages_path: "./pages",

        // style and structure information
        styles: ["./style.css"],
        templates: [
            // name:
            // styles:
            //
        ],

        // optimization options
        client_markdown: false,

    };

    for (property in properties)
    {
        if (json[property])
        {
            settings[property] = json[property];
        }
        else
        {
            settings[property] = properties[property];
        }
    }
}

//
// Parse the pages.
// Return page objects, which have metadata properties and a content property.
//
function parsePages()
{
    let filenames = getFilenames(settings.pages_path);
    let pages = [];
    let pageObjs = [];

    for (file in filenames)
    {
        pages.push(readFile(settings.pages_path + '/' + filenames[file]));
    }

    for (index in pages)
    {
        let page = pages[index];
        let pageObj = {};
        //@@TODO: transform page contents
        //console.log(pages[page]);

        // extract the metadata from the front of the file
        let pos = 0;
        let cur = page.charAt(pos);
        let metadata;
        while (cur != null)
        {
            if (cur == '-')
            {
                if (page.charAt(pos + 1) == '-' &&
                    page.charAt(pos + 2) == '-')
                {
                    pos++;
                    cur = page.charAt(pos);
                    metadata = '';

                    while (cur != '-' ||
                        page.charAt(pos + 1) != '-' ||
                        page.charAt(pos + 2) != '-')
                    {
                        metadata += cur;
                        pos++;
                        cur = page.charAt(pos);
                    }

                    // trim the front:
                    metadata = metadata.slice(2);
                    if (metadata.charAt(0) == '\n' || metadata.charAt(0) == '\r')
                    {
                        metadata = metadata.slice(1);
                    }

                    console.log(metadata);

                    console.log("YAML found, page " + index);
                    break;
                }
            }
            else if (cur != '\n' && cur != ' ' && cur != '\t' && cur != '\r')
            {
                console.log("no valid YAML, page " + index);
                break;
            }

            pos++;
            cur = page.charAt(pos);
        }

        let dataObj = {};
        if (metadata)
        {
            dataObj = parseMetadata(metadata);
        }

        // defaults
        const properties = {
            title: null,
            template: null,
            // ...
        };

        // copy the dataObj properties into the pageObj.
        for (property in properties)
        {
            if (dataObj[property])
            {
                pageObj[property] = dataObj[property];
            }
            else
            {
                pageObj[property] = properties[property];
            }
        }

        pageObj.content = page;

        pageObjs.push(pageObj);
    }
    return pageObjs;
}

//
// Parse YAML metadata.
//
function parseMetadata(metadata)
{
    let lines = metadata.split('\n');
    let dataObj = {};
    for (i in lines)
    {
        let line = lines[i];
        let separator = line.indexOf(':');
        let k = line.slice(0, separator);
        let v = line.substr(separator + 1);
        k = k.trim();
        v = v.trim();
        console.log(k + ", " + v);
        dataObj[k] = v;
    }
    return dataObj;
}

//
// Get template information.
//
function getTemplate(name)
{
    if (settings.templates[name] != null)
    {
        return settings.templates[name];
    }
    else
    {
        //@@TODO: throw error and exit if template not found.
    }
}

//
// Generate HTML
//
function generateHTML(template, page)
{
    let html = '';
    function add(line)
    {
        html += line + '\n';
    }

    //@@TODO: add page contents

    add('<!DOCTYPE html>');
    add('');
        //@@TODO: finish html header
        add('\t');
            add('\t\t<title>' + page.title + '</title>')
            //@@TODO: add references to css and markdown parser as appropriate
        add('\t');
        add('\t');

        add('\t');

    html += '';
    return html;
}

function printHelp()
{
    console.log("Usage: node ossg.js [options] file");
    console.log("Oscar's static site generator. Compile templates, css, & posts into a website.");
}

function printVersion()
{
    console.log("OSSG: v0.0.1");
}

main();<commit_msg>Add the start of the templating engine.<commit_after>const fs = require('fs');

let settings = {};

function main()
{
    let args = getArguments();
    if (args.length < 1)
    {
        printHelp();
    }
    else
    {
        if (args[0] == '-h' || args[0] == '-help' || args[0] == '--help')
        {
            printHelp();
        }
        else if (args[0] == '-V' || args[0] == '-version' || args[0] == '--version')
        {
            printVersion();
        }
        else
        {
            let file = readFile(args[0]);
            if (file != null)
            {
                parseSettings(file);
                let pages = parsePages();
                for (index in pages)
                {
                    let page = pages[index];
                    let template = getTemplate(page.template);
                    let html = generateHTML(template, page);

                    //@@TODO: write html out
                    console.log(html);
                }
            }
        }
    }
}

//
// Get the command line arguments.
//
function getArguments()
{
    return process.argv.slice(2);
}

//
// Read in a utf8-encoded file.
//
function readFile(path)
{
    let data = '';
    try
    {
        data = fs.readFileSync(path, 'utf8');
        return data;
    }
    catch (err)
    {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "File not found: " + path;
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Write out a ut8-encoded file.
//
function writeFile(path, data)
{
    fs.writeFileSync(path, data);
}

//
// Get the names of files in a folder
//
function getFilenames(path)
{
    try {
        return fs.readdirSync(path);
    }
    catch (err) {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "Path not found: " + path;
        }
        else
        {
            console.log(err);
        }
    }
}

//
// Parse the settings JSON file.
//
function parseSettings(data)
{
    let json = JSON.parse(data);
    const properties = {
        // paths
        input_path: ".",
        output_path: ".",
        pages_path: "./pages",

        // style and structure information
        styles: ["./style.css"],
        templates: [
            // name:
            // styles:
            //
        ],

        // optimization options
        client_markdown: false,

    };

    for (property in properties)
    {
        if (json[property])
        {
            settings[property] = json[property];
        }
        else
        {
            settings[property] = properties[property];
        }
    }
}

//
// Parse the pages.
// Return page objects, which have metadata properties and a content property.
//
function parsePages()
{
    let filenames = getFilenames(settings.pages_path);
    let pages = [];
    let pageObjs = [];

    for (file in filenames)
    {
        pages.push(readFile(settings.pages_path + '/' + filenames[file]));
    }

    for (index in pages)
    {
        let page = pages[index];
        let pageObj = {};
        //@@TODO: transform page contents
        //console.log(pages[page]);

        // extract the metadata from the front of the file
        let pos = 0;
        let cur = page.charAt(pos);
        let metadata;

        function advance()
        {
            pos++;
            cur = page.charAt(pos);
        }

        while (cur != null && cur != '')
        {
            if (cur == '-')
            {
                if (page.charAt(pos + 1) == '-' &&
                    page.charAt(pos + 2) == '-')
                {
                    pos++;
                    cur = page.charAt(pos);
                    metadata = '';

                    while (cur != '-' ||
                        page.charAt(pos + 1) != '-' ||
                        page.charAt(pos + 2) != '-')
                    {
                        metadata += cur;
                        advance();
                    }

                    // trim the front:
                    metadata = metadata.slice(2);
                    if (metadata.charAt(0) == '\n' || metadata.charAt(0) == '\r')
                    {
                        metadata = metadata.slice(1);
                    }

                    break;
                }
            }
            else if (cur != '\n' && cur != ' ' && cur != '\t' && cur != '\r')
            {
                //@@TODO: Emit a better error (and stop program?)
                console.log("no valid YAML, page " + index);
                break;
            }

            advance();
        }

        let dataObj = {};
        if (metadata)
        {
            dataObj = parseMetadata(metadata);
        }

        // defaults
        const properties = {
            title: null,
            template: null,
            // ...
        };

        // copy the dataObj properties into the pageObj.
        for (property in properties)
        {
            if (dataObj[property])
            {
                pageObj[property] = dataObj[property];
            }
            else
            {
                pageObj[property] = properties[property];
            }
        }

        pageObj.content = page;

        pageObjs.push(pageObj);
    }
    return pageObjs;
}

//
// Parse YAML metadata.
//
function parseMetadata(metadata)
{
    let lines = metadata.split('\n');
    let dataObj = {};
    for (i in lines)
    {
        let line = lines[i];
        line = line.trim();
        if (line == '') continue; // skip blank lines
        let separator = line.indexOf(':');
        let k = line.slice(0, separator);
        let v = line.substr(separator + 1);
        k = k.trim();
        v = v.trim();
        dataObj[k] = v;
    }
    return dataObj;
}

//
// Get template information.
//
function getTemplate(name)
{
    let template = settings.templates.find(function (e) {
        return e.name == name;
    });

    if (template != null)
    {
        template.html = readFile(settings.input_path + '/' + template.path);
    }
    else
    {
        //@@TODO: throw error and exit if template not found.
        throw 'No template called ' + name + ' found.';
    }

    return template;
}

//
// Generate HTML.
// A basic templating engine.
//
function generateHTML(template, page)
{
    // we use a template scope when executing template code.
    let scope = {};
    scope.page = page;
    scope.template = template;

    console.log(scope)

    let html = template.html;
    let output = '';
    let pos = 0;
    let cur = html.charAt(pos);
    let next = html.charAt(pos + 1);

    function advance()
    {
        pos++;
        cur = html.charAt(pos);
        next = html.charAt(pos + 1);
    }

    //@@TODO: throw proper error.
    if (html.length == 0) throw "Template " + template.name + " has no content.";

    while (cur != null && cur != '')
    {
        if (cur == '{' && next == '{')
        {
            let start = pos;
            let templateString = '';

            while (cur != '}' || next != '}')
            {
                templateString += cur;
                advance();
            }
            let length = pos - start;
            // advance past the '}}'.
            advance();
            advance();

            // trim front
            templateString = templateString.slice(2);
            let templateOutput;
            try {
                //@@TODO: eval is evil!!!!111
                templateOutput = eval("scope." + templateString);
            }
            catch (err)
            {
                //@@TODO: proper error message.
                console.log("Property " + templateString + " not found.");
            }

            if (templateOutput != null)
            {
                output += templateOutput;
            }
            console.log(templateString);
        }

        output += cur;
        advance();
    }

    return output;
}

function printHelp()
{
    console.log("Usage: node ossg.js [options] file");
    console.log("Oscar's static site generator. Compile templates, css, & posts into a website.");
}

function printVersion()
{
    console.log("OSSG: v0.0.1");
}

main();<|endoftext|>


<commit_before>export const NavigationFailureType = {
  redirected: 1,
  aborted: 2,
  cancelled: 3,
  duplicated: 4
}

export function createNavigationRedirectedError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.redirected,
    `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`
  )
}

export function createNavigationDuplicatedError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.duplicated,
    `Avoided redundant navigation to current location: "${from.fullPath}".`
  )
}

export function createNavigationCancelledError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.cancelled,
    `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`
  )
}

export function createNavigationAbortedError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.aborted,
    `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`
  )
}

function createRouterError (from, to, type, message) {
  const error = new Error(message)
  error._isRouter = true
  error.from = from
  error.to = to
  error.type = type

  const newStack = error.stack.split('\n')
  newStack.splice(1, 2) // remove 2 last useless calls
  error.stack = newStack.join('\n')
  return error
}

const propertiesToLog = ['params', 'query', 'hash']

function stringifyRoute (to) {
  if (typeof to === 'string') return to
  if ('path' in to) return to.path
  const location = {}
  for (const key of propertiesToLog) {
    if (key in to) location[key] = to[key]
  }
  return JSON.stringify(location, null, 2)
}
<commit_msg>refactor: use forEach<commit_after>export const NavigationFailureType = {
  redirected: 1,
  aborted: 2,
  cancelled: 3,
  duplicated: 4
}

export function createNavigationRedirectedError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.redirected,
    `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`
  )
}

export function createNavigationDuplicatedError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.duplicated,
    `Avoided redundant navigation to current location: "${from.fullPath}".`
  )
}

export function createNavigationCancelledError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.cancelled,
    `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`
  )
}

export function createNavigationAbortedError (from, to) {
  return createRouterError(
    from,
    to,
    NavigationFailureType.aborted,
    `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`
  )
}

function createRouterError (from, to, type, message) {
  const error = new Error(message)
  error._isRouter = true
  error.from = from
  error.to = to
  error.type = type

  const newStack = error.stack.split('\n')
  newStack.splice(1, 2) // remove 2 last useless calls
  error.stack = newStack.join('\n')
  return error
}

const propertiesToLog = ['params', 'query', 'hash']

function stringifyRoute (to) {
  if (typeof to === 'string') return to
  if ('path' in to) return to.path
  const location = {}
  propertiesToLog.forEach(key => {
    if (key in to) location[key] = to[key]
  })
  return JSON.stringify(location, null, 2)
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

var util = require('util');
var cp = require('child_process');

var Q = require('q');
var _ = require('lodash');
var semver = require('semver');

var exec = function (cmd) {
  return function () {
    var args = Array.prototype.slice.call(arguments, 0);
    args.unshift(cmd);
    var fullCmd = util.format.apply(util, args);
    return Q.nfcall(cp.exec, fullCmd).then(function (out) {
      return out[0].split('\n');
    });
  };
};

var andThen = function (fn, after) {
  return function () {
    return fn.apply(this, arguments).then(after);
  };
};

var oneArg = function (fn) {
  return function (arg) {
    return fn(arg);
  };
};

var oneLine = function (lines) {
  return lines[0].trim();
};

var noArgs = function (fn) {
  return function () {
    return fn();
  };
};

var identity = function (i) { return i; };

// like Q.all, but runs the comands in series
// useful for ensuring env state (like which branch is checked out)
var allInSeries = function (fn) {
  return function (args) {
    var results = [];
    var def;
    while (args.length > 0) {
      (function (arg) {
        if (def) {
          def = def.then(function () {
            return fn(arg);
          });
        } else {
          def = fn(arg);
        }
        def = def.then(function (res) {
          results.push(res);
        });
      }(args.pop()));
    }
    return def.then(function () {
      return results;
    });
  };
};

var compareBranches = function (left, right) {
  console.log('# These commits are in ' + left.name + ' but not in ' + right.name + '\n');
  console.log(_(left.log).
    difference(right.log).
    map(function (line) {
      return left.full[left.log.indexOf(line)]; // lol O(n^2)
    }).
    value().
    join('\n'));
};

var checkout = oneArg(exec('git checkout %s'));

var getCurrentBranch = andThen(noArgs(exec('git rev-parse --abbrev-ref HEAD')), oneLine);
var getTags = noArgs(exec('git tag'));
var getShaOfTag = oneArg(exec('git rev-list %s | head -n 1'));
var getTheLog = oneArg(exec('git log --pretty=oneline %s..HEAD | cat'));

// remember this so we can restore state
var currentBranch;

getCurrentBranch().
then(function (branch) {
  currentBranch = branch;
}).
then(getTags).
then(function (tags) {
  return tags.
    filter(semver.valid).
    map(semver.clean).
    sort(semver.rcompare);
}).
then(function (tags) {
  var major = tags[0].split('.')[0] + '.x';
  return tags.
    filter(function (ver) {
      return semver.satisfies(ver, major);
    });
}).
then(function (tags) {
  return _(tags).
    groupBy(function (tag) {
      return tag.split('.')[1];
    }).
    map(function (group) {
      return _.first(group);
    }).
    map(function (tag) {
      return 'v' + tag;
    }).
    value();
}).
then(function (tags) {
  var master = tags.pop();
  var stable = tags.pop();

  return [
    { name: stable.replace(/\d+$/, 'x'), tag: stable },
    { name: 'master', tag: master}
  ];
}).
then(allInSeries(function (branch) {
  return checkout(branch.name).
    then(function () {
      return getTheLog(branch.tag);
    }).
    then(function (log) {
      return log.
        filter(identity);
    }).
    then(function (log) {
      branch.full = log.map(function (line) {
        line = line.split(' ');
        var sha = line.shift();
        var msg = line.join(' ');
        return sha + ((/fix\([^\)]+\):/i.test(msg))  ? ' * ' : '   ') + msg;
      });
      branch.log = log.map(function (line) {
        return line.substr(41);
      });
      return branch;
    });
})).
then(function (pairs) {
  compareBranches(pairs[0], pairs[1]);
  console.log('\n');
  compareBranches(pairs[1], pairs[0]);
  return pairs;
}).
then(function () {
  return checkout(currentBranch);
}).
catch(function (e) {
  console.log(e.stack);
});

<commit_msg>chore(compare-master-to-stable): support beta release versions<commit_after>#!/usr/bin/env node

'use strict';

var util = require('util');
var cp = require('child_process');

var Q = require('q');
var _ = require('lodash');
var semver = require('semver');

var exec = function (cmd) {
  return function () {
    var args = Array.prototype.slice.call(arguments, 0);
    args.unshift(cmd);
    var fullCmd = util.format.apply(util, args);
    return Q.nfcall(cp.exec, fullCmd).then(function (out) {
      return out[0].split('\n');
    });
  };
};

var andThen = function (fn, after) {
  return function () {
    return fn.apply(this, arguments).then(after);
  };
};

var oneArg = function (fn) {
  return function (arg) {
    return fn(arg);
  };
};

var oneLine = function (lines) {
  return lines[0].trim();
};

var noArgs = function (fn) {
  return function () {
    return fn();
  };
};

var identity = function (i) { return i; };

// like Q.all, but runs the comands in series
// useful for ensuring env state (like which branch is checked out)
var allInSeries = function (fn) {
  return function (args) {
    var results = [];
    var def;
    while (args.length > 0) {
      (function (arg) {
        if (def) {
          def = def.then(function () {
            return fn(arg);
          });
        } else {
          def = fn(arg);
        }
        def = def.then(function (res) {
          results.push(res);
        });
      }(args.pop()));
    }
    return def.then(function () {
      return results;
    });
  };
};

var compareBranches = function (left, right) {
  console.log('# These commits are in ' + left.name + ' but not in ' + right.name + '\n');
  console.log(_(left.log).
    difference(right.log).
    map(function (line) {
      return left.full[left.log.indexOf(line)]; // lol O(n^2)
    }).
    value().
    join('\n'));
};

var checkout = oneArg(exec('git checkout %s'));

var getCurrentBranch = andThen(noArgs(exec('git rev-parse --abbrev-ref HEAD')), oneLine);
var getTags = noArgs(exec('git tag'));
var getShaOfTag = oneArg(exec('git rev-list %s | head -n 1'));
var getTheLog = oneArg(exec('git log --pretty=oneline %s..HEAD | cat'));

// remember this so we can restore state
var currentBranch;

getCurrentBranch().
then(function (branch) {
  currentBranch = branch;
}).
then(getTags).
then(function (tags) {
  return tags.
    filter(semver.valid).
    map(semver.clean).
    sort(semver.rcompare);
}).
then(function (tags) {
  var major = tags[0].split('.')[0];
  return tags.
    filter(function (ver) {
      return semver(ver).major == major;
    });
}).
then(function (tags) {
  return _(tags).
    groupBy(function (tag) {
      return tag.split('.')[1];
    }).
    map(function (group) {
      return _.first(group);
    }).
    map(function (tag) {
      return 'v' + tag;
    }).
    value();
}).
then(function (tags) {
  var master = tags.pop();
  var stable = tags.pop();

  return [
    { name: stable.replace(/\d+$/, 'x'), tag: stable },
    { name: 'master', tag: master}
  ];
}).
then(allInSeries(function (branch) {
  return checkout(branch.name).
    then(function () {
      return getTheLog(branch.tag);
    }).
    then(function (log) {
      return log.
        filter(identity);
    }).
    then(function (log) {
      branch.full = log.map(function (line) {
        line = line.split(' ');
        var sha = line.shift();
        var msg = line.join(' ');
        return sha + ((/fix\([^\)]+\):/i.test(msg))  ? ' * ' : '   ') + msg;
      });
      branch.log = log.map(function (line) {
        return line.substr(41);
      });
      return branch;
    });
})).
then(function (pairs) {
  compareBranches(pairs[0], pairs[1]);
  console.log('\n');
  compareBranches(pairs[1], pairs[0]);
  return pairs;
}).
then(function () {
  return checkout(currentBranch);
}).
catch(function (e) {
  console.log(e.stack);
});

<|endoftext|>


<commit_before>/**
  Copyright 2011 Christian Iversen <ci@sikkerhed.org>

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
**/

var number = __inherit(object, "int");

$PY.number = number;

number.PY$_isnumeric_ = true;

number.PY$__repr__ = number.PY$__str__;

number.PY$__eq__ = function (other) {
    if (typeof(other) === "number") {
        return bool(this._obj === other);
    }
    if (other.PY$__class__ !== this.PY$__class__)
        return False;
    return bool(this._obj === other._obj);
};

number.toString = function () {
    return js(this.PY$__str__());
};

number._js_ = function () {
    return this._obj;
};

number.PY$__bool__ = function() {
    return bool(this._obj);
};

number.PY$__pos__ = function() {
    return this.PY$__class__(+this._obj);
};

number.PY$__neg__ = function() {
    return this.PY$__class__(-this._obj);
};

number.PY$__nonzero__ = function() {
    return bool(this._obj != 0);
};

number.PY$__gt__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot compare number and non-number");
    return bool(this._obj > x._obj);
};

number.PY$__lt__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot compare number and non-number");
    return bool(this._obj < x._obj);
};

number.PY$__ge__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot compare number and non-number");
    return bool(this._obj >= x._obj);
};

number.PY$__le__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot compare number and non-number");
    return bool(this._obj <= x._obj);
};

number.PY$__mul__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot multiply number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj * x._obj);
    else
        return x.PY$__class__(this._obj * x._obj);
};

number.PY$__add__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot add number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj + x._obj);
    else
        return x.PY$__class__(this._obj + x._obj);
};

number.PY$__div__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot divide number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj / x._obj);
    else
        return x.PY$__class__(this._obj / x._obj);
};

number.PY$__sub__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot subtract number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj - x._obj);
    else
        return x.PY$__class__(this._obj - x._obj);
};

number.PY$__pow__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot exponentiate number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(Math.pow(this._obj, x._obj));
    else
        return x.PY$__class__(Math.pow(this._obj, x._obj));
};

number.PY$__imul__      = number.PY$__mul__;
number.PY$__iadd__      = number.PY$__add__;
number.PY$__idiv__      = number.PY$__div__;
number.PY$__isub__      = number.PY$__sub__;
number.PY$__ipow__      = number.PY$__pow__;
<commit_msg> - Removed obsolete number.__le__ and __ge__ (now implemented in object)  * Fixed number.__eq__ by allowing comparison of ints and floats<commit_after>/**
  Copyright 2011 Christian Iversen <ci@sikkerhed.org>

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
**/

var number = __inherit(object, "int");

$PY.number = number;

number.PY$_isnumeric_ = true;

number.PY$__repr__ = number.PY$__str__;

number.PY$__eq__ = function (other) {
    if (typeof(other) === "number") {
        return bool(this._obj === other);
    } else if (other.PY$_isnumeric_) {
        return bool(this._obj === other._obj);
    } else {
        return object.PY$__eq__.call(this, other);
    }
};

number.toString = function () {
    return js(this.PY$__str__());
};

number._js_ = function () {
    return this._obj;
};

number.PY$__bool__ = function() {
    return bool(this._obj);
};

number.PY$__pos__ = function() {
    return this.PY$__class__(+this._obj);
};

number.PY$__neg__ = function() {
    return this.PY$__class__(-this._obj);
};

number.PY$__nonzero__ = function() {
    return bool(this._obj != 0);
};

number.PY$__gt__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot compare number and non-number");
    return bool(this._obj > x._obj);
};

number.PY$__lt__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot compare number and non-number");
    return bool(this._obj < x._obj);
};

number.PY$__mul__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot multiply number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj * x._obj);
    else
        return x.PY$__class__(this._obj * x._obj);
};

number.PY$__add__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot add number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj + x._obj);
    else
        return x.PY$__class__(this._obj + x._obj);
};

number.PY$__div__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot divide number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj / x._obj);
    else
        return x.PY$__class__(this._obj / x._obj);
};

number.PY$__sub__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot subtract number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(this._obj - x._obj);
    else
        return x.PY$__class__(this._obj - x._obj);
};

number.PY$__pow__ = function(x) {
    if (!x.PY$_isnumeric_)
        throw py_builtins.TypeError("Cannot exponentiate number and non-number");
    if (this.PY$_isnumericfloat || (! x.PY$_isnumericfloat))
        return this.PY$__class__(Math.pow(this._obj, x._obj));
    else
        return x.PY$__class__(Math.pow(this._obj, x._obj));
};

number.PY$__imul__      = number.PY$__mul__;
number.PY$__iadd__      = number.PY$__add__;
number.PY$__idiv__      = number.PY$__div__;
number.PY$__isub__      = number.PY$__sub__;
number.PY$__ipow__      = number.PY$__pow__;
<|endoftext|>


<commit_before>const { Router } = require('express');

const { getController } = require('./controller');

const initRouter = (data) => {
    const router = new Router();
    const controller = getController(data.user);

    router
        .get('/', (req, res) => {
            // if (!req.user) {
            //     return res.redirect('/auth/sign-in');
            // }
            return controller.getUserAllProperty(req, res);
        });
    return router;
};

module.exports = { initRouter };
<commit_msg>Add Agent router!<commit_after>const { Router } = require('express');

const { getController } = require('./controller');

const initRouter = (data) => {
    const router = new Router();
    const controller = getController(data.user);

    router
        .get('/', (req, res) => {
            // if (!req.user) {
            //     return res.redirect('/auth/sign-in');
            // }
            return controller.getUserAllProperty(req, res);
        })
        .get('/agents', (req, res) => {
            return controller.getAllUsers(req, res);
        });
    return router;
};

module.exports = { initRouter };
<|endoftext|>


<commit_before>var doEncryptDomains = [
    "facebook.com",
    "mail.google.com"
];

var EMAIL_WINDOW_SELECTOR = '.I5';
var TEXTAREA_SELECTOR = '.Am[role="textbox"][contenteditable="true"]';
var TOOLBAR_SELECT = ".n1tfz";
var FORMAT_BUTTON_SELECT = ".az5";

function usernameGetter($usernameField) {
    return $usernameField.find('*').filter(function(i, elm) {
        return $(elm).attr('email') !== undefined;
    }).toArray().map(function(emailSpan) {
        return $(emailSpan).attr('email');
    });
}

$(function() {
    if (doEncryptDomains.indexOf(document.domain) < 0) {
        return
    }

    $(document).on('DOMNodeInserted', function(e) {
        if ($(e.target).closest(NON_FAKEBLOCK_TEXTAREA_SELECTOR).length > 0) {
            //don't allow insert events for objects appended to the unencrypted area to propagate
            return;
        } else if ($(e.target).parent().hasClass(NON_FAKEBLOCK_TEXTAREA_CLASS) &&
            $(e.target).hasClass('gmail_extra')) {
            //if an old email thread gets revealed in the original textarea
            //append it to associated unencrypted area instead and reencrypt
            var $unencryptedArea = $(e.target).parent().data('unencryptedArea');
            $unencryptedArea.append($(e.target));
            return;
        }

        //gmail makes the first matching textarea found into the compose textarea
        //move the unencrypted textarea after the encrypted textarea
        if ($('.aO7').children(TEXTAREA_SELECTOR).length == 1) {
            $('.aO7').append($(NON_FAKEBLOCK_TEXTAREA_SELECTOR));
        }

        //make overlay if the email window and the associated textarea are found, and if doesn't already have overlay
        var overlayable = $(e.target).closest(EMAIL_WINDOW_SELECTOR).find(TEXTAREA_SELECTOR);
        var email_toolbar = $(e.target).closest(EMAIL_WINDOW_SELECTOR).find(TOOLBAR_SELECT);

        //this is awkward, but we have to wait until the textarea's tabindex is set
        //so can tab to overlay but can't tab to original email
        if (overlayable.length > 0 && overlayable.attr('tabindex')) {
            if (! overlayable.hasClass('has-overlay')) {
                var $email = overlayable.closest(EMAIL_WINDOW_SELECTOR);
                makeOverlay($email);
            } else if (overlayable.hasClass('pre-draft')) {
                if (overlayable.data('draft') === overlayable.html() ||
                    overlayable.html().indexOf('class="gmail_extra"') >= 0) {
                    //if draft has finished loading
                    overlayable.data('unencryptedArea').html(
                        overlayable.data('draft')
                    );
                    overlayable.removeClass('pre-draft');
                } else {
                    //draft hasn't finished loading into compose window
                    overlayable.data('draft', overlayable.html());
                }

            }
            if (email_toolbar.length > 0) {
                var format_button = email_toolbar.find(FORMAT_BUTTON_SELECT);
                if (!(format_button.hasClass("yupper"))) {
                    format_button.addClass("yupper");
                    var $ptButton = $('
' +
                    '
' +
                    '        ' +
                    '
' +
                    '
' +
                    '        ' +
                    '
' +
                    '
');
                    format_button.before($ptButton);
                    $ptButton.find(".pt-button").css(
                        {
//                            "top":" 1px",
                            "padding":" 10px 7px 5px 8px",
                            "width":" 22px",
                            "height":" 25px",
                            "border":" 1px solid #f5f5f5",
                            "border-top":"1px solid rgba(128, 128, 128, 0.32)",
                            "float":"right"
                        });
                    $ptButton.find('.pt-button').hover(
//                        function(){ $(this).css('border', '1px solid gray') },
//                        function(){ $(this).css(
//                            {'border':'1px solid #f5f5f5',
//                            "border-top":"1px solid rgba(128, 128, 128, 0.32)"
//                            }) }
                        function(){ $(this).css('cursor', 'pointer') }
                    );
                    $ptButton.find('.pt_unlocked').click(function(e) { // they clicked the snake, stuff should be encrypted now
                        togglePtButton($(this));
                    });
                    $ptButton.find('.pt_locked').click(function(e) { // they clicked the lock, stuff should be unencrypted now
                        togglePtButton($(this));
                    });
                    bindPtButtons(overlayable, $ptButton);
                }
            }
        }

        //update usernames if (possibly) a new username has been added
        usernameHandler(e.target);
    });

    $(document).on('DOMNodeRemoved', function(e) {
        usernameHandler(e.target, true);
    });
});

function bindPtButtons($encryptedArea, $ptButtons) {
    /*
    initialize parseltongue buttons
    binds parseltongue buttons to the proper unencrypted textarea
    sets the default encrypt/decrypt option based on usermeta.defaultEncrypt
    shows or hides all parseltongue buttons if can encrypt for current usernames
    */
    var $unencryptedArea = $encryptedArea.data('unencryptedArea');
    $unencryptedArea.data('ptButtons', $ptButtons);
    $ptButtons.data('unencryptedArea', $unencryptedArea);
    requestDefaultEncrypt($ptButtons);
    requestCanEncryptFor($unencryptedArea);
}

function togglePtButton($ptButton) {
    $ptButton.hide();
    $ptButton.siblings().show();
    encryptHandler($ptButton.closest('.pt-buttons-wrapper').data('unencryptedArea'));
}

function makeOverlay($email) {
    /* given the top-level email window, make an overlay for it */

    var $textarea = $email.find(TEXTAREA_SELECTOR);
    var $unencryptedArea = makeOverlayHtml($textarea);

    $textarea.after($unencryptedArea);
    $textarea.hide();

    //set doencrypt to false since usernames should be empty when email first opens
    $unencryptedArea.data('doEncrypt', false);

    $unencryptedArea.keyup(function(e) {
        encryptHandler($(this));
    });
    $textarea.addClass(FAKEBLOCK_TEXTAREA_CLASS);
    $textarea.addClass('has-overlay pre-draft');
    $textarea.data('unencryptedArea', $unencryptedArea);
}

function makeOverlayHtml($textarea) {
    /* lots of shit here to make the overlay look like the original */

    var $unencryptedArea = $textarea.clone();
    var unencrypted_id = $unencryptedArea.prop('id') + '_unencrypted';
    $unencryptedArea.attr({
        id : unencrypted_id,
        role : 'textbox',
        contenteditable : true,
        tabindex : $textarea.attr('tabindex')
    });
    $textarea.removeAttr('tabindex');

    $unencryptedArea.click(function(evt) {
        evt.stopPropagation();
    }).focus(function(evt) {
            evt.stopPropagation();
        });

    $unencryptedArea.css({
        'min-height' : '85px',
        'direction' : 'ltr'
    });
    $unencryptedArea.addClass(['Al', 'LW-avf', NON_FAKEBLOCK_TEXTAREA_CLASS].join(' '));

    $unencryptedArea.data('encryptedArea', $textarea);

    return $unencryptedArea;
}

function encryptHandler($unencryptedArea) {
    /*
     handler for keyup in an unencrypted area
     uses doEncrypt in unencrypted area's data to check if should update with ciphertext or plaintext
     */
    var $encryptedArea = $unencryptedArea.data('encryptedArea');
    var message = $unencryptedArea.html();
    var usernames = $unencryptedArea.data('usernames');

    //if can find button, check if button is visible and set to lock
    //otherwise do not encrypt
    var $ptButton = $unencryptedArea.data('ptButtons');
    var canEncryptButton = $ptButton ?
        $ptButton.find('.pt_locked').css('display') !== 'none' && $ptButton.css('display') !== 'none' :
        false;
    if ($unencryptedArea.data('doEncrypt') && canEncryptButton) {
        requestEncrypt($encryptedArea, message, usernames);
    } else {
        $encryptedArea.html(message);
    }

}

function usernameHandler(emailSpan, deleteEmail) {
    /*
     handler for an email address getting added or removed from the addressees
     updates the usernames list for the appropriate unencrypted textarea
     */
    var emailSpans = $(emailSpan).children().add($(emailSpan)).filter(function(i, elm) {
        return $(elm).attr('email') !== undefined;
    });
    //TODO: what happens if more than one email span? is this possible?
    //TODO: normalize emails in frontend too?
    if (emailSpans.length == 1) {
        $usernameField = emailSpans.parent().parent();
        var usernameToDelete = null;
        if (deleteEmail) {
            usernameToDelete = emailSpans.attr('email');
        }
        updateUsernames($usernameField, usernameToDelete);
    }
}

function updateUsernames($usernameField, usernameToDelete) {
    /*
     call to check if the usernames are all still valid parseltongue users
     if yes/no, update the doEncrypt field of the corresponding unencrypted textarea
     */

    //check if username field has been associated with a textarea yet
    var $unencryptedArea = $usernameField.data('unencryptedArea');
    if ($unencryptedArea === undefined) {
        $unencryptedArea = $usernameField.closest(EMAIL_WINDOW_SELECTOR).find(
            NON_FAKEBLOCK_TEXTAREA_SELECTOR
        );
        $usernameField.data('unencryptedArea', $unencryptedArea);
    }

    var usernames = usernameGetter($usernameField);
    var usernameDeleteIndex = usernames.indexOf(usernameToDelete);
    if (usernameDeleteIndex >= 0) {
        usernames.splice(usernameDeleteIndex, 1);
    }
    $unencryptedArea.data('usernames', usernames);
    requestCanEncryptFor($unencryptedArea);
}

function requestEncrypt($encryptedArea, message, usernames) {
    /*
     send message to back asking for the encrypted message
     updates the textarea with the ciphertext
     */
    sendMessage({
        "action" : "encrypt",
        "message" : message,
        "usernames" : usernames,
    }, function(response) {
        var res = $.parseJSON(response).res;
        var msg;
        if (typeof res === "string") {
            msg = res;
        } else {
            var json_encoded = JSON.stringify(res);
            msg = FAKEBLOCK_OPEN_TAG +
                encodeString(json_encoded) +
                FAKEBLOCK_CLOSE_TAG;
        }
        $encryptedArea.html(msg);
    });
}

function requestCanEncryptFor($unencryptedArea) {
    /*
     send message to back asking if the usernames can be encrypted for (are valid parseltongue users)
     if yes or no, updates the doEncrypt state of the corresponding unencrypted textarea, switching encryption on or off
     parseltongue buttons will show or hide accordingly
     */
    var usernames = $unencryptedArea.data('usernames') ?
        $unencryptedArea.data('usernames') : [];
    sendMessage({
        "action" : "canEncryptFor",
        "usernames" : usernames,
    }, function(response) {
        var res = $.parseJSON(response).res;
        $unencryptedArea.data('doEncrypt', res.can_encrypt);
        var ptButtonsWrapper = $unencryptedArea.data('ptButtons');
        if (ptButtonsWrapper) {
            if (res.can_encrypt) {
                ptButtonsWrapper.show();
            } else {
                ptButtonsWrapper.hide();
            }
        }
        encryptHandler($unencryptedArea);
    });
}

function requestDefaultEncrypt($ptButtons) {
    /*
    get default setting for encrypt on or off
    show or hide $unencryptedArea's lock/snake buttons on opening compose window
    */
    sendMessage({
       "action": "getUserMeta",
   }, function(res) {
        userMeta = JSON.parse(res).res;
        if (userMeta.defaultEncrypt) {
            togglePtButton($ptButtons.find('.pt_unlocked'));
        } else {
            togglePtButton($ptButtons.find('.pt_locked'));
        }
   });
}

chrome.runtime.onMessage.addListener(function(message) {
    if ('defaultEncrypt' in message) {
        var classToToggle = message.defaultEncrypt ? '.pt_unlocked' : '.pt_locked';
        $.each($(classToToggle), function() {
            togglePtButton($(this));
        });
    }
});<commit_msg>fixed bug that appended old thread to encrypted textarea instead of old also got rid of something unnecessary...?<commit_after>var doEncryptDomains = [
    "facebook.com",
    "mail.google.com"
];

var EMAIL_WINDOW_SELECTOR = '.I5';
var TEXTAREA_SELECTOR = '.Am[role="textbox"][contenteditable="true"]';
var TOOLBAR_SELECT = ".n1tfz";
var FORMAT_BUTTON_SELECT = ".az5";

function usernameGetter($usernameField) {
    return $usernameField.find('*').filter(function(i, elm) {
        return $(elm).attr('email') !== undefined;
    }).toArray().map(function(emailSpan) {
        return $(emailSpan).attr('email');
    });
}

$(function() {
    if (doEncryptDomains.indexOf(document.domain) < 0) {
        return
    }

    $(document).on('DOMNodeInserted', function(e) {
        if ($(e.target).closest(NON_FAKEBLOCK_TEXTAREA_SELECTOR).length > 0) {
            //don't allow insert events for objects appended to the unencrypted area to propagate
            return;
        } else if ($(e.target).parent().hasClass(FAKEBLOCK_TEXTAREA_CLASS) &&
            $(e.target).hasClass('gmail_extra')) {
            //if an old email thread gets revealed in the original textarea
            //append it to associated unencrypted area instead and reencrypt
            var $unencryptedArea = $(e.target).parent().data('unencryptedArea');
            $unencryptedArea.append($(e.target));
            return;
        }

        //make overlay if the email window and the associated textarea are found, and if doesn't already have overlay
        var overlayable = $(e.target).closest(EMAIL_WINDOW_SELECTOR).find(TEXTAREA_SELECTOR);
        var email_toolbar = $(e.target).closest(EMAIL_WINDOW_SELECTOR).find(TOOLBAR_SELECT);

        //this is awkward, but we have to wait until the textarea's tabindex is set
        //so can tab to overlay but can't tab to original email
        if (overlayable.length > 0 && overlayable.attr('tabindex')) {
            if (! overlayable.hasClass('has-overlay')) {
                var $email = overlayable.closest(EMAIL_WINDOW_SELECTOR);
                makeOverlay($email);
            } else if (overlayable.hasClass('pre-draft')) {
                if (overlayable.data('draft') === overlayable.html() ||
                    overlayable.html().indexOf('class="gmail_extra"') >= 0) {
                    //if draft has finished loading
                    overlayable.data('unencryptedArea').html(
                        overlayable.data('draft')
                    );
                    overlayable.removeClass('pre-draft');
                } else {
                    //draft hasn't finished loading into compose window
                    overlayable.data('draft', overlayable.html());
                }

            }
            if (email_toolbar.length > 0) {
                var format_button = email_toolbar.find(FORMAT_BUTTON_SELECT);
                if (!(format_button.hasClass("yupper"))) {
                    format_button.addClass("yupper");
                    var $ptButton = $('
' +
                    '
' +
                    '        ' +
                    '
' +
                    '
' +
                    '        ' +
                    '
' +
                    '
');
                    format_button.before($ptButton);
                    $ptButton.find(".pt-button").css(
                        {
//                            "top":" 1px",
                            "padding":" 10px 7px 5px 8px",
                            "width":" 22px",
                            "height":" 25px",
                            "border":" 1px solid #f5f5f5",
                            "border-top":"1px solid rgba(128, 128, 128, 0.32)",
                            "float":"right"
                        });
                    $ptButton.find('.pt-button').hover(
//                        function(){ $(this).css('border', '1px solid gray') },
//                        function(){ $(this).css(
//                            {'border':'1px solid #f5f5f5',
//                            "border-top":"1px solid rgba(128, 128, 128, 0.32)"
//                            }) }
                        function(){ $(this).css('cursor', 'pointer') }
                    );
                    $ptButton.find('.pt_unlocked').click(function(e) { // they clicked the snake, stuff should be encrypted now
                        togglePtButton($(this));
                    });
                    $ptButton.find('.pt_locked').click(function(e) { // they clicked the lock, stuff should be unencrypted now
                        togglePtButton($(this));
                    });
                    bindPtButtons(overlayable, $ptButton);
                }
            }
        }

        //update usernames if (possibly) a new username has been added
        usernameHandler(e.target);
    });

    $(document).on('DOMNodeRemoved', function(e) {
        usernameHandler(e.target, true);
    });
});

function bindPtButtons($encryptedArea, $ptButtons) {
    /*
    initialize parseltongue buttons
    binds parseltongue buttons to the proper unencrypted textarea
    sets the default encrypt/decrypt option based on usermeta.defaultEncrypt
    shows or hides all parseltongue buttons if can encrypt for current usernames
    */
    var $unencryptedArea = $encryptedArea.data('unencryptedArea');
    $unencryptedArea.data('ptButtons', $ptButtons);
    $ptButtons.data('unencryptedArea', $unencryptedArea);
    requestDefaultEncrypt($ptButtons);
    requestCanEncryptFor($unencryptedArea);
}

function togglePtButton($ptButton) {
    $ptButton.hide();
    $ptButton.siblings().show();
    encryptHandler($ptButton.closest('.pt-buttons-wrapper').data('unencryptedArea'));
}

function makeOverlay($email) {
    /* given the top-level email window, make an overlay for it */

    var $textarea = $email.find(TEXTAREA_SELECTOR);
    var $unencryptedArea = makeOverlayHtml($textarea);

    $textarea.after($unencryptedArea);
    $textarea.hide();

    //set doencrypt to false since usernames should be empty when email first opens
    $unencryptedArea.data('doEncrypt', false);

    $unencryptedArea.keyup(function(e) {
        encryptHandler($(this));
    });
    $textarea.addClass(FAKEBLOCK_TEXTAREA_CLASS);
    $textarea.addClass('has-overlay pre-draft');
    $textarea.data('unencryptedArea', $unencryptedArea);
}

function makeOverlayHtml($textarea) {
    /* lots of shit here to make the overlay look like the original */

    var $unencryptedArea = $textarea.clone();
    var unencrypted_id = $unencryptedArea.prop('id') + '_unencrypted';
    $unencryptedArea.attr({
        id : unencrypted_id,
        role : 'textbox',
        contenteditable : true,
        tabindex : $textarea.attr('tabindex')
    });
    $textarea.removeAttr('tabindex');

    $unencryptedArea.click(function(evt) {
        evt.stopPropagation();
    }).focus(function(evt) {
            evt.stopPropagation();
        });

    $unencryptedArea.css({
        'min-height' : '85px',
        'direction' : 'ltr'
    });
    $unencryptedArea.addClass(['Al', 'LW-avf', NON_FAKEBLOCK_TEXTAREA_CLASS].join(' '));

    $unencryptedArea.data('encryptedArea', $textarea);

    return $unencryptedArea;
}

function encryptHandler($unencryptedArea) {
    /*
     handler for keyup in an unencrypted area
     uses doEncrypt in unencrypted area's data to check if should update with ciphertext or plaintext
     */
    var $encryptedArea = $unencryptedArea.data('encryptedArea');
    var message = $unencryptedArea.html();
    var usernames = $unencryptedArea.data('usernames');

    //if can find button, check if button is visible and set to lock
    //otherwise do not encrypt
    var $ptButton = $unencryptedArea.data('ptButtons');
    var canEncryptButton = $ptButton ?
        $ptButton.find('.pt_locked').css('display') !== 'none' && $ptButton.css('display') !== 'none' :
        false;
    if ($unencryptedArea.data('doEncrypt') && canEncryptButton) {
        requestEncrypt($encryptedArea, message, usernames);
    } else {
        $encryptedArea.html(message);
    }

}

function usernameHandler(emailSpan, deleteEmail) {
    /*
     handler for an email address getting added or removed from the addressees
     updates the usernames list for the appropriate unencrypted textarea
     */
    var emailSpans = $(emailSpan).children().add($(emailSpan)).filter(function(i, elm) {
        return $(elm).attr('email') !== undefined;
    });
    //TODO: what happens if more than one email span? is this possible?
    //TODO: normalize emails in frontend too?
    if (emailSpans.length == 1) {
        $usernameField = emailSpans.parent().parent();
        var usernameToDelete = null;
        if (deleteEmail) {
            usernameToDelete = emailSpans.attr('email');
        }
        updateUsernames($usernameField, usernameToDelete);
    }
}

function updateUsernames($usernameField, usernameToDelete) {
    /*
     call to check if the usernames are all still valid parseltongue users
     if yes/no, update the doEncrypt field of the corresponding unencrypted textarea
     */

    //check if username field has been associated with a textarea yet
    var $unencryptedArea = $usernameField.data('unencryptedArea');
    if ($unencryptedArea === undefined) {
        $unencryptedArea = $usernameField.closest(EMAIL_WINDOW_SELECTOR).find(
            NON_FAKEBLOCK_TEXTAREA_SELECTOR
        );
        $usernameField.data('unencryptedArea', $unencryptedArea);
    }

    var usernames = usernameGetter($usernameField);
    var usernameDeleteIndex = usernames.indexOf(usernameToDelete);
    if (usernameDeleteIndex >= 0) {
        usernames.splice(usernameDeleteIndex, 1);
    }
    $unencryptedArea.data('usernames', usernames);
    requestCanEncryptFor($unencryptedArea);
}

function requestEncrypt($encryptedArea, message, usernames) {
    /*
     send message to back asking for the encrypted message
     updates the textarea with the ciphertext
     */
    sendMessage({
        "action" : "encrypt",
        "message" : message,
        "usernames" : usernames,
    }, function(response) {
        var res = $.parseJSON(response).res;
        var msg;
        if (typeof res === "string") {
            msg = res;
        } else {
            var json_encoded = JSON.stringify(res);
            msg = FAKEBLOCK_OPEN_TAG +
                encodeString(json_encoded) +
                FAKEBLOCK_CLOSE_TAG;
        }
        $encryptedArea.html(msg);
    });
}

function requestCanEncryptFor($unencryptedArea) {
    /*
     send message to back asking if the usernames can be encrypted for (are valid parseltongue users)
     if yes or no, updates the doEncrypt state of the corresponding unencrypted textarea, switching encryption on or off
     parseltongue buttons will show or hide accordingly
     */
    var usernames = $unencryptedArea.data('usernames') ?
        $unencryptedArea.data('usernames') : [];
    sendMessage({
        "action" : "canEncryptFor",
        "usernames" : usernames,
    }, function(response) {
        var res = $.parseJSON(response).res;
        $unencryptedArea.data('doEncrypt', res.can_encrypt);
        var ptButtonsWrapper = $unencryptedArea.data('ptButtons');
        if (ptButtonsWrapper) {
            if (res.can_encrypt) {
                ptButtonsWrapper.show();
            } else {
                ptButtonsWrapper.hide();
            }
        }
        encryptHandler($unencryptedArea);
    });
}

function requestDefaultEncrypt($ptButtons) {
    /*
    get default setting for encrypt on or off
    show or hide $unencryptedArea's lock/snake buttons on opening compose window
    */
    sendMessage({
       "action": "getUserMeta",
   }, function(res) {
        userMeta = JSON.parse(res).res;
        if (userMeta.defaultEncrypt) {
            togglePtButton($ptButtons.find('.pt_unlocked'));
        } else {
            togglePtButton($ptButtons.find('.pt_locked'));
        }
   });
}

chrome.runtime.onMessage.addListener(function(message) {
    if ('defaultEncrypt' in message) {
        var classToToggle = message.defaultEncrypt ? '.pt_unlocked' : '.pt_locked';
        $.each($(classToToggle), function() {
            togglePtButton($(this));
        });
    }
});<|endoftext|>


<commit_before>var URL_firebase = "https://<app-name>.firebaseio.com/";

var srt_01 = "";
var srt_02 = "";
var srt_03 = "";
var srt_04 = "";
var srt_05 = "";
var srt_06 = "";
var srt_07 = "";
var srt_08 = "";
var srt_09 = "";
var srt_10 = "";
var srt_11 = "";
var srt_12 = "";

var int_01 = "";
var int_02 = "";
var int_03 = "";
var int_04 = "";
var int_05 = "";
var int_06 = "";
var int_07 = "";
var int_08 = "";
var int_09 = "";
var int_10 = "";
var int_11 = "";
var int_12 = "";

var swfObject = document.getElementById("flashObject");

function post_str_01(num1) {
    str_01 = num1;
    var post_str_01 = new Firebase(URL_firebase + "str_01");
    post_str_01.set({
      str_01: str_01,
    });
    update_str_01();
}

function post_str_02(num2) {
    str_02 = num2;
    var post_str_02 = new Firebase(URL_firebase + "str_02");
    post_str_02.set({
      str_02: str_02,
    });
    update_str_02();
}

function post_str_03(num3) {
    str_03 = num3;
    var post_str_03 = new Firebase(URL_firebase + "str_03");
    post_str_03.set({
      str_03: str_03,
    });
    update_str_03();
}

function post_str_04(num4) {
    str_04 = num4;
    var post_str_04 = new Firebase(URL_firebase + "str_04");
    post_str_04.set({
      str_04: str_04,
    });
    update_str_04();
}

function post_str_05(num5) {
    str_05 = num5;
    var post_str_05 = new Firebase(URL_firebase + "str_05");
    post_str_05.set({
      str_05: str_05,
    });
    update_str_05();
}

function post_str_06(num6) {
    str_06 = num6;
    var post_str_06 = new Firebase(URL_firebase + "str_06");
    post_str_06.set({
      str_06: str_06,
    });
    update_str_06();
}

function post_str_07(num7) {
    str_07 = num7;
    var post_str_07 = new Firebase(URL_firebase + "str_07");
    post_str_07.set({
      str_07: str_07,
    });
    update_str_07();
}

function post_str_08(num8) {
    str_08 = num8;
    var post_str_08 = new Firebase(URL_firebase + "str_08");
    post_str_08.set({
      str_08: str_08,
    });
    update_str_08();
}

function post_str_09(num9) {
    str_09 = num9;
    var post_str_09 = new Firebase(URL_firebase + "str_09");
    post_str_09.set({
      str_09: str_09,
    });
    update_str_09();
}

function post_str_10(num10) {
    str_10 = num10;
    var post_str_10 = new Firebase(URL_firebase + "str_10");
    post_str_10.set({
      str_10: str_10,
    });
    update_str_10();
}

function post_str_11(num11) {
    str_11 = num11;
    var post_str_11 = new Firebase(URL_firebase + "str_11");
    post_str_11.set({
      str_11: str_11,
    });
    update_str_11();
}

function post_str_12(num12) {
    str_12 = num12;
    var post_str_12 = new Firebase(URL_firebase + "str_12");
    post_str_12.set({
      str_12: str_12,
    });
    update_str_12();
}

// post int

function post_int_01(num1) {
    int_01 = num1;
    var post_int_01 = new Firebase(URL_firebase + "int_01");
    post_int_01.set({
      int_01: int_01,
    });
    update_int_01();
}

function post_int_02(num2) {
    int_02 = num2;
    var post_int_02 = new Firebase(URL_firebase + "int_02");
    post_int_02.set({
      int_02: int_02,
    });
    update_int_02();
}

function post_int_03(num3) {
    int_03 = num3;
    var post_int_03 = new Firebase(URL_firebase + "int_03");
    post_int_03.set({
      int_03: int_03,
    });
    update_int_03();
}

function post_int_04(num4) {
    int_04 = num4;
    var post_int_04 = new Firebase(URL_firebase + "int_04");
    post_int_04.set({
      int_04: int_04,
    });
    update_int_04();
}

function post_int_05(num5) {
    int_05 = num5;
    var post_int_05 = new Firebase(URL_firebase + "int_05");
    post_int_05.set({
      int_05: int_05,
    });
    update_int_05();
}

function post_int_06(num6) {
    int_06 = num6;
    var post_int_06 = new Firebase(URL_firebase + "int_06");
    post_int_06.set({
      int_06: int_06,
    });
    update_int_06();
}

function post_int_07(num7) {
    int_07 = num7;
    var post_int_07 = new Firebase(URL_firebase + "int_07");
    post_int_07.set({
      int_07: int_07,
    });
    update_int_07();
}

function post_int_08(num8) {
    int_08 = num8;
    var post_int_08 = new Firebase(URL_firebase + "int_08");
    post_int_08.set({
      int_08: int_08,
    });
    update_int_08();
}

function post_int_09(num9) {
    int_09 = num9;
    var post_int_09 = new Firebase(URL_firebase + "int_09");
    post_int_09.set({
      int_09: int_09,
    });
    update_int_09();
}

function post_int_10(num10) {
    int_10 = num10;
    var post_int_10 = new Firebase(URL_firebase + "int_10");
    post_int_10.set({
      int_10: int_10,
    });
    update_int_10();
}

function post_int_11(num11) {
    int_11 = num11;
    var post_int_11 = new Firebase(URL_firebase + "int_11");
    post_int_11.set({
      int_11: int_11,
    });
    update_int_11();
}

function post_int_12(num12) {
    int_12 = num12;
    var post_int_12 = new Firebase(URL_firebase + "int_12");
    post_int_12.set({
      int_12: int_12,
    });
    update_int_12();
}

// Update string memory slots

function update_str_01() {
    var update_str_01 = new Firebase(URL_firebase + "str_01");
    update_str_01.on("value", function(data) {
        var result = data.val().str_01;
        str_01 = result;
        swfObject.set_str_01(str_01);
    });
}

function update_str_02() {
    var update_str_02 = new Firebase(URL_firebase + "str_02");
    update_str_02.on("value", function(data) {
        var result = data.val().str_02;
        str_02 = result;
        swfObject.set_str_02(str_02);
    });
}

function update_str_03() {
    var update_str_03 = new Firebase(URL_firebase + "str_03");
    update_str_03.on("value", function(data) {
        var result = data.val().str_03;
        str_03 = result;
        swfObject.set_str_03(str_03);
    });
}

function update_str_04() {
    var update_str_04 = new Firebase(URL_firebase + "str_04");
    update_str_04.on("value", function(data) {
        var result = data.val().str_04;
        str_04 = result;
        swfObject.set_str_04(str_04);
    });
}

function update_str_05() {
    var update_str_05 = new Firebase(URL_firebase + "str_05");
    update_str_05.on("value", function(data) {
        var result = data.val().str_05;
        str_05 = result;
        swfObject.set_str_05(str_05);
    });
}

function update_str_06() {
    var update_str_06 = new Firebase(URL_firebase + "str_06");
    update_str_06.on("value", function(data) {
        var result = data.val().str_06;
        str_06 = result;
        swfObject.set_str_06(str_06);
    });
}

function update_str_07() {
    var update_str_07 = new Firebase(URL_firebase + "str_07");
    update_str_07.on("value", function(data) {
        var result = data.val().str_07;
        str_07 = result;
        swfObject.set_str_07(str_07);
    });
}

function update_str_08() {
    var update_str_08 = new Firebase(URL_firebase + "str_08");
    update_str_08.on("value", function(data) {
        var result = data.val().str_08;
        str_08 = result;
        swfObject.set_str_08(str_08);
    });
}

function update_str_09() {
    var update_str_09 = new Firebase(URL_firebase + "str_09");
    update_str_09.on("value", function(data) {
        var result = data.val().str_09;
        str_09 = result;
        swfObject.set_str_09(str_09);
    });
}

function update_str_10() {
    var update_str_10 = new Firebase(URL_firebase + "str_10");
    update_str_10.on("value", function(data) {
        var result = data.val().str_10;
        str_10 = result;
        swfObject.set_str_10(str_10);
    });
}

function update_str_11() {
    var update_str_11 = new Firebase(URL_firebase + "str_11");
    update_str_11.on("value", function(data) {
        var result = data.val().str_11;
        str_11 = result;
        swfObject.set_str_11(str_11);
    });
}

function update_str_12() {
    var update_str_12 = new Firebase(URL_firebase + "str_12");
    update_str_12.on("value", function(data) {
        var result = data.val().str_12;
        str_12 = result;
        swfObject.set_str_12(str_12);
    });
}

// Update integer memory slots

function update_int_01() {
    var update_int_01 = new Firebase(URL_firebase + "int_01");
    update_int_01.on("value", function(data) {
        var result = data.val().int_01;
        int_01 = result;
        swfObject.set_int_01(int_01);
    });
}

function update_int_02() {
    var update_int_02 = new Firebase(URL_firebase + "int_02");
    update_int_02.on("value", function(data) {
        var result = data.val().int_02;
        int_02 = result;
        swfObject.set_int_02(int_02);
    });
}

function update_int_03() {
    var update_int_03 = new Firebase(URL_firebase + "int_03");
    update_int_03.on("value", function(data) {
        var result = data.val().int_03;
        int_03 = result;
        swfObject.set_int_03(int_03);
    });
}

function update_int_04() {
    var update_int_04 = new Firebase(URL_firebase + "int_04");
    update_int_04.on("value", function(data) {
        var result = data.val().int_04;
        int_04 = result;
        swfObject.set_int_04(int_04);
    });
}

function update_int_05() {
    var update_int_05 = new Firebase(URL_firebase + "int_05");
    update_int_05.on("value", function(data) {
        var result = data.val().int_05;
        int_05 = result;
        swfObject.set_int_05(int_05);
    });
}

function update_int_06() {
    var update_int_06 = new Firebase(URL_firebase + "int_06");
    update_int_06.on("value", function(data) {
        var result = data.val().int_06;
        int_06 = result;
        swfObject.set_int_06(int_06);
    });
}

function update_int_07() {
    var update_int_07 = new Firebase(URL_firebase + "int_07");
    update_int_07.on("value", function(data) {
        var result = data.val().int_07;
        int_07 = result;
        swfObject.set_int_07(int_07);
    });
}

function update_int_08() {
    var update_int_08 = new Firebase(URL_firebase + "int_08");
    update_int_08.on("value", function(data) {
        var result = data.val().int_08;
        int_08 = result;
        swfObject.set_int_08(int_08);
    });
}

function update_int_09() {
    var update_int_09 = new Firebase(URL_firebase + "int_09");
    update_int_09.on("value", function(data) {
        var result = data.val().int_09;
        int_09 = result;
        swfObject.set_int_09(int_09);
    });
}

function update_int_10() {
    var update_int_10 = new Firebase(URL_firebase + "int_10");
    update_int_10.on("value", function(data) {
        var result = data.val().int_10;
        int_10 = result;
        swfObject.set_int_10(int_10);
    });
}

function update_int_11() {
    var update_int_11 = new Firebase(URL_firebase + "int_11");
    update_int_11.on("value", function(data) {
        var result = data.val().int_11;
        int_11 = result;
        swfObject.set_int_11(int_11);
    });
}

function update_int_12() {
    var update_int_12 = new Firebase(URL_firebase + "int_12");
    update_int_12.on("value", function(data) {
        var result = data.val().int_12;
        int_12 = result;
        swfObject.set_int_12(int_12);
    });
}

<commit_msg>Delete javascriptHX.js<commit_after><|endoftext|>


<commit_before>(function() {
  'use strict';
  var relvis = window.relvis = window.relvis || {};
  relvis.nodes = [];
  relvis.edges = [];
  var categories = { //{{{1
    authorInfo: ['creator', 'dbcaddi:hasCreatorDescription'],
    review: ['dbcaddi:hasReview', 'dbcaddi:hasAnalysis', 'dbcaddi:hasDescriptionFromPublisher', 'dbcaddi:discussedIn'],
    circular: ['subject'],
    structure: ['dbcaddi:isAnalysisOf', 'dbcaddi:isReviewOf', 'dbcbib:isPartOfManifestation', 'dbcaddi:isDescriptionFromPublisherOf', 'dbcaddi:discusses', 'dbcaddi:hasAdaptation', 'dbcaddi:isAdaptationOf', 'dbcaddi:isManuscriptOf', 'dbcaddi:hasManuscript', 'dbcaddi:continues', 'dbcaddi:continuedIn', 'dbcaddi:isSoundtrackOfMovie', 'dbcaddi:isSoundtrackOfGame', 'dbcaddi:hasSoundtrack', 'dbcaddi:isPartOfAlbum', 'dbcaddi:hasTrack']
  };
  //'dbcaddi:hasOnlineAccess', 'dbcaddi:hasSoundClip'

  relvis.addEventListener('data-update', relvis.throttle(300, function createGraph() { //{{{1

    var type = relvis.getType();
    var ids = relvis.getIds();

    var key;
    var nodeMap = {};
    var prevNodes, root, nodes, edges, i, rel, categoryMap, categoryNodes, property, node, categoryNodeList, j;

    //{{{2 general graph generation
    prevNodes = {};
    relvis.nodes.forEach(function(node) {
      prevNodes[node.id] = node;
    });
    nodes = relvis.nodes = [];
    edges = relvis.edges = [];

    function createNode(node) {
      var prev = nodeMap[node.id] || prevNodes[node.id];
      if (prev) {
        for (var key in node) {
          if (node.hasOwnProperty(key)) {
            prev[key] = node[key];
          }
        }
        node = prev;
      }
      if (node.imgSrc === undefined) {
        node.imgSrc = relvis.getValues(node.id, 'cover')[0];
      }
      if (node.label === undefined || node.label === '...') {
        node.label = relvis.getValues(node.id, 'title')[0] || '...';
      }

      nodeMap[node.id] = node;
      return node;
    }

    //circular relations {{{2
    var traverseIds = [];
    var nextIds = {};
    var traverseDepth;

    function traverseGraph() {
      var ids, id, j, i, node, depth;
      ids = traverseIds;
      traverseIds = [];
      depth = traverseDepth[0];
      traverseDepth = traverseDepth.slice(1);
      if (typeof depth !== 'number') {
        return;
      }
      for (j = 0; j < ids.length; ++j) {
        id = ids[j];
        if (!nodeMap[id]) {
          console.log('error: expected id in nodemap', id);
        }
        node = nodeMap[id];
        var related = relvis.getValues(id, 'related');
        if (related.length) {
          related = related[0];
          var count = 0;
          for (i = 0; count < depth && i < related.length; ++i) {
            var branchId = related[i].id;
            if (!nodeMap[branchId]) {
              ++count;
              var branchNode = createNode({
                id: branchId,
                visible: true
              });
              traverseIds.push(branchId);
              edges.push({
                source: node,
                target: branchNode
              });
            }
          }
        }
      }
      traverseGraph();
    }

    //{{{2 external relations
    function createExternalRelations(id, group) {
      function createCategoryNodes() { // {{{3
        var i;
        categoryNodes = {};
        categoryMap = {};
        categoryNodeList = [];
        for (var category in categories) {
          if (categories.hasOwnProperty(category)) {
            categoryNodes[category] = createNode({
              id: 'category:' + category,
              label: category,
              type: 'category',
              visible: false
            });
            categoryNodeList.push(categoryNodes[category]);
            for (i = 0; i < categories[category].length; ++i) {
              categoryMap[categories[category][i]] = category;
            }
          }
        }
        categoryNodeList[0].fixedPosition = {
          x: 0,
          y: 0
        };
        categoryNodeList[1].fixedPosition = {
          x: 1,
          y: 0
        };
        categoryNodeList[2].fixedPosition = {
          x: 0,
          y: 1
        };
        categoryNodeList[3].fixedPosition = {
          x: 1,
          y: 1
        };
      }

      function createRelationNodes() { //{{{3
        Object.keys(categories).forEach(function(category) {
          categories[category].forEach(function(property) {
            relvis.getValues(id, property).forEach(function(value) {
              node = createNode({
                id: group + '-' + property + '-' + value,
                label: value,
                property: property,
                value: value,
                visible: true
              });
              if (node.label.trim().match(/^\d\d\d\d\d\d-[a-z]*:\d*$/)) {
                node.label = relvis.getValues(node.label, 'title')[0] || 'Loading...';
              }
              edges.push({
                source: categoryNodes[category],
                target: node
              });
              edges.push({
                source: root,
                target: node
              });
            });
          });
        });
      }

      function createRootNode() { //{{{3
        root = createNode({
          id: id,
          label: 'root',
          type: 'root',
          visible: true
        });
        root.imgSrc = relvis.getValues(id, 'cover')[0];
        root.label = relvis.getValues(id, 'title')[0] || '...';
      }

      createRootNode();
      createCategoryNodes();
      createRelationNodes();
    }
    //actual execution {{{2
    if (type === 'ext') {
      for (i = 0; i < ids.length; ++i) {
        createExternalRelations(ids[i], ids);
        for (j = 0; j < i; ++j) {
          edges.push({
            source: nodeMap[ids[i]],
            target: nodeMap[ids[j]],
            type: 'collection'
          });
        }
      }
    } else {
      if (ids.length <= 1) {
        traverseDepth = [9, 3];
      } else if (ids.length <= 2) {
        traverseDepth = [4, 3];
      } else if (ids.length <= 3) {
        traverseDepth = [3, 2];
      } else if (ids.length <= 7) {
        traverseDepth = [2, 2];
      } else if (ids.length <= 13) {
        traverseDepth = [3];
      } else if (ids.length <= 20) {
        traverseDepth = [2];
      } else {
        traverseDepth = [1];
      }

      for (i = 0; i < ids.length; ++i) {
        traverseIds.push(ids[i]);
        node = createNode({
          id: ids[i],
          type: 'primary',
          visible: true
        });
        for (j = 0; j < i; ++j) {
          edges.push({
            source: nodeMap[ids[i]],
            target: nodeMap[ids[j]],
            type: 'collection'
          });
        }
      }
      traverseGraph();
    }

    for (key in nodeMap) {
      if (nodeMap.hasOwnProperty(key)) {
        nodes.push(nodeMap[key]);
      }
    }

    relvis.layoutGraph();
    return {
      nodes: nodes,
      edges: edges
    };
  }));
})(); //{{{1
<commit_msg>experiments with different traversals in circular relations<commit_after>(function() {
  'use strict';
  var relvis = window.relvis = window.relvis || {};
  relvis.nodes = [];
  relvis.edges = [];
  var categories = { //{{{1
    authorInfo: ['creator', 'dbcaddi:hasCreatorDescription'],
    review: ['dbcaddi:hasReview', 'dbcaddi:hasAnalysis', 'dbcaddi:hasDescriptionFromPublisher', 'dbcaddi:discussedIn'],
    circular: ['subject'],
    structure: ['dbcaddi:isAnalysisOf', 'dbcaddi:isReviewOf', 'dbcbib:isPartOfManifestation', 'dbcaddi:isDescriptionFromPublisherOf', 'dbcaddi:discusses', 'dbcaddi:hasAdaptation', 'dbcaddi:isAdaptationOf', 'dbcaddi:isManuscriptOf', 'dbcaddi:hasManuscript', 'dbcaddi:continues', 'dbcaddi:continuedIn', 'dbcaddi:isSoundtrackOfMovie', 'dbcaddi:isSoundtrackOfGame', 'dbcaddi:hasSoundtrack', 'dbcaddi:isPartOfAlbum', 'dbcaddi:hasTrack']
  };
  //'dbcaddi:hasOnlineAccess', 'dbcaddi:hasSoundClip'

  relvis.addEventListener('data-update', relvis.throttle(300, function createGraph() { //{{{1

    var type = relvis.getType();
    var ids = relvis.getIds();

    var key;
    var nodeMap = {};
    var prevNodes, root, nodes, edges, i, rel, categoryMap, categoryNodes, property, node, categoryNodeList, j;

    //{{{2 general graph generation
    prevNodes = {};
    relvis.nodes.forEach(function(node) {
      prevNodes[node.id] = node;
    });
    nodes = relvis.nodes = [];
    edges = relvis.edges = [];

    function createNode(node) {
      var prev = nodeMap[node.id] || prevNodes[node.id];
      if (prev) {
        for (var key in node) {
          if (node.hasOwnProperty(key)) {
            prev[key] = node[key];
          }
        }
        node = prev;
      }
      if (node.imgSrc === undefined) {
        node.imgSrc = relvis.getValues(node.id, 'cover')[0];
      }
      if (node.label === undefined || node.label === '...') {
        node.label = relvis.getValues(node.id, 'title')[0] || '...';
      }

      nodeMap[node.id] = node;
      return node;
    }

    //circular relations {{{2
    var traverseIds = [];
    var nextIds = {};
    var traverseDepth;

    function traverseGraph() {
      var ids, id, j, i, node, depth;
      ids = traverseIds;
      traverseIds = [];
      depth = traverseDepth[0];
      traverseDepth = traverseDepth.slice(1);
      if (typeof depth !== 'number') {
        return;
      }
      for (j = 0; j < ids.length; ++j) {
        id = ids[j];
        if (!nodeMap[id]) {
          console.log('error: expected id in nodemap', id);
        }
        node = nodeMap[id];
        var related = relvis.getValues(id, 'related');
        if (related.length) {
          related = related[0];
          var count = 0;
          for (i = 0; count < depth && i < related.length; ++i) {
            var branchId = related[i].id;
            if (!nodeMap[branchId]) {
              ++count;
              var branchNode = createNode({
                id: branchId,
                visible: true
              });
              traverseIds.push(branchId);
              edges.push({
                source: node,
                target: branchNode
              });
            }
          }
        }
      }
      traverseGraph();
    }

    //{{{2 external relations
    function createExternalRelations(id, group) {
      function createCategoryNodes() { // {{{3
        var i;
        categoryNodes = {};
        categoryMap = {};
        categoryNodeList = [];
        for (var category in categories) {
          if (categories.hasOwnProperty(category)) {
            categoryNodes[category] = createNode({
              id: 'category:' + category,
              label: category,
              type: 'category',
              visible: false
            });
            categoryNodeList.push(categoryNodes[category]);
            for (i = 0; i < categories[category].length; ++i) {
              categoryMap[categories[category][i]] = category;
            }
          }
        }
        categoryNodeList[0].fixedPosition = {
          x: 0,
          y: 0
        };
        categoryNodeList[1].fixedPosition = {
          x: 1,
          y: 0
        };
        categoryNodeList[2].fixedPosition = {
          x: 0,
          y: 1
        };
        categoryNodeList[3].fixedPosition = {
          x: 1,
          y: 1
        };
      }

      function createRelationNodes() { //{{{3
        Object.keys(categories).forEach(function(category) {
          categories[category].forEach(function(property) {
            relvis.getValues(id, property).forEach(function(value) {
              node = createNode({
                id: group + '-' + property + '-' + value,
                label: value,
                property: property,
                value: value,
                visible: true
              });
              if (node.label.trim().match(/^\d\d\d\d\d\d-[a-z]*:\d*$/)) {
                node.label = relvis.getValues(node.label, 'title')[0] || 'Loading...';
              }
              edges.push({
                source: categoryNodes[category],
                target: node
              });
              edges.push({
                source: root,
                target: node
              });
            });
          });
        });
      }

      function createRootNode() { //{{{3
        root = createNode({
          id: id,
          label: 'root',
          type: 'root',
          visible: true
        });
        root.imgSrc = relvis.getValues(id, 'cover')[0];
        root.label = relvis.getValues(id, 'title')[0] || '...';
      }

      createRootNode();
      createCategoryNodes();
      createRelationNodes();
    }
    //actual execution {{{2
    if (type === 'ext') {
      for (i = 0; i < ids.length; ++i) {
        createExternalRelations(ids[i], ids);
        for (j = 0; j < i; ++j) {
          edges.push({
            source: nodeMap[ids[i]],
            target: nodeMap[ids[j]],
            type: 'collection'
          });
        }
      }
    } else {
      if(true) {
      if (ids.length <= 1) {
        traverseDepth = [9, 3];
      } else if (ids.length <= 2) {
        traverseDepth = [4, 3];
      } else if (ids.length <= 3) {
        traverseDepth = [3, 2];
      } else if (ids.length <= 7) {
        traverseDepth = [2, 2];
      } else if (ids.length <= 13) {
        traverseDepth = [3];
      } else if (ids.length <= 20) {
        traverseDepth = [2];
      } else {
        traverseDepth = [1];
      }
      } else {
        traverseDepth = [Math.ceil(13 / ids.length)];
        traverseDepth = [Math.ceil(19 / ids.length)];
      }

      for (i = 0; i < ids.length; ++i) {
        traverseIds.push(ids[i]);
        node = createNode({
          id: ids[i],
          type: 'primary',
          visible: true
        });
        for (j = 0; j < i; ++j) {
          edges.push({
            source: nodeMap[ids[i]],
            target: nodeMap[ids[j]],
            type: 'collection'
          });
        }
      }
      traverseGraph();
    }

    for (key in nodeMap) {
      if (nodeMap.hasOwnProperty(key)) {
        nodes.push(nodeMap[key]);
      }
    }

    relvis.layoutGraph();
    return {
      nodes: nodes,
      edges: edges
    };
  }));
})(); //{{{1
<|endoftext|>


<commit_before>import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import Head from "next/head";
import ErrorPage from "../pages/404";
import Container from "./Container";
import Header from "./Header";
import Footer from "./Footer";
import SingleColumn from "./SingleColumn";
import Section from "./Section";
import markdownStyles from "../styles/markdown.module.css";
import { decode } from "html-entities";

const TableOfContents = () => {
  const { nestedHeadings } = useHeadingsData();
  return (

On this page

      <Headings headings={nestedHeadings} />

  );
};

const getNestedHeadings = (headingElements) => {
  const nestedHeadings = [];

  headingElements.forEach((heading, index) => {
    const { innerText: title, id } = heading;

    if (heading.nodeName === "H2") {
      nestedHeadings.push({ id, title, items: [] });
    } else if (heading.nodeName === "H3" && nestedHeadings.length > 0) {
      nestedHeadings[nestedHeadings.length - 1].items.push({
        id,
        title,
      });
    }
  });

  return nestedHeadings;
};

const useHeadingsData = () => {
  const [nestedHeadings, setNestedHeadings] = useState([]);

  useEffect(() => {
    const headingElements = Array.from(document.querySelectorAll("h2, h3"));

    const newNestedHeadings = getNestedHeadings(headingElements);
    setNestedHeadings(newNestedHeadings);
  }, []);

  return { nestedHeadings };
};

const Headings = ({ headings }) => (

    {headings.map((heading) => (

        <a className="font-semibold text-sm" href={`#${heading.id}`}>
          {heading.title}

        {heading.items.length > 0 && (

            {heading.items.map((child) => (

                <a href={`#${child.id}`}>{child.title}

            ))}

        )}

    ))}

);

export default function PageWithIndex({ post, markdown, search }) {
  const router = useRouter();
  if (!router.isFallback && !post?.slug) {
    return <ErrorPage />;
  }
  return (
    <Container>

        <title>Urbit • {post.title}</title>

      <SingleColumn>

{post.title}

            <TableOfContents />

      </SingleColumn>

    </Container>
  );
}
<commit_msg>PageWithIndex: hide on sm<commit_after>import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import Head from "next/head";
import ErrorPage from "../pages/404";
import Container from "./Container";
import Header from "./Header";
import Footer from "./Footer";
import SingleColumn from "./SingleColumn";
import Section from "./Section";
import markdownStyles from "../styles/markdown.module.css";
import { decode } from "html-entities";

const TableOfContents = () => {
  const { nestedHeadings } = useHeadingsData();
  return (

On this page

      <Headings headings={nestedHeadings} />

  );
};

const getNestedHeadings = (headingElements) => {
  const nestedHeadings = [];

  headingElements.forEach((heading, index) => {
    const { innerText: title, id } = heading;

    if (heading.nodeName === "H2") {
      nestedHeadings.push({ id, title, items: [] });
    } else if (heading.nodeName === "H3" && nestedHeadings.length > 0) {
      nestedHeadings[nestedHeadings.length - 1].items.push({
        id,
        title,
      });
    }
  });

  return nestedHeadings;
};

const useHeadingsData = () => {
  const [nestedHeadings, setNestedHeadings] = useState([]);

  useEffect(() => {
    const headingElements = Array.from(document.querySelectorAll("h2, h3"));

    const newNestedHeadings = getNestedHeadings(headingElements);
    setNestedHeadings(newNestedHeadings);
  }, []);

  return { nestedHeadings };
};

const Headings = ({ headings }) => (

    {headings.map((heading) => (

        <a className="font-semibold text-sm" href={`#${heading.id}`}>
          {heading.title}

        {heading.items.length > 0 && (

            {heading.items.map((child) => (

                <a href={`#${child.id}`}>{child.title}

            ))}

        )}

    ))}

);

export default function PageWithIndex({ post, markdown, search }) {
  const router = useRouter();
  if (!router.isFallback && !post?.slug) {
    return <ErrorPage />;
  }
  return (
    <Container>

        <title>Urbit • {post.title}</title>

      <SingleColumn>

{post.title}

            <TableOfContents />

      </SingleColumn>

    </Container>
  );
}
<|endoftext|>