<commit_before>process.env.NODE_ENV = 'test';
var _ = require('underscore');
var assert = require('assert');

// Load application.
require('..');
var tilestream = require('bones').plugin;
tilestream.config = {
    tiles: __dirname + '/fixtures/tiles',
    uiPort: 8888,
    tilePort: 8888,
    subdomains: 'a,b,c,d'
};
var server = new tilestream.servers.Core(tilestream);
var request = { headers: { 'host': 'localhost:8888' } };

exports['tile'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/control_room/3/4/5.png' },
        { status: 200 },
        function(res) {
            assert.equal(res.headers['content-length'], 63554);
            assert.equal(res.headers['content-type'], 'image/png');
            assert.equal(res.headers['cache-control'], 'max-age=3600');
            assert.ok(res.headers['last-modified']);
            assert.ok(res.headers['e-tag']);
        }
    );
};

exports['tile invalid name'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/bad.name/0/0/0.png' },
        {
            body: 'Tileset not found.',
            status: 404
        }
    );
};

exports['error tile'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/control_room/-1/-1/-1.png' },
        {
            body: 'Tile does not exist',
            status: 404
        }
    );
};

exports['grid tile'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/waxtest/0/0/0.grid.json?callback=grid' },
        { status: 200, body: /grid\(/ }
    );
};

exports['layer json'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/waxtest/layer.json?callback=grid' },
        { status: 200, body: /grid\(/ }
    );
};

exports['mbtiles download'] = function() {
    assert.response(
        server,
        { url: '/download/control_room.mbtiles' },
        { status: 200 },
        function(res) {
            // @TODO: determine why download is sometimes off by 1 (or more?) byte(s)
            assert.ok(res.body.length >= 2976208 || res.body.length <= 2976209);
        }
    );
};

exports['load map'] = function() {
    assert.response(
        server,
        { url: '/api/Tileset/control_room' },
        { status: 200 },
        function(res) {
            var map;
            assert.doesNotThrow(function() {
                map = JSON.parse(res.body);
            }, SyntaxError);
            assert.equal(map.id, 'control_room');
            assert.equal(map.type, 'baselayer');
            assert.equal(map.bounds, "-180,-85.05112877980659,180,89.99075251648905");
        }
    );
};

exports['load map v1'] = function() {
    assert.response(
        server,
        { url: '/api/v1/Tileset/control_room' },
        { status: 200 },
        function(res) {
            var map;
            assert.doesNotThrow(function() {
                map = JSON.parse(res.body);
            }, SyntaxError);
            assert.equal(map.id, 'control_room');
            assert.equal(map.type, 'baselayer');
            assert.equal(map.bounds, "-180,-85.05112877980659,180,89.99075251648905");
        }
    );
};

exports['load maps'] = function() {
    assert.response(
        server,
        { url: '/api/Tileset' },
        { status: 200 },
        function(res) {
            var maps;
            assert.doesNotThrow(function() {
                maps = JSON.parse(res.body);
            }, SyntaxError);
            assert.equal(maps.length, 2);
            assert.equal(maps[0].id, 'control_room');
            assert.equal(maps[0].type, 'baselayer');
            assert.equal(maps[0].bounds, "-180,-85.05112877980659,180,89.99075251648905");
        }
    );
};

exports['load maps v1'] = function() {
    assert.response(
        server,
        { url: '/api/v1/Tileset' },
        { status: 200 },
        function(res) {
            var maps;
            assert.doesNotThrow(function() {
                maps = JSON.parse(res.body);
            }, SyntaxError);
            assert.equal(maps.length, 2);
            assert.equal(maps[0].id, 'control_room');
            assert.equal(maps[0].type, 'baselayer');
            assert.equal(maps[0].bounds, "-180,-85.05112877980659,180,89.99075251648905");
        }
    );
};

exports['ssviews list'] = function() {
    assert.response(
        server,
        _({ url: '/' }).extend(request),
        { status: 200, body: /name\">control_room/ }
    );
};

exports['ssviews map'] = function() {
    assert.response(
        server,
        _({ url: '/map/control_room' }).extend(request),
        { status: 200, body: /control_room<\/a/ }
    );
    assert.response(
        server,
        { url: '/?_escaped_fragment_=/map/control_room' },
        { status: 301 }
    );
};

exports['ssviews error'] = function() {
    assert.response(
        server,
        _({ url: '/map/asdf' }).extend(request),
        { status: 200, body: /Connection problem.<\/div/ }
    );
    assert.response(
        server,
        { url: '/?_escaped_fragment_=/map/asdf' },
        { status: 301 }
    );
};

exports['wax endpoint'] = function() {
    var fs = require('fs'),
        fixtures = {
            layers: JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.layers.json', 'utf8')),
            el:     JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.el.json', 'utf8')),
            center: JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.center.json', 'utf8')),
            options: JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.options.json', 'utf8'))
        };
    assert.response(
        server,
        { url: '/api/wax.json?api=foo' },
        { status: 400, body: /`api` is invalid/ }
    );

    assert.response(
        server,
        { url: '/api/wax.json' },
        { status: 400, body: /`layers` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers=foo' },
        { status: 400, body: /`layers` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=foo' },
        { status: 500, body: /Tileset not found/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.layers, JSON.parse(res.body));
        }
    );
    assert.response(
        server,
        _.extend({ url: '/api/v1/wax.json?layers[]=control_room' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.layers, JSON.parse(res.body));
        }
    );

    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&el[]=foo' },
        { status: 400, body: /`el` is invalid/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room&el=foo' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.el, JSON.parse(res.body));
        }
    );

    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&center=foo' },
        { status: 400, body: /`center` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&center[]=0' },
        { status: 400, body: /`center` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&center[]=0&center[]=0&center[]=foo' },
        { status: 400, body: /`center` is invalid/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room&center[]=40&center[]=40&center[]=2' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.center, JSON.parse(res.body));
        }
    );

    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&options=foo' },
        { status: 400, body: /`options` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&options[]=foo' },
        { status: 400, body: /`options` is invalid/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room&options[]=tooltips' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.options, JSON.parse(res.body));
        }
    );
};
<commit_msg>Add test for tilehost info.<commit_after>process.env.NODE_ENV = 'test';
var _ = require('underscore');
var assert = require('assert');

// Load application.
require('..');
var tilestream = require('bones').plugin;
tilestream.config = {
    tiles: __dirname + '/fixtures/tiles',
    uiPort: 8888,
    tilePort: 8888,
    subdomains: 'a,b,c,d'
};
var server = new tilestream.servers.Core(tilestream);
var request = { headers: { 'host': 'localhost:8888' } };

exports['tile'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/control_room/3/4/5.png' },
        { status: 200 },
        function(res) {
            assert.equal(res.headers['content-length'], 63554);
            assert.equal(res.headers['content-type'], 'image/png');
            assert.equal(res.headers['cache-control'], 'max-age=3600');
            assert.ok(res.headers['last-modified']);
            assert.ok(res.headers['e-tag']);
        }
    );
};

exports['tile invalid name'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/bad.name/0/0/0.png' },
        {
            body: 'Tileset not found.',
            status: 404
        }
    );
};

exports['error tile'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/control_room/-1/-1/-1.png' },
        {
            body: 'Tile does not exist',
            status: 404
        }
    );
};

exports['grid tile'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/waxtest/0/0/0.grid.json?callback=grid' },
        { status: 200, body: /grid\(/ }
    );
};

