<commit_before>describe("README: Tests for browsers without localStorage support", function(){

    it("should test that localStorage is enabled in this browser, if it is, this will pass and the remaining tests will be skipped.", function() {

        var msg = [
            "This test is intended to fail to make you aware that",
            "localStorage isn't supported by your browser. While the cache",
            "wont be able to work, its safe to use as it will fail silently.",
            "It will be like always getting a cache miss. Most of the tests",
            "are skipped, but some will run to verify graceful degrading."
        ].join(" ")

        expect(locache.supportsLocalStorage).toBe(true)

    })

    // Tests specific to browsers that don't have local storage. These simply
    // test that the functionality degrades well. All cache attempts will be
    // dropped siliently and fetches should act like a cache miss.
    if(!locache.supportsLocalStorage){

        it("silent failing when localStorage isn't supported", function(){

            locache.set("my_key", "my_value")
            expect(locache.get("my_key")).toBe(null)
            locache.remove("my_key")
            locache.incr("my_key")
            locache.decr("my_key")
            locache.setMany({
                "key 1": "value 1",
                "key 2": "value 2"
            })
            expect(locache.getMany(['key 1', 'key 2'])).toEqual([null, null])
            locache.removeMany(['key 1', 'key 2'])
            locache.flush()
            locache.cleanup()

        })

        it("with no localStorage length and flush", function(){

            expect(locache.length()).toBe(0)
            locache.set("key", "value")
            expect(locache.length()).toBe(0)
            locache.flush()
            expect(locache.length()).toBe(0)

        })

        it("with no localStorage setting, getting and removing simple values", function(){

            var key = "my_key"
            var value = "my_value"

            locache.set(key, value)
            expect(locache.get(key)).toBe(null)

            locache.remove(key)
            expect(locache.get(key)).toBe(null)

        })

        it("with no localStorage setting a value with an expire time", function(){

            var key = "will_expire"
            var value = "value"

            locache.set(key, value, 1)
            expect(locache.get(key)).toBe(null)

        })

        it("with no localStorage incr and decr'ing of keys", function(){

            locache.incr("counter")
            expect(locache.get("counter")).toBe(null)
            locache.decr("counter")
            expect(locache.get("counter")).toBe(null)

        })

        it("with no localStorage test many operations - set, get, remove", function(){

            locache.setMany({
                'key1': 'val1',
                'key2': 'val2',
                'key3': 'val3'
            })

            var vals = locache.getMany(['key1','key2','key3'])
            expect(vals).toEqual([null, null, null])
            locache.removeMany(['key1','key2','key3'])
            var vals2 = locache.getMany(['key1','key2','key3'])
            expect(vals2).toEqual([null, null, null])

        })

        return

    }

})

describe("Tests for browsers with localStorage support", function(){

    if (!locache.supportsLocalStorage) return

    beforeEach(function(){
        localStorage.clear()
    })

    /*
     * Resume normal testing - for browsers that *do* support localStorage.
     */

    it("should test the length of the cache and flushing", function(){

        expect(locache.length()).toBe(0)
        locache.set("key", "value")
        expect(locache.length()).toBe(1)
        locache.flush()
        expect(locache.length()).toBe(0)

    })

    it("should test setting, getting and removing simple values", function(){

        var key = "my_key"
        var value = "my_value"

        locache.set(key, value)
        expect(locache.get(key)).toBe(value)

        locache.remove(key)
        expect(locache.get(key)).toBe(null)

        locache.set("my_number", 11)
        expect(locache.get("my_number")).toBe(11)

    })

    it("should test setting a value with an expire time", function(){

        var key = "will_expire"
        var value = "value"

        locache.set(key, value, 1)
        expect(locache.get(key)).toBe(value)

        var callCount = 0

        // Should still be there after 100 ms
        setTimeout(function(){
            expect(locache.get(key)).toBe(value)
            callCount++
        }, 100)

        // after a full second, it should have expired.
        setTimeout(function(){
            expect(locache.get(key)).toBe(null)
            callCount++
        }, 1000)

        waitsFor(function(){
            return callCount == 2
        })

    })

    it("should test incr and decr'ing of keys", function(){

        locache.incr("counter")
        expect(locache.get("counter")).toBe(1)
        locache.incr("counter")
        expect(locache.get("counter")).toBe(2)
        locache.decr("counter")
        expect(locache.get("counter")).toBe(1)
        locache.decr("counter")
        expect(locache.get("counter")).toBe(0)
        locache.decr("counter")
        expect(locache.get("counter")).toBe(-1)

        locache.decr("negativecounter")
        expect(locache.get("negativecounter")).toBe(-1)

        locache.set("my_number", 11)
        locache.incr("my_number")
        expect(locache.get("my_number")).toBe(12)

    })

    it("should test test many operations - set, get, remove", function(){

        locache.setMany({
            'key1': 'val1',
            'key2': 'val2',
            'key3': 'val3'
        })

        var vals = locache.getMany(['key1','key2','key3'])
        expect(vals).toEqual(['val1', 'val2', 'val3'])

        expect(locache.get('key1')).toBe('val1')

        locache.removeMany(['key1','key2','key3'])
        var vals2 = locache.getMany(['key1','key2','key3'])
        expect(vals2).toEqual([null, null, null])

    })

    it("should test storing objects", function(){

        locache.set('user', {
            'name': "Dougal Matthews",
            'admin': true
        })

        var result = locache.get('user')

        expect(result).toEqual({
            'name': "Dougal Matthews",
            'admin': true
        })

    })

    it("should test expire calculations", function(){

        var now = new Date().getTime(),
            past = now / 10,
            future = now * 10

        var key =  "mykey",
            cacheKey = locache.cachePrefix + key,
            expireKey = locache.expirePrefix + key

        // Bypass the normal setting mechanisims by manually calling the
        // storage wrapper around localStorage.
        locache.storage.set(cacheKey, "value")

        // Now manually set the expire date as in the past.
        locache.storage.set(expireKey, past)
        expect(locache.hasExpired(key)).toBe(true)

        // set the expire to "now", plus 5 seconds to make sure its not quite
        // expired yet.
        locache.storage.set(expireKey, now + 5)
        expect(locache.hasExpired(key)).toBe(false)

        // set the expire to "now", minus a second to make sure its just
        // expired.
        locache.storage.set(expireKey, now - 1)
        expect(locache.hasExpired(key)).toBe(true)

        // Finally, test the future.
        locache.storage.set(expireKey, future)
        expect(locache.hasExpired(key)).toBe(false)

    })

    it("should test cleaning up expired values", function(){

        localStorage.clear()

        var now = new Date().getTime(),
            past = now / 10,
            future = now * 10

        var key1 =  "mykey1",
            cacheKey1 = locache.cachePrefix + key1,
            expireKey1 = locache.expirePrefix + key1,
            key2 =  "mykey2",
            cacheKey2 = locache.cachePrefix + key2,
            expireKey2 = locache.expirePrefix + key2

        // Bypass the normal setting mechanisims by manually calling the
        // storage wrapper around localStorage.
        locache.storage.set(cacheKey1, "value1")
        locache.storage.set(cacheKey2, "value2")

        // set the first value to expire on a date in the past, and then
        // second to expire in the future.
        locache.storage.set(expireKey1, past)
        locache.storage.set(expireKey2, future)

        // Both values should be stored in localStorage - by passing the
        // normal get method to avoid the checks for validation
        expect(locache.storage.get(cacheKey1)).toBe("value1")
        expect(locache.storage.get(cacheKey2)).toBe("value2")

        // Perform a cleanup.
        locache.cleanup()

        // Check the values again, the first should have been removed but the
        // second should be as originally stored.
        expect(locache.storage.get(cacheKey1)).toBe(null)
        expect(locache.storage.get(cacheKey2)).toBe("value2")

    })

})
<commit_msg>Added the jshint directive to the top of the test specs too<commit_after>/*jshint asi:true */

describe("README: Tests for browsers without localStorage support", function(){

    it("should test that localStorage is enabled in this browser, if it is, this will pass and the remaining tests will be skipped.", function() {

        var msg = [
            "This test is intended to fail to make you aware that",
            "localStorage isn't supported by your browser. While the cache",
            "wont be able to work, its safe to use as it will fail silently.",
            "It will be like always getting a cache miss. Most of the tests",
            "are skipped, but some will run to verify graceful degrading."
        ].join(" ")

        expect(locache.supportsLocalStorage).toBe(true)

    })

    // Tests specific to browsers that don't have local storage. These simply
    // test that the functionality degrades well. All cache attempts will be
    // dropped siliently and fetches should act like a cache miss.
    if(!locache.supportsLocalStorage){

        it("silent failing when localStorage isn't supported", function(){

            locache.set("my_key", "my_value")
            expect(locache.get("my_key")).toBe(null)
            locache.remove("my_key")
            locache.incr("my_key")
            locache.decr("my_key")
            locache.setMany({
                "key 1": "value 1",
                "key 2": "value 2"
            })
            expect(locache.getMany(['key 1', 'key 2'])).toEqual([null, null])
            locache.removeMany(['key 1', 'key 2'])
            locache.flush()
            locache.cleanup()

        })

        it("with no localStorage length and flush", function(){

            expect(locache.length()).toBe(0)
            locache.set("key", "value")
            expect(locache.length()).toBe(0)
            locache.flush()
            expect(locache.length()).toBe(0)

        })

        it("with no localStorage setting, getting and removing simple values", function(){

            var key = "my_key"
            var value = "my_value"

            locache.set(key, value)
            expect(locache.get(key)).toBe(null)

            locache.remove(key)
            expect(locache.get(key)).toBe(null)

        })

        it("with no localStorage setting a value with an expire time", function(){

            var key = "will_expire"
            var value = "value"

            locache.set(key, value, 1)
            expect(locache.get(key)).toBe(null)

        })

        it("with no localStorage incr and decr'ing of keys", function(){

            locache.incr("counter")
            expect(locache.get("counter")).toBe(null)
            locache.decr("counter")
            expect(locache.get("counter")).toBe(null)

        })

        it("with no localStorage test many operations - set, get, remove", function(){

            locache.setMany({
                'key1': 'val1',
                'key2': 'val2',
                'key3': 'val3'
            })

            var vals = locache.getMany(['key1','key2','key3'])
            expect(vals).toEqual([null, null, null])
            locache.removeMany(['key1','key2','key3'])
            var vals2 = locache.getMany(['key1','key2','key3'])
            expect(vals2).toEqual([null, null, null])

        })

        return

    }

})

