mirror of https://github.com/JustKato/FreePad.git
				
				
				
			Compare commits
	
		
			No commits in common. "ca10033ecf5d5f1dd2c4fddfbc6cf314e1658969" and "400fd23b3efa74d8c4589200c61f795963918be9" have entirely different histories.
		
	
	
		
			ca10033ecf
			...
			400fd23b3e
		
	
		
							
								
								
									
										2
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										2
									
								
								go.mod
								
								
								
								
							|  | @ -4,8 +4,6 @@ go 1.15 | |||
| 
 | ||||
| require ( | ||||
| 	github.com/gin-gonic/gin v1.7.7 | ||||
| 	github.com/google/uuid v1.3.0 // indirect | ||||
| 	github.com/gorilla/websocket v1.5.0 // indirect | ||||
| 	github.com/joho/godotenv v1.4.0 | ||||
| 	github.com/mrz1836/go-sanitize v1.1.5 | ||||
| 	github.com/ulule/limiter/v3 v3.10.0 | ||||
|  |  | |||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							|  | @ -38,10 +38,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | |||
| github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= | ||||
| github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= | ||||
| github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= | ||||
| github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= | ||||
| github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= | ||||
|  |  | |||
|  | @ -1,197 +0,0 @@ | |||
| package socketmanager | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"github.com/JustKato/FreePad/lib/objects" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/gorilla/websocket" | ||||
| ) | ||||
| 
 | ||||
| var wsUpgrader = websocket.Upgrader{ | ||||
| 	ReadBufferSize:  1024, // TODO: Make it configurable via the .env file
 | ||||
| 	WriteBufferSize: 1024, // TODO: Make it configurable via the .env file
 | ||||
| } | ||||
| 
 | ||||
| // The pad socket map caches all of the existing sockets
 | ||||
| var padSocketMap map[string]map[string]*websocket.Conn = make(map[string]map[string]*websocket.Conn) | ||||
| 
 | ||||
| // TODO: Use generics so that we can take string messages, that'd be nice!
 | ||||
| type SocketMessage struct { | ||||
| 	EventType string                 `json:"eventType"` | ||||
| 	PadName   string                 `json:"padName"` | ||||
| 	Message   map[string]interface{} `json:"message"` | ||||
| } | ||||
| 
 | ||||
| // Bind the websockets to the gin router
 | ||||
