A blog platform written in JavaScript for developers, but with normal people in mind.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

276 lines
7.4 KiB

/*jshint -W054 */
(function (exports) {
"use strict";
function create(Desi) {
// Chrome, Firefox, and even MSIE11+ all support crypto
var crypto = window.crypto || window.msCrypto,
Promise = window.Promise,
algos;
// convenience mappings for common digest algorithms
algos = {
sha1: "SHA-1",
sha256: "SHA-256",
sha512: "SHA-512",
};
// The function to generate a sha1sum is the same as generating any digest
// but here's a shortcut function anyway
function sha1sum(str) {
return hashsum("sha1", str);
}
// a more general convenience function
function hashsum(hash, str) {
// you have to convert from string to array buffer
var ab,
// you have to represent the algorithm as an object
algo = { name: algos[hash] };
if ("string" === typeof str) {
ab = str2ab(str);
} else {
ab = str;
}
// All crypto digest methods return a promise
return crypto.subtle
.digest(algo, ab)
.then(function (digest) {
// you have to convert the ArrayBuffer to a DataView and then to a hex String
return ab2hex(digest);
})
.catch(function (e) {
// if you specify an unsupported digest algorithm or non-ArrayBuffer, you'll get an error
console.error("sha1sum ERROR");
console.error(e);
throw e;
});
}
// convert from arraybuffer to hex
function ab2hex(ab) {
var dv = new DataView(ab),
i,
len,
hex = "",
c;
for (i = 0, len = dv.byteLength; i < len; i += 1) {
c = dv.getUint8(i).toString(16);
if (c.length < 2) {
c = "0" + c;
}
hex += c;
}
return hex;
}
// convert from string to arraybuffer
function str2ab(stringToEncode, insertBom) {
stringToEncode = stringToEncode.replace(/\r\n/g, "\n");
var utftext = [],
n,
c;
if (true === insertBom) {
utftext[0] = 0xef;
utftext[1] = 0xbb;
utftext[2] = 0xbf;
}
for (n = 0; n < stringToEncode.length; n += 1) {
c = stringToEncode.charCodeAt(n);
if (c < 128) {
utftext[utftext.length] = c;
} else if (c > 127 && c < 2048) {
utftext[utftext.length] = (c >> 6) | 192;
utftext[utftext.length] = (c & 63) | 128;
} else {
utftext[utftext.length] = (c >> 12) | 224;
utftext[utftext.length] = ((c >> 6) & 63) | 128;
utftext[utftext.length] = (c & 63) | 128;
}
}
return new Uint8Array(utftext).buffer;
}
exports.hashsum = hashsum;
exports.sha1sum = sha1sum;
//
// FSAPI
//
var fsapi;
function request() {}
request.get = function (url /*, query*/) {
// Return a new promise.
return new Promise(function (resolve, reject) {
// Do the usual XHR stuff
var req = new XMLHttpRequest();
req.onload = function () {
// This is called even on 404 etc
// so check the status
if (200 === req.status) {
// Resolve the promise with the response text
resolve(req.response);
} else {
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Handle network errors
req.onerror = function () {
reject(Error("Network Error"));
};
// Make the request
req.open("GET", url);
req.send();
});
};
request.post = function (url /*, query*/, body) {
// Return a new promise.
return new Promise(function (resolve, reject) {
// Do the usual XHR stuff
var req = new XMLHttpRequest();
req.onload = function () {
// This is called even on 404 etc
// so check the status
if (200 === req.status) {
// Resolve the promise with the response text
resolve(req.response);
} else {
// Otherwise reject with the status text
// which will hopefully be a meaningful error
reject(Error(req.statusText));
}
};
// Handle network errors
req.onerror = function () {
reject(Error("Network Error"));
};
req.open("POST", url);
req.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
// Make the request
if ("string" !== typeof body) {
body = JSON.stringify(body);
}
req.send(body);
});
};
Desi.fsapi = fsapi = Desi.fsapi || {};
fsapi.getMeta = function (collections, opts) {
opts = opts || {};
var extensions = "",
dotfiles = "",
contents = "",
sha1sum = "";
if (Array.isArray(opts.extensions)) {
extensions = "&extensions=" + opts.extensions.join(","); // md,markdown,jade,htm,html
}
if (opts.dotfiles) {
dotfiles = "&dotfiles=true";
}
if (opts.contents) {
contents = "&contents=true";
}
if (false === opts.sha1sum) {
sha1sum = "&sha1sum=false";
}
return request
.post(
"/api/fs/walk?_method=GET" +
dotfiles +
extensions +
contents +
sha1sum,
{
dirs: collections,
}
)
.then(function (resp) {
return JSON.parse(resp);
})
.catch(function (e) {
throw e;
});
};
fsapi.getContents = function (filepaths) {
return request
.post("/api/fs/files?_method=GET", {
paths: filepaths,
})
.then(function (resp) {
return JSON.parse(resp);
});
};
fsapi.getCache = function () {
return request
.get("/api/fs/static/cache.json")
.then(function (resp) {
return JSON.parse(resp);
})
.catch(function (/*e*/) {
return {};
})
.then(function (obj) {
return obj;
});
};
fsapi.copy = function (files) {
var body = { files: files };
body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
return request.post("/api/fs/copy", body).then(function (resp) {
var response = JSON.parse(resp);
// not accurate for utf8/unicode, but close enough
response.size = body.length;
return response;
});
};
fsapi.putFiles = function (files) {
var body = { files: files };
files.forEach(function (file) {
if (!file.contents || "string" === typeof file.contents) {
return;
}
if (/\.json$/i.test(file.path)) {
file.contents = JSON.stringify(file.contents);
} else if (/\.ya?ml$/i.test(file.path)) {
file.contents = exports.jsyaml.dump(file.contents);
}
});
body = JSON.stringify(body); // this is more or less instant for a few MiB of posts
return request.post("/api/fs/files", body).then(function (resp) {
var response = JSON.parse(resp);
// not accurate for utf8/unicode, but close enough
response.size = body.length;
return response;
});
};
}
if (exports.Desirae) {
create(exports.Desirae);
} else {
exports.create = create;
}
})(("undefined" !== typeof exports && exports) || window);