Некоторые полезные функции для userjs

Сохранение информации

Для хранения используется localStorage, а если он недоступен, то кукисы.
Реализация вполне стандартна, только куки перебираются с конца, что для подобного применения быстрее и при установке проверяется их максимальная длина.
Функции аналогичны GM_getValue и GM_setValue.
var getValue = function(name){
if(window.localStorage){
return window.localStorage.getItem(name) || '';
}
else{
var eq = name+'=', ca = document.cookie.split(';');
for(var i = ca.length; i--;){
var c = ca[i];
while(c.charAt(0) == ' ')c = c.slice(1);
if(c.indexOf(eq) == 0)return unescape(c.slice(eq.length));
};
return '';
}
};
var setValue = function(name, value, del){
if(window.localStorage){
if(del){window.localStorage.removeItem(name)}else{window.localStorage.setItem(name, value)};
}
else{
if(document.cookie.split(';').length < 30 && document.cookie.length-escape(getValue(name)).length+escape(value).length < 4000){
var date = new Date();
date.setTime(date.getTime()+((del ? -1 : 10*365)*24*60*60*1000));
document.cookie = name+'='+escape(value)+'; expires='+date.toGMTString()+'; path=/';
}
else{
alert('Cookies is full!');
}
}
};

Работа со стилями

Первая функция создаёт элемент style из переданной строки, аналогично GM_addStyle. Единственная тонкость в ней, это проверка на HTMLHtmlElement, т.к. Opera работает и на wap-страницах.

Вторая функция возвращает массив селекторов полученных из переданной строки. Простой split(',') не подходит, т.к. запятая может встречаться, например в ссылках.
var addStyle = function(css){
if(document.documentElement instanceof HTMLHtmlElement){
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
s.setAttribute('style', 'display: none !important;');
s.appendChild(document.createTextNode(css));
return (document.getElementsByTagName('head')[0] || document.documentElement).appendChild(s);
}
};
var splitCss = function(css){
var rez = [];
css.replace(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/g, function(s, m){rez.push(m.replace(/^\s+|\s+$/g, ''))});
return rez;
};

Включение/выключение userjs кнопкой или букмарклетом

Обычно, для возможности взаимодействия с userjs им создаётся глобальная переменная/объект, которая при необходимости вызывается с помощью кнопки.
Недостатком такого способа, кроме некоторого засорения глобальной области видимости, является невозможность отключения userjs внутри ифреймов внедрённых в страницу с других доменов.
Использование сообщений, не имеющих таких ограничений, решает обе эти проблемы.
// кнопки
javascript:window.postMessage('ujs_something_disable', '*');
javascript:window.postMessage('ujs_something_enable', '*');

// userjs
var prefix = ujs_something;
var toggle = function(block){
var postMsg = function(msg){for(var i = 0, f = window.frames, l = f.length; i < l; i++)f[i].postMessage(msg, '*')};
if(arguments.length ? !block : getValue(prefix) != 'disabled'){
setValue(prefix, 'disabled');
postMsg(prefix + '_disable');
}
else{
setValue(prefix, '', true);
postMsg(prefix + '_enable');
}
};
window.addEventListener('message', function(e){
if(e.data == prefix + '_disable')toggle(false);
if(e.data == prefix + '_enable')toggle(true);
}, false);

Контекстное меню

Данная функция позволяет создавать контекстное меню в Opera 8.5-10.5. Для предотвращения показа собственного меню браузера, под курсором создаётся небольшая невидимая кнопка.

Требуется включение настройки "Allow script to recieve right clicks" в Tools→Preferences→Advanced→Content→Javascript options.
var createMenu = function(pos, items){
var prefix = 'ujs_menu';
var container = document.getElementById(prefix+'_menu');
if(container){
while(container.firstChild){container.removeChild(container.firstChild)};
}else{
container = document.createElement('div');
container.id = prefix+'_menu';
document.addEventListener('click', function(e){this.removeEventListener(e.type, arguments.callee, false); if(container)document.documentElement.removeChild(container)}, false);
document.documentElement.appendChild(container);
};
container.setAttribute('style', 'display:block;position:fixed;visibility:hidden;top:'+pos.y+'px;left:'+pos.x+'px;width:auto;height:auto;margin:0;padding:0;background-color:white;color:black;border:1px solid gray;z-index:1000;');
var button = document.createElement('input');
button.type = 'button';
button.setAttribute('style', 'position:fixed;top:'+(pos.y-2)+'px;left:'+(pos.x-2)+'px;width:5px;height:5px;margin:0;padding:0;z-index:1001;opacity:0;');
container.appendChild(button);
var menu = document.createElement('ul');
menu.setAttribute('style', 'overflow:hidden;margin:0;padding:2px;list-style:none;');
container.appendChild(menu);
for(var i = 0, item; item = items[i]; i++){
var list = document.createElement('li');
list.setAttribute('style', 'font-family:tahoma,sans-serif;font-size:11px;text-align:left;line-height:normal;white-space:nowrap;list-style-position:outside;cursor:default;'+(item[0] == '' ? 'margin:3px;padding:0;border-top:1px solid gray' : 'margin:0;padding:3px 9px;'));
list.appendChild(document.createTextNode(item[0]));
if(item[1]){
list.onclick = item[1];
list.onmouseover = function(){this.style.backgroundColor = '#316AC5'; this.style.color = 'white'};
list.onmouseout = function(){this.style.backgroundColor = 'white'; this.style.color = 'black'};
};
menu.appendChild(list);
};
container.style.visibility = 'visible';
return container;
};


