diff --git a/.gitignore b/.gitignore index a43552418fdae52a0a64d542f70739c8d9719e26..e7f4bedadc5ab48a9ec8221af022d011943c7159 100644 --- a/.gitignore +++ b/.gitignore @@ -11,9 +11,10 @@ langfiles.zip # The Files created by Gulp in the build process /public/build /public/js/lib.js -/public/js/quicktips.js /public/js/scriptStartPage.js /public/js/scriptResultPage.js +/public/js/searchbar.js +/public/js/utility.js /public/css/beitritt.css /public/css/themes/default.css /public/css/utility.css diff --git a/gulpfile.js b/gulpfile.js index 5a1b91ed52e5f11a55ab7fb9cb3d3dce15b67749..d6d633128e2cc0f8138b3222131c7b09056d4fda 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,8 +16,8 @@ elixir(function (mix) { mix.less('metager/beitritt.less', 'public/css/beitritt.css') // js mix.scripts(['lib/jquery.js', 'lib/jquery-ui.min.js', 'lib/bootstrap.js', 'lib/lightslider.js', 'lib/masonry.js', 'lib/imagesloaded.js', 'lib/openpgp.min.js', 'lib/iframeResizer.min.js', 'lib/md5.js'], 'public/js/lib.js') - mix.scripts(['scriptStartPage.js', 'results.js'], 'public/js/scriptStartPage.js'); - mix.scripts(['scriptResultPage.js', 'results.js', 'quicktips.js'], 'public/js/scriptResultPage.js'); + mix.scripts(['scriptStartPage.js', 'result-saver.js'], 'public/js/scriptStartPage.js'); + mix.scripts(['scriptResultPage.js', 'result-saver.js', 'quicktips.js'], 'public/js/scriptResultPage.js'); mix.scripts(['searchbar.js', 'focus-creator.js'], 'public/js/searchbar.js'); // utility mix.scripts(['utility.js'], 'public/js/utility.js'); diff --git a/public/js/searchbar.js b/public/js/searchbar.js deleted file mode 100644 index e8cf47e54c83ef087beab966e4183db9fa74030f..0000000000000000000000000000000000000000 --- a/public/js/searchbar.js +++ /dev/null @@ -1,425 +0,0 @@ - -$(function () { - loadLocalStorage(); - setSearchbarActionListeners(); -}); - -/** - * Loads the user theme and stored settings from local storage - */ -function loadLocalStorage () { - if (localStorage) { - setSettings(); - } -} - -function setSearchbarActionListeners () { - $('#toggleOptBtn').click(toggleOptionsDialog); -} - -function setSettings () { - var acceptedParams = ['autocomplete', 'key', 'lang', 'newtab', 'sprueche']; - for (var key in localStorage) { - var value = localStorage.getItem(key); - var accepted = false; - for (var i in acceptedParams) { - if (key === 'param_' + acceptedParams[i]) { - accepted = true; - } - } - if (accepted) { - key = key.substring(6); - // Check for existing hidden fields for this key - var existing = $('.search-hidden input[name="' + key + '"]'); - if (existing.length === 0) { - // if none exist, create a new one - $('.search-hidden').append('<input type="hidden" name="' + key + '" value="' + value + '">'); - } - } - } - // Change the request method to the given parameter - var requestMethod = localStorage.getItem('request'); - if (requestMethod !== null && (requestMethod === 'GET' || requestMethod === 'POST')) { - $('#searchForm').attr('method', requestMethod); - } -} - -function toggleOptionsDialog () { - var btnMode = $('#toggleOptBtn').attr('data-mode'); - if (btnMode == 'o') { - openOptionsDialog(); - } else { - closeOptionsDialog(); - } -} - -$(function () { - setFocusCreatorActionListeners(); - loadInitialCustomFocuses(); - loadInitialSelectedFocus(); - focusChanged(); -}); - -/** - * Sets all action listeners for this page - */ -function setFocusCreatorActionListeners () { - $('.focusCheckbox').click(checkboxCheckListener); - $('#addFocusBtn').click(() => showFocusCreateDialog('')); - $('#editFocusBtn').click(editCurrentFocus); - $('.save-focus-btn').click(saveFocus); - $('.delete-focus-btn').click(deleteFocus); - $('#focus-select').change(focusChanged); - // Save Focus on clicking enter while in the focus name input - $('#focus-name').keyup(function (event) { - if (event.keyCode == 13) { - saveFocus(); - } - }); - $('#create-focus-modal').on('shown.bs.modal', function () { - $('#focus-name').focus(); - }); -} -/** - * Loads all the custom focuses stored in local storage - */ -function loadInitialCustomFocuses () { - for (var key in localStorage) { - if (key.startsWith('focus_')) { - var focus = loadFocusById(key); - addFocus(focus.name); - } - } -} - -function loadInitialSelectedFocus () { - setFocus(getFocusInUrl()); -} - -/** - * Shows the focus create dialog - * If an id is given it will try to load a focus for the given id - */ -function showFocusCreateDialog (id) { - if (id === undefined) { - id = ''; - } - document.getElementById('original-id').value = id; - $('#create-focus-modal').modal('show'); - var storedFocus = loadFocusById(id); - var focus = {}; - // Try to load a focus for the given id - $('#focus-name').val(''); - uncheckAll(); - if (storedFocus !== null) { - try { - focus = JSON.parse(localStorage.getItem(id)); - $('#focus-name').val(focus.name); - for (var key in focus) { - if (key.startsWith('engine_')) { - $('.focusCheckbox[name=' + key + ']').prop('checked', true); - } - } - } catch (ex) { - console.error(ex); - } - } - toggleDeleteButton(); -} - -/** - * Shows the focus create dialog for a given id - */ -function showFocusEditDialog (id) { - showFocusCreateDialog(id); -} - -function getCurrentFocus () { - return document.getElementById('focus-select').value; -} - -/** - * Shows an edit dialog for the current selected focus - */ -function editCurrentFocus () { - var currentFocus = getCurrentFocus(); - showFocusEditDialog(currentFocus); -} - -/** - * Shows/Hides the delete button if (no) checkboxes are selected - */ -function toggleDeleteButton () { - if (atLeastOneChecked()) { - $('.delete-focus-btn').show(); - } else { - $('.delete-focus-btn').hide(); - } -} - -/** - * Save the current Focus - * Listens for save button - */ -function saveFocus () { - /* Vorprüfungen */ - // Falls keine Suchmaschine ausgewählt wurde - if (!atLeastOneChecked()) { - switch (document.documentElement.lang) { - case 'en': - alert('Please select at least 1 search engine.'); - break; - case 'es': - alert('Por favor, seleccione al menos un motor de búsqueda.'); - break; - default: - alert('Bitte mindestens 1 Suchmaschine auswählen.'); - break; - } - return; - } - // Falls der Name zu kurz ist oder ungültige Zeichen enthält - var name = document.getElementById('focus-name').value; - if (!isValidName(name)) { - switch (document.documentElement.lang) { - case 'en': - alert('No characters other than a-z, A-Z, 0-9, ä, ö, ü, ß, -, _ allowed, at least 1 character'); - break; - case 'es': - alert('Por favor, introduzca un nombre válido'); - break; - default: - alert('Bitte gültigen Namen eingeben:\n* Keine Sonderzeichen\n* Mindestens 1 Buchstabe\n'); - break; - } - return; - } - // Liest die original-id des aktuellen fokus-dialogs (gesetzt wenn man einen Fokus bearbeitet) - var oldId = document.getElementById('original-id').value; - var id = getIdFromName(name); - var overwrite = true; - // Wenn bereits ein Fokus mit dem Namen existiert, man diesen aber nicht editiert sondern gerade einen Neuen erstellt - if (alreadyInUse(name) && oldId !== id) { - // Fragt den Nutzer ob er den Fokus überschreiben möchte - if (!confirm('Name bereits genutzt\nüberschreiben?')) { - // Falls nicht wird das Speichern abgebrochen - return; - } - // Ansonsten wird der andere Fokus gelöscht - deleteFocusById(id); - } - /* Fokus speichern */ - var focus = {}; - // Ausgewählte Suchmaschinen lesen und zu Fokus hinzufügen - $('input[type=checkbox]:checked').each(function (el) { - focus[$(this).attr('name')] = $(this).val(); - }); - - // Name setzen - focus['name'] = name; - // Alte Version des Fokus löschen (aus localStorage und von der Webseite, falls eine existiert) - if (oldId !== '') { - localStorage.removeItem(oldId); - removeFocusById(oldId); - } - // Neue Version des Fokus hinzufügen (zu localStorage und der Webseite) - localStorage.setItem(id, JSON.stringify(focus)); - addFocus(name); - setFocus(id); - // Fokus-Formular verbergen - $('#create-focus-modal').modal('hide'); -} - -/** - * Delete current Focus - * Listens for delete button - */ -function deleteFocusById (id) { - localStorage.removeItem(id); - removeFocusById(id); - $('#focus-select').change(); -} - -/** - * Delete current Focus - * Listens for delete button - */ -function deleteFocus () { - var oldId = document.getElementById('original-id').value; - deleteFocusById(oldId); - $('#create-focus-modal').modal('hide'); - $('#focus-select').change(); -} - -/** - * Is the name valid (in terms of characters)? - */ -function isValidName (name) { - // no Characters other then a-z, A-Z, 0-9, ä, ö, ü, ß, -, _ allowed - // at least 1 character - return /^[a-zA-Z0-9äöüß\-_ ]+$/.test(name); -} - -/** - * Is at least one focus selected? - */ -function atLeastOneChecked () { - return $('.focusCheckbox:checked').length > 0; -} - -/** - * Is there already a focus with this name? - */ -function alreadyInUse (name) { - return localStorage.hasOwnProperty(getIdFromName(name)); -} - -/** - * Adds an option to the focus selector - */ -function addFocus (name) { - var id = getIdFromName(name); - $('#focus-select').append('<option value="' + id + '" style="font-family: FontAwesome, sans-serif;"> ' + name + '</option>'); -} - -/** - * Remove the focuses html-elements - */ -function removeFocus (name) { - removeFocusById(getIdFromName(name)); -} - -/** - * Remove the focuses html-elements - */ -function removeFocusById (id) { - if (id == '') { - return; - } - $('#focus-select option[value="' + id + '"]').remove(); -} - -/** - * Turns a name into an id - * Converts special characters and spaces - */ -function getIdFromName (name) { - name = name.toLowerCase(); - name = name.split(' ').join('_'); - name = name.split('ä').join('ae'); - name = name.split('ö').join('oe'); - name = name.split('ü').join('ue'); - return 'focus_' + name; -} - -/** - * Loads the focus object for the given id from local storage - */ -function loadFocusById (id) { - return JSON.parse(localStorage.getItem(id)); -} - -/** - * Unchecks all focuses from the focus creator dialog - */ -function uncheckAll () { - $('.focusCheckbox').prop('checked', false); -} - -/** - * Sets the selected focus to default - */ -function setFocusToDefault () { - setFocus(DEFAULT_FOCUS); -} - -/** - * Sets the selected focus - * @param {String} focusID The id of the focus, without # - */ -function setFocus (focusID) { - $('#focus-select option[value="' + focusID + '"]').prop('selected', true); - $('#focus-select').change(); -} - -function focusChanged () { - var selectedFocus = getCurrentFocus(); - if (focusIsEditable(selectedFocus)) { - enableEditFocusBtn(); - } else { - disableEditFocusBtn(); - } - loadFocusForSearch(selectedFocus); -} - -function focusIsEditable (focus) { - if (focus.startsWith('focus_')) { - return true; - } else { - return false; - } -} - -function enableEditFocusBtn () { - $('#editFocusBtn').removeClass('disabled').click(editCurrentFocus); -} - -function disableEditFocusBtn () { - $('#editFocusBtn').addClass('disabled').off('click'); -} - -function loadFocusForSearch (focus) { - var focus = loadFocusById(focus); - clearCustomSearch(); - for (var key in focus) { - if (key.startsWith('engine_') && focus[key] == 'on') { - addSumaToCustomSearch(key); - } - } -} - -function clearCustomSearch () { - $('.search-custom-hidden').empty(); -} - -function addSumaToCustomSearch (sumaId) { - $('.search-custom-hidden').append('<input type="hidden" name="' + sumaId + '" value="on">'); -} - -function getFocusInUrl () { - var url = window.location; - var focReg = /focus=(focus_\w+)/.exec(url); - if (focReg && focReg[1]) { - return focReg[1]; - } -} - -function openOptionsDialog () { - $('#toggleOptBtn').html('<i class="fa fa-chevron-up" aria-hidden="true"></i>'); - $('#toggleOptBtn').attr('data-mode', 'c'); - $('.search-option-frame').removeClass('hide'); -} - -function closeOptionsDialog () { - $('#toggleOptBtn').html('<i class="fa fa-sliders" aria-hidden="true"></i>'); - $('#toggleOptBtn').attr('data-mode', 'o'); - $('.search-option-frame').addClass('hide'); -} - -function checkboxCheckListener (event) { - toggleDeleteButton(); - var elem = event.target; - if (elem.name) { - if (elem.checked) { - setCheckedForAllWithName(elem.name, true); - } else { - setCheckedForAllWithName(elem.name, false); - } - } -} - -function setCheckedForAllWithName (name, checked) { - $('.focusCheckbox[name=' + name + ']').prop('checked', checked); -} - -//# sourceMappingURL=searchbar.js.map diff --git a/public/js/utility.js b/public/js/utility.js deleted file mode 100644 index b8d0ca217228a3ba88b809eb9e06cdb8acee72b8..0000000000000000000000000000000000000000 --- a/public/js/utility.js +++ /dev/null @@ -1,6 +0,0 @@ -$(document).ready(function () { - $('.js-only').removeClass('js-only'); - $('.no-js').addClass('hide'); -}); - -//# sourceMappingURL=utility.js.map diff --git a/resources/assets/js/result-saver.js b/resources/assets/js/result-saver.js new file mode 100644 index 0000000000000000000000000000000000000000..9f9669d2ae06136adcc5ff5dd460b0ca254df3e9 --- /dev/null +++ b/resources/assets/js/result-saver.js @@ -0,0 +1,433 @@ +/** + * All results are stored in the global object 'results' + */ +results = new Results(); + +$(document).ready(function () { + // Add all saved results + results.loadAllResults(); + // Sort all results + results.sortResults(); + // Update the visualization + results.updateResultPageInterface(); +}); + +/** + * Load all saved results and sort them + * @param {String} sort The type of sorting function to call for these results + */ +function Results () { + if (!localStorage) return; + this.prefix = 'result_'; + this.sort = 'chronological'; + this.results = []; +} + +/** + * Adds a result to the list of results + * @param {Result} result The result to add + */ +Results.prototype.addResult = function (result) { + if (this.results.every(function (val) { + return val.hash !== result.hash; + })) { + this.results.push(result); + } +}; + +/** + * Sorts all results according to the sort-type given with this.sort + */ +Results.prototype.sortResults = function () { + if (this.sort === undefined) this.sort = 'chronological'; + switch (this.sort) { + case 'chronological': + this.results.sort(function (a, b) { + if (a.added > b.added) return -1; + if (a.added < b.added) return 1; + return 0; + }); + break; + case 'rank': + this.results.sort(function (a, b) { + if (a.rank > b.rank) return -1; + if (a.rank < b.rank) return 1; + return 0; + }); + break; + case 'alphabetical': // by hostname + this.results.sort(function (a, b) { + if (b.hostname > a.hostname) return -1; + if (b.hostname < a.hostname) return 1; + return 0; + }); + break; + } + return this; +}; + +/** + * Load all Results from localstorage + */ +Results.prototype.loadAllResults = function () { + // Iterate over all keys in the localstorage + for (var i = 0; i < localStorage.length; i++) { + // If the key starts with the prefix used for saved results + var key = localStorage.key(i); + if (key.indexOf(this.prefix) === 0) { + // Remove the prefix + key = key.substr(this.prefix.length); + // Create the result for this key by loading it from localstorage + var tmpResult = new Result(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, key); + // Add the result to the list of results + this.results.push(tmpResult); + } + } +}; + +/** + * Delete all results from localstorage + */ +Results.prototype.deleteAllResults = function () { + this.results = []; + var keys = []; + // Save all keys starting with the prefix used for saved results into the keys array + for (var i = 0; i < localStorage.length; i++) { + if (localStorage.key(i).indexOf(this.prefix) === 0) { + var key = localStorage.key(i); + keys.push(key); + } + } + // Remove all keys saved in the keys array from localstorage + $.each(keys, function (index, value) { + localStorage.removeItem(value); + }); +}; + +/** + * Update the result page to match the available results + */ +Results.prototype.updateResultPageInterface = function () { + if (this.results.length === 0) { + // If there are no saved-results left, remove the savedFoki element + $('#savedFoki').remove(); + return; + } + if ($('#savedFoki').length === 0) { + // If there is no savedFoki element yet, create it + var tabPanel = $('\ + <div id="savedFoki">\ + </div>\ + '); + $('#additions-container').append(tabPanel); + } else { + // If there already is a savedFoki element, get it + $('#savedFoki').html(''); + var tabPanel = $('#savedFoki'); + } + // Add the full savedFoki element to the tabPanel + this.addToContainer(tabPanel); +}; + +/** + * Create the savedFoki element and all of its content, + * including the saver-options element and all saved results available + * @param {HTML-Element} container The element to add the saved-results to + */ +Results.prototype.addToContainer = function (container) { + // Create the saver-options element, which is a bar containing + // options for filtering, sorting and deleting all results + var options = $('\ + <div id="saver-options">\ + <div class="saver-option saver-option-filter">\ + <input class="form-control" type="text" placeholder=" Filtern">\ + </div>\ + <div class="saver-option saver-option-sort">\ + <select class="form-control" style="font-family: FontAwesome, sans-serif;">\ + <option value="chronological" style="font-family: FontAwesome, sans-serif;"> Chronologisch</option>\ + <option value="rank" style="font-family: FontAwesome, sans-serif;"> MetaGer-Ranking</option>\ + <option value="alphabetical" style="font-family: FontAwesome, sans-serif;"> Alphabetisch (Hostname)</option>\ + </select>\ + </div>\ + <div class="saver-option saver-option-delete">\ + <button class="btn btn-danger btn-md" id="saver-options-delete-btn">\ + <i class="fa fa-trash-o" aria-hidden="true"></i>\ + <span class="hidden-xs">Ergebnisse</span> löschen</button>\ + </div>\ + </div>\ + '); + + // Set the initial value for the sorting select, based on this.sort + $(options).find('select').val(this.sort); + + // Add the saver-options element to the given container + $(container).append(options); + + /* ~~~ Filter ~~~ */ + // When the user is done typing into the filter input field, + // Filter all results, only showing ones that contain the filer + $(options).find('input').keyup(function () { + // Get the entered filter + var search = $(this).val(); + // Hide all results that do not contain the entered filter + $('#savedFoki .saved-result-content').each(function (index, value) { + // check for filter in all of the elements html-content + var html = $(this).html(); + if (html.toLowerCase().indexOf(search.toLowerCase()) === -1) { + // hide entire result block + $(value).parent().addClass('hidden'); + }else { + // show entire result block + $(value).parent().removeClass('hidden'); + } + }); + }); + + /* ~~~ Sort ~~~ */ + // When the sorting select value is changed, + // Sort all results with the selected sorting function and update their appearance + $(options).find('select').change(function () { + var sort = $(this).val(); + results.sort = sort; + results.sortResults(sort).updateResultPageInterface(); + }); + + /* ~~~ Delete ~~~ */ + // When the delete button is clicked, + // Delete all results and update their appearance + $(options).find('#saver-options-delete-btn').click(function (event) { + results.deleteAllResults(); + results.updateResultPageInterface(); + }); + + // Append all results available + $.each(this.results, function (index, result) { + $(container).append(result.toHtml()); + }); +}; + +/** + * Creates a result object + * @param {String} title The title of this result + * @param {String} link The link to this result + * @param {String} anzeigeLink The displayed link + * @param {String} gefVon The ?? + * @param {String} hoster The website this result is hosted on + * @param {String} anonym The link to open this result anonymously + * @param {String} description The description of this result + * @param {int} rank The rank of this result + * @param {int} hash The hash value for this result + */ +function Result (title, link, anzeigeLink, gefVon, hoster, anonym, description, index, hash) { + // Set prefix for localstorage + this.prefix = 'result_'; + + if (hash === null || hash === undefined) { + // Calculate the hash value of this result + hash = MD5(title + link + anzeigeLink + gefVon + hoster + anonym + description); + } + + this.hash = hash; + + // Try to load the result, if there was none create it + if (!this.load()) { + // Save all important data + this.title = title; + this.link = link; + this.anzeigeLink = anzeigeLink; + this.gefVon = gefVon; + this.hoster = hoster; + this.anonym = anonym; + this.description = description; + this.index = index; + this.rank = index; + this.added = new Date().getTime(); + // read the hostname from the displayed link + // matches everything from after a 'www' to the locality ending ('de', 'com', etc.) + var matches = /(?:www\.)*((?:[\w\-]+\.)+\w{2,3})(?:$|[/?])/.exec(this.anzeigeLink); + this.hostname = matches[1]; + + // Save this result to localstorage + this.save(); + } +} + +/** + * Load this result from local storage + * The key used equals 'prefix + hash' of this result + */ +Result.prototype.load = function () { + if (!localStorage) return false; + + // Try to load from local storage + var result = localStorage.getItem(this.prefix + this.hash); + if (result === null) return false; + + // Decode the base64 result into a normal string, then json + result = b64DecodeUnicode(result); + result = JSON.parse(result); + + // Load all important data + this.title = result.title; + this.link = result.link; + this.anzeigeLink = result.anzeigeLink; + this.gefVon = result.gefVon; + this.hoster = result.hoster; + this.anonym = result.anonym; + this.description = result.description; + this.added = result.added; + this.index = -result.index; + this.rank = result.rank; + this.hostname = result.hostname; + + return true; +}; + +/** + * Save the data of this result into localstorage + * The key used equals 'prefix + hash' of this result + */ +Result.prototype.save = function () { + if (!localStorage) return false; + // Save all important data + var result = { + title: this.title, + link: this.link, + anzeigeLink: this.anzeigeLink, + gefVon: this.gefVon, + hoster: this.hoster, + anonym: this.anonym, + description: this.description, + added: this.added, + index: this.index, + rank: this.rank, + hostname: this.hostname + }; + + // Encode the result object into a string, then into base64 + result = JSON.stringify(result); + result = b64EncodeUnicode(result); + + // Save the result string into local storage + // The key used equals 'prefix + hash' of this result + localStorage.setItem(this.prefix + this.hash, result); + + return true; +}; + +/** + * Convert a string into base64 format + * @param {String} str The string to convert + */ +function b64EncodeUnicode (str) { + return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { + return String.fromCharCode('0x' + p1); + })); +} + +/** + * Convert a base64 string into normal format + * @param {String} str The string to convert + */ +function b64DecodeUnicode (str) { + return decodeURIComponent(Array.prototype.map.call(atob(str), function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); +} + +Result.prototype.setIndex = function (index) { + this.index = index; +}; + +/** + * Remove this result from localstorage and refresh the view + */ +Result.prototype.remove = function () { + localStorage.removeItem(this.prefix + this.hash); + var hash = this.hash; + results.results.splice(results.results.findIndex(function (result) { + return result.hash === hash; + }), 1); + results.updateResultPageInterface(); +}; + +/** + * Converts this result object into an html element + * @returns {HTML-Element} The converted HTML-Element + */ +Result.prototype.toHtml = function () { + // Create the saved-result element + var result = $('\ + <div class="saved-result result" data-count="' + this.index + '">\ + <div class="saved-result-remover remover" title="Ergebnis aus dem Speicher löschen">\ + <i class="fa fa-trash"></i>\ + </div>\ + <div class="saved-result-content">\ + <h2 class="result-title">\ + <a class="title" href="' + this.link + '" target="_blank" data-hoster="' + this.hoster + '" data-count="1" rel="noopener">\ + ' + this.title + '\ + </a>\ + </h2>\ + <div class="result-header">\ + <div class="result-link">\ + <a href="' + this.link + '" target="_blank" data-hoster="' + this.hoster + '" rel="noopener">\ + ' + this.anzeigeLink + '\ + </a>\ + </div>\ + <span class="result-hoster">' + this.gefVon + '</span>\ + <span class="result-proxy">\ + <a class="result-proxy" onmouseover="$(this).popover(\'show\');" onmouseout="$(this).popover(\'hide\');" data-toggle="popover" data-placement="auto right" data-container="body" data-content="Der Link wird anonymisiert geöffnet. Ihre Daten werden nicht zum Zielserver übertragen. Möglicherweise funktionieren manche Webseiten nicht wie gewohnt." href="' + this.proxy + '" target="_blank" rel="noopener" data-original-title="" title="">\ + <img src="/img/proxyicon.png" alt="">\ + anonym öffnen\ + </a>\ + </span>\ + </div>\ + <div class="result-body">\ + <div class="description">' + this.description + '</div>\ + </div>\ + </div>\ + </div>'); + + // Add a click listener to the remover, that removes the result on click + $(result).find('.remover').click({caller: this}, function (event) { + event.data.caller.remove(); + }); + + return result; +}; + +/** + * Saves the result at the given index + * @param {int} index The index of the result to save + */ +function resultSaver (index) { + // Remember the original result element + var original = $('.result[data-count=' + index + ']'); + + // Read the necessary data from the result html + var title = $('.result[data-count=' + index + '] .result-title a').html().trim(); + var link = $('.result[data-count=' + index + '] .result-title a').attr('href').trim(); + var anzeigeLink = $('.result[data-count=' + index + '] .result-link a').html().trim(); + var gefVon = $('.result[data-count=' + index + '] .result-hoster a').html().trim(); + var hoster = $('.result[data-count=' + index + '] .result-hoster a').attr('href').trim(); + var anonym = $('.result[data-count=' + index + '] .result-proxy a').attr('href').trim(); + var description = $('.result[data-count=' + index + '] .result-description').html().trim(); + + // Create the result object + var result = new Result(title, link, anzeigeLink, gefVon, hoster, anonym, description, index, null); + + // Add new result to results + results.addResult(result); + + // Sort results + results.sortResults(); + + // Update the saved results + results.updateResultPageInterface(); + + // Animate the result transfer to the saved results + var transferTarget = $('.saved-result[data-count=' + index + ']'); + if (original.length > 0 && transferTarget.length > 0) { + $(original).transfer({to: transferTarget, duration: 1000}); + } +} diff --git a/resources/assets/js/results.js b/resources/assets/js/results.js deleted file mode 100644 index c551d583efab42a9337fbcd52b2fbf46d299677b..0000000000000000000000000000000000000000 --- a/resources/assets/js/results.js +++ /dev/null @@ -1,285 +0,0 @@ -/* - * This object gathers all stored Result Objects and can Update the Interface to show them. -*/ -function Results(sort){ - if(!localStorage) return; - this.prefix = "result_"; - this.sort = sort; - this.results = []; - this.updateResults(); - this.length = this.results.length; - this.sortResults(); -} - -Results.prototype.sortResults = function(){ - if(this.sort === undefined) this.sort = "chronological"; - var sortType = this.sort; - switch(sortType){ - case "chronological": - this.results.sort(function(a,b){ - if(a.added > b.added) return -1; - if(a.added < b.added) return 1; - return 0; - }); - break; - case "rank": - this.results.sort(function(a,b){ - if(a.rank > b.rank) return -1; - if(a.rank < b.rank) return 1; - return 0; - }); - break; - case "alphabetical": - this.results.sort(function(a,b){ - if(b.hostname > a.hostname) return -1; - if(b.hostname < a.hostname) return 1; - return 0; - }); - break; - } -} - -Results.prototype.updateResults = function(){ - // Iterate over all Keys in the LocalStorage - for(var i = 0; i < localStorage.length; i++){ - if(localStorage.key(i).indexOf(this.prefix) === 0){ - var key = localStorage.key(i); - key = key.substr(this.prefix.length); - var tmpResult = new Result(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, key); - tmpResult.setIndex(i); - this.results.push(tmpResult); - } - } -} - -Results.prototype.deleteResults = function(){ - var keys = []; - for(var i = 0; i < localStorage.length; i++){ - if(localStorage.key(i).indexOf(this.prefix) === 0){ - var key = localStorage.key(i); - keys.push(key); - } - } - $.each(keys, function(index, value){ - localStorage.removeItem(value); - }); -} - -Results.prototype.updateResultPageInterface = function(){ - var sortType = this.sort; - if(this.results.length === 0){ - $("#savedFokiTabSelector, #savedFoki").remove(); - $($("#foki > li[data-loaded=1]").get(0)).find(">a").tab("show"); - return; - } - if($("#savedFokiTabSelector").length === 0){ - var savedFoki = $('\ - <li id="savedFokiTabSelector" data-loaded="1" class="tab-selector" role="presentation">\ - <a aria-controls="savedFoki" href="#savedFoki" role="tab" data-toggle="tab">\ - <span class="glyphicon glyphicon-floppy-disk"></span> gespeicherte Ergebnisse\ - <span class="badge">' + this.results.length + '</span>\ - </a>\ - </li>\ - '); - $("#foki").append(savedFoki); - }else{ - $("#savedFokiTabSelector span.badge").html(this.results.length); - } - if($("#savedFoki").length === 0){ - // Now append the Tab Panel - var tabPanel = $('\ - <div role="tabpanel" class="tab-pane" id="savedFoki">\ - </div>\ - '); - $("#main-content-tabs").append(tabPanel); - }else{ - $("#savedFoki").html(""); - var tabPanel = $("#savedFoki"); - } - this.addToContainer(tabPanel, sortType); -} - -Results.prototype.addToContainer = function(container){ - var sortType = this.sort; - $.each(this.results, function(index, result){ - $(container).append(result.toHtml()); - }); - - var options = $('\ - <div class="saver-options row">\ - <input class="form-control" type="text" placeholder="Filtern">\ - <select class="form-control">\ - <option value="chronological">Chronologisch</option>\ - <option value="rank">MetaGer-Ranking</option>\ - <option value="alphabetical">Alphabetisch (Hostname)</option>\ - </select>\ - <button class="btn btn-danger btn-md"><span class="glyphicon glyphicon-trash"></span> <span class="hidden-xs">Ergebnisse</span> löschen</button>\ - </div>\ - '); - - $(options).find("select").val(sortType); - - $(container).prepend(options); - - $(options).find("select").change(function(){ - new Results($(this).val()).updateResultPageInterface(); - }); - - $(options).find("button").click({caller: this}, function(event){ - event.data.caller.deleteResults(); - new Results().updateResultPageInterface(); - }); - - $(options).find("input").keyup(function(){ - var search = $(this).val(); - $("#savedFoki > div.result").each(function(index, value){ - var html = $(this).html(); - if(html.toLowerCase().indexOf(search.toLowerCase()) === -1){ - $(value).addClass("hidden"); - }else{ - $(value).removeClass("hidden"); - } - }); - }); - -} - -function Result(title, link, anzeigeLink, gefVon, hoster, anonym, description, color, rank, hash){ - this.prefix = "result_"; // Präfix for the localStorage so we can find all Items - - if(hash !== null && hash !== undefined){ - this.hash = hash; - this.load(); - }else{ - this.hash = MD5(title + link + anzeigeLink + gefVon + hoster + anonym + description); - - this.title = title; - this.link = link; - this.anzeigeLink = anzeigeLink; - this.gefVon = gefVon; - this.hoster = hoster; - this.anonym = anonym; - this.description = description; - this.color = color; - this.rank = rank; - this.added = new Date().getTime(); - var parser = document.createElement('a'); - parser.href = this.anzeigeLink; - this.hostname = parser.hostname; - this.save() - } -} - -Result.prototype.load = function(){ - if(localStorage){ - var result = localStorage.getItem(this. prefix + this.hash); - if(result === null) return false; - result = b64DecodeUnicode(result); - result = JSON.parse(result); - this.title = result.title; - this.link = result.link; - this.anzeigeLink = result.anzeigeLink; - this.gefVon = result.gefVon; - this.hoster = result.hoster; - this.anonym = result.anonym; - this.description = result.description; - this.added = result.added; - this.color = result.color; - this.rank = result.rank; - this.hostname = result.hostname; - return true; - }else{ - return false; - } -} - -Result.prototype.save = function(){ - /* - * This function will save the data of this Result to the LocalStorage - */ - if(localStorage){ - - var result = { - title: this.title, - link: this.link, - anzeigeLink: this.anzeigeLink, - gefVon: this.gefVon, - hoster: this.hoster, - anonym: this.anonym, - description: this.description, - added: this.added, - color: this.color, - rank: this.rank, - hostname: this.hostname - }; - - result = JSON.stringify(result); - result = b64EncodeUnicode(result); - - localStorage.setItem(this.prefix + this.hash, result); - - return true; - }else{ - return false; - } -} - -function b64EncodeUnicode(str) { - return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) { - return String.fromCharCode('0x' + p1); - })); -} - -function b64DecodeUnicode(str) { - return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); -} - - -Result.prototype.setIndex = function(index){ - this.index = index; -} - -Result.prototype.remove = function(){ - localStorage.removeItem(this.prefix + this.hash); - new Results().updateResultPageInterface(); -} - -Result.prototype.toHtml = function(){ - var result = $('\ - <div class="result row">\ - <div class="col-sm-1 glyphicon glyphicon-trash remover" title="Ergebnis aus dem Speicher löschen">\ - </div>\ - <div class="result-information col-xs-12 col-sm-11">\ - <div class="col-xs-10 col-sm-11" style="padding:0; ">\ - <p class="title">\ - <a class="title" href="' + this.link + '" target="_blank" data-hoster="' + this.hoster + '" data-count="1" rel="noopener">\ - ' + this.title + '\ - </a>\ - </p>\ - <div class="link">\ - <div>\ - <div class="link-link">\ - <a href="' + this.link + '" target="_blank" data-hoster="' + this.hoster + '" data-count="' + this.index + '" rel="noopener">\ - ' + this.anzeigeLink + '\ - </a>\ - </div>\ - </div>\ - <span class="hoster">\ - ' + this.gefVon + '\ - </span>\ - <a class="proxy" onmouseover="$(this).popover(\'show\');" onmouseout="$(this).popover(\'hide\');" data-toggle="popover" data-placement="auto right" data-container="body" data-content="Der Link wird anonymisiert geöffnet. Ihre Daten werden nicht zum Zielserver übertragen. Möglicherweise funktionieren manche Webseiten nicht wie gewohnt." href="' + this.proxy + '" target="_blank" rel="noopener" data-original-title="" title="">\ - <img src="/img/proxyicon.png" alt="">\ - anonym öffnen\ - </a>\ - </div>\ - </div>\ - <div class="description">' + this.description + '</div>\ - </div>\ - </div>'); - $(result).find(".remover").click({caller: this}, function(event){ - event.data.caller.remove(); - }); - return result; -} \ No newline at end of file diff --git a/resources/assets/js/scriptResultPage.js b/resources/assets/js/scriptResultPage.js index 540590274a9407e5effd56704a676f1b9d35b44a..5e7aabff7d88e087bfc55ced202aacd7225f638b 100644 --- a/resources/assets/js/scriptResultPage.js +++ b/resources/assets/js/scriptResultPage.js @@ -4,7 +4,6 @@ $(document).ready(function () { var custom = $('#foki > li.active').hasClass('custom-focus-tab-selector'); getDocumentReadyForUse(focus, custom); botProtection(); - new Results().updateResultPageInterface(); // Adds the saved Results if they are present if (document.location.href.indexOf('focus=container') !== -1) { $($('#foki > li#savedFokiTabSelector').get(0)).find('>a').tab('show'); } @@ -375,19 +374,3 @@ function initialLoadContent (fokus) { getDocumentReadyForUse(fokus); }); } - -function resultSaver (index) { - var title = $('div.tab-pane.active .result[data-count=' + index + '] a.title').html(); - var link = $('div.tab-pane.active .result[data-count=' + index + '] a.title').attr('href'); - var anzeigeLink = $('div.tab-pane.active .result[data-count=' + index + '] div.link-link > a').html(); - var gefVon = $('div.tab-pane.active .result[data-count=' + index + '] span.hoster').html(); - var hoster = $('div.tab-pane.active .result[data-count=' + index + '] a.title').attr('data-hoster'); - var anonym = $('div.tab-pane.active .result[data-count=' + index + '] a.proxy').attr('href'); - var description = $('div.tab-pane.active .result[data-count=' + index + '] div.description').html(); - var color = $('div.tab-pane.active .result[data-count=' + index + '] div.number').css('color'); - var rank = parseFloat($('div.tab-pane.active .result[data-count=' + index + ']').attr('data-rank')); - new Result(title, link, anzeigeLink, gefVon, hoster, anonym, description, color, rank, undefined); - var to = $('#savedFokiTabSelector').length ? $('#savedFokiTabSelector') : $('#foki'); - $('div.tab-pane.active .result[data-count=' + index + ']').transfer({to: to, duration: 1000}); - new Results().updateResultPageInterface(); -} diff --git a/resources/assets/js/searchbar.js b/resources/assets/js/searchbar.js index 4d5cc1bc743512268bb9d42b840821fe25503b6d..cc5537840e86af0b4eb400fd5a90ab21c2e397c8 100644 --- a/resources/assets/js/searchbar.js +++ b/resources/assets/js/searchbar.js @@ -51,3 +51,5 @@ function toggleOptionsDialog () { closeOptionsDialog(); } } + +//# sourceMappingURL=searchbar.js.map \ No newline at end of file diff --git a/resources/assets/less/metager/result-page.less b/resources/assets/less/metager/result-page.less index 32b060005ba0a8acc887489dfaa25f5666d9b386..71331aa7fc59372faf6f14045b3e274a0e1b2de0 100644 --- a/resources/assets/less/metager/result-page.less +++ b/resources/assets/less/metager/result-page.less @@ -1,6 +1,7 @@ /* Ergebnisseite */ @import "./quicktips.less"; +@import "./result-saver.less"; @import "./result.less"; @import "./product.less"; // @@ -342,12 +343,12 @@ a { @result-width: 700px; @quicktip-width: 500px; -.resultpage-container { +#resultpage-container { width: @result-width + @quicktip-width; align-self: center; @media (max-width: (@result-width + @quicktip-width - 1px)) { width: @result-width; - #quicktips { + #additions-container { display: none; } } @@ -356,11 +357,11 @@ a { } display: flex; justify-content: center; - #results { + #results-container { max-width: @result-width; width: 100%; } - #quicktips { + #additions-container { padding-left: 50px; width: @quicktip-width; } diff --git a/resources/assets/less/metager/result-saver.less b/resources/assets/less/metager/result-saver.less new file mode 100644 index 0000000000000000000000000000000000000000..0a95ec0927d20d0e283dfd60c70f8c28edc72c57 --- /dev/null +++ b/resources/assets/less/metager/result-saver.less @@ -0,0 +1,46 @@ +@saved-result-distance-first: 10px; +@saved-result-distance-between: 10px; + +#savedFoki { + padding-top: 50px; + display: flex; + flex-direction: column; + #saver-options { + display: flex; + flex-direction: column; + align-items: stretch; + >* { + margin: 5px 0px; + display: flex; + align-items: center; + } + .saver-option-filter>input { + font-family: FontAwesome, sans-serif; + } + .saver-option-sort>select { + &, + option { + font-family: FontAwesome, sans-serif; + } + } + .saver-option-delete button { + width: 100%; + } + } + .saved-result { + display: flex; + align-items: baseline; + margin-top: @saved-result-distance-first; + margin-bottom: @saved-result-distance-between; + padding-bottom: @saved-result-distance-between; + .saved-result-remover { + padding: 0px 10px; + .fa { + font-size: 20px; + } + } + .saved-result-content { + overflow: hidden; + } + } +} \ No newline at end of file diff --git a/resources/assets/less/metager/result.less b/resources/assets/less/metager/result.less index 67758cb4d7b3e94c8d3b8b41c1539e337ac72f0f..75d8604103111891ff2dd5e3f10cde005d4b5190 100644 --- a/resources/assets/less/metager/result.less +++ b/resources/assets/less/metager/result.less @@ -79,7 +79,7 @@ margin-top: 0; margin-left: 10px; } - .result-proxy { + .result-proxy, .result-proxy a { font-size: 13px; margin-top: 0; white-space: nowrap; diff --git a/resources/views/layouts/researchandtabs.blade.php b/resources/views/layouts/researchandtabs.blade.php index bfebfcc57bedba2b742f4b704b88e89c58791752..4c1187aa177eb878f9223f02994f95cc36ad488a 100644 --- a/resources/views/layouts/researchandtabs.blade.php +++ b/resources/views/layouts/researchandtabs.blade.php @@ -8,18 +8,22 @@ </div> </div> @endif -<div class="content-wrapper"> - <header id="research-bar"> - <div id="header-logo"> - <a href="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), "/") }}"><h1 class="mg-logo">MetaGer</h1></a> - </div> - <div id="header-searchbar"> - @include('parts.searchbar', ['class' => 'resultpage-searchbar', 'request' => Request::method()]) - </div> - </header> - <div id="research-bar-placeholder"> +<header id="research-bar"> + <div id="header-logo"> + <a href="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), "/") }}"><h1 class="mg-logo">MetaGer</h1></a> + </div> + <div id="header-searchbar"> + @include('parts.searchbar', ['class' => 'resultpage-searchbar', 'request' => Request::method()]) </div> - <main class="resultpage-container"> +</header> +<div id="research-bar-placeholder"></div> +<div id="resultpage-container"> + <div id="results-container"> @yield('results') - </main> + </div> + <div id="additions-container"> + @if( $metager->showQuicktips() ) + <div id="quicktips"></div> + @endif + </div> </div> \ No newline at end of file diff --git a/resources/views/layouts/result.blade.php b/resources/views/layouts/result.blade.php index 7daf17c7315d38441cad5a2cea1346f64346a947..5b2b81cb07719c43f12bfdb51430856f4b7d3c48 100644 --- a/resources/views/layouts/result.blade.php +++ b/resources/views/layouts/result.blade.php @@ -48,7 +48,7 @@ </div> </div> <span class="result-hoster"> - <span>von {!! $result->gefVon !!}</span> + von {!! $result->gefVon !!} </span> @if( isset($result->partnershop) && $result->partnershop === TRUE ) <span class="partnershop-info"> @@ -56,10 +56,12 @@ <a href="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), "/partnershops") }}" target="_blank" rel="noopener">{!! trans('result.options.4') !!}</a> </span> @endif - <a class="result-proxy" onmouseover="$(this).popover('show');" onmouseout="$(this).popover('hide');" data-toggle="popover" data-placement="auto right" data-container="body" data-content="@lang('result.proxytext')" href="{{ $result->proxyLink }}" target="{{ $metager->getNewtab() }}" rel="noopener"> - <img src="/img/proxyicon.png" alt="" /> - <span>{!! trans('result.options.5') !!}</span> - </a> + <span class="result-proxy"> + <a onmouseover="$(this).popover('show');" onmouseout="$(this).popover('hide');" data-toggle="popover" data-placement="auto right" data-container="body" data-content="@lang('result.proxytext')" href="{{ $result->proxyLink }}" target="{{ $metager->getNewtab() }}" rel="noopener"> + <img src="/img/proxyicon.png" alt="" /> + <span>{!! trans('result.options.5') !!}</span> + </a> + </span> </div> <div class="result-body"> @if( isset($result->logo) ) diff --git a/resources/views/layouts/resultPage.blade.php b/resources/views/layouts/resultPage.blade.php index 095e907e36e468231fdfe1819c45c451edb21826..4420e83d0285554e00d8529c216b819875948295 100644 --- a/resources/views/layouts/resultPage.blade.php +++ b/resources/views/layouts/resultPage.blade.php @@ -25,7 +25,7 @@ @include('modals.create-focus-modal') @include('layouts.researchandtabs') @else - <div class="resultpage-container"> + <div id="resultpage-container"> @yield('results') </div> @endif diff --git a/resources/views/metager3.blade.php b/resources/views/metager3.blade.php index c3021078e5c8b90d4aab4318d61c18bdeb2cf673..4828f89fc20c074cd10eff9284432cb668290801 100644 --- a/resources/views/metager3.blade.php +++ b/resources/views/metager3.blade.php @@ -1,27 +1,27 @@ @extends('layouts.resultPage') @section('results') - <div id="results"> - {{-- Show all errors --}} - @if(sizeof($errors) > 0) - <div class="alert alert-danger"> - <ul> - @foreach($errors as $error) - <li>{!! $error !!}</li> - @endforeach - </ul> - </div> - @endif - {{-- Show all warnings --}} - @if(sizeof($warnings) > 0) - <div class="alert alert-warning"> - <ul> - @foreach($warnings as $warning) - <li>{!! $warning !!}</li> - @endforeach - </ul> - </div> - @endif + {{-- Show all errors --}} + @if(sizeof($errors) > 0) + <div class="alert alert-danger"> + <ul> + @foreach($errors as $error) + <li>{!! $error !!}</li> + @endforeach + </ul> + </div> + @endif + {{-- Show all warnings --}} + @if(sizeof($warnings) > 0) + <div class="alert alert-warning"> + <ul> + @foreach($warnings as $warning) + <li>{!! $warning !!}</li> + @endforeach + </ul> + </div> + @endif + <main id="results"> {{-- Show initial products or ads --}} @if($metager->hasProducts()) @if( $metager->getFokus() !== "produktsuche" && !$apiAuthorized) @@ -54,8 +54,5 @@ <a @if($metager->nextSearchLink() !== "#") href="{{ $metager->nextSearchLink() }}" @endif>{{ trans('results.weiter') }}</a> </div> </nav> - </div> - @if( $metager->showQuicktips() ) - <div id="quicktips"></div> - @endif + </main> @endsection