Commit 8d76df00 authored by Mukul's avatar Mukul Committed by Vincent Bechu

[erp5_officejs][erp5_multimedia]: Adds officejs music player app.

parent ab388d7f
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OfficeJS Jio Custom Player</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="gadget_custom_player.js"></script>
</head>
<body>
<div class="field_container">
<div class="audioplayer">
<div class="pDiv">
<button class="play-btn ui-btn ui-btn-icon-left ui-icon-play"></button>
</div>
<div class="duration">
<span class="current_time">00:00:00</span>
<span> / </span>
<span class="total_time">00:00:00</span>
</div>
<progress value="0"></progress>
<div class="pVolumn">
<button class="vol-btn ui-icon ui-icon-left ui-icon-volume-up"></button>
</div>
<div class="volTimeline">
<div class="volHead"></div>
</div>
</div>
</div>
<div class='controller'></div>
</body>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_custom_player.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*global window, rJS, RSVP, jIO, MediaSource,
loopEventListener
*/
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS) {
"use strict";
rJS(window)
.setState({ play: false, mute: false })
//////////////////////////////////////////////
// Acquire Method
//////////////////////////////////////////////
.declareAcquiredMethod("jio_get", "jio_get")
/////////////////////////////////////////////
// Declare Method
////////////////////////////////////////////
.allowPublicAcquisition('updateCurrentTime', function (time) {
this.element.querySelector('.current_time').textContent = new Date(time * 1000).toISOString().substr(11, 8);
this.element.querySelector('progress').value = time;
})
.allowPublicAcquisition('updateTotalTime', function (time) {
this.element.querySelector('.total_time').textContent = new Date(time * 1000).toISOString().substr(11, 8);
this.element.querySelector('progress').max = time;
})
.allowPublicAcquisition('onEnd', function () {
return this.changeState({ play: false, mute: false });
})
.declareMethod('togglePlayPause', function () {
var gadget = this,
play_button = gadget.element.querySelector('.play-btn');
if (gadget.state.play) {
play_button.classList.add('ui-icon-pause');
play_button.classList.remove('ui-icon-play');
} else {
play_button.classList.add('ui-icon-play');
play_button.classList.remove('ui-icon-pause');
}
return gadget.getDeclaredGadget('controller')
.push(function (controller) {
return controller.handlePlayPause(gadget.state.play);
});
})
.declareMethod('toggleSound', function () {
var gadget = this,
volume_button = gadget.element.querySelector('.vol-btn');
if (gadget.state.mute) {
volume_button.classList.remove('ui-icon-volume-up');
volume_button.classList.add('ui-icon-volume-off');
} else {
volume_button.classList.remove('ui-icon-volume-off');
volume_button.classList.add('ui-icon-volume-up');
}
return gadget.getDeclaredGadget('controller')
.push(function (controller) {
return controller.handleSound(gadget.state.mute);
});
})
.declareMethod('render', function (params) {
var gadget = this;
return gadget.jio_get(params.value)
.push(function (doc) {
var name_array = doc.title.split('.'),
type = name_array[name_array.length - 1];
if (type === 'mp3' && MediaSource.isTypeSupported('audio/mpeg')) {
return gadget.declareGadget('gadget_custom_player_controller.html', {
element: gadget.element.querySelector('.controller'),
scope: 'controller'
});
}
return gadget.declareGadget('gadget_custom_player_controller_fallback.html', {
element: gadget.element.querySelector('.controller'),
scope: 'controller'
});
})
.push(function (controller) {
return controller.render({
id: params.value,
name: params.name
});
});
})
.onStateChange(function (modification_dict) {
if (modification_dict.hasOwnProperty('play')) {
return this.togglePlayPause(modification_dict.play);
}
if (modification_dict.hasOwnProperty('mute')) {
return this.toggleSound(modification_dict.mute);
}
})
.declareService(function () {
var gadget = this;
return loopEventListener(
gadget.element.querySelector('.play-btn'),
'click',
false,
function () {
return gadget.changeState({ play: !gadget.state.play });
},
true
);
})
.declareService(function () {
var gadget = this;
return loopEventListener(
gadget.element.querySelector('.vol-btn'),
'click',
false,
function () {
return gadget.changeState({ mute: !gadget.state.mute });
},
true
);
});
}(window, rJS));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_custom_player.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OfficeJS Jio Media Player Controller</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="gadget_custom_player_controller.js"></script>
</head>
<body>
<audio></audio>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_custom_player_controller.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*global window, rJS, RSVP, jIO, AudioContext,
URL, MediaSource, loopEventListener, document,
promiseEventListener, ArrayBuffer */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, RSVP, AudioContext, URL, MediaSource, loopEventListener) {
"use strict";
rJS(window)
.setState({ currentTime: 0 })
//////////////////////////////////////////////
// Acquire methods
/////////////////////////////////////////////
.declareAcquiredMethod('jio_get', 'jio_get')
.declareAcquiredMethod('getSetting', 'getSetting')
.declareAcquiredMethod('jio_getAttachment', 'jio_getAttachment')
.declareAcquiredMethod('updateCurrentTime', 'updateCurrentTime')
.declareAcquiredMethod('updateTotalTime', 'updateTotalTime')
.declareAcquiredMethod('notifySubmitted', 'notifySubmitted')
.declareAcquiredMethod('onEnd', 'onEnd')
//////////////////////////////////////////////
// Declare methods
/////////////////////////////////////////////
.declareMethod('configurePlayerContext', function () {
this.params.source.connect(this.params.gain);
this.params.gain.connect(this.params.audioContext.destination);
})
.declareMethod('getAudioChunk', function () {
var gadget = this, start, end;
start = gadget.params.index;
if (gadget.params.end) {
return;
}
end = start + 10e5;
// Call `getAttachment` method of jIO to fetch a chunk of data from IDB storage.
return new RSVP.Queue()
.push(function () {
return gadget.jio_getAttachment(gadget.params.id, gadget.params.name, {
'start': start,
'end': end,
'format': 'array_buffer'
}).push(function (buffer) {
if (buffer.byteLength < 10e5) {
gadget.params.end = true;
}
gadget.params.index += 10e5;
return buffer;
}, function (error) {
if ((error.constructor.name === 'jIOError' && error.status_code === 404) ||
(error.target && error.target.result === undefined)) {
return gadget.notifySubmitted({message: error.message || error.target.error, status: 'fail'});
}
throw error;
});
});
})
.declareMethod('handlePlayPause', function (play) {
var audio = this.element.querySelector('audio');
if (play) {
audio.play();
} else {
audio.pause();
}
})
.declareMethod('handleSound', function (mute) {
this.element.querySelector('audio').muted = mute;
})
.declareMethod('setSourceBuffer', function () {
this.params.sourceBuffer = this.params.mediaSource.addSourceBuffer('audio/mpeg');
return RSVP.all([this.setUpdateEvent(), this.timeUpdate()]);
})
.declareMethod('setUpdateEvent', function () {
var gadget = this;
return gadget.getAudioChunk()
.push(function (buffer) {
if (buffer instanceof ArrayBuffer && !gadget.params.sourceBuffer.updating) {
gadget.params.sourceBuffer.appendBuffer(buffer);
gadget.params.sourceBuffer.onupdateend = function () {
this.updateTotalTime(this.params.sourceBuffer.timestampOffset);
}.bind(gadget);
}
if (buffer === undefined && gadget.params.mediaSource.readyState === 'open') {
gadget.params.mediaSource.endOfStream();
}
if (gadget.params.replay) {
gadget.element.querySelector('audio').play();
gadget.params.replay = false;
}
});
})
.declareMethod('render', function (params) {
var gadget = this,
queue = new RSVP.Queue();
gadget.params.id = params.id;
gadget.params.name = params.name;
gadget.params.index = 0;
return queue
.push(function () {
return promiseEventListener(gadget.params.mediaSource, 'sourceopen', false);
})
.push(function () {
if (!gadget.params.name) {
return gadget.getSetting('hateoas_url');
}
})
.push(function (hateoas_url) {
if (!gadget.params.name) {
gadget.params.name = hateoas_url + gadget.params.id;
}
return RSVP.all([
gadget.setSourceBuffer(),
gadget.configurePlayerContext()
]);
});
})
.ready(function () {
var audioContext = new AudioContext(),
audio = this.element.querySelector('audio'),
gain = audioContext.createGain(),
source = audioContext.createMediaElementSource(audio),
mediaSource = new MediaSource();
audio.src = URL.createObjectURL(mediaSource);
this.params = {
audioContext: audioContext,
gain: gain,
source: source,
mediaSource: mediaSource
};
})
.onStateChange(function (modification_dict) {
if (modification_dict.hasOwnProperty('currentTime')) {
return this.updateCurrentTime(modification_dict.currentTime);
}
})
.declareJob('requestChunk', function () {
var gadget = this;
return gadget.getAudioChunk()
.push(function (buffer) {
gadget.params.requested = false;
if (buffer instanceof ArrayBuffer && !gadget.params.sourceBuffer.updating) {
return gadget.params.sourceBuffer.appendBuffer(buffer);
}
if (buffer === undefined && gadget.params.mediaSource.readyState === 'open') {
gadget.params.mediaSource.endOfStream();
}
})
.push(function () {
return gadget.changeState({ currentTime: gadget.element.querySelector('audio').currentTime });
});
})
.declareJob('timeUpdate', function () {
var gadget = this,
audio = gadget.element.querySelector('audio');
return loopEventListener(
audio,
'timeupdate',
false,
function () {
if ((gadget.params.sourceBuffer.timestampOffset - audio.currentTime) < 10 && !gadget.params.requested) {
gadget.params.requested = true;
return gadget.requestChunk();
}
return gadget.changeState({ currentTime: audio.currentTime });
},
true
);
})
.declareService(function () {
var gadget = this,
audio = gadget.element.querySelector('audio');
return new RSVP.Queue()
.push(function () {
return loopEventListener(
audio,
'ended',
false,
function () {
audio.currentTime = 0;
return gadget.onEnd();
},
true
);
})
.push(undefined, function (error) {
if (error instanceof RSVP.CancellationError) {
// Pause when gadget go out of scope { CancellationError }.
audio.pause();
gadget.params.source.disconnect(0);
gadget.params.gain.disconnect(0);
gadget.params.audioContext.close();
} else {
throw error;
}
});
});
}(window, rJS, RSVP, AudioContext, URL, MediaSource, loopEventListener));
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_custom_player_controller.js</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/javascript</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OfficeJS Jio Player Controller Fallback</title>
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="gadget_custom_player_controller_fallback.js"></script>
</head>
<body>
<audio></audio>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_custom_player_controller_fallback.html</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*global window, rJS, RSVP, URL, Blob,
loopEventListener, document*/
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, RSVP, URL, loopEventListener) {
"use strict";
rJS(window)
.setState({ currentTime: 0 })
//////////////////////////////////////////////
// Acquire methods
/////////////////////////////////////////////
.declareAcquiredMethod('jio_get', 'jio_get')
.declareAcquiredMethod('getSetting', 'getSetting')
.declareAcquiredMethod('jio_getAttachment', 'jio_getAttachment')
.declareAcquiredMethod('togglePlayPause', 'togglePlayPause')
.declareAcquiredMethod('toggleSound', 'toggleSound')
.declareAcquiredMethod('updateCurrentTime', 'updateCurrentTime')
.declareAcquiredMethod('updateTotalTime', 'updateTotalTime')
.declareAcquiredMethod('notifySubmitted', 'notifySubmitted')
.declareAcquiredMethod('onEnd', 'onEnd')
//////////////////////////////////////////////
// Declare methods
/////////////////////////////////////////////
.declareMethod('getAudioChunk', function () {
var gadget = this;
// Call `getAttachment` method of jIO to fetch a chunk of data from IDB storage.
return new RSVP.Queue()
.push(function () {
return gadget.jio_getAttachment(gadget.params.id, gadget.params.name, {
start: 0
});
})
.push(undefined, function (error) {
if ((error.constructor.name === 'jIOError' && error.status_code === 404) ||
(error.target && error.target.result === undefined)) {
return gadget.notifySubmitted({message: error.message || error.target.error, status: 'fail'});
}
throw error;
});
})
.declareMethod('handlePlayPause', function (play) {
var audio = this.element.querySelector('audio');
if (play) {
audio.play();
} else {
audio.pause();
}
})
.declareMethod('handleSound', function (mute) {
this.element.querySelector('audio').muted = mute;
})
.declareMethod('render', function (params) {
var gadget = this,
queue = new RSVP.Queue();
gadget.params = params;
if (gadget.params.id) {
if (!gadget.params.name) {
queue
.push(function () {
return gadget.getSetting('hateoas_url');
});
}
queue
.push(function (hateoas_url) {
if (!gadget.params.name) {
gadget.params.name = hateoas_url + gadget.params.id;
}
return gadget.getAudioChunk()
.push(function (blob) {
if (!(blob instanceof Blob)) {
blob = new Blob();
}
gadget.element.querySelector('audio').src = URL.createObjectURL(blob);
gadget.element.querySelector('audio').onloadeddata = function () {
gadget.updateTotalTime(gadget.element.querySelector('audio').duration);