My programs for Opera

Subscribe to RSS feed

Sticky post

Compatibility with Opera 10.5

oGet - new version 2.5.17 is working.
Qsaver - latest version 2.4 is working.
AdBlock - is working. Userjs version very recommended.
GetSize - version 1.3.11 - don't work, because ini-file variables is broken.
FlashBlocker - is working.
Copying text with formatting and images to clipboard - is working.
Thumbnails and sorting in opera:cache - outdated. Similar function added to Opera.
Block images from menu Opera - don't work.
Extended menu for Opera 10.6 - is working.

UserJS:
google-translate.js - version 1.7 is working.
bbcode.js - is working.
let-me-see-img.js - latest version is working.
open-in-background-with-long-press.js - is working in Opera 10.6.
block-external-scripts.js - new version is working.

Extensions
http://ruzanow.ru/opera/extensions/noads.oex
http://ruzanow.ru/opera/extensions/open-in-background-with-long-press.oex
http://ruzanow.ru/opera/extensions/let-me-see-img.oex
http://ruzanow.ru/opera/extensions/fix-long-title.oex
http://ruzanow.ru/opera/extensions/google-translate.oex

Fast base64 encoding in js. And test results.

function encodeBase64(str){
	var chr1, chr2, chr3, rez = '', arr = [], i = 0, j = 0, code = 0;
	var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split('');

	while(code = str.charCodeAt(j++)){
		if(code < 128){
			arr[arr.length] = code;
		}
		else if(code < 2048){
			arr[arr.length] = 192 | (code >> 6);
			arr[arr.length] = 128 | (code & 63);
		}
		else if(code < 65536){
			arr[arr.length] = 224 | (code >> 12);
			arr[arr.length] = 128 | ((code >> 6) & 63);
			arr[arr.length] = 128 | (code & 63);
		}
		else{
			arr[arr.length] = 240 | (code >> 18);
			arr[arr.length] = 128 | ((code >> 12) & 63);
			arr[arr.length] = 128 | ((code >> 6) & 63);
			arr[arr.length] = 128 | (code & 63);
		}
	};

	while(i < arr.length){
		chr1 = arr[i++];
		chr2 = arr[i++];
		chr3 = arr[i++];

		rez += chars[chr1 >> 2];
		rez += chars[((chr1 & 3) << 4) | (chr2 >> 4)];
		rez += chars[chr2 === undefined ? 64 : ((chr2 & 15) << 2) | (chr3 >> 6)];
		rez += chars[chr3 === undefined ? 64 : chr3 & 63];
	};
	return rez;
};


convertStringToBase64 - http://dev.opera.com/libraries/base64encoder/
Base64.encode - http://www.webtoolkit.info/javascript-base64.html


http://ruzanow.ru/test/test_base64.html on Core 2 Duo 2.66 GHz:
Chrome 9
convertStringToBase64: 19 ms
encodeBase64: 38 ms
Base64.encode: 75 ms
encodeBtoa: 13 ms

Firefox 4.0 beta 7
convertStringToBase64: 36 ms
encodeBase64: 18 ms
Base64.encode: 30 ms
encodeBtoa: 15 ms

Opera 11
convertStringToBase64: 71 ms
encodeBase64: 31 ms
Base64.encode: 61 ms
encodeBtoa: 31 ms

Firefox 3.6
convertStringToBase64: 190 ms
encodeBase64: 36 ms
Base64.encode: 60 ms
encodeBtoa: 27 ms

Chrome 2
convertStringToBase64: 366 ms
encodeBase64: 88 ms
Base64.encode: 161 ms
encodeBtoa: 80 ms

Opera 9.6
convertStringToBase64: 422 ms
encodeBase64: 188 ms
Base64.encode: 531 ms

IE8
convertStringToBase64: 468 ms
encodeBase64: 313 ms
Base64.encode: 875 ms

Opera 9.2
convertStringToBase64: 656 ms
encodeBase64: 516 ms
Base64.encode: 1109 ms

Opera 8.5
convertStringToBase64: 14219 ms
encodeBase64: 4250 ms
Base64.encode: 11672 ms

IE6 (first 30000 symbols only)
convertStringToBase64: 11781 ms
encodeBase64: 6235 ms
encodeBase64 with rez.join(''): 125 ms
Base64.encode: 922 ms


Resume.
encodeBase64 is fastest method in Opera.
window.btoa in Opera not fast and slightly bugged: javascript:alert(atob(btoa('ф'))) == 'D', instead of error.

