diff --git a/app.js b/app.js new file mode 100644 index 0000000..b20b325 --- /dev/null +++ b/app.js @@ -0,0 +1,209 @@ + +const algo = { name: "AES-GCM", iv: Uint8Array.from([120,1,248,135,62,71,87,156,92,67,155,37]) } +, parseAlgo = str=> ({ name: str.split(' ')[0], iv: Uint8Array.from(str.split(' ').slice(1)) }) +, algos = [ "AES-GCM 120 1 248 135 62 71 87 156 92 67 155 37", 'AES-CBC' ] +//, algo = parseAlgo(algos[0]) +, $pass = $`pass input` +, $text = $`ui-data` +, $filename = $`[filename]` + +opener && document.documentElement.classtList.add('parented') +// sel=opener.document.getSelection().type=='None'||'Range' + +const getParentSelection = ()=> opener && opener.document.getSelection().toString() +const setParentSelection = str=> opener && opener.document.activeElement.setRangeText( str ) + +let key +const ivLen = 16 // the IV is always 16 bytes + +$pass.onchange = async e=> + key = await crypto.subtle.importKey( + 'raw' + , await crypto.subtle.digest( 'SHA-256', new TextEncoder().encode($pass.value) ) + , algo, false, ['encrypt','decrypt'] + ) + + +const joinIvAndData = (iv, data)=> { + var buf = new Uint8Array(iv.length + data.length) + Array.prototype.forEach.call(iv, (byte, i)=> buf[i] = byte ) + Array.prototype.forEach.call(data, (byte, i)=> buf[ivLen + i] = byte ) + return buf +} + +const separateIvFromData = buf=> Array.prototype.reduce.call( new Uint8Array(buf), + ( res, byte, i )=> + i < ivLen + ? ( res.iv[i] = byte, res ) + : ( res.data[i - ivLen] = byte, res ) +, { iv: new Uint8Array(ivLen), data: new Uint8Array(buf.byteLength - ivLen) } ) + + +const encrypt = ()=> { + return crypto.subtle.digest( 'SHA-256', new TextEncoder().encode($pass.value) ) + .then( pwHash=> crypto.subtle.importKey('raw', pwHash, algo, false, ['encrypt']) ) + .then( key=> crypto.subtle.encrypt(algo, key, new TextEncoder().encode($text.value)) ) + //.then( ctBuffer=> $text.value = new Uint8Array(ctBuffer).toString().replace(/,/g,'O') ) + .then( ctBuffer=> $text.value = uint2String( new Uint8Array(ctBuffer) ) ) + //.then( ctBuffer=> $text.value = new TextDecoder().decode(new Uint8Array(ctBuffer)) ) + + //.catch( e=> pulse('red') ) +} +const encrypt2 = str=> + crypto.subtle.encrypt( algo, key, new TextEncoder().encode(str) ) + //.then( ctBuffer=> $text.value = new Uint8Array(ctBuffer).toString().replace(/,/g,'O') ) + // .then( ctBuffer=> uint2String(new Uint8Array(ctBuffer)) ) + .then( ctBuffer=> $text.value = new TextDecoder().decode(new Uint8Array(ctBuffer)) ) + + //.catch( e=> pulse('red') ) + +const encrypt3 = ( data, iv = crypto.getRandomValues(new Uint8Array(ivLen)) )=> + crypto.subtle.encrypt( {name: 'AES-GCM', iv}, key, data ) + // .then( encrypted=> bufferToBinaryString( joinIvAndData(iv, new Uint8Array(encrypted)).buffer ) ) + .then( encrypted=> joinIvAndData(iv, new Uint8Array(encrypted)).buffer ) + // var base64 = Unibabel.bufferToBase64(ciphered) + // .replace(/\-/g, '+') + // .replace(/_/g, '\/') + // ; + + // while (base64.length % 4) { + // base64 += '='; + // } + // return base64; + // }) + + +const decrypt = ()=> { + return crypto.subtle.digest( 'SHA-256', new TextEncoder().encode($pass.value) ) + .then( pwHash=> crypto.subtle.importKey('raw', pwHash, algo, false, ['decrypt']) ) + //.then( key=> crypto.subtle.decrypt(algo, key, Uint8Array.from($text.value.split('O').map(Number)).buffer ) ) + .then( key=> crypto.subtle.decrypt(algo, key, string2Uint($text.value).buffer ) ) + //.then( key=> crypto.subtle.decrypt(algo, key, new TextEncoder().encode($text.value).buffer ) ) + .then( ptBuffer=> $text.value = new TextDecoder().decode(ptBuffer) ) + + //.catch( e=> pulse('red') ) +} + +const decrypt3 = buf=> { + let { iv, data } = separateIvFromData( buf ) + + return crypto.subtle.decrypt( + { name: 'AES-GCM', iv } + , key + , data + )/*.then(function (decrypted) { + var base64 = bufferToUtf8(new Uint8Array(decrypted)) + // .replace(/\-/g, '+') + // .replace(/_/g, '\/') + // ; + + // while (base64.length % 4) { + // base64 += '='; + // } + return base64 + })*/ +} + +var lastSaved +const save = ( content, filename )=> { + let link = document.createElement('a') + link.setAttribute( 'download', filename ) + // If we are replacing a previously generated file we need to + // manually revoke the object URL to avoid memory leaks. + lastSaved && URL.revokeObjectURL( lastSaved ) + link.href = lastSaved = URL.createObjectURL( new Blob([content], {type: 'text/plain'}) ) + //link.href = makeTextFile( content ) + document.body.appendChild( link ) + + // wait for the link to be added to the document + window.requestAnimationFrame( e=> { + link.dispatchEvent( new MouseEvent('click') ) + document.body.removeChild( link ) + }) +} + +const read = file=> new Promise( (ok,ko)=> { + let r = new FileReader() + r.onload = e=> ok( r.result ) + r.onerror = e=> ko() + r.readAsText( file ) +}) + + +var textFile +const makeTextFile = text=> { + + textFile && URL.revokeObjectURL(textFile) + textFile = URL.createObjectURL( new Blob([text], {type: 'text/plain'}) ) + + // returns a URL you can use as a href + return textFile +} + + +const save2 = (file, content, type)=> window.open( "data:application/octet-stream," + encodeURIComponent(content), file ) + +//// conversion //// + +const uint2string = uintArray=> String.fromCharCode.apply( null, uintArray ) + +const string2uint = string=> new Uint8Array( + btoa( string ).split('') + .map( l=>l.charCodeAt(0) ) +) + +const buffer2bin = buf=> Array.prototype.map.call( new Uint8Array(buf), ch=> String.fromCharCode(ch) ).join('') + +const buffer2base64 = arr=> btoa( buffer2bin(arr) ) + +const bin2buffer = binstr=> new Uint8Array( + Array.prototype.map.call( new Uint8Array( binstr.length ), (ch, i)=> binstr.charCodeAt(i) ) +) + +const base642buffer = base64=> bin2buffer( atob(base64) ) + +const bin2utf8 = binstr=> decodeURIComponent( + binstr.replace(/./g, c=> + `%${c.charCodeAt(0) < 16 ? '0' : ''}${c.charCodeAt(0).toString(16).toUpperCase()}` + ) +) + +const buffer2utf8 = buf=> bin2utf8( buffer2bin(buf) ) + +const utf82bin = str=> encodeURIComponent(str) + // replaces any uri escape sequence, such as %0A, + // with binary escape, such as 0x0A + .replace(/%([0-9A-F]{2})/g, (s,$1)=> String.fromCharCode(parseInt($1, 16)) ) + +const utf82buffer = str=> bin2buffer(utf82bin(str)) + + +const CC = { + uint2string +, string2uint +, buffer2bin +, buffer2base64 +, buffer2utf8 +, bin2buffer +, base642buffer +, bin2utf8 +, utf82bin +, utf82buffer +} + +/*const uintToString = (uintArray)=> { + var encodedString = String.fromCharCode.apply(null, uintArray), + decodedString = decodeURIComponent(escape(encodedString)); + return decodedString; +} +function stringToUint(string) { + var string = btoa(unescape(encodeURIComponent(string))), + charList = string.split(''), + uintArray = []; + for (var i = 0; i < charList.length; i++) { + uintArray.push(charList[i].charCodeAt(0)); + } + return new Uint8Array(uintArray); +} + +*/ diff --git a/konstrui.js b/konstrui.js new file mode 100644 index 0000000..c80a2fe --- /dev/null +++ b/konstrui.js @@ -0,0 +1,20 @@ +// node konstrui provo.html spa +// node konstrui app.html kriptopter.html + +const { writeFileSync, readFileSync } = require('fs') +, { resolve } = require('path') +, io = process.argv.slice(2) +, src = /src="(.*?)"/ +, href = /href="(.*?)"/ +, type = /type="(.*?)"/ + +writeFileSync( + resolve(__dirname, io[1]) +, readFileSync( resolve(__dirname, io[0]), 'utf8') + .replace(/` + : s + ) +, 'utf8' +) diff --git a/lang.js b/lang.js new file mode 100644 index 0000000..46b51b1 --- /dev/null +++ b/lang.js @@ -0,0 +1,50 @@ +/* + +*/ + +// lang support +Object.defineProperty( window, 'lang', { + get: ()=> $`:root`.getAttribute('lang') +, set: v=> $`:root`.setAttribute( 'lang', v ) +}) + +new MutationObserver( changes=> + changes.map( ch=> ch.attributeName == 'lang' && $`:root`.dispatchEvent(new Event('lang')) ) +) +.observe( $`:root`, { attributes: true } ) + +const lgAttReg = new RegExp( '^('+$`style[lang]`.attributes.lang.value.split(',').join('|')+'):') +$`:root`.on`lang`( e=> + $`*`.map( node=> [...node.attributes] + .filter( att=> lgAttReg.test(att.name) ) + ) + .filter( arr=> arr.length ) + .map( atts=> atts.filter(att=>new RegExp('^'+lang+':').test(att.name)) + .map(att=> att.ownerElement.setAttribute(att.name.split(':').pop(),att.value) )) +) +// compile lang styles +Q`style[lang]`.map( style=> + style.innerHTML = [...style.sheet.rules] + .map( r=> style.attributes.lang.value.split(',') + .map( lg=> r.cssText.replace(/_lang_/g, lg) ) + .join('\n') + ) + .join('\n') +) +lang = navigator.language diff --git a/provo.html b/provo.html new file mode 100644 index 0000000..5c4cc0a --- /dev/null +++ b/provo.html @@ -0,0 +1,7 @@ +