// Пример использования
var d = document.createElement('div');
d['oncontextmenu' in d ? 'oncontextmenu' : 'onmousedown'] = function(ev){
if(ev.button == 2 && !ev.ctrlKey && !ev.shiftKey && !ev.altKey){
ev.preventDefault();
ev.stopPropagation();
createMenu(
{x: ev.clientX, y: ev.clientY},
[
['Done something', function(){}],
[''],
['End', function(){}]
]
)
}
};
d.setAttribute('style', 'width:100px;height:100px;background-color:green;');
document.documentElement.appendChild(d);

Перевод

Если ваш userjs выводит какие-то сообщения, полезно предусмотреть возможность их перевода. Вот один из вариантов реализации.
var getLng = function(){
switch(window.navigator.language){
case 'ru': return {
msg: 'Сообщение',
other: 'Другое'
};
default: return {
msg: 'Message',
other: 'Another'
}
}
};


// Вызываем в нужном месте
var lng = getLng();
alert(lng.msg);

Убираем ошибку при случайном запуске userjs в Windows

Данный код выведет осмысленное сообщение при попытке запуска userjs в Windows.
(function(){
if(typeof WScript == 'object' && WScript.Arguments){WScript.Echo('Please, copy this file to userjs folder!'); return};
...
})()

Передаём длинную строку внешней программе

Предварительно вам следует задать реакцию браузера на нужный MIME тип. Ctrl+F12→Загрузки→Добавить, указываете тип и ставите радиокнопку "Открыть в другой программе".
Теперь при вызове функции saveFile, указанная программа получит в командной строке путь к текстовому файлу содержащему уже раскодированную строку.

