Snapster plugin for VkOpt

Плагин для VkOpt, добавляющий на сайт ВКонтакте веб-клиент Snapster

  1. // ==UserScript==
  2. // @id Snapster@vkopt
  3. // @name Snapster plugin for VkOpt
  4. // @version 1.3
  5. // @namespace https://greasyfork.org/users/23
  6. // @author Pmmlabs@github
  7. // @description Плагин для VkOpt, добавляющий на сайт ВКонтакте веб-клиент Snapster
  8. // @include *vk.com*
  9. // @run-at document-end
  10. // @icon http://vk.com/images/chronicle/logotype.png
  11. // @noframes
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. if (!window.vkopt_plugins) vkopt_plugins={};
  16. (function(){
  17. var PLUGIN_ID = 'snapster';
  18. var PEOPLE_PHOTO_SIZE = 262; // Размер квадратных фоток-миниатюр в разделе "Люди"
  19. vkopt_plugins[PLUGIN_ID]={
  20. Name: 'Snapster web-client',
  21. css: '.quadro-photo {width: '+PEOPLE_PHOTO_SIZE+'px; height: '+PEOPLE_PHOTO_SIZE+'px;}' +
  22. '.hashtag {font-size:2em}' +
  23. '#snapster_add_table td:first-child {width:10%}' +
  24. '#snapster_add_table input {width:100%}',
  25. // СОБЫТИЯ
  26. init: function(){ // При подключении плагина к Вкопту
  27. // Добавление нового пункта меню в левое меню
  28. var menu=(ge('sideBar') || ge('side_bar')).getElementsByTagName('ol')[0];
  29. menu.appendChild(vkCe('li',{'class':'vk_custom_item'},'<a onclick="return true;" onmousemove="vkMenuHide();" class="left_row vk_custom_link" href="/feed?section=snapster">' +
  30. '<span class="left_label inl_bl">Snapster</span></a>'));
  31. // Исправление работы кнопки "Назад"
  32. var original_popstate = data(window,'events').popstate.shift(); // оригинальный обработчик, который выкидывает на /feed , если section кастомная
  33. data(window, 'events').popstate.push(function () {
  34. var objLoc = nav.fromStr(location.href);
  35. if (objLoc.section == 'snapster' && (objLoc.sub!=nav.objLoc.sub || objLoc.hashtag!=nav.objLoc.hashtag))
  36. vkopt_plugins[PLUGIN_ID].switchSection(objLoc.sub, objLoc.hashtag || null);
  37. else
  38. original_popstate();
  39. });
  40. // Подгрузка записей при скролле
  41. if (cur.module=='feed')
  42. Inj.Start('Feed.showMore','if (cur.section=="snapster") return vkopt_plugins["'+PLUGIN_ID+'"].showMore();');
  43. //this.onLocation(nav.objLoc)
  44. },
  45. onLocation: function(nav_obj){
  46. if (nav_obj[0]=='feed' && nav_obj.section=='snapster')
  47. this.UI(nav_obj.sub, nav_obj.hashtag || null);
  48. },
  49. // ПЕРЕМЕННЫЕ
  50. postTemplate: '<div id="post{post_id}" class="post post_photos post_photos{owner_id}_1" onmouseover="wall.postOver(\'{post_id}\')" onmouseout="wall.postOut(\'{post_id}\')">'+
  51. '<div class="post_table">'+
  52. '<div class="post_image">'+
  53. '<a class="post_image" href="/id{owner_id}"><img src="{avatar}" height="50" width="50"></a>'+
  54. '</div>'+
  55. '<div class="post_info">'+
  56. '<div class="wall_text">'+
  57. '<div class="wall_text_name">'+
  58. '{name_link}{verified} {friend_status}'+
  59. '</div>'+
  60. '<div class="wall_post_text">{text}</div>'+
  61. '<div class="page_post_sized_thumbs clear_fix">'+
  62. '<a href="/photo{photo_id}" onclick="return showPhoto(\'{photo_id}\', \'album{owner_id}_{aid}\', {}, event);" style="width: 537px; height: {height}px;" class="page_post_thumb_wrap page_post_thumb_last_row fl_l"><img src="{src_big}" width="{width}" class="page_post_thumb_sized_photo"></a>'+
  63. '</div>{place}' +
  64. '</div>'+
  65. '<div class="post_full_like_wrap sm fl_r">'+
  66. '<div class="post_full_like">'+
  67. '<div class="post_like fl_r" onmouseover="wall.postLikeOver(\'{photoLike_id}\')" onmouseout="wall.postLikeOut(\'{photoLike_id}\')" onclick="vk_skinman.like(\'{photo_id}\'); event.cancelBubble = true;">'+
  68. '<span class="post_like_link fl_l" id="like_link{photoLike_id}">Мне нравится</span><i id="like_icon{photoLike_id}"></i>'+
  69. '<i class="post_like_icon sp_main fl_l {mylike}" id="s_like_icon{photo_id}"></i>'+
  70. '<span class="post_like_count fl_l" id="s_like_count{photo_id}">{likes}</span>'+
  71. '</div>'+
  72. '<div class="post_share fl_r" onmouseover="wall.postShareOver(\'{photoLike_id}\')" onmouseout="wall.postShareOut(\'{photoLike_id}\', event)" onclick="vkopt_plugins[\''+PLUGIN_ID+'\'].share(\'{photo_id}\',\'{post_id}\'); event.cancelBubble = true;">'+
  73. '<span class="post_share_link fl_l" id="share_link{photoLike_id}">Поделиться</span>'+
  74. '<i class="post_share_icon sp_main fl_l" id="share_icon{photoLike_id}"></i>'+
  75. '</div>'+
  76. '</div>'+
  77. '</div>'+
  78. '<div class="replies">'+
  79. '<div class="reply_link_wrap sm">'+
  80. '<small class="feed_photos_num"><span class="rel_date">{date}</span></small>' +
  81. ' | <a onclick="return nav.change({z: \'album{owner_id}_{aid}\'}, event);" href="/album{owner_id}_{aid}">Альбом</a>'+
  82. '{filter}'+
  83. '</div>{comments}'+
  84. '</div>'+
  85. '</div>'+
  86. '</div>',
  87. peopleTemplate: '<div id="post{post_id}" class="post post_photos post_photos{owner_id}_1" onmouseover="wall.postOver(\'{post_id}\')" onmouseout="wall.postOut(\'{post_id}\')">'+
  88. '<div class="post_table">'+
  89. '<div class="post_image">'+
  90. '<a class="post_image" href="/id{owner_id}"><img src="{avatar}" height="50" width="50"></a>'+
  91. '</div>'+
  92. '<div class="post_info">'+
  93. '<div class="wall_text">'+
  94. '<div class="wall_text_name">'+
  95. '<a class="author" href="/id{owner_id}">{name}</a>{verified} {friend_status}'+
  96. '</div>'+
  97. '<div class="page_post_sized_thumbs clear_fix">'+
  98. '<a href="/photo{photo_id1}" onclick="return showPhoto(\'{photo_id1}\', \'photos{owner_id}\', {}, event);" class="page_post_thumb_wrap quadro-photo fl_l"><img src="{src_big1}" width="'+PEOPLE_PHOTO_SIZE+'" class="page_post_thumb_sized_photo"></a>'+
  99. '<a href="/photo{photo_id2}" onclick="return showPhoto(\'{photo_id2}\', \'photos{owner_id}\', {}, event);" class="page_post_thumb_wrap quadro-photo fl_l"><img src="{src_big2}" width="'+PEOPLE_PHOTO_SIZE+'" class="page_post_thumb_sized_photo"></a>'+
  100. '<a href="/photo{photo_id3}" onclick="return showPhoto(\'{photo_id3}\', \'photos{owner_id}\', {}, event);" class="page_post_thumb_wrap quadro-photo fl_l"><img src="{src_big3}" width="'+PEOPLE_PHOTO_SIZE+'" class="page_post_thumb_sized_photo"></a>'+
  101. '<a href="/photo{photo_id4}" onclick="return showPhoto(\'{photo_id4}\', \'photos{owner_id}\', {}, event);" class="page_post_thumb_wrap quadro-photo fl_l"><img src="{src_big4}" width="'+PEOPLE_PHOTO_SIZE+'" class="page_post_thumb_sized_photo"></a>'+
  102. '</div>'+
  103. '</div>'+
  104. '</div>'+
  105. '</div>',
  106. placeTemplate:'<div class="media_desc">' +
  107. '<a class="page_media_place clear_fix" href="feed?q=near%3A{lat}%2C{long}&section=photos_search" onclick="nav.go(this.href,event)" title="Искать фотографии рядом">' +
  108. '<span class="fl_l checkin_big"></span>' +
  109. '<div class="fl_l page_media_place_label" style="width:480px">{place}<br/>{lat},{long}</div>' +
  110. '</a></div>',
  111. next_from:0,
  112. friend_statuses: ['','в друзьях 1','ваш подписчик','в друзьях'],
  113. // ФУНКЦИИ
  114. UI: function(subsection, hashtag) {
  115. if (isVisible(ge('feed_empty'))) { // делать интерфейс только если его еще нет, т.е. надпись "новостей нет" все еще видна.
  116. hide(ge('feed_empty'));
  117. geByTag('a',ge('l_nwsf'))[0].setAttribute('onclick','return true'); // Удаление аякса при щелчке на Новости в левом меню
  118. clearInterval(cur.updateInt); // Не обновлять ленту стандартными средствами feed
  119. // Удаление шапки
  120. var feed_news_bar = ge('feed_news_bar');
  121. var summary_tabs = geByClass('summary_tab', feed_news_bar);
  122. for (var i in summary_tabs)
  123. feed_news_bar.removeChild(summary_tabs[i]);
  124. // Формирование шапки. Категории новостей.
  125. dApi.call('chronicle.getExplore', {}, function (r, response) {
  126. response = response.concat(
  127. { section: 'recommended', title: 'Рекомендации / Смесь из возможных друзей и популярных пользователей' }
  128. , {section: 'people_list', title: 'Список людей / Заглушка для пустой ленты'}
  129. , {section: 'feed', title: 'Лента'}
  130. , {section: 'search', title: 'Поиск'}
  131. , {section: 'messages', title: 'Сообщения'}
  132. , {section: 'add', title: 'Добавить'}
  133. , {section: 'feedback', title: 'Оповещения'}
  134. );
  135. for (var i = 0; i < response.length; i++) {
  136. if (response[i].section == 'hashtags')
  137. vkopt_plugins[PLUGIN_ID].hashtags = response[i].hashtags;
  138. feed_news_bar.appendChild(vkCe('div', {'class': 'fl_l summary_tab', 'id':'snapster_'+response[i].section},
  139. '<a class="summary_tab2" title="'+response[i].title+'" href="feed?section=snapster&sub=' + response[i].section + '" onclick="if (!checkEvent(event)) {return vkopt_plugins[\'' + PLUGIN_ID + '\'].switchSection(\'' + response[i].section + '\');}"><div class="summary_tab3"><nobr>' + response[i].title.split('/')[0] + '</nobr></div></a>'));
  140. }
  141. // По умолчанию загружается "Популярное"
  142. vkopt_plugins[PLUGIN_ID].switchSection(subsection || 'popular_country', hashtag);
  143. });
  144. }
  145. },
  146. share: function (photo_id, post_id) { // Нажатие на "Поделиться"
  147. showBox('like.php', {
  148. act: 'publish_box',
  149. object: 'photo' + photo_id,
  150. list: 'feed1_' + post_id,
  151. to: 'mail'
  152. }, {stat: ['page.js', 'page.css', 'wide_dd.js', 'wide_dd.css', 'sharebox.js']});
  153. },
  154. filterInfo: function (oid, pid) {
  155. var box=vkAlertBox('',vkBigLdrImg);
  156. dApi.call('chronicle.getPreset', {
  157. owner_id: oid,
  158. photo_id: pid
  159. }, {
  160. ok: function (r, response) {
  161. box.hide();
  162. var html = '<table><tr><td>id:</td><td>'+response.id+'</td></tr>' +
  163. '<tr><td>Название:</td><td>'+response.data.name+'</td></tr>' +
  164. (response.data.app_version ? '<tr><td>Версия приложения:</td><td>'+response.data.app_version+'</td></tr>' : '') +
  165. (response.data.date ? '<tr><td>Дата:</td><td>'+dateFormat(response.data.date * 1000, "dd.mm.yyyy HH:MM:ss")+'</td></tr>' : '') +
  166. '<tr><td>Владелец:</td><td><a href="/id'+response.owner_id+'">id'+response.owner_id+'</a></td></tr>' +
  167. '<tr><td>Данные:<br><a id="snpstr_dt">(в консоль)</a></td><td style="max-height:200px;overflow-y:auto;display:block;">'+response.data.preset.toSource()+'</td></tr>' +
  168. '</table>';
  169. box = vkAlertBox('Информация о фильтре '+oid+'_'+pid, html);
  170. ge('snpstr_dt').onclick = function () {
  171. console.log(response.data.preset);
  172. }
  173. }, error: function(r, error){
  174. box.hide();
  175. box = vkAlertBox('Информация о фильтре '+photo_id, 'Нет информации о фильтре<br><pre>'+error.error_msg+'</pre>');
  176. }
  177. });
  178. },
  179. showMore: function() { // Подгрузка новых записей; замена для Feed.showMore
  180. if (cur.isFeedLoading) return;
  181. cur.isFeedLoading = true;
  182. show('show_more_progress');
  183. hide('show_more_link');
  184. this.switchSection(nav.objLoc.sub,nav.objLoc.hashtag,this.next_from);
  185. },
  186. processHashtags: function (text) { // На самом деле не только теги, а еще смайлики и обращения
  187. if (window.Emoji && Emoji.emojiToHTML)
  188. text = Emoji.emojiToHTML(text,true) || text;
  189. return text.replace(/\[([^\|]+)\|([^\]]+)\]/g,'<a href="/$1">$2</a>')
  190. .replace(/(#[\wа-яА-Я]+)/g,'<a href="feed?section=snapster&sub=search&hashtag=$1" ' +
  191. 'onclick="return vkopt_plugins[\'' + PLUGIN_ID + '\'].switchSection(\'search\',\'$1\');">$1</a>');
  192. },
  193. createNode: function(template, params){
  194. for (var i in params)
  195. template = template.replace(new RegExp('\{'+i+'\}','g'),params[i]);
  196. template = template.replace(/\{\w+\}/g,'');
  197. ge('feed_rows').appendChild(vkCe('div', {'class': 'feed_row'}, template));
  198. },
  199. switchSection: function(section, hashtag, next_from) {
  200. if (!ge('feed_rows')) location.reload(); // случай нажатия "назад" не с новостей
  201. show('feed_progress');
  202. cur.isFeedLoading = true;
  203. if (next_from===undefined) {
  204. ge('feed_rows').innerHTML = '';
  205. removeClass(geByClass('summary_tab_sel')[0], 'summary_tab_sel'); // переключение активной вкладки
  206. addClass('snapster_' + section, 'summary_tab_sel');
  207. }
  208. var postTemplate = this.postTemplate;
  209. var peopleTemplate = this.peopleTemplate;
  210. var fields = 'name,screen_name,photo_50,friend_status,verified';
  211. switch (section) {
  212. case 'hashtags':
  213. if (hashtag) {
  214. dApi.call('chronicle.getExploreSection', {
  215. 'section': 'hashtag',
  216. 'count': 30,
  217. 'start_from': next_from || 0,
  218. //'title': title,
  219. 'fields': fields,
  220. 'hashtag': hashtag
  221. }, vkopt_plugins[PLUGIN_ID].renderPosts);
  222. } else { // Популярные хэштеги
  223. if (!next_from) for (var i = 0; i < this.hashtags.length; i++) {
  224. var photo = this.hashtags[i].photo.top_photo;
  225. var oid = photo.split('_')[0];
  226. var pid = photo.split('_')[1];
  227. this.createNode(postTemplate, {
  228. owner_id: oid,
  229. post_id: '1_' + oid,
  230. photo_id: photo,
  231. photoLike_id: oid + '_photo' + pid,
  232. src_big: this.hashtags[i].photo.src,
  233. name_link: '<a class="hashtag" href="feed?section=snapster&sub=hashtags&hashtag=' + this.hashtags[i].hashtag +
  234. '" onclick="return vkopt_plugins[\'' + PLUGIN_ID + '\'].switchSection(\'hashtags\',\'' + this.hashtags[i].hashtag +
  235. '\');">' + this.hashtags[i].hashtag + '</a>',
  236. aid: 0,
  237. avatar: '/images/chronicle/icon_' + (i % 5 + 1) + '.png', // В качестве аватара - картинка из набора иконок snapster
  238. height: this.hashtags[i].photo.height * 537 / this.hashtags[i].photo.width,
  239. width: 537
  240. });
  241. }
  242. this.afterLoad('');
  243. }
  244. break;
  245. case 'people':
  246. dApi.call('chronicle.getExploreSection', {
  247. 'section': section,
  248. 'start_from': next_from || 0,
  249. //'count': count,
  250. 'fields': 'name,photo_50,friend_status,verified'
  251. }, function (r, response) {
  252. // Рендер постов-людей
  253. for (var i = 0; i < response.items.length; i++) {
  254. var item = response.items[i];
  255. vkopt_plugins[PLUGIN_ID].createNode(peopleTemplate,{
  256. owner_id: item.profile.uid,
  257. post_id: '1_' + item.profile.uid + '_' + item.photos[0].created,
  258. photo_id1: item.photos[0].owner_id + '_' + item.photos[0].pid,
  259. photo_id2: item.photos[1].owner_id + '_' + item.photos[1].pid,
  260. photo_id3: item.photos[2].owner_id + '_' + item.photos[2].pid,
  261. photo_id4: item.photos[3].owner_id + '_' + item.photos[3].pid,
  262. src_big1: item.photos[0].src_big,
  263. src_big2: item.photos[1].src_big,
  264. src_big3: item.photos[2].src_big,
  265. src_big4: item.photos[3].src_big,
  266. name: item.profile.first_name+' '+item.profile.last_name,
  267. size: vkopt_plugins[PLUGIN_ID].PEOPLE_PHOTO_SIZE,
  268. avatar: item.profile.photo_50,
  269. friend_status: item.profile.friend_status ? '<span class="explain">('+vkopt_plugins[PLUGIN_ID].friend_statuses[item.profile.friend_status]+')</span>' : '',
  270. verified: item.profile.verified ? '<span class="vk_profile_verified"></span>' : ''
  271. });
  272. }
  273. vkopt_plugins[PLUGIN_ID].afterLoad(response.next_from);
  274. });
  275. break;
  276. case 'people_list':
  277. dApi.call('chronicle.getExploreSection', {
  278. 'section': section,
  279. 'start_from': next_from || 0,
  280. //'count': 12,
  281. 'fields': fields+',photo_100,photo_200,photo_400_orig,sex,status,photo_id'
  282. }, function (r, response) {
  283. //Рендер постов-людей
  284. for (var i = 0; i < response.profiles.length; i++) {
  285. var item = response.profiles[i];
  286. vkopt_plugins[PLUGIN_ID].createNode(postTemplate, {
  287. owner_id: item.uid,
  288. post_id: '1_' + item.uid,
  289. photo_id: item.photo_id || item.uid+'_0',
  290. photoLike_id: item.photo_id ? item.photo_id.replace('_','_photo') : item.uid+'_photo0',
  291. text: vkopt_plugins[PLUGIN_ID].processHashtags(item.status),
  292. src_big: item.photo_400_orig || item.photo_200 || item.photo_100 || item.photo_50,
  293. name_link: '<a class="author" href="/id'+item.uid+'">'+item.first_name+' '+item.last_name+'</a>',
  294. aid: '0',
  295. avatar: item.photo_50,
  296. friend_status: item.friend_status ? '<span class="explain">('+vkopt_plugins[PLUGIN_ID].friend_statuses[item.friend_status]+')</span>' : '',
  297. verified: item.verified ? '<span class="vk_profile_verified"></span>' : '',
  298. height: 300
  299. });
  300. }
  301. vkopt_plugins[PLUGIN_ID].afterLoad(response.next_from);
  302. });
  303. break;
  304. case 'feed':
  305. if (next_from === undefined) {
  306. ge('feed_rows').appendChild(vkCe('div', {id: 'vk_snapster_own'}));
  307. stManager.add(['ui_controls.js'],function() {
  308. new Checkbox(ge("vk_snapster_own"), {
  309. checked: window.vk_snapster_own,
  310. label: 'Только из Snapster',
  311. onChange: function (state) {
  312. window.vk_snapster_own = (state == 1);
  313. vkopt_plugins[PLUGIN_ID].switchSection(section);
  314. }
  315. });
  316. });
  317. }
  318. dApi.call('chronicle.getFeed', {
  319. 'likes_count': 0,
  320. 'start_from': next_from || 0,
  321. 'count': 10,
  322. 'own': intval(window.vk_snapster_own),
  323. 'fields': fields,
  324. 'v': '5.13'
  325. }, function (r, response) {
  326. var profiles = {}; // Более удобный объект с профилями
  327. for (var i = 0, profLen=response.profiles.length; i < profLen; i++)
  328. profiles[response.profiles[i].id] = response.profiles[i];
  329. // Рендер постов
  330. for (var j = 0, itemsLen = response.items.length; j < itemsLen; j++) {
  331. var item = response.items[j];
  332. for (var i = 0, photosLen = item.photos.items.length; i < photosLen; i++) {
  333. var photo = item.photos.items[i];
  334. vkopt_plugins[PLUGIN_ID].createNode(postTemplate, {
  335. owner_id: item.source_id,
  336. post_id: '1_' + item.source_id + '_' + item.post_id,
  337. photo_id: photo.owner_id + '_' + photo.id,
  338. photoLike_id: photo.owner_id + '_photo' + photo.id,
  339. text: vkopt_plugins[PLUGIN_ID].processHashtags(photo.text),
  340. src_big: photo.sizes[photo.sizes.length-1].src,
  341. name_link: '<a class="author" href="/' + profiles[item.source_id].screen_name + '">' + profiles[item.source_id].first_name + ' ' + profiles[item.source_id].last_name + '</a>',
  342. date: dateFormat(photo.date * 1000, "dd.mm.yyyy HH:MM:ss"),
  343. aid: ((photo.album_id || '0000')+'').replace('-61','00000').replace('-62','0000').replace('-6','0').replace('-7','00').replace('-15','000'),
  344. likes: photo.likes ? photo.likes.count : '',
  345. mylike: photo.likes && photo.likes.user_likes ? 'my_like' : '',
  346. avatar: profiles[item.source_id].photo_50,
  347. verified: profiles[item.source_id].verified ? '<span class="vk_profile_verified"></span>' : '',
  348. place: photo.lat ? vkopt_plugins[PLUGIN_ID].placeTemplate.replace(/\{lat\}/g, photo.lat)
  349. .replace(/\{long\}/g, photo.long)
  350. .replace(/\{place\}/g, photo.place || ''):'',
  351. filter: photo.has_filter ? ' | <a onclick="vkopt_plugins[\'' + PLUGIN_ID + '\'].filterInfo(' + photo.owner_id + ',' + photo.id + ');">О фильтре</a>' : '',
  352. height: photo.sizes[photo.sizes.length-1].height * 537 / photo.sizes[photo.sizes.length-1].width,
  353. width: 537
  354. });
  355. }
  356. }
  357. vkopt_plugins[PLUGIN_ID].afterLoad(response.next_from || '');
  358. });
  359. break;
  360. case 'messages':
  361. dApi.call('chronicle.getMessages', {}, function (r, response) {
  362. stManager.add('im.css'); // Нужно для картиночки с таймером
  363. var new_response = {items: [], profiles: response.profiles};
  364. for (var i = 1, itemsLen = response[0]; i <= itemsLen; i++) { // Перегруппировка данных для использования функции рендера постов
  365. if (response[i].timer)
  366. response[i].photo.text = '<div class="im_row_attach" title="Самоудаляющаяся фотография"><div class="im_attach_chronicle"></div>' + response[i].timer + ' сек.</div>' + response[i].photo.text;
  367. new_response.items[i - 1] = response[i].photo;
  368. }
  369. vkopt_plugins[PLUGIN_ID].renderPosts(r, new_response);
  370. });
  371. break;
  372. case 'feedback':
  373. dApi.call('chronicle.getFeedback', {
  374. 'count': 20,
  375. 'start_from': next_from || 0,
  376. 'fields': fields
  377. }, function (r, response) {
  378. var profiles = {}; // Более удобный объект с профилями
  379. for (var i = 0, profLen = response.profiles.length; i < profLen; i++)
  380. profiles[response.profiles[i].uid] = response.profiles[i];
  381. var new_response = {items: [], profiles: response.profiles};
  382. for (var i = 0, itemsLen = response.items.length; i < itemsLen; i++) { // Перегруппировка данных для использования функции рендера постов
  383. new_response.items[i] = response.items[i].photo || {};
  384. new_response.items[i].created = response.items[i].date;
  385. switch (response.items[i].type) {
  386. case 'snap_received':
  387. var profile = profiles[response.items[i].users[0]];
  388. new_response.items[i].text = 'Входящее от ' +
  389. '<a class="author" href="/' + profile.screen_name + '">' + profile.first_name + ' ' + profile.last_name + '</a><br/>' +
  390. new_response.items[i].text;
  391. break;
  392. case 'snap_sent':
  393. var profile = profiles[response.items[i].actions[0].user_id];
  394. new_response.items[i].text = 'Исходящее для ' +
  395. '<a class="author" href="/' + profile.screen_name + '">' + profile.first_name + ' ' + profile.last_name + '</a> (' + (response.items[i].actions[0].state == 'view' ? '' : 'не ') + 'прочитано)<br/>' +
  396. new_response.items[i].text;
  397. new_response.profiles.push({
  398. uid: response.items[i].photo.owner_id,
  399. first_name: 'Я',
  400. last_name: '',
  401. screen_name: 'id'+vk.id,
  402. photo_50: '/images/address_icon.gif'
  403. });
  404. break;
  405. case 'follow':
  406. case 'friend_accepted':
  407. var profile = profiles[response.items[i].users[0]];
  408. new_response.items[i].owner_id = response.items[i].users[0];
  409. new_response.items[i].height = 1;
  410. new_response.items[i].text = 'Новый подписчик: ' +
  411. '<a class="author" href="/' + profile.screen_name + '">' + profile.first_name + ' ' + profile.last_name + '</a> (type: ' + response.items[i].type + ')';
  412. break;
  413. case 'comment_photo':
  414. new_response.items[i].owner_id = response.items[i].feedback_comment.from_id
  415. || response.items[i].feedback_comment.owner_id
  416. || response.items[i].feedback_comment.uid;
  417. new_response.items[i].width_override = 100;
  418. new_response.items[i].text = response.items[i].feedback_comment.text;
  419. break;
  420. case 'like_photo':
  421. var profile = profiles[response.items[i].users[0]];
  422. new_response.items[i].text = 'Лайк от ' +
  423. '<a class="author" href="/' + profile.screen_name + '">' + profile.first_name + ' ' + profile.last_name + '</a><br/>';
  424. new_response.items[i].width_override = 100;
  425. break;
  426. default:
  427. console.log(response.items[i]);
  428. new_response.items[i].text = 'default! see console!<br/>' + new_response.items[i].text;
  429. }
  430. }
  431. vkopt_plugins[PLUGIN_ID].renderPosts(r, new_response);
  432. });
  433. break;
  434. case 'search': // Поиск
  435. if (next_from === undefined) { // Не подгрузка. Создание поля для ввода поискового запроса.
  436. ge('feed_rows').appendChild(vkCe('input', {
  437. 'type': 'text',
  438. 'class': 'text search',
  439. 'style': 'width: 95%',
  440. 'placeholder': IDL('mMaS'),
  441. 'value': hashtag || '',
  442. 'onkeyup': 'if (event.keyCode == 10 || event.keyCode == 13) vkopt_plugins[\'' + PLUGIN_ID + '\'].switchSection(\'' + section + '\',val(this))'
  443. }));
  444. }
  445. if (hashtag) // hashtag - поисковый запрос
  446. dApi.call('chronicle.search', {
  447. 'q': hashtag,
  448. 'start_from': next_from || 0,
  449. 'count': 10,
  450. 'fields': fields
  451. }, vkopt_plugins[PLUGIN_ID].renderPosts);
  452. else
  453. vkopt_plugins[PLUGIN_ID].afterLoad('');
  454. break;
  455. case 'add': //Добавить
  456. if (vVersion < 232)
  457. vkMsg("Добавление фотографий возможно только при использовании VkOpt версии 2.3.2 и выше", 7000);
  458. var html = '<h2>' + IDL('add') + '</h2><table id="snapster_add_table">\
  459. <tr><td>Описание:</td><td> <input type="text" class="text" id="snapster_add_caption" placeholder="Описание..."></td></tr>\
  460. <tr><td>Фильтр:</td><td> <input type="text" class="text" id="snapster_add_filter" placeholder="В формате oid_fid"></td></tr>\
  461. <tr><td>Получатели:</td><td> <input type="text" class="text" id="snapster_add_message" placeholder="id через запятую (для отправки личным сообщением)"></td></tr>\
  462. <tr><td>Таймер:</td><td> <input type="text" class="text" id="snapster_add_timer" placeholder="Количество секунд (для самоуничтожающейся фотографии)"></td></tr>\
  463. <tr><td>Файл:</td><td><input type="file" class="text" id="fakeupload"></td></table>\
  464. <div id="snapster_add_invk"></div><div id="snapster_add_wall"></div>\
  465. <center><button id="snapster_add_button" class="flat_button">Отправить</button></center>';
  466. ge('feed_rows').appendChild(vkCe('div', {}, html));
  467. stManager.add(['ui_controls.js'],function(){
  468. new Checkbox(ge("snapster_add_wall"), {
  469. checked: false,
  470. label: 'Опубликовать на стене'
  471. });
  472. new Checkbox(ge("snapster_add_invk"), {
  473. checked: true,
  474. label: 'Добавить во ВКонтакте'
  475. });
  476. });
  477. dApi.call('chronicle.getUploadServer', {}, function (r, response) {
  478. ge('snapster_add_button').onclick = function () {
  479. vkopt_plugins[PLUGIN_ID].submitFile(response.upload_url);
  480. };
  481. vkopt_plugins[PLUGIN_ID].afterLoad('');
  482. });
  483. break;
  484. default : // 'recommended', 'popular_country', other...
  485. dApi.call('chronicle.getExploreSection', {
  486. 'section': section,
  487. 'count': 20,
  488. 'start_from': next_from || 0,
  489. 'fields': fields
  490. }, vkopt_plugins[PLUGIN_ID].renderPosts);
  491. break;
  492. }
  493. document.title = 'Snapster - '+section+(hashtag ? ' - '+hashtag : '');
  494. nav.setLoc({'0':'feed','section':'snapster','sub':section,'hashtag':hashtag});
  495. return false;
  496. },
  497. renderPosts: function (r, response) { // Рендеринг постов в категориях "Популярное", "Рекомендации" и "Конкретный хештег"
  498. var profiles = {}; // Более удобный объект с профилями
  499. for (var i = 0, profLen = response.profiles.length; i < profLen; i++)
  500. profiles[response.profiles[i].uid] = response.profiles[i];
  501. // Рендер постов
  502. for (var i = 0, itemsLen = response.items.length; i < itemsLen; i++) {
  503. var item = response.items[i];
  504. // Комменты
  505. var comments = '';
  506. if (item.comments) {
  507. comments = '<div class="clear"><div id="replies'+item.owner_id + '_' + item.pid+'">';
  508. for (var j=item.comments.length-1;j>0;j--)
  509. if (item.comments[j].text)
  510. comments+='<div class="reply" style="padding:3px">'+
  511. '<div class="reply_table">'+
  512. '<a class="reply_image" href="/id'+profiles[item.comments[j].uid].screen_name+'" style="margin:3px">'+
  513. '<img src="'+profiles[item.comments[j].uid].photo_50+'" class="reply_image" height="50" width="50">'+
  514. '</a>'+
  515. '<div class="reply_info">'+
  516. '<div class="reply_text">'+
  517. '<a class="author" href="/'+profiles[item.comments[j].uid].screen_name+'">'+profiles[item.comments[j].uid].first_name+' '+profiles[item.comments[j].uid].last_name+'</a>'+
  518. '<div class="wall_reply_text">'+vkopt_plugins[PLUGIN_ID].processHashtags(item.comments[j].text)+'</div>'+
  519. '</div>'+
  520. '<div class="info_footer sm">'+
  521. dateFormat(item.comments[j].date * 1000, "dd.mm.yyyy HH:MM")+
  522. '</div>'+
  523. '</div>'+
  524. '</div>'+
  525. '</div>';
  526. comments+='</div></div>';
  527. }
  528. if (item && typeof item == 'object') vkopt_plugins[PLUGIN_ID].createNode(vkopt_plugins[PLUGIN_ID].postTemplate, {
  529. owner_id: item.owner_id,
  530. post_id: '1_' + item.owner_id + '_' + item.created,
  531. photo_id: item.owner_id + '_' + item.pid,
  532. photoLike_id: item.owner_id + '_photo' + item.pid,
  533. text: vkopt_plugins[PLUGIN_ID].processHashtags(item.text),
  534. src_big: item.src_big || item.src || item.src_blur,
  535. name_link: '<a class="author" href="/' + profiles[item.owner_id].screen_name + '">' + profiles[item.owner_id].first_name + ' ' + profiles[item.owner_id].last_name + '</a>',
  536. date: dateFormat(item.created * 1000, "dd.mm.yyyy HH:MM:ss"),
  537. aid: ((item.aid || '0000')+'').replace('-61','00000').replace('-62','0000').replace('-6','0').replace('-7','00').replace('-15','000'),
  538. likes: item.likes ? item.likes.count : '',
  539. mylike: item.likes && item.likes.user_likes ? 'my_like' : '',
  540. avatar: profiles[item.owner_id].photo_50,
  541. friend_status: profiles[item.owner_id].friend_status ? '<span class="explain">('+vkopt_plugins[PLUGIN_ID].friend_statuses[profiles[item.owner_id].friend_status]+')</span>' : '',
  542. verified: profiles[item.owner_id].verified ? '<span class="vk_profile_verified"></span>' : '',
  543. place: item.lat ? vkopt_plugins[PLUGIN_ID].placeTemplate.replace(/\{lat\}/g, item.lat)
  544. .replace(/\{long\}/g, item.long)
  545. .replace(/\{place\}/g, item.place || ''):'',
  546. comments: comments,
  547. filter: item.has_filter ? ' | <a onclick="vkopt_plugins[\''+PLUGIN_ID+'\'].filterInfo('+item.owner_id+','+item.pid+');">О фильтре</a>' : '',
  548. height: (item.height || 537) * (item.width_override || 537) / (item.width || 537),
  549. width: item.width_override || 537
  550. });
  551. }
  552. vkopt_plugins[PLUGIN_ID].afterLoad(response.next_from);
  553. },
  554. afterLoad: function(next_from) {
  555. hide('feed_progress');
  556. hide('show_more_progress');
  557. cur.isFeedLoading = false;
  558. if (next_from) { // Если есть ещё
  559. this.next_from = next_from;
  560. show('show_more_link');
  561. hide('all_shown');
  562. } else { // Если больше нет
  563. hide('show_more_link');
  564. show('all_shown');
  565. }
  566. cur.idleManager.isIdle=false; // для правильной работы обработчика события scroll
  567. },
  568. submitFile: function (server_url) {
  569. vkLdr.show();
  570. function toFormData(data) {
  571. var header = '--' + boundary + '\r\nContent-Disposition: form-data; name="file1"; filename="file1.jpg"\r\nContent-Type: image/jpeg\r\n\r\n';
  572. var footer = '\r\n--' + boundary + '--\r\n';
  573. var result = [];
  574.  
  575. var str2bytes = function (str) {
  576. for (var i = 0, strLen=str.length; i < strLen; i++) {
  577. result.push(str.charCodeAt(i) & 0xff);
  578. }
  579. };
  580.  
  581. str2bytes(header);
  582. for (var i = 0, dataLen = data.length; i < dataLen; i++) {
  583. result.push(data[i]);
  584. }
  585. str2bytes(footer);
  586.  
  587. return result;
  588. }
  589.  
  590. var file = ge('fakeupload').files[0];
  591. if (file) {
  592. var reader = new FileReader();
  593. var boundary = "--snapster.user.js--by--pmmlabs";
  594. reader.onload = function (e) {
  595. vk_aj.ajax({
  596. method: 'POST',
  597. headers: {'Content-type': 'multipart/form-data; boundary=' + boundary},
  598. url: server_url,
  599. data: toFormData(new Uint8Array(e.target.result))
  600. }, function (a) {
  601. var saveInfo = JSON.parse(a.text);
  602. dApi.call('chronicle.save', {
  603. server: saveInfo.server,
  604. hash: saveInfo.hash,
  605. photos_list: saveInfo.photos_list,
  606. caption: val(ge('snapster_add_caption')),
  607. filter: val(ge('snapster_add_filter')),
  608. //comment: 1, // Что даёт? непонятно. Но что-то даёт.
  609. wall: val(ge('snapster_add_wall')),
  610. no_vk: intval(!val(ge('snapster_add_invk'))),
  611. timer: val(ge('snapster_add_timer')),
  612. mail: intval(!!val(ge('snapster_add_message'))),
  613. mail_to: val(ge('snapster_add_message'))
  614. }, function (r, response) {
  615. vkLdr.hide();
  616. if (response[0].is_mail)
  617. vkMsg('Фотография отправлена');
  618. else
  619. showPhoto(response[0].owner_id + '_' + response[0].pid, 'album' + response[0].owner_id + '_' + response[0].aid, {});
  620. });
  621. }
  622. );
  623. };
  624. reader.readAsArrayBuffer(file);
  625. } else
  626. vkMsg('Не выбран файл');
  627. }
  628. };
  629. if (window.vkopt_ready) vkopt_plugin_run(PLUGIN_ID);
  630. })();