mirror of
https://github.com/google/blockly.git
synced 2026-01-08 01:20:12 +01:00
Blockly Factory: Confirm Changes with User, Reduce Alerts, Generate Starter Code (#606)
* Automatically generates flyout on switching to category * Fixed import blocks bugs * Made last set of UI changes and changes to starter code exporting * Combined if statements * Only add toolbox if not read only * Removed extra curly braces
This commit is contained in:
committed by
picklesrus
parent
7bbd125bd0
commit
6e493be3c4
@@ -158,9 +158,9 @@
|
||||
<div class="dropdown">
|
||||
<button id="button_export">Export</button>
|
||||
<div id="dropdownDiv_export" class="dropdown-content">
|
||||
<a id='dropdown_exportOptions'>Starter Code</a>
|
||||
<a id='dropdown_exportToolbox'>Toolbox</a>
|
||||
<a id='dropdown_exportPreload'>Workspace Blocks</a>
|
||||
<a id='dropdown_exportOptions'>Inject Options</a>
|
||||
<a id='dropdown_exportAll'>All</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -194,6 +194,7 @@
|
||||
<a id='dropdown_newCategory'>New Category</a>
|
||||
<a id='dropdown_loadCategory'>Standard Category</a>
|
||||
<a id='dropdown_separator'>Separator</a>
|
||||
<a id='dropdown_loadStandardToolbox'>Standard Toolbox</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -220,7 +221,7 @@
|
||||
<div id="preloadHelp">
|
||||
<p>Configure the options for your Blockly inject call.</p>
|
||||
<button id="button_optionsHelp">Help</button>
|
||||
<button class="small" id="button_standardOptions">Restore</button>
|
||||
<button class="small" id="button_standardOptions">Reset to Default</button>
|
||||
</div>
|
||||
<div id="workspace_options">
|
||||
<input type="checkbox" id="option_readOnly_checkbox" class="optionsInput">Read Only<br>
|
||||
|
||||
@@ -98,37 +98,8 @@ WorkspaceFactoryController.MODE_PRELOAD = 'preload';
|
||||
* before), and then creates a tab and switches to it.
|
||||
*/
|
||||
WorkspaceFactoryController.prototype.addCategory = function() {
|
||||
this.allowToTransferFlyoutBlocksToCategory();
|
||||
// Check if it's the first category added.
|
||||
var isFirstCategory = !this.model.hasElements();
|
||||
// Give the option to save blocks if their workspace is not empty and they
|
||||
// are creating their first category.
|
||||
if (isFirstCategory && this.toolboxWorkspace.getAllBlocks().length > 0) {
|
||||
var confirmCreate = confirm('Do you want to save your work in another '
|
||||
+ 'category? If you don\'t, the blocks in your workspace will be ' +
|
||||
'deleted.');
|
||||
|
||||
// Create a new category for current blocks.
|
||||
if (confirmCreate) {
|
||||
var name = prompt('Enter the name of the category for your ' +
|
||||
'current blocks: ');
|
||||
if (!name) { // Exit if cancelled.
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the new category.
|
||||
this.createCategory(name, true);
|
||||
// Set the new category as selected.
|
||||
var id = this.model.getCategoryIdByName(name);
|
||||
this.model.setSelectedById(id);
|
||||
this.view.setCategoryTabSelection(id, true);
|
||||
// Set default options if switching from single flyout to categories.
|
||||
this.view.setCategoryOptions(this.model.hasElements());
|
||||
this.generateNewOptions();
|
||||
// Update preview here in case exit early.
|
||||
this.updatePreview();
|
||||
}
|
||||
}
|
||||
// Transfers the user's blocks to a flyout if it's the first category created.
|
||||
this.transferFlyoutBlocksToCategory();
|
||||
|
||||
// After possibly creating a category, check again if it's the first category.
|
||||
var isFirstCategory = !this.model.hasElements();
|
||||
@@ -188,40 +159,28 @@ WorkspaceFactoryController.prototype.addClickToSwitch = function(tab, id) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows the user to transfer blocks in their flyout to a new category if
|
||||
* Transfers the blocks in the user's flyout to a new category if
|
||||
* the user is creating their first category and their workspace is not
|
||||
* empty. Should be called whenever it is possible to switch from single flyout
|
||||
* to categories (not including importing).
|
||||
*/
|
||||
WorkspaceFactoryController.prototype.allowToTransferFlyoutBlocksToCategory =
|
||||
WorkspaceFactoryController.prototype.transferFlyoutBlocksToCategory =
|
||||
function() {
|
||||
// Give the option to save blocks if their workspace is not empty and they
|
||||
// are creating their first category.
|
||||
// Saves the user's blocks from the flyout in a category if there is no
|
||||
// toolbox and the user has dragged in blocks.
|
||||
if (!this.model.hasElements() &&
|
||||
this.toolboxWorkspace.getAllBlocks().length > 0) {
|
||||
var confirmCreate = confirm('Do you want to save your work in another '
|
||||
+ 'category? If you don\'t, the blocks in your workspace will be ' +
|
||||
'deleted.');
|
||||
|
||||
// Create a new category for current blocks.
|
||||
if (confirmCreate) {
|
||||
var name = prompt('Enter the name of the category for your ' +
|
||||
'current blocks: ');
|
||||
if (!name) { // Exit if cancelled.
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the new category.
|
||||
this.createCategory(name, true);
|
||||
// Set the new category as selected.
|
||||
var id = this.model.getCategoryIdByName(name);
|
||||
this.model.setSelectedById(id);
|
||||
this.view.setCategoryTabSelection(id, true);
|
||||
// Allow user to use the default options for injecting with categories.
|
||||
this.allowToSetDefaultOptions();
|
||||
// Update preview here in case exit early.
|
||||
this.updatePreview();
|
||||
}
|
||||
// Create the new category.
|
||||
this.createCategory('Category 1', true);
|
||||
// Set the new category as selected.
|
||||
var id = this.model.getCategoryIdByName('Category 1');
|
||||
this.model.setSelectedById(id);
|
||||
this.view.setCategoryTabSelection(id, true);
|
||||
// Allow user to use the default options for injecting with categories.
|
||||
this.view.setCategoryOptions(this.model.hasElements());
|
||||
this.generateNewOptions();
|
||||
// Update preview here in case exit early.
|
||||
this.updatePreview();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -391,15 +350,15 @@ WorkspaceFactoryController.prototype.exportXmlFile = function(exportMode) {
|
||||
* Export the options object to be used for the Blockly inject call. Gets a
|
||||
* file name from the user and downloads the options object to that file.
|
||||
*/
|
||||
WorkspaceFactoryController.prototype.exportOptionsFile = function() {
|
||||
var fileName = prompt('File Name for options object for injecting: ');
|
||||
WorkspaceFactoryController.prototype.exportInjectFile = function() {
|
||||
var fileName = prompt('File Name for starter Blockly workspace code: ');
|
||||
if (!fileName) { // If cancelled.
|
||||
return;
|
||||
}
|
||||
// Generate new options to remove toolbox XML from options object (if
|
||||
// necessary).
|
||||
this.generateNewOptions();
|
||||
var printableOptions = this.generator.generateOptionsString()
|
||||
var printableOptions = this.generator.generateInjectString()
|
||||
var data = new Blob([printableOptions], {type: 'text/javascript'});
|
||||
this.view.createAndDownloadFile(fileName, data);
|
||||
};
|
||||
@@ -632,8 +591,9 @@ WorkspaceFactoryController.prototype.loadCategoryByName = function(name) {
|
||||
+ '. Rename your category and try again.');
|
||||
return;
|
||||
}
|
||||
// Allow user to transfer current flyout blocks to a category.
|
||||
this.allowToTransferFlyoutBlocksToCategory();
|
||||
// Transfers current flyout blocks to a category if it's the first category
|
||||
// created.
|
||||
this.transferFlyoutBlocksToCategory();
|
||||
|
||||
var isFirstCategory = !this.model.hasElements();
|
||||
// Copy the standard category in the model.
|
||||
@@ -665,6 +625,22 @@ WorkspaceFactoryController.prototype.loadCategoryByName = function(name) {
|
||||
this.updatePreview();
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the standard Blockly toolbox into the editing space. Should only
|
||||
* be called when the mode is set to toolbox.
|
||||
*/
|
||||
WorkspaceFactoryController.prototype.loadStandardToolbox = function() {
|
||||
this.loadCategoryByName('Logic');
|
||||
this.loadCategoryByName('Loops');
|
||||
this.loadCategoryByName('Math');
|
||||
this.loadCategoryByName('Text');
|
||||
this.loadCategoryByName('Lists');
|
||||
this.loadCategoryByName('Colour');
|
||||
this.addSeparator();
|
||||
this.loadCategoryByName('Variables');
|
||||
this.loadCategoryByName('Functions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the name of a category, determines if it's the name of a standard
|
||||
* category (case insensitive).
|
||||
@@ -688,9 +664,9 @@ WorkspaceFactoryController.prototype.isStandardCategoryName = function(name) {
|
||||
* the separator, and updates the preview.
|
||||
*/
|
||||
WorkspaceFactoryController.prototype.addSeparator = function() {
|
||||
// If adding the first element in the toolbox, allow the user to transfer
|
||||
// their flyout blocks to a category.
|
||||
this.allowToTransferFlyoutBlocksToCategory();
|
||||
// If adding the first element in the toolbox, transfers the user's blocks
|
||||
// in a flyout to a category.
|
||||
this.transferFlyoutBlocksToCategory();
|
||||
// Create the separator in the model.
|
||||
var separator = new ListElement(ListElement.TYPE_SEPARATOR);
|
||||
this.model.addElementToList(separator);
|
||||
@@ -734,12 +710,32 @@ WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
|
||||
try {
|
||||
var tree = Blockly.Xml.textToDom(reader.result);
|
||||
if (importMode == WorkspaceFactoryController.MODE_TOOLBOX) {
|
||||
// Switch mode and import toolbox XML.
|
||||
// Switch mode.
|
||||
controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX);
|
||||
|
||||
// Confirm that the user wants to override their current toolbox.
|
||||
var hasToolboxElements = controller.model.hasElements() ||
|
||||
controller.getAllBlocks().length > 0;
|
||||
if (hasToolboxElements &&
|
||||
!confirm('Are you sure you want to import? You will lose your '
|
||||
+ 'current toolbox. ')) {
|
||||
return;
|
||||
}
|
||||
// Import toolbox XML.
|
||||
controller.importToolboxFromTree_(tree);
|
||||
|
||||
} else if (importMode == WorkspaceFactoryController.MODE_PRELOAD) {
|
||||
// Switch mode and import pre-loaded workspace XML.
|
||||
// Switch mode.
|
||||
controller.setMode(WorkspaceFactoryController.MODE_PRELOAD);
|
||||
|
||||
// Confirm that the user wants to override their current blocks.
|
||||
if (controller.toolboxWorkspace.getAllBlocks().length > 0 &&
|
||||
!confirm('Are you sure you want to import? You will lose your '
|
||||
+ 'current workspace blocks. ')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Import pre-loaded workspace XML.
|
||||
controller.importPreloadFromTree_(tree);
|
||||
} else {
|
||||
// Throw error if invalid mode.
|
||||
@@ -883,6 +879,10 @@ WorkspaceFactoryController.prototype.importPreloadFromTree_ = function(tree) {
|
||||
* "Clear" button.
|
||||
*/
|
||||
WorkspaceFactoryController.prototype.clearAll = function() {
|
||||
if (!confirm('Are you sure you want to clear all of your work in Workspace' +
|
||||
' Factory?')) {
|
||||
return;
|
||||
}
|
||||
var hasCategories = this.model.hasElements();
|
||||
this.model.clearToolboxList();
|
||||
this.view.clearToolboxTabs();
|
||||
@@ -1196,8 +1196,17 @@ WorkspaceFactoryController.prototype.importBlocks =
|
||||
// Define blocks using block types from file.
|
||||
var blockTypes = FactoryUtils.defineAndGetBlockTypes(reader.result,
|
||||
format);
|
||||
var blocks = controller.generator.getDefinedBlocks(blockTypes);
|
||||
|
||||
// If an imported block type is already defined, check if the user wants
|
||||
// to override the current block definition.
|
||||
if (controller.model.hasDefinedBlockTypes(blockTypes) &&
|
||||
!confirm('An imported block uses the same name as a block '
|
||||
+ 'already in your toolbox. Are you sure you want to override the '
|
||||
+ 'currently defined block?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var blocks = controller.generator.getDefinedBlocks(blockTypes);
|
||||
// Generate category XML and append to toolbox.
|
||||
var categoryXml = FactoryUtils.generateCategoryXml(blocks, categoryName);
|
||||
// Get random color for category between 0 and 360. Gives each imported
|
||||
@@ -1207,9 +1216,10 @@ WorkspaceFactoryController.prototype.importBlocks =
|
||||
controller.toolbox.appendChild(categoryXml);
|
||||
controller.toolboxWorkspace.updateToolbox(controller.toolbox);
|
||||
// Update imported block types.
|
||||
this.model.addImportedBlocks(blockTypes);
|
||||
controller.model.addImportedBlockTypes(blockTypes);
|
||||
// Reload current category to possibly reflect any newly defined blocks.
|
||||
this.clearAndLoadXml_(Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
|
||||
controller.clearAndLoadXml_
|
||||
(Blockly.Xml.workspaceToDom(controller.toolboxWorkspace));
|
||||
} catch (e) {
|
||||
alert('Cannot read blocks from file.');
|
||||
window.console.log(e);
|
||||
|
||||
@@ -130,18 +130,18 @@ WorkspaceFactoryGenerator.prototype.generateWorkspaceXml = function() {
|
||||
|
||||
// Generate XML and set attributes.
|
||||
var generatedXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace);
|
||||
generatedXml.setAttribute('id', 'preload_blocks');
|
||||
generatedXml.setAttribute('id', 'workspaceBlocks');
|
||||
generatedXml.setAttribute('style', 'display:none');
|
||||
return generatedXml;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a string representation of the options object for injecting the
|
||||
* workspace.
|
||||
* workspace and starter code.
|
||||
*
|
||||
* @return {!string} String representation of options object.
|
||||
* @return {!string} String representation of starter code for injecting.
|
||||
*/
|
||||
WorkspaceFactoryGenerator.prototype.generateOptionsString = function() {
|
||||
WorkspaceFactoryGenerator.prototype.generateInjectString = function() {
|
||||
|
||||
var addAttributes = function(obj, tabChar) {
|
||||
if (!obj) {
|
||||
@@ -157,14 +157,28 @@ WorkspaceFactoryGenerator.prototype.generateOptionsString = function() {
|
||||
} else {
|
||||
var temp = tabChar + key + ' : ' + obj[key] + ', \n';
|
||||
}
|
||||
str = str.concat(temp);
|
||||
str += temp;
|
||||
}
|
||||
var lastCommaIndex = str.lastIndexOf(',');
|
||||
str = str.slice(0, lastCommaIndex) + '\n';
|
||||
return str;
|
||||
};
|
||||
|
||||
return 'var options = { \n' + addAttributes(this.model.options, '\t') + '};';
|
||||
var attributes = addAttributes(this.model.options, '\t');
|
||||
if (!this.model.options['readOnly']) {
|
||||
attributes = '\ttoolbox : toolbox, /* TODO: Change toolbox XML ID if ' +
|
||||
'necessary. Can export toolbox XML from Workspace Factory. */\n' +
|
||||
attributes;
|
||||
}
|
||||
var finalStr = 'var options = { \n' + attributes + '};';
|
||||
finalStr += '\n\n/* Inject your workspace */ \nvar workspace = Blockly.' +
|
||||
'inject(/* TODO: Add ID of div to inject Blockly into */, options);';
|
||||
finalStr += '\n\n/* Load Workspace Blocks from XML to workspace. ' +
|
||||
'Remove if no blocks to load */' +
|
||||
'\nBlockly.Xml.domToWorkspace(workspace, workspaceBlocks/*' +
|
||||
' TODO: Change workspace blocks XML ID if necessary. Can export' +
|
||||
' workspace blocks XML from Workspace Factory. */);';
|
||||
return finalStr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -193,6 +193,13 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
|
||||
document.getElementById('dropdownDiv_add').classList.remove("show");
|
||||
});
|
||||
|
||||
document.getElementById('dropdown_loadStandardToolbox').addEventListener
|
||||
('click',
|
||||
function() {
|
||||
controller.loadStandardToolbox();
|
||||
document.getElementById('dropdownDiv_add').classList.remove("show");
|
||||
});
|
||||
|
||||
document.getElementById('button_remove').addEventListener
|
||||
('click',
|
||||
function() {
|
||||
@@ -216,16 +223,16 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
|
||||
document.getElementById('dropdown_exportOptions').addEventListener
|
||||
('click',
|
||||
function() {
|
||||
controller.exportOptionsFile();
|
||||
controller.exportInjectFile();
|
||||
document.getElementById('dropdownDiv_export').classList.remove("show");
|
||||
});
|
||||
|
||||
document.getElementById('dropdown_exportAll').addEventListener
|
||||
('click',
|
||||
function() {
|
||||
controller.exportInjectFile();
|
||||
controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX);
|
||||
controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD);
|
||||
controller.exportOptionsFile();
|
||||
document.getElementById('dropdownDiv_export').classList.remove("show");
|
||||
});
|
||||
|
||||
|
||||
@@ -495,6 +495,22 @@ WorkspaceFactoryModel.prototype.isDefinedBlockType = function(blockType) {
|
||||
var isLibBlock = this.libBlockTypes.indexOf(blockType) != -1;
|
||||
var isImportedBlock = this.importedBlockTypes.indexOf(blockType) != -1;
|
||||
return (isStandardBlock || isLibBlock || isImportedBlock);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if any of the block types are already defined.
|
||||
*
|
||||
* @param {!Array<!string>} blockTypes Array of block types.
|
||||
* @return {boolean} True if a block type in the array is already defined,
|
||||
* false if none of the blocks are already defined.
|
||||
*/
|
||||
WorkspaceFactoryModel.prototype.hasDefinedBlockTypes = function(blockTypes) {
|
||||
for (var i = 0, blockType; blockType = blockTypes[i]; i++) {
|
||||
if (this.isDefinedBlockType(blockType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user