summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGubolin <gubolin@fantasymail.de>2015-01-24 10:20:03 +0100
committerGubolin <gubolin@fantasymail.de>2015-01-24 10:20:03 +0100
commit39c061f6a6071d6c4dd3ba5de642226590a5ac47 (patch)
tree1fcfb5373c6fcb50670828fa6dc470ff192d1a58
parent7311c3ee0cc93e50e5adcb521a94e8738ed98eb2 (diff)
parent2ca378c50bf67b6af5e16ba72af81f4f84db308c (diff)
downloadsnap-39c061f6a6071d6c4dd3ba5de642226590a5ac47.tar.gz
snap-39c061f6a6071d6c4dd3ba5de642226590a5ac47.zip
Merge branch 'master' of https://github.com/jmoenig/Snap--Build-Your-Own-Blocks into development
-rw-r--r--README.md2
-rw-r--r--byob.js20
-rw-r--r--cloud.js1281
-rw-r--r--gui.js81
-rwxr-xr-xhistory.txt22
-rw-r--r--lang-ca.js72
-rw-r--r--lang-it.js103
-rwxr-xr-xlang-ko.js2
-rw-r--r--locale.js18
-rw-r--r--manifest.mf3
-rw-r--r--objects.js45
-rwxr-xr-xscriptsPaneTexture.gifbin155 -> 0 bytes
-rw-r--r--store.js31
-rw-r--r--threads.js8
14 files changed, 968 insertions, 720 deletions
diff --git a/README.md b/README.md
index cf17e18..878274d 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ inspired by Scratch
written by Jens Mönig and Brian Harvey
jens@moenig.org, bh@cs.berkeley.edu
-Copyright (C) 2014 by Jens Mönig and Brian Harvey
+Copyright (C) 2015 by Jens Mönig and Brian Harvey
Snap! is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
diff --git a/byob.js b/byob.js
index b5905ce..ec714f1 100644
--- a/byob.js
+++ b/byob.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -106,7 +106,7 @@ SymbolMorph, isNil*/
// Global stuff ////////////////////////////////////////////////////////
-modules.byob = '2014-November-20';
+modules.byob = '2015-January-21';
// Declarations
@@ -209,12 +209,14 @@ CustomBlockDefinition.prototype.copyAndBindTo = function (sprite) {
c.receiver = sprite; // only for (kludgy) serialization
c.declarations = copy(this.declarations); // might have to go deeper
- c.body = Process.prototype.reify.call(
- null,
- this.body.expression,
- new List(this.inputNames())
- );
- c.body.outerContext = null;
+ if (c.body) {
+ c.body = Process.prototype.reify.call(
+ null,
+ this.body.expression,
+ new List(this.inputNames())
+ );
+ c.body.outerContext = null;
+ }
return c;
};
@@ -1649,7 +1651,7 @@ BlockEditorMorph.prototype.init = function (definition, target) {
scripts = new ScriptsMorph(target);
scripts.isDraggable = false;
scripts.color = IDE_Morph.prototype.groupColor;
- scripts.texture = IDE_Morph.prototype.scriptsPaneTexture;
+ scripts.cachedTexture = IDE_Morph.prototype.scriptsPaneTexture;
scripts.cleanUpMargin = 10;
proto = new PrototypeHatBlockMorph(this.definition);
diff --git a/cloud.js b/cloud.js
index 54189d8..ed7233f 100644
--- a/cloud.js
+++ b/cloud.js
@@ -1,639 +1,642 @@
-/*
-
- cloud.js
-
- a backend API for SNAP!
-
- written by Jens Mönig
-
- Copyright (C) 2014 by Jens Mönig
-
- This file is part of Snap!.
-
- Snap! is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of
- the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-// Global settings /////////////////////////////////////////////////////
-
-/*global modules, IDE_Morph, SnapSerializer, hex_sha512, alert, nop,
-localize*/
-
-modules.cloud = '2014-May-26';
-
-// Global stuff
-
-var Cloud;
-
-var SnapCloud = new Cloud(
- 'https://snapcloud.miosoft.com/miocon/app/login?_app=SnapCloud'
-);
-
-// Cloud /////////////////////////////////////////////////////////////
-
-function Cloud(url) {
- this.username = null;
- this.password = null; // hex_sha512 hashed
- this.url = url;
- this.session = null;
- this.api = {};
-}
-
-Cloud.prototype.clear = function () {
- this.username = null;
- this.password = null;
- this.session = null;
- this.api = {};
-};
-
-Cloud.prototype.hasProtocol = function () {
- return this.url.toLowerCase().indexOf('http') === 0;
-};
-
-// Cloud: Snap! API
-
-Cloud.prototype.signup = function (
- username,
- email,
- callBack,
- errorCall
-) {
- // both callBack and errorCall are two-argument functions
- var request = new XMLHttpRequest(),
- myself = this;
- try {
- request.open(
- "GET",
- (this.hasProtocol() ? '' : 'http://')
- + this.url + 'SignUp'
- + '&Username='
- + encodeURIComponent(username)
- + '&Email='
- + encodeURIComponent(email),
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- if (request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText,
- 'Signup'
- );
- } else {
- callBack.call(
- null,
- request.responseText,
- 'Signup'
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url + 'SignUp',
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-Cloud.prototype.getPublicProject = function (
- id,
- callBack,
- errorCall
-) {
- // id is Username=username&projectName=projectname,
- // where the values are url-component encoded
- // callBack is a single argument function, errorCall take two args
- var request = new XMLHttpRequest(),
- responseList,
- myself = this;
- try {
- request.open(
- "GET",
- (this.hasProtocol() ? '' : 'http://')
- + this.url + 'Public'
- + '&'
- + id,
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- if (request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText
- );
- } else {
- responseList = myself.parseResponse(
- request.responseText
- );
- callBack.call(
- null,
- responseList[0].SourceCode
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url + 'Public',
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-Cloud.prototype.resetPassword = function (
- username,
- callBack,
- errorCall
-) {
- // both callBack and errorCall are two-argument functions
- var request = new XMLHttpRequest(),
- myself = this;
- try {
- request.open(
- "GET",
- (this.hasProtocol() ? '' : 'http://')
- + this.url + 'ResetPW'
- + '&Username='
- + encodeURIComponent(username),
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- if (request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText,
- 'Reset Password'
- );
- } else {
- callBack.call(
- null,
- request.responseText,
- 'Reset Password'
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url + 'ResetPW',
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-Cloud.prototype.connect = function (
- callBack,
- errorCall
-) {
- // both callBack and errorCall are two-argument functions
- var request = new XMLHttpRequest(),
- myself = this;
- try {
- request.open(
- "GET",
- (this.hasProtocol() ? '' : 'http://') + this.url,
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- myself.api = myself.parseAPI(request.responseText);
- myself.session = request.getResponseHeader('MioCracker')
- .split(';')[0];
- if (myself.api.login) {
- callBack.call(null, myself.api, 'Snap!Cloud');
- } else {
- errorCall.call(
- null,
- 'connection failed'
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url,
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-
-Cloud.prototype.login = function (
- username,
- password,
- callBack,
- errorCall
-) {
- var myself = this;
- this.connect(
- function () {
- myself.rawLogin(username, password, callBack, errorCall);
- myself.disconnect();
- },
- errorCall
- );
-};
-
-Cloud.prototype.rawLogin = function (
- username,
- password,
- callBack,
- errorCall
-) {
- // both callBack and errorCall are two-argument functions
- var myself = this,
- pwHash = hex_sha512("miosoft%20miocon,"
- + this.session.split('=')[1] + ","
- + encodeURIComponent(username.toLowerCase()) + ","
- + password // alreadey hex_sha512 hashed
- );
- this.callService(
- 'login',
- function (response, url) {
- if (myself.api.logout) {
- myself.username = username;
- myself.password = password;
- callBack.call(null, response, url);
- } else {
- errorCall.call(
- null,
- 'Service catalog is not available,\nplease retry',
- 'Connection Error:'
- );
- }
- },
- errorCall,
- [username, pwHash]
- );
-};
-
-Cloud.prototype.reconnect = function (
- callBack,
- errorCall
-) {
- if (!(this.username && this.password)) {
- this.message('You are not logged in');
- return;
- }
- this.login(
- this.username,
- this.password,
- callBack,
- errorCall
- );
-};
-
-Cloud.prototype.saveProject = function (ide, callBack, errorCall) {
- var myself = this,
- pdata,
- media;
-
- ide.serializer.isCollectingMedia = true;
- pdata = ide.serializer.serialize(ide.stage);
- media = ide.hasChangedMedia ?
- ide.serializer.mediaXML(ide.projectName) : null;
- ide.serializer.isCollectingMedia = false;
- ide.serializer.flushMedia();
-
- // check if serialized data can be parsed back again
- try {
- ide.serializer.parse(pdata);
- } catch (err) {
- ide.showMessage('Serialization of program data failed:\n' + err);
- throw new Error('Serialization of program data failed:\n' + err);
- }
- if (media !== null) {
- try {
- ide.serializer.parse(media);
- } catch (err) {
- ide.showMessage('Serialization of media failed:\n' + err);
- throw new Error('Serialization of media failed:\n' + err);
- }
- }
- ide.serializer.isCollectingMedia = false;
- ide.serializer.flushMedia();
-
- myself.reconnect(
- function () {
- myself.callService(
- 'saveProject',
- function (response, url) {
- callBack.call(null, response, url);
- myself.disconnect();
- ide.hasChangedMedia = false;
- },
- errorCall,
- [
- ide.projectName,
- pdata,
- media,
- pdata.length,
- media ? media.length : 0
- ]
- );
- },
- errorCall
- );
-};
-
-Cloud.prototype.getProjectList = function (callBack, errorCall) {
- var myself = this;
- this.reconnect(
- function () {
- myself.callService(
- 'getProjectList',
- function (response, url) {
- callBack.call(null, response, url);
- myself.disconnect();
- },
- errorCall
- );
- },
- errorCall
- );
-};
-
-Cloud.prototype.changePassword = function (
- oldPW,
- newPW,
- callBack,
- errorCall
-) {
- var myself = this;
- this.reconnect(
- function () {
- myself.callService(
- 'changePassword',
- function (response, url) {
- callBack.call(null, response, url);
- myself.disconnect();
- },
- errorCall,
- [oldPW, newPW]
- );
- },
- errorCall
- );
-};
-
-Cloud.prototype.logout = function (callBack, errorCall) {
- this.clear();
- this.callService(
- 'logout',
- callBack,
- errorCall
- );
-};
-
-Cloud.prototype.disconnect = function () {
- this.callService(
- 'logout',
- nop,
- nop
- );
-};
-
-// Cloud: backend communication
-
-Cloud.prototype.callURL = function (url, callBack, errorCall) {
- // both callBack and errorCall are optional two-argument functions
- var request = new XMLHttpRequest(),
- myself = this;
- try {
- request.open('GET', url, true);
- request.withCredentials = true;
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.setRequestHeader('MioCracker', this.session);
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- var responseList = myself.parseResponse(
- request.responseText
- );
- callBack.call(null, responseList, url);
- } else {
- errorCall.call(
- null,
- url,
- 'no response from:'
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), url);
- }
-};
-
-Cloud.prototype.callService = function (
- serviceName,
- callBack,
- errorCall,
- args
-) {
- // both callBack and errorCall are optional two-argument functions
- var request = new XMLHttpRequest(),
- service = this.api[serviceName],
- myself = this,
- postDict;
-
- if (!this.session) {
- errorCall.call(null, 'You are not connected', 'Cloud');
- return;
- }
- if (!service) {
- errorCall.call(
- null,
- 'service ' + serviceName + ' is not available',
- 'API'
- );
- return;
- }
- if (args && args.length > 0) {
- postDict = {};
- service.parameters.forEach(function (parm, idx) {
- postDict[parm] = args[idx];
- });
- }
- try {
- request.open(service.method, service.url, true);
- request.withCredentials = true;
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.setRequestHeader('MioCracker', this.session);
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- var responseList = [];
- if (request.responseText &&
- request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText,
- localize('Service:') + ' ' + localize(serviceName)
- );
- return;
- }
- if (serviceName === 'login') {
- myself.api = myself.parseAPI(request.responseText);
- }
- responseList = myself.parseResponse(
- request.responseText
- );
- callBack.call(null, responseList, service.url);
- }
- };
- request.send(this.encodeDict(postDict));
- } catch (err) {
- errorCall.call(this, err.toString(), service.url);
- }
-};
-
-// Cloud: payload transformation
-
-Cloud.prototype.parseAPI = function (src) {
- var api = {},
- services;
- services = src.split(" ");
- services.forEach(function (service) {
- var entries = service.split("&"),
- serviceDescription = {},
- parms;
- entries.forEach(function (entry) {
- var pair = entry.split("="),
- key = decodeURIComponent(pair[0]).toLowerCase(),
- val = decodeURIComponent(pair[1]);
- if (key === "service") {
- api[val] = serviceDescription;
- } else if (key === "parameters") {
- parms = val.split(",");
- if (!(parms.length === 1 && !parms[0])) {
- serviceDescription.parameters = parms;
- }
- } else {
- serviceDescription[key] = val;
- }
- });
- });
- return api;
-};
-
-Cloud.prototype.parseResponse = function (src) {
- var ans = [],
- lines;
- if (!src) {return ans; }
- lines = src.split(" ");
- lines.forEach(function (service) {
- var entries = service.split("&"),
- dict = {};
- entries.forEach(function (entry) {
- var pair = entry.split("="),
- key = decodeURIComponent(pair[0]),
- val = decodeURIComponent(pair[1]);
- dict[key] = val;
- });
- ans.push(dict);
- });
- return ans;
-};
-
-Cloud.prototype.parseDict = function (src) {
- var dict = {};
- if (!src) {return dict; }
- src.split("&").forEach(function (entry) {
- var pair = entry.split("="),
- key = decodeURIComponent(pair[0]),
- val = decodeURIComponent(pair[1]);
- dict[key] = val;
- });
- return dict;
-};
-
-Cloud.prototype.encodeDict = function (dict) {
- var str = '',
- pair,
- key;
- if (!dict) {return null; }
- for (key in dict) {
- if (dict.hasOwnProperty(key)) {
- pair = encodeURIComponent(key)
- + '='
- + encodeURIComponent(dict[key]);
- if (str.length > 0) {
- str += '&';
- }
- str += pair;
- }
- }
- return str;
-};
-
-// Cloud: user messages (to be overridden)
-
-Cloud.prototype.message = function (string) {
- alert(string);
-};
+/*
+
+ cloud.js
+
+ a backend API for SNAP!
+
+ written by Jens Mönig
+
+ Copyright (C) 2015 by Jens Mönig
+
+ This file is part of Snap!.
+
+ Snap! is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+// Global settings /////////////////////////////////////////////////////
+
+/*global modules, IDE_Morph, SnapSerializer, hex_sha512, alert, nop,
+localize*/
+
+modules.cloud = '2015-January-12';
+
+// Global stuff
+
+var Cloud;
+var SnapCloud = new Cloud(
+ 'https://snap.apps.miosoft.com/SnapCloud'
+);
+
+// Cloud /////////////////////////////////////////////////////////////
+
+function Cloud(url) {
+ this.username = null;
+ this.password = null; // hex_sha512 hashed
+ this.url = url;
+ this.session = null;
+ this.limo = null;
+ this.route = null;
+ this.api = {};
+}
+
+Cloud.prototype.clear = function () {
+ this.username = null;
+ this.password = null;
+ this.session = null;
+ this.limo = null;
+ this.route = null;
+ this.api = {};
+};
+
+Cloud.prototype.hasProtocol = function () {
+ return this.url.toLowerCase().indexOf('http') === 0;
+};
+
+Cloud.prototype.setRoute = function (username) {
+ var routes = 10,
+ userNum = 0,
+ i;
+
+ for (i = 0; i < username.length; i += 1) {
+ userNum += username.charCodeAt(i);
+ }
+ userNum = userNum % routes + 1;
+ this.route = '.sc1m' +
+ (userNum < 10 ? '0' : '') +
+ userNum;
+};
+
+// Cloud: Snap! API
+
+Cloud.prototype.signup = function (
+ username,
+ email,
+ callBack,
+ errorCall
+) {
+ // both callBack and errorCall are two-argument functions
+ var request = new XMLHttpRequest(),
+ myself = this;
+ try {
+ request.open(
+ "GET",
+ (this.hasProtocol() ? '' : 'http://')
+ + this.url + 'SignUp'
+ + '?Username='
+ + encodeURIComponent(username)
+ + '&Email='
+ + encodeURIComponent(email),
+ true
+ );
+ request.setRequestHeader(
+ "Content-Type",
+ "application/x-www-form-urlencoded"
+ );
+ request.withCredentials = true;
+ request.onreadystatechange = function () {
+ if (request.readyState === 4) {
+ if (request.responseText) {
+ if (request.responseText.indexOf('ERROR') === 0) {
+ errorCall.call(
+ this,
+ request.responseText,
+ 'Signup'
+ );
+ } else {
+ callBack.call(
+ null,
+ request.responseText,
+ 'Signup'
+ );
+ }
+ } else {
+ errorCall.call(
+ null,
+ myself.url + 'SignUp',
+ localize('could not connect to:')
+ );
+ }
+ }
+ };
+ request.send(null);
+ } catch (err) {
+ errorCall.call(this, err.toString(), 'Snap!Cloud');
+ }
+};
+
+Cloud.prototype.getPublicProject = function (
+ id,
+ callBack,
+ errorCall
+) {
+ // id is Username=username&projectName=projectname,
+ // where the values are url-component encoded
+ // callBack is a single argument function, errorCall take two args
+ var request = new XMLHttpRequest(),
+ responseList,
+ myself = this;
+ try {
+ request.open(
+ "GET",
+ (this.hasProtocol() ? '' : 'http://')
+ + this.url + 'Public'
+ + '?'
+ + id,
+ true
+ );
+ request.setRequestHeader(
+ "Content-Type",
+ "application/x-www-form-urlencoded"
+ );
+ request.withCredentials = true;
+ request.onreadystatechange = function () {
+ if (request.readyState === 4) {
+ if (request.responseText) {
+ if (request.responseText.indexOf('ERROR') === 0) {
+ errorCall.call(
+ this,
+ request.responseText
+ );
+ } else {
+ responseList = myself.parseResponse(
+ request.responseText
+ );
+ callBack.call(
+ null,
+ responseList[0].SourceCode
+ );
+ }
+ } else {
+ errorCall.call(
+ null,
+ myself.url + 'Public',
+ localize('could not connect to:')
+ );
+ }
+ }
+ };
+ request.send(null);
+ } catch (err) {
+ errorCall.call(this, err.toString(), 'Snap!Cloud');
+ }
+};
+
+Cloud.prototype.resetPassword = function (
+ username,
+ callBack,
+ errorCall
+) {
+ // both callBack and errorCall are two-argument functions
+ var request = new XMLHttpRequest(),
+ myself = this;
+ try {
+ request.open(
+ "GET",
+ (this.hasProtocol() ? '' : 'http://')
+ + this.url + 'ResetPW'
+ + '?Username='
+ + encodeURIComponent(username),
+ true
+ );
+ request.setRequestHeader(
+ "Content-Type",
+ "application/x-www-form-urlencoded"
+ );
+ request.withCredentials = true;
+ request.onreadystatechange = function () {
+ if (request.readyState === 4) {
+ if (request.responseText) {
+ if (request.responseText.indexOf('ERROR') === 0) {
+ errorCall.call(
+ this,
+ request.responseText,
+ 'Reset Password'
+ );
+ } else {
+ callBack.call(
+ null,
+ request.responseText,
+ 'Reset Password'
+ );
+ }
+ } else {
+ errorCall.call(
+ null,
+ myself.url + 'ResetPW',
+ localize('could not connect to:')
+ );
+ }
+ }
+ };
+ request.send(null);
+ } catch (err) {
+ errorCall.call(this, err.toString(), 'Snap!Cloud');
+ }
+};
+
+Cloud.prototype.login = function (
+ username,
+ password,
+ callBack,
+ errorCall
+) {
+ // both callBack and errorCall are two-argument functions
+ var request = new XMLHttpRequest(),
+ usr = JSON.stringify({'__h': password, '__u': username}),
+ myself = this;
+ this.setRoute(username);
+ try {
+ request.open(
+ "POST",
+ (this.hasProtocol() ? '' : 'http://') +
+ this.url +
+ '?SESSIONGLUE=' +
+ this.route,
+ true
+ );
+ request.setRequestHeader(
+ "Content-Type",
+ "application/json; charset=utf-8"
+ );
+ // glue this session to a route:
+ request.setRequestHeader('SESSIONGLUE', this.route);
+ request.withCredentials = true;
+ request.onreadystatechange = function () {
+ if (request.readyState === 4) {
+ if (request.responseText) {
+ myself.api = myself.parseAPI(request.responseText);
+ myself.session = request.getResponseHeader('MioCracker')
+ .split(';')[0];
+ // set the cookie identifier:
+ myself.limo = this.getResponseHeader("miocracker")
+ .substring(
+ 9,
+ this.getResponseHeader("miocracker").indexOf("=")
+ );
+ if (myself.api.logout) {
+ myself.username = username;
+ myself.password = password;
+ callBack.call(null, myself.api, 'Snap!Cloud');
+ } else {
+ errorCall.call(
+ null,
+ request.responseText,
+ 'connection failed'
+ );
+ }
+ } else {
+ errorCall.call(
+ null,
+ myself.url,
+ localize('could not connect to:')
+ );
+ }
+ }
+ };
+ request.send(usr);
+ } catch (err) {
+ errorCall.call(this, err.toString(), 'Snap!Cloud');
+ }
+};
+
+Cloud.prototype.reconnect = function (
+ callBack,
+ errorCall
+) {
+ if (!(this.username && this.password)) {
+ this.message('You are not logged in');
+ return;
+ }
+ this.login(
+ this.username,
+ this.password,
+ callBack,
+ errorCall
+ );
+};
+
+Cloud.prototype.saveProject = function (ide, callBack, errorCall) {
+ var myself = this,
+ pdata,
+ media;
+
+ ide.serializer.isCollectingMedia = true;
+ pdata = ide.serializer.serialize(ide.stage);
+ media = ide.hasChangedMedia ?
+ ide.serializer.mediaXML(ide.projectName) : null;
+ ide.serializer.isCollectingMedia = false;
+ ide.serializer.flushMedia();
+
+ // check if serialized data can be parsed back again
+ try {
+ ide.serializer.parse(pdata);
+ } catch (err) {
+ ide.showMessage('Serialization of program data failed:\n' + err);
+ throw new Error('Serialization of program data failed:\n' + err);
+ }
+ if (media !== null) {
+ try {
+ ide.serializer.parse(media);
+ } catch (err) {
+ ide.showMessage('Serialization of media failed:\n' + err);
+ throw new Error('Serialization of media failed:\n' + err);
+ }
+ }
+ ide.serializer.isCollectingMedia = false;
+ ide.serializer.flushMedia();
+
+ myself.reconnect(
+ function () {
+ myself.callService(
+ 'saveProject',
+ function (response, url) {
+ callBack.call(null, response, url);
+ myself.disconnect();
+ ide.hasChangedMedia = false;
+ },
+ errorCall,
+ [
+ ide.projectName,
+ pdata,
+ media,
+ pdata.length,
+ media ? media.length : 0
+ ]
+ );
+ },
+ errorCall
+ );
+};
+
+Cloud.prototype.getProjectList = function (callBack, errorCall) {
+ var myself = this;
+ this.reconnect(
+ function () {
+ myself.callService(
+ 'getProjectList',
+ function (response, url) {
+ callBack.call(null, response, url);
+ myself.disconnect();
+ },
+ errorCall
+ );
+ },
+ errorCall
+ );
+};
+
+Cloud.prototype.changePassword = function (
+ oldPW,
+ newPW,
+ callBack,
+ errorCall
+) {
+ var myself = this;
+ this.reconnect(
+ function () {
+ myself.callService(
+ 'changePassword',
+ function (response, url) {
+ callBack.call(null, response, url);
+ myself.disconnect();
+ },
+ errorCall,
+ [hex_sha512(oldPW), hex_sha512(newPW)]
+ );
+ },
+ errorCall
+ );
+};
+
+Cloud.prototype.logout = function (callBack, errorCall) {
+ this.clear();
+ this.callService(
+ 'logout',
+ callBack,
+ errorCall
+ );
+};
+
+Cloud.prototype.disconnect = function () {
+ this.callService(
+ 'logout',
+ nop,
+ nop
+ );
+};
+
+// Cloud: backend communication
+
+Cloud.prototype.callURL = function (url, callBack, errorCall) {
+ // both callBack and errorCall are optional two-argument functions
+ var request = new XMLHttpRequest(),
+ stickyUrl,
+ myself = this;
+ try {
+ // set the Limo. Also set the glue as a query paramter for backup.
+ stickyUrl = url +
+ '&SESSIONGLUE=' +
+ this.route +
+ '&_Limo=' +
+ this.limo;
+ request.open('GET', stickyUrl, true);
+ request.withCredentials = true;
+ request.setRequestHeader(
+ "Content-Type",
+ "application/x-www-form-urlencoded"
+ );
+ request.setRequestHeader('MioCracker', this.session);
+ // Set the glue as a request header.
+ request.setRequestHeader('SESSIONGLUE', this.route);
+ request.onreadystatechange = function () {
+ if (request.readyState === 4) {
+ if (request.responseText) {
+ var responseList = myself.parseResponse(
+ request.responseText
+ );
+ callBack.call(null, responseList, url);
+ } else {
+ errorCall.call(
+ null,
+ url,
+ 'no response from:'
+ );
+ }
+ }
+ };
+ request.send(null);
+ } catch (err) {
+ errorCall.call(this, err.toString(), url);
+ }
+};
+
+Cloud.prototype.callService = function (
+ serviceName,
+ callBack,
+ errorCall,
+ args
+) {
+ // both callBack and errorCall are optional two-argument functions
+ var request = new XMLHttpRequest(),
+ service = this.api[serviceName],
+ myself = this,
+ stickyUrl,
+ postDict;
+
+ if (!this.session) {
+ errorCall.call(null, 'You are not connected', 'Cloud');
+ return;
+ }
+ if (!service) {
+ errorCall.call(
+ null,
+ 'service ' + serviceName + ' is not available',
+ 'API'
+ );
+ return;
+ }
+ if (args && args.length > 0) {
+ postDict = {};
+ service.parameters.forEach(function (parm, idx) {
+ postDict[parm] = args[idx];
+ });
+ }
+ try {
+ stickyUrl = this.url +
+ '/' +
+ service.url +
+ '&SESSIONGLUE=' +
+ this.route +
+ '&_Limo=' +
+ this.limo;
+ request.open(service.method, stickyUrl, true);
+ request.withCredentials = true;
+ request.setRequestHeader(
+ "Content-Type",
+ "application/x-www-form-urlencoded"
+ );
+ request.setRequestHeader('MioCracker', this.session);
+ request.setRequestHeader('SESSIONGLUE', this.route);
+ request.onreadystatechange = function () {
+ if (request.readyState === 4) {
+ var responseList = [];
+ if (request.responseText &&
+ request.responseText.indexOf('ERROR') === 0) {
+ errorCall.call(
+ this,
+ request.responseText,
+ localize('Service:') + ' ' + localize(serviceName)
+ );
+ return;
+ }
+ if (serviceName === 'login') {
+ myself.api = myself.parseAPI(request.responseText);
+ }
+ responseList = myself.parseResponse(
+ request.responseText
+ );
+ callBack.call(null, responseList, service.url);
+ }
+ };
+ request.send(this.encodeDict(postDict));
+ } catch (err) {
+ errorCall.call(this, err.toString(), service.url);
+ }
+};
+
+// Cloud: payload transformation
+
+Cloud.prototype.parseAPI = function (src) {
+ var api = {},
+ services;
+ services = src.split(" ");
+ services.forEach(function (service) {
+ var entries = service.split("&"),
+ serviceDescription = {},
+ parms;
+ entries.forEach(function (entry) {
+ var pair = entry.split("="),
+ key = decodeURIComponent(pair[0]).toLowerCase(),
+ val = decodeURIComponent(pair[1]);
+ if (key === "service") {
+ api[val] = serviceDescription;
+ } else if (key === "parameters") {
+ parms = val.split(",");
+ if (!(parms.length === 1 && !parms[0])) {
+ serviceDescription.parameters = parms;
+ }
+ } else {
+ serviceDescription[key] = val;
+ }
+ });
+ });
+ return api;
+};
+
+Cloud.prototype.parseResponse = function (src) {
+ var ans = [],
+ lines;
+ if (!src) {return ans; }
+ lines = src.split(" ");
+ lines.forEach(function (service) {
+ var entries = service.split("&"),
+ dict = {};
+ entries.forEach(function (entry) {
+ var pair = entry.split("="),
+ key = decodeURIComponent(pair[0]),
+ val = decodeURIComponent(pair[1]);
+ dict[key] = val;
+ });
+ ans.push(dict);
+ });
+ return ans;
+};
+
+Cloud.prototype.parseDict = function (src) {
+ var dict = {};
+ if (!src) {return dict; }
+ src.split("&").forEach(function (entry) {
+ var pair = entry.split("="),
+ key = decodeURIComponent(pair[0]),
+ val = decodeURIComponent(pair[1]);
+ dict[key] = val;
+ });
+ return dict;
+};
+
+Cloud.prototype.encodeDict = function (dict) {
+ var str = '',
+ pair,
+ key;
+ if (!dict) {return null; }
+ for (key in dict) {
+ if (dict.hasOwnProperty(key)) {
+ pair = encodeURIComponent(key)
+ + '='
+ + encodeURIComponent(dict[key]);
+ if (str.length > 0) {
+ str += '&';
+ }
+ str += pair;
+ }
+ }
+ return str;
+};
+
+// Cloud: user messages (to be overridden)
+
+Cloud.prototype.message = function (string) {
+ alert(string);
+};
diff --git a/gui.js b/gui.js
index b60f5e6..2a2fde2 100644
--- a/gui.js
+++ b/gui.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -69,7 +69,7 @@ SpeechBubbleMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.gui = '2014-December-04';
+modules.gui = '2015-January-21';
// Declarations
@@ -119,7 +119,7 @@ IDE_Morph.prototype.setDefaultDesign = function () {
];
IDE_Morph.prototype.rotationStyleColors = IDE_Morph.prototype.tabColors;
IDE_Morph.prototype.appModeColor = new Color();
- IDE_Morph.prototype.scriptsPaneTexture = 'scriptsPaneTexture.gif';
+ IDE_Morph.prototype.scriptsPaneTexture = this.scriptsTexture();
IDE_Morph.prototype.padding = 5;
SpriteIconMorph.prototype.labelColor
@@ -172,6 +172,22 @@ IDE_Morph.prototype.setFlatDesign = function () {
= IDE_Morph.prototype.buttonLabelColor;
};
+IDE_Morph.prototype.scriptsTexture = function () {
+ var pic = newCanvas(new Point(100, 100)), // bigger scales faster
+ ctx = pic.getContext('2d'),
+ i;
+ for (i = 0; i < 100; i += 4) {
+ ctx.fillStyle = this.frameColor.toString();
+ ctx.fillRect(i, 0, 1, 100);
+ ctx.fillStyle = this.groupColor.lighter(6).toString();
+ ctx.fillRect(i + 1, 0, 1, 100);
+ ctx.fillRect(i + 3, 0, 1, 100);
+ ctx.fillStyle = this.groupColor.toString();
+ ctx.fillRect(i + 2, 0, 1, 100);
+ }
+ return pic;
+};
+
IDE_Morph.prototype.setDefaultDesign();
// IDE_Morph instance creation:
@@ -324,7 +340,6 @@ IDE_Morph.prototype.openIn = function (world) {
this.inform('Snap!', motd);
}
*/
-
function interpretUrlAnchors() {
var dict;
if (location.hash.substr(0, 6) === '#open:') {
@@ -377,6 +392,7 @@ IDE_Morph.prototype.openIn = function (world) {
function () {
msg = myself.showMessage('Opening project...');
},
+ function () {nop(); }, // yield (bug in Chrome)
function () {
if (projectData.indexOf('<snapdata') === 0) {
myself.rawOpenCloudDataString(projectData);
@@ -1244,7 +1260,7 @@ IDE_Morph.prototype.createSpriteEditor = function () {
if (this.currentTab === 'scripts') {
scripts.isDraggable = false;
scripts.color = this.groupColor;
- scripts.texture = this.scriptsPaneTexture;
+ scripts.cachedTexture = this.scriptsPaneTexture;
this.spriteEditor = new ScrollFrameMorph(
scripts,
@@ -2155,6 +2171,7 @@ IDE_Morph.prototype.cloudMenu = function () {
'Opening project...'
);
},
+ function () {nop(); }, // yield (Chrome)
function () {
myself.rawOpenCloudDataString(
projectData
@@ -2621,7 +2638,7 @@ IDE_Morph.prototype.aboutSnap = function () {
world = this.world();
aboutTxt = 'Snap! 4.0\nBuild Your Own Blocks\n\n--- beta ---\n\n'
- + 'Copyright \u24B8 2014 Jens M\u00F6nig and '
+ + 'Copyright \u24B8 2015 Jens M\u00F6nig and '
+ 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
@@ -3039,6 +3056,7 @@ IDE_Morph.prototype.openProjectString = function (str) {
function () {
msg = myself.showMessage('Opening project...');
},
+ function () {nop(); }, // yield (bug in Chrome)
function () {
myself.rawOpenProjectString(str);
},
@@ -3057,12 +3075,18 @@ IDE_Morph.prototype.rawOpenProjectString = function (str) {
StageMorph.prototype.enableCodeMapping = false;
if (Process.prototype.isCatchingErrors) {
try {
- this.serializer.openProject(this.serializer.load(str), this);
+ this.serializer.openProject(
+ this.serializer.load(str, this),
+ this
+ );
} catch (err) {
this.showMessage('Load failed: ' + err);
}
} else {
- this.serializer.openProject(this.serializer.load(str), this);
+ this.serializer.openProject(
+ this.serializer.load(str, this),
+ this
+ );
}
this.stopFastTracking();
};
@@ -3074,6 +3098,7 @@ IDE_Morph.prototype.openCloudDataString = function (str) {
function () {
msg = myself.showMessage('Opening project...');
},
+ function () {nop(); }, // yield (bug in Chrome)
function () {
myself.rawOpenCloudDataString(str);
},
@@ -3094,7 +3119,10 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) {
model = this.serializer.parse(str);
this.serializer.loadMediaModel(model.childNamed('media'));
this.serializer.openProject(
- this.serializer.loadProjectModel(model.childNamed('project')),
+ this.serializer.loadProjectModel(
+ model.childNamed('project'),
+ this
+ ),
this
);
} catch (err) {
@@ -3104,7 +3132,10 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) {
model = this.serializer.parse(str);
this.serializer.loadMediaModel(model.childNamed('media'));
this.serializer.openProject(
- this.serializer.loadProjectModel(model.childNamed('project')),
+ this.serializer.loadProjectModel(
+ model.childNamed('project'),
+ this
+ ),
this
);
}
@@ -3118,6 +3149,7 @@ IDE_Morph.prototype.openBlocksString = function (str, name, silently) {
function () {
msg = myself.showMessage('Opening blocks...');
},
+ function () {nop(); }, // yield (bug in Chrome)
function () {
myself.rawOpenBlocksString(str, name, silently);
},
@@ -3164,6 +3196,7 @@ IDE_Morph.prototype.openSpritesString = function (str) {
function () {
msg = myself.showMessage('Opening sprite...');
},
+ function () {nop(); }, // yield (bug in Chrome)
function () {
myself.rawOpenSpritesString(str);
},
@@ -3440,6 +3473,15 @@ IDE_Morph.prototype.toggleAppMode = function (appMode) {
}).forEach(function (s) {
s.adjustScrollBars();
});
+ // prevent rotation and draggability controls from
+ // showing for the stage
+ if (this.currentSprite === this.stage) {
+ this.spriteBar.children.forEach(function (child) {
+ if (child instanceof PushButtonMorph) {
+ child.hide();
+ }
+ });
+ }
}
this.setExtent(this.world().extent()); // resume trackChanges
};
@@ -3643,7 +3685,8 @@ IDE_Morph.prototype.userSetBlocksScale = function () {
sample = new FrameMorph();
sample.acceptsDrops = false;
- sample.texture = this.scriptsPaneTexture;
+ sample.color = IDE_Morph.prototype.groupColor;
+ sample.cachedTexture = this.scriptsPaneTexture;
sample.setExtent(new Point(250, 180));
scrpt.setPosition(sample.position().add(10));
sample.add(scrpt);
@@ -4255,6 +4298,9 @@ IDE_Morph.prototype.cloudResponse = function () {
IDE_Morph.prototype.cloudError = function () {
var myself = this;
+ // try finding an eplanation what's going on
+ // has some issues, commented out for now
+ /*
function getURL(url) {
try {
var request = new XMLHttpRequest();
@@ -4268,13 +4314,15 @@ IDE_Morph.prototype.cloudError = function () {
return null;
}
}
+ */
return function (responseText, url) {
// first, try to find out an explanation for the error
// and notify the user about it,
// if none is found, show an error dialog box
var response = responseText,
- explanation = getURL('http://snap.berkeley.edu/cloudmsg.txt');
+ // explanation = getURL('http://snap.berkeley.edu/cloudmsg.txt'),
+ explanation = null;
if (myself.shield) {
myself.shield.destroy();
myself.shield = null;
@@ -4361,14 +4409,7 @@ IDE_Morph.prototype.setCloudURL = function () {
null,
{
'Snap!Cloud' :
- 'https://snapcloud.miosoft.com/miocon/app/' +
- 'login?_app=SnapCloud',
- 'local network lab' :
- '192.168.2.107:8087/miocon/app/login?_app=SnapCloud',
- 'local network office' :
- '192.168.186.146:8087/miocon/app/login?_app=SnapCloud',
- 'localhost dev' :
- 'localhost/miocon/app/login?_app=SnapCloud'
+ 'https://snap.apps.miosoft.com/SnapCloud'
}
);
};
diff --git a/history.txt b/history.txt
index 6d6f0ff..deb76a6 100755
--- a/history.txt
+++ b/history.txt
@@ -2404,3 +2404,25 @@ ______
* Objects, Store: Experimental “processes” count watcher (hidden in dev mode)
* Threads: Remove terminated processes from expired clones
* Threads: Let “zombifying” scripts access receivers’ local vars
+
+150112
+------
+* Cloud, GUI: Backend load balancing support, eliminate now obsolete authentication roundtrip, Cloud error message tweaks
+* Store: notify users of potential incompatibilities when opening projects created in other forks (e.g. BeetleBlocks)
+* Threads: Don’t highlight scripts running inside clones (boosts performance), Thanks, @aranlunzer, for the hint!
+* Objects: Disable clones from being edited via their context menus or double-click
+* Italian translation update, thanks, Alberto Firpo!
+* GUI: add additional yields to nextSteps() (work around a bug in Chrome)
+
+150113
+------
+* BYOB: fixed #702
+* GUI: fixed #680
+
+150121
+------
+* Objects: Keep layering of nested sprites thru drag & drop
+* GUI, Store, BYOB: Generate ScriptsPaneTexture programmatically
+* GUI: Fix Zoom Dialog’s sample background in “flat” design
+* Updated Korean and Catalan translations, thanks, Yunjae Jang and Bernat Romagosa!
+* Objects: Fix speech bubbles of dragged nested sprites
diff --git a/lang-ca.js b/lang-ca.js
index 8991d45..c57d0d2 100644
--- a/lang-ca.js
+++ b/lang-ca.js
@@ -6,7 +6,7 @@
written by Jens Mönig
- Copyright (C) 2013 by Jens Mönig
+ Copyright (C) 2014 by Jens Mönig
This file is part of Snap!.
@@ -183,9 +183,9 @@ SnapTranslator.dict.ca = {
'language_translator':
'Bernat Romagosa Carrasquer', // your name for the Translators tab
'translator_e-mail':
- 'tibabenfortlapalanca@gmail.com', // optional
+ 'bromagosa@citilab.eu', // optional
'last_changed':
- '2013-11-26', // this, too, will appear in the Translators tab
+ '2015-01-21', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -447,12 +447,20 @@ SnapTranslator.dict.ca = {
'si %b llavors %c si no %c',
'report %s':
'retorna %s',
- 'stop block':
- 'atura el bloc',
- 'stop script':
- 'atura aquest programa',
- 'stop all %stop':
- 'atura-ho tot %stop',
+ 'stop %stopChoices':
+ 'atura %stopChoices',
+ 'all':
+ 'tot',
+ 'this script':
+ 'aquest programa',
+ 'this block':
+ 'aquest block',
+ 'stop %stopOthersChoices':
+ 'atura %stopOthersChoices',
+ 'all but this script':
+ 'tot excepte aquest programa',
+ 'other scripts in sprite':
+ 'els altres programes d\'aquest objecte',
'pause all %pause':
'pausa-ho tot %pause',
'run %cmdRing %inputs':
@@ -645,7 +653,7 @@ SnapTranslator.dict.ca = {
'Import...':
'Importar...',
'file menu import hint':
- 'pistes del menú d\'importació',
+ 'carrega una llibreria de projecte\no de blocs exportada, un vestit\no un so',
'Export project as plain text...':
'Exportar projecte en text pla...',
'Export project...':
@@ -676,6 +684,16 @@ SnapTranslator.dict.ca = {
'Idioma...',
'Zoom blocks...':
'Mida dels blocs...',
+ 'Stage size...':
+ 'Mida de l\'escenari...',
+ 'Stage size':
+ 'Mida de l\'escenari',
+ 'Stage width':
+ 'Amplada de l\'escenari',
+ 'Stage height':
+ 'Alçada de l\'escenari',
+ 'Default':
+ 'Per defecte',
'Blurred shadows':
'Ombres suavitzades',
'uncheck to use solid drop\nshadows and highlights':
@@ -744,9 +762,9 @@ SnapTranslator.dict.ca = {
'marca\'m per habilitar\nles animacions de la interfície',
'Thread safe scripts':
'Fil d\'execució segur',
- 'uncheck to allow\nscript reentrancy':
+ 'uncheck to allow\nscript reentrance':
'desmarca\'m per permetre\nla re-entrada als programes',
- 'check to disallow\nscript reentrancy':
+ 'check to disallow\nscript reentrance':
'marca\'m per no permetre\nla re-entrada als programes',
'Prefer smooth animations':
'Suavitza les animacions',
@@ -754,6 +772,12 @@ SnapTranslator.dict.ca = {
'desmarca\'m per augmentar la velocitat de\nles animacions fins la màxima capacitat d\'aquesta màquina',
'check for smooth, predictable\nanimations across computers':
'marca\'m per aconseguir unes animacions\nmés suaus i a velocitat predible en màquines diferents',
+ 'Flat line ends':
+ 'Línies del llapis rectes',
+ 'check for flat ends of lines':
+ 'marca\'m per fer que els\nextrems de les línies del\nllapis siguin rectes',
+ 'uncheck for round ends of lines':
+ 'desmarca\'m per fer que\nels extrems de les línies\ndel llapis siguin arrodonits',
// inputs
'with inputs':
@@ -806,6 +830,8 @@ SnapTranslator.dict.ca = {
// sprites:
'edit':
'editar',
+ 'move':
+ 'moure',
'detach from':
'desenganxa de',
'detach all parts':
@@ -878,9 +904,9 @@ SnapTranslator.dict.ca = {
// zoom blocks
'Zoom blocks':
- 'Canvia la mida dels blocs',
+ 'Canvia la mida dels blocs',
'build':
- 'fes',
+ 'construeix',
'your own':
'els teus propis',
'blocks':
@@ -934,7 +960,7 @@ SnapTranslator.dict.ca = {
// save project
'Save Project As...':
- 'Anomena i desa projecte...',
+ 'Anomena i desa projecte...',
// export blocks
'Export blocks':
@@ -945,8 +971,6 @@ SnapTranslator.dict.ca = {
'aquest projecte encara no\nté cap bloc personalitzat',
'select':
'seleccionar',
- 'all':
- 'tots els blocs',
'none':
'cap bloc',
@@ -1024,7 +1048,7 @@ SnapTranslator.dict.ca = {
'About Snap':
'Sobre Snap',
'Back...':
- 'Enrera...',
+ 'Enrere...',
'License...':
'Llicència...',
'Modules...':
@@ -1094,8 +1118,16 @@ SnapTranslator.dict.ca = {
'Buit',
// graphical effects
+ 'brightness':
+ 'brillantor',
'ghost':
'fantasma',
+ 'negative':
+ 'negatiu',
+ 'comic':
+ 'còmic',
+ 'confetti':
+ 'confeti',
// keys
'space':
@@ -1187,7 +1219,7 @@ SnapTranslator.dict.ca = {
// math functions
'abs':
- 'abs',
+ 'valor absolut',
'floor':
'part entera',
'sqrt':
@@ -1210,6 +1242,8 @@ SnapTranslator.dict.ca = {
'e^',
// delimiters
+ 'letter':
+ 'lletra',
'whitespace':
'espai en blanc',
'line':
diff --git a/lang-it.js b/lang-it.js
index 44111aa..85600a0 100644
--- a/lang-it.js
+++ b/lang-it.js
@@ -181,11 +181,11 @@ SnapTranslator.dict.it = {
'language_name':
'Italiano', // the name as it should appear in the language menu
'language_translator':
- 'Stefano Federici', // your name for the Translators tab
+ 'Stefano Federici, Alberto Firpo', // your name for the Translators tab
'translator_e-mail':
- 's_federici@yahoo.com', // optional
+ 's_federici@yahoo.com, albertofirpo12@gmail.com', // optional
'last_changed':
- '2012-10-16', // this, too, will appear in the Translators tab
+ '2015-01-12', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -426,6 +426,10 @@ SnapTranslator.dict.it = {
'invia a tutti %msg e attendi',
'Message name':
'Nome messaggio',
+ 'message':
+ 'messaggio',
+ 'any message':
+ 'qualunque messaggio',
'wait %n secs':
'attendi %n secondi',
'wait until %b':
@@ -444,10 +448,22 @@ SnapTranslator.dict.it = {
'risultato %s',
'stop block':
'ferma il blocco',
+ 'all':
+ 'tutti',
+ 'this script':
+ 'questo script',
+ 'this block':
+ 'questo Blocco',
'stop script':
'ferma lo script',
'stop all %stop':
'ferma tutto %stop',
+ 'all but this script':
+ 'tutto tranne questo script',
+ 'other scripts in sprite':
+ 'altri script dello sprite',
+ 'pause all %pause':
+ 'pausa tutto %pause',
'run %cmdRing %inputs':
'esegui %cmdRing %inputs',
'launch %cmdRing %inputs':
@@ -533,6 +549,8 @@ SnapTranslator.dict.it = {
'falso',
'join %words':
'unione di %words',
+ 'split %s by %delim':
+ 'separa %s di %delim',
'hello':
'ciao',
'world':
@@ -558,6 +576,8 @@ SnapTranslator.dict.it = {
'Nuova variabile',
'Variable name':
'Nome della variabile?',
+ 'Script variable name':
+ 'Nome della variabile locale?',
'Delete a variable':
'Cancella variabile',
@@ -604,6 +624,8 @@ SnapTranslator.dict.it = {
// snap menu
'About...':
'Informazioni su Snap!...',
+ 'Reference manual':
+ 'Manuale',
'Snap! website':
'Sito web di Snap!',
'Download source':
@@ -648,6 +670,10 @@ SnapTranslator.dict.it = {
'Importa tools',
'load the official library of\npowerful blocks':
'carica la libreria ufficiale di\nblocchi Snap',
+ 'Libraries...':
+ 'Modulo...',
+ 'Import library':
+ 'Importa modulo',
// cloud menu
'Login...':
@@ -660,6 +686,16 @@ SnapTranslator.dict.it = {
'Lingua...',
'Zoom blocks...':
'Zoom dei blocchi...',
+ 'Stage size...':
+ 'Dimensione pannello...',
+ 'Stage size':
+ 'Dimensione pannello',
+ 'Stage width':
+ 'Larghezza pannello',
+ 'Stage height':
+ 'Altezza pannello',
+ 'Default':
+ 'Default',
'Blurred shadows':
'Ombreggiature attenuate',
'uncheck to use solid drop\nshadows and highlights':
@@ -688,6 +724,13 @@ SnapTranslator.dict.it = {
'disabilitare per permettere agli slot di espellere\ni reporter inclusi al loro interno',
'Long form input dialog':
'Usa finestra degli input estesa',
+ 'Plain prototype labels':
+ 'Etichetta prototipo base',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'disabilitare per visualizzare sempre (+) \nnelle etichette dei blocchi prototipo',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'abilitare per visualizzare sempre (+) \nnelle etichette dei blocchi prototipo',
+
'check to always show slot\ntypes in the input dialog':
'abilitare per mostrare sempre i tipi degli slot\nnella finestra di creazione degli input',
'uncheck to use the input\ndialog in short form':
@@ -734,6 +777,13 @@ SnapTranslator.dict.it = {
'disabilitare per massima velocità\na framerate variabile',
'check for smooth, predictable\nanimations across computers':
'abilitare per avere animazioni\nfluide su tutti i computer',
+ 'Flat line ends':
+ 'fine linea piana',
+ 'check for flat ends of lines':
+ 'abilitare per fine linea netti',
+ 'uncheck for round ends of lines':
+ 'disabilitare per fine linea arrotondati',
+
// inputs
'with inputs':
@@ -742,11 +792,20 @@ SnapTranslator.dict.it = {
'con variabili:',
'Input Names:':
'Con Variabili:',
+ 'input list:':
+ 'con liste:',
+
// context menus:
'help':
'aiuto',
+ // palette:
+ 'hide primitives':
+ 'nascondi primitive',
+ 'show primitives':
+ 'mostra primitive',
+
// blocks:
'help...':
'aiuto...',
@@ -778,6 +837,12 @@ SnapTranslator.dict.it = {
// sprites:
'edit':
'modifica',
+ 'move':
+ 'muovi',
+ 'detach from':
+ 'stacca da',
+ 'detach all parts':
+ 'stacca tutte le parti',
'export...':
'esporta...',
@@ -796,6 +861,15 @@ SnapTranslator.dict.it = {
'riordina gli script\nuno sotto l\'altro',
'add comment':
'aggiungi un commento',
+ 'undrop':
+ 'annulla cancellazione',
+ 'undo the last\nblock drop\nin this pane':
+ 'annulla ultima cancellazione\ndi blocco\n in questo pannello',
+ 'scripts pic...':
+ 'immagine script...',
+ 'open a new window\nwith a picture of all scripts':
+ 'apri una nuova finestra\ncon immagine dello script',
+
'make a block...':
'crea un blocco...',
@@ -823,6 +897,8 @@ SnapTranslator.dict.it = {
// buttons
'OK':
'OK',
+ 'Ok':
+ 'OK',
'Cancel':
'Annulla',
'Yes':
@@ -1057,8 +1133,17 @@ SnapTranslator.dict.it = {
'Vuoto',
// graphical effects
+ 'brightness':
+ 'Luminosita',
'ghost':
'fantasma',
+ 'negative':
+ 'negativo',
+ 'comic':
+ 'comic',
+ 'confetti':
+ 'confetti',
+
// keys
'space':
@@ -1170,6 +1255,18 @@ SnapTranslator.dict.it = {
'e^':
'e^',
+ // delimiters
+ 'letter':
+ 'lettera',
+ 'whitespace':
+ 'spazio',
+ 'line':
+ 'linea',
+ 'tab':
+ 'tabulatore',
+ 'cr':
+ 'A capo',
+
// data types
'number':
'numero',
diff --git a/lang-ko.js b/lang-ko.js
index 3241c7f..8cedf44 100755
--- a/lang-ko.js
+++ b/lang-ko.js
@@ -185,7 +185,7 @@ SnapTranslator.dict.ko = {
'translator_e-mail':
'janggoons@gmail.com', // optional
'last_changed':
- '2014-11-07', // this, too, will appear in the Translators tab
+ '2015-01-21', // this, too, will appear in the Translators tab
// GUI
// control bar:
diff --git a/locale.js b/locale.js
index b3c2ce5..5ec6013 100644
--- a/locale.js
+++ b/locale.js
@@ -6,7 +6,7 @@
written by Jens Mönig
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -42,7 +42,7 @@
/*global modules, contains*/
-modules.locale = '2014-December-15';
+modules.locale = '2015-January-21';
// Global stuff
@@ -157,11 +157,11 @@ SnapTranslator.dict.it = {
'language_name':
'Italiano',
'language_translator':
- 'Stefano Federici',
+ 'Stefano Federici, Alberto Firpo',
'translator_e-mail':
- 's_federici@yahoo.com',
+ 's_federici@yahoo.com, albertofirpo12@gmail.com',
'last_changed':
- '2013-04-08'
+ '2015-01-12'
};
SnapTranslator.dict.ja = {
@@ -195,9 +195,9 @@ SnapTranslator.dict.ko = {
'language_translator':
'Yunjae Jang',
'translator_e-mail':
- 'yunjae.jang@inc.korea.ac.kr',
+ 'janggoons@gmail.com',
'last_changed':
- '2012-11-18'
+ '2015-01-21'
};
SnapTranslator.dict.pt = {
@@ -375,9 +375,9 @@ SnapTranslator.dict.ca = {
'language_translator':
'Bernat Romagosa Carrasquer',
'translator_e-mail':
- 'tibabenfortlapalanca@gmail.com',
+ 'bromagosa@citilab.eu',
'last_changed':
- '2013-11-26'
+ '2015-01-21'
};
SnapTranslator.dict.fi = {
diff --git a/manifest.mf b/manifest.mf
index 353d47f..d040961 100644
--- a/manifest.mf
+++ b/manifest.mf
@@ -10,5 +10,4 @@ threads.js
widgets.js
store.js
xml.js
-scriptsPaneTexture.gif
-snap_logo_sm.gif
+snap_logo_sm.png
diff --git a/objects.js b/objects.js
index 95581c5..cf4d0ea 100644
--- a/objects.js
+++ b/objects.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.objects = '2014-December-17';
+modules.objects = '2015-January-21';
var SpriteMorph;
var StageMorph;
@@ -1406,6 +1406,7 @@ SpriteMorph.prototype.init = function (globals) {
this.anchor = null;
this.nestingScale = 1;
this.rotatesWithAnchor = true;
+ this.layers = null; // cache for dragging nested sprites, don't serialize
this.blocksCache = {}; // not to be serialized (!)
this.paletteCache = {}; // not to be serialized (!)
@@ -2799,7 +2800,9 @@ SpriteMorph.prototype.userMenu = function () {
menu.addItem("duplicate", 'duplicate');
menu.addItem("delete", 'remove');
menu.addItem("move", 'move');
- menu.addItem("edit", 'edit');
+ if (!this.isClone) {
+ menu.addItem("edit", 'edit');
+ }
menu.addLine();
if (this.anchor) {
menu.addItem(
@@ -2815,6 +2818,7 @@ SpriteMorph.prototype.userMenu = function () {
};
SpriteMorph.prototype.exportSprite = function () {
+ if (this.isCoone) {return; }
var ide = this.parentThatIsA(IDE_Morph);
if (ide) {
ide.exportSprite(this);
@@ -3308,13 +3312,11 @@ SpriteMorph.prototype.positionTalkBubble = function () {
bubble.changed();
};
-// dragging and dropping adjustments b/c of talk bubbles
+// dragging and dropping adjustments b/c of talk bubbles and parts
SpriteMorph.prototype.prepareToBeGrabbed = function (hand) {
- var bubble = this.talkBubble();
- if (!bubble) {return null; }
this.removeShadow();
- bubble.hide();
+ this.recordLayers();
if (!this.bounds.containsPoint(hand.position())) {
this.setCenter(hand.position());
}
@@ -3322,6 +3324,7 @@ SpriteMorph.prototype.prepareToBeGrabbed = function (hand) {
};
SpriteMorph.prototype.justDropped = function () {
+ this.restoreLayers();
this.positionTalkBubble();
};
@@ -3697,6 +3700,7 @@ SpriteMorph.prototype.mouseClickLeft = function () {
};
SpriteMorph.prototype.mouseDoubleClick = function () {
+ if (this.isClone) {return; }
this.edit();
};
@@ -4218,6 +4222,33 @@ SpriteMorph.prototype.allAnchors = function () {
return result;
};
+SpriteMorph.prototype.recordLayers = function () {
+ var stage = this.parentThatIsA(StageMorph);
+ if (!stage) {
+ this.layerCache = null;
+ return;
+ }
+ this.layers = this.allParts();
+ this.layers.forEach(function (part) {
+ var bubble = part.talkBubble();
+ if (bubble) {bubble.hide(); }
+ });
+ this.layers.sort(function (x, y) {
+ return stage.children.indexOf(x) < stage.children.indexOf(y) ?
+ -1 : 1;
+ });
+};
+
+SpriteMorph.prototype.restoreLayers = function () {
+ if (this.layers && this.layers.length > 1) {
+ this.layers.forEach(function (sprite) {
+ sprite.comeToFront();
+ sprite.positionTalkBubble();
+ });
+ }
+ this.layers = null;
+};
+
// SpriteMorph highlighting
SpriteMorph.prototype.addHighlight = function (oldHighlight) {
diff --git a/scriptsPaneTexture.gif b/scriptsPaneTexture.gif
deleted file mode 100755
index 846c77f..0000000
--- a/scriptsPaneTexture.gif
+++ /dev/null
Binary files differ
diff --git a/store.js b/store.js
index 0044cbe..7db27a6 100644
--- a/store.js
+++ b/store.js
@@ -7,7 +7,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -61,7 +61,7 @@ SyntaxElementMorph, Variable*/
// Global stuff ////////////////////////////////////////////////////////
-modules.store = '2014-December-17';
+modules.store = '2015-January-21';
// XML_Serializer ///////////////////////////////////////////////////////
@@ -306,16 +306,33 @@ XML_Serializer.prototype.mediaXML = function (name) {
return xml + '</media>';
};
-
// SnapSerializer loading:
-SnapSerializer.prototype.load = function (xmlString) {
+SnapSerializer.prototype.load = function (xmlString, ide) {
// public - answer a new Project represented by the given XML String
- return this.loadProjectModel(this.parse(xmlString));
+ return this.loadProjectModel(this.parse(xmlString), ide);
};
-SnapSerializer.prototype.loadProjectModel = function (xmlNode) {
+SnapSerializer.prototype.loadProjectModel = function (xmlNode, ide) {
// public - answer a new Project represented by the given XML top node
+ // show a warning if the origin apps differ
+
+ var appInfo = xmlNode.attributes.app,
+ app = appInfo ? appInfo.split(' ')[0] : null;
+
+ if (ide && app !== this.app.split(' ')[0]) {
+ ide.inform(
+ app + ' Project',
+ 'This project has been created by a different app:\n\n' +
+ app +
+ '\n\nand may be incompatible or fail to load here.'
+ );
+ }
+ return this.rawLoadProjectModel(xmlNode);
+};
+
+SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
+ // private
var myself = this,
project = {sprites: {}},
model,
@@ -844,7 +861,7 @@ SnapSerializer.prototype.loadScripts = function (scripts, model) {
// private
var myself = this,
scale = SyntaxElementMorph.prototype.scale;
- scripts.texture = 'scriptsPaneTexture.gif';
+ scripts.cachedTexture = IDE_Morph.prototype.scriptsPaneTexture;
model.children.forEach(function (child) {
var element;
if (child.tag === 'script') {
diff --git a/threads.js b/threads.js
index 9626b89..6f5f35c 100644
--- a/threads.js
+++ b/threads.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
// Global stuff ////////////////////////////////////////////////////////
-modules.threads = '2014-December-17';
+modules.threads = '2015-January-12';
var ThreadManager;
var Process;
@@ -168,9 +168,11 @@ ThreadManager.prototype.startProcess = function (
active.stop();
this.removeTerminatedProcesses();
}
- top.addHighlight();
newProc = new Process(block.topBlock(), callback);
newProc.exportResult = exportResult;
+ if (!newProc.homeContext.receiver.isClone) {
+ top.addHighlight();
+ }
this.processes.push(newProc);
return newProc;
};