<commit_before>//Views for individual objects

var objectView = { };

objectView.chat = {
    start: function(x, options) {
        return newChannelWidget(x.id);
    },
    stop: function() { }
};

objectView.info = {
    start: function(x, options) {

        var depthRemaining = options.depthRemaining || 1;
        var depth = options.depth || depthRemaining;
        var nameClickable = (options.nameClickable !== undefined) ? options.nameClickable : true;
        var showName = (options.showName !== undefined) ? options.showName : true;
        var showAuthorIcon = (options.showAuthorIcon !== undefined) ? options.showAuthorIcon : true;
        var showAuthorName = (options.showAuthorName !== undefined) ? options.showAuthorName : true;
        var hideAuthorNameAndIconIfZeroDepth = (options.hideAuthorNameAndIconIfZeroDepth !== undefined) ? options.hideAuthorNameAndIconIfZeroDepth : false;
        var showMetadataLine = (options.showMetadataLine !== undefined) ? options.showMetadataLine : true;
        var showReplyButton = (options.showReplyButton !== undefined) ? options.showReplyButton : true;
        var titleClickMode = (options.titleClickMode !== undefined) ? options.titleClickMode : 'view';
        var showTime = (options.showTime !== undefined) ? options.showTime : true;
        var replyCallback = options.replyCallback ? function(rx) { options.replyCallback(rx); } : null;
        var startMinimized = (options.startMinimized != undefined) ? options.startMinimized : false;

        var xid = x.id;

        var d = newDiv();

        var xn = x.name || '';
        if (x._class)
            xn += ' (tag)';
        if (x._property)
            xn += ' (property)';

        var authorID = x.author;

        //d.append(cd);

            //showAuthorName = showAuthorIcon = false;

        var replies;

        if (showAuthorIcon) {
            if (!((depth === depthRemaining) && (hideAuthorNameAndIconIfZeroDepth))) {

                var authorClient = $N.getObject(authorID);
                if (authorClient) {
                    if (authorID) {
                        newAvatarImage(authorClient).appendTo(d);
                    }
                }
            }
        }

        function minimize() {
            d.addClass('ObjectViewMinimized');
            d.removeClass('ObjectViewMaximized');
        }

        function maximize() {
            d.removeClass('ObjectViewMinimized');
            d.addClass('ObjectViewMaximized');
            ensureMaximized();
        }

        function toggleMaxMin() {
            if (d.hasClass('ObjectViewMinimized'))
                maximize();
            else
                minimize();
            reflowView();
            return false;
        }

        //Name
        if (showName) {

            var haxn = newEle('div').addClass('TitleLine').appendTo(d);
            if (startMinimized)
                haxn.click(toggleMaxMin);

            var xxn = xn.length > 0 ? xn : '?';
            var xauthor = x.author;

            if (!nameClickable) {
                haxn.append(xxn);
            } else {
                haxn.append(newEle('a').html(xxn).click(function(e) {
                    if ((xauthor === $N.id()) && (titleClickMode === 'edit'))
                        newPopupObjectEdit(xid, true);
                    else if (typeof (titleClickMode) === 'function') {
                        titleClickMode(xid);
                    } else {

                        newPopupObjectView(xid, e);
                    }
                    return false;
                }));
            }

            if (showAuthorName && (!((depth === depthRemaining) && (hideAuthorNameAndIconIfZeroDepth)))) {
                if (!isSelfObject(x.id)) { //exclude self objects
                    if (x.author) {
                        var a = x.author;
                        var ai = $N.instance[a];
                        var an = ai ? ai.name || a : a;

                        if (!nameClickable) {
                            haxn.prepend(an, ': ');
                        } else {
                            haxn.prepend(newEle('a').html(an).click(function() {
                                newPopupObjectView(a, true);
                                return false;
                            }), ': ');
                        }
                    }
                }
            }
        }

        var maximized = false;

        function ensureMaximized() {
            if (maximized)
                return;

            maximized = true;

            if (replies)
                replies.detach();

            if ((showMetadataLine) && (!x._class) && (!x._property)) {
                var mdl = newMetadataLine(x, showTime).appendTo(d);

                if (showReplyButton && (x.id !== $N.id())) {
                    mdl.append(
                                            ' ',
                                            newEle('a').html('').attr('title', 'Reply').click(function() {
                            newReplyPopup(xid, replyCallback);
                            return false;
                            }),
                                            ' ',
                        newSubjectTagButton('Like', 'fa-thumbs-o-up', subjectTag.Like),
                                            ' ',
                        newSubjectTagButton('Dislike', 'fa-thumbs-o-down', subjectTag.Dislike),
                                            ' ',
                        newSubjectTagButton('Trust', 'fa-check', subjectTag.Trust)
                    );
                }
            }

            //d.append('
Relevance:' + parseInt(r*100.0)   + '%
');

            //var nod = newObjectDetails(x);
            //if (nod)
                //d.append(nod);

            addNewObjectDetails(x, d, showMetadataLine ? ['spacepoint'] : undefined);

            if (replies)
                replies.appendTo(d);

        }

        var r = x.reply;
        if (r) {
            var vr = _.values(r);

            if (vr.length > 0) {
                if (!replies) {
                    replies = newDiv().appendTo(d);
                    if (!hideAuthorNameAndIconIfZeroDepth)
                        replies.addClass('ObjectReply');
                    else
                        replies.addClass('ObjectReplyChatZeroDepth');
                }
                else {
                    replies.empty();
                }
                if (depthRemaining > 0) {
                    var childOptions = _.clone(options);
                    childOptions.depthRemaining = depthRemaining - 1;
                    childOptions.transparent = true;
                    childOptions.hideAuthorNameAndIconIfZeroDepth = false;
                    delete childOptions.scale;

                    //TODO sort the replies by age, oldest first?
                    vr.forEach(function(p) {
                        replies.append(newObjectView(p, childOptions));
                    });
                }
                else {
                    replies.append(vr.length + ' replies...');
                }
            }
        } else {
            if (replies) {
                replies.remove();
                replies = null;
            }
        }

        if (startMinimized) {
            minimize();
        }
        else {
            maximize();
        }

        return d;
    },
    stop: function() { }
};

objectView.value = {
    start: function(id, options) {
        var x = newDiv();
        /*
         *
         *  fa-cc-amex
                fa-cc-discover
                fa-cc-mastercard
                fa-cc-paypal
                fa-cc-stripe
                fa-cc-visa
                fa-credit-card
                fa-google-wallet
                fa-paypal

         */
        return x;
    },
    stop: function() { }
};

objectView.links = {
    start: function(o, options) {
        var id = o.id;

        var x = newDiv();

        var c = $.getJSON('/object/' + encodeURIComponent(id) + '/activity/json', function(context) {
           x.append(JSON.stringify(context));
        });

        var c = $.getJSON('/object/' + encodeURIComponent(id) + '/context/json', function(context) {
            //x.append(JSON.stringify(context));

            var items = [];
            var ctx = context.context;
            for (var k in ctx) {
                items.push({ name: k, value: ctx[k] } );
            }

            if (items.length > 0) {
                var diameter = 300,
                    format = d3.format(",d"),
                    color = d3.scale.category20c();

                var bubble = d3.layout.pack()
                    .sort(null)
                    .size([diameter, diameter])
                    .padding(1.5);

                var svg = d3.select(x[0]).append("svg")
                    .attr("width", diameter)
                    .attr("height", diameter)
                    .attr("class", "bubble");

                  var node = svg.selectAll(".node")
                      .data(bubble.nodes({children:items}))
                      //.filter(function(d) { return !d.children; })
                    .enter().append("g")
                      .attr("class", "node")
                      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

                  node.append("title")
                      .text(function(d) { return d.name + ": " + format(d.value); });

                  node.append("circle")
                      .attr("r", function(d) { return d.r; })
                      .style("fill", function(d) { return color(d.name); });

                  node.append("text")
                      .attr("dy", ".3em")
                      .style("text-anchor", "middle")
                      .text(function(d) { if (d.name) return d.name.substring(0, d.r / 3); else return '?'; });
                ;
            }

        });

        //    //check for Similarity
        //    var ot = objTags(x);
        //    if ((ot[0] === 'Similar') && (ot[1] === 'similarTo')) {
        //        /*showMetadataLine = false;
        //         showActionPopupButton = false;
        //         showSelectionCheck = false;
        //         showTime = false;
        //         nameClickable = false;*/
        //        return newSimilaritySummary(x);
        //    }

        return x;
    },
    stop: function() { }
};

objectView.empty = {
    start: function(id, options) {
        var x = newDiv();
        return x;
    },
    stop: function() { }
};
<commit_msg>graph vis<commit_after>//Views for individual objects

var objectView = {};

objectView.chat = {
    start: function (x, options) {
        return newChannelWidget(x.id);
    },
    stop: function () {
    }
};

