<template>
	<!-- change class to .master-note-edit to have it popup on the screen  -->
	<div 
		id="InputNotes" 
		class="master-note-edit" 
		@keyup.esc="close" 
		:class="[{'size-down':(sizeDown == true), 'padded-bottom':extraToolbarsVisible },  'position-'+position ]" 
		:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}"
	>

		<!-- Loading indicator  -->
		<div v-if="loading" class="loading-note">
			<div class="ui active dimmer">
				<div class="ui text loader">{{loadingMessage}}</div>
			</div>
		</div>

		<div class="note-menu">

			<nm-button v-on:click.native="close" icon="close" />

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

			<nm-button v-on:click.native="modifyFont('1.4em')" icon="text height" />

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

		</div>

		<div id="squire-id" class="squire-box" ref="squirebox"></div>

		<!-- && 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 }">

			<div class="note-menu shrink-icons-on-mobile">

				<!-- Pin Button  -->
				<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>

				<!-- archive button -->
				<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>
	
				<!-- colors button -->
				<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
					v-on:click.native="showTagSlideMenu = !showTagSlideMenu; modified = true"
					icon="tags"
					text="Tags"
					tip="Tags"
				></nm-button>

				<!-- file upload button  -->
				<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>

			</div>
		</div>

		<!-- Side slide menus for colors, tags and images -->
	
		<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>

		<side-slide-menu v-if="showTagSlideMenu" v-on:close="showTagSlideMenu = false" name="tags" :style-object="styleObject">
			<div class="ui basic segment">
				<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
			</div>
		</side-slide-menu>

		<side-slide-menu v-if="showFilesSideMenu" v-on:close="showFilesSideMenu = false" name="images" :style-object="styleObject">
			<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>

		<side-slide-menu v-if="showNoteOptions" v-on:close="showNoteOptions = false" name="note-options" :style-object="styleObject">
			<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>
					<div class="eight wide column">
						<div class="ui labeled icon fluid basic button" v-on:click="undoCustom">
							<i class="undo icon"></i>
							Undo last change
						</div>
					</div>
					<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>
					<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>

	</div>
</template>