Patch for double gzipped pages

Topic: http://my.opera.com/community/forums/topic.dml?id=803872
Patch: http://ruzanow.ru/opera/patch-gzip.zip (unpack opera.dll before, with «upx -d opera.dll»).

This patch just replaced «Accept-Encoding» header on more popular.
deflate, gzip, x-gzip, identity, *;q=0

on

gzip, deflate, identity, *;q=0


Of course, you use this patch at your own risk.

Some test pages:
http://www.romanianadventure.com/
http://www.weehoo.co.uk/
http://gaertnerblog.de/blog/2010/molch-bergmolch/

The patch is working on Opera 8 - 11.

Some userscripts converted to extensions

NoAds. (NoScript+AdBlock)/2

, ,

NoAds.js 1.1.3 final (58 Kb)
noads.oex - для Opera 11.
NoAds.js 1.0.3 для Opera 9.5-10.54

Скрипт представляет собой компиляцию adblock.js, block-external-scripts.js и abp_import.js и использует глобальное хранилище scriptStorage, появившееся в Opera 10.6, для сохранения настроек.

Возможности:
Блокирование скриптов с других доменов, которые в 99% случаев являются рекламой.
Блокирование элементов страницы, включая текстовую рекламу.
Импорт подписок AdBlock Plus EHH.
Блокирование встроенных в страницу скриптов.

Кнопка быстрого отключения/включения находится в правом нижнем углу страницы. Показывается при наведении.




Установка:
Скрипт устанавливается как обычно, но кроме этого нужно выставить настройку opera:config#PersistentStorage|UserJSStorageQuota >= 500

Кнопки:
Настройки NoAds
Блокировать рекламу
Блокировать элемент страницы
Разблокировать
Разблокировать последнее
Content block Helper

Горячие клавиши:
Настройки - Alt+Shift+P

Блокирование рекламы - Alt+Shift+A
Блокирование элемента страницы - Alt+Shift+B
Разблокирование - Alt+Shift+U
Разблокирование последнего элемента - Alt+Shift+L

(раз)Блокирование нескольких элементов - Shift+Click

Документация
Css-фильтры: http://adblockplus.org/ru/filters#elemhide

Блокирование скриптов:
@@== - Белый список блокировщика внешних скриптов. Обычные регулярки, с единственным отличием - точка после которой следует алфавитно-цифровой символ, считается не спецсимволом, а обычной точкой (с версии 1.1.3).
##$$ - Блокирование встроенных в страницу скриптов, в которых будет найдено совпадение. Используются аналогичные регулярки. Например «localhost##$$document.write» заблокирует на localhost-е все скрипты в которых упоминается «document.write»


Short description in English

UserJS: NoAds.js 1.1.3 final (58 Kb)
noads.oex - for Opera 11.

Functions:
Block external scripts, block elements of a page and import subscriptions from AdBlock Plus EHH.

Required options: opera:config#PersistentStorage|UserJSStorageQuota >= 500

"Quick button" (for enable/disable the blocking) is available in the right bottom corner of a page.

Buttons:
NoAds preferences
Block ads
Unblock
Content block Helper


Hotkeys:
Preferences - Alt+Shift+P

Block ads - Alt+Shift+A
Block element - Alt+Shift+B
Unblock - Alt+Shift+U
Unblock latest element - Alt+Shift+L

Block/unblock many elements - Shift+Click

Documentation
Css-filters: http://adblockplus.org/en/filters#elemhide

Scripts blocking:
@@== - White list for script blocking. Standard regexp, with one exception. A dot before any alphanumeric character, is not special character, but an ordinary dot.
##$$ - Inline-scripts blocking. Used the same regexp. Example: «localhost##$$document.write».

Changelog:
1.1
* Using scriptStorage instead of wsh.
* Many improvements.

1.1.1
* Improvements in noads.setRules
* Some compatibility with Opera 9.6

1.1.2
+ Content block Helper. You can easy block iframes and scripts with the internal Opera blocker.
* Selectors tested for validity, before import.
* More logical behavior when switching modes (block/unblock/preferences).

1.1.3
+ Blocking inline-scripts.

Todo
Improvements in noads.setRules
Block js-functions.
Block external images and flash.

For the extension
Disable/enable from menu
Helper mode for internal opera blocker (show scripts)

