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
				 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