New initial commit with .svn directories and their contents ignored.

This commit is contained in:
ellen.spertus
2013-10-30 14:46:03 -07:00
commit a8acffd81c
754 changed files with 85941 additions and 0 deletions

34
appengine/README.txt Normal file
View File

@@ -0,0 +1,34 @@
Running an App Engine server
This directory contains the files needed to setup the optional Blockly server.
Although Blockly itself is 100% client-side, the server enables cloud storage
and sharing. Store your programs in Datastore and get a unique URL that allows
you to load the program on any computer.
To run your own App Engine instance you'll need to create this directory
structure:
blockly/
|- app.yaml
|- index.yaml
|- index_redirect.py
|- README.txt
|- storage.js
|- storage.py
|- closure-library-read-only/
`- static/
|- apps/
|- core/
|- demos/
|- generators/
|- language/
|- media/
|- tests/
`- blockly_compressed.js
Instructions for fetching Closure may be found here:
http://code.google.com/p/blockly/wiki/Closure
Finally, upload this directory structure to your App Engine account,
wait a minute, then go to http://YOURNAME.appspot.com/

99
appengine/app.yaml Normal file
View File

@@ -0,0 +1,99 @@
application: blockly-demo
version: 1
runtime: python27
api_version: 1
threadsafe: no
handlers:
# Redirect obsolete URLs.
# Blockly files moved from /blockly to /static on 5 Dec 2012.
- url: /blockly/.*
static_files: redirect.html
upload: redirect.html
# Code, Maze and Turtle moved from demos to apps on 29 Dec 2012.
- url: /static/demos/(code|maze|turtle)/.*
static_files: redirect.html
upload: redirect.html
# Vietnamese apps moved from vn to vi on 9 Jun 2012.
- url: /static/apps/.+/vn\.html
static_files: redirect.html
upload: redirect.html
# Code moved to index.html on 7 Aug 2013.
- url: /static/apps/code/code\.html
static_files: redirect.html
upload: redirect.html
# Code became language-agnostic on 20 Jul 2013.
- url: /static/apps/code/(de|en|hu|vi|zh_tw)\.html
static_files: redirect.html
upload: redirect.html
# Graph moved to index.html on 7 Aug 2013.
- url: /static/apps/graph/graph\.html
static_files: redirect.html
upload: redirect.html
# Graph became language-agnostic on 20 Jul 2013.
- url: /static/apps/graph/(de|en|hu|vi)\.html
static_files: redirect.html
upload: redirect.html
# Maze moved to index.html on 7 Aug 2013.
- url: /static/apps/maze/maze\.html
static_files: redirect.html
upload: redirect.html
# Maze became language-agnostic on 20 Jul 2013.
- url: /static/apps/maze/(ca|cs|da|el|en|es|eu|fr|hu|it|lv|nl|pl|pt|ru|sr|sw|th|tr|vi)\.html
static_files: redirect.html
upload: redirect.html
# Plane moved to index.html on 7 Aug 2013.
- url: /static/apps/plane/plane\.html
static_files: redirect.html
upload: redirect.html
# Plane became language-agnostic on 20 Jul 2013.
- url: /static/apps/plane/(1_|2_)?(en|hu|vi|vn)\.html
static_files: redirect.html
upload: redirect.html
# Puzzle moved to index.html on 30 Jul 2013.
- url: /static/apps/puzzle/puzzle\.html
static_files: redirect.html
upload: redirect.html
# Puzzle became language-agnostic on 20 Jul 2013.
- url: /static/apps/puzzle/(af|cs|de|el|en|gl|hak|he|hu|ia|it|ja|ko|ksh|la|lb|lv|mk|ms|nl|pl|pt-br|ru|sv|sw|tzm|vi|zh-hans)\.html
static_files: redirect.html
upload: redirect.html
# Turtle moved to index.html on 7 Aug 2013.
- url: /static/apps/turtle/turtle\.html
static_files: redirect.html
upload: redirect.html
# Turtle became language-agnostic on 20 Jul 2013.
- url: /static/apps/turtle/(de|en|hu|vi)\.html
static_files: redirect.html
upload: redirect.html
# Storage API.
- url: /storage
script: storage.py
- url: /storage\.js
static_files: storage.js
upload: storage\.js
# Blockly files.
- url: /static
static_dir: static
# Closure library for uncompiled Blockly.
- url: /closure-library-read-only
static_dir: closure-library-read-only
# Redirect for root directory.
- url: /
script: index_redirect.py
# Favicon.
- url: /favicon\.ico
static_files: favicon.ico
upload: favicon\.ico
# robot.txt
- url: /robots\.txt
static_files: robots.txt
upload: robots\.txt

BIN
appengine/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

11
appengine/index.yaml Normal file
View File

@@ -0,0 +1,11 @@
indexes:
# AUTOGENERATED
# This index.yaml is automatically updated whenever the dev_appserver
# detects that a new type of query is run. If you want to manage the
# index.yaml file manually, remove the above marker line (the line
# saying "# AUTOGENERATED"). If you want to manage some indexes
# manually, move them above the marker line. The index.yaml file is
# automatically uploaded to the admin console when you next deploy
# your application using appcfg.py.

View File

@@ -0,0 +1,2 @@
print("Status: 302")
print("Location: /static/apps/index.html")

98
appengine/redirect.html Normal file
View File

@@ -0,0 +1,98 @@
<!DOCTYPE html>
<html>
<head>
<script>
var loc = location.href;
// Blockly files moved from /blockly to /static on 5 Dec 2012.
if (loc.match('/blockly/')) {
loc = loc.replace('/blockly/', '/static/');
}
// Code, Maze and Turtle moved from demos to apps on 29 Dec 2012.
if (loc.match(/\/demos\/(code|maze|turtle)\//)) {
loc = loc.replace('/demos/', '/apps/');
}
// Vietnamese apps moved from vn to vi on 9 Jun 2012.
if (loc.match('/vn.html')) {
loc = loc.replace('/vn.html', '/vi.html');
}
if (loc.match('/apps/code/code.html')) {
// Code moved to index.html on 7 Aug 2013.
loc = loc.replace('/code.html', '/index.html');
} else if (loc.match('/apps/code/zh_tw.html')) {
// Code became language-agnostic on 20 Jul 2013.
loc = loc.replace('/zh_tw.html', '/index.html?lang=zh-tw');
} else if (loc.match('/apps/code/index.html')) {
// NOP.
} else if (loc.match(/\/apps\/code\/[-a-z]+\.html/)) {
// Code became language-agnostic on 20 Jul 2013.
loc = loc.replace(/\/([-a-z]+)\.html/, '/index.html?lang=$1');
}
if (loc.match('/apps/graph/graph.html')) {
// Graph moved to index.html on 7 Aug 2013.
loc = loc.replace('/graph.html', '/index.html');
} else if (loc.match('/apps/graph/index.html')) {
// NOP.
} else if (loc.match(/\/apps\/graph\/[-a-z]+\.html/)) {
// Graph became language-agnostic on 20 Jul 2013.
loc = loc.replace(/\/([-a-z]+)\.html/, '/index.html?lang=$1');
}
if (loc.match('/apps/maze/maze.html')) {
// Maze moved to index.html on 7 Aug 2013.
loc = loc.replace('/maze.html', '/index.html');
} else if (loc.match('/apps/maze/index.html')) {
// NOP.
} else if (loc.match(/\/apps\/maze\/[-a-z]+\.html/)) {
// Maze became language-agnostic on 20 Jul 2013.
if (location.search) {
loc = loc.replace(/\/([-a-z]+)\.html\?/, '/index.html?lang=$1&');
} else {
loc = loc.replace(/\/([-a-z]+)\.html/, '/index.html?lang=$1');
}
}
if (loc.match('/apps/plane/plane.html')) {
// Plane moved to index.html on 7 Aug 2013.
loc = loc.replace('/plane.html', '/index.html');
} else if (loc.match('/apps/code/plane.html')) {
// NOP.
} else if (loc.match(/\/apps\/plane\/[\d_]*[-a-z]+\.html/)) {
// Plane became language-agnostic on 20 Jul 2013.
loc = loc.replace('vn.html', 'vi.html');
if (location.search) {
loc = loc.replace(/\/[\d_]*([-a-z]+)\.html\?/, '/index.html?lang=$1&');
} else {
loc = loc.replace(/\/[\d_]*([-a-z]+)\.html/, '/index.html?lang=$1');
}
}
if (loc.match('/apps/puzzle/puzzle.html')) {
// Puzzle moved to index.html on 30 Jul 2013.
loc = loc.replace('/puzzle.html', '/index.html');
} else if (loc.match('/apps/puzzle/index.html')) {
// NOP.
} else if (loc.match(/\/apps\/puzzle\/[-a-z]+\.html/)) {
// Puzzle became language-agnostic on 20 Jul 2013.
loc = loc.replace(/\/([-a-z]+)\.html/, '/index.html?lang=$1');
}
if (loc.match('/apps/turtle/turtle.html')) {
// Turtle moved to index.html on 7 Aug 2013.
loc = loc.replace('/turtle.html', '/index.html');
} else if (loc.match('/apps/turtle/index.html')) {
// NOP.
} else if (loc.match(/\/apps\/turtle\/[-a-z]+\.html/)) {
// Turtle became language-agnostic on 20 Jul 2013.
loc = loc.replace(/\/([-a-z]+)\.html/, '/index.html?lang=$1');
}
location = loc;
</script>
</head>
</html>

53
appengine/report.py Normal file
View File

@@ -0,0 +1,53 @@
"""Blockly Demo: Report
Copyright 2012 Google Inc.
http://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.
"""
"""Store reports about code written by users.
"""
__author__ = "ellen.spertus@gmail.com (Ellen Spertus)"
import cgi
import logging
from google.appengine.ext import db
print "Content-type: text/plain\n"
class Report(db.Model):
identifier = db.FloatProperty()
application = db.StringProperty()
date = db.DateTimeProperty(auto_now_add=True)
level = db.IntegerProperty()
result = db.IntegerProperty()
# StringProperty is limited to 500 characters, so use TextProperty.
program = db.TextProperty()
# Catch errors extracting form fields or converting to numeric types.
# Let any other errors propagate up.
try:
forms = cgi.FieldStorage()
identifier = float(forms["id"].value)
application = forms["app"].value
level = int(forms["level"].value)
result = int(forms["result"].value)
program = forms["program"].value
row = Report(identifier = identifier, application = application,
level = level, result = result, program = program)
row.put()
except ValueError, KeyError:
logging.error("Unable to extract all form fields.")

2
appengine/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow: /storage

180
appengine/storage.js Normal file
View File

@@ -0,0 +1,180 @@
/**
* Blockly Demo: Storage
*
* Copyright 2012 Google Inc.
* http://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);
};

85
appengine/storage.py Normal file
View File

@@ -0,0 +1,85 @@
"""Blockly Demo: Storage
Copyright 2012 Google Inc.
http://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.
"""
"""Store and retrieve XML with App Engine.
"""
__author__ = "q.neutron@gmail.com (Quynh Neutron)"
import cgi
from random import randint
from google.appengine.ext import db
from google.appengine.api import memcache
import logging
print "Content-Type: text/plain\n"
def keyGen():
# Generate a random string of length KEY_LEN.
KEY_LEN = 6
CHARS = "abcdefghijkmnopqrstuvwxyz23456789" # Exclude l, 0, 1.
max_index = len(CHARS) - 1
return "".join([CHARS[randint(0, max_index)] for x in range(KEY_LEN)])
class Xml(db.Model):
# A row in the database.
xml_hash = db.IntegerProperty()
xml_content = db.TextProperty()
forms = cgi.FieldStorage()
if "xml" in forms:
# Store XML and return a generated key.
xml_content = forms["xml"].value
xml_hash = hash(xml_content)
lookup_query = db.Query(Xml)
lookup_query.filter("xml_hash =", xml_hash)
lookup_result = lookup_query.get()
if lookup_result:
xml_key = lookup_result.key().name()
else:
trials = 0
result = True
while result:
trials += 1
if trials == 100:
raise Exception("Sorry, the generator failed to get a key for you.")
xml_key = keyGen()
result = db.get(db.Key.from_path("Xml", xml_key))
xml = db.Text(xml_content, encoding="utf_8")
row = Xml(key_name = xml_key, xml_hash = xml_hash, xml_content = xml)
row.put()
print xml_key
if "key" in forms:
# Retrieve stored XML based on the provided key.
key_provided = forms["key"].value
# Normalize the string.
key_provided = key_provided.lower().strip()
# Check memcache for a quick match.
xml = memcache.get("XML_" + key_provided)
if xml is None:
# Check datastore for a definitive match.
result = db.get(db.Key.from_path("Xml", key_provided))
if not result:
xml = ""
else:
xml = result.xml_content
# Save to memcache for next hit.
if not memcache.add("XML_" + key_provided, xml, 3600):
logging.error("Memcache set failed.")
print xml.encode("utf-8")