objectView.info = {
    start: function (x, options) {

        var depthRemaining = options.depthRemaining || 1;
        var depth = options.depth || depthRemaining;
        var nameClickable = (options.nameClickable !== undefined) ? options.nameClickable : true;
        var showName = (options.showName !== undefined) ? options.showName : true;
        var showAuthorIcon = (options.showAuthorIcon !== undefined) ? options.showAuthorIcon : true;
        var showAuthorName = (options.showAuthorName !== undefined) ? options.showAuthorName : true;
        var hideAuthorNameAndIconIfZeroDepth = (options.hideAuthorNameAndIconIfZeroDepth !== undefined) ? options.hideAuthorNameAndIconIfZeroDepth : false;
        var showMetadataLine = (options.showMetadataLine !== undefined) ? options.showMetadataLine : true;
        var showReplyButton = (options.showReplyButton !== undefined) ? options.showReplyButton : true;
        var titleClickMode = (options.titleClickMode !== undefined) ? options.titleClickMode : 'view';
        var showTime = (options.showTime !== undefined) ? options.showTime : true;
        var replyCallback = options.replyCallback ? function (rx) {
            options.replyCallback(rx);
        } : null;
        var startMinimized = (options.startMinimized != undefined) ? options.startMinimized : false;

        var xid = x.id;

        var d = newDiv();

        var xn = x.name || '';
        if (x._class)
            xn += ' (tag)';
        if (x._property)
            xn += ' (property)';

        var authorID = x.author;

        //d.append(cd);

        //showAuthorName = showAuthorIcon = false;

        var replies;

        if (showAuthorIcon) {
            if (!((depth === depthRemaining) && (hideAuthorNameAndIconIfZeroDepth))) {

                var authorClient = $N.getObject(authorID);
                if (authorClient) {
                    if (authorID) {
                        newAvatarImage(authorClient).appendTo(d);
                    }
                }
            }
        }

        function minimize() {
            d.addClass('ObjectViewMinimized');
            d.removeClass('ObjectViewMaximized');
        }

        function maximize() {
            d.removeClass('ObjectViewMinimized');
            d.addClass('ObjectViewMaximized');
            ensureMaximized();
        }

        function toggleMaxMin() {
            if (d.hasClass('ObjectViewMinimized'))
                maximize();
            else
                minimize();
            reflowView();
            return false;
        }

        //Name
        if (showName) {

            var haxn = newEle('div').addClass('TitleLine').appendTo(d);
            if (startMinimized)
                haxn.click(toggleMaxMin);

            var xxn = xn.length > 0 ? xn : '?';
            var xauthor = x.author;

            if (!nameClickable) {
                haxn.append(xxn);
            } else {
                haxn.append(newEle('a').html(xxn).click(function (e) {
                    if ((xauthor === $N.id()) && (titleClickMode === 'edit'))
                        newPopupObjectEdit(xid, true);
                    else if (typeof (titleClickMode) === 'function') {
                        titleClickMode(xid);
                    } else {

                        newPopupObjectView(xid, e);
                    }
                    return false;
                }));
            }

            if (showAuthorName && (!((depth === depthRemaining) && (hideAuthorNameAndIconIfZeroDepth)))) {
                if (!isSelfObject(x.id)) { //exclude self objects
                    if (x.author) {
                        var a = x.author;
                        var ai = $N.instance[a];
                        var an = ai ? ai.name || a : a;

                        if (!nameClickable) {
                            haxn.prepend(an, ': ');
                        } else {
                            haxn.prepend(newEle('a').html(an).click(function () {
                                newPopupObjectView(a, true);
                                return false;
                            }), ': ');
                        }
                    }
                }
            }
        }

        var maximized = false;

        function ensureMaximized() {
            if (maximized)
                return;

            maximized = true;

            if (replies)
                replies.detach();

            if ((showMetadataLine) && (!x._class) && (!x._property)) {
                var mdl = newMetadataLine(x, showTime).appendTo(d);

                if (showReplyButton && (x.id !== $N.id())) {
                    mdl.append(
                            ' ',
                            newEle('a').html('').attr('title', 'Reply').click(function () {
                        newReplyPopup(xid, replyCallback);
                        return false;
                    }),
                            ' ',
                            newSubjectTagButton('Like', 'fa-thumbs-o-up', subjectTag.Like),
                            ' ',
                            newSubjectTagButton('Dislike', 'fa-thumbs-o-down', subjectTag.Dislike),
                            ' ',
                            newSubjectTagButton('Trust', 'fa-check', subjectTag.Trust)
                            );
                }
            }

            //d.append('
Relevance:' + parseInt(r*100.0)   + '%
');

            //var nod = newObjectDetails(x);
            //if (nod)
            //d.append(nod);

            addNewObjectDetails(x, d, showMetadataLine ? ['spacepoint'] : undefined);

            if (replies)
                replies.appendTo(d);

        }

        var r = x.reply;
        if (r) {
            var vr = _.values(r);

            if (vr.length > 0) {
                if (!replies) {
                    replies = newDiv().appendTo(d);
                    if (!hideAuthorNameAndIconIfZeroDepth)
                        replies.addClass('ObjectReply');
                    else
                        replies.addClass('ObjectReplyChatZeroDepth');
                }
                else {
                    replies.empty();
                }
                if (depthRemaining > 0) {
                    var childOptions = _.clone(options);
                    childOptions.depthRemaining = depthRemaining - 1;
                    childOptions.transparent = true;
                    childOptions.hideAuthorNameAndIconIfZeroDepth = false;
                    delete childOptions.scale;

                    //TODO sort the replies by age, oldest first?
                    vr.forEach(function (p) {
                        replies.append(newObjectView(p, childOptions));
                    });
                }
                else {
                    replies.append(vr.length + ' replies...');
                }
            }
        } else {
            if (replies) {
                replies.remove();
                replies = null;
            }
        }

        if (startMinimized) {
            minimize();
        }
        else {
            maximize();
        }

        return d;
    },
    stop: function () {
    }
};

objectView.value = {
    start: function (id, options) {
        var x = newDiv();
        /*
         *
         *  fa-cc-amex
         fa-cc-discover
         fa-cc-mastercard
         fa-cc-paypal
         fa-cc-stripe
         fa-cc-visa
         fa-credit-card
         fa-google-wallet
         fa-paypal

         */
        return x;
    },
    stop: function () {
    }
};

objectView.links = {
    start: function (o, options) {
        var id = o.id;

        var x = newDiv();

        var c = $.getJSON('/object/' + encodeURIComponent(id) + '/activity/json', function (activity) {
            //x.append(JSON.stringify(activity));

            var n = []; // {"name":"Myriel","group":1},
            var e = []; // {"source":1,"target":0,"value":1},

            //"activity":{"i":"Offer","out":{"is":["Can"]}}}

            n.push({name: id, value: 15}); //index zero

            var i = activity.activity.in;
            var o = activity.activity.out;
            if (i) {
                for (var pred in i) {
                    _.each(i[pred], function (t) {
                        var ti = n.indexOf(t);
                        if (ti === -1) {
                            ti = n.length;
                            n.push({name: t, value: 8});
                        }
                        e.push({source: ti, target: 0, value: 1});
                    });
                }
            }
            if (o) {
                for (var pred in o) {
                    _.each(o[pred], function (t) {
                        var ti = n.indexOf(t);
                        if (ti === -1) {
                            ti = n.length;
                            n.push({name: t, value: 8});
                        }
                        e.push({source: 0, target: ti, value: 1});
                    });
                }
            }

            if (n.length > 0) {
                x.append(newGraphChart({nodes: n, links: e}, 300, 300));
            }
        });

        var c = $.getJSON('/object/' + encodeURIComponent(id) + '/context/json', function (context) {
            var items = [];
            var ctx = context.context;
            for (var k in ctx) {
                items.push({name: k, value: ctx[k]});
            }

            if (items.length > 0) {
                x.append(newBubbleChart(id, items, 300));
            }

        });

        //    //check for Similarity
        //    var ot = objTags(x);
        //    if ((ot[0] === 'Similar') && (ot[1] === 'similarTo')) {
        //        /*showMetadataLine = false;
        //         showActionPopupButton = false;
        //         showSelectionCheck = false;
        //         showTime = false;
        //         nameClickable = false;*/
        //        return newSimilaritySummary(x);
        //    }

        return x;
    },
    stop: function () {
    }
};

objectView.empty = {
    start: function (id, options) {
        var x = newDiv();
        return x;
    },
    stop: function () {
    }
};

function newGraphChart(graph, width, height) {
    var x = newDiv();

    var color = d3.scale.category20();

    var force = d3.layout.force()
            .charge(-120)
            .linkDistance(30)
            .size([width, height]);

    var svg = d3.select(x[0]).append("svg")
            .attr("width", width)
            .attr("height", height);

    force
            .nodes(graph.nodes)
            .links(graph.links)
            .start();

    var link = svg.selectAll(".link")
            .data(graph.links)
            .enter().append("line")
            //.attr("class", "link")

            .style("stroke", "black")
            .style("stroke-width", function (d) {
                return Math.sqrt(d.value);
            });

    var node = svg.selectAll(".node")
            .data(graph.nodes)
            .enter().append("circle")
            //.attr("class", "node")
            .attr("r", function(d) { return d.value; } )
            .style("fill", function (d) {
                return color(d.group);
            })
            .call(force.drag);

    node.append("title")
            .text(function (d) {
                return d.name;
            });

    force.on("tick", function () {
        link.attr("x1", function (d) {
            return d.source.x;
        })
                .attr("y1", function (d) {
                    return d.source.y;
                })
                .attr("x2", function (d) {
                    return d.target.x;
                })
                .attr("y2", function (d) {
                    return d.target.y;
                });

        node.attr("cx", function (d) {
            return d.x;
        })
                .attr("cy", function (d) {
                    return d.y;
                });
    });

    return x;
}

function newBubbleChart(id, items, diameter) {
    var x = newDiv();
    var format = d3.format(",d"),
            color = d3.scale.category20c();

    var bubble = d3.layout.pack()
            .sort(null)
            .size([diameter, diameter])
            .padding(1.5);

    var svg = d3.select(x[0]).append("svg")
            .attr("width", diameter)
            .attr("height", diameter)
            .attr("class", "bubble");

    var node = svg.selectAll(".node")
            .data(bubble.nodes({name: id, children: items}))
            //.filter(function(d) { return !d.children; })
            .enter().append("g")
            .attr("class", "node")
            .attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });

    node.append("title")
            .text(function (d) {
                return d.name + ": " + format(d.value);
            });

    node.append("circle")
            .attr("r", function (d) {
                return d.r;
            })
            .style("fill", function (d) {
                return d.children ? 'transparent' : color(d.name);
            });

    node.append("text")
            .attr("dy", ".3em")
            .style("text-anchor", "middle")
            .text(function (d) {
                if (d.name)
                    return d.name.substring(0, d.r / 3);
                else
                    return '?';
            });
    ;
    return x;
}<|endoftext|>


<commit_before>const { BrowserPool, PlaywrightPlugin } = require('browser-pool');
const { chromium } = require('playwright-core');
const debug = require('debug')('express-dom');
const clone = require('clone');
const { randomUUID } = require('crypto');

const plugins = require('./plugins');
const helpers = require('./helpers');
const tracker = require('./tracker');
const Deferred = require('./deferred');
const asyncEmit = require('./async-emit');

let browserPool;

class RequestTracker extends Deferred {
	#requests = 0;

