Blockly Factory: Add Pre-loaded blocks tab with import/export to workspace factory (#546)

* Added pre-loaded blocks with import and export, squashed commit.

* Configure Options object

Allows user to configure the options object, see changes in the preview workspace, and export the options object (squashed commit).

* Nit changes to style.css
This commit is contained in:
Emma Dauterman 2016-08-15 13:21:50 -07:00 committed by picklesrus
parent 0d66c77357
commit 06f21f85e4
6 changed files with 827 additions and 164 deletions

View file

@ -30,23 +30,45 @@ goog.require('goog.ui.ColorPicker');
<tr>
<td>
<p>
<input type="file" id="input_import" class="inputfile"></input>
<label for="input_import">Import</label>
<div class="dropdown">
<button id="button_import">Import</button>
<div id="dropdownDiv_import" class="dropdown-content">
<input type="file" id="input_importToolbox" class="inputfile"></input>
<label for="input_importToolbox">Toolbox</label>
<input type="file" id="input_importPreload" class="inputfile"></input>
<label for="input_importPreload">Workspace Blocks</label>
</div>
</div>
<div class="dropdown">
<button id="button_export">Export</button>
<button id="button_print">Print</button>
<button id="button_clear">Clear</button>
<div id="dropdownDiv_export" class="dropdown-content">
<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>
<button id="button_print">Print Toolbox</button>
<button id="button_clear">Clear Toolbox</button>
</p>
</td>
</tr>
</table>
<section id="createDiv">
<p>Drag blocks into your toolbox:</p>
<p id="editHelpText">Drag blocks into your toolbox:</p>
<table id='workspaceTabs'>
<td id="tab_toolbox" class="tabon">Toolbox</td>
<td id="tab_preload" class="taboff">Workspace</td>
</table>
<section id="toolbox_section">
<div id="toolbox_blocks" class="content"></div>
<div id='disable_div'></div>
</section>
<aside id="category_section">
<aside id="toolbox_div">
<table id="categoryTable">
<td id="tab_help">Your categories will appear here</td>
</table>
@ -75,7 +97,9 @@ goog.require('goog.ui.ColorPicker');
</div>
</div>
<div class='dropdown'>
</aside>
<div class='dropdown'>
<button id="button_editShadow">Edit Block</button>
<div id="dropdownDiv_editShadowAdd" class="dropdown-content">
<a id='dropdown_addShadow'>Add Shadow</a>
@ -83,9 +107,42 @@ goog.require('goog.ui.ColorPicker');
<div id="dropdownDiv_editShadowRemove" class="dropdown-content">
<a id='dropdown_removeShadow'>Remove Shadow</a>
</div>
</div>
</div>
<aside id='preload_div' style='display:none'>
<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">Standard Options</button>
<form id="workspace_options">
<input type="checkbox" id="option_collapse_checkbox" name="collapse">Collapsible Blocks<br>
<input type="checkbox" id="option_comments_checkbox" name="comments">Comments for Blocks<br>
<input type="checkbox" id="option_css_checkbox" name="css">Use Blockly CSS<br>
<input type="checkbox" id="option_disable_checkbox" name="disable">Disabled Blocks<br>
<input type="checkbox" id="option_grid_checkbox" name="grid">Use Grid<br>
<div id="grid_options" name="grid" style="display:none">
Spacing <input type="text" id="gridOption_spacing_text" name="spacing" value="0"><br>
Length <input type="text" id="gridOption_length_text" name="length" value="1"><br>
Color <input type="text" id="gridOption_colour_text" name="colour" value="#888"><br>
<input type="checkbox" id="gridOption_snap_checkbox" value="grid_snap_checkbox" name="snap">Snap<br>
</div>
Max Blocks <input type="text" id="option_maxBlocks_text" name="maxBlocks" value="Infinity"><br>
Path to Blockly Media <input type="text" id="option_media_text" name="media"><br>
<input type="checkbox" id="option_readOnly_checkbox" name="readOnly">Read Only<br>
<input type="checkbox" id="option_rtl_checkbox" name="rtl">Layout with RTL<br>
<input type="checkbox" id="option_scrollbars_checkbox" name="scrollbars">Scrollbars<br>
<input type="checkbox" id="option_sounds_checkbox" name="sounds">Sounds<br>
<input type="checkbox" id="option_trashcan_checkbox" name="trashcan">Trashcan<br>
<input type="checkbox" id="option_zoom_checkbox" name="zoom">Zoom<br>
<div id="zoom_options" name="zoom" style="display:none">
<input type="checkbox" id="zoomOption_controls_checkbox" name="controls">Zoom Controls<br>
<input type="checkbox" id="zoomOption_wheel_checkbox" name="wheel">Zoom Wheel<br>
Start Scale <input type="text" id="zoomOption_startScale_text" name="startScale" value="1.0"><br>
Max Scale <input type="text" id="zoomOption_maxScale_text" name="maxScale" value="3"><br>
Min Scale <input type="text" id="zoomOption_minScale_text" name="minScale" value="0.3"><br>
Scale Speed <input type="text" id="zoomOption_scaleSpeed_text" name="scaleSpeed" value="1.2"><br>
</div>
</form>
</aside>
</section>
<aside id="previewDiv">
@ -507,7 +564,8 @@ goog.require('goog.ui.ColorPicker');
controller.removeElement();
};
var exportWrapper = function() {
controller.exportConfig();
document.getElementById('dropdownDiv_export').classList.toggle("show");
document.getElementById('dropdownDiv_import').classList.remove("show");
};
var printWrapper = function() {
controller.printConfig();
@ -557,11 +615,53 @@ goog.require('goog.ui.ColorPicker');
}
}
};
var importWrapper = function(event) {
controller.importFile(event.target.files[0]);
var importWrapper = function() {
document.getElementById('dropdownDiv_import').classList.toggle("show");
document.getElementById('dropdownDiv_export').classList.remove("show");
};
var clearWrapper = function() {
controller.clear();
controller.clearToolbox();
};
var editToolboxWrapper = function() {
controller.setMode(FactoryController.MODE_TOOLBOX);
};
var editPreloadWrapper = function() {
controller.setMode(FactoryController.MODE_PRELOAD);
};
var importToolboxWrapper = function(e) {
controller.importFile(event.target.files[0], FactoryController.MODE_TOOLBOX);
document.getElementById('dropdownDiv_import').classList.remove("show");
};
var importPreloadWrapper = function(e) {
controller.importFile(event.target.files[0], FactoryController.MODE_PRELOAD);
document.getElementById('dropdownDiv_import').classList.remove("show");
};
var editToolboxWrapper = function() {
controller.setMode(FactoryController.MODE_TOOLBOX);
};
var editPreloadWrapper = function() {
controller.setMode(FactoryController.MODE_PRELOAD);
};
var exportToolboxWrapper = function() {
controller.exportFile(FactoryController.MODE_TOOLBOX);
document.getElementById('dropdownDiv_export').classList.remove("show");
}
var exportPreloadWrapper = function() {
controller.exportXmlFile(FactoryController.MODE_PRELOAD);
document.getElementById('dropdownDiv_export').classList.remove("show");
}
var exportOptionsWrapper = function() {
controller.exportOptionsFile();
document.getElementById('dropdownDiv_export').classList.remove("show");
}
var exportAllWrapper = function() {
controller.exportXmlFile(FactoryController.MODE_TOOLBOX);
controller.exportXmlFile(FactoryController.MODE_PRELOAD);
controller.exportOptionsFile();
document.getElementById('dropdownDiv_export').classList.remove("show");
}
var standardOptionsWrapper = function() {
controller.setStandardOptionsAndUpdate();
};
document.getElementById('button_add').addEventListener
@ -574,6 +674,14 @@ goog.require('goog.ui.ColorPicker');
('click', separatorWrapper);
document.getElementById('button_remove').addEventListener
('click', removeWrapper);
document.getElementById('dropdown_exportToolbox').addEventListener
('click', exportToolboxWrapper);
document.getElementById('dropdown_exportPreload').addEventListener
('click', exportPreloadWrapper);
document.getElementById('dropdown_exportOptions').addEventListener
('click', exportOptionsWrapper);
document.getElementById('dropdown_exportAll').addEventListener
('click', exportAllWrapper);
document.getElementById('button_export').addEventListener
('click', exportWrapper);
document.getElementById('button_print').addEventListener
@ -588,14 +696,24 @@ goog.require('goog.ui.ColorPicker');
('click', editShadowWrapper);
document.getElementById('dropdown_name').addEventListener
('click', nameWrapper);
document.getElementById('input_import').addEventListener
('change', importWrapper);
document.getElementById('button_import').addEventListener
('click', importWrapper);
document.getElementById('input_importToolbox').addEventListener
('change', importToolboxWrapper);
document.getElementById('input_importPreload').addEventListener
('change', importPreloadWrapper);
document.getElementById('button_clear').addEventListener
('click', clearWrapper);
document.getElementById('dropdown_addShadow').addEventListener
('click', shadowAddWrapper);
document.getElementById('dropdown_removeShadow').addEventListener
('click', shadowRemoveWrapper);
document.getElementById('tab_toolbox').addEventListener
('click', editToolboxWrapper);
document.getElementById('tab_preload').addEventListener
('click', editPreloadWrapper);
document.getElementById('button_standardOptions').addEventListener
('click', standardOptionsWrapper);
// Use up and down arrow keys to move categories.
// TODO(evd2014): When merge with next CL for editing preloaded blocks, make sure
@ -635,6 +753,7 @@ goog.require('goog.ui.ColorPicker');
// blocks when dragging them into workspace. Could cause problems if ever load
// blocks into workspace directly without calling updatePreview.
if (e.type == Blockly.Events.MOVE || e.type == Blockly.Events.DELETE) {
controller.saveStateFromWorkspace();
controller.updatePreview();
}
@ -678,5 +797,51 @@ goog.require('goog.ui.ColorPicker');
}
});
// Add event listeners for checkboxes and text fields for configuring the Options object.
// Checking the grid checkbox displays grid options and updates the options
// object.
document.getElementById('option_grid_checkbox').addEventListener('change', function(e) {
document.getElementById('grid_options').style.display = document.getElementById('option_grid_checkbox').checked ? 'block' : 'none';
controller.generateNewOptions();
});
// Checking the grid checkbox displays zoom options and updates the options
// object.
document.getElementById('option_zoom_checkbox').addEventListener('change', function(e) {
document.getElementById('zoom_options').style.display = document.getElementById('option_zoom_checkbox').checked ? 'block' : 'none';
controller.generateNewOptions();
});
var optionListener = function() {
controller.generateNewOptions();
};
// Add event listeners to generate new options for each options input field.
document.getElementById('option_collapse_checkbox').addEventListener('change', optionListener);
document.getElementById('option_comments_checkbox').addEventListener('change', optionListener);
document.getElementById('option_css_checkbox').addEventListener('change', optionListener);
document.getElementById('option_disable_checkbox').addEventListener('change', optionListener);
document.getElementById('gridOption_spacing_text').addEventListener('change', optionListener);
document.getElementById('gridOption_length_text').addEventListener('change', optionListener);
document.getElementById('gridOption_colour_text').addEventListener('change', optionListener);
document.getElementById('gridOption_snap_checkbox').addEventListener('change', optionListener);
document.getElementById('option_maxBlocks_text').addEventListener('change', optionListener);
document.getElementById('option_media_text').addEventListener('change', optionListener);
document.getElementById('option_readOnly_checkbox').addEventListener('change', optionListener);
document.getElementById('option_rtl_checkbox').addEventListener('change', optionListener);
document.getElementById('option_scrollbars_checkbox').addEventListener('change', optionListener);
document.getElementById('option_sounds_checkbox').addEventListener('change', optionListener);
document.getElementById('option_trashcan_checkbox').addEventListener('change', optionListener);
document.getElementById('zoomOption_controls_checkbox').addEventListener('change', optionListener);
document.getElementById('zoomOption_wheel_checkbox').addEventListener('change', optionListener);
document.getElementById('zoomOption_startScale_text').addEventListener('change', optionListener);
document.getElementById('zoomOption_maxScale_text').addEventListener('change', optionListener);
document.getElementById('zoomOption_minScale_text').addEventListener('change', optionListener);
document.getElementById('zoomOption_scaleSpeed_text').addEventListener('change', optionListener);
// Check standard options and apply the changes to update the view.
controller.setStandardOptionsAndUpdate();
</script>
</html>