Known bugs of Opera 11
DSK-316102 (User JS not loaded when page loads too fast)
DSK-318727 (Extension blocks execution of userjs)


FAQ
Q: It doesn't work for me.
A: 1) Please, read the list of known bugs.
2) Set UserJSStorageQuota. It's really needed!
3) If it doesn't help, post the log from Error Console to the comments.

Q: Is this a replacement for the content blocker/urlfilter.ini?
A: Nope. This is an _addition_ to Opera's content blocker.

Q: How to update urlfilter.ini?
A: This is impossible with the current extension api. However, there is a good java application: http://my.opera.com/urlfilterdownloader/blog/

Q: Is it actually block ads or just hide it?
A: Opera doesn't load the hidden content (exclude iframes).

Q: There are many bugs in Opera 11 Alpha...
A: You can use userjs-version noads.js with the stable Opera 10.63.

Using jsonp in bookmarklets

, ,

Использование jsonp в букмарклетах/кнопках

Полезные ссылки

http://javascript.ru/unsorted/bookmarklet - Хорошее введение в предмет.
dean.edwards.name/packer/ - Пакует скрипты, убирая комментарии, пробелы, переносы строк, может укорачивать имена переменных.
Javascript unpacker and beautifier - Выполняет противоположную задачу, "распаковывая" сжатый код.
Javascript Buttonator - Создаёт кнопки для Opera, в том числе и из букмарклетов.

Но сначала о получении выделенного текста

Задача может показаться элементарной, да в сущности она и была таковой, пока в opera 10.5 не удалили объект document.selection. И теперь получить весь выделенный текст, включая input-ы и textarea, стало достаточно проблематично.
Соответствующее стандартам решение предполагает наличие на странице обработчика onFocus и в букмарклете малоприменимо.
Альтернативой могло бы быть использование document.activeElement, но в opera при вызове букмарклета фокус слетает на [object HTMLBodyElement] (хотя в остальных браузерах всё нормально), так что и этот вариант не подходит. Тоже самое относится и к хаку с "textarea:focus" отсюда.
Ну и последний оставшийся вариант, это перебор всех input-ов и textarea и поиск в них выделенного текста. Способ не идеальный, т.к. реальное выделение может оказаться и в другом input-е, но другой альтернативы похоже нет. Приведу код для этого варианта, с поддержкой фреймов:

var getSel = function (w) {
    var s, d = w.document;
    if (d.selection) {
        var r = d.selection.createRange();
        s = r ? r.text : ''
    } else {
        s = d.getSelection();
        if (!s) for (var i = 0, t = d.querySelectorAll('textarea,input'), e; e = t[ i ]; i++) {
            if (s = e.value.substring(e.selectionStart, e.selectionEnd)) break
        }
    };
    if (!s) for (var j = 0, f; f = w.frames[j]; j++) {
        try {
            if (s = arguments.callee(f)) break
        } catch(e) {}
    };
    return s
};

var str = getSel(window);

Кроссбраузерный вариант:

var getSel = function (w) {
    var s, d = w.document;
    // ie-шная модель удобнее, но поддерживается только в opera < 10.5
    if (d.selection) {
        var r = d.selection.createRange();
        // необходимая проверка, иначе в опере получим ошибку на frameset-ных страницах
        s = r ? r.text : ''
    } else {
        // window.getSelection() в опере багнута, теряя переносы строк, поэтому используем document.getSelection()
        // явное приведение к строке необходимо, потому что в webkit document.getSelection() возвращает объект, такой же как и window.getSelection()
        s = String(d.getSelection());
        // перебираем textarea и могущие иметь выделение input-ы и ищем в них выделенный текст.
        if (!s) for (var i = 0, t = d.querySelectorAll('textarea,input[type=text],input[type=search],input:not([type])'), e; e = t[ i ]; i++) {
            if (s = e.value.substring(e.selectionStart, e.selectionEnd)) break
        }
    };
    // рекурсивно вызываем себя во всех фреймах
    if (!s) for (var i = 0, f; f = w.frames[ i ]; i++) {
        try {
            if (s = arguments.callee(f)) break
        } catch(e) {}
    };
    return s
};

Собственно, получение информации с другого сайта