	constructor(page) {
		super();
		page.on('request', () => this.#creates());
		page.on('requestfinished', () => this.#completes());
		page.on('requestfailed', () => this.#completes());
	}
	#creates() {
		this.#requests++;
	}
	#completes() {
		this.#requests--;
		if (this.#requests == 0) {
			setImmediate(this.resolve);
		}
	}
}

module.exports = class Handler {
	static pool = {
		max: 20
	};
	static plugins = plugins;
	static helpers = helpers;
	static settings = {
		helpers: [
			'view',
			'prioritize'
		],
		prepare: {
			plugins: [
				'types',
				'hide',
				'none',
				'html'
			]
		},
		load: {
			disable: Boolean(process.env.DEVELOP),
			plugins: [
				'types',
				'hide',
				'prerender',
				'redirect',
				'referrer',
				'html'
			]
		},
		filters: [],
		types: new Set([
			"document",
			"stylesheet",
			"image",
			"font",
			"script",
			"xhr",
			"fetch"
		]),
		console: process.env.NODE_ENV != "production",
		stall: 15000,
		scale: 1,
		allow: "same-origin",
		navigation: false,
		verbose: true,
		debug: false
	};

	constructor(...helpers) {
		const h = this;
		h.settings = emptySettings(Handler.settings);
		h.settings.helpers = helpers.concat(Handler.settings.helpers);
		h.settings.prepare = emptySettings(Handler.settings, true);
		h.settings.load = emptySettings(Handler.settings, true);

		h.chain = (...args) => h.middleware(...args);
		h.chain.load = (...args) => h.load(...args);
		h.chain.prepare = (...args) => h.prepare(...args);
	}
	#init(settings, defaults, opts, args) {
		if (!browserPool) browserPool = new BrowserPool({
			browserPlugins: [new PlaywrightPlugin(chromium, {
				maxOpenPagesPerBrowser: Handler.pool.max,
				retireBrowserAfterPageCount: Handler.pool.maxloads,
				useIncognitoPages: true, // each page can have its cookies
				launchOptions: {
					channel: 'chrome',
					devtools: settings.debug,
					timeout: 5000
				}
			})],
		});
		if (typeof opts == "function") opts = null;
		else args.shift();
		let plugins = settings.plugins;
		Object.assign(settings, defaults, opts);
		if ((!opts || !opts.plugins) && plugins.length) settings.plugins = plugins;
		else plugins = settings.plugins;
		if (typeof plugins == "function") plugins = [plugins];
		settings.plugins = args.concat(plugins);
	}

	static async destroy() {
		await browserPool.destroy();
		browserPool = null;
	}
	clone() {
		const h = new Handler();
		h.settings = clone(this.settings);
		return h;
	}
	prepare(opts) {
		this.#init(
			this.settings.prepare,
			Handler.settings.prepare,
			opts,
			Array.from(arguments)
		);
		return this.chain;
	}

	load(opts) {
		this.#init(
			this.settings.load,
			Handler.settings.load,
			opts,
			Array.from(arguments)
		);
		return this.chain;
	}
	async middleware(req, res, next) {
		const manual = {};
		if (typeof req == "string") {
			debug("Called in manual mode");
			const loc = new URL(req);
			req = {
				headers: {
					host: loc.host
				},
				protocol: loc.protocol,
				url: loc.pathname + loc.search,
				path: loc.pathname
			};
			req.get = function (key) {
				return this.headers[key];
			}.bind(req);
			res = { statusCode: 200 };
			res.sendStatus = function (status) {
				res.statusCode = manual.status = status;
			};
			res.send = function (str) {
				manual.body = str;
			};
			res.status = res.sendStatus;
			req.res = res;
		}
		// per request settings
		const rh = this.clone();
		try {
			await rh.runMiddleware(req, res);
			if (!next) {
				return manual;
			}
		} catch (err) {
			if (next) next(err);
			else throw err;
		}
	}

	async runMiddleware(req, res) {
		const { settings } = this;

		const loc = settings.location = getAbsoluteUrl(req);
		if (req.headers.cookie) loc.headers = {
			cookie: req.headers.cookie
		};

		for (const helper of settings.helpers) {
			const fn = typeof helper == "string" ? Handler.helpers[helper] : helper;
			if (!fn) {
				throw new Error(`helper not found: ${helper}`);
			}
			await fn(this, settings, req, res);
		}

		debug('view loaded for', loc.pathname, "with", settings.prepare.plugins.length, "prepare plugins, and", settings.load.plugins.length, "load plugins");

		if (!settings.prepare.disable && settings.prepare.plugins.length > 0) {
			await this.runMethod('prepare', settings, req, res);
		}

		if (!settings.load.disable && settings.load.plugins.length > 0) {
			await this.runMethod('load', settings, req, res);
		}

		let data = settings.input;
		// hopefully free faster that memory
		delete settings.input;
		if (res.get && !res.get('Content-Type')) {
			res.set('Content-Type', "text/html");
		}
		let status = res.statusCode;
		if (data === false) {
			debug("data handled by plugin");
			return;
		}
		if (!data) {
			if (!settings.prepare && !settings.load) {
				return res.sendStatus(501);
			} else {
				return res.sendStatus(404);
			}
		}
		let isPath = true, isStream = false;
		if (data instanceof Error) {
			isPath = false;
			data = null;
		} else if (Buffer.isBuffer(data)) {
			isPath = false;
		} else if (typeof data.pipe == "function") {
			isPath = false;
			isStream = true;
		} else if (typeof data != "string") {
			data = data.toString();
		}
		if (isPath && data.startsWith('<')) {
			isPath = false;
		}
		if (isPath) {
			try {
				res.sendFile(data);
			} catch (ex) {
				// eslint-disable-next-line no-console
				console.error("express-dom thought input was a file path", data);
				status = 500;
				isPath = false;
			}
		}
		if (!isPath) {
			if (status) res.status(status);
			if (isStream) {
				data.once('error', (err) => {
					res.emit('error', err);
					data.unpipe(res);
				});
				data.pipe(res);
			} else {
				res.send(data);
			}
		}
	}

	async runMethod(method, settings, req, res) {
		const page = await browserPool.newPage({
			pageOptions: {
				deviceScaleFactor: settings.scale
			}
		});
		asyncEmit(page);
		const mSettings = Object.assign({}, settings, settings[method]);
		mSettings.location = settings.location;
		const plugins = mSettings.plugins;
		delete mSettings.plugins;
		for (const plugin of plugins) {
			const fn = typeof plugin == "string" ? Handler.plugins[plugin] : plugin;
			if (!fn) {
				throw new Error(`plugin not found: ${plugin}`);
			}
			await fn(page, mSettings, req, res);
		}

		settings.location = mSettings.location;
		settings.input = mSettings.input;
		delete mSettings.location;
		delete mSettings.input;
		const url = settings.location.toString();
		debug(method, url);

		page.route(/.*/, async (route, request) => {
			let accept = true;
			for (const filter of mSettings.filters) {
				accept = await filter(request);
				if (accept === false) break;
			}
			if (accept === false) route.abort();
			else route.continue();
		});

		page.route(url, route => {
			route.fulfill({
				body: settings.input,
				contentType: 'text/html'
			});
		});

		if (mSettings.verbose) page.on('console', msg => {
			// eslint-disable-next-line no-console
			console[msg.type()](msg.text());
		});
		page.on('crash', err => {
			console.error("page crashed", url, err);
		});
		page.on('pageerror', err => {
			res.statusCode = 500;
			settings.input = err;
			if (mSettings.verbose) console.error(err);
		});
		const reqTrack = new RequestTracker(page);

		const fnid = 'signal_' + randomUUID();
		await page.addInitScript(tracker, {
			id: fnid,
			stall: mSettings.stall
		});
		try {
			await page.goto(url, {
				waitUntil: 'domcontentloaded'
			});
			debug("page loaded");
			await reqTrack;
			debug("page requests finished");

			const event = await page.evaluate(id => window[id], fnid);
			debug("page idle");
			if (event == "idle") {
				await asyncEmit(page, 'idle');
				debug("async emit done");
			} else {
				console.warn("Unknown event", event);
			}

		} catch (ex) {
			if (ex.message.startsWith("page.evaluate: Execution context was destroyed")) {
				// pass
			} else {
				console.error(ex);
			}
		}

		if (mSettings.output !== undefined) {
			settings.input = mSettings.output;
		}
		await page.close();
	}
};

function emptySettings(defaults, withArrays) {
	// use defaults but not .load, .prepare sub global settings
	let obj = Object.assign({}, defaults);
	delete obj.load;
	delete obj.prepare;
	obj = clone(obj);
	if (withArrays) {
		if (!obj.filters) obj.filters = [];
		if (!obj.plugins) obj.plugins = [];
		if (!obj.scripts) obj.scripts = [];
	}
	return obj;
}

function getAbsoluteUrl({ protocol, headers, url }) {
	if (protocol == "about:") {
		return new URL(`about:${url}`);
	} else {
		return new URL(`${protocol}://${headers.host}${url}`);
	}
}
<commit_msg>Ignore https errors<commit_after>const { BrowserPool, PlaywrightPlugin } = require('browser-pool');
const { chromium } = require('playwright-core');
const debug = require('debug')('express-dom');
const clone = require('clone');
const { randomUUID } = require('crypto');

const plugins = require('./plugins');
const helpers = require('./helpers');
const tracker = require('./tracker');
const Deferred = require('./deferred');
const asyncEmit = require('./async-emit');

let browserPool;

class RequestTracker extends Deferred {
	#requests = 0;

	constructor(page) {
		super();
		page.on('request', () => this.#creates());
		page.on('requestfinished', () => this.#completes());
		page.on('requestfailed', () => this.#completes());
	}
	#creates() {
		this.#requests++;
	}
	#completes() {
		this.#requests--;
		if (this.#requests == 0) {
			setImmediate(this.resolve);
		}
	}
}

module.exports = class Handler {
	static pool = {
		max: 20
	};
	static plugins = plugins;
	static helpers = helpers;
	static settings = {
		helpers: [
			'view',
			'prioritize'
		],
		prepare: {
			plugins: [
				'types',
				'hide',
				'none',
				'html'
			]
		},
		load: {
			disable: Boolean(process.env.DEVELOP),
			plugins: [
				'types',
				'hide',
				'prerender',
				'redirect',
				'referrer',
				'html'
			]
		},
		filters: [],
		types: new Set([
			"document",
			"stylesheet",
			"image",
			"font",
			"script",
			"xhr",
			"fetch"
		]),
		console: process.env.NODE_ENV != "production",
		stall: 15000,
		scale: 1,
		allow: "same-origin",
		navigation: false,
		verbose: true,
		debug: false
	};

	constructor(...helpers) {
		const h = this;
		h.settings = emptySettings(Handler.settings);
		h.settings.helpers = helpers.concat(Handler.settings.helpers);
		h.settings.prepare = emptySettings(Handler.settings, true);
		h.settings.load = emptySettings(Handler.settings, true);

		h.chain = (...args) => h.middleware(...args);
		h.chain.load = (...args) => h.load(...args);
		h.chain.prepare = (...args) => h.prepare(...args);
	}
	#init(settings, defaults, opts, args) {
		if (!browserPool) browserPool = new BrowserPool({
			browserPlugins: [new PlaywrightPlugin(chromium, {
				maxOpenPagesPerBrowser: Handler.pool.max,
				retireBrowserAfterPageCount: Handler.pool.maxloads,
				useIncognitoPages: true, // each page can have its cookies
				launchOptions: {
					channel: 'chrome',
					devtools: settings.debug,
					timeout: 5000
				}
			})],
		});
		if (typeof opts == "function") opts = null;
		else args.shift();
		let plugins = settings.plugins;
		Object.assign(settings, defaults, opts);
		if ((!opts || !opts.plugins) && plugins.length) settings.plugins = plugins;
		else plugins = settings.plugins;
		if (typeof plugins == "function") plugins = [plugins];
		settings.plugins = args.concat(plugins);
	}