describe("Tests for browsers with localStorage support", function(){

    if (!locache.supportsLocalStorage) return

    beforeEach(function(){
        localStorage.clear()
    })

    /*
     * Resume normal testing - for browsers that *do* support localStorage.
     */

    it("should test the length of the cache and flushing", function(){

        expect(locache.length()).toBe(0)
        locache.set("key", "value")
        expect(locache.length()).toBe(1)
        locache.flush()
        expect(locache.length()).toBe(0)

    })

    it("should test setting, getting and removing simple values", function(){

        var key = "my_key"
        var value = "my_value"

        locache.set(key, value)
        expect(locache.get(key)).toBe(value)

        locache.remove(key)
        expect(locache.get(key)).toBe(null)

        locache.set("my_number", 11)
        expect(locache.get("my_number")).toBe(11)

    })

    it("should test setting a value with an expire time", function(){

        var key = "will_expire"
        var value = "value"

        locache.set(key, value, 1)
        expect(locache.get(key)).toBe(value)

        var callCount = 0

        // Should still be there after 100 ms
        setTimeout(function(){
            expect(locache.get(key)).toBe(value)
            callCount++
        }, 100)

        // after a full second, it should have expired.
        setTimeout(function(){
            expect(locache.get(key)).toBe(null)
            callCount++
        }, 1000)

        waitsFor(function(){
            return callCount == 2
        })

    })

    it("should test incr and decr'ing of keys", function(){

        locache.incr("counter")
        expect(locache.get("counter")).toBe(1)
        locache.incr("counter")
        expect(locache.get("counter")).toBe(2)
        locache.decr("counter")
        expect(locache.get("counter")).toBe(1)
        locache.decr("counter")
        expect(locache.get("counter")).toBe(0)
        locache.decr("counter")
        expect(locache.get("counter")).toBe(-1)

        locache.decr("negativecounter")
        expect(locache.get("negativecounter")).toBe(-1)

        locache.set("my_number", 11)
        locache.incr("my_number")
        expect(locache.get("my_number")).toBe(12)

    })

    it("should test test many operations - set, get, remove", function(){

        locache.setMany({
            'key1': 'val1',
            'key2': 'val2',
            'key3': 'val3'
        })

        var vals = locache.getMany(['key1','key2','key3'])
        expect(vals).toEqual(['val1', 'val2', 'val3'])

        expect(locache.get('key1')).toBe('val1')

        locache.removeMany(['key1','key2','key3'])
        var vals2 = locache.getMany(['key1','key2','key3'])
        expect(vals2).toEqual([null, null, null])

    })

    it("should test storing objects", function(){

        locache.set('user', {
            'name': "Dougal Matthews",
            'admin': true
        })

        var result = locache.get('user')

        expect(result).toEqual({
            'name': "Dougal Matthews",
            'admin': true
        })

    })

    it("should test expire calculations", function(){

        var now = new Date().getTime(),
            past = now / 10,
            future = now * 10

        var key =  "mykey",
            cacheKey = locache.cachePrefix + key,
            expireKey = locache.expirePrefix + key

        // Bypass the normal setting mechanisims by manually calling the
        // storage wrapper around localStorage.
        locache.storage.set(cacheKey, "value")

        // Now manually set the expire date as in the past.
        locache.storage.set(expireKey, past)
        expect(locache.hasExpired(key)).toBe(true)

        // set the expire to "now", plus 5 seconds to make sure its not quite
        // expired yet.
        locache.storage.set(expireKey, now + 5)
        expect(locache.hasExpired(key)).toBe(false)

        // set the expire to "now", minus a second to make sure its just
        // expired.
        locache.storage.set(expireKey, now - 1)
        expect(locache.hasExpired(key)).toBe(true)

        // Finally, test the future.
        locache.storage.set(expireKey, future)
        expect(locache.hasExpired(key)).toBe(false)

    })

    it("should test cleaning up expired values", function(){

        localStorage.clear()

        var now = new Date().getTime(),
            past = now / 10,
            future = now * 10

        var key1 =  "mykey1",
            cacheKey1 = locache.cachePrefix + key1,
            expireKey1 = locache.expirePrefix + key1,
            key2 =  "mykey2",
            cacheKey2 = locache.cachePrefix + key2,
            expireKey2 = locache.expirePrefix + key2

        // Bypass the normal setting mechanisims by manually calling the
        // storage wrapper around localStorage.
        locache.storage.set(cacheKey1, "value1")
        locache.storage.set(cacheKey2, "value2")

        // set the first value to expire on a date in the past, and then
        // second to expire in the future.
        locache.storage.set(expireKey1, past)
        locache.storage.set(expireKey2, future)

        // Both values should be stored in localStorage - by passing the
        // normal get method to avoid the checks for validation
        expect(locache.storage.get(cacheKey1)).toBe("value1")
        expect(locache.storage.get(cacheKey2)).toBe("value2")

        // Perform a cleanup.
        locache.cleanup()

        // Check the values again, the first should have been removed but the
        // second should be as originally stored.
        expect(locache.storage.get(cacheKey1)).toBe(null)
        expect(locache.storage.get(cacheKey2)).toBe("value2")

    })

})
<|endoftext|>


