diff --git a/bloom/bloom.test.ts b/bloom/bloom.test.ts new file mode 100644 index 00000000000..3c98b6b352a --- /dev/null +++ b/bloom/bloom.test.ts @@ -0,0 +1,30 @@ +namespace $ { + $mol_test({ + + 'Add and check strings'() { + + const bloom = new $mol_bloom( 5 ) + $mol_assert_equal( bloom.has_str( 'Hello' ), 0 ) + $mol_assert_equal( bloom.has_str( 'World' ), 0 ) + $mol_assert_equal( bloom.has_str( 'Hello World' ), 0 ) + $mol_assert_equal( bloom.has_str( 'hello' ), 0 ) + $mol_assert_equal( bloom.has_str( 'world' ), 0 ) + + bloom.add_str( 'Hello World' ) + $mol_assert_equal( bloom.has_str( 'Hello' ), 0 ) + $mol_assert_equal( bloom.has_str( 'World' ), 0 ) + $mol_assert_equal( bloom.has_str( 'Hello World' ), 1 ) + $mol_assert_equal( bloom.has_str( 'hello' ), 0 ) + $mol_assert_equal( bloom.has_str( 'world' ), 0 ) + + bloom.add_str( 'Hello' ) + bloom.add_str( 'World' ) + $mol_assert_equal( bloom.has_str( 'Hello' ), 1 ) + $mol_assert_equal( bloom.has_str( 'World' ), 1 ) + $mol_assert_equal( bloom.has_str( 'hello' ), 0 ) + $mol_assert_equal( bloom.has_str( 'world' ), 0 ) + + }, + + }) +} diff --git a/bloom/bloom.ts b/bloom/bloom.ts new file mode 100644 index 00000000000..0b798639c1c --- /dev/null +++ b/bloom/bloom.ts @@ -0,0 +1,74 @@ +namespace $ { + + /** + * Bloom filter with automatic optimal parameters. + * False negative is impossible. + * False positive is controlled. + */ + export class $mol_bloom extends Object { + + bitmap: Uint32Array + hashes: number + + constructor( + /** Max count of stored values. */ + count: number, + /** Chance of false positive. 1e-6 by default */ + public risk = 1e-6, + ) { + super() + this.hashes = Math.ceil( -1.44 * Math.log( risk ) ) + + const length = Math.ceil( -.065 * count * Math.log( risk ) ) + this.bitmap = new Uint32Array( 2 ** Math.ceil( Math.log2( length ) ) ) + } + + add_str( word: string ) { + this.add_bin( $mol_charset_encode( word ) ) + } + + has_str( word: string ) { + return this.has_bin( $mol_charset_encode( word ) ) + } + + add_bin( bin: Uint8Array ) { + for( const index of this.hash( bin ) ) { + this.add_bit( index ) + } + } + + has_bin( bin: Uint8Array ) { + let res = 1 + for( const index of this.hash( bin ) ) { + res &= this.has_bit( index ) + } + return res + } + + hash( data: Uint8Array ) { + const res = [] as number[] + fill: while( true ) { + data = $mol_crypto_hash( data ) + for( const index of new Uint32Array( data.buffer ) ) { + res.push( index ) + if( res.length >= this.hashes ) break fill + } + } + return res + } + + add_bit( index: number ) { + const int = Math.ceil( index / 32 ) % this.bitmap.length + const bit = index & 0b11111 + this.bitmap[ int ] |= 1 << bit + } + + has_bit( index: number ) { + const int = Math.ceil( index / 32 ) % this.bitmap.length + const bit = index & 0b11111 + return 1&( this.bitmap[ int ] >> bit ) + } + + } + +} diff --git a/book2/book2.view.ts b/book2/book2.view.ts index 3d63a3b8e5c..592a269bfa7 100644 --- a/book2/book2.view.ts +++ b/book2/book2.view.ts @@ -45,7 +45,7 @@ namespace $.$$ { left: p.offsetLeft + p.offsetWidth - b.offsetWidth, behavior: 'smooth', }) - new this.$.$mol_after_timeout( 1000, ()=> n.bring() ) + // new this.$.$mol_after_timeout( 1000, ()=> n.bring() ) } ) break diff --git a/crypto/hash/hash.ts b/crypto/hash/hash.ts index 36a64dda7ca..9b43b394d16 100644 --- a/crypto/hash/hash.ts +++ b/crypto/hash/hash.ts @@ -2,7 +2,7 @@ namespace $ { let sponge = new Uint32Array(80) - /** Fast small sync SHA-1 */ + /** Fast small sync SHA-1 (20 bytes, 160 bits) */ export function $mol_crypto_hash( data: Uint8Array ) { const bits = data.byteLength << 3 diff --git a/dom/capture/demo/demo.meta.tree b/dom/capture/demo/demo.meta.tree new file mode 100644 index 00000000000..b69d298d3cc --- /dev/null +++ b/dom/capture/demo/demo.meta.tree @@ -0,0 +1 @@ +deploy \/mol/logo/logo.svg diff --git a/dom/dom.ts b/dom/dom.ts new file mode 100644 index 00000000000..2a29b3223aa --- /dev/null +++ b/dom/dom.ts @@ -0,0 +1,5 @@ +namespace $ { + + export var $mol_dom = $mol_dom_context + +} diff --git a/dom/point/point.ts b/dom/point/point.ts new file mode 100644 index 00000000000..4cb5ad32610 --- /dev/null +++ b/dom/point/point.ts @@ -0,0 +1,106 @@ +namespace $ { + + export class $mol_dom_point extends Object { + + constructor( + public node: Node, + public pos: number, + ) { super() } + + static start( node: Node ) { + return new this( node, 0 ) + } + + static end( node: Node ) { + const length = node.nodeValue?.length ?? node.childNodes.length + return new this( node, length ) + } + + to( point: $mol_dom_point ) { + this.node = point.node + this.pos = point.pos + } + + to_start() { + this.pos = 0 + return this + } + + to_end() { + this.pos = this.node.nodeValue?.length ?? this.node.childNodes.length + return this + } + + is_start() { + return this.pos <= 0 + } + + is_end() { + return this.pos >= ( this.node.nodeValue?.length ?? this.node.childNodes.length ) + } + + char_backward( root: Element ): $mol_dom_point { + return this.backward( ()=> { + if( this.node === root && this.is_start() ) return true + if( this.node.nodeType !== this.node.TEXT_NODE ) return false + if( this.is_start() ) return false + this.pos -= 1 + return true + } ) + } + + char_forward( root: Element ): $mol_dom_point { + return this.forward( ()=> { + if( this.node === root && this.is_end() ) return true + if( this.node.nodeType !== this.node.TEXT_NODE ) return false + if( this.is_end() ) return false + this.pos += 1 + return true + } ) + } + + backward( check: ()=> boolean ): $mol_dom_point { + + if( check() ) return this + + if( !this.is_start() ) { + const kid = this.node.childNodes[ this.pos - 1 ] + this.node = kid + this.to_end() + return this.backward( check ) + } + + const parent = this.node.parentElement + if( !parent ) return this + + const offset = [ ... parent.childNodes ].indexOf( this.node as ChildNode ) + this.node = parent + this.pos = offset + return this.backward( check ) + + } + + forward( check: ()=> boolean ): $mol_dom_point { + + if( check() ) return this + + if( !this.is_end() ) { + const kid = this.node.childNodes[ this.pos ] + this.node = kid + this.to_start() + return this.forward( check ) + } + + const parent = this.node.parentElement + if( !parent ) return this + + const offset = [ ... parent.childNodes ].indexOf( this.node as ChildNode ) + 1 + this.node = parent + this.pos = offset + return this.forward( check ) + + } + + } + +} diff --git a/dom/range/range.ts b/dom/range/range.ts new file mode 100644 index 00000000000..e4a2ff80810 --- /dev/null +++ b/dom/range/range.ts @@ -0,0 +1,61 @@ +namespace $ { + export class $mol_dom_range extends Object { + + constructor( + readonly head: $mol_dom_point, + readonly foot: $mol_dom_point, + ) { super() } + + static from_selection( sel = $mol_dom_context.getSelection()! ) { + return this.from_native( sel?.getRangeAt(0) ) + } + + static from_native( range: Range ) { + return new this( + new $mol_dom_point( range.startContainer, range.startOffset ), + new $mol_dom_point( range.endContainer, range.endOffset ), + ) + } + + static inside( node: Node ) { + return new this( + $mol_dom_point.start( node ), + $mol_dom_point.end( node ), + ) + } + + static around( node: Node ) { + + const parent = node.parentNode! + const pos = [ ... parent.childNodes ].indexOf( node as ChildNode ) + + return new this( + new $mol_dom_point( parent, pos ), + new $mol_dom_point( parent, pos + 1 ), + ) + + } + + is_empty() { + return this.head.node === this.foot.node && this.head.pos === this.foot.pos + } + + clear() { + this.native().deleteContents() + } + + select() { + const sel = $mol_dom_context.document.getSelection()! + sel.removeAllRanges() + sel.addRange( this.native() ) + } + + native() { + const range = $mol_dom_context.document.createRange() + range.setEnd( this.foot.node, this.foot.pos ) + range.setStart( this.head.node, this.head.pos ) + return range + } + + } +} diff --git a/html/decode/decode.ts b/html/decode/decode.ts index 42b5fa40cae..227bc8f0072 100644 --- a/html/decode/decode.ts +++ b/html/decode/decode.ts @@ -3,7 +3,7 @@ namespace $ { export function $mol_html_decode( text : string ) { return text - .replace( /&(?:#(\d+)|(lt|gt|quot));/gi , ( str , numb , name )=> { + .replace( /&(?:#(\d+)|(lt|gt|quot|amp));/gi , ( str , numb , name )=> { if( numb ) return String.fromCharCode( numb ) const mapping = { diff --git a/lang/iso639/iso639.ts b/lang/iso639/iso639.ts new file mode 100644 index 00000000000..2a9d5985613 --- /dev/null +++ b/lang/iso639/iso639.ts @@ -0,0 +1,187 @@ +namespace $ { + export const $mol_lang_iso639 = { + ab: "Abkhazian", + aa: "Afar", + af: "Afrikaans", + ak: "Akan", + sq: "Albanian", + am: "Amharic", + ar: "Arabic", + an: "Aragonese", + hy: "Armenian", + as: "Assamese", + av: "Avaric", + ae: "Avestan", + ay: "Aymara", + az: "Azerbaijani", + bm: "Bambara", + ba: "Bashkir", + eu: "Basque", + be: "Belarusian", + bn: "Bengali", + bi: "Bislama", + bs: "Bosnian", + br: "Breton", + bg: "Bulgarian", + my: "Burmese", + ca: "Catalan", + ch: "Chamorro", + ce: "Chechen", + ny: "Nyanja", + zh: "Chinese", + cu: "Church Slavonic", + cv: "Chuvash", + kw: "Cornish", + co: "Corsican", + cr: "Cree", + hr: "Croatian", + cs: "Czech", + da: "Danish", + dv: "Divehi", + nl: "Dutch", + dz: "Dzongkha", + en: "English", + eo: "Esperanto", + et: "Estonian", + ee: "Ewe", + fo: "Faroese", + fj: "Fijian", + fi: "Finnish", + fr: "French", + fy: "Western Frisian", + ff: "Fulah", + gd: "Gaelic", + gl: "Galician", + lg: "Ganda", + ka: "Georgian", + de: "German", + el: "Greek", + kl: "Kalaallisut", + gn: "Guarani", + gu: "Gujarati", + ht: "Haitian", + ha: "Hausa", + he: "Hebrew", + hz: "Herero", + hi: "Hindi", + ho: "Hiri Motu", + hu: "Hungarian", + is: "Icelandic", + io: "Ido", + ig: "Igbo", + id: "Indonesian", + ia: "InterlinguA", + ie: "InterlinguE", + iu: "Inuktitut", + ik: "Inupiaq", + ga: "Irish", + it: "Italian", + ja: "Japanese", + jv: "Javanese", + kn: "Kannada", + kr: "Kanuri", + ks: "Kashmiri", + kk: "Kazakh", + km: "Central Khmer", + ki: "Kikuyu", + rw: "Kinyarwanda", + ky: "Kyrgyz", + kv: "Komi", + kg: "Kongo", + ko: "Korean", + kj: "Kwanyama", + ku: "Kurdish", + lo: "Lao", + la: "Latin", + lv: "Latvian", + li: "Limburgan", + ln: "Lingala", + lt: "Lithuanian", + lu: "Luba-Katanga", + lb: "Luxembourgish", + mk: "Macedonian", + mg: "Malagasy", + ms: "Malay", + ml: "Malayalam", + mt: "Maltese", + gv: "Manx", + mi: "Maori", + mr: "Marathi", + mh: "Marshallese", + mn: "Mongolian", + na: "Nauru", + nv: "Navaho", + nd: "North Ndebele", + nr: "South Ndebele", + ng: "Ndonga", + ne: "Nepali", + no: "Norwegian", + nb: "Norwegian Bokmål", + nn: "Norwegian Nynorsk", + ii: "Sichuan Yi", + oc: "Occitan", + oj: "Ojibwa", + or: "Oriya", + om: "Oromo", + os: "Ossetian", + pi: "Pali", + ps: "Pushto", + fa: "Persian", + pl: "Polish", + pt: "Portuguese", + pa: "Panjabi", + qu: "Quechua", + ro: "Romanian", + rm: "Romansh", + rn: "Rundi", + ru: "Russian", + se: "Northern Sami", + sm: "Samoan", + sg: "Sango", + sa: "Sanskrit", + sc: "Sardinian", + sr: "Serbian", + sn: "Shona", + sd: "Sindhi", + si: "Sinhala", + sk: "Slovak", + sl: "Slovenian", + so: "Somali", + st: "Southern Sotho", + es: "Spanish", + su: "Sundanese", + sw: "Swahili", + ss: "Swati", + sv: "Swedish", + tl: "Tagalog", + ty: "Tahitian", + tg: "Tajik", + ta: "Tamil", + tt: "Tatar", + te: "Telugu", + th: "Thai", + bo: "Tibetan", + ti: "Tigrinya", + to: "Tonga", + ts: "Tsonga", + tn: "Tswana", + tr: "Turkish", + tk: "Turkmen", + tw: "Twi", + ug: "Uyghur", + uk: "Ukrainian", + ur: "Urdu", + uz: "Uzbek", + ve: "Venda", + vi: "Vietnamese", + vo: "Volapük", + wa: "Walloon", + cy: "Welsh", + wo: "Wolof", + xh: "Xhosa", + yi: "Yiddish", + yo: "Yoruba", + za: "Zhuang", + zu: "Zulu", + } +} diff --git a/page/page.view.css.ts b/page/page.view.css.ts index 14ee3a20285..3043a5661a8 100644 --- a/page/page.view.css.ts +++ b/page/page.view.css.ts @@ -16,7 +16,7 @@ namespace $.$$ { maxHeight: per(100) , boxSizing: 'border-box' , color: $mol_theme.text , - backdropFilter: blur( `3px` ), + // backdropFilter: blur( `3px` ), enforces layering // zIndex: 0 , ':focus': { diff --git a/source/http/http.ts b/source/http/http.ts new file mode 100644 index 00000000000..6835cf2201b --- /dev/null +++ b/source/http/http.ts @@ -0,0 +1,28 @@ +namespace $ { + export class $mol_source_http_json extends $mol_source { + + query() { + return {} + } + + base() { + return '' + } + + @ $mol_mem + uri() { + let url = this.base() + + const search = '' + new URLSearchParams( this.query() ) + if( search ) url += ( url.includes( '?' ) ? '&' : '?' ) + search + + return url + } + + @ $mol_mem + output() { + return this.$.$mol_fetch.json( this.uri() ) + } + + } +} diff --git a/source/source.ts b/source/source.ts new file mode 100644 index 00000000000..c59874bfe5a --- /dev/null +++ b/source/source.ts @@ -0,0 +1,9 @@ +namespace $ { + export class $mol_source extends $mol_object2 { + + output() { + return null as any + } + + } +} diff --git a/view/view/view.ts b/view/view/view.ts index 173ac278abb..48929ec9539 100644 --- a/view/view/view.ts +++ b/view/view/view.ts @@ -490,9 +490,9 @@ namespace $ { // this.dom_node().scrollIntoView({ block: 'start', inline: 'nearest' }) // } ) - // new this.$.$mol_after_timeout( 1000, ()=> { - // this.focused( true ) - // } ) + new this.$.$mol_after_timeout( 0, ()=> { + this.focused( true ) + } ) }