<commit_before>/*!
 * wq.app 0.6.2-dev - pages.js
 * Dynamically generate jQuery Mobile pages for specified URLs
 * (c) 2012-2014, S. Andrew Sheppard
 * http://wq.io/license
 */

define(['jquery', 'jquery.mobile', './router', './template', './console'],
function($, jqm, router, tmpl, console) {

// Exported module object
var pages = {
    'slug': '([^/?#]+)',
    'query': '(?:[?](.*))?(?:[#](.*))?$'
};

var _debug = false;

// Configuration
pages.init = function(baseurl, opts) {
    // Define baseurl (without trailing slash) if it is not /
    if (baseurl)
        pages.info.base_url = baseurl;

    if (!opts)
        return;

    // Define tmpl404 if there is not a template named '404'
    if (opts.tmpl404)
        _404 = opts.tmpl404;

    // Re-use rendered templates
    if (opts.injectOnce)
        _injectOnce = opts.injectOnce;

    // Debug
    if (opts.debug)
        _debug = opts.debug;
};

// Register URL patterns to override default JQM behavior and inject pages
// Callback fn should call pages.go() with desired template
pages.register = function(path, fn, obj, prevent) {
    var events = "bC";
    if (!fn) {
        fn = function(match, ui, params) {
            // Assume there is a template with the same name
            pages.go(path, path, params, ui);
        };
    }
    if (prevent === undefined) {
        prevent = function(match, ui, params) {
            /* jshint unused: false */
            // By default, prevent default changePage behavior
            // (unless this is a form post and is not being handled by app.js)
            if (ui && ui.options && ui.options.data && ui.options.fromPage) {
                var $form = ui.options.fromPage.find('form');
                if ($form.data('json') !== undefined && !$form.data('json'))
                    return false;
            }
            return true;
        };
    }
    var callback = function(match, ui, params, hash, evt, $page) {
        var curpath = jqm.activePage && jqm.activePage.jqmData('url');

        // Capture URLs only, not completed pages
        if (typeof ui.toPage !== "string")
            return;

        // Don't handle urls that app.js specifically marked for server loading
        if (ui.options && ui.options.wqSkip)
            return;

        // Avoid interfering with hash updates when popups open & close
        if (curpath == match[0] || curpath + '#' + hash == match[0])
            return;

        // Prevent default changePage behavior?
        if (typeof prevent === 'function' && prevent(match, ui, params))
            evt.preventDefault();
        else if (typeof prevent !== 'function' && prevent)
            evt.preventDefault();

        fn = (typeof fn == "string" ? obj[fn] : fn);
        fn(match, ui, params, hash, evt, $page);
    };
    pages.addRoute(path, events, callback);
};

// Wrapper for router.add - adds URL base and parameter regex to path
pages.addRoute = function(path, events, fn, obj) {
    var rt = {};
    path = path.replace(/<slug>/g, pages.slug);
    var url = '^' +  pages.info.base_url + '/' + path + pages.query;
    var callback = function(etype, match, ui, page, evt) {
        var hash = match.pop();
        var params = router.getParams(match.pop());
        fn = (typeof fn == "string" ? obj[fn] : fn);
        fn(match, ui, params, hash, evt, $(page));
    };
    rt[url] = {
        'events': events,
        'handler': callback,
        'step': 'all'
    };
    router.add(rt);
};

// Render page and inject it into DOM (replace existing page if it exists)
pages.inject = function(path, template, context, pageid) {
    _updateInfo(path);
    var html  = tmpl.render(template, context);
    if (!html.match(/<div/))
        throw "No content found in template '" + template + "'!";
    var title = html.split(/<\/?title>/)[1];
    var body  = html.split(/<\/?body[^>?]*>/)[1];
    if (body)
        html = body;
    var $page = $(html.trim());
    var role = $page.jqmData('role');
    var url   = pages.info.full_path;
    var $oldpage;
    if (pageid) {
        if (pageid === true)
            pageid = template + '-page';
        $oldpage = $('#' + pageid);
    } else {
        $oldpage = $(":jqmData(url='" + url + "')");
    }
    if (role == 'popup' || role == 'panel') {
        $page.appendTo(jqm.activePage[0]);
        if (role == 'popup')
            $page.popup();
        else
            $page.panel();
        $page.trigger('create');
    } else if ($oldpage.length) {
        $oldpage.jqmData('title', title);
        if (pageid)
            $oldpage.jqmData('url', url);

        var header = ":jqmData(role='header')";
        var $header = $page.find(header).find("h1,h2,h3");
        var $oldheader = $oldpage.find(header).find("h1,h2,h3");
        $oldheader.html($header.html());

        var content = ":jqmData(role='content')";
        var $content = $page.find(content);
        var $oldcontent = $oldpage.find(content);
        $oldcontent.html($content.html());

        $oldpage.trigger('create');
        $page = $oldpage;
    } else {
        $page.attr("data-" + jqm.ns + "url", url);
        $page.attr("data-" + jqm.ns + "title", title);
        if (pageid)
            $page.attr('id', pageid);
        $page.appendTo(jqm.pageContainer);
        $page.page();
    }
    return $page;
};

// Render template only once
pages.injectOnce = function(path, template, context, id) {
    if(!id)
        id = template + "-page";
    var $page = $('#' + id);
    if (!$page.length) {
        // Initial render, use context if available
        $page = pages.inject(path, template, context);
        $page.attr("id", id);
    } else {
        // Template was already rendered; ignore context but update URL
        // - it is up to the caller to update the DOM
        _updateInfo(path);
        $page.attr("data-" + jqm.ns + "url", pages.info.full_path);
        $page.jqmData("url", pages.info.full_path);
    }
    return $page;
};

// Inject and display page
pages.go = function(path, template, context, ui, once, pageid) {
    if (_debug) {
        console.log(
            "Rendering " + pages.info.base_url + '/' + path +
            " with template '" + template +
            "' and context:"
        );
        console.log(context);
        pages.info.context = context;
    }
    var $page, role, options;
    once = once || _injectOnce;
    if (once)
        // Only render the template once
        $page = pages.injectOnce(path, template, context, pageid);
    else
        // Default: render the template every time the page is loaded
        $page = pages.inject(path, template, context, pageid);

    role = $page.jqmData('role');
    if (role == 'page') {
        options = ui && ui.options || {};
        options._jqmrouter_bC = true;
        if (once || _injectOnce)
            options.allowSamePageTransition = true;
        jqm.changePage($page, options);
    } else if (role == 'popup') {
        options = {};
        if (ui && ui.options) {
            options.transition = ui.options.transition;
            options.positionTo = $page.jqmData('position-to');
            var link = ui.options.link;
            if (link) {
                if (link.jqmData('position-to'))
                    options.positionTo = link.jqmData('position-to');
                // 'origin' won't work since we're opening the popup manually
                if (!options.positionTo || options.positionTo == 'origin')
                    options.positionTo = link[0];
                // Remove link highlight *after* popup is closed
                $page.bind('popupafterclose.resetlink', function() {
                    link.removeClass('ui-btn-active');
                    $(this).unbind('popupafterclose.resetlink');
                });
            }
        }
        $page.popup('open', options);
    } else if (role == 'panel') {
        $page.panel('open');
    }
    return $page;
};

// Simple 404 page helper
pages.notFound = function(url, ui) {
    var context  = {'url': url};
    pages.go(url, _404, context, ui);
};

// Context variable (accessible in templates via pages_info)
pages.info = {
    'base_url': ""
};

function _updateInfo(path) {
    pages.info.prev_path = pages.info.path;
    pages.info.path = path;
    pages.info.full_path = pages.info.base_url + "/" + path;
    tmpl.setDefault('pages_info', pages.info);
}

var _404  = "404";
var _injectOnce = false;

return pages;

});
<commit_msg>allow same page transition in pages.register fn<commit_after>/*!
 * wq.app 0.6.2-dev - pages.js
 * Dynamically generate jQuery Mobile pages for specified URLs
 * (c) 2012-2014, S. Andrew Sheppard
 * http://wq.io/license
 */

define(['jquery', 'jquery.mobile', './router', './template', './console'],
function($, jqm, router, tmpl, console) {

// Exported module object
var pages = {
    'slug': '([^/?#]+)',
    'query': '(?:[?](.*))?(?:[#](.*))?$'
};

var _debug = false;

// Configuration
pages.init = function(baseurl, opts) {
    // Define baseurl (without trailing slash) if it is not /
    if (baseurl)
        pages.info.base_url = baseurl;

    if (!opts)
        return;

    // Define tmpl404 if there is not a template named '404'
    if (opts.tmpl404)
        _404 = opts.tmpl404;

    // Re-use rendered templates
    if (opts.injectOnce)
        _injectOnce = opts.injectOnce;

    // Debug
    if (opts.debug)
        _debug = opts.debug;
};

// Register URL patterns to override default JQM behavior and inject pages
// Callback fn should call pages.go() with desired template
pages.register = function(path, fn, obj, prevent) {
    var events = "bC";
    if (!fn) {
        fn = function(match, ui, params) {
            // Assume there is a template with the same name
            pages.go(path, path, params, ui);
        };
    }
    if (prevent === undefined) {
        prevent = function(match, ui, params) {
            /* jshint unused: false */
            // By default, prevent default changePage behavior
            // (unless this is a form post and is not being handled by app.js)
            if (ui && ui.options && ui.options.data && ui.options.fromPage) {
                var $form = ui.options.fromPage.find('form');
                if ($form.data('json') !== undefined && !$form.data('json'))
                    return false;
            }
            return true;
        };
    }
    var callback = function(match, ui, params, hash, evt, $page) {
        var curpath = jqm.activePage && jqm.activePage.jqmData('url');

        // Capture URLs only, not completed pages
        if (typeof ui.toPage !== "string")
            return;

        // Don't handle urls that app.js specifically marked for server loading
        if (ui.options && ui.options.wqSkip)
            return;

        // Avoid interfering with hash updates when popups open & close
        if ((curpath == match[0] || curpath + '#' + hash == match[0]) &&
               !ui.options.allowSamePageTransition)
            return;

        // Prevent default changePage behavior?
        if (typeof prevent === 'function' && prevent(match, ui, params))
            evt.preventDefault();
        else if (typeof prevent !== 'function' && prevent)
            evt.preventDefault();

        fn = (typeof fn == "string" ? obj[fn] : fn);
        fn(match, ui, params, hash, evt, $page);
    };
    pages.addRoute(path, events, callback);
};

// Wrapper for router.add - adds URL base and parameter regex to path
pages.addRoute = function(path, events, fn, obj) {
    var rt = {};
    path = path.replace(/<slug>/g, pages.slug);
    var url = '^' +  pages.info.base_url + '/' + path + pages.query;
    var callback = function(etype, match, ui, page, evt) {
        var hash = match.pop();
        var params = router.getParams(match.pop());
        fn = (typeof fn == "string" ? obj[fn] : fn);
        fn(match, ui, params, hash, evt, $(page));
    };
    rt[url] = {
        'events': events,
        'handler': callback,
        'step': 'all'
    };
    router.add(rt);
};

// Render page and inject it into DOM (replace existing page if it exists)
pages.inject = function(path, template, context, pageid) {
    _updateInfo(path);
    var html  = tmpl.render(template, context);
    if (!html.match(/<div/))
        throw "No content found in template '" + template + "'!";
    var title = html.split(/<\/?title>/)[1];
    var body  = html.split(/<\/?body[^>?]*>/)[1];
    if (body)
        html = body;
    var $page = $(html.trim());
    var role = $page.jqmData('role');
    var url   = pages.info.full_path;
    var $oldpage;
    if (pageid) {
        if (pageid === true)
            pageid = template + '-page';
        $oldpage = $('#' + pageid);
    } else {
        $oldpage = $(":jqmData(url='" + url + "')");
    }
    if (role == 'popup' || role == 'panel') {
        $page.appendTo(jqm.activePage[0]);
        if (role == 'popup')
            $page.popup();
        else
            $page.panel();
        $page.trigger('create');
    } else if ($oldpage.length) {
        $oldpage.jqmData('title', title);
        if (pageid)
            $oldpage.jqmData('url', url);

        var header = ":jqmData(role='header')";
        var $header = $page.find(header).find("h1,h2,h3");
        var $oldheader = $oldpage.find(header).find("h1,h2,h3");
        $oldheader.html($header.html());

        var content = ":jqmData(role='content')";
        var $content = $page.find(content);
        var $oldcontent = $oldpage.find(content);
        $oldcontent.html($content.html());

        $oldpage.trigger('create');
        $page = $oldpage;
    } else {
        $page.attr("data-" + jqm.ns + "url", url);
        $page.attr("data-" + jqm.ns + "title", title);
        if (pageid)
            $page.attr('id', pageid);
        $page.appendTo(jqm.pageContainer);
        $page.page();
    }
    return $page;
};

// Render template only once
pages.injectOnce = function(path, template, context, id) {
    if(!id)
        id = template + "-page";
    var $page = $('#' + id);
    if (!$page.length) {
        // Initial render, use context if available
        $page = pages.inject(path, template, context);
        $page.attr("id", id);
    } else {
        // Template was already rendered; ignore context but update URL
        // - it is up to the caller to update the DOM
        _updateInfo(path);
        $page.attr("data-" + jqm.ns + "url", pages.info.full_path);
        $page.jqmData("url", pages.info.full_path);
    }
    return $page;
};

// Inject and display page
pages.go = function(path, template, context, ui, once, pageid) {
    if (_debug) {
        console.log(
            "Rendering " + pages.info.base_url + '/' + path +
            " with template '" + template +
            "' and context:"
        );
        console.log(context);
        pages.info.context = context;
    }
    var $page, role, options;
    once = once || _injectOnce;
    if (once)
        // Only render the template once
        $page = pages.injectOnce(path, template, context, pageid);
    else
        // Default: render the template every time the page is loaded
        $page = pages.inject(path, template, context, pageid);

    role = $page.jqmData('role');
    if (role == 'page') {
        options = ui && ui.options || {};
        options._jqmrouter_bC = true;
        if (once || _injectOnce)
            options.allowSamePageTransition = true;
        jqm.changePage($page, options);
    } else if (role == 'popup') {
        options = {};
        if (ui && ui.options) {
            options.transition = ui.options.transition;
            options.positionTo = $page.jqmData('position-to');
            var link = ui.options.link;
            if (link) {
                if (link.jqmData('position-to'))
                    options.positionTo = link.jqmData('position-to');
                // 'origin' won't work since we're opening the popup manually
                if (!options.positionTo || options.positionTo == 'origin')
                    options.positionTo = link[0];
                // Remove link highlight *after* popup is closed
                $page.bind('popupafterclose.resetlink', function() {
                    link.removeClass('ui-btn-active');
                    $(this).unbind('popupafterclose.resetlink');
                });
            }
        }
        $page.popup('open', options);
    } else if (role == 'panel') {
        $page.panel('open');
    }
    return $page;
};

// Simple 404 page helper
pages.notFound = function(url, ui) {
    var context  = {'url': url};
    pages.go(url, _404, context, ui);
};

// Context variable (accessible in templates via pages_info)
pages.info = {
    'base_url': ""
};

function _updateInfo(path) {
    pages.info.prev_path = pages.info.path;
    pages.info.path = path;
    pages.info.full_path = pages.info.base_url + "/" + path;
    tmpl.setDefault('pages_info', pages.info);
}

var _404  = "404";
var _injectOnce = false;

return pages;

});
<|endoftext|>


<commit_before>var Promise = require('promise');

var dataParser = require("./dataParser.js");
var schemaParser = require("./schemaParser.js");
var validator = require("./validator.js");
var shapeFinder = require("./shapeFinder.js");

var shexjs = require("shex");
var n3 = require("n3");

var DefaultBase = "";

function Validator(schemaText, dataText, callbacks, options) {
    this.callbacks = callbacks;
    this.options = options;

    this.updateSchema(location.origin + '/schema.shex', schemaText);
    this.updateData(dataText);

    // console.log("validator has instantiated");
    // console.log("callbacks:\n",callbacks);
    // console.log("options:\n",options);

}

Validator.prototype = {
    updateSchema: function (base, schemaText) {
        this.schema = parseSchema(base ,schemaText);
        // this.schema = schemaParser.parseSchema(schemaText);
        this.schema.done(this.callbacks.schemaParsed, this.callbacks.schemaParseError);
        // var _schema = shexjs.Parser.construct(DefaultBase).parse(schemaText);
        // console.log('UPDATING THE FUCKIGN Schema',this.schema, _schema);
    },
    updateData: function (dataText) {
        this.data = parseData(dataText);
        // this.data = dataParser.parseData(dataText);
        this.data.done(this.callbacks.dataParsed, this.callbacks.dataParseError);
    },
    findShapes: function () {
        var _this = this;
        return Promise.all([this.schema, this.data]).then(function (a) {
            console.log('showing shapes');
            console.log(this.schema);
            console.log(this.data);
        });
    },
    validate: function(startingNodes) {
        var _this = this;
        console.log('validate function',_this);
        return Promise.all([this.schema, this.data, this.options]).then(function (a) {
            console.log(a);
            return validator.validate(
                a[0].schema,                       // Schema
                a[0].resolver,
                startingNodes,      // Starting Node
                a[1].db,                       // db
                a[1].resolver,
                _this.options.closedShapes,
                _this.callbacks.validationResult);
        });
    }
};

module.exports.Validator = Validator;

function parseData(dataText){
    return new Promise(function (resolve, reject) {

        var db = n3.Store();
        n3.Parser({documentIRI: DefaultBase, format: "text/turtle"}).parse(dataText, function (error, triple, prefixes) {
            if (error) {
                // throw Error("error parsing " + data + ": " + error);
                reject(parseN3Error(error));
            } else if (triple) {
                db.addTriple(triple)
            // console.log(triple.subject, triple.predicate, triple.object, '.');
            } else {
                resolve({db: db, triples:db.getTriples()});
            }
        });

    });
}

function parseSchema(base, schemaText) {
    return new Promise(function (resolve, reject) {
        var schema;
        try {
            schema = shexjs.Parser.construct(base).parse(schemaText);
        }
        catch (e) {
            reject(e);
        }
        resolve({schema: schema, shapes: schema.shapes});

    });
};
<commit_msg>added debug<commit_after>var Promise = require('promise');

var dataParser = require("./dataParser.js");
var schemaParser = require("./schemaParser.js");
var validator = require("./validator.js");
var shapeFinder = require("./shapeFinder.js");

var shexjs = require("shex");
var n3 = require("n3");

var DefaultBase = "";

function Validator(schemaText, dataText, callbacks, options) {
    this.callbacks = callbacks;
    this.options = options;

    this.updateSchema(location.origin + '/schema.shex', schemaText);
    this.updateData(dataText);

    // console.log("validator has instantiated");
    // console.log("callbacks:\n",callbacks);
    // console.log("options:\n",options);

}

Validator.prototype = {
    updateSchema: function (base, schemaText) {
        this.schema = parseSchema(base ,schemaText);
        // this.schema = schemaParser.parseSchema(schemaText);
        this.schema.done(this.callbacks.schemaParsed, this.callbacks.schemaParseError);
        // var _schema = shexjs.Parser.construct(DefaultBase).parse(schemaText);
        // console.log('UPDATING THE FUCKIGN Schema',this.schema, _schema);
    },
    updateData: function (dataText) {
        this.data = parseData(dataText);
        // this.data = dataParser.parseData(dataText);
        this.data.done(this.callbacks.dataParsed, this.callbacks.dataParseError);
    },
    findShapes: function () {
        var _this = this;
        return Promise.all([this.schema, this.data]).then(function (a) {
            console.log('showing shapes');
            console.log(this.schema);
            console.log(this.data);
        });
    },
    validate: function(startingNodes) {
        var _this = this;
        console.log('validate function',_this);
        return Promise.all([this.schema, this.data, this.options]).then(function (a) {
            console.log(a);
            var node = Object.keys(a[2].resourceShapeMap)[0];
            var result = shexjs.Validator.construct(a[0].schema).validate(a[1].db, node, a[2].resourceShapeMap[node]);
            console.log(result);
            return validator.validate(
                a[0].schema,                       // Schema
                a[0].resolver,
                startingNodes,      // Starting Node
                a[1].db,                       // db
                a[1].resolver,
                _this.options.closedShapes,
                _this.callbacks.validationResult);
        });
    }
};

module.exports.Validator = Validator;

function parseData(dataText){
    return new Promise(function (resolve, reject) {

        var db = n3.Store();
        n3.Parser({documentIRI: DefaultBase, format: "text/turtle"}).parse(dataText, function (error, triple, prefixes) {
            if (error) {
                // throw Error("error parsing " + data + ": " + error);
                reject(parseN3Error(error));
            } else if (triple) {
                db.addTriple(triple)
            // console.log(triple.subject, triple.predicate, triple.object, '.');
            } else {
                resolve({db: db, triples:db.getTriples()});
            }
        });

    });
}

function parseSchema(base, schemaText) {
    return new Promise(function (resolve, reject) {
        var schema;
        try {
            schema = shexjs.Parser.construct(base).parse(schemaText);
        }
        catch (e) {
            reject(e);
        }
        resolve({schema: schema, shapes: schema.shapes});

    });
};
<|endoftext|>


<commit_before>/*

xtal.js Support for Crystallographic Information Format.

Exports:
	Reader
	Block
	Parser

*/
var xtal = (function(module) {return module})(xtal||{});
xtal.cif = (function(module) {

  // Regular expressions for matching commands
	// and data types.
  var re_statement = /^\s*(data)?(loop)?(_[\.\w]+)?(save_)?/;
  var re_tag = /^\s*([^\s]+)\s+(.*)/;
  var re_block = /data_(\w+)/;
  var re_float = /^[+-]?(\d+)?(\.\d+)?$/;
	var re_whitespace = /\s/;

  function checknumber(v) {
		/* Try to coerce a string into a Number */
  	if (re_float.test(v)) {
  		return Number(v);
  	}
  	return v
  }

	function trim(key) {
		return key.trim().toLowerCase();
	}

  /*********************************************/
	function input_generator(buffer, sep) {
		/* Input generator */
		// Would be nice if ECMAScript 6 iterators
		// were widely available.
    this.buffer = buffer || "";
    this.sep = sep || "\n";
    this.i = 0;
    this.j = this.buffer.indexOf(this.sep, this.i);
    this.end = this.buffer.lastIndexOf(this.sep);
	}
	input_generator.prototype.add = function(input) {
		this.buffer = this.remainder() + input;
		this.i = 0;
    this.j = this.buffer.indexOf(this.sep, this.i);
    this.end = this.buffer.lastIndexOf(this.sep);
	}
	input_generator.prototype.next = function() {
    if (this.j == -1) {return null}
    var line = this.buffer.substring(this.i, this.j);
    this.i = this.j + 1;
    this.j = this.buffer.indexOf(this.sep, this.i);
    return line
	}
	input_generator.prototype.remainder = function() {
		return this.buffer.substring(this.i, this.end);
	}

  /*********************************************/
  function Reader() {
    /* mmCIF Reader */
		// A dictionary of blocks.
    this.blocks = {};
  }
  Reader.prototype.add_block = function(block) {
    this.blocks[block.name] = block;
  }
  Reader.prototype.get_block = function(name) {
    return this.blocks[trim(name)];
  }
  Reader.prototype.first_block = function(name) {
    return this.blocks[Object.keys(this.blocks)[0]];
  }
  Reader.prototype.load = function(url, callback) {
    /* Load data */
    // Create a new parser.
  	var self = this;
  	var parser = new Parser(function(b){self.add_block(b)});
  	function listen(e) {
			parser.parse_chunk(this.response);
      callback(self);
  	}
    // Request file.
  	var req = new XMLHttpRequest();
  	req.open("GET", url, true);
		// req.onprogress is supported for streaming
		//	if responseType = 'moz-chunked-text', but this
		//	is a Firefox only extension.
		// req.onload will fire when the response is complete.
  	req.onload = listen;
  	req.send();
  }
	Reader.prototype.parse = function(data) {
		var self = this;
		var parser = new Parser(function(b){self.add_block(b)});
		parser.parse_chunk(data);
	}
  /*********************************************/
  function Block(name) {
    /* mmCIF Data Block */
    this.name = trim(name);
    this.data = {};
  }
  Block.prototype.get = function(key) {
		/* Get a mmCIF value for key */
    return this.data[key]
  }
  Block.prototype.loop_dict = function(group) {
		var keys = [];
		var keys_split = []; // save some splits
		var ret = [];
		for (var i in this.data) {
			if (i.split(".")[0]==group) {
				keys.push(i);
				keys_split.push(i.split(".")[1]);
			}
		}
		var count = this.data[keys[0]].length;
		for (var i=0;i<count;i++) {
			var d = {}
			for (var j=0;j<keys.length;j++) {
				d[keys_split[j]] = this.data[keys[j]][i];
			}
			ret.push(d);
		}
		return ret
  }
  Block.prototype.loop_list = function(key) {
		return {}
  }
  Block.prototype.set = function(key, value) {
		/* Set a mmCIF key */
  	this.data[trim(key)] = value;
  }
	Block.prototype.append = function(key, value) {
		this.data[key].push(value);
	}
  Block.prototype.append_row = function(keys, values) {
		/* Add a row to a loop. keys and values must be same length. */
  	if (keys.length != values.length) {
  		return
  	}
  	for (var i=0;i<keys.length;i++) {
  		this.data[keys[i]].push(values[i]);
  	}
  }
  Block.prototype.add_loop_key = function(key) {
		/* Initialize a key in a loop */
  	this.data[key] = [];
  }

  /*********************************************/
  function Parser(block_callback) {
  	/* mmCIF Parser

  	Processes a stream of mmCIF statements. Each statement can update the
		state, and/or return a function for handling the next line of input.
		(TODO: better explanation.)

  	I may add support for events, where listeners may subscribe to changes
  	in the CIF model as they are parsed.

  	*/
    // The input buffer line generator.
  	this.gen = new input_generator("");
    // The new block callback
    this.block_callback = block_callback || function(block) {};
    // The current mmCIF Block
  	this.block = null;
    // The current loop keys
  	this.loop = null;
		this.loop_i = 0;
    // The current step
  	this.step = this.step_init;
  }
  Parser.prototype.parse_chunk = function(input) {
  	// Process the lines in the input data.
		this.gen.add(input);
		var line = this.gen.next();
    console.log(line);
		while (line != null) {
      if (line[0] != '#') {
        console.log(line);
			  this.step = this.step(line);
      }
			line = this.gen.next();
		}
  }
  /***** Steps *****/
  Parser.prototype.step_init = function(line) {
  	// Initial step, or after returning from a loop or block.
  	var m = re_statement.exec(line);
  	if (m[1]) {
  		// Start a block.
  		return this.step_block(line)
  	} else if (m[2]) {
  		// Start a loop.
  		return this.step_loop(line)
  	} else if (m[3]) {
  		// Start a tag.
  		return this.step_tag(line)
  	}
  	// Default:
  	// Next step: stay in initial state.
  	return this.step_init
  }
  Parser.prototype.step_block = function(line) {
    // Create a new block.
  	var name = re_block.exec(line)[1];
    this.block = new Block(name);
    // Call block_callback whenever a block is created.
    this.block_callback(this.block);
  	// Next step: step_block is always a single line command
  	return this.step_init
  }
  Parser.prototype.step_loop = function(line) {
  	// Initialize the loop
  	this.loop = [];
		this.loop_i = 0;
  	// Next step: add loop keys.
  	return this.step_loop_keys
  }
  Parser.prototype.step_loop_keys = function(line) {
  	var m = re_statement.exec(line);
  	if (m[3]) {
  		// Add the key to the loop.
      var key = trim(m[3]);
      this.loop.push(key);
      // Initialize the array in the block.
  		this.block.add_loop_key(key);
  		// Next step: stay in loop keys.
  		return this.step_loop_keys;
  	} else {
			// No more keys...
  		// Next step: start appending loop data.
  		return this.step_loop_append(line);
  	}
  }
  Parser.prototype.step_loop_append = function(line) {
  	var m = re_statement.exec(line);
  	if (m[1]||m[2]||m[3]||m[4]) {
  		// Next step: a statement was found, return to initial step.
  		return this.step_init(line)
  	}
		var values = this.parse_values(line);
		var loop_length = this.loop.length;
		for (var i=0; i<values.length; i++) {
			this.block.append(this.loop[this.loop_i % loop_length], values[i]);
			this.loop_i += 1;
		}
  	return this.step_loop_append
  }
  Parser.prototype.step_tag = function(line) {
  	var m = re_tag.exec(line);
  	if (!m[0]) {
  		// Next step: Return to init
  		return this.step_init(line)
  	}
  	// Set the current tag
  	var tag = m[1];
  	var value = this.parse_values(m[2]);
		// if (m[2] == "") {
		// 	// Next step: multi-line tag text
		// 	value = this.parse_values(this.gen.next());
		// 	// return this.step_tag_text
		// }
  	this.block.set(tag, value[0]);
  	return this.step_init
  }
  Parser.prototype.step_save = function(line) {
		var m = re_statement.exec(line);
		if (m[4]) {
			// Next step: found end save_; break
			return this.step_init
		}
  	return this.step_save
  }
  Parser.prototype.parse_values = function(line) {
    /* Process a string containing values.
    These may or may not be quoted with single or double quotes.

    Example:
      abc "with space" -1.2 +4.5
      ->
      ["abc", "with space", -1.2, 4.5]
    1. Find the first quote or non-whitespace character
    2. If quoted, copy until next quote.
    3. If not quoted, copy until next whitespace.
    4. Skip to end of value. Repeat until end of line.
    */
  	var p = []; // return values
  	var char = null;  // current character
  	var i = 0; // current index
  	var j = line.length; // end index
		if (line[0] == ";") {
			return [this.parse_value_text(line.substring(1, j))]
		}

  	while (i < (j-1)) {
  		char = line[i];
  		if (re_whitespace.test(char)) {
  			// unquoted leading whitespace; skip to next char.
  			i++;
			} else if (char == "#") {
				// Unquoted hash -- rest of line is a comment; return
				i = j;
  		} else if (char == '"' || char == "'") {
  			// append value between quotes
  			n = line.indexOf(char+" ", i+1); // find next quote after quote
  			if (n<1){n=j} // ...until end of line if not found
  			p.push(checknumber(line.substring(i+1, n))); // find string between
  			i = n + 1; // advance past end quote
  		} else {
  			// append until next whitespace
  			n = line.indexOf(" ", i+1);
  			if (n<1){n=j}
  			var test = checknumber(line.substring(i, n));
  			p.push(test);
  			i = n; // advance past next whitespace
  		}
  	}
  	return p
  }
	Parser.prototype.parse_value_text = function(line) {
		var text = "";
		// Feed forward multi-line text comments...
		while (line != null) {
			text += line;
			//text += " ";
			line = this.gen.next();
			// until we find a solitary ";"
			if (line == ";") {
				break
			}
		}
		return text
	}

  // Exports
  return {
    'Parser':Parser,
    'Block':Block,
    'Reader':Reader
  }
})(xtal);
<commit_msg>Added some convenience methods<commit_after>/*

xtal.js Support for Crystallographic Information Format.

Exports:
	Reader
	Block
	Parser

*/
var xtal = (function(module) {return module})(xtal||{});
xtal.cif = (function(module) {

  // Regular expressions for matching commands
	// and data types.
  var re_statement = /^\s*(data)?(loop)?(_[\.\w]+)?(save_)?/;
  var re_tag = /^\s*([^\s]+)\s+(.*)/;
  var re_block = /data_(\w+)/;
  var re_float = /^[+-]?(\d+)?(\.\d+)?$/;
	var re_whitespace = /\s/;

  function checknumber(v) {
		/* Try to coerce a string into a Number */
  	if (re_float.test(v)) {
  		return Number(v);
  	}
  	return v
  }

	function trim(key) {
		return key.trim().toLowerCase();
	}

  /*********************************************/
	function input_generator(buffer, sep) {
		/* Input generator */
		// Would be nice if ECMAScript 6 iterators
		// were widely available.
    this.buffer = buffer || "";
    this.sep = sep || "\n";
    this.i = 0;
    this.j = this.buffer.indexOf(this.sep, this.i);
    this.end = this.buffer.lastIndexOf(this.sep);
	}
	input_generator.prototype.add = function(input) {
		this.buffer = this.remainder() + input;
		this.i = 0;
    this.j = this.buffer.indexOf(this.sep, this.i);
    this.end = this.buffer.lastIndexOf(this.sep);
	}
	input_generator.prototype.next = function() {
    if (this.j == -1) {return null}
    var line = this.buffer.substring(this.i, this.j);
    this.i = this.j + 1;
    this.j = this.buffer.indexOf(this.sep, this.i);
    return line
	}
	input_generator.prototype.remainder = function() {
		return this.buffer.substring(this.i, this.end);
	}

  /*********************************************/
  function Reader() {
    /* mmCIF Reader */
		// A dictionary of blocks.
    this.blocks = {};
  }
  Reader.prototype.add_block = function(block) {
    this.blocks[block.name] = block;
  }
  Reader.prototype.get_block = function(name) {
    return this.blocks[trim(name)];
  }
  Reader.prototype.first_block = function(name) {
    return this.blocks[Object.keys(this.blocks)[0]];
  }
  Reader.prototype.load = function(url, callback) {
    /* Load data */
    // Create a new parser.
  	var self = this;
  	var parser = new Parser(function(b){self.add_block(b)});
  	function listen(e) {
			parser.parse_chunk(this.response);
      callback(self);
  	}
    // Request file.
  	var req = new XMLHttpRequest();
  	req.open("GET", url, true);
		// req.onprogress is supported for streaming
		//	if responseType = 'moz-chunked-text', but this
		//	is a Firefox only extension.
		// req.onload will fire when the response is complete.
  	req.onload = listen;
  	req.send();
  }
	Reader.prototype.parse = function(data) {
		var self = this;
		var parser = new Parser(function(b){self.add_block(b)});
		parser.parse_chunk(data);
	}
  /*********************************************/
  function Block(name) {
    /* mmCIF Data Block */
    this.name = trim(name);
    this.data = {};
  }
  Block.prototype.get = function(key) {
		/* Get a mmCIF value for key */
    return this.data[key]
  }
  Block.prototype.loop_dict = function(group) {
		var keys = [];
		var keys_split = []; // save some splits
		var ret = [];
		for (var i in this.data) {
			if (i.split(".")[0]==group) {
				keys.push(i);
				keys_split.push(i.split(".")[1]);
			}
		}
		var count = this.data[keys[0]].length;
		for (var i=0;i<count;i++) {
			var d = {}
			for (var j=0;j<keys.length;j++) {
				d[keys_split[j]] = this.data[keys[j]][i];
			}
			ret.push(d);
		}
		return ret
  }
  Block.prototype.loop_list = function(key) {
		return {}
  }
  Block.prototype.set = function(key, value) {
		/* Set a mmCIF key */
  	this.data[trim(key)] = value;
  }
	Block.prototype.append = function(key, value) {
		this.data[key].push(value);
	}
  Block.prototype.append_row = function(keys, values) {
		/* Add a row to a loop. keys and values must be same length. */
  	if (keys.length != values.length) {
  		return
  	}
  	for (var i=0;i<keys.length;i++) {
  		this.data[keys[i]].push(values[i]);
  	}
  }
  Block.prototype.add_loop_key = function(key) {
		/* Initialize a key in a loop */
  	this.data[key] = [];
  }
	Block.prototype.groups = function() {
		var ret = {};
		for (var key in this.data) {
			key = key.split(".")[0];
			ret[key] = true;
		}
		return Object.keys(ret)
	}
	Block.prototype.group_keys = function(group) {
		return this.keys_filter(group)
	}
	Block.prototype.keys_filter = function(group) {
		return this.keys().filter(function(i){return i.lastIndexOf(group,0)===0});
	}
	Block.prototype.keys = function() {
		return Object.keys(this.data);
	}

  /*********************************************/
  function Parser(block_callback) {
  	/* mmCIF Parser

  	Processes a stream of mmCIF statements. Each statement can update the
		state, and/or return a function for handling the next line of input.
		(TODO: better explanation.)

  	I may add support for events, where listeners may subscribe to changes
  	in the CIF model as they are parsed.

  	*/
    // The input buffer line generator.
  	this.gen = new input_generator("");
    // The new block callback
    this.block_callback = block_callback || function(block) {};
    // The current mmCIF Block
  	this.block = null;
    // The current loop keys
  	this.loop = null;
		this.loop_i = 0;
    // The current step
  	this.step = this.step_init;
  }
  Parser.prototype.parse_chunk = function(input) {
  	// Process the lines in the input data.
		this.gen.add(input);
		var line = this.gen.next();
    // console.log(line);
		while (line != null) {
      if (line[0] != '#') {
        // console.log(line);
			  this.step = this.step(line);
      }
			line = this.gen.next();
		}
  }
  /***** Steps *****/
  Parser.prototype.step_init = function(line) {
  	// Initial step, or after returning from a loop or block.
  	var m = re_statement.exec(line);
  	if (m[1]) {
  		// Start a block.
  		return this.step_block(line)
  	} else if (m[2]) {
  		// Start a loop.
  		return this.step_loop(line)
  	} else if (m[3]) {
  		// Start a tag.
  		return this.step_tag(line)
  	}
  	// Default:
  	// Next step: stay in initial state.
  	return this.step_init
  }
  Parser.prototype.step_block = function(line) {
    // Create a new block.
  	var name = re_block.exec(line)[1];
    this.block = new Block(name);
    // Call block_callback whenever a block is created.
    this.block_callback(this.block);
  	// Next step: step_block is always a single line command
  	return this.step_init
  }
  Parser.prototype.step_loop = function(line) {
  	// Initialize the loop
  	this.loop = [];
		this.loop_i = 0;
  	// Next step: add loop keys.
  	return this.step_loop_keys
  }
  Parser.prototype.step_loop_keys = function(line) {
  	var m = re_statement.exec(line);
  	if (m[3]) {
  		// Add the key to the loop.
      var key = trim(m[3]);
      this.loop.push(key);
      // Initialize the array in the block.
  		this.block.add_loop_key(key);
  		// Next step: stay in loop keys.
  		return this.step_loop_keys;
  	} else {
			// No more keys...
  		// Next step: start appending loop data.
  		return this.step_loop_append(line);
  	}
  }
  Parser.prototype.step_loop_append = function(line) {
  	var m = re_statement.exec(line);
  	if (m[1]||m[2]||m[3]||m[4]) {
  		// Next step: a statement was found, return to initial step.
  		return this.step_init(line)
  	}
		var values = this.parse_values(line);
		var loop_length = this.loop.length;
		for (var i=0; i<values.length; i++) {
			this.block.append(this.loop[this.loop_i % loop_length], values[i]);
			this.loop_i += 1;
		}
  	return this.step_loop_append
  }
  Parser.prototype.step_tag = function(line) {
  	var m = re_tag.exec(line);
  	if (!m[0]) {
  		// Next step: Return to init
  		return this.step_init(line)
  	}
  	// Set the current tag
  	var tag = m[1];
  	var value = this.parse_values(m[2]);
		// if (m[2] == "") {
		// 	// Next step: multi-line tag text
		// 	value = this.parse_values(this.gen.next());
		// 	// return this.step_tag_text
		// }
  	this.block.set(tag, value[0]);
  	return this.step_init
  }
  Parser.prototype.step_save = function(line) {
		var m = re_statement.exec(line);
		if (m[4]) {
			// Next step: found end save_; break
			return this.step_init
		}
  	return this.step_save
  }
  Parser.prototype.parse_values = function(line) {
    /* Process a string containing values.
    These may or may not be quoted with single or double quotes.

    Example:
      abc "with space" -1.2 +4.5
      ->
      ["abc", "with space", -1.2, 4.5]
    1. Find the first quote or non-whitespace character
    2. If quoted, copy until next quote.
    3. If not quoted, copy until next whitespace.
    4. Skip to end of value. Repeat until end of line.
    */
  	var p = []; // return values
  	var char = null;  // current character
  	var i = 0; // current index
  	var j = line.length; // end index
		if (line[0] == ";") {
			return [this.parse_value_text(line.substring(1, j))]
		}

  	while (i < (j-1)) {
  		char = line[i];
  		if (re_whitespace.test(char)) {
  			// unquoted leading whitespace; skip to next char.
  			i++;
			} else if (char == "#") {
				// Unquoted hash -- rest of line is a comment; return
				i = j;
  		} else if (char == '"' || char == "'") {
  			// append value between quotes
  			n = line.indexOf(char+" ", i+1); // find next quote after quote
  			if (n<1){n=j} // ...until end of line if not found
  			p.push(checknumber(line.substring(i+1, n))); // find string between
  			i = n + 1; // advance past end quote
  		} else {
  			// append until next whitespace
  			n = line.indexOf(" ", i+1);
  			if (n<1){n=j}
  			var test = checknumber(line.substring(i, n));
  			p.push(test);
  			i = n; // advance past next whitespace
  		}
  	}
  	return p
  }
	Parser.prototype.parse_value_text = function(line) {
		var text = "";
		// Feed forward multi-line text comments...
		while (line != null) {
			text += line;
			//text += " ";
			line = this.gen.next();
			// until we find a solitary ";"
			if (line == ";") {
				break
			}
		}
		return text
	}

  // Exports
  return {
    'Parser':Parser,
    'Block':Block,
    'Reader':Reader
  }
})(xtal);
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';

const propTypes = {
  className: PropTypes.string,
  onloadCallbackName: PropTypes.string,
  elementID: PropTypes.string,
  onloadCallback: PropTypes.func,
  verifyCallback: PropTypes.func,
  expiredCallback: PropTypes.func,
  render: PropTypes.string,
  sitekey: PropTypes.string,
  theme: PropTypes.string,
  type: PropTypes.string,
  verifyCallbackName: PropTypes.string,
  expiredCallbackName: PropTypes.string,
  size: PropTypes.string,
  tabindex: PropTypes.string,
};

const defaultProps = {
  elementID: 'g-recaptcha',
  onloadCallback: undefined,
  onloadCallbackName: 'onloadCallback',
  verifyCallback: undefined,
  verifyCallbackName: 'verifyCallback',
  expiredCallback: undefined,
  expiredCallbackName: 'expiredCallback',
  render: 'onload',
  theme: 'light',
  type: 'image',
  size: 'normal',
  tabindex: '0',
};

export default class Recaptcha extends Component {

  render() {
    if (this.props.render === 'explicit' && this.props.onloadCallback) {
      window[this.props.onloadCallbackName] = () => {
        grecaptcha.render(this.props.elementID, {
          sitekey: this.props.sitekey,
          callback: (this.props.verifyCallback) ? this.props.verifyCallback : undefined,
          theme: this.props.theme,
          type: this.props.type,
          size: this.props.size,
          tabindex: this.props.tabindex,
          'expired-callback': (this.props.expiredCallback) ? this.props.expiredCallback : undefined,
        });

        if (this.props.onloadCallback) {
          this.props.onloadCallback();
        }
      };

      return (

      );
    }

    return (

    );
  }
}

Recaptcha.propTypes = propTypes;
Recaptcha.defaultProps = defaultProps;
<commit_msg>fix(window): support SSR<commit_after>import React, { PropTypes, Component } from 'react';

const propTypes = {
  className: PropTypes.string,
  onloadCallbackName: PropTypes.string,
  elementID: PropTypes.string,
  onloadCallback: PropTypes.func,
  verifyCallback: PropTypes.func,
  expiredCallback: PropTypes.func,
  render: PropTypes.string,
  sitekey: PropTypes.string,
  theme: PropTypes.string,
  type: PropTypes.string,
  verifyCallbackName: PropTypes.string,
  expiredCallbackName: PropTypes.string,
  size: PropTypes.string,
  tabindex: PropTypes.string,
};

const defaultProps = {
  elementID: 'g-recaptcha',
  onloadCallback: undefined,
  onloadCallbackName: 'onloadCallback',
  verifyCallback: undefined,
  verifyCallbackName: 'verifyCallback',
  expiredCallback: undefined,
  expiredCallbackName: 'expiredCallback',
  render: 'onload',
  theme: 'light',
  type: 'image',
  size: 'normal',
  tabindex: '0',
};

export default class Recaptcha extends Component {

  componentDidMount() {
    if (this.props.render === 'explicit' && this.props.onloadCallback) {
      grecaptcha.render(this.props.elementID, {
        sitekey: this.props.sitekey,
        callback: (this.props.verifyCallback) ? this.props.verifyCallback : undefined,
        theme: this.props.theme,
        type: this.props.type,
        size: this.props.size,
        tabindex: this.props.tabindex,
        'expired-callback': (this.props.expiredCallback) ? this.props.expiredCallback : undefined,
      });
    }
  }

  render() {
    if (this.props.render === 'explicit' && this.props.onloadCallback) {
      return (

      );
    }

    return (

    );
  }
}

Recaptcha.propTypes = propTypes;
Recaptcha.defaultProps = defaultProps;
<|endoftext|>


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

var Promise = require('bluebird');
var git = require('nodegit');
var program = require('commander');
var _ = require('lodash');
var moment = require('moment');
var fs = require('fs');

var DATE_FORMAT = 'YYYY-MM-DD';

var config = {
    // Maximum time diff between 2 subsequent commits in minutes which are
    // counted to be in the same coding "session"
    maxCommitDiffInMinutes: 2 * 60,

    // How many minutes should be added for the first commit of coding session
    firstCommitAdditionInMinutes: 2 * 60,

    // Include commits since time x
    since: 'always'
};

function main() {
    exitIfShallow();

    parseArgs();
    config = mergeDefaultsWithArgs(config);
    config.since = parseSinceDate(config.since);

    getCommits('.').then(function(commits) {
        var commitsByEmail = _.groupBy(commits, function(commit) {
            return commit.author.email || 'unknown';
        });
        var authorWorks = _.map(commitsByEmail, function(authorCommits, authorEmail) {
            return {
                email: authorEmail,
                name: authorCommits[0].author.name,
                hours: estimateHours(_.pluck(authorCommits, 'date')),
                commits: authorCommits.length
            };
        });

        // XXX: This relies on the implementation detail that json is printed
        // in the same order as the keys were added. This is anyway just for
        // making the output easier to read, so it doesn't matter if it
        // isn't sorted in some cases.
        var sortedWork = {};
        _.each(_.sortBy(authorWorks, 'hours'), function(authorWork) {
            sortedWork[authorWork.email] = _.omit(authorWork, 'email');
        });

        var totalHours = _.reduce(sortedWork, function(sum, authorWork) {
            return sum + authorWork.hours;
        }, 0);
        sortedWork.total = {
            hours: totalHours,
            commits: commits.length
        };

        console.log(JSON.stringify(sortedWork, undefined, 2));
    }).catch(function(e) {
        console.error(e.stack);
    });
}

function exitIfShallow() {
    if (fs.existsSync(".git/shallow")) {
        console.log("Cannot analyze shallow copies!");
        console.log("Please run git fetch --unshallow before continuing!");
        process.exit(1);
    }
}

function parseArgs() {
    function int(val) {
        return parseInt(val, 10);
    }

    program
        .version(require('../package.json').version)
        .usage('[options]')
        .option(
            '-d, --max-commit-diff [max-commit-diff]',
            'maximum difference in minutes between commits counted to one' +
            ' session. Default: ' + config.maxCommitDiffInMinutes,
            int
        )
        .option(
            '-a, --first-commit-add [first-commit-add]',
            'how many minutes first commit of session should add to total.' +
            ' Default: ' + config.firstCommitAdditionInMinutes,
            int
        )
        .option(
            '-s, --since [since-certain-date]',
            'Analyze data since certain date.' +
            ' [always|yesterday|today|lastweek|thisweek|yyyy-mm-dd] Default: ' + config.since,
            String
        );

    program.on('--help', function() {
        console.log('  Examples:');
        console.log('');
        console.log('   - Estimate hours of project');
        console.log('');
        console.log('       $ git hours');
        console.log('');
        console.log('   - Estimate hours in repository where developers commit' +
                    ' more seldom: they might have 4h(240min) pause between commits');
        console.log('');
        console.log('       $ git hours --max-commit-diff 240');
        console.log('');
        console.log('   - Estimate hours in repository where developer works 5' +
                    ' hours before first commit in day');
        console.log('');
        console.log('       $ git hours --first-commit-add 300');
        console.log('');
        console.log('   - Estimate hours work in repository since yesterday');
        console.log('');
        console.log('       $ git hours --since yesterday');
        console.log('');
        console.log('   - Estimate hours work in repository since 2015-01-31');
        console.log('');
        console.log('       $ git hours --since 2015-01-31');
        console.log('');
        console.log('  For more details, visit https://github.com/kimmobrunfeldt/git-hours');
        console.log('');
    });

    program.parse(process.argv);
}

function parseSinceDate(since) {
    switch (since) {
        case 'today':
            return moment().startOf('day');
        case 'yesterday':
            return moment().startOf('day').subtract(1, 'day');
        case 'thisweek':
            return moment().startOf('week');
        case 'lastweek':
            return moment().startOf('week').subtract(1, 'week');
        case 'always':
            return 'always';
        default:
            // XXX: Moment tries to parse anything, results might be weird
            return moment(since, DATE_FORMAT);
    }
}

function mergeDefaultsWithArgs(conf) {
    return {
        range: program.range,
        maxCommitDiffInMinutes: program.maxCommitDiff || conf.maxCommitDiffInMinutes,
        firstCommitAdditionInMinutes: program.firstCommitAdd || conf.firstCommitAdditionInMinutes,
        since: program.since || conf.since
    };
}

// Estimates spent working hours based on commit dates
function estimateHours(dates) {
    if (dates.length < 2) {
        return 0;
    }

    // Oldest commit first, newest last
    var sortedDates = dates.sort(function(a, b) {
        return a - b;
    });
    var allButLast = _.take(sortedDates, sortedDates.length - 1);

    var totalHours = _.reduce(allButLast, function(hours, date, index) {
        var nextDate = sortedDates[index + 1];
        var diffInMinutes = (nextDate - date) / 1000 / 60;

        // Check if commits are counted to be in same coding session
        if (diffInMinutes < config.maxCommitDiffInMinutes) {
            return hours + diffInMinutes / 60;
        }

        // The date difference is too big to be inside single coding session
        // The work of first commit of a session cannot be seen in git history,
        // so we make a blunt estimate of it
        return hours + config.firstCommitAdditionInMinutes / 60;

    }, 0);

    return Math.round(totalHours);
}

// Promisify nodegit's API of getting all commits in repository
function getCommits(gitPath) {
    return git.Repository.open(gitPath)
    .then(function(repo) {
        var allReferences = getAllReferences(repo);

        return Promise.filter(allReferences, function(reference) {
            return reference.match(/refs\/heads\/.*/);
        })
        .map(function(branchName) {
            return getBranchLatestCommit(repo, branchName);
        })
        .map(function(branchLatestCommit) {
            return getBranchCommits(branchLatestCommit);
        })
        .reduce(function(allCommits, branchCommits) {
            _.each(branchCommits, function(commit) {
                allCommits.push(commit);
            });

            return allCommits;
        }, [])
        .then(function(commits) {
            // Multiple branches might share commits, so take unique
            var uniqueCommits = _.uniq(commits, function(item, key, a) {
                return item.sha;
            });

            return uniqueCommits;
        });
    });
}

function getAllReferences(repo) {
    return repo.getReferenceNames(git.Reference.TYPE.LISTALL);
}

function getBranchLatestCommit(repo, branchName) {
    return repo.getBranch(branchName).then(function(reference) {
        return repo.getBranchCommit(reference.name());
    });
}

function getBranchCommits(branchLatestCommit) {
    return new Promise(function(resolve, reject) {
        var history = branchLatestCommit.history();
        var commits = [];

        history.on('commit', function(commit) {
            var author = null;
            if (!_.isNull(commit.author())) {
                author = {
                    name: commit.author().name(),
                    email: commit.author().email()
                };
            }

            var commitData = {
                sha: commit.sha(),
                date: commit.date(),
                message: commit.message(),
                author: author
            };

            var sinceAlways = config.since === 'always' || !config.since;
            if (sinceAlways || moment(commitData.date.toISOString()).isAfter(config.since)) {
                commits.push(commitData);
            }
        });

        history.on('end', function() {
            resolve(commits);
        });

        history.on('error', function(err) {
            reject(err);
        });

        // Start emitting events.
        history.start();
    });
}

main();
<commit_msg>adding support for until<commit_after>#!/usr/bin/env node

var Promise = require('bluebird');
var git = require('nodegit');
var program = require('commander');
var _ = require('lodash');
var moment = require('moment');
var fs = require('fs');

var DATE_FORMAT = 'YYYY-MM-DD';

var config = {
    // Maximum time diff between 2 subsequent commits in minutes which are
    // counted to be in the same coding "session"
    maxCommitDiffInMinutes: 2 * 60,

    // How many minutes should be added for the first commit of coding session
    firstCommitAdditionInMinutes: 2 * 60,

    // Include commits since time x
    since: 'always',
    until: 'always'
};

function main() {
    exitIfShallow();

    parseArgs();
    config = mergeDefaultsWithArgs(config);
    config.since = parseSinceDate(config.since);
    config.until = parseUntilDate(config.until);

    getCommits('.').then(function(commits) {
        var commitsByEmail = _.groupBy(commits, function(commit) {
            return commit.author.email || 'unknown';
        });
        var authorWorks = _.map(commitsByEmail, function(authorCommits, authorEmail) {
            return {
                email: authorEmail,
                name: authorCommits[0].author.name,
                hours: estimateHours(_.pluck(authorCommits, 'date')),
                commits: authorCommits.length
            };
        });

        // XXX: This relies on the implementation detail that json is printed
        // in the same order as the keys were added. This is anyway just for
        // making the output easier to read, so it doesn't matter if it
        // isn't sorted in some cases.
        var sortedWork = {};
        _.each(_.sortBy(authorWorks, 'hours'), function(authorWork) {
            sortedWork[authorWork.email] = _.omit(authorWork, 'email');
        });

        var totalHours = _.reduce(sortedWork, function(sum, authorWork) {
            return sum + authorWork.hours;
        }, 0);
        sortedWork.total = {
            hours: totalHours,
            commits: commits.length
        };

        console.log(JSON.stringify(sortedWork, undefined, 2));
    }).catch(function(e) {
        console.error(e.stack);
    });
}

function exitIfShallow() {
    if (fs.existsSync(".git/shallow")) {
        console.log("Cannot analyze shallow copies!");
        console.log("Please run git fetch --unshallow before continuing!");
        process.exit(1);
    }
}

function parseArgs() {
    function int(val) {
        return parseInt(val, 10);
    }

    program
        .version(require('../package.json').version)
        .usage('[options]')
        .option(
            '-d, --max-commit-diff [max-commit-diff]',
            'maximum difference in minutes between commits counted to one' +
            ' session. Default: ' + config.maxCommitDiffInMinutes,
            int
        )
        .option(
            '-a, --first-commit-add [first-commit-add]',
            'how many minutes first commit of session should add to total.' +
            ' Default: ' + config.firstCommitAdditionInMinutes,
            int
        )
        .option(
            '-s, --since [since-certain-date]',
            'Analyze data since certain date.' +
            ' [always|yesterday|today|lastweek|thisweek|yyyy-mm-dd] Default: ' + config.since,
            String
        )
        .option(
            '-u, --until [until-certain-date]',
            'Analyze data until certain date.' +
            ' [always|yesterday|today|lastweek|thisweek|yyyy-mm-dd] Default: ' + config.until,
            String
        );

    program.on('--help', function() {
        console.log('  Examples:');
        console.log('');
        console.log('   - Estimate hours of project');
        console.log('');
        console.log('       $ git hours');
        console.log('');
        console.log('   - Estimate hours in repository where developers commit' +
                    ' more seldom: they might have 4h(240min) pause between commits');
        console.log('');
        console.log('       $ git hours --max-commit-diff 240');
        console.log('');
        console.log('   - Estimate hours in repository where developer works 5' +
                    ' hours before first commit in day');
        console.log('');
        console.log('       $ git hours --first-commit-add 300');
        console.log('');
        console.log('   - Estimate hours work in repository since yesterday');
        console.log('');
        console.log('       $ git hours --since yesterday');
        console.log('');
        console.log('   - Estimate hours work in repository since 2015-01-31');
        console.log('');
        console.log('       $ git hours --since 2015-01-31');
        console.log('');
        console.log('  For more details, visit https://github.com/kimmobrunfeldt/git-hours');
        console.log('');
    });

    program.parse(process.argv);
}

function parseInputDate(inputDate) {
    switch (inputDate) {
        case 'today':
            return moment().startOf('day');
        case 'yesterday':
            return moment().startOf('day').subtract(1, 'day');
        case 'thisweek':
            return moment().startOf('week');
        case 'lastweek':
            return moment().startOf('week').subtract(1, 'week');
        case 'always':
            return 'always';
        default:
            // XXX: Moment tries to parse anything, results might be weird
            return moment(inputDate, DATE_FORMAT);
    }
}
function parseSinceDate(since) {
    return parseInputDate(since);
}
function parseUntilDate(until) {
    return parseInputDate(until);
}

function mergeDefaultsWithArgs(conf) {

    return {
        range: program.range,
        maxCommitDiffInMinutes: program.maxCommitDiff || conf.maxCommitDiffInMinutes,
        firstCommitAdditionInMinutes: program.firstCommitAdd || conf.firstCommitAdditionInMinutes,
        since: program.since || conf.since,
        until: program.until || conf.until
    };
}

// Estimates spent working hours based on commit dates
function estimateHours(dates) {
    if (dates.length < 2) {
        return 0;
    }

    // Oldest commit first, newest last
    var sortedDates = dates.sort(function(a, b) {
        return a - b;
    });
    var allButLast = _.take(sortedDates, sortedDates.length - 1);

    var totalHours = _.reduce(allButLast, function(hours, date, index) {
        var nextDate = sortedDates[index + 1];
        var diffInMinutes = (nextDate - date) / 1000 / 60;

        // Check if commits are counted to be in same coding session
        if (diffInMinutes < config.maxCommitDiffInMinutes) {
            return hours + diffInMinutes / 60;
        }

        // The date difference is too big to be inside single coding session
        // The work of first commit of a session cannot be seen in git history,
        // so we make a blunt estimate of it
        return hours + config.firstCommitAdditionInMinutes / 60;

    }, 0);

    return Math.round(totalHours);
}

// Promisify nodegit's API of getting all commits in repository
function getCommits(gitPath) {
    return git.Repository.open(gitPath)
    .then(function(repo) {
        var allReferences = getAllReferences(repo);

        return Promise.filter(allReferences, function(reference) {
            return reference.match(/refs\/heads\/.*/);
        })
        .map(function(branchName) {
            return getBranchLatestCommit(repo, branchName);
        })
        .map(function(branchLatestCommit) {
            return getBranchCommits(branchLatestCommit);
        })
        .reduce(function(allCommits, branchCommits) {
            _.each(branchCommits, function(commit) {
                allCommits.push(commit);
            });

            return allCommits;
        }, [])
        .then(function(commits) {
            // Multiple branches might share commits, so take unique
            var uniqueCommits = _.uniq(commits, function(item, key, a) {
                return item.sha;
            });

            return uniqueCommits;
        });
    });
}

function getAllReferences(repo) {
    return repo.getReferenceNames(git.Reference.TYPE.LISTALL);
}

function getBranchLatestCommit(repo, branchName) {
    return repo.getBranch(branchName).then(function(reference) {
        return repo.getBranchCommit(reference.name());
    });
}

function getBranchCommits(branchLatestCommit) {
    return new Promise(function(resolve, reject) {
        var history = branchLatestCommit.history();
        var commits = [];

        history.on('commit', function(commit) {
            var author = null;
            if (!_.isNull(commit.author())) {
                author = {
                    name: commit.author().name(),
                    email: commit.author().email()
                };
            }

            var commitData = {
                sha: commit.sha(),
                date: commit.date(),
                message: commit.message(),
                author: author
            };

            var isValidSince = true;
            var sinceAlways = config.since === 'always' || !config.since;
            if (sinceAlways || moment(commitData.date.toISOString()).isAfter(config.since)) {
                isValidSince = true;
            } else {
                isValidSince = false;
            }

            var isValidUntil = true;
            var untilAlways = config.until === 'always' || !config.until;
            if (untilAlways || moment(commitData.date.toISOString()).isBefore(config.until)) {
                isValidUntil = true;
            } else {
                isValidUntil = false;
            }

            if (isValidSince && isValidUntil) {
                commits.push(commitData);
            }
        });

        history.on('end', function() {
            resolve(commits);
        });

        history.on('error', function(err) {
            reject(err);
        });

        // Start emitting events.
        history.start();
    });
}

main();
<|endoftext|>


<commit_before>// Verify NodeJS version
const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10);
if (nodeMajorVersion < 8) {
  console.error('Unsupported NodeJS version! Please install NodeJS 8 or newer.');
  process.exit(1);
}

// Verify node modules have been installed
const fs = require('fs');
const path = require('path');

try {
  fs.accessSync(path.join(__dirname, '..', 'node_modules'));
} catch (e) {
  console.error('Please run "npm install" before starting the bot');
  process.exit(1);
}

let testedPackage = '';
try {
  const packageJson = require('../package.json');
  const modules = Object.keys(packageJson.dependencies);
  modules.forEach(mod => {
    testedPackage = mod;
    fs.accessSync(path.join(__dirname, '..', 'node_modules', mod))
  });
} catch (e) {
  console.error(`Please run "npm install" again! Package "${testedPackage}" is missing.`);
  process.exit(1);
}

const config = require('./config');
const utils = require('./utils');
const main = require('./main');
const knex = require('./knex');
const legacyMigrator = require('./legacy/legacyMigrator');

// Force crash on unhandled rejections (use something like forever/pm2 to restart)
process.on('unhandledRejection', err => {
  if (err instanceof utils.BotError || (err && err.code)) {
    // We ignore stack traces for BotErrors (the message has enough info) and network errors from Eris (their stack traces are unreadably long)
    console.error(`Error: ${err.message}`);
  } else {
    console.error(err);
  }

  process.exit(1);
});

(async function() {
  // Make sure the database is up to date
  await knex.migrate.latest();

  // Migrate legacy data if we need to
  if (await legacyMigrator.shouldMigrate()) {
    console.log('=== MIGRATING LEGACY DATA ===');
    console.log('Do not close the bot!');
    console.log('');

    await legacyMigrator.migrate();

    const relativeDbDir = (path.isAbsolute(config.dbDir) ? config.dbDir : path.resolve(process.cwd(), config.dbDir));
    const relativeLogDir = (path.isAbsolute(config.logDir) ? config.logDir : path.resolve(process.cwd(), config.logDir));

    console.log('');
    console.log('=== LEGACY DATA MIGRATION FINISHED ===');
    console.log('');
    console.log('IMPORTANT: After the bot starts, please verify that all logs, threads, blocked users, and snippets are still working correctly.');
    console.log('Once you\'ve done that, the following files/directories are no longer needed. I would recommend keeping a backup of them, however.');
    console.log('');
    console.log('FILE: ' + path.resolve(relativeDbDir, 'threads.json'));
    console.log('FILE: ' + path.resolve(relativeDbDir, 'blocked.json'));
    console.log('FILE: ' + path.resolve(relativeDbDir, 'snippets.json'));
    console.log('DIRECTORY: ' + relativeLogDir);
    console.log('');
    console.log('Starting the bot...');
  }

  // Start the bot
  main.start();
})();
<commit_msg>Ignore errors from unknown message types<commit_after>// Verify NodeJS version
const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10);
if (nodeMajorVersion < 8) {
  console.error('Unsupported NodeJS version! Please install NodeJS 8 or newer.');
  process.exit(1);
}

// Verify node modules have been installed
const fs = require('fs');
const path = require('path');

try {
  fs.accessSync(path.join(__dirname, '..', 'node_modules'));
} catch (e) {
  console.error('Please run "npm install" before starting the bot');
  process.exit(1);
}

// Error handling
process.on('uncaughtException', err => {
  // Unknown message types (nitro boosting messages at the time) should be safe to ignore
  if (err && err.message && err.message.startsWith('Unhandled MESSAGE_CREATE type')) {
    return;
  }

  // For everything else, crash with the error
  console.error(err);
  process.exit(1);
});

let testedPackage = '';
try {
  const packageJson = require('../package.json');
  const modules = Object.keys(packageJson.dependencies);
  modules.forEach(mod => {
    testedPackage = mod;
    fs.accessSync(path.join(__dirname, '..', 'node_modules', mod))
  });
} catch (e) {
  console.error(`Please run "npm install" again! Package "${testedPackage}" is missing.`);
  process.exit(1);
}

const config = require('./config');
const utils = require('./utils');
const main = require('./main');
const knex = require('./knex');
const legacyMigrator = require('./legacy/legacyMigrator');

// Force crash on unhandled rejections (use something like forever/pm2 to restart)
process.on('unhandledRejection', err => {
  if (err instanceof utils.BotError || (err && err.code)) {
    // We ignore stack traces for BotErrors (the message has enough info) and network errors from Eris (their stack traces are unreadably long)
    console.error(`Error: ${err.message}`);
  } else {
    console.error(err);
  }

  process.exit(1);
});