Делать кроссдоменные запросы, как таковые, в букмарклете нельзя, но ситуацию спасает то что многие сервисы поддерживают JSONP. Фактически это просто способ получить в ответ на GET-запрос, js-файл содержащий вызов функции с именем заданным в параметре callback и в которую передаётся JSON-объект. Нечто подобное:
showGoogleTranslate({responseStatus: "200"})
После загрузки этот js-файл вызовет предварительно созданную нами на странице функцию showGoogleTranslate и передаст ей объект содержащий нужную информацию. Как видим всё очень просто. И пара примеров для иллюстрации.

Получаем перевод выделенного текста (Text translation to Russian):

javascript: (function () {
	var getSel = function (w) {
		var s, d = w.document;
		if (d.selection) {
			var r = d.selection.createRange();
			s = r ? r.text : ''
		} else {
			s = d.getSelection();
			if (!s) for (var i = 0, t = d.querySelectorAll('textarea,input'), e; e = t[ i ]; i++) {
				if (s = e.value.substring(e.selectionStart, e.selectionEnd)) break
			}
		};
		if (!s) for (var j = 0, f; f = w.frames[j]; j++) {
			try {
				if (s = arguments.callee(f)) break
			} catch(e) {}
		};
		return s
	};
	var txt = encodeURIComponent(getSel(window));
	if (!txt || txt.length > 1900) {
		window.open('http://translate.google.com/translate?u=' + escape(location.href) + '&hl=ru&langpair=auto|ru&tbb=1&ie=' + document.characterSet)
	} else {
		var ele = document.documentElement.appendChild(document.createElement('script'));
		window.showGoogleTranslate = function (o) {
			alert(o.responseStatus == 200 ? o.responseData.translatedText : o.responseStatus + ': ' + o.responseDetails);
			ele.parentNode.removeChild(ele);
			delete window.showGoogleTranslate
		};
		ele.src = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=' + txt + '&langpair=|ru&callback=showGoogleTranslate&format=text'
	}
})()

Другой пример, с комментариями. Получаем укороченную ссылку на текущую страницу (Create a short link with bit.ly):

javascript: (function () {
	// генерим уникальное имя функции, которая будет вызываться из скрипта и создаём сам скрипт
	var callback = ('js'+Math.random()).replace('.', 'n'), ele = document.documentElement.appendChild(document.createElement('script'));
	// создаём функцию с уникальным именем
	window[callback] = function (o) {
		// обрабатываем полученный json
		prompt('Укороченная ссылка:', o.status_code == 200 ? o.data.url : o.status_code + ': ' + o.status_txt);
		// убираем скрипт и функцию
		ele.parentNode.removeChild(ele);
		delete window[callback];
	};
	// в конце задаём адрес скрипта, что вызывает его загрузку; такой вариант нормально работает и в старых версиях оперы
	ele.src = 'http://api.bit.ly/v3/shorten?login=operafanuser&apiKey=R_3519a5e5f78017f55544b0b2053d4982&longUrl=' + encodeURIComponent(location.href) + '&format=json&callback=' + callback;

})()

И ещё один пример. Для использования в меню. Получаем размер файла находящегося на другом домене (Get file size):

javascript: (function () {
    var formatSize = function (h) {
        if (!h || isNaN(h = parseInt(h))) return 'неизвестно';
        var add = ' (' + h + ' byte)',
            kb = 1024,
            mb = kb * kb,
            gb = mb * kb;
        if (h < kb) return h + ' B';
        if (h < mb) return (h / kb).toFixed(1) + ' KiB' + add;
        if (h < gb) return (h / mb).toFixed(1) + ' MiB' + add;
        return (h / gb).toFixed(1) + ' GiB' + add
    };
    var formatDate = function (h) {
        return h ? new Date(h).toLocaleString() : 'неизвестно'
    };
    var responseTime = function (s) {
        return Math.round((new Date() - s) / 100) / 10 + ' сек'
    };
    var reqTimeout, url = %%22%l%%22;
    if (!/^https?:/i.test(url)) {
        alert('Протокол не поддерживается.');
        return
    };
    var req = new XMLHttpRequest();
    req.open('HEAD', url + '?', true);
    req.onreadystatechange = function () {
        if (this.readyState == 4) {
            clearTimeout(reqTimeout);
            alert('Файл: ' + url + '\n\nВремя отклика: ' + responseTime(start) + '\n\nРазмер: ' + formatSize(this.getResponseHeader('Content-Length')) + '\nДата: ' + formatDate(this.getResponseHeader('Last-Modified')) + '\n\nЗаголовки:\nStatus: ' + this.status + ' ' + this.statusText + '\n' + this.getAllResponseHeaders())
        }
    };
    var start = new Date();
    try {
        req.send(null);
        reqTimeout = setTimeout(function () {
            req.abort()
        },
        10000)
    } catch(e) {
        var callback = ('js' + Math.random()).replace('.', 'n'),
            ele = document.documentElement.appendChild(document.createElement('script'));
        window[callback] = function (o) {
            alert(o.ok && o.headers ? 'Файл: ' + url + '\n\nВремя отклика: ' + responseTime(start) + '\n\nРазмер: ' + formatSize(o.headers['content-length']) + '\nДата: ' + formatDate(o.headers['last-modified']) + '\n\nStatus: ' + o['status_code'] + (o.headers['server'] ? '\nServer: ' + o.headers['server'] : '') + (o.headers['content-type'] ? '\nContent-Type: ' + o.headers['content-type'] : '') + (o.headers['cache-control'] ? '\nCache-Control: ' + o.headers['cache-control'] : '') : 'Информация недоступна.');
            ele.parentNode.removeChild(ele);
            delete window[callback];
        };
        ele.src = 'http://json-head.appspot.com/?url=' + encodeURIComponent(url) + '&callback=' + callback;
    }
})()