Кодирование с помощью encodeBase64, вообще говоря не является обязательным, но оно позволяет избежать нежелательного срабатывания встроенного блокера оперы, обрабатывающего в том числе и data: URI.
var encodeBase64 = function(string){var out='',charCode=0,i=0,endBytes='',length=string.length;var puffer=[];var base64EncodeChars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";while(charCode=string.charCodeAt(i++)){if(charCode<0x80){puffer[puffer.length]=charCode}else if(charCode<0x800){puffer[puffer.length]=0xc0|(charCode>>6);puffer[puffer.length]=0x80|(charCode&0x3f)}else if(charCode<0x10000){puffer[puffer.length]=0xe0|(charCode>>12);puffer[puffer.length]=0x80|((charCode>>6)&0x3f);puffer[puffer.length]=0x80|(charCode&0x3f)}else{puffer[puffer.length]=0xf0|(charCode>>18);puffer[puffer.length]=0x80|((charCode>>12)&0x3f);puffer[puffer.length]=0x80|((charCode>>6)&0x3f);puffer[puffer.length]=0x80|(charCode&0x3f)}if(i==length){while(puffer.length%3){puffer[puffer.length]=0;endBytes+='='}}if(puffer.length>2){out+=base64EncodeChars.charAt(puffer[0]>>2);out+=base64EncodeChars.charAt(((puffer.shift()&3)<<4)|(puffer[0]>>4));out+=base64EncodeChars.charAt(((puffer.shift()&0xf)<<2)|(puffer[0]>>6));out+=base64EncodeChars.charAt(puffer.shift()&0x3f)}}return(out+endBytes)};
var saveFile = function(txt){
if(txt.length>131071){alert('Error! String too long!');return};
var src='data:text/something;charset=UTF-8;base64,'+encodeBase64(txt);
if(top==self){
var f=document.createElement('iframe');
f.width=0;
f.height=0;
f.frameBorder='no';
f.scrolling='no';
document.documentElement.appendChild(f);
f.src=src;
setTimeout(function(){f.parentNode.removeChild(f)},1)
}else{
location.href=src
}
};

Работа с JSON без использования eval

JSON достаточно удобен в случае использования внешней программы, т.к. его парсеры существуют для большинства языков (кстати этот код можно использовать и в WSH).

Следует заметить, что Opera 10.5 уже имеет встроенную поддержку JSON-а.
var jsonParse = (function(){var j='(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)';var k='(?:[^\\0-\\x08\\x0a-\\x1f\"\\\\]'+'|\\\\(?:[\"/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';var l='(?:\"'+k+'*\")';var m=new RegExp('(?:false|true|null|[\\{\\}\\[\\]]'+'|'+j+'|'+l+')','g');var o=new RegExp('\\\\(?:([^u])|u(.{4}))','g');var p={'"':'"','/':'/','\\':'\\','b':'\b','f':'\f','n':'\n','r':'\r','t':'\t'};function unescapeOne(_,a,b){return a?p[a]:String.fromCharCode(parseInt(b,16))}var q=new String('');var r='\\';var s={'{':Object,'[':Array};var t=Object.hasOwnProperty;return function(a){var b=a.match(m);var c;var d=b[0];var e=false;if('{'===d){c={}}else if('['===d){c=[]}else{c=[];e=true}var f;var g=[c];for(var i=1-e,n=b.length;i<n;++i){d=b[i];var h;switch(d.charCodeAt(0)){default:h=g[0];h[f||h.length]=+(d);f=void 0;break;case 0x22:d=d.substring(1,d.length-1);if(d.indexOf(r)!==-1){d=d.replace(o,unescapeOne)}h=g[0];if(!f){if(h instanceof Array){f=h.length}else{f=d||q;break}}h[f]=d;f=void 0;break;case 0x5b:h=g[0];g.unshift(h[f||h.length]=[]);f=void 0;break;case 0x5d:g.shift();break;case 0x66:h=g[0];h[f||h.length]=false;f=void 0;break;case 0x6e:h=g[0];h[f||h.length]=null;f=void 0;break;case 0x74:h=g[0];h[f||h.length]=true;f=void 0;break;case 0x7b:h=g[0];g.unshift(h[f||h.length]={});f=void 0;break;case 0x7d:g.shift();break}}if(e){if(g.length!==1){throw new Error();}c=c[0]}else{if(g.length){throw new Error();}}return c}})();
var jsonStringify = (function(){var g=/[\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\"\\]/g;var h={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};var j=function(b){return'"'+b.replace(g,function(a){if(!h[a])h[a]='\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);return h[a]})+'"'};var l=function(d){function f(v){return v<10?'0'+v:v}return'"'+d.getUTCFullYear()+'-'+f(d.getUTCMonth()+1)+'-'+f(d.getUTCDate())+'T'+f(d.getUTCHours())+':'+f(d.getUTCMinutes())+':'+f(d.getUTCSeconds())+'Z"'};var m=function(a,b,e){return a.length===0?b+e:b+'\n'+a.join(',\n').replace(/^/gm,'\t')+'\n'+e};var n=function(a,b){var i,k,v,p=[],o=b[a],t=typeof o,c=Object.prototype.toString.call(o);if(c==='[object Date]')return l(o);if(t==='string'||c==='[object String]')return j(o);if(t==='boolean'||c==='[object Boolean]')return String(o);if(t==='number'||c==='[object Number]')return isFinite(o)?String(o):'null';if(t==='object'){if(!o)return'null';if(Object.prototype.toString.apply(o)==='[object Array]'){for(i=0;i<o.length;i++){p[i]=n(i,o)||'null'};return m(p,'[',']')};for(k in o){if(Object.hasOwnProperty.call(o,k)){v=n(k,o);if(v){p.push(j(k)+': '+v)}}};return m(p,'{','}')}};return function(a){return n('',{'':a})}})();

Создаём перетаскиваемое окошко

