// ==UserScript== // @name VKOpt // @version 3.0.7.20 // @author KiberInfinity [id13391307] // @namespace http://vkopt.net/ // @description Vkontakte Optimizer 3.x // @downloadUrl https://raw.githubusercontent.com/VkOpt/VkOpt/master/builds/vkopt_script.user.js // @updateUrl https://raw.githubusercontent.com/VkOpt/VkOpt/master/builds/vkopt_script.meta.js // @match *://vkontakte.ru/* // @match *://*.vkontakte.ru/* // @match *://vk.com/* // @match *://*.vk.com/* // @match *://userapi.com/* // @match *://*.userapi.com/* // @match *://vk.me/* // @match *://*.vk.me/* // @match *://*.vkuseraudio.net/* // @match *://*.vkuservideo.net/* // @connect self // @connect vkontakte.ru // @connect vk.com // @connect userapi.com // @connect vk.me // @connect vkuseraudio.net // @connect vkuservideo.net // @connect vkopt.net // @run-at document-start // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_download // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function(win, doc, code){ var mark = 'vkopt_loader', __key = (Math.round(Math.random()*10000000)).toString(35); var ext_browser={ mozilla:(function(){try{return !chrome && Components.interfaces.nsIObserverService!=null} catch(e){return false} })(), mozilla_jetpack: !!(typeof self != 'undefined' && self.port && self.port.emit && self.port.on), opera: !!(window.opera && opera.extension), webext: (function() { try { return !!chrome && !!chrome.extension } catch (e) {return false} })(), chrome: !!(window.chrome && chrome.extension), safari: !!(window.safari && safari.self), maxthon: (function(){try{return window.external.mxGetRuntime!=null} catch(e){return false} })(), //without try{}catch it fail script on Firefox gm: (typeof GM_xmlhttpRequest != 'undefined') && {download: typeof GM_download != 'undefined'}, }; var ex_msg={ init:function(handler){ if (!doc.head){ setTimeout(function(){ ex_msg.init(handler); },10) return; } if (typeof CustomEvent != 'undefined'){ win.addEventListener('vkopt_messaging_request', function(e) { handler(e.detail,function(data){ var data_obj = {data:data} var response = new CustomEvent("vkopt_messaging_response",{detail:JSON.stringify(data_obj)}); win.dispatchEvent(response); }); }); } else { doc.addEventListener("vkopt_messaging_request", function(event) { var node = event.target; if (!node || node.nodeType != Node.TEXT_NODE) return; var _doc = node.ownerDocument; handler(JSON.parse(node.nodeValue), function(response) { node.nodeValue = JSON.stringify(response); var event = _doc.createEvent("HTMLEvents"); event.initEvent("vkopt_messaging_response", true, false); return node.dispatchEvent(event); }); }, false, true); } } } var ex_loader = { get:function(key){ try{ return GM_getValue(key, null); } catch(e){ console.error('GM_getValue error key:"'+key+'" ', e); } }, set:function(key,value){ GM_setValue(key, value); } } var ext_api={ __key:(Math.round(Math.random()*10000000)).toString(35), ready:false, callbacks:{}, cid:1, prepare_data:function(msg){ msg = msg || {}; msg.__key=ex_ldr.__key; msg._req=ext_api.cid++; return msg }, post_message:function(data){console.log("can't post message to bg process",data)}, init:function(win){ ex_msg.init(ext_api.on_message); }, /* message_handler:function(data){ data = data || {}; if (data && data._req && ext_api.callbacks['cb_'+data._req]){ ext_api.callbacks['cb_'+data._req](data); } else { if (data._req) console.log('Response from bg:',data); } },*/ on_message:function(e,send_response){ // FOR PAGE <-> CONTENT SCRIPT var res = e.data; //console.log('msg_get:',res); if (!res.act || res.mark!=mark) return; switch (res.act){ case 'get': case 'post': case 'head': case 'ajax': case 'storage_get': case 'storage_set': case 'storage_keys': case 'storage_delete': case 'storage_clear': case 'download': case 'check_ext': ext_api.message_handler(res,function(data){ send_response(JSON.parse(JSON.stringify({response:data,sub:res._sub}))); }); break; } }, // from bg part store_val_prefix: 'custval_', message_handler:function(data,send_response){ //console.log('BG_GET:',data,send_response); switch(data.act){ case 'check_ext': send_response({act:'get_response'}); break; case 'get': ext_api.get(data.url,function(t,status){ send_response({act:'GET_response', response:t}); }); break; case 'post': ext_api.post(data.url,data.params,function(t,status){ send_response({act:'POST_response', response:t}); }); break; case 'head': ext_api.head(data.url,function(headers,status){ send_response({act:'POST_response', response:headers}); }); break; case 'ajax': ext_api.ajax(data.options,function(r){ /* r.text is xhr.responseText; r.headers is xhr.getAllResponseHeaders(); r.status is xhr.status; OR r.error */ send_response({act:'AJAX_response', response:r}); }); break; case 'storage_get': var prefix = data.reserved_access_allowed ? '' : ext_api.store_val_prefix; if (data.keys){ var vals={}; for (var i=0; i-1) return false; if (/\.(js|css)$/.test(key)) return false; return true; }, ajax:function(options,callback){ /* options={ url method: POST | GET | HEAD data : POST data params: GET params headers: if headers['Content-type']=='multipart/form-data' use data as Uint8Array } */ if (!options.url || (options.url || '').replace(/^\s+|\s+$/g, '') == '') { var response = {}; response.text = ''; response.headers = ''; response.status = 0; response.error = 'No URL'; callback(response); return; } var serialize = function (obj) { var pairs = []; for (var key in obj) { pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])); } return pairs.join('&'); }; var isEmptyObject = function (obj) { for (var key in obj) return false; return true; }, method = options.method || 'GET', params = serialize(options.params || {}), headers = options.headers || {}, data = options.data || null, url = options.url || '', responseType = options.responseType || '', contentType = headers['Content-type'] || 'application/x-www-form-urlencoded'; if (!headers['Content-type']) headers['Content-type'] = contentType; if (data && (typeof data == 'object') && isEmptyObject(data)) data = null; if (data && (typeof data == 'object') && Object.prototype.toString.call(data) !== '[object Array]') data = serialize(data); if (~contentType.indexOf('multipart/form-data') && method == 'POST' && data && data.length) { var buffer = new Uint8Array(data.length); for (var i = 0; i < data.length; i++) { buffer[i] = data[i]; } data = buffer.buffer; } if (params) url += ~url.indexOf('?') ? '&' + params : '?' + params; var opts = { method: method, url: url, headers: headers, data: data, binary: !(typeof data == 'string'), onload: function (xhr) { var response = {}; if (!responseType || responseType == 'text') response.text = xhr.responseText; response.headers = xhr.responseHeaders; response.status = xhr.status; response.raw = (responseType == 'arraybuffer' ? [].slice.call(new Uint8Array(xhr.response)) : xhr.response); callback(response); } } if (responseType) opts.responseType = responseType; try { var gxhr = GM_xmlhttpRequest(opts); } catch (e) { console.log('XHR ERROR', e); callback({ error: e }); } }, get:function(url,params,callback){ if (!callback){ callback=params; params=null; } ext_api.ajax({url:url, params:params, method:'GET'},function(r){ callback(r.text,r.status); }) }, post:function(url,params,callback){ ext_api.ajax({url:url, data:params, method:'POST'},function(r){ callback(r.text,r.status); }) }, head:function(url, callback){ ext_api.ajax({url:url, method:'HEAD'},function(r){ callback(r.headers,r.status); }) } }; ext_api.init(win); var init = function(){ if (!doc.getElementsByTagName("head")[0]){ setTimeout(init,10); return; } ext_api.ready=true; ext_api.post_message=function(msg){ msg=ext_api.prepare_data(msg); opera.extension.postMessage(msg); return msg; } var script = " window._ext_ldr_"+mark+"=true;\n window._"+mark+"_browser = "+ JSON.stringify(ext_browser) +";\n" + code; var js = doc.createElement('script'); js.type = 'text/javascript'; js.charset = 'UTF-8'; js.innerHTML=script; js.setAttribute(mark,"3.0.7.20"); doc.getElementsByTagName('head')[0].appendChild(js); } init(); })(unsafeWindow, document, (function(){ var target_script = function(){ ////////////////////////////////////////////////// /////////////////// vkopt.js /////////////////// ////////////////////////////////////////////////// // VKOpt 3.x (Vkontakte Optimizer) // // Author: KiberInfinity( /id13391307 ) // // Web: http://vkopt.net/ // // (c) All Rights Reserved. VkOpt. // ////////////////////////////////////////////////// /* VERSION INFO */ var vVersion = 307; var vBuild = 191215; var vVersionRev = 20; var vPostfix = ''; if (!window.vkopt) window.vkopt={}; vkopt.versions = vkopt.versions || []; vkopt.versions.push({ version: vVersion, build: vBuild, rev: vVersionRev, postfix: vPostfix, base: window._vkopt_loader_browser }) var vkopt_defaults = { config: { scroll_to_next: false, ad_block: true, skip_away: true, compact_audio: false, audio_full_title: false, disable_border_radius: false, audio_dl: true, audio_wait_hover: true, vid_dl: true, audio_size_info: false, audio_clean_titles: false, audio_album_info: true, scrobbler: true, im_dialogs_right: false, dont_cut_bracket: false, postpone_custom_interval: true, pv_comm_move_down: false, calc_age: true, audio_pos: false, old_unread_msg: false, old_unread_msg_bg: 'c5d9e7', im_recent_emoji: false, ru_vk_logo: false, rn_label_communities: false, old_icon_verify: false, //hide_big_like: false, hide_left_set: false, hide_recommendations: false, show_full_user_info: false, switch_kbd_lay: true, show_online_status: false, show_common_group: false, common_group_color: '90ee90', dislikes_enabled: false, dislike_icon_index: 1, //disabled: im_store_h: false, //Extra: vkopt_guide: true, // показываем, где находится кнопка настроек, до тех пор, пока в настройки всё же не зайдут photo_replacer: true, audio_more_acts: true, // доп. менюшка для каждой аудиозаписи vk_audio_icon_dots: false, // иконка аудио "действия/скачать" в виде трех точек //audio_dl_acts_2_btns: false, // разделить на аудио кнопки скачивания и меню доп.действий audio_force_flash: false, // принудительно использовать Flash для аудио-плеера im_hide_dialogs: false, // Новый стиль диалогов. Полотно переписки на всю ширину, список диалогов скрывается при клике по истории, показ списка - клик по заголовку переписки attach_media_by_id: true, // при вставке айди медиа в поле поиска из диалога прикрепления, в диалог подгружается медиа-файл с этим айди datepicker_inj: true, // активна ли инъекция в конструктор DatePicker'а zodiak_ophiuchus:false, // 13ый знак зодиака, Змееносец, между 30 ноября и 17 декабря photo_search_copy: true, ph_download_with_name: false, stealth_addons: true, // прикидываемся перед ТП, что у нас не стоит расширение для скачивания. im_block_typing: false, im_block_mark_read: false, gim_block_typing: false, gim_block_mark_read: false, accept_more_cats: true, lastfm_enable_scrobbling: false, lastfm_token: '', lastfm_username: 'NO_AUTH', lastfm_session_key: '', //Consts: SAVE_MSG_HISTORY_PATTERN: "%username% (%date%):\r\n%message%\r\n%attachments%\r\n\r\n", SAVE_MSG_HISTORY_DATE_FORMAT: "HH:MM:ss dd/mm/yyyy", AUDIO_INFO_LOAD_THREADS_COUNT: 5, AUTO_LIST_DRAW_ROWS_INJ: true, // На случай, если инъекция будет убивать редер автоподгружаемых списков MAX_CACHE_AUDIO_SIZE_ITEMS: 10000 // максимальное количество запомненных размеров аудио в локальном хранилище }, popular: [ 'hold_menu', //'dislikes_enabled', 'scroll_to_next', 'pv_comm_move_down', 'disable_border_radius' ], disabled_modules: [ /* "res", "settings", "lang", "owners", "away", "photoview", "photos", "audio", "scrobbler", "audio_info", "audioplayer", "videoview", "messages", "attacher", "face", "profile", "groups", "wall", "friends", "support", "test_module", "turn_blocks", "vk_dislike" */ ] }; var vkopt_core = { disallow_location: /\/m\.vk\.com|login\.vk\.com|oauth\.vk\.com|al_index\.php|frame\.php|widget_.+php|notifier\.php|audio\?act=done_add/i, dom_ready: function(fn, ctx){ var ready, timer; var onChange = function (e) { if ((typeof IDL == "undefined") || (typeof VK_LANGS == "undefined")) return; // Ждём vk_lib.js и vklang.js if (document.getElementById('footer') || document.getElementById('footer_wrap')) { fireDOMReady(); } else if (e && e.type == "DOMContentLoaded") { fireDOMReady(); } else if (e && e.type == "load") { fireDOMReady(); } else if (document.readyState) { if ((/loaded|complete/).test(document.readyState)) { fireDOMReady(); } else if (!!document.documentElement.doScroll) { try { ready || document.documentElement.doScroll('left'); } catch (e) { return; } fireDOMReady(); } } }; var fireDOMReady = function () { if (!ready) { ready = true; fn.call(ctx || window); if (document.removeEventListener) document.removeEventListener("DOMContentLoaded", onChange, false); document.onreadystatechange = null; window.onload = null; clearInterval(timer); timer = null; } }; if (document.addEventListener) document.addEventListener("DOMContentLoaded", onChange, false); document.onreadystatechange = onChange; timer = setInterval(onChange, 5); window.onload = onChange; }, init: function(){ if (vkopt_core.disallow_location.test(document.location.href)) return; var run = vkopt_core.run; var check = vkopt_core.conflicts; var lng = vkopt.lang.lng; //TODO: тут ещё бы дождаться подгрузки vk_lib.js vkopt_core.dom_ready(function(){ // if (!isNewVk()) return; if (!window.StaticFiles){ console.log('avoid vkopt init'); } else { console.log('init vkopt 3.x'); run(function(){ check(lng); }); } }); }, conflicts: function(IDL){ var content; if (vkopt.versions.length > 1){ var vers = []; each(vkopt.versions, function(i,info){ var types = []; var lbr = window._vkopt_loader_browser; for (var type in lbr) lbr[type] && types.push(type); var ver = vk_lib.format( '
%1%2 (build %3) [%4]
', String(info.version).split('').join('.'), info.postfix, info.build, types.join(' & ') ); vers.push(ver); }); content = IDL('VkoptDupFound') + '