Descriptions in opera:plugins and more

, ,

Unfortunately, Opera does not show the versions of Flash-plugins. This addition corrects it.
Replace in yours *.lng file, string «-1303632626="Plug-ins"» on
-1303632626="Plug-ins<script type='text/javascript'>document.title=document.title.replace(/<.*>/,'');document.addEventListener('load',function(){var p=navigator.plugins;var t=document.getElementsByTagName('tbody');for(var i=0, ti; ti=t[i]; i++){ti.innerHTML='<TR><TD>Description:</TD><TD>'+(p[i].description?p[i].description:'Plug-in disabled')+'</TD></TR>'+ti.innerHTML}},false)</script>"

And go to opera:plugins wink .


GetSize (16Kb). Installer (130Kb) for Opera.
GetSize is an small and open source tool that allows you to find out the size of a file before downloading it. GetSize is similar GetFileSize but has some additional possibilities (support referrer, show size of files bigger 4 Gb) and very simple interface smile

Last version of Qsaver (50Kb) supported fast saving of images via long click, double click and mouse wheel click.

Перевод настроек opera:config

Скачать ru.lng c переводом (100Kb) для Opera 9.6

Кроме собственно перевода, который выглядит примерно так:

туда добавлены следующие возможности:
  • В opera:cache добавлены функции сортировки по размеру и имени файла в кэше (оно пропорционально дате), предпросмотр для картинок и флешек и быстрый поиск.
  • Возможность добавлять кнопки из адресной строки. Вставив, например, «opera:/button/Go,,,"Пуск","Open Link"».
  • Ссылки в меню Справка->О программе сделаны "кликабельными".
  • На страницу показываемую при недоступности сервера, добавлен пункт «попробуйте найти эту страницу в кэше Google или на Web Archive».

Установка сводится к замене файла Opera\locale\ru\ru.lng.
__________________________________________________________

Дополнения для языковых файлов Opera 8-10. В частности, перевод opera:config и сортировка и предпросмотр в кэше.

Thumbnails in opera cache

Replace in yours *.lng file, string «-1713924769="Size"» on (press Ctrl+F11 before copying)
-1713924769="Size<script type='text/javascript'>document.addEventListener('load',function(){for(var i=0;document.links[i];i++){document.links[i].target='_blank'};var tr=document.getElementsByTagName('tr');var sf=document.createElement('input');sf.type='text';sf.defaultValue='Search';sf.style='margin:0 4px;float:right;';sf.addEventListener('focus',function(){if(this.value==this.defaultValue){this.value=''}},false);sf.addEventListener('blur',function(){if(!this.value){this.value=this.defaultValue}},false);sf.addEventListener('keyup',function(){var v=this.value.toLowerCase();for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild.innerText;if(p&&p.toLowerCase().indexOf(v)==-1){l.style='display: none'}else{l.style='display: table-row'}}},false);var bt=document.createElement('input');bt.type='button';bt.value='Thumbnails';bt.style='float:right;';bt.addEventListener('click',function(){var b=/\.(jpe?g|bmp|gif|png|ico|swf|flv)$/i;var f=/\.(swf|flv)$/i;for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild;var c=l.firstChild.innerText;if(l.offsetHeight!=0&&p.childNodes.length<2){if(b.test(c)){var stl='float:right;border:1px dotted black;margin:1px;padding:1px;max-height:100px;max-width:100px;';if(f.test(c)){var dv=document.createElement('div');p.appendChild(dv);dv.innerHTML='<EMBED src='+p.href+'>';var fh=dv.firstChild;fh.setAttribute('height',100);fh.setAttribute('width',100);fh.setAttribute('style',stl+'content:normal;display:inherit;outline:none;background-image:none;')}else{var image=document.createElement('img');image.setAttribute('src',p.href);image.setAttribute('style',stl);p.appendChild(image)}}}}},false);var th=document.getElementsByTagName('th')[2];th.appendChild(bt);th.appendChild(sf)},false)</script>"


