<commit_before>/*©agpl*************************************************************************
*                                                                              *
* This file is part of FRIEND UNIFYING PLATFORM.                               *
* Copyright (c) Friend Software Labs AS. All rights reserved.                  *
*                                                                              *
* Licensed under the Source EULA. Please refer to the copy of the GNU Affero   *
* General Public License, found in the file license_agpl.txt.                  *
*                                                                              *
*****************************************************************************©*/

var Config = {
};

Application.lastSaved = 0;

var filebrowserCallbacks = {
	// Check a file on file extension
	checkFile( path, extension )
	{
		var ext = extension.toLowerCase();
		if( ext == 'html' || ext == 'htm' )
		{
			Application.loadFile( path );
		}
		else
		{
			return;
		}
	},
	// Load a file
	loadFile( path )
	{
		var pp = path.toLowerCase();
		if( pp.substr( pp.length - 4, 4 ) == '.htm' || pp.substr( pp.length - 5, 5 ) == '.html' )
		{
			Application.loadFile( path );
		}
		else
		{
			return;
		}
	},
	folderOpen( ele )
	{
		Application.browserPath = ele;
		Application.fileSaved = false;
		Application.lastSaved = 0;
		Application.currentDocument = null;
		Application.refreshFilePane( 'findFirstFile' );
	},
	folderClose( ele )
	{
		Application.currentDocument = null;
		Application.browserPath = ele;
		Application.refreshFilePane( 'findFirstFile' );
	}
};

Application.checkFileType = function( p )
{
	if( p.indexOf( '/' ) > 0 )
	{
		p = p.split( '/' );
		p.pop();
		p = p.join( '/' ) + '/';
	}
	else if( p.indexOf( ':' ) > 0 )
	{
		p = p.split( ':' );
		p = p[0] + ':';
	}
	if( Application.browserPath != p )
	{
		Application.browserPath = p;
	}
}

Application.refreshFilePane = function( method )
{
	if( !method ) method = false;
	
	if( Application.fileBrowser.flags.path.split( '/' ).length > 2 )
	{
		Application.fld.classList.add( 'Hidden' );
	}
	else
	{
		Application.fld.classList.remove( 'Hidden' );
	}
	
	var d = new Door( Application.browserPath );
	
	var self = this;
	
	Application.path = Application.browserPath;
	
	d.getIcons( function( items )
	{
		Application._toBeSaved = null;
		
		if( !items )
		{
			ge( 'FileBar' ).innerHTML = '';
			return;
		}
		var byDate = [];
		items = items.sort( function( a, b ){ return ( new Date( a.DateModified ) ).getTime() - ( new Date( b.DateModified ) ).getTime(); } );
		items.reverse();
		
		var fBar = ge( 'FileBar' );
		if( !fBar.contents )
		{	
			fBar.contents = document.createElement( 'div' );
			fBar.appendChild( fBar.contents );
			
			// Make an "add new note" button
			fBar.add = document.createElement( 'div' );
			fBar.add.className = 'NewItem';
			fBar.add.innerHTML = '<div class="Button IconButton IconSmall fa-plus">&nbsp;' + i18n( 'i18n_add_note' ) + '</div>';
			fBar.add.onclick = function()
			{
				var testFile = 'unnamed';
				var nextTest = testFile;
				var d = new Door( Application.browserPath );
				d.getIcons( function( icons )
				{
					if( icons )
					{
						var found = false;
						var tries = 1;
						
						do
						{
							found = false;
							for( var a = 0; a < icons.length; a++ )
							{
								if( icons[ a ].Filename == nextTest + '.html' )
								{
									nextTest = testFile + '_' + ( ++tries );
									found = true;
									break;
								}
							}
						}
						while( found );
					}
					
					var f = new File();
					f.save( "\n", Application.browserPath + nextTest + '.html' );
					f.onSave = function()
					{
						Application.currentDocument = Application.browserPath + nextTest + '.html';
						Application.sendMessage( {
							command: 'setfilename',
							data: Application.currentDocument
						} );
						Application.refreshFilePane();
						Application.loadFile( Application.browserPath + nextTest + '.html' );
					}
				} );
			}
			fBar.appendChild( fBar.add );
		}
		fBar.contents.innerHTML = '';
		fBar.contents.className = 'ContentFull List ScrollArea ScrollBarSmall BorderRight';
		
		var sw = 2;
		var firstFileNum = 0;
		var foundFile = false;
				
		for( var a = 0; a < items.length; a++ )
		{
			var num = items[ a ];
			var ext = num.Filename.split( '.' );
			ext = ext.pop().toLowerCase();
			if( ext != 'html' && ext != 'htm' ) continue;
			
			if( firstFileNum++ == 0 && method == 'findFirstFile' && !foundFile && !Application.fileSaved )
			{
				Application.loadFile( items[ a ].Path );
				Application.currentDocument = items[ a ].Path;
				fouldFile = true;
			}
			
			sw = sw == 2 ? 1 : 2;
			
			var d = document.createElement( 'div' );
			d.className = 'NotesFileItem Padding BorderBottom MousePointer sw' + sw;
			d.path = items[ a ].Path;
			d.ondblclick = function()
			{
				var s = this;
				if( this.tm )
				{
					clearTimeout( this.tm );
				}
				this.tm = 'block';
				
				var p = this.getElementsByTagName( 'p' )[0];
				var ml = p.innerHTML;
				var inp = document.createElement( 'input' );
				inp.type = 'text';
				inp.className = 'NoMargins';
				inp.style.width = 'calc(100% - 32px)';
				inp.value = p.innerText;
				p.innerHTML = '';
				p.appendChild( inp );
				inp.select();
				inp.focus();
				function renameNow()
				{
					var val = inp.value;
					if( val.substr( val.length - 4, 4 ) != '.htm' && val.substr( val.length - 5, 5 ) != '.html' )
						val += '.html';
					var l = new Library( 'system.library' );
					l.onExecuted = function( e, d )
					{
						if( e == 'ok' )
						{
							Application.sendMessage( {
								command: 'setfilename',
								data: Application.path + val
							} );
							Application.currentDocument = Application.path + val;
							Application.refreshFilePane();
						}
						// Perhaps give error - file exists
						else
						{
							inp.select();
						}
					}
					l.execute( 'file/rename', { path: s.path, newname: val } );
				}
				p.onkeydown = function( e )
				{
					var k = e.which ? e.which : e.keyCode;
					// Abort
					if( k == 27 )
					{
						this.innerHTML = ml;
						s.tm = null;
					}
					// Rename
					else if( k == 13 )
					{
						renameNow();
					}
				}
				inp.onblur = function()
				{
					console.log( 'Blurring?' );
					p.innerHTML = ml;
					s.tm = null;
				}
			}
			
			if( Application.currentDocument && Application.currentDocument == num.Path )
			{
				d.classList.add( 'Selected' );
			}
			else
			{
				d.onmouseover = function()
				{
					this.classList.add( 'Selected' );
				}
				d.onmouseout = function()
				{
					this.classList.remove( 'Selected' );
				}
			}
			
			// No listing here
			var fn = num.Filename.split( '.' );
			fn.pop();
			fn = fn.join( '.' );
			
			d.innerHTML = '<p class="Layout"><strong>' + fn + '</strong></p><p class="Layout"><em>' + num.DateModified + '</em></p>';
			
			// File permissions are given
			// TODO: Check permissions
			var rem = false;
			if( 1 == 1 )
			{
				( function( dd, path ){
					rem = document.createElement( 'div' );
					rem.className = 'IconButton fa-remove IconSmall FloatRight';
					rem.onclick = function( e )
					{
						Confirm( i18n( 'i18n_are_you_sure' ), i18n( 'i18n_delete_note' ), function( result )
						{
							if( result.data )
							{
								var l = new Library( 'system.library' );
								l.onExecuted = function( e, mess )
								{
									if( e == 'ok' )
									{
										if( Application.currentDocument == path )
										{
											Application.newDocument( { just: 'makenew' } );
										}
										Application.refreshFilePane();
									}
								}
								l.execute( 'file/delete', { path: path } );
							}
						} );
						return cancelBubble( e );
					}
					d.insertBefore( rem, d.firstChild );
				} )( d, num.Path );
			}
			
			fBar.contents.appendChild( d );
			( function( dl, path ){
				dl.onclick = function()
				{
					if( dl.tm ) return;
					dl.tm = setTimeout( function()
					{
						dl.tm = null;
						Application.currentDocument = path;
						Application.loadFile( path );
					}, 250 );
				}
			} )( d, num.Path );
		}
		
		if( !foundFile && method == 'findFirstFile' )
		{
			Application.newDocument( { just: 'makenew' } );
		}
	} );
}

