var d3 = require("d3"), $ = require("jquery"), preview = require("./preview.js"), video = require("./video.js"), audio = require("./audio.js"); d3.json("/settings/themes.json", function(err, themes){ var errorMessage; // Themes are missing or invalid if (err || !d3.keys(themes).filter(function(d){ return d !== "default"; }).length) { if (err instanceof SyntaxError) { errorMessage = "Error in settings/themes.json:
" + err.toString() + ""; } else if (err instanceof ProgressEvent) { errorMessage = "Error: no settings/themes.json."; } else if (err) { errorMessage = "Error: couldn't load settings/themes.json."; } else { errorMessage = "No themes found in settings/themes.json."; } d3.select("#loading-bars").remove(); d3.select("#loading-message").html(errorMessage); if (err) { throw err; } return; } for (var key in themes) { themes[key] = $.extend({}, themes.default, themes[key]); } preloadImages(themes); }); function submitted() { d3.event.preventDefault(); var theme = preview.theme(), caption = preview.caption(), selection = preview.selection(), file = preview.file(); if (!file) { d3.select("#row-audio").classed("error", true); return setClass("error", "No audio file selected."); } if (theme.maxDuration && selection.duration > theme.maxDuration) { return setClass("error", "Your Audiogram must be under " + theme.maxDuration + " seconds."); } if (!theme || !theme.width || !theme.height) { return setClass("error", "No valid theme detected."); } video.kill(); audio.pause(); var formData = new FormData(); formData.append("audio", file); if (selection.start || selection.end) { formData.append("start", selection.start); formData.append("end", selection.end); } formData.append("theme", JSON.stringify($.extend({}, theme, { backgroundImageFile: null }))); formData.append("caption", caption); setClass("loading"); d3.select("#loading-message").text("Uploading audio..."); $.ajax({ url: "/submit/", type: "POST", data: formData, contentType: false, dataType: "json", cache: false, processData: false, success: function(data){ poll(data.id, 0); }, error: error }); } function poll(id) { setTimeout(function(){ $.ajax({ url: "/status/" + id + "/", error: error, dataType: "json", success: function(result){ if (result && result.status && result.status === "ready" && result.url) { video.update(result.url, preview.theme().name); setClass("rendered"); } else if (result.status === "error") { error(result.error); } else { d3.select("#loading-message").text(statusMessage(result)); poll(id); } } }); }, 2500); } function error(msg) { if (msg.responseText) { msg = msg.responseText; } if (typeof msg !== "string") { msg = JSON.stringify(msg); } if (!msg) { msg = "Unknown error"; } d3.select("#loading-message").text("Loading..."); setClass("error", msg); } // Once images are downloaded, set up listeners function initialize(err, themesWithImages) { // Populate dropdown menu d3.select("#input-theme") .on("change", updateTheme) .selectAll("option") .data(themesWithImages) .enter() .append("option") .text(function(d){ return d.name; }); // Get initial theme d3.select("#input-theme").each(updateTheme); // Get initial caption (e.g. back button) d3.select("#input-caption").on("change keyup", updateCaption).each(updateCaption); // Space bar listener for audio play/pause d3.select(document).on("keypress", function(){ if (!d3.select("body").classed("rendered") && d3.event.key === " " && !d3.matcher("input, textarea, button, select").call(d3.event.target)) { audio.toggle(); } }); // Button listeners d3.selectAll("#play, #pause").on("click", function(){ d3.event.preventDefault(); audio.toggle(); }); d3.select("#restart").on("click", function(){ d3.event.preventDefault(); audio.restart(); }); // If there's an initial piece of audio (e.g. back button) load it d3.select("#input-audio").on("change", updateAudioFile).each(updateAudioFile); d3.select("#return").on("click", function(){ d3.event.preventDefault(); video.kill(); setClass(null); }); d3.select("#submit").on("click", submitted); } function updateAudioFile() { d3.select("#row-audio").classed("error", false); audio.pause(); video.kill(); // Skip if empty if (!this.files || !this.files[0]) { d3.select("#minimap").classed("hidden", true); preview.file(null); setClass(null); return true; } d3.select("#loading-message").text("Analyzing..."); setClass("loading"); preview.loadAudio(this.files[0], function(err){ if (err) { d3.select("#row-audio").classed("error", true); setClass("error", "Error decoding audio file"); } else { setClass(null); } d3.selectAll("#minimap, #submit").classed("hidden", !!err); }); } function updateCaption() { preview.caption(this.value); } function updateTheme() { preview.theme(d3.select(this.options[this.selectedIndex]).datum()); } function preloadImages(themes) { // preload images var imageQueue = d3.queue(); d3.entries(themes).forEach(function(theme){ if (!theme.value.name) { theme.value.name = theme.key; } if (theme.key !== "default") { imageQueue.defer(getImage, theme.value); } }); imageQueue.awaitAll(initialize); function getImage(theme, cb) { if (!theme.backgroundImage) { return cb(null, theme); } theme.backgroundImageFile = new Image(); theme.backgroundImageFile.onload = function(){ return cb(null, theme); }; theme.backgroundImageFile.onerror = function(e){ console.warn(e); return cb(null, theme); }; theme.backgroundImageFile.src = "/settings/backgrounds/" + theme.backgroundImage; } } function setClass(cl, msg) { d3.select("body").attr("class", cl || null); d3.select("#error").text(msg || ""); } function statusMessage(result) { switch (result.status) { case "queued": return "Waiting for other jobs to finish, #" + (result.position + 1) + " in queue"; case "audio-download": return "Downloading audio for processing"; case "trim": return "Trimming audio"; case "probing": return "Probing audio file"; case "waveform": return "Analyzing waveform"; case "renderer": return "Initializing renderer"; case "frames": var msg = "Generating frames"; if (result.numFrames) { msg += ", " + Math.round(100 * (result.framesComplete || 0) / result.numFrames) + "% complete"; } return msg; case "combine": return "Combining frames with audio"; case "ready": return "Cleaning up"; default: return JSON.stringify(result); } }