summaryrefslogtreecommitdiffstats
path: root/dist/js/messaging.js
diff options
context:
space:
mode:
Diffstat (limited to 'dist/js/messaging.js')
-rw-r--r--dist/js/messaging.js615
1 files changed, 615 insertions, 0 deletions
diff --git a/dist/js/messaging.js b/dist/js/messaging.js
new file mode 100644
index 0000000..c548965
--- /dev/null
+++ b/dist/js/messaging.js
@@ -0,0 +1,615 @@
+const API_ENDPOINT = "https://gimb.tk/test.php";
+const DIRECTORY_URL = "/directory.json";
+
+const ENCRYPTED_MESSAGE_REGEX = /<!-- ba-e2eemsg-(\d{4}) -->(\S+?)<!-- end-msg -->/;
+
+// "Global" object for name directory and messages
+var directory = null;
+var messages = {
+ "0": [],
+ "1": [],
+ "2": []
+}
+var current_tab = 0;
+
+async function checkLogin() {
+ localforage.getItem("logged_in").then(function (value) {
+ // This code runs once the value has been loaded
+ // from the offline store.
+ if (value !== true) {
+ window.location.replace("/index.html");
+ }
+ }).catch(function (err) {
+ // This code runs if there were any errors
+ console.log(err);
+ });
+}
+
+// -----------HTML HELPERS-----------
+function htmlEncode(value) {
+ // Create a in-memory element, set its inner text (which is automatically encoded)
+ // Then grab the encoded contents back out. The element never exists on the DOM.
+ return $("<textarea/>").text(value).html();
+}
+
+function htmlDecode(value) {
+ return $("<textarea/>").html(value).text();
+}
+// ---------------------------------
+
+// Try to fetch name:id directory
+function loadDirectory() {
+ $.ajax({
+ url: DIRECTORY_URL,
+ crossDomain: true,
+
+ dataType: "json",
+ cache: false,
+ type: "GET",
+
+ success: (data) => {
+ // If we were able to retrieve it, update the saved directory
+ localforage.setItem("directory", data);
+ directory = data;
+ // Populate autocomplete
+ populateAutocomplete();
+ },
+
+ error: () => {
+ // Otherwise, try to retrieve stored directory
+ localforage.getItem("directory").then((stored_directory) => {
+ if (stored_directory === null) {
+ // If unable, set directory to null (so other functions know that we don't have it)
+ UIAlert( D("nameDirectoryNotSet"), "loadDirectory(): stored_directory === null" );
+ directory = null;
+ // Disable send button
+ document.getElementById("msg-send").disabled = true;
+ } else {
+ directory = stored_directory;
+ // Populate autocomplete
+ populateAutocomplete();
+ }
+ });
+ }
+ });
+}
+
+function populateAutocomplete() {
+ let elems = document.querySelectorAll(".autocomplete-fullname");
+ // če se uporablja globalna var directory, ki je shranjena kot objekt (vedno shranjen kot reference), bo pri let x=y x le pointer na object y
+ // in se bo spremenil z spremembo "originala". spodnja stvar itak ni preveč efficent, loop čez vseh 7000 ljudi bi lahko delal težave...
+ // kakšen Object.keys bi bila boljša varianta ampak raje napišem tale komentar... idk, to se mi je zdelo uporabno ampak sedaj obžalujem
+ // samo guglal sem "copying an object js"
+ let autocomplete_entries = Object.assign({}, directory);
+ for (let variableKey in autocomplete_entries) {
+ autocomplete_entries[variableKey] = null;
+ }
+
+ M.Autocomplete.init(elems, {
+ data: autocomplete_entries,
+ onAutocomplete: validateName,
+ minLength: 0
+ });
+
+ if (window.location.hash.length > 1 && !window.location.hash.substring(1).startsWith("beziapp")) {
+ $("#full-name").val(decodeURIComponent(window.location.hash.substring(1)));
+ }
+
+ M.updateTextFields();
+ validateName();
+}
+
+// Function to toggle loading bar
+function setLoading(state) {
+ if (state) {
+ $("#loading-bar").removeClass("hidden");
+ } else {
+ $("#loading-bar").addClass("hidden");
+ }
+}
+
+// Function, responsible for fetching and displaying data
+async function loadMessages(force_refresh = true, messageType = 0) {
+ setLoading(true);
+ // Load required data
+ let promises_to_run = [
+ localforage.getItem("username").then((value) => {
+ username = value;
+ }),
+ localforage.getItem("password").then((value) => {
+ password = value;
+ }),
+ localforage.getItem("messages").then((value) => {
+ messages = value;
+ })
+ ];
+
+ Promise.all(promises_to_run).then(() => {
+
+ if (messages[messageType] == null || messages[messageType].length === 0 || force_refresh) {
+ $.ajax({
+ url: API_ENDPOINT,
+ crossDomain: true,
+ data: {
+ "u": username,
+ "p": password,
+ "m": "fetchsporocilaseznam",
+ "a": messageType // Message type, see API doc for details
+ },
+ dataType: "json",
+ cache: false,
+ type: "GET",
+
+ success: (data) => {
+ // If data is null, the request failed
+ if (data === null) {
+ UIAlert( D("requestFailed") );
+ setLoading(false);
+ } else {
+ // Save messages & populate view
+ // console.log(data); // debug
+ messages[messageType.toString()] = data;
+ localforage.setItem("messages", messages).then((value) => {
+ displayData(messageType);
+ setLoading(false);
+ });
+ }
+ },
+
+ error: () => {
+ UIAlert( D("errorFetchingMessages") );
+ setLoading(false);
+ }
+ })
+ } else {
+ displayData(messageType);
+ setLoading(false);
+ }
+ });
+}
+
+async function loadMsg(id) {
+
+ setLoading(true);
+ // Load required data
+ let promises_to_run = [
+ localforage.getItem("username").then((value) => {
+ username = value;
+ }),
+ localforage.getItem("password").then((value) => {
+ password = value;
+ }),
+ ];
+
+ Promise.all(promises_to_run).then(() => {
+ $.ajax({
+ url: API_ENDPOINT,
+ crossDomain: true,
+ data: {
+ "u": username,
+ "p": password,
+ "m": "fetchsporocilo",
+ "a": id.replace(/_/g, "|")
+ },
+ dataType: "json",
+ cache: false,
+ type: "GET",
+ success: (data) => {
+ // If data is null, the request failed
+ if (data === null) {
+ UIAlert( `${D("unableToReceiveTheMessage")} ${D("requestFailed")}` );
+ } else {
+ displayMessage(id, data);
+ }
+ setLoading(false);
+ },
+
+ error: () => {
+ UIAlert( `${D("unableToReceiveTheMessage")} ${D("noInternetConnection")}` );
+ setLoading(false);
+ }
+
+ })
+ });
+}
+
+
+async function deleteMsg(id) {
+ setLoading(true);
+ // Load required data
+ let promises_to_run = [
+ localforage.getItem("username").then((value) => {
+ username = value;
+ }), localforage.getItem("password").then((value) => {
+ password = value;
+ }),
+ ];
+ Promise.all(promises_to_run).then(() => {
+ $.ajax({
+ url: API_ENDPOINT,
+ crossDomain: true,
+ data: {
+ "u": username,
+ "p": password,
+ "m": "izbrisisporocilo",
+ "a": id
+ },
+ dataType: "json",
+ cache: false,
+ type: "GET",
+ success: (data) => {
+ // If data is null, the request failed
+ if (data === null) {
+ UIAlert( `${D("unableToDeleteTheMessage")} ${D("requestFailed")}` );
+ } else {
+ document.getElementById("msg_box-" + id).remove();
+ }
+ setLoading(false);
+ },
+
+ error: () => {
+ UIAlert( `${D("unableToDeleteTheMessage")} ${D("noInternetConnection")}` );
+ setLoading(false);
+ }
+
+ })
+ });
+}
+
+function displayMessage(id, data) {
+ let regex_results = ENCRYPTED_MESSAGE_REGEX.exec(data["telo"]);
+ if (regex_results != null) {
+ var datatodecrypt = regex_results[2];
+ var randomencdivid = Math.floor(Math.random() * 9999).toString().padStart(4, "0");
+
+ var msgcontent = `
+ <div id='ba-msg-e2ee-form-${randomencdivid}'>
+ ${D("thisMessageWasEncrypted")}
+ <input type="password" autocomplete="new-password" id="ba-msg-e2ee-password-${randomencdivid}" placeholder="${S("password")} ...">
+ <button
+ type="button"
+ value="Decrypt"
+ class="btn waves-effect waves-light"
+ onclick="
+ try {
+ $('#ba-msg-e2ee-content-${randomencdivid}').html(
+ filterXSS(
+ sjcl.decrypt(
+ $('#ba-msg-e2ee-password-${randomencdivid}').val(),
+ $('#ba-msg-e2ee-content-${randomencdivid}').html()
+ )
+ )
+ );
+ $('#ba-msg-e2ee-content-${randomencdivid}').show();
+ $('#ba-msg-e2ee-form-${randomencdivid}').hide();
+ } catch (err) {
+ alert('${D("incorrectPassword")}');
+ }
+ "
+ >
+ ${S("decrypt")}
+ </button>
+ </div>
+ <div id="ba-msg-e2ee-content-${randomencdivid}" hidden>
+ ${datatodecrypt}
+ </div>
+ `
+ $(`#msg_body-${id.replace(/\|/g, "_")}`).html(msgcontent);
+ } else {
+ $(`#msg_body-${id.replace(/\|/g, "_")}`).html(filterXSS(data["telo"]));
+ }
+}
+
+// Function for displaying data
+function displayData(messageType) {
+ let div_selector = "";
+ switch (messageType) {
+ case 0:
+ div_selector = "#beziapp-received";
+ break;
+ case 1:
+ div_selector = "#beziapp-sent";
+ break;
+ case 2:
+ div_selector = "#beziapp-deleted";
+ break;
+ }
+
+ // $("#storage-bar").show();
+ $("#storage-progressbar").width(Number(Number(getNumMessages(messageType) / 120) * 100).toFixed(2) + "%");
+ $("#storage-stats").text(`${getNumMessages(messageType)}/120`);
+
+ let msg_list = $(div_selector);
+ msg_list.html("");
+ messages[messageType.toString()].forEach(element => {
+ if (!element["zadeva"].startsWith("ba-ctlmsg")) {
+
+ msg_list.append(`
+ <div class="col s12 m12" id="msg_box-${filterXSS(element["id"])}">
+ <div class="card blue-grey darken-1">
+ <div class="card-content white-text">
+ <span class="card-title">
+ ${filterXSS(element["zadeva"])}
+ </span>
+ <p id="msg_body-${filterXSS(element["id"]).replace(/\|/g, "_")}">
+ <button
+ class="btn waves-effect waves-light"
+ onclick="loadMsg('${filterXSS(element["id"])}')"
+ type="submit"
+ >
+ Load message
+ <i class="material-icons right">move_to_inbox</i>
+ </button>
+ <p>
+ </div>
+ <div class="card-action">
+ <a onclick="deleteMsg('${filterXSS(element["id"])}')">
+ <i class="material-icons">delete</i>
+ </a>
+ <a onclick="
+ $('#full_name').val('${filterXSS(element["posiljatelj"])}');
+ $('#msg_subject').val('Re: ${filterXSS(element["zadeva"])}');
+ M.updateTextFields();
+ $('#navigation-main').scrollIntoView();
+ "
+ >
+ <i class="material-icons">reply</i>
+ </a>
+ ${filterXSS(element["posiljatelj"])} &raquo; ${filterXSS(element["datum"]["dan"])}. ${filterXSS(element["datum"]["mesec"])}. ${filterXSS(element["datum"]["leto"])} at ${filterXSS(element["cas"]["ura"]).padStart(2, "0")}:${filterXSS(element["cas"]["minuta"]).padStart(2, "0")}
+ </div>
+ </div>
+ </div>
+ `);
+ }
+ });
+}
+
+// -1 = cumulative
+function getNumMessages(messageType = -1) {
+ if (messageType === -1) {
+ let sum = 0;
+ for (const [messageType, messageList] of Object.entries(messages)) {
+ sum += messageList.length;
+ }
+ return sum;
+ } else {
+ return (messages[messageType].length ?? 0);
+ }
+}
+
+async function sendMessage(recipient_number, subject, body) {
+ setLoading(true);
+ let promises_to_run = [
+ localforage.getItem("username").then((value) => {
+ username = value;
+ }),
+ localforage.getItem("password").then((value) => {
+ password = value;
+ }),
+ ];
+ Promise.all(promises_to_run).then(() => {
+ $.ajax({
+ url: API_ENDPOINT,
+ crossDomain: true,
+ data: {
+ "u": username,
+ "p": password,
+ "m": "posljisporocilo",
+ "a": recipient_number,
+ "b": subject,
+ "c": body
+ },
+
+ dataType: "json",
+ cache: false,
+
+ type: "POST",
+ success: () => {
+ // we CAN'T know wether the message was delievered
+ UIAlert(D("messageWasProbablySent"));
+ setLoading(false);
+ },
+ error: () => {
+ UIAlert(D("errorSendingMessage"), D("noInternetConnection"));
+ setLoading(false);
+ }
+ })
+ });
+}
+
+async function validateName() {
+ if (directory !== null) {
+ if ($("#full-name").val() in directory) {
+ $("#full-name").addClass("valid");
+ $("#full-name").removeClass("invalid");
+ $("#msg-send").removeAttr("disabled");
+ } else {
+ $("#full-name").addClass("invalid");
+ $("#full-name").removeClass("valid");
+ $("#msg-send").attr("disabled", "disabled");
+ }
+ }
+}
+
+// Setup event listeners for buttons
+function setupEventListeners() {
+ // Button to add a photo
+ $("#msg-add-photo").click(() => {
+ let input = document.createElement("input");
+ input.type = "file";
+ input.onchange = (e) => {
+ // getting a hold of the file reference
+ let file = e.target.files[0];
+ // setting up the reader
+ let reader = new FileReader();
+ reader.readAsDataURL(file); // this is reading as data url
+
+ // here we tell the reader what to do when it's done reading...
+ reader.onload = readerEvent => {
+ additionalstufftoaddtomessage += `<br><img src="${readerEvent.target.result}" />`; // this is the content!
+ if ($("#msg-added-image").html().length > 1) {
+ $("#msg-added-image").append(`<img style="width: 20mm" src="${readerEvent.target.result}" />`);
+ } else {
+ $("#msg-added-image").html(`
+ <input
+ type="button"
+ value="${S("removeImages")}"
+ class="btn waves-effect waves-light"
+ onclick="
+ additionalstufftoaddtomessage = '';
+ $('#msg-added-image').html('');
+ "
+ />
+ <br>
+ ${D("largeImagesNote")}
+ <br>
+ ${S("attachedImages")}:
+ <br>
+ <img style="width:20mm" src="${readerEvent.target.result}" />
+ `);
+ // ravno obratni narekovaji
+ }
+ UIAlert(D("imageAddedAsAnAttachment"));
+ }
+ }
+ input.click();
+ });
+
+ // Verify recipient when input loses focus
+ $("#full-name").on("blur", validateName);
+
+ // Setup refresh icon
+ $("#refresh-icon").click(() => {
+ loadMessages(true, current_tab);
+ });
+
+ // Setup checkbox handler
+ $("#encrypt-checkbox").change(function() {
+ if (this.checked) {
+ $("#encryption-key-input").prop("hidden", false);
+ } else {
+ $("#encryption-key-input").prop("hidden", true);
+ }
+ });
+
+ // Button to send message
+ $("#msg-send").click(() => {
+ localforage.getItem("directory").then(function (value) {
+ var msgcontent = $("#msg-body").val() + additionalstufftoaddtomessage;
+ var msgsubject = $("#msg-subject").val();
+ if ($("#encryption-key-input").prop("hidden") !== true) {
+ var randomencdivid = Math.floor(Math.random() * 9999).toString().padStart(4, "0");
+ var addrparts = window.location.href.split("/"); // engleski
+
+ var encrypted_message = sjcl.encrypt($("#msg-e2ee-pass-input").val(), msgcontent);
+ msgcontent = `
+ <script src="${addrparts[0]}//${addrparts[2]}/js/lib/sjcl.js"></script>
+ <div id="ba-msg-e2ee-form-${randomencdivid}">
+ This message was encrypted by BežiApp.
+ <input type="password" autocomplete="new-password" id="ba-msg-e2ee-password-${randomencdivid}" placeholder="Enter password ...">
+ <input type="button" value="Decrypt" onclick="
+ try {
+ console.log($('ba-msg-e2ee-content-${randomencdivid}').text());
+ $('#ba-msg-e2ee-content-${randomencdivid}').html(
+ sjcl.decrypt(
+ $('#ba-msg-e2ee-password-${randomencdivid}').val(),
+ $('#ba-msg-e2ee-content-${randomencdivid}').text()
+ )
+ );
+ $('#ba-msg-e2ee-content-${randomencdivid}').show();
+ $('#ba-msg-e2ee-form-${randomencdivid}').hide();
+ } catch(err) {
+ alert('${D("incorrectPassword")}');
+ }
+ ">
+ </div>
+ <div id="ba-msg-e2ee-content-${randomencdivid}" hidden>
+ <!-- ba-e2eemsg-${msgcontent.length.toString().padStart(4, "0")} -->${encrypted_message}<!-- end-msg -->
+ </div>
+ `
+ }
+
+ console.log(msgcontent);
+ console.log(encrypted_message);
+
+ sendMessage(value[$("#full-name").val()], msgsubject, htmlEncode(msgcontent));
+ $("#msg-body").val("");
+ $("#full-name").val("");
+ $("#msg-subject").val("");
+ // $("#msg-send").prop("disabled", true);
+ $("#msg-send").attr("disabled", "disabled");
+ additionalstufftoaddtomessage = "";
+
+ $("#msg-added-image").html("");
+ $("#msg-e2ee-pass").hide();
+
+ }).catch(function (err) {
+ UIAlert( `${D("unableToReadDirectory")} ${D("messageCouldNotBeSend")}`, "45245" );
+ console.log(err);
+ });
+ });
+}
+
+function getUrlParameter(sParam) {
+ const url_params = new URLSearchParams(window.location.search);
+ const found_param = url_params.get(sParam);
+ return found_param
+}
+
+var additionalstufftoaddtomessage = "";
+document.addEventListener("DOMContentLoaded", () => {
+
+ checkLogin();
+ loadDirectory();
+ setupEventListeners();
+
+ // Setup tabs
+ const tabs = document.querySelectorAll(".tabs");
+ const tab_options = {
+ // swipeable: true, // TODO: figure out how to fix height when it's enabled (it's good for UX to have it enabled)
+ onShow: (tab) => {
+ if ($(tab).hasClass("active")) {
+ switch (tab.id) {
+ case "beziapp-received":
+ current_tab = 0;
+ loadMessages(false, 0);
+ break;
+ case "beziapp-sent":
+ current_tab = 1;
+ loadMessages(false, 1);
+ break;
+ case "beziapp-deleted":
+ current_tab = 2;
+ loadMessages(false, 2);
+ break;
+ }
+ }
+ }
+ };
+ M.Tabs.init(tabs, tab_options);
+
+ // Setup floating action button
+ const fab_options = {
+ hoverEnabled: false,
+ toolbarEnabled: false
+ }
+ const fab_elem = document.querySelectorAll(".fixed-action-btn");
+ M.FloatingActionButton.init(fab_elem, fab_options);
+
+ // Setup modals
+ const modal_elems = document.querySelectorAll('.modal');
+ const modal_options = {
+ onOpenStart: () => { $("#fab-new").hide() },
+ onCloseEnd: () => { $("#fab-new").show() },
+ dismissible: false
+ };
+ M.Modal.init(modal_elems, modal_options);
+
+ var receivedmessages = null;
+ loadMessages(true, 0);
+ M.updateTextFields();
+
+ // Setup side menu
+ const menus = document.querySelectorAll(".side-menu");
+ M.Sidenav.init(menus, { edge: "right", draggable: true });
+});