	static async destroy() {
		await browserPool.destroy();
		browserPool = null;
	}
	clone() {
		const h = new Handler();
		h.settings = clone(this.settings);
		return h;
	}
	prepare(opts) {
		this.#init(
			this.settings.prepare,
			Handler.settings.prepare,
			opts,
			Array.from(arguments)
		);
		return this.chain;
	}

	load(opts) {
		this.#init(
			this.settings.load,
			Handler.settings.load,
			opts,
			Array.from(arguments)
		);
		return this.chain;
	}
	async middleware(req, res, next) {
		const manual = {};
		if (typeof req == "string") {
			debug("Called in manual mode");
			const loc = new URL(req);
			req = {
				headers: {
					host: loc.host
				},
				protocol: loc.protocol,
				url: loc.pathname + loc.search,
				path: loc.pathname
			};
			req.get = function (key) {
				return this.headers[key];
			}.bind(req);
			res = { statusCode: 200 };
			res.sendStatus = function (status) {
				res.statusCode = manual.status = status;
			};
			res.send = function (str) {
				manual.body = str;
			};
			res.status = res.sendStatus;
			req.res = res;
		}
		// per request settings
		const rh = this.clone();
		try {
			await rh.runMiddleware(req, res);
			if (!next) {
				return manual;
			}
		} catch (err) {
			if (next) next(err);
			else throw err;
		}
	}

	async runMiddleware(req, res) {
		const { settings } = this;

		const loc = settings.location = getAbsoluteUrl(req);
		if (req.headers.cookie) loc.headers = {
			cookie: req.headers.cookie
		};

		for (const helper of settings.helpers) {
			const fn = typeof helper == "string" ? Handler.helpers[helper] : helper;
			if (!fn) {
				throw new Error(`helper not found: ${helper}`);
			}
			await fn(this, settings, req, res);
		}

		debug('view loaded for', loc.pathname, "with", settings.prepare.plugins.length, "prepare plugins, and", settings.load.plugins.length, "load plugins");

		if (!settings.prepare.disable && settings.prepare.plugins.length > 0) {
			await this.runMethod('prepare', settings, req, res);
		}

		if (!settings.load.disable && settings.load.plugins.length > 0) {
			await this.runMethod('load', settings, req, res);
		}

		let data = settings.input;
		// hopefully free faster that memory
		delete settings.input;
		if (res.get && !res.get('Content-Type')) {
			res.set('Content-Type', "text/html");
		}
		let status = res.statusCode;
		if (data === false) {
			debug("data handled by plugin");
			return;
		}
		if (!data) {
			if (!settings.prepare && !settings.load) {
				return res.sendStatus(501);
			} else {
				return res.sendStatus(404);
			}
		}
		let isPath = true, isStream = false;
		if (data instanceof Error) {
			isPath = false;
			data = null;
		} else if (Buffer.isBuffer(data)) {
			isPath = false;
		} else if (typeof data.pipe == "function") {
			isPath = false;
			isStream = true;
		} else if (typeof data != "string") {
			data = data.toString();
		}
		if (isPath && data.startsWith('<')) {
			isPath = false;
		}
		if (isPath) {
			try {
				res.sendFile(data);
			} catch (ex) {
				// eslint-disable-next-line no-console
				console.error("express-dom thought input was a file path", data);
				status = 500;
				isPath = false;
			}
		}
		if (!isPath) {
			if (status) res.status(status);
			if (isStream) {
				data.once('error', (err) => {
					res.emit('error', err);
					data.unpipe(res);
				});
				data.pipe(res);
			} else {
				res.send(data);
			}
		}
	}

	async runMethod(method, settings, req, res) {
		const page = await browserPool.newPage({
			pageOptions: {
				ignoreHTTPSErrors: true,
				deviceScaleFactor: settings.scale
			}
		});
		asyncEmit(page);
		const mSettings = Object.assign({}, settings, settings[method]);
		mSettings.location = settings.location;
		const plugins = mSettings.plugins;
		delete mSettings.plugins;
		for (const plugin of plugins) {
			const fn = typeof plugin == "string" ? Handler.plugins[plugin] : plugin;
			if (!fn) {
				throw new Error(`plugin not found: ${plugin}`);
			}
			await fn(page, mSettings, req, res);
		}

		settings.location = mSettings.location;
		settings.input = mSettings.input;
		delete mSettings.location;
		delete mSettings.input;
		const url = settings.location.toString();
		debug(method, url);

		page.route(/.*/, async (route, request) => {
			let accept = true;
			for (const filter of mSettings.filters) {
				accept = await filter(request);
				if (accept === false) break;
			}
			if (accept === false) route.abort();
			else route.continue();
		});

		page.route(url, route => {
			route.fulfill({
				body: settings.input,
				contentType: 'text/html'
			});
		});

		if (mSettings.verbose) page.on('console', msg => {
			// eslint-disable-next-line no-console
			console[msg.type()](msg.text());
		});
		page.on('crash', err => {
			console.error("page crashed", url, err);
		});
		page.on('pageerror', err => {
			res.statusCode = 500;
			settings.input = err;
			if (mSettings.verbose) console.error(err);
		});
		const reqTrack = new RequestTracker(page);

		const fnid = 'signal_' + randomUUID();
		await page.addInitScript(tracker, {
			id: fnid,
			stall: mSettings.stall
		});
		try {
			await page.goto(url, {
				waitUntil: 'domcontentloaded'
			});
			debug("page loaded");
			await reqTrack;
			debug("page requests finished");

			const event = await page.evaluate(id => window[id], fnid);
			debug("page idle");
			if (event == "idle") {
				await asyncEmit(page, 'idle');
				debug("async emit done");
			} else {
				console.warn("Unknown event", event);
			}

		} catch (ex) {
			if (ex.message.startsWith("page.evaluate: Execution context was destroyed")) {
				// pass
			} else {
				console.error(ex);
			}
		}

		if (mSettings.output !== undefined) {
			settings.input = mSettings.output;
		}
		await page.close();
	}
};

function emptySettings(defaults, withArrays) {
	// use defaults but not .load, .prepare sub global settings
	let obj = Object.assign({}, defaults);
	delete obj.load;
	delete obj.prepare;
	obj = clone(obj);
	if (withArrays) {
		if (!obj.filters) obj.filters = [];
		if (!obj.plugins) obj.plugins = [];
		if (!obj.scripts) obj.scripts = [];
	}
	return obj;
}

function getAbsoluteUrl({ protocol, headers, url }) {
	if (protocol == "about:") {
		return new URL(`about:${url}`);
	} else {
		return new URL(`${protocol}://${headers.host}${url}`);
	}
}
<|endoftext|>


<commit_before>angular.module('anyday.templates', ['any-task-bottom-sheet.jade', 'any-task.jade', 'details.jade', 'fixtures.jade', 'login.jade', 'panel.jade', 'sidenav.jade', 'tasks.jade']);

angular.module("any-task-bottom-sheet.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("any-task-bottom-sheet.jade",
    "<md-bottom-sheet layout=\"row\" controller=\"AnyTasksBottomSheetController as vm\" class=\"md-grid\"><md-list flex layout=\"row\" layout-align=\"center center\"><md-list-item ng-repeat=\"item in vm.items\"><md-button ng-click=\"vm.menu_click($index)\" class=\"md-grid-item-content\"><md-icon md-font-icon=\"material-icons\">{{ item.icon }}</md-icon>
{{ item.name }}
</md-button></md-list-item></md-list></md-bottom-sheet>");
}]);

angular.module("any-task.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("any-task.jade",
    "

{{ task.name }}

{{ task.when.relative() }}

<md-divider></md-divider>");
}]);

angular.module("details.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("details.jade",
    "");
}]);

angular.module("fixtures.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("fixtures.jade",
    "

I want to<md-autocomplete md-floating-label=\"do something...\" md-autoselect=\"true\" md-select-on-match=\"false\" md-no-cache=\"true\" md-delay=\"150\" md-selected-item=\"vm.fixture\" md-search-text=\"vm.search_text\" md-min-length=\"0\" md-items=\"fixture in vm.get_matches(vm.search_text)\" md-item-text=\"fixture.name\"><md-item-template>{{ fixture.name }}</md-item-template></md-autocomplete>every<md-input-container>frequency<input any-number type=\"number\" name=\"frequency\" ng-model=\"vm.fixture.frequency\"></md-input-container>or so, days.

<md-button ng-click=\"vm.create_from_fixture()\">Make It So</md-button>

");
}]);

angular.module("login.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("login.jade",
    "
<form ng-hide=\"enter_token\" ng-submit=\"submit_login()\">
Hi, you can call me<md-input-container>email<input type=\"text\" name=\"email\" ng-model=\"email\" placeholder=\"email\"></md-input-container>.

<md-button type=\"submit\">Login/Register</md-button>
</form>

Enter the token you recieved here:
<md-input-container>token<input type=\"text\" ng-model=\"token\" ng-keyup=\"$event.keyCode == 13 && submit_token()\" placeholder=\"token\"></md-input-container>

<md-button ng-click=\"submit_token()\">Submit token</md-button><md-button ng-click=\"reset_token()\">Resend token</md-button>
");
}]);

angular.module("panel.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("panel.jade",
    "");
}]);

angular.module("sidenav.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("sidenav.jade",
    "<md-sidenav layout-fill md-component-id=\"menu\" md-is-locked-open=\"$mdMedia('gt-md')\" class=\"md-sidenav-left md-whiteframe-z2\"><md-toolbar>

Menu
<md-button hide-gt-md ng-click=\"toggle_sidenav()\" class=\"md-icon-button\"><md-icon aria-label=\"Close Menu\" md-font-icon=\"material-icons\">close</md-icon></md-button>
</md-toolbar><md-content layout=\"column\" layout-padding flex><md-button hide-gt-md ng-click=\"toggle_sidenav()\">Close Menu</md-button><md-divider></md-divider><md-button ng-if=\"config.user\" ng-click=\"logout()\">Logout</md-button></md-content></md-sidenav>");
}]);

