1/** 2 * Task 3 * 4 * @see https://todotxt.org/ 5 * 6 * @typedef {Object} Task 7 * @property {string} task Task description 8 * @property {boolean} done Task completion 9 * @property {string} project Task project 10 * @property {string} context Task context 11 * @property {string} color Task color ID 12 */ 13 14window.addEventListener('DOMContentLoaded', init) 15window.APP_NAME = 'MyList' 16window.DEBUG_MODE = false 17window.SORTABLE_INSTANCES = [] 18window.STORAGE_NAME = 'todoState' 19window.DEFAULT_SORTABLE_CONFIG = { 20 animation: 150, 21 chosenClass: 'dragging', 22 fallbackOnBody: false, 23 ghostClass: 'zoom-in-out', 24 sort: true, 25 onSort: function(ev) { 26 const newState = StateManager.updateState() 27 postToBackend(newState) 28 }, 29} 30window.NEW_LIST_PROMPT = 'What is this list about?' 31window.backendClient = new BackendClient( 32 localStorage.getItem('backend_url'), 33 localStorage.getItem('backend_username'), 34 localStorage.getItem('backend_passphrase'), 35 !!localStorage.getItem('backend_enabled') 36) 37window.AUTO_REMOVE_TIMEOUT = 6500 38 39function cleanUpSortable() { 40 let sortable = null 41 42 while (sortable = SORTABLE_INSTANCES.pop()) { 43 sortable.destroy() 44 } 45} 46 47// TODO: call persistState after an element is moved 48function setupSortable() { 49 cleanUpSortable() 50 51 document.querySelectorAll(`.${TaskList.TAG} .tasks-container`).forEach(tasksContainer => { 52 SORTABLE_INSTANCES.push(new Sortable( 53 tasksContainer, 54 Object.assign({ group: 'tasks', handle: '.task-grabber' }, DEFAULT_SORTABLE_CONFIG))) 55 }) 56} 57 58/** 59 * Apply application settings from query string parameters. 60 * 61 * @param config {object} 62 * @return {object} Passed config with values overriden from query string. 63 */ 64function applyQueryStringSettings(config) { 65 const url = new URL(window.location); 66 const theme = url.searchParams.get('theme'); 67 const queryStringConfig = Object.assign({}, config); 68 69 if (theme != null) { 70 queryStringConfig.theme = theme; 71 } 72 73 applySettings(queryStringConfig); 74 75 return queryStringConfig; 76} 77 78function init(ev) { 79 if (window.DEBUG_MODE) { 80 setupDebugControls() 81 } 82 83 document.addEventListener(LIST_EVENTS.CHANGE, stateUpdated) 84 document.addEventListener(LIST_EVENTS.DELETE, ev => { removeListLink(ev) ; stateUpdated() }) 85 document.addEventListener(TASK_EVENTS.CHANGE, stateUpdated) 86 document.addEventListener(TASK_EVENTS.DELETE, stateUpdated) 87 88 document.addEventListener(TodoSettings.SETTINGS_CHANGE_EVENT, (ev) => applySettings(ev.detail)) 89 90 document.querySelector('.new-list')?.addEventListener('click', ev => { 91 const listName = TaskList.addList(document.getElementById('lists')) 92 93 if (listName) 94 addListLink(listName) 95 }) 96 document.querySelector('#sync_btn')?.addEventListener('click', syncBackend) 97 document.querySelector('#export_json')?.addEventListener( 98 'click', 99 ev => downloadUsingBrowser(`${APP_NAME}.json`, JSON.stringify(StateManager.readState())) 100 ) 101 document.querySelector('#import_json')?.addEventListener('change', importJson) 102 document.querySelector('#clear_cache_btn')?.addEventListener('click', requestCacheFlush) 103 104 const todoSettings = document.querySelector(TodoSettings.TAG) 105 let config = StateManager.settings() 106 107 StateManager.loadState() 108 config = applyQueryStringSettings(config); 109 110 window.backendClient.get() 111 setupSortable() 112 113 if (todoSettings) { 114 todoSettings.applyConfig(config) 115 } 116}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.