diff --git a/demos/blockfactory/analytics.js b/demos/blockfactory/analytics.js
new file mode 100644
index 000000000..6473f8858
--- /dev/null
+++ b/demos/blockfactory/analytics.js
@@ -0,0 +1,210 @@
+/**
+ * @license
+ * Blockly Demos: Block Factory
+ *
+ * Copyright 2017 Google Inc.
+ * https://developers.google.com/blockly/
+ *
+ * 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 Stubbed interface functions for analytics integration.
+ */
+
+goog.provide('BlocklyDevTools.Analytics');
+
+/**
+ * Whether these stub methods should log analytics calls to the console.
+ * @private
+ * @const
+ */
+BlocklyDevTools.Analytics.LOG_TO_CONSOLE_ = false;
+
+/**
+ * An import/export type id for a library of BlockFactory's original block
+ * save files (each a serialized workspace of block definition blocks).
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.BLOCK_FACTORY_LIBRARY = "Block Factory library";
+/**
+ * An import/export type id for a standard Blockly library of block
+ * definitions.
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.BLOCK_DEFINITIONS = "Block definitions";
+/**
+ * An import/export type id for a code generation function, or a
+ * boilerplate stub of the same.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.GENERATOR = "Generator";
+/**
+ * An import/export type id for a Blockly Toolbox.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.TOOLBOX = "Toolbox";
+/**
+ * An import/export type id for the serialized contents of a workspace.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.WORKSPACE_CONTENTS = "Workspace contents";
+
+/**
+ * Format id for imported/exported JavaScript resources.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.FORMAT_JS = "JavaScript";
+/**
+ * Format id for imported/exported JSON resources.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.FORMAT_JSON = "JSON";
+/**
+ * Format id for imported/exported XML resources.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.FORMAT_XML = "XML";
+
+/**
+ * Platform id for resources exported for use in Android projects.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.PLATFORM_ANDROID = "Android";
+/**
+ * Platform id for resources exported for use in iOS projects.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.PLATFORM_IOS = "iOS";
+/**
+ * Platform id for resources exported for use in web projects.
+ *
+ * @package
+ * @const
+ */
+BlocklyDevTools.Analytics.PLATFORM_WEB = "web";
+
+/**
+ * Initializes the analytics framework, including noting that the page/app was
+ * opened.
+ * @package
+ */
+BlocklyDevTools.Analytics.init = function() {
+ // stub
+ this.LOG_TO_CONSOLE_ && console.log('Analytics.init');
+};
+
+/**
+ * Event noting the user navigated to a specific view.
+ *
+ * @package
+ * @param viewId {string} An identifier for the view state.
+ */
+BlocklyDevTools.Analytics.onNavigateTo = function(viewId) {
+ // stub
+ this.LOG_TO_CONSOLE_ &&
+ console.log('Analytics.onNavigateTo(' + viewId + ')');
+};
+
+/**
+ * Event noting a project resource was saved. In the web Block Factory, this
+ * means saved to localStorage.
+ *
+ * @package
+ * @param typeId {string} An identifying string for the saved type.
+ */
+BlocklyDevTools.Analytics.onSave = function(typeId) {
+ // stub
+ this.LOG_TO_CONSOLE_ && console.log('Analytics.onSave(' + typeId + ')');
+};
+
+/**
+ * Event noting the user attempted to import a resource file.
+ *
+ * @package
+ * @param typeId {string} An identifying string for the imported type.
+ * @param optMetadata {Object} Metadata about the import, such as format and
+ * platform.
+ */
+BlocklyDevTools.Analytics.onImport = function(typeId, optMetadata) {
+ // stub
+ this.LOG_TO_CONSOLE_ && console.log('Analytics.onImport(' + typeId +
+ (optMetadata ? '): ' + JSON.stringify(optMetadata) : ')'));
+};
+
+/**
+ * Event noting a project resource was saved. In the web Block Factory, this
+ * means downloaded to the user's system.
+ *
+ * @package
+ * @param typeId {string} An identifying string for the exported object type.
+ * @param optMetadata {Object} Metadata about the import, such as format and
+ * platform.
+ */
+BlocklyDevTools.Analytics.onExport = function(typeId, optMetadata) {
+ // stub
+ this.LOG_TO_CONSOLE_ && console.log('Analytics.onExport(' + typeId +
+ (optMetadata ? '): ' + JSON.stringify(optMetadata) : ')'));
+};
+
+/**
+ * Event noting the system encountered an error. It should attempt to send
+ * immediately.
+ *
+ * @package
+ * @param e {!Object} A value representing or describing the error.
+ */
+BlocklyDevTools.Analytics.onError = function(e) {
+ // stub
+ this.LOG_TO_CONSOLE_ &&
+ console.log('Analytics.onError("' + e.toString() + '")');
+};
+
+/**
+ * Event noting the user was notified with a warning.
+ *
+ * @package
+ * @param msg {string} The warning message, or a description thereof.
+ */
+BlocklyDevTools.Analytics.onWarning = function(msg) {
+ // stub
+ this.LOG_TO_CONSOLE_ && console.log('Analytics.onWarning("' + msg + '")');
+};
+
+/**
+ * Request the analytics framework to send any queued events to the server.
+ * @package
+ */
+BlocklyDevTools.Analytics.sendQueued = function() {
+ // stub
+ this.LOG_TO_CONSOLE_ && console.log('Analytics.sendQueued');
+};
+
diff --git a/demos/blockfactory/app_controller.js b/demos/blockfactory/app_controller.js
index ac69780b2..e3fc1bd23 100644
--- a/demos/blockfactory/app_controller.js
+++ b/demos/blockfactory/app_controller.js
@@ -28,6 +28,7 @@
goog.provide('AppController');
goog.require('BlockFactory');
+goog.require('BlocklyDevTools.Analytics');
goog.require('FactoryUtils');
goog.require('BlockLibraryController');
goog.require('BlockExporterController');
@@ -85,6 +86,10 @@ AppController.prototype.importBlockLibraryFromFile = function() {
var files = document.getElementById('files');
// If the file list is empty, the user likely canceled in the dialog.
if (files.files.length > 0) {
+ BlocklyDevTools.Analytics.onImport(
+ BlocklyDevTools.Analytics.BLOCK_FACTORY_LIBRARY,
+ { format: BlocklyDevTools.Analytics.FORMAT_XML });
+
// The input tag doesn't have the "multiple" attribute
// so the user can only choose 1 file.
var file = files.files[0];
@@ -137,9 +142,14 @@ AppController.prototype.exportBlockLibraryToFile = function() {
// Download file if all necessary parameters are provided.
if (filename) {
FactoryUtils.createAndDownloadFile(blockLibText, filename, 'xml');
+ BlocklyDevTools.Analytics.onExport(
+ BlocklyDevTools.Analytics.BLOCK_FACTORY_LIBRARY,
+ { format: BlocklyDevTools.Analytics.FORMAT_XML });
} else {
- alert('Could not export Block Library without file name under which to ' +
- 'save library.');
+ var msg = 'Could not export Block Library without file name under which ' +
+ 'to save library.';
+ BlocklyDevTools.Analytics.onWarning(msg);
+ alert(msg);
}
};
@@ -201,7 +211,7 @@ AppController.prototype.formatBlockLibraryForImport_ = function(xmlText) {
var blockType = this.getBlockTypeFromXml_(xmlText).toLowerCase();
// Some names are invalid so fix them up.
blockType = FactoryUtils.cleanBlockType(blockType);
-
+
blockXmlTextMap[blockType] = xmlText;
}
@@ -285,13 +295,17 @@ AppController.prototype.onTab = function() {
var hasUnsavedChanges =
!FactoryUtils.savedBlockChanges(this.blockLibraryController);
- if (hasUnsavedChanges &&
- !confirm('You have unsaved changes in Block Factory.')) {
- // If the user doesn't want to switch tabs with unsaved changes,
- // stay on Block Factory Tab.
- this.setSelected_(AppController.BLOCK_FACTORY);
- this.lastSelectedTab = AppController.BLOCK_FACTORY;
- return;
+ if (hasUnsavedChanges) {
+ var msg = 'You have unsaved changes in Block Factory.';
+ var continueAnyway = confirm(msg);
+ BlocklyDevTools.Analytics.onWarning(msg);
+ if (!continueAnyway) {
+ // If the user doesn't want to switch tabs with unsaved changes,
+ // stay on Block Factory Tab.
+ this.setSelected_(AppController.BLOCK_FACTORY);
+ this.lastSelectedTab = AppController.BLOCK_FACTORY;
+ return;
+ }
}
}
@@ -304,6 +318,8 @@ AppController.prototype.onTab = function() {
this.styleTabs_();
if (this.selectedTab == AppController.EXPORTER) {
+ BlocklyDevTools.Analytics.onNavigateTo('Exporter');
+
// Hide other tabs.
FactoryUtils.hide('workspaceFactoryContent');
FactoryUtils.hide('blockFactoryContent');
@@ -325,6 +341,8 @@ AppController.prototype.onTab = function() {
this.exporter.updatePreview();
} else if (this.selectedTab == AppController.BLOCK_FACTORY) {
+ BlocklyDevTools.Analytics.onNavigateTo('BlockFactory');
+
// Hide other tabs.
FactoryUtils.hide('blockLibraryExporter');
FactoryUtils.hide('workspaceFactoryContent');
@@ -332,6 +350,9 @@ AppController.prototype.onTab = function() {
FactoryUtils.show('blockFactoryContent');
} else if (this.selectedTab == AppController.WORKSPACE_FACTORY) {
+ // TODO: differentiate Workspace and Toolbox editor, based on the other tab state.
+ BlocklyDevTools.Analytics.onNavigateTo('WorkspaceFactory');
+
// Hide other tabs.
FactoryUtils.hide('blockLibraryExporter');
FactoryUtils.hide('blockFactoryContent');
@@ -624,12 +645,14 @@ AppController.prototype.onresize = function(event) {
* @param {!Event} e beforeunload event.
*/
AppController.prototype.confirmLeavePage = function(e) {
+ BlocklyDevTools.Analytics.sendQueued();
if ((!BlockFactory.isStarterBlock() &&
!FactoryUtils.savedBlockChanges(blocklyFactory.blockLibraryController)) ||
blocklyFactory.workspaceFactoryController.hasUnsavedChanges()) {
var confirmationMessage = 'You will lose any unsaved changes. ' +
'Are you sure you want to exit this page?';
+ BlocklyDevTools.Analytics.onWarning(confirmationMessage);
e.returnValue = confirmationMessage;
return confirmationMessage;
}
diff --git a/demos/blockfactory/block_exporter_controller.js b/demos/blockfactory/block_exporter_controller.js
index b237f48a7..705de2667 100644
--- a/demos/blockfactory/block_exporter_controller.js
+++ b/demos/blockfactory/block_exporter_controller.js
@@ -31,6 +31,7 @@
goog.provide('BlockExporterController');
+goog.require('BlocklyDevTools.Analytics');
goog.require('FactoryUtils');
goog.require('StandardCategories');
goog.require('BlockExporterView');
@@ -103,7 +104,9 @@ BlockExporterController.prototype.export = function() {
// User wants to export selected blocks' definitions.
if (!blockDef_filename) {
// User needs to enter filename.
- alert('Please enter a filename for your block definition(s) download.');
+ var msg = 'Please enter a filename for your block definition(s) download.';
+ BlocklyDevTools.Analytics.onWarning(msg);
+ alert(msg);
} else {
// Get block definition code in the selected format for the blocks.
var blockDefs = this.tools.getBlockDefinitions(blockXmlMap,
@@ -111,6 +114,13 @@ BlockExporterController.prototype.export = function() {
// Download the file, using .js file ending for JSON or Javascript.
FactoryUtils.createAndDownloadFile(
blockDefs, blockDef_filename, 'javascript');
+ BlocklyDevTools.Analytics.onExport(
+ BlocklyDevTools.Analytics.BLOCK_DEFINITIONS,
+ {
+ format: (definitionFormat == 'JSON' ?
+ BlocklyDevTools.Analytics.FORMAT_JSON :
+ BlocklyDevTools.Analytics.FORMAT_JS)
+ });
}
}
@@ -118,7 +128,9 @@ BlockExporterController.prototype.export = function() {
// User wants to export selected blocks' generator stubs.
if (!generatorStub_filename) {
// User needs to enter filename.
- alert('Please enter a filename for your generator stub(s) download.');
+ var msg = 'Please enter a filename for your generator stub(s) download.';
+ BlocklyDevTools.Analytics.onWarning(msg);
+ alert(msg);
} else {
// Get generator stub code in the selected language for the blocks.
var genStubs = this.tools.getGeneratorCode(blockXmlMap,
@@ -128,6 +140,10 @@ BlockExporterController.prototype.export = function() {
// Download the file.
FactoryUtils.createAndDownloadFile(
genStubs, generatorStub_filename, fileType);
+ BlocklyDevTools.Analytics.onExport(
+ BlocklyDevTools.Analytics.GENERATOR,
+ (fileType == 'javascript' ?
+ { format: BlocklyDevTools.Analytics.FORMAT_JS } : undefined));
}
}
diff --git a/demos/blockfactory/block_library_controller.js b/demos/blockfactory/block_library_controller.js
index 1066ab127..b0e540489 100644
--- a/demos/blockfactory/block_library_controller.js
+++ b/demos/blockfactory/block_library_controller.js
@@ -34,6 +34,7 @@
goog.provide('BlockLibraryController');
+goog.require('BlocklyDevTools.Analytics');
goog.require('BlockLibraryStorage');
goog.require('BlockLibraryView');
goog.require('BlockFactory');
@@ -110,8 +111,9 @@ BlockLibraryController.prototype.getSelectedBlockType = function() {
* updating the dropdown and displaying the starter block (factory_base).
*/
BlockLibraryController.prototype.clearBlockLibrary = function() {
- var check = confirm('Delete all blocks from library?');
- if (check) {
+ var msg = 'Delete all blocks from library?';
+ BlocklyDevTools.Analytics.onWarning(msg);
+ if (confirm(msg)) {
// Clear Block Library Storage.
this.storage.clear();
this.storage.saveToLocalStorage();
@@ -133,9 +135,11 @@ BlockLibraryController.prototype.saveToBlockLibrary = function() {
// If user has not changed the name of the starter block.
if (blockType == 'block_type') {
// Do not save block if it has the default type, 'block_type'.
- alert('You cannot save a block under the name "block_type". Try changing ' +
- 'the name before saving. Then, click on the "Block Library" button ' +
- 'to view your saved blocks.');
+ var msg = 'You cannot save a block under the name "block_type". Try ' +
+ 'changing the name before saving. Then, click on the "Block Library"' +
+ ' button to view your saved blocks.';
+ alert(msg);
+ BlocklyDevTools.Analytics.onWarning(msg);
return;
}
@@ -159,6 +163,7 @@ BlockLibraryController.prototype.saveToBlockLibrary = function() {
// Add select handler to the new option.
this.addOptionSelectHandler(blockType);
+ BlocklyDevTools.Analytics.onSave('Block');
};
/**
diff --git a/demos/blockfactory/index.html b/demos/blockfactory/index.html
index 2b49f7ef2..9190bd132 100644
--- a/demos/blockfactory/index.html
+++ b/demos/blockfactory/index.html
@@ -9,6 +9,7 @@
+
@@ -32,6 +33,8 @@