mirror of
https://github.com/google/blockly.git
synced 2025-12-13 12:50:07 +01:00
191 lines
6.2 KiB
JavaScript
191 lines
6.2 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2012 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Loading and saving blocks with localStorage and cloud storage.
|
|
*/
|
|
'use strict';
|
|
|
|
// Create a namespace.
|
|
var BlocklyStorage = {};
|
|
|
|
/**
|
|
* Backup code blocks to localStorage.
|
|
* @param {!Blockly.WorkspaceSvg} workspace Workspace.
|
|
* @private
|
|
*/
|
|
BlocklyStorage.backupBlocks_ = function(workspace) {
|
|
if ('localStorage' in window) {
|
|
var xml = Blockly.Xml.workspaceToDom(workspace);
|
|
// Gets the current URL, not including the hash.
|
|
var url = window.location.href.split('#')[0];
|
|
window.localStorage.setItem(url, Blockly.Xml.domToText(xml));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Bind the localStorage backup function to the unload event.
|
|
* @param {Blockly.WorkspaceSvg=} opt_workspace Workspace.
|
|
*/
|
|
BlocklyStorage.backupOnUnload = function(opt_workspace) {
|
|
var workspace = opt_workspace || Blockly.getMainWorkspace();
|
|
window.addEventListener('unload',
|
|
function() {BlocklyStorage.backupBlocks_(workspace);}, false);
|
|
};
|
|
|
|
/**
|
|
* Restore code blocks from localStorage.
|
|
* @param {Blockly.WorkspaceSvg=} opt_workspace Workspace.
|
|
*/
|
|
BlocklyStorage.restoreBlocks = function(opt_workspace) {
|
|
var url = window.location.href.split('#')[0];
|
|
if ('localStorage' in window && window.localStorage[url]) {
|
|
var workspace = opt_workspace || Blockly.getMainWorkspace();
|
|
var xml = Blockly.utils.xml.textToDom(window.localStorage[url]);
|
|
Blockly.Xml.domToWorkspace(xml, workspace);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Save blocks to database and return a link containing key to XML.
|
|
* @param {Blockly.WorkspaceSvg=} opt_workspace Workspace.
|
|
*/
|
|
BlocklyStorage.link = function(opt_workspace) {
|
|
var workspace = opt_workspace || Blockly.getMainWorkspace();
|
|
var xml = Blockly.Xml.workspaceToDom(workspace, true);
|
|
// Remove x/y coordinates from XML if there's only one block stack.
|
|
// There's no reason to store this, removing it helps with anonymity.
|
|
if (workspace.getTopBlocks(false).length === 1 && xml.querySelector) {
|
|
var block = xml.querySelector('block');
|
|
if (block) {
|
|
block.removeAttribute('x');
|
|
block.removeAttribute('y');
|
|
}
|
|
}
|
|
var data = Blockly.Xml.domToText(xml);
|
|
BlocklyStorage.makeRequest_('/storage', 'xml', data, workspace);
|
|
};
|
|
|
|
/**
|
|
* Retrieve XML text from database using given key.
|
|
* @param {string} key Key to XML, obtained from href.
|
|
* @param {Blockly.WorkspaceSvg=} opt_workspace Workspace.
|
|
*/
|
|
BlocklyStorage.retrieveXml = function(key, opt_workspace) {
|
|
var workspace = opt_workspace || Blockly.getMainWorkspace();
|
|
BlocklyStorage.makeRequest_('/storage', 'key', key, workspace);
|
|
};
|
|
|
|
/**
|
|
* Global reference to current AJAX request.
|
|
* @type {XMLHttpRequest}
|
|
* @private
|
|
*/
|
|
BlocklyStorage.httpRequest_ = null;
|
|
|
|
/**
|
|
* Fire a new AJAX request.
|
|
* @param {string} url URL to fetch.
|
|
* @param {string} name Name of parameter.
|
|
* @param {string} content Content of parameter.
|
|
* @param {!Blockly.WorkspaceSvg} workspace Workspace.
|
|
* @private
|
|
*/
|
|
BlocklyStorage.makeRequest_ = function(url, name, content, workspace) {
|
|
if (BlocklyStorage.httpRequest_) {
|
|
// AJAX call is in-flight.
|
|
BlocklyStorage.httpRequest_.abort();
|
|
}
|
|
BlocklyStorage.httpRequest_ = new XMLHttpRequest();
|
|
BlocklyStorage.httpRequest_.name = name;
|
|
BlocklyStorage.httpRequest_.onreadystatechange =
|
|
BlocklyStorage.handleRequest_;
|
|
BlocklyStorage.httpRequest_.open('POST', url);
|
|
BlocklyStorage.httpRequest_.setRequestHeader('Content-Type',
|
|
'application/x-www-form-urlencoded');
|
|
BlocklyStorage.httpRequest_.send(name + '=' + encodeURIComponent(content));
|
|
BlocklyStorage.httpRequest_.workspace = workspace;
|
|
};
|
|
|
|
/**
|
|
* Callback function for AJAX call.
|
|
* @private
|
|
*/
|
|
BlocklyStorage.handleRequest_ = function() {
|
|
if (BlocklyStorage.httpRequest_.readyState === 4) {
|
|
if (BlocklyStorage.httpRequest_.status !== 200) {
|
|
BlocklyStorage.alert(BlocklyStorage.HTTPREQUEST_ERROR + '\n' +
|
|
'httpRequest_.status: ' + BlocklyStorage.httpRequest_.status);
|
|
} else {
|
|
var data = BlocklyStorage.httpRequest_.responseText.trim();
|
|
if (BlocklyStorage.httpRequest_.name === 'xml') {
|
|
window.location.hash = data;
|
|
BlocklyStorage.alert(BlocklyStorage.LINK_ALERT.replace('%1',
|
|
window.location.href));
|
|
} else if (BlocklyStorage.httpRequest_.name === 'key') {
|
|
if (!data.length) {
|
|
BlocklyStorage.alert(BlocklyStorage.HASH_ERROR.replace('%1',
|
|
window.location.hash));
|
|
} else {
|
|
// Remove poison line to prevent raw content from being served.
|
|
data = data.replace(/^\{\[\(\< UNTRUSTED CONTENT \>\)\]\}\n/, '');
|
|
BlocklyStorage.loadXml_(data, BlocklyStorage.httpRequest_.workspace);
|
|
}
|
|
}
|
|
BlocklyStorage.monitorChanges_(BlocklyStorage.httpRequest_.workspace);
|
|
}
|
|
BlocklyStorage.httpRequest_ = null;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Start monitoring the workspace. If a change is made that changes the XML,
|
|
* clear the key from the URL. Stop monitoring the workspace once such a
|
|
* change is detected.
|
|
* @param {!Blockly.WorkspaceSvg} workspace Workspace.
|
|
* @private
|
|
*/
|
|
BlocklyStorage.monitorChanges_ = function(workspace) {
|
|
var startXmlDom = Blockly.Xml.workspaceToDom(workspace);
|
|
var startXmlText = Blockly.Xml.domToText(startXmlDom);
|
|
function change() {
|
|
var xmlDom = Blockly.Xml.workspaceToDom(workspace);
|
|
var xmlText = Blockly.Xml.domToText(xmlDom);
|
|
if (startXmlText !== xmlText) {
|
|
window.location.hash = '';
|
|
workspace.removeChangeListener(change);
|
|
}
|
|
}
|
|
workspace.addChangeListener(change);
|
|
};
|
|
|
|
/**
|
|
* Load blocks from XML.
|
|
* @param {string} xml Text representation of XML.
|
|
* @param {!Blockly.WorkspaceSvg} workspace Workspace.
|
|
* @private
|
|
*/
|
|
BlocklyStorage.loadXml_ = function(xml, workspace) {
|
|
try {
|
|
xml = Blockly.utils.xml.textToDom(xml);
|
|
} catch (e) {
|
|
BlocklyStorage.alert(BlocklyStorage.XML_ERROR + '\nXML: ' + xml);
|
|
return;
|
|
}
|
|
// Clear the workspace to avoid merge.
|
|
workspace.clear();
|
|
Blockly.Xml.domToWorkspace(xml, workspace);
|
|
};
|
|
|
|
/**
|
|
* Present a text message to the user.
|
|
* Designed to be overridden if an app has custom dialogs, or a butter bar.
|
|
* @param {string} message Text to alert.
|
|
*/
|
|
BlocklyStorage.alert = function(message) {
|
|
window.alert(message);
|
|
};
|