// ==UserScript==
// @name Google Searching Tags Box
// @version 1.2.1
// @description Make your searches easier by adding tags to your search queries with one click
// @author OpenDec
// @match https://www.google.com/*
// @match https://www.google.co.jp/*
// @match https://www.google.co.uk/*
// @match https://www.google.es/*
// @match https://www.google.ca/*
// @match https://www.google.de/*
// @match https://www.google.it/*
// @match https://www.google.fr/*
// @match https://www.google.com.au/*
// @match https://www.google.com.tw/*
// @match https://www.google.nl/*
// @match https://www.google.com.br/*
// @match https://www.google.com.tr/*
// @match https://www.google.be/*
// @match https://www.google.com.gr/*
// @match https://www.google.co.in/*
// @match https://www.google.com.mx/*
// @match https://www.google.dk/*
// @match https://www.google.com.ar/*
// @match https://www.google.ch/*
// @match https://www.google.cl/*
// @match https://www.google.at/*
// @match https://www.google.co.kr/*
// @match https://www.google.ie/*
// @match https://www.google.com.co/*
// @match https://www.google.pl/*
// @match https://www.google.pt/*
// @match https://www.google.com.pk/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.deleteValue
// @run-at document-start
// @namespace https://gf.qytechs.cn/users/873547
// @license MIT
// ==/UserScript==
/* jshint esversion: 8 */
window.addEventListener('DOMContentLoaded', function stageReady(){
'use strict';
// --------------------------------------------------
// --- INIT ---
// --------------------------------------------------
addGlobalStyle(css(getColorMode(rgbIsDark(window.getComputedStyle(document.body).backgroundColor) ? 'dark' : 'light')));
//var input = document.querySelector('input.gLFyf.gsfi'); // CHANGED 221117
var input = document.querySelector('input.gLFyf'); // FIX
var container = document.querySelector('.RNNXgb');
var tagsBox = document.createElement('div');
var tagsBoxWrapper = document.createElement('div');
var settings = {};
var defaultSettings = {tagsWidth: ''};
var items;
var arrTags = [];
var actions = {};
var draggedItem = null;
var draggedData = null;
var dragenterBoxCounter = 0;
var deletingZone = document.createElement('div');
var inputFile = document.createElement('input');
var contextMenu = document.createElement('div');
var contextMenuItems = [
'<li data-action="copyTags" ><i>📋</i> Copy Tags <kbd>Ctrl+C</kbd>',
'<li data-action="appendTags" ><i>📌</i> Append copied Tags <kbd>Ctrl+V</kbd>',
'<li>',
'<li data-action="importTags" ><i>📂</i> Import and append Tags',
'<li data-action="exportTags" ><i>💾</i> Export Tags as txt',
'<li>',
'<li data-action="setWidthSmall" class="od-checkable od-tagswidth od-sizekey-" ><i>◻</i> Small Tags width',
'<li data-action="setWidthLarge" class="od-checkable od-tagswidth od-sizekey-L" ><i>▭</i> Large Tags width',
'<li data-action="setWidthAuto" class="od-checkable od-tagswidth od-sizekey-A" ><i>↔️</i> Auto Tags width',
'<li>',
'<li data-action="clearBox" ><i>🗑️</i> Clear the Tags Box'
].join('');
// --------------------------------------------------
// --- THE TAGS BOX ---
// --------------------------------------------------
tagsBoxWrapper.id = 'od-tagsbox-wrapper';
tagsBox.id = 'od-tagsbox';
tagsBoxWrapper.appendChild(tagsBox);
container.parentNode.insertBefore(tagsBoxWrapper, container.nextSibling);
// --------------------------------------------------
// --- DRAG-AND-DROP SETTINGS ---
// --------------------------------------------------
// BOX HANDLERS
tagsBox.addEventListener('dragenter', function (e){
e.preventDefault();
e.dataTransfer.dropEffect = draggedItem ? 'move' : draggedData ? 'copy' : 'none' ;
});
tagsBox.addEventListener('dragover', function (e){
e.preventDefault();
e.dataTransfer.dropEffect = draggedItem ? 'move' : draggedData ? 'copy' : 'none' ;
});
// ITEMS HANDLERS
function itemDragstart (e){
e.dataTransfer.effectAllowed = "move";
deletingZone.classList.add('od-dragging');
draggedItem = e.target;
draggedItem.classList.add('od-draggeditem');
for (var i = 0; i < items.length; i++) {
items.item(i).classList.add('od-hintitem');
}
}
function itemDragend (e){
draggedItem = null;
deletingZone.classList.remove('od-dragging', 'od-dragging-hover');
for (var i = 0; i < items.length; i++) {
items.item(i).classList.remove('od-hintitem', 'od-belowitem', 'od-draggeditem');
}
saveData();
}
function itemDragenter (e){
e.preventDefault();
if (draggedItem === null && draggedData === null){
e.dataTransfer.effectAllowed = "none";
}
if (draggedItem === null){
return false;
}
var swapItem = e.target;
swapItem.classList.add('od-belowitem');
swapItem = swapItem === draggedItem.nextSibling ? swapItem.nextSibling : swapItem;
tagsBox.insertBefore(draggedItem, swapItem);
items = tagsBox.getElementsByTagName('div');
}
function itemDragleave (e){
e.target.classList.remove('od-belowitem');
}
function itemDragover (e){
e.preventDefault();
e.dataTransfer.dropEffect = draggedItem === null && draggedData === null ? 'none' : 'move';
}
// TAG DELETING ZONE
deletingZone.id = 'od-deletingZone';
tagsBoxWrapper.appendChild(deletingZone);
deletingZone.addEventListener('dragenter', function (e){
e.preventDefault();
if (!draggedItem.classList.contains('od-additem')){
deletingZone.classList.add('od-dragging-hover');
}
});
deletingZone.addEventListener('dragleave', function (e){
e.preventDefault();
deletingZone.classList.remove('od-dragging-hover');
});
deletingZone.addEventListener('dragover', function (e){
e.preventDefault();
e.dataTransfer.dropEffect = draggedItem.classList.contains('od-additem') ? 'none' : 'move';
});
deletingZone.addEventListener('drop', function (e){
e.preventDefault();
if (!draggedItem.classList.contains('od-additem')){
removeItem(draggedItem);
}
});
// --------------------------------------------------
// --- INPUT FILE ---
// --------------------------------------------------
inputFile.id = 'od-inputFile';
inputFile.type = 'file';
inputFile.style = 'display:none';
inputFile.accept = '.txt';
inputFile.addEventListener('change', function (e){ importData(this.files); });
// Required for iOS Safari
tagsBoxWrapper.appendChild(inputFile);
// --------------------------------------------------
// --- CONTEXT MENU ---
// --------------------------------------------------
contextMenu.id = 'od-contextMenu';
contextMenu.innerHTML = '<ul>' + contextMenuItems + '</ul>';
tagsBox.addEventListener('contextmenu', contextMenuOpen);
onoffListeners(contextMenu, 'mousedown contextmenu wheel', function(e){
e.preventDefault();
e.stopPropagation();
}, true);
contextMenu.querySelector('ul').addEventListener('mouseup', contextMenuClick);
tagsBoxWrapper.appendChild(contextMenu);
function contextMenuOpen(e){
var x = e.clientX - 1;
var y = e.clientY - 1;
e.preventDefault();
contextMenu.style = 'top: ' + y + 'px; left: ' + x + 'px';
contextMenu.classList.add('open');
setTimeout(function(){
onoffListeners(window, 'wheel resize blur mousedown contextmenu', contextMenuClose, true);
}, 1);
}
function contextMenuClose(e){
setTimeout(function(){
contextMenu.classList.remove('open');
contextMenu.removeAttribute('style');
onoffListeners(window, 'wheel resize blur mousedown contextmenu', contextMenuClose, false);
}, 1);
}
function contextMenuClick(e){
e.preventDefault();
e.stopPropagation();
if (contextMenu.querySelector('ul').contains(e.target)){
var item = e.target.closest('li[data-action]');
if (!item) return;
contextMenuClose();
actions[item.dataset.action]();
}
}
function onoffListeners(element, events, listener, flag){
var ev = events.trim().split(/ +/);
for (var i = 0; i < ev.length; i++){
element[(flag ? 'add' : 'remove') + 'EventListener'](ev[i], listener);
}
}
// --------------------------------------------------
// --- CONTEXT MENU ACTIONS ---
// --------------------------------------------------
actions.copyTags = function(){
clipboardCopy(encodeData(settings, arrTags))
.then(function(){
fxGlow(tagsBox);
})
.catch(function(){
// Cannot write on clipboard
modal(50);
})
;
};
actions.appendTags = function(){
clipboardPaste()
.then(function(str){
var arr = decodeData(str).tags;
if (arr !== null){
appendTagsArr(arr);
fxGlow(tagsBox);
} else {
// Unknoun data format
modal(10);
}
})
.catch(function(){
// Cannot read clipboard data
modal(60);
})
;
};
actions.importTags = function(){
inputFile.value = null;
inputFile.click();
};
actions.exportTags = function(){
exportData(encodeData(settings, arrTags));
};
actions.setWidthSmall = function(){
setTagsWidth('');
saveData();
};
actions.setWidthLarge = function(){
setTagsWidth('L');
saveData();
};
actions.setWidthAuto = function(){
setTagsWidth('A');
saveData();
};
actions.clearBox = function(){
var additem = tagsBox.querySelector('.od-additem');
tagsBox.innerHTML = '';
tagsBox.append(additem);
saveData();
fxGlow(tagsBox);
};
// --------------------------------------------------
// --- DATA MANAGEMENT ---
// --------------------------------------------------
function encodeData(params, tags){
var arrParams = [params.tagsWidth];
var strParams = arrParams.join(',');
return ':tags' +
(strParams ? '['+ strParams +']' : '')+
':' +
tags.map(function(e){
return e.text + e.color;
}).join('');
}
function decodeData(str){
var res = {};
// New compact data format
if (isValidDataFormat(str)){
var parts = str.match(/^:tags(?:\[(.*)])?:(.*)$/);
var arrParams = (parts[1] || '').split(',');
res.params = {tagsWidth: arrParams[0]};
res.tags = parts[2]
.split('')
.map(function(e){
return {text: e.slice(0, -6), color: e.slice(-6)};
});
} else {
// Backward compatibility for the old JSON format
try{
res.tags = JSON.parse(str || '[]');
res.params = {};
} catch(e) {
res.tags = [];
res.params = {};
}
}
return res;
}
function parseData(){
setTagsWidth(settings.tagsWidth);
if (!tagsBox.querySelector('.od-additem')) fxFadein(addItem(), 500);
var delay = 0;
for (var i in arrTags){
fxFadein(addItem(arrTags[i].text, arrTags[i].color), 500, delay += 30);
}
}
function parseDataString(str){
str = str.trim();
if (str) {
if (isValidDataFormat(str)){
// Check for data with :tags: format
appendTagsArr(decodeData(str).tags);
fxGlow(tagsBox);
} else {
// Check for plain text, each not-blank line of the string is taken as a TAG text
var arr = str.split(/\r?\n/).reduce(function(a, b){
var tag = b.trim().toLowerCase();
if (tag){
a.push({text: tag});
}
return a;
},[]);
if (arr.length){
appendTagsArr(arr);
fxGlow(tagsBox);
} else {
// Unknoun data format
modal(10);
}
}
} else {
// Unknoun data format
modal(10);
}
}
function updateData(){
arrTags = Array.from(items).flatMap(function(e){return e.classList.contains('od-additem') ? [] : [{text: e.dataset.title, color: e.dataset.color}]; });
}
function restoreData(str){
var data = decodeData(str);
arrTags = data.tags;
settings.tagsWidth = data.params.tagsWidth;
}
async function saveData(){
updateData();
if (arrTags.length){
var data = encodeData(settings, arrTags);
// Store data via GM APIs
!!GM && await GM.setValue('odtagsbox', data);
// Maintain forward compatibility through localStorage
localStorage.setItem('odtagsbox', data);
} else {
!!GM && await GM.deleteValue('odtagsbox')
localStorage.removeItem('odtagsbox');
}
}
function importData(files){
var file;
if (window.FileReader){
file = files[0];
var reader = new FileReader();
reader.addEventListener('load', function (e){
var str = reader.result;
if (arrTags.length === 0 && settings.tagsWidth === ''){
// If the BOX is blank and not set, restore all data and settings using the imported data
restoreData(str);
parseData();
saveData();
fxGlow(tagsBox);
} else {
// Otherwise, just add TAGs to existing ones
var arr = decodeData(str).tags;
if (arr !== null){
appendTagsArr(arr);
saveData();
fxGlow(tagsBox);
} else {
// Unknoun data format
modal(10);
}
}
});
reader.addEventListener('error', function (e){
if (e.target.error.name == 'NotReadableError'){
modal(21);
}
});
reader.readAsText(file, 'utf-8');
} else {
modal(20);
}
}
function exportData(str){
var name = 'tagsbox_data.txt';
var blob = new Blob(['\ufeff' + str], { type: 'text/plain;charset=utf-8' });
var objUrl = window.URL.createObjectURL(blob, { type: 'text/plain' });
var a = document.createElement('a');
a.href = objUrl;
a.download = name;
tagsBoxWrapper.appendChild(a);
a.click();
setTimeout(function (){
window.URL.revokeObjectURL(objUrl);
tagsBoxWrapper.removeChild(a);
}, 100);
}
function isValidDataFormat(str){
return /^:tags(?:\[.*])?:/.test(str);
}
function clipboardCopy(txt){
// Return a promise
if (navigator.clipboard){
return navigator.clipboard.writeText(txt);
} else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
var textarea = document.createElement('textarea');
textarea.value = txt;
textarea.style.position = 'fixed';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
return new Promise(function(ok, ko){
if (document.execCommand('copy')) ok();
else ko();
document.body.removeChild(textarea);
});
}
}
function clipboardPaste(){
// Return a promise
if (navigator.clipboard){
var a = navigator.clipboard.readText();
return a;
} else if (document.queryCommandSupported && document.queryCommandSupported('paste')) {
return new Promise(function(ok, ko){
if (document.execCommand('paste')) ok();
else ko();
});
}
}
// --------------------------------------------------
// --- DATA TRANSFER ---
// --------------------------------------------------
// COPY-PASTE KEYBOARD SHORTCUTS
function isCopyPasteAllowed(denyIfTextFieldsFocused){
// Returns TRUE if nothing is selected on the page
var actEl = document.activeElement;
return (
(
!(// check if there are no focused fields
denyIfTextFieldsFocused &&
actEl &&
(
actEl.tagName.toLowerCase() === 'input' &&
actEl.type == 'text' ||
actEl.tagName.toLowerCase() === 'textarea'
)
) &&
(actEl.selectionStart === actEl.selectionEnd)
) &&
['none', 'caret'].includes(window.getSelection().type.toLowerCase())
);
}
window.addEventListener('paste', function(e){
var str = (e.clipboardData || window.clipboardData).getData('text');
if (isCopyPasteAllowed(true)){
parseDataString(str);
e.preventDefault();
}
});
window.addEventListener('copy', function(e){
if (isCopyPasteAllowed()) {
// Put the tags data on the clipboard
e.clipboardData.setData('text/plain', encodeData(settings, arrTags));
e.preventDefault();
fxGlow(tagsBox);
}
});
// DRAG-AND-DROP STRING OR EXTERNAL TXT FILE
function isValidDraggedDataType(data){
// Accept only TEXT in external data type
for (var i = 0; i < data.length; i++){
if (data[i].type.match('^text/plain')){
return true;
}
}
return false;
}
tagsBox.addEventListener('dragenter', function (e){
dragenterBoxCounter++;
var data = e.dataTransfer.items;
if (draggedData === null && isValidDraggedDataType(data)){
draggedData = data[0];
tagsBox.classList.add('od-dragging-external-data');
}
});
tagsBox.addEventListener('dragleave', function (e){
dragenterBoxCounter--;
// Counter needed to prevent bubbling effect
if (dragenterBoxCounter === 0){
if(draggedData === null) return;
draggedData = null;
tagsBox.classList.remove('od-dragging-external-data');
}
});
tagsBox.addEventListener('drop', function (e){
e.preventDefault();
tagsBox.classList.remove('od-dragging-external-data');
var data = e.dataTransfer.items;
// Exit if not TEXT data type
if (!isValidDraggedDataType(data)) return false;
if (data[0].kind === 'string'){
// If string
parseDataString(e.dataTransfer.getData('Text'));
} else if (data[0].kind === 'file'){
// If file, only :tags: format data is accepted
importData(e.dataTransfer.files);
}
draggedData = null;
dragenterBoxCounter = 0;
});
// --------------------------------------------------
// --- TAGS FUNCTIONS ---
// --------------------------------------------------
// Append TAGs by array
function appendTagsArr(arr){
for (var i in arr) {
var text = arr[i].text;
var color = arr[i].color;
var dupl = tagsBox.querySelector('.od-item[title="' + text + '"]');
if (dupl) removeItem(dupl);
addItem(text, color);
}
items = tagsBox.getElementsByTagName('div');
saveData();
}
// Add and set a item in the BOX
function addItem(str, color, index){
str = str || '';
// Double check to ensure backward compatibility for the old HSL format of stored colors
color = color ? color[0] === 'h' ? HSLToHex(color) : color : randomColor();
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.dataset.title = str;
label.style.backgroundColor = '#' + color;
item.title = str || 'ADD TAG';
item.dataset.title = str;
item.dataset.color = color;
item.draggable = true;
item.appendChild(label);
if (index < tagsBox.childElementCount) tagsBox.insertBefore(item, tagsBox.children[index]);
else tagsBox.appendChild(item);
// --------------------------------------------------
// --- ITEMS ACTIONS ---
// --------------------------------------------------
// ON CLICK
item.addEventListener('click', str ?
// TAG BUTTON - 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();
} :
// PLUS BUTTON (+) - 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 = randomColor();
var dupl = tagsBox.querySelector('.od-item[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-AND-DROP
item.addEventListener('dragstart', itemDragstart);
item.addEventListener('dragend', itemDragend);
item.addEventListener('dragenter', itemDragenter);
item.addEventListener('dragleave', itemDragleave);
item.addEventListener('dragover', itemDragover);
return item;
}
// Remove a TAG item
function removeItem(el){
tagsBox.removeChild(el);
saveData();
}
// Set the items width via a size key
function setTagsWidth(sizeKey){
var classList = tagsBox.classList;
sizeKey = ['', 'L','A'].includes(sizeKey) ? sizeKey : defaultSettings.tagsWidth || '';
classList.remove('od-smallwidth', 'od-largewidth', 'od-autowidth');
classList.add(sizeKey ? {'L': 'od-largewidth', 'A': 'od-autowidth'}[sizeKey] : 'od-smallwidth');
settings.tagsWidth = sizeKey;
// Set the checked item in the context menu
var checkedItem = contextMenu.querySelector('.od-tagswidth[data-checked]');
if (checkedItem) checkedItem.removeAttribute('data-checked');
contextMenu.querySelector('.od-sizekey-' + sizeKey).dataset.checked = '';
}
// --------------------------------------------------
// --- COLOR PROCESSING ---
// --------------------------------------------------
function HSLToHex(hsl){
if (!hsl) return;
hsl = hsl.slice(4, -1).split(',');
var h = hsl[0];
var s = hsl[1].slice(0, -1) / 100;
var l = hsl[2].slice(0, -1) / 100;
var a = s * Math.min(l, 1 - l);
var f = function(n){
var k = (n + h / 30) % 12;
var color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return f(0) + f(8) + f(4);
}
function randomHSL(){
return 'hsl(' + ~~(360 * Math.random()) + ',' + (40 + 30 * Math.random()) + '%,' + (40 + 20 * Math.random()) + '%)';
}
function randomColor(){
return HSLToHex(randomHSL());
}
function rgbIsDark(rgb) {
rgb = rgb.slice(4, -1).split(',');
return ((rgb[0] * 299) + (rgb[1] * 587) + (rgb[2] * 114)) / 1000 < 155;
}
// --------------------------------------------------
// --- EFFECTS ---
// --------------------------------------------------
function fxGlow(el){
el.classList.add('od-highlight');
setTimeout(function(){el.classList.remove('od-highlight');}, 500);
}
function fxFadein(el, duration, delay){
duration = duration == null ? 300 : +duration;
delay = delay == null ? 0 : +delay;
el.style.opacity = '0';
el.style.transition = duration + 'ms ' + delay + 'ms ease-in-out';
setTimeout(function(){
el.style.removeProperty('opacity');
setTimeout(function(){
el.style.removeProperty('transition');
}, duration + delay);
}, 1);
}
// --------------------------------------------------
// --- MODAL ---
// --------------------------------------------------
function modal(msg, delay){
if (typeof delay === 'undefined') delay = 10;
if (typeof msg === 'number'){
msg = modal.msgList[msg];
}
setTimeout(function(){
alert(msg);
}, delay);
}
modal.msgList = {
10: '⚠️ Sorry!\nI don\'t understand the format of this data.\n\nNo tags have been added.',
20: '⚠️ Oops!\nI can\'t open the file reader.💡 But...\nyou can open it elsewhere, then try the copy-paste functions.',
21: '⚠️ Oops!\nI can\'t read this file.💡 Try picking it up and opening it again.',
50: '⚠️ Oops!\nUnable to copy data to clipboard.\n\n💡 But...\nyou can copy the string from the search field.',
60: '⚠️ Oops!\nI can\'t read data from the clipboard.\n\n💡 But... try with CTRL+V.\n– Close this modal first –',
};
// --------------------------------------------------
// --- START ---
// --------------------------------------------------
async function start(){
// Retrieve data via GM APIs
var data = !!GM && await GM.getValue('odtagsbox');
// Maintain backward compatibility through localStorage
if (!data) data = localStorage.getItem('odtagsbox');
tagsBox.innerHTML = '';
restoreData(data);
parseData();
items = tagsBox.getElementsByTagName('div');
setTimeout(function(){ tagsBox.classList.remove('od-hidein');}, 2);
}
// --------------------------------------------------
// --- STYLE ---
// --------------------------------------------------
function addGlobalStyle(strCSS){
var h = document.querySelector('head');
if (!h) return;
var s = document.createElement('style');
s.type = 'text/css';
s.innerHTML = strCSS;
h.appendChild(s);
}
function getColorMode(mode){
return {dark: mode === 'dark', light: mode !== 'dark'};
}
function css (colorMode){ return (
// RESET
'.ikrT4e { max-height: initial !important; }'+
'.e9EfHf { padding-top: 30px !important; }'+
'.Q3DXx.yIbDgf { margin-top: 16px !important; }'+
'#searchform > .sfbg { margin-top: 0 !important }'+
// CONTAINERS
'#od-tagsbox-wrapper *,'+
'#od-tagsbox-wrapper *::before,'+
'#od-tagsbox-wrapper *::after {'+
' box-sizing: border-box;'+
'}'+
'#od-tagsbox {'+
' position: absolute;'+
' top: -34px;'+
' max-width: 100%;'+
' max-height: 32px;'+
' margin-top: 2px;'+
' border: 1px solid;'+
' border-color: rgba(' + ( colorMode.dark ? '95, 99, 104' : '208, 211, 215' ) + ', 0);'+
' border-radius: 16px;'+
' outline: 2px solid transparent;'+
' background: rgba(' + ( colorMode.dark ? '75, 75, 75' : '240, 240, 240' ) + ', 0);'+
' box-shadow: 0 2px 5px 1px rgba(64, 60, 67, 0);'+
' overflow: hidden;'+
' transition: all .3s ease-out, outline 0s, outline-color .3s ease-out, max-height .4s ease;'+
' z-index: 999;'+
'}'+
'#searchform #od-tagsbox {'+
' top: -42px;'+
' left: 30px;'+
'}'+
'#searchform.minidiv #od-tagsbox {'+
' top: -33px;'+
'}'+
'#od-tagsbox:hover {'+
' max-height: 200px;'+
' border-color: rgba(' + ( colorMode.dark ? '95, 99, 104' : '208, 211, 215' ) + ', 1);'+
' background: rgba(' + ( colorMode.dark ? '75, 75, 75' : '240, 240, 240' ) + ', .8);'+
' box-shadow: 0 2px 5px 1px rgba(64, 60, 67, .3);'+
'}'+
// ITEMS
'.od-item {'+
' float: left;'+
' min-width: 30px;'+
' max-width: 30px;'+
' height: 30px;'+
' cursor: pointer;'+
' transition: all .3s ease-out, opacity .3s .1s ease-out;'+
'}'+
// ITEMS WIDTH PRESETS
'.od-smallwidth > .od-item { max-width: 30px; }'+
'.od-largewidth > .od-item { max-width: 60px; }'+
'.od-autowidth > .od-item { max-width: 180px; }'+
'.od-smallwidth > .od-item > i { min-width: 24px; }'+
'.od-largewidth > .od-item > i { min-width: 54px; }'+
'.od-autowidth > .od-item > i { text-overflow: ellipsis; }'+
// TAGS LABEL
'.od-item > i {'+
' display: block;'+
' top: 0;'+
' left: 0;'+
' min-width: 24px;'+
' height: calc(100% - 6px);'+
' margin: 3px;'+
' border: 2px solid rgba(0, 0, 0, .2);'+
' outline: 1px solid transparent;'+
' border-radius: 15px;'+
' text-align: center;'+
' text-transform: uppercase;'+
' white-space: nowrap;'+
' font: normal 12px/20px Arial, sans-serif;'+
' color: #fff;'+
' overflow: hidden;'+
' pointer-events: none;'+
' transition: .2s ease-out, font-size 0s, font-weight 0s;'+
'}'+
'.od-item > i::before {'+
' content: attr(data-title);'+
'}'+
'.od-item.od-additem > i{'+
' font-size: 18px;'+
' font-weight: bold;'+
'}'+
'.od-item.od-additem > i::before {'+
' content: "+";'+
'}'+
// ITEMS HOVER
'#od-tagsbox > .od-item:not(.od-draggeditem):hover > i {'+
' border-color: rgba(255, 255, 255, .4);'+
' outline: 1px solid rgba(0, 0, 0, .4);'+
' transition: 0s;'+
'}'+
// DRAG-AND-DROP
'.od-draggeditem > i {'+
' opacity: 0;'+
'}'+
'.od-hintitem {'+
' opacity: .6;'+
' transition-delay: 0s;'+
'}'+
'.od-belowitem {'+
'}'+
'#od-deletingZone {'+
' position: fixed;'+
' top: 0;'+
' right: 0;'+
' bottom: 0;'+
' left: 0;'+
' z-index: 998;'+
' background: rgba(255, 0, 0, .2);'+
' opacity: 0;'+
' visibility: hidden;'+
' transition: .3s;'+
'}'+
'#od-deletingZone.od-dragging {'+
' visibility: visible;'+
'}'+
'#od-deletingZone.od-dragging-hover {'+
' opacity: 1;'+
'}'+
'#od-tagsbox.od-dragging-external-data {'+
' outline: 2px dashed #45bfff;'+
'}'+
'#od-tagsbox.od-dragging-external-data > .od-item {'+
' pointer-events: none;'+
'}'+
// CONTEXT MENU
'#od-contextMenu {'+
' display: none;'+
' position: fixed;'+
' z-index: 999;'+
' padding: 3px 0;'+
' font: 400 12px/23px "Segoe UI", Calibri, Arial, sans-serif;'+
' color: #000;'+
' border: 1px #dadce0 solid;'+
' background: #fff;'+
' box-shadow: 5px 5px 4px -4px rgba(0, 0, 0, .9);'+
' cursor: default;'+
' user-select: none;'+
'}'+
'#od-contextMenu.open {'+
' display: block;'+
'}'+
'#od-contextMenu > ul {'+
' list-style-type: none;'+
' margin: 0;'+
' padding: 0;'+
'}'+
'#od-contextMenu > ul > li {'+
' position: relative;'+
' margin: 0;'+
' padding: 0 22px 0 38px;'+
' line-height: 23px;'+
'}'+
'#od-contextMenu > ul > li:empty {'+
' margin: 4px 1px;'+
' padding: 0;'+
' border-top: 1px #dadce0 solid;'+
'}'+
'#od-contextMenu > ul > li > i:first-child {'+
' position: absolute;'+
' top: 0;'+
' left: 0;'+
' display: block;'+
' width: 35px;'+
' text-align: center;'+
' font-size: 1.3em;'+
' line-height: 23px;'+
' font-style: normal;'+
'}'+
'#od-contextMenu > ul > li > kbd {'+
' float: right;'+
' display: block;'+
' padding-left: 10px;'+
' text-align: right;'+
'}'+
'#od-contextMenu > ul > li:not(:hover) > kbd {'+
' color: #5f6368;'+
'}'+
'#od-contextMenu > ul > li:hover {'+
' color: #000;'+
' background: #e8e8e9;'+
'}'+
'#od-contextMenu > ul > li.od-checkable {'+
' padding-left: 48px;'+
'}'+
'#od-contextMenu > ul > li.od-checkable[data-checked]::before {'+
' content: "✓";'+
' position: absolute;'+
' left: 32px;'+
'}'+
// EFFECTS
// Glow
'#od-tagsbox.od-highlight {'+
' outline: 3px solid #45bfff;'+
' background: rgba(100, 180, 255, .6);'+
' transition: 0s;'+
'}'+
// COLOR SCHEME
'@media (prefers-color-scheme: dark) {'+
// Dark-mode applies to the context menu according to the system color scheme
' #od-contextMenu {'+
' background: #292a2d;'+
' color: #fff;'+
' font-weight: 100;'+
' border-color: #3c4043;'+
' }'+
' #od-contextMenu > ul > li:empty {'+
' border-color: #3c4043;'+
' }'+
' #od-contextMenu > ul > li:hover {'+
' color: #fff;'+
' background: #3f4042;'+
' }'+
' #od-contextMenu > ul > li:not(:hover) > kbd {'+
' color: #9aa0a6;'+
' }'+
'}'
);
}
// --------------------------------------------------
// --- WE CAN START! ---
// --------------------------------------------------
start();
});