<commit_before>define(['jquery', 'commonmark'], function ($, commonmark) {
	var reader = commonmark.DocParser();
	var writer = commonmark.HtmlRenderer();

	var convert = function (str) { return writer.render(reader.parse(str)); };

	$.fn.commonMarkEditor = function(options) {
		this.each(function(index, item) {
			// Preview
			var preview = $('

');

			// The editor component
			var textarea = $('<textarea class="form-control"></textarea>');
			var editor = $('

')
				.append(textarea);

			$(item).addClass('commonmark-editor')
				.append(editor)
				.append(preview);
		});
	};
});
<commit_msg>Tied preview panel to editor<commit_after>define(['jquery', 'commonmark'], function ($, commonmark) {
	var reader = commonmark.DocParser();
	var writer = commonmark.HtmlRenderer();

	var convert = function (str) { return writer.render(reader.parse(str)); };

	$.fn.commonMarkEditor = function(options) {
		this.each(function(index, item) {
			var text = '';

			// Preview
			var preview = $('

');

			// The editor component
			var onChange = function() {
				var newValue = $(this).val();

				if(newValue === text)
					return;

				text = newValue;
				preview.html(convert(text));
			};

			var textarea = $('<textarea class="form-control"></textarea>')
				.change(onChange)
				.keydown(onChange)
				.keyup(onChange);

			var editor = $('

').append(textarea);

			$(item).addClass('commonmark-editor')
				.append(editor)
				.append(preview);
		});
	};
});
<|endoftext|>


<commit_before>var games = [
    "SuperSeaSerpentSimulator",
    "RationsPlease"
];

var portfolioItemTemplate = "templates/portfolioItemTemplate.html";
var modalTemplate = "templates/modalTemplate.html";

var GeneratePortfolioItem = function(data)
{
	var portfolioItem = $("<div>").load(portfolioItemTemplate);
	if(data.hasOwnProperty("id"))
	{
		console.log("setting id");
		var modalIdHolder = portfolioItem.find(".portfolio-link");
		//modalIdHolder.href = "#" + data["id"] + "-modal";
		console.log(modalIdHolder);
	}
	else
	{
		console.log("missing id");
	}

	if(data.hasOwnProperty("portfolio-item-title"))
	{
		console.log("setting title");
		var title = portfolioItem.find(".portfolio-item-title");
		//title.innerHTML = data["portfolio-item-title"];
		console.log(title);
	}
	else
	{
		console.log("missing title");
	}

	if(data.hasOwnProperty("portfolio-item-image"))
	{
		console.log("setting image");
		var image = portfolioItem.find(".portfolio-item-image");
		//image.src = data["portfolio-item-image"];
		console.log(image);
	}
	else
	{
		console.log("missing image");
	}

	console.log(portfolioItem);
	$("#portfolio-items").append(portfolioItem);
};

var GenerateModal = function(data)
{
	var modal = $("<div>").load(modalTemplate);

	console.log(modal);
	$("#modals").append(modal);
};

var GenerateFromJson = function(data)
{
	console.log(data);
	GeneratePortfolioItem(data);
	GenerateModal(data);
};

var GenerateContent = function(game)
{
	console.log("Generating content for " + game);
	$.getJSON("data/" + game + ".json", null, GenerateFromJson);
};

$(document).ready(function(){

	for (var i = games.length - 1; i >= 0; i--) {
		var game = games[i];
		GenerateContent(game);
	}
});<commit_msg>json test 21<commit_after>var games = [
    "SuperSeaSerpentSimulator",
    "RationsPlease"
];

var portfolioItemTemplate = "templates/portfolioItemTemplate.html";
var modalTemplate = "templates/modalTemplate.html";

var versionNum = "v.21";

var GeneratePortfolioItem = function(data)
{
	var portfolioItem = $('<div id="'+ data['id'] +'-portfolio-item">').load(portfolioItemTemplate, function() {
		var item = $("#"+data['id'] +"-portfolio-item");
		if(data.hasOwnProperty("id"))
		{
			console.log("setting id");
			var modalIdHolder = item.find(".portfolio-link");
			//modalIdHolder.href = "#" + data["id"] + "-modal";
			console.log(modalIdHolder);
		}
		else
		{
			console.log("missing id");
		}

		if(data.hasOwnProperty("portfolio-item-title"))
		{
			console.log("setting title");
			var title = item.find(".portfolio-item-title");
			//title.innerHTML = data["portfolio-item-title"];
			console.log(title);
		}
		else
		{
			console.log("missing title");
		}

		if(data.hasOwnProperty("portfolio-item-image"))
		{
			console.log("setting image");
			var image = item.find(".portfolio-item-image");
			//image.src = data["portfolio-item-image"];
			console.log(image);
		}
		else
		{
			console.log("missing image");
		}

		console.log(portfolioItem);
	});

	$("#portfolio-items").append(portfolioItem);
};

var GenerateModal = function(data)
{
	var modal = $("<div>").load(modalTemplate);

	console.log(modal);
	$("#modals").append(modal);
};

var GenerateFromJson = function(data)
{
	console.log(data);
	GeneratePortfolioItem(data);
	GenerateModal(data);
};

var GenerateContent = function(game)
{
	console.log("Generating content for " + game);
	$.getJSON("data/" + game + ".json", null, GenerateFromJson);
};

$(document).ready(function(){
	console.log(versionNum);
	for (var i = games.length - 1; i >= 0; i--) {
		var game = games[i];
		GenerateContent(game);
	}
});<|endoftext|>


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

/**
 * Markdown-plus-plus is a project of markdown syntax highlighting for Notepad++, by customized UDL file (user defined language).
 * This project is open source on:
 * [Source]{@link https://github.com/Edditoria/markdown-plus-plus}.
 * Code released under the MIT license:
 * [License]{@link https://github.com/Edditoria/validid/blob/master/LICENSE.txt}.
 *
 * @file Build UML XML file(s) in current working directory.
 * @auther Edditoria
 * @license MIT
 */

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

/*
Expected usage:
```cmd
npx markdown-plus-plus [options]    # In UDL folder of Notepad++
mpp [options]                       # After install this package globally
npm run mpp -- [options]            # Develop in package directory
````
*/
/* @type {Array<string>} - Usage messages to print */
var usageMsg = [
	'Build UML XML file(s) for Notepad++ in current working directory.',
	'',
	'Usage: npx markdown-plus-plus [theme-name...] [--all] [-f | --force]',
	'',
	'Options:',
	'theme-name       Build UDL file(s) for specified theme(s)',
	'                 You can give multiple theme names',
	'                 e.g. npx markdown-plus-plus solarized zenburn deep-black',
	'-f, --force      Force to overwrite any file that already exists in directory',
	'-l, --list       List bundled themes in this package',
	'-v, --version    Print current version of this package',
	'-h, --help       Print usage',
	'',
	'Examples:',
	'npx markdown-plus-plus               Build all UDL files without overwrite',
	'npx markdown-plus-plus zenburn       Build Zenburn UDL file without overwrite',
	'npx markdown-plus-plus zenburn -f    Build Zenburn UDL file and overwrite',
	''
];

/* @type {string} - Version of this npm package. */
var version = process.env.npm_package_version;
/* @type {Array<string>} - Arguments from node. */
var args = process.argv.slice(2);
/* @type {string} - Current working directory. */
var cwd = process.cwd();
/* @type {string} - Root directory of this NPM package. */
var packagePath = path.resolve(__dirname, '../');
/* @type {string} - Path for <udl/> in this package. */
var udlPath = packagePath + '/udl';

/*
 * Parse arguments passed from user command input.
 * "-f" can only be passed at first or last argument.
 * "-l/-h/-v" can only be passed at first argument.
 * @param {Array<string>} args - Arguments from Node's `process.argv.slice(2)`.
 * @param {buildinUdls} buildinUdls - Basically the theme names and filenames in <udl/>.
 * @return {Object} - The parsed object passing to build steps.
 */
var parseArgs = function(args, buildinUdls) {
	var options = {
		themeList: [],
		fileList: [],
		shouldContinue: false,
		isForce: false
	};
	// Parse the first argument. Quit asap if user put a proper flag.
	var firstArg = args[0];
	switch (firstArg) {
		case '-l': case '--list':
			console.log(buildinUdls.themeList.join('\n'));
			return options;
		case '-v': case '--version':
			console.log(version);
			return options;
		case '-h': case '--help':
			console.log(usageMsg.join('\n'));
			return options;
	}
	// Loop all arguments. Match the theme names, otherwise the program should quits.
	var arg, indexOfTheme;
	for (var i = 0; i < args.length; i++) {
		arg = args[i];
		if (arg === '-f' || arg === '--force') {
			if (i === 0 || i === args.length - 1) {
				console.log('Warning! In force mode, any existing file(s) will be overwritten.\n');
				options.isForce = true;
			} else {
				console.log('arg[' + i + ']: You pass the \"' + arg + '\" flag in wrong position. Quit for safety.');
				return options;
			}
		} else if (buildinUdls.themeList.indexOf(arg) >= 0) {
			options.themeList.push(arg);
			indexOfTheme = buildinUdls.themeList.indexOf(arg);
			options.fileList.push(buildinUdls.fileList[indexOfTheme]);
		} else {
			console.log('arg[' + i + ']: ' + 'Unsupported option \"' + arg + '\" or combination of options.');
			return options;
		}
	}
	options.shouldContinue = true;
	return options;
};

/**
 * Get file list in a directory.
 * Similar to `dir /w` in command prompt and `ls` in bash.
 * Expect program quit with error if it does not find the directory.
 * @param {string} dir - Path of a directory.
 * @return {string[]} - An array as a list of filenames.
 */
var getFileList = function(dir) {
	return fs.readdirSync(dir);
};

/**
 * Get theme name from a UDL XML filename, assuming the filename is in proper format.
 * @param {string} filename - Expected format: 'markdown.hyphen-lowercase-theme-name.udl.xml'.
 * @return {string}
 */
var getThemeName = function(filename) {
	var reHead = /^(markdown\.)/;
	var reTail = /(\.udl\.xml)$/;
	return filename.replace(reHead, '').replace(reTail, '');
};

/**
 * Get a list of theme names in a directory.
 * Theme names are stripped from filename of the built-in UDL files in <udl/>.
 * @param {Array<string>} - A list of theme names, e.g. ['default', 'zenburn'].
 */
var getThemeList = function(dir) {
	var files = getFileList(dir);
	var themeList = [];
	for (var i = 0; i < files.length; i++) {
		themeList.push(getThemeName(files[i]));
	}
	return themeList;
};

/**
 * @typedef {Object} BuildinUdls
 * @description An object that contains a list of theme names and a list of their filenames.
 * @property {Array<string>} themeList - A list of theme names corresponding to the file list.
 * @property {Array<string>} fileList - A list of filenames in <udl/> of this package.
 */
/**
 * Create a special object that contains information of files in <udl/>.
 * This approach could avoid some wired theme names, e.g. '__proto__'
 * @param {udlPath} - The path of <udl/>.
 * @return {BuildinUdls}
 */
var createBuildinUdls = function(udlPath) {
	var fileList = getFileList(udlPath);
	var themeList = [];
	var filename, themeName;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		themeName = getThemeName(filename);
		themeList.push(themeName);
	}
	return { themeList: themeList, fileList: fileList };
};

/**
 * Copy file from source to destination using `fs.createReadSteam()`.
 * This function will overwritten any files that already exists.
 * This function is created because `fs.copyFile()` requires Node v8.5+.
 * This NPM package targets to serve under Node v4+.
 * @param {string} src - Path of source directory.
 * @param {string} dest - Path of destination directory.
 */
var copyFile = function(src, dest) {
	var readSteam = fs.createReadStream(src);
	readSteam.once('error', function(error) {
		console.log(error);
	});
	// readSteam.once('end', function() {
	// 	console.log('copyFile() done');
	// });
	readSteam.pipe(fs.createWriteStream(dest));
};

/**
 * Build UDL XML file(s) in current working directory.
 * Normally the file list should be checked and they are available in <udl/>.
 * @param {Object} options - User options originally from command line.
 * @param {BuildinUdls} - Will use it when need to build all built-in files.
 */
var main = function(options, buildinUdls) {
	var isForce = options.isForce;
	var isBuildAll = options.themeList.length === 0;
	var themeList = isBuildAll ? buildinUdls.themeList : options.themeList;
	var fileList = isBuildAll ? buildinUdls.fileList : options.fileList;
	// Loop the file list.
	var filename, src, dest, themeName, isExist;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		src = packagePath + '/udl/' + filename;
		dest = cwd + '/' + filename;
		themeName = themeList[i];
		// Synchronously check whether file exists.
		isExist = fs.existsSync(dest);
		// Jump to next iteration if file already exists and not in force mode.
		if (isExist && !isForce) {
			console.log('[' + themeName + '] Warning: File already exists. Have skipped this one.');
			continue; // Jump to next iteration.
		}
		copyFile(src, dest);
		console.log('[' + themeName + '] Done: UDL file is created');
	}
};

/* @type {BuildinUdls} - Create a special object that contains theme list and file list. */
var buildinUdls = createBuildinUdls(udlPath);
/* @type {Object} - Parse args into options object. */
var options = parseArgs(args, buildinUdls);
if (options.shouldContinue) {
	main(options, buildinUdls);
}
<commit_msg>Change some keywords; Improve "JSDoc-like" documentation...<commit_after>#!/usr/bin/env node

/**
 * Markdown-plus-plus is a project of markdown syntax highlighting for Notepad++, by customized UDL file (user defined language).
 * This project is open source on:
 * [Source]{@link https://github.com/Edditoria/markdown-plus-plus}.
 * Code released under the MIT license:
 * [License]{@link https://github.com/Edditoria/validid/blob/master/LICENSE.txt}.
 *
 * @file Fetch UML XML file(s) for Notepad++ in current working directory.
 * @auther Edditoria
 * @license MIT
 */

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

/*
Expected usage:
```cmd
npx markdown-plus-plus [options]    # In UDL folder of Notepad++
mpp [options]                       # After install this package globally
npm run mpp -- [options]            # Develop in package directory
````
*/
/* @type {Array<string>} - Usage messages to print */
var usageMsg = [
	'Fetch UML XML file(s) for Notepad++ in current working directory.',
	'',
	'Usage: npx markdown-plus-plus [theme-name...] [-f | --force]',
	'',
	'Options:',
	'theme-name       Fetch UDL file(s) for specified theme(s)',
	'                 You can give multiple theme names',
	'                 e.g. npx markdown-plus-plus solarized zenburn deep-black',
	'-f, --force      Force to overwrite any file that already exists in directory',
	'-l, --list       List bundled themes in this package',
	'-v, --version    Print current version of this package',
	'-h, --help       Print usage',
	'',
	'Examples:',
	'npx markdown-plus-plus               Fetch all UDL files without overwrite',
	'npx markdown-plus-plus zenburn       Fetch Zenburn UDL file without overwrite',
	'npx markdown-plus-plus zenburn -f    Fetch Zenburn UDL file and overwrite',
	''
];

/* @type {string} - Version of this npm package. */
var version = process.env.npm_package_version;
/* @type {Array<string>} - Arguments from node. */
var args = process.argv.slice(2);
/* @type {string} - Current working directory. */
var cwd = process.cwd();
/* @type {string} - Root directory of this npm package. */
var packagePath = path.resolve(__dirname, '../');
/* @type {string} - Path for <udl/> in this package. */
var udlPath = packagePath + '/udl';

/**
 * Parse arguments passed from user command input.
 * "-f" can only be passed at first or last argument.
 * "-l/-h/-v" can only be passed at first argument.
 * @param {Array<string>} args - Arguments from Node's `process.argv.slice(2)`.
 * @param {bundledUdls} bundledUdls - Basically the theme names and filenames in <udl/>.
 * @return {Object} - The parsed object passing to fetch steps.
 */
var parseArgs = function(args, bundledUdls) {
	var options = {
		themeList: [],
		fileList: [],
		shouldContinue: false,
		isForce: false
	};
	// Parse the first argument. Quit asap if user put a proper flag.
	var firstArg = args[0];
	switch (firstArg) {
		case '-l': case '--list':
			console.log(bundledUdls.themeList.join('\n'));
			return options;
		case '-v': case '--version':
			console.log(version);
			return options;
		case '-h': case '--help':
			console.log(usageMsg.join('\n'));
			return options;
	}
	// Loop all arguments. Match the theme names, otherwise the program should quits.
	var arg, indexOfTheme;
	for (var i = 0; i < args.length; i++) {
		arg = args[i];
		if (arg === '-f' || arg === '--force') {
			if (i === 0 || i === args.length - 1) {
				console.log('Warning! In force mode, any existing file(s) will be overwritten.\n');
				options.isForce = true;
			} else {
				console.log('arg[' + i + ']: You pass the \"' + arg + '\" flag in wrong position. Quit for safety.');
				return options;
			}
		} else if (bundledUdls.themeList.indexOf(arg) >= 0) {
			options.themeList.push(arg);
			indexOfTheme = bundledUdls.themeList.indexOf(arg);
			options.fileList.push(bundledUdls.fileList[indexOfTheme]);
		} else {
			console.log('arg[' + i + ']: ' + 'Unsupported option \"' + arg + '\" or combination of options.');
			return options;
		}
	}
	options.shouldContinue = true;
	return options;
};

/**
 * Get file list in a directory.
 * Similar to `dir /w` in command prompt and `ls` in bash.
 * Expect program quit with error if it does not find the directory.
 * @param {string} dir - Path of a directory.
 * @return {Array<string>} - An array as a list of filenames.
 */
var getFileList = function(dir) {
	return fs.readdirSync(dir);
};

/**
 * Get theme name from a UDL XML filename, assuming the filename is in proper format.
 * @param {string} filename - Expected format: 'markdown.hyphen-lowercase-theme-name.udl.xml'.
 * @return {string} - Usually 'hyphen-lowercase-theme-name'.
 */
var getThemeName = function(filename) {
	var reHead = /^(markdown\.)/;
	var reTail = /(\.udl\.xml)$/;
	return filename.replace(reHead, '').replace(reTail, '');
};

/**
 * Get a list of theme names in a directory.
 * Theme names are stripped from filename of the built-in UDL files in <udl/>.
 * @param {Array<string>} - A list of theme names, e.g. ['default', 'zenburn'].
 */
var getThemeList = function(dir) {
	var files = getFileList(dir);
	var themeList = [];
	for (var i = 0; i < files.length; i++) {
		themeList.push(getThemeName(files[i]));
	}
	return themeList;
};

/**
 * @typedef {Object} BundledUdls
 * @description An object that contains a list of theme names and a list of their filenames.
 * @property {Array<string>} themeList - A list of theme names corresponding to the file list.
 * @property {Array<string>} fileList - A list of filenames in <udl/> of this package.
 */
/**
 * Create a special object that contains information of files in <udl/>.
 * This approach could avoid some wired theme names, e.g. '__proto__'
 * @param {udlPath} - The path of <udl/>.
 * @return {BundledUdls}
 */
var createBundledUdls = function(udlPath) {
	var fileList = getFileList(udlPath);
	var themeList = [];
	var filename, themeName;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		themeName = getThemeName(filename);
		themeList.push(themeName);
	}
	return { themeList: themeList, fileList: fileList };
};

/**
 * Copy file from source to destination using `fs.createReadSteam()`.
 * This function will overwritten any files that already exists.
 * This function is created because `fs.copyFile()` requires Node v8.5+.
 * This npm package targets to serve under Node v4+.
 * @param {string} src - Path of source directory.
 * @param {string} dest - Path of destination directory.
 */
var copyFile = function(src, dest) {
	var readSteam = fs.createReadStream(src);
	readSteam.once('error', function(error) {
		console.log(error);
	});
	// readSteam.once('end', function() {
	// 	console.log('copyFile() done');
	// });
	readSteam.pipe(fs.createWriteStream(dest));
};

/**
 * Fetch UDL XML file(s) in current working directory.
 * Normally the file list should be checked and they are available in <udl/>.
 * @param {Object} options - User options originally from command line.
 * @param {BundledUdls} - Will use it when need to fetch all bundled UDL files.
 */
var main = function(options, bundledUdls) {
	var isForce = options.isForce;
	var isFetchAll = options.themeList.length === 0;
	var themeList = isFetchAll ? bundledUdls.themeList : options.themeList;
	var fileList = isFetchAll ? bundledUdls.fileList : options.fileList;
	// Loop the file list.
	var filename, src, dest, themeName, isExist;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		src = packagePath + '/udl/' + filename;
		dest = cwd + '/' + filename;
		themeName = themeList[i];
		// Synchronously check whether file exists.
		isExist = fs.existsSync(dest);
		// Jump to next iteration if file already exists and not in force mode.
		if (isExist && !isForce) {
			console.log('[' + themeName + '] Warning: File already exists. Have skipped this one.');
			continue; // Jump to next iteration.
		}
		copyFile(src, dest);
		console.log('[' + themeName + '] Done: UDL file is created');
	}
};

