4pdaQmsRtc

Добавляет возможность передачи файлов напрямую между пользователями на сайте 4pda.ru

  1. // ==UserScript==
  2. // @id 4pda.ru-QMS-RTC@scriptish
  3. // @name 4pdaQmsRtc
  4. // @version 1.2
  5. // @namespace https://greatest.deepsurf.us/users/23
  6. // @author kilowatt36@4pda.ru
  7. // @description Добавляет возможность передачи файлов напрямую между пользователями на сайте 4pda.ru
  8. // @require https://cdn.firebase.com/js/client/1.0.6/firebase.js
  9. // @require https://greatest.deepsurf.us/scripts/348/code.user.js
  10. // @include http://4pda.ru/forum/index.php?act=qms*
  11. // @run-at document-end
  12. // ==/UserScript==
  13.  
  14. var $ = unsafeWindow.$; // нужен jQuery именно из библиотеки QMS, потому что потребуется управлять событиями, связанными с ajax
  15. window.Firebase = Firebase; // в библиотеке RTCMultiConnection автор обращается не к Firebase, а к window.Firebase. Так что вот так.
  16. var connection;
  17. var iwanttosendfile = false;
  18. var sessions = { };
  19. var progressHelper = { };
  20.  
  21. function printToChat(title, text) {// добавление ложного "нового сообщения" в чат
  22. var new_row = $(
  23. '<div data-toggle="checkbox" class="list-group-item">'+
  24. '<label class="check-item">'+
  25. '<input type="checkbox" data-toggle="class" data-event="change" data-event-init="1">'+
  26. '<span class="checkbox">'+
  27. '<i class="icon-uncheck"></i>'+
  28. '<i class="icon-check"></i>'+
  29. '</span>'+
  30. '</label>'+
  31. '<div class="time"><b class="read-status read big-dot"></b> '+(new Date())+' </div> <div class="avatar-wrap"><img alt="" class="avatar" data-scroll-init-load="1"></div> '+
  32. '<strong><span style="color:#FF9900">'+title+'</span></strong><br>'+
  33. '<div class="msg-content emoticons">'+
  34. (typeof text === 'string' ? text : '')+
  35. '</div>'+
  36. '</div>'
  37. );
  38. if (typeof text !== 'string') new_row.find('.msg-content').append(text);
  39. $('#scroll-thread .scrollframe-body .list-group-item').last().after(new_row);
  40. var pieces = $('#scroll-thread .scrollframe-body').css('transform').split(' '); // нужно
  41. pieces[5]=(pieces[5].split(')')[0] - 50)+')'; // для
  42. $('#scroll-thread .scrollframe-body').css('transform',pieces.join(' ')); // прокрутки
  43. }
  44.  
  45.  
  46. function HangHandlers_dialog() { // Создать UI и повесить все необходимые обработчики на экране диалога
  47. var inputFile = $('<input id="4pdaQmsRTC" type=file />');
  48. var button = $('<button id="4pdaQmsRTCbutton">Отправить</button>');
  49. $('#btn-bb-codes').before('Отправить файл: ').before(inputFile).before(button);
  50. button.click(function() { // Нажатие на кнопку "отправить"
  51. if (Object.getOwnPropertyNames(connection.peers).length === 1) { // Если еще ни одной сессии не было создано, создание сессии, после которой автоматом пойдет отправка файла
  52. var sessionName = '4pdaSession';
  53. connection.extra = {
  54. 'session-name': sessionName || 'Anonymous'
  55. };
  56. connection.sessionid = sessionName || 'Anonymous';
  57. connection.maxParticipantsAllowed = 1;
  58. printToChat('Отправка файла', 'Ожидание соединения с собеседником...');
  59. connection.open(connection.channel);
  60. iwanttosendfile = true;
  61. } else {// если сессия уже есть, просто отправить
  62. connection.send($('#4pdaQmsRTC')[0].files[0]);
  63. }
  64. });
  65. $('.icon-back-up').click(HangHandlers_topicchoise); // При щелчке на возврат надо опять расставлять обработчики
  66. }
  67.  
  68. function HangHandlers_topicchoise() { // Повесить все необходимые обработчики на экране выбора темы
  69. $(document).ajaxComplete(function() { // Чтобы повесить событие на НОВЫЙ список тем, который загрузится через ajax
  70. $('#threads-form .list-group-item.text-overflow').each(function(){
  71. $(this).click(function() { // щелчок по теме
  72. $(document).ajaxComplete(function() { // После загрузки окна диалога
  73. HangHandlers_dialog();
  74. $(document).off('ajaxComplete');
  75. });
  76. });
  77. });
  78. $(document).off('ajaxComplete'); // чтобы функция выше больше не срабатывала при каждом ajax запросе
  79. });
  80. }
  81.  
  82. function createConnection(peer) { // создание нового connection в соответствии с id собеседника
  83.  
  84. connection = new RTCMultiConnection([peer, $('.i-code').attr('href').split('=')[1]].sort().join('_'));
  85. connection.session = {
  86. data: true
  87. };
  88. connection.autoSaveToDisk = false;
  89. sessions = { };
  90. progressHelper = { };
  91. iwanttosendfile = false;
  92. connection.onNewSession = function(session) {
  93. if (sessions[session.sessionid]) return;
  94. sessions[session.sessionid] = session;
  95. console.log('new session: '+session.extra['session-name']);
  96. connection.join(session); // при каждом создании сессии присоединяемся к ней, не спрашивая разрешения (это же удобно!)
  97. };
  98.  
  99. connection.onmessage = function(e) {
  100. console.debug(e.userid, 'posted', e.data);
  101. console.log('latency:', e.latency, 'ms');
  102. };
  103.  
  104. connection.onclose = function(e) {
  105. console.log('Data connection is closed between you and ' + e.userid);
  106. };
  107.  
  108. connection.onleave = function(e) {
  109. console.log(e.userid + ' left the session.');
  110. };
  111.  
  112. // когда собеседник подтверждает наш запрос на присоединение к сесси (а это делается автоматически)
  113. connection.onopen = function() {
  114. if (iwanttosendfile) {
  115. connection.send($('#4pdaQmsRTC')[0].files[0]);
  116. $('#4pdaQmsRTC').val('');
  117. }
  118. iwanttosendfile = false;
  119. //console.log('connection.onopen');
  120. };
  121. function updateLabel(progress, label) {
  122. if (progress.position == -1) return;
  123. var position = +progress.position.toFixed(2).split('.')[1] || 100;
  124. label.innerHTML = position + '%';
  125. }
  126. connection.onFileProgress = function(chunk) {
  127. var helper = progressHelper[chunk.uuid];
  128. helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max;
  129. updateLabel(helper.progress, helper.label);
  130. };
  131. connection.onFileStart = function(file) {
  132. var div = document.createElement('div');
  133. div.title = file.name;
  134. div.innerHTML = '<label id="RTCLabel">0%</label> <progress></progress>';
  135. printToChat('Передача файла',div);
  136. progressHelper[file.uuid] = {
  137. div: div,
  138. progress: div.querySelector('progress'),
  139. label: div.querySelector('#RTCLabel')
  140. };
  141. progressHelper[file.uuid].progress.max = file.maxChunks;
  142. };
  143.  
  144. connection.onFileEnd = function(file) {
  145. progressHelper[file.uuid].div.innerHTML = '<a href="' + file.url + '" target="_blank" download="' + file.name + '">' + file.name + '</a>';
  146. };
  147.  
  148.  
  149.  
  150. connection.connect(connection.channel);
  151. }
  152.  
  153.  
  154. /////////////////////////////// MAIN ////////////////////////////////////////////
  155.  
  156. /* у firebase такая замечательна клиентская библиотека.
  157. Создает в документе скрытый iframe и грузит туда скрипт, вызывающий parent.window["somefunc"]
  158. Просто офигеть. Так что надо сделать перенаправлялочку для этих функций. (требуется только для работоспособности в юзерскрипте)
  159. */
  160. function inject_FbFunctions(funcnum) {
  161. if (typeof(window["pLPCommand"+funcnum]) == 'function' && typeof(window["pRTLPCB"+funcnum]) == 'function') {
  162. unsafeWindow.window["pLPCommand"+funcnum] = window["pLPCommand"+funcnum];
  163. unsafeWindow.window["pRTLPCB"+funcnum] = window["pRTLPCB"+funcnum];
  164. //console.log(funcnum+" ready");
  165. }
  166. else
  167. setTimeout(function(){inject_FbFunctions(funcnum);},50);
  168. }
  169.  
  170. for (var i=1; i<8; i++) { inject_FbFunctions(i); }
  171.  
  172. // Если пользователь зашел сразу на страницу диалога или выбора темы, то сразу создаем соединение.
  173. if (location.href.indexOf("&mid=") != -1) {
  174. createConnection(location.href.split("&mid=")[1].split("&")[0]);
  175. if (location.href.indexOf("&t=") != -1) {
  176. HangHandlers_dialog();
  177. } else {
  178. HangHandlers_topicchoise();
  179. }
  180. } else {
  181. createConnection('4pda-temp');
  182. }
  183.  
  184. // При щелчке на пользователя слева пусть создается соединение с этим пользователем.
  185. $('#contacts .list-group-item.text-overflow').each(function(){
  186. $(this).click(function() { // Щелчок по имени пользователя
  187. var peer = $(this).attr('data-member-id');
  188. createConnection(peer);
  189. if (peer > 0) { HangHandlers_topicchoise(); }
  190. });
  191. });