' + IDL('Found') + '

' + vers.join('\n'); } else { try{ MessageBox.toString(); } catch(e) { content = IDL('VkoptDupFound'); } } if (!content) return; try { showFastBox( getLang('global_box_error_title'), content ).setControlsText( '' + IDL('Help') + ' | ' + 'vkopt.net' ); } catch(e) { alert(content.replace(/<\/?[^>]+>/,'')); } }, run: function(check){ // Под новый дизайн чуть другие функции работы с локализацией. vkopt.lang.override(); // TODO: убрать этот костыль при удалении скриптов для старого дизайна vkopt.settings.init_defaults(); check && check(); var wait = []; for (var key in StaticFiles) if (StaticFiles[key].t == 'js'){ if (StaticFiles[key].l) vk_glue.inj_handler([key])(); else wait.push(key); // добавляем в список недозагруженных скриптов } if (window.stManager && wait.length){ vkopt.log('Wait ' + wait.length + ' scripts'); // чтоб узнать когда дозагрузятся подключенные скрипты, // просто ещё раз запросим их подключение у stManager // и получим колбек по завершению загрузки. stManager.add(wait, function(){ vkopt.log('Loaded ' + wait.length + ' scripts'); vk_glue.inj_handler(wait)(); }); } vkopt_core.plugins.on_init(); vk_glue.nav_handler(); window.vkopt_core_ready = true; vkCheckUpdates(); }, timeout: function(fn, t){ return window['set'+'Timeout'](fn,t); }, mod_str_as_node: function(str, func, params){ if (!str || str.tagName) return str; var is_table = str.substr(0,3)=='") txt = txt.substr(7,txt.length-15); return txt; //TODO: call to plugins }, setLoc: function(new_location){ // использовать вместо nav.setLoc для избежания рекурсии, обход реакции на смену URL'а nav.setLoc(new_location,'vkopt'); }, plugins: { delayed_run: function(plug_id){ //функция для пуска отдельного плагина, который не был подключен до основного запуска вкопта // сначала прописываем в vkopt_defaults.config данные о значениях опций по умолчанию var options_list = vkopt_core.plugins.call_method(plug_id, 'onSettings'); vkopt.settings.update_defaults(options_list); // добавляем стили модуля в страницу var css = vkopt_core.plugins.get_css(plug_id); if (css != '') vkopt.set_css(css, 'vkopt_'+plug_id+'_styles'); vkopt_core.plugins.call_method(plug_id, 'onInit'); vkopt_core.plugins.call_modules('onModuleDelayedInit', plug_id); // сообщаем всем модулям о подключении опоздавшего for (var key in StaticFiles) if (StaticFiles[key].t == 'js') vkopt_core.plugins.call_method(plug_id, 'onLibFiles', key); vkopt_core.plugins.call_method(plug_id, 'onLocation', nav.objLoc, cur.module); vkopt_core.plugins.call_method(plug_id, 'processNode', null, {source:'delayed_run'}); }, call_method: function(){ // (plug_id, method, arg1, arg2 ...) var args = Array.prototype.slice.call(arguments); var plug_id = args.shift(); var method = args.shift(); //Предотвращаем вызов обработчиков у отключенных модулей if (vkopt_defaults.disabled_modules.indexOf(plug_id) > -1) return; var field = vkopt[plug_id][method]; if (field) // TODO: && isModuleEnabled(plug_id) return isFunction(field) ? field.apply(this, args) : field; }, call_modules: function(){ // (method, arg1, arg2 ...) var args = Array.prototype.slice.call(arguments); var results = {}; for (var plug_id in vkopt){ var res = vkopt_core.plugins.call_method.apply({plugin_id:plug_id}, [plug_id].concat(args)); if (res != undefined) results[plug_id] = res; } return results; }, on_init:function(){ vkopt_core.plugins.add_css(); vkopt_core.plugins.call_modules('onInit'); vkopt_core.plugins.process_node(ge('page_body')); }, add_css:function(){ var code = ''; if (!vkopt.settings.get('style_per_module')){ for (var plug_id in vkopt) code += vkopt_core.plugins.get_css(plug_id); vkaddcss(code); } else { for (var plug_id in vkopt){ code = vkopt_core.plugins.get_css(plug_id); code && vkopt.set_css(code,'vkopt_module_'+plug_id); } } }, get_css:function(plug_id){ var css = vkopt[plug_id].css; if (!css) return ''; return (Object.prototype.toString.call(css) === '[object Function]')?css():css; }, on_js_file: function(file, full_file_name){ //console.log('on *.js: '+file); vkopt_core.plugins.call_modules('onLibFiles', file, full_file_name); }, on_location: function(){ //console.log('on nav: ', cur.module, ' obj: ', JSON.stringify(nav.objLoc)); vkopt_core.plugins.call_modules('onLocation', nav.objLoc, cur.module); }, on_ajax_post: function(url, query, options){ var res = vkopt_core.plugins.call_modules('onRequestQuery', url, query, options); if (url === 'al_im.php' && query.act === 'a_send') { res = extend(res, vkopt_core.plugins.call_modules('onImSend', query)); } for (var i in res) if (res[i] === false) return false; return true; }, process_response: function(answer, url, q){ // answer - массив, элементы которого в последствии становятся аргументами вызываемых колбеков. можно править его элементы var _rx = /^\s*<(div|table|input|a)/i; for (var i=0; i < answer.length; i++){ if (typeof answer[i]=='string' && _rx.test(answer[i]) ){ answer[i] = vkopt_core.mod_str_as_node(answer[i], vkopt_core.plugins.process_node, {source:'process_response', url:url, q:q}); } else if (isArray(answer[i])){ var sub = answer[i]; for (var j = 0; j < sub.length; j++) if (typeof sub[j]=='string' && _rx.test(sub[j]) ){ sub[j] = vkopt_core.mod_str_as_node(sub[j], vkopt_core.plugins.process_node, {source:'process_response', url:url, q:q}); } } } // для случаев, когда тело html'а передано не отдельным аргументом, а внутри какого-то JSON'а: if (url === '/al_im.php' && q.act == 'a_start' && answer[0] && answer[0].history){ // открытие диалога answer[0].history = vkopt_core.mod_str_as_node(answer[0].history, vkopt_core.plugins.process_node, {source:'process_response_im_a_start', url:url, q:q}); } vkopt_core.plugins.call_modules('onResponseAnswer', answer,url,q); }, process_node: function(node, params){ node = node || ge('content'); if (!node) return; var nodes=node.getElementsByTagName('a'); for (var i=0;i vk_glue.inj_handler(files) /* // узнаем имя JS-файла, для которой file_name является одной из зависимостей var depOf = function(file_name){ if (window.stDeps) for (var js in stDeps) if (stDeps[js].indexOf(file_name) > -1) return js; return null; } */ return function(no_pending){ if (no_pending) console.log('no need inject?', files); if (!isArray(files)) files = [files]; /* // добавляем в колбек имена файлов, которые зависят от подгруженных зависимостей for (var i = 0; i < files.length; i++){ var dep = depOf(files[i]); if (dep && files.indexOf(dep) > -1) files.push(dep); } */ for (var i in files){ if (isString(files[i]) && files[i].indexOf('.js') != -1) vk_glue.inj_to_file(files[i].split('/').pop().replace(/\.[a-f0-9]{20}/,''), files[i]); } } }, inj_to_file: function(file_name, full_file_name){ switch (file_name){ case 'common.js': case 'common_web.js': case 'cmodules/web/common_web.js': case 'lite.js': vk_glue.inj.common(); break; case 'auto_list.js': vk_glue.inj.auto_list(); break; case 'ui_controls.js': vk_glue.inj.ui_controls(); break; case 'datepicker.js': vk_glue.inj.datepicker(); break; case 'notifier.js': vk_glue.inj.notifier(); break; } vkopt_core.plugins.on_js_file(file_name, full_file_name); }, inj: { common: function(){ // перехватываем момент подключения скриптов: Inj.Start('stManager.add', function(files, cb){ var f, wait = []; if (!isArray(files)) files = [files]; for (var i in files){ f = files[i]; if (!f) continue; if (f.indexOf('?') != -1) f = f.split('?')[0]; if (StaticFiles[f] && !StaticFiles[f].l) wait.push(f); } var newCallback = function(f){ if (wait.length) vk_glue.inj_handler(files)(); cb && cb.apply(this, arguments); } this.args[1] = newCallback; }); // перехват события об аякс загрузке новой страницы / смене URL'а Inj.End('nav.setLoc',function(){ if (arguments[1]!="vkopt") setTimeout(vk_glue.nav_handler,2); }); // Ловим момент, когда через extend меняют информацию о текущем модуле страницы Inj.End('extend', function(obj, obj2){ if (obj && obj2 && window.cur && obj == cur && obj2.module) setTimeout(vk_glue.nav_handler,2); }) // Перехватываем результат ajax-запросов с возможностью модификации перед колбеком Inj.Start('ajax.post', // ARG0 - url; ARG1 - query object; ARG2 - options function(url, query, options){ // Mod callback: var ctx = this; if (ctx.__ARG2__){ var onDoneOrig = ctx.__ARG2__.onDone; ctx.__ARG2__.onDone = function(){ var argarr = Array.prototype.slice.call(arguments); vk_glue.response_handler(argarr, ctx.__ARG0__, ctx.__ARG1__); onDoneOrig && onDoneOrig.apply(window, argarr); } } // End of callback mod } ); // айфремовая загрузка выглядит так - загрузили каркас с частью данных, дальше по ходу загрузки айфреймовой страницы выполняются куски заполнения элементов карскаса. // эти кучки тоже надо перехватывать. Inj.Start('ajax.framegot',function(cont, html, js, params){ if (this.args && this.args[1]) this.args[1]=vk_glue.process_on_framegot(this.args[1]); }); // Можем модифицировать поля запроса перед отсылкой ajax-запроса, либо заблокировать его Inj.Start('ajax.post',function(url, query, options){ if (vk_glue.process_on_post(url, query, options) === false) this.prevent = true; }); // цепляемся к возвращению обработанного шаблона из getTemplate Inj.End('getTemplate',function(tplName, state){ // если исходная функция что-то вернула if (this.result){ // возвращаем модифицированный ответ this.return_result = vk_glue.tpl_hook(this.result, tplName, state) this.prevent_all = true; } }) /* Inj.Start('renderFlash','vkOnRenderFlashVars(vars);'); */ // перехват тултипов при создании их контента. например для перехвата создания меню "Ещё" перед его показом в просмотрщике фото Inj.Start('ElementTooltip.prototype.show', function(){ var handle = function(){ vkopt_core.plugins.eltt_first_show(this); } var obj = this.this_obj; if (obj._opts){ if (obj._opts.onFirstTimeShow){ var orig = obj._opts.onFirstTimeShow; obj._opts.onFirstTimeShow = function(){ var args = Array.prototype.slice.call(arguments); handle.apply(obj, args); orig.apply(obj, args); } } else { obj._opts.onFirstTimeShow = handle; } } }); }, notifier: function(){ Notifier.addRecvClbk('vkcmd', 0, function(data){ vkopt_core.plugins.on_cmd(data); }, true); }, auto_list: function(){ if (vkopt_defaults.config.AUTO_LIST_DRAW_ROWS_INJ){ Inj.End('AutoList.prototype._drawRows',function(t){ var s = this.this_obj; var N = 0; each(t, function(i, o) { if ("string" == typeof o) // копипаст фильтра из _drawRows N++; // считаем количество элементов, которые должны были быть вставлены }); var new_rows = domChildren(s._containerEl).slice(-N); // получаем список из N последних вставленных элементов for (var i = 0; i < new_rows.length; i++) vkopt_core.plugins.process_node(new_rows[i]); }) } // Inj.Replace('AutoList.prototype._drawRows',/\.appendChild\(([A-Za-z_0-9]+)\)/,'$&; vkopt_core.plugins.process_node($1);'); // Inj.End('AutoList.prototype._drawRows', '; vkopt_core.plugins.process_node(this._containerEl);'); // Другой вариант. Меньше шансов того, что отвалится, но тут выходит повторная обработка ранее выведенного. }, ui_controls: function(){ /* var orig = InlineDropdown; // бекап Inj.Start('InlineDropdown','vkopt_core.plugins.inline_dd_create(#ARG0#, #ARG1#);'); // Инжект в конструктор не работает с текущим враппером функций из обновленного Inj for (var method in orig.prototype) //восстанавливаем методы из бекапа InlineDropdown.prototype[method] = orig.prototype[method]; */ }, datepicker: function(){ if (!vkopt.settings.get('datepicker_inj')) return; // передаём в наш перехватчик id элемента в котором указана дата. if (!window.vkorigDatepicker || (window.Datepicker && Datepicker.toString().indexOf('onDatepickerCreate') == -1)){ window.vkorigDatepicker = window.Datepicker; window.Datepicker = function(el, options){ // moved from vkopt.wall.onLibFiles mod. Attach to this by event onDatepickerCreate var args = Array.prototype.slice.call(arguments); vkopt_core.plugins.datepicker_create.apply(this, args); vkorigDatepicker.apply(this, args); } } } }, nav_handler: function(){ //TODO: debounce? // тут какие-то системные действия движка до передаче события в плагины // ... vkopt_core.plugins.on_location(); }, process_on_framegot: function(html){ return vkopt_core.mod_str_as_node(html, vkopt_core.plugins.process_node); }, process_on_post: function(url, query, options){ return vkopt_core.plugins.on_ajax_post(url, query || {}, options); }, response_handler: function(answer,url,q){ // try{ vkopt_core.plugins.process_response(answer, url, q); // catch(e){ // vkopt_core.ping_stat({type: 'fail', source: 'response_handler', url: url, q: q ? q.act : ''}) // } }, tpl_hook: function(html, tpl_name, state){ // Сообщаем модулям, что нужно как элемент обработать содержимое возвращаемое функцией getTemplate return vkopt_core.mod_str_as_node(html, vkopt_core.plugins.process_node, {source:"getTemplate", tpl_name: tpl_name, state: state}) } }; /* (function(){ window.vkopt = (window.vkopt || {}); var m = { id: 'vkopt_any_plugin', // onInit: function(){}, // выполняется один раз после загрузки стрницы onLibFiles: function(file_name, full_file_name){}, // место для инъекций = срабатывает при подключении нового js-файла движком контакта. onLocation: function(nav_obj,cur_module_name){}, // вызывается при переходе между страницами onRequestQuery: function(url, query, options){} // вызывается перед выполнением ajax.post метода. Если функция вернёт false, то запрос выполнен не будет. onResponseAnswer: function(answer,url,params){}, // answer - массив, изменять только его элементы onCmd: function(command_obj){}, // слушает сообщения отосланные из других вкладок вк через vkopt.cmd(command_obj) processNode: function(node, params){} // обработка элемента processLinks: function(link, params){}, // обработка ссылки onModuleDelayedInit: function(plugin_id){}, // реакция на подключение модуля, опоздавшего к загрузке страницы. onElementTooltipFirstTimeShow: function(ett, ett_options), // реакция на первый показ ElementTooltip, при создании его контента. На момент вызова в элементе ett._ttel уже есть контент. По ett._opts.id можно определить к чему тултип относится. // UNAVAILABLE! onInlineDropdownCreate: function(el, options), // реакция на создание контрола InlineDropdown. Можно использовать для добавления своих пунктов в options.items для меню элемента el onDatepickerCreate: function(args){}, // вызывается при создании селектора даты new Datepicker(), args - массив аргументов переданных в конструктор // onSettings: function(){} || {} // возвращаем объект с перечисленными по категориям настройками этого модуля onOptionChanged: function(option_id, val, option_data){}, // реакция на изменение опции firstRun: function(){} // вызывается при первом запуске (не найдены ранее прописанные настройки vkopt'a), // P.S. вкопт из хранилища расширения восстанавливает только данные из своего конфига, если они были сохранены, // тогда это первым запуском не считается, но в локальном хранилище могут отсутствовать данные от других модулей, // хранящих инфу не через vkopt.settings.get | vkopt.settings.set // onImSend: function(query){} // вызывается перед отправкой личного сообщения. Если функция вернёт false, то сообщение не отправится. onImReceive: //