/* @type {BundledUdls} - Create a special object that contains theme list and file list. */
var bundledUdls = createBundledUdls(udlPath);
/* @type {Object} - Parse args into options object. */
var options = parseArgs(args, bundledUdls);
if (options.shouldContinue) {
	main(options, bundledUdls);
}
<|endoftext|>


<commit_before>import { documentHelpers } from '../model'
import ApiExtension from './ApiExtension'

export default class AuthorApi extends ApiExtension {
  /**
   * @param {object} data from AuthorModal state
   */
  addAuthor (data) {
    this.insertAuthor(data)
  }

  insertAuthor (data, currentAuthor) {
    const editorSession = this.api.getEditorSession()
    const doc = editorSession.getDocument()
    const root = doc.root
    let insertPos = root.authors.length
    if (currentAuthor) {
      insertPos = currentAuthor.getPosition() + 1
    }
    const nodeData = Object.assign({}, data, { type: 'author' })
    this.api.insertNode([root.id, 'authors'], insertPos, nodeData)
  }

  /**
   * @param {string} authorId
   * @param {object} data from AuthorModal state
   */
  updateAuthor (authorId, data) {
    this.api.getEditorSession().transaction(tx => {
      documentHelpers.updateProperty(tx, [authorId, 'firstName'], data.firstName)
      documentHelpers.updateProperty(tx, [authorId, 'middleNames'], data.middleNames)
      documentHelpers.updateProperty(tx, [authorId, 'lastName'], data.lastName)
      documentHelpers.updateProperty(tx, [authorId, 'prefix'], data.prefix)
      documentHelpers.updateProperty(tx, [authorId, 'suffix'], data.suffix)
      documentHelpers.updateProperty(tx, [authorId, 'affiliations'], data.affiliations)
      this.api._selectItem(tx, tx.get(authorId))
    })
  }
}
<commit_msg>Prune empty middle names.<commit_after>import { documentHelpers } from '../model'
import ApiExtension from './ApiExtension'

export default class AuthorApi extends ApiExtension {
  /**
   * @param {object} data from AuthorModal state
   */
  addAuthor (data) {
    this.insertAuthor(data)
  }

  insertAuthor (data, currentAuthor) {
    const editorSession = this.api.getEditorSession()
    const doc = editorSession.getDocument()
    const root = doc.root
    let insertPos = root.authors.length
    if (currentAuthor) {
      insertPos = currentAuthor.getPosition() + 1
    }
    const nodeData = Object.assign({}, data, { type: 'author' })
    // prune empty middlenames
    nodeData.middleNames = nodeData.middleNames.filter(Boolean)
    this.api.insertNode([root.id, 'authors'], insertPos, nodeData)
  }

  /**
   * @param {string} authorId
   * @param {object} data from AuthorModal state
   */
  updateAuthor (authorId, data) {
    // prune empty middlenames
    data.middleNames = data.middleNames.filter(Boolean)
    this.api.getEditorSession().transaction(tx => {
      documentHelpers.updateProperty(tx, [authorId, 'firstName'], data.firstName)
      documentHelpers.updateProperty(tx, [authorId, 'middleNames'], data.middleNames)
      documentHelpers.updateProperty(tx, [authorId, 'lastName'], data.lastName)
      documentHelpers.updateProperty(tx, [authorId, 'prefix'], data.prefix)
      documentHelpers.updateProperty(tx, [authorId, 'suffix'], data.suffix)
      documentHelpers.updateProperty(tx, [authorId, 'affiliations'], data.affiliations)
      this.api._selectItem(tx, tx.get(authorId))
    })
  }
}
<|endoftext|>


