mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
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:
parent
0d66c77357
commit
06f21f85e4
6 changed files with 827 additions and 164 deletions
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue