<commit_before>/*
 * Copyright (c) 2016, Rogassi Ent. All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

'use strict';

var Promise = global.Promise || require('promise');
var _ = require('underscore');
var React = require('react');
var ReactDOM = require('react-dom');
var ReactDOMServer = require('react-dom/server');

var glob = require('glob');
//var Handlebars = require('handlebars');
var fs = require('graceful-fs');
var path = require('path');

var utils = require('./utils');

module.exports = ExpressReactServerPages;

// -----------------------------------------------------------------------------

function ExpressReactServerPages(config) {
    // Config properties with defaults.
    utils.assign(this, {
        //handlebars     : Handlebars,
        extname: '.rt',
        wrapperExt: '.html',
        layoutsDir: 'views/layouts/',
        partialsDir: 'views/partials/',
        defaultLayout: undefined,
        helpers: undefined,
        compilerOptions: undefined,
    }, config);

    // Express view engine integration point.
    this.engine = this.renderView.bind(this);

    // Normalize `extname`.
    if (this.extname.charAt(0) !== '.') {
        this.extname = '.' + this.extname;
    }

    // Internal caches of compiled and precompiled templates.
    this.compiled = Object.create(null);
    this.precompiled = Object.create(null);

    // Private internal file system cache.
    this._fsCache = Object.create(null);
}

ExpressReactServerPages.prototype.getPartials = function (options) {
    var partialsDirs = Array.isArray(this.partialsDir) ?
        this.partialsDir : [this.partialsDir];

    partialsDirs = partialsDirs.map(function (dir) {
        var dirPath;
        var dirTemplates;
        var dirNamespace;

        // Support `partialsDir` collection with object entries that contain a
        // templates promise and a namespace.
        if (typeof dir === 'string') {
            dirPath = dir;
        } else if (typeof dir === 'object') {
            dirTemplates = dir.templates;
            dirNamespace = dir.namespace;
            dirPath = dir.dir;
        }

        // We must have some path to templates, or templates themselves.
        if (!(dirPath || dirTemplates)) {
            throw new Error('A partials dir must be a string or config object');
        }

        // Make sure we're have a promise for the templates.
        var templatesPromise = dirTemplates ? Promise.resolve(dirTemplates) :
            this.getTemplates(dirPath, options);

        return templatesPromise.then(function (templates) {
            return {
                templates: templates,
                namespace: dirNamespace,
            };
        });
    }, this);

    return Promise.all(partialsDirs).then(function (dirs) {
        var getTemplateName = this._getTemplateName.bind(this);

        return dirs.reduce(function (partials, dir) {
            var templates = dir.templates;
            var namespace = dir.namespace;
            var filePaths = Object.keys(templates);

            filePaths.forEach(function (filePath) {
                var partialName = getTemplateName(filePath, namespace);
                partials[partialName] = templates[filePath];
            });

            return partials;
        }, {});
    }.bind(this));
};

ExpressReactServerPages.prototype.getTemplate = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var precompiled = options.precompiled;
    var cache = precompiled ? this.precompiled : this.compiled;
    var template = options.cache && cache[filePath];

    if (template) {
        return template;
    }

    //// Optimistically cache template promise to reduce file system I/O, but
    //// remove from cache if there was a problem.
    //template = cache[filePath] = this._getFile(filePath, { cache: options.cache })
    //    .then(function (file) {
    //    /// WE DO NOT DO THIS
    //    //if (precompiled) {
    //    //    return this._precompileTemplate(file, this.compilerOptions);
    //    //}
    //    return this._compileTemplate(file, this.compilerOptions);
    //}.bind(this));

    var _tmp = require(filePath.replace(/\.rt.html/ig, '.js').replace(/\.rt/ig, '.js'));

    /// DEAL WITH ES6
    if (_tmp) {
        if (_tmp.__esModule) {
            template = cache[filePath] = _tmp.default;
        }
        else {
            template = cache[filePath] = _tmp;
        }
    }
    /// THIS IS PRETTY DIRTY
    if (!options.cache) {
        delete cache[filePath]
        delete require.cache[filePath];
        delete require.cache[filePath.replace(/\.js/, '-rt.js')];
    }

    //return template.catch(function (err) {
    //    delete cache[filePath];
    //    throw err;
    //});
    return template;
};

ExpressReactServerPages.prototype.getTemplates = function (dirPath, options) {
    options || (options = {});
    var cache = options.cache;

    return this._getDir(dirPath, { cache: cache }).then(function (filePaths) {
        var templates = filePaths.map(function (filePath) {
            return this.getTemplate(path.join(dirPath, filePath), options);
        }, this);

        return Promise.all(templates).then(function (templates) {
            return filePaths.reduce(function (hash, filePath, i) {
                hash[filePath] = templates[i];
                return hash;
            }, {});
        });
    }.bind(this));
};

ExpressReactServerPages.prototype.render = function (filePath, context, options) {
    options || (options = {});

    return Promise.all([
        this.getTemplate(filePath, { cache: options.cache }),
        options.partials || this.getPartials({ cache: options.cache }),
    ]).then(function (templates) {
        var template = templates[0];
        var partials = templates[1];
        var helpers = options.helpers || this.helpers;

        // DOES TEMPLATE EXPOSE OVERRIDE SETTINGS ?

        // DOES CURRENT DIRECTORY OVERRIDE SETTINGS ?

        // RECURSE THE DIRECTORY UP UNTIL WE FIND A OVERRIDE SETTINGS ?

        // DO NOTHING IF WE DO NOT FIND ONE AND USE THE OPTIONS SETTING

        context.template = template;
        // Add ExpressHandlebars metadata to the data channel so that it's
        // accessible within the templates and helpers, namespaced under:
        // `@exprsp.*`
        var data = utils.assign({}, options.data, {
            exprsp: utils.assign({}, options, {
                filePath: filePath,
                helpers: helpers,
                partials: partials,
            }),
        });

        return this._renderTemplate(template, context, {
            data: data,
            helpers: helpers,
            partials: partials,
        });
    }.bind(this));
};

ExpressReactServerPages.prototype.renderView = function (viewPath, options, callback) {
    options || (options = {});

    var context = options;

    // Express provides `settings.views` which is the path to the views dir that
    // the developer set on the Express app. When this value exists, it's used
    // to compute the view's name.
    var view;
    var viewsPath = options.settings && options.settings.views;
    if (viewsPath) {
        view = this._getTemplateName(path.relative(viewsPath, viewPath));
    }

    // Merge render-level and instance-level helpers together.
    var helpers = utils.assign({}, this.helpers, options.helpers);

    // Merge render-level and instance-level partials together.
    var partials = Promise.all([
        this.getPartials({ cache: options.cache }),
        Promise.resolve(options.partials),
    ]).then(function (partials) {
        return utils.assign.apply(null, [{}].concat(partials));
    });

    // Pluck-out ExpressHandlebars-specific options and Handlebars-specific
    // rendering options.
    options = {
        cache: options.cache,
        view: view,
        layout: 'layout' in options ? options.layout : this.defaultLayout,

        data: options.data,
        helpers: helpers,
        partials: partials,
    };

    this.render(viewPath, context, options)
        .then(function (body) {
            var layoutPath = this._resolveLayoutPath(options.layout);

            if (layoutPath) {

                var _body = this._getTemplateArray(layoutPath, options).then(function (templateAr) {
                    var _template = context.template;
                    var templateOut = [];
                    var last = 0;
                    //var rex = /<REACT.*"(.*)".*\/>/gm;
                    //var myArray;
                    var fileToEnd = null;

                    var seo = context.seo
                    if (_template.seo != null)
                        seo = _.extend(context.seo, _template.seo);

                    for(var pos in templateAr) {
                        var myArray = templateAr[pos];
                        templateOut.push(myArray.input.substr(last, myArray.index - last));
                        last = myArray.index + myArray[0].length;
                        fileToEnd = myArray.input.substr(last, myArray.input.length - last);
                        var component = null
                        if (myArray[1] == "REACT") {
                            if (myArray[2] == 'body') {
                                component = body;
                            }
                            else if (_template.renderComponent != null) {
                                //var _comp = React.createElement(_template.renderSection(myArray[1]), reqContext, {});
                                //if (_comp != null) {
                                //    component = ReactDOMServer.renderToStaticMarkup(_comp);
                                //}
                            }
                            else if (_template.renderRaw != null) {
                                //var _comp = React.createElement(_template.renderSection(myArray[1]), reqContext, {});
                                //if (_comp != null) {
                                component = _template.renderRaw(myArray[2], utils.assign({}, context, options));
                                //}
                            }
                        } else if (seo && myArray[3] == "SEO") {

                            switch (myArray[4].toUpperCase()) {
                                case "TITLE":
                                    if (seo.title)
                                        component = "<title>" + seo.title + "</title>";
                                    break;
                                case "DESCRIPTION":
                                    if (seo.description)
                                        component = "<meta name=\"description\" content=\"" + seo.description + "\">";
                                    break;
                                case "KEYWORDS":
                                    if (seo.keywords)
                                        component = "<meta name=\"keywords\" content=\"" + seo.keywords + "\">";
                                    break;
                            }
                        }
                        else if (myArray[7] == "BUILDID") {
                            component = "v=" + context.build;
                        }
                        else if (seo && seo.title && myArray.length > 0 && myArray[5].toUpperCase() == "TITLE") {
                            /// Don't destroy the title tag if not needed
                            component = myArray[0];
                            component = "<title>" + seo.title + "</title>";
                        }

                        if (component != null) {
                            templateOut.push(component);
                        }

                    }
                    templateOut.push(fileToEnd);
                    return templateOut.join('');;
                });

                //return this.render(
                //    layoutPath,
                //        utils.assign({}, context, { body: body }),
                //        utils.assign({}, options, { layout: undefined })
                //);
                return _body;

            }

            return body;
        }.bind(this))
        .then(utils.passValue(callback))
        .catch(utils.passError(callback));
};

// -- Protected Hooks ----------------------------------------------------------

//ExpressReactServerPages.prototype._compileTemplate = function (template, options) {
//    return this.handlebars.compile(template, options);
//};

//ExpressReactServerPages.prototype._precompileTemplate = function (template, options) {
//    return this.handlebars.precompile(template, options);
//};

ExpressReactServerPages.prototype._renderTemplate = function (template, context, options) {
    if (template && template.renderIsoMorphic) {
        /// TO USE RENDER TO STRING SO WE CAN ISOMORPHIC WHEN NEEDED
        return ReactDOMServer.renderToString(React.createElement(template, utils.assign({}, context, options), {}));
    }
    else {
        return ReactDOMServer.renderToStaticMarkup(React.createElement(template, utils.assign({}, context, options), {}));
    }
};

// -- Private ------------------------------------------------------------------

ExpressReactServerPages.prototype._getDir = function (dirPath, options) {
    dirPath = path.resolve(dirPath);
    options || (options = {});

    var cache = this._fsCache;
    var dir = options.cache && cache[dirPath];

    if (dir) {
        return dir.then(function (dir) {
            return dir.concat();
        });
    }

    var pattern = '**/*' + this.extname;

    // Optimistically cache dir promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    dir = cache[dirPath] = new Promise(function (resolve, reject) {
        glob(pattern, {
            cwd: dirPath,
            follow: true
        }, function (err, dir) {
            if (err) {
                reject(err);
            } else {
                resolve(dir);
            }
        });
    });

    return dir.then(function (dir) {
        return dir.concat();
    }).catch(function (err) {
        delete cache[dirPath];
        throw err;
    });
};

ExpressReactServerPages.prototype._getTemplateArray = function (filePath, options) {

    const rex = /<(REACT).*"(.*)".*\/>|<(SEO).*"(.*)".*\/>|<([title|TITLE]+).*>(.*)<\/.*[title|TITLE]>|\{\{(BUILDID)\}\}/gm;

    filePath = path.resolve(filePath);
    options || (options = {});

    var cache = this._fsCache;
    var file = options.cache && cache[filePath];

    if (file) {
        return file;
    }

    // Optimistically cache file promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    file = cache[filePath] = new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, file) {
            if (err) {
                reject(err);
            } else {
                var tempAr = [];
                var tempArItem = null;
                while((tempArItem = rex.exec(file)) != null)
                {
                    tempAr.push(tempArItem);
                }
                resolve(tempAr);
            }
        });
    });

    return file.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressReactServerPages.prototype._getFile = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var cache = this._fsCache;
    var file = options.cache && cache[filePath];

    if (file) {
        return file;
    }

    // Optimistically cache file promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    file = cache[filePath] = new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, file) {
            if (err) {
                reject(err);
            } else {
                resolve(file);
            }
        });
    });

    return file.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressReactServerPages.prototype._getTemplateName = function (filePath, namespace) {
    var extRegex = new RegExp(this.extname + '$');
    var name = filePath.replace(extRegex, '');

    if (namespace) {
        name = namespace + '/' + name;
    }

    return name;
};

ExpressReactServerPages.prototype._resolveLayoutPath = function (layoutPath) {
    if (!layoutPath) {
        return null;
    }

    if (!path.extname(layoutPath)) {
        layoutPath += this.wrapperExt;
    }

    return path.resolve(this.layoutsDir, layoutPath);
};
<commit_msg>updates to support dynamic titles at runtime<commit_after>/*
 * Copyright (c) 2016, Rogassi Ent. All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

'use strict';

var Promise = global.Promise || require('promise');
var _ = require('underscore');
var React = require('react');
var ReactDOM = require('react-dom');
var ReactDOMServer = require('react-dom/server');

var glob = require('glob');
//var Handlebars = require('handlebars');
var fs = require('graceful-fs');
var path = require('path');

var utils = require('./utils');

module.exports = ExpressReactServerPages;

// -----------------------------------------------------------------------------

function ExpressReactServerPages(config) {
    // Config properties with defaults.
    utils.assign(this, {
        //handlebars     : Handlebars,
        extname: '.rt',
        wrapperExt: '.html',
        layoutsDir: 'views/layouts/',
        partialsDir: 'views/partials/',
        defaultLayout: undefined,
        helpers: undefined,
        compilerOptions: undefined,
    }, config);

    // Express view engine integration point.
    this.engine = this.renderView.bind(this);

    // Normalize `extname`.
    if (this.extname.charAt(0) !== '.') {
        this.extname = '.' + this.extname;
    }

    // Internal caches of compiled and precompiled templates.
    this.compiled = Object.create(null);
    this.precompiled = Object.create(null);

    // Private internal file system cache.
    this._fsCache = Object.create(null);
}

ExpressReactServerPages.prototype.getPartials = function (options) {
    var partialsDirs = Array.isArray(this.partialsDir) ?
        this.partialsDir : [this.partialsDir];

    partialsDirs = partialsDirs.map(function (dir) {
        var dirPath;
        var dirTemplates;
        var dirNamespace;

        // Support `partialsDir` collection with object entries that contain a
        // templates promise and a namespace.
        if (typeof dir === 'string') {
            dirPath = dir;
        } else if (typeof dir === 'object') {
            dirTemplates = dir.templates;
            dirNamespace = dir.namespace;
            dirPath = dir.dir;
        }

        // We must have some path to templates, or templates themselves.
        if (!(dirPath || dirTemplates)) {
            throw new Error('A partials dir must be a string or config object');
        }

        // Make sure we're have a promise for the templates.
        var templatesPromise = dirTemplates ? Promise.resolve(dirTemplates) :
            this.getTemplates(dirPath, options);

        return templatesPromise.then(function (templates) {
            return {
                templates: templates,
                namespace: dirNamespace,
            };
        });
    }, this);

    return Promise.all(partialsDirs).then(function (dirs) {
        var getTemplateName = this._getTemplateName.bind(this);

        return dirs.reduce(function (partials, dir) {
            var templates = dir.templates;
            var namespace = dir.namespace;
            var filePaths = Object.keys(templates);

            filePaths.forEach(function (filePath) {
                var partialName = getTemplateName(filePath, namespace);
                partials[partialName] = templates[filePath];
            });

            return partials;
        }, {});
    }.bind(this));
};

ExpressReactServerPages.prototype.getTemplate = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var precompiled = options.precompiled;
    var cache = precompiled ? this.precompiled : this.compiled;
    var template = options.cache && cache[filePath];

    if (template) {
        return template;
    }

    //// Optimistically cache template promise to reduce file system I/O, but
    //// remove from cache if there was a problem.
    //template = cache[filePath] = this._getFile(filePath, { cache: options.cache })
    //    .then(function (file) {
    //    /// WE DO NOT DO THIS
    //    //if (precompiled) {
    //    //    return this._precompileTemplate(file, this.compilerOptions);
    //    //}
    //    return this._compileTemplate(file, this.compilerOptions);
    //}.bind(this));

    var _tmp = require(filePath.replace(/\.rt.html/ig, '.js').replace(/\.rt/ig, '.js'));

    /// DEAL WITH ES6
    if (_tmp) {
        if (_tmp.__esModule) {
            template = cache[filePath] = _tmp.default;
        }
        else {
            template = cache[filePath] = _tmp;
        }
    }
    /// THIS IS PRETTY DIRTY
    if (!options.cache) {
        delete cache[filePath]
        delete require.cache[filePath];
        delete require.cache[filePath.replace(/\.js/, '-rt.js')];
    }

    //return template.catch(function (err) {
    //    delete cache[filePath];
    //    throw err;
    //});
    return template;
};

ExpressReactServerPages.prototype.getTemplates = function (dirPath, options) {
    options || (options = {});
    var cache = options.cache;

    return this._getDir(dirPath, { cache: cache }).then(function (filePaths) {
        var templates = filePaths.map(function (filePath) {
            return this.getTemplate(path.join(dirPath, filePath), options);
        }, this);

        return Promise.all(templates).then(function (templates) {
            return filePaths.reduce(function (hash, filePath, i) {
                hash[filePath] = templates[i];
                return hash;
            }, {});
        });
    }.bind(this));
};

ExpressReactServerPages.prototype.render = function (filePath, context, options) {
    options || (options = {});

    return Promise.all([
        this.getTemplate(filePath, { cache: options.cache }),
        options.partials || this.getPartials({ cache: options.cache }),
    ]).then(function (templates) {
        var template = templates[0];
        var partials = templates[1];
        var helpers = options.helpers || this.helpers;

        // DOES TEMPLATE EXPOSE OVERRIDE SETTINGS ?

        // DOES CURRENT DIRECTORY OVERRIDE SETTINGS ?

        // RECURSE THE DIRECTORY UP UNTIL WE FIND A OVERRIDE SETTINGS ?

        // DO NOTHING IF WE DO NOT FIND ONE AND USE THE OPTIONS SETTING

        context.template = template;
        // Add ExpressHandlebars metadata to the data channel so that it's
        // accessible within the templates and helpers, namespaced under:
        // `@exprsp.*`
        var data = utils.assign({}, options.data, {
            exprsp: utils.assign({}, options, {
                filePath: filePath,
                helpers: helpers,
                partials: partials,
            }),
        });

        return this._renderTemplate(template, context, {
            data: data,
            helpers: helpers,
            partials: partials,
        });
    }.bind(this));
};

ExpressReactServerPages.prototype.renderView = function (viewPath, options, callback) {
    options || (options = {});

    var context = options;

    // Express provides `settings.views` which is the path to the views dir that
    // the developer set on the Express app. When this value exists, it's used
    // to compute the view's name.
    var view;
    var viewsPath = options.settings && options.settings.views;
    if (viewsPath) {
        view = this._getTemplateName(path.relative(viewsPath, viewPath));
    }

    // Merge render-level and instance-level helpers together.
    var helpers = utils.assign({}, this.helpers, options.helpers);

    // Merge render-level and instance-level partials together.
    var partials = Promise.all([
        this.getPartials({ cache: options.cache }),
        Promise.resolve(options.partials),
    ]).then(function (partials) {
        return utils.assign.apply(null, [{}].concat(partials));
    });

    // Pluck-out ExpressHandlebars-specific options and Handlebars-specific
    // rendering options.
    options = {
        cache: options.cache,
        view: view,
        layout: 'layout' in options ? options.layout : this.defaultLayout,

        data: options.data,
        helpers: helpers,
        partials: partials,
    };

    this.render(viewPath, context, options)
        .then(function (body) {
            var layoutPath = this._resolveLayoutPath(options.layout);

            if (layoutPath) {

                var _body = this._getTemplateArray(layoutPath, options).then(function (templateAr) {
                    var _template = context.template;
                    var templateOut = [];
                    var last = 0;
                    //var rex = /<REACT.*"(.*)".*\/>/gm;
                    //var myArray;
                    var fileToEnd = null;

                    var seo = context.seo
                    if (_template.seo != null)
                        seo = _.extend(context.seo, _template.seo);

                    for (var pos in templateAr) {
                        var myArray = templateAr[pos];
                        templateOut.push(myArray.input.substr(last, myArray.index - last));
                        last = myArray.index + myArray[0].length;
                        fileToEnd = myArray.input.substr(last, myArray.input.length - last);
                        var component = null
                        var mergedContext = null;
                        if (myArray[1] == "REACT") {

                            if (mergedContext == null) {
                                mergedContext = utils.assign({}, context, options)
                            }

                            if (myArray[2] == 'body') {
                                component = body;
                            }
                            else if (_template.renderComponent != null) {
                                //var _comp = React.createElement(_template.renderSection(myArray[1]), reqContext, {});
                                //if (_comp != null) {
                                //    component = ReactDOMServer.renderToStaticMarkup(_comp);
                                //}
                            }
                            else if (_template.renderRaw != null) {
                                //var _comp = React.createElement(_template.renderSection(myArray[1]), reqContext, {});
                                //if (_comp != null) {
                                component = _template.renderRaw(myArray[2], mergedContext);
                                //}
                            }
                        } else if (seo && myArray[3] == "SEO") {

                            if (mergedContext == null) {
                                mergedContext = utils.assign({}, context, options)
                            }

                            switch (myArray[4].toUpperCase()) {
                                case "TITLE":
                                    if (_template.renderTitle != null) {
                                        component = _template.renderTitle(myArray[4], mergedContext);
                                        if (component != null) {
                                            component = "<title>" + component + "</title>";
                                        }
                                    }
                                    if (component == null && seo.title)
                                        component = "<title>" + seo.title + "</title>";
                                    break;
                                case "DESCRIPTION":
                                    if (_template.renderDescription != null) {
                                        component = _template.renderDescription(myArray[4], mergedContext);
                                        if (component != null) {
                                            component = "<meta name=\"description\" content=\"" + component + "\">";
                                        }
                                    }
                                    if (component == null && seo.description)
                                        component = "<meta name=\"description\" content=\"" + seo.description + "\">";
                                    break;
                                case "KEYWORDS":
                                    if (_template.renderKeywords != null) {
                                        component = _template.renderKeywords(myArray[4], mergedContext);
                                        if (component != null) {
                                            component = "<meta name=\"keywords\" content=\"" + component + "\">";
                                        }
                                    }
                                    if (component == null && seo.keywords)
                                        component = "<meta name=\"keywords\" content=\"" + seo.keywords + "\">";
                                    break;
                            }
                        }
                        else if (myArray[7] == "BUILDID") {
                            component = "v=" + context.build;
                        }
                        else if (seo && seo.title && myArray.length > 0 && myArray[5].toUpperCase() == "TITLE") {
                            /// Don't destroy the title tag if not needed
                            if (_template.renderTitle != null) {
                                component = _template.renderTitle(myArray[4], mergedContext);
                                if (component != null) {
                                    component = "<title>" + component + "</title>";
                                }
                            }
                            if (component == null && seo.title) {
                                component = "<title>" + seo.title + "</title>";
                            }
                            else {
                                /// Output original title tag - worst case scenario
                                component = myArray[0];
                            }
                        }

                        if (component != null) {
                            templateOut.push(component);
                        }

                    }
                    templateOut.push(fileToEnd);
                    return templateOut.join('');;
                });

                //return this.render(
                //    layoutPath,
                //        utils.assign({}, context, { body: body }),
                //        utils.assign({}, options, { layout: undefined })
                //);
                return _body;

            }

            return body;
        }.bind(this))
        .then(utils.passValue(callback))
        .catch(utils.passError(callback));
};

// -- Protected Hooks ----------------------------------------------------------

//ExpressReactServerPages.prototype._compileTemplate = function (template, options) {
//    return this.handlebars.compile(template, options);
//};

//ExpressReactServerPages.prototype._precompileTemplate = function (template, options) {
//    return this.handlebars.precompile(template, options);
//};

ExpressReactServerPages.prototype._renderTemplate = function (template, context, options) {
    if (template && template.renderIsoMorphic) {
        /// TO USE RENDER TO STRING SO WE CAN ISOMORPHIC WHEN NEEDED
        return ReactDOMServer.renderToString(React.createElement(template, utils.assign({}, context, options), {}));
    }
    else {
        return ReactDOMServer.renderToStaticMarkup(React.createElement(template, utils.assign({}, context, options), {}));
    }
};

// -- Private ------------------------------------------------------------------

ExpressReactServerPages.prototype._getDir = function (dirPath, options) {
    dirPath = path.resolve(dirPath);
    options || (options = {});

    var cache = this._fsCache;
    var dir = options.cache && cache[dirPath];

    if (dir) {
        return dir.then(function (dir) {
            return dir.concat();
        });
    }

    var pattern = '**/*' + this.extname;

    // Optimistically cache dir promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    dir = cache[dirPath] = new Promise(function (resolve, reject) {
        glob(pattern, {
            cwd: dirPath,
            follow: true
        }, function (err, dir) {
            if (err) {
                reject(err);
            } else {
                resolve(dir);
            }
        });
    });

    return dir.then(function (dir) {
        return dir.concat();
    }).catch(function (err) {
        delete cache[dirPath];
        throw err;
    });
};

ExpressReactServerPages.prototype._getTemplateArray = function (filePath, options) {

    const rex = /<(REACT).*"(.*)".*\/>|<(SEO).*"(.*)".*\/>|<([title|TITLE]+).*>(.*)<\/.*[title|TITLE]>|\{\{(BUILDID)\}\}/gm;

    filePath = path.resolve(filePath);
    options || (options = {});

    var cache = this._fsCache;
    var file = options.cache && cache[filePath];

    if (file) {
        return file;
    }

    // Optimistically cache file promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    file = cache[filePath] = new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, file) {
            if (err) {
                reject(err);
            } else {
                var tempAr = [];
                var tempArItem = null;
                while ((tempArItem = rex.exec(file)) != null) {
                    tempAr.push(tempArItem);
                }
                resolve(tempAr);
            }
        });
    });

    return file.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressReactServerPages.prototype._getFile = function (filePath, options) {
    filePath = path.resolve(filePath);
    options || (options = {});

    var cache = this._fsCache;
    var file = options.cache && cache[filePath];

    if (file) {
        return file;
    }

    // Optimistically cache file promise to reduce file system I/O, but remove
    // from cache if there was a problem.
    file = cache[filePath] = new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, file) {
            if (err) {
                reject(err);
            } else {
                resolve(file);
            }
        });
    });

    return file.catch(function (err) {
        delete cache[filePath];
        throw err;
    });
};

ExpressReactServerPages.prototype._getTemplateName = function (filePath, namespace) {
    var extRegex = new RegExp(this.extname + '$');
    var name = filePath.replace(extRegex, '');

    if (namespace) {
        name = namespace + '/' + name;
    }

    return name;
};

ExpressReactServerPages.prototype._resolveLayoutPath = function (layoutPath) {
    if (!layoutPath) {
        return null;
    }

    if (!path.extname(layoutPath)) {
        layoutPath += this.wrapperExt;
    }

    return path.resolve(this.layoutsDir, layoutPath);
};
<|endoftext|>


<commit_before>'use strict';
const fs = require('fs');
const path = require('path');
const jsdiff = require('diff');
const {SError} = require('error');
const GroupedQueue = require('grouped-queue');

const binaryDiff = require('./binary-diff');

class AbortedError extends SError {}

class ConflicterConflictError extends SError {}

/**
 * The Conflicter is a module that can be used to detect conflict between files. Each
 * Generator file system helpers pass files through this module to make sure they don't
 * break a user file.
 *
 * When a potential conflict is detected, we prompt the user and ask them for
 * confirmation before proceeding with the actual write.
 *
 * @constructor
 * @property {Boolean} force - same as the constructor argument
 *
 * @param  {TerminalAdapter} adapter - The generator adapter
 * @param  {Boolean} force - When set to true, we won't check for conflict. (the
 *                           conflicter become a passthrough)
 * @param  {Boolean} bail - When set to true, we will abort on first conflict. (used for
 *                           testing reproducibility)
 */
class Conflicter {
  constructor(adapter, force, options = {}) {
    if (typeof options === 'boolean') {
      this.bail = options;
    } else {
      this.bail = options.bail;
      this.ignoreWhitespace = options.ignoreWhitespace;
      this.skipRegenerate = options.skipRegenerate;
      this.dryRun = options.dryRun;
    }

    this.force = force === true;
    this.adapter = adapter;
    this.conflicts = [];

    this.diffOptions = options.diffOptions;

    if (this.bail) {
      // Set ignoreWhitespace as true by default for bail.
      // Probably just testing, so don't override.
      this.ignoreWhitespace = true;
      this.skipRegenerate = true;
    }

    if (this.dryRun) {
      // Ignore whitespace changes with "ignoreWhitespace === true" option
      this.skipRegenerate = true;
    }

    this.queue = new GroupedQueue(['log', 'conflicts'], false);
  }

  log(file) {
    const logStatus = file.conflicterLog || file.conflicter;
    this._log(logStatus, path.relative(process.cwd(), file.path));
  }

  _log(logStatus, ...args) {
    const log = typeof logStatus === 'function' ? logStatus : this.adapter.log[logStatus].bind(this.adapter.log);
    if (log) {
      this.queue.add('log', done => {
        log(...args);
        done();
      });
      this.queue.start();
    }
  }

  /**
   * Print the file differences to console
   *
   * @param  {Object}   file File object respecting this interface: { path, contents }
   */
  _printDiff(file, queue = false) {
    if (file.binary === undefined) {
      file.binary = binaryDiff.isBinary(file.path, file.contents);
    }

    let args;
    let logFunction;
    if (file.binary) {
      logFunction = this.adapter.log.writeln.bind(this.adapter.log);
      args = [binaryDiff.diff(file.path, file.contents)];
    } else {
      const existing = fs.readFileSync(file.path);
      logFunction = this.adapter.diff.bind(this.adapter);
      args = [
        existing.toString(),
        (file.contents || '').toString(),
        file.conflicterChanges
      ];
    }
    if (queue) {
      this._log(logFunction, ...args);
    } else {
      logFunction(...args);
    }
  }

  /**
   * Detect conflicts between file contents at `filepath` with the `contents` passed to the
   * function
   *
   * If `filepath` points to a folder, we'll always return true.
   *
   * Based on detect-conflict module
   *
   * @param  {Object}   file File object respecting this interface: { path, contents }
   * @return {Boolean} `true` if there's a conflict, `false` otherwise.
   */
  _detectConflict(file) {
    let {contents} = file;
    const filepath = path.resolve(file.path);

    // If file path point to a directory, then it's not safe to write
    if (fs.statSync(filepath).isDirectory()) {
      return true;
    }

    if (file.binary === undefined) {
      file.binary = binaryDiff.isBinary(file.path, file.contents);
    }

    const actual = fs.readFileSync(path.resolve(filepath));

    if (!(contents instanceof Buffer)) {
      contents = Buffer.from(contents || '', 'utf8');
    }

    if (file.binary) {
      return actual.toString('hex') !== contents.toString('hex');
    }

    let modified;
    let changes;
    if (this.ignoreWhitespace) {
      changes = jsdiff.diffWords(
        actual.toString(),
        contents.toString(),
        this.diffOptions
      );
      modified = changes.some(change => change.value && change.value.trim() && (change.added || change.removed));
    } else {
      changes = jsdiff.diffLines(
        actual.toString(),
        contents.toString(),
        this.diffOptions
      );
      modified = changes.length > 1 || changes[0].added || changes[0].removed;
    }
    file.conflicterChanges = changes;
    return modified;
  }

  /**
   * Check if a file conflict with the current version on the user disk
   *
   * A basic check is done to see if the file exists, if it does:
   *
   *   1. Read its content from  `fs`
   *   2. Compare it with the provided content
   *   3. If identical, mark it as is and skip the check
   *   4. If diverged, prepare and show up the file collision menu
   *
   * @param {Object} file - Vinyl file
   * @return  {Promise} Promise a status string ('identical', 'create',
   *                       'skip', 'force')
   */
  checkForCollision(file) {
    const rfilepath = path.relative(process.cwd(), file.path);
    if (file.conflicter) {
      this._log(file.conflicter, rfilepath);
      return Promise.resolve(file);
    }

    if (!fs.existsSync(file.path)) {
      if (this.bail) {
        this._log('writeln', 'Aborting ...');
        return Promise.reject(ConflicterConflictError.create('Process aborted by conflict'));
      }

      this._log('create', rfilepath);
      file.conflicter = this.dryRun ? 'skip' : 'create';
      return Promise.resolve(file);
    }

    if (this.force) {
      this._log('force', rfilepath);
      file.conflicter = 'force';
      return Promise.resolve(file);
    }

    if (this._detectConflict(file)) {
      if (this.bail) {
        this.adapter.log.conflict(rfilepath);
        this._printDiff(file);
        this.adapter.log.writeln('Aborting ...');
        return Promise.reject(ConflicterConflictError.create('Process aborted by conflict'));
      }

      if (this.dryRun) {
        this._log('conflict', rfilepath);
        this._printDiff(file, true);
        file.conflicter = 'skip';
        return Promise.resolve(file);
      }

      return new Promise((resolve, reject) => {
        this.queue.add('conflicts', next => {
          this.adapter.log.conflict(rfilepath);
          return this._ask(file, 1).then(action => {
            this.adapter.log[action || 'force'](rfilepath);
            file.conflicter = action;
            next();
            resolve(file);
          }).catch(reject);
        });
        this.queue.start();
      });
    }
    this._log('identical', rfilepath);
    if (this.skipRegenerate) {
      file.conflicter = 'skip';
      return Promise.resolve(file);
    }

    file.conflicter = 'identical';
    return Promise.resolve(file);
  }

  /**
   * Actual prompting logic
   * @private
   * @param {Object} file vinyl file object
   * @param {Number} counter prompts
   */
  _ask(file, counter) {
    if (file.conflicter) {
      return Promise.resolve(file.conflicter);
    }
    const rfilepath = path.relative(process.cwd(), file.path);
    const prompt = {
      name: 'action',
      type: 'expand',
      message: `Overwrite ${rfilepath}?`,
      choices: [
        {
          key: 'y',
          name: 'overwrite',
          value: 'write'
        },
        {
          key: 'n',
          name: 'do not overwrite',
          value: 'skip'
        },
        {
          key: 'a',
          name: 'overwrite this and all others',
          value: 'force'
        },
        {
          key: 'x',
          name: 'abort',
          value: 'abort'
        }
      ]
    };

    // Only offer diff option for files
    if (fs.statSync(file.path).isFile()) {
      prompt.choices.push({
        key: 'd',
        name: 'show the differences between the old and the new',
        value: 'diff'
      });
    }

    return this.adapter.prompt([prompt]).then(result => {
      if (result.action === 'abort') {
        this.adapter.log.writeln('Aborting ...');
        throw AbortedError.create('Process aborted by user');
      }

      if (result.action === 'diff') {
        this._printDiff(file);

        counter++;
        if (counter === 5) {
          throw new Error(`Recursive error ${prompt.message}`);
        }

        return this._ask(file, counter);
      }

      if (result.action === 'force') {
        this.force = true;
      }

      if (result.action === 'write') {
        return 'force';
      }

      return result.action;
    });
  }
}

module.exports = Conflicter;
<commit_msg>Fixes to _log.<commit_after>'use strict';
const fs = require('fs');
const path = require('path');
const jsdiff = require('diff');
const {SError} = require('error');
const GroupedQueue = require('grouped-queue');

const binaryDiff = require('./binary-diff');

class AbortedError extends SError {}

class ConflicterConflictError extends SError {}

/**
 * The Conflicter is a module that can be used to detect conflict between files. Each
 * Generator file system helpers pass files through this module to make sure they don't
 * break a user file.
 *
 * When a potential conflict is detected, we prompt the user and ask them for
 * confirmation before proceeding with the actual write.
 *
 * @constructor
 * @property {Boolean} force - same as the constructor argument
 *
 * @param  {TerminalAdapter} adapter - The generator adapter
 * @param  {Boolean} force - When set to true, we won't check for conflict. (the
 *                           conflicter become a passthrough)
 * @param  {Boolean} bail - When set to true, we will abort on first conflict. (used for
 *                           testing reproducibility)
 */
class Conflicter {
  constructor(adapter, force, options = {}) {
    if (typeof options === 'boolean') {
      this.bail = options;
    } else {
      this.bail = options.bail;
      this.ignoreWhitespace = options.ignoreWhitespace;
      this.skipRegenerate = options.skipRegenerate;
      this.dryRun = options.dryRun;
    }

    this.force = force === true;
    this.adapter = adapter;
    this.conflicts = [];

    this.diffOptions = options.diffOptions;

    if (this.bail) {
      // Set ignoreWhitespace as true by default for bail.
      // Probably just testing, so don't override.
      this.ignoreWhitespace = true;
      this.skipRegenerate = true;
    }

    if (this.dryRun) {
      // Ignore whitespace changes with "ignoreWhitespace === true" option
      this.skipRegenerate = true;
    }

    this.queue = new GroupedQueue(['log', 'conflicts'], false);
  }

  log(file) {
    const logStatus = file.conflicterLog || file.conflicter;
    this._log(logStatus, path.relative(process.cwd(), file.path));
  }

  _log(logStatus, ...args) {
    let log;
    if (typeof logStatus === 'function') {
      log = logStatus;
    } else {
      log = this.adapter.log[logStatus];
      if (log) {
        log = log.bind(this.adapter.log);
      }
    }
    if (log) {
      this.queue.add('log', done => {
        log(...args);
        done();
      });
      this.queue.start();
    }
  }

  /**
   * Print the file differences to console
   *
   * @param  {Object}   file File object respecting this interface: { path, contents }
   */
  _printDiff(file, queue = false) {
    if (file.binary === undefined) {
      file.binary = binaryDiff.isBinary(file.path, file.contents);
    }

    let args;
    let logFunction;
    if (file.binary) {
      logFunction = this.adapter.log.writeln.bind(this.adapter.log);
      args = [binaryDiff.diff(file.path, file.contents)];
    } else {
      const existing = fs.readFileSync(file.path);
      logFunction = this.adapter.diff.bind(this.adapter);
      args = [
        existing.toString(),
        (file.contents || '').toString(),
        file.conflicterChanges
      ];
    }
    if (queue) {
      this._log(logFunction, ...args);
    } else {
      logFunction(...args);
    }
  }

  /**
   * Detect conflicts between file contents at `filepath` with the `contents` passed to the
   * function
   *
   * If `filepath` points to a folder, we'll always return true.
   *
   * Based on detect-conflict module
   *
   * @param  {Object}   file File object respecting this interface: { path, contents }
   * @return {Boolean} `true` if there's a conflict, `false` otherwise.
   */
  _detectConflict(file) {
    let {contents} = file;
    const filepath = path.resolve(file.path);

    // If file path point to a directory, then it's not safe to write
    if (fs.statSync(filepath).isDirectory()) {
      return true;
    }

    if (file.binary === undefined) {
      file.binary = binaryDiff.isBinary(file.path, file.contents);
    }

    const actual = fs.readFileSync(path.resolve(filepath));

    if (!(contents instanceof Buffer)) {
      contents = Buffer.from(contents || '', 'utf8');
    }

    if (file.binary) {
      return actual.toString('hex') !== contents.toString('hex');
    }

    let modified;
    let changes;
    if (this.ignoreWhitespace) {
      changes = jsdiff.diffWords(
        actual.toString(),
        contents.toString(),
        this.diffOptions
      );
      modified = changes.some(change => change.value && change.value.trim() && (change.added || change.removed));
    } else {
      changes = jsdiff.diffLines(
        actual.toString(),
        contents.toString(),
        this.diffOptions
      );
      modified = changes.length > 1 || changes[0].added || changes[0].removed;
    }
    file.conflicterChanges = changes;
    return modified;
  }

  /**
   * Check if a file conflict with the current version on the user disk
   *
   * A basic check is done to see if the file exists, if it does:
   *
   *   1. Read its content from  `fs`
   *   2. Compare it with the provided content
   *   3. If identical, mark it as is and skip the check
   *   4. If diverged, prepare and show up the file collision menu
   *
   * @param {Object} file - Vinyl file
   * @return  {Promise} Promise a status string ('identical', 'create',
   *                       'skip', 'force')
   */
  checkForCollision(file) {
    const rfilepath = path.relative(process.cwd(), file.path);
    if (file.conflicter) {
      this._log(file.conflicter, rfilepath);
      return Promise.resolve(file);
    }

    if (!fs.existsSync(file.path)) {
      if (this.bail) {
        this._log('writeln', 'Aborting ...');
        return Promise.reject(ConflicterConflictError.create('Process aborted by conflict'));
      }

      this._log('create', rfilepath);
      file.conflicter = this.dryRun ? 'skip' : 'create';
      return Promise.resolve(file);
    }

    if (this.force) {
      this._log('force', rfilepath);
      file.conflicter = 'force';
      return Promise.resolve(file);
    }

    if (this._detectConflict(file)) {
      if (this.bail) {
        this.adapter.log.conflict(rfilepath);
        this._printDiff(file);
        this.adapter.log.writeln('Aborting ...');
        return Promise.reject(ConflicterConflictError.create('Process aborted by conflict'));
      }

      if (this.dryRun) {
        this._log('conflict', rfilepath);
        this._printDiff(file, true);
        file.conflicter = 'skip';
        return Promise.resolve(file);
      }

      return new Promise((resolve, reject) => {
        this.queue.add('conflicts', next => {
          this.adapter.log.conflict(rfilepath);
          return this._ask(file, 1).then(action => {
            this.adapter.log[action || 'force'](rfilepath);
            file.conflicter = action;
            next();
            resolve(file);
          }).catch(reject);
        });
        this.queue.start();
      });
    }
    this._log('identical', rfilepath);
    if (this.skipRegenerate) {
      file.conflicter = 'skip';
      return Promise.resolve(file);
    }

    file.conflicter = 'identical';
    return Promise.resolve(file);
  }

  /**
   * Actual prompting logic
   * @private
   * @param {Object} file vinyl file object
   * @param {Number} counter prompts
   */
  _ask(file, counter) {
    if (file.conflicter) {
      return Promise.resolve(file.conflicter);
    }
    const rfilepath = path.relative(process.cwd(), file.path);
    const prompt = {
      name: 'action',
      type: 'expand',
      message: `Overwrite ${rfilepath}?`,
      choices: [
        {
          key: 'y',
          name: 'overwrite',
          value: 'write'
        },
        {
          key: 'n',
          name: 'do not overwrite',
          value: 'skip'
        },
        {
          key: 'a',
          name: 'overwrite this and all others',
          value: 'force'
        },
        {
          key: 'x',
          name: 'abort',
          value: 'abort'
        }
      ]
    };

    // Only offer diff option for files
    if (fs.statSync(file.path).isFile()) {
      prompt.choices.push({
        key: 'd',
        name: 'show the differences between the old and the new',
        value: 'diff'
      });
    }

    return this.adapter.prompt([prompt]).then(result => {
      if (result.action === 'abort') {
        this.adapter.log.writeln('Aborting ...');
        throw AbortedError.create('Process aborted by user');
      }

      if (result.action === 'diff') {
        this._printDiff(file);

        counter++;
        if (counter === 5) {
          throw new Error(`Recursive error ${prompt.message}`);
        }

        return this._ask(file, counter);
      }

      if (result.action === 'force') {
        this.force = true;
      }

      if (result.action === 'write') {
        return 'force';
      }

      return result.action;
    });
  }
}

module.exports = Conflicter;
<|endoftext|>


<commit_before>import {readSource} from '../lib/xhtreqreader.js'
import * as transcribers from '../lib/transcribers.js'
import {visualize} from './visualizer.js'
import {renderArrows} from './arrows.js'

var ldtrEditor = {

  init: function () {
    this.viewDiv = document.querySelector('#view')
    this.editorArea = document.querySelector('#editor')
    this.statusDiv = document.querySelector('#status')
    this.urlInput = document.querySelector('#url')
    this.typeSelect = document.querySelector('select#typesel')
    let reload = document.querySelector('#reload')
    this.toggleedit = document.querySelector('#toggleedit')

    this.params = this.parseParams(
        window.location.search.substring(1))

    if (!this.params.url || this.params.edit) {
      document.body.classList.add('edit')
    }

    this.editorArea.onkeyup = function () {
      this.parseAndProcess()
    }.bind(this)

    const toggleEditLabel = on => {
      this.toggleedit.textContent = on ? '»' : '«'
    }
    toggleEditLabel(document.body.classList.contains('edit'))
    this.toggleedit.addEventListener('click', evt => {
      toggleEditLabel(document.body.classList.toggle('edit'))
    })

    reload.addEventListener('click', async evt => {
      this.loadData(this.urlInput.value)
    })

    for (let type in transcribers.transcribers) {
      this.typeSelect.add(new Option(type, type))
      this.typeSelect.selectedOptions[0].value
    }

    this.typeSelect.addEventListener('change', async evt => {
      let type = this.typeSelect.options[this.typeSelect.selectedIndex].value
      if (type === 'application/ld+json') {
        this.setData(JSON.stringify(this.currentData, null, 2), type)
      } else if (type === 'text/turtle' || type === 'text/trig') {
        const serializer = await import('../lib/trig/serializer.js')
        let chunks = []
        serializer.serialize(this.currentData, {write (chunk) {chunks.push(chunk)}})
        this.setData(chunks.join(''), type)
      }
    })

    if (this.params.url) {
      this.loadData(decodeURIComponent(this.params.url))
    }
  },

  parseParams: function (query) {
    return query.split(/\&/).reduce(function (map, pair) {
      var tuple = pair.split(/=/)
      map[tuple[0]] = tuple[1] || true
      return map
    }, {})
  },

  loadData: async function (url) {
    if (!url) return

    let headers = transcribers.requestHeaders()
    let { data, type } = await readSource(url, headers)

    if (data.nodeType === 9) {
      data = data.documentElement.outerHTML
    }
    this.urlInput.value = url
    this.setData(data, type)

    await this.parseAndProcess()

    // force browser to recognize dynamically generated :target
    if (window.location.hash) {
      window.location = window.location.hash
    }
  },

  setData: function (data, type) {
    this.editorArea.value = data
    this.typeSelect.selectedIndex = Array.prototype.findIndex.call(
        this.typeSelect.options, it => it.value === type)
  },

  parseData: async function () {
    let data = this.editorArea.value
    let index = this.typeSelect.selectedIndex
    let type = this.typeSelect.options[index > -1 ? index : 0].value
    let url = this.urlInput.value
    let transcribe = transcribers.transcribers[type]
    return await transcribe({data, type, base: url})
  },

  parseAndProcess: async function () {
    let data = this.editorArea.value
    var pos = this.editorArea.selectionStart
    var row = 1
    var col = 1
    for (var i=0; i < pos; i++) {
      if (data[i] === '\n') {
        row += 1
        col = 1
      } else {
        col++
      }
    }
    this.statusDiv.innerText = 'Line: '+ row +' Col: '+ col +' \n'

    let result
    try {
      result = await this.parseData()
    } catch (e) {
      if (!e.location)
        throw e
      this.statusDiv.classList.add('error')
      this.statusDiv.innerText += e.name +' at Line: '+ e.location.start.line
        +' Col: '+ e.location.start.column +'\n'+ e.message
      return
    }
    this.currentData = result

    this.statusDiv.classList.remove('error')
    this.statusDiv.innerText += "OK"

    if (this.params.indexed) {
      const indexer = await import('../lib/util/indexer.js')
      result = indexer.index(result)
    }
    visualize(this.viewDiv, result, this.params)
    renderArrows(this.viewDiv)
  }

}

window.onload = ldtrEditor.init()
<commit_msg>Guess media type from url if no known was given<commit_after>import {readSource} from '../lib/xhtreqreader.js'
import * as transcribers from '../lib/transcribers.js'
import { guessMediaType }  from '../lib/mediatypes.js'
import {visualize} from './visualizer.js'
import {renderArrows} from './arrows.js'

