function MyThoughts(asGlobals) { self = this; this.consts = asGlobals.consts; this.consts.hash_sep = '-'; this.consts.default_page = 'home'; this.consts.title = 'My Thoughts'; this.consts.root = location.protocol+'//'+location.host+location.pathname; this.init = function() { //Variables & constants from php self.vars('tmp', 'object'); self.vars('page', 'string'); self.vars('template', 'boolean'); self.updateVars(asGlobals.vars); //page elem self.elem = {}; self.elem.$Container = $('#container'); //on window resize $(window).resize(self.onResize).resize(); //Hash management self.resetTmpFunctions(); $(window) .bind('hashchange', self.onHashChange) .trigger('hashchange'); $('html').on('keydown', self.onKeydown); }; this.updateVars = function(asVars) { $.each(asVars, function(sKey, oValue){self.vars(sKey, oValue)}); }; /* Scrollbar */ /*this.scrollbar = function(sPos) { var $Cv = $('#main'); if(!self.vars('mobile')) { if(typeof self.vars('scrollbar') === 'undefined') { $Cv.tinyscrollbar({axis:'y', thumbSizeMin:'20', thumbSize:'20'}); self.vars('scrollbar', $Cv.data("plugin_tinyscrollbar")); } else self.vars('scrollbar').update(sPos); } else $Cv.unbind("tinyscrollbar"); }*/ /* Menu */ this.initMenu = function() { self.elem.$Menu.show('fast'); //Thoughts on side menu self.updateSideMenu(); }; this.updateSideMenu = function() { Tools.ajax( 'thoughts', function(asData){ self.elem.$Side.find('.tag:not(.write)').remove(); $.each(asData, function(iKey, asThought) { $Tile = $('
', {'class': 'tag'}).appendTo(self.elem.$Side); var $Link = $('', {'href': '#read-'+asThought.id_thought, title:asThought.created_d+', '+asThought.created_h}) .html(asThought.created_d.replace(' ', '
')) .appendTo($Tile); }); self.elem.$Side.slideDown('fast', self.setSideElemVisibility); } ); }; this.setSideElemVisibility = function() { if(self.elem.$Side) { var iHeight = 0; var iMaxHeight = self.elem.$Side.height(); self.elem.$Side.children().each(function(){ $(this).toggle(iMaxHeight - iHeight > 0); iHeight += $(this).outerHeight(true); }); } }; /* Events */ this.onResize = function() { self.vars('mobile', $('body').css('min-width')=='120px'); self.setSideElemVisibility(); //self.scrollbar(); }; this.onHashChange = function() { var asHash = self.getHash(); self.switchPage(asHash); }; this.resetTmpFunctions = function() { self.pageInit = function(asHash, bFirstPage){console.log('no init for the page: '+asHash.page)}; self.onSamePageMove = function(asHash){return false}; self.onQuitPage = function(){return true}; self.onFeedback = function(sType, sMsg){Tools.feedback(sType, sMsg);}; self.onKeydown = function(oEvent){}; }; this.feedback = function(sType, sMsg) { self.onFeedback(sType, sMsg); }; /* Hash Handling */ this.getHash = function() { var sHash = self.hash(); var asHash = sHash.split(self.consts.hash_sep); var sPage = asHash.shift() || ''; return {hash:sHash, page:sPage, items:asHash}; }; this.setHash = function(sPage, asItems, bReboot) { bReboot = bReboot || false; sPage = sPage || ''; asItems = asItems || []; if(typeof asItems == 'string') asItems = [asItems]; if(sPage != '') { var sItems = (asItems.length > 0)?self.consts.hash_sep+asItems.join(self.consts.hash_sep):''; self.hash(sPage+sItems, bReboot); } }; this.hash = function(hash, bReboot) { bReboot = bReboot || false; if(!hash) return window.location.hash.slice(1); else window.location.hash = '#'+hash; if(bReboot) location.reload(); }; this.getVars = function(fOnSuccess) { fOnSuccess = fOnSuccess || function(){}; getInfo ( 'vars', function(asData) { self.updateVars(asData.vars); fOnSuccess(); }, {} ); }; /* Page Switching */ this.loadHome = function(asData) { self.vars('log_in', asData.log_in); self.vars('id', asData.id); if(self.vars('log_in')) { //Setup menu self.initMenu(); //Swap to default page self.setHash('write'); } }; this.getActionLink = function(sAction, oVars) { if(!oVars) oVars = {}; sVars = ''; for(i in oVars) { sVars += '&'+i+'='+oVars[i]; } return self.consts.process_page+'?a='+sAction+sVars; }; this.switchPage = function(asHash) { var sCurrPage = self.vars('page'); var sNextPage = asHash.page; var bLoggedIn = self.vars('log_in'); var sDefaultPage = bLoggedIn?'write':'logon'; if( asHash.hash == '' || sNextPage == '' || !bLoggedIn && sNextPage != sDefaultPage || bLoggedIn && sNextPage == 'logon' ) { self.setHash(sDefaultPage); //force first page } else { var bSamePage = (sCurrPage == sNextPage); if(self.onQuitPage(bSamePage) && !bSamePage || self.onSamePageMove(asHash)) { //Delete tmp variables self.vars('tmp', {}); //disable tmp functions self.resetTmpFunctions(); //Officially a new page var bFirstPage = (sCurrPage == ''); self.vars('page', sNextPage); //Update Page Title var sDetail = asHash.items[0] || ''; document.title = self.consts.title+' - '+sNextPage+' '+sDetail; //Replacing DOM var $Dom = $(self.consts.pages[sNextPage]); if(bFirstPage) { self.elem.$Container.html($(self.consts.pages['template'])); self.elem.$Main = self.elem.$Container.find('#main'); self.elem.$Menu = self.elem.$Container.find('#menu'); self.elem.$Side = self.elem.$Container.find('#side'); if(self.vars.log_in) self.initMenu(); self.splash(self.elem.$Main, $Dom, asHash, bFirstPage); //first page } else { self.elem.$Main.stop().fadeTo('fast', 0, function(){self.splash(self.elem.$Main, $Dom, asHash, bFirstPage);}); //Switching page } } } }; this.splash = function($FadeInElem, $Dom, asHash, bFirstPage) { //Switch main content $FadeInElem.empty(); $FadeInElem.html($Dom); //Page Bootstrap self.elem.$Main.removeClass('no_frame'); //Show main $FadeInElem.fadeTo('fast', 1, function(){self.pageInit(asHash, bFirstPage);}); }; /* Variables Handling */ this.vars = function(sVarName, oValue) { var asTypes = {boolean:false, string:'', integer:0, array:[], object:{}}; var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName]; var sFirstVarName = asVarName[0]; //Get, only name parameter if(typeof oValue == 'undefined') { return getElem(self.vars, asVarName); } //Init, name & type / default value else if(typeof oValue !== 'undefined' && typeof self.vars[sFirstVarName] === 'undefined') { self.vars[sFirstVarName] = (typeof asTypes[oValue] !== 'undefined')?asTypes[oValue]:oValue; return self.vars[sFirstVarName]; } //Set, name & value else if(typeof oValue !== 'undefined' && typeof self.vars[sFirstVarName] !== 'undefined') { setElem(self.vars, asVarName, oValue); return getElem(self.vars, asVarName); } return null; }; this.tmp = function(sVarName, oValue) { var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName]; asVarName.unshift('tmp'); return self.vars(asVarName, oValue); }; } //Date format const Inline = Quill.import('blots/inline'); class ThoughtDate extends Inline { static create(value) { let node = super.create(); node.setAttribute('class', 'edi_thought_date'); return node; } } ThoughtDate.blotName = 'thought_date'; ThoughtDate.tagName = 'div'; //Time format class ThoughtTime extends Inline { static create(value) { let node = super.create(); node.setAttribute('class', 'edi_thought_time'); return node; } } ThoughtTime.blotName = 'thought_time'; ThoughtTime.tagName = 'div'; Quill.register({'blots/thought_date':ThoughtDate, 'blots/thought_time':ThoughtTime}); class Editor { constructor(sContainerId, bReadOnly) { this.id = 0; this.prevId = 0; this.nextId = 0; this.thoughts = []; this.page = 0; this.keystrokes = 0; this.line = false; this.readOnly = bReadOnly || false; this.sCursorPos = ''; //DOM Elements var sEditorId = 'edi'+Math.floor(Math.random() * 1000); this.$Container = $(sContainerId).addClass('editor').append(self.consts.pages['editor']); this.$EditorBox = this.$Container.find('.edi_container'); this.$Editor = this.$Container.find('.edi_table').attr('id', sEditorId); this.$PrevBtn = $('.prev'); this.$NextBtn = $('.next'); this.onKeyStroke = function(e){}; this.oQuill = new Quill('#'+sEditorId, { theme: 'bubble', placeholder: 'What\'s on your mind?', readOnly: bReadOnly, modules: { toolbar: [['bold', 'italic', 'underline'], [{ 'list': 'ordered'}, { 'list': 'bullet' }], ['clean']] } }); this._initEvents(); this._postInit(); } _initEvents() { //Key strokes this.$Editor.keydown((e) => { if($.inArray(e.which, [13, 37, 38, 39, 40]) != -1) this._onChange('', '', 'user', e); else if(e.which == 33) this.prevPage(); else if(e.which == 34) this.nextPage(); else this.onKeyStroke(e); }); //On text modification this.oQuill.on('text-change', (delta, oldDelta, sSource) => {this._onChange(delta, oldDelta, sSource);}); //Mouse wheel events $(window).mousewheel((turn, iDelta) => { var iNewPage = this.page + ((iDelta > 0)?-1:1); this.moveToPage(iNewPage); return false; }); //Page buttons this.$PrevBtn.click(() => {this.moveToPage(this.page - 1);}); this.$NextBtn.click(() => {this.moveToPage(this.page + 1);}); } _postInit(iPage) { iPage = iPage || 0; this._setPageHeight(); this.moveToPage(iPage); if(!this.readOnly) this.oQuill.focus(); } prevPage() { this.$PrevBtn.click(); } nextPage() { this.$NextBtn.click(); } open(iThoughtId) { if(!(iThoughtId in this.thoughts)) { Tools.ajax( 'load', (asData) => { this.prevId = this.id; this.id = asData.id; this.nextId = asData.next_id; this.thoughts[asData.id] = asData; if(this.prevId==0 || asData.created_d != this.thoughts[this.prevId].created_d) { asData.ops.unshift({ 'attributes': {'thought_date': true}, 'insert': 'Thoughts on '+asData.created_d+"\n" }); } else { asData.ops.unshift({ 'attributes': {'thought_time': true}, 'insert': asData.created_h+"\n" }); } if(!this.isEmpty()) asData.ops = this.getContent().concat(asData.ops); this.oQuill.setContents(asData.ops); this._postInit(this.page); }, {id: iThoughtId} ); } } _setPageHeight() { var iHeight = this.$EditorBox.height(); var iLineHeight = parseInt(this.$Editor.find('p').css('line-height')); var iMaxHeight = Math.floor(iHeight / iLineHeight) * iLineHeight; this.$EditorBox.height(iMaxHeight+'px'); this.lineHeight = iLineHeight; this.pageHeight = iMaxHeight; } _onChange(delta, oldDelta, sSource, e) { if(sSource == 'user') { var range = this.oQuill.getSelection(); if(range) { var bSelection = (typeof e != 'undefined')?e.shiftKey:false; var oSelBound = this.oQuill.getBounds(range.index, range.length); var oEditorCurBound = { top: this.pageHeight * this.page, bottom: this.pageHeight * (this.page + 1)}; //console.log('oEditorCurBound: top='+oEditorCurBound.top+' bottom='+oEditorCurBound.bottom); //console.log('---------------------'); //console.log('range.length = '+range.length); //console.log('oSelBound: top='+oSelBound.top+' bottom='+oSelBound.bottom); //Detecting new selection & saving original line if(!this.line && bSelection) this.line = $.extend({}, oSelBound); else if(!bSelection) this.line = false; //Detecting navigating back to original line var bReset = (this.line && this.line.top == oSelBound.top && this.line.bottom == oSelBound.bottom); //Anticipating arrows (downside of using keydown event) if(e) { switch(e.which) { case 13: //Enter case 40: //Down if(bSelection) { if(bReset) this.sCursorPos = 'last'; if(this.sCursorPos == 'last') { //Downwards selection, expanding oSelBound.bottom += this.lineHeight; } else { //Upwards selection, reducing oSelBound.top += this.lineHeight; } } else oSelBound.bottom += this.lineHeight; break; case 38: //Up if(bSelection) { if(bReset) this.sCursorPos = 'first'; if(this.sCursorPos == 'last') { //Downwards selection, reducing oSelBound.bottom -= this.lineHeight; } else { //Upwards selection, expanding oSelBound.top -= this.lineHeight; } } else oSelBound.top = Math.max(0, oSelBound.top - this.lineHeight); break; case 37: //Left if(bReset && bSelection) this.sCursorPos = 'first'; oSelBound = this.oQuill.getBounds(Math.max(0, range.index - 1), range.length); break; case 39: //Right if(bReset && bSelection) this.sCursorPos = 'last'; oSelBound = this.oQuill.getBounds(range.index + 1, range.length); break; } } else this._incKeyStrokes(); //console.log('oSelBound: top='+oSelBound.top+' bottom='+oSelBound.bottom); var sNewPage = this.page; if( oSelBound.top < oEditorCurBound.top && (!bSelection || this.sCursorPos == 'first') || oSelBound.bottom < oEditorCurBound.top && (!bSelection || this.sCursorPos == 'last')) { sNewPage--; } else if(oSelBound.bottom > oEditorCurBound.bottom && (!bSelection || this.sCursorPos == 'last') || oSelBound.top > oEditorCurBound.bottom && (!bSelection || this.sCursorPos == 'first')) { sNewPage++; } this.moveToPage(sNewPage); } } } moveToPage(iNewPage) { var iContentHeight = this.$Editor.height(); var iLastPage = Math.floor(iContentHeight / this.pageHeight); if(iNewPage == 'last') iNewPage = iLastPage; if(iNewPage >= 0 && iNewPage <= iLastPage) { if(iNewPage != this.page) { var iOldPage = this.page; this.page = iNewPage; //Page Position var iOffset = this.page * this.pageHeight * -1; this.$Editor.css('top', iOffset); } //Page Number //$('.curr').text(self.vars('quill_page') + 1); //Detect First/Last Page this.$PrevBtn.toggleClass('visible', this.page != 0); this.$NextBtn.toggleClass('visible', this.page != iLastPage); if(this.readOnly && this.page == iLastPage && this.nextId > 0) { this.open(this.nextId); } } } _incKeyStrokes() { this.keystrokes += 1; } getContent() { return this.oQuill.getContents().ops; } isEmpty() { const rEmpty = /^(

(|\s+|)<\/p>|)$/gm; return rEmpty.test(this.oQuill.getText().trim()); } quill2Html(asDelta) { var tempCont = document.createElement('div'); (new Quill(tempCont)).setContents(asDelta); return tempCont.getElementsByClassName('ql-editor')[0].innerHTML; } }