[TDK] Arama Yardımcısı

TDK'nın sözlük sitesinde URL'den arama yapmaya olanak sağlar (URL örneği: http://sozluk.gov.tr/?ara=KAİDE), tarayıcı geçmişinde aranan kelimeler için kayıt oluşturur ve site anasayfasında eski aramaları gösterir.

// ==UserScript==
// @name         [TDK] Arama Yardımcısı
// @namespace    https://github.com/nhtctn
// @version      1.8
// @description  TDK'nın sözlük sitesinde URL'den arama yapmaya olanak sağlar (URL örneği: http://sozluk.gov.tr/?ara=KAİDE), tarayıcı geçmişinde aranan kelimeler için kayıt oluşturur ve site anasayfasında eski aramaları gösterir.
// @author       nht.ctn & Magnum357
// @match        *://sozluk.gov.tr/*
// @match        *://www.sozluk.gov.tr/*
// @icon         data:image/png;base64,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

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_info
// @run-at       document-idle

// @require	 https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
/* global $ */
/*jshint esversion: 6 */
// ==/UserScript==
(function() {
	'use strict';

	/*
	 ** [ ESSENTIALS ]
	 */

	var urlParams = new URLSearchParams(window.location.search);
	var word = urlParams.get('ara') || urlParams.get('q');
	var dict = urlParams.get('dic');
	var inputEl = document.querySelector('#tdk-srch-input');
	var formEl = document.querySelector('#tdk-srch-form');
	var buttonEl = document.querySelector('#tdk-search-btn');
	var suggestionPanelEl = document.querySelector('.autocmp');
	if (GM_getValue("pastArray") == null) GM_setValue("pastArray", []);
	var enter = `
`;

  // Notify users
  versionNotifier();

  // Storage update
	storageUpt();

	// Show the result by a query when the page opened
	if (word != undefined) {
    setTimeout(function() {showResult(word, dict);}, 200); // Wait for official search param run. Otherwise it loops endlessly.
		setWord(word, dict);
	}

	// Select the first suggestion on the enter press
	inputEl.addEventListener('keypress', goToFirstSuggestion);

	// Refresh query
	formEl.onsubmit = realTimeSearch;

	function showResult(s_word, s_dict) {
		// If specified, select dictionary to search
		if (s_dict) {
			var dictList = $('#dictList1, #dictList2').find('input[type="checkbox"]').map(function(i, item) {
				return $(item).attr("id");
			}).get();
			var checks = s_dict.split(" ");
			for (let x = 0; x < dictList.length; x++) {
				var d = dictList[x];
				if (checks.includes(d)) {
					$('#' + d).prop('checked', true);
				} else {
					$('#' + d).prop('checked', false);
				}
			}
		}

		inputEl.value = s_word;
		buttonEl.click();
	}

	function setWord(s_word, s_dict) {
		// Browser History
		if (history.pushState) {
			// Refresh the query and write it history
			var newurl = window.location.protocol + '//' + window.location.host + window.location.pathname + '?ara=' + s_word + (s_dict ? '&dic=' + s_dict.replace(/ /g, "+") : '');
			window.history.pushState({}, '', newurl);
		}

		// Script History (maximum 1000 entry)
		var pastArray = GM_getValue("pastArray");
		if (pastArray.length == 0 || s_word != pastArray[pastArray.length - 1].word || s_dict != pastArray[pastArray.length - 1].dict) {
			let item = {
				word: s_word,
				time: new Date().getTime(),
				dict: s_dict
			};
			pastArray[pastArray.length] = item;
		} else {
			pastArray[pastArray.length - 1].time = new Date().getTime(); // If the word searched for second time, just update the time.
		}
		var cleaner = (pastArray.length > 1000) ? 1 : 0; // If the word number goes beyond 1000, it will delete one word for each new word. The number will remain 1000.
		GM_setValue("pastArray", pastArray.splice(cleaner, 1000));
	}

	function realTimeSearch() {
		var realWord = inputEl.value;
		var realDict = $('#dictList1, #dictList2').find('input[type="checkbox"]:checked').map(function(i, item) {
			return $(item).attr("id");
		}).get().toString().replace(/,/g, " ");
		if (realWord.length > 0 && realDict.length > 0) {
			realDict = (realDict == "gts") ? null : realDict;
			setWord(realWord, realDict);
			addPastToPage();
		}
	}

	function goToFirstSuggestion(e) {
		if (e.keyCode == 13) {
			var wordEl = suggestionPanelEl.querySelector('.selected');

			if (wordEl != undefined) {
				inputEl.value = wordEl.innerText;
			}
		}
	}

	/*
	 ** [ HIDING SIGN LANGUAGE ]
	 */

	var signLangContHTML = `
<div id="signs_648" class="card">
    <div class="card-header" id="headingThree">
        <h5 class="mb-0">
            <button class="btn collapsed" btn-link="" collapsed="" type="button" data-toggle="collapse" data-target="#isaret-gts0" aria-expanded="false" aria-controls="collapseThree">
                <strong style="padding:5px;font-size:15px">İşaret Dili</strong>
            </button>
        </h5>
    </div>
    <div id="isaret-gts0" class="collapse">
        <div id="isaretler-gts0" class="card-body">
        </div>
    </div>
</div>
`;

	var accordionHTML = `<div class="accordion accordion_648" id="accordionExample-gts1"> </div>`;

	waitForKeyElements('div#isaretBulunan', hideSignLang);

	function hideSignLang() {
    var signs = document.querySelector('div#isaretSoz');
    if (document.querySelector('.accordion_648') == null) {
      signs.parentElement.insertAdjacentHTML("afterend", accordionHTML);
    }
    signs.parentElement.style.display = "none";


		var oldSignHTML = document.querySelector('div#signs_648');
		if (oldSignHTML == null) {
		  document.querySelector('div.accordion_648').insertAdjacentHTML("beforeend", signLangContHTML);
		}

		var signContainer = document.querySelector('div#isaretler-gts0');

    if (document.querySelector('#isaretler-gts0 > .isaret-sar') == null) {
      signContainer.insertAdjacentElement("afterbegin", document.querySelector('.isaret-sar'));
    }
    if (document.querySelector('#isaretler-gts0 > #isaretSozluk') == null) {
      signContainer.insertAdjacentElement("afterbegin", document.querySelector('#isaretSozluk'));
    }
	}

	/*
	 ** [ PAST MODULE ]
	 */

	var pastContainerHTML = `
<div class="contain">
    <div id="past" style="padding: 0 5px;">
        <span class="thumbnail text-center" style="height: auto;">
            <h4 class="text-danger" style="color:red; font-weight:bolder; margin: 15px 0;">Geçmiş</h4>
            <ul class="pastUl">
            </ul>
            <div style="margin: 15px; text-align: left;">
                <button id="deletePast" class="btn" type="button"> <strong style="padding:5px;font-size:15px">Geçmişi Sil</strong> </button>
            </div>
        </span>
    </div>
</div>
`;

	// Sonradan eklenebilir:
	// <button class="btn" type="button"> <strong style="padding:5px;font-size:15px">Geçmiş Detayları</strong> </button>

	let styles = `
.pastUl {margin: 15px; list-style: none; overflow: auto;}
.wordElements, .wordElements:hover {color:#cd853f; font-size:18px; font-weight: 700; margin: 0 10px 5px 10px; display: inline-block;}
`;
	addStyle(styles);

	addPastToPage();
	document.querySelector('button#deletePast').onclick = function() {
		deleteWord("all");
	};
	waitForKeyElements('div#bulunmayan-gts.hata', function() {
		deleteWord("last");
	}, false);

	function addPastToPage() {
		// Container
		var pastContainer = document.querySelector('div#past > span > ul');
		if (pastContainer == null) {
			document.querySelector('div#kelime').parentElement.parentElement.parentElement.insertAdjacentHTML("afterbegin", pastContainerHTML);
		}

		// Words
		getPast();
	}

	function getPast() {
		var wordElements = '';
		var counter = 0;
		var pastArray = GM_getValue("pastArray");
		for (var x = pastArray.length - 1; x > -1 && counter < 40; x--) {
			if (wordElements.indexOf('?ara=' + pastArray[x].word + '"') <= 0) {
				wordElements += '<a href="http://sozluk.gov.tr/?ara=' + pastArray[x].word + (pastArray[x].dict != null ? '&dic=' + pastArray[x].dict.replace(/ /g, "+") : '') + '" class="wordElements" title="' + convertDate(pastArray[x].time) + '">' + pastArray[x].word + '</a>';
				counter++;
			}
		}
		document.querySelector('div#past > span > ul').innerHTML = wordElements;
	}

	function convertDate(milliseconds) {
		var months = ["Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"];
		var d = new Date(milliseconds);
		var newDate = d.getDate() + ' ' + months[d.getMonth()] + ' ' + d.getFullYear() + ' (' + d.getHours().toString().padStart(2, "0") + ':' + d.getMinutes().toString().padStart(2, "0") + ')';
		return newDate;
	}

	function deleteWord(type) {
		if (type == "all") {
			var c = confirm("Arama geçmişinizi silmek istediğinize emin misiniz?");
			if (c) {
				GM_setValue("pastArray", []);
				document.querySelector('div#past > span > ul').innerHTML = '';
			}
		} else if (type == "last") {
			var pastArray = GM_getValue("pastArray");
			pastArray.splice(pastArray.length - 1, 1);
			GM_setValue("pastArray", pastArray);
			document.querySelector('div#past > span > ul > a').remove();
		} else if (isNumber(type)) {
			// En mükemmel kod henüz yazılmamış olandır.
		}
	}

	function isNumber(val) {
		return (val >= 0 || val < 0);
	}

  function versionNotifier() {
    let message = null;

    if (GM_info.script.version == 1.8 && GM_getValue("scriptVer") != 1.8) {
      message = `TDK Arama Yardımcısı Betiği:${enter+enter}Betiğin 1.8 sürümü başarıyla yüklendi.${enter+enter}Bilgilendirme:${enter}TDK sözlük sitesi, yakın zamanda url ile kelime aramayı mümkün kılan bir özellik ekledi. Sitede arattığınız kelimenin yanındaki paylaş butonuna tıklayarak kelime url'sini bulabilirsiniz.${enter+enter}https://sozluk.gov.tr/?ara=kelime${enter+enter}Url ile kelime arama dışındaki özelliklere ihtiyacınız yoksa bu betiği artık kaldırabilirsiniz.${enter+enter}Arama geçmişi kutusu, tarayıcı geçmişinde kayıt, işaret dili gizleme, url ile farklı sözlük tiplerinde arama gibi özellikler için betiği kullanmaya devam edebilirsiniz. Betik, artık site varsayılanı ile aynı biçimi kullanacaktır.${enter+enter}https://sozluk.gov.tr/?ara=kaide`;
    }

    if (message) {
      console.log(message);
      alert(message);
    }
  }

	function storageUpt() {
		try {
			// İlk verisonlu sürüme (1.6) geçiş. Kelime ve zaman kütüphanelerini birleştirilip tek obje yap. ========
			if (GM_getValue("scriptVer") == null && GM_getValue("pastArray_word") != null) {
				var newPast = [];
				var oldPast_word = GM_getValue("pastArray_word");
				var oldPast_time = GM_getValue("pastArray_time");
				for (let x = 0; x < oldPast_word.length; x++) {
					newPast[x] = {
						word: oldPast_word[x],
						time: oldPast_time[x],
						dict: null
					};
				}
				GM_setValue("pastArray", newPast);
				GM_setValue("pastArray_word", null);
				GM_setValue("pastArray_time", null);
				GM_setValue("scriptVer", 1.6);
				alert("TDK Arama Yardımcısı Betiği:" + enter + enter + "Güncelleme işlemi tamamlandı. Bu yeni sürümde URL üzerinden TDK'nın diğer sözlüklerini de aramaya dahil edebilirsiniz. Derleme Sözlüğü, Atasözleri ve Deyimler Sözlüğü vs. Daha fazla ayrıntı için Greasyfork'taki eklenti görsellerine bakın.");
			}
      // Rutin sürüm bilgisi güncelleme
			else if (GM_info.script.version > (GM_getValue("scriptVer") || 0) ) {
				GM_setValue("scriptVer", GM_info.script.version);
			}
		} catch (err) {
			alert("TDK Arama Yardımcısı Betiği:" + enter + enter + "Güncelleme işlemi sırasında bir sorun oluştu." + enter + "Sürüm " + GM_getValue("scriptVer") + " => Sürüm " + GM_info.script.version + enter + enter + "Lütfen aşağıdaki hata mesajı ile birlikte uygulama yazarına haber verin." + enter + enter + err.message);
		}
	}

	function addStyle (style) {
		document.head.appendChild(document.createElement('style')).textContent = style;
	}

  function waitForKeyElements (
      selectorTxt,    /* Required: The jQuery selector string that specifies the desired element(s). */
      actionFunction, /* Required: The code to run when elements are found. It is passed a jNode to the matched element. */
      bWaitOnce,      /* Optional: If false, will continue to scan for new elements even after the first match is found. */
      iframeSelector  /* Optional: If set, identifies the iframe to search. */
  ) {
      var targetNodes, btargetsFound;

      if (typeof iframeSelector == "undefined")
          targetNodes = $(selectorTxt);
      else
          targetNodes = $(iframeSelector).contents().find(selectorTxt);

      if (targetNodes && targetNodes.length > 0) {
          btargetsFound = true;
          /*--- Found target node(s).  Go through each and act if they are new. */
          targetNodes.each(function() {
              var jThis        = $(this);
              var alreadyFound = jThis.data('alreadyFound') || false;

              if (!alreadyFound) {
                  //--- Call the payload function.
                  var cancelFound = actionFunction(jThis);
                  if (cancelFound)
                      btargetsFound = false;
                  else
                      jThis.data('alreadyFound', true);
              }
          });
      }
      else {
          btargetsFound = false;
      }

      //--- Get the timer-control variable for this selector.
      var controlObj  = waitForKeyElements.controlObj  ||  {};
      var controlKey  = selectorTxt.replace(/[^\w]/g, "_");
      var timeControl = controlObj[controlKey];

      //--- Now set or clear the timer as appropriate.
      if (btargetsFound && bWaitOnce && timeControl) {
          //--- The only condition where we need to clear the timer.
          clearInterval(timeControl);
          delete controlObj[controlKey];
      }
      else {
          //--- Set a timer, if needed.
          if (!timeControl) {
              timeControl = setInterval(function() {
                      waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector);
                  },
                  300
              );
              controlObj [controlKey] = timeControl;
          }
      }
      waitForKeyElements.controlObj = controlObj;
  }
})();