Initial import
This commit is contained in:
306
static/cryptopad.html
Normal file
306
static/cryptopad.html
Normal file
@@ -0,0 +1,306 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CryptoPad</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="my.css" rel="stylesheet" />
|
||||
|
||||
<script type="text/javascript" src="lib/sjcl.js"></script>
|
||||
<script type="text/javascript" src="lib/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="lib/bootstrap-typeahead.js"></script>
|
||||
<script type="text/javascript" src="lib/bootstrap-modal.js"></script>
|
||||
<script type="text/javascript" src="lib/bootstrap-button.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
var key;
|
||||
var pads;
|
||||
var curSel = null;
|
||||
var curSelName = null;
|
||||
var useRemote = true;
|
||||
var waitingCount = 0;
|
||||
|
||||
function getKVSItem(key, func) {
|
||||
if (useRemote) {
|
||||
showLoader();
|
||||
return $.getJSON("storage/" + key, createGetKVSCallback(func));
|
||||
} else {
|
||||
return func(localStorage.getItem(key));
|
||||
}
|
||||
}
|
||||
|
||||
function createGetKVSCallback(func) {
|
||||
return function (arg) {
|
||||
hideOnCompletion();
|
||||
func(arg);
|
||||
}
|
||||
}
|
||||
|
||||
function showLoader() {
|
||||
if (waitingCount == 0)
|
||||
$("#loader").fadeIn();
|
||||
waitingCount++;
|
||||
}
|
||||
|
||||
function hideOnCompletion() {
|
||||
waitingCount--;
|
||||
if (waitingCount == 0)
|
||||
$("#loader").fadeOut();
|
||||
}
|
||||
|
||||
function deleteKVSItem(key) {
|
||||
if (useRemote) {
|
||||
showLoader();
|
||||
return $.getJSON("storage/delete/" + key, hideOnCompletion);
|
||||
} else {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
function setKVSItem(key, value) {
|
||||
if (useRemote) {
|
||||
showLoader();
|
||||
$.post("storage/" + key, {value: value}, hideOnCompletion);
|
||||
} else {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function hashSomething(something) {
|
||||
var hmackey = sjcl.hash.sha256.hash(key);
|
||||
var hmacr = new sjcl.misc.hmac(hmackey);
|
||||
return sjcl.codec.hex.fromBits(hmacr.encrypt(something));
|
||||
}
|
||||
|
||||
function hashTitle(title) {
|
||||
return hashSomething("pad:"+title)
|
||||
}
|
||||
|
||||
function supportsStorage() {
|
||||
try {
|
||||
return 'localStorage' in window && window['localStorage'] !== null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function navbarHandler(e) {
|
||||
loadNote(e.data);
|
||||
}
|
||||
|
||||
function genPadItem(title) {
|
||||
var titlehash = hashTitle(title);
|
||||
var link = $('<a />').attr("href", "#").click(title, navbarHandler).text(title);
|
||||
var item = $('<li/>').attr("id", titlehash).append(link);
|
||||
return item;
|
||||
}
|
||||
|
||||
function addPadList(title) {
|
||||
$("#padList").append(genPadItem(title));
|
||||
}
|
||||
|
||||
function saveNote() {
|
||||
if ($("#title").val() == "") {
|
||||
$("#title").val(new Date().toString())
|
||||
}
|
||||
var title = $("#title").val();
|
||||
if (($.inArray(title, pads) != -1) && (title != curSelName)) {
|
||||
alert("An item with this title already exists! Cannot save until this is corrected.");
|
||||
return;
|
||||
}
|
||||
var titlehash = hashTitle(title);
|
||||
var crypted = sjcl.encrypt(key, $("#data").val(), {iter: 2000});
|
||||
setKVSItem(titlehash, crypted);
|
||||
if(curSel != null && curSelName != title) {
|
||||
pads[$.inArray(curSelName, pads)] = title;
|
||||
var newSel = genPadItem(title);
|
||||
curSel.replaceWith(newSel);
|
||||
curSel = newSel;
|
||||
curSel.addClass("active");
|
||||
curSelName = title;
|
||||
}
|
||||
if ($.inArray(title, pads) == -1) {
|
||||
pads.push(title);
|
||||
addPadList(title);
|
||||
setActiveTab(titlehash);
|
||||
curSelName = title;
|
||||
}
|
||||
savePads();
|
||||
$("#searchtext").attr("data-source", JSON.stringify(pads));
|
||||
}
|
||||
|
||||
function savePads() {
|
||||
var crypted = sjcl.encrypt(key, JSON.stringify(pads), {iter: 2000});
|
||||
setKVSItem(hashSomething("pads"), crypted);
|
||||
}
|
||||
|
||||
function setActiveTab(titlehash) {
|
||||
if (curSel != null)
|
||||
curSel.removeClass("active");
|
||||
$("#" + titlehash).addClass("active");
|
||||
curSel = $("#" + titlehash);
|
||||
}
|
||||
|
||||
function createNoteCallback(title, titlehash) {
|
||||
return function(notedata) {
|
||||
if (notedata != null) {
|
||||
try {
|
||||
$("#data").val(sjcl.decrypt(key, notedata));
|
||||
$("#title").val(title);
|
||||
$("#searchtext").val("");
|
||||
} catch (err) {
|
||||
alert("Cryptography error: it's likely you entered the wrong passphrase. If you update, it WILL be overwritten.")
|
||||
}
|
||||
setActiveTab(titlehash);
|
||||
curSelName = title;
|
||||
} else {
|
||||
alert("Pad does not exist.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadNote(title) {
|
||||
var titlehash = hashTitle(title);
|
||||
getKVSItem(titlehash, createNoteCallback(title, titlehash));
|
||||
}
|
||||
|
||||
function searchNote() {
|
||||
var title = $("#searchtext").val();
|
||||
loadNote(title);
|
||||
return false;
|
||||
}
|
||||
|
||||
function padsLoaded(paddata) {
|
||||
if (paddata == null)
|
||||
pads = new Array();
|
||||
else {
|
||||
try {
|
||||
pads = JSON.parse(sjcl.decrypt(key, paddata));
|
||||
} catch (err) {
|
||||
alert("Cryptography error: it's likely you entered the wrong passphrase. If you update, it WILL be overwritten.")
|
||||
}
|
||||
}
|
||||
|
||||
for (var pad in pads) {
|
||||
addPadList(pads[pad]);
|
||||
}
|
||||
$("#searchtext").attr("data-source", JSON.stringify(pads));
|
||||
}
|
||||
|
||||
function login() {
|
||||
key = $("#password").val();
|
||||
getKVSItem(hashSomething("pads"),padsLoaded);
|
||||
$('#myModal').modal('hide')
|
||||
return false; // for form.
|
||||
}
|
||||
|
||||
function deleteNote() {
|
||||
var index = $.inArray($("#title").val(), pads);
|
||||
if ((index != -1) && (curSel != null)) {
|
||||
curSel.remove();
|
||||
curSel = null;
|
||||
deleteKVSItem(hashTitle(pads[index]));
|
||||
delete pads[index];
|
||||
savePads();
|
||||
$("#title").val("");
|
||||
$("#data").val("");
|
||||
}
|
||||
}
|
||||
|
||||
function newNote() {
|
||||
if (curSel != null)
|
||||
curSel.removeClass("active");
|
||||
$("#title").val("");
|
||||
$("#data").val("");
|
||||
curSel = null;
|
||||
curSelName = null;
|
||||
}
|
||||
|
||||
function init() {
|
||||
sjcl.random.startCollectors();
|
||||
|
||||
if (!supportsStorage()) {
|
||||
alert("Your browser does not have Local Storage support. Come back when you've upgraded.");
|
||||
}
|
||||
|
||||
$('.tabs').button()
|
||||
$("#remoteButton").button('toggle');
|
||||
|
||||
|
||||
$("#remoteButton").click(function() {
|
||||
useRemote = true;
|
||||
});
|
||||
$("#localButton").click(function() {
|
||||
useRemote = false;
|
||||
});
|
||||
|
||||
$("#myModal").modal({backdrop: "static", keyboard: false}); // undocumented trick to not hide the dialog
|
||||
$("#password").select();
|
||||
$("#submitter").click(saveNote);
|
||||
$("#deleter").click(deleteNote);
|
||||
$("#newer").click(newNote);
|
||||
$("#searchform").submit(searchNote);
|
||||
$("#loginForm").submit(login);
|
||||
$("#loginBtn").click(login);
|
||||
}
|
||||
|
||||
$(document).ready(init);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="modal" id="myModal">
|
||||
|
||||
<div class="modal-header">
|
||||
<h3>Login to CryptoPad</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
Login to:
|
||||
<div class="btn-group" data-toggle="buttons-radio">
|
||||
<button class="btn" id="localButton">Local</button>
|
||||
<button class="btn" id="remoteButton">Server</button>
|
||||
</div> <br />
|
||||
<form id="loginForm">
|
||||
<input name="password" type="password" id="password" placeholder="Password" />
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="loginBtn" class="btn btn-primary">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="brand" href="#">CryptoPad</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<form id="searchform" class="form-search">
|
||||
<input autocomplete="off" id="searchtext" type="text" class="input-medium search-query" placeholder="Search" data-provide="typeahead" />
|
||||
</form>
|
||||
<ul id="padList" class="nav nav-pills nav-stacked">
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span10">
|
||||
<div class="well">
|
||||
<input type="text" id="title" class="input-medium" placeholder="Title" />
|
||||
<textarea id="data" rows="10"></textarea><br />
|
||||
<button id="submitter" class="btn btn-primary">Save</button>
|
||||
<button id="newer" class="btn btn-success">New</button>
|
||||
<span id="loader" class="middle"><img alt="Working..." class="middle" src="loading.gif"></span>
|
||||
<button id="deleter" class="btn btn-danger">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user