2019-07-19 20:51:57 +00:00
|
|
|
<template>
|
2019-12-20 05:50:50 +00:00
|
|
|
<!-- change class to .master-note-edit to have it popup on the screen -->
|
2019-07-29 07:22:47 +00:00
|
|
|
<div
|
2019-12-20 05:50:50 +00:00
|
|
|
id="InputNotes"
|
|
|
|
class="master-note-edit"
|
2019-07-29 07:22:47 +00:00
|
|
|
@keyup.esc="close"
|
2020-03-11 03:47:07 +00:00
|
|
|
:class="[{ 'size-down':(sizeDown == true), 'full-focus':(fullFocusEditor) }, 'position-'+position ]"
|
2019-12-20 05:50:50 +00:00
|
|
|
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}"
|
2019-07-29 07:22:47 +00:00
|
|
|
>
|
2019-09-10 18:10:11 +00:00
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
<div class="input-container-wrapper">
|
|
|
|
|
2019-12-20 05:50:50 +00:00
|
|
|
<!-- Loading indicator -->
|
2019-09-10 18:10:11 +00:00
|
|
|
<div v-if="loading" class="loading-note">
|
|
|
|
<div class="ui active dimmer">
|
2019-12-20 05:50:50 +00:00
|
|
|
<div class="ui text loader">{{loadingMessage}}</div>
|
2019-09-10 18:10:11 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
<div class="note-menu">
|
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
<nm-button v-on:click.native="close" icon="close" />
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
<nm-button v-on:click.native="toggleList('ol')" icon="list ol" />
|
|
|
|
|
|
|
|
<nm-button v-on:click.native="toggleList('ul')" icon="tasks" />
|
|
|
|
|
|
|
|
<nm-button v-on:click.native="toggleBold()" icon="bold" />
|
|
|
|
|
|
|
|
<nm-button v-on:click.native="toggleItalic()" icon="quote left" />
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
<nm-button v-on:click.native="modifyFont('1.4em')" icon="text height" />
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
<nm-button v-on:click.native="undoCustom()" icon="undo" />
|
|
|
|
|
|
|
|
<nm-button v-if="usersOnNote > 1" icon="green user circle" />
|
|
|
|
|
|
|
|
<nm-button icon="ellipsis horizontal" v-on:click.native="showNoteOptions = !showNoteOptions" />
|
2019-09-10 18:10:11 +00:00
|
|
|
</div>
|
2019-07-29 07:22:47 +00:00
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
<!-- Squire box grows -->
|
|
|
|
<div class="note-wrapper">
|
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
<textarea
|
|
|
|
ref="titleTextarea"
|
|
|
|
v-on:keyup="titleResize"
|
|
|
|
v-on:keydown="titleResize"
|
|
|
|
@keydown.enter.exact.prevent="editor.focus()"
|
|
|
|
rows="1"
|
|
|
|
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}"
|
|
|
|
v-on:blur="save" type="text" v-model="noteTitle" placeholder="Title" class="stealth-input">
|
|
|
|
</textarea>
|
|
|
|
|
|
|
|
<div v-show="isDecrypted" id="squire-id" class="squire-box" ref="squirebox" placeholder="Note Text"></div>
|
|
|
|
|
|
|
|
<!-- Decrypt note prompt -->
|
|
|
|
<div v-if="isEncrypted && !isDecrypted" class="ui basic padded segment">
|
|
|
|
<div class="ui raised segment">
|
|
|
|
<h3 class="ui center aligned icon header">
|
|
|
|
<i class="green lock alternate icon"></i>
|
|
|
|
|
|
|
|
<span v-if="!lockedOut" :data-tooltip="`Unlock Attempts: ${decryptAttempts}`">
|
|
|
|
This note is encrypted and requires a password to be opened.
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<!-- note is locked for 5 minutes -->
|
|
|
|
<span v-if="lockedOut">
|
|
|
|
To many unlock attempts. Note is locked for 5 minutes.
|
|
|
|
</span>
|
|
|
|
</h3>
|
|
|
|
<!-- Decrypt note -->
|
|
|
|
<div class="ui form" v-if="!lockedOut">
|
|
|
|
<h5 class="ui horizontal divider header" v-if="passwordHint && passwordHint.length > 0">
|
|
|
|
Hint: {{ passwordHint }}
|
|
|
|
</h5>
|
|
|
|
<div class="field">
|
|
|
|
<input :name="`randomThing-${noteid}`" :id="`yupper-${noteid}`"type="password" v-model="password" placeholder="Note Password" v-on:keyup.enter="decryptNote" autofocus ref="decryptNotePrompt">
|
|
|
|
</div>
|
|
|
|
<div class="field">
|
|
|
|
<div v-on:click="decryptNote" class="ui green fluid button" v-if="password.length >= 3">
|
|
|
|
Unlock Note
|
|
|
|
</div>
|
|
|
|
<div class="ui disabled fluid button" v-if="password.length < 3">
|
|
|
|
Unlock Note
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
|
|
|
|
<!-- bottom stats -->
|
|
|
|
<div class="ui basic segment">
|
|
|
|
<div class="ui grid">
|
|
|
|
<div class="sixteen wide column">
|
2020-03-13 23:34:32 +00:00
|
|
|
|
|
|
|
<div class="ui basic button"v-if="!isEncrypted" v-on:click="passwordEnterVisible = true">
|
|
|
|
<i class="shield alternate icon"></i>
|
|
|
|
Password Protect
|
|
|
|
</div>
|
|
|
|
<div class="ui icon basic button" v-if="isEncrypted && isDecrypted" v-on:click="disableEncryption">
|
|
|
|
<i class="unlock icon"></i>
|
|
|
|
Remove Password
|
|
|
|
</div>
|
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
<div class="ui basic compact button">
|
|
|
|
Status: {{ statusText }}
|
|
|
|
</div>
|
|
|
|
<div class="ui basic compact button" :data-tooltip="`Created: ${$helpers.timeAgo(created)}`">
|
|
|
|
Last Change: {{ $helpers.timeAgo(updated) }}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-03-13 23:34:32 +00:00
|
|
|
|
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
</div>
|
2020-02-01 22:21:22 +00:00
|
|
|
|
|
|
|
<!-- && this.$store.getters.getIsUserOnMobile -->
|
|
|
|
<span class="note-status-indicator" v-on:click="save()" v-if="statusText != 'Saved' && $store.getters.getIsUserOnMobile">
|
|
|
|
<div class="ui green button">{{statusText}}</div>
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<!-- Note options on the bottom of note -->
|
|
|
|
<div class="all-settings" :class="{ 'low-settings':!extraToolbarsVisible }">
|
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
<div class="note-menu shrink-icons-on-mobile">
|
2020-02-01 22:21:22 +00:00
|
|
|
|
|
|
|
<!-- Pin Button -->
|
2020-02-10 17:44:43 +00:00
|
|
|
<nm-button
|
|
|
|
v-on:click.native="onToggleArchived"
|
|
|
|
:icon="(archived == 1)?'green archive':'archive'"
|
|
|
|
:text="(archived == 1)?'Archived':'Archive'"
|
|
|
|
tip="Show in archive"
|
|
|
|
:showText="true"
|
|
|
|
></nm-button>
|
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
<!-- archive button -->
|
2020-02-10 17:44:43 +00:00
|
|
|
<nm-button
|
|
|
|
v-on:click.native="onTogglePinned"
|
|
|
|
:icon="(pinned == 1)?'green pin':'pin'"
|
|
|
|
:text="(pinned == 1)?'Pinned':'Pin'"
|
|
|
|
tip="Pin to top of list"
|
|
|
|
:showText="true"
|
|
|
|
></nm-button>
|
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
<!-- colors button -->
|
2020-02-10 17:44:43 +00:00
|
|
|
<nm-button
|
|
|
|
v-on:click.native="showColorPicker"
|
|
|
|
icon="paint brush"
|
|
|
|
text="Colors"
|
|
|
|
tip="Colors"
|
|
|
|
></nm-button>
|
|
|
|
|
|
|
|
<!-- add images panel -->
|
|
|
|
<nm-button
|
|
|
|
v-on:click.native="showFilesSideMenu = !showFilesSideMenu"
|
|
|
|
icon="image"
|
|
|
|
text="Images"
|
|
|
|
tip="Images"
|
|
|
|
></nm-button>
|
|
|
|
|
|
|
|
<!-- Tags -->
|
|
|
|
<nm-button
|
2020-02-18 22:52:12 +00:00
|
|
|
v-on:click.native="showTagSlideMenu = !showTagSlideMenu; modified = true"
|
2020-02-10 17:44:43 +00:00
|
|
|
icon="tags"
|
|
|
|
text="Tags"
|
|
|
|
tip="Tags"
|
|
|
|
></nm-button>
|
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
<!-- file upload button -->
|
2020-02-10 17:44:43 +00:00
|
|
|
<file-upload-button
|
|
|
|
class="nm-button"
|
|
|
|
:noteId="noteid" />
|
|
|
|
|
|
|
|
<!-- files button -->
|
|
|
|
<nm-button
|
|
|
|
v-on:click.native="openEditAttachment"
|
|
|
|
icon="folder"
|
|
|
|
text="Files"
|
|
|
|
tip="Files on Note"
|
|
|
|
:showText="true"
|
|
|
|
></nm-button>
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-01-03 01:26:55 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Side slide menus for colors, tags, images and other options -->
|
2020-02-10 17:44:43 +00:00
|
|
|
|
|
|
|
<side-slide-menu v-if="colorPickerVisible" v-on:close="colorPickerVisible = false" name="colors">
|
|
|
|
<color-picker
|
|
|
|
@changeColor="onChangeColor"
|
|
|
|
@close="onCloseColorChanger"
|
|
|
|
:style-object="styleObject"
|
|
|
|
/>
|
|
|
|
</side-slide-menu>
|
|
|
|
|
2020-02-10 22:21:06 +00:00
|
|
|
<side-slide-menu v-if="showTagSlideMenu" v-on:close="showTagSlideMenu = false" name="tags" :style-object="styleObject">
|
2020-02-10 17:44:43 +00:00
|
|
|
<div class="ui basic segment">
|
|
|
|
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
|
|
|
|
</div>
|
|
|
|
</side-slide-menu>
|
|
|
|
|
2020-02-10 22:21:06 +00:00
|
|
|
<side-slide-menu v-if="showFilesSideMenu" v-on:close="showFilesSideMenu = false" name="images" :style-object="styleObject">
|
2020-02-10 17:44:43 +00:00
|
|
|
<div class="ui basic segment">
|
|
|
|
<simple-attachment-note
|
|
|
|
v-on:close="showFilesSideMenu = false"
|
|
|
|
:note-id="noteid"
|
|
|
|
:squire-editor="editor">
|
|
|
|
</simple-attachment-note>
|
|
|
|
</div>
|
|
|
|
</side-slide-menu>
|
|
|
|
|
2020-02-10 22:21:06 +00:00
|
|
|
<side-slide-menu v-if="showNoteOptions" v-on:close="showNoteOptions = false" name="note-options" :style-object="styleObject">
|
2020-02-10 17:44:43 +00:00
|
|
|
<div class="ui basic padded segment">
|
|
|
|
<div class="ui grid">
|
|
|
|
<div class="sixteen wide column">
|
|
|
|
<h2>Additional Note Options</h2>
|
|
|
|
</div>
|
|
|
|
<div class="sixteen wide column">
|
|
|
|
<div class="ui labeled icon fluid basic button" v-on:click="sortList">
|
|
|
|
<i class="sort amount up icon"></i>
|
|
|
|
Sort List items (Move checked to bottom)
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="eight wide column">
|
|
|
|
<div class="ui labeled icon fluid basic button" v-on:click="deleteCompletedListItems">
|
|
|
|
<i class="trash icon"></i>
|
|
|
|
Delete Checked Items
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="eight wide column">
|
|
|
|
<div class="ui labeled icon fluid basic button" v-on:click="uncheckAllListItems">
|
|
|
|
<i class="list ul icon"></i>
|
|
|
|
Uncheck all Checked items
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-02-19 00:31:18 +00:00
|
|
|
<div class="eight wide column">
|
2020-02-10 17:44:43 +00:00
|
|
|
<div class="ui labeled icon fluid basic button" v-on:click="undoCustom">
|
|
|
|
<i class="undo icon"></i>
|
|
|
|
Undo last change
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-02-19 00:31:18 +00:00
|
|
|
<div class="eight wide column">
|
|
|
|
<div class="ui labeled icon fluid basic button" v-on:click="calculateMath" data-tooltip="Calculates algebra before '='">
|
|
|
|
<i class="calculator icon"></i>
|
|
|
|
Simple Math
|
|
|
|
</div>
|
|
|
|
</div>
|
2020-02-10 17:44:43 +00:00
|
|
|
<div class="sixteen wide column" v-if="rawTextId > 0">
|
|
|
|
<share-note-component
|
|
|
|
:note-id="noteid"
|
|
|
|
:raw-text-id="rawTextId"
|
|
|
|
:share-username="shareUsername"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</side-slide-menu>
|
2019-07-19 20:51:57 +00:00
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
<side-slide-menu v-if="passwordEnterVisible" v-on:close="passwordEnterVisible = false" :fullShadow="true" name="encrypt note">
|
|
|
|
<div class="ui basic segment" v-if="isDecrypted && isEncrypted">
|
|
|
|
<p>Note Decrypted</p>
|
|
|
|
<div class="ui green button" v-on:click="lockNote">Lock Note</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-if="!isEncrypted" class="ui basic segment">
|
|
|
|
|
|
|
|
<div class="ui top attached segment">
|
|
|
|
|
|
|
|
<h2><i class="green lock alternate icon"></i>Password protect this Note</h2>
|
|
|
|
<p>Password protection will prevent anyone from reading the text of this note, unless they enter the correct password.</p>
|
|
|
|
<p><b>Only the note text is protected. Title, tags, and files are not encrypted and remain visible without a password.</b></p>
|
|
|
|
<p>The password you select will only be used for this note. You can use the same password on multiple notes. The note will be encrypted using the password entered. A longer password is will be more secure.</p>
|
|
|
|
<h4><i class="red icon exclamation triangle"></i> Warning. There is no way to recover a lost password.</h4>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="ui bottom attached segment">
|
|
|
|
<div class="ui form">
|
|
|
|
<div class="field">
|
|
|
|
<label>New Password to lock this note</label>
|
|
|
|
<input :name="`randomThing-${noteid}`" :id="`yupper-${noteid}`"type="password" v-model="password" placeholder="Note Password">
|
|
|
|
</div>
|
|
|
|
<div class="field" v-if="password.length > 3">
|
|
|
|
<label>Confirm Password</label>
|
|
|
|
<input :name="`randomStuff-${noteid}`" :id="`heckye-${noteid}`"type="password" v-model="passwordConfirm" placeholder="Confirm Password">
|
|
|
|
</div>
|
|
|
|
<div class="field" v-if="password.length > 3">
|
|
|
|
<label>Password Hint - visible when unlocking note</label>
|
|
|
|
<input :name="`randomStuzz-${noteid}`" :id="`heckyo-${noteid}`"type="text" v-model="passwordHint" placeholder="Optional Password Hint" v-on:keyup.enter="enableEncryption">
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="field" v-if="passwordConfirm.length > 3 && password != passwordConfirm">
|
|
|
|
<div v-on:click="enableEncryption" class="ui disabled green button">
|
|
|
|
Passwords do not match
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="field" v-if="passwordConfirm.length > 3 && password == passwordConfirm">
|
|
|
|
<div v-on:click="enableEncryption" class="ui green button">
|
|
|
|
Protect!
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</side-slide-menu>
|
|
|
|
|
|
|
|
<!-- <div class="full-focus-shade"></div> -->
|
2020-03-11 03:47:07 +00:00
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
|
|
import axios from 'axios'
|
2020-03-13 23:34:32 +00:00
|
|
|
const crypto = require('crypto')
|
2020-02-01 22:21:22 +00:00
|
|
|
const DiffMatchPatch = require('../../../server/helpers/DiffMatchPatch')
|
2019-07-19 20:51:57 +00:00
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'InputNotes',
|
2019-07-20 23:07:22 +00:00
|
|
|
props: [ 'noteid', 'position' ],
|
2019-07-19 20:51:57 +00:00
|
|
|
components:{
|
2020-02-10 17:44:43 +00:00
|
|
|
'note-tag-edit': () => import('@/components/NoteTagEdit.vue'),
|
|
|
|
'color-picker': () => import('@/components/ColorPicker.vue'),
|
|
|
|
'file-upload-button': () => import('@/components/FileUploadButton.vue'),
|
|
|
|
// 'delete-button': () => import('@/components/NoteDeleteButtonComponent.vue'),
|
|
|
|
'side-slide-menu': () => import('@/components/SideSlideMenuComponent.vue'),
|
|
|
|
'simple-attachment-note': () => import('@/components/SimpleAttachmentNoteComponent.vue'),
|
|
|
|
'share-note-component': () => import('@/components/ShareNoteComponent.vue'),
|
|
|
|
|
|
|
|
'nm-button':require('@/components/NoteMenuButtonComponent.vue').default
|
2019-07-19 20:51:57 +00:00
|
|
|
},
|
|
|
|
data(){
|
|
|
|
return {
|
2019-09-10 18:10:11 +00:00
|
|
|
loading: true,
|
|
|
|
loadingMessage: 'Loading Note',
|
2019-07-19 20:51:57 +00:00
|
|
|
currentNoteId: 0,
|
2020-02-11 21:11:14 +00:00
|
|
|
modified: false,
|
2019-07-19 20:51:57 +00:00
|
|
|
noteText: '',
|
2020-03-13 23:34:32 +00:00
|
|
|
noteTitle: '',
|
2020-02-10 17:44:43 +00:00
|
|
|
rawTextId: 0,
|
2020-02-26 05:35:43 +00:00
|
|
|
created: '',
|
|
|
|
updated: '',
|
2020-02-10 17:44:43 +00:00
|
|
|
shareUsername: null,
|
2020-02-01 22:21:22 +00:00
|
|
|
diffNoteText: '',
|
2019-07-29 07:22:47 +00:00
|
|
|
statusText: 'Saved',
|
2019-07-19 20:51:57 +00:00
|
|
|
lastNoteHash: null,
|
2019-07-24 18:06:50 +00:00
|
|
|
saveDebounce: null, //Prevent save from being called numerous times quickly
|
2019-07-19 20:51:57 +00:00
|
|
|
updated: 'Never',
|
|
|
|
editDebounce: null,
|
2020-02-01 22:21:22 +00:00
|
|
|
emitChangeDebounce: null,
|
2019-12-20 05:50:50 +00:00
|
|
|
keyPressesCounter: 0, //Determen keys pressed between saves
|
2019-09-10 18:10:11 +00:00
|
|
|
pinned: 0,
|
|
|
|
archived: 0,
|
2019-12-20 05:50:50 +00:00
|
|
|
attachmentCount: 0,
|
|
|
|
styleObject: { 'noteText':null,'noteBackground':null, 'noteIcon':null, 'iconColor':null }, //Style object. Determines colors and badges
|
|
|
|
|
|
|
|
sizeDown: false, //Used to animate close state
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2019-12-20 05:50:50 +00:00
|
|
|
colorPickerLocation: null,
|
|
|
|
|
2020-03-11 03:47:07 +00:00
|
|
|
fullFocusEditor: true, //Initialized editor instance
|
2020-01-03 01:26:55 +00:00
|
|
|
|
|
|
|
//Settings vars
|
|
|
|
showAllSettings: true,
|
|
|
|
lastVisibilityState: null,
|
2020-02-01 22:21:22 +00:00
|
|
|
|
|
|
|
//All the squire settings
|
|
|
|
editor: null,
|
|
|
|
// pastFocusedNode: null,
|
|
|
|
usersOnNote: 0,
|
|
|
|
|
|
|
|
extraToolbarsVisible: true,
|
2020-02-10 17:44:43 +00:00
|
|
|
showTagSlideMenu: false,
|
|
|
|
colorPickerVisible: false,
|
|
|
|
showFilesSideMenu: false,
|
|
|
|
showNoteOptions: false,
|
2020-03-13 23:34:32 +00:00
|
|
|
|
|
|
|
//Encryption options
|
|
|
|
passwordHint: '',
|
|
|
|
password: '', //Field Variables, only for form
|
|
|
|
passwordConfirm: '', //Only a form variable
|
|
|
|
hashedPass: '', //sha-256 password hash, sends to server for decryption
|
|
|
|
isEncrypted: false,
|
|
|
|
isDecrypted: false,
|
|
|
|
passwordEnterVisible: false,
|
|
|
|
decryptAttempts: 0,
|
|
|
|
lockedOut: false,
|
|
|
|
autoLockTimeout: null,
|
2019-07-19 20:51:57 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
noteid:function(newVal, oldVal){
|
|
|
|
|
|
|
|
if(newVal == this.currentNoteId){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if(newVal == oldVal){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.currentNoteId = newVal
|
|
|
|
this.loadNote(this.currentNoteId)
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
|
|
|
beforeMount(){
|
2020-02-01 22:21:22 +00:00
|
|
|
this.$bus.$on('new_file_upload', ({noteId, imageCode}) => {
|
|
|
|
if(this.noteid == noteId && this.editor){
|
|
|
|
this.editor.moveCursorToEnd()
|
|
|
|
this.editor.insertHTML(imageCode)
|
2020-02-10 17:44:43 +00:00
|
|
|
this.save()
|
2020-02-01 22:21:22 +00:00
|
|
|
}
|
|
|
|
})
|
2019-07-19 20:51:57 +00:00
|
|
|
},
|
|
|
|
beforeDestroy(){
|
2020-01-03 01:26:55 +00:00
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
this.password = ''
|
|
|
|
this.passwordConfirm = ''
|
|
|
|
this.hashedPass = ''
|
|
|
|
clearTimeout(this.autoLockTimeout)
|
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
this.$io.emit('leave_room', this.rawTextId)
|
2020-02-01 22:21:22 +00:00
|
|
|
|
|
|
|
document.removeEventListener('visibilitychange', this.checkForUpdatedNote)
|
2020-01-03 01:26:55 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
this.editor.destroy()
|
2020-01-03 01:26:55 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
this.$bus.$off('new_file_upload')
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
},
|
|
|
|
mounted: function() {
|
|
|
|
|
2020-01-03 01:26:55 +00:00
|
|
|
document.addEventListener('visibilitychange', this.checkForUpdatedNote)
|
|
|
|
|
2019-12-20 05:50:50 +00:00
|
|
|
this.$nextTick(() => {
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2019-12-20 05:50:50 +00:00
|
|
|
this.loadNote(this.noteid)
|
|
|
|
})
|
2019-07-19 20:51:57 +00:00
|
|
|
},
|
|
|
|
methods: {
|
2020-02-01 22:21:22 +00:00
|
|
|
initSquire(){
|
|
|
|
|
|
|
|
//Set up squire and load note text
|
|
|
|
this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
|
|
|
|
this.setText(this.noteText)
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
//focus on open, not on mobile, thats annoying
|
|
|
|
if(!this.$store.getters.getIsUserOnMobile){
|
2020-03-13 23:34:32 +00:00
|
|
|
// this.editor.focus()
|
|
|
|
|
2020-03-13 23:51:45 +00:00
|
|
|
if(this.noteTitle.length == 0){
|
2020-03-13 23:34:32 +00:00
|
|
|
this.$refs.titleTextarea.focus()
|
|
|
|
} else {
|
2020-03-13 23:51:45 +00:00
|
|
|
this.editor.focus()
|
2020-03-13 23:34:32 +00:00
|
|
|
this.editor.moveCursorToEnd()
|
|
|
|
}
|
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
}
|
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
//Click Event - Open links when clicked in editor or toggle checks
|
2020-02-01 22:21:22 +00:00
|
|
|
this.editor.addEventListener('click', e => {
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
//Link clicked in editor - open link
|
|
|
|
if(e.target.nodeName == 'A' && e.target.href){
|
|
|
|
window.open(e.target.href)
|
|
|
|
}
|
2020-01-03 01:26:55 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
//List Item clicked in editor - toggle link state
|
|
|
|
if(e.target.nodeName == 'LI'){
|
2020-01-03 01:26:55 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
let el = e.target
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
//Adjust ofset by 40 px
|
|
|
|
let correction = 40
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
//Determine if element was clicked or area before it, before means checkbox was clicked
|
|
|
|
if (e.offsetX > e.target.offsetLeft - correction) {
|
|
|
|
//Element was clicked
|
|
|
|
} else {
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
//Will hide keyboard if clicked, much better for mobile
|
|
|
|
this.editor.blur()
|
2020-01-03 01:26:55 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
//Area before element was clicked, they clicked the checkbox
|
|
|
|
this.onKeyup()
|
|
|
|
if (el.className === 'active'){
|
|
|
|
el.className = 'inactive';
|
|
|
|
} else {
|
|
|
|
el.className = 'active';
|
2020-01-03 01:26:55 +00:00
|
|
|
}
|
2020-02-01 22:21:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
this.editor.addEventListener('keydown', event => {
|
|
|
|
|
|
|
|
//Prevent new list items from having
|
|
|
|
this.$nextTick( () => {
|
|
|
|
//Wait a moment to get item under cursor
|
|
|
|
let selection = this.editor.getSelection()
|
|
|
|
let container = selection.commonAncestorContainer
|
|
|
|
|
|
|
|
//If user hit enter on a list, make sure the next list item isn't active
|
|
|
|
if(container.nodeName == 'LI' && event.keyCode == 13 && container.classList){
|
|
|
|
container.classList.remove('active')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
//Bind event handlers
|
|
|
|
this.editor.addEventListener('keyup', event => this.onKeyup(event) )
|
|
|
|
|
|
|
|
//Show and hide additional toolbars
|
|
|
|
this.editor.addEventListener('focus', e => {
|
|
|
|
if(this.$store.getters.getIsUserOnMobile){
|
|
|
|
this.extraToolbarsVisible = false
|
2020-01-03 01:26:55 +00:00
|
|
|
}
|
2019-12-20 05:50:50 +00:00
|
|
|
})
|
2020-02-01 22:21:22 +00:00
|
|
|
this.editor.addEventListener('blur', e => {
|
|
|
|
this.save()
|
|
|
|
this.extraToolbarsVisible = true
|
|
|
|
})
|
2019-12-20 05:50:50 +00:00
|
|
|
},
|
2020-02-01 22:21:22 +00:00
|
|
|
//If nothing is selected, select the entire line
|
|
|
|
selectLineIfNoSelect(){
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
//Select entire line if range is not set
|
|
|
|
let selection = this.editor.getSelection()
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
if(selection.startOffset == selection.endOffset && selection.startContainer == selection.endContainer){
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
let squireRange = this.editor.createRange(
|
|
|
|
selection.startContainer, 0,
|
|
|
|
selection.endContainer, selection.commonAncestorContainer.textContent.length)
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
this.editor.setSelection(squireRange)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
modifyFont(inSize){
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
this.selectLineIfNoSelect()
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
let fontInfo = this.editor.getFontInfo()
|
|
|
|
//Toggle font size between large and normal
|
|
|
|
if(fontInfo.size){
|
|
|
|
this.editor.setFontSize(null)
|
|
|
|
} else {
|
|
|
|
this.editor.setFontSize(inSize)
|
|
|
|
}
|
2019-12-20 05:50:50 +00:00
|
|
|
},
|
2020-02-01 22:21:22 +00:00
|
|
|
toggleList(type){
|
|
|
|
|
|
|
|
//Undo list if its already a lits
|
|
|
|
if(this.editor.hasFormat(type)){
|
|
|
|
this.editor.removeList()
|
|
|
|
return
|
2019-12-20 05:50:50 +00:00
|
|
|
}
|
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
if(type == 'ol'){
|
|
|
|
this.editor.makeOrderedList()
|
|
|
|
}
|
|
|
|
if(type == 'ul'){
|
|
|
|
this.editor.makeUnorderedList()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
toggleBold(){
|
|
|
|
this.selectLineIfNoSelect()
|
|
|
|
if( this.editor.hasFormat('b') ){
|
|
|
|
this.editor.removeBold()
|
|
|
|
} else {
|
|
|
|
this.editor.bold()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
toggleItalic(){
|
|
|
|
this.selectLineIfNoSelect()
|
|
|
|
if( this.editor.hasFormat('i') ){
|
|
|
|
this.editor.removeItalic()
|
|
|
|
} else {
|
|
|
|
this.editor.italic()
|
|
|
|
}
|
2020-01-03 01:26:55 +00:00
|
|
|
},
|
2020-02-10 17:44:43 +00:00
|
|
|
undoCustom(){
|
|
|
|
//The same as pressing CTRL + Z
|
|
|
|
// this.editor.focus()
|
|
|
|
// document.execCommand("undo", false, null)
|
|
|
|
this.editor.undo()
|
|
|
|
},
|
|
|
|
uncheckAllListItems(){
|
|
|
|
//
|
|
|
|
// Uncheck All List Items
|
|
|
|
//
|
|
|
|
|
|
|
|
//Close menu if user is on mobile, then sort list
|
|
|
|
if(this.$store.getters.getIsUserOnMobile){
|
|
|
|
this.showNoteOptions = false
|
|
|
|
}
|
|
|
|
|
|
|
|
//Fetch the container
|
|
|
|
let container = document.getElementById('squire-id')
|
|
|
|
|
|
|
|
Array.from( container.getElementsByClassName('active') ).forEach(item => {
|
|
|
|
item.classList.remove('active');
|
|
|
|
})
|
|
|
|
},
|
|
|
|
deleteCompletedListItems(){
|
|
|
|
//
|
|
|
|
// Delete Completed List Items
|
|
|
|
//
|
|
|
|
|
|
|
|
//Close menu if user is on mobile, then sort list
|
|
|
|
if(this.$store.getters.getIsUserOnMobile){
|
|
|
|
this.showNoteOptions = false
|
|
|
|
}
|
|
|
|
|
|
|
|
//Fetch the container
|
|
|
|
let container = document.getElementById('squire-id')
|
|
|
|
|
|
|
|
//Go through each item, on first level, look for Unordered Lists
|
|
|
|
container.childNodes.forEach( (node) => {
|
|
|
|
if(node.nodeName == 'UL'){
|
|
|
|
|
|
|
|
//Create two categories, done and not done list items
|
|
|
|
let undoneElements = document.createDocumentFragment()
|
|
|
|
|
|
|
|
//Go through each item in each list we found
|
|
|
|
node.childNodes.forEach( (checkListItem, index) => {
|
|
|
|
|
|
|
|
//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
|
|
|
|
if(checkListItem.nodeName == 'UL'){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
//Check if list item has active class
|
|
|
|
const checkedItem = checkListItem.classList.contains('active')
|
|
|
|
|
|
|
|
//Check if the next item is a list, Keep lists with intented items together
|
|
|
|
let sublist = null
|
|
|
|
if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
|
|
|
|
sublist = node.childNodes[index+1]
|
|
|
|
}
|
|
|
|
|
|
|
|
//Push checked items and their sub lists to the done set
|
|
|
|
if(!checkedItem){
|
|
|
|
|
|
|
|
undoneElements.appendChild( checkListItem.cloneNode(true) )
|
|
|
|
if(sublist){
|
|
|
|
undoneElements.appendChild( sublist.cloneNode(true) )
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
//Remove all HTML from node, push unfinished items, then finished below them
|
|
|
|
node.innerHTML = null
|
|
|
|
node.appendChild(undoneElements)
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
sortList(){
|
|
|
|
//
|
|
|
|
// Sort list, checked at the bottom, unchecked at the top
|
|
|
|
//
|
|
|
|
|
|
|
|
//Close menu if user is on mobile, then sort list
|
|
|
|
if(this.$store.getters.getIsUserOnMobile){
|
|
|
|
this.showNoteOptions = false
|
|
|
|
}
|
|
|
|
|
|
|
|
//Fetch the container
|
|
|
|
let container = document.getElementById('squire-id')
|
|
|
|
|
|
|
|
//Go through each item, on first level, look for Unordered Lists
|
|
|
|
container.childNodes.forEach( (node) => {
|
|
|
|
if(node.nodeName == 'UL'){
|
|
|
|
|
|
|
|
//Create two categories, done and not done list items
|
|
|
|
let doneElements = document.createDocumentFragment()
|
|
|
|
let undoneElements = document.createDocumentFragment()
|
|
|
|
|
|
|
|
//Go through each item in each list we found
|
|
|
|
node.childNodes.forEach( (checkListItem, index) => {
|
|
|
|
|
|
|
|
//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
|
|
|
|
if(checkListItem.nodeName == 'UL'){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
//Check if list item has active class
|
|
|
|
const checkedItem = checkListItem.classList.contains('active')
|
|
|
|
|
|
|
|
//Check if the next item is a list, Keep lists with intented items together
|
|
|
|
let sublist = null
|
|
|
|
if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
|
|
|
|
sublist = node.childNodes[index+1]
|
|
|
|
}
|
|
|
|
|
|
|
|
//Push checked items and their sub lists to the done set
|
|
|
|
if(checkedItem){
|
|
|
|
|
|
|
|
doneElements.appendChild( checkListItem.cloneNode(true) )
|
|
|
|
if(sublist){
|
|
|
|
doneElements.appendChild( sublist.cloneNode(true) )
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
undoneElements.appendChild( checkListItem.cloneNode(true) )
|
|
|
|
if(sublist){
|
|
|
|
undoneElements.appendChild( sublist.cloneNode(true) )
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
//Remove all HTML from node, push unfinished items, then finished below them
|
|
|
|
node.innerHTML = null
|
|
|
|
node.appendChild(undoneElements)
|
|
|
|
node.appendChild(doneElements)
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
2020-02-19 00:31:18 +00:00
|
|
|
calculateMath(){
|
|
|
|
//
|
|
|
|
// Find math in note and calculate the outcome
|
|
|
|
//
|
|
|
|
|
|
|
|
//Close menu if user is on mobile, then sort list
|
|
|
|
if(this.$store.getters.getIsUserOnMobile){
|
|
|
|
this.showNoteOptions = false
|
|
|
|
}
|
|
|
|
|
|
|
|
//Fetch the container
|
|
|
|
let container = document.getElementById('squire-id')
|
|
|
|
|
|
|
|
// simple function that trys to evaluate javascript
|
|
|
|
const shittyMath = (string) => {
|
2020-02-23 06:27:49 +00:00
|
|
|
//Remove all chars but math chars
|
|
|
|
const cleanString = String(string).replace(/[a-zA-Z\s]*/g,'')
|
2020-02-19 00:31:18 +00:00
|
|
|
try {
|
2020-02-23 06:27:49 +00:00
|
|
|
return Function('"use strict"; return (' + cleanString + ')')();
|
2020-02-19 00:31:18 +00:00
|
|
|
} catch (error) {
|
|
|
|
console.log('Math Error: ', string)
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//Go through each item, on first level, look for Unordered Lists
|
|
|
|
container.childNodes.forEach( (node) => {
|
|
|
|
|
|
|
|
const line = node.innerText.trim()
|
|
|
|
|
|
|
|
// = sign exists and its the last character in the string
|
2020-02-23 06:27:49 +00:00
|
|
|
if(line.indexOf('=') != -1 && (line.length-1) == line.indexOf('=')){
|
2020-02-19 00:31:18 +00:00
|
|
|
|
|
|
|
//Pull out everything before the formula and try to evaluate it
|
|
|
|
const formula = line.split('=').shift()
|
|
|
|
const output = shittyMath(formula)
|
|
|
|
|
|
|
|
//If its a number and didn't throw an error, update the line
|
|
|
|
if(!isNaN(output) && output != null){
|
|
|
|
|
|
|
|
//Since there is HTML in the line, splice in the number after the = sign
|
2020-02-23 06:27:49 +00:00
|
|
|
let equalLocation = node.innerHTML.indexOf('=')
|
2020-02-19 00:31:18 +00:00
|
|
|
let newLine = node.innerHTML.slice(0, equalLocation+1).trim()
|
|
|
|
newLine += ` ${output}`
|
|
|
|
newLine += node.innerHTML.slice(equalLocation+1).trim()
|
|
|
|
|
|
|
|
//Slam in that new HTML with the output
|
|
|
|
node.innerHTML = newLine
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
},
|
2020-01-03 01:26:55 +00:00
|
|
|
setText(inText){
|
2020-02-01 22:21:22 +00:00
|
|
|
|
|
|
|
this.editor.setHTML(inText)
|
|
|
|
this.noteText = this.editor._getHTML()
|
|
|
|
this.diffNoteText = this.editor._getHTML()
|
2019-12-20 05:50:50 +00:00
|
|
|
},
|
|
|
|
getText(){
|
2020-02-01 22:21:22 +00:00
|
|
|
|
|
|
|
return this.editor.getHTML()
|
2019-12-20 05:50:50 +00:00
|
|
|
},
|
|
|
|
showColorPicker(event){
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2019-12-20 05:50:50 +00:00
|
|
|
this.colorPickerVisible = !this.colorPickerVisible
|
|
|
|
this.colorPickerLocation = {'x':event.clientX, 'y':event.clientY}
|
|
|
|
},
|
|
|
|
openEditAttachment(){
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2019-12-20 05:50:50 +00:00
|
|
|
this.$router.push('/attachments/note/'+this.currentNoteId)
|
2019-07-21 16:28:07 +00:00
|
|
|
},
|
2019-09-10 18:10:11 +00:00
|
|
|
onTogglePinned(){
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2019-09-10 18:10:11 +00:00
|
|
|
if(this.pinned == 0){
|
|
|
|
this.pinned = 1
|
|
|
|
} else {
|
|
|
|
this.pinned = 0;
|
|
|
|
}
|
|
|
|
//Update last note hash, this will tell note to save next update
|
|
|
|
this.lastNoteHash = 0
|
2019-12-20 05:50:50 +00:00
|
|
|
this.save()
|
2019-09-10 18:10:11 +00:00
|
|
|
},
|
|
|
|
onToggleArchived(){
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2019-09-10 18:10:11 +00:00
|
|
|
if(this.archived == 0){
|
|
|
|
this.archived = 1
|
|
|
|
} else {
|
|
|
|
this.archived = 0;
|
|
|
|
}
|
|
|
|
//Update last note hash, this will tell note to save next update
|
|
|
|
this.lastNoteHash = 0
|
2019-12-20 05:50:50 +00:00
|
|
|
this.save()
|
2019-09-10 18:10:11 +00:00
|
|
|
},
|
2020-02-01 22:21:22 +00:00
|
|
|
onCloseColorChanger(){
|
2020-02-10 17:44:43 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
this.colorPickerVisible = false
|
|
|
|
},
|
2019-12-20 05:50:50 +00:00
|
|
|
onChangeColor(newStyleObject){
|
|
|
|
|
|
|
|
//Set new style object for note, page will use some styles, styles will be saved to database
|
|
|
|
this.styleObject = newStyleObject
|
|
|
|
|
2019-07-21 16:28:07 +00:00
|
|
|
this.lastNoteHash = 0 //Update hash to force note update on next save
|
|
|
|
this.save()
|
|
|
|
},
|
2019-07-19 20:51:57 +00:00
|
|
|
loadNote(noteId){
|
2019-09-10 18:10:11 +00:00
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
//Generate a random loading message
|
2019-12-20 05:50:50 +00:00
|
|
|
let doing = ['Loading','Loading','Getting','Fetching','Grabbing','Sequencing','Organizing','Untangling','Processing','Refining','Extracting','Fusing','Pruning','Expanding','Enlarging','Transfiguring','Quantizing','Ingratiating','Lumping']
|
2019-09-10 18:10:11 +00:00
|
|
|
let thing = ['Note','Note','Note','Note','Data','Text','Document','Algorithm','Buffer','Client','Download','File','Frame','Graphics','Hardware','HTML','Interface','Logic','Mainframe','Memory','Media','Nodes','Network','Chaos']
|
|
|
|
let p1 = doing[Math.floor(Math.random() * doing.length)]
|
|
|
|
let p2 = thing[Math.floor(Math.random() * thing.length)]
|
2020-03-13 23:34:32 +00:00
|
|
|
this.loadingMessage = p1 + ' ' + p2
|
2019-09-10 18:10:11 +00:00
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
//Component is activated with NoteId in place, lookup text with associated ID
|
|
|
|
if(this.$store.getters.getLoggedIn){
|
2020-03-13 23:34:32 +00:00
|
|
|
axios.post('/api/note/get', { 'noteId': this.noteid, 'password':this.hashedPass })
|
2019-07-19 20:51:57 +00:00
|
|
|
.then(response => {
|
2019-07-21 16:28:07 +00:00
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
//Set up local data
|
2020-03-13 23:34:32 +00:00
|
|
|
this.currentNoteId = this.noteid
|
2020-02-10 17:44:43 +00:00
|
|
|
this.rawTextId = response.data.rawTextId
|
|
|
|
this.shareUsername = response.data.shareUsername
|
2020-03-13 23:34:32 +00:00
|
|
|
this.passwordHint = response.data.password_hint
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
this.created = response.data.created
|
|
|
|
this.updated = response.data.updated
|
2020-03-13 23:34:32 +00:00
|
|
|
this.noteTitle = response.data.title
|
2020-02-26 05:35:43 +00:00
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
this.noteText = response.data.text
|
|
|
|
this.diffNoteText = response.data.text
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
this.lastNoteHash = this.hashString(response.data.text)
|
2020-02-01 22:21:22 +00:00
|
|
|
//Set up note colors
|
2019-12-20 05:50:50 +00:00
|
|
|
if(response.data.color){
|
2020-03-13 23:34:32 +00:00
|
|
|
this.styleObject = JSON.parse(response.data.color)
|
2019-12-20 05:50:50 +00:00
|
|
|
}
|
|
|
|
|
2019-09-10 18:10:11 +00:00
|
|
|
if(response.data.pinned != null){
|
2020-03-13 23:34:32 +00:00
|
|
|
this.pinned = response.data.pinned
|
2019-09-10 18:10:11 +00:00
|
|
|
}
|
2020-03-13 23:34:32 +00:00
|
|
|
this.archived = response.data.archived
|
|
|
|
this.attachmentCount = response.data.attachment_count
|
2019-07-24 18:06:50 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
this.loading = false
|
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
this.isDecrypted = response.data.decrypted
|
|
|
|
this.isEncrypted = response.data.encrypted == 1
|
|
|
|
this.decryptAttempts = response.data.decrypt_attempts_count
|
|
|
|
this.lockedOut = response.data.lockedOut
|
|
|
|
|
|
|
|
|
|
|
|
//If password is required, display a prompt and focus on it
|
|
|
|
if(this.password.length == 0 && this.isEncrypted && !this.isDecrypted){
|
|
|
|
this.$nextTick(() => {
|
|
|
|
if(this.$refs.decryptNotePrompt){
|
|
|
|
// this.editor.moveCursorToEnd()
|
|
|
|
this.$refs.decryptNotePrompt.focus()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
//Adjust note title size after load
|
|
|
|
this.titleResize()
|
2020-02-10 17:44:43 +00:00
|
|
|
|
|
|
|
this.setupWebSockets()
|
2020-02-01 22:21:22 +00:00
|
|
|
this.initSquire()
|
2020-03-13 23:34:32 +00:00
|
|
|
this.startAutolockTimer()
|
2019-07-19 20:51:57 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
console.log('Could not fetch note')
|
|
|
|
}
|
|
|
|
},
|
2020-02-01 22:21:22 +00:00
|
|
|
diffText(){
|
|
|
|
|
|
|
|
// dont emit to one user
|
|
|
|
if(this.usersOnNote <= 1){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
//Post latest diff to server, server will emit change event to all connected clients
|
|
|
|
// clearTimeout(this.emitChangeDebounce)
|
|
|
|
this.emitChangeDebounce = setTimeout(i => {
|
|
|
|
|
|
|
|
//caldulate text diff
|
|
|
|
let oldText = this.diffNoteText
|
|
|
|
let newText = this.getText()
|
|
|
|
|
|
|
|
if(oldText == newText){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const dmp = new DiffMatchPatch.diff_match_patch()
|
|
|
|
const diff = dmp.diff_main(oldText, newText)
|
|
|
|
// dmp.diff_cleanupSemantic(diff)
|
|
|
|
const patch_list = dmp.patch_make(oldText, newText, diff);
|
|
|
|
const patch_text = dmp.patch_toText(patch_list);
|
|
|
|
|
|
|
|
|
|
|
|
var patches = dmp.patch_fromText(patch_text);
|
|
|
|
var results = dmp.patch_apply(patches, oldText);
|
|
|
|
|
|
|
|
const computedText = results[0]
|
|
|
|
|
|
|
|
//Save computed diff text
|
|
|
|
this.noteText = computedText
|
|
|
|
this.diffNoteText = computedText
|
|
|
|
|
|
|
|
if(patch_text == ''){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// console.log(patch_text)
|
|
|
|
this.$io.emit('note_diff', {
|
2020-02-10 17:44:43 +00:00
|
|
|
id: this.rawTextId,
|
|
|
|
diff: patch_text
|
2020-02-01 22:21:22 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
}, 5)
|
|
|
|
},
|
|
|
|
patchText(patch_text){
|
|
|
|
|
|
|
|
console.log(patch_text)
|
|
|
|
|
|
|
|
//
|
|
|
|
// Capture x,y of caret and position into string
|
|
|
|
//
|
|
|
|
|
|
|
|
let currentSelection = this.editor.getSelection()
|
|
|
|
let lineText = currentSelection.startContainer.textContent
|
|
|
|
console.log(lineText)
|
|
|
|
let cursorOffset = parseInt(currentSelection.startOffset) //number of characters in
|
|
|
|
let path = this.xpath(currentSelection.commonAncestorContainer.parentElement)
|
|
|
|
console.log(path)
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
//Set up text to process diff
|
|
|
|
//
|
|
|
|
|
|
|
|
let currentText = this.editor._getHTML()
|
|
|
|
const startingLines = (currentText.match(/<br>/g) || '').length + 1
|
|
|
|
console.log('1')
|
|
|
|
|
|
|
|
const dmp = new DiffMatchPatch.diff_match_patch()
|
|
|
|
var patches = dmp.patch_fromText(patch_text);
|
|
|
|
var results = dmp.patch_apply(patches, currentText);
|
|
|
|
let newText = results[0]
|
|
|
|
console.log('2')
|
|
|
|
|
|
|
|
this.noteText = newText
|
|
|
|
this.diffNoteText = newText
|
|
|
|
console.log('3')
|
|
|
|
// this.editor._setHTML(newText)
|
|
|
|
this.editor.setHTML(newText)
|
2019-07-19 20:51:57 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
console.log('4')
|
|
|
|
|
|
|
|
//
|
|
|
|
// I user hasn't selected the document, we are done here
|
|
|
|
// @TODO add code to halt execution
|
|
|
|
//
|
|
|
|
|
|
|
|
const endingLines = (newText.match(/<br>/g) || '').length + 1
|
|
|
|
|
|
|
|
// if(this.pastFocusedNode != null || true){
|
|
|
|
setTimeout( ()=>{
|
|
|
|
|
|
|
|
var root = this.editor.getRoot()
|
|
|
|
//Get node under current x,y on dom (may break on scroll)
|
|
|
|
// let node = document.elementFromPoint(mouse.x, mouse.y)
|
|
|
|
let node = this.getElementByXPath(path)
|
|
|
|
|
|
|
|
if(node.firstChild){
|
|
|
|
node = node.firstChild
|
|
|
|
}
|
|
|
|
|
|
|
|
//If the number of lines changed
|
|
|
|
if(startingLines != endingLines){
|
|
|
|
|
|
|
|
//Line diff may be +1 or -1
|
|
|
|
let lineDiff = endingLines - startingLines
|
|
|
|
console.log('Line Diff => ', lineDiff)
|
|
|
|
|
|
|
|
//Pull out node number from path
|
|
|
|
var nodeNumber = path.match(/\d+/)
|
|
|
|
let modifyNode = null
|
|
|
|
if(nodeNumber.length == 1){
|
|
|
|
modifyNode = parseInt(nodeNumber[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
path = path.replace(modifyNode, modifyNode + lineDiff )
|
|
|
|
console.log(path)
|
|
|
|
let maybeNext = this.getElementByXPath(path)
|
|
|
|
if(maybeNext && maybeNext.firstChild){
|
|
|
|
maybeNext = maybeNext.firstChild
|
|
|
|
}
|
|
|
|
|
|
|
|
if(maybeNext && maybeNext.textContent == lineText){
|
|
|
|
node = maybeNext
|
|
|
|
console.log('The Node Moved!')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('Targeting Node')
|
|
|
|
console.log(node)
|
|
|
|
|
|
|
|
//Create and set range
|
|
|
|
let squireRange = this.editor.createRange(node, cursorOffset)
|
|
|
|
squireRange.collapse(true)
|
|
|
|
this.editor.setSelection(squireRange)
|
|
|
|
console.log('cursor set')
|
|
|
|
|
|
|
|
}, 20)
|
|
|
|
// }
|
|
|
|
},
|
|
|
|
xpath(el) {
|
|
|
|
//Skip things we can't use
|
|
|
|
if (typeof el == "string") return document.evaluate(el, document, null, 0, null)
|
|
|
|
if (!el || el.nodeType != 1) return ''
|
|
|
|
|
|
|
|
//Anchor xpath using Ids or test-ids
|
|
|
|
const testId = el.getAttribute('test-id')
|
|
|
|
if (el.id) return "//*[@id='" + el.id + "']"
|
|
|
|
|
|
|
|
//Continue to build path
|
|
|
|
const sames = [].filter.call(el.parentNode.children, function (x) { return x.tagName == el.tagName })
|
|
|
|
return this.xpath(el.parentNode) + '/' + el.tagName.toLowerCase() + (sames.length > 1 ? '['+([].indexOf.call(sames, el)+1)+']' : '')
|
|
|
|
},
|
2020-02-10 17:44:43 +00:00
|
|
|
getElementByXPath(xpath){
|
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
return new XPathEvaluator()
|
|
|
|
.createExpression(xpath)
|
|
|
|
.evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE) .singleNodeValue
|
|
|
|
},
|
2020-03-13 23:34:32 +00:00
|
|
|
onKeyup(){
|
2020-02-01 22:21:22 +00:00
|
|
|
|
|
|
|
this.statusText = 'Save'
|
|
|
|
|
|
|
|
this.diffText()
|
2019-07-19 20:51:57 +00:00
|
|
|
|
2019-07-21 16:28:07 +00:00
|
|
|
//Each note, save after 5 seconds, focus lost or 30 characters typed.
|
2019-12-20 05:50:50 +00:00
|
|
|
clearTimeout(this.editDebounce)
|
|
|
|
this.editDebounce = setTimeout(() => {
|
|
|
|
this.save()
|
2019-07-21 16:28:07 +00:00
|
|
|
}, 5000)
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2019-07-29 07:22:47 +00:00
|
|
|
//Save after 30 keystrokes
|
2019-12-20 05:50:50 +00:00
|
|
|
this.keyPressesCounter = (this.keyPressesCounter + 1)
|
|
|
|
if(this.keyPressesCounter > 30){
|
|
|
|
this.keyPressesCounter = 0
|
|
|
|
this.save()
|
2019-07-21 16:28:07 +00:00
|
|
|
}
|
|
|
|
},
|
2019-07-19 20:51:57 +00:00
|
|
|
save(){
|
2019-07-29 07:22:47 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
2019-12-20 05:50:50 +00:00
|
|
|
//Clear other debounced events to prevent double calling of save
|
2020-01-03 01:26:55 +00:00
|
|
|
// clearTimeout(this.editDebounce)
|
2019-07-29 07:22:47 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
// return resolve(true)
|
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
|
|
|
|
//Encrypted notes that are not decrypted should not be saved
|
|
|
|
if(this.isEncrypted && !this.isDecrypted){
|
|
|
|
return resolve(true)
|
|
|
|
}
|
|
|
|
|
2019-07-29 07:22:47 +00:00
|
|
|
//Don't save note if its hash doesn't change
|
2019-12-20 05:50:50 +00:00
|
|
|
const currentNoteText = this.getText()
|
|
|
|
if( this.lastNoteHash == this.hashString( currentNoteText )){
|
2020-01-03 01:26:55 +00:00
|
|
|
this.statusText = 'Saved'
|
2019-12-20 05:50:50 +00:00
|
|
|
return resolve(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
//If user accidentally clears note, it won't delete it
|
|
|
|
if(currentNoteText == ''){
|
|
|
|
this.statusText = 'Empty'
|
|
|
|
console.log('Prevented from saving empty note.')
|
|
|
|
return resolve(true)
|
2019-07-29 07:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const postData = {
|
2020-03-13 23:34:32 +00:00
|
|
|
'noteId': this.currentNoteId,
|
|
|
|
'title': this.noteTitle,
|
2019-12-20 05:50:50 +00:00
|
|
|
'text': currentNoteText,
|
|
|
|
'color': JSON.stringify(this.styleObject), //Save little json color object
|
2019-09-10 18:10:11 +00:00
|
|
|
'pinned': this.pinned,
|
2020-03-13 23:34:32 +00:00
|
|
|
'archived': this.archived,
|
|
|
|
'password': this.hashedPass,
|
|
|
|
'hint': this.passwordHint,
|
2019-07-29 07:22:47 +00:00
|
|
|
}
|
2019-07-20 23:07:22 +00:00
|
|
|
|
2020-02-11 21:11:14 +00:00
|
|
|
this.statusText = 'Saving'
|
|
|
|
axios.post('/api/note/update', postData).then( response => {
|
|
|
|
this.statusText = 'Saved'
|
|
|
|
this.updated = Math.round((+new Date)/1000)
|
|
|
|
this.modified = true
|
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
console.log('Saved')
|
|
|
|
|
2020-02-11 21:11:14 +00:00
|
|
|
//Update last saved note hash
|
|
|
|
this.lastNoteHash = this.hashString( currentNoteText )
|
2020-03-13 23:34:32 +00:00
|
|
|
this.startAutolockTimer()
|
2020-02-11 21:11:14 +00:00
|
|
|
return resolve(true)
|
|
|
|
})
|
2019-07-29 07:22:47 +00:00
|
|
|
})
|
2019-07-19 20:51:57 +00:00
|
|
|
},
|
2020-01-03 01:26:55 +00:00
|
|
|
checkForUpdatedNote(){
|
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
// return
|
|
|
|
|
2020-01-03 01:26:55 +00:00
|
|
|
//If user leaves page then returns to page, reload the first batch
|
|
|
|
if(this.lastVisibilityState == 'hidden' && document.visibilityState == 'visible'){
|
2020-02-10 17:44:43 +00:00
|
|
|
// console.log('Checking for note updates after visibility change.')
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-01-03 01:26:55 +00:00
|
|
|
const postData = {
|
|
|
|
noteId:this.currentNoteId,
|
|
|
|
text:this.getText(),
|
|
|
|
updated: this.updated
|
|
|
|
}
|
|
|
|
|
|
|
|
axios.post('/api/note/difftext', postData)
|
|
|
|
.then( ({data}) => {
|
|
|
|
|
|
|
|
//Don't do anything if nothing has changed
|
|
|
|
if(data == ''){ return }
|
|
|
|
|
|
|
|
if(data.diffs > 0){
|
|
|
|
//Update text and last updated time for note
|
|
|
|
this.setText(data.updatedText)
|
|
|
|
this.updated = data.updated
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
//Track visibility state
|
|
|
|
this.lastVisibilityState = document.visibilityState
|
|
|
|
},
|
2019-07-19 20:51:57 +00:00
|
|
|
hashString(text){
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
text = this.noteTitle + text
|
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
var hash = 0;
|
2020-02-01 22:21:22 +00:00
|
|
|
if (text == null || text.length == 0) {
|
2019-07-19 20:51:57 +00:00
|
|
|
return hash;
|
|
|
|
}
|
2019-12-20 05:50:50 +00:00
|
|
|
|
|
|
|
//Simplified for speed
|
2020-02-01 22:21:22 +00:00
|
|
|
// return text.length
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
for (let i = 0; i < text.length; i++) {
|
|
|
|
let char = text.charCodeAt(i);
|
|
|
|
hash = ((hash<<5)-hash)+char;
|
|
|
|
hash = hash & hash; // Convert to 32bit integer
|
|
|
|
}
|
2019-12-20 05:50:50 +00:00
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
return hash;
|
|
|
|
},
|
|
|
|
close(){
|
2019-12-20 05:50:50 +00:00
|
|
|
|
|
|
|
// this.loading = true
|
|
|
|
// this.loadingMessage = 'Save and Close'
|
|
|
|
|
2019-07-30 20:46:24 +00:00
|
|
|
this.save().then( result => {
|
2019-12-20 05:50:50 +00:00
|
|
|
|
|
|
|
this.sizeDown = true
|
|
|
|
//This timeout allows animation to play before closing
|
|
|
|
setTimeout(() => {
|
2020-02-11 21:11:14 +00:00
|
|
|
this.$bus.$emit('close_active_note', {
|
|
|
|
position: this.position, noteId: this.noteid, modified: this.modified
|
|
|
|
})
|
2019-07-30 20:46:24 +00:00
|
|
|
return
|
2019-12-20 05:50:50 +00:00
|
|
|
}, 300)
|
2019-07-29 07:22:47 +00:00
|
|
|
})
|
2020-02-10 17:44:43 +00:00
|
|
|
},
|
|
|
|
setupWebSockets(){
|
|
|
|
//Tell server to push this note into a room
|
|
|
|
this.$io.emit('join_room', this.rawTextId)
|
|
|
|
|
|
|
|
this.$io.on('update_user_count', userCount => {
|
|
|
|
this.usersOnNote = userCount
|
|
|
|
})
|
2019-07-29 07:22:47 +00:00
|
|
|
|
2020-02-10 17:44:43 +00:00
|
|
|
//Server will hand deliver diffs from other notes to this one
|
|
|
|
this.$io.on('incoming_diff', incomingDiffData => {
|
|
|
|
this.patchText(incomingDiffData)
|
|
|
|
})
|
2020-03-13 23:34:32 +00:00
|
|
|
},
|
|
|
|
decryptNote(){
|
|
|
|
|
|
|
|
const hashed = crypto.createHash('sha256').update(this.password).digest().toString('base64')
|
|
|
|
//Remove plaintext password
|
|
|
|
this.hashedPass = hashed
|
|
|
|
this.password = ''
|
|
|
|
this.passwordConfirm = ''
|
|
|
|
|
|
|
|
this.loadNote()
|
|
|
|
},
|
|
|
|
lockNote(){
|
|
|
|
this.save().then(results => {
|
|
|
|
this.isDecrypted = false
|
|
|
|
this.password = ''
|
|
|
|
this.hashedPass = ''
|
|
|
|
this.passwordEnterVisible = false
|
|
|
|
this.setText('')
|
|
|
|
})
|
|
|
|
},
|
|
|
|
enableEncryption(){
|
|
|
|
|
|
|
|
if(this.noteText == ''){
|
|
|
|
this.noteText = 'Text Typed here is encrypted.'
|
|
|
|
}
|
|
|
|
|
|
|
|
const hashed = crypto.createHash('sha256').update(this.password).digest().toString('base64')
|
|
|
|
//Remove plaintext password
|
|
|
|
this.hashedPass = hashed
|
|
|
|
|
|
|
|
this.lastNoteHash = 0
|
|
|
|
this.password = ''
|
|
|
|
this.passwordConfirm = ''
|
|
|
|
this.passwordEnterVisible = false
|
|
|
|
|
|
|
|
this.save()
|
|
|
|
.then(results => {
|
|
|
|
this.$bus.$emit('notification', 'Password Protection Enabled')
|
|
|
|
this.loadNote()
|
|
|
|
})
|
|
|
|
},
|
|
|
|
disableEncryption(){
|
|
|
|
|
|
|
|
this.lastNoteHash = 0
|
|
|
|
this.isEncrypted = false
|
|
|
|
this.password = ''
|
|
|
|
this.passwordConfirm = ''
|
|
|
|
this.hashedPass = ''
|
|
|
|
this.passwordEnterVisible = false
|
|
|
|
|
|
|
|
//Reload Note
|
|
|
|
this.save()
|
|
|
|
.then(results => {
|
|
|
|
this.loadNote()
|
|
|
|
this.$bus.$emit('notification', 'Password Protection Removed')
|
|
|
|
})
|
|
|
|
},
|
|
|
|
titleResize(){
|
|
|
|
//Resize the title field
|
|
|
|
let element = this.$refs.titleTextarea
|
|
|
|
let padding = 0
|
|
|
|
|
|
|
|
element.style.height = 'auto';
|
|
|
|
element.style.height = (element.scrollHeight + padding) +'px';
|
|
|
|
},
|
|
|
|
startAutolockTimer(){
|
|
|
|
//Start autolock timer on encrypted notes that are encrypted and in a decrypted state
|
|
|
|
if(this.isEncrypted && this.isDecrypted){
|
|
|
|
clearTimeout(this.autoLockTimeout)
|
|
|
|
this.autoLockTimeout = setTimeout(() => {
|
|
|
|
this.lockNote()
|
|
|
|
}, (60 * 1000 * 20) ) //Autolock after 20 min
|
|
|
|
}
|
|
|
|
},
|
2019-07-19 20:51:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style type="text/css" scoped>
|
2019-07-21 16:28:07 +00:00
|
|
|
|
2020-02-01 22:21:22 +00:00
|
|
|
/* squire styles */
|
2020-02-26 05:35:43 +00:00
|
|
|
.input-container-wrapper {
|
|
|
|
|
|
|
|
display: block;
|
|
|
|
height: 100%;
|
|
|
|
width: 100%;
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
}
|
|
|
|
/*Three main elements nested in panel */
|
|
|
|
.note-menu {
|
|
|
|
/*position: absolute;*/
|
|
|
|
top: 0;/*
|
|
|
|
left: 0;
|
|
|
|
right: 0;*/
|
|
|
|
|
|
|
|
flex-grow: 0;
|
|
|
|
}
|
|
|
|
.note-wrapper {
|
|
|
|
flex-grow: 1;
|
|
|
|
overflow: scroll;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
.stealth-input {
|
|
|
|
width: 100%;
|
|
|
|
padding: 10px 15px 5px;
|
|
|
|
background-color: rgba(255,255,255,0.1);
|
|
|
|
border: none;
|
|
|
|
font-size: 1.7em;
|
|
|
|
/*line-height: 1.7em;*/
|
|
|
|
color: var(--text_color);
|
|
|
|
resize: none;
|
|
|
|
overflow: hidden;
|
2020-02-26 05:35:43 +00:00
|
|
|
|
2020-03-13 23:34:32 +00:00
|
|
|
}
|
2020-02-01 22:21:22 +00:00
|
|
|
|
2020-01-03 01:26:55 +00:00
|
|
|
/*Settings manager styles */
|
|
|
|
.all-settings {
|
2020-02-01 22:21:22 +00:00
|
|
|
/*border-top: 1px solid #534c68;*/
|
|
|
|
background: #221f2b;
|
2020-02-26 05:35:43 +00:00
|
|
|
/*position: absolute;*/
|
|
|
|
/*bottom: 40px;*/
|
|
|
|
/*right: 0;*/
|
|
|
|
/*left: 0;*/
|
2020-01-03 01:26:55 +00:00
|
|
|
z-index: 99;
|
2020-02-01 22:21:22 +00:00
|
|
|
/*border: 1px solid;*/
|
|
|
|
/*background-color: var(--background_color);*/
|
|
|
|
/*border-color: var(--border_color);*/
|
2020-02-26 05:35:43 +00:00
|
|
|
/*box-sizing: border-box;*/
|
2020-02-01 22:21:22 +00:00
|
|
|
/*border-radius: 7px;*/
|
|
|
|
/*box-shadow: 0px 3px 7px 0px rgba(140,140,140,1);*/
|
|
|
|
/*padding: 1.2em 0 0;*/
|
2020-02-26 05:35:43 +00:00
|
|
|
flex-grow: 0;
|
2020-02-01 22:21:22 +00:00
|
|
|
}
|
|
|
|
.low-settings {
|
|
|
|
bottom: 0px;
|
|
|
|
cursor: pointer;
|
|
|
|
height: 1.4em;
|
|
|
|
padding-top: 1.5em;
|
|
|
|
overflow: hidden;
|
|
|
|
border: 1px solid #534c68;
|
2020-01-03 01:26:55 +00:00
|
|
|
}
|
|
|
|
/*End Settings manager styles */
|
|
|
|
|
2019-07-21 16:28:07 +00:00
|
|
|
|
2020-02-26 05:35:43 +00:00
|
|
|
|
2019-07-21 16:28:07 +00:00
|
|
|
/* container styles change based on mobile and number of open screens */
|
2019-07-19 20:51:57 +00:00
|
|
|
.master-note-edit {
|
|
|
|
position: fixed;
|
|
|
|
bottom: 0;
|
2019-07-29 07:22:47 +00:00
|
|
|
background: var(--background_color);
|
|
|
|
/*color: var(--text_color);*/
|
2019-07-21 16:28:07 +00:00
|
|
|
height: 100vh;
|
2019-07-19 20:51:57 +00:00
|
|
|
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
2019-09-10 18:10:11 +00:00
|
|
|
z-index: 1001;
|
2019-12-20 05:50:50 +00:00
|
|
|
/*overflow-x: scroll;*/
|
2020-02-26 05:35:43 +00:00
|
|
|
|
|
|
|
overflow-y: scroll;
|
|
|
|
overflow-x: hidden;
|
2020-02-01 22:21:22 +00:00
|
|
|
}
|
2019-09-10 18:10:11 +00:00
|
|
|
.loading-note {
|
|
|
|
position: absolute;
|
2019-12-20 05:50:50 +00:00
|
|
|
top: 0;
|
2019-09-10 18:10:11 +00:00
|
|
|
left: 0;
|
|
|
|
right: 0;
|
2019-12-20 05:50:50 +00:00
|
|
|
bottom: 0;
|
2019-07-19 20:51:57 +00:00
|
|
|
}
|
2019-07-20 23:07:22 +00:00
|
|
|
/* One note open, in the middle of the screen */
|
|
|
|
.master-note-edit.position-0 {
|
2019-12-20 05:50:50 +00:00
|
|
|
left: 50%;
|
2019-09-10 18:10:11 +00:00
|
|
|
right: 0;
|
2019-07-20 23:07:22 +00:00
|
|
|
}
|
2019-07-21 16:28:07 +00:00
|
|
|
@media only screen and (max-width: 740px) {
|
|
|
|
.master-note-edit.position-0 {
|
|
|
|
left: 0;
|
|
|
|
right: 0;
|
|
|
|
top: 0;
|
|
|
|
bottom: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-20 23:07:22 +00:00
|
|
|
/* Two Notes Open, each takes up 50% of the space */
|
|
|
|
.master-note-edit.position-1 {
|
|
|
|
left: 50%;
|
|
|
|
right: 0%;
|
|
|
|
}
|
2020-03-11 03:47:07 +00:00
|
|
|
.master-note-edit.position-1.full-focus {
|
|
|
|
left: 20%;
|
|
|
|
right: 20%;
|
|
|
|
}
|
2019-07-20 23:07:22 +00:00
|
|
|
.master-note-edit.position-2 {
|
|
|
|
left: 0%;
|
|
|
|
right: 50%;
|
|
|
|
}
|
2020-02-26 05:35:43 +00:00
|
|
|
.master-note-edit.position-3 {
|
|
|
|
display: inline-block;
|
|
|
|
position: inherit;
|
|
|
|
width: 100%;
|
|
|
|
min-height: 200px;
|
|
|
|
height: auto;
|
|
|
|
box-shadow: none;
|
|
|
|
}
|
2019-07-19 20:51:57 +00:00
|
|
|
|
2019-07-29 07:22:47 +00:00
|
|
|
.size-down {
|
2019-07-30 20:27:26 +00:00
|
|
|
animation: size-down 0.5s ease;
|
2019-07-29 07:22:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@keyframes size-down {
|
|
|
|
0% {
|
|
|
|
top: 0;
|
|
|
|
}
|
|
|
|
100% {
|
2019-12-20 05:50:50 +00:00
|
|
|
top: 150vh;
|
2019-07-29 07:22:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-19 20:51:57 +00:00
|
|
|
</style>
|