<script>

	import axios from 'axios'
	const DiffMatchPatch = require('../../../server/helpers/DiffMatchPatch')
	
	export default {
	name: 'InputNotes',
		props: [ 'noteid', 'position' ],
		components:{
			'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
		},
		data(){
			return {
				loading: true,
				loadingMessage: 'Loading Note',
				currentNoteId: 0,
				modified: false,
				noteText: '',
				rawTextId: 0,
				shareUsername: null,
				diffNoteText: '',
				statusText: 'Saved',
				lastNoteHash: null,
				saveDebounce: null, //Prevent save from being called numerous times quickly
				updated: 'Never',
				editDebounce: null,
				emitChangeDebounce: null,
				keyPressesCounter: 0, //Determen keys pressed between saves
				pinned: 0,
				archived: 0,
				attachmentCount: 0,
				styleObject: { 'noteText':null,'noteBackground':null, 'noteIcon':null, 'iconColor':null }, //Style object. Determines colors and badges

				sizeDown: false, //Used to animate close state
				
				colorPickerLocation: null,

                tinymce: null, //Initialized editor instance

                //Settings vars
                showAllSettings: true,
                lastVisibilityState: null,

                //All the squire settings
                editor: null,
                // pastFocusedNode: null,
                usersOnNote: 0,

                extraToolbarsVisible: true,
                showTagSlideMenu: false,
                colorPickerVisible: false, 
                showFilesSideMenu: false,
                showNoteOptions: false,
			}
		},
		watch: {
			noteid:function(newVal, oldVal){

				if(newVal == this.currentNoteId){
					return
				}

				if(newVal == oldVal){
					return
				}
				
				this.currentNoteId = newVal
				this.loadNote(this.currentNoteId)
				
			}
		},
		beforeMount(){
			this.$bus.$on('new_file_upload', ({noteId, imageCode}) => {
				if(this.noteid == noteId && this.editor){
					this.editor.moveCursorToEnd()
					this.editor.insertHTML(imageCode)
					this.save()
				}
			})
		},
		beforeDestroy(){

			this.$io.emit('leave_room', this.rawTextId)

			document.removeEventListener('visibilitychange', this.checkForUpdatedNote)

			this.editor.destroy()

			this.$bus.$off('new_file_upload')
			
		},
		mounted: function() {

			document.addEventListener('visibilitychange', this.checkForUpdatedNote)

			this.$nextTick(() => {

				this.loadNote(this.noteid)
			})
		},
		methods: {
			initSquire(){
				
				//Set up squire and load note text
				this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
				this.setText(this.noteText)

				//Click Event - Open links when clicked in editor or toggle checks
				this.editor.addEventListener('click', e => {

					//Link clicked in editor - open link
					if(e.target.nodeName == 'A' && e.target.href){
						window.open(e.target.href)
					}

					//List Item clicked in editor - toggle link state
					if(e.target.nodeName == 'LI'){

						let el = e.target

						//Adjust ofset by 40 px
						let correction = 40

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

							//Will hide keyboard if clicked, much better for mobile
							this.editor.blur()

							//Area before element was clicked, they clicked the checkbox
							this.onKeyup()
							if (el.className === 'active'){
								el.className = 'inactive';
							} else {
								el.className = 'active';
							}
						}

						
					}
					
				})

				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
					}
				})
				this.editor.addEventListener('blur',  e => {
					this.save()
					this.extraToolbarsVisible = true
				})
			},
			//If nothing is selected, select the entire line
			selectLineIfNoSelect(){

				//Select entire line if range is not set 
				let selection = this.editor.getSelection()

				if(selection.startOffset == selection.endOffset && selection.startContainer == selection.endContainer){

					let squireRange = this.editor.createRange(
						selection.startContainer, 0, 
						selection.endContainer, selection.commonAncestorContainer.textContent.length)

					this.editor.setSelection(squireRange)
				}
			},
			modifyFont(inSize){

				this.selectLineIfNoSelect()

				let fontInfo = this.editor.getFontInfo()
				//Toggle font size between large and normal
				if(fontInfo.size){
					this.editor.setFontSize(null)
				} else {
					this.editor.setFontSize(inSize)
				}
			},
			toggleList(type){

				//Undo list if its already a lits
				if(this.editor.hasFormat(type)){
					this.editor.removeList()
					return
				}

				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()
				}
			},
			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)
						
					}
				})
			},
			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) => {
					try {
						return Function('"use strict"; return (' + string + ')')();
					} 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
					let equalLocation = line.indexOf('=')
					if(equalLocation != -1 && (line.length-1) == equalLocation ){

						//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
							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
						}
					}
					
				})
			},
			setText(inText){

				this.editor.setHTML(inText)
				this.noteText = this.editor._getHTML()
				this.diffNoteText = this.editor._getHTML()
			},
			getText(){

				return this.editor.getHTML()
			},
			showColorPicker(event){

				this.colorPickerVisible = !this.colorPickerVisible
				this.colorPickerLocation = {'x':event.clientX, 'y':event.clientY}
			},
			openEditAttachment(){

				this.$router.push('/attachments/note/'+this.currentNoteId)
			},
			onTogglePinned(){

				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
				this.save()
			},
			onToggleArchived(){

				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
				this.save()
			},
			onCloseColorChanger(){

				this.colorPickerVisible = false
			},
			onChangeColor(newStyleObject){

				//Set new style object for note, page will use some styles, styles will be saved to database
				this.styleObject = newStyleObject

				this.lastNoteHash = 0 //Update hash to force note update on next save
				this.save()
			},
			loadNote(noteId){

				let vm = this

				let doing = ['Loading','Loading','Getting','Fetching','Grabbing','Sequencing','Organizing','Untangling','Processing','Refining','Extracting','Fusing','Pruning','Expanding','Enlarging','Transfiguring','Quantizing','Ingratiating','Lumping']
				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)]
				vm.loadingMessage = p1 + ' ' + p2
				
				//Component is activated with NoteId in place, lookup text with associated ID
				if(this.$store.getters.getLoggedIn){
					axios.post('/api/note/get', {'noteId': noteId})
					.then(response => {

						//Set up local data
						vm.currentNoteId = noteId
						this.rawTextId = response.data.rawTextId
						this.shareUsername = response.data.shareUsername

						vm.noteText = response.data.text
						vm.diffNoteText = response.data.text

						vm.updated = response.data.updated
						vm.lastNoteHash = vm.hashString(response.data.text)
						//Set up note colors
						if(response.data.color){
							vm.styleObject = JSON.parse(response.data.color)
						}
						
						if(response.data.pinned != null){
							vm.pinned = response.data.pinned
						}
						vm.archived = response.data.archived
						vm.attachmentCount = response.data.attachment_count

						this.loading = false

						vm.$nextTick(() => {
							
							this.setupWebSockets()
							this.initSquire()
						})

					})
				} else {
					console.log('Could not fetch note')
				}
			},
			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', {
						id: this.rawTextId,
						diff: patch_text
					})


				}, 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)

				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)+']' : '')
			},
			getElementByXPath(xpath){

				return new XPathEvaluator()
					.createExpression(xpath)
					.evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE) .singleNodeValue 
			},
			onKeyup(event){

				this.statusText = 'Save'

				this.diffText()

				//Each note, save after 5 seconds, focus lost or 30 characters typed.
				clearTimeout(this.editDebounce)
				this.editDebounce = setTimeout(() => {
					this.save()
				}, 5000)

				//Save after 30 keystrokes
				this.keyPressesCounter = (this.keyPressesCounter + 1)
				if(this.keyPressesCounter > 30){
					this.keyPressesCounter = 0
					this.save()
				}
			},
			save(){
				return new Promise((resolve, reject) => {
					//Clear other debounced events to prevent double calling of save
					// clearTimeout(this.editDebounce)

					// return resolve(true)

					//Don't save note if its hash doesn't change
					const currentNoteText = this.getText()
					if( this.lastNoteHash == this.hashString( currentNoteText )){
						this.statusText = 'Saved'
						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)
					}
					
					const postData = {
						'noteId':this.currentNoteId,
						'text': currentNoteText,
						'color': JSON.stringify(this.styleObject), //Save little json color object
						'pinned': this.pinned,
						'archived':this.archived,
					}

					this.statusText = 'Saving'
					axios.post('/api/note/update', postData).then( response => {
						this.statusText = 'Saved'
						this.updated = Math.round((+new Date)/1000)
						this.modified = true

						//Update last saved note hash
						this.lastNoteHash = this.hashString( currentNoteText )
						return resolve(true)
					})
				})
			},
			checkForUpdatedNote(){

				// return

				//If user leaves page then returns to page, reload the first batch
				if(this.lastVisibilityState == 'hidden' && document.visibilityState == 'visible'){
					// console.log('Checking for note updates after visibility change.')

					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
			},
			hashString(text){

				var hash = 0;
				if (text == null || text.length == 0) {
					return hash;
				}

				//Simplified for speed
				// return text.length

				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
				}

				return hash;
			},
			close(){

				// this.loading = true
				// this.loadingMessage = 'Save and Close'

				this.save().then( result => {

					this.sizeDown = true
					//This timeout allows animation to play before closing 
					setTimeout(() => {
						this.$bus.$emit('close_active_note', {
							position: this.position, noteId: this.noteid, modified: this.modified
						})
						return
					}, 300)
				})
			},
			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
				})
				
				//Server will hand deliver diffs from other notes to this one
				this.$io.on('incoming_diff', incomingDiffData => {
					this.patchText(incomingDiffData)
				})
			}
		}
	}