Application.run = function( msg, iface )
{
	var self = this;
	
	// To tell about ck
	this.ckinitialized = false;
	this.newLine = true;
	this.initCKE();
	
	this.browserPath = 'Home:Notes/';
	
	this.sessionObject.currentZoom = '100%';
	
	AddEvent( 'onresize', function( e )
	{
		Application.checkWidth();
	} );
	
	// Remember content on scroll!
	document.body.onScroll = function( e )
	{
		if( Application.contentTimeout )
			clearTimeout( Application.contentTimeout );
		Application.contentTimeout = setTimeout( function()
		{
			Application.sendMessage( { 
				command: 'remembercontent', 
				data: Application.editor.getData(),
				scrollTop: Application.editor.element.scrollTop
			} );
			Application.contentTimeout = false;
		}, 250 );
	}
	
	var FileBrowser = new Friend.FileBrowser( ge( 'LeftBar' ), { displayFiles: true, path: 'Home:Notes/', bookmarks: false, rootPath: 'Home:Notes/' }, filebrowserCallbacks );
	FileBrowser.render();
	this.fileBrowser = FileBrowser;
	
	// Make an "add new folder" button
	this.fld = document.createElement( 'div' );
	this.fld.className = 'NewFolder';
	this.fld.innerHTML = '<div class="Button IconButton IconSmall fa-folder">&nbsp;' + i18n( 'i18n_add_folder' ) + '</div>';
	this.fld.onclick = function( e )
	{
		var el = document.createElement( 'input' );
		el.type = 'text';
		el.className = 'FullWidth InputHeight';
		el.placeholder = 'foldername';
		ge( 'LeftBar' ).appendChild( el );
		el.select();
		el.focus();
		el.onkeydown = function( e )
		{
			var w = e.which ? e.which : e.keyCode;
			if( w == 27 )
			{
				el.parentNode.removeChild( el );
			}
			else if( w == 13 )
			{
				var l = new Library( 'system.library' );
				l.onExecuted = function()
				{
					self.fileBrowser.refresh( 'Home:Notes/' );
				}
				l.execute( 'file/makedir', { path: Application.path + this.value } );
			}
		}
		el.blur = function()
		{
			el.parentNode.removeChild( el );
		}
		return cancelBubble( e );
	}
	ge( 'LeftBar' ).parentNode.appendChild( this.fld );
}

Application.checkWidth = function()
{
	var ww = window.innerWidth;
	if( ww < 840 )
	{
		editorCommand( 'staticWidth' );
	}
	else
	{
		editorCommand( 'dynamicWidth' );
	}
}

Application.initCKE = function()
{
	if( typeof( ClassicEditor ) == 'undefined' )
		return setTimeout( 'Application.initCKE()', 50 );
	
	if( Application.ckinitialized ) return;
	Application.ckinitialized = true;
	
	ClassicEditor.create( ge( 'Editor' ), {
			fontFamily: {
				options: [
					'default',
					'Ubuntu, Arial, sans-serif',
					'Ubuntu Mono, Courier New, Courier, monospace',
					'Assistant'
				]
		    }
		} )
		.then( editor => {
		
			Application.editor = editor;
			Application.initializeToolbar();
			
			editor.keystrokes.set( 'Ctrl+S', ( data, stop ) => {
				Application.lastSaved = ( new Date() ).getTime();
				Application.sendMessage( {
					command: 'keydown',
					key: 83,
					ctrlKey: true
				} );
				stop();
			} );
			
			// Load
			editor.keystrokes.set( 'Ctrl+L', ( data, stop ) => {
				Application.sendMessage( {
					command: 'keydown',
					key: 79,
					ctrlKey: true
				} );
				stop();
			} );
			
			editor.keystrokes.set( 'Ctrl+Shift+S', ( data, stop ) => {
				Application.sendMessage( {
					command: 'save_as'
				} );
				stop();
			} );
			
			editor.keystrokes.set( 'Ctrl+Q', ( data, stop ) => {
				Application.sendMessage( {
					command: 'keydown',
					key: 81,
					ctrlKey: true
				} );
				stop();
			} );
			
			// Close
			editor.keystrokes.set( 'Ctrl+W', ( data, stop ) => {
				Application.sendMessage( {
					command: 'keydown',
					key: 73,
					ctrlKey: true
				} );
				stop();
			} );
			
			// Other keys...
			editor.editing.view.document.on( 'keyup', ( evt, data ) => {
			
				// Create temporary file "to be saved"
				if( !Application.currentDocument )
				{
					if( !Application._toBeSaved )
					{
						var fb = ge( 'FileBar' );
						if( fb )
						{
							var d = fb.getElementsByClassName( 'List' );
							if( d.length )
							{
								var el = document.createElement( 'div' );
								el.className = 'NotesFileItem Padding BorderBottom MousePointer New';
								d[0].insertBefore( el, d[0].firstChild );
								Application._toBeSaved = el;
							}
						}
					}
					var data = Application.editor.element.innerText.split( "\n" )[0].substr( 0, 32 );
					data = data.split( /[^ a-z0-9]/i ).join( '' );
					if( !data.length )
						data = 'unnamed';
					Application._toBeSaved.innerHTML = '<p class="Layout"><strong>' + data + '</strong></p><p class="Layout"><em>' + i18n( 'i18n_unsaved' ) + '...</em></p>';
					Application.sendMessage( {
						command: 'setfilename',
						data: Application.path + data + '.html'
					} );
				}
				// Remove "to be saved"
				else if( Application._toBeSaved )
				{
					Application._toBeSaved.parentNode.removeChild( Application._toBeSaved );
					Application._toBeSaved = null;
				}
			
				if( Application.contentTimeout )
					clearTimeout( Application.contentTimeout );
				Application.contentTimeout = setTimeout( function()
				{
					Application.sendMessage( { 
						command: 'remembercontent', 
						data: Application.editor.getData(),
						scrollTop: Application.editor.element.scrollTop
					} );
					// Save again
					if( Application.fileSaved )
					{
						var test = ( new Date() ).getTime();
						if( test - Application.lastSaved > 1000 )
						{
							Application.sendMessage( {
								command: 'keydown',
								key: 83,
								ctrlKey: true
							} );
						}
					}
					Application.contentTimeout = false;
					ge( 'Printable' ).innerHTML = Application.editor.getData();
				}, 750 );
			} );
		} )
		.catch( error => {
			console.error( error );
		} );
	
	return;
	
	//CKEDITOR.config.extraAllowedContent = 'img[src,alt,width,height];h1;h2;h3;h4;h5;h6;p';
}

Application.resetToolbar = function()
{
	// ..

	SelectOption( ge( 'ToolFormat' ), 0 );
}

