Igor Zhukov
7 years ago
22 changed files with 965 additions and 135 deletions
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -0,0 +1,64 @@ |
|||||||
|
<form class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length, composer_progress_enabled: draftMessage.inlineProgress, im_voice_recording: voiceRecorder.recording, im_processing_recording: voiceRecorder.processing}"> |
||||||
|
|
||||||
|
<div class="im_send_form_inline_results" my-inline-results="inlineResults"></div> |
||||||
|
|
||||||
|
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMsgID > 0"> |
||||||
|
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear(true)"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a> |
||||||
|
<a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMsgID" watch="true" edit="{{draftMessage.type == 'edit'}}"></a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_reply_wrap im_send_fwds_wrap" ng-if="draftMessage.fwdMessages.length > 0"> |
||||||
|
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.fwdsClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a> |
||||||
|
<div class="im_message_reply_wrap" my-forwarded-messages="draftMessage.fwdMessages"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_field_wrap hasselect" ng-class="historyState.replyKeyboard._ == 'replyKeyboardMarkup' ? 'im_send_field_wrap_2ndbtn' : ''"> |
||||||
|
<a class="composer_emoji_insert_btn"><i class="icon icon-emoji"></i></a> |
||||||
|
<div class="composer_progress_icon_wrap"> |
||||||
|
<div class="composer_progress_icon" my-arc-progress width="22" stroke="2.5"></div> |
||||||
|
</div> |
||||||
|
<a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && (!draftMessage.text.length || draftMessage.text == '/')" ng-mousedown="draftMessage.toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a> |
||||||
|
<a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="draftMessage.replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a> |
||||||
|
|
||||||
|
<div class="im_send_dropbox_wrap" my-i18n="im_photos_drop_text"></div> |
||||||
|
<div class="im_voice_recorder_wrap"> |
||||||
|
<div class="im_recorder_indicator"><i></i></div> |
||||||
|
<div class="im_recorder_time" ng-bind="voiceRecorder.duration | duration"></div> |
||||||
|
<div class="im_recorder_label" ng-switch="voiceRecorder.processing" my-i18n> |
||||||
|
<span ng-switch-when="true" my-i18n-format="im_voice_processing_label"></span> |
||||||
|
<span ng-switch-default my-i18n-format="im_voice_recording_label"></span> |
||||||
|
<my-i18n-param name="dots"></my-i18n-param> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<textarea ng-model="draftMessage.text" class="form-control im_message_field no_outline" dir="auto" ng-trim="false"></textarea> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_buttons_wrap clearfix"> |
||||||
|
<button type="submit" class="btn btn-md im_submit" ng-class="draftMessage.type == 'edit' ? 'im_submit_edit' : 'im_submit_send'"> |
||||||
|
<span class="im_submit_send_label nocopy" my-i18n="im_submit_message"></span> |
||||||
|
<span class="im_submit_edit_label nocopy" my-i18n="im_submit_edit_message"></span> |
||||||
|
</button> |
||||||
|
|
||||||
|
<div class="im_attach pull-left"> |
||||||
|
<input type="file" class="im_attach_input" size="28" multiple="multiple" title="{{'im_attach_file_title' | i18n}}" /> |
||||||
|
<i class="icon icon-paperclip"></i> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_media_attach pull-left"> |
||||||
|
<input type="file" class="im_media_attach_input" size="28" multiple="multiple" accept="image/*, video/*, audio/*" title="{{'im_media_attach_title' | i18n}}"/> |
||||||
|
<i class="icon icon-camera"></i> |
||||||
|
</div> |
||||||
|
|
||||||
|
<a class="im_record pull-left"> |
||||||
|
<i class="icon icon-mic"></i> |
||||||
|
</a> |
||||||
|
|
||||||
|
<div class="composer_emoji_panel"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_keyboard_wrap" ng-if="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-show="!historyState.replyKeyboard.pFlags.hidden"> |
||||||
|
<div my-reply-markup="historyState.replyKeyboard"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</form> |
@ -0,0 +1,152 @@ |
|||||||
|
<div class="chat_modal_wrap"> |
||||||
|
|
||||||
|
<div class="tg_page_head tg_modal_head"> |
||||||
|
<div class="navbar navbar-static-top navbar-inverse"> |
||||||
|
<div class="container"> |
||||||
|
|
||||||
|
<div class="navbar-toggle-wrap dropdown" dropdown ng-if="hasRights('edit_title') || hasRights('edit_photo')"> |
||||||
|
<a class="dropdown-toggle navbar-toggle" dropdown-toggle> |
||||||
|
<span class="icon-bar"></span> |
||||||
|
<span class="icon-bar"></span> |
||||||
|
<span class="icon-bar"></span> |
||||||
|
</a> |
||||||
|
<ul class="dropdown-menu"> |
||||||
|
<li ng-if="chatFull.chat.photo.photo_small" ng-if="hasRights('edit_photo')"> |
||||||
|
<a ng-click="deletePhoto()" my-i18n="group_modal_menu_delete_photo"></a> |
||||||
|
</li> |
||||||
|
<li ng-if="hasRights('edit_title')"> |
||||||
|
<a ng-click="editChannel()" ng-switch="isMegagroup"> |
||||||
|
<span ng-switch-when="true" my-i18n="group_modal_menu_edit_group"></span> |
||||||
|
<span ng-switch-default my-i18n="channel_modal_menu_edit"></span> |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="navbar-header"> |
||||||
|
|
||||||
|
<ul class="nav navbar-nav navbar-quick-nav"> |
||||||
|
<li> |
||||||
|
<a ng-click="$close()" class="navbar-quick-media-back"> |
||||||
|
<i class="icon icon-back"></i> |
||||||
|
<div class="navbar-quick-back-title" ng-switch="isMegagroup"> |
||||||
|
<h4> |
||||||
|
<span ng-switch-when="true" my-i18n="group_modal_info"></span> |
||||||
|
<span ng-switch-default my-i18n="channel_modal_info"></span> |
||||||
|
</h4> |
||||||
|
</div> |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="modal-body mobile_modal_body"> |
||||||
|
|
||||||
|
<div class="mobile_user_modal_photo_profile_wrap"> |
||||||
|
|
||||||
|
<a ng-click="openPhoto(chatFull.chat_photo.id, {p: -chatFull.chat.id})" class="mobile_user_modal_image_wrap pull-left" my-peer-photolink="::-chatFull.chat.id" img-class="mobile_user_modal_image mobile_chat_modal_image" no-open="true" watch="true" ng-class="{disabled: !chatFull.chat.photo.photo_small}" ng-disabled="!chatFull.chat.photo.photo_small"></a> |
||||||
|
|
||||||
|
<div class="mobile_user_modal_info_wrap clearfix"> |
||||||
|
<h4 class="mobile_user_modal_header" my-peer-link="-chatFull.chat.id"></h4> |
||||||
|
<p class="mobile_user_modal_status" ng-if="chatFull.participants_count > 0"> |
||||||
|
<ng-pluralize count="chatFull.participants_count" |
||||||
|
when="group_modal_pluralize_participants"> |
||||||
|
</ng-pluralize> |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_section" ng-if="chatFull.chat.username"> |
||||||
|
<h4 class="mobile_modal_section_header" my-i18n="channel_modal_share_link"></h4> |
||||||
|
<div class="mobile_modal_section_value"> |
||||||
|
<a class="settings_modal_username_link" ng-click="shareLink($event)" ng-bind="'https://t.me/' + chatFull.chat.username" ng-href="https://t.me/{{chatFull.chat.username}}" target="_blank"></a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_section" ng-if="!chatFull.chat.username && chatFull.chat.pFlags.creator"> |
||||||
|
<h4 class="mobile_modal_section_header" my-i18n="channel_modal_share_link"></h4> |
||||||
|
<div class="mobile_modal_section_value" ng-switch="chatFull.exported_invite._"> |
||||||
|
<a ng-switch-when="chatInviteExported" class="settings_modal_username_link" ng-click="shareLink($event)" ng-bind="chatFull.exported_invite.link" ng-href="{{chatFull.exported_invite.link}}" target="_blank"></a> |
||||||
|
<span ng-switch-default my-i18n="channel_modal_share_loading"> |
||||||
|
<my-i18n-param name="dots"><span my-loading-dots></span></my-i18n-param> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_section" ng-if="chatFull.rAbout"> |
||||||
|
<h4 class="mobile_modal_section_header" my-i18n="channel_modal_description"></h4> |
||||||
|
<div class="mobile_modal_section_value" ng-bind-html="chatFull.rAbout"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_action_wrap" ng-if="hasRights('edit_photo') && !photo.updating"> |
||||||
|
<span class="mobile_modal_action mobile_modal_upload_action"> |
||||||
|
<input my-file-upload type="file" multiple="false" class="im_attach_input" size="120" multiple="false" accept="image/x-png, image/png, image/gif, image/jpeg" /> |
||||||
|
<span my-i18n="group_modal_update_photo"></span> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
<div class="mobile_modal_action_wrap" ng-if="photo.updating"> |
||||||
|
<span class="mobile_modal_action" my-i18n> |
||||||
|
<span my-i18n="group_modal_update_active"></span> |
||||||
|
<span my-loading-dots></span> |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_action_wrap" ng-if="hasRights('invite') || chatFull.chat.pFlags.left" ng-switch="chatFull.chat.pFlags.left"> |
||||||
|
<a ng-switch-when="true" class="mobile_modal_action" ng-click="joinChannel()" ng-switch="isMegagroup"> |
||||||
|
<span ng-switch-when="true" my-i18n="group_modal_join"></span> |
||||||
|
<span ng-switch-default my-i18n="channel_modal_join"></span> |
||||||
|
</a> |
||||||
|
<a ng-switch-default class="mobile_modal_action" ng-click="inviteToChannel()" my-i18n="channel_modal_add_member"></a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_action_wrap"> |
||||||
|
<a class="mobile_modal_action tg_checkbox clearfix" ng-click="settings.notifications = !settings.notifications" ng-class="settings.notifications ? 'tg_checkbox_on' : ''"> |
||||||
|
<span class="icon icon-checkbox-outer"><i class="icon-checkbox-inner"></i></span> |
||||||
|
<span class="tg_checkbox_label" my-i18n="group_modal_notifications"></span> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_action_wrap" ng-if="!chatFull.chat.pFlags.creator && !chatFull.chat.pFlags.left && !chatFull.chat.pFlags.kicked && !isMegagroup"> |
||||||
|
<a class="mobile_modal_action" ng-click="leaveChannel()" my-i18n="channel_modal_leave_channel"></a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_action_wrap" ng-if="chatFull.chat.pFlags.creator"> |
||||||
|
<a class="mobile_modal_action" ng-click="deleteChannel()" ng-switch="isMegagroup"> |
||||||
|
<span ng-switch-when="true" my-i18n="group_modal_delete_group"></span> |
||||||
|
<span ng-switch-default my-i18n="channel_modal_delete_channel"></span> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="mobile_modal_section" ng-if="chatFull.participants.participants.length > 0"> |
||||||
|
<h4 class="mobile_modal_section_header" my-i18n="group_modal_members"></h4> |
||||||
|
<div class="mobile_modal_section_body"> |
||||||
|
|
||||||
|
<div class="chat_modal_members_list"> |
||||||
|
|
||||||
|
<div class="chat_modal_participant_wrap clearfix" ng-repeat="participant in chatFull.participants.participants | orderBy:'-user.sortStatus'"> |
||||||
|
|
||||||
|
<a ng-if="participant.canLeave" ng-click="leaveChannel()" class="chat_modal_participant_kick pull-right" my-i18n="group_modal_menu_leave"></a> |
||||||
|
<a ng-if="participant.canKick" ng-click="kickFromChannel(participant.user_id)" class="chat_modal_participant_kick pull-right" my-i18n="group_modal_members_kick"></a> |
||||||
|
|
||||||
|
<a class="chat_modal_participant_photo pull-left" my-peer-photolink="participant.user_id" img-class="chat_modal_participant_photo" status="true"></a> |
||||||
|
|
||||||
|
<div class="chat_modal_participant_name"> |
||||||
|
<a my-peer-link="participant.user_id"></a> |
||||||
|
</div> |
||||||
|
<div class="chat_modal_participant_status" my-user-status="::participant.user_id" bot-chat-privacy="true"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
@ -0,0 +1,55 @@ |
|||||||
|
<form class="im_send_form" ng-class="{im_send_form_empty: !draftMessage.text.length && draftMessage.type != 'edit', composer_progress_enabled: draftMessage.inlineProgress, im_voice_recording: voiceRecorder.recording, im_processing_recording: voiceRecorder.processing}"> |
||||||
|
|
||||||
|
<div class="im_send_reply_wrap" ng-if="draftMessage.replyToMsgID > 0"> |
||||||
|
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.replyClear(true)"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a> |
||||||
|
<a class="im_message_reply_wrap" my-reply-message="draftMessage.replyToMsgID" watch="true" edit="{{draftMessage.type == 'edit'}}"></a> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_reply_wrap im_send_fwds_wrap" ng-if="draftMessage.fwdMessages.length > 0"> |
||||||
|
<a class="im_send_reply_cancel" ng-mousedown="draftMessage.fwdsClear()"><i class="icon icon-reply-bar"></i><i class="icon icon-reply-bar"></i></a> |
||||||
|
<div class="im_message_reply_wrap" my-forwarded-messages="draftMessage.fwdMessages"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_field_panel"> |
||||||
|
|
||||||
|
<div class="im_voice_recorder_wrap"> |
||||||
|
<div class="im_recorder_indicator"><i></i></div> |
||||||
|
<div class="im_recorder_time" ng-bind="voiceRecorder.duration | duration"></div> |
||||||
|
<div class="im_recorder_label" ng-switch="voiceRecorder.processing" my-i18n> |
||||||
|
<span ng-switch-when="true" my-i18n-format="im_voice_processing_label"></span> |
||||||
|
<span ng-switch-default my-i18n-format="im_voice_recording_label"></span> |
||||||
|
<my-i18n-param name="dots"></my-i18n-param> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_field_wrap" ng-class="historyState.replyKeyboard._ == 'replyKeyboardMarkup' ? 'im_send_field_wrap_2ndbtn' : ''"> |
||||||
|
<a class="composer_emoji_insert_btn pull-right"><i class="icon icon-emoji"></i></a> |
||||||
|
<a class="composer_command_btn" ng-show="!historyState.replyKeyboard && commands.list.length > 0 && (!draftMessage.text.length || draftMessage.text[0] == '/')" ng-mousedown="draftMessage.toggleSlash($event)" ng-class="draftMessage.text[0] == '/' ? 'active' : ''"><i class="icon icon-slash"></i></a> |
||||||
|
<a class="composer_keyboard_btn" ng-show="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-mousedown="draftMessage.replyKeyboardToggle($event)" ng-class="!historyState.replyKeyboard.pFlags.hidden ? 'active' : ''"><i class="icon icon-keyboard"></i></a> |
||||||
|
|
||||||
|
<div class="composer_progress_icon_wrap"> |
||||||
|
<div class="composer_progress_icon" my-arc-progress width="22" stroke="2.5"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_send_dropbox_wrap" my-i18n="im_photos_drop_text"></div> |
||||||
|
<textarea ng-model="draftMessage.text" class="form-control im_message_field no_outline" dir="auto" ng-trim="false"></textarea> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_attach pull-left"> |
||||||
|
<input type="file" class="im_attach_input" size="28" multiple="true" title="{{'im_media_attach_title' | i18n}}" /> |
||||||
|
<i class="icon icon-paperclip"></i> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="im_record pull-right"> |
||||||
|
<i class="icon icon-mic"></i> |
||||||
|
</div> |
||||||
|
|
||||||
|
<button type="submit" class="btn btn-success im_submit"></button> |
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
<div class="im_send_keyboard_wrap" ng-if="historyState.replyKeyboard._ == 'replyKeyboardMarkup'" ng-show="!historyState.replyKeyboard.pFlags.hidden"> |
||||||
|
<div my-reply-markup="historyState.replyKeyboard"></div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</form> |
File diff suppressed because one or more lines are too long
@ -0,0 +1,245 @@ |
|||||||
|
"use strict"; |
||||||
|
|
||||||
|
var root = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || this; |
||||||
|
|
||||||
|
(function( global ) { |
||||||
|
|
||||||
|
var Recorder = function( config ){ |
||||||
|
|
||||||
|
var that = this; |
||||||
|
|
||||||
|
if ( !Recorder.isRecordingSupported() ) { |
||||||
|
throw new Error("Recording is not supported in this browser"); |
||||||
|
} |
||||||
|
|
||||||
|
this.state = "inactive"; |
||||||
|
this.eventTarget = global.document.createDocumentFragment(); |
||||||
|
this.audioContext = new global.AudioContext(); |
||||||
|
this.monitorNode = this.audioContext.createGain(); |
||||||
|
|
||||||
|
this.config = config = config || {}; |
||||||
|
this.config.command = "init"; |
||||||
|
this.config.bufferLength = config.bufferLength || 4096; |
||||||
|
this.config.monitorGain = config.monitorGain || 0; |
||||||
|
this.config.numberOfChannels = config.numberOfChannels || 1; |
||||||
|
this.config.originalSampleRate = this.audioContext.sampleRate; |
||||||
|
this.config.encoderSampleRate = config.encoderSampleRate || 48000; |
||||||
|
this.config.encoderPath = config.encoderPath || 'encoderWorker.min.js'; |
||||||
|
this.config.streamPages = config.streamPages || false; |
||||||
|
this.config.leaveStreamOpen = config.leaveStreamOpen || false; |
||||||
|
this.config.maxBuffersPerPage = config.maxBuffersPerPage || 40; |
||||||
|
this.config.encoderApplication = config.encoderApplication || 2049; |
||||||
|
this.config.encoderFrameSize = config.encoderFrameSize || 20; |
||||||
|
this.config.resampleQuality = config.resampleQuality || 3; |
||||||
|
this.config.streamOptions = config.streamOptions || { |
||||||
|
optional: [], |
||||||
|
mandatory: { |
||||||
|
googEchoCancellation: false, |
||||||
|
googAutoGainControl: false, |
||||||
|
googNoiseSuppression: false, |
||||||
|
googHighpassFilter: false |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
this.setMonitorGain( this.config.monitorGain ); |
||||||
|
this.scriptProcessorNode = this.audioContext.createScriptProcessor( this.config.bufferLength, this.config.numberOfChannels, this.config.numberOfChannels ); |
||||||
|
this.scriptProcessorNode.onaudioprocess = function( e ){ |
||||||
|
that.encodeBuffers( e.inputBuffer ); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.isRecordingSupported = function(){ |
||||||
|
return global.AudioContext && global.navigator && ( global.navigator.getUserMedia || ( global.navigator.mediaDevices && global.navigator.mediaDevices.getUserMedia ) ); |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.addEventListener = function( type, listener, useCapture ){ |
||||||
|
this.eventTarget.addEventListener( type, listener, useCapture ); |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.clearStream = function() { |
||||||
|
if ( this.stream ) { |
||||||
|
|
||||||
|
if ( this.stream.getTracks ) { |
||||||
|
this.stream.getTracks().forEach(function ( track ) { |
||||||
|
track.stop(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
else { |
||||||
|
this.stream.stop(); |
||||||
|
} |
||||||
|
|
||||||
|
delete this.stream; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.encodeBuffers = function( inputBuffer ){ |
||||||
|
if ( this.state === "recording" ) { |
||||||
|
var buffers = []; |
||||||
|
for ( var i = 0; i < inputBuffer.numberOfChannels; i++ ) { |
||||||
|
buffers[i] = inputBuffer.getChannelData(i); |
||||||
|
} |
||||||
|
|
||||||
|
this.encoder.postMessage({ |
||||||
|
command: "encode", |
||||||
|
buffers: buffers |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.initStream = function(){ |
||||||
|
var that = this; |
||||||
|
|
||||||
|
var onStreamInit = function( stream ){ |
||||||
|
that.stream = stream; |
||||||
|
that.sourceNode = that.audioContext.createMediaStreamSource( stream ); |
||||||
|
that.sourceNode.connect( that.scriptProcessorNode ); |
||||||
|
that.sourceNode.connect( that.monitorNode ); |
||||||
|
that.eventTarget.dispatchEvent( new global.Event( "streamReady" ) ); |
||||||
|
return stream; |
||||||
|
} |
||||||
|
|
||||||
|
var onStreamError = function( e ){ |
||||||
|
that.eventTarget.dispatchEvent( new global.ErrorEvent( "streamError", { error: e } ) ); |
||||||
|
} |
||||||
|
|
||||||
|
var constraints = { audio : this.config.streamOptions }; |
||||||
|
|
||||||
|
if ( this.stream ) { |
||||||
|
this.eventTarget.dispatchEvent( new global.Event( "streamReady" ) ); |
||||||
|
return global.Promise.resolve( this.stream ); |
||||||
|
} |
||||||
|
|
||||||
|
if ( global.navigator.mediaDevices && global.navigator.mediaDevices.getUserMedia ) { |
||||||
|
return global.navigator.mediaDevices.getUserMedia( constraints ).then( onStreamInit, onStreamError ); |
||||||
|
} |
||||||
|
|
||||||
|
if ( global.navigator.getUserMedia ) { |
||||||
|
return new global.Promise( function( resolve, reject ) { |
||||||
|
global.navigator.getUserMedia( constraints, resolve, reject ); |
||||||
|
}).then( onStreamInit, onStreamError ); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.pause = function(){ |
||||||
|
if ( this.state === "recording" ){ |
||||||
|
this.state = "paused"; |
||||||
|
this.eventTarget.dispatchEvent( new global.Event( 'pause' ) ); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.removeEventListener = function( type, listener, useCapture ){ |
||||||
|
this.eventTarget.removeEventListener( type, listener, useCapture ); |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.resume = function() { |
||||||
|
if ( this.state === "paused" ) { |
||||||
|
this.state = "recording"; |
||||||
|
this.eventTarget.dispatchEvent( new global.Event( 'resume' ) ); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.setMonitorGain = function( gain ){ |
||||||
|
this.monitorNode.gain.value = gain; |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.start = function(){ |
||||||
|
if ( this.state === "inactive" && this.stream ) { |
||||||
|
var that = this; |
||||||
|
this.encoder = new global.Worker( this.config.encoderPath ); |
||||||
|
|
||||||
|
if (this.config.streamPages){ |
||||||
|
this.encoder.addEventListener( "message", function( e ) { |
||||||
|
that.streamPage( e.data ); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
else { |
||||||
|
this.recordedPages = []; |
||||||
|
this.totalLength = 0; |
||||||
|
this.encoder.addEventListener( "message", function( e ) { |
||||||
|
that.storePage( e.data ); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// First buffer can contain old data. Don't encode it.
|
||||||
|
this.encodeBuffers = function(){ |
||||||
|
delete this.encodeBuffers; |
||||||
|
}; |
||||||
|
|
||||||
|
this.state = "recording"; |
||||||
|
this.monitorNode.connect( this.audioContext.destination ); |
||||||
|
this.scriptProcessorNode.connect( this.audioContext.destination ); |
||||||
|
this.eventTarget.dispatchEvent( new global.Event( 'start' ) ); |
||||||
|
this.encoder.postMessage( this.config ); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.stop = function(){ |
||||||
|
if ( this.state !== "inactive" ) { |
||||||
|
this.state = "inactive"; |
||||||
|
this.monitorNode.disconnect(); |
||||||
|
this.scriptProcessorNode.disconnect(); |
||||||
|
|
||||||
|
if ( !this.config.leaveStreamOpen ) { |
||||||
|
this.clearStream(); |
||||||
|
} |
||||||
|
|
||||||
|
this.audioContext.close(); |
||||||
|
this.audioContext = null; |
||||||
|
|
||||||
|
this.encoder.postMessage({ command: "done" }); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.storePage = function( page ) { |
||||||
|
if ( page === null ) { |
||||||
|
var outputData = new Uint8Array( this.totalLength ); |
||||||
|
var outputIndex = 0; |
||||||
|
|
||||||
|
for ( var i = 0; i < this.recordedPages.length; i++ ) { |
||||||
|
outputData.set( this.recordedPages[i], outputIndex ); |
||||||
|
outputIndex += this.recordedPages[i].length; |
||||||
|
} |
||||||
|
|
||||||
|
this.eventTarget.dispatchEvent( new global.CustomEvent( 'dataAvailable', { |
||||||
|
detail: outputData |
||||||
|
})); |
||||||
|
|
||||||
|
this.recordedPages = []; |
||||||
|
this.eventTarget.dispatchEvent( new global.Event( 'stop' ) ); |
||||||
|
} |
||||||
|
|
||||||
|
else { |
||||||
|
this.recordedPages.push( page ); |
||||||
|
this.totalLength += page.length; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
Recorder.prototype.streamPage = function( page ) { |
||||||
|
if ( page === null ) { |
||||||
|
this.eventTarget.dispatchEvent( new global.Event( 'stop' ) ); |
||||||
|
} |
||||||
|
|
||||||
|
else { |
||||||
|
this.eventTarget.dispatchEvent( new global.CustomEvent( 'dataAvailable', { |
||||||
|
detail: page |
||||||
|
})); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// Exports
|
||||||
|
global.Recorder = Recorder; |
||||||
|
|
||||||
|
if ( typeof define === 'function' && define.amd ) { |
||||||
|
define( [], function() { |
||||||
|
return Recorder; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
else if ( typeof module == 'object' && module.exports ) { |
||||||
|
module.exports = Recorder; |
||||||
|
} |
||||||
|
|
||||||
|
})(root); |
@ -0,0 +1 @@ |
|||||||
|
"use strict";var root="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||this;!function(e){var t=function(n){var i=this;if(!t.isRecordingSupported())throw new Error("Recording is not supported in this browser");this.state="inactive",this.eventTarget=e.document.createDocumentFragment(),this.audioContext=new e.AudioContext,this.monitorNode=this.audioContext.createGain(),this.config=n=n||{},this.config.command="init",this.config.bufferLength=n.bufferLength||4096,this.config.monitorGain=n.monitorGain||0,this.config.numberOfChannels=n.numberOfChannels||1,this.config.originalSampleRate=this.audioContext.sampleRate,this.config.encoderSampleRate=n.encoderSampleRate||48e3,this.config.encoderPath=n.encoderPath||"encoderWorker.min.js",this.config.streamPages=n.streamPages||!1,this.config.leaveStreamOpen=n.leaveStreamOpen||!1,this.config.maxBuffersPerPage=n.maxBuffersPerPage||40,this.config.encoderApplication=n.encoderApplication||2049,this.config.encoderFrameSize=n.encoderFrameSize||20,this.config.resampleQuality=n.resampleQuality||3,this.config.streamOptions=n.streamOptions||{optional:[],mandatory:{googEchoCancellation:!1,googAutoGainControl:!1,googNoiseSuppression:!1,googHighpassFilter:!1}},this.setMonitorGain(this.config.monitorGain),this.scriptProcessorNode=this.audioContext.createScriptProcessor(this.config.bufferLength,this.config.numberOfChannels,this.config.numberOfChannels),this.scriptProcessorNode.onaudioprocess=function(e){i.encodeBuffers(e.inputBuffer)}};t.isRecordingSupported=function(){return e.AudioContext&&e.navigator&&(e.navigator.getUserMedia||e.navigator.mediaDevices&&e.navigator.mediaDevices.getUserMedia)},t.prototype.addEventListener=function(e,t,n){this.eventTarget.addEventListener(e,t,n)},t.prototype.clearStream=function(){this.stream&&(this.stream.getTracks?this.stream.getTracks().forEach(function(e){e.stop()}):this.stream.stop(),delete this.stream)},t.prototype.encodeBuffers=function(e){if("recording"===this.state){for(var t=[],n=0;n<e.numberOfChannels;n++)t[n]=e.getChannelData(n);this.encoder.postMessage({command:"encode",buffers:t})}},t.prototype.initStream=function(){var t=this,n=function(n){return t.stream=n,t.sourceNode=t.audioContext.createMediaStreamSource(n),t.sourceNode.connect(t.scriptProcessorNode),t.sourceNode.connect(t.monitorNode),t.eventTarget.dispatchEvent(new e.Event("streamReady")),n},i=function(n){t.eventTarget.dispatchEvent(new e.ErrorEvent("streamError",{error:n}))},o={audio:this.config.streamOptions};return this.stream?(this.eventTarget.dispatchEvent(new e.Event("streamReady")),e.Promise.resolve(this.stream)):e.navigator.mediaDevices&&e.navigator.mediaDevices.getUserMedia?e.navigator.mediaDevices.getUserMedia(o).then(n,i):e.navigator.getUserMedia?new e.Promise(function(t,n){e.navigator.getUserMedia(o,t,n)}).then(n,i):void 0},t.prototype.pause=function(){"recording"===this.state&&(this.state="paused",this.eventTarget.dispatchEvent(new e.Event("pause")))},t.prototype.removeEventListener=function(e,t,n){this.eventTarget.removeEventListener(e,t,n)},t.prototype.resume=function(){"paused"===this.state&&(this.state="recording",this.eventTarget.dispatchEvent(new e.Event("resume")))},t.prototype.setMonitorGain=function(e){this.monitorNode.gain.value=e},t.prototype.start=function(){if("inactive"===this.state&&this.stream){var t=this;this.encoder=new e.Worker(this.config.encoderPath),this.config.streamPages?this.encoder.addEventListener("message",function(e){t.streamPage(e.data)}):(this.recordedPages=[],this.totalLength=0,this.encoder.addEventListener("message",function(e){t.storePage(e.data)})),this.encodeBuffers=function(){delete this.encodeBuffers},this.state="recording",this.monitorNode.connect(this.audioContext.destination),this.scriptProcessorNode.connect(this.audioContext.destination),this.eventTarget.dispatchEvent(new e.Event("start")),this.encoder.postMessage(this.config)}},t.prototype.stop=function(){"inactive"!==this.state&&(this.state="inactive",this.monitorNode.disconnect(),this.scriptProcessorNode.disconnect(),this.config.leaveStreamOpen||this.clearStream(),this.encoder.postMessage({command:"done"}))},t.prototype.storePage=function(t){if(null===t){for(var n=new Uint8Array(this.totalLength),i=0,o=0;o<this.recordedPages.length;o++)n.set(this.recordedPages[o],i),i+=this.recordedPages[o].length;this.eventTarget.dispatchEvent(new e.CustomEvent("dataAvailable",{detail:n})),this.recordedPages=[],this.eventTarget.dispatchEvent(new e.Event("stop"))}else this.recordedPages.push(t),this.totalLength+=t.length},t.prototype.streamPage=function(t){null===t?this.eventTarget.dispatchEvent(new e.Event("stop")):this.eventTarget.dispatchEvent(new e.CustomEvent("dataAvailable",{detail:t}))},e.Recorder=t,"function"==typeof define&&define.amd?define([],function(){return t}):"object"==typeof module&&module.exports&&(module.exports=t)}(root); |
Loading…
Reference in new issue