(async function() {
  // Make sure the database is up to date
  await knex.migrate.latest();

  // Migrate legacy data if we need to
  if (await legacyMigrator.shouldMigrate()) {
    console.log('=== MIGRATING LEGACY DATA ===');
    console.log('Do not close the bot!');
    console.log('');

    await legacyMigrator.migrate();

    const relativeDbDir = (path.isAbsolute(config.dbDir) ? config.dbDir : path.resolve(process.cwd(), config.dbDir));
    const relativeLogDir = (path.isAbsolute(config.logDir) ? config.logDir : path.resolve(process.cwd(), config.logDir));

    console.log('');
    console.log('=== LEGACY DATA MIGRATION FINISHED ===');
    console.log('');
    console.log('IMPORTANT: After the bot starts, please verify that all logs, threads, blocked users, and snippets are still working correctly.');
    console.log('Once you\'ve done that, the following files/directories are no longer needed. I would recommend keeping a backup of them, however.');
    console.log('');
    console.log('FILE: ' + path.resolve(relativeDbDir, 'threads.json'));
    console.log('FILE: ' + path.resolve(relativeDbDir, 'blocked.json'));
    console.log('FILE: ' + path.resolve(relativeDbDir, 'snippets.json'));
    console.log('DIRECTORY: ' + relativeLogDir);
    console.log('');
    console.log('Starting the bot...');
  }

  // Start the bot
  main.start();
})();
<|endoftext|>


<commit_before>var Promise = require('promise');
var shexjs = require("shex");
var n3 = require("n3");
var isNode = require('detect-node');
var DefaultBase = "";

function Validator(schemaText, dataText, callbacks, options) {
    this.callbacks = callbacks;
    this.options = options;

    if (isNode) {
        console.log("Running under Node.JS");
        this.updateSchema('http://127.0.0.1:9999/schema.shex', schemaText);
    } else {
        this.updateSchema(location.origin + '/schema.shex', schemaText);
    }

    this.updateData(dataText);
}

Validator.prototype = {
    updateSchema: function (base, schemaText) {
        this.schema = parseSchema(base ,schemaText);
        this.schema.done(this.callbacks.schemaParsed, this.callbacks.schemaParseError);
    },
    updateData: function (dataText) {
        this.data = parseData(dataText);
        this.data.done(this.callbacks.dataParsed, this.callbacks.dataParseError);
    },
    findShapes: function () {
        var _this = this;
        return Promise.all([this.schema, this.data]).then(function (a) {
            // console.log('showing shapes');
            // console.log(this.schema);
            // console.log(this.data);
        });
    },
    validate: function(startingNodes) {
        var _this = this;
        return Promise.all([this.schema, this.data, this.options]).then(function (a) {
            console.log('validate called a:',a);
            var node = Object.keys(a[2].resourceShapeMap)[0];
            var validator = shexjs.Validator.construct(a[0].schema);
            try {

                var result = validator.validate(a[1].db, node, a[2].resourceShapeMap[node]);
            } catch (error){
                console.error(error);
            }
            console.log('valres',result);
            // console.log('callbacks in validator',_this.callbacks);
            // console.log('db to validate',a[1].db)
            return cleanResult(result, a[1].triples, _this.callbacks.validationResult)
        });
    }
};

module.exports.Validator = Validator;

function parseData(dataText){
    return new Promise(function (resolve, reject) {
        var lineIndex = new Object();
        var db = n3.Store({meta:true});
        n3.Parser({documentIRI: DefaultBase}).parse(dataText, function (error, triple, prefixes) {
            // console.log('db', db);
            // console.log('DB');
            // console.log('triple callback')
            if (error) {
                // throw Error("error parsing " + data + ": " + error);
                reject(parseN3Error(error));
            } else if (triple) {
                // console.log("N3triple",triple);
                lineIndex[JSON.stringify({'subject':triple.subject,'predicate':triple.predicate,'object':triple.object,'graph':triple.graph})] = triple.line;
                lineIndex[triple.line] = {'object':triple.object,'subject':triple.subject,'predicate':triple.predicate,'graph':triple.graph};
                console.log('triple added ',triple);
                db.addTriple(triple.subject, triple.predicate, triple.object,triple.graph, {line:triple.line});

            } else {
                // db.setMetaFlag(true);
                var triples = db.getTriples();
                // db.setMetaFlag(false);
                for (var i = triples.length - 1; i >= 0; i--) {
                    var triple_key = JSON.stringify({'subject':triples[i].subject,'predicate':triples[i].predicate,'object':triples[i].object,'graph':""});
                    triples[i].line = lineIndex[triple_key];
                }
                resolve({db: db, triples:triples});
            }
        });

    });
}

function parseSchema(base, schemaText) {
    return new Promise(function (resolve, reject) {
        var schema;

        var preprocessed = parseReqLevels(schemaText);

        try {
            schema = shexjs.Parser.construct(base).parse(preprocessed.data);
        }
        catch (e) {
            reject(e);
        }
        resolve({schema: schema, shapes: schema.shapes, levels:preprocessed.lineRules});

    });
};

function cleanResult(result, parsedTriples, callback){
    console.log('validation result', result);
    var errors = [];
    var matches = [];
    if (result.type == 'Failure'){
        errors = cleanErrors(parsedTriples, result);
    } else {
        matches = cleanMatches(parsedTriples, result);
        // console.log('result',result);
        // console.log('matches',matches);
    }
    var clean_result = {
            errors: errors,
            matches: matches,
            startingResource: result.node,
            passed: errors.length === 0,
            full_result:result
        };
    return callback(clean_result);
}

function parseN3Error(error) {
    var line = error.message.match(/line [0-9]*/g);

    line = (line && line.length > 0 && line[0].length > 5)?Number(line[0].substr(5)):null;

    return {
        message: error.message,
        line: line,
        column: 0
    }
}
// returns the matched triples and the corresponding lines

function cleanMatches(parsedTriples, validationResult){
    var results = [];
    var messages = [];
    var matches = parseMatches(validationResult.solution);
    for (var i = matches.length - 1; i >= 0; i--) {
        var currentMatch = {};
        currentMatch.line = matchTriple(parsedTriples, matches[i]).line;
        currentMatch.subject = matches[i].subject;
        currentMatch.predicate = matches[i].predicate;

        if (matches[i].object.value && matches[i].object.type) {
                currentMatch.object = '<type>:' +String(matches[i].object.type) +' <value>:'+ String(matches[i].object.value);

        } else {
            currentMatch.object = matches[i].object;
        }
            currentMatch.message = 'Matched '+ String(currentMatch.predicate)+' '+ String(currentMatch.object) + ' on line ' + String(currentMatch.line);

        // console.log('cleanMatches',matches[i]);

        if (!messages.includes(currentMatch.message)){
            results.push(currentMatch);
            messages.push(currentMatch.message);
        }

    }
    // console.log('results returned',results);
    return results
}

function parseMatches(solution){
    var results = [];
    var values = Object.values(solution)
    // console.log('object values',values);
    for (var i = values.length - 1; i >= 0; i--) {
        if (values[i].subject && values[i].predicate && values[i].object){
            // console.log('istriple',values[i]);
            results.push(values[i]);
        }
        else if (typeof(values[i]) == 'string'){
            // console.log('string found',values[i])
        } else {
            Array.prototype.push.apply(results,parseMatches(values[i]));
        }
    }

    return results
}

function parseErrors(errors){
    var results = [];
    for (var i = errors.length - 1; i >= 0; i--) {
        if (Array.isArray(errors[i])){
            // console.log('rec call',errors[i]);
            Array.prototype.push.apply(results,parseErrors(errors[i]));

        } else {
            // console.log('non rec call',errors[i]);
            results.push(errors[i])
        }
    }
    console.log('results',results);
    return results
}

function matchTriple(parsedTriples, keyTriple){
    var match;

    try {
        match = parsedTriples.find(function (triple) {
            var lookup = []
            if (typeof(keyTriple.subject) == 'string' ){
                if (triple.subject === keyTriple.subject){
                    lookup.push(true)
                } else{
                    return false
                }
            } else {
                lookup.push(true);
            }
            // console.log('subject',keyTriple.subject);

            if (typeof(keyTriple.predicate) == 'string'){
                if (triple.predicate === keyTriple.predicate){
                    lookup.push(true);
                } else {
                    return false
                }
            }else {
                lookup.push(true);
            }

            // console.log('predicate',keyTriple.predicate);

            if (typeof(keyTriple.object) == 'string'){
                if (triple.object === keyTriple.object){
                    lookup.push(true);
                } else {
                    return false
                }
            }else {
                lookup.push(true);
            }
            // console.log('object',keyTriple.object);

            return lookup[0] == lookup[1] == lookup[2]
        });
    } catch (error) {
        console.error(error);
    }
    // console.log('keyTriple',keyTriple,'matchedTriple',match);

    return match
}

function cleanErrors(parsedTriples, validationResult){
    var results = [];
    var messages = [];
    var errors = parseErrors(validationResult.errors);
    // console.log('errors',validationResult.errors);
    //sometimes errors are in arrays of length one for some reason :/

    // console.log('errors',errors);
    // console.log('validationResult',validationResult);
    for (var i = errors.length - 1; i >= 0; i--) {
        if (errors[i].type == 'TypeMismatch'){
            if (errors[i].triple) {

                var match = matchTriple(parsedTriples,errors[i].triple);
                // console.log('match',match);
                errors[i].line = match.line;

                errors[i].message = 'Type mismatch on line ' + String(errors[i].line) + ' in ' + String(validationResult.node);

            } else {
                errors[i].message = ' Missing property ' + String(errors[i].property) + ' in ' + String(validationResult.node);
            }
        }

        else if (errors[i].type == 'MissingProperty') {
            try {
                errors[i].message = ' Missing property ' + String(errors[i].property) ;

            }catch (error){
                console.error(error);
            }
            // console.log('missing property error',errors[i]);

        }
        else {
            console.log('VALIDATION ERROR:',errors[i].type);
        }
        //check if the same error has been already parsed, as the validation library does produce duplicates
        if (!messages.includes(errors[i].message)){
            results.push(errors[i]);
            messages.push(errors[i].message);
        }
    }

    return results
}

function parseReqLevels(rawSchema, levels){
    var defaultLevels = ['`MUST`','`MAY`','`SHOULD`'];
    var quoteChar = '`';
    if (levels) {
        defaultLevels = levels;
        for (var i = levels.length - 1; i >= 0; i--) {
            defaultLevels[i] = quoteChar + levels[i] + quoteChar;
        }
    }

    var lines = [];
    var result = {'data':'',lineRules:{}};

    lines = rawSchema.split("\n");

    var re = new RegExp('(.*)('+defaultLevels[0]+'|'+defaultLevels[1]+'|'+defaultLevels[2]+')(.*)');

    for (var i = 0; i <= lines.length - 1; i++) {
        var match = lines[i].match(re);
        if (match) {
            result.lineRules[i] = match[2];
            result.data = String.concat(result.data,match[3] + '\n');
        } else {
            result.data = String.concat(result.data,lines[i] + '\n');
        }
    }
    console.log('PREPROCESSING OF schema DONE',result.data);
    return result
}

