Google Searching Tags Box

Make your searches easier by adding tags to your search queries with one click

目前為 2022-05-09 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Google Searching Tags Box
// @version      1.0
// @description  Make your searches easier by adding tags to your search queries with one click
// @author       OpenDec
// @license      MIT
// @match        https://www.google.com/
// @match        https://www.google.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=google.com
// @grant        none
// @run-at       document-start
// @namespace https://greasyfork.org/users/873547
// ==/UserScript==

window.addEventListener('DOMContentLoaded', function stageReady() {

  // INIT
  var input = document.querySelector('input.gLFyf.gsfi');
  var container = document.querySelector('.RNNXgb');
  var tagsBox = document.createElement('div');
  var items;
  var validTarget;
  var data = [];
  var draggedItem = null;

  tagsBox.id = 'od-tagsbox';
  tagsBox.draggable = true;
  container.parentNode.insertBefore(tagsBox, container.nextSibling);

  // tagsBox is a valid target
  tagsBox.addEventListener('dragover', function (e) {
    e.preventDefault();
  });
  tagsBox.addEventListener('drop', function (e) {
    e.preventDefault();
    validTarget = true;
  });

  // DATA STORAGE
  function loadData() {
    data = JSON.parse(localStorage.getItem('odtagsbox') || '[]');
  }
  function saveData() {
    updateData();
    localStorage.setItem('odtagsbox', JSON.stringify(data));
  }

  // PARSING
  function parseData() {
    addItem();
    for (var i in data) {
      addItem(data[i].text, data[i].color);
    }
  }
  function updateData() {
    data = Array.from(items).flatMap(function(e){return e.classList.contains('od-additem') ? [] : [{ text: e.dataset.title, color: e.dataset.color }]; });
  }

  // ADD ITEM
  function addItem(str, color, index) {
    str = str || '';
    color = color || randomHSL();
    index = index || tagsBox.childElementCount;

    var item = document.createElement('div');
    var label = document.createElement('i');

    item.classList.add('od-item');
    if(!str) item.classList.add('od-additem');

    label.title = str;
    label.style.backgroundColor = color;

    item.dataset.title = str;
    item.dataset.color = color;
    item.draggable = true;

    item.appendChild(label);

    (index < tagsBox.childElementCount) ? tagsBox.insertBefore(item, tagsBox.children[index]) : tagsBox.appendChild(item);

    // :: ACTIONS ::

    // ON CLICK
    item.addEventListener('click', str ?
    // Add my tag in the search field
      function () {
        if (typeof input.selectionStart !== 'undefined') {
          var startPos = input.selectionStart;
          var endPos = input.selectionEnd;
          var text = (startPos > 0 ? ' ' : '') + str + ' ';

          if (startPos > 0 && input.value[startPos-1] === ' ') startPos--;

          if (endPos < input.value.length && input.value[endPos] === ' ') endPos++;

          input.value = input.value.slice(0, startPos) + text + input.value.slice(endPos);
          input.focus();
          input.selectionStart = input.selectionEnd = startPos + text.length;
        } else {
          input.value = input.value.trim() + ' ' + str + ' ';
          input.focus();
        }
        input.click();
      } :
    // Create a new tag from the search field (highlighted) text
      function () {
      	var text;
        if (input.selectionStart !== input.selectionEnd) {
          text = input.value.substring(input.selectionStart, input.selectionEnd).trim().toLowerCase();
        } else {
          text = input.value.trim().toLowerCase();
        }

        if (!text) {
          input.focus();
          return;
        }

        var newColor = randomHSL();

        var dupl = tagsBox.querySelector('.od-item[data-title=\'' + text + '\']');
        if (dupl) removeItem(dupl);

        label.style.backgroundColor = newColor;
        item.dataset.color = newColor;

        var index = Array.from(tagsBox.children).indexOf(this) + 1;

        addItem(text, color, index);

        color = newColor;

        saveData();
      }
    );

    // DRAG START
    item.addEventListener('dragstart', function (e) {
      e.dataTransfer.effectAllowed = "move";
      draggedItem = this;
      tagsBox.classList.add('od-grabbing');
      for (var i = 0; i < items.length; i++) {
        items.item(i).classList.add(items.item(i) == draggedItem ? 'od-draggeditem' : 'od-hintitem');
      }
    });

    // DRAG ENTER
    item.addEventListener('dragenter', function () {
      if (this != draggedItem) { this.classList.add('od-activeitem'); }
    });

    // DRAG LEAVE
    item.addEventListener('dragleave', function () {
      this.classList.remove('od-activeitem');
    });

    // DRAG END
    item.addEventListener('dragend', function () {
      if (!validTarget && !this.classList.contains('od-additem')) {
        removeItem(this);
      }
      validTarget = false;

      tagsBox.classList.remove('od-grabbing')
      for (var i = 0; i < items.length; i++) {
        items.item(i).classList.remove('od-hintitem');
        items.item(i).classList.remove('od-activeitem');
        items.item(i).classList.remove('od-draggeditem');
      }
    });

    // DRAG OVER - PREVENT THE DEFAULT 'DROP', SO WE CAN DO OUR OWN
    item.addEventListener('dragover', function (e) {
      e.preventDefault();
    });

    // ON DROP
    item.addEventListener('drop', function (e) {
      e.dataTransfer.dropEffect = 'move';
      e.preventDefault();
      validTarget = true;

      if (this != draggedItem) {
        var currentpos = 0, droppedpos = 0;
        for (var it = 0; it < items.length; it++) {
          if (draggedItem == items[it]) { currentpos = it; }
          if (this == items[it]) { droppedpos = it; }
        }
        if (currentpos < droppedpos) {
          this.parentNode.insertBefore(draggedItem, this.nextSibling);
        } else {
          this.parentNode.insertBefore(draggedItem, this);
        }
        saveData();
      }
    });
  }

  // REMOVE ITEM
  function removeItem(el){
    var text = el.dataset.title;
    var removeIndex = data.findIndex(function(i){return i.text === text;});
    data.splice(removeIndex, 1);
    tagsBox.removeChild(el);
    saveData();
  }

  // START
  function start(){
    addGlobalStyle(css);
    tagsBox.innerHTML = '';
    loadData();
    parseData();
    items = tagsBox.getElementsByTagName('div');
  }






// STYLE

var css = '/* RESET */';

css+= '.ikrT4e { max-height: initial !important; }';
css+= '.e9EfHf { padding-top: 30px !important; }';
css+= '.Q3DXx.yIbDgf { margin-top: 16px !important; }';
css+= '#searchform > .sfbg { margin-top: 0 !important }';

css+= '/* CONTAINER */';

css+= '#od-tagsbox {';
css+= '    position: absolute;';
css+= '    top: -34px;';
css+= '    max-width: 100%;';
css+= '    max-height: 30px;';
css+= '    margin-top: 2px;';
css+= '    border: 1px solid transparent;';
css+= '    border-radius: 20px;';
css+= '    background: rgba(255,255,255, 0);';
css+= '    overflow: hidden;';
css+= '    transition: .3s ease-out;';
css+= '    z-index: 999;';
css+= '}';
css+= '#od-tagsbox * {';
css+= '    box-sizing: border-box;';
css+= '}';
css+= '#searchform #od-tagsbox {';
css+= '    top: -42px;';
css+= '    left: 30px;';
css+= '}';
css+= '#searchform.minidiv #od-tagsbox {';
css+= '    top: -33px;';
css+= '}';
css+= '#od-tagsbox:hover {';
css+= '    max-height: 100px;';
css+= '		border-color: rgba(200,200,200, .2);';
css+= '		box-shadow: 0 2px 5px 1px rgba(64,60,67,.3);';
css+= '		background: rgba(255,255,255, .2);';
css+= '}';

css+= '/* ITEMS */';

css+= '.od-item {';
css+= '    float: left;';
css+= '    padding: 3px;';
css+= '    transition: opacity .3s .1s ease-out;';
css+= '    cursor: pointer;';
css+= '}';
css+= '.od-item > i {';
css+= '    position: relative;';
css+= '    top: 0;';
css+= '    display: block;';
css+= '    width: 24px;';
css+= '    height: 24px;';
css+= '    border: 2px solid #0002;';
css+= '    text-align: center;';
css+= '    text-transform: uppercase;';
css+= '    white-space: nowrap;';
css+= '    font: normal 12px/20px Arial, sans-serif;';
css+= '    color: #fff;';
css+= '    border-radius: 50%;';
css+= '    overflow: hidden;';
css+= '    cursor: pointer;';
css+= '    transition: .2s ease-out, font-size 0s, font-weight 0s;';
css+= '}';
css+= '.od-item > i::before {';
css+= '    content: attr(title);';
css+= '}';
css+= '.od-item.od-additem > i{';
css+= '    font-size: 18px;';
css+= '    font-weight: bold;';
css+= '}';
css+= '.od-item.od-additem > i::before {';
css+= '    content: "+";';
css+= '}';

css+= '/* DRAGGING */';

css+= '#od-tagsbox.od-grabbing {';
css+= '    cursor: move;';
css+= '}';
css+= '#od-tagsbox.od-grabbing > .od-item {';
css+= '    transition-delay: 0;';
css+= '}';
css+= '.od-hintitem {';
css+= '    opacity: .4;';
css+= '}';
css+= '.od-hintitem > i {';
css+= '    pointer-events: none;';
css+= '}';
css+= '.od-draggeditem {';
css+= '    opacity: 0;';
css+= '}';
css+= '.od-activeitem {';
css+= '    opacity: .25;';
css+= '}';
css+= '.od-activeitem > i {';
css+= '    top: -3px;';
css+= '}';

  function randomHSL() {
    return 'hsl(' + ~~(360 * Math.random()) + ',50%,50%)';
  }

  function addGlobalStyle(css) {
    var h = document.querySelector('head');
    if (!h) return;
    var s = document.createElement('style');
    s.type = 'text/css';
    s.innerHTML = css;
    h.appendChild(s);
  }

  start();

});