var ldtrEditor = {

  init: function () {
    this.viewDiv = document.querySelector('#view')
    this.editorArea = document.querySelector('#editor')
    this.statusDiv = document.querySelector('#status')
    this.urlInput = document.querySelector('#url')
    this.typeSelect = document.querySelector('select#typesel')
    let reload = document.querySelector('#reload')
    this.toggleedit = document.querySelector('#toggleedit')

    this.params = this.parseParams(
        window.location.search.substring(1))

    if (!this.params.url || this.params.edit) {
      document.body.classList.add('edit')
    }

    this.editorArea.onkeyup = function () {
      this.parseAndProcess()
    }.bind(this)

    const toggleEditLabel = on => {
      this.toggleedit.textContent = on ? '»' : '«'
    }
    toggleEditLabel(document.body.classList.contains('edit'))
    this.toggleedit.addEventListener('click', evt => {
      toggleEditLabel(document.body.classList.toggle('edit'))
    })

    reload.addEventListener('click', async evt => {
      this.loadData(this.urlInput.value)
    })

    for (let type in transcribers.transcribers) {
      this.typeSelect.add(new Option(type, type))
      this.typeSelect.selectedOptions[0].value
    }

    this.typeSelect.addEventListener('change', async evt => {
      let type = this.typeSelect.options[this.typeSelect.selectedIndex].value
      if (type === 'application/ld+json') {
        this.setData(JSON.stringify(this.currentData, null, 2), type)
      } else if (type === 'text/turtle' || type === 'text/trig') {
        const serializer = await import('../lib/trig/serializer.js')
        let chunks = []
        serializer.serialize(this.currentData, {write (chunk) {chunks.push(chunk)}})
        this.setData(chunks.join(''), type)
      }
    })

    if (this.params.url) {
      this.loadData(decodeURIComponent(this.params.url))
    }
  },

  parseParams: function (query) {
    return query.split(/\&/).reduce(function (map, pair) {
      var tuple = pair.split(/=/)
      map[tuple[0]] = tuple[1] || true
      return map
    }, {})
  },

  loadData: async function (url) {
    if (!url) return

    let headers = transcribers.requestHeaders()
    let { data, type } = await readSource(url, headers)

    if (data.nodeType === 9) {
      data = data.documentElement.outerHTML
    }
    this.urlInput.value = url
    if (!(type in transcribers.transcribers)) {
      type = guessMediaType(url)
    }
    this.setData(data, type)

    await this.parseAndProcess()

    // force browser to recognize dynamically generated :target
    if (window.location.hash) {
      window.location = window.location.hash
    }
  },

  setData: function (data, type) {
    this.editorArea.value = data
    this.typeSelect.selectedIndex = Array.prototype.findIndex.call(
        this.typeSelect.options, it => it.value === type)
  },

  parseData: async function () {
    let data = this.editorArea.value
    let index = this.typeSelect.selectedIndex
    let type = this.typeSelect.options[index > -1 ? index : 0].value
    let url = this.urlInput.value
    let transcribe = transcribers.transcribers[type]
    return await transcribe({data, type, base: url})
  },

  parseAndProcess: async function () {
    let data = this.editorArea.value
    var pos = this.editorArea.selectionStart
    var row = 1
    var col = 1
    for (var i=0; i < pos; i++) {
      if (data[i] === '\n') {
        row += 1
        col = 1
      } else {
        col++
      }
    }
    this.statusDiv.innerText = 'Line: '+ row +' Col: '+ col +' \n'

    let result
    try {
      result = await this.parseData()
    } catch (e) {
      if (!e.location)
        throw e
      this.statusDiv.classList.add('error')
      this.statusDiv.innerText += e.name +' at Line: '+ e.location.start.line
        +' Col: '+ e.location.start.column +'\n'+ e.message
      return
    }
    this.currentData = result

    this.statusDiv.classList.remove('error')
    this.statusDiv.innerText += "OK"

    if (this.params.indexed) {
      const indexer = await import('../lib/util/indexer.js')
      result = indexer.index(result)
    }
    visualize(this.viewDiv, result, this.params)
    renderArrows(this.viewDiv)
  }

}

window.onload = ldtrEditor.init()
<|endoftext|>


<commit_before>'use strict';
function populate(targetColumn, callBack) {
    var myJson = {};
    $('.thing').each(
        function () {
            var word = $(this).children().find('.text').first().html();
            var button = $(this).find('button')[targetColumn - 1];
            myJson[word] = {
                'word': word,
                'id': $(this).data('data-thing-id'),
                'hasFile': !($(button).hasClass('disabled'))
            };
        });

    callBack(myJson);
}

function comunication (myWord) {
    if( Object.getOwnPropertyNames(myWord).length > 0){
        self.port.emit('list populated', myWord);
    }
    else{
        self.port.emit('list empty');
    }
}

self.port.on('filetype', function () {
    // get asked to return which filetype the user is uploading
    if( $('#choice').prop('checked') ) {
        self.port.emit('audio');
    } else {
        self.port.emit('image');
    }
});

self.port.on('check', function (data) {
    // search for the correct column to upload
    if ($('td').filter(data.tipo)) {
        self.port.emit('targetColumn', {
            targetColumn : $('td').filter(data.tipo).attr('data-key')
        });
    } else {
        self.port.emit('No appropriate column', data);
    }

} );

self.port.on('populate', function (data) {
    populate(data.targetColumn, comunication);
});<commit_msg>from $().filter() to $().find()<commit_after>'use strict';
function populate(targetColumn, callBack) {
    var myJson = {};
    $('.thing').each(
        function () {
            var word = $(this).children().find('.text').first().html();
            var button = $(this).find('button')[targetColumn - 1];
            myJson[word] = {
                'word': word,
                'id': $(this).data('data-thing-id'),
                'hasFile': !($(button).hasClass('disabled'))
            };
        });

    callBack(myJson);
}

function comunication (myWord) {
    if( Object.getOwnPropertyNames(myWord).length > 0){
        self.port.emit('list populated', myWord);
    }
    else{
        self.port.emit('list empty');
    }
}

self.port.on('filetype', function () {
    // get asked to return which filetype the user is uploading
    if( $('#choice').prop('checked') ) {
        self.port.emit('audio');
    } else {
        self.port.emit('image');
    }
});

self.port.on('check', function (data) {
    // search for the correct column to upload
    if ($('td').find(data.tipo)) {
        self.port.emit('targetColumn', {
            'targetColumn' : $('td').find(data.tipo).attr('data-key')
        });
    } else {
        self.port.emit('No appropriate column', data);
    }

} );

self.port.on('populate', function (data) {
    populate(data.targetColumn, comunication);
});<|endoftext|>


<commit_before>// Test the widget framework.
casper.notebook_test(function () {
    var index;

    // Test widget dependencies ////////////////////////////////////////////////
    this.then(function () {

        // Check if the WidgetManager class is defined.
        this.test.assert(this.evaluate(function() {
            return IPython.WidgetManager != undefined;
        }), 'WidgetManager class is defined');
    });

    index = this.append_cell(
        'from IPython.html import widgets\n' +
        'from IPython.display import display, clear_output\n' +
        'print("Success")');
    this.execute_cell_then(index);

    this.wait(500); // Wait for require.js async callbacks to load dependencies.

    this.then(function () {
        // Check if the widget manager has been instanciated.
        this.test.assert(this.evaluate(function() {
            return IPython.widget_manager != undefined;
        }), 'Notebook widget manager instanciated');
    });

    // Check widget mapping ////////////////////////////////////////////////////
    index = this.append_cell(
        'names = [name for name in dir(widgets)' +
        ' if name.endswith("Widget") and name!= "Widget"]\n' +
        'for name in names:\n' +
        '    print(name)\n');
    this.execute_cell_then(index, function(index){

        // Get the widget names that are registered with the widget manager.  Assume
        // a 1 to 1 mapping of model and widgets names (model names just have 'model'
        // suffixed).
        var javascript_names = this.evaluate(function () {
            names = [];
            for (var name in IPython.widget_manager.widget_model_types) {
                names.push(name.replace('Model',''));
            }
            return names;
        });

        // Get the widget names registered in python.
        var python_names = this.get_output_cell(index).text.split('\n');

        // Make sure the two lists have the same items.
        for (var i in javascript_names) {
            var javascript_name = javascript_names[i];
            var found = false;
            for (var j in python_names) {
                var python_name = python_names[j];
                if (python_name==javascript_name) {
                    found = true;
                    break;
                }
            }
            this.test.assert(found, javascript_name + ' exists in python');
        }
        for (var i in python_names) {
            var python_name = python_names[i];
            if (python_name.length > 0) {
                var found = false;
                for (var j in javascript_names) {
                    var javascript_name = javascript_names[j];
                    if (python_name==javascript_name) {
                        found = true;
                        break;
                    }
                }
                this.test.assert(found, python_name + ' exists in javascript');
            }
        }
    });

    // Test bool widget ////////////////////////////////////////////////////////
    var bool_index = this.append_cell(
        'bool_widget = widgets.BoolWidget(description="Title", value=True)\n' +
        'display(bool_widget)\n'+
        'display(bool_widget, view_name="ToggleButtonView")\n' +
        'print("Success")');
    this.execute_cell_then(bool_index, function(index){

        var button_output = this.get_output_cell(index).text;
        this.test.assert(button_output == 'Success\n',
            'Create bool widget cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single input'),
            'Checkbox exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel'),
            'Checkbox label exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel', 'html')=="Title",
            'Checkbox labeled correctly.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea div button'),
            'Toggle button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'html')=="Title",
            'Toggle button labeled correctly.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled.');

    });

    index = this.append_cell(
        'bool_widget.value = False\n' +
        'print("Success")');
    this.execute_cell_then(index, function(index){

        var button_output = this.get_output_cell(index).text;
        this.test.assert(button_output == 'Success\n',
            'Change bool widget value cell executed with correct output.');

        this.test.assert(!this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (1)');

        this.test.assert(!this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (1)');

        // Try toggling the bool by clicking on the toggle button.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea div button', 'click');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked. (2)');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled. (2)');

        // Try toggling the bool by clicking on the checkbox.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea .widget-hbox-single input', 'click');

        this.test.assert(!this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (3)');

        this.test.assert(!this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (3)');

    });

    // Test button widget //////////////////////////////////////////////////////
    var button_index = this.append_cell(
        'button = widgets.ButtonWidget(description="Title")\n' +
        'display(button)\n'+
        'print("Success")\n' +
        'def handle_click(sender):\n' +
        '    print("Clicked")\n' +
        'button.on_click(handle_click)');
    this.execute_cell_then(button_index, function(index){

        var button_output = this.get_output_cell(index).text;
        this.test.assert(button_output == 'Success\n',
            'Create button cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea button'),
            'Widget button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'html')=='Title',
            'Set button description.');

        this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'click');
    });

    this.wait(500); // Wait for click to execute in kernel and write output

    this.then(function () {
        this.test.assert(this.get_output_cell(button_index, 1).text == 'Clicked\n',
            'Button click event fires.');
    });

    index = this.append_cell(
        'button.close()\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        var button_output = this.get_output_cell(index).text;
        this.test.assert(button_output == 'Success\n',
            'Close button cell executed with correct output.');

        this.test.assert(! this.cell_element_exists(button_index,
            '.widget-area .widget-subarea button'),
            'Widget button doesn\'t exists.');
    });
});