exports['layer json'] = function() {
    assert.response(
        server,
        { url: '/1.0.0/waxtest/layer.json?callback=grid' },
        { status: 200, body: /grid\(/ }
    );
};

exports['mbtiles download'] = function() {
    assert.response(
        server,
        { url: '/download/control_room.mbtiles' },
        { status: 200 },
        function(res) {
            // @TODO: determine why download is sometimes off by 1 (or more?) byte(s)
            assert.ok(res.body.length >= 2976208 || res.body.length <= 2976209);
        }
    );
};

exports['load map'] = function() {
    assert.response(
        server,
        { url: '/api/Tileset/control_room' },
        { status: 200 },
        function(res) {
            var map;
            assert.doesNotThrow(function() {
                map = JSON.parse(res.body);
            }, SyntaxError);
            assert.equal(map.id, 'control_room');
            assert.equal(map.type, 'baselayer');
            assert.equal(map.bounds, "-180,-85.05112877980659,180,89.99075251648905");
        }
    );
};

exports['load map v1'] = function() {
    assert.response(
        server,
        { url: '/api/v1/Tileset/control_room' },
        { status: 200 },
        function(res) {
            var map;
            assert.doesNotThrow(function() {
                map = JSON.parse(res.body);
            }, SyntaxError);
            assert.equal(map.id, 'control_room');
            assert.equal(map.type, 'baselayer');
            assert.equal(map.bounds, '-180,-85.05112877980659,180,89.99075251648905');
            assert.deepEqual(mapl.host, ['http://a.localhost:8888/', 'http://b.localhost:8888/', 'http:/c.localhost:8888/', 'http://d.localhost:8888/']);

        }
    );
};

var loadMaps = function(res) {
    var maps;
    assert.doesNotThrow(function() {
        maps = JSON.parse(res.body);
    }, SyntaxError);
    assert.equal(maps.length, 2);
    assert.equal(maps[0].id, 'control_room');
    assert.equal(maps[0].type, 'baselayer');
    assert.equal(maps[0].bounds, '-180,-85.05112877980659,180,89.99075251648905');
    assert.deepEqual(maps[0].host, ['http://a.localhost:8888/', 'http://b.localhost:8888/', 'http://c.localhost:8888/', 'http://d.localhost:8888/']);
};

exports['load maps'] = function() {
    assert.response(
        server,
        _({ url: '/api/Tileset' }).extend(request),
        { status: 200 },
        loadMaps
    );
};

exports['load maps v1'] = function() {
    assert.response(
        server,
        _({ url: '/api/v1/Tileset' }).extend(request),
        { status: 200 },
        loadMaps
    );
};

exports['ssviews list'] = function() {
    assert.response(
        server,
        _({ url: '/' }).extend(request),
        { status: 200, body: /name\">control_room/ }
    );
};

exports['ssviews map'] = function() {
    assert.response(
        server,
        _({ url: '/map/control_room' }).extend(request),
        { status: 200, body: /control_room<\/a/ }
    );
    assert.response(
        server,
        { url: '/?_escaped_fragment_=/map/control_room' },
        { status: 301 }
    );
};

exports['ssviews error'] = function() {
    assert.response(
        server,
        _({ url: '/map/asdf' }).extend(request),
        { status: 200, body: /Connection problem.<\/div/ }
    );
    assert.response(
        server,
        { url: '/?_escaped_fragment_=/map/asdf' },
        { status: 301 }
    );
};

exports['wax endpoint'] = function() {
    var fs = require('fs'),
        fixtures = {
            layers: JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.layers.json', 'utf8')),
            el:     JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.el.json', 'utf8')),
            center: JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.center.json', 'utf8')),
            options: JSON.parse(fs.readFileSync(__dirname + '/fixtures/wax/wax.options.json', 'utf8'))
        };
    assert.response(
        server,
        { url: '/api/wax.json?api=foo' },
        { status: 400, body: /`api` is invalid/ }
    );

    assert.response(
        server,
        { url: '/api/wax.json' },
        { status: 400, body: /`layers` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers=foo' },
        { status: 400, body: /`layers` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=foo' },
        { status: 500, body: /Tileset not found/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.layers, JSON.parse(res.body));
        }
    );
    assert.response(
        server,
        _.extend({ url: '/api/v1/wax.json?layers[]=control_room' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.layers, JSON.parse(res.body));
        }
    );

    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&el[]=foo' },
        { status: 400, body: /`el` is invalid/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room&el=foo' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.el, JSON.parse(res.body));
        }
    );

    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&center=foo' },
        { status: 400, body: /`center` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&center[]=0' },
        { status: 400, body: /`center` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&center[]=0&center[]=0&center[]=foo' },
        { status: 400, body: /`center` is invalid/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room&center[]=40&center[]=40&center[]=2' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.center, JSON.parse(res.body));
        }
    );

    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&options=foo' },
        { status: 400, body: /`options` is invalid/ }
    );
    assert.response(
        server,
        { url: '/api/wax.json?layers[]=control_room&options[]=foo' },
        { status: 400, body: /`options` is invalid/ }
    );
    assert.response(
        server,
        _.extend({ url: '/api/wax.json?layers[]=control_room&options[]=tooltips' }, request),
        { status: 200 },
        function(res) {
            assert.deepEqual(fixtures.options, JSON.parse(res.body));
        }
    );
};
<|endoftext|>


<commit_before>function WindowPage() {
	var inst = window.Page;
	if (inst) {
		// because using instanceof requires reference to the same WindowPage
		if (inst.name == "WindowPage") return inst;
		else if (inst.state) this.state = inst.state;
	}
	this.name = "WindowPage";

	var QueryString = require('query-string');

	this.location = new URL("", document.location);

	this.parse = function(str) {
		if (!str) str = document.location.search;
		return QueryString.parse(str);
	};
	this.stringify = function(obj) {
		return QueryString.stringify(obj);
	};

	this.builds = [];
	this.handles = [];

	this.historyListener = this.historyListener.bind(this);
	window.addEventListener('popstate', this.historyListener);

	this.import = this.import.bind(this);
	this.build = this.build.bind(this);

	this.waitBuild(function() {
		if (this.stage() == "build") {
			this.builder = Promise.resolve();
			this.waitHandle(this.handle.bind(this));
		} else {
			this.build();
		}
	}.bind(this));
}

require('component-emitter')(WindowPage.prototype);

WindowPage.prototype.build = function(fn) {
	var phase = "build";
	if (!fn) {
		this.stage(phase);
		this.builder = this.allFn(this.builds, phase);
		this.waitHandle(this.handle.bind(this));
	} else {
		this.builds.push(fn);
		if (this.builder) this.builder = this.oneFn(this.builder, fn, phase);
	}
	return this;
};

WindowPage.prototype.handle = function(fn) {
	var phase = "handle";
	if (!fn) {
		this.handler = this.allFn(this.handles, phase);
	} else {
		this.handles.push(fn);
		if (this.handler) this.handler = this.oneFn(this.handler, fn, phase);
	}
	return this;
};

WindowPage.prototype.catcher = function(phase, err) {
	if (this.listeners('error').length) {
		this.emit('error', err, phase);
	} else {
		console.error("Uncaught error during", phase, err);
	}
};

WindowPage.prototype.oneFn = function(p, fn, phase) {
	return p.then(fn).catch(this.catcher.bind(this, phase));
};

WindowPage.prototype.allFn = function(list, phase) {
	var p = Promise.resolve();
	var self = this;
	list.forEach(function(fn) {
		p = self.oneFn(p, fn, phase);
	});
	return p;
};

WindowPage.prototype.stage = function(name) {
	var root = document.documentElement;
	if (name === null) root.removeAttribute("stage");
	else if (name) root.setAttribute("stage", name);
	else name = root.getAttribute("stage");
	return name;
};

WindowPage.prototype.waitHandle = function(cb) {
	var visible = document.documentElement.offsetWidth || document.documentElement.offsetHeight;
	if (!visible) return;
	this.builder.then(function() {
		return Promise.all(Array.from(document.querySelectorAll('link[rel="import"]'))
		.map(function(link) {
			if (link.import) {
				var state = link.import.readyState;
				var isWebkit = window.navigator.userAgent.indexOf('AppleWebKit');
				if (state == "complete" || (state == "loading" && isWebkit && window.WebComponents)) {
					return Promise.resolve();
				}
			}
			return new Promise(function(resolve, reject) {
				function loadListener() {
					link.removeEventListener('load', loadListener);
					resolve();
				}
				function errorListener() {
					link.removeEventListener('error', errorListener);
					resolve();
				}
				link.addEventListener('load', loadListener);
				link.addEventListener('error', errorListener);
			});
		})).then(function() {
			cb();
		});
	});
};

WindowPage.prototype.waitBuild = function(cb) {
	if (document.readyState == "interactive" || document.readyState == "complete") {
		cb();
	} else {
		function listener() {
			document.removeEventListener('DOMContentLoaded', listener);
			cb();
		}
		document.addEventListener('DOMContentLoaded', listener);
	}
};

WindowPage.prototype.reset = function() {
	this.builder = null;
	this.handler = null;
	this.builds = [];
	this.handles = [];
	this.stage(null);
};

WindowPage.prototype.import = function(doc) {
	this.reset();
	var scripts = Array.from(doc.querySelectorAll('script')).map(function(node) {
		if (node.type && node.type != "text/javascript") return Promise.resolve({});
		// make sure script is not loaded when inserted into document
		node.type = "text/plain";
		// fetch script content ourselves
		if (node.src) return GET(node.src).then(function(txt) {
			return {src: node.src, txt: txt, node: node};
		}).catch(function(err) {
			console.error("Error loading", node.src, err);
			return {};
		});
		else return Promise.resolve({
			src: "inline", txt: node.textContent, node: node
		});
	});

	var imports = Array.from(doc.querySelectorAll('link[rel="import"]'));
	imports.forEach(function(link) {
		link.rel = "";
	});

	var root = document.documentElement;
	if (doc.attributes) for (var i=0; i < doc.attributes.length; i++) {
		root.setAttribute(doc.attributes[i].name, doc.attributes[i].value);
	}
	root.replaceChild(document.adoptNode(doc.head), document.head);
	root.replaceChild(document.adoptNode(doc.body), document.body);

	// execute all scripts at once in their original order
	return Promise.all(scripts).then(function(list) {
		list.forEach(function(obj) {
			if (!obj.txt) return;
			var script = document.createElement("script");
			script.textContent = obj.txt;
			document.head.appendChild(script).remove();
			obj.node.type = "text/javascript";
		});
		imports.forEach(function(link) {
			var after = link.nextSibling;
			var parent = link.parentNode;
			link.remove();
			link.rel = "import";
			if (after) parent.insertBefore(link, after);
			else parent.appendChild(link);
		});
	}).then(this.build);
};

/*
 * types of browse events (if they were all caught, which they will at some point):
 * same href, changing hash, changing query, changing path, changing domain
 * in case of same href, it is ignored
 * changing hash or query should trigger a query event
 * changing path should trigger a destroy/creation phase
 * changing domain should allow dialog, target blank
 */
WindowPage.prototype.historyListener = function(e) {
	// would not make sense but happens on some browsers
	var nloc = new URL("", document.location);
	var tloc = this.location;
	if (nloc.href == tloc.href) return;
	this.location = nloc;
	if (nloc.host == tloc.host && nloc.pathname == tloc.pathname && nloc.search != tloc.search) {
		this.emit('query', this.parse(nloc.search));
	}
};

/*
 * useless for now, will be called when changing path in location is handled
 */
WindowPage.prototype.destroy = function() {
	window.removeEventListener('popstate', this.historyListener);
	this.off();
};

window.Page = new WindowPage(window.Page);

<commit_msg>Initialize without arg<commit_after>function WindowPage() {
	var inst = window.Page;
	if (inst) {
		// because using instanceof requires reference to the same WindowPage
		if (inst.name == "WindowPage") return inst;
		else if (inst.state) this.state = inst.state;
	}
	this.name = "WindowPage";

	var QueryString = require('query-string');

	this.location = new URL("", document.location);

	this.parse = function(str) {
		if (!str) str = document.location.search;
		return QueryString.parse(str);
	};
	this.stringify = function(obj) {
		return QueryString.stringify(obj);
	};

	this.builds = [];
	this.handles = [];

	this.historyListener = this.historyListener.bind(this);
	window.addEventListener('popstate', this.historyListener);

	this.import = this.import.bind(this);
	this.build = this.build.bind(this);

	this.waitBuild(function() {
		if (this.stage() == "build") {
			this.builder = Promise.resolve();
			this.waitHandle(this.handle.bind(this));
		} else {
			this.build();
		}
	}.bind(this));
}

require('component-emitter')(WindowPage.prototype);

WindowPage.prototype.build = function(fn) {
	var phase = "build";
	if (!fn) {
		this.stage(phase);
		this.builder = this.allFn(this.builds, phase);
		this.waitHandle(this.handle.bind(this));
	} else {
		this.builds.push(fn);
		if (this.builder) this.builder = this.oneFn(this.builder, fn, phase);
	}
	return this;
};

WindowPage.prototype.handle = function(fn) {
	var phase = "handle";
	if (!fn) {
		this.handler = this.allFn(this.handles, phase);
	} else {
		this.handles.push(fn);
		if (this.handler) this.handler = this.oneFn(this.handler, fn, phase);
	}
	return this;
};

WindowPage.prototype.catcher = function(phase, err) {
	if (this.listeners('error').length) {
		this.emit('error', err, phase);
	} else {
		console.error("Uncaught error during", phase, err);
	}
};

WindowPage.prototype.oneFn = function(p, fn, phase) {
	return p.then(fn).catch(this.catcher.bind(this, phase));
};

WindowPage.prototype.allFn = function(list, phase) {
	var p = Promise.resolve();
	var self = this;
	list.forEach(function(fn) {
		p = self.oneFn(p, fn, phase);
	});
	return p;
};

WindowPage.prototype.stage = function(name) {
	var root = document.documentElement;
	if (name === null) root.removeAttribute("stage");
	else if (name) root.setAttribute("stage", name);
	else name = root.getAttribute("stage");
	return name;
};

WindowPage.prototype.waitHandle = function(cb) {
	var visible = document.documentElement.offsetWidth || document.documentElement.offsetHeight;
	if (!visible) return;
	this.builder.then(function() {
		return Promise.all(Array.from(document.querySelectorAll('link[rel="import"]'))
		.map(function(link) {
			if (link.import) {
				var state = link.import.readyState;
				var isWebkit = window.navigator.userAgent.indexOf('AppleWebKit');
				if (state == "complete" || (state == "loading" && isWebkit && window.WebComponents)) {
					return Promise.resolve();
				}
			}
			return new Promise(function(resolve, reject) {
				function loadListener() {
					link.removeEventListener('load', loadListener);
					resolve();
				}
				function errorListener() {
					link.removeEventListener('error', errorListener);
					resolve();
				}
				link.addEventListener('load', loadListener);
				link.addEventListener('error', errorListener);
			});
		})).then(function() {
			cb();
		});
	});
};

WindowPage.prototype.waitBuild = function(cb) {
	if (document.readyState == "interactive" || document.readyState == "complete") {
		cb();
	} else {
		function listener() {
			document.removeEventListener('DOMContentLoaded', listener);
			cb();
		}
		document.addEventListener('DOMContentLoaded', listener);
	}
};

WindowPage.prototype.reset = function() {
	this.builder = null;
	this.handler = null;
	this.builds = [];
	this.handles = [];
	this.stage(null);
};

WindowPage.prototype.import = function(doc) {
	this.reset();
	var scripts = Array.from(doc.querySelectorAll('script')).map(function(node) {
		if (node.type && node.type != "text/javascript") return Promise.resolve({});
		// make sure script is not loaded when inserted into document
		node.type = "text/plain";
		// fetch script content ourselves
		if (node.src) return GET(node.src).then(function(txt) {
			return {src: node.src, txt: txt, node: node};
		}).catch(function(err) {
			console.error("Error loading", node.src, err);
			return {};
		});
		else return Promise.resolve({
			src: "inline", txt: node.textContent, node: node
		});
	});

	var imports = Array.from(doc.querySelectorAll('link[rel="import"]'));
	imports.forEach(function(link) {
		link.rel = "";
	});

	var root = document.documentElement;
	if (doc.attributes) for (var i=0; i < doc.attributes.length; i++) {
		root.setAttribute(doc.attributes[i].name, doc.attributes[i].value);
	}
	root.replaceChild(document.adoptNode(doc.head), document.head);
	root.replaceChild(document.adoptNode(doc.body), document.body);

	// execute all scripts at once in their original order
	return Promise.all(scripts).then(function(list) {
		list.forEach(function(obj) {
			if (!obj.txt) return;
			var script = document.createElement("script");
			script.textContent = obj.txt;
			document.head.appendChild(script).remove();
			obj.node.type = "text/javascript";
		});
		imports.forEach(function(link) {
			var after = link.nextSibling;
			var parent = link.parentNode;
			link.remove();
			link.rel = "import";
			if (after) parent.insertBefore(link, after);
			else parent.appendChild(link);
		});
	}).then(this.build);
};

/*
 * types of browse events (if they were all caught, which they will at some point):
 * same href, changing hash, changing query, changing path, changing domain
 * in case of same href, it is ignored
 * changing hash or query should trigger a query event
 * changing path should trigger a destroy/creation phase
 * changing domain should allow dialog, target blank
 */
WindowPage.prototype.historyListener = function(e) {
	// would not make sense but happens on some browsers
	var nloc = new URL("", document.location);
	var tloc = this.location;
	if (nloc.href == tloc.href) return;
	this.location = nloc;
	if (nloc.host == tloc.host && nloc.pathname == tloc.pathname && nloc.search != tloc.search) {
		this.emit('query', this.parse(nloc.search));
	}
};

/*
 * useless for now, will be called when changing path in location is handled
 */
WindowPage.prototype.destroy = function() {
	window.removeEventListener('popstate', this.historyListener);
	this.off();
};

window.Page = new WindowPage();

<|endoftext|>


<commit_before>var exec = require('child_process').exec;
var path = require('path');

const DBUS_COMMAND = "bash "+__dirname+"/dbus.sh ";
const DBUS_DEST_DEFAULT = 'org.mpris.MediaPlayer2.omxplayer';

class OmxInstance {

	constructor(options) {
		this.options = options;

		if (options && options.layer) {
			this.dbusDest = DBUS_DEST_DEFAULT + '_layer' + options.layer;
			this.layer = options.layer;
			console.log('setup for layered mode');
		} else {
			this.dbusDest = DBUS_DEST_DEFAULT;
			this.layer = 1;
			console.log('not layered mode');
		}
		console.log('dbus name will be', this.dbusDest);

		exec('mkfifo omxpipe'+this.layer);

		this.defaults = null;
		this.progressHandler = null;

	}

	getLayer() {
		return this.layer;
	}

	cancelProgressHandlerIfActive() {
		if (this.progressHandler) {
			clearInterval(this.progressHandler);
			console.log('progressHandler cancelled');
		}
	}

	dbusCommand (command)  {
		let merge = "bash " +__dirname+"/dbus.sh " + this.dbusDest + " " + command;
		if (command != 'getplaystatus' && command !='getvolume' && command != 'getposition') { console.log('merge:', merge); }
		return merge;
	}

	resume () {
		exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
			// Ignore if already playing
			if (stdout.indexOf("Paused")>-1) {
				this.togglePlay();
			}
		});
	}

	pause () {
		exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
			// Ignore if already paused
			if (stdout.indexOf("Playing")>-1) {
				this.togglePlay();
			}
		});
	}

	stop () {
		exec(this.dbusCommand('stop'), (error, stdout, stderr) => {
			this.cancelProgressHandlerIfActive();
		});
	}

	quit () {
		exec(this.dbusCommand('quit'), (error, stdout, stderr) => {
			this.cancelProgressHandlerIfActive();
	  });
	}

	togglePlay () {
		exec(this.dbusCommand('toggleplay'), (error, stdout, stderr) => {});
	}

	seek (offset) {
		//seek offset in seconds; relative from current position; negative values will cause a jump back;
		exec(this.dbusCommand('seek ' +Math.round(offset*1000000)), (error, stdout, stderr) => {});
	}

	setPosition (position) {
		//position in seconds from start; //positions larger than the duration will stop the player;
		exec(this.dbusCommand('setposition '+Math.round(position*1000000)), (error, stdout, stderr) => {});
	}

	setVolume (volume) {
		// volume range [0.0, 1.0];
		if (volume > 0 && volume < 1.0) {
			exec(this.dbusCommand('setvolume '+volume), (error, stdout, stderr) => {});
		}
	}

	setVisibility (visible) {
		let command = visible ? 'unhidevideo' : 'hidevideo';
		exec(this.dbusCommand(command), (err, stdout, stderr) => {});
	}

	setAlpha (alpha) {
		exec(this.dbusCommand('setalpha ' + alpha), (err, stdout, stderr) => {});
	}

	getCurrentPosition () {
		console.log('getCurrentPosition');
		return new Promise( (resolve, reject) => {
			exec(this.dbusCommand('getposition'), (error, stdout, stderr) => {
				console.log('getposition error, stdout, stderr:', error, stdout, stderr);
				if (error) reject();

				let position = parseInt(stdout);
				console.log('currentPosition:', position, 'or in seconds:', position / 1000);
				resolve(position);
			});
		});
	}

	getIsPlaying () {
		console.log('getIsPlaying');
		return new Promise( (resolve, reject) => {
			exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
				console.log('getplaystatus error, stdout, stderr:', error, stdout, stderr);
				if (error) {
					console.error('error getting play status:', err);
					reject();
				}
				if (stdout === 'Playing') {
					resolve(true);
				} else {
					resolve(false);
				}
			});
		});
	}

	getDuration () {
		return new Promise( (resolve, reject) => {
			if (this.duration) {
				resolve(this.duration);
			} else {
				exec( this.dbusCommand('getduration'), (error, stdout, stderr) => {
					if (error) reject();

					let duration = parseInt(stdout);
					console.log('getDuration:', duration, 'or in seconds:', duration / 1000);
					resolve(duration);
					this.duration = duration; // cache last known duration
				});
			}
		});
	}

	getVolume () {
		exec(this.dbusCommand('getvolume'), (error, stdout, stderr) => {
			if (error) return null;
			let volume = parseFloat(stdout);
			console.log('getVolume:', volume);
		});	}

	onProgress (callback) {
		console.log('add new progress handler for layer', this.layer);
		this.progressHandler = setInterval( () => {
			this.getIsPlaying()
				 .then( (isPlaying) => {
					 if (isPlaying) {
						 this.getCurrentPosition()
						 	.then( (position) => {
								this.getDuration()
									.then( (duration) => {
										callback({ 'position': position, 'duration': duration });
									});
							});
					 } else {
						 callback({ 'playing': false });
					 }
				 })
				 .catch( () => {
					 console.error('error getting isPlaying status');
					 callback({ 'status': 'error', 'playing': false });
				 });
		}, 1000);
	}

	onStart (callback) {
		console.log('onStart event');
		if (callback) {
			callback();
		}
	}

	waitTillPlaying (callback) {
		console.log('waitTillPlaying');
		let countAttempts = 0;
		let interval;
		interval = setInterval( () => {
			countAttempts++;
			exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
				if (error) {
					console.log('error on getplaystus:', error);
				} else {
					console.log('getplaystatus result after', countAttempts, ':', stdout);
					clearInterval(interval);
					callback();
				}
			});
		}, 1000);
	}

	open (path, doneCallback, holdMode) {
		console.log('OmxInstance open() for layer #', this.layer, 'holdMode?', holdMode);
		let settings = this.options || {};
		let args = [];
		let command = 'omxplayer';

		args.push('"'+path+'"');

		if (['hdmi','local','both'].indexOf(settings.audioOutput) != -1) {
			args.push('-o');
			args.push(settings.audioOutput);
		}

		if (settings.blackBackground !== false) { // defaults to true
			args.push('-b');
		}

		if (settings.disableKeys === true) { //defaults to  false
			args.push('--no-keys')
		}

		if (settings.disableOnScreenDisplay === true) { //defaults to  false
			args.push('--no-osd')
		}

		if (settings.disableGhostbox === true) { //defaults to  false
			args.push('--no-ghost-box');
		}

		if (settings.loop === true) { // defaults to false
			args.push('--loop');
		}

		if (settings.subtitlePath && settings.subtitlePath != "" ){
			args.push('--subtitles');
			args.push('"'+settings.subtitlePath+'"');
		}

		if (settings.startAt){
			args.push('--pos');
			args.push(''+settings.startAt+'');
		}

		if (settings.layer) {
			args.push('--layer');
			args.push(settings.layer);
		}

		if (holdMode) {
			args.push('--alpha');
			args.push(0);
		}

		args.push('--dbus_name');
		args.push(this.dbusDest);

		let finalOpenCommand = command+' '+args.join(' ')+' < omxpipe'+this.layer;
		console.log('finalOpenCommand:', finalOpenCommand);

	  exec(finalOpenCommand, (error, stdout, stderr) => {
			doneCallback();
			console.log('omxpipe done for layer', this.layer);
  		this.cancelProgressHandlerIfActive();
	  	console.log(stdout);
	  });
	  exec(' . > omxpipe'+this.layer, (error, stdout, stderr) => {
			this.waitTillPlaying( () => {
				console.log('started ok');
				this.onStart();
				if (holdMode) {
					console.log('holdMode ON, so immediately pause and hide');
					this.pause();
					this.setVisibility(false);
				}
			});
		});

	}

}

