<commit_before>/*
 * grunt-smart-rev
 *
 *
 * Copyright (c) 2014 Disqus
 * Licensed under the MIT license.
 */

'use strict';

var crypto = require('crypto');
var fs = require('fs');
var path = require('path');

var _ = require('lodash');

var normalizePath = function (name) {
    // We need the split/join below due to Windows having \ as path separator :(
    return path.normalize(name).split(path.sep).join('/');
};

var Dependency;  // will be defined below and is used by Tree.prototype.get
var Tree = function (baseUrl, noRename, hashAlgo) {
    this.baseUrl = baseUrl;
    this.noRename = noRename;
    this.hashAlgo = hashAlgo || 'md5';
    this.nodes = {};
};

Tree.handlers = require('./handlers');

Tree._dummyHandler = {
    extract: _.noop,
    substitute: _.noop
};

Tree.getHandler = function (node) {
    return this.handlers[node.ext] || Tree._dummyHandler;
};

Tree.prototype.generate = function (files) {
    var file;
    for (var i = 0, len = files.length; i < len; i++) {
        file = files[i];
        this.get(file).findDependencies(file);
    }

    return this;
};

Tree.prototype.get = function (name) {
    name = normalizePath(name);
    var dep = this.nodes[name];
    if (!dep)
        dep = this.nodes[name] = new Dependency(name, this);

    return dep;
};

Tree.prototype.process = function () {
    var levels = _(this.nodes).groupBy(function (node) {
        return node.height();
    }).toArray().value();

    // Not using the functional style (.forEach) below since we have 2 nested
    // loops and creating a new function in a loop (the first one) would be bad
    var hashAlgo = this.hashAlgo;
    var level, node, name;
    for (var height = 0, maxHeight = levels.length - 1; height <= maxHeight; height++) {
        level = levels[height];
        for (var i = 0, len = level.length; i < len; i++) {
            node = level[i];
            name = node.name;

            // Should be done before hashing and renaming!
            node.updateDependencyReferences();

            node.marks = null;
            node.ast = null;

            if (_.contains(this.noRename, name))
                continue;

            node.hash = crypto.createHash(hashAlgo)
                              .update(fs.readFileSync(name))
                              .digest('hex');
            node.name = normalizePath(
                path.dirname(name) + '/' +
                path.basename(name, node.ext) + '.' +  node.hash + node.ext
            );

            fs.renameSync(name, node.name);
        }
    }

    return this;
};

Tree.prototype.stats = function () {
    var tree = this;
    return Object.keys(tree.nodes).reduce(function (stats, name) {
        var dep = tree.nodes[name];
        stats[name] = {
            newName: dep.name,
            height: dep.height(),
            size: fs.statSync(dep.name).size,
            dependencies: Object.keys(dep.dependencies)
        };

        return stats;
    }, {});
};

Dependency = function (name, tree) {
    this.tree = tree;
    this.name = name;
    this.ext = path.extname(name);
    this.dependencies = {};

    // This array will store the "AST-nodes" that contain references to other
    // files so that we won't need to parse the source again, when replacing
    // those references with the modified files names (the ones with hashes).
    this.marks = [];
};

Dependency.prototype._height = null;
Dependency.prototype.ast = null;
Dependency.prototype.hash = null;

Dependency.prototype.resolve = function (url) {
    return path.relative(process.cwd(), path.resolve(path.dirname(this.name), url));
};

Dependency.prototype.dependOn = function (node) {
    this.dependencies[node.name] = node;
};

Dependency.prototype.height = function () {
    if (this._height !== null)
        return this._height;

    var dependencies = _.values(this.dependencies);
    var result;
    if (!dependencies.length)
        result = 0;
    else
        result = 1 + Math.max.apply(Math, _.invoke(dependencies, 'height'));

    /* jshint boss: true */
    return this._height = result;
    /* jshint boss: false */
};

Dependency.prototype.findDependencies = function () {
    Tree.getHandler(this).extract.call(this);

    return this;
};

Dependency.prototype.updateDependencyReferences = function () {
    // Zero-height nodes don't have any dependencies so we don't need to update
    // any references. Yay, skip!
    if (!this.height())
        return this;

    Tree.getHandler(this).substitute.call(this);

    return this;
};

module.exports = function (grunt) {
    var filterFiles = function (filepath) {
        if (!grunt.file.exists(filepath)) {
            grunt.log.warn('Source file "' + filepath + '" not found.');
            return false;
        } else {
            return true;
        }
    };

    grunt.registerMultiTask(
        'smartrev', 'Walks all files, creates a dependency tree and renames all of them using hashes of their contents',
        function () {
            var owd = process.cwd();
            var options = this.options({
                cwd: '.',
                baseUrl: '',
                noRename: [],
                algorithm: 'md5'
            });

            process.chdir(options.cwd);

            // Iterate over all specified file groups.
            this.files.forEach(function (f) {
                var tree = new Tree(
                    _.result(options, 'baseUrl'),
                    grunt.file.expand({}, options.noRename),
                    options.algorithm
                ).generate(f.src.filter(filterFiles)).process();

                if (f.dest)
                    grunt.file.write(f.dest, JSON.stringify(tree.stats()));
            });

            process.chdir(owd);
        }
    );
};
<commit_msg>Don't use `_.toArray` since it acts carelessly<commit_after>/*
 * grunt-smart-rev
 *
 *
 * Copyright (c) 2014 Disqus
 * Licensed under the MIT license.
 */

'use strict';

var crypto = require('crypto');
var fs = require('fs');
var path = require('path');

var _ = require('lodash');

var normalizePath = function (name) {
    // We need the split/join below due to Windows having \ as path separator :(
    return path.normalize(name).split(path.sep).join('/');
};

var Dependency;  // will be defined below and is used by Tree.prototype.get
var Tree = function (baseUrl, noRename, hashAlgo) {
    this.baseUrl = baseUrl;
    this.noRename = noRename;
    this.hashAlgo = hashAlgo || 'md5';
    this.nodes = {};
};

Tree.handlers = require('./handlers');

Tree._dummyHandler = {
    extract: _.noop,
    substitute: _.noop
};

Tree.getHandler = function (node) {
    return this.handlers[node.ext] || Tree._dummyHandler;
};

Tree.prototype.generate = function (files) {
    var file;
    for (var i = 0, len = files.length; i < len; i++) {
        file = files[i];
        this.get(file).findDependencies(file);
    }

    return this;
};

Tree.prototype.get = function (name) {
    name = normalizePath(name);
    var dep = this.nodes[name];
    if (!dep)
        dep = this.nodes[name] = new Dependency(name, this);

    return dep;
};

Tree.prototype.process = function () {
    var levels = _.groupBy(this.nodes, function (node) {
        return node.height();
    });

    var hashAlgo = this.hashAlgo;
    var level, node, name;
    for (var height = 0, maxHeight = _.size(levels) - 1; height <= maxHeight; height++) {
        level = levels[height];
        for (var i = 0, len = level.length; i < len; i++) {
            node = level[i];
            name = node.name;

            // Should be done before hashing and renaming!
            node.updateDependencyReferences();

            node.marks = null;
            node.ast = null;

            if (_.contains(this.noRename, name))
                continue;

            node.hash = crypto.createHash(hashAlgo)
                              .update(fs.readFileSync(name))
                              .digest('hex');
            node.name = normalizePath(
                path.dirname(name) + '/' +
                path.basename(name, node.ext) + '.' +  node.hash + node.ext
            );

            fs.renameSync(name, node.name);
        }
    }

    return this;
};

Tree.prototype.stats = function () {
    var tree = this;
    return Object.keys(tree.nodes).reduce(function (stats, name) {
        var dep = tree.nodes[name];
        stats[name] = {
            newName: dep.name,
            height: dep.height(),
            size: fs.statSync(dep.name).size,
            dependencies: Object.keys(dep.dependencies)
        };

        return stats;
    }, {});
};

Dependency = function (name, tree) {
    this.tree = tree;
    this.name = name;
    this.ext = path.extname(name);
    this.dependencies = {};

    // This array will store the "AST-nodes" that contain references to other
    // files so that we won't need to parse the source again, when replacing
    // those references with the modified files names (the ones with hashes).
    this.marks = [];
};

Dependency.prototype._height = null;
Dependency.prototype.ast = null;
Dependency.prototype.hash = null;

Dependency.prototype.resolve = function (url) {
    return path.relative(process.cwd(), path.resolve(path.dirname(this.name), url));
};

Dependency.prototype.dependOn = function (node) {
    this.dependencies[node.name] = node;
};

Dependency.prototype.height = function () {
    if (this._height !== null)
        return this._height;

    var dependencies = _.values(this.dependencies);
    var result;
    if (!dependencies.length)
        result = 0;
    else
        result = 1 + Math.max.apply(Math, _.invoke(dependencies, 'height'));

    /* jshint boss: true */
    return this._height = result;
    /* jshint boss: false */
};

Dependency.prototype.findDependencies = function () {
    Tree.getHandler(this).extract.call(this);

    return this;
};

Dependency.prototype.updateDependencyReferences = function () {
    // Zero-height nodes don't have any dependencies so we don't need to update
    // any references. Yay, skip!
    if (!this.height())
        return this;

    Tree.getHandler(this).substitute.call(this);

    return this;
};

module.exports = function (grunt) {
    var filterFiles = function (filepath) {
        if (!grunt.file.exists(filepath)) {
            grunt.log.warn('Source file "' + filepath + '" not found.');
            return false;
        } else {
            return true;
        }
    };

    grunt.registerMultiTask(
        'smartrev', 'Walks all files, creates a dependency tree and renames all of them using hashes of their contents',
        function () {
            var owd = process.cwd();
            var options = this.options({
                cwd: '.',
                baseUrl: '',
                noRename: [],
                algorithm: 'md5'
            });

            process.chdir(options.cwd);

            // Iterate over all specified file groups.
            this.files.forEach(function (f) {
                var tree = new Tree(
                    _.result(options, 'baseUrl'),
                    grunt.file.expand({}, options.noRename),
                    options.algorithm
                ).generate(f.src.filter(filterFiles)).process();

                if (f.dest)
                    grunt.file.write(f.dest, JSON.stringify(tree.stats()));
            });

            process.chdir(owd);
        }
    );
};
<|endoftext|>