<commit_before>/**
 * Instantiates and exports custom configured instance of bunyan for JSON formatted logging
 * throughout application
 * @module server/lib/logger
 */
'use strict';
require('loadenv')();

var bunyan = require('bunyan');
var put = require('101/put');

var streams = [{
  level: process.env.LOG_LEVEL_STDOUT,
  stream: process.stdout
}];

var serializers = put(bunyan.stdSerializers, {
  /* custom serializers will go here */
});

module.exports = bunyan.createLogger({
  name: 'hyperion',
  streams: streams,
  serializers: serializers
});
<commit_msg>Modify logger module to export generator function<commit_after>/**
 * Instantiates and exports custom configured instance of bunyan for JSON formatted logging
 * throughout application
 * @module server/lib/logger
 */
'use strict';
require('loadenv')();

var bunyan = require('bunyan');
var put = require('101/put');

var streams = [{
  level: process.env.LOG_LEVEL_STDOUT,
  stream: process.stdout
}];

var serializers = put(bunyan.stdSerializers, {
  /* custom serializers will go here */
});

var logger = bunyan.createLogger({
  name: 'hyperion',
  streams: streams,
  serializers: serializers
});

/**
 * https://github.com/trentm/node-bunyan#logchild
 * Initialize and return instance of a bunyan child logger
 * @param {String} module
 * @return {Object} instance of bunyan child logger
 */
function createChildLogger (module) {
  return logger.child({
    module: module
  });
}

module.exports = createChildLogger;
<|endoftext|>


<commit_before>const groupFn = function(fn, label) {
  console.group(label || 'Group-function')
  const ret = fn()
  console.groupEnd()
  return ret
}

class Program {
  constructor() {
    this.functions = {}
    this.labels = {}
    this.defaultAttributes = {
      type: 'c', auto: true, conditional: false
    }
  }

  parseAttributes(str) {
    const timesAppearing = {}
    const validAttributes = [
      'c', 'i', 'r',
      '0', '1',
      '!', '?'
    ]
    const attributes = {}
    let contradictory
    let overallContradictory = 0
    const result = {}

    for (let char of str) {
      timesAppearing[char] = (timesAppearing[char] || 0) + 1
      if (timesAppearing[char] > 1) throw new Error(
        `In attributes "${str}" ${char} appears more than once`)

      if (!validAttributes.includes(char)) throw new Error(
        `In attributes "${str}" ${char} is an invalid attribute`)

      attributes[char] = true
    }

    contradictory = 0
    if (attributes['c']) {
      contradictory++
      result.type = 'c'
    }
    if (attributes['i']) {
      contradictory++
      result.type = 'i'
    }
    if (attributes['r']) {
      contradictory++
      result.type = 'r'
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['0']) {
      contradictory++
      result.auto = false
    }
    if (attributes['1']) {
      contradictory++
      result.auto = true
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['?']) {
      result.conditional = true
      contradictory++
    }
    if (attributes['!']) {
      result.conditional = false
      contradictory++
    }
    overallContradictory |= contradictory > 1

    if (overallContradictory) throw new Error(
      `Attributes "${str}" is contradictory`)

    const withDefaults = Object.assign({}, this.defaultAttributes, result)

    return withDefaults
  }

  compile(code, environment = {vars: {}}) {
    const lines = code.split('\n')
    const results = []

    LINE_LOOP:
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      let line = lines[lineIndex]

      if (line === '')
        continue

      if (line.startsWith('define')) {
        // Getting name
        let name = ''

        let defCharIndex = 6
        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === '(')
            break

          name += char
        }

        // Getting parameters
        let params = []
        let currentParam = ''

        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === ')') {
            if (currentParam) {
              params.push(currentParam)
            }
            break
          }

          if (char === ',') {
            params.push(currentParam)
            currentParam = ''
            continue
          }

          if (char === ' ' && currentParam === '')
            continue

          currentParam += char
        }

        // Getting code lines
        const codeLines = []

        while (true) {
          lineIndex++
          line = lines[lineIndex]

          if (lineIndex > lines.length)
            break

          if (!line.startsWith('  '))
            break

          codeLines.push(line.slice(2))
        }

        // Storing function
        this.functions[name] = {codeLines, params}

        continue
      }

      if (line.startsWith('!')) {
        const env = Object.assign({}, environment)

        if (line.endsWith(':')) {
          const argCodeLines = []
          while (true) {
            lineIndex++
            const line = lines[lineIndex]
            if (lineIndex >= lines.length) break
            if (!line.startsWith('  ')) break
            argCodeLines.push(line.slice(2))
          }
          env.passedCode = argCodeLines
        }

        console.log('line:', line)

        const { funcResults, name } = this.processFunctionCall(
          line.slice(1), env)

        if (funcResults.length === 0) {
          console.warn(`Procedure call "${name}" had no results!`)
        }

        for (let r of funcResults) {
          results.push(r)
        }

        continue
      }

      if (line.startsWith('#')) {
        const blockName = line.slice(1)
        results.push({block: blockName})
        continue
      }

      if (line.startsWith('@')) {
        const labelName = line.slice(1)
        this.labels[labelName] = results.length
        continue
      }

      let attributeStr = ''
      let charIndex = -1
      let char = ''
      while (true) {
        charIndex++
        char = line[charIndex]
        if (char === ' ') {
          attributeStr = ''
          break
        }
        if (charIndex >= line.length) {
          attributeStr = ''
          break
        }
        if (char === ':') break
        attributeStr += char
      }

      let commandPart
      if (attributeStr) {
        let char = ''
        do {
          charIndex++
          char = line[charIndex]
        } while (char === ' ')
        commandPart = line.slice(charIndex)
      } else {
        commandPart = line
      }
      const attributes = this.parseAttributes(attributeStr)
      console.log('attributes proper:', attributes)

      const command = this.expression(commandPart, environment)

      const obj = Object.assign({command}, attributes)

      results.push(obj)
    }

    return results
  }

  expression(code, environment = {vars: {}}) {
    // TODO: put reused stuff in here because having identical code in multiple
    // parts of a program is BAAAD!

    // Parts of lines that should be put in here:
    // * everything past the command name part of code lines (past first space)
    //   (or just the whole command line)
    // * arguments (each in a separate expression call)

    let charIndex = -1
    let result = ''

    console.log('EXPRESSION:', code)

    while (true) {
      charIndex++
      let char = code[charIndex]
      if (charIndex >= code.length) break

      // Variables
      if (char === '$') {
        let name = ''
        while (true) {
          charIndex++
          char = code[charIndex]
          if (charIndex >= code.length) break

          if (char === ' ') {
            charIndex--
            break
          }

          name += char
        }

        // console.log('Variable:', name)
        // console.log('Environment:', environment)
        // console.log('Value:', environment.vars[name])

        if (!environment.vars.hasOwnProperty(name))
          throw new Error(`Access to undefined variable: "${name}"`)

        result += environment.vars[name]

        continue
      }

      // Function calls
      if (char === '^') {
        // console.group('fn call')

        const { funcResults, newCharIndex, name } = this.processFunctionCall(
          code.slice(charIndex + 1), environment)
        charIndex += newCharIndex

        const lastLine = funcResults.slice(-1)[0]

        if (funcResults.length > 1)
          console.warn(`Function call "${name}" had more than one result!`
                    + ' (Using last result.)')
        if (!lastLine) console.warn(`Function "${name}" returned nothing!`)

        result += lastLine.command

        // console.groupEnd()

        continue
      }

      result += char
    }

    console.log('RESULT:', result)

    return result
  }

  processFunctionCall(code, environment) {
    // PROCESS PROCESS PROCESS

    // Getting name.
    let name = ''

    let char = ''
    let charIndex = -1
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break

      if (char === '(') break

      name += char
    }

    let func
    console.log(environment.vars)
    if (environment.vars.hasOwnProperty(name)) {
      func = environment.vars[name]
      console.log('lel')
    } else {
      func = this.functions[name]
    }
    console.log('hi:', environment)
    console.log('fn:', func)
    const params = func.params
    const codeLines = func.codeLines
    const env = {vars: {}}

    // Getting arguments.
    const argsSliceStart = charIndex + 1
    let parenWeight = 0
    let currentArg = ''
    const args = []
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break
      if (char === ')') {
        if (parenWeight === 0) {
          if (currentArg) {
            args.push(currentArg)
          }
          break
        } else {
          parenWeight--
        }
      }
      if (char === '(') {
        parenWeight++
      }
      if (char === ',' && parenWeight === 0) {
        args.push(currentArg)
        currentArg = ''
        continue
      }
      if (char === ' ' && currentArg == '') continue
      currentArg += char
    }
    const argCode = code.slice(argsSliceStart, charIndex)
    // console.log('Function name:', name)
    // console.log('Arguments:', args)

    console.log('test', environment.passedCode)
    for (let argIndex = 0; argIndex < params.length; argIndex++) {
      console.log('Param/arg', params[argIndex], '=', args[argIndex])

      if (args[argIndex]) {
        env.vars[params[argIndex]] = this.expression(
          args[argIndex], environment)
      }
    }

    if (environment.passedCode) {
      env.vars[params[params.length - 1]] = {
        codeLines: environment.passedCode,
        params: []
      }
    }

    const funcResults = this.compile(codeLines.join('\n'), env)
    return {newCharIndex: charIndex + 1, funcResults, name}
  }

  handleLabels(stack) {
    // postprocessing is s c a r y

    const results = []

    let blockIndex = -1
    let block
    while (true) {
      blockIndex++
      block = stack[blockIndex]
      if (blockIndex >= stack.length) break

      if (!block.command) {
        results.push(block)
        continue
      }

      let newCommand = ''
      let charIndex = -1
      let char
      while (true) {
        charIndex++
        char = block.command[charIndex]
        if (charIndex >= block.command.length) break

        if (char === '@') {
          let labelName = ''
          while (true) {
            charIndex++
            char = block.command[charIndex]
            if (char === ' ') break
            if (charIndex > block.command.length) break
            labelName += char
          }
          const pos = `~ ~${this.labels[labelName] - blockIndex} ~`
          newCommand += pos + ' '
          continue
        }

        newCommand += char
      }

      results.push(Object.assign({}, block, {command: newCommand}))
    }

    return results
  }
}

const p = new Program()
let stack
stack = p.compile(`

define loop(times, main)
  scoreboard players set LOOP loopCounter $times
  setblock ~ ~2 ~ redstone_block
  #glass
  #quartz_ore
  i0: scoreboard players remove LOOP loopCounter 1
  scoreboard players test loopCounter LOOP 0 0
  ?: setblock @done redstone_block
  scoreboard players test loopCounter LOOP 1 *
  ?: setblock @body redstone_block
  #glass
  @body
  #quartz_ore
  i0: say Loop
  !main()
  setblock @done redstone_block
  #glass
  @done
  #quartz_ore
  i0: say Done
  #glass

i0:
!loop(5):
  say foo

`)
stack = p.handleLabels(stack)
console.dir(stack)
console.log(CBU.summonStack(stack))
<commit_msg>Help prevent label overwriting<commit_after>const groupFn = function(fn, label) {
  console.group(label || 'Group-function')
  const ret = fn()
  console.groupEnd()
  return ret
}

class Program {
  constructor() {
    this.functions = {}
    this.labels = {}
    this.defaultAttributes = {
      type: 'c', auto: true, conditional: false
    }
  }

  parseAttributes(str) {
    const timesAppearing = {}
    const validAttributes = [
      'c', 'i', 'r',
      '0', '1',
      '!', '?'
    ]
    const attributes = {}
    let contradictory
    let overallContradictory = 0
    const result = {}

    for (let char of str) {
      timesAppearing[char] = (timesAppearing[char] || 0) + 1
      if (timesAppearing[char] > 1) throw new Error(
        `In attributes "${str}" ${char} appears more than once`)

      if (!validAttributes.includes(char)) throw new Error(
        `In attributes "${str}" ${char} is an invalid attribute`)

      attributes[char] = true
    }

    contradictory = 0
    if (attributes['c']) {
      contradictory++
      result.type = 'c'
    }
    if (attributes['i']) {
      contradictory++
      result.type = 'i'
    }
    if (attributes['r']) {
      contradictory++
      result.type = 'r'
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['0']) {
      contradictory++
      result.auto = false
    }
    if (attributes['1']) {
      contradictory++
      result.auto = true
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['?']) {
      result.conditional = true
      contradictory++
    }
    if (attributes['!']) {
      result.conditional = false
      contradictory++
    }
    overallContradictory |= contradictory > 1

    if (overallContradictory) throw new Error(
      `Attributes "${str}" is contradictory`)

    const withDefaults = Object.assign({}, this.defaultAttributes, result)

    return withDefaults
  }

  compile(code, environment = {vars: {}}) {
    const lines = code.split('\n')
    const results = []

    LINE_LOOP:
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      let line = lines[lineIndex]

      if (line === '')
        continue

      if (line.startsWith('define')) {
        // Getting name
        let name = ''

        let defCharIndex = 6
        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === '(')
            break

          name += char
        }

        // Getting parameters
        let params = []
        let currentParam = ''

        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === ')') {
            if (currentParam) {
              params.push(currentParam)
            }
            break
          }

          if (char === ',') {
            params.push(currentParam)
            currentParam = ''
            continue
          }

          if (char === ' ' && currentParam === '')
            continue

          currentParam += char
        }

        // Getting code lines
        const codeLines = []

        while (true) {
          lineIndex++
          line = lines[lineIndex]

          if (lineIndex > lines.length)
            break

          if (!line.startsWith('  '))
            break

          codeLines.push(line.slice(2))
        }

        // Storing function
        this.functions[name] = {codeLines, params}

        continue
      }

      if (line.startsWith('!')) {
        const env = Object.assign({}, environment)

        if (line.endsWith(':')) {
          const argCodeLines = []
          while (true) {
            lineIndex++
            const line = lines[lineIndex]
            if (lineIndex >= lines.length) break
            if (!line.startsWith('  ')) break
            argCodeLines.push(line.slice(2))
          }
          env.passedCode = argCodeLines
        }

        console.log('line:', line)

        const { funcResults, name } = this.processFunctionCall(
          line.slice(1), env)

        if (funcResults.length === 0) {
          console.warn(`Procedure call "${name}" had no results!`)
        }

        for (let r of funcResults) {
          results.push(r)
        }

        continue
      }

      if (line.startsWith('#')) {
        const blockName = line.slice(1)
        results.push({block: blockName})
        continue
      }

      if (line.startsWith('@')) {
        let labelName
        if (environment.labelSafe) {
          labelName = `__${environment.labelSafe}__${line.slice(1)}`
        } else {
          labelName = line.slice(1)
        }
        results.push({label: labelName})
        continue
      }

      let attributeStr = ''
      let charIndex = -1
      let char = ''
      while (true) {
        charIndex++
        char = line[charIndex]
        if (char === ' ') {
          attributeStr = ''
          break
        }
        if (charIndex >= line.length) {
          attributeStr = ''
          break
        }
        if (char === ':') break
        attributeStr += char
      }

      let commandPart
      if (attributeStr) {
        let char = ''
        do {
          charIndex++
          char = line[charIndex]
        } while (char === ' ')
        commandPart = line.slice(charIndex)
      } else {
        commandPart = line
      }
      const attributes = this.parseAttributes(attributeStr)
      // console.log('attributes proper:', attributes)

      const command = this.expression(commandPart, environment)

      const obj = Object.assign({command}, attributes)

      results.push(obj)
    }

    return results
  }

  expression(code, environment = {vars: {}}) {
    // TODO: put reused stuff in here because having identical code in multiple
    // parts of a program is BAAAD!

    // Parts of lines that should be put in here:
    // * everything past the command name part of code lines (past first space)
    //   (or just the whole command line)
    // * arguments (each in a separate expression call)

    let charIndex = -1
    let result = ''

    console.log('-')
    console.log('EXPRESSION:', code)
    console.log('ENVIRONMENT:', environment)

    while (true) {
      charIndex++
      let char = code[charIndex]
      if (charIndex >= code.length) break

      // Variables
      if (char === '$') {
        let name = ''
        while (true) {
          charIndex++
          char = code[charIndex]
          if (charIndex >= code.length) break

          if (char === ' ') {
            charIndex--
            break
          }

          name += char
        }

        // console.log('Variable:', name)
        // console.log('Environment:', environment)
        // console.log('Value:', environment.vars[name])

        if (!environment.vars.hasOwnProperty(name))
          throw new Error(`Access to undefined variable: "${name}"`)

        result += environment.vars[name]

        continue
      }

      // Function calls
      if (char === '^') {
        // console.group('fn call')

        const { funcResults, newCharIndex, name } = this.processFunctionCall(
          code.slice(charIndex + 1), environment)
        charIndex += newCharIndex

        const lastLine = funcResults.slice(-1)[0]

        if (funcResults.length > 1)
          console.warn(`Function call "${name}" had more than one result!`
                    + ' (Using last result.)')
        if (!lastLine) console.warn(`Function "${name}" returned nothing!`)

        result += lastLine.command

        // console.groupEnd()

        continue
      }

      // Labels - this is to add special codes for each scope kinda.
      // To avoid passed functions overwriting labels etc blah blah
      if (environment.labelSafe) {
        if (char === '@') {
          let labelName = ''
          while (true) {
            charIndex++
            char = code[charIndex]
            if (char === ' ') break
            if (charIndex >= code.length) break
            labelName += char
          }
          console.log('foo:', labelName)
          result += '@__' + environment.labelSafe + '__' + labelName
          continue
        }
      }

      result += char
    }

    console.log('RESULT:', result)

    return result
  }

  processFunctionCall(code, environment) {
    // PROCESS PROCESS PROCESS

    // Getting name.
    let name = ''

    let char = ''
    let charIndex = -1
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break

      if (char === '(') break

      name += char
    }

    let func
    console.log(environment.vars)
    if (environment.vars.hasOwnProperty(name)) {
      func = environment.vars[name]
      console.log('lel')
    } else {
      func = this.functions[name]
    }
    console.log('hi:', environment)
    console.log('fn:', func)
    const params = func.params
    const codeLines = func.codeLines
    const env = {
      vars: {},
      labelSafe: 'safe-' + Math.random()
    }

    // Getting arguments.
    const argsSliceStart = charIndex + 1
    let parenWeight = 0
    let currentArg = ''
    const args = []
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break
      if (char === ')') {
        if (parenWeight === 0) {
          if (currentArg) {
            args.push(currentArg)
          }
          break
        } else {
          parenWeight--
        }
      }
      if (char === '(') {
        parenWeight++
      }
      if (char === ',' && parenWeight === 0) {
        args.push(currentArg)
        currentArg = ''
        continue
      }
      if (char === ' ' && currentArg == '') continue
      currentArg += char
    }
    const argCode = code.slice(argsSliceStart, charIndex)
    // console.log('Function name:', name)
    // console.log('Arguments:', args)

    console.log('test', environment.passedCode)
    for (let argIndex = 0; argIndex < params.length; argIndex++) {
      console.log('Param/arg', params[argIndex], '=', args[argIndex])

      if (args[argIndex]) {
        env.vars[params[argIndex]] = this.expression(
          args[argIndex], environment)
      }
    }

    if (environment.passedCode) {
      env.vars[params[params.length - 1]] = {
        codeLines: environment.passedCode,
        params: []
      }
    }

    const funcResults = this.compile(codeLines.join('\n'), env)
    return {newCharIndex: charIndex + 1, funcResults, name}
  }

  handleLabels(stack) {
    // postprocessing is s c a r y

    const results = []

    let labelPositions = {}

    // Set up label positions
    let blockIndex = -1
    let labelCount = 0
    while (true) {
      blockIndex++
      const block = stack[blockIndex]
      if (blockIndex >= stack.length) break
      if (block.label) {
        // Labels aren't actual blocks so ignore them in the position.
        labelPositions[block.label] = blockIndex - labelCount
        labelCount++
      }
    }

    // Remove label "blocks"
    stack = stack.filter(t => !t.label)

    // Replace inline-labels with the correct relative positions
    blockIndex = -1
    while (true) {
      blockIndex++
      const block = stack[blockIndex]
      if (blockIndex >= stack.length) break

      if (!block.command) {
        results.push(block)
        continue
      }

      let newCommand = ''
      let charIndex = -1
      let char
      while (true) {
        charIndex++
        char = block.command[charIndex]
        if (charIndex >= block.command.length) break

        if (char === '@') {
          let labelName = ''
          while (true) {
            charIndex++
            char = block.command[charIndex]
            if (char === ' ') break
            if (charIndex >= block.command.length) break
            labelName += char
          }

          newCommand += `~ ~${labelPositions[labelName] - blockIndex} ~ `
          continue
        }

        newCommand += char
      }

      results.push(Object.assign({}, block, {command: newCommand}))
    }

    return results
  }
}

