diff --git a/docs/extensions.md b/docs/extensions.md index 21ae3ef23f3..ab9fd4dfd42 100644 --- a/docs/extensions.md +++ b/docs/extensions.md @@ -3,9 +3,6 @@ This document describes technical topics related to Scratch 3.0 extension development, including the Scratch 3.0 extension specification. -For documentation regarding other aspects of Scratch 3.0 extensions see [this Extensions page on the -wiki](https://github.com/LLK/docs/wiki/Extensions). - ## Types of Extensions There are four types of extensions that can define everything from the Scratch's core library (such as the "Looks" and @@ -13,8 +10,6 @@ There are four types of extensions that can define everything from the Scratch's **Scratch 3.0 does not yet support unofficial extensions.** -For more details, see [this Extensions page on the wiki](https://github.com/LLK/docs/wiki/Extensions). - | | Core | Team | Official | Unofficial | | ------------------------------ | ---- | ---- | -------- | ---------- | | Developed by Scratch Team | √ | √ | O | X | diff --git a/package.json b/package.json index c280d2133e7..c9c0c95efb1 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "babel-loader": "^8.0.4", "callsite": "^1.0.0", "clipcc-block": "3.0.0-7", + "callsite": "^1.0.0", "copy-webpack-plugin": "^4.5.4", "docdash": "^1.0.0", "eslint": "^5.3.0", diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 04827608139..e775bf025a9 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -392,6 +392,13 @@ class Runtime extends EventEmitter { * @type {function} */ this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager); + + /** + * A string representing the origin of the current project from outside of the + * Scratch community, such as CSFirst. + * @type {?string} + */ + this.origin = null; } /** diff --git a/src/import/load-costume.js b/src/import/load-costume.js index b93227792f0..d76e8b91276 100644 --- a/src/import/load-costume.js +++ b/src/import/load-costume.js @@ -311,7 +311,7 @@ const loadCostume = function (md5ext, costume, runtime, optVersion) { costume.dataFormat = ext; if (costume.asset) { - // Costume comes with asset. It could be coming from camera, image upload, drag and drop, or file + // Costume comes with asset. It could be coming from image upload, drag and drop, or file return loadCostumeFromAsset(costume, runtime, optVersion); } diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 36158812bcf..dda795e0bb7 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -561,6 +561,9 @@ const serialize = function (runtime, targetId) { const meta = Object.create(null); meta.semver = '3.0.0'; meta.vm = vmPackage.version; + if (runtime.origin) { + meta.origin = runtime.origin; + } // Attach full user agent string to metadata if available meta.agent = 'none'; @@ -1235,6 +1238,13 @@ const deserialize = function (json, runtime, zip, isSingleSprite) { extensionURLs: new Map() }; + // Store the origin field (e.g. project originated at CSFirst) so that we can save it again. + if (json.meta && json.meta.origin) { + runtime.origin = json.meta.origin; + } else { + runtime.origin = null; + } + // First keep track of the current target order in the json, // then sort by the layer order property before parsing the targets // so that their corresponding render drawables can be created in diff --git a/test/fixtures/origin-absent.sb3 b/test/fixtures/origin-absent.sb3 new file mode 100644 index 00000000000..c6c7755d895 Binary files /dev/null and b/test/fixtures/origin-absent.sb3 differ diff --git a/test/fixtures/origin.sb3 b/test/fixtures/origin.sb3 new file mode 100644 index 00000000000..b8356a1f0da Binary files /dev/null and b/test/fixtures/origin.sb3 differ diff --git a/test/unit/serialization_sb3.js b/test/unit/serialization_sb3.js index baeb235432f..efba61e92f1 100644 --- a/test/unit/serialization_sb3.js +++ b/test/unit/serialization_sb3.js @@ -11,6 +11,8 @@ const commentsSB3NoDupeIds = path.resolve(__dirname, '../fixtures/comments_no_du const variableReporterSB2ProjectPath = path.resolve(__dirname, '../fixtures/top-level-variable-reporter.sb2'); const topLevelReportersProjectPath = path.resolve(__dirname, '../fixtures/top-level-reporters.sb3'); const draggableSB3ProjectPath = path.resolve(__dirname, '../fixtures/draggable.sb3'); +const originSB3ProjectPath = path.resolve(__dirname, '../fixtures/origin.sb3'); +const originAbsentSB3ProjectPath = path.resolve(__dirname, '../fixtures/origin-absent.sb3'); const FakeRenderer = require('../fixtures/fake-renderer'); test('serialize', t => { @@ -324,3 +326,38 @@ test('(#1850) sprite draggability state read when loading SB3 file', t => { t.end(); }); }); + +test('load origin value from SB3 file json metadata', t => { + const vm = new VirtualMachine(); + vm.loadProject(readFileToBuffer(originSB3ProjectPath)) + .then(() => { + t.type(vm.runtime.origin, 'string'); + }) + .then(() => vm.loadProject(readFileToBuffer(originAbsentSB3ProjectPath))) + .then(() => { + // After loading a project with an origin, then loading one without an origin, + // origin value should no longer be set. + t.equal(vm.runtime.origin, null); + t.end(); + }); +}); + +test('serialize origin value if it is present', t => { + const vm = new VirtualMachine(); + vm.loadProject(readFileToBuffer(originSB3ProjectPath)) + .then(() => { + const result = sb3.serialize(vm.runtime); + t.type(result.meta.origin, 'string'); + t.end(); + }); +}); + +test('do not serialize origin value if it is not present', t => { + const vm = new VirtualMachine(); + vm.loadProject(readFileToBuffer(originAbsentSB3ProjectPath)) + .then(() => { + const result = sb3.serialize(vm.runtime); + t.equal(result.meta.origin, undefined); + t.end(); + }); +});