angular.module("tasks.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("tasks.jade",
    "
<md-list><md-subheader class=\"md-no-sticky\">Tasks</md-subheader><md-list-item any-task ng-repeat=\"task in vm.tasks\" ng-click=\"vm.show_bottom_sheet($event, task)\" class=\"md-2-line\">

{{ task.name }}

{{ task.when.relative() }}

<md-icon aria-label=\"Update Time\" md-font-icon=\"material-icons\" ng-click=\"vm.update_time(task)\" class=\"md-secondary\">check</md-icon></md-list-item></md-list>
");
}]);
<commit_msg>mod: update templates<commit_after>angular.module('anyday.templates', ['any-task-bottom-sheet.jade', 'any-task.jade', 'details.jade', 'fixtures.jade', 'login.jade', 'panel.jade', 'sidenav.jade', 'tasks.jade']);

angular.module("any-task-bottom-sheet.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("any-task-bottom-sheet.jade",
    "<md-bottom-sheet layout=\"row\" controller=\"AnyTasksBottomSheetController as vm\" class=\"md-grid\"><md-list flex layout=\"row\" layout-align=\"center center\"><md-list-item ng-repeat=\"item in vm.items\"><md-button ng-click=\"vm.menu_click($index)\" class=\"md-grid-item-content\"><md-icon md-font-icon=\"material-icons\">{{ item.icon }}</md-icon>
{{ item.name }}
</md-button></md-list-item></md-list></md-bottom-sheet>");
}]);

angular.module("any-task.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("any-task.jade",
    "

{{ task.name }}

{{ task.when.relative() }}

<md-divider></md-divider>");
}]);

angular.module("details.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("details.jade",
    "");
}]);

angular.module("fixtures.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("fixtures.jade",
    "
<md-autocomplete md-floating-label=\"do something...\" md-autoselect=\"true\" md-select-on-match=\"false\" md-no-cache=\"true\" md-delay=\"150\" md-selected-item=\"vm.fixture\" md-search-text=\"vm.search_text\" md-min-length=\"0\" md-items=\"fixture in vm.get_matches(vm.search_text)\" md-item-text=\"fixture.name\"><md-item-template>{{ fixture.name }}</md-item-template></md-autocomplete>
<md-input-container flex>every {{ vm.fixture.frequency | default:\"x\" }} days<input any-number type=\"number\" name=\"frequency\" ng-disabled=\"!vm.fixture\" ng-model=\"vm.fixture.frequency\"></md-input-container><md-datepicker ng-model=\"vm.fixture.date\" md-placeholder=\"Future Date\"></md-datepicker>

<md-button ng-disabled=\"!vm.fixture\" ng-click=\"vm.create_from_fixture()\">Add</md-button>

");
}]);

angular.module("login.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("login.jade",
    "
<form ng-hide=\"enter_token\" ng-submit=\"submit_login()\">
Hi, you can call me<md-input-container>email<input type=\"text\" name=\"email\" ng-model=\"email\" placeholder=\"email\"></md-input-container>.

<md-button type=\"submit\">Login/Register</md-button>
</form>

Enter the token you recieved here:
<md-input-container>token<input type=\"text\" ng-model=\"token\" ng-keyup=\"$event.keyCode == 13 && submit_token()\" placeholder=\"token\"></md-input-container>

<md-button ng-click=\"submit_token()\">Submit token</md-button><md-button ng-click=\"reset_token()\">Resend token</md-button>
");
}]);

angular.module("panel.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("panel.jade",
    "");
}]);

angular.module("sidenav.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("sidenav.jade",
    "<md-sidenav layout-fill md-component-id=\"menu\" md-is-locked-open=\"$mdMedia('gt-md')\" class=\"md-sidenav-left md-whiteframe-z2\"><md-toolbar>

Menu
<md-button hide-gt-md ng-click=\"toggle_sidenav()\" class=\"md-icon-button\"><md-icon aria-label=\"Close Menu\" md-font-icon=\"material-icons\">close</md-icon></md-button>
</md-toolbar><md-content layout=\"column\" layout-padding flex><md-button hide-gt-md ng-click=\"toggle_sidenav()\">Close Menu</md-button><md-divider></md-divider><md-button ng-if=\"config.user\" ng-click=\"logout()\">Logout</md-button></md-content></md-sidenav>");
}]);

angular.module("tasks.jade", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("tasks.jade",
    "
<md-list><md-list-item any-task ng-repeat=\"task in vm.tasks\" ng-click=\"vm.show_bottom_sheet($event, task)\" class=\"md-2-line\">

{{ task.name }}

{{ task.when.relative() }}

<md-icon aria-label=\"Update Time\" md-font-icon=\"material-icons\" ng-click=\"vm.update_time(task)\" class=\"md-secondary\">check</md-icon></md-list-item></md-list>
");
}]);
<|endoftext|>


<commit_before>// ========================================================================
// Copyright (c) 2008-2009, Metaweb Technologies, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY METAWEB TECHNOLOGIES AND CONTRIBUTORS
// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL METAWEB
// TECHNOLOGIES OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
// ========================================================================

/*
**  Automatic reconciliation
*/
var manualQueue = {};
var automaticQueue = new AutomaticQueue();

/** @constructor
  * @param {Array.<tEntity>=} initialValues
  */
function AutomaticQueue(initialValues) {
    /** @const
        @type {Array.<tEntity>}*/
    this.internalQueue = initialValues || [];
    /** @type {number} */
    this.length = this.internalQueue.length;
}

/** @param {tEntity} entity
  */
AutomaticQueue.prototype.push = function(entity) {
    entity['/rec_ui/rec_begun'] = true;
    this.length++;
    this.internalQueue.push(entity);
    updateUnreconciledCount();
    if (reconciliationBegun && !autoreconciling)
        autoReconcile();
}

/** @return {tEntity} */
AutomaticQueue.prototype.peek = function() {
    return this.internalQueue[0];
}

/** @return {tEntity|undefined} */
AutomaticQueue.prototype.shift = function() {
    if (this.length === 0)
        return undefined;
    this.length--;
    updateUnreconciledCount();
    return this.internalQueue.shift();
}

function beginAutoReconciliation() {
    $(".nowReconciling").show();
    $(".notReconciling").hide();
    $("#gettingInput").remove();
    autoReconcile();
}

var autoreconciling = false;
function finishedAutoReconciling() {
    $(".nowReconciling").hide();
    $('.notReconciling').show();
    autoreconciling = false;
}

function autoReconcile() {
    autoreconciling = true;
    if (automaticQueue.length == 0) {
        finishedAutoReconciling();
        return;
    }

    var entity = automaticQueue.peek();
    if (entity.getID() !== undefined) {
        automaticQueue.shift();
        addTimeout(autoReconcile, 0);
        if (!internalReconciler.getRecGroup(entity).shouldMerge)
            addReviewItem(entity);
        return;
    }
    getCandidates(entity, autoReconcileResults, function(){automaticQueue.shift();autoReconcile();});
}

/** @param {!tEntity} entity
    @param {boolean=} typeless
*/
function constructReconciliationQuery(entity, typeless) {
    var query = {}
    var headers = entity["/rec_ui/headers"];
    for (var i = 0; i < headers.length; i++) {
        var prop = headers[i];
        if (prop.charAt(0) != "/") continue;
        var parts = prop.split(":");
        $.each($.makeArray(entity.getChainedProperty(prop)),function(j, value) {
            var slot = query;
            if (value == undefined || value == "")
                return;
            if (parts.length === 1){
                slot[prop] = slot[prop] || [];
                slot[prop][j] = constructQueryPart(value);
                return;
            }
            slot[parts[0]]    = slot[parts[0]]    || [];
            slot[parts[0]][j] = slot[parts[0]][j] || {};
            slot = slot[parts[0]][j];
            $.each(parts.slice(1,parts.length-1), function(k,part) {
                slot[part] = slot[part] || {};
                slot = slot[part];
            });
            var lastPart = parts[parts.length-1];
            slot[lastPart] = constructQueryPart(value);
        })
    }
    if (typeless || !query['/type/object/type'])
        query['/type/object/type'] = ['/common/topic'];
    query = cleanup(query);
    entity['/rec_ui/recon_query'] = query;
    return query;

    function constructQueryPart(value) {
        if (value instanceof tEntity && !Arr.contains([undefined, "", "None", "None (merged"], value.getID()))
            return {"id":value.getID(), "name":value["/type/object/name"]}
        if (value['/rec_ui/id'] !== undefined)
            return $.makeArray(value["/type/object/name"])[0];
        return value;
    }

    /* Removes undefined values, nulls, empty lists, empty objects,
       and collapses singleton arrays down to their single values
       to better feed the reconciliation service.  */
    function cleanup(value) {
        switch(getType(value)){
        case "array":
            value = $.map(value, function(v) {
                return cleanup(v);
            });
            if (value.length === 0)
                return null;
            if (value.length === 1)
                return value[0];
            return value;
        case "object":
            var clone = {};
            for (var key in value) {
                var v = cleanup(value[key]);
                if (v === null || v === undefined)
                    continue;
                clone[key] = v;
            }
            if (isObjectEmpty(clone))
                return null;
            return clone;
        default:
            return value;
        }
    }
}

/**
 *  @param {tEntity} entity
 *  @param {function(tEntity)} callback
 *  @param {function(...)} onError
 *  @param {boolean=} typeless
 */
function getCandidates(entity, callback, onError,typeless) {
    function handler(results) {
        entity.reconResults = results;
        callback(entity);
    }
    var defaultLimit = 4;
    var limit = defaultLimit;
    if (entity.reconResults)
        limit = entity.reconResults.length * 2;
    if (!entity.typelessRecon && typeless){
        entity.typelessRecon = true;
        limit = defaultLimit;
    }
    var query = constructReconciliationQuery(entity,typeless);
    getJSON(reconciliation_url + "query?jsonp=?", {q:JSON.stringify(query), limit:limit}, handler, onError);
}

/** @param {tEntity} entity */
function autoReconcileResults(entity) {
    automaticQueue.shift();
    // no results, set to None:
    if(entity.reconResults.length == 0) {
        if (!entity.typelessRecon)
            getCandidates(entity,autoReconcileResults,function(){automaticQueue.shift();autoReconcile();},true);

        //If this entity is identical in name to several others then
        //it's not safe to automatically create new here, we need the
        //user to do internal reconciliation or duplicate entities could
        //be created
        if (internalReconciler.getRecGroup(entity).members.length > 1) {
            addToManualQueue(entity);
            return;
        }
        entity.reconcileWith("None", true);
        warn("No candidates found for the object:");
        warn(entity);
        addColumnRecCases(entity);
    }
    // match found:
    else if(entity.reconResults[0]["match"] == true) {
        var matchedResult = entity.reconResults[0];
        if (require_exact_name_match && !Arr.contains($.makeArray(entity['/type/object/name']),$.makeArray(matchedResult.name)[0])) {
            addToManualQueue(entity);
        }
        else {
            entity.reconcileWith(matchedResult.id, true);
            entity["/rec_ui/freebase_name"] = matchedResult.name;
            addColumnRecCases(entity);
        }
    }
    else
        addToManualQueue(entity)
    autoReconcile();
}<commit_msg>Ensure that rec_begun stays consistent<commit_after>// ========================================================================
// Copyright (c) 2008-2009, Metaweb Technologies, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY METAWEB TECHNOLOGIES AND CONTRIBUTORS
// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL METAWEB
// TECHNOLOGIES OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
// ========================================================================