const p = new Program()
let stack
stack = p.compile(`

define foo(x)
  @hog
  say this is a command
  !x()
  say @hog

!foo():
  say bar
  @hog
  say OVERWRITTEN NOW!

`)
console.dir(stack)
stack = p.handleLabels(stack)
console.dir(stack)
console.log(CBU.summonStack(stack))
<|endoftext|>


<commit_before>define(["require", "js/html/HtmlElement", "js/ui/ContentPlaceHolder", "js/core/Module", "underscore", "js/conf/ModuleConfiguration", "flow"],
    function (require, HtmlElement, ContentPlaceHolder, Module, _, ModuleConfiguration, flow) {
        var ModuleLoader = HtmlElement.inherit("js.core.ModuleLoader", {

            $classAttributes: ['router', 'currentModuleName', 'state'],
            defaults: {
                currentModuleName:  null,
                tagName: 'div',
                componentClass: "module-loader",
                state: null
            },

            ctor: function (attributes) {
                this.callBase();
                this.$modules = {};
                this.$moduleCache = {};
            },

            _initializationComplete: function () {
                this.callBase();

                for (var i = 0; i < this.$configurations.length; i++) {
                    var config = this.$configurations[i];

                    if (config instanceof ModuleConfiguration) {
                        this.addModule(config.$);
                    }
                }
            },

            /***
             * adds a module to the list of known modules
             *
             * @param {js.conf.ModuleConfiguration} module
             */
            addModule: function (module) {
                _.defaults(module, {
                    name: null,
                    moduleClass: null,
                    route: null
                });

                if (!module.name) {
                    throw "module cannot be added: module name is required";
                }

                if (!module.moduleClass) {
                    throw "no moduleClass defined for module";
                }

                if (this.$modules.hasOwnProperty(module.name)) {
                    throw "module with name '" + module.name + "' already registered"
                }

                this.$modules[module.name] = module;

                if (module.route) {
                    if (!this.$.router) {
                        throw "defining modules with routes requires a router instance to be set"
                    }

                    var self = this;
                    this.$.router.addRoute({
                        name: module.name,
                        route: module.route,
                        fn: function (routeContext) {

                            // route triggered -> load module
                            self.loadModule(module, routeContext.callback, routeContext);
                        }.async()
                    });
                }

            },

            _startModule: function (moduleName, moduleInstance, callback, routeContext, cachedInstance) {

                var self = this;
                this.set('currentModuleName', moduleName);

                var contentPlaceHolders = this.getContentPlaceHolders();

                // set content
                for (var i = 0; i < contentPlaceHolders.length; i++) {
                    var contentPlaceHolder = contentPlaceHolders[i];
                    contentPlaceHolder.set("content", moduleInstance.findContent(contentPlaceHolder.$.name));
                }

                var internalCallback = function(err) {

                    self.set('state', err ? 'error' : null);

                    if (callback) {
                        callback(err);
                    }
                };

                // start module
                moduleInstance.start(function (err) {

                    if (err || cachedInstance) {
                        internalCallback(err);
                    } else {
                        if (routeContext) {
                            // fresh instance with maybe new routers -> exec routes for new router
                            var routeExecutionStack = [];

                            for (var i = 0; i < moduleInstance.$routers.length; i++) {
                                routeExecutionStack = routeExecutionStack.concat(moduleInstance.$routers[i].generateRoutingStack(routeContext.fragment));
                            }

                            flow()
                                .seqEach(routeExecutionStack, function(routingFunction, cb) {
                                    routingFunction(cb);
                                })
                                .exec(internalCallback);

                        } else {
                            internalCallback();
                        }

                    }
                }, routeContext);

            },

            /***
             * loads an module instance into the module loader and starts the module
             *
             * @param {js.conf.ModuleConfiguration} module - the module to load
             * @param {Function} [callback] - a callback function which gets invoked after the module is loaded or an error occurred
             * @param {js.core.Router.RouteContext} [routeContext]
             */
            loadModule: function (module, callback, routeContext) {
                if (module.name === this.$.currentModuleName) {
                    // module already shown
                    if (callback) {
                        callback();
                    }
                } else {

                    this._clearContentPlaceHolders();
                    this.$lastModuleName = this.$.currentModuleName;
                    this.set('currentModuleName', null);
                    this.set('state', 'loading');

                    if (this.$moduleCache.hasOwnProperty(module.name)) {
                        this._startModule(module.name, this.$moduleCache[module.name], callback, routeContext, true);
                    } else {

                        var self = this;
                        // load module

                        require([this.$stage.$applicationContext.getFqClassName(module.moduleClass)], function (moduleBaseClass) {
                            var moduleInstance = new moduleBaseClass(null, false, self.$stage, null, null);

                            if (moduleInstance instanceof Module) {
                                moduleInstance._initialize("auto");

                                // cache instance
                                self.$moduleCache[module.name] = moduleInstance;

                                self._startModule(module.name, moduleInstance, callback, routeContext, false);

                            } else {
                                if (callback) {
                                    callback("Module '" + module.moduleClass + "' isn't an instance of js.core.Module");
                                }
                            }

                        }, function(err) {

                            self.set('state', 'error');
                            this.log(err, "warn");

                            if (callback) {
                                callback(err);
                            }
                        });
                    }
                }

            },

            _clearContentPlaceHolders: function() {

                var contentPlaceHolders = this.getContentPlaceHolders();

                // set content
                for (var i = 0; i < contentPlaceHolders.length; i++) {
                    var contentPlaceHolder = contentPlaceHolders[i];
                    contentPlaceHolder.set("content", null);
                }

            },

            // TODO: remove this method and make the modules bindable

            /***
             * @deprecated will be removed soon
             * @return {Array}
             */
            moduleNames: function(){
                var modules = [], conf;
                for(var i = 0; i < this.$configurations.length; i++){
                    conf = this.$configurations[i];
                    if(conf instanceof ModuleConfiguration){
                        modules.push(conf.$.name);
                    }
                }
                return modules;
            },

            _renderState: function(state, oldState) {
                oldState && this.removeClass(oldState);
                state && this.addClass(state);
            }
        });

        ModuleLoader.findContentPlaceHolders = function (component) {
            var ret = [];

            for (var i = 0; i < component.$children.length; i++) {
                var child = component.$children[i];
                if (child instanceof ContentPlaceHolder) {
                    ret.push(child);
                } else {
                    ret.concat(ModuleLoader.findContentPlaceHolders(child));
                }
            }

            return ret;

        };

        return ModuleLoader;
    });<commit_msg>fixed logging<commit_after>define(["require", "js/html/HtmlElement", "js/ui/ContentPlaceHolder", "js/core/Module", "underscore", "js/conf/ModuleConfiguration", "flow"],
    function (require, HtmlElement, ContentPlaceHolder, Module, _, ModuleConfiguration, flow) {
        var ModuleLoader = HtmlElement.inherit("js.core.ModuleLoader", {

            $classAttributes: ['router', 'currentModuleName', 'state'],
            defaults: {
                currentModuleName:  null,
                tagName: 'div',
                componentClass: "module-loader",
                state: null
            },

            ctor: function (attributes) {
                this.callBase();
                this.$modules = {};
                this.$moduleCache = {};
            },

            _initializationComplete: function () {
                this.callBase();

                for (var i = 0; i < this.$configurations.length; i++) {
                    var config = this.$configurations[i];

                    if (config instanceof ModuleConfiguration) {
                        this.addModule(config.$);
                    }
                }
            },

            /***
             * adds a module to the list of known modules
             *
             * @param {js.conf.ModuleConfiguration} module
             */
            addModule: function (module) {
                _.defaults(module, {
                    name: null,
                    moduleClass: null,
                    route: null
                });

                if (!module.name) {
                    throw "module cannot be added: module name is required";
                }

                if (!module.moduleClass) {
                    throw "no moduleClass defined for module";
                }

                if (this.$modules.hasOwnProperty(module.name)) {
                    throw "module with name '" + module.name + "' already registered"
                }

                this.$modules[module.name] = module;

                if (module.route) {
                    if (!this.$.router) {
                        throw "defining modules with routes requires a router instance to be set"
                    }

                    var self = this;
                    this.$.router.addRoute({
                        name: module.name,
                        route: module.route,
                        fn: function (routeContext) {

                            // route triggered -> load module
                            self.loadModule(module, routeContext.callback, routeContext);
                        }.async()
                    });
                }

            },

            _startModule: function (moduleName, moduleInstance, callback, routeContext, cachedInstance) {

                var self = this;
                this.set('currentModuleName', moduleName);

                var contentPlaceHolders = this.getContentPlaceHolders();

                // set content
                for (var i = 0; i < contentPlaceHolders.length; i++) {
                    var contentPlaceHolder = contentPlaceHolders[i];
                    contentPlaceHolder.set("content", moduleInstance.findContent(contentPlaceHolder.$.name));
                }

                var internalCallback = function(err) {

                    self.set('state', err ? 'error' : null);

                    if (callback) {
                        callback(err);
                    }
                };

                // start module
                moduleInstance.start(function (err) {

                    if (err || cachedInstance) {
                        internalCallback(err);
                    } else {
                        if (routeContext) {
                            // fresh instance with maybe new routers -> exec routes for new router
                            var routeExecutionStack = [];

                            for (var i = 0; i < moduleInstance.$routers.length; i++) {
                                routeExecutionStack = routeExecutionStack.concat(moduleInstance.$routers[i].generateRoutingStack(routeContext.fragment));
                            }

                            flow()
                                .seqEach(routeExecutionStack, function(routingFunction, cb) {
                                    routingFunction(cb);
                                })
                                .exec(internalCallback);

                        } else {
                            internalCallback();
                        }

                    }
                }, routeContext);

            },

            /***
             * loads an module instance into the module loader and starts the module
             *
             * @param {js.conf.ModuleConfiguration} module - the module to load
             * @param {Function} [callback] - a callback function which gets invoked after the module is loaded or an error occurred
             * @param {js.core.Router.RouteContext} [routeContext]
             */
            loadModule: function (module, callback, routeContext) {
                if (module.name === this.$.currentModuleName) {
                    // module already shown
                    if (callback) {
                        callback();
                    }
                } else {

                    this._clearContentPlaceHolders();
                    this.$lastModuleName = this.$.currentModuleName;
                    this.set('currentModuleName', null);
                    this.set('state', 'loading');

                    if (this.$moduleCache.hasOwnProperty(module.name)) {
                        this._startModule(module.name, this.$moduleCache[module.name], callback, routeContext, true);
                    } else {

                        var self = this;
                        // load module

                        require([this.$stage.$applicationContext.getFqClassName(module.moduleClass)], function (moduleBaseClass) {
                            var moduleInstance = new moduleBaseClass(null, false, self.$stage, null, null);

                            if (moduleInstance instanceof Module) {
                                moduleInstance._initialize("auto");

                                // cache instance
                                self.$moduleCache[module.name] = moduleInstance;

                                self._startModule(module.name, moduleInstance, callback, routeContext, false);

                            } else {
                                if (callback) {
                                    callback("Module '" + module.moduleClass + "' isn't an instance of js.core.Module");
                                }
                            }

                        }, function(err) {

                            self.set('state', 'error');
                            self.log(err, "warn");

                            if (callback) {
                                callback(err);
                            }
                        });
                    }
                }

            },

            _clearContentPlaceHolders: function() {

                var contentPlaceHolders = this.getContentPlaceHolders();

                // set content
                for (var i = 0; i < contentPlaceHolders.length; i++) {
                    var contentPlaceHolder = contentPlaceHolders[i];
                    contentPlaceHolder.set("content", null);
                }

            },

            // TODO: remove this method and make the modules bindable

            /***
             * @deprecated will be removed soon
             * @return {Array}
             */
            moduleNames: function(){
                var modules = [], conf;
                for(var i = 0; i < this.$configurations.length; i++){
                    conf = this.$configurations[i];
                    if(conf instanceof ModuleConfiguration){
                        modules.push(conf.$.name);
                    }
                }
                return modules;
            },

            _renderState: function(state, oldState) {
                oldState && this.removeClass(oldState);
                state && this.addClass(state);
            }
        });

        ModuleLoader.findContentPlaceHolders = function (component) {
            var ret = [];

            for (var i = 0; i < component.$children.length; i++) {
                var child = component.$children[i];
                if (child instanceof ContentPlaceHolder) {
                    ret.push(child);
                } else {
                    ret.concat(ModuleLoader.findContentPlaceHolders(child));
                }
            }

            return ret;

        };

        return ModuleLoader;
    });<|endoftext|>


