diff options
author | rstular <rok@stular.eu> | 2020-05-14 13:31:33 +0200 |
---|---|---|
committer | rstular <rok@stular.eu> | 2020-05-14 18:26:52 +0200 |
commit | b86ef6379a2e4b4df90dabefe2b48054035da800 (patch) | |
tree | d7a51a5847b78909f1d43acd1a1f5c5c14d4c93f | |
parent | Revert debugging step (diff) | |
download | beziapp-b86ef6379a2e4b4df90dabefe2b48054035da800.tar beziapp-b86ef6379a2e4b4df90dabefe2b48054035da800.tar.gz beziapp-b86ef6379a2e4b4df90dabefe2b48054035da800.tar.bz2 beziapp-b86ef6379a2e4b4df90dabefe2b48054035da800.tar.lz beziapp-b86ef6379a2e4b4df90dabefe2b48054035da800.tar.xz beziapp-b86ef6379a2e4b4df90dabefe2b48054035da800.tar.zst beziapp-b86ef6379a2e4b4df90dabefe2b48054035da800.zip |
-rw-r--r-- | css/styles.css | 27 | ||||
-rw-r--r-- | js/lang/bundle.js | 6 | ||||
-rw-r--r-- | js/messaging.js | 133 | ||||
-rw-r--r-- | pages-src/messaging.bvr | 152 | ||||
-rw-r--r-- | pages/messaging.html | 152 |
5 files changed, 320 insertions, 150 deletions
diff --git a/css/styles.css b/css/styles.css index a06c906..c728463 100644 --- a/css/styles.css +++ b/css/styles.css @@ -5,6 +5,7 @@ --color-primary-light: rgba(230, 250, 231, 1); --color-invalid: rgba(192, 0, 0, 1); --background-color: rgba(255, 255, 255, 1); + --background-accent: rgba(0, 156, 101, 0.2); } body { @@ -162,3 +163,29 @@ a.collection-item { text-align: center; z-index: 60; } + +/* Tabs */ +.tab a { + color: var(--color-secondary) !important; +} + +.tab a:hover { + color: var(--color-primary) !important; +} + +.tab a.active { + color: var(--color-primary) !important; +} + +.tab a:focus.active { + background-color: var(--background-accent) !important; +} + +.tabs .indicator { + background-color: var(--color-primary) !important; +} + +/* Floating action button */ +.fab-new-message { + background-color: var(--color-secondary) !important; +} diff --git a/js/lang/bundle.js b/js/lang/bundle.js index 6e6c5b6..bf1a361 100644 --- a/js/lang/bundle.js +++ b/js/lang/bundle.js @@ -181,6 +181,7 @@ var langstrings = { unauthorizedAbsence: "unauthorized", doesNotCount: "does not count", // messaging + loadingMessages: "Loading messages...", sendAMessage: "send a message", recipient: "recipient", messageSubject: "subject", @@ -189,6 +190,7 @@ var langstrings = { note: "note", largeImagesNote: "GimB servers don't like large messages, so only very small images may be attached or your message will not be delivered", attachedImages: "attached images", + encryptMessage: "Encrypt message", passwordForE2EE: "password for encrypting the message", messages: "messages", received: "received", @@ -361,6 +363,7 @@ var langstrings = { unauthorizedAbsence: "neopravičeno", doesNotCount: "ne šteje", // messaging + loadingMessages: "Nalagam sporočila...", sendAMessage: "pošlji sporočilo", recipient: "prejemnik", messageSubject: "zadeva", @@ -369,13 +372,14 @@ var langstrings = { note: "opomba", largeImagesNote: "GimB strežniki ne marajo velikih sporočil, zato lahko pošiljate le zelo majhne slike, v nasprotnem primeru sporočilo ne bo dostavljeno", attachedImages: "pripete slike", + encryptMessage: "Šifriraj sporočilo", passwordForE2EE: "geslo za šifriranje sporočila", messages: "sporočila", received: "prejeta", sent: "poslana", deleted: "izbrisana", messageStorageUsed: "zasedenost shrambe sporočil v tej mapi", - maxMessagesNote: "v vsaki mapi imate lahko največ sto dvajset sporočil. Starejša sporočila ne bodo prikazana. Redno brišite sporočila, da se izognete morebitnim težavam.", + maxMessagesNote: "v vsaki mapi imate lahko največ 120 sporočil. Starejša sporočila ne bodo prikazana. Redno brišite sporočila, da se izognete morebitnim težavam.", loadMessageBody: "naloži telo sporočila", thisMessageWasEncrypted: "to sporočilo je šifriral BežiApp", enterPassword: "vnesite geslo", diff --git a/js/messaging.js b/js/messaging.js index 145711d..a061ca9 100644 --- a/js/messaging.js +++ b/js/messaging.js @@ -3,8 +3,14 @@ const DIRECTORY_URL = "/directory.json"; const ENCRYPTED_MESSAGE_REGEX = /<!-- beziapp-e2eemsg-(\d{4}) -->(\S+?)<!-- end-msg -->/g; -// "Global" object for name directory +// "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) { @@ -85,10 +91,8 @@ function populateAutocomplete() { minLength: 0 }); - if(window.location.hash.length > 1) { + if (window.location.hash.length > 1 && !window.location.hash.substring(1).startsWith("beziapp")) { $("#full-name").val(decodeURIComponent(window.location.hash.substring(1))); - } else { - $("#full-name").val(getUrlParameter("m")); } M.updateTextFields(); @@ -105,7 +109,7 @@ function setLoading(state) { } // Function, responsible for fetching and displaying data -async function loadMessages(force_refresh = true, katera = 0) { +async function loadMessages(force_refresh = true, messageType = 0) { setLoading(true); // Load required data let promises_to_run = [ @@ -122,7 +126,7 @@ async function loadMessages(force_refresh = true, katera = 0) { Promise.all(promises_to_run).then(() => { - if (messages === null || force_refresh) { + if (messages[messageType] == null || messages[messageType].length === 0 || force_refresh) { $.ajax({ url: API_ENDPOINT, crossDomain: true, @@ -130,7 +134,7 @@ async function loadMessages(force_refresh = true, katera = 0) { "u": username, "p": password, "m": "fetchsporocilaseznam", - "a": katera // Message type, see API doc for details + "a": messageType // Message type, see API doc for details }, dataType: "json", cache: false, @@ -144,9 +148,9 @@ async function loadMessages(force_refresh = true, katera = 0) { } else { // Save messages & populate view // console.log(data); // debug - localforage.setItem("messages", data).then((value) => { - messages = value; - displayData(); + messages[messageType.toString()] = data; + localforage.setItem("messages", messages).then((value) => { + displayData(messageType); setLoading(false); }); } @@ -158,7 +162,7 @@ async function loadMessages(force_refresh = true, katera = 0) { } }) } else { - displayData(); + displayData(messageType); setLoading(false); } }); @@ -293,13 +297,27 @@ function displayMessage(id, data) { } // Function for displaying data -function displayData() { - let msg_list = $("#msg-list"); +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; + } + + let msg_list = $(div_selector); msg_list.html(""); - messages.forEach(element => { - if (element["zadeva"].substr(0, 14) != "beziapp-ctlmsg") { + messages[messageType].forEach(element => { + if (!element["zadeva"].startsWith("beziapp-ctlmsg")) { + msg_list.append(` - <div class="col s12 m6" id="msg_box-${filterXSS(element["id"])}"> + <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"> @@ -311,8 +329,8 @@ function displayData() { onclick="loadMsg('${filterXSS(element["id"])}')" type="submit" > - Load message body - <i class="material-icons right">system_update</i> + Load message + <i class="material-icons right">move_to_inbox</i> </button> <p> </div> @@ -329,16 +347,30 @@ function displayData() { > <i class="material-icons">reply</i> </a> - ${filterXSS(element["posiljatelj"])} » ${filterXSS(element["datum"]["dan"])}. ${filterXSS(element["datum"]["mesec"])}. ${filterXSS(element["datum"]["leto"])} at ${filterXSS(element["cas"]["ura"])} : ${filterXSS(element["cas"]["minuta"])} + ${filterXSS(element["posiljatelj"])} » ${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> `); - } + } }); + $("#storage-bar").show(); - $("#storage-progressbar").width(Number(Number(messages.length / 120) * 100).toFixed(2) + "%"); - $("#storage-desc").html( `${messages.length}/120 ${s("messages")} ${$("#storage-progressbar").width()}`); + $("#storage-progressbar").width(Number(Number(getNumMessages(messageType) / 120) * 100).toFixed(2) + "%"); + $("#storage-desc").html( `${getNumMessages(messageType)}/120 ${s("messages")} ${$("#storage-progressbar").width()}`); +} + +// -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) { @@ -442,6 +474,20 @@ function setupEventListeners() { // 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) { @@ -510,6 +556,48 @@ document.addEventListener("DOMContentLoaded", () => { 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; + } + } + } + }; + var instance = 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"); + var instances = 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 + }; + var instances = M.Modal.init(modal_elems, modal_options); + var receivedmessages = null; loadMessages(true, 0); M.updateTextFields(); @@ -517,5 +605,4 @@ document.addEventListener("DOMContentLoaded", () => { // Setup side menu const menus = document.querySelectorAll(".side-menu"); M.Sidenav.init(menus, { edge: "right", draggable: true }); - });
\ No newline at end of file diff --git a/pages-src/messaging.bvr b/pages-src/messaging.bvr index ad1d33a..dff3d00 100644 --- a/pages-src/messaging.bvr +++ b/pages-src/messaging.bvr @@ -39,75 +39,18 @@ <span class="right white-text"> <i class="material-icons sidenav-trigger" data-target="side-menu">menu</i> </span> + <span class="right white-text" id="refresh-icon"> + <a href="#"><i class="material-icons">refresh</i></a> + </span> </div> <div id="loading-bar" class="progress hidden"> <div class="indeterminate"></div> </div> </nav> - <@?i navigation@> + <@?i navigation@> <div class="container"> - <h4><x-su>sendAMessage</x-su></h4> <div class="row"> - <div class="col s12"> - <div class="row"> - <div class="input-field col s5"> - <i class="material-icons prefix">account_circle</i> - <input id="full-name" type="text" class="autocomplete-fullname validate"> - <label for="full-name"><x-su>recipient</x-su></label> - </div> - <div class="input-field col s5"> - <i class="material-icons prefix">subject</i> - <input id="msg-subject" type="text" class=""> - <label for="msg-subject"><x-su>messageSubject</x-su></label> - </div> - <div class="input-field col s2"> - <button class="btn waves-effect waves-light" id="msg-send" type="button" disabled="disabled" name="action"> - <i class="material-icons">send</i> - </button> - </div> - </div> - <div class="row"> - <div class="input-field col s8"> - <i class="material-icons prefix">mode_edit</i> - <textarea id="msg-body" class="materialize-textarea"></textarea> - <label for="msg-body"><x-su>messageBody</x-su></label> - </div> - <div class="input-field col s2"> - <button class="btn waves-effect waves-light" id="msg-add-photo" type="button"> - <i class="material-icons" style="margin: 0 auto">add_a_photo</i> - </button> - </div> - <div class="input-field col s2"> - <button onclick="document.getElementById('msg-e2ee-pass').hidden = false" class="btn waves-effect waves-light" id="msg-use-e2ee" type="button"> - <i class="material-icons" style="margin: 0 auto">vpn_key</i> - </button> - </div> - </div> - <div hidden="hidden" class="row" id="msg-e2ee-pass"> - <div class="input-field col s12"> - <i class="material-icons prefix">lock</i> - <input id="msg-e2ee-pass-input" type="password" autocomplete="new-password" class=""> - <label for="msg-e2ee-pass-input"><x-su>passwordForE2EE</x-su></label> - </div> - </div> - </div> - <div id="msg-added-image"></div> - </div> - <h4><x-su>messages</x-su></h4> - <button class="btn waves-effect waves-light" id="msg-load-btn" onclick="loadMessages(true, 0);" type="button"> - <x-su>received</x-su> - <i class="material-icons right">inbox</i> - </button> - <button class="btn waves-effect waves-light" id="msg-load-btn" onclick="loadMessages(true, 1);" type="button"> - <x-su>sent</x-su> - <i class="material-icons right">forward</i> - </button> - <button class="btn waves-effect waves-light" id="msg-load-btn" onclick="loadMessages(true, 2);" type="button"> - <x-su>deleted</x-su> - <i class="material-icons right">delete</i> - </button> - <div id="msg-list"></div> - <p> + <div class="row" id="storage-bar"> <div class="col s4"> <x-su>messageStorageUsed</x-su>: @@ -124,7 +67,90 @@ <x-du>maxMessagesNote</x-du> </p> </div> - </p> + + <div class="col s12 m12"> + <ul class="tabs"> + <li class="tab col s4"><a href="#beziapp-received" class="active">Received</a></li> + <li class="tab col s4"><a href="#beziapp-sent">Sent</a></li> + <li class="tab col s4"><a href="#beziapp-deleted">Deleted</a></li> + </ul> + </div> + <br> + <div id="beziapp-received" class="col s12"><p class="center-align grey-text text-darken-2"><x-su>loadingMessages</x-su></p></div> + <div id="beziapp-sent" class="col s12"><p class="center-align grey-text text-darken-2"><x-su>loadingMessages</x-su></p></div> + <div id="beziapp-deleted" class="col s12"><p class="center-align grey-text text-darken-2"><x-su>loadingMessages</x-su></p></div> + </div> + </div> + + <!-- FAB --> + <div class="fixed-action-btn" id="fab-new"> + <a class="btn-floating btn-large fab-new-message modal-trigger" href="#beziapp-new-message"> + <i class="large material-icons">mode_edit</i> + </a> + </div> + + <!-- Modal Structure --> + <div id="beziapp-new-message" class="modal modal-fixed-footer"> + + <div class="modal-content"> + + <h4><x-su>sendAMessage</x-su></h4> + + <div class="input-field"> + <i class="material-icons prefix">account_circle</i> + <input id="full-name" type="text" class="autocomplete-fullname validate"> + <label for="full-name"><x-su>recipient</x-su></label> + </div> + + <div class="row"> + + <div class="col 11s 11m"> + <div class="input-field"> + <i class="material-icons prefix">subject</i> + <input id="msg-subject" type="text" class=""> + <label for="msg-subject"><x-su>messageSubject</x-su></label> + </div> + </div> + + <div class="col 1s 1m"> + <button class="btn waves-effect waves-light" id="msg-add-photo" type="button"> + <i class="material-icons" style="margin: 0 auto">add_a_photo</i> + </button> + </div> + + </div> + + <div class="row"> + <div class="col s6 m4"> + <label> + <input type="checkbox" id="encrypt-checkbox" /> + <span><x-su>encryptMessage</x-su></span> + </label> + </div> + + <div class="col s6 m8"> + <div class="input-field" id="encryption-key-input" hidden> + <i class="material-icons prefix">lock</i> + <input id="msg-e2ee-pass-input" type="password" autocomplete="new-password" class=""> + <label for="msg-e2ee-pass-input"><x-su>passwordForE2EE</x-su></label> + </div> + </div> + </div> + + <div class="input-field"> + <i class="material-icons prefix">mode_edit</i> + <textarea id="msg-body" class="materialize-textarea"></textarea> + <label for="msg-body"><x-su>messageBody</x-su></label> + </div> + + </div> + + <div class="modal-footer"> + <div id="modal-footer-right"> + <a href="#" class="modal-close waves-effect waves-green btn-flat">Cancel <i class="material-icons right">close</i></a> + <a href="#" id="msg-send" class="modal-close waves-effect waves-green btn-flat" disabled>Send <i class="material-icons right">send</i></a> + </div> + </div> </div> </body> </html> diff --git a/pages/messaging.html b/pages/messaging.html index 515770a..31c4749 100644 --- a/pages/messaging.html +++ b/pages/messaging.html @@ -42,12 +42,15 @@ <span class="right white-text"> <i class="material-icons sidenav-trigger" data-target="side-menu">menu</i> </span> + <span class="right white-text" id="refresh-icon"> + <a href="#"><i class="material-icons">refresh</i></a> + </span> </div> <div id="loading-bar" class="progress hidden"> <div class="indeterminate"></div> </div> </nav> - + <ul id="side-menu" class="sidenav side-menu"> <li><a class="subheader"><h4><b>Beži</b>App</h4></a></li> <li><a href="/pages/timetable.html" class="waves-effect"><i class="material-icons">view_module</i><x-su>timetable</x-su></a></li> @@ -68,68 +71,8 @@ <div class="container"> - <h4><x-su>sendAMessage</x-su></h4> <div class="row"> - <div class="col s12"> - <div class="row"> - <div class="input-field col s5"> - <i class="material-icons prefix">account_circle</i> - <input id="full-name" type="text" class="autocomplete-fullname validate"> - <label for="full-name"><x-su>recipient</x-su></label> - </div> - <div class="input-field col s5"> - <i class="material-icons prefix">subject</i> - <input id="msg-subject" type="text" class=""> - <label for="msg-subject"><x-su>messageSubject</x-su></label> - </div> - <div class="input-field col s2"> - <button class="btn waves-effect waves-light" id="msg-send" type="button" disabled="disabled" name="action"> - <i class="material-icons">send</i> - </button> - </div> - </div> - <div class="row"> - <div class="input-field col s8"> - <i class="material-icons prefix">mode_edit</i> - <textarea id="msg-body" class="materialize-textarea"></textarea> - <label for="msg-body"><x-su>messageBody</x-su></label> - </div> - <div class="input-field col s2"> - <button class="btn waves-effect waves-light" id="msg-add-photo" type="button"> - <i class="material-icons" style="margin: 0 auto">add_a_photo</i> - </button> - </div> - <div class="input-field col s2"> - <button onclick="document.getElementById('msg-e2ee-pass').hidden = false" class="btn waves-effect waves-light" id="msg-use-e2ee" type="button"> - <i class="material-icons" style="margin: 0 auto">vpn_key</i> - </button> - </div> - </div> - <div hidden="hidden" class="row" id="msg-e2ee-pass"> - <div class="input-field col s12"> - <i class="material-icons prefix">lock</i> - <input id="msg-e2ee-pass-input" type="password" autocomplete="new-password" class=""> - <label for="msg-e2ee-pass-input"><x-su>passwordForE2EE</x-su></label> - </div> - </div> - </div> - <div id="msg-added-image"></div> - </div> - <h4><x-su>messages</x-su></h4> - <button class="btn waves-effect waves-light" id="msg-load-btn" onclick="loadMessages(true, 0);" type="button"> - <x-su>received</x-su> - <i class="material-icons right">inbox</i> - </button> - <button class="btn waves-effect waves-light" id="msg-load-btn" onclick="loadMessages(true, 1);" type="button"> - <x-su>sent</x-su> - <i class="material-icons right">forward</i> - </button> - <button class="btn waves-effect waves-light" id="msg-load-btn" onclick="loadMessages(true, 2);" type="button"> - <x-su>deleted</x-su> - <i class="material-icons right">delete</i> - </button> - <div id="msg-list"></div> - <p> + <div class="row" id="storage-bar"> <div class="col s4"> <x-su>messageStorageUsed</x-su>: @@ -146,7 +89,90 @@ <x-du>maxMessagesNote</x-du> </p> </div> - </p> + + <div class="col s12 m12"> + <ul class="tabs"> + <li class="tab col s4"><a href="#beziapp-received" class="active">Received</a></li> + <li class="tab col s4"><a href="#beziapp-sent">Sent</a></li> + <li class="tab col s4"><a href="#beziapp-deleted">Deleted</a></li> + </ul> + </div> + <br> + <div id="beziapp-received" class="col s12"><p class="center-align grey-text text-darken-2"><x-su>loadingMessages</x-su></p></div> + <div id="beziapp-sent" class="col s12"><p class="center-align grey-text text-darken-2"><x-su>loadingMessages</x-su></p></div> + <div id="beziapp-deleted" class="col s12"><p class="center-align grey-text text-darken-2"><x-su>loadingMessages</x-su></p></div> + </div> + </div> + + <!-- FAB --> + <div class="fixed-action-btn" id="fab-new"> + <a class="btn-floating btn-large fab-new-message modal-trigger" href="#beziapp-new-message"> + <i class="large material-icons">mode_edit</i> + </a> + </div> + + <!-- Modal Structure --> + <div id="beziapp-new-message" class="modal modal-fixed-footer"> + + <div class="modal-content"> + + <h4><x-su>sendAMessage</x-su></h4> + + <div class="input-field"> + <i class="material-icons prefix">account_circle</i> + <input id="full-name" type="text" class="autocomplete-fullname validate"> + <label for="full-name"><x-su>recipient</x-su></label> + </div> + + <div class="row"> + + <div class="col 11s 11m"> + <div class="input-field"> + <i class="material-icons prefix">subject</i> + <input id="msg-subject" type="text" class=""> + <label for="msg-subject"><x-su>messageSubject</x-su></label> + </div> + </div> + + <div class="col 1s 1m"> + <button class="btn waves-effect waves-light" id="msg-add-photo" type="button"> + <i class="material-icons" style="margin: 0 auto">add_a_photo</i> + </button> + </div> + + </div> + + <div class="row"> + <div class="col s6 m4"> + <label> + <input type="checkbox" id="encrypt-checkbox" /> + <span><x-su>encryptMessage</x-su></span> + </label> + </div> + + <div class="col s6 m8"> + <div class="input-field" id="encryption-key-input" hidden> + <i class="material-icons prefix">lock</i> + <input id="msg-e2ee-pass-input" type="password" autocomplete="new-password" class=""> + <label for="msg-e2ee-pass-input"><x-su>passwordForE2EE</x-su></label> + </div> + </div> + </div> + + <div class="input-field"> + <i class="material-icons prefix">mode_edit</i> + <textarea id="msg-body" class="materialize-textarea"></textarea> + <label for="msg-body"><x-su>messageBody</x-su></label> + </div> + + </div> + + <div class="modal-footer"> + <div id="modal-footer-right"> + <a href="#" class="modal-close waves-effect waves-green btn-flat">Cancel <i class="material-icons right">close</i></a> + <a href="#" id="msg-send" class="modal-close waves-effect waves-green btn-flat" disabled>Send <i class="material-icons right">send</i></a> + </div> + </div> </div> </body> </html> |