<commit_before>!function(d3) {
    var NODE_SIZE = 3 // XXX Sync with stairs.less
    ,RADIUS = NODE_SIZE * (3/8) //XXX
    ,UNIT = "vw"
    ,optGroupLabels = {
        'Vars' : 'Train',
        'Terms' : 'Promote'
    }

    // Container for a mutable, d3-compatible graph-structure with mapped
    // HTML DOM nodes.
    function Graph() {
    }

    function makeTerm(txt) {
        var s = document.createElement("span");
        s.innerHTML = txt;
        return s;
    }

    function populateSelect(specifyOptionsProvider, select) {
        var specifyOptions = specifyOptionsProvider();
        var oldValue = select.value;
        while (select.lastChild != select.firstChild) {
            select.removeChild(select.lastChild);
        }
        for (var k in specifyOptions) if (specifyOptions.hasOwnProperty(k)) {
            var og = select.appendChild(document.createElement("optGroup"));
            og.label = optGroupLabels.hasOwnProperty(k) ?
                optGroupLabels[k] : k;
            og.className = k;
            specifyOptions[k].forEach(function(o, i) {
                var opt = og.appendChild(
                    document.createElement("option"));
                opt.value = JSON.stringify([k, i]);
                if (opt.value == oldValue) {
                    opt.selected = "selected";
                }
                opt.innerHTML = o;
            });
        }
    }

    // TODO: ugly: varMap managed here but created elsewhere
    function makeVar(varMap, txt, path, opts) {
        if (txt === undefined) throw new Error("undef");
        if (!varMap.hasOwnProperty(txt)) {
            varMap[txt] = [];
        }
        var s = document.createElement("select");
        varMap[txt].push(s);
        s.className = "select";
        var ph = s.appendChild(document.createElement("option"));
        ph.innerHTML = txt;
        ph.className = 'placeholder';
        ph.selected = 'selected';
        ph.disabled = 'disabled';
        if (!opts.editable) {
            s.disabled = "disabled";
            return s;
        }

        var populator = populateSelect.bind(null, opts.getSpecifyOptions);
        s.addEventListener("focus", populator.bind(null, s));
        s.addEventListener("change", function(ev) {
            var value = JSON.parse(s.value);
            if (value !== null) {
                varMap[txt].forEach(function(select) {
                    if (select !== s) {
                        populator(select);
                        select.value = s.value;
                    }
                });
                //XXX opts.onChange(path, value[0], value[1]);
            }
        });

        return s;
    }

    function makeGraph(exp, groupDiv, linkGroup, spanMap, opts) {
        var ancestors = [{div: groupDiv, path:[], tool: null, numArgs: null}];
        function recurse(exp, i) {
            var parent = ancestors[ancestors.length-1];
            var n = {
                exp: exp,
                path: parent.path.slice(),
                div: document.createElement("div"),
                height: 1
            };
            if (i !== undefined) {
                n.path.push(i + 1);
                n.link = linkGroup.appendChild(document.createElement("div"));
                n.link.className = "link";
            }
            parent.div.appendChild(n.div);
            spanMap[n.path] = n.div;
            if (opts.onclick) {
                n.div.addEventListener("click", function(ev) {
                    opts.onclick(ev, n.path);
                });
            }

            n.div.className = "depth" + (n.path.length) +
                " input" + (i+1) + "of" + parent.numArgs +
                " tool" + parent.tool
            if (Array.isArray(exp)) {
                var termName = opts.fact.Skin.TermNames[exp[0]];
                n.tool = cssEscape(termName);
                n.span = makeTerm(termName);
                n.div.appendChild(n.span);
                n.div.className += " treeKids" + (exp.length - 1);
                n.numArgs = exp.length - 1;
                ancestors.push(n);
                n.children = exp.slice(1).map(recurse);
                ancestors.pop();
            } else {
                n.span = makeVar(opts.varMap, opts.fact.Skin.VarNames[exp], n.path,
                                 opts);
                n.div.appendChild(n.span);
                n.div.className += " treeLeaf treeText" + exp;
            }
            parent.height = Math.max(parent.height, n.height + 1);
            return n;
        }
        return recurse(exp);
    }

    // Walk tree, positioning and sizing container divs. Grows sets
    // node.divRect, and grows parentRect to contain it.
    function measureDivs(parentRect, node) {
        var myRect = {top: node.y - NODE_SIZE / 2,
                      left: node.x - NODE_SIZE / 2,
                      right:node.x + NODE_SIZE / 2,
                      bottom:node.y + NODE_SIZE / 2};
        if (node.children) {
            myRect = node.children.reduce(measureDivs, myRect);
            node.divRect = myRect;
        } else {
            node.divRect = myRect;
        }
        parentRect.top = Math.min(parentRect.top, myRect.top);
        parentRect.left = Math.min(parentRect.left, myRect.left);
        parentRect.right = Math.max(parentRect.right, myRect.right);
        parentRect.bottom = Math.max(parentRect.bottom, myRect.bottom);
        return parentRect;
    }

    function positionDivs(origins, scale, node, index) {
        node.div.style.width = scale * (node.divRect.right - node.divRect.left) + UNIT;
        node.div.style.height = scale * (node.divRect.bottom - node.divRect.top) + UNIT;
        node.div.style["z-index"] = 100 - node.height; // TODO:imperfect
        var origin = origins[origins.length-1];
        node.div.style.left = scale * (node.divRect.left - origin.left) + UNIT;
        node.div.style.top = scale * (node.divRect.top - origin.top) + UNIT;
        if (Array.isArray(node.exp)) {
            // term nodes sized and positioned here.
            // var nodes (leaves) positiend and sized through CSS.
            node.span.style.left = scale * (node.x - RADIUS - node.divRect.left) + UNIT;
            node.span.style.top = scale * (node.y - RADIUS - node.divRect.top) + UNIT;
            node.span.style.width =
                node.span.style.height =
                scale * RADIUS * 2 + UNIT;
            if (node.children) {
                origins.push(node.divRect);
                node.children.map(positionDivs.bind(null, origins, scale));
                origins.pop();
            }
        }
        if (node.link) {
            node.link.style.left = scale * (node.parent.x - origins[0].left) + UNIT;
            node.link.style.top = scale * (node.parent.y - origins[0].top) + UNIT;
            node.link.style.height = scale * (node.y - node.parent.y) + UNIT;
            // Matrix: should keep 0,0 constant, and move (0,y) to (n.x-p.x,y).
            var matrix = [1, 0,
                          (node.x - node.parent.x) / (node.y - node.parent.y), 1,
                          0, 0];
            node.link.style.transform = 'matrix(' + matrix.join(',') + ')';
        }
    }
    /**
     * Make a tree, i.e. a two-dimensional hierarchical display of an expression.
     *
     * @param opts.fact the Fact object for looking up operator names, etc.
     * @param opts.exp the expression to draw
     * @param opts.onclick
     * @param opts.size
     * @param opts.editable
     * @param opts.getSpecifyOptions
     * @return a DOM element containing the tree
     */
    var tm = function(opts) {
        var fact = opts.fact
        ,exp = opts.exp
        ,d3tree = d3.layout.tree()
        ,nodes = null
        ,root = document.createElement("div")
        ,linkGroup = document.createElement("div")
        ,nodeGroup = document.createElement("div")

        opts.varMap = {} // varName -> [select node]

        root.setAttribute("class", "root");
        root.appendChild(linkGroup);
        root.appendChild(nodeGroup);
        linkGroup.setAttribute("class", "linkGroup");
        nodeGroup.setAttribute("class", "nodeGroup");
        root.spanMap = {};

        d3tree.nodeSize([NODE_SIZE, NODE_SIZE]);
        d3tree.separation(function(a,b) {
            return a.parent == b.parent ? 1 : 1.5;
        });
        // Turning a term into a graph structure for d3. Also constructing
        // nested divs to mirror the graph structure.
        var graph = makeGraph(exp, nodeGroup, linkGroup, root.spanMap, opts);

        d3tree.nodes(graph);

        var rect = {left:Infinity, right:-Infinity, bottom:-Infinity, top: Infinity};
        measureDivs(rect, graph);
        rect.width = (rect.right - rect.left);
        rect.height = (rect.bottom - rect.top);
        // make fit within bounds. TODO: this is not exactly right
        var largerDim = (rect.width > rect.height) ? "width" : "height";
        var scale = opts.size / rect[largerDim];
        nodeGroup.style["font-size"] = 0.5 * NODE_SIZE * scale + UNIT;
        nodeGroup.style.width = linkGroup.style.width = scale * rect.width + UNIT;
        nodeGroup.style.height = linkGroup.style.height = scale * rect.height + UNIT;
        nodeGroup.style.left = nodeGroup.style.top = 0;
        var origins = [rect];
        positionDivs(origins, scale, graph);
        return root;
    };

    if (typeof define === "function" && define.amd) define(tm); else if (typeof module === "object" && module.exports) module.exports = tm;
    this.tm = tm;
}(d3);
<commit_msg>WIP: refactor rect<commit_after>!function(d3) {
    var NODE_SIZE = 3 // XXX Sync with stairs.less
    ,RADIUS = NODE_SIZE * (3/8) //XXX
    ,UNIT = "vw"
    ,optGroupLabels = {
        'Vars' : 'Train',
        'Terms' : 'Promote'
    }

    // Container for a mutable, d3-compatible graph-structure with mapped
    // HTML DOM nodes.
    function Graph() {
    }

    function makeTerm(txt) {
        var s = document.createElement("span");
        s.innerHTML = txt;
        return s;
    }

    function populateSelect(specifyOptionsProvider, select) {
        var specifyOptions = specifyOptionsProvider();
        var oldValue = select.value;
        while (select.lastChild != select.firstChild) {
            select.removeChild(select.lastChild);
        }
        for (var k in specifyOptions) if (specifyOptions.hasOwnProperty(k)) {
            var og = select.appendChild(document.createElement("optGroup"));
            og.label = optGroupLabels.hasOwnProperty(k) ?
                optGroupLabels[k] : k;
            og.className = k;
            specifyOptions[k].forEach(function(o, i) {
                var opt = og.appendChild(
                    document.createElement("option"));
                opt.value = JSON.stringify([k, i]);
                if (opt.value == oldValue) {
                    opt.selected = "selected";
                }
                opt.innerHTML = o;
            });
        }
    }

    // TODO: ugly: varMap managed here but created elsewhere
    function makeVar(varMap, txt, path, opts) {
        if (txt === undefined) throw new Error("undef");
        if (!varMap.hasOwnProperty(txt)) {
            varMap[txt] = [];
        }
        var s = document.createElement("select");
        varMap[txt].push(s);
        s.className = "select";
        var ph = s.appendChild(document.createElement("option"));
        ph.innerHTML = txt;
        ph.className = 'placeholder';
        ph.selected = 'selected';
        ph.disabled = 'disabled';
        if (!opts.editable) {
            s.disabled = "disabled";
            return s;
        }

        var populator = populateSelect.bind(null, opts.getSpecifyOptions);
        s.addEventListener("focus", populator.bind(null, s));
        s.addEventListener("change", function(ev) {
            var value = JSON.parse(s.value);
            if (value !== null) {
                varMap[txt].forEach(function(select) {
                    if (select !== s) {
                        populator(select);
                        select.value = s.value;
                    }
                });
                //XXX opts.onChange(path, value[0], value[1]);
            }
        });

        return s;
    }

    function makeGraph(exp, groupDiv, linkGroup, spanMap, opts) {
        var ancestors = [{div: groupDiv, path:[], tool: null, numArgs: null}];
        function recurse(exp, i) {
            var parent = ancestors[ancestors.length-1];
            var n = {
                exp: exp,
                path: parent.path.slice(),
                div: document.createElement("div"),
                height: 1
            };
            if (i !== undefined) {
                n.path.push(i + 1);
                n.link = linkGroup.appendChild(document.createElement("div"));
                n.link.className = "link";
            }
            parent.div.appendChild(n.div);
            spanMap[n.path] = n.div;
            if (opts.onclick) {
                n.div.addEventListener("click", function(ev) {
                    opts.onclick(ev, n.path);
                });
            }

            n.div.className = "depth" + (n.path.length) +
                " input" + (i+1) + "of" + parent.numArgs +
                " tool" + parent.tool
            if (Array.isArray(exp)) {
                var termName = opts.fact.Skin.TermNames[exp[0]];
                n.tool = cssEscape(termName);
                n.span = makeTerm(termName);
                n.div.appendChild(n.span);
                n.div.className += " treeKids" + (exp.length - 1);
                n.numArgs = exp.length - 1;
                ancestors.push(n);
                n.children = exp.slice(1).map(recurse);
                ancestors.pop();
            } else {
                n.span = makeVar(opts.varMap, opts.fact.Skin.VarNames[exp], n.path,
                                 opts);
                n.div.appendChild(n.span);
                n.div.className += " treeLeaf treeText" + exp;
            }
            parent.height = Math.max(parent.height, n.height + 1);
            return n;
        }
        return recurse(exp);
    }

    // Walk tree, positioning and sizing container divs. Grows sets
    // node.divRect, and grows parentRect to contain it.
    function measureDivs(parentRect, node) {
        if (!parentRect) {
            parentRect = {left:Infinity, right:-Infinity,
                          bottom:-Infinity, top: Infinity};
        }
        var myRect = {top: node.y - NODE_SIZE / 2,
                      left: node.x - NODE_SIZE / 2,
                      right:node.x + NODE_SIZE / 2,
                      bottom:node.y + NODE_SIZE / 2};
        if (node.children) {
            myRect = node.children.reduce(measureDivs, myRect);
            node.divRect = myRect;
        } else {
            node.divRect = myRect;
        }
        parentRect.top = Math.min(parentRect.top, myRect.top);
        parentRect.left = Math.min(parentRect.left, myRect.left);
        parentRect.right = Math.max(parentRect.right, myRect.right);
        parentRect.bottom = Math.max(parentRect.bottom, myRect.bottom);
        return parentRect;
    }

    function positionDivs(origins, scale, node, index) {
        node.div.style.width = scale * (node.divRect.right - node.divRect.left) + UNIT;
        node.div.style.height = scale * (node.divRect.bottom - node.divRect.top) + UNIT;
        node.div.style["z-index"] = 100 - node.height; // TODO:imperfect
        var origin = origins[origins.length-1];
        node.div.style.left = scale * (node.divRect.left - origin.left) + UNIT;
        node.div.style.top = scale * (node.divRect.top - origin.top) + UNIT;
        if (Array.isArray(node.exp)) {
            // term nodes sized and positioned here.
            // var nodes (leaves) positiend and sized through CSS.
            node.span.style.left = scale * (node.x - RADIUS - node.divRect.left) + UNIT;
            node.span.style.top = scale * (node.y - RADIUS - node.divRect.top) + UNIT;
            node.span.style.width =
                node.span.style.height =
                scale * RADIUS * 2 + UNIT;
            if (node.children) {
                origins.push(node.divRect);
                node.children.map(positionDivs.bind(null, origins, scale));
                origins.pop();
            }
        }
        if (node.link) {
            node.link.style.left = scale * (node.parent.x - origins[0].left) + UNIT;
            node.link.style.top = scale * (node.parent.y - origins[0].top) + UNIT;
            node.link.style.height = scale * (node.y - node.parent.y) + UNIT;
            // Matrix: should keep 0,0 constant, and move (0,y) to (n.x-p.x,y).
            var matrix = [1, 0,
                          (node.x - node.parent.x) / (node.y - node.parent.y), 1,
                          0, 0];
            node.link.style.transform = 'matrix(' + matrix.join(',') + ')';
        }
    }
    /**
     * Make a tree, i.e. a two-dimensional hierarchical display of an expression.
     *
     * @param opts.fact the Fact object for looking up operator names, etc.
     * @param opts.exp the expression to draw
     * @param opts.onclick
     * @param opts.size
     * @param opts.editable
     * @param opts.getSpecifyOptions
     * @return a DOM element containing the tree
     */
    var tm = function(opts) {
        var fact = opts.fact
        ,exp = opts.exp
        ,d3tree = d3.layout.tree()
        ,nodes = null
        ,root = document.createElement("div")
        ,linkGroup = document.createElement("div")
        ,nodeGroup = document.createElement("div")

        opts.varMap = {} // varName -> [select node]

        root.setAttribute("class", "root");
        root.appendChild(linkGroup);
        root.appendChild(nodeGroup);
        linkGroup.setAttribute("class", "linkGroup");
        nodeGroup.setAttribute("class", "nodeGroup");
        root.spanMap = {};

        // Turning a term into a graph structure for d3. Also constructing
        // nested divs to mirror the graph structure.
        var graph = makeGraph(exp, nodeGroup, linkGroup, root.spanMap, opts);

        d3tree.nodeSize([NODE_SIZE, NODE_SIZE]);
        d3tree.separation(function(a,b) {
            return a.parent == b.parent ? 1 : 1.5;
        });
        d3tree.nodes(graph);

        var rect = measureDivs(null, graph);
        rect.width = (rect.right - rect.left);
        rect.height = (rect.bottom - rect.top);
        // make fit within bounds. TODO: this is not exactly right
        var largerDim = (rect.width > rect.height) ? "width" : "height";
        var scale = opts.size / rect[largerDim];
        nodeGroup.style["font-size"] = 0.5 * NODE_SIZE * scale + UNIT;
        nodeGroup.style.width = linkGroup.style.width = scale * rect.width + UNIT;
        nodeGroup.style.height = linkGroup.style.height = scale * rect.height + UNIT;
        nodeGroup.style.left = nodeGroup.style.top = 0;
        var origins = [rect];
        positionDivs(origins, scale, graph);
        return root;
    };

    if (typeof define === "function" && define.amd) define(tm); else if (typeof module === "object" && module.exports) module.exports = tm;
    this.tm = tm;
}(d3);
<|endoftext|>