<commit_msg>Add container tests<commit_after>// Test the widget framework.
casper.notebook_test(function () {
    var index;

    // Test widget dependencies ////////////////////////////////////////////////
    this.then(function () {

        // Check if the WidgetManager class is defined.
        this.test.assert(this.evaluate(function() {
            return IPython.WidgetManager != undefined;
        }), 'WidgetManager class is defined');
    });

    index = this.append_cell(
        'from IPython.html import widgets\n' +
        'from IPython.display import display, clear_output\n' +
        'print("Success")');
    this.execute_cell_then(index);

    this.wait(500); // Wait for require.js async callbacks to load dependencies.

    this.then(function () {
        // Check if the widget manager has been instanciated.
        this.test.assert(this.evaluate(function() {
            return IPython.widget_manager != undefined;
        }), 'Notebook widget manager instanciated');
    });

    // Check widget mapping ////////////////////////////////////////////////////
    index = this.append_cell(
        'names = [name for name in dir(widgets)' +
        ' if name.endswith("Widget") and name!= "Widget"]\n' +
        'for name in names:\n' +
        '    print(name)\n');
    this.execute_cell_then(index, function(index){

        // Get the widget names that are registered with the widget manager.  Assume
        // a 1 to 1 mapping of model and widgets names (model names just have 'model'
        // suffixed).
        var javascript_names = this.evaluate(function () {
            names = [];
            for (var name in IPython.widget_manager.widget_model_types) {
                names.push(name.replace('Model',''));
            }
            return names;
        });

        // Get the widget names registered in python.
        var python_names = this.get_output_cell(index).text.split('\n');

        // Make sure the two lists have the same items.
        for (var i in javascript_names) {
            var javascript_name = javascript_names[i];
            var found = false;
            for (var j in python_names) {
                var python_name = python_names[j];
                if (python_name==javascript_name) {
                    found = true;
                    break;
                }
            }
            this.test.assert(found, javascript_name + ' exists in python');
        }
        for (var i in python_names) {
            var python_name = python_names[i];
            if (python_name.length > 0) {
                var found = false;
                for (var j in javascript_names) {
                    var javascript_name = javascript_names[j];
                    if (python_name==javascript_name) {
                        found = true;
                        break;
                    }
                }
                this.test.assert(found, python_name + ' exists in javascript');
            }
        }
    });

    // Test bool widget ////////////////////////////////////////////////////////
    var bool_index = this.append_cell(
        'bool_widget = widgets.BoolWidget(description="Title", value=True)\n' +
        'display(bool_widget)\n'+
        'display(bool_widget, view_name="ToggleButtonView")\n' +
        'print("Success")');
    this.execute_cell_then(bool_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create bool widget cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single input'),
            'Checkbox exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel'),
            'Checkbox label exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea .widget-hbox-single .widget-hlabel', 'html')=="Title",
            'Checkbox labeled correctly.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea div button'),
            'Toggle button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'html')=="Title",
            'Toggle button labeled correctly.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled.');

    });

    index = this.append_cell(
        'bool_widget.value = False\n' +
        'print("Success")');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Change bool widget value cell executed with correct output.');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (1)');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (1)');

        // Try toggling the bool by clicking on the toggle button.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea div button', 'click');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is checked. (2)');

        this.test.assert(this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is toggled. (2)');

        // Try toggling the bool by clicking on the checkbox.
        this.cell_element_function(bool_index, '.widget-area .widget-subarea .widget-hbox-single input', 'click');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea .widget-hbox-single input', 'prop', ['checked']),
            'Checkbox is not checked. (3)');

        this.test.assert(! this.cell_element_function(bool_index,
            '.widget-area .widget-subarea div button', 'hasClass', ['active']),
            'Toggle button is not toggled. (3)');

    });

    // Test button widget //////////////////////////////////////////////////////
    var button_index = this.append_cell(
        'button = widgets.ButtonWidget(description="Title")\n' +
        'display(button)\n'+
        'print("Success")\n' +
        'def handle_click(sender):\n' +
        '    print("Clicked")\n' +
        'button.on_click(handle_click)');
    this.execute_cell_then(button_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create button cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea button'),
            'Widget button exists.');

        this.test.assert(this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'html')=='Title',
            'Set button description.');

        this.cell_element_function(index,
            '.widget-area .widget-subarea button', 'click');
    });

    this.wait(500); // Wait for click to execute in kernel and write output

    this.then(function () {
        this.test.assert(this.get_output_cell(button_index, 1).text == 'Clicked\n',
            'Button click event fires.');
    });

    index = this.append_cell(
        'button.close()\n'+
        'print("Success")');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Close button cell executed with correct output.');

        this.test.assert(! this.cell_element_exists(button_index,
            '.widget-area .widget-subarea button'),
            'Widget button doesn\'t exists.');
    });

    // Test container widget ///////////////////////////////////////////////////
    var container_index = this.append_cell(
        'container = widgets.ContainerWidget()\n' +
        'button = widgets.ButtonWidget(parent=container)\n'+
        'display(container)\n'+
        'container.add_class("my-test-class")\n'+
        'print("Success")\n');
    this.execute_cell_then(container_index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Create container cell executed with correct output.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea'),
            'Widget subarea exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-container'),
            'Widget container exists.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .my-test-class'),
            'add_class works.');

        this.test.assert(this.cell_element_exists(index,
            '.widget-area .widget-subarea .my-test-class button'),
            'Container parent/child relationship works.');
    });

    index = this.append_cell(
        'container.set_css("display", "none")\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Set container class CSS cell executed with correct output.');

        this.test.assert(this.cell_element_function(container_index,
            '.widget-area .widget-subarea .my-test-class', 'css', ['display'])=='none',
            'set_css works.');
    });

    index = this.append_cell(
        'container.remove_class("my-test-class")\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Remove container class cell executed with correct output.');

        this.test.assert(! this.cell_element_exists(container_index,
            '.widget-area .widget-subarea .my-test-class'),
            'remove_class works.');
    });

    index = this.append_cell(
        'display(button)\n'+
        'print("Success")\n');
    this.execute_cell_then(index, function(index){

        this.test.assert(this.get_output_cell(index).text == 'Success\n',
            'Display container child executed with correct output.');

        this.test.assert(! this.cell_element_exists(index,
            '.widget-area .widget-subarea .widget-container'),
            'Parent container not displayed.');

        // this.test.assert(this.cell_element_exists(index,
        //     '.widget-area .widget-subarea button'),
        //     'Child displayed.');
    });
});

<|endoftext|>


<commit_before>module.exports = {
    pages: {
        index: {
            entry: "src/index.js",
            template: "public/index.html",
            filename: "index.html"
        }
    },
    filenameHashing: false,
    css: {
        loaderOptions: {
            postcss: {
                plugins: [require("postcss-preset-env")()]
            }
        }
    },
    chainWebpack: config => {
        config.optimization.delete("splitChunks");
        config.optimization.removeAvailableModules(false);
        config.optimization.concatenateModules(false);
        config.optimization.providedExports(false);
        config.optimization.usedExports(false);
        // config.optimization.sideEffects(false)
        // config.optimization.minimize(false)
        // config.optimization.flagIncludedChunks(false)

        config.resolve.alias.set("@inkline/inkline", __dirname).end();

        // config
        //     .plugin("webpack-bundle-analyzer")
        //     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
        //     .init(Plugin => new Plugin({
        //         analyzerMode: 'static'
        //     }));
    },
    lintOnSave: true,
    pluginOptions: {
        lintStyleOnBuild: true,
        stylelint: {
            fix: false,
            files: ['src/**/*.{css,scss}'],
            syntax: 'scss'
        }
    }
};
<commit_msg>Changed main entry point.<commit_after>module.exports = {
    pages: {
        index: {
            entry: "src/main.js",
            template: "public/index.html",
            filename: "index.html"
        }
    },
    filenameHashing: false,
    css: {
        loaderOptions: {
            postcss: {
                plugins: [require("postcss-preset-env")()]
            }
        }
    },
    chainWebpack: config => {
        config.optimization.delete("splitChunks");
        config.optimization.removeAvailableModules(false);
        config.optimization.concatenateModules(false);
        config.optimization.providedExports(false);
        config.optimization.usedExports(false);
        // config.optimization.sideEffects(false)
        // config.optimization.minimize(false)
        // config.optimization.flagIncludedChunks(false)

        config.resolve.alias.set("@inkline/inkline", __dirname).end();

        // config
        //     .plugin("webpack-bundle-analyzer")
        //     .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin)
        //     .init(Plugin => new Plugin({
        //         analyzerMode: 'static'
        //     }));
    },
    lintOnSave: true,
    pluginOptions: {
        lintStyleOnBuild: true,
        stylelint: {
            fix: false,
            files: ['src/**/*.{css,scss}'],
            syntax: 'scss'
        }
    }
};
<|endoftext|>