Update
Added "advanced search".

Update
Added sorting by name (~date) and size.
-1713924769="Size<script type='text/javascript'>function sorter(t){var table=document.getElementsByTagName('table')[0];var node;var col=t.cellIndex;var span=t.getElementsByTagName('span')[0];var a=new Array();var tbody=table.getElementsByTagName('tbody')[0];var tr=tbody.getElementsByTagName('tr');for(var i=0;(node=tr[i+1]);i++){a[i]=new Array();a[i][0]=getConcatenedTextContent(node.getElementsByTagName('td')[col]);a[i][1]=node}a.sort(_sort);var dtitle=span.getAttribute('title');if(dtitle==0){a.reverse()}dtitle=dtitle==0?1:0;span.innerHTML=dtitle==0?'↓':'↑';span.setAttribute('title',dtitle);for(var i=0;i<a.length;i++){tbody.appendChild(a[i][1])}function _sort(a,b){var a=a[0];var b=b[0];if(Number(a)&&Number(b)){return sort_numbers(a,b)}else{return sort_sensitive(a,b)}};function sort_numbers(a,b){return a-b};function sort_sensitive(a,b){if(a<b)return-1;if(a>b)return 1;return 0};function getConcatenedTextContent(node){var _result='';if(node==null){return _result}var childrens=node.childNodes;var i=0;while(i<childrens.length){var child=childrens[i];switch(child.nodeType){case 1:case 5:_result+=getConcatenedTextContent(child);break;case 3:case 2:case 4:_result+=child.nodeValue;break;case 6:case 7:case 8:case 9:case 10:case 11:case 12:break}i++}return _result}};document.addEventListener('load',function(){for(var i=0;document.links[i];i++){document.links[i].target='_blank'};var tr=document.getElementsByTagName('tr');var sf=document.createElement('input');sf.type='text';sf.defaultValue='Search';sf.style='font-size:100%;margin:0 20px 0 30px;width:40%;';sf.addEventListener('focus',function(){if(this.value==this.defaultValue){this.value=''}},false);sf.addEventListener('blur',function(){if(!this.value){this.value=this.defaultValue}},false);sf.addEventListener('keyup',function(){var v=this.value.toLowerCase();for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild.innerText;if(p&&p.toLowerCase().indexOf(v)==-1){l.style='display: none'}else{l.style='display:table-row'}}},false);var bt=document.createElement('input');bt.type='button';bt.value='Thumbnails';bt.style='font-size:100%;cursor:pointer;float:right;';bt.addEventListener('click',function(){var b=/\.(jpe?g|bmp|gif|png|ico|swf|flv)$/i;var f=/\.(swf|flv)$/i;for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild;var c=l.lastChild.firstChild.innerText;if(l.offsetHeight!=0&&p.childNodes.length<2){if(b.test(c)){var stl='float:right;border:1px dotted black;margin:1px;padding:1px;max-height:100px;max-width:100px;';if(f.test(c)){var dv=document.createElement('div');p.appendChild(dv);dv.innerHTML='<EMBED src='+p.href+'>';var fh=dv.firstChild;fh.setAttribute('height',100);fh.setAttribute('width',100);fh.setAttribute('style',stl+'content:normal;display:inherit;outline:none;background-image:none;')}else{var image=document.createElement('img');image.setAttribute('src',p.href);image.setAttribute('style',stl);p.appendChild(image)}}}}},false);var th=document.getElementsByTagName('th');th[2].appendChild(bt);th[2].appendChild(sf);var addSort=function(ele){var span=document.createElement('span');span.title='0';span.appendChild(document.createTextNode('•'));ele.insertBefore(span,ele.firstChild);ele.onclick="sorter(this);";ele.style.cursor='pointer';};addSort(th[0]);addSort(th[1]);},false)</script>"


Update
Speed-up version for Opera 9.51 and later. Sorting works more fast (~5x).
-1713924769="Size<script type='text/javascript'>function sorter(span){var _sort=function(a,b){var a=a[1];var b=b[1];if(Number(a)&&Number(b)){return a-b}else{if(a<b)return-1;if(a>b)return 1;return 0}};var dir=span.innerHTML.charAt(0);var col=span.parentNode.cellIndex;var tbody=document.selectSingleNode('//tbody');var tr=tbody.selectNodes('tr[td]');var len=tr.length;var a=new Array(len);for(var i=0;i<len;i++){a[i]=new Array(2);a[i][0]=tr[i];a[i][1]=a[i][0].selectNodes('td')[col].innerText};a.sort(_sort);if(dir!='↓'&&dir!='↑')dir=arguments[1]?'↓':'↑';if(dir=='↑'){a.reverse()};span.innerHTML=(dir=='↑'?'↓':'↑')+span.innerHTML.substr(1);for(var i=0;i<len;i++){tbody.appendChild(a[i][0])}};document.addEventListener('load',function(){for(var i=0;document.links[i];i++){document.links[i].target='_blank'};var tr=document.selectNodes('//tr[td]');if(location.search){var ls=location.search.substring(1);for(var i=0,ti;ti=tr[i];i++){var tt=ti.lastChild.firstChild.innerText;if(tt&&tt.indexOf(ls)==-1){ti.parentNode.removeChild(ti)}}};var sf=document.createElement('input');sf.type='text';sf.defaultValue='Search';sf.style='font-size:100%;margin:0 20px 0 30px;width:40%;';sf.addEventListener('focus',function(){if(this.value==this.defaultValue){this.value=''}},false);sf.addEventListener('blur',function(){if(!this.value){this.value=this.defaultValue}},false);sf.addEventListener('keyup',function(){var v=this.value.toLowerCase();for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild.innerText;if(p&&p.toLowerCase().indexOf(v)==-1){l.style='display: none'}else{l.style='display:table-row'}}},false);var bt=document.createElement('input');bt.type='button';bt.value='Thumbnails';bt.style='font-size:100%;cursor:pointer;float:right;';bt.addEventListener('click',function(){var b=/\\.(jpe?g|bmp|gif|png|ico|swf|flv)$/i;var f=/\\.(swf|flv)$/i;var stl='float:right;border:1px dotted black;margin:1px;padding:1px;max-height:100px;max-width:100px;';for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild;var c=l.lastChild.firstChild.innerText;if(l.offsetHeight!=0&&p.childNodes.length<2){if(b.test(c)){if(f.test(c)){var dv=document.createElement('div');p.appendChild(dv);dv.innerHTML='<EMBED src='+p.href+'>';var fh=dv.firstChild;fh.setAttribute('height',100);fh.setAttribute('width',100);fh.setAttribute('style',stl+'content:normal;display:inherit;outline:none;background-image:none;')}else{var image=document.createElement('img');image.setAttribute('src',p.href);image.setAttribute('style',stl);p.appendChild(image)}}}}},false);var th=document.selectNodes('//th');th[2].appendChild(bt);th[2].appendChild(sf);var addSort=function(ele){var arg=arguments[1];var txt=ele.firstChild;var span=document.createElement('span');span.title='Sort';span.appendChild(document.createTextNode('•'));ele.insertBefore(span,txt);span.addEventListener('click',function(){sorter(this,arg)},false);span.style.cursor='pointer';span.appendChild(txt)};addSort(th[0]);addSort(th[1]);addSort(th[2],true)},false)</script>"


english.lng for Opera 9.5x.

