Browse Source
- use dexie instead of idb_wrapper.imba - use dexie-export-import instead of upload.imba - use downloadjs instead of download.imba - use fzi instead of bundling fzy.imba - rename link.link to link.url in database v2 - change default links - use config.search_engine object for search engine options - allow multiple of same name - show date in main-input instead of fuzzyhome version - print fuzzyhome version in consolemain
familyfriendlymikey
2 years ago
9 changed files with 2965 additions and 458 deletions
@ -1,5 +0,0 @@ |
|||||
let state = {} |
|
||||
state.query = '' |
|
||||
state.links = [] |
|
||||
state.scored_links = [] |
|
||||
export default state |
|
@ -1,14 +0,0 @@ |
|||||
def get_datetime_string |
|
||||
let obj = new Date!.toString!.split(" ") |
|
||||
let date = obj.slice(1, 4).join("-").toLowerCase! |
|
||||
let time = obj[4].split(":").join("-") |
|
||||
"{date}_{time}" |
|
||||
|
|
||||
export default def download_json_file data, prefix="" |
|
||||
let element = document.createElement 'a' |
|
||||
element.setAttribute 'href', 'data:text/plain;charset=utf-8,' + window.encodeURIComponent(data) |
|
||||
element.setAttribute 'download', "{prefix}{get_datetime_string!}.json" |
|
||||
element.style.display = 'none' |
|
||||
document.body.appendChild element |
|
||||
element.click! |
|
||||
document.body.removeChild element |
|
@ -1,95 +0,0 @@ |
|||||
let SCORE_MIN = -Infinity |
|
||||
let SCORE_MAX = Infinity |
|
||||
let SCORE_GAP_LEADING = -0.005 |
|
||||
let SCORE_GAP_TRAILING = -0.005 |
|
||||
let SCORE_GAP_INNER = -0.01 |
|
||||
let SCORE_MATCH_CONSECUTIVE = 1.0 |
|
||||
let SCORE_MATCH_SLASH = 0.9 |
|
||||
let SCORE_MATCH_WORD = 0.8 |
|
||||
let SCORE_MATCH_DOT = 0.6 |
|
||||
|
|
||||
def fzy arr, query, keyname="name" |
|
||||
let needle = query.trim!.toLowerCase! |
|
||||
return [] unless arr.length > 0 |
|
||||
let scored = [] |
|
||||
let M = new Array(100_000) |
|
||||
let D = new Array(100_000) |
|
||||
let B = new Array(100_000) |
|
||||
for obj in arr |
|
||||
continue unless obj.hasOwnProperty keyname |
|
||||
let haystack = obj[keyname].trim!.toLowerCase! |
|
||||
continue unless has_match needle, haystack |
|
||||
obj.fzy_score = score(needle, haystack, M, D, B) |
|
||||
sorted_insert obj, scored |
|
||||
scored |
|
||||
|
|
||||
def score needle, haystack, M, D, match_bonus |
|
||||
let n = needle.length |
|
||||
let m = haystack.length |
|
||||
if n < 1 or m < 1 |
|
||||
return SCORE_MIN |
|
||||
if n === m |
|
||||
return SCORE_MAX |
|
||||
if m > 1024 |
|
||||
return SCORE_MIN |
|
||||
compute needle, haystack, M, D, match_bonus |
|
||||
M[(n - 1)*m + (m - 1)] |
|
||||
|
|
||||
def compute needle, haystack, M, D, match_bonus |
|
||||
let n = needle.length |
|
||||
let m = haystack.length |
|
||||
precompute_bonus haystack, match_bonus |
|
||||
for i in [0 .. n - 1] |
|
||||
let prev_score = SCORE_MIN |
|
||||
let gap_score = i === n - 1 ? SCORE_GAP_TRAILING : SCORE_GAP_INNER |
|
||||
for j in [0 .. m - 1] |
|
||||
let ij = i*m + j |
|
||||
let pij = (i - 1)*m + (j - 1) |
|
||||
if needle[i] === haystack[j] |
|
||||
let score = SCORE_MIN |
|
||||
if i === 0 |
|
||||
score = (j * SCORE_GAP_LEADING) + match_bonus[j] |
|
||||
elif j > 0 |
|
||||
score = Math.max(M[pij] + match_bonus[j], D[pij] + SCORE_MATCH_CONSECUTIVE) |
|
||||
D[ij] = score |
|
||||
M[ij] = prev_score = Math.max(score, prev_score + gap_score) |
|
||||
else |
|
||||
D[ij] = SCORE_MIN |
|
||||
M[ij] = prev_score = prev_score + gap_score |
|
||||
|
|
||||
def precompute_bonus haystack, match_bonus |
|
||||
let m = haystack.length |
|
||||
let last_ch = '/' |
|
||||
for i in [0 .. m - 1] |
|
||||
let ch = haystack[i] |
|
||||
if last_ch === '/' |
|
||||
match_bonus[i] = SCORE_MATCH_SLASH |
|
||||
elif last_ch === '-' || last_ch === '_' || last_ch === ' ' |
|
||||
match_bonus[i] = SCORE_MATCH_WORD |
|
||||
elif last_ch === '.' |
|
||||
match_bonus[i] = SCORE_MATCH_DOT |
|
||||
else |
|
||||
match_bonus[i] = 0 |
|
||||
last_ch = ch |
|
||||
|
|
||||
def has_match needle, haystack |
|
||||
let i = 0 |
|
||||
let n = -1 |
|
||||
let letter |
|
||||
while letter = needle[i++] |
|
||||
if (n = haystack.indexOf(letter, n + 1)) === -1 |
|
||||
return no |
|
||||
return yes |
|
||||
|
|
||||
def sorted_insert elem, arr |
|
||||
let low = 0 |
|
||||
let high = arr.length |
|
||||
while low < high |
|
||||
let mid = (low + high) >>> 1 |
|
||||
if elem.fzy_score > arr[mid].fzy_score |
|
||||
high = mid |
|
||||
else |
|
||||
low = mid + 1 |
|
||||
arr.splice(low, 0, elem) |
|
||||
|
|
||||
export default fzy |
|
@ -1,94 +0,0 @@ |
|||||
let p = console.log |
|
||||
|
|
||||
class idb_wrapper |
|
||||
|
|
||||
constructor db_name, table_name, version |
|
||||
db_name = db_name |
|
||||
table_name = table_name |
|
||||
version = version |
|
||||
openRequest = null |
|
||||
|
|
||||
def open |
|
||||
|
|
||||
openRequest = global.indexedDB.open(db_name, version) |
|
||||
|
|
||||
openRequest.onupgradeneeded = do |event| |
|
||||
p "Upgrading from DB version {event.oldVersion} to {event.newVersion}." |
|
||||
let db = openRequest.result |
|
||||
switch event.oldVersion |
|
||||
|
|
||||
when 0 |
|
||||
db.createObjectStore(table_name, { keyPath: 'id', autoIncrement: true }) |
|
||||
|
|
||||
openRequest.onerror = do |
|
||||
p "Open db error." |
|
||||
|
|
||||
openRequest.onsuccess = do |
|
||||
p "Open db success." |
|
||||
if global.navigator.storage and global.navigator.storage.persist |
|
||||
global.navigator.storage.persist!.then! do |persistent| |
|
||||
p "db is persistent: {persistent}" |
|
||||
|
|
||||
def reload |
|
||||
let store |
|
||||
|
|
||||
while yes |
|
||||
try |
|
||||
store = #get_store "readonly" |
|
||||
p "Get store success." |
|
||||
break |
|
||||
catch |
|
||||
p "Failed to get store, retrying." |
|
||||
await #sleep 10 |
|
||||
|
|
||||
let request = store.getAll! |
|
||||
|
|
||||
return new Promise! do |resolve| |
|
||||
|
|
||||
request.onsuccess = do |
|
||||
p "Load db success." |
|
||||
resolve request.result |
|
||||
imba.commit! |
|
||||
|
|
||||
request.onerror = do |
|
||||
p "Load db error." |
|
||||
resolve no |
|
||||
|
|
||||
def delete obj |
|
||||
let store = #get_store! |
|
||||
let request = store.delete(obj.id) |
|
||||
|
|
||||
return new Promise! do |resolve| |
|
||||
|
|
||||
request.onsuccess = do |
|
||||
p "deleted link: {obj}" |
|
||||
resolve no |
|
||||
|
|
||||
request.onerror = do |
|
||||
p "Failed to delete link: {obj}" |
|
||||
resolve yes |
|
||||
|
|
||||
def put obj |
|
||||
let store = #get_store! |
|
||||
let request = store.put(obj) |
|
||||
|
|
||||
return new Promise! do |resolve| |
|
||||
|
|
||||
request.onsuccess = do |
|
||||
p "Successfully put link: {obj}" |
|
||||
resolve request.result |
|
||||
|
|
||||
request.onerror = do |
|
||||
p "Failed to put link: {obj}" |
|
||||
resolve no |
|
||||
|
|
||||
def #get_store permission="readwrite" |
|
||||
let db = openRequest.result |
|
||||
let transaction = db.transaction(table_name, permission) |
|
||||
transaction.objectStore(table_name) |
|
||||
|
|
||||
def #sleep ms |
|
||||
new Promise! do |resolve| |
|
||||
setTimeout resolve, ms |
|
||||
|
|
||||
export default idb_wrapper |
|
@ -1,14 +0,0 @@ |
|||||
export default def upload_json_file e |
|
||||
return new Promise! do |resolve| |
|
||||
let files = e.target.files |
|
||||
resolve no if files.length < 1 |
|
||||
let file = files[0] |
|
||||
let reader = new FileReader() |
|
||||
reader.onloadend = do |
|
||||
try |
|
||||
resolve JSON.parse(reader.result) |
|
||||
catch |
|
||||
resolve no |
|
||||
reader.onerror = do |
|
||||
resolve no |
|
||||
reader.readAsText(file) |
|
File diff suppressed because it is too large
Loading…
Reference in new issue