UNPKG

@minima-global/create-minima-app

Version:

<p align="center"> A Cli for creating and building MiniDapps on the Minima Blockchain. </p>

788 lines (670 loc) 19.1 kB
/** * MDS JS lib for MiniDAPPs.. * * @spartacusrex */ /** * The MAIN Minima Callback function */ var MDS_MAIN_CALLBACK = null /** * API call id array */ var API_CALLS = [] /** * Main MINIMA Object for all interaction */ var MDS = { //Main File host filehost: "", //RPC Host for Minima mainhost: "", //The MiniDAPP UID minidappuid: "", //Is logging RPC enabled logging: false, //When debuggin you can hard set the Host and port DEBUG_HOST: null, DEBUG_PORT: -1, //An allowed TEST Minidapp ID for SQL - can be overridden DEBUG_MINIDAPPID: "0x00", /** * Minima Startup - with the callback function used for all Minima messages */ init: function (callback) { //Log a little.. MDS.log("Initialising MDS..") //Is logging enabled.. via the URL if (MDS.form.getParams("MDS_LOGGING") != null) { MDS.logging = true } //Get the host and port.. var host = window.location.hostname var port = Math.floor(window.location.port) //Get ther MiniDAPP UID MDS.minidappuid = MDS.form.getParams("uid") //HARD SET if debug mode - running from a file if (MDS.DEBUG_HOST != null) { MDS.log("DEBUG Settings Found..") host = MDS.DEBUG_HOST port = MDS.DEBUG_PORT } if (MDS.minidappuid == null) { MDS.minidappuid = MDS.DEBUG_MINIDAPPID } //Is one specified.. if (MDS.minidappuid == "0x00") { MDS.log("No MiniDAPP UID specified.. using test value") } MDS.filehost = "https://" + host + ":" + port + "/" MDS.mainhost = "https://" + host + ":" + port + "/mdscommand_/" MDS.log("MDS HOST : " + MDS.filehost) //Store this for poll messages MDS_MAIN_CALLBACK = callback //Start the Long Poll listener PollListener() //And Post a message MDSPostMessage({ event: "inited" }) }, /** * Log some data with a timestamp in a consistent manner to the console */ log: function (output) { console.log("Minima @ " + new Date().toLocaleString() + " : " + output) }, /** * Notify the User - on Phone it pops up in status bar. On desktop appears in Logs */ notify: function (output) { //Send via POST httpPostAsync("notify", output) }, /** * Cancel this MiniDAPPs notification */ notifycancel: function () { //Send via POST httpPostAsync("notifycancel", "*") }, /** * Runs a function on the Minima Command Line - same format as MInima */ cmd: function (command, callback) { //Send via POST httpPostAsync("cmd", command, callback) }, /** * Runs a SQL command on this MiniDAPPs SQL Database */ sql: function (command, callback) { //Send via POST httpPostAsync("sql", command, callback) }, /** * Get a link to a different Dapp. READ dapps can only get READ DAPPS. WRITE can get all dapps. */ dapplink: function (dappname, callback) { //Send via POST httpPostAsync("dapplink", dappname, function (result) { var linkdata = {} linkdata.status = result.status //Create the link.. if (result.status) { linkdata.uid = result.response.uid linkdata.sessionid = result.response.sessionid linkdata.base = MDS.filehost + linkdata.uid + "/index.html?uid=" + result.response.sessionid } else { //Not found.. linkdata.error = result.error } callback(linkdata) }) }, /** * API call to a different minidapp */ api: { call: function (dappname, data, callback) { //Construct a unique API request var rand = "" + Math.random() * 1000000000 //Construct a callback list object var callitem = {} callitem.id = rand callitem.callback = callback //Add to the api calls.. API_CALLS.push(callitem) //Create the single line var commsline = dappname + "&request&" + rand + "&" + data //Send via POST httpPostAsync("api", commsline) }, reply: function (dappname, id, data, callback) { //Create the single line var commsline = dappname + "&response&" + id + "&" + data //Send via POST httpPostAsync("api", commsline, callback) }, }, /** * Network Commands */ net: { /** * Make a GET request */ GET: function (url, callback) { //Send via POST httpPostAsync("net", url, callback) }, /** * Make a GET request WITH a Basic Auth token header */ GETAUTH: function (url, basicauth, callback) { //Create the single line var commsline = url + "&" + basicauth //Send via POST httpPostAsync("netauth", commsline, callback) }, /** * Make a POST request */ POST: function (url, data, callback) { //Create the sinlg eline version.. var postline = url + "&" + data //Send via POST httpPostAsync("netpost", postline, callback) }, }, /** * Simple GET and SET key value pairs that are saved persistently */ keypair: { /** * GET a value */ get: function (key, callback) { //Create the single line var commsline = "get&" + key //Send via POST httpPostAsync("keypair", commsline, callback) }, /** * SET a value */ set: function (key, value, callback) { //Create the single line var commsline = "set&" + key + "&" + value //Send via POST httpPostAsync("keypair", commsline, callback) }, }, /** * COMMS - send a message to ALL minidapps or JUST your own service.js */ comms: { /** * PUBLIC message broadcast to ALL (callback is optional) */ broadcast: function (msg, callback) { //Create the single line var commsline = "public&" + msg //Send via POST httpPostAsync("comms", commsline, callback) }, /** * PRIVATE message send just to this MiniDAPP (callback is optional) */ solo: function (msg, callback) { //Create the single line var commsline = "private&" + msg //Send via POST httpPostAsync("comms", commsline, callback) }, }, /** * File access */ file: { /** * List file in a folder .. start at / */ list: function (folder, callback) { //Create the single line var commsline = "list&" + folder //Send via POST httpPostAsync("file", commsline, callback) }, /** * Save text - can be text, a JSON in string format or hex encoded data */ save: function (filename, text, callback) { //Create the single line var commsline = "save&" + filename + "&" + text //Send via POST httpPostAsync("file", commsline, callback) }, /** * Save Binary Data - supply as a HEX string */ savebinary: function (filename, hexdata, callback) { //Create the single line var commsline = "savebinary&" + filename + "&" + hexdata //Send via POST httpPostAsync("file", commsline, callback) }, /** * Load text - can be text, a JSON in string format or hex encoded data */ load: function (filename, callback) { //Create the single line var commsline = "load&" + filename //Send via POST httpPostAsync("file", commsline, callback) }, /** * Load Binary data - returns the HEX data */ loadbinary: function (filename, callback) { //Create the single line var commsline = "loadbinary&" + filename //Send via POST httpPostAsync("file", commsline, callback) }, /** * Delete a file */ delete: function (filename, callback) { //Create the single line var commsline = "delete&" + filename //Send via POST httpPostAsync("file", commsline, callback) }, /** * Get the full path - if you want to run a command on the file / import a txn / unsigned txn etc */ getpath: function (filename, callback) { //Create the single line var commsline = "getpath&" + filename //Send via POST httpPostAsync("file", commsline, callback) }, /** * Make a directory */ makedir: function (filename, callback) { //Create the single line var commsline = "makedir&" + filename //Send via POST httpPostAsync("file", commsline, callback) }, /** * Copy a file */ copy: function (filename, newfilename, callback) { //Create the single line var commsline = "copy&" + filename + "&" + newfilename //Send via POST httpPostAsync("file", commsline, callback) }, /** * Move a file */ move: function (filename, newfilename, callback) { //Create the single line var commsline = "move&" + filename + "&" + newfilename //Send via POST httpPostAsync("file", commsline, callback) }, /** * Download a File from the InterWeb - Will be put in Downloads folder */ download: function (url, callback) { //Create the single line var commsline = "download&" + url //Send via POST httpPostAsync("file", commsline, callback) }, /** * Upload a file in chunks to the /fileupload folder */ upload: function (file, callback) { //Start the file recursion.. _recurseUploadMDS(file, 0, callback) }, /** * List file in a folder .. start at / */ listweb: function (folder, callback) { //Create the single line var commsline = "listweb&" + folder //Send via POST httpPostAsync("file", commsline, callback) }, /** * Copy a file to your web folder */ copytoweb: function (file, webfile, callback) { //Create the single line var commsline = "copytoweb&" + file + "&" + webfile //Send via POST httpPostAsync("file", commsline, callback) }, /** * Delete a file or folder from web folder */ deletefromweb: function (file, callback) { //Create the single line var commsline = "deletefromweb&" + file //Send via POST httpPostAsync("file", commsline, callback) }, }, /** * Function for GET parameters.. */ form: { //Return the GET parameter by scraping the location.. getParams: function (parameterName) { var result = null, tmp = [] var items = window.location.search.substr(1).split("&") for (var index = 0; index < items.length; index++) { tmp = items[index].split("=") //console.log("TMP:"+tmp); if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]) } return result }, }, /** * UTILITY functions.. very useful */ util: { //Convert HEX to Base 64 - removes the 0x if necessary hexToBase64(hexstring) { //Check if starts with 0x var thex = hexstring if (hexstring.startsWith("0x")) { thex = hexstring.substring(2) } return btoa( thex .match(/\w{2}/g) .map(function (a) { return String.fromCharCode(parseInt(a, 16)) }) .join("") ) }, //Convert Base64 to HEX base64ToHex(str) { const raw = atob(str) let result = "" for (let i = 0; i < raw.length; i++) { const hex = raw.charCodeAt(i).toString(16) result += hex.length === 2 ? hex : "0" + hex } return result.toUpperCase() }, //Convert Base64 to a Uint8Array - useful for Blobs base64ToArrayBuffer(base64) { var binary_string = window.atob(base64) var len = binary_string.length var bytes = new Uint8Array(len) for (var i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i) } return bytes.buffer }, //Return a state variable given the coin getStateVariable(coin, port) { //Get the state vars var statvars = coin.state var len = statvars.length for (var i = 0; i < len; i++) { var state = statvars[i] if (state.port == port) { return state.data } } return undefined }, }, } /** * Post a message to the Minima Event Listeners */ function MDSPostMessage(json) { //And dispatch if (MDS_MAIN_CALLBACK) { //Is this an API response call.. if (json.event == "MDSAPI") { //Check if it is a response.. if (!json.data.request) { //Find the API CALL Object var found = "" var len = API_CALLS.length for (var i = 0; i < len; i++) { if (API_CALLS[i].id == json.data.id) { //found it..! found = json.data.id //Construct a reply.. var reply = {} reply.status = json.data.status reply.data = json.data.message API_CALLS[i].callback(reply) } } //Remove it.. if (found != "") { API_CALLS = API_CALLS.filter(function (apic) { return apic.id != found }) } else { //MDS.log("API CALL NOT FOUND!"+JSON.stringify(json)); } //Response messages not forwarded - only via API call return } } //Call the main function MDS_MAIN_CALLBACK(json) } } var PollCounter = 0 var PollSeries = 0 function PollListener() { //The POLL host var pollhost = MDS.mainhost + "poll?" + "uid=" + MDS.minidappuid var polldata = "series=" + PollSeries + "&counter=" + PollCounter httpPostAsyncPoll(pollhost, polldata, function (msg) { //Are we on the right Series.. if (PollSeries != msg.series) { //Reset to the right series.. PollSeries = msg.series PollCounter = msg.counter } else { //Is there a message ? if (msg.status == true) { //Get the current counter.. PollCounter = msg.response.counter + 1 //And Post the message.. MDSPostMessage(msg.response.message) } } //And around we go again.. PollListener() }) } function postMDSFail(command, params, status) { //Some error.. if (MDS.logging) { MDS.log("** An error occurred during an MDS command!") } //Create the message var errormsg = {} errormsg.event = "MDSFAIL" errormsg.data = {} errormsg.data.command = command errormsg.data.params = params errormsg.data.error = status //Post it to the stack MDSPostMessage(errormsg) } /** * Utility function for GET request * * @param theUrl * @param callback * @param params * @returns */ function httpPostAsync(theUrl, params, callback) { //Add the MiniDAPP UID.. var finalurl = MDS.mainhost + theUrl + "?uid=" + MDS.minidappuid //Do we log it.. if (MDS.logging) { MDS.log("POST_RPC:" + finalurl + " PARAMS:" + params) } var xmlHttp = new XMLHttpRequest() xmlHttp.onreadystatechange = function () { var status = xmlHttp.status if (xmlHttp.readyState == XMLHttpRequest.DONE) { if (status === 0 || (status >= 200 && status < 400)) { //Do we log it.. if (MDS.logging) { MDS.log("RESPONSE:" + xmlHttp.responseText) } //Send it to the callback function.. if (callback) { callback(JSON.parse(xmlHttp.responseText)) } } else { //Some error.. postMDSFail(finalurl, params, xmlHttp.status) } } } xmlHttp.open("POST", finalurl, true) // true for asynchronous xmlHttp.overrideMimeType("text/plain; charset=UTF-8") xmlHttp.send(encodeURIComponent(params)) //xmlHttp.onerror = function () { // console.log("** An error occurred during the transaction"); //}; } /** * POLLING Call */ function httpPostAsyncPoll(theUrl, params, callback) { //Do we log it.. if (MDS.logging) { MDS.log("POST_POLL_RPC:" + theUrl + " PARAMS:" + params) } var xmlHttp = new XMLHttpRequest() xmlHttp.onreadystatechange = function () { var status = xmlHttp.status if (xmlHttp.readyState == XMLHttpRequest.DONE) { if (status === 0 || (status >= 200 && status < 400)) { //Do we log it.. if (MDS.logging) { MDS.log("RESPONSE:" + xmlHttp.responseText) } //Send it to the callback function.. if (callback) { callback(JSON.parse(xmlHttp.responseText)) } } else { //Some error.. postMDSFail(theUrl, params, xmlHttp.status) } } } xmlHttp.addEventListener("error", function (ev) { MDS.log("Error Polling - reconnect in 10s") setTimeout(function () { PollListener() }, 10000) }) xmlHttp.open("POST", theUrl, true) // true for asynchronous xmlHttp.overrideMimeType("text/plain; charset=UTF-8") xmlHttp.send(encodeURIComponent(params)) } /** * Internal recursive function for file upload */ function _recurseUploadMDS(thefullfile, chunk, callback) { //Get some details var filename = thefullfile.name var filesize = thefullfile.size //1MB MAX Chunk size.. var chunk_size = 1024 * 1024 var allchunks = Math.ceil(filesize / chunk_size) //Have we finished.. if (chunk > allchunks - 1) { return } var startbyte = chunk_size * chunk var endbyte = startbyte + chunk_size if (endbyte > filesize) { endbyte = filesize } //Get a piece of the file var filepiece = thefullfile.slice(startbyte, endbyte) //Create a form.. var formdata = new FormData() formdata.append("uid", MDS.minidappuid) //Filedata handled a little differently formdata.append("filename", filename) formdata.append("filesize", filesize) formdata.append("allchunks", allchunks) formdata.append("chunknum", chunk) formdata.append("fileupload", filepiece) var request = new XMLHttpRequest() request.open("POST", "/fileuploadchunk.html") request.onreadystatechange = function () { var status = request.status if (request.readyState == XMLHttpRequest.DONE) { if (status === 0 || (status >= 200 && status < 400)) { //Send it to the callback function.. if (callback) { var resp = {} resp.status = true resp.filename = filename resp.size = filesize resp.allchunks = allchunks resp.chunk = chunk + 1 resp.start = startbyte resp.end = endbyte callback(resp) } //And now continue uploading.. if (callback) { _recurseUploadMDS(thefullfile, chunk + 1, callback) } else { _recurseUploadMDS(thefullfile, chunk + 1) } } else { if (callback) { var resp = {} resp.status = false resp.error = request.responseText resp.filename = filename resp.size = filesize resp.allchunks = allchunks resp.chunk = chunk resp.start = startbyte resp.end = endbyte callback(resp) } //Some error.. MDS.log("MDS FILEUPLOAD CHUNK ERROR: " + request.responseText) } } } //And finally send the POST request request.send(formdata) }