status-react/resources/js/bots/console/bot.js

535 lines
15 KiB
JavaScript

var jsSuggestionContainerStyle = {
backgroundColor: "white"
};
var jsSubContainerStyle = {
paddingTop: 9,
borderBottomWidth: 1,
borderBottomColor: "#0000001f"
};
function jsSuggestionSubContainerStyle(isLast) {
var borderBottomWidth = (isLast ? 0 : 1);
return {
paddingTop: 14,
paddingBottom: 14,
paddingRight: 14,
marginLeft: 14,
borderBottomWidth: borderBottomWidth,
borderBottomColor: "#e8ebec"
};
}
var jsValueStyle = {
fontSize: 15,
fontFamily: "font",
color: "#000000de"
};
var jsBoldValueStyle = {
fontSize: 15,
fontFamily: "font",
color: "#000000de",
fontWeight: "bold"
};
var jsDescriptionStyle = {
marginTop: 2,
fontSize: 14,
fontFamily: "font",
color: "#838c93de"
};
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (searchString, position) {
position = position || 0;
return this.substr(position, searchString.length) === searchString;
};
}
function matchSubString(array, string) {
var matched = [];
for (var i = 0; i < array.length; i++) {
var item = array[i];
if (item.toLowerCase().startsWith(string.toLowerCase())) {
matched.push(item);
}
}
return matched;
}
function cleanCode(code) {
// remove comments
var commentsRegex = /\/\*.+?\*\/|\/\/.*/g;
code = code.replace(commentsRegex, "");
// replace string literals
var literalsRegex = /\"(?:\\\\\"|[^\"])*?\"/g;
code = code.replace(literalsRegex, '""');
var literalsRegex = /\'(?:\\\\\'|[^\'])*?\'/g;
code = code.replace(literalsRegex, '""');
return code
}
function createObjectSuggestion(name, docInfo, code, parameterNumber) {
var title = name;
if (docInfo.args) {
title += "(";
for (var i = 0; i < docInfo.args.length; i++) {
var argument = docInfo.args[i];
var argumentText = (i > 0 ? ", " : "") + (parameterNumber === i ? "*" + argument.name + "*" : argument.name);
if (argument.optional) {
argumentText = "[" + argumentText + "]";
}
title += argumentText;
}
title += ")";
}
if (!docInfo.desc) {
name += ".";
} else if (docInfo.args) {
name += "(";
if (docInfo.args.length == 0) {
name += ")";
}
}
var suggestion = {
title: title,
desc: docInfo.desc
};
if (code != null) {
suggestion.pressValue = code + name;
}
return suggestion;
}
var lastMessage = null;
function getLastForm(code) {
var codeLength = code.length;
var form = '';
var level = 0;
var index = codeLength - 1;
while (index >= 0) {
var char = code[index];
if (level == 0 && index != 0 && (char == '(' || char == ',')) {
break;
}
if (char == ')') {
level--;
}
if (char == '(') {
level++;
}
form = char + form;
index--;
}
return form;
}
function getLastLevel(code) {
var codeLength = code.length;
var form = '';
var index = codeLength - 1;
var nested = false;
var level = 0;
while (index >= 0) {
var char = code[index];
if (char == ')') {
level--;
// A single unbalanced ')' shouldn't set nested to true.
if (level > 0) {
nested = true;
}
}
if (char == '(') {
level++;
if (level == 0) {
nested = false;
}
if (level <= 0) {
form = "argument" + form;
index--;
continue;
}
}
if ((level == 1 && char == ',') || level == 2) {
break;
}
if (!nested || level > 0) {
form = char + form;
}
index--;
}
if (form.indexOf("(") < 0 && form != ",") {
var parts = form.split(',');
form = parts[parts.length - 1];
}
return form;
}
function getPartialSuggestions(doc, fullCode, code) {
var suggestions = [];
var functionParts = code.split("(");
var objectParts = code.split(/[^a-zA-Z_0-9\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/);
var index = 0;
var suggestedFunction = '';
while (index < objectParts.length) {
var part = objectParts[index];
if (part != "desc" && part != "args" && doc[part] != null) {
doc = doc[part];
suggestedFunction += part + '.';
index++;
} else {
break;
}
}
suggestedFunction = suggestedFunction.substring(0, suggestedFunction.length - 1)
if (functionParts.length == 1) {
// object suggestions
if (index > objectParts.length - 1) {
var suggestion = objectParts[objectParts.length - 1];
suggestions.push(createObjectSuggestion(suggestion, doc, fullCode.substring(0, fullCode.length - suggestion.length)));
} else if (index === objectParts.length - 1) {
var lastPart = objectParts[index];
var keys = Object.keys(doc);
var matches = matchSubString(keys, lastPart);
for (var i = 0; i < matches.length; i++) {
var suggestion = matches[i];
if (suggestion == "desc" || suggestion == "args") {
continue;
}
var docInfo = doc[suggestion];
if (docInfo != null) {
suggestions.push(createObjectSuggestion(suggestion, docInfo, fullCode.substring(0, fullCode.length - lastPart.length)));
}
}
}
} else if (functionParts.length == 2) {
// parameter suggestions
var parameters = functionParts[1].split(",");
if (doc.args && parameters.length <= doc.args.length && parameters[parameters.length - 1].indexOf(")") < 0) {
var paramInfo = doc.args[parameters.length - 1];
var docInfo = doc;
docInfo.desc = paramInfo.name + ": " + paramInfo.desc;
suggestions.push(createObjectSuggestion(suggestedFunction, docInfo, null, parameters.length - 1));
}
}
//console.debug(suggestions);
return suggestions;
}
function getJsSuggestions(code, context) {
var suggestions = [];
var doc = DOC_MAP;
// TODO: what's /c / doing there ???
//console.debug(code);
var previousMessage = localStorage.getItem("previousMessage");
if (!code || code == "" || code == "c ") {
code = "";
//console.debug("Last message: " + context.data);
if (previousMessage != null) {
suggestions.push({
title: 'Last command used:',
desc: previousMessage,
pressValue: previousMessage
});
}
var keys = Object.keys(doc);
for (var i = 0; i < keys.length; i++) {
var suggestion = keys[i];
var docInfo = doc[suggestion];
if (docInfo != null) {
suggestions.push(createObjectSuggestion(suggestion, docInfo, ""));
}
}
} else {
// TODO: what's /c / doing there ???
if (code.startsWith("c ")) {
code = code.substring(2);
}
if (previousMessage != null &&
(typeof previousMessage === 'string' || previousMessage instanceof String) &&
previousMessage.startsWith(code)) {
suggestions.unshift({
title: 'Last command used:',
desc: previousMessage,
pressValue: previousMessage
});
}
var originalCode = code;
code = cleanCode(code);
var levelCode = getLastLevel(code);
var code = getLastForm(levelCode);
if (levelCode != code) {
suggestions = getPartialSuggestions(doc, originalCode, levelCode);
}
//console.debug("Final code: " + code);
//console.debug("Level code: " + levelCode);
suggestions = suggestions.concat(getPartialSuggestions(doc, originalCode, code));
}
return suggestions;
}
function createMarkupText(text) {
var parts = [];
var index = 0;
var currentText = '';
var isBold = false;
while (index < text.length) {
var char = text[index];
if (char == '*') {
if (currentText != '') {
parts.push(
status.components.text(
{style: isBold ? jsBoldValueStyle : jsValueStyle},
currentText
)
);
currentText = '';
}
isBold = !isBold;
} else {
currentText += char;
}
index++;
}
if (currentText != '') {
parts.push(
status.components.text(
{style: isBold ? jsBoldValueStyle : jsValueStyle},
currentText
)
);
}
//console.debug(parts);
return parts;
}
function jsSuggestions(params, context) {
var suggestions = getJsSuggestions(params.code, context);
var sugestionsMarkup = [];
for (var i = 0; i < suggestions.length; i++) {
var suggestion = suggestions[i];
if (suggestion.title.indexOf('*') >= 0) {
suggestion.title = createMarkupText(suggestion.title);
}
var suggestionMarkup = status.components.view(jsSuggestionContainerStyle,
[status.components.view(jsSuggestionSubContainerStyle(i == suggestions.length - 1),
[
status.components.text({style: jsValueStyle},
suggestion.title),
status.components.text({style: jsDescriptionStyle},
suggestion.desc)
])]);
if (suggestion.pressValue) {
suggestionMarkup = status.components.touchable({
onPress: status.components.dispatch([status.events.SET_VALUE, suggestion.pressValue])
},
suggestionMarkup
);
}
sugestionsMarkup.push(suggestionMarkup);
}
if (sugestionsMarkup.length > 0) {
return {markup: status.components.view({}, sugestionsMarkup)};
} else {
return {markup: null};
}
}
function jsHandler(params, context) {
var result = {
err: null,
data: null,
messages: []
};
messages = [];
try {
result["text-message"] = String(JSON.stringify(eval(params.code)));
localStorage.setItem("previousMessage", params.code);
} catch (e) {
result.err = e;
}
result.messages = messages;
return result;
}
var suggestionsContainerStyle = {
keyboardShouldPersistTaps: "always",
backgroundColor: "white",
flexGrow: 1,
bounces: false
}
var suggestionContainerStyle = {
backgroundColor: "white"
};
function suggestionSubContainerStyle(isTwoLineEntry, isLast) {
var height = (isTwoLineEntry ? 64 : 48);
var borderBottomWidth = (isLast ? 0 : 1);
return {
paddingTop: 14,
paddingBottom: 14,
paddingRight: 14,
marginLeft: 14,
height: height,
borderBottomWidth: borderBottomWidth,
borderBottomColor: "#e8ebec"
};
}
var valueStyle = {
fontSize: 15,
fontFamily: "font",
color: "#000000de"
};
var descriptionStyle = {
marginTop: 2,
fontSize: 13,
fontFamily: "font",
color: "#838c93de"
};
function startsWith(str1, str2) {
// String.startsWith(...) doesn't work in otto
return str1.lastIndexOf(str2, 0) == 0 && str1 != str2;
}
var ropstenNetworkId = 3;
var rinkebyNetworkId = 4;
var ropstenFaucets = [
{
name: "Status Testnet Faucet",
url: "http://51.15.45.169:3001",
}
];
var rinkebyFaucets = [
{
name: "Status Rinkeby Faucet",
url: "http://51.15.60.23:3001",
}
];
function getFaucets(networkId) {
if (networkId == ropstenNetworkId) {
return ropstenFaucets;
} else if (networkId == rinkebyNetworkId) {
return rinkebyFaucets;
} else {
return [];
}
}
var faucets = getFaucets(status.ethereumNetworkId);
function faucetSuggestions(params) {
var subContainerStyle = suggestionSubContainerStyle(true, false);
var suggestions = faucets.map(function (entry, index) {
return status.components.touchable(
{onPress: status.components.dispatch([status.events.SET_COMMAND_ARGUMENT, [0, entry.url, true]])},
status.components.view(
suggestionContainerStyle,
[status.components.view(
(index == faucets.length - 1 ? suggestionSubContainerStyle(true, true) : subContainerStyle),
[
status.components.text(
{style: valueStyle},
entry.name
),
status.components.text(
{style: descriptionStyle},
entry.url
)
]
)]
)
);
});
return {markup: status.components.view({}, suggestions)};
}
var faucetCommandConfig ={
name: "faucet",
title: I18n.t('faucet_title'),
description: I18n.t('faucet_description'),
color: "#7099e6",
scope: ["personal-chats", "registered", "dapps"],
params: [{
name: "url",
type: status.types.TEXT,
suggestions: faucetSuggestions,
placeholder: I18n.t('faucet_placeholder')
}],
preview: function (params) {
return {
markup: status.components.text(
{},
params.url
)
};
},
shortPreview: function (params) {
return {
markup: status.components.chatPreviewText(
{},
I18n.t('faucet_title') + ": " + params.url
)
};
},
validator: function (params, context) {
var f = faucets.map(function (entry) {
return entry.url;
});
if (f.indexOf(params.url) == -1) {
var error = status.components.validationMessage(
I18n.t('faucet_incorrect_title'),
I18n.t('faucet_incorrect_description')
);
return {markup: error};
}
}
}
;
if (faucets.length > 0) {
status.command(faucetCommandConfig);
}
status.response({
name: "grant-permissions",
scope: ["personal-chats", "anonymous", "registered", "dapps"],
color: "#7099e6",
description: "Grant permissions",
icon: "lock_white",
executeImmediately: true
});
status.addListener("on-message-input-change", function (params, context) {
return jsSuggestions({code: params.message}, context);
});
status.addListener("on-message-send", function (params, context) {
return jsHandler({code: params.message}, context);
});