module.exports = OmxInstance;
<commit_msg>What is being returned from isPlaying?<commit_after>var exec = require('child_process').exec;
var path = require('path');

const DBUS_COMMAND = "bash "+__dirname+"/dbus.sh ";
const DBUS_DEST_DEFAULT = 'org.mpris.MediaPlayer2.omxplayer';

class OmxInstance {

	constructor(options) {
		this.options = options;

		if (options && options.layer) {
			this.dbusDest = DBUS_DEST_DEFAULT + '_layer' + options.layer;
			this.layer = options.layer;
			console.log('setup for layered mode');
		} else {
			this.dbusDest = DBUS_DEST_DEFAULT;
			this.layer = 1;
			console.log('not layered mode');
		}
		console.log('dbus name will be', this.dbusDest);

		exec('mkfifo omxpipe'+this.layer);

		this.defaults = null;
		this.progressHandler = null;

	}

	getLayer() {
		return this.layer;
	}

	cancelProgressHandlerIfActive() {
		if (this.progressHandler) {
			clearInterval(this.progressHandler);
			console.log('progressHandler cancelled');
		}
	}

	dbusCommand (command)  {
		let merge = "bash " +__dirname+"/dbus.sh " + this.dbusDest + " " + command;
		if (command != 'getplaystatus' && command !='getvolume' && command != 'getposition') { console.log('merge:', merge); }
		return merge;
	}