/*
**  Automatic reconciliation
*/
var manualQueue = {};
var automaticQueue = new AutomaticQueue();

/** @constructor
  * @param {Array.<tEntity>=} initialValues
  */
function AutomaticQueue(initialValues) {
    /** @const
        @type {Array.<tEntity>}*/
    this.internalQueue = initialValues || [];
    $.each(this.internalQueue, function(_,entity) {
        entity['/rec_ui/rec_begun'] = true;
    });
    /** @type {number} */
    this.length = this.internalQueue.length;
}

/** @param {tEntity} entity
  */
AutomaticQueue.prototype.push = function(entity) {
    entity['/rec_ui/rec_begun'] = true;
    this.length++;
    this.internalQueue.push(entity);
    updateUnreconciledCount();
    if (reconciliationBegun && !autoreconciling)
        autoReconcile();
}

/** @return {tEntity} */
AutomaticQueue.prototype.peek = function() {
    return this.internalQueue[0];
}

/** @return {tEntity|undefined} */
AutomaticQueue.prototype.shift = function() {
    if (this.length === 0)
        return undefined;
    this.length--;
    updateUnreconciledCount();
    return this.internalQueue.shift();
}

function beginAutoReconciliation() {
    $(".nowReconciling").show();
    $(".notReconciling").hide();
    $("#gettingInput").remove();
    autoReconcile();
}

var autoreconciling = false;
function finishedAutoReconciling() {
    $(".nowReconciling").hide();
    $('.notReconciling').show();
    autoreconciling = false;
}

function autoReconcile() {
    autoreconciling = true;
    if (automaticQueue.length == 0) {
        finishedAutoReconciling();
        return;
    }

    var entity = automaticQueue.peek();
    if (entity.getID() !== undefined) {
        automaticQueue.shift();
        addTimeout(autoReconcile, 0);
        if (!internalReconciler.getRecGroup(entity).shouldMerge)
            addReviewItem(entity);
        return;
    }
    getCandidates(entity, autoReconcileResults, function(){automaticQueue.shift();autoReconcile();});
}

/** @param {!tEntity} entity
    @param {boolean=} typeless
*/
function constructReconciliationQuery(entity, typeless) {
    var query = {}
    var headers = entity["/rec_ui/headers"];
    for (var i = 0; i < headers.length; i++) {
        var prop = headers[i];
        if (prop.charAt(0) != "/") continue;
        var parts = prop.split(":");
        $.each($.makeArray(entity.getChainedProperty(prop)),function(j, value) {
            var slot = query;
            if (value == undefined || value == "")
                return;
            if (parts.length === 1){
                slot[prop] = slot[prop] || [];
                slot[prop][j] = constructQueryPart(value);
                return;
            }
            slot[parts[0]]    = slot[parts[0]]    || [];
            slot[parts[0]][j] = slot[parts[0]][j] || {};
            slot = slot[parts[0]][j];
            $.each(parts.slice(1,parts.length-1), function(k,part) {
                slot[part] = slot[part] || {};
                slot = slot[part];
            });
            var lastPart = parts[parts.length-1];
            slot[lastPart] = constructQueryPart(value);
        })
    }
    if (typeless || !query['/type/object/type'])
        query['/type/object/type'] = ['/common/topic'];
    query = cleanup(query);
    entity['/rec_ui/recon_query'] = query;
    return query;

    function constructQueryPart(value) {
        if (value instanceof tEntity && !Arr.contains([undefined, "", "None", "None (merged"], value.getID()))
            return {"id":value.getID(), "name":value["/type/object/name"]}
        if (value['/rec_ui/id'] !== undefined)
            return $.makeArray(value["/type/object/name"])[0];
        return value;
    }

    /* Removes undefined values, nulls, empty lists, empty objects,
       and collapses singleton arrays down to their single values
       to better feed the reconciliation service.  */
    function cleanup(value) {
        switch(getType(value)){
        case "array":
            value = $.map(value, function(v) {
                return cleanup(v);
            });
            if (value.length === 0)
                return null;
            if (value.length === 1)
                return value[0];
            return value;
        case "object":
            var clone = {};
            for (var key in value) {
                var v = cleanup(value[key]);
                if (v === null || v === undefined)
                    continue;
                clone[key] = v;
            }
            if (isObjectEmpty(clone))
                return null;
            return clone;
        default:
            return value;
        }
    }
}

/**
 *  @param {tEntity} entity
 *  @param {function(tEntity)} callback
 *  @param {function(...)} onError
 *  @param {boolean=} typeless
 */
function getCandidates(entity, callback, onError,typeless) {
    function handler(results) {
        entity.reconResults = results;
        callback(entity);
    }
    var defaultLimit = 4;
    var limit = defaultLimit;
    if (entity.reconResults)
        limit = entity.reconResults.length * 2;
    if (!entity.typelessRecon && typeless){
        entity.typelessRecon = true;
        limit = defaultLimit;
    }
    var query = constructReconciliationQuery(entity,typeless);
    getJSON(reconciliation_url + "query?jsonp=?", {q:JSON.stringify(query), limit:limit}, handler, onError);
}

/** @param {tEntity} entity */
function autoReconcileResults(entity) {
    automaticQueue.shift();
    // no results, set to None:
    if(entity.reconResults.length == 0) {
        if (!entity.typelessRecon)
            getCandidates(entity,autoReconcileResults,function(){automaticQueue.shift();autoReconcile();},true);

        //If this entity is identical in name to several others then
        //it's not safe to automatically create new here, we need the
        //user to do internal reconciliation or duplicate entities could
        //be created
        if (internalReconciler.getRecGroup(entity).members.length > 1) {
            addToManualQueue(entity);
            return;
        }
        entity.reconcileWith("None", true);
        warn("No candidates found for the object:");
        warn(entity);
        addColumnRecCases(entity);
    }
    // match found:
    else if(entity.reconResults[0]["match"] == true) {
        var matchedResult = entity.reconResults[0];
        if (require_exact_name_match && !Arr.contains($.makeArray(entity['/type/object/name']),$.makeArray(matchedResult.name)[0])) {
            addToManualQueue(entity);
        }
        else {
            entity.reconcileWith(matchedResult.id, true);
            entity["/rec_ui/freebase_name"] = matchedResult.name;
            addColumnRecCases(entity);
        }
    }
    else
        addToManualQueue(entity)
    autoReconcile();
}<|endoftext|>


<commit_before>import { observer as globalObserver } from '../../../common/utils/observer';

export default Interface =>
    class extends Interface {
        getMiscInterface() {
            return {
                notify        : args => globalObserver.emit('Notify', args),
                getTotalRuns  : () => this.tradeEngine.getTotalRuns(),
                getBalance    : type => this.tradeEngine.getBalance(type),
                getTotalProfit: () => this.tradeEngine.getTotalProfit(),
            };
        }
    };
<commit_msg>Create notifyTelegram logic<commit_after>import { notify } from '../broadcast';
import { translate } from '../../../common/i18n';
import { observer as globalObserver } from '../../../common/utils/observer';

export default Interface =>
    class extends Interface {
        notifyTelegram(botId, chatId, message) {
            const url = encodeURI(`https://api.telegram.org/bot${botId}/sendMessage?chat_id=${chatId}&text=${message}`);
            const onError = () => notify('warn', translate('The Telegram notification could not be sent'));

            fetch(url)
                .then(response => {
                    if (!response.ok) {
                        onError();
                    }
                })
                .catch(onError);
        }

        getMiscInterface() {
            return {
                notify        : args => globalObserver.emit('Notify', args),
                notifyTelegram: this.notifyTelegram,
                getTotalRuns  : () => this.tradeEngine.getTotalRuns(),
                getBalance    : type => this.tradeEngine.getBalance(type),
                getTotalProfit: () => this.tradeEngine.getTotalProfit(),
            };
        }
    };
<|endoftext|>


<commit_before>import qs from 'qs';
import _ from 'lodash';
import Axios from 'axios';
import {
  message
} from 'antd';

import {
  getTimeStamp
} from '../utils/time';

const reqConfig = {
  withCredentials: true
};

const Request = Axios.create(reqConfig);

function del(url, data) {
  return Request.delete(url, {
    params: data
  });
}

function get(url, data) {
  return Request.get(url, {
    params: data
  });
}

function post(url, data) {
  return Request.post(url, data);
}

function throwSrvError(resp) {
  if (resp.data) {
    message.error(`[${resp.data.code}] Server error: ${resp.data.msg}`, 5);
  } else {
    message.error(`Server error: ${JSON.stringify(resp)}`, 10);
  }
  return resp;
}

function procReqError(resp) {
  const error = new Error(resp.statusText);
  error.resp = resp;
  throw error;
}

function checkStatus(resp) {
  if ((resp.status >= 200) && (resp.status < 300)) {
    return resp;
  }
  return throwSrvError(resp);
}

// function checkPermission(resp) {
//   if (resp.data && (resp.data.code === 403)) {
//     return throwSrvError(resp);
//   }
//   return resp;
// }

// function checkRedirect(resp) {
//   if (resp.data && (resp.data.code === 302)) {
//     return throwSrvError(resp);
//   }
//   return resp;
// }

// function checkCode(resp) {
//   if (resp.data && (resp.data.code !== 0)) {
//     return throwSrvError(resp);
//   }
//   return resp;
// }

function procRequest(req) {
  return req
    .then(checkStatus)
    // .then(checkPermission)
    // .then(checkRedirect)
    // .then(checkCode)
    .then(resp => resp.data)
    .catch(procReqError);
}

export function DelJson(url, data) {
  const _data = data ? _.cloneDeep(data) : {};
  _data._t_ = getTimeStamp();
  return procRequest(del(url, _data));
}

export function GetJson(url, data) {
  const _data = data ? _.cloneDeep(data) : {};
  _data._t_ = getTimeStamp();
  return procRequest(get(url, _data));
}

