html_url,issue_url,id,node_id,user,created_at,updated_at,author_association,body,reactions,issue,performed_via_github_app
https://github.com/simonw/datasette/issues/987#issuecomment-752696499,https://api.github.com/repos/simonw/datasette/issues/987,752696499,MDEyOklzc3VlQ29tbWVudDc1MjY5NjQ5OQ==,9599,2020-12-30T17:21:08Z,2020-12-30T17:21:08Z,OWNER,"More generally, I need to document certain areas of the page that JavaScript plugins are invited to append their content to - such that plugin authors can use them and feel confident that future changes to the Datasette templates won't break their plugins.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",712984738,
https://github.com/simonw/datasette/issues/987#issuecomment-752697279,https://api.github.com/repos/simonw/datasette/issues/987,752697279,MDEyOklzc3VlQ29tbWVudDc1MjY5NzI3OQ==,9599,2020-12-30T17:23:27Z,2020-12-30T17:23:32Z,OWNER,Related problem: right now if you're writing custom template it's not at all obvious how to write them such that visualization plugins like `datasette-cluster-map` will continue to work.,"{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",712984738,
https://github.com/simonw/datasette/issues/983#issuecomment-752742669,https://api.github.com/repos/simonw/datasette/issues/983,752742669,MDEyOklzc3VlQ29tbWVudDc1Mjc0MjY2OQ==,9599,2020-12-30T20:07:05Z,2020-12-30T20:07:18Z,OWNER,"Initial prototype:
```javascript
window.datasette = {};
window.datasette.plugins = (function() {
var registry = {};
function extractParameters(fn) {
var match = /\((.*)\)/.exec(fn.toString());
if (match && match[1].trim()) {
return match[1].split(',').map(s => s.trim());
} else {
return [];
}
}
function register(hook, fn, parameters) {
parameters = parameters || extractParameters(fn);
if (!registry[hook]) {
registry[hook] = [];
}
registry[hook].push([fn, parameters]);
}
function call(hook, args) {
args = args || {};
var implementations = registry[hook] || [];
var results = [];
implementations.forEach(([fn, parameters]) => {
/* Call with the correct arguments */
var callWith = parameters.map(parameter => args[parameter]);
var result = fn.apply(fn, callWith);
if (result) {
results.push(result);
}
});
return results;
}
return {
register: register,
_registry: registry,
call: call
};
})();
```
Usage example:
```javascript
datasette.plugins.register('numbers', (a, b) => a + b)
datasette.plugins.register('numbers', (a, b) => a * b)
datasette.plugins.call('numbers', {a: 4, b: 6})
/* Returns [10, 24] */
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",712260429,
https://github.com/simonw/datasette/issues/983#issuecomment-752744195,https://api.github.com/repos/simonw/datasette/issues/983,752744195,MDEyOklzc3VlQ29tbWVudDc1Mjc0NDE5NQ==,9599,2020-12-30T20:12:26Z,2020-12-30T20:12:26Z,OWNER,"This implementation doesn't have an equivalent of ""hookspecs"" which can identify if a registered plugin implementation matches a known signature. I should add that, it will provide a better developer experience if someone has a typo.","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",712260429,
https://github.com/simonw/datasette/issues/983#issuecomment-752744311,https://api.github.com/repos/simonw/datasette/issues/983,752744311,MDEyOklzc3VlQ29tbWVudDc1Mjc0NDMxMQ==,9599,2020-12-30T20:12:50Z,2020-12-30T20:13:02Z,OWNER,"This could work to define a plugin hook:
```javascript
datasette.plugins.define('numbers', ['a' ,'b'])
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",712260429,
https://github.com/simonw/datasette/issues/983#issuecomment-752747169,https://api.github.com/repos/simonw/datasette/issues/983,752747169,MDEyOklzc3VlQ29tbWVudDc1Mjc0NzE2OQ==,9599,2020-12-30T20:24:07Z,2020-12-30T20:24:07Z,OWNER,"This version adds `datasette.plugins.define()` plus extra validation of both `.register()` and `.call()`:
```javascript
window.datasette = {};
window.datasette.plugins = (function() {
var registry = {};
var definitions = {};
function extractParameters(fn) {
var match = /\((.*)\)/.exec(fn.toString());
if (match && match[1].trim()) {
return match[1].split(',').map(s => s.trim());
} else {
return [];
}
}
function define(hook, parameters) {
definitions[hook] = parameters || [];
}
function isSubSet(a, b) {
return a.every(parameter => b.includes(parameter))
}
function register(hook, fn, parameters) {
parameters = parameters || extractParameters(fn);
if (!definitions[hook]) {
throw new Error('""' + hook + '"" is not a defined plugin hook');
}
if (!definitions[hook]) {
throw new Error('""' + hook + '"" is not a defined plugin hook');
}
/* Check parameters is a subset of definitions[hook] */
var validParameters = definitions[hook];
if (!isSubSet(parameters, validParameters)) {
throw new Error('""' + hook + '"" valid parameters are ' + JSON.stringify(validParameters));
}
if (!registry[hook]) {
registry[hook] = [];
}
registry[hook].push([fn, parameters]);
}
function call(hook, args) {
args = args || {};
if (!definitions[hook]) {
throw new Error('""' + hook + '"" hook has not been defined');
}
if (!isSubSet(Object.keys(args), definitions[hook])) {
throw new Error('""' + hook + '"" valid arguments are ' + JSON.stringify(definitions[hook]));
}
var implementations = registry[hook] || [];
var results = [];
implementations.forEach(([fn, parameters]) => {
/* Call with the correct arguments */
var callWith = parameters.map(parameter => args[parameter]);
var result = fn.apply(fn, callWith);
if (result) {
results.push(result);
}
});
return results;
}
return {
define: define,
register: register,
_registry: registry,
call: call
};
})();
```
Usage:
```javascript
datasette.plugins.define('numbers', ['a', 'b'])
datasette.plugins.register('numbers', (a, b) => a + b)
datasette.plugins.register('numbers', (a, b) => a * b)
datasette.plugins.call('numbers', {a: 4, b: 6})
```","{""total_count"": 0, ""+1"": 0, ""-1"": 0, ""laugh"": 0, ""hooray"": 0, ""confused"": 0, ""heart"": 0, ""rocket"": 0, ""eyes"": 0}",712260429,
https://github.com/simonw/datasette/issues/983#issuecomment-752747999,https://api.github.com/repos/simonw/datasette/issues/983,752747999,MDEyOklzc3VlQ29tbWVudDc1Mjc0Nzk5OQ==,9599,2020-12-30T20:27:00Z,2020-12-30T20:27:00Z,OWNER,"I need to decide how this code is going to be loaded. Putting it in a blocking `