function MyMouseListener( e )
{
	Application.elementHasLineHeight = false;
	
	var ele = e.target;
	if( !ele )
	{
		Application.resetToolbar();
	}
	else
	{
		// Check node name -----------------------------------------------------
		
		// Need a real node
		while( 
			ele.parentNode && ( !ele.nodeName || ele.nodeName == 'SPAN' || 
			ele.nodeName == 'EM' || ele.nodeName == 'STRONG' || 
			ele.nodeName == 'U' )
		)
		{
			ele = ele.parentNode;
		}
		if( !ele.parentNode )
		{
			SelectOption( ge( 'ToolFormat' ), 0 );
		}
		else
		{
			switch( ele.nodeName.toLowerCase() )
			{
				case 'h1':
					SelectOption( ge( 'ToolFormat' ), 'h1' );
					break;
				case 'h2':
					SelectOption( ge( 'ToolFormat' ), 'h2' );
					break;
				case 'h3':
					SelectOption( ge( 'ToolFormat' ), 'h3' );
					break;
				case 'h4':
					SelectOption( ge( 'ToolFormat' ), 'h4' );
					break;
				case 'h5':
					SelectOption( ge( 'ToolFormat' ), 'h5' );
					break;
				case 'h6':
					SelectOption( ge( 'ToolFormat' ), 'h6' );
					break;
				case 'p':
					SelectOption( ge( 'ToolFormat' ), 'p' );
					break;
				default:
					SelectOption( ge( 'ToolFormat' ), 0 );
					break;
			}
		}
		
		// Check font style ----------------------------------------------------
		var fs = e.target;
		while( fs.parentNode && !fs.style.fontFamily )
		{
			fs = fs.parentNode;
		}
		
		/*if( fs.parentNode && fs.style.fontFamily )
		{
			// TODO: Dynamically load font list!
			var fonts = [ 'default',
					'Ubuntu, Arial, sans-serif',
					'Ubuntu Mono, Courier New, Courier, monospace',
					'Assistant' ];
			var current = Trim( fs.style.fontFamily.toLowerCase().split( '\'' ).join( '' ) );
			var found = false;
			for( var a = 0; a < fonts.length; a++ )
			{
				if( fonts[a] == current )
				{
					SelectOption( ge( 'FontSelector' ), fonts[a] );
					found = true;
					break;
				}
			}
			if( !found )
			{
				SelectOption( ge( 'FontSelector' ), 0 );
			}
		}
		else
		{
			SelectOption( ge( 'FontSelector' ), 0 );
		}*/
		
		// Check for lineheight
		var fs = e.target;
		while( fs.parentNode && !fs.style.lineHeight )
		{
			fs = fs.parentNode;
		}
		if( fs.parentNode && fs.style.lineHeight )
		{
			Application.elementHasLineHeight = true;
		}
		
		// Check font size -----------------------------------------------------
		fs = e.target;
		
		while( fs.parentNode && !fs.style.fontSize )
		{
			fs = fs.parentNode;
		}
		
		if( fs.parentNode && fs.style.fontSize )
		{
			// TODO: Dynamically load font list!
			var fonts = [ 
				 '6pt',  '7pt',  '8pt',  '9pt', '10pt', '11pt', 
				'12pt', '14pt', '16pt', '18pt', '20pt', '24pt',
				'28pt', '32pt', '36pt', '40pt', '48px', '56pt' 
			];
			var current = Trim( fs.style.fontSize.toLowerCase().split( '\'' ).join( '' ) );
			var found = false;
			for( var a = 0; a < fonts.length; a++ )
			{
				if( fonts[a] == current )
				{
					SelectOption( ge( 'FontSize' ), fonts[a] );
					found = true;
					break;
				}
			}
			if( !found )
			{
				SelectOption( ge( 'FontSize' ), '12pt' );
			}
		}
		else
		{
			SelectOption( ge( 'FontSize' ), '12pt' );
		}
	}
	
	// Set the content!
	if( Application.contentTimeout ) clearTimeout( Application.contentTimeout );
	Application.contentTimeout = setTimeout( function()
	{
		Application.sendMessage( { 
			command: 'remembercontent', 
			data: Application.editor.getData(),
			scrollTop: Application.editor.element.scrollTop
		} );
		Application.contentTimeout = false;
	}, 250 );
}

Application.parseText = function( str )
{
	// TODO: Language settings.
	
	
	// Some quick ones
	if( str.toLowerCase() == 'comma' )
	{
		CleanSpeecher( ',' );
		return;
	}
	
	if( str.toLowerCase() == 'period' )
	{
		CleanSpeecher( '.' );
		return;
	}
	
	if( str.toLowerCase() == 'exclamation mark' )
	{
		CleanSpeecher( '!' );
		return;
	}
	
	if( str.toLowerCase() == 'question mark' )
	{
		CleanSpeecher( '?' );
		return;
	}
	
	if( str.toLowerCase() == 'slash' || str.toLowerCase() == 'forward slash' )
	{
		CleanSpeecher( '/' );
		return;
	}
	
	// Fallback if we don't have what we wanted
	var textHere = str.charAt( 0 ).toUpperCase() + str.substr( 1, str.length - 1 );
	
	// Find period
	var end = textHere.split( ' ' );
	if( end[end.length-1].toLowerCase() == 'period' )
	{
		end[end.length-1] = '.';
		textHere = end.join( ' ' ).split( ' .' ).join( '.' );
	}
	
	// New line
	if( str.toLowerCase() == 'new line' )
	{
		textHere = "<br>";
		this.newLine = true;
	}
	
	if( str.toLowerCase().indexOf( 'comma' ) > 0 )
	{
		textHere = textHere.split( 'comma' ).join( ',' );
	}
	
	textHere = textHere.split( ' , ' ).join( ', ' );
	
	// Insert it
	CleanSpeecher( textHere );
}

function CleanSpeecher( textHere )
{
	if( !Application.newLine && textHere.length != 1 )
	{
		textHere = ' ' + textHere;
	}
	textHere = textHere.split( ' i ' ).join( ' I ' );
	if( textHere.substr( 0, 2 ) == 'i ' )
	{
		textHere = 'I ' + textHere.substr( 2, textHere.length - 2 );
	}
	Application.editor.setData( Application.editor.getData() + textHere );
	
	ge( 'Speecher' ).value = '';
	ge( 'Speecher' ).blur();
	
	Application.newLine = false;
}

// Open a file
Application.open = function()
{
	this.sendMessage( { command: 'openfile' } );
}

Application.initializeToolbar = function()
{
	if( !this.toolbar )
	{
		var d = document.createElement( 'div' );
		d.id = 'AuthorToolbar';
		d.className = 'BackgroundDefault ColorDefault BorderTop';
		this.toolbar = d;
		ge( 'RightBar' ).insertBefore( d, ge( 'RightBar' ).firstChild );
		
		var f = new File( 'Progdir:Templates/toolbar.html' );
		f.onLoad = function( data )
		{
			d.innerHTML = data;
			if( isMobile )
			{
				var menuContents = ge( 'MobileMenu' ).getElementsByClassName( 'MenuContents' )[0];
				ge( 'MobileMenu' ).classList.add( 'ScrollBarSmall', 'Button', 'ImageButton', 'IconSmall', 'fa-navicon' );
				ge( 'MobileMenu' ).onclick = function()
				{
					if( this.classList.contains( 'Open' ) )
					{
						this.classList.remove( 'Open' );
					}
					else
					{
						this.classList.add( 'Open' );
					}
				}
				
				function _gtn( p, tags )
				{
					var eles = [];
					for( var a = 0; a < tags.length; a++ )
					{
						var els = p.getElementsByTagName( tags[a] );
						if( els.length )
						{
							var makeAr = [];
							for( var b = 0; b < els.length; b++ )
								makeAr.push( els[b] );
							eles = eles.concat( makeAr );
						}
					}
					return eles;
				}
				
				var eles = _gtn( ge( 'MobileMenu' ), [ 'div', 'input', 'select' ] );
				for( var a = 0; a < eles.length; a++ )
				{
					if( eles[a].title && eles[a].title.length )
					{	
						if( eles[a].style.display == 'none' ) continue;
						var span = document.createElement( 'label' );
						if( !eles[a].id )
							eles[a].id = 'test_' + Math.floor( Math.random() * 100000 );
						span.for = eles[a].id;
						span.classList.add( 'PaddingSmall', 'MarginTop', 'PaddingTop' );
						span.innerHTML = eles[a].title + ':';
						span.ontouchstart = function( e )
						{
							return cancelBubble( e );
						}
						eles[a].parentNode.insertBefore( span, eles[a] );
					}
				}
			}
		}
		f.i18n();
		f.load();
		var bt = ge( 'cke_1_bottom' );
		if( bt )
		{
			bt.className = d.className;
		}
		
		this.initializeBody();
	}
}

