// ==UserScript==
// @name Clip-to-Gist Quote Script (ES5, Lemur Compatible)
// @namespace http://tampermonkey.net/
// @version 2.3.1
// @description One-click clipboard quotes → GitHub Gist, with keyword highlighting, versioning & Lemur Browser compatibility
// @author Your Name
// @include *://*/*
// @run-at document-end
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @connect api.github.com
// ==/UserScript==
(function(){
'use strict';
// --- Fallback wrappers ---
var setValue = (typeof GM_setValue === 'function') ?
GM_setValue :
function(k, v){ localStorage.setItem(k, v); };
var getValue = (typeof GM_getValue === 'function') ?
function(k, def){ var v = GM_getValue(k); return (v===undefined||v===null)?def:v; } :
function(k, def){ var v = localStorage.getItem(k); return (v===undefined||v===null)?def:v; };
var httpRequest = (typeof GM_xmlhttpRequest === 'function') ?
GM_xmlhttpRequest :
function(opts){
var headers = opts.headers || {};
if(opts.method === 'GET'){
fetch(opts.url, { headers: headers })
.then(function(res){ return res.text().then(function(txt){
opts.onload({ status: res.status, responseText: txt });
}); });
} else {
fetch(opts.url, { method: opts.method, headers: headers, body: opts.data })
.then(function(res){ return res.text().then(function(txt){
opts.onload({ status: res.status, responseText: txt });
}); });
}
};
// --- Version key init ---
var VERSION_KEY = 'clip2gistVersion';
if(getValue(VERSION_KEY, null) === null){
setValue(VERSION_KEY, 1);
}
// --- Inject global CSS via <style> ---
function addGlobalStyle(css){
var head = document.getElementsByTagName('head')[0];
if(!head){ return; }
var style = document.createElement('style');
style.type = 'text/css';
style.textContent = css;
head.appendChild(style);
}
addGlobalStyle(
'#clip2gist-trigger{' +
'position:fixed!important;bottom:20px!important;right:20px!important;' +
'width:40px;height:40px;line-height:40px;text-align:center;' +
'background:#4CAF50;color:#fff;border-radius:50%;cursor:pointer;' +
'z-index:2147483647!important;font-size:24px;box-shadow:0 2px 6px rgba(0,0,0,0.3);' +
'}' +
'.clip2gist-mask{' +
'position:fixed;top:0;left:0;right:0;bottom:0;' +
'background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;' +
'z-index:2147483646;' +
'}' +
'.clip2gist-dialog{' +
'background:#fff;padding:20px;border-radius:8px;' +
'max-width:90%;max-height:90%;overflow:auto;' +
'box-shadow:0 2px 10px rgba(0,0,0,0.3);' +
'}' +
'.clip2gist-dialog input{' +
'width:100%;padding:6px;margin:4px 0 12px;box-sizing:border-box;font-size:14px;' +
'}' +
'.clip2gist-dialog button{' +
'margin-left:8px;padding:6px 12px;font-size:14px;cursor:pointer;' +
'}' +
'.clip2gist-word{' +
'display:inline-block;margin:2px;padding:4px 6px;border:1px solid #ccc;' +
'border-radius:4px;cursor:pointer;user-select:none;' +
'}' +
'.clip2gist-word.selected{' +
'background:#ffeb3b;border-color:#f1c40f;' +
'}' +
'#clip2gist-preview{' +
'margin-top:12px;padding:8px;border:1px solid #ddd;' +
'min-height:40px;font-family:monospace;' +
'}'
);
// --- Insert floating trigger button ---
function insertTrigger(){
if(!document.body){
return setTimeout(insertTrigger, 100);
}
var btn = document.createElement('div');
btn.id = 'clip2gist-trigger';
btn.textContent = '📝';
btn.addEventListener('click', mainFlow, false);
btn.addEventListener('dblclick', openConfigDialog, false);
document.body.appendChild(btn);
}
insertTrigger();
// --- Main: read from clipboard and pop editor ---
function mainFlow(){
navigator.clipboard && navigator.clipboard.readText
? navigator.clipboard.readText().then(function(txt){
if(!txt.trim()){
alert('Clipboard is empty');
} else {
showEditor(txt.trim());
}
}, function(){
alert('Please use HTTPS and allow clipboard access');
})
: alert('Clipboard API not supported');
}
// --- Show editor dialog ---
function showEditor(rawText){
var mask = document.createElement('div');
mask.className = 'clip2gist-mask';
var dlg = document.createElement('div');
dlg.className = 'clip2gist-dialog';
// word spans
var wrap = document.createElement('div');
var words = rawText.split(/\s+/);
for(var i=0;i<words.length;i++){
var sp = document.createElement('span');
sp.className = 'clip2gist-word';
sp.textContent = words[i];
sp.onclick = (function(el){
return function(){
el.classList.toggle('selected');
updatePreview();
};
})(sp);
wrap.appendChild(sp);
}
dlg.appendChild(wrap);
// preview
var prev = document.createElement('div');
prev.id = 'clip2gist-preview';
dlg.appendChild(prev);
// buttons
var row = document.createElement('div');
['Cancel','Configure','Confirm'].forEach(function(label){
var b = document.createElement('button');
b.textContent = label;
if(label==='Cancel'){
b.onclick = function(){ document.body.removeChild(mask); };
} else if(label==='Configure'){
b.onclick = openConfigDialog;
} else {
b.onclick = confirmUpload;
}
row.appendChild(b);
});
dlg.appendChild(row);
mask.appendChild(dlg);
document.body.appendChild(mask);
updatePreview();
// update preview text
function updatePreview(){
var spans = wrap.children;
var parts = [];
for(var j=0;j<spans.length;){
if(spans[j].classList.contains('selected')){
var grp = [spans[j].textContent], k = j+1;
while(k<spans.length && spans[k].classList.contains('selected')){
grp.push(spans[k].textContent);
k++;
}
parts.push('{' + grp.join(' ') + '}');
j = k;
} else {
parts.push(spans[j].textContent);
j++;
}
}
prev.textContent = parts.join(' ');
}
// upload to Gist
function confirmUpload(){
var gistId = getValue('gistId','');
var token = getValue('githubToken','');
if(!gistId || !token){
alert('Please configure Gist ID and GitHub Token first');
return;
}
var ver = getValue(VERSION_KEY,1);
var header = 'Version ' + ver;
var content = prev.textContent;
// GET existing
httpRequest({
method: 'GET',
url: 'https://api.github.com/gists/' + gistId,
headers: { 'Authorization': 'token ' + token },
onload: function(res1){
if(res1.status !== 200){
alert('Failed to fetch Gist: ' + res1.status);
return;
}
var data = JSON.parse(res1.responseText);
var file = Object.keys(data.files)[0];
var oldc = data.files[file].content;
var upd = '\n\n----\n' + header + '\n' + content + oldc;
// PATCH update
httpRequest({
method: 'PATCH',
url: 'https://api.github.com/gists/' + gistId,
headers: {
'Authorization': 'token ' + token,
'Content-Type': 'application/json'
},
data: JSON.stringify({ files: (function(){ var o={}; o[file]={content:upd}; return o; })() }),
onload: function(res2){
if(res2.status === 200){
alert('Upload successful! Version ' + ver);
setValue(VERSION_KEY, ver+1);
document.body.removeChild(mask);
} else {
alert('Failed to update Gist: ' + res2.status);
}
}
});
}
});
}
}
// --- Configuration dialog ---
function openConfigDialog(){
var mask = document.createElement('div');
mask.className = 'clip2gist-mask';
var dlg = document.createElement('div');
dlg.className = 'clip2gist-dialog';
var label1 = document.createElement('label');
label1.textContent = 'Gist ID:';
var input1 = document.createElement('input');
input1.value = getValue('gistId','');
var label2 = document.createElement('label');
label2.textContent = 'GitHub Token:';
var input2 = document.createElement('input');
input2.value = getValue('githubToken','');
dlg.appendChild(label1);
dlg.appendChild(input1);
dlg.appendChild(label2);
dlg.appendChild(input2);
var save = document.createElement('button');
save.textContent = 'Save';
save.onclick = function(){
setValue('gistId', input1.value.trim());
setValue('githubToken', input2.value.trim());
alert('Configuration saved');
document.body.removeChild(mask);
};
var cancel = document.createElement('button');
cancel.textContent = 'Cancel';
cancel.onclick = function(){
document.body.removeChild(mask);
};
dlg.appendChild(save);
dlg.appendChild(cancel);
mask.appendChild(dlg);
document.body.appendChild(mask);
}
})();