htmlZclip: Возвращение копирования текста с форматированием в Оперу
Ранее я на нашем форуме писал про расширение (htm2clip) для Оперы, позволяющее копировать фрагмент страницы с сохранением его оформления. С того момента я почти им не пользовался, пока недавно мне не понадобилось быстро скопировать кусок сайта, сохранив его форматирование...
И тогда обнаружил, что оно не работает с новой версией Оперы T_T
Зайдя на страничку автора (A.Ruzanov - Lex1), и прочитав комментарии к htm2clip, понял, что новую версию ждать придется долго (похоже автор ее забросил).
Так и родился htmlZclip.
Основные его преимущества перед htm2clip:
- работает на новых версиях Оперы
- делает это в 1,7 раз быстрее
Мне от htm2clip нужна была только функция копирования (вставкой не пользовался), поэтому htmlZclip может только копировать (форматированной вставки нет).
Установка
Ставится он практически также, как и htm2clip. Нужно скопировать 2 файла, и прописать в браузере MIME тип.
- скачиваем
- копируем из архива файл 'copy\htmlZclip.exe' в
'<путь к папке с установленной оперой>\program\'
(обычно 'htmlZclip.exe' нужно копировать в
'C:\Program Files (x86)\Opera\program\' – для 64bit систем, либо,
если такого пути нет, то в 'C:\Program Files\Opera\program\' – для 32bit) - теперь выделим следующую строку:
opera:config#UserPrefs|MenuConfiguration
нажмем Ctrl+C затем Ctrl+Shift+V
и получим путь (в первой строчке) к месту, куда надо скопировать из архива файл 'copy\standard_menu.ini'
обычно 'standard_menu.ini' нужно копировать в
'C:\Users\<имя пользователя>\AppData\Roaming\Opera\Opera\menu\' предварительно создав отсутствующую папку menu - нажмем Ctrl+F12 , откроем вкладку 'Расширенные', зайдем в 'Загрузки' (слева), нажмем кнопку 'Добавить...',
в появившемся окне указываем в поле MIME-тип text/htmlZclip
выбираем Действие Открыть в другой программе:, и вписываем в поле строку program\htmlZclip.exe
далее жмем кнопку OK, и снова OK
Всё, теперь нужно перезагрузить Оперу, и можно копировать текст с форматированием (в контекстном меню выделенного текста появится пункт 'Copy with format').
На этом обычный пользователь может пойти и заняться своими делами, но, если вы себя таковым не считаете, и излишняя подробность в мелочах и недосказанность в сути происходящего приведенной выше инструкции вам не по нутру, то смело изучайте статью до конца.
Допиливаем установку
Я не буду здесь говорить, что копируя мой 'standard_menu.ini' вы могли затереть свою менюшку, и что лучше вместо этого вставить новый элемент в свое меню. Раз вы это читаете, вам это должно быть известно.
В этом разделе я бы хотел сказать, например, то, что кидая к себе 'htmlZclip.exe' и прописывая к нему MIME, вы проделываете в своем браузере приличную дырку отбойным молотком. Если оставить все как есть, то браузер, встретив на любом сайте объект с MIME text/htmlZclip, сразу отошлет его в 'htmlZclip.exe', а тот, в свою очередь, поместит его в буфер обмена...
Чтоб этого не случилось, достаточно заменить MIME на какую-нибудь ерунду, например text/trali-vali;eli;sandali. MIME следует заменить в браузере и в скрипте (он прописан в начале скрипта, можно найти поиском; ваш К.О.) в 'standard_menu.ini'.
Можно прописать клавиатурное сокращение в секцию Application своего 'standard_keyboard.ini', например для сочетания Ctrl+Alt+C:
...
[Application]
...
c ctrl alt="Go to page, "javascript:(function(){...
...
Либо прописать то же самое в GUI: Расширенные настройки -> Управление. Только не забудьте удалить внешние кавычки.
Исходники
В директории 'src\' архива находятся все исходники. Исходники содержат шапку, в которой указана их версия
(формат - 'htmlZclip v<версия оперы>.<номер версии htmlZclip для данной версии оперы>'), а также даны все необходимые ссылки:
Вначале стоит посмотреть, что именно заносится в буфер обмена при копировании, например в IE:
Затем лучше почитать описание на MSDN точное описание формата заголовка HTML Clipboard Format (CF_HTML).
Далее можно посмотреть несколько реализаций копирования в буфер обмена CF_HTML:
- C++ Как добавить HTML-код в буфер обмена с помощью Visual C++ (официально, от MS)
- .NET C# Copying HTML on the clipboard
- .NET C# Sample code for copying Html to Clipboard
И в конце следует взглянуть на некоторые WinAPI для работы с буфером обмена (там особенно ценны комментарии):
htmlZclp.js
Этот код, после упаковки в YUI Compressor переносится в 'standard_menu.ini'.
Все основные действия происходят в нем, и, в отличии от htm2clip, здесь же, на JavaScript, формируется полный заголовок для CF_HTML. Что касается всего остального, то смотрите исходники, понять их несложно.
encodeBase64full
В исходнике спрятано (закомментировано) пасхальное яйцо - самая быстрая реализация кодирования в Base64 на JavaScript в Опере. Вы можете на досуге сами сравнить мою реализацию с предыдущей самой быстрой реализацией, которая, кстати от того же автора (A.Ruzanov).
htmlZclip.exe
Здесь делается как раз то, что нельзя сделать в браузере на JavaScript, - скопировать в буфер обмена с указанием формата CF_HTML. Вот и все, больше ничего оно не делает, так как формирование заголовка CF_HTML было перенесено на JavaScript.
Оно написано на C, и в собранном виде занимает 3 072 байа, в отличии от htm2clip - Delphi и 14 336 байт.
В архиве находится целиком весь проект, созданный в Visual Studio 2010 с использованием Windows SDK 7.1 (задается в свойствах проекта - 'Platform Toolset').
Если загляните в файл 'htmlZclip.c', то в середине функции wWMain увидите забавную конструкцию, начинающеюся с '(hText ='. Предлагаю самостоятельно разобраться с тем, как она работает, и лишь скажу, что это очень удобный способ проверки отработки WinAPI функций.
Надо только помолиться перед компиляцией: некоторые компиляторы при оптимизации кода могут переставить вызовы функций так, что они будут вызываться не в нужном нам порядке (!!!), и программа работать не будет!
Если же вы считаете себя параноиком, то смело переписывайте нормально этот участок кода, дополнив его недостающими проверками, дополнительными циклами и сообщениями пользователю, что проге стало плохо. Ах да, чуть не забыл, еще можете для успокоения души убрать _GS_CHECK_OFF.
Как это работает
После формирования CF_HTML (данных согласно этому формату), он передается в виде DataURL в качестве src новому iframe.
Далее браузер, видя появление нового объекта, по MIME определяет что ему делать. В нашем случае браузер создает в директории для временных файлов новый файл и записывает в него наш CF_HTML. Затем путь к временному файлу передается в 'htmlZclip.exe' в качестве опции запуска.
И, в конце, 'htmlZclip.exe' переписывает содержимое временного файла в буфер обмена с указанием формата 'HTML Format'.
За кадром
Google & сжатие JS
JavaScript я пробовал сжимать разными оптимизаторами/компрессорами, в этом помог compressorrater.thruhere.net, но особенно мне запомнился Google Closure Compiler. Попробуем в нем 'сжать' следующий код в режиме Simple или Advanced:
alert(function(){var z="мая любить копировать ::-)";return z[1]+z[1]+z[3]+z[4]+z[6]+z[10]+z[1]+z[8]+z[2]+z[5]})
И вот что получим :lol: :
Original Size: 130 bytes (121 bytes gzipped) Compiled Size: 1.27KB (135 bytes gzipped) Saved -898.46% off the original size
Пути JIT-компиляции неисповедимы
Вначале я не планировал переносить формирование заголовка CF_HTML на JavaScript, боясь увеличить время выполнения. Это коренным образом изменилось после того, как сравнил скорость работы готового 'htmlZclip.js' с htm2clip, и получил, что JavaScript htmlZclip'а работает в 1,6 раз быстрее, чем JavaScript htm2clip'а.
Решив, что изменение кода для формирования заголовка CF_HTML не сделает htmlZclip медленнее, чем htm2clip, стал менять код. Каково же было мое удивление, когда новый 'htmlZclip.js' стал работать на 8% быстрее, чем старый 8-O. Видать, не зря, когда его модифицировал, постоянно думал о производительности.
Подпись файла размером с сам файл
Обычно в релизы своих проектов я добавляю цифровую подпись.
В этот раз мне этого делать не захотелось. Посудите сами, файл 'htmlZclip.exe' размером 3 КБ, после подписывания со штампом времени стал весить 8 КБ.
Из-за чего htm2clip перестал работать
Чтоб лучше это понять, вначале нужно прочитать эту статью: Исследуем скорость выполнения JS и алгоритм отображения страниц, а также этот комментарий.
Часто ради оптимизации пользуются определенными особенностями, пологая, что эти особенности далее останутся неизменными. Вот наглядный пример такой особенности: посмотрим на конец JavaScript'а оригинального htm2clip:
doc.documentElement.appendChild(f);f.parentNode.removeChild(f)}})();
Здесь видно, что добавленный только что объект сразу удаляется. В старой Опере выполнение данной конструкции было реализовано не очень эффективно и приводило к перерисовке. В новой версии Оперы убрали этот недостаток.
Но постойте, отсутствие отрисовки означает, что не появится новый объект, и дальше ничего работать не будет (см. выше Как это работает). Поэтому в htmlZclip предположение об обязательной отрисовки я заменил на предположение о том, что отрисовка точно должна произойти за 5 секунд:
setTimeout(function(){
object.parentNode.removeChild(object);
object=null;},5000);
Вообще, работать будет даже если поставить 0 мс, я просто даю небольшую передышку. Кстати, теперь если несколько раз использовать это копирование, то можно наблюдать в определенной части страницы некое подобие прогресс-бара из небольших iframe'ов ;) , каждый из которых будет исчезать через 5 сек.