Blockly Factory: Workspace Factory Options (#600)

* Some options stuff changed

* Finished changes to options, including moving readonly to toop, fixing zoom errors, indenting suboptions, generating an options string, getting category options automatically, and making max blocks clearer, and having number inputs

* Added null check and nit line length
This commit is contained in:
Emma Dauterman
2016-08-26 12:03:15 -07:00
committed by picklesrus
parent b7940fd156
commit 9bb02abb2c
7 changed files with 187 additions and 94 deletions

View File

@@ -478,6 +478,10 @@ td {
z-index: -1; /* Start behind workspace */
}
#grid_options, #zoom_options, #maxBlockNumber_option {
padding-left: 15px;
}
/* Rules for Closure popup color picker */
.goog-palette {
outline: none;

View File

@@ -217,36 +217,42 @@
<aside id='preload_div' style='display:none'>
<div id="preloadHelp">
<p>Configure the <a href="https://developers.google.com/blockly/guides/get-started/web">options</a> for your Blockly inject call.</p>
<button class="small" id="button_standardOptions">Reset Options</button>
<p>Configure the options for your Blockly inject call.</p>
<button id="button_optionsHelp">Help</button>
<button class="small" id="button_standardOptions">Restore</button>
</div>
<div id="workspace_options">
<input type="checkbox" id="option_readOnly_checkbox" class="optionsInput">Read Only<br>
<input type="checkbox" id="option_collapse_checkbox" class="optionsInput">Collapsible Blocks<br>
<input type="checkbox" id="option_comments_checkbox" class="optionsInput">Comments for Blocks<br>
<input type="checkbox" id="option_css_checkbox" class="optionsInput">Use Blockly CSS<br>
<input type="checkbox" id="option_disable_checkbox" class="optionsInput">Disabled Blocks<br>
<input type="checkbox" id="option_grid_checkbox" class="optionsInput">Use Grid<br>
<div id="grid_options" name="grid" style="display:none">
Spacing <input type="text" id="gridOption_spacing_text" class="optionsInput"value="0"><br>
Length <input type="text" id="gridOption_length_text" class="optionsInput" value="1"><br>
Spacing <input type="number" id="gridOption_spacing_number" class="optionsInput" value="0"><br>
Length <input type="number" id="gridOption_length_number" class="optionsInput" value="1"><br>
Color <input type="text" id="gridOption_colour_text" class="optionsInput" value="#888"><br>
<input type="checkbox" id="gridOption_snap_checkbox" class="optionsInput" value="grid_snap_checkbox">Snap<br>
</div>
Max Blocks <input type="text" id="option_maxBlocks_text" class="optionsInput" value="Infinity"><br>
<input type="checkbox" id="option_infiniteBlocks_checkbox" class="optionsInput" value="checked">Infinite Blocks<br>
<div id="maxBlockNumber_option" style="display:none">
Max Blocks <input type="number" id="option_maxBlocks_number" class="optionsInput" value=100><br>
</div>
Path to Blockly Media <input type="text" id="option_media_text" class="optionsInput"><br>
<input type="checkbox" id="option_readOnly_checkbox" class="optionsInput">Read Only<br>
<input type="checkbox" id="option_rtl_checkbox" class="optionsInput">Layout with RTL<br>
<input type="checkbox" id="option_scrollbars_checkbox" class="optionsInput">Scrollbars<br>
<input type="checkbox" id="option_sounds_checkbox" class="optionsInput">Sounds<br>
<input type="checkbox" id="option_trashcan_checkbox" class="optionsInput">Trashcan<br>
<div id="trashcan_option">
<input type="checkbox" id="option_trashcan_checkbox" class="optionsInput">Trashcan<br>
</div>
<input type="checkbox" id="option_zoom_checkbox" class="optionsInput">Zoom<br>
<div id="zoom_options" name="zoom" style="display:none">
<input type="checkbox" id="zoomOption_controls_checkbox" class="optionsInput">Zoom Controls<br>
<input type="checkbox" id="zoomOption_wheel_checkbox" class="optionsInput">Zoom Wheel<br>
Start Scale <input type="text" id="zoomOption_startScale_text" class="optionsInput" name="startScale" value="1.0"><br>
Max Scale <input type="text" id="zoomOption_maxScale_text" class="optionsInput" value="3"><br>
Min Scale <input type="text" id="zoomOption_minScale_text" class="optionsInput" value="0.3"><br>
Scale Speed <input type="text" id="zoomOption_scaleSpeed_text" class="optionsInput" value="1.2"><br>
Start Scale <input type="number" id="zoomOption_startScale_number" class="optionsInput" name="startScale" value="1.0"><br>
Max Scale <input type="number" id="zoomOption_maxScale_number" class="optionsInput" value="3"><br>
Min Scale <input type="number" id="zoomOption_minScale_number" class="optionsInput" value="0.3"><br>
Scale Speed <input type="number" id="zoomOption_scaleSpeed_number" class="optionsInput" value="1.2"><br>
</div>
</div>
</aside>

View File

@@ -57,7 +57,7 @@ WorkspaceFactoryController = function(toolboxName, toolboxDiv, previewDiv) {
colour: '#ccc',
snap: true},
media: '../../media/',
toolbox: this.toolbox,
toolbox: this.toolbox
});
// Workspace for user to preview their changes.
@@ -99,6 +99,36 @@ WorkspaceFactoryController.MODE_PRELOAD = 'preload';
*/
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();
}
}
// After possibly creating a category, check again if it's the first category.
var isFirstCategory = !this.model.hasElements();
@@ -112,10 +142,11 @@ WorkspaceFactoryController.prototype.addCategory = function() {
// Switch to category.
this.switchElement(this.model.getCategoryIdByName(name));
// Allow the user to use the default options for injecting the workspace
// Sets the default options for injecting the workspace
// when there are categories if adding the first category.
if (isFirstCategory) {
this.allowToSetDefaultOptions();
this.view.setCategoryOptions(this.model.hasElements());
this.generateNewOptions();
}
// Update preview.
this.updatePreview();
@@ -238,9 +269,6 @@ WorkspaceFactoryController.prototype.removeElement = function() {
this.toolboxWorkspace.clear();
this.toolboxWorkspace.clearUndo();
this.model.createDefaultSelectedIfEmpty();
// Allow the user to use the default options for injecting the workspace
// when there are no categories.
this.allowToSetDefaultOptions();
}
// Update preview.
this.updatePreview();
@@ -371,9 +399,8 @@ WorkspaceFactoryController.prototype.exportOptionsFile = function() {
// Generate new options to remove toolbox XML from options object (if
// necessary).
this.generateNewOptions();
// TODO(evd2014): Use Regex to prettify JSON generated.
var data = new Blob([JSON.stringify(this.model.options)],
{type: 'text/javascript'});
var printableOptions = this.generator.generateOptionsString()
var data = new Blob([printableOptions], {type: 'text/javascript'});
this.view.createAndDownloadFile(fileName, data);
};
@@ -465,8 +492,9 @@ WorkspaceFactoryController.prototype.saveStateFromWorkspace = function() {
*/
WorkspaceFactoryController.prototype.reinjectPreview = function(tree) {
this.previewWorkspace.dispose();
this.model.setOptionsAttribute('toolbox', Blockly.Xml.domToPrettyText(tree));
this.previewWorkspace = Blockly.inject('preview_blocks', this.model.options);
var injectOptions = this.readOptions_();
injectOptions['toolbox'] = Blockly.Xml.domToPrettyText(tree);
this.previewWorkspace = Blockly.inject('preview_blocks', injectOptions);
Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(),
this.previewWorkspace);
};
@@ -630,7 +658,8 @@ WorkspaceFactoryController.prototype.loadCategoryByName = function(name) {
if (isFirstCategory) {
// Allow the user to use the default options for injecting the workspace
// when there are categories.
this.allowToSetDefaultOptions();
this.view.setCategoryOptions(this.model.hasElements());
this.generateNewOptions();
}
// Update preview.
this.updatePreview();
@@ -795,9 +824,11 @@ WorkspaceFactoryController.prototype.importToolboxFromTree_ = function(tree) {
(this.model.getSelectedId()), this.model.getSelected());
this.saveStateFromWorkspace();
// Allow the user to set default configuration options for a single flyout
// or multiple categories.
this.allowToSetDefaultOptions();
// Set default configuration options for a single flyout or multiple
// categories.
this.view.setCategoryOptions(this.model.hasElements());
this.generateNewOptions();
this.updatePreview();
};
@@ -861,9 +892,8 @@ WorkspaceFactoryController.prototype.clearAll = function() {
this.toolboxWorkspace.clear();
this.toolboxWorkspace.clearUndo();
this.saveStateFromWorkspace();
if (hasCategories) {
this.allowToSetDefaultOptions();
}
this.view.setCategoryOptions(this.model.hasElements());
this.generateNewOptions();
this.updatePreview();
};
@@ -1047,35 +1077,27 @@ WorkspaceFactoryController.prototype.setStandardOptionsAndUpdate = function() {
this.generateNewOptions();
};
/**
* Asks the user if they want to use default configuration options specific
* to categories or a single flyout of blocks. If desired, makes the necessary
* changes to the options object depending on if there are categories and then
* updates the preview workspace. Only updates category/flyout specific
* options, not the base default options that are set regardless of if
* categories or a single flyout are used.
*/
WorkspaceFactoryController.prototype.allowToSetDefaultOptions = function() {
if (!this.model.hasElements() && !confirm('Do you want to use the default ' +
'workspace configuration options for injecting a workspace without ' +
'categories?')) {
return;
} else if (this.model.hasElements() && !confirm('Do you want to use the ' +
'default workspace configuration options for injecting a workspace ' +
'with categories?')) {
return;
}
this.view.setCategoryOptions(this.model.hasElements());
this.generateNewOptions();
};
/**
* Generates a new options object for injecting a Blockly workspace based
* on user input. Should be called every time a change has been made to
* an input field. Updates the model and reinjects the preview workspace.
*/
WorkspaceFactoryController.prototype.generateNewOptions = function() {
var optionsObj = new Object(null);
this.model.setOptions(this.readOptions_());
this.reinjectPreview(Blockly.Options.parseToolboxTree
(this.generator.generateToolboxXml()));
};
/**
* Generates a new options object for injecting a Blockly workspace based on
* user input.
* @private
*
* @return {!Object} Blockly injection options object.
*/
WorkspaceFactoryController.prototype.readOptions_ = function() {
var optionsObj = Object.create(null);
// Add all standard options to the options object.
// Use parse int to get numbers from value inputs.
@@ -1086,8 +1108,14 @@ WorkspaceFactoryController.prototype.generateNewOptions = function() {
optionsObj['css'] = document.getElementById('option_css_checkbox').checked;
optionsObj['disable'] =
document.getElementById('option_disable_checkbox').checked;
optionsObj['maxBlocks'] =
parseInt(document.getElementById('option_maxBlocks_text').value);
if (document.getElementById('option_infiniteBlocks_checkbox').checked) {
optionsObj['maxBlocks'] = Infinity;
} else {
var maxBlocksValue =
document.getElementById('option_maxBlocks_number').value;
optionsObj['maxBlocks'] = typeof maxBlocksValue == 'string' ?
parseInt(maxBlocksValue) : maxBlocksValue;
}
optionsObj['media'] = document.getElementById('option_media_text').value;
optionsObj['readOnly'] =
document.getElementById('option_readOnly_checkbox').checked;
@@ -1096,16 +1124,21 @@ WorkspaceFactoryController.prototype.generateNewOptions = function() {
document.getElementById('option_scrollbars_checkbox').checked;
optionsObj['sounds'] =
document.getElementById('option_sounds_checkbox').checked;
optionsObj['trashcan'] =
document.getElementById('option_trashcan_checkbox').checked;
if (!optionsObj['readOnly']) {
optionsObj['trashcan'] =
document.getElementById('option_trashcan_checkbox').checked;
}
// If using a grid, add all grid options.
if (document.getElementById('option_grid_checkbox').checked) {
var grid = new Object(null);
grid['spacing'] =
parseInt(document.getElementById('gridOption_spacing_text').value);
grid['length'] =
parseInt(document.getElementById('gridOption_length_text').value);
var grid = Object.create(null);
var spacingValue =
document.getElementById('gridOption_spacing_number').value;
grid['spacing'] = typeof spacingValue == 'string' ?
parseInt(spacingValue) : spacingValue;
var lengthValue = document.getElementById('gridOption_length_number').value;
grid['length'] = typeof lengthValue == 'string' ?
parseInt(lengthValue) : lengthValue;
grid['colour'] = document.getElementById('gridOption_colour_text').value;
grid['snap'] = document.getElementById('gridOption_snap_checkbox').checked;
optionsObj['grid'] = grid;
@@ -1113,26 +1146,31 @@ WorkspaceFactoryController.prototype.generateNewOptions = function() {
// If using zoom, add all zoom options.
if (document.getElementById('option_zoom_checkbox').checked) {
var zoom = new Object(null);
var zoom = Object.create(null);
zoom['controls'] =
document.getElementById('zoomOption_controls_checkbox').checked;
zoom['wheel'] =
document.getElementById('zoomOption_wheel_checkbox').checked;
zoom['startScale'] =
parseInt(document.getElementById('zoomOption_startScale_text').value);
zoom['maxScale'] =
parseInt(document.getElementById('zoomOption_maxScale_text').value);
zoom['minScale'] =
parseInt(document.getElementById('zoomOption_minScale_text').value);
zoom['scaleSpeed'] =
parseInt(document.getElementById('zoomOption_scaleSpeed_text').value);
var startScaleValue =
document.getElementById('zoomOption_startScale_number').value;
zoom['startScale'] = typeof startScaleValue == 'string' ?
parseFloat(startScaleValue) : startScaleValue;
var maxScaleValue =
document.getElementById('zoomOption_maxScale_number').value;
zoom['maxcale'] = typeof maxScaleValue == 'string' ?
parseFloat(maxScaleValue) : maxScaleValue;
var minScaleValue =
document.getElementById('zoomOption_minScale_number').value;
zoom['minScale'] = typeof minScaleValue == 'string' ?
parseFloat(minScaleValue) : minScaleValue;
var scaleSpeedValue =
document.getElementById('zoomOption_scaleSpeed_number').value;
zoom['startScale'] = typeof startScaleValue == 'string' ?
parseFloat(scaleSpeedValue) : scaleSpeedValue;
optionsObj['zoom'] = zoom;
}
this.model.setOptions(optionsObj);
this.reinjectPreview(Blockly.Options.parseToolboxTree
(this.generator.generateToolboxXml()));
return optionsObj;
};
/**
@@ -1156,7 +1194,8 @@ WorkspaceFactoryController.prototype.importBlocks =
reader.onload = function() {
try {
// Define blocks using block types from file.
var blockTypes = FactoryUtils.defineAndGetBlockTypes(reader.result, format);
var blockTypes = FactoryUtils.defineAndGetBlockTypes(reader.result,
format);
var blocks = controller.generator.getDefinedBlocks(blockTypes);
// Generate category XML and append to toolbox.

View File

@@ -118,6 +118,8 @@ WorkspaceFactoryGenerator.prototype.generateToolboxXml = function() {
* it includes XY and ID attributes). Uses a workspace and converts user
* generated shadow blocks to actual shadow blocks.
*
* @return {!Element} XML element representing toolbox or flyout corresponding
* to toolbox workspace.
*/
WorkspaceFactoryGenerator.prototype.generateWorkspaceXml = function() {
// Load workspace XML to hidden workspace with user-generated shadow blocks
@@ -133,6 +135,38 @@ WorkspaceFactoryGenerator.prototype.generateWorkspaceXml = function() {
return generatedXml;
};
/**
* Generates a string representation of the options object for injecting the
* workspace.
*
* @return {!string} String representation of options object.
*/
WorkspaceFactoryGenerator.prototype.generateOptionsString = function() {
var addAttributes = function(obj, tabChar) {
if (!obj) {
return '{}\n';
}
var str = '';
for (var key in obj) {
if (key == 'grid' || key == 'zoom') {
var temp = tabChar + key + ' : {\n' + addAttributes(obj[key],
tabChar + '\t') + tabChar + '}, \n';
} else if (typeof obj[key] == 'string') {
var temp = tabChar + key + ' : \'' + obj[key] + '\', \n';
} else {
var temp = tabChar + key + ' : ' + obj[key] + ', \n';
}
str = str.concat(temp);
}
var lastCommaIndex = str.lastIndexOf(',');
str = str.slice(0, lastCommaIndex) + '\n';
return str;
};
return 'var options = { \n' + addAttributes(this.model.options, '\t') + '};';
}
/**
* Loads the given XML to the hidden workspace and sets any user-generated
* shadow blocks to be actual shadow blocks.

View File

@@ -351,6 +351,11 @@ document.getElementById('button_importBlocks').addEventListener
('click', function() {
controller.setStandardOptionsAndUpdate();
});
document.getElementById('button_optionsHelp').addEventListener
('click', function() {
open('https://developers.google.com/blockly/guides/get-started/web');
});
};
/**
@@ -578,6 +583,20 @@ WorkspaceFactoryInit.addWorkspaceFactoryOptionsListeners_ =
'block' : 'none';
});
document.getElementById('option_readOnly_checkbox').addEventListener('change',
function(e) {
document.getElementById('trashcan_option').style.display =
document.getElementById('option_readOnly_checkbox').checked ?
'none' : 'block';
});
document.getElementById('option_infiniteBlocks_checkbox').addEventListener('change',
function(e) {
document.getElementById('maxBlockNumber_option').style.display =
document.getElementById('option_infiniteBlocks_checkbox').checked ?
'none' : 'block';
});
// Generate new options every time an options input is updated.
var optionsElements = document.getElementsByClassName('optionsInput');
for (var i = 0; i < optionsElements.length; i++) {

View File

@@ -420,16 +420,6 @@ WorkspaceFactoryModel.prototype.setOptions = function(options) {
this.options = options;
};
/**
* Sets an attribute of the options object.
*
* @param {!string} name Name of the attribute to add.
* @param {Object} value The value of the attribute to add.
*/
WorkspaceFactoryModel.prototype.setOptionsAttribute = function(name, value) {
this.options[name] = value;
};
/*
* Returns an array of all the block types currently being used in the toolbox
* and the pre-loaded blocks. No duplicates.

View File

@@ -407,7 +407,8 @@ WorkspaceFactoryView.prototype.updateHelpText = function(mode) {
WorkspaceFactoryView.prototype.setBaseOptions = function() {
// Set basic options.
document.getElementById('option_css_checkbox').checked = true;
document.getElementById('option_maxBlocks_text').value = Infinity;
document.getElementById('option_infiniteBlocks_checkbox').checked = true;
document.getElementById('option_maxBlocks_number').value = 100;
document.getElementById('option_media_text').value =
'https://blockly-demo.appspot.com/static/media/';
document.getElementById('option_readOnly_checkbox').checked = false;
@@ -421,18 +422,18 @@ WorkspaceFactoryView.prototype.setBaseOptions = function() {
document.getElementById('zoom_options').style.display = 'none';
// Set grid options.
document.getElementById('gridOption_spacing_text').value = 0;
document.getElementById('gridOption_length_text').value = 1;
document.getElementById('gridOption_spacing_number').value = 0;
document.getElementById('gridOption_length_number').value = 1;
document.getElementById('gridOption_colour_text').value = '#888';
document.getElementById('gridOption_snap_checkbox').checked = false;
// Set zoom options.
document.getElementById('zoomOption_controls_checkbox').checked = false;
document.getElementById('zoomOption_wheel_checkbox').checked = false;
document.getElementById('zoomOption_startScale_text').value = 1.0;
document.getElementById('zoomOption_maxScale_text').value = 3;
document.getElementById('zoomOption_minScale_text').value = 0.3;
document.getElementById('zoomOption_scaleSpeed_text').value = 1.2;
document.getElementById('zoomOption_controls_checkbox').checked = true;
document.getElementById('zoomOption_wheel_checkbox').checked = true;
document.getElementById('zoomOption_startScale_number').value = 1.0;
document.getElementById('zoomOption_maxScale_number').value = 3;
document.getElementById('zoomOption_minScale_number').value = 0.3;
document.getElementById('zoomOption_scaleSpeed_number').value = 1.2;
};
/**