<script src="/js/vendor/recorderjs/recorder.js"></script>
<script src="/js/vendor/recorderjs/recorderWorker.js"></script>
+ <!--
+ /\
+ / O\ The Unlimited Pizza Family's Own Libraries
+ /O o \ * Melty Cheese: Header Expandos Widget
+ |______| * Pepperoni: Audio Recorder Widget
+ * Tasty Crust: [Coming Soon] Colorful Navigation Widget
+ -->
<script src="js/unlimited_pizza.js"></script>
<script src="js/unlimited_pizza/melty_cheese.js"></script>
+ <script src="js/unlimited_pizza/pepperoni.js"></script>
<script type="text/javascript">
window.addEventListener('load', function () {
width: 8.571em;
float: left;
text-align: right;
- padding: 1.071em;
+ padding: 0.5em 1.071em;
+ text-transform: uppercase;
}
.guestbook-form .guestbook-control {
.guestbook-post .author {
font-weight: bold;
}
+
+/*
+ * The Recorder CSS
+ */
+.pepperoni-widget {
+ justify-content: space-between;
+ display: flex;
+}
+
+.pepperoni-widget .record-button {
+ display: flex;
+ font-size: 1.538461em;
+ padding: 1em;
+ margin: 1em;
+ height: 1em;
+ background-color: #f1f1f1;
+ cursor: pointer;
+ transition: background-color 0.2s ease, color 0.2s ease;
+}
+
+.record-info {
+ margin: 1em 0;
+}
+
+.pepperoni-widget .record-button.recording {
+ background-color: #00cc12;
+ color: white;
+ padding: 1em 1.1em 1em 0.68em;
+}
+
+.pepperoni-widget .record-button:hover {
+ text-decoration: none;
+ background-color: #fafafa;
+}
+
+.pepperoni-widget .record-button.recording:hover {
+ background-color: #d30058;
+ color: #f1f1f1;
+}
+
+.pepperoni-widget .record-progress {
+ justify-content: space-between;
+ display: flex;
+ margin: 0.5em 0.76em;
+}
+
+.record-progress-bar-container {
+ background-color: #f1f1f1;
+ height: 2em;
+ width: 15.385em;
+ margin-right: 0.76em;
+}
+
+.record-progress-bar {
+ height: 2em;
+ background-color: #d30058;
+ width: 0;
+}
+
+.record-clear {
+ margin-top: 0.1em;
+ text-transform: uppercase;
+ font-size: 1em;
+ width: 4em;
+ cursor: pointer;
+}
+
+.record-clear:hover {
+ text-decoration: none;
+}
+
+.record-preview {
+ margin: 0.76em;
+ width: 15.385em;
+ height: 2em;
+}
+
+.guestbook-submit {
+ border-radius: 0;
+ border: 0;
+ background-color: #d30058;
+ font-family: 'Oswald', sans-serif;
+ color: #fff;
+ padding: 1em;
+ margin-left: 15em;
+}
--- /dev/null
+---
+layout: default
+title: Guestbook
+description: "Audio Guestbook"
+---
+
+<div class="content">
+ <div class="related">
+ <div class="guestbook-form">
+ <h1>Audio Guestbook</h1>
+ <p>
+ Clips will live for a week before being destroyed.
+ <em>Los clips durarán una semana antes de ser destruĂdos</em>
+ </p>
+ <form method="post">
+ <ul>
+ <div class="guestbook-control-group">
+ <li class="guestbook-label"><label for="name-control">Nombre / Name</label></li>
+ <li class="guestbook-control"><input name="name" class="name-control" id="name-control" /></li>
+ </div>
+ <div class="guestbook-control-group">
+ <li class="guestbook-control"><div class="pepperoni-widget"></div></li>
+ </div>
+ <div class="guestbook-control-group">
+ <li class="guestbook-control"><input type="submit" value="OK!" class="guestbook-submit" /></li>
+ </div>
+ </ul>
+ </form>
+ </div>
+ <div class="guestbook-content">
+ <h1>CLIPS</h1>
+ <ul class="guestbook-feed">
+ </ul>
+ </div>
+ </div>
+</div>
},
prototype : {
+ _fb : null,
_loaded : false,
init : function (config) {
Widget.prototype.init.call(this, config)
+ this._fb = new Firebase("https://guestbook-nsovocal.firebaseio.com");
+
this._bindInternalEvents();
},
// Melty cheese is our header image widget.
this._loadMeltyCheese();
+
+ // Pepperoni is our recording widget.
+ this._loadPepperoni();
+
+ // Simple guestbook functionality
+ this._loadGuestbook();
+ this._loadPosts();
},
- _loadMeltyCheese : function() {
+ _loadMeltyCheese : function () {
this.element.find('.post-image').each(function (i, headerElement) {
// Create and activate
}));
this['header-' + i].activate();
}.bind(this));
+ },
+
+ _loadPepperoni : function () {
+ this.element.find('.pepperoni-widget').each(function (i, widgetElement) {
+
+ // Create and activate
+ this.appendChild(new UnlimitedPizza.Pepperoni({
+ element : $(widgetElement),
+ name : 'recorder-' + i
+ }));
+ this['recorder-' + i].activate();
+ }.bind(this));
+ },
+
+ _loadGuestbook : function () {
+ var form = this.element.find('.guestbook-form form');
+ form.on('submit', function submitPost(ev) {
+ ev.preventDefault();
+
+ var formArray = form.serializeArray();
+ var recorder = this['recorder-0'];
+
+ recorder.finalize(function (buffer) {
+ var fb, arrayBuffer, fileReader;
+
+ if (buffer.size <= 44) {
+ alert("You need to record something.");
+ return;
+ }
+ if (formArray[0].value.length === 0) {
+ alert("You need a name.");
+ return;
+ }
+
+ fb = this._fb;
+
+ fileReader = new FileReader();
+ fileReader.onload = function() {
+ var binary, bytes, length, i;
+
+ binary = '';
+ bytes = new Uint8Array( this.result );
+ length = bytes.byteLength;
+ for (i = 0; i < length; i++) {
+ binary += String.fromCharCode( bytes[ i ] );
+ }
+
+ fb.push({
+ buffer: btoa(binary),
+ name: formArray[0].value
+ });
+ recorder.clear();
+ };
+ fileReader.readAsArrayBuffer(buffer);
+ }.bind(this));
+ return false;
+ }.bind(this))
+ },
+
+ _loadPosts : function () {
+ var feed = this.element.find('.guestbook-feed');
+
+ console.log("Loadin", feed.length);
+ if (feed.length > 0) {
+ this._fb.on('value', function (data) {
+ var posts, property, post;
+
+ // Clear feed
+ feed.empty();
+ posts = data.val();
+
+ for (property in posts) {
+ if (posts.hasOwnProperty(property)) {
+ post = posts[property];
+
+ feed.append($('<li>\
+ <div class="author">FROM: ' + post.name + '</div>\
+ <div class="content">\
+ <audio src="data:audio/wav;base64,' + post.buffer + '" controls></audio>\
+ </div></li>'))
+ }
+ }
+ });
+ }
}
}
});
--- /dev/null
+Class(UnlimitedPizza, "Pepperoni").inherits(Widget)({
+ INNER_HTML : ' \
+<a class="record-button">⬤</a> \
+<div class="record-info"> \
+ <div class="record-progress"> \
+ <div class="record-progress-bar-container"> \
+ <div class="record-progress-bar"></div> \
+ </div> \
+ <a class="record-clear">X</a> \
+ </div> \
+ <audio class="record-preview" controls></audio> \
+</div> \
+ ',
+ PAUSE : '▐▐',
+ RECORD : '⬤',
+ prototype : {
+ maxSize : 1048576,
+ recording : false,
+ source : null,
+ recorder : null,
+ context : null,
+ workerPath : '/js/vendor/recorderjs/recorderWorker.js',
+ init : function init(config) {
+ Widget.prototype.init.call(this, config);
+
+ if (!this.context) {
+ this.context = new (window.AudioContext || window.webkitAudioContext)();
+ }
+
+ if (!this.source) {
+ this._getUserMedia({
+ audio : true
+ }, this._onUserMedia.bind(this), this._onUserMediaError.bind(this))
+ }
+
+ this.element.html(this.constructor.INNER_HTML);
+
+ this.controlButton = this.element.find('.record-button');
+ this.clearButton = this.element.find('.record-clear');
+ this.audioElement = this.element.find('audio');
+ this.progressBarContainer = this.element.find('.record-progress-bar-container');
+ this.progressBar = this.element.find('.record-progress-bar');
+
+ this._bindEvents();
+ },
+
+ record : function record() {
+ if (this.recorder && !this.recording) {
+ this._canRecord(function handleCanRecord(canRecord) {
+ if (canRecord) {
+ this.recording = true;
+ this.controlButton.addClass('recording')
+ this.controlButton.html(this.constructor.PAUSE);
+ this._interval = setInterval(this._onRecordCheck.bind(this), 100);
+ this.recorder.record();
+ }
+ }.bind(this));
+ }
+ },
+
+ stop : function stop() {
+ if (this.recorder && this.recording) {
+ this.recording = false;
+ this.controlButton.removeClass('recording')
+ this.controlButton.html(this.constructor.RECORD);
+ clearInterval(this._interval);
+ this.recorder.stop();
+ this.recorder.exportWAV();
+ }
+ },
+
+ clear : function clear() {
+ if (this.recorder) {
+ this.progressBar.width(0);
+ this.audioElement.attr('src', '');
+ this.audioElement[0].load();
+ this.recorder.clear()
+ }
+ },
+
+ finalize : function finalize(callback) {
+ if (this.recorder) {
+ this.recorder.exportWAV(callback);
+ }
+ },
+
+ getBuffer : function getBuffer(callback) {
+ if (this.recorder) {
+ this.recorder.getBuffer(callback);
+ }
+ },
+
+ _bindEvents : function bindEvents() {
+ this.controlButton.on('click', function () {
+ if (this.recording) {
+ this.stop();
+ } else {
+ this.record();
+ }
+ }.bind(this))
+
+ this.clearButton.on('click', function () {
+ if (!this.recording) {
+ this.clear();
+ }
+ }.bind(this))
+ },
+
+ _onRecording : function _onRecording(buffer) {
+ this._buffer = buffer;
+
+ this.audioElement.attr('src', URL.createObjectURL(buffer));
+ this.audioElement[0].load();
+ },
+
+ _onUserMedia : function _onUserMedia(localMediaStream) {
+ this.source = this.context.createMediaStreamSource(localMediaStream);
+ this.recorder = new Recorder(this.source, {
+ workerPath : this.workerPath,
+ callback : this._onRecording.bind(this)
+ });
+ },
+
+ _onUserMediaError : function _onUserMediaError(error) {
+ console.log("Something went wrong", error);
+ this.disable();
+ },
+
+ _onRecordCheck : function _onRecordCheck() {
+ this._canRecord(function (canRecord, bufferSize) {
+ var width = bufferSize * this.progressBarContainer.width() / this.maxSize;
+ this.progressBar.width(width);
+ if (!canRecord) {
+ this.stop();
+ }
+ }.bind(this));
+ },
+
+ _canRecord : function _canRecord(callback) {
+ this.recorder.getBuffer(function getBuffer(buffer) {
+ var bufferSize = buffer[0].length + buffer[1].length;
+ callback && callback(bufferSize <= this.maxSize, bufferSize);
+ }.bind(this));
+ },
+
+ // Normalize get user media
+ _getUserMedia : (navigator.getUserMedia ||
+ navigator.webkitGetUserMedia ||
+ navigator.mozGetUserMedia ||
+ navigator.msGetUserMedia).bind(navigator)
+ }
+});