Update
Version for Opera 10 with a built-in correction for the new ugly style.
-1713924769="Size:   <script type='text/javascript'>function sorter(span){var _sort=function(a,b){var a=a[1];var b=b[1];if(Number(a)&&Number(b)){return a-b}else{if(a<b)return-1;if(a>b)return 1;return 0}};var dir=span.innerHTML.charAt(0);var col=span.parentNode.cellIndex;var tbody=document.selectSingleNode('//tbody');var tr=tbody.selectNodes('tr[td]');var len=tr.length;var a=new Array(len);for(var i=0;i<len;i++){a[i]=new Array(2);a[i][0]=tr[i];a[i][1]=a[i][0].selectNodes('td')[col].innerText};a.sort(_sort);if(dir!='↓'&&dir!='↑')dir=arguments[1]?'↓':'↑';if(dir=='↑'){a.reverse()};span.innerHTML=(dir=='↑'?'↓':'↑')+span.innerHTML.substr(1);for(var i=0;i<len;i++){tbody.appendChild(a[i][0])}};if(location.search)document.documentElement.style.display='none';var style=document.createElement('style');style.setAttribute('type','text/css');style.appendChild(document.createTextNode('td{border-width:1px 0;border-style:solid;border-color:#DEDEDE #F5F5F5 #F5F5F5;}td:first-child,td:first-child+td{border-width: 1px 1px 1px 0;border-color:#DEDEDE #DEDEDE #F5F5F5 #F5F5F5;}th{width:auto;}td+td+td a{-o-text-overflow:ellipsis;overflow:hidden;display:block;}'));document.getElementsByTagName('head')[0].appendChild(style);document.addEventListener('load',function(){for(var i=0;document.links[i];i++){document.links[i].target='_blank'};var tr=document.selectNodes('//tr[td]');if(location.search){var ls=location.search.substring(1);if(ls)for(var i=tr.length-1,ti;ti=tr[i];i--){var tt=ti.lastChild.firstChild.innerText;if(tt&&tt.indexOf(ls)==-1){ti.parentNode.removeChild(ti)}};document.documentElement.style.display='block'};var sf=document.createElement('input');sf.type='text';sf.defaultValue='Filter';sf.style='font-size:100%;margin:0 20px 0 30px;width:40%;';sf.addEventListener('focus',function(){if(this.value==this.defaultValue){this.value=''}},false);sf.addEventListener('blur',function(){if(!this.value){this.value=this.defaultValue}},false);sf.addEventListener('keyup',function(){var v=this.value.toLowerCase();for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild.innerText;if(p&&p.toLowerCase().indexOf(v)==-1){l.style='display: none'}else{l.style='display:table-row'}}},false);var bt=document.createElement('input');bt.type='button';bt.value='Thumbnails';bt.style='font-size:100%;cursor:pointer;float:right;';bt.addEventListener('click',function(){var b=/\\.(jpe?g|bmp|gif|png|ico|swf|flv)$/i;var f=/\\.(swf|flv)$/i;var stl='float:right;border:1px dotted black;margin:1px;padding:1px;max-height:100px;max-width:100px;';for(var i=0,l;l=tr[i];i++){var p=l.lastChild.firstChild;var c=l.lastChild.firstChild.innerText;if(l.offsetHeight!=0&&p.childNodes.length<2){if(b.test(c)){if(f.test(c)){var dv=document.createElement('div');p.appendChild(dv);dv.innerHTML='<EMBED src='+p.href+'>';var fh=dv.firstChild;fh.setAttribute('height',100);fh.setAttribute('width',100);fh.setAttribute('style',stl+'content:normal;display:inherit;outline:none;background-image:none;')}else{var image=document.createElement('img');image.setAttribute('src',p.href);image.setAttribute('style',stl);p.appendChild(image)}}}}},false);var th=document.selectNodes('//th');th[2].appendChild(bt);th[2].appendChild(sf);var addSort=function(ele){var arg=arguments[1];var txt=ele.firstChild;var span=document.createElement('span');span.title='Sort';span.appendChild(document.createTextNode('•'));ele.insertBefore(span,txt);span.addEventListener('click',function(){sorter(this,arg);for(var i=0;i<th.length;i++){var s=th[i].firstChild;if(s!=this)s.innerHTML='•'+s.innerHTML.substr(1)}},false);span.style.cursor='pointer';span.appendChild(txt)};addSort(th[0]);addSort(th[1]);addSort(th[2],true);if(location.search)sorter(th[0].firstChild)},false)</script>"


По русски здесь.
ru.lng для 9.5x.

Copying text with formatting to clipboard and Autocopy.js

,

I forgot to mention about it before smile
Program for copying text with links and images to clipboard, from menu of Opera. Also included autocopy.js: htm2clip.zip (Installer).

Thread.

In Opera 10 you can use button Edit mode and simple copy/paste.
Or just button: Copy with formatting (drag'n'drop this button directly on panel).