View file

@ -21,6 +21,10 @@ aside {
border-bottom: none;
}
#workspaceTabs>table {
float: right;
}
td.tabon {
border-bottom-color: #ddd !important;
background-color: #ddd;
@ -63,31 +67,8 @@ button:hover:not(:disabled)>* {
opacity: 1;
}
label {
border-radius: 4px;
border: 1px solid #ddd;
background-color: #eee;
color: #000;
font-size: large;
margin: 0 5px;
padding: 10px;
}
label:hover:not(:disabled) {
box-shadow: 2px 2px 5px #888;
}
label:disabled {
opacity: .6;
}
label>* {
opacity: .6;
vertical-align: text-bottom;
}
label:hover:not(:disabled)>* {
opacity: 1;
button.small {
font-size: small;
}
table {
@ -135,7 +116,11 @@ td {
width: 30%;
}
#category_section {
#toolbox_div {
width: 20%;
}
#preload_div {
width: 20%;
}
@ -200,25 +185,32 @@ td {
/* Dropdown Content (Hidden by Default) */
.dropdown-content {
background-color: #f9f9f9;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,.2);
display: none;
min-width: 170px;
opacity: 1;
position: absolute;
z-index: 1;
background-color: #f9f9f9;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,.2);
display: none;
min-width: 170px;
opacity: 1;
position: absolute;
z-index: 1;
}
/* Links inside the dropdown */
.dropdown-content a {
color: black;
display: block;
padding: 12px 16px;
text-decoration: none;
color: black;
display: block;
padding: 12px 16px;
text-decoration: none;
}
/* Change color of dropdown links on hover */
.dropdown-content a:hover {
.dropdown-content label {
color: black;
display: block;
padding: 12px 16px;
text-decoration: none;
}
/* Change color of dropdown links on hover. */
.dropdown-content a:hover, .dropdown-content label:hover {
background-color: #f1f1f1
}

View file

@ -53,21 +53,31 @@ FactoryController = function(toolboxWorkspace, previewWorkspace) {
this.view = new FactoryView();
// Generates XML for categories.
this.generator = new FactoryGenerator(this.model);
// Tracks which editing mode the user is in. Toolbox mode on start.
this.selectedMode = FactoryController.MODE_TOOLBOX;
};
// Toolbox editing mode. Changes the user makes to the workspace updates the
// toolbox.
FactoryController.MODE_TOOLBOX = 'toolbox';
// Pre-loaded workspace editing mode. Changes the user makes to the workspace
// udpates the pre-loaded blocks.
FactoryController.MODE_PRELOAD = 'preload';
/**
* Currently prompts the user for a name, checking that it's valid (not used
* before), and then creates a tab and switches to it.
*/
FactoryController.prototype.addCategory = function() {
// Check if it's the first category added.
var firstCategory = !this.model.hasToolbox();
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 (firstCategory && this.toolboxWorkspace.getAllBlocks().length > 0) {
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 ' +
@ -75,21 +85,35 @@ FactoryController.prototype.addCategory = function() {
if (!name) { // Exit if cancelled.
return;
}
// Create the new category.
this.createCategory(name, true);
this.model.setSelectedById(this.model.getCategoryIdByName(name));
// 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();
}
}
// After possibly creating a category, check again if it's the first category.
firstCategory = !this.model.hasToolbox();
isFirstCategory = !this.model.hasElements();
// Get name from user.
name = this.promptForNewCategoryName('Enter the name of your new category: ');
if (!name) { //Exit if cancelled.
return;
}
// Create category.
this.createCategory(name, firstCategory);
this.createCategory(name, isFirstCategory);
// Switch to category.
this.switchElement(this.model.getCategoryIdByName(name));
// Allow the user to use the default options for injecting the workspace
// when there are categories if adding the first category.
if (isFirstCategory) {
this.allowToSetDefaultOptions();
}
// Update preview.
this.updatePreview();
};
@ -101,15 +125,15 @@ FactoryController.prototype.addCategory = function() {
*
* @param {!string} name Name of category being added.
* @param {!string} id The ID of the category being added.
* @param {boolean} firstCategory True if it's the first category created,
* @param {boolean} isFirstCategory True if it's the first category created,
* false otherwise.
*/
FactoryController.prototype.createCategory = function(name, firstCategory) {
FactoryController.prototype.createCategory = function(name, isFirstCategory) {
// Create empty category
var category = new ListElement(ListElement.TYPE_CATEGORY, name);
this.model.addElementToList(category);
// Create new category.
var tab = this.view.addCategoryRow(name, category.id, firstCategory);
var tab = this.view.addCategoryRow(name, category.id, isFirstCategory);
this.addClickToSwitch(tab, category.id);
};
@ -142,30 +166,44 @@ FactoryController.prototype.removeElement = function() {
if (!this.model.getSelected()) {
return;
}
// Check if user wants to remove current category.
var check = confirm('Are you sure you want to delete the currently selected '
+ this.model.getSelected().type + '?');
if (!check) { // If cancelled, exit.
return;
}
var selectedId = this.model.getSelectedId();
var selectedIndex = this.model.getIndexByElementId(selectedId);
// Delete element visually.
this.view.deleteElementRow(selectedId, selectedIndex);
// Delete element in model.
this.model.deleteElementFromList(selectedIndex);
// Find next logical element to switch to.
var next = this.model.getElementByIndex(selectedIndex);
if (!next && this.model.hasToolbox()) {
if (!next && this.model.hasElements()) {
next = this.model.getElementByIndex(selectedIndex - 1);
}
var nextId = next ? next.id : null;
// Open next element.
this.clearAndLoadElement(nextId);
// If no element to switch to, display message, clear the workspace, and
// set a default selected element not in toolbox list in the model.
if (!nextId) {
alert('You currently have no categories or separators. All your blocks' +
' will be displayed in a single flyout.');
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();
};
@ -226,28 +264,21 @@ FactoryController.prototype.clearAndLoadElement = function(id) {
this.view.disableWorkspace(false);
}
// Set next category.
this.model.setSelectedById(id);
// Clear workspace.
this.toolboxWorkspace.clear();
this.toolboxWorkspace.clearUndo();
// Loads next category if switching to an element.
// If switching to another category, set category selection in the model and
// view.
if (id != null) {
this.view.setCategoryTabSelection(id, true);
Blockly.Xml.domToWorkspace(this.model.getSelectedXml(),
this.toolboxWorkspace);
// Disable workspace if switching to a separator.
if (this.model.getSelected().type == ListElement.TYPE_SEPARATOR) {
this.view.disableWorkspace(true);
}
}
// Set next category.
this.model.setSelectedById(id);
// Mark all shadow blocks laoded and order blocks as if shown in a flyout.
this.view.markShadowBlocks(this.model.getShadowBlocksInWorkspace
(toolboxWorkspace.getAllBlocks()));
this.toolboxWorkspace.cleanUp_();
// Clears workspace and loads next category.
this.clearAndLoadXml_(this.model.getSelectedXml());
// Selects the next tab.
this.view.setCategoryTabSelection(id, true);
// Order blocks as if shown in the flyout.
this.toolboxWorkspace.cleanUp_();
}
// Update category editing buttons.
this.view.updateState(this.model.getIndexByElementId
@ -255,15 +286,34 @@ FactoryController.prototype.clearAndLoadElement = function(id) {
};
/**
* Tied to "Export Config" button. Gets a file name from the user and downloads
* Tied to "Export" button. Gets a file name from the user and downloads
* the corresponding configuration xml to that file.
*
* @param {!string} exportMode The type of file to export
* (FactoryController.MODE_TOOLBOX for the toolbox configuration, and
* FactoryController.MODE_PRELOAD for the pre-loaded workspace configuration)
*/
FactoryController.prototype.exportConfig = function() {
FactoryController.prototype.exportXmlFile = function(exportMode) {
// Generate XML.
var configXml = Blockly.Xml.domToPrettyText
(this.generator.generateConfigXml(this.toolboxWorkspace));
if (exportMode == FactoryController.MODE_TOOLBOX) {
// Export the toolbox XML.
var configXml = Blockly.Xml.domToPrettyText
(this.generator.generateToolboxXml());
} else if (exportMode == FactoryController.MODE_PRELOAD) {
// Export the pre-loaded block XML.
var configXml = Blockly.Xml.domToPrettyText
(this.generator.generateWorkspaceXml());
} else {
// Unknown mode. Throw error.
throw new Error ("Unknown export mode: " + exportMode);
}
// Get file name.
var fileName = prompt("File Name: ");
var fileName = prompt('File Name for ' + (exportMode ==
FactoryController.MODE_TOOLBOX ? 'toolbox XML: ' :
'pre-loaded workspace XML: '));
if (!fileName) { // If cancelled
return;
}
@ -273,12 +323,33 @@ FactoryController.prototype.exportConfig = function() {
};
/**
* Tied to "Print Config" button. Mainly used for debugging purposes. Prints
* 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.
*/
FactoryController.prototype.exportOptionsFile = function() {
var fileName = prompt('File Name for options object for injecting: ');
if (!fileName) { // If cancelled.
return;
}
// 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/plain'});
this.view.createAndDownloadFile(fileName, data);
};
/**
* Tied to "Print" button. Mainly used for debugging purposes. Prints
* the configuration XML to the console.
*/
FactoryController.prototype.printConfig = function() {
// Capture any changes made by user before generating XML.
this.saveStateFromWorkspace();
// Print XML.
window.console.log(Blockly.Xml.domToPrettyText
(this.generator.generateConfigXml(this.toolboxWorkspace)));
(this.generator.generateToolboxXml()));
};
/**
@ -287,33 +358,71 @@ FactoryController.prototype.printConfig = function() {
* Blockly with reinjectPreview, otherwise just updates without reinjecting.
* Called whenever a list element is created, removed, or modified and when
* Blockly move and delete events are fired. Do not call on create events
* or disabling will cause the user to "drop" their current blocks.
* or disabling will cause the user to "drop" their current blocks. Make sure
* that no changes have been made to the workspace since updating the model
* (if this might be the case, call saveStateFromWorkspace).
*/
FactoryController.prototype.updatePreview = function() {
// Disable events to stop updatePreview from recursively calling itself
// through event handlers.
Blockly.Events.disable();
var tree = Blockly.Options.parseToolboxTree
(this.generator.generateConfigXml(this.toolboxWorkspace));
// No categories, creates a simple flyout.
if (tree.getElementsByTagName('category').length == 0) {
if (this.previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Switch to simple flyout, more expensive.
if (this.selectedMode == FactoryController.MODE_TOOLBOX) {
// If currently editing the toolbox.
// Get toolbox XML.
var tree = Blockly.Options.parseToolboxTree
(this.generator.generateToolboxXml());
// No categories, creates a simple flyout.
if (tree.getElementsByTagName('category').length == 0) {
// No categories, creates a simple flyout.
if (this.previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Switch to simple flyout, more expensive.
} else {
this.previewWorkspace.flyout_.show(tree.childNodes);
}
} else {
this.previewWorkspace.flyout_.show(tree.childNodes);
}
// Uses categories, creates a toolbox.
} else {
if (!previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Create a toolbox, more expensive.
} else {
this.previewWorkspace.toolbox_.populate_(tree);
// Uses categories, creates a toolbox.
if (!previewWorkspace.toolbox_) {
this.reinjectPreview(tree); // Create a toolbox, more expensive.
} else {
this.previewWorkspace.toolbox_.populate_(tree);
}
}
// Update pre-loaded blocks in the preview workspace to make sure that
// blocks don't get cleared when updating preview from event listeners while
// switching modes.
this.previewWorkspace.clear();
Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(),
this.previewWorkspace);
} else if (this.selectedMode == FactoryController.MODE_PRELOAD){
// If currently editing the pre-loaded workspace.
this.previewWorkspace.clear();
Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(),
this.previewWorkspace);
}
// Reenable events.
Blockly.Events.enable();
};
/**
* Saves the state from the workspace depending on the current mode. Should
* be called after making changes to the workspace.
*/
FactoryController.prototype.saveStateFromWorkspace = function() {
if (this.selectedMode == FactoryController.MODE_TOOLBOX) {
// If currently editing the toolbox.
this.model.getSelected().saveFromWorkspace(toolboxWorkspace);
} else if (this.selectedMode == FactoryController.MODE_PRELOAD) {
// If currently editing the pre-loaded workspace.
this.model.savePreloadXml
(Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
}
};
/**
* Used to completely reinject the preview workspace. This should be used only
* when switching from simple flyout to categories, or categories to simple
@ -324,19 +433,10 @@ FactoryController.prototype.updatePreview = function() {
*/
FactoryController.prototype.reinjectPreview = function(tree) {
this.previewWorkspace.dispose();
previewToolbox = Blockly.Xml.domToPrettyText(tree);
this.previewWorkspace = Blockly.inject('preview_blocks',
{grid:
{spacing: 25,
length: 3,
colour: '#ccc',
snap: true},
media: '../../../media/',
toolbox: previewToolbox,
zoom:
{controls: true,
wheel: true}
});
this.model.setOptionsAttribute('toolbox', Blockly.Xml.domToPrettyText(tree));
this.previewWorkspace = Blockly.inject('preview_blocks', this.model.options);
Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(),
this.previewWorkspace);
};
/**
@ -345,9 +445,8 @@ FactoryController.prototype.reinjectPreview = function(tree) {
* currently in use, exits if user presses cancel.
*/
FactoryController.prototype.changeCategoryName = function() {
// Return if no category selected or element a separator.
if (!this.model.getSelected() ||
this.model.getSelected().type == ListElement.TYPE_SEPARATOR) {
// Return if a category is not selected.
if (this.model.getSelected().type != ListElement.TYPE_CATEGORY) {
return;
}
// Get new name from user.
@ -415,9 +514,8 @@ FactoryController.prototype.moveElementToIndex = function(element, newIndex,
* a valid CSS string.
*/
FactoryController.prototype.changeSelectedCategoryColor = function(color) {
// Return if no category selected or element a separator.
if (!this.model.getSelected() ||
this.model.getSelected().type == ListElement.TYPE_SEPARATOR) {
// Return if category is not selected.
if (this.model.getSelected().type != ListElement.TYPE_CATEGORY) {
return;
}
// Change color of selected category.
@ -461,17 +559,15 @@ FactoryController.prototype.loadCategory = function() {
return;
}
var isFirstCategory = !this.model.hasElements();
// Copy the standard category in the model.
var copy = standardCategory.copy();
// Add the copy in the view.
var tab = this.view.addCategoryRow(copy.name, copy.id,
!this.model.hasToolbox());
// Add it to the model.
this.model.addElementToList(copy);
// Update the view.
// Update the copy in the view.
var tab = this.view.addCategoryRow(copy.name, copy.id, isFirstCategory);
this.addClickToSwitch(tab, copy.id);
// Color the category tab in the view.
if (copy.color) {
@ -480,7 +576,14 @@ FactoryController.prototype.loadCategory = function() {
// Switch to loaded category.
this.switchElement(copy.id);
// Convert actual shadow blocks to user-generated shadow blocks.
this.convertShadowBlocks();
this.convertShadowBlocks_();
// Save state from workspace before updating preview.
this.saveStateFromWorkspace();
if (isFirstCategory) {
// Allow the user to use the default options for injecting the workspace
// when there are categories.
this.allowToSetDefaultOptions();
}
// Update preview.
this.updatePreview();
};
@ -509,7 +612,7 @@ FactoryController.prototype.isStandardCategoryName = function(name) {
*/
FactoryController.prototype.addSeparator = function() {
// Don't allow the user to add a separator if a category has not been created.
if (!this.model.hasToolbox()) {
if (!this.model.hasElements()) {
alert('Add a category before adding a separator.');
return;
}
@ -526,47 +629,64 @@ FactoryController.prototype.addSeparator = function() {
/**
* Connected to the import button. Given the file path inputted by the user
* from file input, this function loads that toolbox XML to the workspace,
* creating category and separator tabs as necessary. This allows the user
* to be able to edit toolboxes given their XML form. Catches errors from
* from file input, if the import mode is for the toolbox, this function loads
* that toolbox XML to the workspace, creating category and separator tabs as
* necessary. If the import mode is for pre-loaded blocks in the workspace,
* this function loads that XML to the workspace to be edited further. This
* function switches mode to whatever the import mode is. Catches errors from
* file reading and prints an error message alerting the user.
*
* @param {string} file The path for the file to be imported into the workspace.
* Should contain valid toolbox XML.
* @param {!string} importMode The mode corresponding to the type of file the
* user is importing (FactoryController.MODE_TOOLBOX or
* FactoryController.MODE_PRELOAD).
*/
FactoryController.prototype.importFile = function(file) {
FactoryController.prototype.importFile = function(file, importMode) {
// Exit if cancelled.
if (!file) {
return;
}
var reader = new FileReader();
// To be executed when the reader has read the file.
reader.onload = function() {
// Try to parse XML from file and load it into toolbox editing area.
// Print error message if fail.
try {
var tree = Blockly.Xml.textToDom(reader.result);
controller.importFromTree_(tree);
} catch(e) {
alert('Cannot load XML from file.');
console.log(e);
}
if (importMode == FactoryController.MODE_TOOLBOX) {
// Switch mode and import toolbox XML.
controller.setMode(FactoryController.MODE_TOOLBOX);
controller.importToolboxFromTree_(tree);
} else if (importMode == FactoryController.MODE_PRELOAD) {
// Switch mode and import pre-loaded workspace XML.
controller.setMode(FactoryController.MODE_PRELOAD);
controller.importPreloadFromTree_(tree);
} else {
// Throw error if invalid mode.
throw new Error("Unknown import mode: " + importMode);
}
} catch(e) {
alert('Cannot load XML from file.');
console.log(e);
}
}
// Read the file.
// Read the file asynchronously.
reader.readAsText(file);
};
/**
* Given a XML DOM tree, loads it into the toolbox editing area so that the
* user can continue editing their work. Assumes that tree is in valid toolbox
* XML format.
* XML format. Assumes that the mode is MODE_TOOLBOX.
* @private
*
* @param {!Element} tree XML tree to be loaded to toolbox editing area.
*/
FactoryController.prototype.importFromTree_ = function(tree) {
FactoryController.prototype.importToolboxFromTree_ = function(tree) {
// Clear current editing area.
this.model.clearToolboxList();
this.view.clearToolboxTabs();
@ -582,9 +702,11 @@ FactoryController.prototype.importFromTree_ = function(tree) {
// Add message to denote empty category.
this.view.addEmptyCategoryMessage();
} else {
// Categories/separators present.
for (var i = 0, item; item = tree.children[i]; i++) {
if (item.tagName == 'category') {
// If the element is a category, create a new category and switch to it.
this.createCategory(item.getAttribute('name'), false);
@ -623,20 +745,79 @@ FactoryController.prototype.importFromTree_ = function(tree) {
}
this.view.updateState(this.model.getIndexByElementId
(this.model.getSelectedId()), this.model.getSelected());
this.saveStateFromWorkspace();
this.saveStateFromWorkspace();
// Allow the user to set default configuration options for a single flyout
// or multiple categories.
this.allowToSetDefaultOptions();
this.updatePreview();
};
/**
* Clears the toolbox editing area completely, deleting all categories and all
* blocks in the model and view.
* Given a XML DOM tree, loads it into the pre-loaded workspace editing area.
* Assumes that tree is in valid XML format and that the selected mode is
* MODE_PRELOAD.
*
* @param {!Element} tree XML tree to be loaded to pre-loaded block editing
* area.
*/
FactoryController.prototype.clear = function() {
FactoryController.prototype.importPreloadFromTree_ = function(tree) {
this.clearAndLoadXml_(tree);
this.model.savePreloadXml(tree);
this.updatePreview();
}
/**
* Given a XML DOM tree, loads it into the pre-loaded workspace editing area.
* Assumes that tree is in valid XML format and that the selected mode is
* MODE_PRELOAD.
*
* @param {!Element} tree XML tree to be loaded to pre-loaded block editing
* area.
*/
FactoryController.prototype.importPreloadFromTree_ = function(tree) {
this.clearAndLoadXml_(tree);
this.model.savePreloadXml(tree);
this.saveStateFromWorkspace();
this.updatePreview();
}
/**
* Given a XML DOM tree, loads it into the pre-loaded workspace editing area.
* Assumes that tree is in valid XML format and that the selected mode is
* MODE_PRELOAD.
*
* @param {!Element} tree XML tree to be loaded to pre-loaded block editing
* area.
*/
FactoryController.prototype.importPreloadFromTree_ = function(tree) {
this.clearAndLoadXml_(tree);
this.model.savePreloadXml(tree);
this.saveStateFromWorkspace();
this.updatePreview();
}
/**
* Clears the toolbox editing area completely, deleting all categories and all
* blocks in the model and view. Sets the mode to toolbox mode. Tied to "Clear
* Toolbox" button.
*/
FactoryController.prototype.clearToolbox = function() {
this.setMode(FactoryController.MODE_TOOLBOX);
var hasCategories = this.model.hasElements();
this.model.clearToolboxList();
this.view.clearToolboxTabs();
this.view.addEmptyCategoryMessage();
this.view.updateState(-1, null);
this.toolboxWorkspace.clear();
this.toolboxWorkspace.clearUndo();
this.saveStateFromWorkspace();
if (hasCategories) {
this.allowToSetDefaultOptions();
}
this.updatePreview();
};
@ -655,6 +836,7 @@ FactoryController.prototype.addShadow = function() {
}
this.view.markShadowBlock(Blockly.selected);
this.model.addShadowBlock(Blockly.selected.id);
this.saveStateFromWorkspace();
this.updatePreview();
};
@ -671,6 +853,7 @@ FactoryController.prototype.removeShadow = function() {
}
this.model.removeShadowBlock(Blockly.selected.id);
this.view.unmarkShadowBlock(Blockly.selected);
this.saveStateFromWorkspace();
this.updatePreview();
};
@ -702,3 +885,167 @@ FactoryController.prototype.convertShadowBlocks = function() {
}
}
};
/**
* Sets the currently selected mode that determines what the toolbox workspace
* is being used to edit. Updates the view and then saves and loads XML
* to and from the toolbox and updates the help text.
*
* @param {!string} tab The type of tab being switched to
* (FactoryController.MODE_TOOLBOX or FactoryController.MODE_PRELOAD).
*/
FactoryController.prototype.setMode = function(mode) {
// No work to change mode that's currently set.
if (this.selectedMode == mode) {
return;
}
// No work to change mode that's currently set.
if (this.selectedMode == mode) {
return;
}
// Set tab selection and display appropriate tab.
this.view.setModeSelection(mode);
// Update selected tab.
this.selectedMode = mode;
// Update help text above workspace.
this.view.updateHelpText(mode);
if (mode == FactoryController.MODE_TOOLBOX) {
// Open the toolbox editing space.
document.getElementById('editHelpText').textContent =
'Drag blocks into your toolbox:';
this.model.savePreloadXml
(Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
this.clearAndLoadXml_(this.model.getSelectedXml());
this.view.disableWorkspace(this.view.shouldDisableWorkspace
(this.model.getSelected()));
} else {
// Open the pre-loaded workspace editing space.
document.getElementById('editHelpText').textContent =
'Drag blocks into your pre-loaded workspace:';
if (this.model.getSelected()) {
this.model.getSelected().saveFromWorkspace(this.toolboxWorkspace);
}
this.clearAndLoadXml_(this.model.getPreloadXml());
this.view.disableWorkspace(false);
}
};
/**
* Clears the toolbox workspace and loads XML to it, marking shadow blocks
* as necessary.
* @private
*
* @param {!Element} xml The XML to be loaded to the workspace.
*/
FactoryController.prototype.clearAndLoadXml_ = function(xml) {
this.toolboxWorkspace.clear();
this.toolboxWorkspace.clearUndo();
Blockly.Xml.domToWorkspace(xml, this.toolboxWorkspace);
this.view.markShadowBlocks(this.model.getShadowBlocksInWorkspace
(this.toolboxWorkspace.getAllBlocks()));
};
/**
* Sets the standard default options for the options object and updates
* the preview workspace. The default values depends on if categories are
* present.
*/
FactoryController.prototype.setStandardOptionsAndUpdate = function() {
this.view.setBaseOptions();
this.view.setCategoryOptions(this.model.hasElements());
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.
*/
FactoryController.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.
*/
FactoryController.prototype.generateNewOptions = function() {
var optionsObj = new Object(null);
// Add all standard options to the options object.
// Use parse int to get numbers from value inputs.
optionsObj['collapse'] =
document.getElementById('option_collapse_checkbox').checked;
optionsObj['comments'] =
document.getElementById('option_comments_checkbox').checked;
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);
optionsObj['media'] = document.getElementById('option_media_text').value;
optionsObj['readOnly'] =
document.getElementById('option_readOnly_checkbox').checked;
optionsObj['rtl'] = document.getElementById('option_rtl_checkbox').checked;
optionsObj['scrollbars'] =
document.getElementById('option_scrollbars_checkbox').checked;
optionsObj['sounds'] =
document.getElementById('option_sounds_checkbox').checked;
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);
grid['colour'] = document.getElementById('gridOption_colour_text').value;
grid['snap'] = document.getElementById('gridOption_snap_checkbox').checked;
optionsObj['grid'] = grid;
}
// If using zoom, add all zoom options.
if (document.getElementById('option_zoom_checkbox').checked) {
var zoom = new Object(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);
optionsObj['zoom'] = zoom;
}
this.model.setOptions(optionsObj);
this.reinjectPreview(Blockly.Options.parseToolboxTree
(this.generator.generateToolboxXml()));
};

View file

@ -49,23 +49,24 @@ FactoryGenerator = function(model) {
/**
* Generates the xml for the toolbox or flyout with information from
* toolboxWorkspace and the model. Uses the hiddenWorkspace to generate XML.
* Save state of workspace in model (saveFromWorkspace) before calling if
* changes might have been made to the selected category.
*
* @param {!Blockly.workspace} toolboxWorkspace Toolbox editing workspace where
* blocks are added by user to be part of the toolbox.
* @return {!Element} XML element representing toolbox or flyout corresponding
* to toolbox workspace.
*/
FactoryGenerator.prototype.generateConfigXml = function(toolboxWorkspace) {
FactoryGenerator.prototype.generateToolboxXml = function() {
// Create DOM for XML.
var xmlDom = goog.dom.createDom('xml',
{
'id' : 'toolbox',
'style' : 'display:none'
});
if (!this.model.hasToolbox()) {
if (!this.model.hasElements()) {
// Toolbox has no categories. Use XML directly from workspace.
this.loadToHiddenWorkspaceAndSave_
(Blockly.Xml.workspaceToDom(toolboxWorkspace), xmlDom);
this.loadToHiddenWorkspaceAndSave_(this.model.getSelectedXml(), xmlDom);
} else {
// Toolbox has categories.
// Assert that selected != null
@ -73,8 +74,6 @@ FactoryGenerator.prototype.generateConfigXml = function(toolboxWorkspace) {
throw new Error('Selected is null when the toolbox is empty.');
}
// Capture any changes made by user before generating XML.
this.model.getSelected().saveFromWorkspace(toolboxWorkspace);
var xml = this.model.getSelectedXml();
var toolboxList = this.model.getToolboxList();
@ -87,7 +86,7 @@ FactoryGenerator.prototype.generateConfigXml = function(toolboxWorkspace) {
if (element.type == ListElement.TYPE_SEPARATOR) {
// If the next element is a separator.
var nextElement = goog.dom.createDom('sep');
} else {
} else if (element.type == ListElement.TYPE_CATEGORY) {
// If the next element is a category.
var nextElement = goog.dom.createDom('category');
nextElement.setAttribute('name', element.name);
@ -109,6 +108,27 @@ FactoryGenerator.prototype.generateConfigXml = function(toolboxWorkspace) {
return xmlDom;
};
/**
* Generates XML for the workspace (different from generateConfigXml in that
* it includes XY and ID attributes). Uses a workspace and converts user
* generated shadow blocks to actual shadow blocks.
*
*/
FactoryGenerator.prototype.generateWorkspaceXml = function() {
// Load workspace XML to hidden workspace with user-generated shadow blocks
// as actual shadow blocks.
this.hiddenWorkspace.clear();
Blockly.Xml.domToWorkspace(this.model.getPreloadXml(), this.hiddenWorkspace);
this.setShadowBlocksInHiddenWorkspace_();
// Generate XML and set attributes.
var generatedXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace);
generatedXml.setAttribute('id', 'preload_blocks');
generatedXml.setAttribute('style', 'display:none');
return generatedXml;
}
/**
* Load the given XML to the hidden workspace, set any user-generated shadow
* blocks to be actual shadow blocks, then append the XML from the workspace

View file

@ -35,21 +35,26 @@
* @constructor
*/
FactoryModel = function() {
// Ordered list of ListElement objects.
// Ordered list of ListElement objects. Empty if there is a single flyout.
this.toolboxList = [];
// ListElement for blocks in a single flyout. Null if a toolbox exists.
this.flyout = new ListElement(ListElement.TYPE_FLYOUT);
// Array of block IDs for all user created shadow blocks.
this.shadowBlocks = [];
// String name of current selected list element, null if no list elements.
this.selected = null;
// Reference to currently selected ListElement. Stored in this.toolboxList if
// there are categories, or in this.flyout if blocks are displayed in a single
// flyout.
this.selected = this.flyout;
// Boolean for if a Variable category has been added.
this.hasVariableCategory = false;
// Boolean for if a Procedure category has been added.
this.hasProcedureCategory = false;
// XML to be pre-loaded to workspace. Empty on default;
this.preloadXml = Blockly.Xml.textToDom('<xml></xml>');
// Options object to be configured for Blockly inject call.
this.options = new Object(null);
};
// String name of current selected list element, null if no list elements.
FactoryModel.prototype.selected = null;
/**
* Given a name, determines if it is the name of a category already present.
* Used when getting a valid category name from the user.
@ -91,9 +96,9 @@ FactoryModel.prototype.hasProcedures = function() {
* Determines if the user has any elements in the toolbox. Uses the length of
* toolboxList.
*
* @return {boolean} True if categories exist, false otherwise.
* @return {boolean} True if elements exist, false otherwise.
*/
FactoryModel.prototype.hasToolbox = function() {
FactoryModel.prototype.hasElements = function() {
return this.toolboxList.length > 0;
};
@ -110,6 +115,8 @@ FactoryModel.prototype.addElementToList = function(element) {
this.hasProcedureCategory;
// Add element to toolboxList.
this.toolboxList.push(element);
// Empty single flyout.
this.flyout = null;
};
/**
@ -131,6 +138,20 @@ FactoryModel.prototype.deleteElementFromList = function(index) {
this.toolboxList.splice(index, 1);
};
/**
* Sets selected to be an empty category not in toolbox list if toolbox list
* is empty. Should be called when removing the last element from toolbox list.
* If the toolbox list is empty, selected stores the XML for the single flyout
* of blocks displayed.
*
*/
FactoryModel.prototype.createDefaultSelectedIfEmpty = function() {
if (this.toolboxList.length == 0) {
this.flyout = new ListElement(ListElement.TYPE_FLYOUT);
this.selected = this.flyout;
}
}
/**
* Moves a list element to a certain position in toolboxList by removing it
* and then inserting it at the correct index. Checks that indices are in
@ -364,6 +385,43 @@ FactoryModel.prototype.addCustomTag = function(category, tag) {
}
};
/**
* Have basic pre-loaded workspace working
* Saves XML as XML to be pre-loaded into the workspace.
*
* @param {!Element} xml The XML to be saved.
*/
FactoryModel.prototype.savePreloadXml = function(xml) {
this.preloadXml = xml
};
/**
* Gets the XML to be pre-loaded into the workspace.
*
* @return {!Element} The XML for the workspace.
*/
FactoryModel.prototype.getPreloadXml = function() {
return this.preloadXml;
};
/**
* Sets a new options object for injecting a Blockly workspace.
*
* @param {Object} options Options object for injecting a Blockly workspace.
*/
FactoryModel.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.
*/
FactoryModel.prototype.setOptionsAttribute = function(name, value) {
this.options[name] = value;
};
/**
* Class for a ListElement.
@ -386,6 +444,7 @@ ListElement = function(type, opt_name) {
// List element types.
ListElement.TYPE_CATEGORY = 'category';
ListElement.TYPE_SEPARATOR = 'separator';
ListElement.TYPE_FLYOUT = 'flyout';
/**
* Saves a category by updating its XML (does not save XML for
@ -395,11 +454,11 @@ ListElement.TYPE_SEPARATOR = 'separator';
* from.
*/
ListElement.prototype.saveFromWorkspace = function(workspace) {
// Only save list elements that are categories.
if (this.type != ListElement.TYPE_CATEGORY) {
return;
// Only save XML for categories and flyouts.
if (this.type == ListElement.TYPE_FLYOUT ||
this.type == ListElement.TYPE_CATEGORY) {
this.xml = Blockly.Xml.workspaceToDom(workspace);
}
this.xml = Blockly.Xml.workspaceToDom(workspace);
};

View file

@ -339,3 +339,83 @@ FactoryView.prototype.unmarkShadowBlock = function(block) {
Blockly.removeClass_(block.svgGroup_, 'shadowBlock');
}
};
/**
* Sets the tabs for modes according to which mode the user is currenly
* editing in.
*
* @param {!string} mode The mode being switched to
* (FactoryController.MODE_TOOLBOX or FactoryController.MODE_PRELOAD).
*/
FactoryView.prototype.setModeSelection = function(mode) {
document.getElementById('tab_preload').className = mode ==
FactoryController.MODE_PRELOAD ? 'tabon' : 'taboff';
document.getElementById('preload_div').style.display = mode ==
FactoryController.MODE_PRELOAD ? 'block' : 'none';
document.getElementById('tab_toolbox').className = mode ==
FactoryController.MODE_TOOLBOX ? 'tabon' : 'taboff';
document.getElementById('toolbox_div').style.display = mode ==
FactoryController.MODE_TOOLBOX ? 'block' : 'none';
};
/**
* Updates the help text above the workspace depending on the selected mode.
*
* @param {!string} mode The selected mode (FactoryController.MODE_TOOLBOX or
* FactoryController.MODE_PRELOAD).
*/
FactoryView.prototype.updateHelpText = function(mode) {
var helpText = 'Drag your blocks into your ' + (mode ==
FactoryController.MODE_TOOLBOX ? 'toolbox: ' : 'pre-loaded workspace: ');
document.getElementById('editHelpText').textContent = helpText;
};
/**
* Sets the basic options that are not dependent on if there are categories
* or a single flyout of blocks. Updates checkboxes and text fields.
*/
FactoryView.prototype.setBaseOptions = function() {
// Set basic options.
document.getElementById('option_css_checkbox').checked = true;
document.getElementById('option_maxBlocks_text').value = Infinity;
document.getElementById('option_media_text').value =
'https://blockly-demo.appspot.com/static/media/';
document.getElementById('option_readOnly_checkbox').checked = false;
document.getElementById('option_rtl_checkbox').checked = false;
document.getElementById('option_sounds_checkbox').checked = true;
// Uncheck grid and zoom options and hide suboptions.
document.getElementById('option_grid_checkbox').checked = false;
document.getElementById('grid_options').style.display = 'none';
document.getElementById('option_zoom_checkbox').checked = false;
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_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;
};
/**
* Updates category specific options depending on if there are categories
* currently present. Updates checkboxes and text fields in the view.
*
* @param {boolean} hasCategories True if categories are present, false if all
* blocks are displayed in a single flyout.
*/
FactoryView.prototype.setCategoryOptions = function(hasCategories) {
document.getElementById('option_collapse_checkbox').checked = hasCategories;
document.getElementById('option_comments_checkbox').checked = hasCategories;
document.getElementById('option_disable_checkbox').checked = hasCategories;
document.getElementById('option_scrollbars_checkbox').checked = hasCategories;
document.getElementById('option_trashcan_checkbox').checked = hasCategories;
}