Voice Recorder / js / views /
main.js
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*global define, console, document, tau, setInterval, clearInterval*/
/*jslint plusplus: true*/
/**
* Main page module,
*
* @module views/main
* @requires {@link core/event}
* @requires {@link core/application}
* @requires {@link models/stream}
* @requires {@link models/audio}
* @namespace views/main
* @memberof views
*/
define({
name: 'views/main',
requires: [
'core/event',
'core/application',
'models/stream',
'models/audio'
],
def: function viewsMain(req) {
'use strict';
/**
* File write error constant string.
*
* @private
* @const {string}
*/
var ERROR_FILE_WRITE = 'FILE_WRITE_ERR',
/**
* No free space constant message string.
*
* @private
* @const {string}
*/
NO_FREE_SPACE_MSG = 'No free space.',
/**
* Cannot access audio stream constant message string.
*
* @private
* @const {string}
*/
CANNOT_ACCESS_AUDIO_MSG = 'Cannot access audio stream. ' +
'Please close all applications that use the audio stream and ' +
'open the application again.',
/**
* Recording interval step constant number.
*
* 2private
* @const {number}
*/
RECORDING_INTERVAL_STEP = 100,
/**
* Event module object.
*
* @private
* @type {Module}
*/
e = req.core.event,
/**
* Application module object.
*
* @private
* @type {Module}
*/
app = req.core.application,
/**
* Stream module object.
*
* @private
* @type {Module}
*/
s = req.models.stream,
/**
* Audio module object.
*
* @private
* @type {Module}
*/
a = req.models.audio,
/**
* Page element.
*
* @private
* @type {HTMLElement}
*/
page = null,
/**
* Record button element.
*
* @private
* @type {HTMLElement}
*/
recordBtn = null,
/**
* Record button icon element.
*
* @private
* @type {HTMLElement}
*/
recordBtnIcon = null,
/**
* Record button map element.
*
* @private
* @type {HTMLElement}
*/
recordBtnMap = null,
/**
* Record progress element.
*
* @private
* @type {HTMLElement}
*/
recordProgress = null,
/**
* Record progress value element.
*
* @private
* @type {HTMLElement}
*/
recordProgressVal = null,
/**
* Exit alert message element.
*
* @private
* @type {HTMLElement}
*/
exitAlertMessage = null,
/**
* Exit alert OK button element.
*
* @private
* @type {HTMLElement}
*/
exitAlertOk = null,
/**
* Recording interval id.
*
* @private
* @type {number}
*/
recordingInterval = null,
/**
* Boolean flag indicating that record button has been clicked
* and recording process should start.
*
* @private
* @type {boolean}
*/
recording = false,
/**
* Recording time in milliseconds.
*
* @private
* @type {number}
*/
recordingTime = 0,
/**
* Boolean flag that prevents record button action
* on multiple clicks.
*
* @private
* @type {boolean}
*/
recordingLock = false,
/**
* Touch counter that prevents record button action
* on multiple touch.
*
* @private
* @type {number}
*/
recordBtnTouchCounter = 0;
/**
* Toggles between recording/no recording state.
*
* @private
* @param {boolean} forceValue
*/
function toggleRecording(forceValue) {
if (forceValue !== undefined) {
recording = !!forceValue;
} else {
recording = !recording;
}
}
/**
* Shows stop button.
*
* @private
*/
function showStopButton() {
recordBtnIcon.classList.add('recording');
}
/**
* Hides stop button.
*
* @private
*/
function hideStopButton() {
recordBtnIcon.classList.remove('recording');
}
/**
* Shows recording view.
*
* @private
*/
function showRecordingView() {
showStopButton();
}
/**
* Renders recording progress bar value.
*
* @private
* @param {number} value
*/
function renderRecordingProgressBarValue(value) {
recordProgressVal.style.width = value + 'px';
}
/**
* Renders recording progress bar.
*
* @private
*/
function renderRecordingProgressBar() {
var parentWidth = recordProgress.clientWidth,
width = recordingTime / a.MAX_RECORDING_TIME * parentWidth;
renderRecordingProgressBarValue(width);
}
/**
* Resets recording progress.
*
* @private
*/
function resetRecordingProgress() {
recordingTime = 0;
renderRecordingProgressBar();
}
/**
* Removes recording interval.
*
* @private
*/
function removeRecordingInterval() {
clearInterval(recordingInterval);
}
/**
* Updates recording progress.
*
* @private
*/
function updateRecordingProgress() {
recordingTime = a.getRecordingTime();
renderRecordingProgressBar();
}
/**
* Sets recording interval.
*
* @private
*/
function setRecordingInterval() {
recordingInterval = setInterval(
updateRecordingProgress,
RECORDING_INTERVAL_STEP
);
}
/**
* Starts audio recording.
*
* @private
*/
function startRecording() {
recordingLock = true;
a.startRecording();
resetRecordingProgress();
showRecordingView();
}
/**
* Stops audio recording.
*
* @private
*/
function stopRecording() {
recordingLock = true;
a.stopRecording();
}
/**
* Starts or stops audio recording.
*
* @private
*/
function setRecording() {
if (recording) {
startRecording();
} else {
stopRecording();
}
}
/**
* Handles click event on record button.
*
* @private
*/
function onRecordBtnClick() {
if (recordingLock || document.hidden) {
return;
}
toggleRecording();
setRecording();
}
/**
* Sets pressed class for button.
*
* @private
*/
function setPressButtonState() {
recordBtn.classList.add('pressed');
recordBtnIcon.classList.add('pressed');
}
/**
* Removes pressed class for button.
*
* @private
*/
function removePressButtonState() {
recordBtn.classList.remove('pressed');
recordBtnIcon.classList.remove('pressed');
}
/**
* Handles touchstart event on record button.
*
* @private
* @param {Event} ev
*/
function onRecordBtnTouchStart(ev) {
recordBtnTouchCounter = recordBtnTouchCounter + 1;
if (ev.touches.length === 1) {
setPressButtonState();
}
}
/**
* Handles touchend event on record button.
*
* @private
*/
function onRecordBtnTouchEnd() {
recordBtnTouchCounter = recordBtnTouchCounter - 1;
if (recordBtnTouchCounter === 0) {
removePressButtonState();
}
}
/**
* Handles page before show event.
*
* @private
*/
function onPageBeforeShow() {
recordingLock = false;
toggleRecording(false);
hideStopButton();
resetRecordingProgress();
}
/**
* Handles click event on exit alert OK button.
*
* @private
*/
function onExitAlertOkClick() {
app.exit();
}
/**
* Registers event listeners.
*
* @private
*/
function bindEvents() {
page.addEventListener('pagebeforeshow', onPageBeforeShow);
recordBtnMap.addEventListener('click', onRecordBtnClick);
recordBtnMap.addEventListener('touchstart', onRecordBtnTouchStart);
recordBtnMap.addEventListener('touchend', onRecordBtnTouchEnd);
exitAlertOk.addEventListener('click', onExitAlertOkClick);
}
/**
* Shows exit alert popup.
*
* @private
* @param {string} message
*/
function showExitAlert(message) {
exitAlertMessage.innerHTML = message;
tau.openPopup('#exit-alert');
}
/**
* Handles models.stream.ready event.
*
* @private
* @param {Event} ev
*/
function onStreamReady(ev) {
a.registerStream(ev.detail.stream);
}
/**
* Handles models.stream.cannot.access.audio event.
*
* @private
*/
function onStreamCannotAccessAudio() {
if (document.visibilityState === 'visible') {
showExitAlert(CANNOT_ACCESS_AUDIO_MSG);
}
}
/**
* Initiates stream.
*
* @private
*/
function initStream() {
s.getStream();
}
/**
* Handles models.audio.ready event.
*
* @private
*/
function onAudioReady() {
console.log('onAudioReady()');
}
/**
* Handles models.audio.error event.
*
* @private
*/
function onAudioError() {
console.error('onAudioError()');
}
/**
* Handles models.audio.recording.start event.
*
* @private
*/
function onRecordingStart() {
setRecordingInterval();
toggleRecording(true);
recordingLock = false;
}
/**
* Handles models.audio.recording.done event.
*
* @private
* @param {Event} ev
* @fires views.main.show.preview
*/
function onRecordingDone(ev) {
var path = ev.detail.path;
removeRecordingInterval();
toggleRecording(false);
updateRecordingProgress();
e.fire('show.preview', {audio: path});
}
/**
* Handles models.audio.recording.cancel event.
*
* @private
*/
function onRecordingCancel() {
toggleRecording(false);
removePressButtonState();
hideStopButton();
}
/**
* Handles models.audio.recording.error event.
*
* @private
* @param {CustomEvent} ev
*/
function onRecordingError(ev) {
var error = ev.detail.error;
if (error === ERROR_FILE_WRITE) {
console.error(NO_FREE_SPACE_MSG);
} else {
console.error('Error: ' + error);
}
removeRecordingInterval();
toggleRecording(false);
}
/**
* Handles views.init.visibility.change event.
* (document.visibilityState can be set to 'visible' or 'hidden').
*
* @private
*/
function visibilityChange() {
if (document.visibilityState !== 'visible') {
if (a.isReady()) {
a.stopRecording();
a.release();
}
} else {
if (!a.isReady()) {
initStream();
}
}
}
/**
* Handles views.init.application.state.background event.
*
* @private
*/
function onApplicationStateBackground() {
removePressButtonState();
recordBtnTouchCounter = 0;
}
/**
* Initializes module.
*
* @memberof views/main
* @public
*/
function init() {
page = document.getElementById('main');
recordBtn = document.getElementById(
'main-navigation-bar-button'
);
recordBtnIcon = document.getElementById(
'main-navigation-bar-button-icon'
);
recordBtnMap = document.getElementById(
'main-navigation-bar-button-map'
);
recordProgress = document.getElementById('record-progress');
recordProgressVal = document.getElementById('record-progress-val');
exitAlertMessage = document.getElementById('exit-alert-message');
exitAlertOk = document.getElementById('exit-alert-ok');
bindEvents();
initStream();
}
e.listeners({
'models.stream.ready': onStreamReady,
'models.stream.cannot.access.audio': onStreamCannotAccessAudio,
'models.audio.ready': onAudioReady,
'models.audio.error': onAudioError,
'models.audio.recording.start': onRecordingStart,
'models.audio.recording.done': onRecordingDone,
'models.audio.recording.error': onRecordingError,
'models.audio.recording.cancel': onRecordingCancel,
'views.init.visibility.change': visibilityChange,
'views.init.application.state.background':
onApplicationStateBackground
});
return {
init: init
};
}
});