	resume () {
		exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
			// Ignore if already playing
			if (stdout.indexOf("Paused")>-1) {
				this.togglePlay();
			}
		});
	}

	pause () {
		exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
			// Ignore if already paused
			if (stdout.indexOf("Playing")>-1) {
				this.togglePlay();
			}
		});
	}

	stop () {
		exec(this.dbusCommand('stop'), (error, stdout, stderr) => {
			this.cancelProgressHandlerIfActive();
		});
	}

	quit () {
		exec(this.dbusCommand('quit'), (error, stdout, stderr) => {
			this.cancelProgressHandlerIfActive();
	  });
	}

	togglePlay () {
		exec(this.dbusCommand('toggleplay'), (error, stdout, stderr) => {});
	}

	seek (offset) {
		//seek offset in seconds; relative from current position; negative values will cause a jump back;
		exec(this.dbusCommand('seek ' +Math.round(offset*1000000)), (error, stdout, stderr) => {});
	}

	setPosition (position) {
		//position in seconds from start; //positions larger than the duration will stop the player;
		exec(this.dbusCommand('setposition '+Math.round(position*1000000)), (error, stdout, stderr) => {});
	}

	setVolume (volume) {
		// volume range [0.0, 1.0];
		if (volume > 0 && volume < 1.0) {
			exec(this.dbusCommand('setvolume '+volume), (error, stdout, stderr) => {});
		}
	}

	setVisibility (visible) {
		let command = visible ? 'unhidevideo' : 'hidevideo';
		exec(this.dbusCommand(command), (err, stdout, stderr) => {});
	}

	setAlpha (alpha) {
		exec(this.dbusCommand('setalpha ' + alpha), (err, stdout, stderr) => {});
	}

	getCurrentPosition () {
		console.log('getCurrentPosition');
		return new Promise( (resolve, reject) => {
			exec(this.dbusCommand('getposition'), (error, stdout, stderr) => {
				console.log('getposition error, stdout, stderr:', error, stdout, stderr);
				if (error) reject();

				let position = parseInt(stdout);
				console.log('currentPosition:', position, 'or in seconds:', position / 1000);
				resolve(position);
			});
		});
	}

	getIsPlaying () {
		console.log('getIsPlaying');
		return new Promise( (resolve, reject) => {
			exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
				console.log('getplaystatus error, stdout, stderr:', error, stdout, stderr);
				if (error) {
					console.error('error getting play status:', err);
					reject();
				}
				if (stdout === 'Playing') {
					resolve(true);
				} else {
					resolve(false);
				}
			});
		});
	}

	getDuration () {
		return new Promise( (resolve, reject) => {
			if (this.duration) {
				resolve(this.duration);
			} else {
				exec( this.dbusCommand('getduration'), (error, stdout, stderr) => {
					if (error) reject();

					let duration = parseInt(stdout);
					console.log('getDuration:', duration, 'or in seconds:', duration / 1000);
					resolve(duration);
					this.duration = duration; // cache last known duration
				});
			}
		});
	}

	getVolume () {
		exec(this.dbusCommand('getvolume'), (error, stdout, stderr) => {
			if (error) return null;
			let volume = parseFloat(stdout);
			console.log('getVolume:', volume);
		});	}

	onProgress (callback) {
		console.log('add new progress handler for layer', this.layer);
		this.progressHandler = setInterval( () => {
			this.getIsPlaying()
				 .then( (isPlaying) => {
					 console.log('isPlaying?', isPlaying);
					 if (isPlaying) {
						 this.getCurrentPosition()
						 	.then( (position) => {
								this.getDuration()
									.then( (duration) => {
										callback({ 'position': position, 'duration': duration });
									});
							});
					 } else {
						 callback({ 'playing': false });
					 }
				 })
				 .catch( () => {
					 console.error('error getting isPlaying status');
					 callback({ 'status': 'error', 'playing': false });
				 });
		}, 1000);
	}

	onStart (callback) {
		console.log('onStart event');
		if (callback) {
			callback();
		}
	}

	waitTillPlaying (callback) {
		console.log('waitTillPlaying');
		let countAttempts = 0;
		let interval;
		interval = setInterval( () => {
			countAttempts++;
			exec(this.dbusCommand('getplaystatus'), (error, stdout, stderr) => {
				if (error) {
					console.log('error on getplaystus:', error);
				} else {
					console.log('getplaystatus result after', countAttempts, ':', stdout);
					clearInterval(interval);
					callback();
				}
			});
		}, 1000);
	}

	open (path, doneCallback, holdMode) {
		console.log('OmxInstance open() for layer #', this.layer, 'holdMode?', holdMode);
		let settings = this.options || {};
		let args = [];
		let command = 'omxplayer';

		args.push('"'+path+'"');

		if (['hdmi','local','both'].indexOf(settings.audioOutput) != -1) {
			args.push('-o');
			args.push(settings.audioOutput);
		}

		if (settings.blackBackground !== false) { // defaults to true
			args.push('-b');
		}

		if (settings.disableKeys === true) { //defaults to  false
			args.push('--no-keys')
		}

		if (settings.disableOnScreenDisplay === true) { //defaults to  false
			args.push('--no-osd')
		}

		if (settings.disableGhostbox === true) { //defaults to  false
			args.push('--no-ghost-box');
		}

		if (settings.loop === true) { // defaults to false
			args.push('--loop');
		}

		if (settings.subtitlePath && settings.subtitlePath != "" ){
			args.push('--subtitles');
			args.push('"'+settings.subtitlePath+'"');
		}

		if (settings.startAt){
			args.push('--pos');
			args.push(''+settings.startAt+'');
		}

		if (settings.layer) {
			args.push('--layer');
			args.push(settings.layer);
		}

		if (holdMode) {
			args.push('--alpha');
			args.push(0);
		}

		args.push('--dbus_name');
		args.push(this.dbusDest);

		let finalOpenCommand = command+' '+args.join(' ')+' < omxpipe'+this.layer;
		console.log('finalOpenCommand:', finalOpenCommand);

	  exec(finalOpenCommand, (error, stdout, stderr) => {
			doneCallback();
			console.log('omxpipe done for layer', this.layer);
  		this.cancelProgressHandlerIfActive();
	  	console.log(stdout);
	  });
	  exec(' . > omxpipe'+this.layer, (error, stdout, stderr) => {
			this.waitTillPlaying( () => {
				console.log('started ok');
				this.onStart();
				if (holdMode) {
					console.log('holdMode ON, so immediately pause and hide');
					this.pause();
					this.setVisibility(false);
				}
			});
		});

	}

}