| func BindSocket(router *gin.RouterGroup) { | ||||
| 
 | ||||
| 	router.GET("/get/:pad", func(ctx *gin.Context) { | ||||
| 		// Get the name of the pad to assign to this socket
 | ||||
| 		padName := ctx.Param("pad") | ||||
| 		// Upgrade the socket connection
 | ||||
| 		webSocketUpgrade(ctx, padName) | ||||
| 	}) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func webSocketUpgrade(ctx *gin.Context, padName string) { | ||||
| 
 | ||||
| 	conn, err := wsUpgrader.Upgrade(ctx.Writer, ctx.Request, ctx.Request.Header) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Failed to set websocket upgrade: %v\n", err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if we have any sockets in this padName
 | ||||
| 	if _, ok := padSocketMap[padName]; !ok { | ||||
| 		// Initialize a new map of sockets
 | ||||
| 		padSocketMap[padName] = make(map[string]*websocket.Conn) | ||||
| 	} | ||||
| 
 | ||||
| 	// Give this socket a token
 | ||||
| 	socketToken := uuid.NewString() | ||||
| 
 | ||||
| 	// Set the current connection at the socket Token position
 | ||||
| 	padSocketMap[padName][socketToken] = conn | ||||
| 
 | ||||
| 	// Somone just connected
 | ||||
| 	UpdatePadStatus(padName) | ||||
| 
 | ||||
| 	// Start listening to this socket
 | ||||
| 	for { | ||||
| 		// Try Read the JSON input from the socket
 | ||||
| 		_, msg, err := conn.ReadMessage() | ||||
| 
 | ||||
| 		// Check if anything but a read limit was created
 | ||||
| 		if err != nil && !errors.Is(err, websocket.ErrReadLimit) { | ||||
| 			// Remove self from the cache
 | ||||
| 			delete(padSocketMap[padName], socketToken) | ||||
| 			// Somone just disconnected
 | ||||
| 			UpdatePadStatus(padName) | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			// There has been an error reading the message
 | ||||
| 			fmt.Println("Failed to read from the socket but probably still connected") | ||||
| 			// Skip this cycle
 | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Init the variable
 | ||||
| 		var p SocketMessage | ||||
| 		// Try and parse the json
 | ||||
| 		err = json.Unmarshal([]byte(msg), &p) | ||||
| 		if err != nil { | ||||
| 			// There has been an error reading the message
 | ||||
| 			fmt.Println("Failed to parse the JSON", err) | ||||
| 			// Skip this cycle
 | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Pass the message to the proper handlers
 | ||||
| 		handleSocketMessage(p, socketToken, padName) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Handle the socket's message
 | ||||
| func handleSocketMessage(msg SocketMessage, socketToken string, padName string) { | ||||
| 
 | ||||
| 	// Check if this is a pad Update
 | ||||
| 	if msg.EventType == `padUpdate` { | ||||
| 		handlePadUpdate(msg, socketToken, padName) | ||||
| 
 | ||||
| 		// Serialize the message
 | ||||
| 		serialized, err := json.Marshal(msg) | ||||
| 		// Check if there was an error
 | ||||
| 		if err != nil { | ||||
| 			fmt.Println(`Failed to broadcast the padUpdate`, err) | ||||
| 			// Stop the execution
 | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// Alert all the other pads other than this one.
 | ||||
| 		for k, pad := range padSocketMap[padName] { | ||||
| 			// Check if this is the same socket.
 | ||||
| 			if k == socketToken { | ||||
| 				// Skip self
 | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 			// Send the message to the others.
 | ||||
| 			pad.WriteMessage(websocket.TextMessage, serialized) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func handlePadUpdate(msg SocketMessage, socketToken string, padName string) { | ||||
| 
 | ||||
| 	// Check if the msg content is valid
 | ||||
| 	if _, ok := msg.Message[`content`]; !ok { | ||||
| 		fmt.Printf("Failed to update pad %s, invalid message\n", padName) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Check that the content is string
 | ||||
| 	newPadContent, ok := msg.Message[`content`].(string) | ||||
| 	if !ok { | ||||
| 		fmt.Printf("Type assertion failed for %s, invalid message\n", padName) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Get the pad
 | ||||
| 	pad := objects.GetPost(padName, false) | ||||
| 	// Update the pad contents
 | ||||
| 	pad.Content = newPadContent | ||||
| 
 | ||||
| 	// Save to file
 | ||||
| 	objects.WritePost(pad) | ||||
| } | ||||
| 
 | ||||
| // Update the current users of the pad about the amount of live viewers.
 | ||||
| func UpdatePadStatus(padName string) { | ||||
| 
 | ||||
| 	// Grab info about the map's key
 | ||||
| 	sockets, ok := padSocketMap[padName] | ||||
| 	// Check if the pad is set and has sockets connected.
 | ||||
| 	if !ok || len(sockets) < 1 { | ||||
| 		// Quit
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate the message
 | ||||
| 	msg := SocketMessage{ | ||||
| 		EventType: `statusUpdate`, | ||||
| 		PadName:   padName, | ||||
| 		Message: gin.H{ | ||||
| 			// Send the current amount of live viewers
 | ||||
| 			"currentViewers": len(sockets), | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	BroadcastMessage(padName, msg) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func BroadcastMessage(padName string, msg SocketMessage) { | ||||
| 
 | ||||
| 	// Grab info about the map's key
 | ||||
| 	sockets, ok := padSocketMap[padName] | ||||
| 	// Check if the pad is set and has sockets connected.
 | ||||
| 	if !ok || len(sockets) < 1 { | ||||
| 		// Quit
 | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// Get all the participants of the pad group
 | ||||
| 	for _, s := range sockets { | ||||
| 		// Send the message to the socket
 | ||||
| 		s.WriteJSON(msg) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										4
									
								
								main.go
								
								
								
								
							
							
						
						
									
										4
									
								
								main.go
								
								
								
								
							|  | @ -7,7 +7,6 @@ import ( | |||
| 	"github.com/JustKato/FreePad/lib/controllers" | ||||
| 	"github.com/JustKato/FreePad/lib/objects" | ||||
| 	"github.com/JustKato/FreePad/lib/routes" | ||||
| 	"github.com/JustKato/FreePad/lib/socketmanager" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/joho/godotenv" | ||||
| ) | ||||
|  | @ -53,9 +52,6 @@ func main() { | |||
| 	// Add Routes
 | ||||
| 	routes.HomeRoutes(router) | ||||
| 
 | ||||
| 	// Bind the Web Sockets
 | ||||
| 	socketmanager.BindSocket(router.Group("/ws")) | ||||
| 
 | ||||
| 	router.Run(":8080") | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -45,24 +45,6 @@ main#main-card { | |||
|     tab-size: 2; | ||||
| 
 | ||||
|     font-family: 'Roboto Mono', monospace !important; | ||||
| 
 | ||||
|     padding-top: 2rem; | ||||
| } | ||||
| 
 | ||||
| #padTitle { | ||||
|     padding: .3rem .75rem !important; | ||||
|     border-radius: .25rem; | ||||
|     display: inline-block; | ||||
| } | ||||
| 
 | ||||
| .dark #padTitle { | ||||
|     color: #db3384; | ||||
|     background-color: rgba(0, 0, 0, 0.10); | ||||
| } | ||||
| 
 | ||||
| .light #padTitle { | ||||
|     color: #555273; | ||||
|     border: 1px solid rgba(0, 0, 0, 0.15); | ||||
| } | ||||
| 
 | ||||
| #pad-content-area { | ||||
|  | @ -72,16 +54,11 @@ main#main-card { | |||
|     flex-flow: column; | ||||
| } | ||||
| 
 | ||||
| .light .edit-content-text { | ||||
|     color: white !important; | ||||
| } | ||||
| 
 | ||||
| .edit-content-text { | ||||
|     display: none; | ||||
| } | ||||
| 
 | ||||
| .read-only-content .edit-content-text { | ||||
|     margin-top: 1rem; | ||||
|     display: block !important; | ||||
| } | ||||
| 
 | ||||
|  | @ -101,9 +78,6 @@ main#main-card { | |||
|     max-height: calc(17rem + 30vh); | ||||
|     min-height: 17rem; | ||||
|     overflow: auto; | ||||
| 
 | ||||
|     padding-top: 2rem; | ||||
|     margin-top: 1rem; | ||||
| } | ||||
| 
 | ||||
| textarea:focus, | ||||
|  |  | |||
|  | @ -138,10 +138,7 @@ function renderArchivesSelection() { | |||
|             let resp = confirm("Load contents of pad from memory? This will overwrite the current pad for everyone."); | ||||
| 
 | ||||
|             if (!!resp) { | ||||
|                 // Update visually for the client
 | ||||
|                 updatePadContent(a.content); | ||||
|                 // Send the update
 | ||||
|                 window.socket.sendPadUpdate(); | ||||
|                 document.getElementById(`pad-content`).value = a.content; | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|  | @ -213,8 +210,6 @@ function setTextareaPreview(t = true) { | |||
| 
 | ||||
|         padContentArea.classList.add(`read-only-content`); | ||||
| 
 | ||||
|         prev.scrollTop = prev.scrollHeight; | ||||
| 
 | ||||
|         textarea.classList.add(`hidden`); | ||||
|     } else { | ||||
|         // Toggle edit mode
 | ||||
|  |  | |||
							
								
								
									
										261
									
								
								static/js/ws.js
								
								
								
								
							
							
						
						
									
										261
									
								
								static/js/ws.js
								
								
								
								
							|  | @ -1,261 +0,0 @@ | |||
| class PadSocket { | ||||
| 
 | ||||
|     /** | ||||
|      * @type {WebSocket} | ||||
|      */ | ||||
|     ws      = null; | ||||
|     /** | ||||
|      * @type {String} | ||||
|      */ | ||||
|     padName = null; | ||||
| 
 | ||||
|     /** | ||||
|      * The actual textarea you write in | ||||
|      * @type {HTMLTextAreaElement} | ||||
|      */ | ||||
|     padContents = null; | ||||
|     /** | ||||
|      * The <code> of the preview | ||||
|      * @type {HTMLElement} | ||||
|      */ | ||||
|     padPreview = null; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a new PadSocket | ||||
|      * @param {string} padName The name of the pad | ||||
|      * @param {string} connUrl The URL to the websocket | ||||
|      */ | ||||
|     constructor(padName, connUrl = null) { | ||||
|         // Assign the pad name
 | ||||
|         this.padName = padName; | ||||
| 
 | ||||
|         // Check if a connection URL was mentioned
 | ||||
|         if ( connUrl == null ) { | ||||
| 
 | ||||
|             let connProtocol = `ws://`; | ||||
|             if ( window.location.protocol == `https:` ) { | ||||
|                 connProtocol = `wss://`; | ||||
|             } | ||||
| 
 | ||||
|             // Try and connect to the local websocket
 | ||||
|             connUrl = connProtocol + window.location.host + `/ws/get/${padName}`; | ||||
|         } | ||||
| 
 | ||||
|         // Connect to the websocket
 | ||||
|         const ws = new WebSocket(connUrl); | ||||
| 
 | ||||
|         // Bind the onMessage function
 | ||||
|         ws.onmessage = this.handleMessage; | ||||
| 
 | ||||
|         ws.onopen = () => { | ||||
|             updateStatus(`Established`, `text-success`); | ||||
|         } | ||||
| 
 | ||||
|         function onFail() { | ||||
|             updateStatus(`Connection Failed`, `text-dangerous`); | ||||
|         } | ||||
| 
 | ||||
|         // Try and reconnect on failure
 | ||||
|         ws.onclose = onFail; | ||||
|         ws.onerror = onFail; | ||||
|          | ||||
|         // Assign the websocket
 | ||||
|         this.ws = ws; | ||||
| 
 | ||||
|         // Get all relevant references from the HTML
 | ||||
|         this.padContents = document.getElementById(`pad-content`); | ||||
|         this.padPreview  = document.getElementById(`textarea-preview`); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @description Send a message to the server | ||||
|      * @param {string} eventType The type of event, this can be anything really, it's just used for routing by the server | ||||
|      * @param {Object} message The message to send out to the server, this can only be of format string but JSON is parsed. | ||||
|      */ | ||||
|     sendMessage = (eventType, message) => { | ||||
| 
 | ||||
|         if ( this.ws.readyState !== WebSocket.OPEN ) { | ||||
|             throw new Error(`The websocket connection is not active`); | ||||
|         } | ||||
| 
 | ||||
|         // Check if the message is a string
 | ||||
|         if ( typeof message !== 'object' ) { | ||||
|             // Convert the message into a map[string]interface{}
 | ||||
|             message = { | ||||
|                 "message": message, | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         // TODO: Compress the message, usually we will be sending the whole body of the pad from the client to the server or vice-versa.
 | ||||
|         this.ws.send( JSON.stringify({ | ||||
|             eventType, | ||||
|             padName: this.padName, | ||||
|             message, | ||||
|         })) | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle the message from the socket based on the message type | ||||
|      * @param {MessageEvent} e The websocket message | ||||
|      */ | ||||
|     handleMessage = ev => { | ||||
|         updateStatus(`Catching Message`, `text-white`); | ||||
| 
 | ||||
|         // Check if the message has valid data
 | ||||
|         if ( !!ev.data ) { | ||||
|             // Try and parse the data
 | ||||
|             let parsedData = null; | ||||
| 
 | ||||
|             try {  | ||||
|                 parsedData = JSON.parse(ev.data); | ||||
|             } catch ( err ) { | ||||
|                 console.error(`Failed to parse the WebSocket data`,err); | ||||
|                 updateStatus(`Parse Fail`, `text-warning`); | ||||
|             } | ||||
| 
 | ||||
|             if ( !!!parsedData['message'] ) { | ||||
|                 console.error(`Failed to find the message`) | ||||
|                 updateStatus(`Message Fail`, `text-warning`); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             // Check if this is a pad Content Update
 | ||||
|             if ( parsedData['eventType'] === `padUpdate`) { | ||||
|                 // Pass on the parsed data
 | ||||
|                 this.onPadUpdate(parsedData); | ||||
|             } // Check if this is a pad  Status Update 
 | ||||
|             else if ( parsedData['eventType'] === `statusUpdate`) { | ||||
|                 // Pass on the parsed data
 | ||||
|                 this.onStatusUpdate(parsedData); | ||||
|             } | ||||
| 
 | ||||
|             updateStatus(`Established`, `text-success`); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Whenever a pad update is trigered, run this function | ||||
|      * @param {Object} The response from the server | ||||
|      */ | ||||
|     onPadUpdate = data => { | ||||
|         // Check that the content is clear
 | ||||
|         if ( !!data['message']['content'] ) { | ||||
|             // Send over the new content to be updated.
 | ||||
|             updatePadContent(data['message']['content']); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     onStatusUpdate = data => { | ||||
|         // Check that the content is clear
 | ||||
|         if ( !!data['message']['currentViewers'] ) { | ||||
|             // Get the amount of viewers reported by the server
 | ||||
|             const viewerCount = Number(data['message']['currentViewers']); | ||||
|             // Check if this is a valid number
 | ||||
|             if ( Number.isNaN(viewerCount) ) { | ||||
|                 // Looks like this is a malformed message
 | ||||
|                 return console.error(`Malformed Message`, data); | ||||
|             } | ||||
| 
 | ||||
|             // Send over the new content to be updated.
 | ||||
|             updatePadViewers(viewerCount); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sending a pad update for each keystroke to the server. | ||||
|      * @param {String} msg The new contents of the pad | ||||
|      */ | ||||
|     sendPadUpdate = msg => { | ||||
|         // Get the contents of the pad
 | ||||
|         const padContents = this.padContents.value; | ||||
| 
 | ||||
|         // Send the data over the webSocket
 | ||||
|         this.sendMessage(`padUpdate`, { | ||||
|             "content": padContents, | ||||
|         }); | ||||
| 
 | ||||
|         updatePadContent(padContents, false); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Update the contents of the pad | ||||
|  * @param {String} newContent  | ||||
|  */ | ||||
| function updatePadContent(newContent, textArea = true) { | ||||
|     // Update the textarea
 | ||||
|     if ( textArea ) { | ||||
|         document.getElementById(`pad-content`).value = newContent; | ||||
|     } | ||||
|      | ||||
|     // Update the preview
 | ||||
|     const prev = document.getElementById(`textarea-preview`); | ||||
|     const shouldScroll = prev.scrollTop >= (prev.scrollHeight - Number(getComputedStyle(prev).height.replace(/px/g, ''))) * 0.98; | ||||
| 
 | ||||
|     prev.innerHTML = escapeHtml(newContent); | ||||
| 
 | ||||
|     prev.classList.remove(`language-undefined`); | ||||
| 
 | ||||
|     prev.classList.forEach( c => { | ||||
|         if ( c.indexOf(`language-`) != -1 ) { | ||||
|             prev.classList.remove(c); | ||||
|         } | ||||
|     }) | ||||
| 
 | ||||
|     try { // highlights
 | ||||
|         hljs.highlightElement(document.getElementById(`textarea-preview`)); | ||||
|     } catch ( err ) { | ||||
|         console.err(err); | ||||
|     } | ||||
| 
 | ||||
|     // Check if we should follow the bottom scrolling
 | ||||
|     if (shouldScroll) { | ||||
|         prev.scrollTop = prev.scrollHeight; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| function updatePadViewers(vc) { | ||||
|     // Get the reference to the viewers count inputElement
 | ||||
|     /** | ||||
|      * @type {HTMLInputElement} | ||||
|      */ | ||||
|     const viewerCount = document.getElementById(`currentViewers`); | ||||
| 
 | ||||
|     // Get the amount of total viewers
 | ||||
|     const totalViews = viewerCount.value.split("|")[1].trim(); | ||||
| 
 | ||||
|     // Set back the real value
 | ||||
|     viewerCount.value = `${vc} | ${totalViews}`; | ||||
| } | ||||
| 
 | ||||
| function connectSocket() { | ||||
|     // Check if the socket is established
 | ||||
|     if ( !!!window.socket || window.socket.readyState !== WebSocket.OPEN ) { | ||||
|         updateStatus(`Connecting...`, `text-warning`); | ||||
|         // Connect the socket
 | ||||
|         window.socket = new PadSocket(padTitle); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // TODO: Test if this is actually necessary or the DOMContentLoaded event would suffice
 | ||||
| // wait for the whole window to load
 | ||||
| window.addEventListener(`load`, e => { | ||||
|     connectSocket() | ||||
| }) | ||||
| 
 | ||||
| 
 | ||||
| // lol
 | ||||
| function escapeHtml(html){ | ||||
|     const text = document.createTextNode(html); | ||||
|     const p = document.createElement('p'); | ||||
| 
 | ||||
|     p.appendChild(text); | ||||
|     const content = p.innerHTML; | ||||
|     p.remove(); | ||||
| 
 | ||||
|     return content; | ||||
|   } | ||||
|  | @ -35,9 +35,7 @@ | |||
| 
 | ||||
|         </div> | ||||
| 
 | ||||
|         <h2 class="mb-4" id="padTitle"> | ||||
|             {{.title}} | ||||
|         </h2> | ||||
|         <h2 class="mb-4">{{.title}}</h2> | ||||
| 
 | ||||
|         <div id="pad-content-area"> | ||||
|             <div class="btn-sm btn" id="pad-content-toggler" onclick="toggleTextareaPreview()"> | ||||
|  | @ -55,7 +53,8 @@ | |||
| 
 | ||||
|             <pre><code id="textarea-preview" class="form-control hidden">{{.post_content}}</code></pre> | ||||
| 
 | ||||
|             <textarea maxlength="{{.maximumPadSize}}" name="pad-content" id="pad-content" onkeyup="window.socket.sendPadUpdate()" | ||||
|             <textarea maxlength="{{.maximumPadSize}}" name="pad-content" id="pad-content" onchange="sendMyData(this)" | ||||
|                 onkeydown="updateStatus(`Not Saved`, `text-warning`); toggleWritingWatch(this)" | ||||
|                 class="form-control hidden">{{.post_content}}</textarea> | ||||
|         </div> | ||||
| 
 | ||||
|  | @ -74,7 +73,7 @@ | |||
|                 </div> | ||||
|             </div> | ||||
| 
 | ||||
|             <div class="col-md-12 col-lg-4 col-xl-4 mt-4 mt-lg-0 mt-xl-0" title="Current Viewers | Total Views"> | ||||
|             <div class="col-md-12 col-lg-4 col-xl-4 mt-4 mt-lg-0 mt-xl-0" title="Current Viewers"> | ||||
|                 <div class="input-group"> | ||||
|                     <span class="input-group-text"> | ||||
|                         <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" | ||||
|  | @ -87,7 +86,7 @@ | |||
|                             </path> | ||||
|                         </svg> | ||||
|                     </span> | ||||
|                     <input type="text" class="form-control" readonly value="1 | {{.views}}" id="currentViewers"> | ||||
|                     <input type="text" class="form-control" readonly value="{{.views}}"> | ||||
|                 </div> | ||||
|             </div> | ||||
| 
 | ||||
|  | @ -210,7 +209,6 @@ | |||
|     {{ template "inc/theme-toggle.html" .}} | ||||
| </body> | ||||
| 
 | ||||
| <script src="/static/js/ws.js"></script> | ||||
| <script src="/static/js/fileSaver.js"></script> | ||||
| <script src="/static/js/pad.js"></script> | ||||
| <script src="/static/js/pad-scripts.js"></script> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue