blockly/appengine/storage.js
/**
* @license
* Visual Blocks Editor
*
* Copyright 2012 Google Inc.
* https://blockly.googlecode.com/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Loading and saving blocks with localStorage and cloud storage.
* @author q.neutron@gmail.com (Quynh Neutron)
*/
'use strict';
// Create a namespace.
var BlocklyStorage = {};
/**
* Backup code blocks to localStorage.
* @private
*/
BlocklyStorage.backupBlocks_ = function() {
if ('localStorage' in window) {
var xml = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
// 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.
*/
BlocklyStorage.backupOnUnload = function() {
window.addEventListener('unload', BlocklyStorage.backupBlocks_, false);
};
/**
* Restore code blocks from localStorage.
*/
BlocklyStorage.restoreBlocks = function() {
var url = window.location.href.split('#')[0];
if ('localStorage' in window && window.localStorage[url]) {
var xml = Blockly.Xml.textToDom(window.localStorage[url]);
Blockly.Xml.domToWorkspace(Blockly.getMainWorkspace(), xml);
}
};
/**
* Save blocks to database and return a link containing key to XML.
*/
BlocklyStorage.link = function() {
var xml = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
var data = Blockly.Xml.domToText(xml);
BlocklyStorage.makeRequest_('/storage', 'xml', data);
};
/**
* Retrieve XML text from database using given key.
* @param {string} key Key to XML, obtained from href.
*/
BlocklyStorage.retrieveXml = function(key) {
var xml = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
BlocklyStorage.makeRequest_('/storage', 'key', key);
};
/**
* 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.
* @private
*/
BlocklyStorage.makeRequest_ = function(url, name, content) {
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));
};
/**
* 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 {
BlocklyStorage.loadXml_(data);
}
}
BlocklyStorage.monitorChanges_();
}
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.
* @private
*/
BlocklyStorage.monitorChanges_ = function() {
var startXmlDom = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
var startXmlText = Blockly.Xml.domToText(startXmlDom);
function change() {
var xmlDom = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
var xmlText = Blockly.Xml.domToText(xmlDom);
if (startXmlText != xmlText) {
window.location.hash = '';
Blockly.removeChangeListener(bindData);
}
}
var bindData = Blockly.addChangeListener(change);
};
/**
* Load blocks from XML.
* @param {string} xml Text representation of XML.
* @private
*/
BlocklyStorage.loadXml_ = function(xml) {
try {
xml = Blockly.Xml.textToDom(xml);
} catch (e) {
BlocklyStorage.alert(BlocklyStorage.XML_ERROR + '\nXML: ' + xml);
return;
}
// Clear the workspace to avoid merge.
Blockly.getMainWorkspace().clear();
Blockly.Xml.domToWorkspace(Blockly.getMainWorkspace(), xml);
};
/**
* 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);
};