Перетаскиваемое окошко, работающее в Opera 8.5-10.5:
function createWindow(strContent, strStatus, strTitle, id, pos, size){
var wId = 'ujs_window';
if(id)wId += id;
var win = document.getElementById(wId);
if(win)win.parentNode.removeChild(win);
win = document.createElement('div');
win.setAttribute('style', 'position:fixed;display:block;visibility:hidden;left:0;top:0;width:auto;height:auto;background:-o-skin("Window Skin");border:1px solid gray;padding:3px;z-index:9999;overflow:visible;cursor:move;');
win.id = wId;
var img = document.createElement('img');
img.setAttribute('style', 'display:block;float:right;background:-o-skin("Caption Close Button Skin");width:18px;height:18px;padding:0;margin:0;border:none;cursor:pointer;');
img.src = 'data:image/gif;base64,R0lGODlhAQABAIABAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAICTAEAOw==';
img.title = (navigator.language == 'ru') ? '\u0417\u0430\u043A\u0440\u044B\u0442\u044C' : 'Close';
img.onclick = function(){this.parentNode.parentNode.removeChild(this.parentNode)};
win.appendChild(img);
var title = document.createElement('span');
title.setAttribute('style', 'display:inline;color:#000000;font:16px Times New Roman;width:auto;height:auto;padding:1px 2px;margin:0;');
title.innerHTML = strTitle || '';
win.appendChild(title);
var content = document.createElement('div');
content.setAttribute('style', 'display:block;border:1px solid #aaaaaa;margin:2px 0 0 0;padding:4px;background-color:#fafcfe;color:#000000;font:14px Times New Roman;width:240px;height:120px;overflow:auto;cursor:text;');
content.innerHTML = strContent || '';
win.appendChild(content);
var status = document.createElement('div');
status.setAttribute('style', 'display:block;color:#555555;font:10px Times New Roman;width:auto;height:auto;padding:0;margin:1px 2px;cursor:text;');
status.innerHTML = strStatus || '';
win.appendChild(status);
win.addEventListener('mousedown', function(e){
if(e.target == win){
e.preventDefault();
var grabX = e.clientX;
var grabY = e.clientY;
var origX = parseInt(win.style.left);
var origY = parseInt(win.style.top);
var dnd = function(e){
win.style.left = origX+e.clientX-grabX+'px';
win.style.top = origY+e.clientY-grabY+'px';
};
document.addEventListener('mousemove', dnd, false);
document.addEventListener('mouseup', function(){document.removeEventListener('mousemove', dnd, false)}, false);
}
}, false);
document.documentElement.appendChild(win);

if(size){
content.style.height = size.height;
content.style.width = size.width;
}
else{
for(var i = 3; i < 10; i++){
if(content.scrollHeight > content.offsetHeight || content.scrollWidth > content.offsetWidth){
content.style.height = 50*i+'px';
content.style.width = 100*i+'px';
}
else break;
}
};
var docEle = (document.compatMode == 'CSS1Compat' && window.postMessage) ? document.documentElement : document.body;
var mX = docEle.clientWidth-win.offsetWidth;
var mY = docEle.clientHeight-win.offsetHeight;
if(mX < 0){content.style.width = parseInt(content.style.width)+mX+'px'; mX = 0};
if(mY < 0){content.style.height = parseInt(content.style.height)+mY+'px'; mY =0};
win.style.left = (pos && pos.left < mX ? pos.left : mX)+'px';
win.style.top = (pos && pos.top < mY ? pos.top : mY)+'px';
win.style.visibility = 'visible';
document.addEventListener('keypress', function(e){
if(e.keyCode == 27){this.removeEventListener(e.type, arguments.callee, false); var t = document.getElementById(wId); if(t)t.parentNode.removeChild(t)};
}, false);
return win;
};

// Пример использования
// Сначала создадим объект с координатами
if(typeof navigator.lastClick != 'object')navigator.lastClick = {};
document.addEventListener('mouseup', function(e){
if(e && e.button == 0){
navigator.lastClick.left = e.clientX;
navigator.lastClick.top = e.clientY;
}
}, false);


// И после этого окошко
createWindow('Content', 'Status', 'Title', '_myId', navigator.lastClick);

// Другой пример. Открываем в этом окне ифрейм с результатами проверки текущей страницы DrWeb-ом.
javascript:void(createWindow('<iframe width=640 height=400 frameborder=0 src=http://online.drweb.com/result?url='+escape(location.href)+'>','','Dr-Web','',navigator.lastClick,{width: '644px', height: '404px'}))

Определение основных версий Opera

Объект opera может быть убран на некоторых сайтах из соображений совместимости, поэтому добавлена проверка на существование характерных функций.
var ver = (window.opera && window.opera.version) ? window.opera.version() : (window.postMessage ? 9.5 : (window.getSelection ? 9 : 8));

Получение выделенного текста включая textarea

В Opera 10.5 больше не поддерживается объект document.selection позволявший легко получить выделенный текст на странице и в textarea. Эта функция является некоторой альтернативой.
var getText=function(){var d=document,s=d.getSelection(),t=d.getElementsByTagName('textarea');if(!s)for(var i=0,e;e=t[i];i++){if(s=e.value.substring(e.selectionStart,e.selectionEnd))break}return s};
Комментарии


Вы должны войти, чтобы отправлять комментарии на этот сайт - пожалуйста, либо войдите, либо - если вы еще не зарегистрированы - щелкните здесь , чтобы зарегистрироваться
Этот сайт работает на e107, которая распространяется на условиях лицензии GNU GPL.
Бесплатный хостинг от EOMY.NET