Application.initializeBody = function()
{
	AddEvent( 'onmouseup', MyMouseListener );
	AddEvent( 'onkeyup', MyKeyListener );
}

var _repag = 0;
var _lastPageCnt = 0;
var _pageCount = 0;
var _a4pageHeightPx = 1122.66;
function MyKeyListener( e )
{
	if( !Config.usePagination )
	{
		return;
	}
	if( _repag ) return;
}

Application.setCurrentDocument = function( pth )
{
	if( pth.indexOf( '/' ) > 0 )
	{
		var fname = pth.split( '/' );
		fname = fname[fname.length-1];
		this.fileName = fname;
	}
	else
	{
		var fname = pth.split( ':' );
		fname = fname[fname.length-1];
		this.fileName = fname;
	}
	
	this.path = pth.substr( 0, pth.length - this.fileName.length );
	this.currentDocument = pth;
	
	// Update filebrowser
	this.fileBrowser.setPath( this.path );
	
	Application.refreshFilePane();
	
	this.sendMessage( {
		command: 'currentfile',
		path: this.path,
		filename: this.fileName
	} );
}

Application.loadFile = function( path )
{
	Application.statusMessage( 'i18n_status_loading' );
	
	Application.lastSaved = ( new Date() ).getTime();
	
	var extension = path.split( '.' ); extension = extension[extension.length-1];
	
	switch( extension )
	{
		case 'doc':
		case 'docx':
		case 'odt':
		case 'rtf':
			var m = new Module( 'system' );
			m.onExecuted = function( e, data )
			{
				if( e == 'ok' )
				{					
					Application.statusMessage( i18n( 'i18n_loaded' ) );
					Application.editor.setData( data,
						function()
						{
							Application.initializeBody();
						}
					);
					ge( 'Printable' ).innerHTML = Application.editor.getData();
					
					// Remember content and top scroll
					Application.sendMessage( { 
						command: 'remembercontent', 
						data: data,
						path: path,
						scrollTop: 0
					} );
					
					Application.setCurrentDocument( path );
				}
				
				// We got an error...
				else
				{
					Application.statusMessage( i18n('i18n_failed_to_load_document') );
				}	
			}
			m.execute( 'convertfile', { path: path, format: 'html', returnData: true } );
			break;
		default:
			var f = new File( path );
			f.onLoad = function( data )
			{
				Application.statusMessage( i18n('i18n_loaded') );
				
				// Let's fix authid paths and sessionid paths
				var m = false;
				data = data.split( /authid\=[^&]+/i ).join ( 'authid=' + Application.authId );
				data = data.split( /sessionid\=[^&]+/i ).join ( 'authid=' + Application.authId );
		
				var bdata = data.match( /\<body[^>]*?\>([\w\W]*?)\<\/body[^>]*?\>/i );
				if( bdata && bdata[1] )
				{
					function loader( num )
					{
						if( !num ) num = 0;
						if( num > 2 ) return; // <- failed
						var f = Application.editor;
						
						// retry
						if( !f || ( f && !f.element ) )
						{
							return setTimeout( function(){ loader( num+1 ); }, 150 );
						}
						Application.editor.setData( bdata[1] );
						ge( 'Printable' ).innerHTML = Application.editor.getData();
					
						// Remember content and top scroll
						Application.sendMessage( { 
							command: 'remembercontent', 
							data: bdata[1],
							path: path,
							scrollTop: 0
						} );
						
						Application.setCurrentDocument( path );
					}
					loader();
					
				}
				// This is not a compliant HTML document
				else
				{
					Application.editor.setData( data );
					ge( 'Printable' ).innerHTML = Application.editor.getData();
					
					// Remember content and top scroll
					Application.sendMessage( { 
						command: 'remembercontent', 
						path: path,
						data: data,
						scrollTop: 0
					} );
					
					Application.refreshFilePane();
				}
			}
			f.load();
			break;
	}
}

Application.statusMessage = function( msg )
{
	var s = ge( 'Status' );
	if( s.timeout )
	{
		clearTimeout( s.timeout );
		s.style.transition = '';
		s.style.transform = 'translateX(0)';
	}
	s.innerHTML = msg;
	s.timeout = setTimeout( function()
	{
		s.style.transition = 'left,opacity 0.25s,0.25s';
		s.style.transform = 'translateX(0)';
		s.style.opacity = 1;
		s.timeout = setTimeout( function()
		{
			s.style.transform = 'translateX(20px)';
			s.style.opacity = 0;
			s.timeout = setTimeout( function()
			{
				s.innerHTML = '';
				s.style.transform = 'translateX(0)';
				s.style.opacity = 1;
			}, 250 );
		}, 250 );
	}, 1000 );
}

Application.saveFile = function( path, content )
{
	Application.statusMessage( i18n( 'i18n_status_saving' ) );
	
	var extension = path.split( '.' ); extension = extension[extension.length-1];
	
	switch( extension )
	{
		case 'doc':
		case 'docx':
		case 'odt':
		case 'rtf':
			Application.statusMessage( i18n('i18n_converting') );
					
			var m = new Module( 'system' );
			m.onExecuted = function( e, data )
			{
				if( e == 'ok' )
				{
					Application.fileSaved = true;
					Application.lastSaved = ( new Date() ).getTime();
					Application.statusMessage( i18n('i18n_written') );
					Application.currentDocument = path;
					Application.refreshFilePane();
				}
				// We got an error...
				else
				{
					Application.statusMessage( data );
				}
				Application.refreshFilePane();
			}
			m.execute( 'convertfile', { path: path, data: content, dataFormat: 'html', format: extension } );
			break;
		default:
			var f = new File();
			f.onSave = function()
			{
				Application.fileSaved = true;
				Application.lastSaved = ( new Date() ).getTime();
				Application.statusMessage(  i18n('i18n_written') );
				Application.currentDocument = path;
				Application.refreshFilePane();
			}
			f.save( content, path );
			break;
	}
	
	// Save state
	Application.sendMessage( { 
		command: 'remembercontent', 
		data: Application.editor.getData(),
		scrollTop: Application.editor.element.scrollTop
	} );
}

Application.print = function( path, content, callback )
{
	var v = new View( { title: i18n('i18n_print_preview'), width: 200, height: 100 } );
	v.setContent( '<div class="Padding"><p><strong>' + i18n('i18n_generating_print_preview') + '</strong></p></div>' );
	var m = new Module( 'system' );
	m.onExecuted = function( e, data )
	{
		if( e == 'ok' )
		{
			Application.statusMessage( i18n('i18n_print_ready') );
			
			v.close();
			
			if( callback )
			{
				callback( data );
			}
		}
		// We got an error...
		else
		{
			Application.statusMessage( data );
		}
	}
	m.execute( 'convertfile', { path: path, format: 'pdf' } );
}

