// Github: https://github.com/shdwjk/Roll20API/blob/master/TableExport/TableExport.js
// By: The Aaron, Arcane Scriptomancer
// Contact: https://app.roll20.net/users/104025/the-aaron
var TableExport = TableExport || (function() {
'use strict';
var version = '0.2.3',
lastUpdate = 1453209012,
tableCache = {},
escapes = {
'[' : '<%%91%%>',
']' : '<%%93%%>',
'--' : '<%%-%%>',
' --' : '[TABLEEXPORT:ESCAPE]'
},
esRE = function (s) {
var escapeForRegexp = /(\\|\/|\[|\]|\(|\)|\{|\}|\?|\+|\*|\||\.|\^|\$)/g;
return s.replace(escapeForRegexp,"\\$1");
},
ch = function (c) {
var entities = {
'<' : 'lt',
'>' : 'gt',
"'" : '#39',
'@' : '#64',
'{' : '#123',
'|' : '#124',
'}' : '#125',
'[' : '#91',
']' : '#93',
'"' : 'quot',
'-' : 'mdash',
' ' : 'nbsp'
};
if(_.has(entities,c) ){
return ('&'+entities[c]+';');
}
return '';
},
checkInstall = function() {
log('-=> TableExport v'+version+' <=- ['+(new Date(lastUpdate*1000))+']');
},
showHelp = function() {
sendChat('',
'/w gm '
+'
'
+'
'
+'TableExport v'+version
+'
'
+'
'
+'
This script dumps commands to the chat for reconstructing a rollable table on another campaign. While this can be done on your own campaigns via the transmogrifyer, this script allows you to pass those commands to a friend and thus share your own creative works with others.
'
+'
Caveat: Avatar images that are not in your own library will be ignored by the API on import, but will not prevent creation of the table and table items.
'
+'
'
+'
Commands'
+'
'
+'
!export-table --'+ch('<')+'Table Name'+ch('>')+' [ --'+ch('<')+'Table Name'+ch('>')+' ...]'
+'
'
+'
For all table names, case is ignored and you may use partial names so long as they are unique. For example, '+ch('"')+'King Maximillian'+ch('"')+' could be called '+ch('"')+'max'+ch('"')+' as long as '+ch('"')+'max'+ch('"')+' does not appear in any other table names. Exception: An exact match will trump a partial match. In the previous example, if a table named '+ch('"')+'Max'+ch('"')+' existed, it would be the only table matched for --max.
'
+'
'
+'- '
+'--'+ch('<')+'Table Name'+ch('>')+' '+ch('-')+' This is the name of a table to export. You can specify as many tables as you like in a single command.'
+'
'
+'
'
+'
'
+'
'
+'
'
+'
!import-table --'+ch('<')+'Table Name'+ch('>')+' --'+ch('<')+'[ show | hide ]'+ch('>')+''
+'
'
+'
This is the command output by !export-table to create the new table. You likely will not need issue these commands directly.
'
+'
'
+'- '
+'--'+ch('<')+'Table Name'+ch('>')+' '+ch('-')+' This is the name of the table to be create.'
+'
'
+'- '
+'--'+ch('<')+'[ show | hide ]'+ch('>')+' '+ch('-')+' This whether to show the table to players or hide it.'
+'
'
+'
'
+'
'
+'
'
+'
'
+'
!import-table-item --'+ch('<')+'Table Name'+ch('>')+' --'+ch('<')+'Table Item Name'+ch('>')+' --'+ch('<')+'Weight'+ch('>')+' --'+ch('<')+'Avatar URL'+ch('>')+''
+'
'
+'
This is the command output by !export-table to create the new table. You likely will not need issue these commands directly.
'
+'
'
+'- '
+'--'+ch('<')+'Table Name'+ch('>')+' '+ch('-')+' This is the name of the table to add items to. Note: unlike for !export-table, this must be an exact name match to the created table.'
+'
'
+'- '
+'--'+ch('<')+'Table Item Name'+ch('>')+' '+ch('-')+' This is the name of the table item to create. Note: Because the string '+ch('"')+' --'+ch('"')+' may occur in a table item name, you may see '+ch('"')+'[TABLEEXPORT:ESCAPE]'+ch('"')+' show up as a replacement in these commands. This value is corrected internally to the correct '+ch('"')+' --'+ch('"')+' sequence on import.'
+'
'
+'- '
+'--'+ch('<')+'Weight'+ch('>')+' '+ch('-')+' This is the weight for this table item and should be an integer value.'
+'
'
+'- '
+'--'+ch('<')+'Avatar URL'+ch('>')+' '+ch('-')+' This is the URL for the avatar image of the table item.'
+'
'
+'
'
+'
'
+'
'
+'
'
);
},
nameEscape = (function(){
var re=new RegExp('('+_.map(_.keys(escapes),esRE).join('|')+')','g');
return function(s){
return s.replace(re, function(c){ return escapes[c] || c; });
};
}()),
nameUnescape = (function(){
var sepacse = _.invert(escapes),
re=new RegExp('('+_.map(_.keys(sepacse),esRE).join('|')+')','g');
return function(s){
return s.replace(re, function(c){ return sepacse[c] || c; });
};
}()),
handleInput = function(msg) {
var args, matches, tables, tableIDs=[], errors=[], items, itemMatches, accum='';
if (msg.type !== "api" || !playerIsGM(msg.playerid)) {
return;
}
args = msg.content.split(/\s+/);
switch(args[0]) {
case '!import-table':
args = msg.content.split(/\s+--/);
if(args.length === 1) {
showHelp();
break;
}
if(_.has(tableCache,args[1])) {
sendChat('','/w gm '
+''
+'Warning: '
+'Table ['+args[1]+'] already exists, skipping create.'
+'
'
);
} else {
tableIDs=findObjs({type: 'rollabletable', name: args[1]});
if(tableIDs.length) {
sendChat('','/w gm '
+''
+'Warning: '
+'Table ['+args[1]+'] already exists, skipping create.'
+'
'
);
} else {
tableIDs=createObj('rollabletable',{
name: args[1],
showplayers: ('show'===args[2])
});
tableCache[args[1]]=tableIDs.id;
}
}
break;
case '!import-table-item':
args = msg.content.split(/\s+--/);
if(args.length === 1) {
showHelp();
break;
}
args[2] = nameUnescape(args[2]);
if(!_.has(tableCache,args[1])) {
tableIDs=findObjs({type: 'rollabletable', name: args[1]});
if(!tableIDs.length) {
sendChat('','/w gm '
+''
+'Error: '
+'Table ['+args[1]+'] doesn not exist. Cannot create table item.'
+'
'
);
break;
} else {
tableCache[args[1]]=tableIDs[0].id;
}
}
createObj('tableitem',{
name: args[2],
rollabletableid: tableCache[args[1]],
weight: parseInt(args[3],10)||1,
avatar: args[4]||''
});
break;
case '!export-table':
args = msg.content.split(/\s+--/);
if(args.length === 1) {
showHelp();
break;
}
tables=findObjs({type: 'rollabletable'});
matches=_.chain(args)
.rest()
.map(function(n){
var l=_.filter(tables,function(t){
return t.get('name').toLowerCase() === n.toLowerCase();
});
return ( 1 === l.length ? l : _.filter(tables,function(t){
return -1 !== t.get('name').toLowerCase().indexOf(n.toLowerCase());
}));
})
.value();
_.each(matches,function(o,idx){
if(1 !== o.length) {
if(o.length) {
errors.push('Rollable Table ['+args[idx+1]+'] is ambiguous and matches '+o.length+' names: '+_.map(o,function(e){
return e.get('name');
}).join(', ')+'');
} else {
errors.push('Rollable Table ['+args[idx+1]+'] does not match any names.');
}
}
},errors);
if(errors.length) {
sendChat('','/w gm '
+''
+'
Error: '
+errors.join('
Error: ')
+'
'
+'
'
);
break;
}
if( ! errors.length) {
matches=_.chain(matches)
.flatten(true)
.map(function(t){
tableIDs.push(t.id);
return t;
})
.value();
items=findObjs({type: 'tableitem'});
itemMatches=_.chain(items)
.filter(function(i){
return _.contains(tableIDs,i.get('rollabletableid'));
})
.reduce(function(memo,e){
if(!_.has(memo,e.get('rollabletableid'))) {
memo[e.get('rollabletableid')]=[e];
} else {
memo[e.get('rollabletableid')].push(e);
}
return memo;
},{})
.value();
_.each(matches, function(t){
accum+='!import-table --'+nameEscape(t.get('name'))+' --'+(t.get('showplayers') ? 'show' : 'hide')+'
';
_.each(itemMatches[t.id], function(i){
accum+='!import-table-item --'+nameEscape(t.get('name'))+' --'+nameEscape(i.get('name'))+' --'+i.get('weight')+' --'+i.get('avatar')+'
';
});
});
sendChat('', '/w gm '+accum);
}
break;
}
},
handleRemoveTable = function(obj){
tableCache = _.without(tableCache,obj.id);
},
registerEventHandlers = function() {
on('chat:message', handleInput);
on('destroy:rollabletable', handleRemoveTable);
};
return {
CheckInstall: checkInstall,
RegisterEventHandlers: registerEventHandlers
};
}());
on("ready",function(){
'use strict';
TableExport.CheckInstall();
TableExport.RegisterEventHandlers();
});