diff --git a/app.html b/app.html index 70be537..46ad8dd 100644 --- a/app.html +++ b/app.html @@ -2,231 +2,122 @@ Cryptopter Cryptoptère - - - - - + + + + - - - - + + + + - + - - + + diff --git a/app.js b/app.js index b20b325..31b6f47 100644 --- a/app.js +++ b/app.js @@ -1,11 +1,11 @@ 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]` +, 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' @@ -17,127 +17,128 @@ 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'] - ) + 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 + 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) } ) + ( 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') ) + 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') ) + 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, '\/') - // ; + 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; - // }) + // 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') ) + 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 ) + 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, '\/') - // ; + 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 + // 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 ) + 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 ) - }) + // 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 ) + 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'}) ) + + textFile && URL.revokeObjectURL(textFile) + textFile = URL.createObjectURL( new Blob([text], {type: 'text/plain'}) ) - // returns a URL you can use as a href - return textFile + // returns a URL you can use as a href + return textFile } @@ -148,8 +149,8 @@ const save2 = (file, content, type)=> window.open( "data:application/octet-strea const uint2string = uintArray=> String.fromCharCode.apply( null, uintArray ) const string2uint = string=> new Uint8Array( - btoa( string ).split('') - .map( l=>l.charCodeAt(0) ) + btoa( string ).split('') + .map( l=>l.charCodeAt(0) ) ) const buffer2bin = buf=> Array.prototype.map.call( new Uint8Array(buf), ch=> String.fromCharCode(ch) ).join('') @@ -157,7 +158,7 @@ const buffer2bin = buf=> Array.prototype.map.call( new Uint8Array(buf), ch=> Str const buffer2base64 = arr=> btoa( buffer2bin(arr) ) const bin2buffer = binstr=> new Uint8Array( - Array.prototype.map.call( new Uint8Array( binstr.length ), (ch, i)=> binstr.charCodeAt(i) ) + Array.prototype.map.call( new Uint8Array( binstr.length ), (ch, i)=> binstr.charCodeAt(i) ) ) const base642buffer = base64=> bin2buffer( atob(base64) ) @@ -179,31 +180,32 @@ const utf82buffer = str=> bin2buffer(utf82bin(str)) const CC = { - uint2string -, string2uint -, buffer2bin -, buffer2base64 -, buffer2utf8 -, bin2buffer -, base642buffer -, bin2utf8 -, utf82bin -, utf82buffer + uint2string +, string2uint +, buffer2bin +, buffer2uint: buf=> new Uint8Array(buf) +, buffer2utf8 +, buffer2base64 +, bin2buffer +, base642buffer +, bin2utf8 +, utf82bin +, utf82buffer } /*const uintToString = (uintArray)=> { - var encodedString = String.fromCharCode.apply(null, uintArray), - decodedString = decodeURIComponent(escape(encodedString)); - return decodedString; + 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); + 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 index c80a2fe..8a31344 100644 --- a/konstrui.js +++ b/konstrui.js @@ -16,5 +16,10 @@ writeFileSync( ? `` : s ) + .replace(//g + , (s,$1)=> $1.match(href) && !/^https?:\/\//.test( $1.match(href)[1] ) + ? `` + : s + ) , 'utf8' ) diff --git a/lang.js b/lang.js index 46b51b1..a849a3f 100644 --- a/lang.js +++ b/lang.js @@ -1,50 +1,50 @@ /* */ // lang support Object.defineProperty( window, 'lang', { - get: ()=> $`:root`.getAttribute('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')) ) + 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) )) + $`*`.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') + 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/ui.js b/ui.js index ecf69ae..919fc37 100644 --- a/ui.js +++ b/ui.js @@ -1,96 +1,97 @@ const _s = (ss, ...pp)=> ss.map((s,i)=> s + (pp[i]||'')).join('') -, _a = iterable=> iterable.length > 1 ? iterable : iterable[0] -, Q = (ss, ...pp)=> [...document.querySelectorAll( _s(ss, ...pp))] -, T = document.createElement('template') -, DOM = (ss, ...pp)=> ( T.innerHTML = _s(ss, ...pp), document.adoptNode(T.content).childNodes ) -, $ = (ss, ...pp)=> _a( ~_s(ss, ...pp).indexOf('<') ? DOM(ss, ...pp) : Q(ss, ...pp) ) +, _a = iterable=> iterable.length > 1 ? iterable : iterable[0] +, Q = (ss, ...pp)=> [...document.querySelectorAll( _s(ss, ...pp))] +, T = document.createElement('template') +, DOM = (ss, ...pp)=> ( T.innerHTML = _s(ss, ...pp), document.adoptNode(T.content).childNodes ) +, $ = (ss, ...pp)=> _a( ~_s(ss, ...pp).indexOf('<') ? DOM(ss, ...pp) : Q(ss, ...pp) ) -, notify = (html,attrs)=> $`body`.add( $`${html}` ) -, bubbleOn = el=> e=> el.dispatchEvent( new ErrorEvent('Error',{bubbles:true, error:e}) ) -, pulse = color=> (document.body.style.background = color) && setTimeout(o=> document.body.style.background = 'lightgrey', 1000) +, notify = (html,attrs)=> $`body`.add( $`${html}` ) +, bubbleOn = el=> e=> el.dispatchEvent( new ErrorEvent('Error',{bubbles:true, error:e}) ) +, pulse = color=> (document.body.style.background = color) && setTimeout(o=> document.body.style.background = 'lightgrey', 1000) Node.prototype.add = function( ...els ) { - this.append( ...els ) - return _a( els ) + this.append( ...els ) + return _a( els ) } Node.prototype.$ = function(ss, ...pp) { - return _a( - ~_s(ss, ...pp).indexOf('<') - ? this.add(...DOM(ss, ...pp)) - : [...this.querySelectorAll( _s(ss, ...pp))] - ) + return _a( + ~_s(ss, ...pp).indexOf('<') + ? this.add(...DOM(ss, ...pp)) + : [...this.querySelectorAll( _s(ss, ...pp))] + ) } Node.prototype.on = function(ss, ...pp) { - let types = _s( [].concat(ss), ...pp ).split(/[,\s]/) - return (...args)=> ( types.map( type=> this.addEventListener(type,...args)), this ) + let types = _s( [].concat(ss), ...pp ).split(/[,\s]/) + return (...args)=> ( types.map( type=> this.addEventListener(type,...args)), this ) } class UI extends HTMLElement { - static get is(){ return 'ui' + this.name.replace(/[A-Z]/g, L=> '-'+L.toLocaleLowerCase()) } - constructor() - { - super() - let tpl = $`template[${this.constructor.is}]` - tpl && this.attachShadow({mode: 'open'}) - .appendChild( tpl.content.cloneNode(true) ) - } + static get is(){ return 'ui' + this.name.replace(/[A-Z]/g, L=> '-'+L.toLocaleLowerCase()) } + constructor() + { + super() + let tpl = $`template[${this.constructor.is}]` + tpl && this.attachShadow({mode: 'open'}) + .appendChild( tpl.content.cloneNode(true) ) + } } class Toast extends UI { - static get duration(){ return this._duration || 10000 } - static set duration( v ){ this._duration = v } - connectedCallback() - { - setTimeout( toast=> toast.remove(), this.getAttribute('duration') || Toast.duration, this ) - } + static get duration(){ return this._duration || 10000 } + static set duration( v ){ this._duration = v } + connectedCallback() + { + setTimeout( toast=> toast.remove(), this.getAttribute('duration') || Toast.duration, this ) + } } class Data extends UI { - static get observedAttributes(){ return ['bin','uint','utf8','base64','frame'] } - connectedCallback() - { - // this.observer = new MutationObserver( - // changes=> console.log(changes.filter(m=>~['bin','255','utf8','base64','object'].indexOf(m.attributeName)) ) - // ) - // .observe( this, { attributes: true } ) - this.shadowRoot.append( - ...Data.observedAttributes - .map( type=> $`` - .on`click`( e=> this.type = e.target.innerText ) - ) - ) - // this.shadowRoot.$`button`.map( button=> button.onclick = e=> this.type = e.target.innerText ) - this.shadowRoot.$`textarea`.on`change`( e=> this._value = CC[this.type+'2buffer'](e.target.value) ) - } + static get observedAttributes(){ return ['bin','uint','utf8','base64','frame'] } + connectedCallback() + { + // this.observer = new MutationObserver( + // changes=> console.log(changes.filter(m=>~['bin','255','utf8','base64','object'].indexOf(m.attributeName)) ) + // ) + // .observe( this, { attributes: true } ) + this.shadowRoot.append( + ...Data.observedAttributes + .map( type=> $`` + .on`click`( e=> this.type = e.target.innerText ) + ) + ) + // this.shadowRoot.$`button`.map( button=> button.onclick = e=> this.type = e.target.innerText ) + this.shadowRoot.$`textarea`.on`change`( e=> this._value = CC[this.type+'2buffer'](e.target.value) ) + } - get type(){ return this.attributes[0].name } - set type( v ){ - if( this.type == v ) return; - [...this.attributes].map( o=> o.ownerElement.removeAttributeNode(o) ) - this.setAttribute( v, '' ) - } + get type(){ return this.attributes[0].name } + set type( v ){ + if( this.type == v ) return; + [...this.attributes].map( o=> o.ownerElement.removeAttributeNode(o) ) + this.setAttribute( v, '' ) + } - get value(){ return this._value } - set value( v ){ this._value = v; this.shadowRoot.$`textarea`.value = CC['buffer2'+this.type](v) } + get value(){ return this._value } + set value( v ){ this._value = v; this.shadowRoot.$`textarea`.value = CC['buffer2'+this.type](v) } - attributeChangedCallback(name, oldValue, newValue) - { - if( this.shadowRoot.$`textarea`.value ) - this.shadowRoot.$`textarea`.value = CC['buffer2'+this.type]( this._value ) - } + attributeChangedCallback(name, oldValue, newValue) + { + console.log(arguments) + if( newValue != null && this._value && CC['buffer2'+this.type] ) + this.shadowRoot.$`textarea`.value = CC['buffer2'+this.type]( this._value ) + } } ;[ Toast, Data ] - .map( klass=> customElements.define(klass.is, klass) ) + .map( klass=> customElements.define(klass.is, klass) ) // document.body.addEventListener('Error', e=> pulse('red') && (notify(e.error.message).style.color = 'red') ) $`body`.on`Error`( e=> - pulse('red') && notify( - `${e.error.code||''} ${e.error.name}: ${e.error.message}` - , 'error') + pulse('red') && notify( + `${e.error.code||''} ${e.error.name}: ${e.error.message}` + , 'error') )