module.exports = OmxInstance;
<|endoftext|>


<commit_before>'use strict';

var Medusa = (function() {

  var cache = {};
  var hOP = cache.hasOwnProperty;
  var settings = {
    debug: false,
    returnMutator: false,
  };

  var md = {

    // Alter settings for Medusa
    settings: function(newSettings) {
      if (!newSettings) {
        return settings;
      }

      Object.assign(settings, newSettings);

      return settings;
    },

    // Allows you to get from the cache or pull from the promise
    get: function(key, prom, policy) {

      return new Promise(function(resolve, reject) {

        if (hOP.call(cache, key)) {
          // The cached item exists, return it immediatly
          if (settings.returnMutator) {
            resolve(settings.returnMutator(cache[key].val));
          } else {
            resolve(cache[key].val);
          }

        } else {
          // The cached item does not exist, resolve and return
          var resolveExt = (v) => {
            md.put(key, v, policy);
            if (settings.returnMutator) {
              v = settings.returnMutator(v);
            }
            resolve(v);
          };
          prom(resolveExt, reject);

        }

      });

    },

    // Allows you to get from the cache or pull from the promise
    overwrite: function(key, prom, policy) {

      return new Promise(function(resolve, reject) {

        // Re-put the object no matter what
        var resolveExt = function(v) {
          md.put(key, v, policy);
          if (settings.returnMutator) {
            v = settings.returnMutator(v);
          }
          resolve(v);
        };
        prom(resolveExt, reject)

      });

    },

    // Place an item into the cache
    put: function(key, value, policy) {

      // Clear in case it exists
      md.clear(key);

      // Set a timemout to self-remove from the cache if in policy
      var to = false;
      if (policy && parseInt(policy) > 0) {
        to = setTimeout(function() {
          md.clear(key);
        }, parseInt(policy));
      }

      // Store the cached item
      cache[key] = {
        policy: policy,
        val: value,
        to: to,
      };

    },

    // Clear one or all items in the cache
    clear: function(key) {

      // Clear all items in the cache
      if (!key) {
        for (var i in cache) {
          if (hOP.call(cache, i)) {
            md._clear(i);
          }
        }
        return true;
      }

      // Clear a wildcard search of objects
      if (key.indexOf('*') > -1) {
        var cacheMatchKeys = Object.keys(cache).filter(function(str) {
          return new RegExp('^' + key.split('*').join('.*') + '$').test(str);
        });
        cacheMatchKeys.forEach(md._clear);
        // Incase someone somehow used a wildcard in their cached key (don't do this)
        return cacheMatchKeys.length > 0 || md._clear(key);
      }

      // Not a special clear
      return md._clear(key);

    },

    // Internal clear to bypass extra checking of the key
    _clear: function(key) {
      // Clear a single item, making sure to remove the extra timeout
      if (hOP.call(cache, key)) {
        if (cache[key].to) {
          clearTimeout(cache[key].to);
        }

        delete cache[key];
        return true;
      }

      return false;
    },

  };

  return md;

})();

// CommonJS binding
if (typeof module === 'object' && module.exports) {
  module.exports = Medusa;
}
<commit_msg>fixed up an arrow function, need to be es5 compat<commit_after>'use strict';

var Medusa = (function() {

  var cache = {};
  var hOP = cache.hasOwnProperty;
  var settings = {
    debug: false,
    returnMutator: false,
  };

  var md = {

    // Alter settings for Medusa
    settings: function(newSettings) {
      if (!newSettings) {
        return settings;
      }

      Object.assign(settings, newSettings);

      return settings;
    },

    // Allows you to get from the cache or pull from the promise
    get: function(key, prom, policy) {

      return new Promise(function(resolve, reject) {

        if (hOP.call(cache, key)) {
          // The cached item exists, return it immediatly
          if (settings.returnMutator) {
            resolve(settings.returnMutator(cache[key].val));
          } else {
            resolve(cache[key].val);
          }

        } else {
          // The cached item does not exist, resolve and return
          var resolveExt = function(v) {
            md.put(key, v, policy);
            if (settings.returnMutator) {
              v = settings.returnMutator(v);
            }
            resolve(v);
          };
          prom(resolveExt, reject);

        }

      });

    },

    // Allows you to get from the cache or pull from the promise
    overwrite: function(key, prom, policy) {

      return new Promise(function(resolve, reject) {

        // Re-put the object no matter what
        var resolveExt = function(v) {
          md.put(key, v, policy);
          if (settings.returnMutator) {
            v = settings.returnMutator(v);
          }
          resolve(v);
        };
        prom(resolveExt, reject)

      });

    },

    // Place an item into the cache
    put: function(key, value, policy) {

      // Clear in case it exists
      md.clear(key);

      // Set a timemout to self-remove from the cache if in policy
      var to = false;
      if (policy && parseInt(policy) > 0) {
        to = setTimeout(function() {
          md.clear(key);
        }, parseInt(policy));
      }

      // Store the cached item
      cache[key] = {
        policy: policy,
        val: value,
        to: to,
      };

    },

    // Clear one or all items in the cache
    clear: function(key) {

      // Clear all items in the cache
      if (!key) {
        for (var i in cache) {
          if (hOP.call(cache, i)) {
            md._clear(i);
          }
        }
        return true;
      }

      // Clear a wildcard search of objects
      if (key.indexOf('*') > -1) {
        var cacheMatchKeys = Object.keys(cache).filter(function(str) {
          return new RegExp('^' + key.split('*').join('.*') + '$').test(str);
        });
        cacheMatchKeys.forEach(md._clear);
        // Incase someone somehow used a wildcard in their cached key (don't do this)
        return cacheMatchKeys.length > 0 || md._clear(key);
      }

      // Not a special clear
      return md._clear(key);

    },

    // Internal clear to bypass extra checking of the key
    _clear: function(key) {
      // Clear a single item, making sure to remove the extra timeout
      if (hOP.call(cache, key)) {
        if (cache[key].to) {
          clearTimeout(cache[key].to);
        }

        delete cache[key];
        return true;
      }

      return false;
    },

  };

  return md;

})();