var schemaText =
"# Issue-simple-annotated.shex - Issue representation in Turtle\n" +
"\n" +
"#BASE <http://base.example/#>\n" +
"PREFIX ex: <http://ex.example/#>\n" +
"PREFIX foaf: <http://xmlns.com/foaf/>\n" +
"PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n" +
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
"start = <IssueShape>  # Issue validation starts with <IssueShape>\n" +
"\n" +
"<IssueShape> {                       # An <IssueShape> has:\n" +
"    ex:state [ex:unassigned            # state which is\n" +
"              ex:assigned],            #   unassigned or assigned.\n" +
"    `MUST`ex:reportedBy @<UserShape>,        # reported by a <UserShape>.\n" +
"    ex:reportedOn xsd:dateTime,        # reported some date/time.\n" +
"    (                                  # optionally\n" +
"     ex:reproducedBy @<EmployeeShape>, #   reproduced by someone\n" +
"     ex:reproducedOn xsd:dateTime      #   at some data/time.\n" +
"    )?,\n" +
"    ex:related @<IssueShape>*          # n related issues.\n" +
"}\n" +
"\n" +
"<UserShape> {                        # A <UserShape> has:\n" +
"    (                                  # either\n" +
"       foaf:name xsd:string            #   a FOAF name\n" +
"     |                                 #  or\n" +
"       foaf:givenName xsd:string+,     #   one or more givenNames\n" +
"       foaf:familyName xsd:string),    #   and one familyName.\n" +
"    foaf:mbox IRI                      # one FOAF mbox.\n" +
"}\n" +
"\n" +
"<EmployeeShape> {                    # An <EmployeeShape> has:\n" +
"    `MUST`foaf:givenName xsd:string+,        # at least one givenName.\n" +
"    `MAY`foaf:familyName xsd:string,        # one familyName.\n" +
"    `SHOULD`foaf:phone IRI*,                   # any number of phone numbers.\n" +
"    foaf:mbox IRI                      # one FOAF mbox.\n" +
"}";

<commit_msg>changes made<commit_after>var Promise = require('promise');
var shexjs = require("shex");
var n3 = require("n3");
var isNode = require('detect-node');
var DefaultBase = "";

Validator.prototype = {
    updateSchema: function (base, schemaText) {
        this.schema = parseSchema(base ,schemaText);
        this.schema.done(this.callbacks.schemaParsed, this.callbacks.schemaParseError);
    },
    updateData: function (dataText) {
        this.data = parseData(dataText);
        this.data.done(this.callbacks.dataParsed, this.callbacks.dataParseError);
    },
    findShapes: function () {
        var _this = this;
        return Promise.all([this.schema, this.data]).then(function (a) {
            // console.log('showing shapes');
            // console.log(this.schema);
            // console.log(this.data);
        });
    },
    validate: function(startingNodes) {
        var _this = this;
        return Promise.all([this.schema, this.data, this.options]).then(function (a) {
            console.log('validate called a:',a);
            var node = Object.keys(a[2].resourceShapeMap)[0];
            var validator = shexjs.Validator.construct(a[0].schema);
            try {

                var result = validator.validate(a[1].db, node, a[2].resourceShapeMap[node]);
            } catch (error){
                console.error(error);
            }
            console.log('valres',result);
            // console.log('callbacks in validator',_this.callbacks);
            // console.log('db to validate',a[1].db)
            return cleanResult(result, a[1].triples, _this.callbacks.validationResult)
        });
    }
};

function parseData(dataText){
    return new Promise(function (resolve, reject) {
        var lineIndex = new Object();
        var db = n3.Store({meta:true});
        n3.Parser({documentIRI: DefaultBase}).parse(dataText, function (error, triple, prefixes) {
            // console.log('db', db);
            // console.log('DB');
            // console.log('triple callback')
            if (error) {
                // throw Error("error parsing " + data + ": " + error);
                reject(parseN3Error(error));
            } else if (triple) {
                // console.log("N3triple",triple);
                lineIndex[JSON.stringify({'subject':triple.subject,'predicate':triple.predicate,'object':triple.object,'graph':triple.graph})] = triple.line;
                lineIndex[triple.line] = {'object':triple.object,'subject':triple.subject,'predicate':triple.predicate,'graph':triple.graph};
                console.log('triple added ',triple);
                db.addTriple(triple.subject, triple.predicate, triple.object,triple.graph, {line:triple.line});

            } else {
                // db.setMetaFlag(true);
                var triples = db.getTriples();
                // db.setMetaFlag(false);
                for (var i = triples.length - 1; i >= 0; i--) {
                    var triple_key = JSON.stringify({'subject':triples[i].subject,'predicate':triples[i].predicate,'object':triples[i].object,'graph':""});
                    triples[i].line = lineIndex[triple_key];
                }
                resolve({db: db, triples:triples});
            }
        });

    });
}

function parseSchema(base, schemaText) {
    return new Promise(function (resolve, reject) {
        var schema;

        var preprocessed = parseReqLevels(schemaText);

        try {
            schema = shexjs.Parser.construct(base).parse(preprocessed.data);
        }
        catch (e) {
            reject(e);
        }
        resolve({schema: schema, shapes: schema.shapes, levels:preprocessed.lineRules});

    });
};

function cleanResult(result, parsedTriples, callback){
    console.log('validation result', result);
    var errors = [];
    var matches = [];
    if (result.type == 'Failure'){
        errors = cleanErrors(parsedTriples, result);
    } else {
        matches = cleanMatches(parsedTriples, result);
        // console.log('result',result);
        // console.log('matches',matches);
    }
    var clean_result = {
            errors: errors,
            matches: matches,
            startingResource: result.node,
            passed: errors.length === 0,
            full_result:result
        };
    return callback(clean_result);
}

function parseN3Error(error) {
    var line = error.message.match(/line [0-9]*/g);

    line = (line && line.length > 0 && line[0].length > 5)?Number(line[0].substr(5)):null;

    return {
        message: error.message,
        line: line,
        column: 0
    }
}
// returns the matched triples and the corresponding lines

function cleanMatches(parsedTriples, validationResult){
    var results = [];
    var messages = [];
    var matches = parseMatches(validationResult.solution);
    for (var i = matches.length - 1; i >= 0; i--) {
        var currentMatch = {};
        currentMatch.line = matchTriple(parsedTriples, matches[i]).line;
        currentMatch.subject = matches[i].subject;
        currentMatch.predicate = matches[i].predicate;

        if (matches[i].object.value && matches[i].object.type) {
                currentMatch.object = '<type>:' +String(matches[i].object.type) +' <value>:'+ String(matches[i].object.value);

        } else {
            currentMatch.object = matches[i].object;
        }
            currentMatch.message = 'Matched '+ String(currentMatch.predicate)+' '+ String(currentMatch.object) + ' on line ' + String(currentMatch.line);

        // console.log('cleanMatches',matches[i]);

        if (!messages.includes(currentMatch.message)){
            results.push(currentMatch);
            messages.push(currentMatch.message);
        }

    }
    // console.log('results returned',results);
    return results
}

function parseMatches(solution){
    var results = [];
    var values = Object.values(solution)
    // console.log('object values',values);
    for (var i = values.length - 1; i >= 0; i--) {
        if (values[i].subject && values[i].predicate && values[i].object){
            // console.log('istriple',values[i]);
            results.push(values[i]);
        }
        else if (typeof(values[i]) == 'string'){
            // console.log('string found',values[i])
        } else {
            Array.prototype.push.apply(results,parseMatches(values[i]));
        }
    }

    return results
}

function parseErrors(errors){
    var results = [];
    for (var i = errors.length - 1; i >= 0; i--) {
        if (Array.isArray(errors[i])){
            // console.log('rec call',errors[i]);
            Array.prototype.push.apply(results,parseErrors(errors[i]));

        } else {
            // console.log('non rec call',errors[i]);
            results.push(errors[i])
        }
    }
    console.log('results',results);
    return results
}

function matchTriple(parsedTriples, keyTriple){
    var match;

    try {
        match = parsedTriples.find(function (triple) {
            var lookup = []
            if (typeof(keyTriple.subject) == 'string' ){
                if (triple.subject === keyTriple.subject){
                    lookup.push(true)
                } else{
                    return false
                }
            } else {
                lookup.push(true);
            }
            // console.log('subject',keyTriple.subject);

            if (typeof(keyTriple.predicate) == 'string'){
                if (triple.predicate === keyTriple.predicate){
                    lookup.push(true);
                } else {
                    return false
                }
            }else {
                lookup.push(true);
            }

            // console.log('predicate',keyTriple.predicate);

            if (typeof(keyTriple.object) == 'string'){
                if (triple.object === keyTriple.object){
                    lookup.push(true);
                } else {
                    return false
                }
            }else {
                lookup.push(true);
            }
            // console.log('object',keyTriple.object);

            return lookup[0] == lookup[1] == lookup[2]
        });
    } catch (error) {
        console.error(error);
    }
    // console.log('keyTriple',keyTriple,'matchedTriple',match);

    return match
}

function cleanErrors(parsedTriples, validationResult){
    var results = [];
    var messages = [];
    var errors = parseErrors(validationResult.errors);
    // console.log('errors',validationResult.errors);
    //sometimes errors are in arrays of length one for some reason :/

    // console.log('errors',errors);
    // console.log('validationResult',validationResult);
    for (var i = errors.length - 1; i >= 0; i--) {
        if (errors[i].type == 'TypeMismatch'){
            if (errors[i].triple) {

                var match = matchTriple(parsedTriples,errors[i].triple);
                // console.log('match',match);
                errors[i].line = match.line;

                errors[i].message = 'Type mismatch on line ' + String(errors[i].line) + ' in ' + String(validationResult.node);

            } else {
                errors[i].message = ' Missing property ' + String(errors[i].property) + ' in ' + String(validationResult.node);
            }
        }

        else if (errors[i].type == 'MissingProperty') {
            try {
                errors[i].message = ' Missing property ' + String(errors[i].property) ;

            }catch (error){
                console.error(error);
            }
            // console.log('missing property error',errors[i]);

        }
        else {
            console.log('VALIDATION ERROR:',errors[i].type);
        }
        //check if the same error has been already parsed, as the validation library does produce duplicates
        if (!messages.includes(errors[i].message)){
            results.push(errors[i]);
            messages.push(errors[i].message);
        }
    }

    return results
}

function parseReqLevels(rawSchema, levels){
    var defaultLevels = ['`MUST`','`MAY`','`SHOULD`'];
    var quoteChar = '`';
    if (levels) {
        defaultLevels = levels;
        for (var i = levels.length - 1; i >= 0; i--) {
            defaultLevels[i] = quoteChar + levels[i] + quoteChar;
        }
    }

    var lines = [];
    var result = {'data':'',lineRules:{}};

    lines = rawSchema.split("\n");

    var re = new RegExp('(.*)('+defaultLevels[0]+'|'+defaultLevels[1]+'|'+defaultLevels[2]+')(.*)');

    for (var i = 0; i <= lines.length - 1; i++) {
        var match = lines[i].match(re);
        if (match) {
            result.lineRules[i] = match[2];
            result.data = String.concat(result.data,match[3] + '\n');
        } else {
            result.data = String.concat(result.data,lines[i] + '\n');
        }
    }
    console.log('PREPROCESSING OF schema DONE',result.data);
    return result
}

function Validator(schemaText, dataText, callbacks, options) {
    this.callbacks = callbacks;
    this.options = options;

    if (isNode) {
        console.log("Running under Node.JS");
        this.updateSchema('http://127.0.0.1:9999/schema.shex', schemaText);
    } else {
        this.updateSchema(location.origin + '/schema.shex', schemaText);
    }

    this.updateData(dataText);
}

module.exports.Validator = Validator;<|endoftext|>