diff --git a/README.md b/README.md
index 1ae0b9f..e201e2a 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,185 @@
# CalendarNotes
-An Electron application for daily note (or journal) entries saved to a MySQL DB and searchable by words.
+An Electron application for daily notes (or journal) entries saved to a SQLite or MySQL DB and searchable by words.
-This is a port of an application originally written in Gambas. Much of the code for the calendar component was taken from a
-javascript tutorial taken from here https://code.tutsplus.com/tutorials/how-to-build-a-beautiful-calendar-widget--net-12538
+Current release:
+
https://github.com/dhunt84971/CalendarNotes/releases/tag/v1.1.6
-While the settings dialog will create the database tables the setup of the MySQL database and configuration/creation of a
-database and username and password is on the end user. Keep in mind, the entered database user must be able to create tables
-in order to use the create tables button.
-This is a work in progress and JavaScript is relatively new to me, so any suggestions or code improvements are welcome.
+This is a port of an application originally written in Gambas. Much of the code for the calendar component came from a
+javascript tutorial taken from here:
+
https://code.tutsplus.com/tutorials/how-to-build-a-beautiful-calendar-widget--net-12538
-Screenshot(s):
+
-Application -
+## Installation
+To install the latest release, download the install package appropriate for the platform (exe for Windows, appimage/deb for Linux).
+
+https://github.com/dhunt84971/CalendarNotes/releases
-
+### Windows Installation
+For Windows simply execute the setup.exe file.
-Search -
+### Linux Installation
+There are two options for Linux.
-
+**App Image Installation**
-Settings -
+The appimage is probably the easiest to install, but does not typically result in a menu integrated installation meaning it will be necessary to execute the appimage file to launch the application. Simply use a file explorer and open the appimage and the application will run.
-
+**DEB Installation**
+
+The deb file is suitable for all debian based distros include Ubuntu, however there is an additional dependency that is not included in the deb file that needs to be installed manually. To install the deb file use the following commands from a terminal in the folder where the package was downloaded:
+
+```
+sudo apt-get update
+sudo apt-get install libappindicator1
+sudo dpkg -i calendarNotes_1.0.1_amd64.deb
+```
+NOTE: Substitute the example deb file name with the version that was downloaded.
+
+This should result in the Calendar Notes application becoming available from the launcher menu. This will depend on the distro being used.
+
+
+## Configuration
+When the application is first run it will display a warning that no settings file was found.
+
+
+
+After clicking OK the application is ready to use with the default settings. This includes using the default theme and a local SQLite database file.
+
+### MySQL Configuration
+The Calendar Notes application can be configured to store all notes and tasks in a MySQL database. Click on the gear icon in the upper right corner to display the settings and tick the MySQL DB Server radio button. This will display additional settings for making a connection to a MySQL database.
+
+
+
+Enter the required information and click on **TEST CONNECTION**. If the connection is successful then provided the user has the correct privileges simply click on the **CREATE TABLES** button. This will create the necessary tables for storing the notes and tasks. If a connection is being made to an existing Calendar Notes database that already has the tables, then skip this step. The main advantage of using a MySQL database is that the notes become accessible from multiple locations. Just install the Calendar Notes application on any number of computers and configure the MySQL connection. Now notes can be accessed from anywhere.
+
+### Themes
+Several pre-configured themes are available from the application settings. Examples of each are displayed below:
+
+**Default**
+
+
+
+**Warm**
+
+
+
+**Cool**
+
+
+
+**Green**
+
+
+
+**Pink**
+
+
+
+**Tron**
+
+
+
+**Clu**
+
+
+
+### Experimental
+The **Experimental** option is for enabling features that are still being tested.
+
+## Features
+The main feature of the application is to provide a place to enter and store notes for a particular day on the calendar. Click on a day in the calendar and the notes for that day are displayed. Use the **>>** button to navigate directly to today's notes.
+
+
+
+### Tasks
+The tasks area provides a single location for any active tasks that need to be always displayed. The tasks are not associated with any particular day and are not archived meaning if changes are made to the tasks area and the **SAVE** button is pressed whatever changes were made to tasks are permanent. That is why this area is meant for reminders, such as TO DO lists. Use the Notes area to record when a task has actually been worked on.
+
+### Save and Revert
+The **SAVE** button is used to save any changes that are made to the notes or tasks. To remind the user to press the **SAVE** button astericks (*) are placed on either side of the **SAVE** button to indicate that an edit has been made but not saved. The only time that edits are automatically saved is when the selected date is changed. **NOTE: This means unsaved edits will be lost if the application is closed.**
+
+The **REVERT** button is there to remove any unsaved edits. For example, if the user has inadvertantly mashed on the keyboard and destroyed their notes, clicking on the **REVERT** button will pull the unedited notes from the database and load these back into the notes area. This also applies to tasks.
+
+### Date Navigation
+Date navigation is accomplished by clicking on a date in the calendar. This will immediately save the current notes and tasks and load the notes for the selected date. Use the **>>** button to navigate to today and the left and right arrow buttons to move to the previous or next month. By holding the **Ctrl** key and clicking on an arrow it is possible to move 12 months into the past or future.
+
+### Search
+One of the most powerful features of the Calendar Notes application is the simple to use search feature. To search for a note containing some words click on the **SEARCH** button and enter the words into the text field to the left of the **GO** button then click on the **GO** button to execute the search. The search uses an inclusive condition for the words being sought meaning that only notes containing ALL of the words will be included in the results. The results are displayed as buttons below the search entry field. Each button displays a date where matching words were found.
+
+
+
+By hovering over a date in the search results a preview of the note will be displayed in the notes area. The words being searched for will be highlighted in the previewed notes. To edit the notes for this day click on the dated button and the calendar will select that date and display the notes.
+
+### Markdown
+A markdown renderer has been included to allow the user to enter notes using markdown syntax. To display the fully rendered markdown click on the **MARKDOWN** button. It is not possible to edit the notes while markdown rendering is enabled. To disable the markdown mode click on the **EDIT** button. The markdown mode applies to the search result previews as well as the currently displayed notes.
+
+
+
+### Full Window Notes
+In order to keep the application as functional as possible the left side of the application can be hidden so that only the notes editor area is displayed. To do this click on the left pointing arrow next to the word **NOTES** at the top of the notes editor area. To restore the left side, click on the right pointing arrow.
+
+
+
+
+
+## Documents
+Currently an experimental feature, enabling documents provides additional functionality allowing the user to create documents and multiple pages for each document. Use the gear to expose the settings and check the box next to Documents to enable the feature.
+
+
+
+### Add a Document
+To add a document, click on the **DOCS** button to reveal the documents and then click the **+ ADD DOC** button.
+
+
+
+A page will be automatically added for the newly created document. The new document and new page will have a default name. Right click on the Document or Page button to display the context menu for the selected item.
+
+
+
+Use the document and page context menus to modify the selected item.
+
+#### Document Context Menu
+ + **ADD DOC** - Add a document under the selected document.
+
+ + **RENAME** - Rename the selected document. Documents at the same level cannot have the same name.
+
+ + **REMOVE** - Delete the selected document. Deleting a document deletes all sub-documents and pages.
+
+ + **MOVE UP** - Move the selected document up in the list. This does not change the level, only the order.
+
+ + **MOVE DOWN** - Move the selected document down in the list. This does not change the level, only the order.
+
+#### Page Context Menu
+ + **RENAME** - Rename the selected page. Pages within the same document cannot have the same name.
+
+ + **REMOVE** - Delete the selected page.
+
+ + **INCREASE INDENT** - Indent the page button to the right. This is just a visual means of establishing a relationship between pages in the same document.
+
+ + **DECREASE INDENT** - Remove indent from the page button. This moves the button to the left by one indent level per click.
+
+ + **MOVE UP** - Move the selected page up in the list.
+
+ + **MOVE DOWN** - Move the selected page down in the list.
+
+### Drag and Drop
+Documents and pages can be moved by dragging and dropping. Dragging a document will move all sub-documents under that document. Pages can be dragged from one document to another.
+
+## Development Setup
+VS Code is the recommended IDE. Install VS Code using the instructions from the VS Code website. Install nodejs and npm:
+
+For Debian based Linux systems run:
+```
+sudo apt-get update
+sudo apt-get install nodejs npm
+```
+
+### Development SQLite Dependencies
+To setup a session for development it may be necessary to install python and make sure it is in the path. This is because if a SQLite binary cannot be found for the installed version of Electron it will need to be built from source and the source includes python code.
+
+For Debian based Linux systems run:
+```
+sudo apt-get update
+sudo apt-get install python
+```
diff --git a/app_documents.js b/app_documents.js
new file mode 100644
index 0000000..09f5e57
--- /dev/null
+++ b/app_documents.js
@@ -0,0 +1,1450 @@
+"use strict";
+
+var app_documents = {
+
+ //#region GLOBAL DECLARATIONS
+ lstDocuments: {},
+ dvDocuments: {},
+ txtRename: {},
+ txtDoc: {},
+ contextSelectedDoc: "",
+ contextSelectedPage: "",
+ renameTarget: {},
+ lastFullPath: "",
+ indentChange: 10,
+ draggedPageEl: undefined,
+ draggedDocEl: undefined,
+ //#endregion GLOBAL DECLARATIONS
+
+ //#region PAGE RENDER FUNCTIONS
+ loadPages: function (docFullName) {
+ return new Promise((resolve, reject) => {
+ console.log(docFullName);
+ this.getPages(docFullName)
+ .then((data) => {
+ emptyDiv("lstDocs");
+ console.log(data);
+ if(data){
+ if (data.length > 0){
+ let firstPage = {};
+ for (var i = 0; i < data.length; i++) {
+ let pageBtnEl = addItemtoDiv("lstDocs", data[i].DocName, "btn pageItem", "data-grp=page");
+ pageBtnEl.setAttribute("draggable", "true");
+ let marginL = data[i].DocIndentLevel * 5;
+ pageBtnEl.style.marginLeft = `${marginL}px`;
+ pageBtnEl.addEventListener("click", (e)=>{
+ this.btnPage_Clicked(e.target);
+ });
+ pageBtnEl.addEventListener("contextmenu", (e)=>{
+ this.btnPage_RtClicked(e);
+ });
+ if (i == 0) firstPage = pageBtnEl;
+ }
+ this.selectPage(firstPage);
+ resolve();
+ }
+ else{
+ resolve();
+ }
+ }
+ else{
+ resolve();
+ }
+ })
+ .catch((err) => {
+ console.log(err);
+ reject(err);
+ });
+ });
+
+ },
+
+ docContextMenu: function (el, fullPath) {
+ this.contextSelectedDoc = fullPath;
+ var menu = document.querySelector(".docsMenu");
+ locateMenu(menu, el.clientX, el.clientY);
+ },
+
+ pageContextMenu: function (e) {
+ let el = e.target;
+ this.contextSelectedPage = el.innerHTML;
+ var menu = document.querySelector(".pagesMenu");
+ locateMenu(menu, e.clientX, e.clientY);
+ },
+
+ showtxtRename: function (el) {
+ console.log(el);
+ var rect = el.getBoundingClientRect();
+ console.log(rect);
+ this.txtRename.style.left = rect.left + "px";
+ this.txtRename.style.top = rect.top + "px";
+ this.txtRename.style.width = rect.width + "px";
+ this.txtRename.style.height = rect.height + "px";
+ this.txtRename.value = el.innerText;
+ this.txtRename.classList.remove("hide");
+ this.txtRename.focus();
+ this.txtRename.select();
+ },
+
+ unselectPageButtons: function () {
+ let pages = document.querySelectorAll("div[data-grp='page']");
+ for (let el of pages){
+ el.classList.remove("selected");
+ }
+ },
+
+ selectPageButton: function (el) {
+ this.unselectPageButtons();
+ el.classList.add("selected");
+ },
+
+ showPageData: function (data) {
+ this.txtDoc.value = (data) ? data : " ";
+ showPageMarkdown();
+ },
+
+ //#endregion PAGE RENDER FUNCTIONS
+
+ //#region DATABASE FUNCTIONS
+ getPagesMySQL: function (docFullName) {
+ let sql = `
+ SELECT DocName, DocIndentLevel
+ FROM Docs
+ WHERE DocLocation = '${docFullName}'
+ ORDER BY PageOrder ASC
+ `;
+ return this.execQueryMySQL(sql);
+ },
+
+ getPagesSqlite: function (docFullName) {
+ let sql = `
+ SELECT DocName, DocIndentLevel
+ FROM Docs
+ WHERE DocLocation = '${docFullName}'
+ ORDER BY PageOrder ASC
+ `;
+ return this.execQuerySqlite(sql);
+ },
+
+ addDocLocationMySQL: function (parentDoc, docName, docOrder) {
+ return new Promise((resolve, reject) => {
+ var docFullName = parentDoc == "" ? docName : parentDoc + "/" + docName;
+ // Make sure the document name is unique.
+ this.getUniqueDocName(docFullName)
+ .then((docNewName) => {
+ // Add the document name to the database.
+ showWaitImage();
+ var connection = mysql.createConnection(_settings);
+ connection.connect(function (err) {
+ if (err) throw err;
+ var sql = `
+ INSERT INTO Docs
+ (DocName, DocLocation, DocColor, DocText, LastModified, DocOrder, PageOrder)
+ VALUES ('New Page', '${docNewName}',-1, '', '${getMySQLNow()}', ${docOrder}, 0)
+ `;
+ console.log("Executing SQL query = " + sql);
+ connection.query(sql, function (err, result) {
+ hideWaitImage();
+ if (err) reject(err)
+ else resolve(result);
+ connection.end();
+ });
+ });
+ });
+ });
+ },
+
+ addDocLocationSqlite: function (parentDoc, docName, docOrder) {
+ return new Promise((resolve, reject) => {
+ var docFullName = parentDoc == "" ? docName : parentDoc + "/" + docName;
+ // Make sure the document name is unique.
+ this.getUniqueDocName(docFullName)
+ .then((docNewName) => {
+ // Add the document name to the database.
+ let db = new sqlite3.Database(dbFile, (err) => {
+ if (err) throw err;
+ var sql = `
+ INSERT INTO Docs
+ (DocName, DocLocation, DocColor, DocText, LastModified, DocOrder, PageOrder)
+ VALUES ('New Page', '${docNewName}',-1, '', '${getMySQLNow()}', ${docOrder}, 0)
+ `;
+ console.log("Executing SQL query = " + sql);
+ db.run(sql, (err) => {
+ if (err) reject(err)
+ else resolve();
+ });
+ db.close();
+ });
+ });
+ });
+ },
+
+ addPageMySQL: function (path, docOrder, pageOrder) {
+ // Get a unique page name.
+ let newPageName = this.getUniquePageName("New Page");
+ // Add the document to the database.
+ var sql = `
+ INSERT INTO Docs
+ (DocName, DocLocation, DocColor, DocText, LastModified, DocOrder, PageOrder)
+ VALUES ('${newPageName}', '${path}',
+ -1, '','${getMySQLNow()}', ${docOrder}, ${pageOrder})
+ `;
+ return this.execCommandMySQL(sql);
+ },
+
+ addPageSqlite: function (path, docOrder, pageOrder) {
+ // Get a unique page name.
+ let newPageName = this.getUniquePageName("New Page");
+ // Add the document to the database.
+ var sql = `
+ INSERT INTO Docs
+ (DocName, DocLocation, DocColor, DocText, LastModified, DocOrder, PageOrder)
+ VALUES ('${newPageName}', '${path}',
+ -1, '','${getMySQLNow()}', ${docOrder}, ${pageOrder})
+ `;
+ return this.execCommandSqlite(sql);
+ },
+
+ docNameExistsMySQL: function (docFullName) {
+ return new Promise(function (resolve, reject) {
+ var retValue = docFullName;
+ showWaitImage();
+ var connection = mysql.createConnection(_settings);
+ connection.connect();
+ connection.query(
+ "SELECT DocLocation from Docs WHERE DocLocation = '" + docFullName + "'",
+ function (err, rows, fields) {
+ hideWaitImage();
+ if (err) {
+ reject(new Error("DB error occurred!"));
+ } else {
+ console.log("Rows found = " + rows.length);
+ console.log("Returning = " + (rows.length > 0));
+ retValue = rows.length > 0;
+ resolve(retValue);
+ }
+ connection.end();
+ return retValue;
+ }
+ );
+ });
+ },
+
+ docNameExistsSqlite: function (docFullName) {
+ return new Promise(function (resolve, reject) {
+ var retValue = docFullName;
+ let db = new sqlite3.Database(dbFile, (err) => {
+ if (!err) {
+ let sql = `
+ SELECT DocLocation
+ FROM Docs
+ WHERE DocLocation = '${docFullName}'
+ `;
+ db.all(sql, [], (err, rows) => {
+ if (!err){
+ retValue = rows.length > 0;
+ resolve(retValue);
+ }
+ else{
+ reject(err);
+ }
+ });
+ }
+ else{
+ reject(err);
+ }
+ db.close();
+ return;
+ });
+ });
+ },
+
+ docPageExistsMySQL: function(fullPath, pageName){
+ return new Promise(function (resolve, reject) {
+ let sql = `
+ SELECT DocName
+ FROM Docs
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ app_documents.execQueryMySQL(sql)
+ .then((data)=>{
+ let retValue = data.length > 0;
+ resolve(retValue);
+ })
+ .catch((err)=>{
+ console.log(err);
+ reject(err);
+ });
+ });
+ },
+
+ docPageExistsSqlite: function(fullPath, pageName){
+ return new Promise(function (resolve, reject) {
+ let sql = `
+ SELECT DocName
+ FROM Docs
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ app_documents.execQuerySqlite(sql)
+ .then((data)=>{
+ let retValue = data.length > 0;
+ resolve(retValue);
+ })
+ .catch((err)=>{
+ console.log(err);
+ reject(err);
+ });
+ });
+ },
+
+ getDocsMySQL: function () {
+ return new Promise((resolve, reject)=>{
+ showWaitImage();
+ var connection = mysql.createConnection(_settings);
+ connection.connect();
+ let sql = `
+ SELECT DISTINCT DocLocation, DocOrder
+ FROM Docs
+ ORDER BY DocOrder ASC
+ `;
+ connection.query( sql, (err, data) => {
+ hideWaitImage();
+ if (err) reject(err);
+ console.log(data);
+ resolve(data);
+ connection.end();
+ });
+ });
+ },
+
+ getDocsSqlite: function () {
+ // Load treeview with the documents.
+ let sql = `
+ SELECT DISTINCT DocLocation
+ FROM Docs
+ ORDER BY DocOrder ASC
+ `;
+ return this.execQuerySqlite(sql);
+ },
+
+ updateDocNameMySQL: function (oldName, newName){
+ var sql1 = `
+ UPDATE Docs
+ SET DocLocation = REPLACE(DocLocation, '${oldName}', '${newName}')
+ WHERE DocLocation = '${oldName}';
+ `;
+ var sql2= `
+ UPDATE Docs
+ SET DocLocation = CONCAT('${newName}', SUBSTR(DocLocation, LENGTH('${oldName}/')))
+ WHERE INSTR(DocLocation, '${oldName}/') = 1;
+ `;
+ return Promise.all([this.execCommandMySQL(sql1), this.execCommandMySQL(sql2)])
+ .catch((err)=>{
+ console.log(err);
+ ShowWarningMessageBox("Database command failure!");
+ });
+ },
+
+ updateDocNameSqlite: function (oldName, newName){
+ var sql1 = `
+ UPDATE Docs
+ SET DocLocation = REPLACE(DocLocation, '${oldName}', '${newName}')
+ WHERE DocLocation = '${oldName}';
+ `;
+ var sql2= `
+ UPDATE Docs
+ SET DocLocation = '${newName}' || SUBSTR(DocLocation, LENGTH('${oldName}/'))
+ WHERE INSTR(DocLocation, '${oldName}/') = 1;
+ `;
+ return Promise.all([this.execCommandSqlite(sql1), this.execCommandSqlite(sql2)])
+ .catch((err)=>{
+ console.log(err);
+ ShowWarningMessageBox("Database command failure!");
+ });
+ },
+
+ updatePageNameMySQL: function (fullPath, oldName, newName){
+ var sql = `
+ UPDATE Docs
+ SET DocName = '${newName}'
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${oldName}';
+ `;
+ return this.execCommandMySQL(sql);
+ },
+
+ updatePageNameSqlite: function (fullPath, oldName, newName){
+ var sql = `
+ UPDATE Docs
+ SET DocName = '${newName}'
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${oldName}';
+ `;
+ return this.execCommandSqlite(sql);
+ },
+
+ deleteDocMySQL: function (fullPath){
+ var sql1 = `
+ DELETE FROM Docs
+ WHERE DocLocation = '${fullPath}';
+ `;
+ var sql2 = `
+ DELETE FROM Docs
+ WHERE INSTR(DocLocation, '${fullPath}/') = 1;
+ `;
+ return Promise.all([this.execCommandMySQL(sql1), this.execCommandMySQL(sql2)])
+ .catch((err)=>{
+ console.log(err);
+ ShowWarningMessageBox("Database command failure!");
+ });
+ },
+
+ deleteDocSqlite: function (fullPath){
+ var sql1 = `
+ DELETE FROM Docs
+ WHERE DocLocation = '${fullPath}';
+ `;
+ var sql2 = `
+ DELETE FROM Docs
+ WHERE INSTR(DocLocation, '${fullPath}/') = 1;
+ `;
+ return Promise.all([this.execCommandSqlite(sql1), this.execCommandSqlite(sql2)])
+ .catch((err)=>{
+ console.log(err);
+ ShowWarningMessageBox("Database command failure!");
+ });
+ },
+
+ deletePageMySQL: function(fullPath, pageName){
+ var sql = `
+ DELETE FROM Docs
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execCommandMySQL(sql)
+ .catch((err)=>{
+ console.log(err);
+ ShowWarningMessageBox("Database command failure!");
+ });
+ },
+
+ deletePageSqlite: function(fullPath, pageName){
+ var sql = `
+ DELETE FROM Docs
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execCommandSqlite(sql)
+ .catch((err)=>{
+ console.log(err);
+ ShowWarningMessageBox("Database command failure!");
+ });
+ },
+
+ getPageNoteMySQL: function (fullPath, pageName){
+ let sql = `
+ SELECT DocText FROM Docs
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execQueryMySQL(sql);
+ },
+
+ getPageNoteSqlite: function (fullPath, pageName){
+ let sql = `
+ SELECT DocText FROM Docs
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execQuerySqlite(sql);
+ },
+
+ updatePageMySQL: function(fullPath, pageName, docText){
+ let sql = `
+ UPDATE Docs
+ SET DocText = '${sqlSafeText(docText)}',
+ LastModified = '${getMySQLNow()}'
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execCommandMySQL(sql);
+ },
+
+ updatePageSqlite: function(fullPath, pageName, docText){
+ let sql = `
+ UPDATE Docs
+ SET DocText = '${sqlSafeText(docText)}',
+ LastModified = '${getMySQLNow()}'
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execCommandSqlite(sql);
+ },
+
+ updatePageIndentMySQL: function(fullPath, pageName, indent){
+ let sql = `
+ UPDATE Docs
+ SET DocIndentLevel = ${indent}
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execCommandMySQL(sql);
+ },
+
+ updatePageIndentSqlite: function(fullPath, pageName, indent){
+ let sql = `
+ UPDATE Docs
+ SET DocIndentLevel = ${indent}
+ WHERE DocLocation = '${fullPath}'
+ AND DocName = '${pageName}';
+ `;
+ return this.execCommandSqlite(sql);
+ },
+
+ execQueryMySQL: function (sql) {
+ return new Promise(function (resolve, reject) {
+ console.log("Executing SQL query = " + sql);
+ showWaitImage();
+ var connection = mysql.createConnection(_settings);
+ connection.connect();
+ connection.query(sql,
+ function (err, rows, fields) {
+ hideWaitImage();
+ if (err) {
+ reject(new Error("DB error occurred!"));
+ } else {
+ resolve(rows);
+ }
+ connection.end();
+ }
+ );
+ });
+ },
+
+ execCommandMySQL: function (sql){
+ return new Promise((resolve, reject) => {
+ showWaitImage();
+ var connection = mysql.createConnection(_settings);
+ connection.connect(function (err) {
+ if (err) reject(err);
+ console.log("Executing SQL query = " + sql);
+ connection.query(sql, function (err, result) {
+ hideWaitImage();
+ if (err) reject(err)
+ else resolve(result);
+ connection.end();
+ });
+ });
+ });
+ },
+
+ execQuerySqlite: function (sql) {
+ // Load treeview with the documents.
+ return new Promise((resolve, reject)=>{
+ let db = new sqlite3.Database(dbFile, (err) => {
+ if (!err) {
+ db.all(sql, [], (err, rows) => {
+ if (!err){
+ console.log(rows);
+ resolve(rows);
+ }
+ else{
+ reject(err);
+ }
+ });
+ }
+ else{
+ reject(err);
+ }
+ db.close();
+ return;
+ });
+ });
+ },
+
+ execCommandSqlite: function (sql){
+ return new Promise((resolve, reject) => {
+ // Add the document name to the database.
+ let db = new sqlite3.Database(dbFile, (err) => {
+ if (err) throw err;
+ console.log("Executing SQL command = " + sql);
+ db.run(sql, (err) => {
+ if (err) reject(err)
+ else resolve();
+ });
+ db.close();
+ });
+ });
+ },
+
+ //#endregion DATABASE FUNCTIONS
+
+ //#region DATA RETRIEVAL FUNCTIONS
+ getPages: function (docFullName) {
+ return (_settings.dbType == "MySql") ?
+ this.getPagesMySQL(docFullName) :
+ this.getPagesSqlite(docFullName);
+ },
+
+ getPageNote: function (docFullName, pageName){
+ return (_settings.dbType == "MySql") ?
+ this.getPageNoteMySQL(docFullName, pageName) :
+ this.getPageNoteSqlite(docFullName, pageName);
+ },
+
+ getUniqueDocName: function (docFullName) {
+ return new Promise(async (resolve, reject) => {
+ var fileNameIndex = 0;
+ var docReturnName = docFullName;
+ console.log("looking for name " + docFullName);
+ var nameFound = await this.docNameExists(docFullName);
+ console.log("looping");
+ while (nameFound) {
+ console.log("incrementing name");
+ fileNameIndex += 1;
+ docReturnName = docFullName + "(" + fileNameIndex + ")";
+ console.log("searching for " + docReturnName);
+ nameFound = await this.docNameExists(docReturnName);
+ }
+ resolve(docReturnName);
+ return docReturnName;
+ });
+ },
+
+ docNameExists: function (docFullName) {
+ return (_settings.dbType == "MySql") ?
+ this.docNameExistsMySQL(docFullName) :
+ this.docNameExistsSqlite(docFullName);
+ },
+
+ getUniquePageDocName: function (path, pageName) {
+ return new Promise(async (resolve, reject) => {
+ var fileNameIndex = 0;
+ var pageReturnName = pageName;
+ console.log("looking for name " + pageName);
+ var nameFound = await this.docPageExists(path, pageName);
+ console.log("looping");
+ while (nameFound) {
+ console.log("incrementing name");
+ fileNameIndex += 1;
+ pageReturnName = pageName + "(" + fileNameIndex + ")";
+ console.log("searching for " + pageReturnName);
+ nameFound = await this.docPageExists(path, pageReturnName);
+ }
+ resolve(pageReturnName);
+ return pageReturnName;
+ });
+ },
+
+ docPageExists: function (fullPath, pageName){
+ return (_settings.dbType == "MySql") ?
+ this.docPageExistsMySQL(fullPath, pageName) :
+ this.docPageExistsSqlite(fullPath, pageName);
+ },
+
+ getDocs: function (){
+ return (_settings.dbType == "MySql") ?
+ this.getDocsMySQL() :
+ this.getDocsSqlite();
+ },
+
+ loadDocs: function (selectFirst) {
+ return new Promise((resolve, reject)=>{
+ document.getElementById("txtDoc").value = "";
+ document.getElementById("txtDocView").value = "";
+ emptyDiv("lstDocuments");
+ emptyDiv("lstDocs");
+ this.getDocs()
+ .then((data)=>{
+ console.log(data);
+ for (var i = 0; i < data.length; i++) {
+ this.dvDocuments.addTVItem(this.lstDocuments, data[i].DocLocation, false);
+ }
+ data.length > 0 ?
+ document.getElementById("btnAddPage").classList.remove("hide") :
+ document.getElementById("btnAddPage").classList.add("hide");
+ // Pick the first location.
+ if (selectFirst) this.dvDocuments.selectFirstItem();
+ resolve();
+ })
+ .catch((err)=>{
+ console.log(err);
+ reject(err);
+ });
+ });
+ },
+
+ getMaxDocOrder: function (){
+ return new Promise(async (resolve, reject)=>{
+ let sql = `
+ SELECT MAX(DocOrder) AS MaxDocOrder FROM Docs
+ `;
+ let data = _settings.dbType == "MySql" ?
+ await this.execQueryMySQL(sql) :
+ await this.execQuerySqlite(sql);
+ data.length > 0 ? resolve(data[0].MaxDocOrder) : resolve(-1);
+ });
+ },
+
+ getMaxPageOrder: function (docLocation){
+ return new Promise(async (resolve, reject)=>{
+ let sql = `
+ SELECT MAX(PageOrder) AS MaxPageOrder FROM Docs
+ WHERE DocLocation = '${docLocation}'
+ `;
+ let data = _settings.dbType == "MySql" ?
+ await this.execQueryMySQL(sql) :
+ await this.execQuerySqlite(sql);
+ data.length > 0 ? resolve(data[0].MaxPageOrder) : resolve(-1);
+ });
+ },
+
+ getDocOrder: function (docLocation){
+ return new Promise(async (resolve, reject)=>{
+ let sql = `
+ SELECT DocOrder FROM Docs
+ WHERE DocLocation = '${docLocation}'
+ `;
+ let data = _settings.dbType == "MySql" ?
+ await this.execQueryMySQL(sql) :
+ await this.execQuerySqlite(sql);
+ data.length > 0 ? resolve(data[0].DocOrder) : resolve(0);
+ });
+ },
+
+ getUpDocOrder: function (path, docOrder){
+ return new Promise(async (resolve, reject)=>{
+ let sql = `
+ SELECT MAX(DocOrder) AS UpDocOrder FROM Docs
+ WHERE DocLocation LIKE '${path}%'
+ AND DocOrder < ${docOrder}
+ `;
+ let data = _settings.dbType == "MySql" ?
+ await this.execQueryMySQL(sql) :
+ await this.execQuerySqlite(sql);
+ data.length > 0 ? resolve(data[0].UpDocOrder) : resolve();
+ });
+ },
+
+ getDownDocOrder: function (path, docOrder){
+ return new Promise(async (resolve, reject)=>{
+ let sql = `
+ SELECT MIN(DocOrder) AS DownDocOrder FROM Docs
+ WHERE DocLocation LIKE '${path}%'
+ AND DocOrder > ${docOrder}
+ `;
+ let data = _settings.dbType == "MySql" ?
+ await this.execQueryMySQL(sql) :
+ await this.execQuerySqlite(sql);
+ data.length > 0 ? resolve(data[0].DownDocOrder) : resolve();
+ });
+ },
+
+ getPageOrder: function (docLocation, pageName){
+ return new Promise(async (resolve, reject)=>{
+ let sql = `
+ SELECT PageOrder FROM Docs
+ WHERE DocLocation = '${docLocation}'
+ AND DocName = '${pageName}'
+ `;
+ let data = _settings.dbType == "MySql" ?
+ await this.execQueryMySQL(sql) :
+ await this.execQuerySqlite(sql);
+ resolve(data[0].PageOrder);
+ });
+ },
+
+ //#endregion DATA RETRIEVAL FUNCTIONS
+
+ //#region DATA TRANSMITTAL FUNCTIONS
+ addDocLocation: async function (parentDoc, docName) {
+ let path = this.dvDocuments.getSelectedFullPath();
+ let docOrder = await this.getMaxDocOrder();
+ docOrder ++;
+ if (_settings.dbType == "MySql"){
+ await this.addDocLocationMySQL(parentDoc, docName, docOrder);
+ }
+ else{
+ await this.addDocLocationSqlite(parentDoc, docName, docOrder);
+ }
+ await this.loadDocs();
+ (path) ? this.dvDocuments.setSelectedPath(path) : this.dvDocuments.selectFirstItem();
+ },
+
+ addPage: async function (path) {
+ let pageOrder = await this.getMaxPageOrder(path);
+ pageOrder ++;
+ let docOrder = await this.getDocOrder(path);
+ return (_settings.dbType == "MySql") ?
+ this.addPageMySQL(path, docOrder, pageOrder) :
+ this.addPageSqlite(path, docOrder, pageOrder);
+ },
+
+ addPage_Clicked: function(){
+ let path = this.dvDocuments.getSelectedFullPath();
+ let page = this.getSelectedPageName();
+ console.log(path);
+ this.addPage(path)
+ .then(()=>{
+ this.loadPages(path);
+ })
+ .then(()=>{
+ this.selectPageByName(page);
+ });
+ },
+
+ updatePage: function(path, pageName, docText){
+ return (_settings.dbType == "MySql") ?
+ this.updatePageMySQL(path, pageName, docText) :
+ this.updatePageSqlite(path, pageName, docText);
+ },
+
+ updatePageIndent(path, pageName, indent){
+ return (_settings.dbType == "MySql") ?
+ this.updatePageIndentMySQL(path, pageName, indent) :
+ this.updatePageIndentSqlite(path, pageName, indent);
+ },
+
+ savePage: function(){
+ return new Promise((resolve, reject)=>{
+ let path = this.lastFullPath;
+ let pageName = this.getSelectedPageName();
+ if (!pageName) resolve();
+ let docText = this.txtDoc.value;
+ this.updatePage(path, pageName, docText)
+ .then(()=>{
+ document.getElementById("btnSave").innerHTML = "SAVE";
+ resolve();
+ })
+ .catch((err)=>{
+ console.catch(err);
+ reject(err);
+ });
+ });
+ },
+
+ getUniqueDocName: function (docFullName) {
+ return new Promise(async (resolve, reject) => {
+ var fileNameIndex = 0;
+ var docReturnName = docFullName;
+ console.log("looking for name " + docFullName);
+ var nameFound = await this.docNameExists(docFullName);
+ console.log("looping");
+ while (nameFound) {
+ console.log("incrementing name");
+ fileNameIndex += 1;
+ docReturnName = docFullName + "(" + fileNameIndex + ")";
+ console.log("searching for " + docReturnName);
+ nameFound = await this.docNameExists(docReturnName);
+ }
+ resolve(docReturnName);
+ return docReturnName;
+ });
+ },
+
+ getUniquePageName: function (pageName) {
+ let pages = document.querySelectorAll("div[data-grp='page']");
+ let pagesArr = Array.from(pages);
+ console.log(pagesArr);
+ let newPageName = pageName;
+ let idx = 0;
+ while (pagesArr.find(x=>x.innerText==newPageName)){
+ idx += 1;
+ newPageName = `${pageName} (${idx})`;
+ }
+ return newPageName;
+ },
+
+ updateDocName: function (oldDocFullPath, newDocFullPath) {
+ return (_settings.dbType == "MySql") ?
+ this.updateDocNameMySQL(oldDocFullPath, newDocFullPath) :
+ this.updateDocNameSqlite(oldDocFullPath, newDocFullPath);
+ },
+
+ updatePageName: function (fullPath, oldName, newName) {
+ return (_settings.dbType == "MySql") ?
+ this.updatePageNameMySQL(fullPath, oldName, newName) :
+ this.updatePageNameSqlite(fullPath, oldName, newName);
+ },
+
+ deleteDoc: function (fullPath){
+ return (_settings.dbType == "MySql") ?
+ this.deleteDocMySQL(fullPath) :
+ this.deleteDocSqlite(fullPath);
+ },
+
+ deletePage: function (fullPath, pageName){
+ return (_settings.dbType == "MySql") ?
+ this.deletePageMySQL(fullPath, pageName) :
+ this.deletePageSqlite(fullPath, pageName);
+ },
+
+ swapPages: function (fullPath, pageOrder1, pageOrder2){
+ return new Promise(async (resolve, reject)=>{
+ let holdPageOrder = -999;
+ let sql = `
+ UPDATE Docs SET PageOrder = ${holdPageOrder}
+ WHERE DocLocation = '${fullPath}' AND PageOrder = ${pageOrder1}
+ `;
+ await this.execCommandSql(sql);
+ sql = `
+ UPDATE Docs SET PageOrder = ${pageOrder1}
+ WHERE DocLocation = '${fullPath}' AND PageOrder = ${pageOrder2}
+ `;
+ await this.execCommandSql(sql);
+ sql = `
+ UPDATE Docs SET PageOrder = ${pageOrder2}
+ WHERE DocLocation = '${fullPath}' AND PageOrder = ${holdPageOrder}
+ `;
+ await this.execCommandSql(sql);
+ resolve();
+ });
+ },
+
+ movePageUp: async function (fullPath, pageName){
+ let pageOrder1 = await this.getPageOrder(fullPath, pageName);
+ if (pageOrder1 <= 0) return; // Already at the top.
+ let pageOrder2 = pageOrder1 - 1;
+ await this.swapPages(fullPath, pageOrder1, pageOrder2);
+ this.loadPages(fullPath)
+ .then(()=>{
+ this.selectPageByName(pageName);
+ });
+ },
+
+ movePageDown: async function (fullPath, pageName){
+ let pageOrder1 = await this.getPageOrder(fullPath, pageName);
+ let maxPageOrder = await this.getMaxPageOrder(fullPath);
+ if (pageOrder1 == maxPageOrder) return; // Already at the bottom.
+ let pageOrder2 = pageOrder1 + 1;
+ await this.swapPages(fullPath, pageOrder1, pageOrder2);
+ this.loadPages(fullPath)
+ .then(()=>{
+ this.selectPageByName(pageName);
+ });
+ },
+
+ swapDocs: function (docOrder1, docOrder2){
+ return new Promise(async (resolve, reject)=>{
+ let holdDocOrder = -999;
+ let sql = `
+ UPDATE Docs SET DocOrder = ${holdDocOrder}
+ WHERE DocOrder = ${docOrder1}
+ `;
+ await this.execCommandSql(sql);
+ sql = `
+ UPDATE Docs SET DocOrder = ${docOrder1}
+ WHERE DocOrder = ${docOrder2}
+ `;
+ await this.execCommandSql(sql);
+ sql = `
+ UPDATE Docs SET DocOrder = ${docOrder2}
+ WHERE DocOrder = ${holdDocOrder}
+ `;
+ await this.execCommandSql(sql);
+ resolve();
+ });
+ },
+
+ moveUpDoc: async function (fullPath){
+ // Get the selected location's DocOrder.
+ let docOrder1 = await this.getDocOrder(fullPath);
+ if (docOrder1 == 0) return; // Cannot go up from the highest point.
+ // Get the parent path to the passed fullPath.
+ let locations = fullPath.split("/");
+ let parentPath = locations.length > 1 ? locations.slice(0,locations.length-1).join("/") : "";
+ // Get the DocOrder number of the next highest doc location less than this one.
+ let docOrder2 = await this.getUpDocOrder(parentPath, docOrder1);
+ if (docOrder2){
+ await this.swapDocs(docOrder1, docOrder2);
+ await this.loadDocs();
+ this.dvDocuments.setSelectedPath(fullPath);
+ }
+ },
+
+ moveDownDoc: async function (fullPath){
+ // Get the selected location's DocOrder.
+ let docOrder1 = await this.getDocOrder(fullPath);
+ // Get the parent path to the passed fullPath.
+ let locations = fullPath.split("/");
+ let parentPath = locations.length > 1 ? locations.slice(0,locations.length-1).join("/") : "";
+ // Get the DocOrder number of the next highest doc location less than this one.
+ let docOrder2 = await this.getDownDocOrder(parentPath, docOrder1);
+ if (docOrder2){
+ await this.swapDocs(docOrder1, docOrder2);
+ await this.loadDocs();
+ this.dvDocuments.setSelectedPath(fullPath);
+ }
+ },
+
+ swapPagesByName: async function (fullPath, pageNameSrc, pageNameDst){
+ if (getDocChanged()){
+ await this.savePage();
+ }
+ let pageOrder1 = await this.getPageOrder(fullPath, pageNameSrc);
+ let pageOrder2 = await this.getPageOrder(fullPath, pageNameDst);
+ await this.swapPages(fullPath, pageOrder1, pageOrder2);
+ this.loadPages(fullPath)
+ .then(()=>{
+ this.selectPageByName(pageNameSrc);
+ });
+ },
+
+ movePageByName: async function(fullPath, pageNameSrc, docLocationDst){
+ if (getDocChanged()){
+ await this.savePage();
+ }
+ let movePageOrder = await this.getMaxPageOrder(docLocationDst);
+ let newPageName = await this.getUniquePageDocName(docLocationDst, pageNameSrc);
+ movePageOrder ++;
+ let sql = `
+ UPDATE Docs SET PageOrder = ${movePageOrder}, DocLocation = '${docLocationDst}',
+ DocName = '${newPageName}'
+ WHERE DocLocation = '${fullPath}' AND DocName = '${pageNameSrc}'
+ `;
+ await this.execCommandSql(sql);
+ await this.loadDocs();
+ this.dvDocuments.setSelectedPath(docLocationDst);
+ this.loadPages(docLocationDst)
+ .then(()=>{
+ this.selectPageByName(newPageName);
+ });
+ },
+
+ swapDocsByLocation: async function (docLocationSrc, docLocationDst){
+ if (getDocChanged()){
+ await this.savePage();
+ }
+ let docOrder1 = await this.getDocOrder(docLocationSrc);
+ let docOrder2 = await this.getDocOrder(docLocationDst);
+ await this.swapDocs(docOrder1, docOrder2);
+ await this.loadDocs();
+ this.dvDocuments.setSelectedPath(docLocationDst);
+ },
+
+ updateDocLocation: function (docLocation, docNewLocation){
+ let sql = `
+ UPDATE Docs SET DocLocation = REPLACE(DocLocation, '${docLocation}', '${docNewLocation}')
+ WHERE DocLocation LIKE '${docLocation}/%'
+ OR DocLocation = '${docLocation}'
+ `;
+ return this.execCommandSql(sql);
+ },
+
+ moveDocByLocation: async function (docLocationSrc, docLocationDst){
+ // Get the last portion of the source location.
+ if (getDocChanged()){
+ await this.savePage();
+ }
+ let srcLocations = docLocationSrc.split("/");
+ let srcLastPath = srcLocations[srcLocations.length-1];
+ let srcParentPath = srcLocations.length > 1 ? srcLocations.slice(0,srcLocations.length-1).join("/") : "";
+ let dstLocations = docLocationDst.split("/");
+ let dstParentPath = dstLocations.length > 1 ? dstLocations.slice(0,dstLocations.length-1).join("/") : "";
+ if (srcParentPath == dstParentPath) { // The doc is being dropped onto a doc in the same location.
+ await this.swapDocsByLocation(docLocationSrc, docLocationDst);
+ }
+ else { // The doc is being dropped into a different location.
+ let docNewLocation = docLocationDst != "" ? `${docLocationDst}/${srcLastPath}`: srcLastPath;
+ docNewLocation = await this.getUniqueDocName(docNewLocation);
+ await this.updateDocLocation(docLocationSrc, docNewLocation);
+ await this.loadDocs();
+ this.dvDocuments.setSelectedPath(docNewLocation);
+ }
+ },
+
+ execCommandSql: function (sql){
+ return (_settings.dbType == "MySql") ?
+ this.execCommandMySQL(sql) :
+ this.execCommandSqlite(sql);
+ },
+
+ //#endregion DATA TRANSMITTAL FUNCTIONS
+
+ //#region DOCUMENT MANAGEMENT FUNCTIONS
+ selectDocument: async function (docName) {
+ console.log(`Document ${docName} selected.`);
+ if (getDocChanged()){
+ await this.savePage();
+ }
+ this.loadPages(docName);
+ },
+
+ renameDoc_Clicked: function (docName) {
+ let el = this.dvDocuments.getSelectedElement();
+ this.showtxtRename(el);
+ this.renameTarget = "document";
+ },
+
+ renamePage_Clicked: function (fullPath, pageName) {
+ let el = this.getSelectedPageElement();
+ this.showtxtRename(el);
+ this.renameTarget = "page";
+ },
+
+ renamed: async function (){
+ let newName = this.txtRename.value;
+ if (this.renameTarget == "document"){
+ let newFullPath = await this.renameDoc(newName);
+ await this.loadDocs();
+ this.dvDocuments.setSelectedPath(newFullPath);
+ }
+ else{
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ let oldName = this.getSelectedPageName();
+ let newPageName = await this.renamePage(fullPath, oldName, newName);
+ this.loadPages(this.dvDocuments.getSelectedFullPath())
+ .then(()=>{
+ this.selectPageByName(newPageName);
+ });
+ }
+ this.txtRename.classList.add("hide");
+ },
+
+ renameDoc: function(newName){
+ return new Promise (async (resolve, reject) => {
+ // Create the new full path.
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ let oldName = this.dvDocuments.getSelectedElement().innerText;
+ let paths = fullPath.split("/");
+ paths[paths.length-1] = newName;
+ let newFullPath = paths.join("/");
+ console.log(newFullPath);
+ // Make sure the new full path does not exist.
+
+ let exists = await this.docNameExists(newFullPath);
+ if (exists == true){
+ ShowWarningMessageBox("Name already exists!");
+ reject("Name already exists!");
+ }
+ else{
+ await this.updateDocName(fullPath, newFullPath)
+ .catch((err)=>{reject(err);});
+ resolve(newFullPath);
+ return newFullPath;
+ }
+ });
+ },
+
+ renamePage: function (fullPath, oldName, newName){
+ return new Promise (async (resolve, reject) => {
+ let exists = await this.docPageExists(fullPath, newName);
+ if (exists == true){
+ ShowWarningMessageBox("Name already exists!");
+ reject("Name already exists!");
+ }
+ else{
+ await this.updatePageName(fullPath, oldName, newName)
+ .catch((err)=>{reject(err);});
+ resolve(newName);
+ return newName;
+ }
+ });
+ },
+
+ removeDoc_Clicked: function (docName) {
+ if (showConfirmationBox("Are you sure?\nNote: Deleting this document deletes all children.")){
+ this.deleteDoc(docName)
+ .then(()=>{
+ this.loadDocs(true);
+ });
+ }
+ },
+
+ removePage_Clicked: function (pageName) {
+ if (showConfirmationBox("Are you sure?")){
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ this.deletePage(fullPath, pageName)
+ .then(()=>{
+ this.loadPages(this.dvDocuments.getSelectedFullPath());
+ });
+ }
+ },
+
+ pageIncIndent_Clicked: function (pageName) {
+ let pageEl = this.getSelectedPageElement();
+ let marginL = parseInt(pageEl.style.marginLeft);
+ console.log(marginL);
+ if (marginL < 30){
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ let newMarginL = marginL + this.indentChange;
+ let indent = newMarginL / this.indentChange;
+ this.updatePageIndent(fullPath, pageName, indent);
+ pageEl.style.marginLeft = `${newMarginL}px`;
+ }
+ },
+
+ pageDecIndent_Clicked: function (pageName) {
+ let pageEl = this.getSelectedPageElement();
+ let marginL = parseInt(pageEl.style.marginLeft);
+ console.log(marginL);
+ if (marginL >= this.indentChange){
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ let newMarginL = marginL - this.indentChange;
+ let indent = newMarginL / this.indentChange;
+ this.updatePageIndent(fullPath, pageName, indent);
+ pageEl.style.marginLeft = `${newMarginL}px`;
+ }
+ },
+
+ pageMoveUp_Clicked: function (pageName) {
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ this.movePageUp(fullPath, pageName);
+ },
+
+ pageMoveDown_Clicked: function (pageName) {
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ this.movePageDown(fullPath, pageName);
+ },
+
+ moveUpDoc_Clicked: function (docName) {
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ this.moveUpDoc(fullPath, docName);
+ },
+
+ moveDownDoc_Clicked: function (docName) {
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ this.moveDownDoc(fullPath, docName);
+ },
+
+ swapPage_Dropped: function (pageNameSrc, pageNameDst) {
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ this.swapPagesByName(fullPath, pageNameSrc, pageNameDst);
+ },
+
+ movePage_Dropped: function (pageNameSrc, docLocationDst) {
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ this.movePageByName(fullPath, pageNameSrc, docLocationDst);
+ },
+
+ moveDoc_Dropped: function (docLocationSrc, docLocationDst) {
+ this.moveDocByLocation(docLocationSrc, docLocationDst);
+ },
+
+
+ selectPage: async function(el){
+ if (!el) return;
+ if (getDocChanged()) {
+ await this.savePage();
+ }
+ this.selectPageButton(el);
+ let fullPath = this.dvDocuments.getSelectedFullPath();
+ let pageName = el.innerHTML;
+ this.getPageNote(fullPath, pageName)
+ .then((data)=>{
+ if (data){
+ this.showPageData(data[0].DocText);
+ }
+ this.lastFullPath = fullPath;
+ })
+ .catch((err)=>{
+ console.log(err);
+ })
+ },
+
+ selectPageByName: function(name){
+ let pages = document.querySelectorAll("div[data-grp='page']");
+ let elSelect = {};
+ for (let el of pages){
+ if (el.innerHTML == name){
+ elSelect = el;
+ }
+ }
+ this.selectPage(elSelect);
+ },
+
+ btnPage_Clicked: function (el) {
+ this.selectPage(el);
+ },
+
+ btnPage_RtClicked: function (e) {
+ this.selectPage(e.target);
+ this.pageContextMenu(e);
+ },
+
+ //#endregion DOCUMENT MANAGEMENT FUNCTIONS
+
+ //#region HELPER FUNCTIONS
+ getSelectedPageName: function(){
+ let el = this.getSelectedPageElement();
+ if (el) return el.innerHTML;
+ return;
+ },
+
+ getSelectedPageElement: function(){
+ return document.querySelector("#lstDocs .selected");
+ },
+ //#endregion HELPER FUNCTIONS
+
+ //#region INITIALIZATION
+ init: function () {
+ this.lstDocuments = document.getElementById("lstDocuments");
+ this.txtRename = document.getElementById("txtRename");
+ this.txtDoc = document.getElementById("txtDoc");
+ this.dvDocuments = new div_treeview(this.lstDocuments, "/");
+ this.dvDocuments.onSelect((text)=> {
+ this.selectDocument(text);
+ });
+ this.dvDocuments.onRightClick((el, fullPath)=>{
+ this.docContextMenu(el, fullPath)
+ });
+ },
+ //#endregion INITIALIZATION
+
+}
+
+app_documents.init();
+
+//#region DOM EVENT HANDLERS
+document.getElementById("btnAddDoc").addEventListener("click", () => {
+ app_documents.addDocLocation("", "New Document");
+});
+
+document.getElementById("btnAddPage").addEventListener("click", () => {
+ app_documents.addPage_Clicked();
+});
+
+document.getElementById("txtRename").addEventListener("keyup", (e) =>{
+ if (e.key == "Enter"){
+ app_documents.renamed();
+ }
+});
+
+document.getElementById("txtRename").addEventListener("click", (e) =>{
+ e.stopPropagation();
+});
+
+document.getElementById("btnExpandAll").addEventListener("click", (e) =>{
+ app_documents.dvDocuments.expandAll();
+});
+
+document.getElementById("btnCollapseAll").addEventListener("click", (e) =>{
+ app_documents.dvDocuments.collapseAll();
+});
+
+//#region DRAG AND DROP EVENT HANDLERS
+
+document.addEventListener("dragstart", (e)=>{
+ if (e.target.classList.contains("pageItem")){
+ app_documents.draggedPageEl = e.target;
+ }
+ else if (e.target.classList.contains("div_treeview_item")) {
+ app_documents.draggedDocEl = e.target;
+ }
+ e.target.style.opacity = .5;
+});
+
+document.addEventListener("dragend", (e)=> {
+ // reset the transparency
+ e.target.style.opacity = "";
+});
+
+document.addEventListener("dragenter", (e)=> {
+ // highlight potential drop target when the draggable element enters it
+ if (app_documents.draggedPageEl){
+ if (e.target.classList.contains("pageItem")) e.target.classList.add("dragTarget");
+ if (e.target.classList.contains("div_treeview_item")) e.target.classList.add("dragTarget");
+ }
+ else if (app_documents.draggedDocEl){
+ if (e.target.classList.contains("div_treeview_item")) e.target.classList.add("dragTarget");
+ if (e.target.id == "btnAddDoc") e.target.classList.add("dragTarget");
+ }
+});
+
+document.addEventListener("dragleave", (e)=> {
+ // reset background of potential drop target when the draggable element leaves it
+ e.target.classList.remove("dragTarget");
+});
+
+document.addEventListener("dragover", (e)=> {
+ // prevent default to allow drop
+ e.preventDefault();
+});
+
+document.addEventListener("drop", (e)=> {
+ // move dragged elem to the selected drop target
+ if (app_documents.draggedPageEl){
+ if (e.target.classList.contains("pageItem")) {
+ app_documents.swapPage_Dropped(app_documents.draggedPageEl.innerText, e.target.innerText);
+ }
+ else if (e.target.classList.contains("div_treeview_item")){
+ console.log(app_documents.dvDocuments.getFullPath(e.target));
+ app_documents.movePage_Dropped(app_documents.draggedPageEl.innerText,
+ app_documents.dvDocuments.getFullPath(e.target));
+ }
+ }
+ else if (app_documents.draggedDocEl){
+ if (e.target.classList.contains("div_treeview_item")){
+ app_documents.moveDoc_Dropped(app_documents.dvDocuments.getFullPath(app_documents.draggedDocEl),
+ app_documents.dvDocuments.getFullPath(e.target));
+ }
+ else if (e.target.id == "btnAddDoc"){
+ app_documents.moveDoc_Dropped(app_documents.dvDocuments.getFullPath(app_documents.draggedDocEl),
+ "");
+ }
+ }
+ e.target.classList.remove("dragTarget");
+ // Reinitialize the dragged item references.
+ app_documents.draggedDocEl = undefined;
+ app_documents.draggedPageEl = undefined;
+ // prevent default action.
+ e.preventDefault();
+});
+
+//#endregion DRAG AND DROP EVENT HANDLERS
+
+// #region DOC CONTEXT MENU EVENT HANDLERS
+document.getElementById("btnAddSubDoc").addEventListener("click", ()=>{
+ app_documents.addDocLocation(app_documents.contextSelectedDoc,"New Document");
+});
+
+document.getElementById("btnRenameDoc").addEventListener("click", (e)=>{
+ app_documents.renameDoc_Clicked(app_documents.contextSelectedDoc);
+ e.stopPropagation();
+ document.querySelector(".docsMenu").classList.add("hide");
+});
+
+document.getElementById("btnRemoveDoc").addEventListener("click", (e)=>{
+ app_documents.removeDoc_Clicked(app_documents.contextSelectedDoc);
+});
+
+document.getElementById("btnPageRenameDoc").addEventListener("click", (e)=>{
+ app_documents.renamePage_Clicked(app_documents.contextSelectedPage);
+ e.stopPropagation();
+ document.querySelector(".pagesMenu").classList.add("hide");
+});
+
+document.getElementById("btnPageRemoveDoc").addEventListener("click", (e)=>{
+ app_documents.removePage_Clicked(app_documents.contextSelectedPage);
+});
+
+document.getElementById("btnPageIncIndent").addEventListener("click", (e)=>{
+ app_documents.pageIncIndent_Clicked(app_documents.contextSelectedPage);
+});
+
+document.getElementById("btnPageDecIndent").addEventListener("click", (e)=>{
+ app_documents.pageDecIndent_Clicked(app_documents.contextSelectedPage);
+});
+
+document.getElementById("btnPageMoveUp").addEventListener("click", (e)=>{
+ app_documents.pageMoveUp_Clicked(app_documents.contextSelectedPage);
+});
+
+document.getElementById("btnPageMoveDown").addEventListener("click", (e)=>{
+ app_documents.pageMoveDown_Clicked(app_documents.contextSelectedPage);
+});
+
+document.getElementById("btnMoveUpDoc").addEventListener("click", (e)=>{
+ app_documents.moveUpDoc_Clicked(app_documents.contextSelectedDoc);
+});
+
+document.getElementById("btnMoveDownDoc").addEventListener("click", (e)=>{
+ app_documents.moveDownDoc_Clicked(app_documents.contextSelectedDoc);
+});
+
+// #endregion DOC CONTEXT MENU EVENT HANDLERS
+
+
+//#endregion DOM EVENT HANDLERS
\ No newline at end of file
diff --git a/calendar.css b/calendar.css
index bc7ebf3..5acc340 100644
--- a/calendar.css
+++ b/calendar.css
@@ -27,6 +27,10 @@
user-select: none;
}
+input, button, textarea, :focus {
+ outline: none;
+}
+
#txtView, #txtView::after, #txtView::before {
-webkit-user-select: auto;
user-select: auto;
@@ -125,6 +129,11 @@ body {
width: 100%;
}
+#btnAddDoc {
+ justify-content: center;
+ align-items: center;
+}
+
.vbox {
display: flex;
flex-direction: column;
@@ -188,6 +197,18 @@ body {
margin-left: 0px !important;
}
+.overflowAuto {
+ overflow: auto;
+}
+
+.overflowHidden {
+ overflow: hidden;
+}
+
+.overflowScrollY {
+ overflow-y: scroll;
+}
+
/*#endregion LAYOUT STYLES */
textarea {
@@ -199,10 +220,12 @@ textarea {
/* #region BUTTONS */
.btn {
+ /*
display:flex;
flex-direction: column;
justify-content: center;
align-items: center;
+ */
background-color: var(--buttonface);
color: var(--buttontext);
padding: 5px;
@@ -228,6 +251,10 @@ textarea {
color: var(--buttonhovertext);
}
+.btnSmall {
+ width: 20px;
+}
+
/* #endregion BUTTONS */
/* #region TABS */
@@ -557,6 +584,7 @@ background: var(--buttonhover);
box-sizing: border-box;
background-color: var(--notesBGColor);
color: var(--notesTextColor);
+ box-shadow: none;
}
.notes .botButton {
@@ -656,6 +684,11 @@ background: var(--buttonhover);
color: var(--buttonhovertext);
}
+.selected {
+ background-color: var(--buttonhover) !important;
+ color: var(--buttonhovertext) !important;
+}
+
.tasksButton {
min-width: 70px;
}
@@ -810,6 +843,10 @@ background: var(--buttonhover);
right: 5px;
}
+.version {
+ font: 10px/1.1 "Helvetica Neue", Helvetica, Arial, san-serif;
+}
+
#settingsSlider {
box-sizing: border-box;
position: absolute;
@@ -826,6 +863,28 @@ background: var(--buttonhover);
-webkit-box-shadow: 0px 10px 10px rgba(0, 0, 0, 0.25);
}
+.boxedValue {
+ border: 1px;
+ height: 19px;
+ width: 160px;
+ border-style: solid;
+ border-color: black;
+ background-color: white;
+ border-top-left-radius: 2px;
+ border-top-right-radius: 2px;
+ border-bottom-left-radius: 2px;
+ border-bottom-right-radius: 2px;
+ padding-left: 5px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.browseButton {
+ height: 19px;
+ font: 10px/1.1 "Helvetica Neue", Helvetica, Arial, san-serif;
+ margin-left: 2px;
+}
+
/* #endregion SETTINGS BOX */
/* #region SEARCH RESULT PREVIEW */
@@ -860,6 +919,14 @@ background: var(--buttonhover);
margin: 5px 5px 5px 2px;
}
+.pagesMenu {
+ background-color: var(--notesMenuBGColor);
+ position: fixed;
+ width: 200px;
+ padding: 5px 5px 5px 5px;
+ margin: 5px 5px 5px 2px;
+}
+
.btnMenu {
background-color: var(--buttonface);
color: var(--buttontext);
@@ -885,3 +952,68 @@ background: var(--buttonhover);
color: var(--buttonhovertext);
}
/* #endregion CONTEXT MENU */
+
+/* #region DOCUMENTS */
+
+#txtRename {
+ position: fixed;
+ border: 1px;
+ background-color: var(--notesBGColor);
+ color: var(--notesTextColor);
+ border-style: solid;
+ border-color: lightgray;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+ padding-left: 5px;
+}
+
+.pageItem {
+ background-color: var(--compColor2);
+ color: #000;
+ padding: 5px;
+ text-align: center;
+ border-style: solid;
+ border-color: lightgray;
+ border-width: 1px;
+ min-height: 30px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.dragTarget {
+ background-color: var(--buttonhover) !important;
+}
+
+.dropNotAllowed {
+ cursor: not-allowed;
+}
+/* #endregion DOCUMENTS */
+
+/* #region WAIT IMAGE */
+/* The wait image is displayed in the center of the displaying window.*/
+#imgWaitImage {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width:100px;
+ height:100px;
+ -webkit-animation: 2s ease 0s normal forwards 1 fadein;
+ animation: 2s ease 0s normal forwards 1 fadein;
+ }
+
+ @keyframes fadein{
+ 0% { opacity:0; }
+ 90% { opacity:0; }
+ 100% { opacity:1; }
+ }
+
+ @-webkit-keyframes fadein{
+ 0% { opacity:0; }
+ 90% { opacity:0; }
+ 100% { opacity:1; }
+ }
+/* #endregion WAIT IMAGE */
\ No newline at end of file
diff --git a/calendar.js b/calendar.js
index 63186ee..e631ea3 100644
--- a/calendar.js
+++ b/calendar.js
@@ -1,6 +1,11 @@
-const { dialog } = require("electron").remote;
+//#region GLOBAL REFERENCES
+const {
+ dialog
+} = require("electron").remote;
const electron = require("electron");
-const { remote } = require("electron");
+const {
+ remote
+} = require("electron");
const ipc = require("electron").ipcRenderer;
const libAppSettings = require("lib-app-settings");
@@ -8,19 +13,25 @@ var mysql = require("mysql");
var sqlite3 = require("sqlite3").verbose();
var fs = require("fs");
var marked = require("marked");
+//#endregion GLOBAL REFERENCES
+//#region GLOBAL VARIABLES
var initialLoad = true;
var settingsShown = false;
var calRows = 5;
const settingsFile = "./.settings";
-const dbFile = "./.calendarNotes.db";
+var dbFile = "./.calendarNotes.db";
+var settingsdbFile = "./.calendarNotes.db";
var appSettings = new libAppSettings(settingsFile);
var monthDisplayed, daySelected, yearDisplayed;
var lastDaySelected;
+const APPDIR = electron.remote.app.getAppPath();
+var numWaiting = 0;
+
// These are placeholders that will be written over when the settings
// are read from the settings file.
var _settings = {
@@ -33,6 +44,7 @@ var _settings = {
var calChangeDate;
var blockInterface = false;
+//#endregion GLOBAL VARIABLES
// #region THEMES
var select = document.getElementById("selThemes");
@@ -88,18 +100,16 @@ var CALENDAR = function () {
label = wrap.find("#label");
wrap.find("#prev").bind("click.calender", function (ev) {
- if (ev.ctrlKey){
+ if (ev.ctrlKey) {
switchYear(false);
- }
- else{
+ } else {
switchMonth(false);
}
});
wrap.find("#next").bind("click.calender", function (ev) {
- if (ev.ctrlKey){
+ if (ev.ctrlKey) {
switchYear(true);
- }
- else{
+ } else {
switchMonth(true);
}
});
@@ -118,35 +128,53 @@ var CALENDAR = function () {
// Load the settings from the file.
await appSettings.loadSettingsFromFile()
- .then((settings)=>{
- document.getElementById("selThemes").selectedIndex = settings.themeIndex;
- changeTheme(settings.themeIndex, function () {
- initSettingsIcon();
- });
- document.getElementById("txtHost").value = settings.host;
- document.getElementById("txtPort").value = settings.port;
- document.getElementById("txtDatabase").value = settings.database;
- document.getElementById("txtUsername").value = settings.user;
- document.getElementById("txtPassword").value = settings.password;
- document.getElementById("chkDocuments").checked = settings.documents == true;
- settings.documents ? null : document.getElementById("btnDocs").classList.add("hide");
- _settings = settings;
- dateSelected(daySelected);
- //loadDocs();
- document.getElementById("leftSideBar").style.width = settings.leftSideBarWidth;
- document.getElementById("docsSideBar").style.width = settings.docsSideBarWidth;
- document.getElementById("optSqlite").checked = (settings.dbType == "Sqlite");
- document.getElementById("optMySql").checked = (settings.dbType == "MySql");
- updateDBSelection("opt" + settings.dbType);
- })
- .catch((err)=>{
+ .then(async (settings) => {
+ _settings = settings;
+ if (!_settings.dbFile) _settings.dbFile = dbFile;
+ dbFile = _settings.dbFile;
+ settingsdbFile = dbFile;
+ console.log(dbFile);
+ if (!fs.existsSync(dbFile)){
+ await createSqliteDB();
+ ShowWarningMessageBox("DB file not found. Creating it.");
+ }
+ document.getElementById("selThemes").selectedIndex = _settings.themeIndex;
+ changeTheme(settings.themeIndex, function () {
+ initSettingsIcon();
+ });
+ document.getElementById("txtHost").value = _settings.host;
+ document.getElementById("txtPort").value = _settings.port;
+ document.getElementById("txtDatabase").value = _settings.database;
+ document.getElementById("txtUsername").value = _settings.user;
+ document.getElementById("txtPassword").value = _settings.password;
+ document.getElementById("chkDocuments").checked = _settings.documents == true;
+ document.getElementById("chkSpelling").checked = _settings.spellChecking == true;
+ // Set spell checking.
+ setSpellChecking(_settings.spellChecking);
+ if (_settings.documents) {
+ document.getElementById("btnDocs").classList.remove("hide");
+ app_documents.loadDocs(true);
+ } else {
+ document.getElementById("btnDocs").classList.add("hide");
+ };
+ dateSelected(daySelected);
+ document.getElementById("leftSideBar").style.width = _settings.leftSideBarWidth;
+ document.getElementById("docsSideBar").style.width = _settings.docsSideBarWidth;
+ document.getElementById("optSqlite").checked = (_settings.dbType == "Sqlite");
+ document.getElementById("optMySql").checked = (_settings.dbType == "MySql");
+ updateDBSelection("opt" + _settings.dbType);
+ document.getElementById("lbldbFile").innerHTML = getFilename(_settings.dbFile);
+ document.getElementById("lbldbFile").title = _settings.dbFile;
+ })
+ .catch((err) => {
// Assume any error means the settings file does not exist and create it.
////alert("No settings found. Configure your settings.");
+ console.log(err);
ShowWarningMessageBox("No settings found. Assigning defaults.");
appSettings.setSettingsInFile(getSettingsfromDialog());
createSqliteDB();
- });
-
+ });
+
console.log("1" + document.querySelector(".curr").innerHTML);
}
@@ -162,7 +190,7 @@ var CALENDAR = function () {
year =
year || (next ? tempYear + 1 : tempYear - 1);
}
-
+
switchMonth(null, months.indexOf(curr[0]), year);
}
@@ -358,13 +386,15 @@ var CALENDAR = function () {
// #endregion CALENDAR OBJECT CODE
// #region DATABASE CODE
-function getRowsMySql(sql, callback){
+function getRowsMySql(sql, callback) {
console.log("SQL = " + sql);
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect();
connection.query(sql, function (err, rows, fields) {
+ hideWaitImage();
if (!err) {
- if (callback){
+ if (callback) {
callback(err, rows);
}
} else {
@@ -376,13 +406,13 @@ function getRowsMySql(sql, callback){
connection.end();
}
-function getRowsSqlite(sql, callback){
+function getRowsSqlite(sql, callback) {
console.log("SQL = " + sql);
let db = new sqlite3.Database(dbFile, (err) => {
if (!err) {
db.all(sql, [], (err, rows) => {
if (!err) {
- if (callback){
+ if (callback) {
callback(err, rows);
}
} else {
@@ -396,93 +426,108 @@ function getRowsSqlite(sql, callback){
});
}
-
-function getNotesMySQL(dateForDay, callback){
+function getNotesMySQL(dateForDay, callback) {
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect();
-
- var sqlQuery = "SELECT * from Notes where NoteDate = '" + dateForDay + "'";
+ var sqlQuery = `SELECT * from Notes where NoteDate = '${dateForDay}'`;
+ console.log(sqlQuery);
connection.query(sqlQuery, function (err, rows, fields) {
+ hideWaitImage();
+ console.log("Notes received");
if (!err) {
if (rows.length > 0) {
- if (callback){
+ if (callback) {
callback(err, rows[0].NoteText);
}
} else {
- if (callback){
+ if (callback) {
callback(err, " ");
}
return;
}
} else {
- if (callback){ callback(err); }
+ if (callback) {
+ callback(err);
+ }
return;
}
connection.end();
});
}
-function getNotesSqlite(dateForDay, callback){
+function getNotesSqlite(dateForDay, callback) {
+ console.log(dbFile);
+ console.log(dateForDay);
+ console.log(formatDateSqlite(dateForDay));
+ let sql = "SELECT * FROM Notes WHERE NoteDate = '" + formatDateSqlite(dateForDay) + "'";
+ console.log(sql);
let db = new sqlite3.Database(dbFile, (err) => {
if (!err) {
- db.all("SELECT * FROM Notes WHERE NoteDate = '" + formatDateSqlite(dateForDay) + "'", [], (err, rows) => {
+ db.all(sql, [], (err, rows) => {
if (!err) {
if (rows.length > 0) {
- if (callback){
+ if (callback) {
callback(err, rows[0].NoteText);
}
} else {
- if (callback){
+ if (callback) {
callback(err, " ");
}
return;
}
} else {
- if (callback){ callback(err); }
+ if (callback) {
+ callback(err);
+ }
return;
}
db.close();
});
} else {
console.error(err.message);
- if (callback){ callback(err); }
+ if (callback) {
+ callback(err);
+ }
return;
}
});
}
-function saveNotesMySql(dateForDay, notesText) {
+function saveNotesMySql(dateForDay, notesText, callback) {
//var noteExists = sqlNoteExists(dateForDay);
console.log("Saving notes = '" + notesText + "'");
if (notesText == "") notesText = " ";
sqlNoteExistsMySql(dateForDay, function (result) {
if (result) {
- updateNotesMySql(dateForDay, notesText, null);
+ updateNotesMySql(dateForDay, notesText, callback);
} else {
- insertNotesMySql(dateForDay, notesText, null);
+ insertNotesMySql(dateForDay, notesText, callback);
}
});
}
-function saveNotesSqlite(dateForDay, notesText) {
+function saveNotesSqlite(dateForDay, notesText, callback) {
//var noteExists = sqlNoteExists(dateForDay);
console.log("Saving notes = '" + notesText + "'");
if (notesText == "") notesText = " ";
sqlNoteExistsSqlite(dateForDay, function (result) {
if (result) {
- updateNotesSqlite(dateForDay, notesText, null);
+ updateNotesSqlite(dateForDay, notesText, callback);
} else {
- insertNotesSqlite(dateForDay, notesText, null);
+ insertNotesSqlite(dateForDay, notesText, callback);
}
});
}
function getTasksMySql(callback) {
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect();
- connection.query("SELECT * FROM TasksList LIMIT 1", (err, rows, fields) => {
+ connection.query("SELECT * FROM TasksList LIMIT 1", (err, rows, fields) => {
+ hideWaitImage();
if (!err) {
console.log(rows);
if (callback) {
@@ -504,54 +549,78 @@ function getTasksSqlite(callback) {
db.all("SELECT * FROM TasksList", [], (err, rows) => {
if (!err) {
if (rows.length > 0) {
- if (callback){
+ if (callback) {
callback(err, rows[0].TasksList);
}
} else {
- if (callback){
+ if (callback) {
callback(err, " ");
}
return;
}
} else {
- if (callback){ callback(err); }
+ if (callback) {
+ callback(err);
+ }
return;
}
db.close();
});
} else {
console.error(err.message);
- if (callback){ callback(err); }
+ if (callback) {
+ callback(err);
+ }
return;
}
});
}
-function createSqliteDB(callback){
+function createSqliteDB(callback) {
return new Promise((resolve, reject) => {
let db = new sqlite3.Database(dbFile, (err) => {
if (err) {
console.error(err.message);
reject();
} else {
- // Create the tables.
- var sql = `CREATE TABLE Notes (
- ID INTEGER PRIMARY KEY,
- NoteDate TEXT,
- NoteText TEXT,
- LastModified TEXT
- )`;
- db.run(sql);
- sql = `CREATE TABLE TasksList (
- ID INTEGER PRIMARY KEY,
- TasksList TEXT
- )`;
- db.run(sql);
- db.close();
- resolve();
- if (callback) callback();
+ (async (err) => {
+ // Create the tables.
+ var sql = `CREATE TABLE Notes (
+ ID INTEGER PRIMARY KEY,
+ NoteDate TEXT,
+ NoteText TEXT,
+ LastModified TEXT
+ )`;
+ await new Promise((resolve)=>{
+ db.run(sql,()=>{ resolve(); });
+ });
+ sql = `CREATE TABLE TasksList (
+ ID INTEGER PRIMARY KEY,
+ TasksList TEXT
+ )`;
+ await new Promise((resolve)=>{
+ db.run(sql,()=>{ resolve(); });
+ });
+ sql = `CREATE TABLE Docs (
+ ID INTEGER PRIMARY KEY,
+ DocName TEXT,
+ DocLocation TEXT,
+ DocColor INTEGER DEFAULT 16777215,
+ DocText TEXT,
+ LastModified TEXT,
+ DocIndentLevel INTEGER DEFAULT 0,
+ DocOrder INTEGER DEFAULT 0,
+ PageOrder INTEGER DEFAULT 0
+ )`;
+ await new Promise((resolve)=>{
+ db.run(sql,()=>{ resolve(); });
+ });
+ db.close(()=>{
+ resolve();
+ if (callback) callback();
+ });
+ })();
}
- console.log('Connected to the database.');
});
});
}
@@ -559,7 +628,7 @@ function createSqliteDB(callback){
// #region NOTES CODE
// Get the notes from the MySQL database.
-function loadNotes(notes){
+function loadNotes(notes) {
document.getElementById("txtNotes").value = notes;
if (!document.getElementById("btnViewText").classList.contains("btnSelected")) {
document.getElementById("txtView").innerHTML = marked(notes);
@@ -567,9 +636,9 @@ function loadNotes(notes){
}
function getNotes(dateForDay, callback) {
- return new Promise(function(resolve, reject){
+ return new Promise(function (resolve, reject) {
// Block the interface from acting on any input.
- if (_settings.dbType == "MySql"){
+ if (_settings.dbType == "MySql") {
getNotesMySQL(dateForDay, (err, notes) => {
if (!err) {
loadNotes(notes);
@@ -598,19 +667,24 @@ function getNotes(dateForDay, callback) {
}
function saveNotes(dateForDay, notesText) {
- //var noteExists = sqlNoteExists(dateForDay);
- console.log("Saving notes = '" + notesText + "'");
- if (notesText == "") notesText = " ";
- if (_settings.dbType == "MySql"){
- saveNotesMySql(dateForDay, notesText);
- }
- else {
- saveNotesSqlite(dateForDay, notesText);
- }
- document.getElementById("btnSave").innerHTML = "SAVE";
+ return new Promise(function (resolve, reject) {
+ console.log("Saving notes = '" + notesText + "'");
+ if (notesText == "") notesText = " ";
+ if (_settings.dbType == "MySql") {
+ saveNotesMySql(dateForDay, notesText, () => {
+ resolve();
+ });
+ } else {
+ saveNotesSqlite(dateForDay, notesText, () => {
+ resolve();
+ });
+ }
+ document.getElementById("btnSave").innerHTML = "SAVE";
+ });
}
function updateNotesMySql(dateForDay, notesText, callback) {
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect(function (err) {
if (err) throw err;
@@ -620,6 +694,7 @@ function updateNotesMySql(dateForDay, notesText, callback) {
console.log("Executing SQL query = " + sql);
connection.query(sql, function (err, result) {
+ hideWaitImage();
if (err) throw err;
console.log(result.affectedRows + " record(s) updated");
if (callback) callback(err, result);
@@ -646,6 +721,7 @@ function updateNotesSqlite(dateForDay, notesText, callback) {
}
function insertNotesMySql(dateForDay, notesText, callback) {
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect(function (err) {
if (err) throw err;
@@ -656,6 +732,7 @@ function insertNotesMySql(dateForDay, notesText, callback) {
console.log("Executing SQL query = " + sql);
connection.query(sql, function (err, result) {
+ hideWaitImage();
if (err) throw err;
if (callback) callback(err, result);
connection.end();
@@ -674,7 +751,7 @@ function insertNotesSqlite(dateForDay, notesText, callback) {
db.run(sql, (err) => {
if (err) throw err;
- if (callback) callback(err, result);
+ if (callback) callback(err, "success");
});
db.close();
});
@@ -682,6 +759,7 @@ function insertNotesSqlite(dateForDay, notesText, callback) {
function sqlNoteExistsMySql(dateForDay, callback) {
var retValue = false;
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect();
console.log(
@@ -694,6 +772,7 @@ function sqlNoteExistsMySql(dateForDay, callback) {
connection.query(
"SELECT * from Notes where NoteDate = '" + dateForDay + "'",
function (err, rows, fields) {
+ hideWaitImage();
if (!err) {
console.log("Rows found = " + rows.length);
console.log("Returning = " + (rows.length > 0));
@@ -732,24 +811,24 @@ function sqlNoteExistsSqlite(dateForDay, callback) {
return retValue;
}
-function saveTasksMySql(tasksText) {
+function saveTasksMySql(tasksText, callback) {
//var noteExists = sqlNoteExists(dateForDay);
sqlTasksExistsMySql(function (result) {
if (result) {
- updateTasksMySql(tasksText, null);
+ updateTasksMySql(tasksText, callback);
} else {
- insertTasksMySql(tasksText, null);
+ insertTasksMySql(tasksText, callback);
}
});
}
-function saveTasksSqlite(tasksText) {
+function saveTasksSqlite(tasksText, callback) {
//var noteExists = sqlNoteExists(dateForDay);
sqlTasksExistsSqlite(function (result) {
if (result) {
- updateTasksSqlite(tasksText, null);
+ updateTasksSqlite(tasksText, callback);
} else {
- insertTasksSqlite(tasksText, null);
+ insertTasksSqlite(tasksText, callback);
}
});
}
@@ -761,21 +840,50 @@ function showNoteMarkdown() {
console.log("show notesText = " + notesText.value);
var customMods = notesText.value;
console.log("Loading markdown view.");
-
+
// Replace all check marks with their respective images.
- // ~_~ =
- // ~X~ =
- customMods = customMods.replace(/\|_\|/g, "
")
- customMods = customMods.replace(/\|X\|/g, "
")
+ // |_| =
+ // |X| =
+ var checkedSrc = "
";
+ var uncheckedSrc = "
";
+ if (_settings.themeIndex == 5) {
+ checkedSrc = "
";
+ uncheckedSrc = "
"
+ } else if (_settings.themeIndex == 6) {
+ checkedSrc = "
";
+ uncheckedSrc = "
"
+ }
+ customMods = customMods.replace(/\|X\|/g, checkedSrc);
+ customMods = customMods.replace(/\|_\|/g, uncheckedSrc);
var markedNote = marked(customMods);
viewDiv.innerHTML = markedNote;
- //marked(notesText.value, () => {
- //viewDiv.classList.remove("hide");
- //});
+}
+function showPageMarkdown() {
+ var viewDiv = document.getElementById("txtDocView");
+ var notesText = document.getElementById("txtDoc");
+ var customMods = notesText.value;
+ // Replace all check marks with their respective images.
+ // |_| =
+ // |X| =
+ var checkedSrc = "
";
+ var uncheckedSrc = "
";
+ if (_settings.themeIndex == 5) {
+ checkedSrc = "
";
+ uncheckedSrc = "
"
+ } else if (_settings.themeIndex == 6) {
+ checkedSrc = "
";
+ uncheckedSrc = "
"
+ }
+ customMods = customMods.replace(/\|X\|/g, checkedSrc);
+ customMods = customMods.replace(/\|_\|/g, uncheckedSrc);
+
+ var markedNote = marked(customMods);
+
+ viewDiv.innerHTML = markedNote;
}
function hideAllViews() {
@@ -791,11 +899,12 @@ function notesViewSelected() {
document.getElementById("btnViewText").classList.add("btnSelected");
document.getElementById("btnViewMD").classList.remove("btnSelected");
if (document.getElementById("btnDocs").classList.contains("tabSelected")) {
+ document.getElementById("divDocsView").classList.remove("hide");
document.getElementById("txtDocArea").classList.remove("hide");
+ document.getElementById("txtDocView").classList.add("hide");
} else {
document.getElementById("txtNotesArea").classList.remove("hide");
}
-
}
function mdViewSelected() {
@@ -803,11 +912,14 @@ function mdViewSelected() {
document.getElementById("btnViewText").classList.remove("btnSelected");
document.getElementById("btnViewMD").classList.add("btnSelected");
if (document.getElementById("btnDocs").classList.contains("tabSelected")) {
+ document.getElementById("divDocsView").classList.remove("hide");
document.getElementById("txtDocView").classList.remove("hide");
+ document.getElementById("txtDocArea").classList.add("hide");
} else {
document.getElementById("txtView").classList.remove("hide");
}
showNoteMarkdown();
+ showPageMarkdown();
}
function docsViewSelected() {
@@ -834,15 +946,15 @@ function docsViewUnselected() {
// #endregion NOTES CODE
// #region TASKS CODE
-function loadTasks(tasks){
+function loadTasks(tasks) {
document.getElementById("txtTasks").value = tasks;
}
// Get the tasks from the database.
function getTasks() {
- return new Promise(function(resolve, reject){
- // Block the interface from acting on any input.
- if (_settings.dbType == "MySql"){
+ return new Promise(function (resolve, reject) {
+ // Block the interface from acting on any input.
+ if (_settings.dbType == "MySql") {
getTasksMySql((err, tasks) => {
if (!err) {
loadTasks(tasks);
@@ -871,24 +983,30 @@ function getTasks() {
}
function saveTasks(tasksText) {
- //var noteExists = sqlNoteExists(dateForDay);
- if (_settings.dbType == "MySql"){
- saveTasksMySql(tasksText);
- }
- else {
- saveTasksSqlite(tasksText);
- }
+ return new Promise(function (resolve, reject) {
+ if (_settings.dbType == "MySql") {
+ saveTasksMySql(tasksText, () => {
+ resolve();
+ });
+ } else {
+ saveTasksSqlite(tasksText, () => {
+ resolve();
+ });
+ }
+ });
}
function updateTasksMySql(tasksText, callback) {
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect(function (err) {
if (err) throw err;
var sql =
"UPDATE TasksList SET TasksList = '" + sqlSafeText(tasksText) + "'";
console.log("Executing SQL query = " + sql);
-
connection.query(sql, function (err, result) {
+ hideWaitImage();
+ console.log("4");
if (err) throw err;
console.log(result.affectedRows + " record(s) updated");
if (callback) callback(err, result);
@@ -913,6 +1031,7 @@ function updateTasksSqlite(tasksText, callback) {
}
function insertTasksMySql(tasksText, callback) {
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect(function (err) {
if (err) throw err;
@@ -921,6 +1040,7 @@ function insertTasksMySql(tasksText, callback) {
console.log("Executing SQL query = " + sql);
connection.query(sql, function (err, result) {
+ hideWaitImage();
if (err) throw err;
if (callback) callback(err, result);
connection.end();
@@ -947,11 +1067,13 @@ function insertTasksSqlite(tasksText, callback) {
function sqlTasksExistsMySql(callback) {
var retValue = false;
+ showWaitImage();
var connection = mysql.createConnection(_settings);
connection.connect();
console.log("Searching for Tasks : " + "SELECT * from TasksList");
connection.query("SELECT * from TasksList", function (err, rows, fields) {
+ hideWaitImage();
if (!err) {
console.log("Rows found = " + rows.length);
console.log("Returning = " + (rows.length > 0));
@@ -986,255 +1108,13 @@ function sqlTasksExistsSqlite(callback) {
// #endregion TASKS CODE
-// #region DOCS CODE
-
-var lstDocuments = document.getElementById("lstDocuments");
-var dvDocuments = new div_treeview(lstDocuments, "/");
-var documentSelected = function (text){
- selectDocument(text);
-}
-dvDocuments.onSelect(documentSelected);
-var onDocContextMenu = function (el, fullPath){
- docContextMenu(el, fullPath);
-}
-dvDocuments.onRightClick(onDocContextMenu);
-
-// :: TODO ::
-
-// Show Doc context menu
-var contextSelectedDoc;
-function docContextMenu (el, fullPath){
- contextSelectedDoc = fullPath;
- console.log(el.clientX);
- var menu = document.querySelector(".docsMenu");
- menu.style.left = el.clientX + "px";
- menu.style.top = el.clientY + "px";
- menu.classList.remove("hide");
- console.log(menu);
-}
-
-// Select doc
-function selectDocument(docName){
- loadPages(docName);
-}
-
-// Load docs
-function loadDocs(){
- // Load treeview with the documents.
- var connection = mysql.createConnection(_settings);
- connection.connect();
- connection.query(
- "SELECT DISTINCT DocLocation from Docs", function (err, data) {
- if (err) throw err;
- console.log(data);
- for(var i=0; i
+ - diff --git a/rename.js b/rename.js deleted file mode 100644 index 7e96a33..0000000 --- a/rename.js +++ /dev/null @@ -1,22 +0,0 @@ -const electron = require("electron"); -const remote = require('electron').remote; -const libAppSettings = require("lib-app-settings"); - -const settingsFile = "./.settings"; -var appSettings = new libAppSettings(settingsFile); - -document.getElementById("btnCancel").addEventListener("click", () =>{ - var window = remote.getCurrentWindow(); - window.close(); -}); - -function init(){ - await appSettings.loadSettingsFromFile() - .then((settings)=>{ - changeTheme(settings.themeIndex); - }) - .catch((err)=>{ - }); -} - -init(); \ No newline at end of file diff --git a/renameItem.html b/renameItem.html deleted file mode 100644 index e0a59cd..0000000 --- a/renameItem.html +++ /dev/null @@ -1,40 +0,0 @@ - - - -
- -
- - - -
-