export function PostJson(url, data) {
  return procRequest(post(url, data));
}

export function PostForm(url, data) {
  return procRequest(post(url, qs.stringify(data)));
}
<commit_msg>refactor(prj): src code<commit_after>import qs from 'qs';
import _ from 'lodash';
import Axios from 'axios';
import {
  message
} from 'antd';

import {
  getTimeStamp
} from './time.js';

const reqConfig = {
  withCredentials: true
};

const Request = Axios.create(reqConfig);

function del(url, data) {
  return Request.delete(url, {
    params: data
  });
}

function get(url, data) {
  return Request.get(url, {
    params: data
  });
}

function post(url, data) {
  return Request.post(url, data);
}

function throwSrvError(resp) {
  if (resp.data) {
    message.error(`[${resp.data.code}] Server error: ${resp.data.msg}`, 5);
  } else {
    message.error(`Server error: ${JSON.stringify(resp)}`, 10);
  }
  return resp;
}

function procReqError(resp) {
  const error = new Error(resp.statusText);
  error.resp = resp;
  throw error;
}

function checkStatus(resp) {
  if ((resp.status >= 200) && (resp.status < 300)) {
    return resp;
  }
  return throwSrvError(resp);
}

// function checkPermission(resp) {
//   if (resp.data && (resp.data.code === 403)) {
//     return throwSrvError(resp);
//   }
//   return resp;
// }

// function checkRedirect(resp) {
//   if (resp.data && (resp.data.code === 302)) {
//     return throwSrvError(resp);
//   }
//   return resp;
// }

// function checkCode(resp) {
//   if (resp.data && (resp.data.code !== 0)) {
//     return throwSrvError(resp);
//   }
//   return resp;
// }

function procRequest(req) {
  return req
    .then(checkStatus)
    // .then(checkPermission)
    // .then(checkRedirect)
    // .then(checkCode)
    .then(resp => resp.data)
    .catch(procReqError);
}

export function DelJson(url, data) {
  const _data = data ? _.cloneDeep(data) : {};
  _data._t_ = getTimeStamp();
  return procRequest(del(url, _data));
}

export function GetJson(url, data) {
  const _data = data ? _.cloneDeep(data) : {};
  _data._t_ = getTimeStamp();
  return procRequest(get(url, _data));
}

export function PostJson(url, data) {
  return procRequest(post(url, data));
}

export function PostForm(url, data) {
  return procRequest(post(url, qs.stringify(data)));
}
<|endoftext|>


<commit_before>/*
 * example.js - Example command to show basic structure of commands.
 *
 * Contributed by Capuccino, Ovyerus.
 */

exports.commands = [
    // Define the names of your commands here.
    'ping',
    'pong'
];

// Example ping command.
exports.ping = {
    desc: '',
    fullDesc: '',
    adminOnly: true,
    usage: '',
    main: (bot, ctx) => {
        return new Promise((resolve, reject) => {
            ctx.createMessage('Ping!').then(() => resolve()).catch(err => reject([err]));
        });
    }
};

// Example pong command (demonstrating a full command, and multiple commands in a file).
exports.pong = {
    desc: 'Ping!',
    fullDesc: 'Simple response command.',
    main: (bot, ctx) => {
        return new Promise((resolve, reject) => {
            ctx.createMessage('Pong!').then(() => resolve()).catch(err => reject([err]));
        });
    }
};<commit_msg>moved example to new system<commit_after>/*
 * example.js - Example command to show basic structure of commands.
 *
 * Contributed by Capuccino, Ovyerus.
 */

exports.commands = [
    // Define the names of your commands here.
    'ping',
    'pong'
];

// Example ping command.
exports.ping = {
    desc: '',
    fullDesc: '',
    adminOnly: true,
    usage: '',
    main: (bot, ctx) => {
        return new Promise((resolve, reject) => {
            ctx.createMessage('Ping!').then(resolve).catch(reject);
        });
    }
};

// Example pong command (demonstrating a full command, and multiple commands in a file).
exports.pong = {
    desc: 'Ping!',
    fullDesc: 'Simple response command.',
    main: (bot, ctx) => {
        return new Promise((resolve, reject) => {
            ctx.createMessage('Pong!').then(resolve).catch(reject);
        });
    }
};<|endoftext|>


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

let proxyURL = 'http://localhost:8686';

if (process.env.NODE_ENV == 'docker') {
  proxyURL = 'http://web:8686';
}

module.exports = {
  // entry file - starting point for the app
  entry: './src',

  // where to dump the output of a production build
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'bundle.js'
  },

  module: {
    rules: [
      {
        test: /\.css$/,
        loader: 'style-loader'
      },
      {
        test: /\.css$/,
        loader: 'css-loader',
        query: {
          modules: true,
          localIdentName: '[name]'
        }
      },
      {
        test: /\.jsx?/i,
        loader: 'babel-loader',
        options: {
          presets: [
            '@babel/preset-env'
          ],
          plugins: [
            ['transform-react-jsx']
          ]
        }
      }
    ]
  },

  // enable Source Maps
  devtool: 'source-map',

  devServer: {
    host: '0.0.0.0',
    disableHostCheck: true,
    // serve up any static files from src/
    contentBase: path.join(__dirname, 'src'),

    // enable gzip compression:
    compress: true,

    // enable pushState() routing, as used by preact-router et al:
    historyApiFallback: true,

    proxy: {
      '/api': {
        target: proxyURL,
        secure: false
      }
    }
  },

  resolve: {
    alias: {
      config: path.join(__dirname, 'src', 'config', process.env.NODE_ENV || 'development')
    }
  }

  // resolve: {
  //   alias: {
  //     'react': 'preact-compat',
  //     'react-dom': 'preact-compat',
  //     // Not necessary unless you consume a module using `createClass`
  //     'create-react-class': 'preact-compat/lib/create-react-class'
  //   }
  // }
};<commit_msg>update config<commit_after>const path = require('path');

let proxyURL = 'http://localhost:8686';

if (process.env.NODE_ENV == 'docker') {
  proxyURL = 'http://web:8686';
}

module.exports = {
  // entry file - starting point for the app
  entry: './src',

  // where to dump the output of a production build
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'bundle.js'
  },

  module: {
    rules: [
      {
        test: /\.css$/,
        loader: 'style-loader'
      },
      {
        test: /\.css$/,
        loader: 'css-loader',
        query: {
          modules: true,
          localIdentName: '[name]'
        }
      },
      {
        test: /\.jsx?/i,
        loader: 'babel-loader',
        options: {
          presets: [
            '@babel/preset-env'
          ],
          plugins: [
            ['transform-react-jsx']
          ]
        }
      }
    ]
  },

  // enable Source Maps
  devtool: 'source-map',

  devServer: {
    host: '0.0.0.0',
    disableHostCheck: true,
    // serve up any static files from src/
    contentBase: path.join(__dirname, 'src'),

    // enable gzip compression:
    compress: true,

    // enable pushState() routing, as used by preact-router et al:
    historyApiFallback: true,

    proxy: {
      '/api': {
        target: proxyURL,
        secure: false
      }
    }
  },

  resolve: {
    alias: {
      config: path.join(__dirname, 'src', 'config', process.env.NODE_ENV || 'development')
    }
  },

  stats: 'verbose',

  // resolve: {
  //   alias: {
  //     'react': 'preact-compat',
  //     'react-dom': 'preact-compat',
  //     // Not necessary unless you consume a module using `createClass`
  //     'create-react-class': 'preact-compat/lib/create-react-class'
  //   }
  // }
};<|endoftext|>


<commit_before>// popup script

window.onload = () => {
	browser.runtime.sendMessage({
		behavior: 'load'
	}).then( ( resObj => {
		if (!(resObj && resObj.data)) return false;
		var selector   = document.querySelector('#cache_seletor');
		var show_cache = document.querySelector('#show_cache');
		var copy_btn   = document.querySelector('#copy_btn');

		var escapeHTML = str => str.toString().replace(/[&"'<>]/g, m => ({
			"&": "&",
			'"': """,
			"'": "'",
			"<": "<",
			">": ">"
		})[m]);

		var selectText = dom  => {
			if (document.selection) {
				var range = document.body.createTextRange();
				range.moveToElementText(dom);
				range.select();
			}
			else if (window.getSelection) {
				var range = document.createRange();
				range.selectNode(dom);
				window.getSelection().addRange(range);
			}
		};

		var showPreview = (isWYSIWYG, val) => {
			// val is used to show preview of WYSIWYG,
			// so it should not be escaped.

			if (isWYSIWYG) {
				show_cache.type = 'WYSIWYG';
				show_cache.innerHTML = val;
			}
			else {
				show_cache.type = 'txt';
				show_cache.innerHTML = `<textarea>${escapeHTML(val)}</textarea>`;
			}
		};

		for (var key in resObj.data) {

            if (!resObj.data[key] || key == 'version') continue;
            var type = resObj.data[key].type;
            var cache = resObj.data[key].val;

            var select_title = escapeHTML(key).substr(0,50) + '...';
            selector.innerHTML += `<option value="${escapeHTML(key)}">${select_title}</option>`;

            if (show_cache.innerHTML == '')
                showPreview(type == 'WYSIWYG', cache);

		}

		if (!document.querySelector('option')) {
			document.querySelector('body').innerHTML = '
You don\'t have any cache yet!
';
		}

		selector.addEventListener('change', e => {
			var key = e.target.value;
			var cache = resObj.data[key].val;
			var isWYSIWYG = resObj.data[key].type == 'WYSIWYG';

			showPreview(isWYSIWYG, cache);
		});

		copy_btn.addEventListener('click', () => {
			if (show_cache.type == 'WYSIWYG') {
				selectText(show_cache);
			}
			else {
				document.querySelector("textarea").select();
			}
			document.execCommand("Copy");
			// alert('You got it, now put your cache anyway!');
		});
	}));
}
<commit_msg>cancel the title truncated<commit_after>// popup script

window.onload = () => {
	browser.runtime.sendMessage({
		behavior: 'load'
	}).then( ( resObj => {
		if (!(resObj && resObj.data)) return false;
		var selector   = document.querySelector('#cache_seletor');
		var show_cache = document.querySelector('#show_cache');
		var copy_btn   = document.querySelector('#copy_btn');

		var escapeHTML = str => str.toString().replace(/[&"'<>]/g, m => ({
			"&": "&",
			'"': """,
			"'": "'",
			"<": "<",
			">": ">"
		})[m]);

		var selectText = dom  => {
			if (document.selection) {
				var range = document.body.createTextRange();
				range.moveToElementText(dom);
				range.select();
			}
			else if (window.getSelection) {
				var range = document.createRange();
				range.selectNode(dom);
				window.getSelection().addRange(range);
			}
		};

		var showPreview = (isWYSIWYG, val) => {
			// val is used to show preview of WYSIWYG,
			// so it should not be escaped.

			if (isWYSIWYG) {
				show_cache.type = 'WYSIWYG';
				show_cache.innerHTML = val;
			}
			else {
				show_cache.type = 'txt';
				show_cache.innerHTML = `<textarea>${escapeHTML(val)}</textarea>`;
			}
		};

		for (var key in resObj.data) {

            if (!resObj.data[key] || key == 'version') continue;
            var type = resObj.data[key].type;
            var cache = resObj.data[key].val;

            var select_title = escapeHTML(key);
            selector.innerHTML += `<option value="${escapeHTML(key)}">${select_title}</option>`;

            if (show_cache.innerHTML == '')
                showPreview(type == 'WYSIWYG', cache);

		}

		if (!document.querySelector('option')) {
			document.querySelector('body').innerHTML = '
You don\'t have any cache yet!
';
		}

		selector.addEventListener('change', e => {
			var key = e.target.value;
			var cache = resObj.data[key].val;
			var isWYSIWYG = resObj.data[key].type == 'WYSIWYG';

			showPreview(isWYSIWYG, cache);
		});

		copy_btn.addEventListener('click', () => {
			if (show_cache.type == 'WYSIWYG') {
				selectText(show_cache);
			}
			else {
				document.querySelector("textarea").select();
			}
			document.execCommand("Copy");
			// alert('You got it, now put your cache anyway!');
		});
	}));
}
<|endoftext|>


<commit_before>import React from 'react';

const config = require('../config');
import Clock from '../components/Clock';
import Weather from '../components/Weather';
import Forecast from '../components/Forecast'
import News from '../components/News';
import RecordingStatus from '../components/RecordingStatus';
import Message from '../components/Message';
import Tasks from '../components/Tasks';
import Article from '../components/Article';
import TemperatureGraph from '../components/TemperatureGraph';
import Transfers from '../components/Transfers';

import moment from 'moment'
moment.locale('sv');

import { Col, Row } from 'react-bootstrap';

export default class App extends React.Component {
  constructor(props) {
    super(props);
    let s = new WebSocket('ws://' + config.wsServerBaseURL);
    s.onmessage = this.handleMessage.bind(this);
    s.addEventListener('error', m => console.log(m));
    s.addEventListener('open', m => {
      console.log(m);
      s.send({event: 'connect', data: 'Hey there'});
    });
    this.state = {
      temperature: '22.6',
      isRecording: false,
      message: {
        text: 'No messages set',
        visible: false
      },
      visibility: {
        news: true,
        forecasts: true,
        article: false,
        tasks: true,
        weather: true,
        clock: true,
        temperatureGraph: false,
        transfers: true,
      }
    };
  }

  handleMessage(message) {
    message = JSON.parse(message.data);
    const data = message.data;
    switch(message.event){
      case 'temperature':
        this.setState({
          temperature: data.temperature
        })
        break;
      case 'recording':
        this.setState({
          isRecording: message.data.isRecording
        })
        break;
      case 'motion':
        if(!this.state.message.visible){
          this.setState({
            message: {
              text: data.message,
              visible: true
            }
          })
          setTimeout(() => {
            this.setState({
              message: {
                visible: false
              }
            })
          }, 10000)
        }
        break;
      case 'visibility':
        const prevStateVisability = this.state.visibility;
        prevStateVisability[data.component] = data.visible;
        this.setState({
          visibility: prevStateVisability
        })
        break;
      case 'command':
        this.refs[data.component].onEvent(data);
        break;
      default:
        console.log('Unhandled event: ' + message.event);
        break;
    }
  }

  render() {
    let dateTime, transfers, news, tasks, weather, forecast, temperatureGraph, articles = null;
    if (config.modules.dateTime === true) {
      dateTime = (<Clock temperature={this.state.temperature} visible={this.state.visibility.clock} showTemperature={config.modules.tempPirSensor}/>)
    }
    if (config.modules.transfer === true) {
      transfers = (<Transfers visible={this.state.visibility.transfers} />);
    }
    if (config.modules.news === true) {
      news = (<News visible={this.state.visibility.news} />)
    }
    if (config.modules.wunderlistTasks === true) {
      tasks = (<Tasks visible={this.state.visibility.tasks} />)
    }
    if (config.modules.weather === true) {
      weather = (<Weather visible={this.state.visibility.weather} />)
    }
    if (config.modules.forecast === true) {
      forecast = (<Forecast visible={this.state.visibility.forecasts} />)
    }
    if (config.modules.temperatureGraph === true) {
      temperatureGraph = (<TemperatureGraph ref='temperatureGraph' visible={this.state.visibility.temperatureGraph} />)
    }
    if (config.modules.articles === true) {
      articles = (
)
    }

    const AppStyles = {
      fontSize: config.styles.textScale,
      fontFamily: config.styles.fontFamily,
      fontWeight: config.styles.fontWeight,
      paddingTop: config.styles.paddingTop,
      paddingLeft: config.styles.paddingLeft,
      paddingRight: config.styles.paddingRight,
      paddingBottom: config.styles.paddingBottom
    };

    return (

        {articles}
        {temperatureGraph}

        <Row className='Container'>
          <Col xs={4}>
            {dateTime}
            <RecordingStatus isRecording={this.state.isRecording} />
            {tasks}
            {transfers}

          </Col>
          <Col xs={4} />
          <Col xs={4}>
            <Row>
              {weather}
            </Row>
            <Row style={{marginTop: 50}}>
              {forecast}
            </Row>
          </Col>
        </Row>
        <Row style={{height: '1%'}}/>
        <Row style={{marginBottom: 100, marginTop: 50}}>
          <Message props={{visible: this.state.message.visible, message: this.state.message.text}}/>
        </Row>
        <Row style={{position: 'absolute', bottom: '0px', left: '0px', width: '100%', padding: 60, paddingBottom: 0}}>
          {news}
        </Row>

    );
  }
}
<commit_msg>Update moment.locale to use config language in App.js  (#39)<commit_after>import React from 'react';

const config = require('../config');
import Clock from '../components/Clock';
import Weather from '../components/Weather';
import Forecast from '../components/Forecast'
import News from '../components/News';
import RecordingStatus from '../components/RecordingStatus';
import Message from '../components/Message';
import Tasks from '../components/Tasks';
import Article from '../components/Article';
import TemperatureGraph from '../components/TemperatureGraph';
import Transfers from '../components/Transfers';

import moment from 'moment'
config.language ? moment.locale(config.language) : moment.locale('en');

import { Col, Row } from 'react-bootstrap';

export default class App extends React.Component {
  constructor(props) {
    super(props);
    let s = new WebSocket('ws://' + config.wsServerBaseURL);
    s.onmessage = this.handleMessage.bind(this);
    s.addEventListener('error', m => console.log(m));
    s.addEventListener('open', m => {
      console.log(m);
      s.send({event: 'connect', data: 'Hey there'});
    });
    this.state = {
      temperature: '22.6',
      isRecording: false,
      message: {
        text: 'No messages set',
        visible: false
      },
      visibility: {
        news: true,
        forecasts: true,
        article: false,
        tasks: true,
        weather: true,
        clock: true,
        temperatureGraph: false,
        transfers: true,
      }
    };
  }

  handleMessage(message) {
    message = JSON.parse(message.data);
    const data = message.data;
    switch(message.event){
      case 'temperature':
        this.setState({
          temperature: data.temperature
        })
        break;
      case 'recording':
        this.setState({
          isRecording: message.data.isRecording
        })
        break;
      case 'motion':
        if(!this.state.message.visible){
          this.setState({
            message: {
              text: data.message,
              visible: true
            }
          })
          setTimeout(() => {
            this.setState({
              message: {
                visible: false
              }
            })
          }, 10000)
        }
        break;
      case 'visibility':
        const prevStateVisability = this.state.visibility;
        prevStateVisability[data.component] = data.visible;
        this.setState({
          visibility: prevStateVisability
        })
        break;
      case 'command':
        this.refs[data.component].onEvent(data);
        break;
      default:
        console.log('Unhandled event: ' + message.event);
        break;
    }
  }

  render() {
    let dateTime, transfers, news, tasks, weather, forecast, temperatureGraph, articles = null;
    if (config.modules.dateTime === true) {
      dateTime = (<Clock temperature={this.state.temperature} visible={this.state.visibility.clock} showTemperature={config.modules.tempPirSensor}/>)
    }
    if (config.modules.transfer === true) {
      transfers = (<Transfers visible={this.state.visibility.transfers} />);
    }
    if (config.modules.news === true) {
      news = (<News visible={this.state.visibility.news} />)
    }
    if (config.modules.wunderlistTasks === true) {
      tasks = (<Tasks visible={this.state.visibility.tasks} />)
    }
    if (config.modules.weather === true) {
      weather = (<Weather visible={this.state.visibility.weather} />)
    }
    if (config.modules.forecast === true) {
      forecast = (<Forecast visible={this.state.visibility.forecasts} />)
    }
    if (config.modules.temperatureGraph === true) {
      temperatureGraph = (<TemperatureGraph ref='temperatureGraph' visible={this.state.visibility.temperatureGraph} />)
    }
    if (config.modules.articles === true) {
      articles = (
)
    }

    const AppStyles = {
      fontSize: config.styles.textScale,
      fontFamily: config.styles.fontFamily,
      fontWeight: config.styles.fontWeight,
      paddingTop: config.styles.paddingTop,
      paddingLeft: config.styles.paddingLeft,
      paddingRight: config.styles.paddingRight,
      paddingBottom: config.styles.paddingBottom
    };

    return (

        {articles}
        {temperatureGraph}

        <Row className='Container'>
          <Col xs={4}>
            {dateTime}
            <RecordingStatus isRecording={this.state.isRecording} />
            {tasks}
            {transfers}

          </Col>
          <Col xs={4} />
          <Col xs={4}>
            <Row>
              {weather}
            </Row>
            <Row style={{marginTop: 50}}>
              {forecast}
            </Row>
          </Col>
        </Row>
        <Row style={{height: '1%'}}/>
        <Row style={{marginBottom: 100, marginTop: 50}}>
          <Message props={{visible: this.state.message.visible, message: this.state.message.text}}/>
        </Row>
        <Row style={{position: 'absolute', bottom: '0px', left: '0px', width: '100%', padding: 60, paddingBottom: 0}}>
          {news}
        </Row>

    );
  }
}
<|endoftext|>