// CommonJS binding
if (typeof module === 'object' && module.exports) {
  module.exports = Medusa;
}
<|endoftext|>


<commit_before>// http://stackoverflow.com/questions/11935175/sampling-a-random-subset-from-an-array
var featureCollection = require('turf-featurecollection');

/**
 * Selects a given number of {@link Feature|features} from a {@link FeatureCollection}
 * at random.
 *
 * @module turf/sample
 * @param {FeatureCollection} features
 * @param {number} n number of features to select
 * @return {FeatureCollection} output
 * @example
 * var pts = JSON.parse(fs.readFileSync('/path/to/pts.geojson'))
 * var num = 10
 * var sampled = turf.sample(pts, num)
 * console.log(sampled)
 */
module.exports = function(fc, num){
  var outFC = featureCollection(getRandomSubarray(fc.features, num));
  return outFC;
}

function getRandomSubarray(arr, size) {
  var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
  while (i-- > min) {
      index = Math.floor((i + 1) * Math.random());
      temp = shuffled[index];
      shuffled[index] = shuffled[i];
      shuffled[i] = temp;
  }
  return shuffled.slice(min);
}
<commit_msg>Update example. Fixes #2<commit_after>// http://stackoverflow.com/questions/11935175/sampling-a-random-subset-from-an-array
var featureCollection = require('turf-featurecollection');

/**
 * Selects a given number of {@link Feature|features} from a {@link FeatureCollection}
 * at random.
 *
 * @module turf/sample
 * @param {FeatureCollection} features
 * @param {number} n number of features to select
 * @return {FeatureCollection} output
 * @example
 * // create a lot of points
 * var points = turf.random('points', 1000);
 * //=points
 *
 * // sample just a few of them
 * var sample = turf.sample(points, 10);
 * //=sample
 */
module.exports = function(fc, num){
  var outFC = featureCollection(getRandomSubarray(fc.features, num));
  return outFC;
};

function getRandomSubarray(arr, size) {
  var shuffled = arr.slice(0), i = arr.length, min = i - size, temp, index;
  while (i-- > min) {
      index = Math.floor((i + 1) * Math.random());
      temp = shuffled[index];
      shuffled[index] = shuffled[i];
      shuffled[i] = temp;
  }
  return shuffled.slice(min);
}
<|endoftext|>


<commit_before>'use strict';
var dns = require('native-dns');
var net = require('net');
var eachAsync = require('each-async');
var onetime = require('onetime');
var roots = require('root-hints')('A');

var timeout = 1000;
var domains = [
	'www.google.com',
	'www.cloudflare.com',
	'www.baidu.com',
	'www.yandex.ru'
];

module.exports = function (cb) {
	cb = onetime(cb);

	// Pick a random root server to query
	var server = roots[Math.floor(Math.random() * roots.length)];

	// Set up a DNS request that requests the authoritative information for
	// the 'com' zone
	var req = dns.Request({
		question: dns.Question({
			name: 'com',
			type: 'NS'
		}),
		server: {
			address: server
		},
		timeout: timeout
	});

	req.on('timeout', function () {
		// We ran into the timeout, we're offline with high confidence
		cb(null, false);
	});

	req.on('message', function (err, answer) {
		if (answer.authority.length && answer._socket.address === server) {
			// We got an answer and the source matches the queried server,
			// we're online with high confidence
			cb(null, true);
		} else {
			// Either DNS intercepting is in place or the response in mangled,
			// try connecting to our domains on port 80, and if one handshake
			// succeeds, we're definitely online
			eachAsync(domains, function (domain, i, done) {
				var socket = new net.Socket();
				done = onetime(done);

				socket.setTimeout(timeout);
				socket.on('error', function () {
					socket.destroy();
					done();
				});
				socket.connect(80, domain, function () {
					cb(null, true);
					socket.end();
					done(new Error()); // skip to end
				});
			}, function () {
				cb(null, false);
			});
		}
	});

	req.send();
};
<commit_msg>Add timeout handling for the sockets<commit_after>'use strict';
var dns = require('native-dns');
var net = require('net');
var eachAsync = require('each-async');
var onetime = require('onetime');
var roots = require('root-hints')('A');

var timeout = 1000;
var domains = [
	'www.google.com',
	'www.cloudflare.com',
	'www.baidu.com',
	'www.yandex.ru'
];

module.exports = function (cb) {
	cb = onetime(cb);

	// Pick a random root server to query
	var server = roots[Math.floor(Math.random() * roots.length)];

	// Set up a DNS request that requests the authoritative information for
	// the 'com' zone
	var req = dns.Request({
		question: dns.Question({
			name: 'com',
			type: 'NS'
		}),
		server: {
			address: server
		},
		timeout: timeout
	});

	req.on('timeout', function () {
		// We ran into the timeout, we're offline with high confidence
		cb(null, false);
	});

	req.on('message', function (err, answer) {
		if (answer.authority.length && answer._socket.address === server) {
			// We got an answer and the source matches the queried server,
			// we're online with high confidence
			cb(null, true);
		} else {
			// Either DNS intercepting is in place or the response in mangled,
			// try connecting to our domains on port 80, and if one handshake
			// succeeds, we're definitely online
			eachAsync(domains, function (domain, i, done) {
				var socket = new net.Socket();
				done = onetime(done);

				socket.setTimeout(timeout);

				socket.on('timeout', function () {
					socket.destroy();
					done();
				});

				socket.on('error', function () {
					socket.destroy();
					done();
				});

				socket.connect(80, domain, function () {
					cb(null, true);
					socket.end();
					done(new Error()); // skip to end
				});
			}, function () {
				cb(null, false);
			});
		}
	});

	req.send();
};
<|endoftext|>


<commit_before>/** @jsx React.DOM */

/**
 * Module dependencies
 */

var React = require('react');
var noop = require('react/lib/emptyFunction');
var SCStream = require('../sc-stream');
var Display = require('./lib/Display');
var sc;

/**
 * SoundCloud component
 */

var SoundCloud = React.createClass({
  getInitialState: function() {
    return {
      track: undefined,
      stream: undefined
    };
  },

  componentDidMount: function() {
    sc = new SCStream(this.props.clientId);
    this._loadTrack(this.props.url);
  },

  componentWillReceiveProps: function(newProps) {
    var newUrl = newProps.url;
    if (this._isNewUrl(newUrl)) {
      this._loadTrack(newUrl);
    }
  },

  /**
   * Load a new player
   *
   * @param {string} url
   */

  _loadTrack: function(url) {
    var _this = this;
    this._prepareForNewTrack();

    sc.stream(url).then(function(response) {
      _this.setState({track: response.track});
      response.stream.then(function(stream) {
        _this._loadNewStream(stream);
      });
    }).catch(function(e) { console.log(e); });
  },

  /**
   * Clean up before loading a new track
   */

  _prepareForNewTrack: function() {
    if (this.state.stream) {
      this.state.stream.destruct();
      this.setState({stream: undefined, track: undefined});
    }
  },

  /**
   * Load a new stream
   *
   * @param {object} stream
   */

  _loadNewStream: function(stream) {
    this._bindEvents(stream);
    this.setState({stream: stream});
  },

  /**
   * Bind events to new stream. Has to be done
   * through the play method, as SoundManager doesn't
   * stream bindings outside of certain contexts.
   *
   * @param {object} newStream
   */

  _bindEvents: function(stream) {
    stream.play({
      onplay: this.props.onPlay || noop,
      onpause: this.props.onPause || noop,
      onfinish: this.props.onEnd || noop,
      whileplaying: this._updateProgress
    });
    stream.pause();
  },

  /**
   * Determine if url is already loaded
   *
   * @param {string} url
   */

  _isNewUrl: function(url) {
    if (!this.state.track) return false;
    var currentUrl = this.state.track.permalink_url.split('://')[1];
    var newUrl = url.split('://')[1];
    return currentUrl !== newUrl;
  },

  /**
   * Toggle pause/play on current stream
   * Must force an update, isPlaying is only calculated on render.
   */

  _togglePlayback: function() {
    this.state.stream.togglePause();
    this.forceUpdate();
  },

  /**
   * Force duration & position props passed to display to update.
   */

  _updateProgress: function() {
    this.forceUpdate();
  },

  /**
   * Skip to a certain part of the track
   *
   * @param {number} newPosition
   */

  _setPosition: function(newPosition) {
    this.state.stream.setPosition(newPosition);
  },

  render: function() {
    var isPlaying = this.state.stream && this.state.stream.paused !== undefined && !this.state.stream.paused;
    var streamLoaded = this.state.stream !== undefined;

    return <Display track={this.state.track}
                    duration={this.state.stream.durationEstimate}
                    position={this.state.stream.position}
                    togglePlayback={this._togglePlayback}
                    isPlaying={isPlaying}
                    streamLoaded={streamLoaded}
                    setPosition={this._setPosition}/>
  }
});