Application.newDocument = function( args )
{
	this.fileSaved = false;
	this.lastSaved = 0;
	
	// Wait till ready
	if( typeof( ClassicEditor ) == 'undefined' )
	{
		return setTimeout( function()
		{
			Application.newDocument( args );
		}, 50 );
	}
	
	// No content? Forget session..<commit_msg>Allow autosave on loaded documents.<commit_after>/*©agpl*************************************************************************
*                                                                              *
* This file is part of FRIEND UNIFYING PLATFORM.                               *
* Copyright (c) Friend Software Labs AS. All rights reserved.                  *
*                                                                              *
* Licensed under the Source EULA. Please refer to the copy of the GNU Affero   *
* General Public License, found in the file license_agpl.txt.                  *
*                                                                              *
*****************************************************************************©*/

var Config = {
};

Application.lastSaved = 0;

var filebrowserCallbacks = {
	// Check a file on file extension
	checkFile( path, extension )
	{
		var ext = extension.toLowerCase();
		if( ext == 'html' || ext == 'htm' )
		{
			Application.loadFile( path );
		}
		else
		{
			return;
		}
	},
	// Load a file
	loadFile( path )
	{
		var pp = path.toLowerCase();
		if( pp.substr( pp.length - 4, 4 ) == '.htm' || pp.substr( pp.length - 5, 5 ) == '.html' )
		{
			Application.loadFile( path );
		}
		else
		{
			return;
		}
	},
	folderOpen( ele )
	{
		Application.browserPath = ele;
		Application.fileSaved = false;
		Application.lastSaved = 0;
		Application.currentDocument = null;
		Application.refreshFilePane( 'findFirstFile' );
	},
	folderClose( ele )
	{
		Application.currentDocument = null;
		Application.browserPath = ele;
		Application.refreshFilePane( 'findFirstFile' );
	}
};

Application.checkFileType = function( p )
{
	if( p.indexOf( '/' ) > 0 )
	{
		p = p.split( '/' );
		p.pop();
		p = p.join( '/' ) + '/';
	}
	else if( p.indexOf( ':' ) > 0 )
	{
		p = p.split( ':' );
		p = p[0] + ':';
	}
	if( Application.browserPath != p )
	{
		Application.browserPath = p;
	}
}

Application.refreshFilePane = function( method )
{
	if( !method ) method = false;
	
	if( Application.fileBrowser.flags.path.split( '/' ).length > 2 )
	{
		Application.fld.classList.add( 'Hidden' );
	}
	else
	{
		Application.fld.classList.remove( 'Hidden' );
	}
	
	var d = new Door( Application.browserPath );
	
	var self = this;
	
	Application.path = Application.browserPath;
	
	d.getIcons( function( items )
	{
		Application._toBeSaved = null;
		
		if( !items )
		{
			ge( 'FileBar' ).innerHTML = '';
			return;
		}
		var byDate = [];
		items = items.sort( function( a, b ){ return ( new Date( a.DateModified ) ).getTime() - ( new Date( b.DateModified ) ).getTime(); } );
		items.reverse();
		
		var fBar = ge( 'FileBar' );
		if( !fBar.contents )
		{	
			fBar.contents = document.createElement( 'div' );
			fBar.appendChild( fBar.contents );
			
			// Make an "add new note" button
			fBar.add = document.createElement( 'div' );
			fBar.add.className = 'NewItem';
			fBar.add.innerHTML = '<div class="Button IconButton IconSmall fa-plus">&nbsp;' + i18n( 'i18n_add_note' ) + '</div>';
			fBar.add.onclick = function()
			{
				var testFile = 'unnamed';
				var nextTest = testFile;
				var d = new Door( Application.browserPath );
				d.getIcons( function( icons )
				{
					if( icons )
					{
						var found = false;
						var tries = 1;
						
						do
						{
							found = false;
							for( var a = 0; a < icons.length; a++ )
							{
								if( icons[ a ].Filename == nextTest + '.html' )
								{
									nextTest = testFile + '_' + ( ++tries );
									found = true;
									break;
								}
							}
						}
						while( found );
					}
					
					var f = new File();
					f.save( "\n", Application.browserPath + nextTest + '.html' );
					f.onSave = function()
					{
						Application.currentDocument = Application.browserPath + nextTest + '.html';
						Application.sendMessage( {
							command: 'setfilename',
							data: Application.currentDocument
						} );
						Application.refreshFilePane();
						Application.loadFile( Application.browserPath + nextTest + '.html' );
					}
				} );
			}
			fBar.appendChild( fBar.add );
		}
		fBar.contents.innerHTML = '';
		fBar.contents.className = 'ContentFull List ScrollArea ScrollBarSmall BorderRight';
		
		var sw = 2;
		var firstFileNum = 0;
		var foundFile = false;
				
		for( var a = 0; a < items.length; a++ )
		{
			var num = items[ a ];
			var ext = num.Filename.split( '.' );
			ext = ext.pop().toLowerCase();
			if( ext != 'html' && ext != 'htm' ) continue;
			
			if( firstFileNum++ == 0 && method == 'findFirstFile' && !foundFile && !Application.fileSaved )
			{
				Application.loadFile( items[ a ].Path );
				Application.currentDocument = items[ a ].Path;
				fouldFile = true;
			}
			
			sw = sw == 2 ? 1 : 2;
			
			var d = document.createElement( 'div' );
			d.className = 'NotesFileItem Padding BorderBottom MousePointer sw' + sw;
			d.path = items[ a ].Path;
			d.ondblclick = function()
			{
				var s = this;
				if( this.tm )
				{
					clearTimeout( this.tm );
				}
				this.tm = 'block';
				
				var p = this.getElementsByTagName( 'p' )[0];
				var ml = p.innerHTML;
				var inp = document.createElement( 'input' );
				inp.type = 'text';
				inp.className = 'NoMargins';
				inp.style.width = 'calc(100% - 32px)';
				inp.value = p.innerText;
				p.innerHTML = '';
				p.appendChild( inp );
				inp.select();
				inp.focus();
				function renameNow()
				{
					var val = inp.value;
					if( val.substr( val.length - 4, 4 ) != '.htm' && val.substr( val.length - 5, 5 ) != '.html' )
						val += '.html';
					var l = new Library( 'system.library' );
					l.onExecuted = function( e, d )
					{
						if( e == 'ok' )
						{
							Application.sendMessage( {
								command: 'setfilename',
								data: Application.path + val
							} );
							Application.currentDocument = Application.path + val;
							Application.refreshFilePane();
						}
						// Perhaps give error - file exists
						else
						{
							inp.select();
						}
					}
					l.execute( 'file/rename', { path: s.path, newname: val } );
				}
				p.onkeydown = function( e )
				{
					var k = e.which ? e.which : e.keyCode;
					// Abort
					if( k == 27 )
					{
						this.innerHTML = ml;
						s.tm = null;
					}
					// Rename
					else if( k == 13 )
					{
						renameNow();
					}
				}
				inp.onblur = function()
				{
					console.log( 'Blurring?' );
					p.innerHTML = ml;
					s.tm = null;
				}
			}
			
			if( Application.currentDocument && Application.currentDocument == num.Path )
			{
				d.classList.add( 'Selected' );
			}
			else
			{
				d.onmouseover = function()
				{
					this.classList.add( 'Selected' );
				}
				d.onmouseout = function()
				{
					this.classList.remove( 'Selected' );
				}
			}
			
			// No listing here
			var fn = num.Filename.split( '.' );
			fn.pop();
			fn = fn.join( '.' );
			
			d.innerHTML = '<p class="Layout"><strong>' + fn + '</strong></p><p class="Layout"><em>' + num.DateModified + '</em></p>';
			
			// File permissions are given
			// TODO: Check permissions
			var rem = false;
			if( 1 == 1 )
			{
				( function( dd, path ){
					rem = document.createElement( 'div' );
					rem.className = 'IconButton fa-remove IconSmall FloatRight';
					rem.onclick = function( e )
					{
						Confirm( i18n( 'i18n_are_you_sure' ), i18n( 'i18n_delete_note' ), function( result )
						{
							if( result.data )
							{
								var l = new Library( 'system.library' );
								l.onExecuted = function( e, mess )
								{
									if( e == 'ok' )
									{
										if( Application.currentDocument == path )
										{
											Application.newDocument( { just: 'makenew' } );
										}
										Application.refreshFilePane();
									}
								}
								l.execute( 'file/delete', { path: path } );
							}
						} );
						return cancelBubble( e );
					}
					d.insertBefore( rem, d.firstChild );
				} )( d, num.Path );
			}
			
			fBar.contents.appendChild( d );
			( function( dl, path ){
				dl.onclick = function()
				{
					if( dl.tm ) return;
					dl.tm = setTimeout( function()
					{
						dl.tm = null;
						Application.currentDocument = path;
						Application.loadFile( path );
					}, 250 );
				}
			} )( d, num.Path );
		}
		
		if( !foundFile && method == 'findFirstFile' )
		{
			Application.newDocument( { just: 'makenew' } );
		}
	} );
}

Application.run = function( msg, iface )
{
	var self = this;
	
	// To tell about ck
	this.ckinitialized = false;
	this.newLine = true;
	this.initCKE();
	
	this.browserPath = 'Home:Notes/';
	
	this.sessionObject.currentZoom = '100%';
	
	AddEvent( 'onresize', function( e )
	{
		Application.checkWidth();
	} );
	
	// Remember content on scroll!
	document.body.onScroll = function( e )
	{
		if( Application.contentTimeout )
			clearTimeout( Application.contentTimeout );
		Application.contentTimeout = setTimeout( function()
		{
			Application.sendMessage( { 
				command: 'remembercontent', 
				data: Application.editor.getData(),
				scrollTop: Application.editor.element.scrollTop
			} );
			Application.contentTimeout = false;
		}, 250 );
	}
	
	var FileBrowser = new Friend.FileBrowser( ge( 'LeftBar' ), { displayFiles: true, path: 'Home:Notes/', bookmarks: false, rootPath: 'Home:Notes/' }, filebrowserCallbacks );
	FileBrowser.render();
	this.fileBrowser = FileBrowser;
	
	// Make an "add new folder" button
	this.fld = document.createElement( 'div' );
	this.fld.className = 'NewFolder';
	this.fld.innerHTML = '<div class="Button IconButton IconSmall fa-folder">&nbsp;' + i18n( 'i18n_add_folder' ) + '</div>';
	this.fld.onclick = function( e )
	{
		var el = document.createElement( 'input' );
		el.type = 'text';
		el.className = 'FullWidth InputHeight';
		el.placeholder = 'foldername';
		ge( 'LeftBar' ).appendChild( el );
		el.select();
		el.focus();
		el.onkeydown = function( e )
		{
			var w = e.which ? e.which : e.keyCode;
			if( w == 27 )
			{
				el.parentNode.removeChild( el );
			}
			else if( w == 13 )
			{
				var l = new Library( 'system.library' );
				l.onExecuted = function()
				{
					self.fileBrowser.refresh( 'Home:Notes/' );
				}
				l.execute( 'file/makedir', { path: Application.path + this.value } );
			}
		}
		el.blur = function()
		{
			el.parentNode.removeChild( el );
		}
		return cancelBubble( e );
	}
	ge( 'LeftBar' ).parentNode.appendChild( this.fld );
}

Application.checkWidth = function()
{
	var ww = window.innerWidth;
	if( ww < 840 )
	{
		editorCommand( 'staticWidth' );
	}
	else
	{
		editorCommand( 'dynamicWidth' );
	}
}

Application.initCKE = function()
{
	if( typeof( ClassicEditor ) == 'undefined' )
		return setTimeout( 'Application.initCKE()', 50 );
	
	if( Application.ckinitialized ) return;
	Application.ckinitialized = true;
	
	ClassicEditor.create( ge( 'Editor' ), {
			fontFamily: {
				options: [
					'default',
					'Ubuntu, Arial, sans-serif',
					'Ubuntu Mono, Courier New, Courier, monospace',
					'Assistant'
				]
		    }
		} )
		.then( editor => {
		
			Application.editor = editor;
			Application.initializeToolbar();
			
			editor.keystrokes.set( 'Ctrl+S', ( data, stop ) => {
				Application.lastSaved = ( new Date() ).getTime();
				Application.sendMessage( {
					command: 'keydown',
					key: 83,
					ctrlKey: true
				} );
				stop();
			} );
			
			// Load
			editor.keystrokes.set( 'Ctrl+L', ( data, stop ) => {
				Application.sendMessage( {
					command: 'keydown',
					key: 79,
					ctrlKey: true
				} );
				stop();
			} );
			
			editor.keystrokes.set( 'Ctrl+Shift+S', ( data, stop ) => {
				Application.sendMessage( {
					command: 'save_as'
				} );
				stop();
			} );
			
			editor.keystrokes.set( 'Ctrl+Q', ( data, stop ) => {
				Application.sendMessage( {
					command: 'keydown',
					key: 81,
					ctrlKey: true
				} );
				stop();
			} );
			
			// Close
			editor.keystrokes.set( 'Ctrl+W', ( data, stop ) => {
				Application.sendMessage( {
					command: 'keydown',
					key: 73,
					ctrlKey: true
				} );
				stop();
			} );
			
			// Other keys...
			editor.editing.view.document.on( 'keyup', ( evt, data ) => {
			
				// Create temporary file "to be saved"
				if( !Application.currentDocument )
				{
					if( !Application._toBeSaved )
					{
						var fb = ge( 'FileBar' );
						if( fb )
						{
							var d = fb.getElementsByClassName( 'List' );
							if( d.length )
							{
								var el = document.createElement( 'div' );
								el.className = 'NotesFileItem Padding BorderBottom MousePointer New';
								d[0].insertBefore( el, d[0].firstChild );
								Application._toBeSaved = el;
							}
						}
					}
					var data = Application.editor.element.innerText.split( "\n" )[0].substr( 0, 32 );
					data = data.split( /[^ a-z0-9]/i ).join( '' );
					if( !data.length )
						data = 'unnamed';
					Application._toBeSaved.innerHTML = '<p class="Layout"><strong>' + data + '</strong></p><p class="Layout"><em>' + i18n( 'i18n_unsaved' ) + '...</em></p>';
					Application.sendMessage( {
						command: 'setfilename',
						data: Application.path + data + '.html'
					} );
				}
				// Remove "to be saved"
				else if( Application._toBeSaved )
				{
					Application._toBeSaved.parentNode.removeChild( Application._toBeSaved );
					Application._toBeSaved = null;
				}
			
				if( Application.contentTimeout )
					clearTimeout( Application.contentTimeout );
				Application.contentTimeout = setTimeout( function()
				{
					Application.sendMessage( { 
						command: 'remembercontent', 
						data: Application.editor.getData(),
						scrollTop: Application.editor.element.scrollTop
					} );
					// Save again
					if( Application.fileSaved )
					{
						var test = ( new Date() ).getTime();
						if( test - Application.lastSaved > 1000 )
						{
							Application.sendMessage( {
								command: 'keydown',
								key: 83,
								ctrlKey: true
							} );
						}
					}
					Application.contentTimeout = false;
					ge( 'Printable' ).innerHTML = Application.editor.getData();
				}, 750 );
			} );
		} )
		.catch( error => {
			console.error( error );
		} );
	
	return;
	
	//CKEDITOR.config.extraAllowedContent = 'img[src,alt,width,height];h1;h2;h3;h4;h5;h6;p';
}

Application.resetToolbar = function()
{
	// ..

	SelectOption( ge( 'ToolFormat' ), 0 );
}

function MyMouseListener( e )
{
	Application.elementHasLineHeight = false;
	
	var ele = e.target;
	if( !ele )
	{
		Application.resetToolbar();
	}
	else
	{
		// Check node name -----------------------------------------------------
		
		// Need a real node
		while( 
			ele.parentNode && ( !ele.nodeName || ele.nodeName == 'SPAN' || 
			ele.nodeName == 'EM' || ele.nodeName == 'STRONG' || 
			ele.nodeName == 'U' )
		)
		{
			ele = ele.parentNode;
		}
		if( !ele.parentNode )
		{
			SelectOption( ge( 'ToolFormat' ), 0 );
		}
		else
		{
			switch( ele.nodeName.toLowerCase() )
			{
				case 'h1':
					SelectOption( ge( 'ToolFormat' ), 'h1' );
					break;
				case 'h2':
					SelectOption( ge( 'ToolFormat' ), 'h2' );
					break;
				case 'h3':
					SelectOption( ge( 'ToolFormat' ), 'h3' );
					break;
				case 'h4':
					SelectOption( ge( 'ToolFormat' ), 'h4' );
					break;
				case 'h5':
					SelectOption( ge( 'ToolFormat' ), 'h5' );
					break;
				case 'h6':
					SelectOption( ge( 'ToolFormat' ), 'h6' );
					break;
				case 'p':
					SelectOption( ge( 'ToolFormat' ), 'p' );
					break;
				default:
					SelectOption( ge( 'ToolFormat' ), 0 );
					break;
			}
		}
		
		// Check font style ----------------------------------------------------
		var fs = e.target;
		while( fs.parentNode && !fs.style.fontFamily )
		{
			fs = fs.parentNode;
		}
		
		/*if( fs.parentNode && fs.style.fontFamily )
		{
			// TODO: Dynamically load font list!
			var fonts = [ 'default',
					'Ubuntu, Arial, sans-serif',
					'Ubuntu Mono, Courier New, Courier, monospace',
					'Assistant' ];
			var current = Trim( fs.style.fontFamily.toLowerCase().split( '\'' ).join( '' ) );
			var found = false;
			for( var a = 0; a < fonts.length; a++ )
			{
				if( fonts[a] == current )
				{
					SelectOption( ge( 'FontSelector' ), fonts[a] );
					found = true;
					break;
				}
			}
			if( !found )
			{
				SelectOption( ge( 'FontSelector' ), 0 );
			}
		}
		else
		{
			SelectOption( ge( 'FontSelector' ), 0 );
		}*/
		
		// Check for lineheight
		var fs = e.target;
		while( fs.parentNode && !fs.style.lineHeight )
		{
			fs = fs.parentNode;
		}
		if( fs.parentNode && fs.style.lineHeight )
		{
			Application.elementHasLineHeight = true;
		}
		
		// Check font size -----------------------------------------------------
		fs = e.target;
		
		while( fs.parentNode && !fs.style.fontSize )
		{
			fs = fs.parentNode;
		}
		
		if( fs.parentNode && fs.style.fontSize )
		{
			// TODO: Dynamically load font list!
			var fonts = [ 
				 '6pt',  '7pt',  '8pt',  '9pt', '10pt', '11pt', 
				'12pt', '14pt', '16pt', '18pt', '20pt', '24pt',
				'28pt', '32pt', '36pt', '40pt', '48px', '56pt' 
			];
			var current = Trim( fs.style.fontSize.toLowerCase().split( '\'' ).join( '' ) );
			var found = false;
			for( var a = 0; a < fonts.length; a++ )
			{
				if( fonts[a] == current )
				{
					SelectOption( ge( 'FontSize' ), fonts[a] );
					found = true;
					break;
				}
			}
			if( !found )
			{
				SelectOption( ge( 'FontSize' ), '12pt' );
			}
		}
		else
		{
			SelectOption( ge( 'FontSize' ), '12pt' );
		}
	}
	
	// Set the content!
	if( Application.contentTimeout ) clearTimeout( Application.contentTimeout );
	Application.contentTimeout = setTimeout( function()
	{
		Application.sendMessage( { 
			command: 'remembercontent', 
			data: Application.editor.getData(),
			scrollTop: Application.editor.element.scrollTop
		} );
		Application.contentTimeout = false;
	}, 250 );
}

Application.parseText = function( str )
{
	// TODO: Language settings.
	
	
	// Some quick ones
	if( str.toLowerCase() == 'comma' )
	{
		CleanSpeecher( ',' );
		return;
	}
	
	if( str.toLowerCase() == 'period' )
	{
		CleanSpeecher( '.' );
		return;
	}
	
	if( str.toLowerCase() == 'exclamation mark' )
	{
		CleanSpeecher( '!' );
		return;
	}
	
	if( str.toLowerCase() == 'question mark' )
	{
		CleanSpeecher( '?' );
		return;
	}
	
	if( str.toLowerCase() == 'slash' || str.toLowerCase() == 'forward slash' )
	{
		CleanSpeecher( '/' );
		return;
	}
	
	// Fallback if we don't have what we wanted
	var textHere = str.charAt( 0 ).toUpperCase() + str.substr( 1, str.length - 1 );
	
	// Find period
	var end = textHere.split( ' ' );
	if( end[end.length-1].toLowerCase() == 'period' )
	{
		end[end.length-1] = '.';
		textHere = end.join( ' ' ).split( ' .' ).join( '.' );
	}
	
	// New line
	if( str.toLowerCase() == 'new line' )
	{
		textHere = "<br>";
		this.newLine = true;
	}
	
	if( str.toLowerCase().indexOf( 'comma' ) > 0 )
	{
		textHere = textHere.split( 'comma' ).join( ',' );
	}
	
	textHere = textHere.split( ' , ' ).join( ', ' );
	
	// Insert it
	CleanSpeecher( textHere );
}

function CleanSpeecher( textHere )
{
	if( !Application.newLine && textHere.length != 1 )
	{
		textHere = ' ' + textHere;
	}
	textHere = textHere.split( ' i ' ).join( ' I ' );
	if( textHere.substr( 0, 2 ) == 'i ' )
	{
		textHere = 'I ' + textHere.substr( 2, textHere.length - 2 );
	}
	Application.editor.setData( Application.editor.getData() + textHere );
	
	ge( 'Speecher' ).value = '';
	ge( 'Speecher' ).blur();
	
	Application.newLine = false;
}

// Open a file
Application.open = function()
{
	this.sendMessage( { command: 'openfile' } );
}

Application.initializeToolbar = function()
{
	if( !this.toolbar )
	{
		var d = document.createElement( 'div' );
		d.id = 'AuthorToolbar';
		d.className = 'BackgroundDefault ColorDefault BorderTop';
		this.toolbar = d;
		ge( 'RightBar' ).insertBefore( d, ge( 'RightBar' ).firstChild );
		
		var f = new File( 'Progdir:Templates/toolbar.html' );
		f.onLoad = function( data )
		{
			d.innerHTML = data;
			if( isMobile )
			{
				var menuContents = ge( 'MobileMenu' ).getElementsByClassName( 'MenuContents' )[0];
				ge( 'MobileMenu' ).classList.add( 'ScrollBarSmall', 'Button', 'ImageButton', 'IconSmall', 'fa-navicon' );
				ge( 'MobileMenu' ).onclick = function()
				{
					if( this.classList.contains( 'Open' ) )
					{
						this.classList.remove( 'Open' );
					}
					else
					{
						this.classList.add( 'Open' );
					}
				}
				
				function _gtn( p, tags )
				{
					var eles = [];
					for( var a = 0; a < tags.length; a++ )
					{
						var els = p.getElementsByTagName( tags[a] );
						if( els.length )
						{
							var makeAr = [];
							for( var b = 0; b < els.length; b++ )
								makeAr.push( els[b] );
							eles = eles.concat( makeAr );
						}
					}
					return eles;
				}
				
				var eles = _gtn( ge( 'MobileMenu' ), [ 'div', 'input', 'select' ] );
				for( var a = 0; a < eles.length; a++ )
				{
					if( eles[a].title && eles[a].title.length )
					{	
						if( eles[a].style.display == 'none' ) continue;
						var span = document.createElement( 'label' );
						if( !eles[a].id )
							eles[a].id = 'test_' + Math.floor( Math.random() * 100000 );
						span.for = eles[a].id;
						span.classList.add( 'PaddingSmall', 'MarginTop', 'PaddingTop' );
						span.innerHTML = eles[a].title + ':';
						span.ontouchstart = function( e )
						{
							return cancelBubble( e );
						}
						eles[a].parentNode.insertBefore( span, eles[a] );
					}
				}
			}
		}
		f.i18n();
		f.load();
		var bt = ge( 'cke_1_bottom' );
		if( bt )
		{
			bt.className = d.className;
		}
		
		this.initializeBody();
	}
}

Application.initializeBody = function()
{
	AddEvent( 'onmouseup', MyMouseListener );
	AddEvent( 'onkeyup', MyKeyListener );
}

var _repag = 0;
var _lastPageCnt = 0;
var _pageCount = 0;
var _a4pageHeightPx = 1122.66;
function MyKeyListener( e )
{
	if( !Config.usePagination )
	{
		return;
	}
	if( _repag ) return;
}

Application.setCurrentDocument = function( pth )
{
	if( pth.indexOf( '/' ) > 0 )
	{
		var fname = pth.split( '/' );
		fname = fname[fname.length-1];
		this.fileName = fname;
	}
	else
	{
		var fname = pth.split( ':' );
		fname = fname[fname.length-1];
		this.fileName = fname;
	}
	
	this.path = pth.substr( 0, pth.length - this.fileName.length );
	this.currentDocument = pth;
	
	// Update filebrowser
	this.fileBrowser.setPath( this.path );
	
	Application.refreshFilePane();
	
	this.sendMessage( {
		command: 'currentfile',
		path: this.path,
		filename: this.fileName
	} );
}

Application.loadFile = function( path )
{
	this.loading = true;
	
	Application.statusMessage( 'i18n_status_loading' );
	
	Application.lastSaved = ( new Date() ).getTime();
	Application.fileSaved = true;
	
	var extension = path.split( '.' ); extension = extension[extension.length-1];
	
	switch( extension )
	{
		case 'doc':
		case 'docx':
		case 'odt':
		case 'rtf':
			var m = new Module( 'system' );
			m.onExecuted = function( e, data )
			{
				if( e == 'ok' )
				{					
					Application.statusMessage( i18n( 'i18n_loaded' ) );
					Application.editor.setData( data,
						function()
						{
							Application.initializeBody();
						}
					);
					ge( 'Printable' ).innerHTML = Application.editor.getData();
					
					// Remember content and top scroll
					Application.sendMessage( { 
						command: 'remembercontent', 
						data: data,
						path: path,
						scrollTop: 0
					} );
					
					Application.setCurrentDocument( path );
				}
				
				// We got an error...
				else
				{
					Application.statusMessage( i18n('i18n_failed_to_load_document') );
				}	
				Application.loading = false
			}
			m.execute( 'convertfile', { path: path, format: 'html', returnData: true } );
			break;
		default:
			var f = new File( path );
			f.onLoad = function( data )
			{
				Application.statusMessage( i18n('i18n_loaded') );
				
				// Let's fix authid paths and sessionid paths
				var m = false;
				data = data.split( /authid\=[^&]+/i ).join ( 'authid=' + Application.authId );
				data = data.split( /sessionid\=[^&]+/i ).join ( 'authid=' + Application.authId );
		
				var bdata = data.match( /\<body[^>]*?\>([\w\W]*?)\<\/body[^>]*?\>/i );
				if( bdata && bdata[1] )
				{
					function loader( num )
					{
						if( !num ) num = 0;
						if( num > 2 ) return; // <- failed
						var f = Application.editor;
						
						// retry
						if( !f || ( f && !f.element ) )
						{
							return setTimeout( function(){ loader( num+1 ); }, 150 );
						}
						Application.editor.setData( bdata[1] );
						ge( 'Printable' ).innerHTML = Application.editor.getData();
					
						// Remember content and top scroll
						Application.sendMessage( { 
							command: 'remembercontent', 
							data: bdata[1],
							path: path,
							scrollTop: 0
						} );
						
						Application.setCurrentDocument( path );
					}
					loader();
					
				}
				// This is not a compliant HTML document
				else
				{
					Application.editor.setData( data );
					ge( 'Printable' ).innerHTML = Application.editor.getData();
					
					// Remember content and top scroll
					Application.sendMessage( { 
						command: 'remembercontent', 
						path: path,
						data: data,
						scrollTop: 0
					} );
					
					Application.refreshFilePane();
				}
				Application.loading = false;
			}
			f.load();
			break;
	}
}

Application.statusMessage = function( msg )
{
	var s = ge( 'Status' );
	if( s.timeout )
	{
		clearTimeout( s.timeout );
		s.style.transition = '';
		s.style.transform = 'translateX(0)';
	}
	s.innerHTML = msg;
	s.timeout = setTimeout( function()
	{
		s.style.transition = 'left,opacity 0.25s,0.25s';
		s.style.transform = 'translateX(0)';
		s.style.opacity = 1;
		s.timeout = setTimeout( function()
		{
			s.style.transform = 'translateX(20px)';
			s.style.opacity = 0;
			s.timeout = setTimeout( function()
			{
				s.innerHTML = '';
				s.style.transform = 'translateX(0)';
				s.style.opacity = 1;
			}, 250 );
		}, 250 );
	}, 1000 );
}

Application.saveFile = function( path, content )
{
	Application.statusMessage( i18n( 'i18n_status_saving' ) );
	
	var extension = path.split( '.' ); extension = extension[extension.length-1];
	
	switch( extension )
	{
		case 'doc':
		case 'docx':
		case 'odt':
		case 'rtf':
			Application.statusMessage( i18n('i18n_converting') );
					
			var m = new Module( 'system' );
			m.onExecuted = function( e, data )
			{
				if( e == 'ok' )
				{
					Application.fileSaved = true;
					Application.lastSaved = ( new Date() ).getTime();
					Application.statusMessage( i18n('i18n_written') );
					Application.currentDocument = path;
					Application.refreshFilePane();
				}
				// We got an error...
				else
				{
					Application.statusMessage( data );
				}
				Application.refreshFilePane();
			}
			m.execute( 'convertfile', { path: path, data: content, dataFormat: 'html', format: extension } );
			break;
		default:
			var f = new File();
			f.onSave = function()
			{
				Application.fileSaved = true;
				Application.lastSaved = ( new Date() ).getTime();
				Application.statusMessage(  i18n('i18n_written') );
				Application.currentDocument = path;
				Application.refreshFilePane();
			}
			f.save( content, path );
			break;
	}
	
	// Save state
	Application.sendMessage( { 
		command: 'remembercontent', 
		data: Application.editor.getData(),
		scrollTop: Application.editor.element.scrollTop
	} );
}

Application.print = function( path, content, callback )
{
	var v = new View( { title: i18n('i18n_print_preview'), width: 200, height: 100 } );
	v.setContent( '<div class="Padding"><p><strong>' + i18n('i18n_generating_print_preview') + '</strong></p></div>' );
	var m = new Module( 'system' );
	m.onExecuted = function( e, data )
	{
		if( e == 'ok' )
		{
			Application.statusMessage( i18n('i18n_print_ready') );
			
			v.close();
			
			if( callback )
			{
				callback( data );
			}
		}
		// We got an error...
		else
		{
			Application.statusMessage( data );
		}
	}
	m.execute( 'convertfile', { path: path, format: 'pdf' } );
}

Application.newDocument = function( args )
{
	// Don't make new document while loading..
	if( this.loading )
		return;
	
	this.fileSaved = false;
	this.lastSaved = 0;
	
	// Wait till ready
	if( typeof( ClassicEditor ) == 'undefined' )
	{
		return setTimeout( function()
		{
			Application.newDocument( args );
		}, 50 );
	}
	
	// No content? Forget session..
{'fraction_non_alphanumeric': 0.17080679027723406, 'fraction_numerical': 0.010285063525392362, 'mean_word_length': 2.3112773980674053, 'pattern_counts': {'":': 0, '<': 79, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2554686', 'n_tokens_mistral': 24747, 'n_tokens_neox': 19080, 'n_words': 4164}