</script>

<style type="text/css" scoped>

	/* squire styles */

	/*Settings manager styles */
	.all-settings {
		/*border-top: 1px solid #534c68;*/
		background: #221f2b;
		position: absolute;
		bottom: 0;
		right: 0;
		left: 0;
		z-index: 99;
		/*border: 1px solid;*/
		/*background-color: var(--background_color);*/
		/*border-color: var(--border_color);*/
		box-sizing: border-box;
		/*border-radius: 7px;*/
		/*box-shadow: 0px 3px 7px 0px rgba(140,140,140,1);*/
		/*padding: 1.2em 0 0;*/
	}
	.low-settings {
		bottom: 0px;
		cursor: pointer;
		height: 1.4em;
		padding-top: 1.5em;
		overflow: hidden;
		border: 1px solid #534c68;
	}
	/*End Settings manager styles */


	/* container styles change based on mobile and number of open screens */
	.master-note-edit {
		position: fixed;
		bottom: 0;
		background: var(--background_color);
		/*color: var(--text_color);*/
		height: 100vh;
		box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
		z-index: 1001;
		/*overflow-x: scroll;*/
	}
	.padded-bottom {
		padding-bottom: 20px;
	}
	.loading-note {
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
	}
	/* One note open, in the middle of the screen */
	.master-note-edit.position-0 {
		left: 50%;
		right: 0;
	}
	@media only screen and (max-width: 740px) {
		.master-note-edit.position-0 {
			left: 0;
			right: 0;
			top: 0;
			bottom: 0;
		}
	}

	/* Two Notes Open, each takes up 50% of the space */
	.master-note-edit.position-1 {
		left: 50%;
		right: 0%;
	}
	.master-note-edit.position-2 {
		left: 0%;
		right: 50%;
	}

	.size-down {
		animation: size-down 0.5s ease;
	}

	@keyframes size-down {
		0% {
			/*opacity: 1;*/
			/*top: 0;*/
			top: 0;
		}
		100% {
			/*opacity: 0;*/
			/*top: 30vh;*/
			top: 150vh;
		}
	}

</style>