/**
 * Expose SoundCloud component
 */

module.exports = SoundCloud;
<commit_msg>wait til track is played to load audio<commit_after>/** @jsx React.DOM */

/**
 * Module dependencies
 */

var React = require('react');
var noop = require('react/lib/emptyFunction');
var SCStream = require('../sc-stream');
var Display = require('./lib/Display');
var sc;

/**
 * SoundCloud component
 */

var SoundCloud = React.createClass({
  getInitialState: function() {
    return {
      track: undefined,
      stream: undefined,
      eventsBound: false
    };
  },

  componentDidMount: function() {
    sc = new SCStream(this.props.clientId);
    this._loadTrack(this.props.url);
  },

  componentWillReceiveProps: function(newProps) {
    var newUrl = newProps.url;
    if (this._isNewUrl(newUrl)) {
      this._loadTrack(newUrl);
    }
  },

  /**
   * Load a new player
   *
   * @param {string} url
   */

  _loadTrack: function(url) {
    var _this = this;
    this._prepareForNewTrack();

    sc.stream(url).then(function(response) {
      _this.setState({track: response.track});
      response.stream.then(function(stream) {
        _this.setState({stream: stream});
      });
    }).catch(function(e) { console.log(e); });
  },

  /**
   * Clean up before loading a new track
   */

  _prepareForNewTrack: function() {
    var stream = this.state.stream;
    if (stream) {
      stream.destruct();
      this.setState({stream: undefined, track: undefined, eventsBound: false});
    }
  },

  /**
   * Bind events to new stream. Has to be done
   * through the play method, as SoundManager doesn't
   * stream bindings outside of certain contexts.
   */

  _bindEvents: function() {
    var stream = this.state.stream;
    stream.play({
      onplay: this.props.onPlay || noop,
      onpause: this.props.onPause || noop,
      onfinish: this.props.onEnd || noop,
      whileplaying: this._updateProgress
    });
    stream.pause();
    this.setState({eventsBound: true});
  },

  /**
   * Determine if url is already loaded
   *
   * @param {string} url
   */

  _isNewUrl: function(url) {
    if (!this.state.track) return false;
    var currentUrl = this.state.track.permalink_url.split('://')[1];
    var newUrl = url.split('://')[1];
    return currentUrl !== newUrl;
  },

  /**
   * Toggle pause/play on current stream
   * Must force an update, isPlaying is only calculated on render.
   */

  _togglePlayback: function() {
    if (!this.state.eventsBound) this._bindEvents();
    this.state.stream.togglePause();
    this.forceUpdate();
  },

  /**
   * Force duration & position props passed to display to update.
   */

  _updateProgress: function() {
    this.forceUpdate();
  },

  /**
   * Skip to a certain part of the track
   *
   * @param {number} newPosition
   */

  _setPosition: function(newPosition) {
    this.state.stream.setPosition(newPosition);
  },

  render: function() {
    var stream = this.state.stream;
    var streamLoaded = stream !== undefined;
    var isPlaying;
    var duration;
    var position;

    if (streamLoaded) {
      duration = stream.durationEstimate ? stream.durationEstimate : 100;
      position = stream.position ? stream.position : 0;
      isPlaying = position && !stream.paused;
    } else {
      isPlaying = false;
      duration = 100;
      position = 0;
    }

    return <Display track={this.state.track}
                    duration={duration}
                    position={position}
                    togglePlayback={this._togglePlayback}
                    isPlaying={isPlaying}
                    streamLoaded={streamLoaded}
                    setPosition={this._setPosition}/>
  }
});

/**
 * Expose SoundCloud component
 */

module.exports = SoundCloud;
<|endoftext|>


<commit_before>import Quill, { overload } from '../../../core/quill';
import { Range } from '../../../core/selection';
import Emitter from '../../../core/emitter';
import Delta from 'rich-text/lib/delta';