<commit_before>import React from 'react';
import SPELLS from 'common/SPELLS';
import TalentStatisticBox from 'interface/others/TalentStatisticBox';
import STATISTIC_ORDER from 'interface/others/STATISTIC_ORDER';
import Events from 'parser/core/Events';
import Analyzer, { SELECTED_PLAYER } from 'parser/core/Analyzer';
import SpellLink from 'common/SpellLink';

/**
 * Example Report:
 */

const META_BUFF_DURATION_EYEBEAM = 8000;

class Demonic extends Analyzer{

  eyeBeamCasts = 0;
  goodDeathSweep = 0;
  eyeBeamTimeStamp = undefined;
  counter = 0;
  badCasts = 0;

  constructor(...args) {
    super(...args);
    this.active = this.selectedCombatant.hasTalent(SPELLS.DEMONIC_TALENT.id);
    if (!this.active) {
      return;
    }
    this.addEventListener(Events.applybuff.by(SELECTED_PLAYER).spell(SPELLS.METAMORPHOSIS_HAVOC_BUFF), this.getTimeStamp);
    this.addEventListener(Events.cast.by(SELECTED_PLAYER).spell(SPELLS.DEATH_SWEEP), this.countCasts);
  }

  getTimeStamp(event) {
    const hasMetaBuff = this.selectedCombatant.hasBuff(SPELLS.EYE_BEAM.id, event.timestamp);

    if (hasMetaBuff) {
      return;
    }
    this.eyeBeamCasts += 1;
    this.eyeBeamTimeStamp = event.timestamp;
    this.counter = 0;
  }

  countCasts(event) {
    if (this.eyeBeamTimeStamp !== undefined && (event.timestamp - this.eyeBeamTimeStamp) < META_BUFF_DURATION_EYEBEAM) {
      this.goodDeathSweep += 1;
      console.log("time dif", event.timestamp - this.eyeBeamTimeStamp);
      this.counter += 1;
    }
  }

  get AvgPerEyeBeam(){
    return this.goodDeathSweep / this.eyeBeamCasts;
  }

  get suggestionThresholds() {
    return {
      actual: this.badCasts,
      isGreaterThan: {
        minor: 0,
        average: 0,
        major: 1,
      },
      style: 'number',
    };
  }

  suggestions(when) {
    when(this.suggestionThresholds)
      .addSuggestion((suggest, actual, recommended) => {
        return suggest(<>Squeezing in atleast 2 <SpellLink id={SPELLS.DEATH_SWEEP.id} /> during the meta phase you gain from <SpellLink id={SPELLS.EYE_BEAM.id} /> due to the <SpellLink id={SPELLS.DEMONIC_TALENT.id} /> talent is important to maximizing your dps.</>)
          .icon(SPELLS.DEMONIC_TALENT.icon)
          .actual(`${actual} bad casts`)
          .recommended(`No bad casts is recommended.`);
      });
  }

  statistic(){
    return (
      <TalentStatisticBox
        talent={SPELLS.DEMONIC_TALENT.id}
        position={STATISTIC_ORDER.OPTIONAL(6)}
        value={(<>
                  {this.badCasts} Bad casts

                  {this.AvgPerEyeBeam} Avg Death Sweep casts
                </>)}
        tooltip={`A bad cast is triggered when you don't do atleast 2 Death Sweep casts inside

                  the Meta window you get from Eye Beam due to the Demonic talent.`}
      />
    );
  }
}
export default Demonic;
<commit_msg>finalized<commit_after>import React from 'react';
import SPELLS from 'common/SPELLS';
import TalentStatisticBox from 'interface/others/TalentStatisticBox';
import STATISTIC_ORDER from 'interface/others/STATISTIC_ORDER';
import Events from 'parser/core/Events';
import Analyzer, { SELECTED_PLAYER } from 'parser/core/Analyzer';
import SpellLink from 'common/SpellLink';

/**
 * Example Report: https://www.warcraftlogs.com/reports/23dHWCrT18qhaJbz/#fight=1&source=16
 */

const META_BUFF_DURATION_EYEBEAM = 9000;

class Demonic extends Analyzer{

  eyeBeamCasts = 0;
  goodDeathSweep = 0;
  eyeBeamTimeStamp = undefined;
  counter = undefined;
  badCasts = 0;

  constructor(...args) {
    super(...args);
    this.active = this.selectedCombatant.hasTalent(SPELLS.DEMONIC_TALENT.id);
    if (!this.active) {
      return;
    }
    this.addEventListener(Events.cast.by(SELECTED_PLAYER).spell(SPELLS.EYE_BEAM), this.getTimeStamp);
    this.addEventListener(Events.cast.by(SELECTED_PLAYER).spell(SPELLS.DEATH_SWEEP), this.countCasts);
  }

  getTimeStamp(event) {
    const hasMetaBuff = this.selectedCombatant.hasBuff(SPELLS.METAMORPHOSIS_HAVOC_BUFF.id, event.timestamp - 1000);

    if (hasMetaBuff) {
      return;
    }
    this.eyeBeamCasts += 1;
    this.eyeBeamTimeStamp = event.timestamp;

    if (this.counter === undefined) {
      this.counter = 0;
      return;
    }

    if (this.counter < 2) {
      this.badCasts += 1;
    }

    this.counter = 0;
    console.log("reset to 0");
  }

  countCasts(event) {
    if (this.eyeBeamTimeStamp !== undefined && (event.timestamp - this.eyeBeamTimeStamp) < META_BUFF_DURATION_EYEBEAM) {
      this.goodDeathSweep += 1;
      this.counter += 1;
      console.log("counter #", this.counter, "  TS: ", this.eyeBeamTimeStamp );
    }
  }

  get AvgPerEyeBeam(){
    return this.goodDeathSweep / this.eyeBeamCasts;
  }

  get suggestionThresholds() {
    return {
      actual: this.badCasts,
      isGreaterThan: {
        minor: 0,
        average: 0,
        major: 1,
      },
      style: 'number',
    };
  }

  suggestions(when) {
    when(this.suggestionThresholds)
      .addSuggestion((suggest, actual, recommended) => {
        return suggest(<>Squeezing in atleast 2 <SpellLink id={SPELLS.DEATH_SWEEP.id} /> during the meta phase you gain from <SpellLink id={SPELLS.EYE_BEAM.id} /> due to the <SpellLink id={SPELLS.DEMONIC_TALENT.id} /> talent is important to maximizing your dps.</>)
          .icon(SPELLS.DEMONIC_TALENT.icon)
          .actual(`${actual} bad casts`)
          .recommended(`No bad casts is recommended.`);
      });
  }

  statistic(){
    return (
      <TalentStatisticBox
        talent={SPELLS.DEMONIC_TALENT.id}
        position={STATISTIC_ORDER.OPTIONAL(6)}
        value={(<>
                  {this.badCasts} Bad casts

                  {this.AvgPerEyeBeam} Avg Death Sweep casts
                </>)}
        tooltip={`A bad cast is triggered when you don't do atleast 2 Death Sweep casts inside

                  the Meta window you get from Eye Beam due to the Demonic talent.`}
      />
    );
  }
}
export default Demonic;
<|endoftext|>


<commit_before>/* default dom id (particles-js) */
//particlesJS();

/* config dom id */
//particlesJS('dom-id');

/* config dom id (optional) + config particles params */
particlesJS('particles-js', {
  particles: {
    color: '#fff',
    shape: 'circle', // "circle", "edge" or "triangle"
    opacity: .5,
    size: 2,
    size_random: true,
    nb: 100,
    line_linked: {
      enable_auto: true,
      distance: 100,
      color: '#fff',
      opacity: 1,
      width: 1,
      condensed_mode: {
        enable: false,
        rotateX: 600,
        rotateY: 600
      }
    },
    anim: {
      enable: true,
      speed: 2
    }
  },
  interactivity: {
    enable: true,
    mouse: {
      distance: 250
    },
    detect_on: 'canvas', // "canvas" or "window"
    mode: 'grab',
    line_linked: {
    	opacity: .5
    },
    events: {
    	onclick: {
    		push_particle: {
    			enable: true,
    			nb: 4
    		}
    	}
    }
  },
  /* Retina Display Support */
  retina_detect: true
});
<commit_msg>update demo / fix name space<commit_after>/* default dom id (particles-js) */
//particlesJS();

/* config dom id */
//particlesJS('dom-id');

/* config dom id (optional) + config particles params */
particlesJS('particles-js', {
  particles: {
    color: '#fff',
    shape: 'circle', // "circle", "edge" or "triangle"
    opacity: .5,
    size: 2,
    size_random: true,
    nb: 100,
    line_linked: {
      enable_auto: true,
      distance: 100,
      color: '#fff',
      opacity: 1,
      width: 1,
      condensed_mode: {
        enable: false,
        rotateX: 600,
        rotateY: 600
      }
    },
    anim: {
      enable: true,
      speed: 2
    }
  },
  interactivity: {
    enable: true,
    mouse: {
      distance: 250
    },
    detect_on: 'canvas', // "canvas" or "window"
    mode: 'grab',
    line_linked: {
    	opacity: .5
    },
    events: {
    	onclick: {
    		push_particles: {
    			enable: true,
    			nb: 4
    		}
    	}
    }
  },
  /* Retina Display Support */
  retina_detect: true
});
<|endoftext|>