// ==UserScript==
// @name Y-marker
// @namespace trespassersW
// @description Sets up keyboard shortcuts for placing bookmarks and navigation inside webpage.
// @include http://*
// @include https://*
// @include file://*
// ** about:config -> greasemonkey.fileIsGreaseable <- true
// @version 16.03.04
// @license MIT
// @released 2013-12-11
// @run-at document-end
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant unsafeWindow
// ==/UserScript==
/*
16.03.04 bookmarklets works in chrome
16.02.26
[+] hotkey configuration dialogue invoked from GM menu;
[+] Bookmarlets interface:
javascript:postMessage('Y-marker0','*') - jump to bookmark # 0
javascript:postMessage('Y-marker0=','*') - set up bookmark #0
16.02.22
[+] press Alt-Shift-0 twice to set starting Y-position for whole domain;
* 13-12-12 click on msg removes all marks
* 1.1 don't run in editable fields
*/
(function(){ "use strict";
if(top!=self || !document || !document.body ) return;
var W = unsafeWindow || window; // localStorage doesn't live in sandbox?
/* key kombinations to invoke the skript */
var kShift = 1, kCtrl = 2, kAlt = 4, kWin = 8;
var kJump = kAlt;
var kMark = kAlt+kShift;
/* sec; 0 to disable; -1 status bar only */
var tipShowtime = 2.2;
var kNobs = "-0123456789";
var kKeys = {
173: 0, 48: 1, 49: 2, 50: 3, 51: 4, 52: 5, 53: 6, 54: 7, 55: 8, 56: 9, 57: 10,
189: 0, /* chrome, opera caps lock */
109: 0 /* opera */
};
var minus1="-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1",
min1=minus1.split(',');
var LH=location.hostname || "Ym", LHP=LH+location.pathname;
function kNob(k){ return kNobs.charAt(k); }
var U=undefined, D=window.document;
var _L= function() {};
//_L=console.log.bind(console);
var statMsg;
function msg(t,k, y){
statMsg = "";
if(U!==k)
statMsg += "["+kNob(k)+"] ";
if(U!==y)
statMsg += ""+pt(y)+"% ";
statMsg += t;
}
var locStor,locStored;
// see SQlite manager -> %FFpath%\webappstore.sqlite -> find -> key -> contains Ym#
try {
locStor = W.localStorage;
locStored=locStor.getItem("Ym#"+location.pathname);
} catch(e){ locStor = locStored =null; };
function pt(y) {
try{
y =Math.round(y*1000/(document.body.scrollHeight+1))/10;
if(y>111.1) y=111.1;
}catch(e){console.log("math\n"+e); return -1}
return y;
}
var Ym;
var pz;
function ldYm(){
var s,t,y;
if(locStor && locStored){
pz=locStored.split(",");
svYm();
locStor.removeItem("Ym#"+location.pathname);
console.log('Ym: locS -> GM_S '+LHP);
locStor = locStored = null;
}
s=GM_getValue(Ym,null);
if(!s){ s=minus1;
}else {
try{ y=JSON.parse(s);
}catch(e){console.log('ldYm: buggy data\n'+e)};
s=y[LHP] || y[LH] || minus1;
}
pz=s.split(",");
if(y && (y=y['#HotKeys#'])){
kJump = +y[0], kMark = +y[1];
}
}
function svYm(d){
var y={},s=pz.join(",");
try{
y=JSON.parse(GM_getValue(Ym,'{}'));
}catch(e){console.log('svYm: buggy data\n'+e);}
if(!d) y[LHP]= s;
else if(d==='#'){
y['#HotKeys#']=[kJump,kMark];
}else{ // marker for whole domain
y[LH]= s;
}
GM_setValue(Ym,JSON.stringify(y));
}
function rmYm(){
var y={};
try{
y=JSON.parse(GM_getValue(Ym,'{}'));
}catch(e){console.log('rmYm: buggy data\n'+e); y={}; }
if(y[LH]) delete y[LH], _L("rmH: " +LH);
if(y[LHP]) delete y[LHP], _L("rmHP: " +LHP);
GM_setValue(Ym,JSON.stringify(y));
pz=min1;
}
function jumpY(k){
var y=pz[k];
if(y > -1) {
if( y!= scrollY ){
pz[0] = scrollY;
scroll(0,y);
msg("jump",k,y);
}else
msg("here",k,y);
return 1;
}
msg("none",k);
return 0;
}
function markY(k){
var d="";
if(k==1 && scrollY == pz[k] ) d="!";
pz[k]=scrollY;
if(k>=1)
svYm(d);
msg("mark"+d,k,pz[k]);
return 1;
}
var kMap= {16: kShift, 17:kCtrl, 18: kAlt, 91: kWin};
var keyMod=0;
var kkWin= (kMark | kJump) & kWin;
function klear(e) {
var k= e.keyCode;
if(!k) return;
var km=kMap[k];
if(km) {
keyMod &= -1 ^ km;
}
}
function onKeydown(e) {
try{
var rc=0, k= e.keyCode;
if(!k) return;
// Don't run in input, select, textarea etc.
var E = D.activeElement.tagName.toLowerCase();
if (E == "input" || E == "select" || E == "textarea" ||
(E=D.activeElement.contentEditable) == "true" ||
E == "")
{ keyMod = 0; return; }
var km =kMap[k]; // ff doesnt track metaKey. stsuko
if(km) {
keyMod |= km;
if( kkWin && km === kWin ) //???
e.preventDefault(), e.stopPropagation();
return;
}
km=keyMod;
if( U=== (k=kKeys[k]) ){ keyMod = 0; return; }
if( km === kJump ) {
rc = jumpY(k);
}else if( km === kMark ) {
rc = markY(k);
}else {
return;
}
statSay();
if(rc) e.preventDefault(),e.stopPropagation();
}catch(e){console.log('smth wromg\n'+e)};
}
function mk(p, t, id, s) {
var e = D.createElement(t);
e.id=id;
e.style.cssText = s;
return p.appendChild(e);
};
/* RIP status bar replacement */
var sb;
function _css(){
if(!sb) sb = mk(
//D.body,
D.documentElement, // 2014-06-30 ???!!1
'section', "Y-marker-userjs-inf",
"position: fixed!important;\
z-index: 214748!important;\
top: 0px; right: 1px; bottom: auto; left: auto;\
background: rgba(221,255,221,.75)!important;\
padding: 2px 3px 2px 8px; margin:0;\
border: none;\
border-radius: 12px 3px 3px 12px;\
color: #131!important;\
opacity: 1; display:none;\
font: normal 12px/14px sans-serif !important;\
text-shadow: #373 2px 2px 4px, #7F7 -2px -2px 4px;\
cursor:no-drop;\
"
);
/* click removes all */
if(tipShowtime>0){
sb.addEventListener("click",function (e) {
rmYm(); //locStor.removeItem(Ym);
ldYm(); noTout();
},false);
};
}
var tO;
function onTout(){
tO=0; sb.style.display="none";
}
function noTout(){
if(tO) clearTimeout(tO), onTout();
}
function statSay(t) {
if(statMsg){
_css();
if(tipShowtime)
W.status=statMsg;
if(tipShowtime>0){
noTout();
sb.innerHTML= statMsg;
sb.style.display="block";
tO=setTimeout(onTout, (t? t: tipShowtime)*1000);
}
}
statMsg="";
}
Ym="Ym#";//+location.pathname;
ldYm();
for(var k=1; k<2; k++){ /* pz.length */
if(pz[k]>-1){
scroll(0,pz[k]);
msg('jump',k,pz[k]);
statSay(5);
break;
}
};
function $i(id, parent){
return (parent || document).getElementById(id)
}
var hk;
function saveHk(){
function K(id, b)
{ return $i('Ym-bt'+id).checked? b: 0 }
kJump=K('JS',kShift)|K('JA',kAlt)|K('JW',kWin)|K('JC',kCtrl);
kMark=K('MS',kShift)|K('MA',kAlt)|K('MW',kWin)|K('MC',kCtrl);
svYm('#');
}
function setKeys(){
function J(j){return kJump&j? 'checked': ''}
function M(m){return kMark&m? 'checked': ''}
if(hk) hk.parentNode.removeChild(hk);
hk = D.createElement('section');
hk.style.cssText =
"position: fixed!important;\
z-index: 214748!important;\
top: 10px; left: 16px; bottom: auto; right: auto;\
background: rgb(221,255,221)!important;\
padding: 4px; margin:0;\
border: 1px solid #131;\
border-radius: 3px; \
color: #131!important;\
opacity: 1; display:block;\
font: normal 12px/14px sans-serif !important;\
";
hk.innerHTML=('\
<form style="padding:0;margin:0"><b>\
<center style="padding-bottom:.5em">Y-marker hotkeys</center></b>\
<b style="width:3em;display:inline-block;">Jump</b>: \
Shift<input type=checkbox id=Ym-btJS '+J(kShift)+'> \
Alt<input type=checkbox id=Ym-btJA '+J(kAlt)+'> \
Win<input type=checkbox id=Ym-btJW '+J(kWin)+'> \
Ctrl<input type=checkbox id=Ym-btJC '+J(kCtrl)+'> <br>\
<b style="width:3em;display:inline-block;">Mark</b>: \
Shift<input type=checkbox id=Ym-btMS '+M(kShift)+'> \
Alt<input type=checkbox id=Ym-btMA '+M(kAlt)+'> \
Win<input type=checkbox id=Ym-btMW '+M(kWin)+'> \
Ctrl<input type=checkbox id=Ym-btMC '+M(kCtrl)+'><hr><center>\
<input type="button" tabIndex=1 value="Save" id="Ym-btSave"> \
<input type="button" tabIndex=2 value="Cancel" id="Ym-btCancel">\
</center></form>\
');
hk.addEventListener("click",function(e){
var t= e.target,c=0;
if(!t || !t.id) return;
if(t.id=="Ym-btSave")try{
c=1;
saveHk();
}catch(e){console.log("Ym: problem in hotkey dialog\n"+e)}
else if(t.id=="Ym-btCancel"){
c=1;
}
if(c){
e.preventDefault(), e.stopPropagation();
hk.parentNode.removeChild(hk),hk=null;
}
},false);
D.documentElement.appendChild(hk);
}
function wMsg(e){
//event.source!=window in Chrome
if(typeof e.data==='string' && e.data.substr(0,8)==='Y-marker'){
e.stopPropagation();
if(e.data === 'Y-marker hotkeys')
return setKeys();
var m=e.data.match(/Y-marker\s*([0-9-])(\=?)/);
var p = (m[1]==='-')?0: +m[1]+1;
if(m[2])
markY(p);
else
jumpY(p);
statSay();
}
}
addEventListener("keydown",onKeydown,false);
addEventListener("keyup",klear,false);
GM_registerMenuCommand("Y-marker hotkeys", setKeys);
window.addEventListener("message", wMsg, false);
})();