describe('Quill', function() {
  beforeEach(function() {
    this.quill = this.initialize(Quill, '
01234567
');
  });

  describe('deleteText', function() {
    it('(index, length)', function(done) {
      this.quill.emitter.on(Emitter.events.TEXT_CHANGE, function(change, oldDelta, source) {
        expect(change).toEqual(new Delta().retain(1).delete(2));
        expect(oldDelta).toEqual(new Delta().insert('01234567\n'));
        expect(source).toEqual(Emitter.sources.API);
        done();
      });
      this.quill.deleteText(1, 2);
      expect(this.container.firstChild).toEqualHTML('
034567
');
    });
  });

  describe('overload', function() {
    it('(index:number, length:number)', function() {
      let [index, length, formats, source] = overload(0, 1);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(index:number, length:number, format:string, value:boolean, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, 'bold', true, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, length:number, format:string, value:string, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, 'color', Quill.sources.USER, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.USER });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, length:number, format:string, value:string)', function() {
      let [index, length, formats, source] = overload(0, 1, 'color', Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.USER });
      expect(source).toBe(Quill.sources.API);
    });

    it('(index:number, length:number, format:object)', function() {
      let [index, length, formats, source] = overload(0, 1, { bold: true });
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.API);
    });

    it('(index:number, length:number, format:object, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, { bold: true }, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, length:number, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, source:string)', function() {
      let [index, length, formats, source] = overload(0, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(0);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1));
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, format:string, value:boolean, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), 'bold', true, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range, format:string, value:string, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), 'color', Quill.sources.USER, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.USER });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range, format:string, value:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), 'color', Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.USER });
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, format:object)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), { bold: true });
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, format:object, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), { bold: true }, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1));
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, dummy:number)', function() {
      let [index, length, formats, source] = overload(new Range(10, 1), 0);
      expect(index).toBe(10);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, dummy:number, format:string, value:boolean)', function() {
      let [index, length, formats, source] = overload(new Range(10, 1), 0, 'bold', true);
      expect(index).toBe(10);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, dummy:number, format:object, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(10, 1), 0, { bold: true }, Quill.sources.USER);
      expect(index).toBe(10);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });
  });

  xdescribe('selection preservation', function() {
    beforeEach(function() {
      this.quill = this.initialize(Quill, '
01234567
');
    });

    it('format surround', function() {
      this.quill.setSelection(1, 3);
      this.quill.formatText(0, 4, 'italic', true);
      expect(this.quill.getSelection()).toEqual(new Range(1, 3));
    });

    it('user text change', function() {
      this.quill.setSelection(1, 3);
      let text = document.createTextNode('!');
      this.quill.scroll.domNode.firstChild.firstChild.appendChild(text);
      expect(this.quill.getSelection()).toEqual(new Range(1, 3));
    });

    it('user dom text change', function() {
      this.quill.setSelection(2, 3);
      let textNode = this.quill.scroll.domNode.firstChild.firstChild.firstChild;
      textNode.splitText(1);
      textNode.parentNode.removeChild(textNode);
      expect(this.quill.getSelection()).toEqual(new Range(1, 2));
    });
  });

  describe('setContents()', function() {
    it('should accept a delta', function() {
      let delta = new Delta().insert('test\n');
      this.quill.setContents(delta);
      expect(this.quill.getContents()).toEqual(delta);
    });

    it('should accept an array of operations', function() {
      let delta = new Delta().insert('test').insert('123', {bold: true}).insert('\n');
      this.quill.setContents(delta.ops);
      expect(this.quill.getContents()).toEqual(delta);
    });

    it('should accept a json representation of a delta', function() {
      let delta = { ops: [{ insert: 'test\n'}] };
      this.quill.setContents(delta);
      expect(this.quill.getContents()).toEqual(new Delta(delta));
    });
  });

  describe('setText()', function() {
    it('overwrite', function() {
      this.quill.setText('abc');
      expect(this.quill.root.innerHTML).toEqualHTML('
abc
');
    });

    it('set to newline', function() {
      this.quill.setText('\n');
      expect(this.quill.root.innerHTML).toEqualHTML('

');
    });

    it('multiple newlines', function() {
      this.quill.setText('\n\n');
      expect(this.quill.root.innerHTML).toEqualHTML('

');
    });

    it('content with trailing newline', function() {
      this.quill.setText('abc\n');
      expect(this.quill.root.innerHTML).toEqualHTML('
abc
');
    });
  });

  describe('insertEmbed', function() {
    it('image', function(done) {
      this.quill.emitter.once(Emitter.events.TEXT_CHANGE, function(change, oldDelta, source) {
        expect(change).toEqual(new Delta().insert({ image: '/assets/favicon.png'}));
        expect(oldDelta).toEqual(new Delta().insert('01234567\n'));
        expect(source).toEqual(Emitter.sources.API);
        done();
      });
      this.quill.insertEmbed(0, 'image', '/assets/favicon.png');
      let expectedDelta = new Delta().insert({ image: '/assets/favicon.png'}).insert('01234567\n');
      expect(this.quill.getContents()).toEqual(expectedDelta);
      expect(this.container.firstChild).toEqualHTML('
01234567
');
    });
  });
});
<commit_msg>tweak test<commit_after>import Quill, { overload } from '../../../core/quill';
import { Range } from '../../../core/selection';
import Emitter from '../../../core/emitter';
import Delta from 'rich-text/lib/delta';

describe('Quill', function() {
  beforeEach(function() {
    this.quill = this.initialize(Quill, '
01234567
');
  });

  describe('deleteText', function() {
    it('(index, length)', function(done) {
      this.quill.emitter.on(Emitter.events.TEXT_CHANGE, function(change, oldDelta, source) {
        expect(change).toEqual(new Delta().retain(1).delete(2));
        expect(oldDelta).toEqual(new Delta().insert('01234567\n'));
        expect(source).toEqual(Emitter.sources.API);
        done();
      });
      this.quill.deleteText(1, 2);
      expect(this.container.firstChild).toEqualHTML('
034567
');
    });
  });

  describe('overload', function() {
    it('(index:number, length:number)', function() {
      let [index, length, formats, source] = overload(0, 1);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(index:number, length:number, format:string, value:boolean, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, 'bold', true, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, length:number, format:string, value:string, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, 'color', Quill.sources.USER, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.USER });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, length:number, format:string, value:string)', function() {
      let [index, length, formats, source] = overload(0, 1, 'color', Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.USER });
      expect(source).toBe(Quill.sources.API);
    });

    it('(index:number, length:number, format:object)', function() {
      let [index, length, formats, source] = overload(0, 1, { bold: true });
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.API);
    });

    it('(index:number, length:number, format:object, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, { bold: true }, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, length:number, source:string)', function() {
      let [index, length, formats, source] = overload(0, 1, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.USER);
    });

    it('(index:number, source:string)', function() {
      let [index, length, formats, source] = overload(0, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(0);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1));
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, format:string, value:boolean, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), 'bold', true, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range, format:string, value:string, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), 'color', Quill.sources.API, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.API });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range, format:string, value:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), 'color', Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ color: Quill.sources.USER });
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, format:object)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), { bold: true });
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, format:object, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), { bold: true }, Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1), Quill.sources.USER);
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.USER);
    });

    it('(range:range)', function() {
      let [index, length, formats, source] = overload(new Range(0, 1));
      expect(index).toBe(0);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, dummy:number)', function() {
      let [index, length, formats, source] = overload(new Range(10, 1), 0);
      expect(index).toBe(10);
      expect(length).toBe(1);
      expect(formats).toEqual({});
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, dummy:number, format:string, value:boolean)', function() {
      let [index, length, formats, source] = overload(new Range(10, 1), 0, 'bold', true);
      expect(index).toBe(10);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.API);
    });

    it('(range:range, dummy:number, format:object, source:string)', function() {
      let [index, length, formats, source] = overload(new Range(10, 1), 0, { bold: true }, Quill.sources.USER);
      expect(index).toBe(10);
      expect(length).toBe(1);
      expect(formats).toEqual({ bold: true });
      expect(source).toBe(Quill.sources.USER);
    });
  });

  xdescribe('selection preservation', function() {
    beforeEach(function() {
      this.quill = this.initialize(Quill, '
01234567
');
    });

    it('format surround', function() {
      this.quill.setSelection(1, 3);
      this.quill.formatText(0, 4, 'italic', true);
      expect(this.quill.getSelection()).toEqual(new Range(1, 3));
    });

    it('user text change', function() {
      this.quill.setSelection(1, 3);
      let text = document.createTextNode('!');
      this.quill.scroll.domNode.firstChild.firstChild.appendChild(text);
      expect(this.quill.getSelection()).toEqual(new Range(1, 3));
    });

    it('user dom text change', function() {
      this.quill.setSelection(2, 3);
      let textNode = this.quill.scroll.domNode.firstChild.firstChild.firstChild;
      textNode.splitText(1);
      textNode.parentNode.removeChild(textNode);
      expect(this.quill.getSelection()).toEqual(new Range(1, 2));
    });
  });

  describe('setContents()', function() {
    it('should accept a delta', function() {
      let delta = new Delta().insert('test\n');
      this.quill.setContents(delta);
      expect(this.quill.getContents()).toEqual(delta);
    });

    it('should accept an array of operations', function() {
      let delta = new Delta().insert('test').insert('123', {bold: true}).insert('\n');
      this.quill.setContents(delta.ops);
      expect(this.quill.getContents()).toEqual(delta);
    });

    it('should accept a json representation of a delta', function() {
      let delta = { ops: [{ insert: 'test\n'}] };
      this.quill.setContents(delta);
      expect(this.quill.getContents()).toEqual(new Delta(delta));
    });
  });

  describe('setText()', function() {
    it('overwrite', function() {
      this.quill.setText('abc');
      expect(this.quill.root.innerHTML).toEqualHTML('
abc
');
    });

    it('set to newline', function() {
      this.quill.setText('\n');
      expect(this.quill.root.innerHTML).toEqualHTML('

');
    });

    it('multiple newlines', function() {
      this.quill.setText('\n\n');
      expect(this.quill.root.innerHTML).toEqualHTML('

');
    });

    it('content with trailing newline', function() {
      this.quill.setText('abc\n');
      expect(this.quill.root.innerHTML).toEqualHTML('
abc
');
    });
  });

  describe('insertEmbed', function() {
    it('image', function(done) {
      this.quill.emitter.once(Emitter.events.TEXT_CHANGE, function(change, oldDelta, source) {
        expect(change).toEqual(new Delta().insert({ image: '/assets/favicon.png'}));
        expect(oldDelta).toEqual(new Delta().insert('01234567\n'));
        expect(source).toEqual(Emitter.sources.API);
        done();
      });
      this.quill.insertEmbed(0, 'image', '/assets/favicon.png');
      let expectedDelta = new Delta().insert({ image: '/assets/favicon.png'}).insert('01234567\n');
      expect(this.quill.getContents()).toEqual(expectedDelta);
      expect(this.container.firstChild).toEqualHTML('
01234567
');
    });
  });
});
<|endoftext|>


<commit_before>var basename  = require('path').basename;
var dirname   = require('path').dirname;
var extname   = require('path').extname;
var debug     = require('debug')('metalsmith-pandoc');
var pdcPath   = require('pandoc-bin').path;
var pdc       = require('pdc');
var minimatch = require('minimatch');
var async     = require('async');

// use pandoc-bin
pdc.path = pdcPath;

/**
 * Expose `plugin`.
 */

module.exports = plugin;

/**
 * Metalsmith plugin to convert files using pandoc.
 *
 */

function plugin(options){
  options = options || {};
  var from = options.from || 'markdown';
  var to   = options.to   || 'html';
  var args = options.args || [];
  var opts = options.opts || [];
  var pattern = options.pattern || '**/*.md';

  return function(files, metalsmith, done){
    async.each(Object.keys(files), function(file, cb){
      debug('Checking file: %s', file);
      if (!minimatch(file, pattern)) {
        cb(); // count
        return; // do nothing
      }
      var data = files[file];
      var dir = dirname(file);
      var html = basename(file, extname(file)) + '.html';
      if ('.' != dir) html = dir + '/' + html;

      debug('Converting file %s', file);
      var md = data.contents.toString();
      pdc(md, from, to, args, opts, function(err,res){
        if (err){
          msg = file + ': ' + err;
          debug(msg);
          cb(msg);
          return;
        }
        if (res === undefined || res === ''){
          var msg = 'ERROR: nothing returned from pandoc for file ' + file;
          debug(msg);
          cb(new Error(msg));
          return;
        }
        debug('Converted file %s. Converted: %s...', file, res.substring(0,10).replace('\n',''));
        data.contents = new Buffer(res);
        delete files[file];
        files[html] = data;
        cb();
      });
    }, done);
  };
}
<commit_msg>default to html5<commit_after>var basename  = require('path').basename;
var dirname   = require('path').dirname;
var extname   = require('path').extname;
var debug     = require('debug')('metalsmith-pandoc');
var pdcPath   = require('pandoc-bin').path;
var pdc       = require('pdc');
var minimatch = require('minimatch');
var async     = require('async');

// use pandoc-bin
pdc.path = pdcPath;

/**
 * Expose `plugin`.
 */

module.exports = plugin;

/**
 * Metalsmith plugin to convert files using pandoc.
 *
 */

function plugin(options){
  options = options || {};
  var from = options.from || 'markdown';
  var to   = options.to   || 'html5';
  var args = options.args || [];
  var opts = options.opts || [];
  var pattern = options.pattern || '**/*.md';

  return function(files, metalsmith, done){
    async.each(Object.keys(files), function(file, cb){
      debug('Checking file: %s', file);
      if (!minimatch(file, pattern)) {
        cb(); // count
        return; // do nothing
      }
      var data = files[file];
      var dir = dirname(file);
      var html = basename(file, extname(file)) + '.html';
      if ('.' != dir) html = dir + '/' + html;

      debug('Converting file %s', file);
      var md = data.contents.toString();
      pdc(md, from, to, args, opts, function(err,res){
        if (err){
          msg = file + ': ' + err;
          debug(msg);
          cb(msg);
          return;
        }
        if (res === undefined || res === ''){
          var msg = 'ERROR: nothing returned from pandoc for file ' + file;
          debug(msg);
          cb(new Error(msg));
          return;
        }
        debug('Converted file %s. Converted: %s...', file, res.substring(0,10).replace('\n',''));
        data.contents = new Buffer(res);
        delete files[file];
        files[html] = data;
        cb();
      });
    }, done);
  };
}
<|endoftext|>