moe-video.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /** moe-video enabler component **/
  2. (function() {
  3. // state
  4. let moeOTSession = false;
  5. let moePublisher = false;
  6. let moeVideoElement = false;
  7. let moeArchiveID = false;
  8. let uploadOrRecord = 'record';
  9. // initialize elements, events
  10. function init(_container = false) {
  11. $('[moe-video-progress]').remove();
  12. $('<div/>')
  13. .attr('moe-video-progress', 1)
  14. .append('<img src="/img/loading.svg">')
  15. .appendTo('body');
  16. _container = $(_container || 'body');
  17. _container
  18. .find('[moe-video]:not([moe-initialized])')
  19. .each(function() {
  20. // assign a unique id
  21. let moeID = Math.ceil(Math.random() * 1000000);
  22. $(this).attr('moe-id', moeID);
  23. var buttonsDiv = $('<div class="d-flex flex-wrap" />').appendTo(this);
  24. // record button
  25. $('<button type="button" />')
  26. .addClass('btn btn-sm btn-info ml-2')
  27. .attr('moe-show-popup', 1)
  28. .html('<i class="fa fa-video"></i>')
  29. .appendTo(buttonsDiv);
  30. // mark as initialized
  31. $(this).attr('moe-initialized', 1);
  32. });
  33. // bind events
  34. $(document).off('click.moe-show-popup', '[moe-video] [moe-show-popup]')
  35. $(document).on('click.moe-show-popup', '[moe-video] [moe-show-popup]', function() {
  36. initVideoPopup();
  37. moeVideoElement = $(this).closest('[moe-video]');
  38. let title = moeVideoElement.find('p').first().text();
  39. if(!title) {
  40. title = moeVideoElement.attr('data-title');
  41. }
  42. $('[moe-video-backdrop]')
  43. .css('display', 'flex')
  44. .find('.card-title').first().text(title);
  45. });
  46. $(document).off('change.moe-video-file', '[moe-video-file]')
  47. $(document).on('change.moe-video-file', '[moe-video-file]', function() {
  48. // check if video
  49. let file = this.files[0];
  50. if(file.type !== 'video/mp4' && file.type !== 'video/mpeg' && file.type !== 'video/webm') {
  51. toastr.error('Invalid video file!');
  52. return false;
  53. }
  54. let blobURL = URL.createObjectURL(file);
  55. $('[moe-video-player]')
  56. .attr('src', blobURL)
  57. .removeClass('d-none')
  58. .addClass('d-block');
  59. $('.upload-or-record')
  60. .removeClass('d-block')
  61. .addClass('d-none');
  62. $('[moe-play-recording]').show();
  63. $('[moe-submit-recording]').show();
  64. $('[moe-close-popup]').show();
  65. uploadOrRecord = 'upload';
  66. return false;
  67. });
  68. $(document).off('click.moe-choose-record', '[moe-choose-record]')
  69. $(document).on('click.moe-choose-record', '[moe-choose-record]', function() {
  70. initCamera(moeVideoElement);
  71. uploadOrRecord = 'record';
  72. return false;
  73. });
  74. $(document).off('click.moe-start-recording', '[moe-video-backdrop] [moe-start-recording]')
  75. $(document).on('click.moe-start-recording', '[moe-video-backdrop] [moe-start-recording]', function() {
  76. startWait();
  77. $('[moe-video-player]')
  78. .attr('src', '')
  79. .removeClass('d-block')
  80. .addClass('d-none');
  81. $('[moe-video-canvas]')
  82. .removeClass('d-none')
  83. .addClass('d-block');
  84. $.post('/start/' + moeVideoElement.attr('moe-ot-session-id'));
  85. });
  86. $(document).off('click.moe-stop-recording', '[moe-video-backdrop] [moe-stop-recording]')
  87. $(document).on('click.moe-stop-recording', '[moe-video-backdrop] [moe-stop-recording]', function() {
  88. startWait();
  89. $.post('stop/' + moeArchiveID).done(function () {
  90. $.ajax('/archive/'+ moeArchiveID, {
  91. success: function (data) { // success callback function
  92. stopWait();
  93. $('[moe-video-player]')
  94. .attr('src', data.video)
  95. .removeClass('d-none')
  96. .addClass('d-block');
  97. $('[moe-video-canvas]')
  98. .removeClass('d-block')
  99. .addClass('d-none');
  100. $('[moe-start-recording]').show();
  101. $('[moe-stop-recording]').hide();
  102. $('[moe-play-recording]').show();
  103. $('[moe-restart-recording]').hide();
  104. $('[moe-submit-recording]').show();
  105. $('[moe-close-popup]').show();
  106. }
  107. });
  108. });
  109. });
  110. $(document).off('click.moe-play-recording', '[moe-video-backdrop] [moe-play-recording]')
  111. $(document).on('click.moe-play-recording', '[moe-video-backdrop] [moe-play-recording]', function() {
  112. $('[moe-video-player]')[0].play();
  113. });
  114. $(document).off('click.moe-submit-recording', '[moe-video-backdrop] [moe-submit-recording]')
  115. $(document).on('click.moe-submit-recording', '[moe-video-backdrop] [moe-submit-recording]', function() {
  116. //TODO: remove closest('body')
  117. var toProUid = $('[moe-submit-recording]').closest('body').find('[to-pro-uid]').attr('to-pro-uid');
  118. var regardingClientUid = $('[moe-submit-recording]').closest('body').find('[regarding-client-uid]').attr('regarding-client-uid');
  119. if(uploadOrRecord === 'upload') {
  120. startWait();
  121. let formData = new FormData();
  122. formData.set('regardingClientUid', regardingClientUid);
  123. formData.set('contentText', '');
  124. let videoInput = $('[moe-submit-recording]').closest('body').find('[moe-video-file]').first()[0];
  125. formData.append('messageVideo', videoInput.files[0]);
  126. $.ajax('/api/internalMessage/create', {
  127. dataType: 'json',
  128. data: formData,
  129. processData: false,
  130. contentType: false,
  131. type: 'POST',
  132. }).done(function (_data) {
  133. if(!hasResponseError(_data)) {
  134. window.top.location.reload();
  135. }
  136. });
  137. return false;
  138. }
  139. else if(uploadOrRecord === 'record') {
  140. startWait();
  141. $.post('/save-archive', {
  142. archiveID: moeArchiveID,
  143. toProUid: toProUid,
  144. regardingClientUid: regardingClientUid,
  145. contentText: '',
  146. }, function(_data) {
  147. stopWait();
  148. if(!!moeOTSession) {
  149. moeOTSession.disconnect();
  150. moeOTSession = false;
  151. moePublisher = false;
  152. }
  153. // $('[moe-video-backdrop]').css('display', 'none');
  154. window.top.location.reload();
  155. }, 'json');
  156. }
  157. return false;
  158. });
  159. $(document).off('click.moe-close-popup', '[moe-video-backdrop] [moe-close-popup]')
  160. $(document).on('click.moe-close-popup', '[moe-video-backdrop] [moe-close-popup]', function() {
  161. if(!!moeOTSession) {
  162. moeOTSession.disconnect();
  163. moeOTSession = false;
  164. moePublisher = false;
  165. }
  166. $('[moe-video-backdrop]').css('display', 'none');
  167. });
  168. $(document).off('change.input-file', '[moe-video] .input-file')
  169. $(document).on('change.input-file', '[moe-video] .input-file', function() {
  170. $(this).closest('form').submit();
  171. });
  172. }
  173. function initVideoPopup() {
  174. $('[moe-video-backdrop]').remove();
  175. $('<div moe-video-backdrop>' +
  176. '<div class="card mcp-theme-1">' +
  177. '<div class="card-header py-2">' +
  178. '<div class="card-title text-center"></div>' +
  179. '</div>' +
  180. '<div class="card-body">' +
  181. '<div class="upload-or-record">' +
  182. '<div class="d-flex align-items-center justify-content-center">' +
  183. '<button type="button" class="btn btn-primary font-weight-bold mr-3 position-relative overflow-hidden" moe-choose-upload>' +
  184. '<i class="fa fa-file-upload mr-2"></i>Upload' +
  185. '<input type="file" moe-video-file class="position-absolute opacity-0x" style="bottom: 0; right: 0">' +
  186. '</button>' +
  187. '<button type="button" class="btn btn-primary font-weight-bold" moe-choose-record>' +
  188. '<i class="fa fa-video mr-2"></i>Record' +
  189. '</button>' +
  190. '</div>' +
  191. '</div>' +
  192. '<div class="access-information d-none">' +
  193. '<p>Setting up camera and microphone access...</p>' +
  194. '<p class="click-allow d-none">Click "Allow" to allow hardware access and begin the recording</p>' +
  195. '<div class="denied d-none">' +
  196. '<p class="text-danger"><b>It appears we do not have access to your input hardware.</b></p>' +
  197. '<p class="font-weight-normal">Please allow access to your hardware by clicking the ' +
  198. '<img class="border border-dark" src="/img/denied.png" alt=""> icon ' +
  199. 'towards the far right of the browser address bar. <b>Once done, please click <a href="">here</a> to refresh the page and retry.</b></p>' +
  200. '</div>' +
  201. '</div>' +
  202. '<div moe-video-canvas class="d-none"></div>' +
  203. '<video moe-video-player class="d-none" controls><source src="" type="video/mp4"></video>' +
  204. '</div>' +
  205. '<div class="card-footer">' +
  206. '<div class="d-flex align-items-center justify-content-center">' +
  207. '<button type="button" class="btn btn-danger btn-sm mr-3" moe-start-recording style="display: none">' +
  208. '<i class="fa fa-video mr-2"></i>Record' +
  209. '</button>' +
  210. '<button type="button" class="btn btn-danger btn-sm mr-3" moe-stop-recording style="display: none">' +
  211. '<i class="fa fa-stop mr-2"></i>Stop' +
  212. '</button>' +
  213. '<button type="button" class="btn btn-info btn-sm mr-3" moe-play-recording style="display: none">' +
  214. '<i class="fa fa-play mr-2"></i>Play' +
  215. '</button>' +
  216. '<button type="button" class="btn btn-info btn-sm mr-3" moe-restart-recording style="display: none">' +
  217. '<i class="fa fa-play mr-2"></i>Play' +
  218. '</button>' +
  219. '<button type="button" class="btn btn-success btn-sm mr-3" moe-submit-recording style="display: none">' +
  220. '<i class="fa fa-check mr-2"></i>Submit' +
  221. '</button>' +
  222. '<button type="button" class="btn btn-default bg-light border border-default btn-sm mr-3" moe-close-popup style="display: none">' +
  223. '<i class="fa fa-times mr-2"></i>Close' +
  224. '</button>' +
  225. '<span moe-wait>Please wait...</span>' +
  226. '</div>' +
  227. '</div>' +
  228. '</div>' +
  229. '</div>').appendTo('body');
  230. }
  231. function startWait() {
  232. $('[moe-start-recording]').hide();
  233. $('[moe-stop-recording]').hide();
  234. $('[moe-play-recording]').hide();
  235. $('[moe-restart-recording]').hide();
  236. $('[moe-submit-recording]').hide();
  237. $('[moe-close-popup]').hide();
  238. $('[moe-wait]').show();
  239. $('[moe-video-progress]').removeClass('d-none').addClass('d-flex');
  240. }
  241. function stopWait() {
  242. $('[moe-wait]').hide();
  243. $('[moe-video-progress]').removeClass('d-flex').addClass('d-none');
  244. }
  245. // init ot, publisher and show cam feed
  246. function initCamera(_moeVideoElement) {
  247. startWait();
  248. $('.upload-or-record').removeClass('d-block').addClass('d-none');
  249. $('.access-information').removeClass('d-none').addClass('d-block');
  250. // cleanup
  251. if(!!moeOTSession) {
  252. moeOTSession.disconnect();
  253. moeOTSession = false;
  254. moePublisher = false;
  255. }
  256. // create session (using sessionID given by the server)
  257. moeOTSession = OT.initSession(
  258. _moeVideoElement.attr('moe-ot-api-key'),
  259. _moeVideoElement.attr('moe-ot-session-id')
  260. );
  261. // init publisher
  262. moePublisher = OT.initPublisher(
  263. $('[moe-video-canvas]').first()[0],
  264. {
  265. //videoSource: 'screen', // comment out in prod
  266. width: 540
  267. }
  268. );
  269. // publisher events
  270. moePublisher.on({
  271. accessAllowed: function (event) {
  272. // The user has granted access to the camera and mic.
  273. console.log('ALIX: accessAllowed');
  274. $('.access-information').removeClass('d-block').addClass('d-none');
  275. $('[moe-video-canvas]').removeClass('d-none').addClass('d-block');
  276. stopWait()
  277. },
  278. accessDenied: function accessDeniedHandler(event) {
  279. // The user has denied access to the camera and mic.
  280. console.log('ALIX: accessDenied');
  281. $('.click-allow').addClass('d-none');
  282. $('.denied').removeClass('d-none');
  283. stopWait()
  284. },
  285. accessDialogOpened: function (event) {
  286. // The Allow/Deny dialog box is opened.
  287. console.log('ALIX: accessDialogOpened');
  288. $('.click-allow').removeClass('d-none');
  289. stopWait()
  290. },
  291. accessDialogClosed: function (event) {
  292. // The Allow/Deny dialog box is closed.
  293. console.log('ALIX: accessDialogClosed');
  294. stopWait()
  295. }
  296. });
  297. // OT events
  298. moeOTSession.connect(_moeVideoElement.attr('moe-ot-client-token'), function(error) {
  299. if (error) {
  300. console.error('Failed to connect', error);
  301. } else {
  302. moeOTSession.publish(moePublisher, function(error) {
  303. if (error) {
  304. console.error('Failed to publish', error);
  305. }
  306. else {
  307. stopWait();
  308. $('[moe-start-recording]').show();
  309. $('[moe-stop-recording]').hide();
  310. $('[moe-play-recording]').hide();
  311. $('[moe-restart-recording]').hide();
  312. $('[moe-submit-recording]').hide();
  313. $('[moe-close-popup]').show();
  314. }
  315. });
  316. }
  317. });
  318. moeOTSession.on('archiveStarted', function(event) {
  319. stopWait();
  320. moeArchiveID = event.id;
  321. $('[moe-start-recording]').hide();
  322. $('[moe-stop-recording]').show();
  323. $('[moe-play-recording]').hide();
  324. $('[moe-restart-recording]').hide();
  325. $('[moe-submit-recording]').hide();
  326. $('[moe-close-popup]').hide();
  327. });
  328. moeOTSession.on('archiveStopped', function() {
  329. // moeArchiveID = false;
  330. // stopWait();
  331. // $('[moe-start-recording]').show();
  332. // $('[moe-stop-recording]').hide();
  333. // $('[moe-play-recording]').hide();
  334. // $('[moe-restart-recording]').hide();
  335. // $('[moe-submit-recording]').hide();
  336. // $('[moe-close-popup]').show();
  337. });
  338. }
  339. $(document).ready(function() {
  340. init();
  341. });
  342. })();