// ==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();
});