scratchx/js/scratchx.js

446 lines
13 KiB
JavaScript
Raw Normal View History

2015-05-01 09:32:37 -04:00
// Simulate the bare minimum of the view that exists on the main site
var Scratch = Scratch || {};
Scratch.FlashApp = Scratch.FlashApp || {};
2015-05-08 10:06:50 -04:00
var editorId = "scratch";
var initialPage = "home";
2015-05-09 14:29:40 -04:00
var ShortURL = {
key : "AIzaSyBlaftRUIOLFVs8nfrWvp4IBrqq9-az46A",
api : "https://www.googleapis.com/urlshortener/v1/url",
domain : "http://goo.gl"
}
2015-05-08 10:06:50 -04:00
2015-05-01 09:32:37 -04:00
function handleEmbedStatus(e) {
$('#scratch-loader').hide();
var scratch = $(document.getElementById(editorId));
2015-05-01 09:32:37 -04:00
if (!e.success) {
scratch.css('marginTop', '10');
scratch.find('IMG.proj_thumb').css('width', '179px');
scratch.find('DIV.scratch_unsupported').show();
scratch.find('DIV.scratch_loading').hide();
} else {
Scratch.FlashApp.ASobj = scratch[0];
Scratch.FlashApp.$ASobj = $(Scratch.FlashApp.ASobj);
2015-05-01 09:32:37 -04:00
}
}
// enables the SWF to log errors
function JSthrowError(e) {
if (window.onerror) window.onerror(e, 'swf', 0);
else console.error(e);
}
function JSeditorReady() {
try {
handleParameters();
Scratch.FlashApp.$ASobj.trigger("editorReady");
2015-05-01 09:32:37 -04:00
return true;
} catch (error) {
console.error(error.message, "\n", error.stack);
throw error;
}
}
function JSprojectLoaded() {
loadExtensionQueue();
}
function JSshowExtensionDialog() {
showModal("dialogs");
}
2015-05-01 09:32:37 -04:00
var extensionQueue = [];
function handleParameters() {
var project;
var queryString = window.location.search.substring(1);
var queryVars = queryString.split(/[&;]/);
for (var i = 0; i < queryVars.length; i++) {
var nameVal = queryVars[i].split('=');
switch(nameVal[0]){
case 'ext':
extensionQueue.push(nameVal[1]);
break;
case 'proj':
project = nameVal[1];
break;
}
}
if (project) {
Scratch.FlashApp.ASobj.ASloadSBXFromURL(project);
}
else {
loadExtensionQueue();
}
}
function loadExtensionQueue() {
for (var i = 0; i < extensionQueue.length; ++i) {
var extensionURL = extensionQueue[i];
ScratchExtensions.loadExternalJS(extensionURL);
}
extensionQueue = [];
}
var flashVars = {
autostart: 'false',
extensionDevMode: 'true',
server: encodeURIComponent(location.host),
cloudToken: '4af4863d-a921-4004-b2cb-e0ad00ee1927',
cdnToken: '34f16bc63e8ada7dfd7ec12c715d0c94',
urlOverrides: {
sitePrefix: "http://scratch.mit.edu/",
siteCdnPrefix: "http://cdn.scratch.mit.edu/",
assetPrefix: "http://assets.scratch.mit.edu/",
assetCdnPrefix: "http://cdn.assets.scratch.mit.edu/",
projectPrefix: "http://projects.scratch.mit.edu/",
projectCdnPrefix: "http://cdn.projects.scratch.mit.edu/",
internalAPI: "internalapi/",
siteAPI: "site-api/",
staticFiles: "scratchr2/static/"
},
inIE: (navigator.userAgent.indexOf('MSIE') > -1)
};
var params = {
allowscriptaccess: 'always',
allowfullscreen: 'true',
wmode: 'direct',
menu: 'false'
};
$.each(flashVars, function (prop, val) {
if ($.isPlainObject(val))
flashVars[prop] = encodeURIComponent(JSON.stringify(val));
});
swfobject.switchOffAutoHideShow();
2015-05-08 10:06:50 -04:00
swfobject.embedSWF('Scratch.swf', 'scratch', '100%', '100%', '11.7.0', 'libs/expressInstall.swf',
2015-05-01 09:32:37 -04:00
flashVars, params, null, handleEmbedStatus);
2015-05-05 16:53:44 -04:00
2015-05-06 10:18:15 -04:00
/* File uploads */
2015-05-06 19:36:53 -04:00
function sendFileToFlash(file) {
2015-05-06 20:44:24 -04:00
/*
* Use the HTML5 FileReader API to send base-64 encoded file
* contents to Flash via ASloadBase64SBX (or do it when the SWF
* is ready).
*/
2015-05-06 13:58:03 -04:00
var fileReader = new FileReader();
2015-05-06 14:30:40 -04:00
fileReader.onload = function (e) {
var fileAsB64 = ab_to_b64(fileReader.result);
2015-05-08 10:06:50 -04:00
showPage(editorId);
2015-05-06 14:30:40 -04:00
if (Scratch.FlashApp.ASobj.ASloadBase64SBX !== undefined) {
Scratch.FlashApp.ASobj.ASloadBase64SBX(fileAsB64);
} else {
2015-05-06 20:17:43 -04:00
$(document).on("editorReady", function(e) {
2015-05-06 14:30:40 -04:00
Scratch.FlashApp.ASobj.ASloadBase64SBX(fileAsB64);
2015-05-06 20:17:43 -04:00
$(this).off(e);
});
2015-05-06 14:30:40 -04:00
}
}
2015-05-06 19:36:53 -04:00
fileReader.readAsArrayBuffer(file);
2015-05-06 10:18:15 -04:00
}
var loadFileListener = function(e) {
2015-05-06 20:44:24 -04:00
/*
* Buttons with data-action="load-file" trigger a file input
* prompt, passed to a handler that passes the file to Flash.
*/
$('<input type="file" />').on('change', function(){
sendFileToFlash(this.files[0])
}).click();
}
2015-05-06 10:18:15 -04:00
2015-05-08 20:07:05 -04:00
function sendURLtoFlash() {
2015-05-06 20:44:24 -04:00
/*
* Send a URL to Flash with ASloadGithubURL, or do it when the
* editor is ready.
*/
var urls = [];
for (var i = 0; i < arguments.length; i++) {
urls.push(arguments[i]);
}
if (urls.length <= 0) return;
2015-05-06 19:36:53 -04:00
if (Scratch.FlashApp.ASobj.ASloadGithubURL !== undefined) {
Scratch.FlashApp.ASobj.ASloadGithubURL(urls);
2015-05-06 14:30:40 -04:00
} else {
2015-05-06 20:17:43 -04:00
$(document).on("editorReady", function(e) {
Scratch.FlashApp.ASobj.ASloadGithubURL(urls);
2015-05-06 20:17:43 -04:00
$(this).off(e);
});
2015-05-06 14:30:40 -04:00
}
2015-05-06 10:18:15 -04:00
}
2015-05-06 20:44:24 -04:00
/* Load from URL */
var loadURLlistener = function(e) {
2015-05-06 20:44:24 -04:00
/*
* Links with data-action="load-url" send their href to Flash
* So use like...
* <a href="?url=urlToLoad" data-action="load-url">Load this</a>
*/
2015-05-06 19:55:28 -04:00
e.preventDefault();
2015-05-08 10:06:50 -04:00
showPage(editorId);
loadFromURLParameter($(this).attr("href"));
}
2015-05-06 19:55:28 -04:00
var loadURLformListener = function(e) {
2015-05-06 20:44:24 -04:00
// Load text input value on submit
e.preventDefault();
2015-05-08 10:06:50 -04:00
showPage(editorId);
sendURLtoFlash($('input[type="text"]', this).val());
}
2015-05-06 10:18:15 -04:00
function loadFromURLParameter(queryString) {
2015-05-06 20:44:24 -04:00
/*
* Get all url=urlToLoad from the querystring and send to Flash
* Use like...
* http://scratchx.org/?url=urlToLoad1&url=urlToLoad2
*/
var paramString = queryString.replace(/^\?|\/$/g, '');
2015-05-06 19:36:53 -04:00
var vars = paramString.split("&");
var showedEditor = false;
2015-05-08 20:07:05 -04:00
var urls = [];
2015-05-06 19:36:53 -04:00
for (var i=0; i<vars.length; i++) {
var pair = vars[i].split("=");
if (pair.length > 1 && pair[0]=="url") {
if (!showedEditor) {
2015-05-06 20:44:24 -04:00
// Only try to switch to the editor once
2015-05-08 10:06:50 -04:00
showPage(editorId);
2015-05-06 19:36:53 -04:00
showedEditor = true;
}
2015-05-08 20:07:05 -04:00
urls.push(pair[1]);
2015-05-06 19:36:53 -04:00
}
}
2015-05-08 20:07:05 -04:00
if (urls.length > 0) sendURLtoFlash.apply(window, urls);
2015-05-06 19:36:53 -04:00
}
2015-05-06 10:18:15 -04:00
/* Modals */
2015-05-07 11:00:47 -04:00
2015-05-09 14:29:40 -04:00
function getOrCreateFromTemplate(elementId, templateId, elementType, appendTo, wrapper, data) {
elementType = elementType || "div";
appendTo = appendTo || "body";
data = data || {};
var $element = $(document.getElementById(elementId));
2015-05-07 11:00:47 -04:00
if (!$element.length) {
$template = _.template($(document.getElementById(templateId)).html());
$element = $("<"+elementType+"></"+elementType+">")
2015-05-07 11:00:47 -04:00
.attr("id", elementId)
2015-05-09 14:29:40 -04:00
.html($template(data));
if (wrapper) $element.wrapInner(wrapper);
$element.appendTo(appendTo)
2015-05-07 11:00:47 -04:00
}
return $element;
};
2015-05-09 14:29:40 -04:00
function showModal(templateId, data) {
2015-05-07 11:00:47 -04:00
/*
* Copies the HTML referenced by data-template into a new element,
* with id="modal-[template value]" and creates an overlay on the
2015-05-07 11:00:47 -04:00
* page, which when clicked will close the popup.
*/
var zIndex = 100;
var modalId = "modal-" + templateId;
$modalwrapper = $("<div class='modal-fade-screen'><div class='modal-inner'></div></div>");
2015-05-09 14:29:40 -04:00
var $modal = getOrCreateFromTemplate(modalId, templateId, "dialog", "body", $modalwrapper, data);
$modal.addClass("modal");
$(".modal-fade-screen", $modal)
.addClass("visible")
.click(function(e){$(this).trigger("modal:exit")});
2015-05-09 14:29:40 -04:00
$("body").addClass("modal-open");
2015-05-09 14:29:40 -04:00
attachListeners();
var triggerExit = function (e) {$(this).trigger("modal:exit");}
2015-05-09 14:29:40 -04:00
$(".modal-inner", $modal).click(function(e){e.stopPropagation();})
$(document).on("click", "[data-action='load-file'], [data-action='load-url'], [data-action='show']", triggerExit);
$(document).on("submit", ".url-load-form", triggerExit)
$(document).on("modal:exit", function(){
$("body").removeClass("modal-open");
2015-05-08 15:34:00 -04:00
Scratch.FlashApp.ASobj.ASsetModalOverlay(false);
2015-05-09 14:29:40 -04:00
$modal.remove();
2015-05-07 11:00:47 -04:00
$(this).off();
});
2015-05-09 14:29:40 -04:00
2015-05-08 18:53:57 -04:00
return $modal;
}
2015-05-08 10:09:16 -04:00
$(document).keyup(function(e) {
// Exit modals with esc key
if (e.keyCode == 27) $(document).trigger("modal:exit");
});
$(document).on("modal:exit", function(e){Scratch.FlashApp.ASobj.ASsetModalOverlay(false);});
$(document).on('click', "[data-action='modal']", function(e){
/*
* Usage:
* <a href="#content" data-action="modal" data-template="id-for-content">Popup</a>
*/
e.preventDefault();
showModal($(this).data("template"));
2015-05-07 11:00:47 -04:00
});
2015-05-08 18:53:57 -04:00
function JSshowWarning(extensionData) {
$modal = showModal("template-warning");
$("button, .modal-close", $modal).click(function(e){
e.preventDefault();
$(document).trigger("modal:exit")
});
}
2015-05-07 11:00:47 -04:00
/* Page switching */
var showClickListener = function(e) {
2015-05-06 20:44:24 -04:00
/*
* Links with data-action="static-link" should switch the view
2015-05-06 20:44:24 -04:00
* to that page. Works like tabs sort of. Use like...
* <!-- Makes a link to the Privacy Policy section -->
* <a href="#privacy-policy" data-action="static-link">Privacy Policy</a>
2015-05-06 20:44:24 -04:00
*
*/
var path = $(this).attr("href").substring(1);
showPage(path);
};
$(window).bind('hashchange', function(e) {
if (document.location.hash == '') showPage('home');
});
2015-05-05 16:53:44 -04:00
function showPage(path) {
2015-05-06 20:44:24 -04:00
/*
* Show a part of the page. The site is set up like
* body
* main
* article#home
* article#privacy-policy
* ...
* editor
*
* Each <article> is a "page" of the site, plus one special
* view, which is the editor.
*
* The editor is not actually hidden, but located -9999px above
* the viewport. This is because if it's hidden, it doesn't load
* when the page is loaded.
*
* So first we have to hide everything that we're not going to show
* or move the editor up, then display everything we're going to show
* if it's hidden.
*
* If we are linking to an anchor within a page, then show its parent.
*/
var toHide = "body > main, body > main > article";
2015-05-05 16:53:44 -04:00
var toShow = "#" + path;
var $toShow = $(toShow);
2015-05-05 16:53:44 -04:00
2015-05-07 17:06:58 -04:00
if (!$toShow.length) return;
2015-05-06 19:36:53 -04:00
$(toHide).filter(":visible").hide();
if (!$toShow.is(Scratch.FlashApp.$ASobj)) $(document.getElementById(editorId)).css({top: "-9999px"});
$("body > main, body > main > article").has($toShow).show();
$toShow.show();
if ($toShow.is(Scratch.FlashApp.$ASobj)) {
$toShow.css({top: 0});
}
2015-05-05 16:53:44 -04:00
}
2015-05-09 14:29:40 -04:00
/* URL Shortening */
function shorten(url, done) {
var data = {longUrl: url};
$.ajax({
url : ShortURL.api + '?' + $.param({key : ShortURL.key}),
type : "post",
data : JSON.stringify(data),
dataType : "json",
contentType : "application/json"
}).done(done);
}
function getUrlFor(extensions) {
return document.location.origin + '/?' + $.param(
extensions.map(function(url){
return {name: 'url', value: url}
})
);
}
function UrlParser(url) {
parser = document.createElement('a');
parser.href = url;
return parser
}
function showShortUrl(url) {
shorten(url, function(data) {
var parser = UrlParser(data.id);
var id = parser.pathname.replace('/', '');
parser.href = window.location.origin;
parser.hash = "#!" + id;
var shortUrl = parser.href;
var context = {
longUrl : data.longUrl,
shortUrl : shortUrl
}
$modal = showModal("template-short-url", context);
var client = new ZeroClipboard($('button', $modal));
});
}
function JSshowShortUrlFor() {
showShortUrl(getUrlFor(Array.prototype.slice.call(arguments)));
}
function decompress(id, done) {
var data = {shortUrl: ShortURL.domain + id}
$.ajax({
url : ShortURL.api + '?' + $.param({
key : ShortURL.key,
shortUrl : ShortURL.domain + '/' + id}),
dataType : "json",
contentType : "application/json"
}).done(done);
}
/* Setup */
function attachListeners(){
$("[data-action='load-file']").on('click', loadFileListener);
$("[data-action='load-url']").on('click', loadURLlistener);
$(".url-load-form").on('submit', loadURLformListener);
$("[data-action='show']").on('click', showClickListener);
}
2015-05-06 09:53:59 -04:00
function initPage() {
2015-05-06 20:44:24 -04:00
/*
* On load, show the page identified by the URL fragment. Default to #home.
*/
attachListeners();
2015-05-09 14:29:40 -04:00
if (window.location.hash) {
if (window.location.hash.charAt(1) == "!") {
decompress(window.location.hash.substr(2), function(data) {
var parser = UrlParser(data.longUrl);
if (parser.hostname == window.location.hostname) window.location = data.longUrl;
return;
});
} else {
initialPage = window.location.hash.substr(1);
2015-05-09 14:29:40 -04:00
}
}
showPage(initialPage);
loadFromURLParameter(window.location.search);
2015-05-06 09:53:59 -04:00
}
$(initPage);