<commit_before>// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
// Source: https://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
  var timeout;
  return function() {
    var context = this, args = arguments;
    var later = function() {
      timeout = null;
      if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
};

function urlParam(name){
    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
    if (results==null){
       return null;
    }
    else{
       return results[1] || 0;
    }
}

window.searchHandler = function() {
  var searchContainer = $('.search'),
  searchInput = $('.search input'),
  searchResultsPane = $('.search--results'),
  HORIZONTAL_OFFSET = 40;

  function search(token){
    $.post('/api/quick_search', {token: token}, function(data){
      appendResults(data);
      showSearchResults();
      hover();
    });
  }

  function showSearchResults(){
    if(!searchResultsPane.is(":visible")) {
      searchResultsPane.show();
      $('body').append(searchResultsPane.detach());
      var eOffset = searchContainer.offset();
      // make sure to place it where it would normally go (this could be improved)
      searchResultsPane.css({
          'display': 'block',
              'top': eOffset.top + searchContainer.outerHeight() + 10,
              'left': eOffset.left - HORIZONTAL_OFFSET,
              'width': searchContainer.width() + 2 * HORIZONTAL_OFFSET
      });
    }
  }

  function appendResults(results) {
    var object = '';
    if(results.packages.length === 0 && results.topics.length === 0){
      return searchResultsPane.html('
No results found. Press [enter] for full-text search.
')
    }
    if(results.packages.length > 0){
      object += '
Packages
';
      object += '
';
      results.packages.forEach(function(package){
        object += "
" + package.name + "
";
      });
      object += '
';
    }

    if(results.topics.length > 0){
      object += '
Topics
';
      object += '
';
      results.topics.forEach(function(topic){
        object += "
" + topic.name + " ("+ topic.package_name + " - " + topic.package_version + ") " + "
";
      });
      object += '
';
    }
    searchResultsPane.html(object);

  }

  function hover(){
    //var clas = document.getElementsByClassName("search--results");
    var elements = $(".search--results li");
    elements.each(function(i){
      $(this).mouseenter(function(){
        sethovering($(this).find("a"));
      });
      $(this).mouseleave(function(){
        unsethovering($(this).find("a"));
      });
    });
  }
  function sethovering(element){
    if($("li a.hover").length>0){
      $("li a.hover").eq(0).removeClass("hover");
    }
    element.addClass("hover");
  }
  function unsethovering(element){
    element.removeClass("hover");
  }

  searchInput.on("keyup",debounce(function(e){
    if(e.keyCode !== 40&&e.keyCode!==38){
    search(searchInput.val());
  }
  }, 100));

  $(document).click(function(event) {
    if(!$(event.target).closest(searchContainer).length &&
       !$(event.target).is(searchContainer)) {
        if(searchResultsPane.is(":visible")) {
          searchResultsPane.hide();
        }
    }
  });

  searchInput.bind('keydown', function(e) {
    // DOWN array (only works with keydown and not keypress)
    if(e.keyCode==13 && $("li a.hover").length>0){
      e.preventDefault();
      document.location =  $("li a.hover").attr('href');
    }else if(e.keyCode == 40){
      if($("li a.hover").length>0){
        var next =($("li a.hover").parent().next("li"));
        if(next.length > 0){
          sethovering(next.find("a"));
        }
        else if($("li a.hover").parent().parent().hasClass("packages")){
          if($(".search--results .topics li").length > 0){
          sethovering($(".search--results .topics li").eq(0).find("a"));
          }
        }
      }else{
        sethovering($(".search--results li").eq(0).find("a"));
      }
    }else if(e.keyCode == 38){
      if($("li a.hover").length>0){
        e.preventDefault();
        var next =($("li a.hover").parent().prev("li"));
        if(next.length > 0){
          sethovering(next.find("a"));
        }
        else if($("li a.hover").parent().parent().hasClass("topics")){
          if($(".search--results .packages li").length>0){
          sethovering($(".search--results .packages li").last().find("a"));
          }
        }
      }else{
        sethovering($(".search--results li").last().find("a"));
      }
    }
  });
};

$(document).ready(window.searchHandler);
<commit_msg>code clean-up<commit_after>// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
// Source: https://davidwalsh.name/javascript-debounce-function
function debounce(func, wait, immediate) {
  var timeout;
  return function() {
    var context = this, args = arguments;
    var later = function() {
      timeout = null;
      if (!immediate) func.apply(context, args);
    };
    var callNow = immediate && !timeout;
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
    if (callNow) func.apply(context, args);
  };
};

function urlParam(name){
    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
    if (results==null){
       return null;
    }
    else{
       return results[1] || 0;
    }
}

window.searchHandler = function() {
  var searchContainer = $('.search'),
  searchInput = $('.search input'),
  searchResultsPane = $('.search--results'),
  HORIZONTAL_OFFSET = 40;

  function search(token){
    $.post('/api/quick_search', {token: token}, function(data){
      appendResults(data);
      showSearchResults();
      hover();
    });
  }

  function showSearchResults(){
    if(!searchResultsPane.is(":visible")) {
      searchResultsPane.show();
      $('body').append(searchResultsPane.detach());
      var eOffset = searchContainer.offset();
      // make sure to place it where it would normally go (this could be improved)
      searchResultsPane.css({
          'display': 'block',
              'top': eOffset.top + searchContainer.outerHeight() + 10,
              'left': eOffset.left - HORIZONTAL_OFFSET,
              'width': searchContainer.width() + 2 * HORIZONTAL_OFFSET
      });
    }
  }

  function appendResults(results) {
    var object = '';
    if(results.packages.length === 0 && results.topics.length === 0){
      return searchResultsPane.html('
No results found. Press [enter] for full-text search.
')
    }
    if(results.packages.length > 0){
      object += '
Packages
';
      object += '
';
      results.packages.forEach(function(package){
        object += "
" + package.name + "
";
      });
      object += '
';
    }

    if(results.topics.length > 0){
      object += '
Topics
';
      object += '
';
      results.topics.forEach(function(topic){
        object += "
" + topic.name + " ("+ topic.package_name + " - " + topic.package_version + ") " + "
";
      });
      object += '
';
    }
    searchResultsPane.html(object);

  }

  function hover(){
    var elements = $(".search--results li");
    elements.each(function(i){
      $(this).mouseenter(function(){
        sethovering($(this).find("a"));
      });
      $(this).mouseleave(function(){
        unsethovering($(this).find("a"));
      });
    });
  }
  function sethovering(element){
    if($("li a.hover").length>0){
      $("li a.hover").eq(0).removeClass("hover");
    }
    element.addClass("hover");
  }
  function unsethovering(element){
    element.removeClass("hover");
  }

  searchInput.on("keyup",debounce(function(e){
    if(e.keyCode !== 40&&e.keyCode!==38){
    search(searchInput.val());
  }
  }, 100));

  $(document).click(function(event) {
    if(!$(event.target).closest(searchContainer).length &&
       !$(event.target).is(searchContainer)) {
        if(searchResultsPane.is(":visible")) {
          searchResultsPane.hide();
        }
    }
  });

  searchInput.bind('keydown', function(e) {
    // DOWN array (only works with keydown and not keypress)
    if(e.keyCode==13 && $("li a.hover").length>0){
      e.preventDefault();
      document.location =  $("li a.hover").attr('href');
    }else if(e.keyCode == 40){
      if($("li a.hover").length>0){
        var next =($("li a.hover").parent().next("li"));
        if(next.length > 0){
          sethovering(next.find("a"));
        }
        else if($("li a.hover").parent().parent().hasClass("packages")){
          if($(".search--results .topics li").length > 0){
          sethovering($(".search--results .topics li").eq(0).find("a"));
          }
        }
      }else{
        sethovering($(".search--results li").eq(0).find("a"));
      }
    }else if(e.keyCode == 38){
      if($("li a.hover").length>0){
        e.preventDefault();
        var next =($("li a.hover").parent().prev("li"));
        if(next.length > 0){
          sethovering(next.find("a"));
        }
        else if($("li a.hover").parent().parent().hasClass("topics")){
          if($(".search--results .packages li").length>0){
          sethovering($(".search--results .packages li").last().find("a"));
          }
        }
      }else{
        sethovering($(".search--results li").last().find("a"));
      }
    }
  });
};

$(document).ready(window.searchHandler);
<|endoftext|>


<commit_before>require('colors');
var cli = require('optimist'),
    p = require('path'),
    u = require('util'),
    fs = require('fs'),
    exec = require('child_process').exec;

var available = require(p.join(process.mainModule.paths[0],'..','..','package.json')).ngn;

// Basic CLI
var argv = cli
		  .usage('Usage: ngn support <module or group>')
		  .argv,
		mod = argv._.filter(function(value){return value !== 'add'})[0];

// Global Package Installer
var install = function(ngnpkg){
  exec('npm install -g '+ngnpkg+' --json --loglevel=silent',function(err,stdout,stderr){
    try {
      // Handle edge case when gyp output screws up npm json format
      if (stdout.substr(0,1) !== '['){
        stdout = stdout.substr(stdout.indexOf('['),stdout.length);
      }
      var out = JSON.parse(stdout)[0];
      console.log((out.name.toString()+' v'+out.version+' support added.').green.bold);
    } catch (e) {
      console.log('Module installed, but may have errors:'.yellow.bold);
      console.log(e.message.toString().yellow);
    }
  });
};

// Check first for a module or group and install/warn accordingly
if (available.modules[mod] !== undefined){
  exec('npm install -g '+mod);
} else if (available.groups[mod] !== undefined){
  var cmd = [];
  console.log(('Adding '+available.groups[mod].length+' package'+(available.groups[mod].length==1?'':'s')+'...').cyan.bold);
  for (var i=0;i<available.groups[mod].length;i++){
    install(available.groups[mod][i]);
  };
} else if (['all','*'].indexOf(mod.toString().trim().toLowerCase()) >= 0){
  console.log('Installing every add-on...'.cyan.bold);
  for (var ngnpkg in available.modules){
    install(ngnpkg);
  };
} else {
  throw 'No module or group called \"'+mod+'\" is available.';
}
<commit_msg>Time warning<commit_after>require('colors');
var cli = require('optimist'),
    p = require('path'),
    u = require('util'),
    fs = require('fs'),
    exec = require('child_process').exec;

var available = require(p.join(process.mainModule.paths[0],'..','..','package.json')).ngn;

// Basic CLI
var argv = cli
		  .usage('Usage: ngn support <module or group>')
		  .argv,
		mod = argv._.filter(function(value){return value !== 'add'})[0];

// Global Package Installer
var install = function(ngnpkg){
  exec('npm install -g '+ngnpkg+' --json --loglevel=silent',function(err,stdout,stderr){
    try {
      // Handle edge case when gyp output screws up npm json format
      if (stdout.substr(0,1) !== '['){
        stdout = stdout.substr(stdout.indexOf('['),stdout.length);
      }
      var out = JSON.parse(stdout)[0];
      console.log((out.name.toString()+' v'+out.version+' support added.').green.bold);
    } catch (e) {
      console.log('Module installed, but may have errors:'.yellow.bold);
      console.log(e.message.toString().yellow);
    }
  });
};

// Check first for a module or group and install/warn accordingly
if (available.modules[mod] !== undefined){
  exec('npm install -g '+mod);
} else if (available.groups[mod] !== undefined){
  var cmd = [];
  console.log(('Adding '+available.groups[mod].length+' package'+(available.groups[mod].length==1?'':'s')+'...').cyan.bold);
  for (var i=0;i<available.groups[mod].length;i++){
    install(available.groups[mod][i]);
  };
} else if (['all','*'].indexOf(mod.toString().trim().toLowerCase()) >= 0){
  console.log('Installing every add-on...'.cyan.bold+' (this may take a few moments)');
  for (var ngnpkg in available.modules){
    install(ngnpkg);
  };
} else {
  throw 'No module or group called \"'+mod+'\" is available.';
}
<|endoftext|>


<commit_before>(function(W, E) {
	'use strict';

	var events = [
			['swipeLeft', 'X', 1],
			['swipeRight', 'X', -1],
			['swipeUp', 'Y', 1],
			['swipeDown', 'Y', -1]
		],
		down = 'mousedown',
		up = 'mouseup';

	/**
	 * Loop through custom events and bind applicable standard events
	 */
	events.forEach(function(event) {
		E.addEvent(event[0], function(el, fn, conf) {
			var scope = this;
			conf = W.$extend({
				distance: 50
			}, conf);

			E.on(el, down + '.' + event[0], function(e) {
				scope.start = e['client' + event[1]];
			}, conf);

			E.on(el, up + '.' + event[0], function(e, el) {
				var travel = (scope.start - e['client' + event[1]]) * event[2];

				if (travel > conf.distance) {
					fn.apply(conf.scope || el, W._slice.call(arguments));
				}
			}, conf);
		}, function(el, fn) {
			E.off(el, down + '.' + event[0] + ' ' + up + '.' + event[0], fn);
		});
	});
})(Wee, Wee.events);<commit_msg>Optimize distance variable configuration<commit_after>(function(W, E) {
	'use strict';

	var events = [
			['swipeLeft', 'X', 1],
			['swipeRight', 'X', -1],
			['swipeUp', 'Y', 1],
			['swipeDown', 'Y', -1]
		],
		down = 'mousedown',
		up = 'mouseup';

	/**
	 * Loop through custom events and bind applicable standard events
	 */
	events.forEach(function(event) {
		E.addEvent(event[0], function(el, fn, conf) {
			var scope = this,
				distance = conf.distance || 50;

			E.on(el, down + '.' + event[0], function(e) {
				scope.start = e['client' + event[1]];
			}, conf);

			E.on(el, up + '.' + event[0], function(e, el) {
				var travel = (scope.start - e['client' + event[1]]) * event[2];

				if (travel > distance) {
					fn.apply(conf.scope || el, W._slice.call(arguments));
				}
			}, conf);
		}, function(el, fn) {
			E.off(el, down + '.' + event[0] + ' ' + up + '.' + event[0], fn);
		});
	});
})(Wee, Wee.events);<|endoftext|>


<commit_before>var https = require('https');
var http  = require('http');
var util  = require('util');
var path  = require('path');
var fs    = require('fs');
var url   = require('url');
var httpProxy = require('http-proxy');

var proxy = httpProxy.createProxyServer({});

proxy.on('proxyReq', function (proxyReq, req, res) {
});

proxy.on('proxyRes', function (proxyRes, req, res) {
});

var server = http.createServer(function(req, res) {
  var target = req.headers['x-target'];
  var proxyURL = url.parse(target);
  var host = proxyURL.host;
  var protocol = proxyURL.protocol;
  var agent = (protocol == 'https:' ? https.globalAgent:http.globalAgent);
  req.url = '';

  proxy.web(req, res, {
    target: target,
    agent: agent,
    headers: {
        host: host
    }
  });
});

server.listen(80);
<commit_msg>added SSL support<commit_after>var https = require('https');
var http  = require('http');
var util  = require('util');
var path  = require('path');
var fs    = require('fs');
var url   = require('url');
var httpProxy = require('http-proxy');

var httpsOpts = {
  key: fs.readFileSync('/path/to/key.pem', 'utf8'),
  cert: fs.readFileSync('/path/to/cert.pem', 'utf8')
};

var proxy = httpProxy.createProxyServer({});

proxy.on('proxyReq', function (proxyReq, req, res) {
});

proxy.on('proxyRes', function (proxyRes, req, res) {
});

var server = http.createServer(function(req, res) {
  var target = req.headers['x-target'];
  var proxyURL = url.parse(target);
  var host = proxyURL.host;
  var protocol = proxyURL.protocol;
  var agent = (protocol == 'https:' ? https.globalAgent:http.globalAgent);
  req.url = '';

  proxy.web(req, res, {
    ssl: httpsOpts,
    target: target,
    agent: agent,
    headers: {
        host: host
    }
  });
});

server.listen(443);
<|endoftext|>


<commit_before>/**
 * A simple JSON search
 * Requires jQuery (v 1.7+)
 *
 * @author  Mat Hayward - Erskine Design
 * @version  0.1
 */

 /* ==========================================================================
    Initialisation
    ========================================================================== */

var q, jsonFeedUrl = "feed.json",
    $searchForm = $("[data-search-form]"),
    $searchInput = $("[data-search-input]"),
    $resultTemplate = $("#search-result"),
    $resultsPlaceholder = $("[data-search-results]"),
    $foundContainer = $("[data-search-found]"),
    $foundTerm = $("[data-search-found-term]"),
    $foundCount = $("[data-search-found-count]"),
    allowEmpty = true,
    showLoader = true,
    loadingClass = "is--loading";

$(document).ready( function() {
    // hide items found string
    $foundContainer.hide();

    // initiate search functionality
    initSearch();
});

 /* ==========================================================================
    Search functions
    ========================================================================== */

/**
 * Initiate search functionality.
 * Shows results based on querystring if present.
 * Binds search function to form submission.
 */
function initSearch() {

    // Get search results if q parameter is set in querystring
    if (getParameterByName('q')) {
        q = decodeURIComponent(getParameterByName('q'));
        $searchInput.val(q);
        execSearch(q);
    }

    // Get search results on submission of form
    $(document).on("submit", $searchForm, function(e) {
        e.preventDefault();
        q = $searchInput.val();
        execSearch(q);
        $('#template').hide();
    });
}

/**
 * Executes search
 * @param {String} q
 * @return null
 */
function execSearch(q) {
    if (q != '' || allowEmpty) {
        if (showLoader) {
            toggleLoadingClass();
        }

        getSearchResults(processData());
    }
}

/**
 * Toggles loading class on results and found string
 * @return null
 */
function toggleLoadingClass() {
    $resultsPlaceholder.toggleClass(loadingClass);
    $foundContainer.toggleClass(loadingClass);
}

/**
 * Get Search results from JSON
 * @param {Function} callbackFunction
 * @return null
 */
function getSearchResults(callbackFunction) {
    $.get(jsonFeedUrl, callbackFunction, 'json');
}

/**
 * Process search result data
 * @return null
 */
function processData() {
    $results = [];

    return function(data) {

        var resultsCount = 0,
            results = "";

        $.each(data, function(index, item) {
            // check if search term is in content or title
            if (item.seach_omit != "true" && (item.content.toLowerCase().indexOf(q.toLowerCase()) > -1 || item.title.toLowerCase().indexOf(q.toLowerCase()) > -1)) {
                var result = populateResultContent($resultTemplate.html(), item);
                resultsCount++;
                results += result;
            }
        });

        if (showLoader) {
            toggleLoadingClass();
        }

        populateResultsString(resultsCount);
        showSearchResults(results);
    }
}

/**
 * Add search results to placeholder
 * @param {String} results
 * @return null
 */
function showSearchResults(results) {
    // Add results HTML to placeholder
    $resultsPlaceholder.html(results);
}

/**
 * Add results content to item template
 * @param {String} html
 * @param {object} item
 * @return {String} Populated HTML
 */
function populateResultContent(html, item) {
    html = injectContent(html, item.title, '##Title##');
    html = injectContent(html, item.link, '##Url##');
    // html = injectContent(html, item.excerpt, '##Excerpt##');
    html = injectContent(html, item.date, '##Date##');
    html = injectContent(html, item.author, '##Author##');
    html = injectContent(html, item.category, '##Category##');
    html = injectContent(html, item.blurb, '##Blurb##');
    html = injectContent(html, item.tag, '##Tags##');
    console.log(item.tag);
    return html;
}

/**
 * Populates results string
 * @param {String} count
 * @return null
 */
function populateResultsString(count) {
    $foundTerm.text(q);
    $foundCount.text(count);
    $foundContainer.show();
}

 /* ==========================================================================
    Helper functions
    ========================================================================== */

/**
 * Gets query string parameter - taken from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
 * @param {String} name
 * @return {String} parameter value
 */
function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

/**
 * Injects content into template using placeholder
 * @param {String} originalContent
 * @param {String} injection
 * @param {String} placeholder
 * @return {String} injected content
 */
function injectContent(originalContent, injection, placeholder) {
    var regex = new RegExp(placeholder, 'g');
    return originalContent.replace(regex, injection);
}<commit_msg>fixed search bug for newer posts<commit_after>---
---
/**
 * A simple JSON search
 * Requires jQuery (v 1.7+)
 *
 * @author  Mat Hayward - Erskine Design
 * @version  0.1
 */

 /* ==========================================================================
    Initialisation
    ========================================================================== */

var q, jsonFeedUrl = "{{ "/feed.json" | prepend: site.baseurl }}",
    $searchForm = $("[data-search-form]"),
    $searchInput = $("[data-search-input]"),
    $resultTemplate = $("#search-result"),
    $resultsPlaceholder = $("[data-search-results]"),
    $foundContainer = $("[data-search-found]"),
    $foundTerm = $("[data-search-found-term]"),
    $foundCount = $("[data-search-found-count]"),
    allowEmpty = true,
    showLoader = true,
    loadingClass = "is--loading";

$(document).ready( function() {
    // hide items found string
    $foundContainer.hide();

    // initiate search functionality
    initSearch();
});

 /* ==========================================================================
    Search functions
    ========================================================================== */

/**
 * Initiate search functionality.
 * Shows results based on querystring if present.
 * Binds search function to form submission.
 */
function initSearch() {

    // Get search results if q parameter is set in querystring
    if (getParameterByName('q')) {
        q = decodeURIComponent(getParameterByName('q'));
        $searchInput.val(q);
        execSearch(q);
    }

    // Get search results on submission of form
    $(document).on("submit", $searchForm, function(e) {
        e.preventDefault();
        q = $searchInput.val();
        execSearch(q);
        $('#template').hide();
    });
}

/**
 * Executes search
 * @param {String} q
 * @return null
 */
function execSearch(q) {
    if (q != '' || allowEmpty) {
        if (showLoader) {
            toggleLoadingClass();
        }

        getSearchResults(processData());
    }
}

/**
 * Toggles loading class on results and found string
 * @return null
 */
function toggleLoadingClass() {
    $resultsPlaceholder.toggleClass(loadingClass);
    $foundContainer.toggleClass(loadingClass);
}

/**
 * Get Search results from JSON
 * @param {Function} callbackFunction
 * @return null
 */
function getSearchResults(callbackFunction) {
    $.get(jsonFeedUrl, callbackFunction, 'json');
}

/**
 * Process search result data
 * @return null
 */
function processData() {
    $results = [];

    return function(data) {

        var resultsCount = 0,
            results = "";

        $.each(data, function(index, item) {
            // check if search term is in content or title
            if (item.seach_omit != "true" && (item.content.toLowerCase().indexOf(q.toLowerCase()) > -1 || item.title.toLowerCase().indexOf(q.toLowerCase()) > -1)) {
                var result = populateResultContent($resultTemplate.html(), item);
                resultsCount++;
                results += result;
            }
        });

        if (showLoader) {
            toggleLoadingClass();
        }

        populateResultsString(resultsCount);
        showSearchResults(results);
    }
}

/**
 * Add search results to placeholder
 * @param {String} results
 * @return null
 */
function showSearchResults(results) {
    // Add results HTML to placeholder
    $resultsPlaceholder.html(results);
}

/**
 * Add results content to item template
 * @param {String} html
 * @param {object} item
 * @return {String} Populated HTML
 */
function populateResultContent(html, item) {
    html = injectContent(html, item.title, '##Title##');
    html = injectContent(html, item.link, '##Url##');
    // html = injectContent(html, item.excerpt, '##Excerpt##');
    html = injectContent(html, item.date, '##Date##');
    html = injectContent(html, item.author, '##Author##');
    html = injectContent(html, item.category, '##Category##');
    html = injectContent(html, item.blurb, '##Blurb##');
    html = injectContent(html, item.tag, '##Tags##');
    console.log(item.tag);
    return html;
}

/**
 * Populates results string
 * @param {String} count
 * @return null
 */
function populateResultsString(count) {
    $foundTerm.text(q);
    $foundCount.text(count);
    $foundContainer.show();
}

 /* ==========================================================================
    Helper functions
    ========================================================================== */

/**
 * Gets query string parameter - taken from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
 * @param {String} name
 * @return {String} parameter value
 */
function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

/**
 * Injects content into template using placeholder
 * @param {String} originalContent
 * @param {String} injection
 * @param {String} placeholder
 * @return {String} injected content
 */
function injectContent(originalContent, injection, placeholder) {
    var regex = new RegExp(placeholder, 'g');
    return originalContent.replace(regex, injection);
}<|endoftext|>


<commit_before>import {cE, aC, aE, tN} from './DOM';
import Music from './Music';
import i18n from 'i18n';

/* Global input state */
export const State = {
	mouseX: 0,
	mouseY: 0,
	movementX: 0,
	movementY: 0,
	primary: 0,
	secondary: 0,
	forward: 0,
	backward: 0,
	left: 0,
	right: 0,
	run: 0,
	flight: 0,
	fastTime: 0,
	screenshot: 0
};
const resetState = () => {
	const persist = [
		'run',
		'flight',
		'fastTime'
	];
	for(let id in State) !(~persist.indexOf(id)) && (State[id] = 0);
};
aE(window, 'blur', resetState);

/* Capture status  */
let isEnabled = true;
export const Capture = ({enabled, lock}) => {
	!(isEnabled = enabled) && resetState();
	if(lock === undefined) return;
	autoLock = lock;
	if(lock) !isLocked && requestPointerLock();
	else isLocked && exitPointerLock();
};

/* Pointer lock stuff */
let isLocked = false;
let autoLock = true;
const pointerLockChange = () => {
	isLocked = !!(document.pointerLockElement			||
								document.mozPointerLockElement	||
								document.webkitPointerLockElement);
	settings.style.display = isLocked ? 'none' : '';
};

aE(document, 'pointerlockchange', pointerLockChange);
aE(document, 'mozpointerlockchange', pointerLockChange);
aE(document, 'webkitpointerlockchange', pointerLockChange);

const requestPointerLock = () => {
	if(document.documentElement.mozRequestPointerLock) document.documentElement.mozRequestPointerLock();
	else if(document.documentElement.webkitRequestPointerLock) document.documentElement.webkitRequestPointerLock();
	else document.documentElement.requestPointerLock();
};

const exitPointerLock = () => {
	if(document.mozExitPointerLock) document.mozExitPointerLock();
	else if(document.webkitExitPointerLock) document.webkitExitPointerLock();
	else document.exitPointerLock();
};

/* Fullscreen stuff */
let isFullScreen = false;
let autoFullscreen = localStorage.fullscreen;
const fullScreenChange = () => {
	isFullScreen = !!(document.fullscreenElement			||
										document.mozFullScreenElement		||
										document.webkitFullscreenElement);
	isFullScreen && autoLock && !isLocked && requestPointerLock();
};

aE(document, 'fullscreenchange', fullScreenChange);
aE(document, 'mozfullscreenchange', fullScreenChange);
aE(document, 'webkitfullscreenchange', fullScreenChange);

const requestFullScreen = () => {
	if(document.documentElement.mozRequestFullScreen) document.documentElement.mozRequestFullScreen();
	else if(document.documentElement.webkitRequestFullscreen) document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
	else document.documentElement.requestFullscreen();
};

const cancelFullScreen = () => {
	if(document.mozCancelFullScreen) document.mozCancelFullScreen();
	else if(document.webkitCancelFullScreen) document.webkitCancelFullScreen();
	else document.cancelFullScreen();
};

/* Mouse events handling */
const onButton = (e) => {
	if(e.target === settings || e.target.parentNode === settings || e.target.parentNode.parentNode === settings) return;
	if(autoFullscreen && !isFullScreen) return requestFullScreen();
	if(autoLock && !isLocked) return requestPointerLock();
	if(!isEnabled) return;
	const state = e.type === 'mousedown';
	switch(e.button) {
		case 0:
			State.primary = state;
		break;
		case 2:
			State.secondary = state;
		break;
	}
};

aE(window, 'mousedown', onButton);
aE(window, 'mouseup', onButton);
aE(window, 'contextmenu', (e) => e.preventDefault());

aE(window, 'mousemove', (e) => {
	State.mouseX = e.clientX / window.innerWidth * 2 - 1;
	State.mouseY = (window.innerHeight - e.clientY) / window.innerHeight * 2 - 1;
	State.movementX = !isEnabled || !isLocked ? 0 : (e.movementX || e.mozMovementX || e.webkitMovementX || 0);
	State.movementY = !isEnabled || !isLocked ? 0 : (e.movementY || e.mozMovementY || e.webkitMovementY || 0);
});

/* Electron */
const electron = window.process && window.process.type;

/* Keyboard events handling */
const onKey = (e) => {
	if(!isEnabled || e.repeat) return;
	const state = e.type === 'keydown';
	switch(e.keyCode) {
		case 87:
			State.forward = state;
		break;
		case 83:
			State.backward = state;
		break;
		case 65:
			State.left = state;
		break;
		case 68:
			State.right = state;
		break;
		case 16:
			e.preventDefault();
			State.run = state;
		break;
		case 32:
			e.preventDefault();
			if(state) {
				State.flight = !State.flight;
			}
		break;
		case 79:
			if(state) {
				State.fastTime = !State.fastTime;
			}
		break;
		case 80:
			State.screenshot = state ? {} : false;
		break;
	}
};

aE(window, 'keydown', onKey);
aE(window, 'keyup', onKey);

/* Settings Menu */
const settings = cE('settings');
['postprocessing', 'antialias', 'highDPI', 'debug', 'mute', 'fullscreen'].forEach((setting) => {
	if(setting === 'highDPI' && (window.devicePixelRatio || 1) < 2) return;
	if(setting === 'fullscreen' && electron) return;
	const input = cE('input', {
		type: 'checkbox',
		checked: localStorage.getItem(setting),
		onchange: () => {
			if(input.checked) localStorage.setItem(setting, 1);
			else localStorage.removeItem(setting);
			if(setting === 'mute') {
				Music.toggle();
			} else if(setting === 'fullscreen') {
				(autoFullscreen = input.checked) && !isFullScreen && requestFullScreen();
			} else window.location.reload();
		}
	})
	const label = cE('label', null, input);
	aC(label, tN(i18n[setting]));
	aC(settings, label);
});
aC(document.body, settings);
<commit_msg>no need to persist that<commit_after>import {cE, aC, aE, tN} from './DOM';
import Music from './Music';
import i18n from 'i18n';

/* Global input state */
export const State = {
	mouseX: 0,
	mouseY: 0,
	movementX: 0,
	movementY: 0,
	primary: 0,
	secondary: 0,
	forward: 0,
	backward: 0,
	left: 0,
	right: 0,
	run: 0,
	flight: 0,
	fastTime: 0,
	screenshot: 0
};
const resetState = () => {
	const persist = [
		'flight',
		'fastTime'
	];
	for(let id in State) !(~persist.indexOf(id)) && (State[id] = 0);
};
aE(window, 'blur', resetState);

/* Capture status  */
let isEnabled = true;
export const Capture = ({enabled, lock}) => {
	!(isEnabled = enabled) && resetState();
	if(lock === undefined) return;
	autoLock = lock;
	if(lock) !isLocked && requestPointerLock();
	else isLocked && exitPointerLock();
};

/* Pointer lock stuff */
let isLocked = false;
let autoLock = true;
const pointerLockChange = () => {
	isLocked = !!(document.pointerLockElement			||
								document.mozPointerLockElement	||
								document.webkitPointerLockElement);
	settings.style.display = isLocked ? 'none' : '';
};

aE(document, 'pointerlockchange', pointerLockChange);
aE(document, 'mozpointerlockchange', pointerLockChange);
aE(document, 'webkitpointerlockchange', pointerLockChange);

const requestPointerLock = () => {
	if(document.documentElement.mozRequestPointerLock) document.documentElement.mozRequestPointerLock();
	else if(document.documentElement.webkitRequestPointerLock) document.documentElement.webkitRequestPointerLock();
	else document.documentElement.requestPointerLock();
};

const exitPointerLock = () => {
	if(document.mozExitPointerLock) document.mozExitPointerLock();
	else if(document.webkitExitPointerLock) document.webkitExitPointerLock();
	else document.exitPointerLock();
};

/* Fullscreen stuff */
let isFullScreen = false;
let autoFullscreen = localStorage.fullscreen;
const fullScreenChange = () => {
	isFullScreen = !!(document.fullscreenElement			||
										document.mozFullScreenElement		||
										document.webkitFullscreenElement);
	isFullScreen && autoLock && !isLocked && requestPointerLock();
};

aE(document, 'fullscreenchange', fullScreenChange);
aE(document, 'mozfullscreenchange', fullScreenChange);
aE(document, 'webkitfullscreenchange', fullScreenChange);

const requestFullScreen = () => {
	if(document.documentElement.mozRequestFullScreen) document.documentElement.mozRequestFullScreen();
	else if(document.documentElement.webkitRequestFullscreen) document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
	else document.documentElement.requestFullscreen();
};

const cancelFullScreen = () => {
	if(document.mozCancelFullScreen) document.mozCancelFullScreen();
	else if(document.webkitCancelFullScreen) document.webkitCancelFullScreen();
	else document.cancelFullScreen();
};

/* Mouse events handling */
const onButton = (e) => {
	if(e.target === settings || e.target.parentNode === settings || e.target.parentNode.parentNode === settings) return;
	if(autoFullscreen && !isFullScreen) return requestFullScreen();
	if(autoLock && !isLocked) return requestPointerLock();
	if(!isEnabled) return;
	const state = e.type === 'mousedown';
	switch(e.button) {
		case 0:
			State.primary = state;
		break;
		case 2:
			State.secondary = state;
		break;
	}
};

aE(window, 'mousedown', onButton);
aE(window, 'mouseup', onButton);
aE(window, 'contextmenu', (e) => e.preventDefault());

aE(window, 'mousemove', (e) => {
	State.mouseX = e.clientX / window.innerWidth * 2 - 1;
	State.mouseY = (window.innerHeight - e.clientY) / window.innerHeight * 2 - 1;
	State.movementX = !isEnabled || !isLocked ? 0 : (e.movementX || e.mozMovementX || e.webkitMovementX || 0);
	State.movementY = !isEnabled || !isLocked ? 0 : (e.movementY || e.mozMovementY || e.webkitMovementY || 0);
});

/* Electron */
const electron = window.process && window.process.type;

/* Keyboard events handling */
const onKey = (e) => {
	if(!isEnabled || e.repeat) return;
	const state = e.type === 'keydown';
	switch(e.keyCode) {
		case 87:
			State.forward = state;
		break;
		case 83:
			State.backward = state;
		break;
		case 65:
			State.left = state;
		break;
		case 68:
			State.right = state;
		break;
		case 16:
			e.preventDefault();
			State.run = state;
		break;
		case 32:
			e.preventDefault();
			if(state) {
				State.flight = !State.flight;
			}
		break;
		case 79:
			if(state) {
				State.fastTime = !State.fastTime;
			}
		break;
		case 80:
			State.screenshot = state ? {} : false;
		break;
	}
};

aE(window, 'keydown', onKey);
aE(window, 'keyup', onKey);

/* Settings Menu */
const settings = cE('settings');
['postprocessing', 'antialias', 'highDPI', 'debug', 'mute', 'fullscreen'].forEach((setting) => {
	if(setting === 'highDPI' && (window.devicePixelRatio || 1) < 2) return;
	if(setting === 'fullscreen' && electron) return;
	const input = cE('input', {
		type: 'checkbox',
		checked: localStorage.getItem(setting),
		onchange: () => {
			if(input.checked) localStorage.setItem(setting, 1);
			else localStorage.removeItem(setting);
			if(setting === 'mute') {
				Music.toggle();
			} else if(setting === 'fullscreen') {
				(autoFullscreen = input.checked) && !isFullScreen && requestFullScreen();
			} else window.location.reload();
		}
	})
	const label = cE('label', null, input);
	aC(label, tN(i18n[setting]));
	aC(settings, label);
});
aC(document.body, settings);
<|endoftext|>