<commit_before>#!/usr/bin/env node
var foreground = require('foreground-child')
var NYC = require('../')
var path = require('path')
var sw = require('spawn-wrap')

if (process.env.NYC_CWD) {
  ;(new NYC()).wrap()

  // make sure we can run coverage on
  // our own index.js, I like turtles.
  var name = require.resolve('../')
  delete require.cache[name]

  sw.runMain()
} else {
  var yargs = require('yargs')
    .usage('$0 [command] [options]\n\nrun your tests with the nyc bin to instrument them with coverage')
    .command('report', 'run coverage report for .nyc_output', function (yargs) {
      yargs
        .usage('$0 report [options]')
        .option('r', {
          alias: 'reporter',
          describe: 'coverage reporter(s) to use',
          default: 'text'
        })
        .help('h')
        .alias('h', 'help')
        .example('$0 report --reporter=lcov', 'output an HTML lcov report to ./coverage')
    })
    .command('check-coverage', 'check whether coverage is within thresholds provided', function (yargs) {
      yargs
        .usage('$0 check-coverage [options]')
        .option('b', {
          alias: 'branches',
          default: 0,
          description: 'what % of branches must be covered?'
        })
        .option('f', {
          alias: 'functions',
          default: 0,
          description: 'what % of functions must be covered?'
        })
        .option('l', {
          alias: 'lines',
          default: 90,
          description: 'what % of lines must be covered?'
        })
        .option('s', {
          alias: 'statements',
          default: 0,
          description: 'what % of statements must be covered?'
        })
        .help('h')
        .alias('h', 'help')
        .example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided")
    })
    .option('r', {
      alias: 'reporter',
      describe: 'coverage reporter(s) to use',
      default: 'text'
    })
    .option('s', {
      alias: 'silent',
      default: false,
      type: 'boolean',
      describe: "don't output a report after tests finish running"
    })
    .help('h')
    .alias('h', 'help')
    .version(require('../package.json').version)
    .example('$0 npm test', 'instrument your tests with coverage')
    .example('$0 report --reporter=text-lcov', 'output lcov report after running your tests')
    .epilog('visit http://git.io/vTJJB for list of available reporters')
  var argv = yargs.argv

  if (~argv._.indexOf('report')) {
    // run a report.
    process.env.NYC_CWD = process.cwd()

    report(argv)
  } else if (~argv._.indexOf('check-coverage')) {
    foreground(
      require.resolve('istanbul/lib/cli'),
      [
        'check-coverage',
        '--lines=' + argv.lines,
        '--functions=' + argv.functions,
        '--branches=' + argv.branches,
        '--statements=' + argv.statements,
        path.resolve(process.cwd(), './.nyc_output/*.json')
      ]
    )
  } else if (argv._.length) {
    // wrap subprocesses and execute argv[1]
    var nyc = (new NYC())
    nyc.cleanup()

    sw([__filename], {
      NYC_CWD: process.cwd()
    })

    foreground(nyc.mungeArgs(argv), function (done) {
      if (!argv.silent) report(argv)
      return done()
    })
  } else {
    // I don't have a clue what you're doing.
    yargs.showHelp()
  }
}

function report (argv) {
  process.env.NYC_CWD = process.cwd()

  ;(new NYC({
    reporter: argv.reporter
  })).report()
}
<commit_msg>spawn istanbul with node<commit_after>#!/usr/bin/env node
var foreground = require('foreground-child')
var NYC = require('../')
var path = require('path')
var sw = require('spawn-wrap')

if (process.env.NYC_CWD) {
  ;(new NYC()).wrap()

  // make sure we can run coverage on
  // our own index.js, I like turtles.
  var name = require.resolve('../')
  delete require.cache[name]

  sw.runMain()
} else {
  var yargs = require('yargs')
    .usage('$0 [command] [options]\n\nrun your tests with the nyc bin to instrument them with coverage')
    .command('report', 'run coverage report for .nyc_output', function (yargs) {
      yargs
        .usage('$0 report [options]')
        .option('r', {
          alias: 'reporter',
          describe: 'coverage reporter(s) to use',
          default: 'text'
        })
        .help('h')
        .alias('h', 'help')
        .example('$0 report --reporter=lcov', 'output an HTML lcov report to ./coverage')
    })
    .command('check-coverage', 'check whether coverage is within thresholds provided', function (yargs) {
      yargs
        .usage('$0 check-coverage [options]')
        .option('b', {
          alias: 'branches',
          default: 0,
          description: 'what % of branches must be covered?'
        })
        .option('f', {
          alias: 'functions',
          default: 0,
          description: 'what % of functions must be covered?'
        })
        .option('l', {
          alias: 'lines',
          default: 90,
          description: 'what % of lines must be covered?'
        })
        .option('s', {
          alias: 'statements',
          default: 0,
          description: 'what % of statements must be covered?'
        })
        .help('h')
        .alias('h', 'help')
        .example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided")
    })
    .option('r', {
      alias: 'reporter',
      describe: 'coverage reporter(s) to use',
      default: 'text'
    })
    .option('s', {
      alias: 'silent',
      default: false,
      type: 'boolean',
      describe: "don't output a report after tests finish running"
    })
    .help('h')
    .alias('h', 'help')
    .version(require('../package.json').version)
    .example('$0 npm test', 'instrument your tests with coverage')
    .example('$0 report --reporter=text-lcov', 'output lcov report after running your tests')
    .epilog('visit http://git.io/vTJJB for list of available reporters')
  var argv = yargs.argv

  if (~argv._.indexOf('report')) {
    // run a report.
    process.env.NYC_CWD = process.cwd()

    report(argv)
  } else if (~argv._.indexOf('check-coverage')) {
    foreground(
      process.execPath,
      [
        require.resolve('istanbul/lib/cli'),
        'check-coverage',
        '--lines=' + argv.lines,
        '--functions=' + argv.functions,
        '--branches=' + argv.branches,
        '--statements=' + argv.statements,
        path.resolve(process.cwd(), './.nyc_output/*.json')
      ]
    )
  } else if (argv._.length) {
    // wrap subprocesses and execute argv[1]
    var nyc = (new NYC())
    nyc.cleanup()

    sw([__filename], {
      NYC_CWD: process.cwd()
    })

    foreground(nyc.mungeArgs(argv), function (done) {
      if (!argv.silent) report(argv)
      return done()
    })
  } else {
    // I don't have a clue what you're doing.
    yargs.showHelp()
  }
}

function report (argv) {
  process.env.NYC_CWD = process.cwd()

  ;(new NYC({
    reporter: argv.reporter
  })).report()
}
<|endoftext|>