diff --git a/demos/blocklyfactory/factory.css b/demos/blocklyfactory/factory.css index 6118816f0..704b8de4d 100644 --- a/demos/blocklyfactory/factory.css +++ b/demos/blocklyfactory/factory.css @@ -478,6 +478,10 @@ td { z-index: -1; /* Start behind workspace */ } +#grid_options, #zoom_options, #maxBlockNumber_option { + padding-left: 15px; +} + /* Rules for Closure popup color picker */ .goog-palette { outline: none; diff --git a/demos/blocklyfactory/index.html b/demos/blocklyfactory/index.html index 6c5aadf5e..b002b4061 100644 --- a/demos/blocklyfactory/index.html +++ b/demos/blocklyfactory/index.html @@ -217,36 +217,42 @@ diff --git a/demos/blocklyfactory/workspacefactory/wfactory_controller.js b/demos/blocklyfactory/workspacefactory/wfactory_controller.js index 9e8a37655..af2f9b695 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_controller.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_controller.js @@ -57,7 +57,7 @@ WorkspaceFactoryController = function(toolboxName, toolboxDiv, previewDiv) { colour: '#ccc', snap: true}, media: '../../media/', - toolbox: this.toolbox, + toolbox: this.toolbox }); // Workspace for user to preview their changes. @@ -99,6 +99,36 @@ WorkspaceFactoryController.MODE_PRELOAD = 'preload'; */ WorkspaceFactoryController.prototype.addCategory = function() { this.allowToTransferFlyoutBlocksToCategory(); + // Check if it's the first category added. + var isFirstCategory = !this.model.hasElements(); + // Give the option to save blocks if their workspace is not empty and they + // are creating their first category. + if (isFirstCategory && this.toolboxWorkspace.getAllBlocks().length > 0) { + var confirmCreate = confirm('Do you want to save your work in another ' + + 'category? If you don\'t, the blocks in your workspace will be ' + + 'deleted.'); + + // Create a new category for current blocks. + if (confirmCreate) { + var name = prompt('Enter the name of the category for your ' + + 'current blocks: '); + if (!name) { // Exit if cancelled. + return; + } + + // Create the new category. + this.createCategory(name, true); + // Set the new category as selected. + var id = this.model.getCategoryIdByName(name); + this.model.setSelectedById(id); + this.view.setCategoryTabSelection(id, true); + // Set default options if switching from single flyout to categories. + this.view.setCategoryOptions(this.model.hasElements()); + this.generateNewOptions(); + // Update preview here in case exit early. + this.updatePreview(); + } + } // After possibly creating a category, check again if it's the first category. var isFirstCategory = !this.model.hasElements(); @@ -112,10 +142,11 @@ WorkspaceFactoryController.prototype.addCategory = function() { // Switch to category. this.switchElement(this.model.getCategoryIdByName(name)); - // Allow the user to use the default options for injecting the workspace + // Sets the default options for injecting the workspace // when there are categories if adding the first category. if (isFirstCategory) { - this.allowToSetDefaultOptions(); + this.view.setCategoryOptions(this.model.hasElements()); + this.generateNewOptions(); } // Update preview. this.updatePreview(); @@ -238,9 +269,6 @@ WorkspaceFactoryController.prototype.removeElement = function() { this.toolboxWorkspace.clear(); this.toolboxWorkspace.clearUndo(); this.model.createDefaultSelectedIfEmpty(); - // Allow the user to use the default options for injecting the workspace - // when there are no categories. - this.allowToSetDefaultOptions(); } // Update preview. this.updatePreview(); @@ -371,9 +399,8 @@ WorkspaceFactoryController.prototype.exportOptionsFile = function() { // Generate new options to remove toolbox XML from options object (if // necessary). this.generateNewOptions(); - // TODO(evd2014): Use Regex to prettify JSON generated. - var data = new Blob([JSON.stringify(this.model.options)], - {type: 'text/javascript'}); + var printableOptions = this.generator.generateOptionsString() + var data = new Blob([printableOptions], {type: 'text/javascript'}); this.view.createAndDownloadFile(fileName, data); }; @@ -465,8 +492,9 @@ WorkspaceFactoryController.prototype.saveStateFromWorkspace = function() { */ WorkspaceFactoryController.prototype.reinjectPreview = function(tree) { this.previewWorkspace.dispose(); - this.model.setOptionsAttribute('toolbox', Blockly.Xml.domToPrettyText(tree)); - this.previewWorkspace = Blockly.inject('preview_blocks', this.model.options); + var injectOptions = this.readOptions_(); + injectOptions['toolbox'] = Blockly.Xml.domToPrettyText(tree); + this.previewWorkspace = Blockly.inject('preview_blocks', injectOptions); Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(), this.previewWorkspace); }; @@ -630,7 +658,8 @@ WorkspaceFactoryController.prototype.loadCategoryByName = function(name) { if (isFirstCategory) { // Allow the user to use the default options for injecting the workspace // when there are categories. - this.allowToSetDefaultOptions(); + this.view.setCategoryOptions(this.model.hasElements()); + this.generateNewOptions(); } // Update preview. this.updatePreview(); @@ -795,9 +824,11 @@ WorkspaceFactoryController.prototype.importToolboxFromTree_ = function(tree) { (this.model.getSelectedId()), this.model.getSelected()); this.saveStateFromWorkspace(); - // Allow the user to set default configuration options for a single flyout - // or multiple categories. - this.allowToSetDefaultOptions(); + + // Set default configuration options for a single flyout or multiple + // categories. + this.view.setCategoryOptions(this.model.hasElements()); + this.generateNewOptions(); this.updatePreview(); }; @@ -861,9 +892,8 @@ WorkspaceFactoryController.prototype.clearAll = function() { this.toolboxWorkspace.clear(); this.toolboxWorkspace.clearUndo(); this.saveStateFromWorkspace(); - if (hasCategories) { - this.allowToSetDefaultOptions(); - } + this.view.setCategoryOptions(this.model.hasElements()); + this.generateNewOptions(); this.updatePreview(); }; @@ -1047,35 +1077,27 @@ WorkspaceFactoryController.prototype.setStandardOptionsAndUpdate = function() { this.generateNewOptions(); }; -/** - * Asks the user if they want to use default configuration options specific - * to categories or a single flyout of blocks. If desired, makes the necessary - * changes to the options object depending on if there are categories and then - * updates the preview workspace. Only updates category/flyout specific - * options, not the base default options that are set regardless of if - * categories or a single flyout are used. - */ -WorkspaceFactoryController.prototype.allowToSetDefaultOptions = function() { - if (!this.model.hasElements() && !confirm('Do you want to use the default ' + - 'workspace configuration options for injecting a workspace without ' + - 'categories?')) { - return; - } else if (this.model.hasElements() && !confirm('Do you want to use the ' + - 'default workspace configuration options for injecting a workspace ' + - 'with categories?')) { - return; - } - this.view.setCategoryOptions(this.model.hasElements()); - this.generateNewOptions(); -}; - /** * Generates a new options object for injecting a Blockly workspace based * on user input. Should be called every time a change has been made to * an input field. Updates the model and reinjects the preview workspace. */ WorkspaceFactoryController.prototype.generateNewOptions = function() { - var optionsObj = new Object(null); + this.model.setOptions(this.readOptions_()); + + this.reinjectPreview(Blockly.Options.parseToolboxTree + (this.generator.generateToolboxXml())); +}; + +/** + * Generates a new options object for injecting a Blockly workspace based on + * user input. + * @private + * + * @return {!Object} Blockly injection options object. + */ +WorkspaceFactoryController.prototype.readOptions_ = function() { + var optionsObj = Object.create(null); // Add all standard options to the options object. // Use parse int to get numbers from value inputs. @@ -1086,8 +1108,14 @@ WorkspaceFactoryController.prototype.generateNewOptions = function() { optionsObj['css'] = document.getElementById('option_css_checkbox').checked; optionsObj['disable'] = document.getElementById('option_disable_checkbox').checked; - optionsObj['maxBlocks'] = - parseInt(document.getElementById('option_maxBlocks_text').value); + if (document.getElementById('option_infiniteBlocks_checkbox').checked) { + optionsObj['maxBlocks'] = Infinity; + } else { + var maxBlocksValue = + document.getElementById('option_maxBlocks_number').value; + optionsObj['maxBlocks'] = typeof maxBlocksValue == 'string' ? + parseInt(maxBlocksValue) : maxBlocksValue; + } optionsObj['media'] = document.getElementById('option_media_text').value; optionsObj['readOnly'] = document.getElementById('option_readOnly_checkbox').checked; @@ -1096,16 +1124,21 @@ WorkspaceFactoryController.prototype.generateNewOptions = function() { document.getElementById('option_scrollbars_checkbox').checked; optionsObj['sounds'] = document.getElementById('option_sounds_checkbox').checked; - optionsObj['trashcan'] = - document.getElementById('option_trashcan_checkbox').checked; + if (!optionsObj['readOnly']) { + optionsObj['trashcan'] = + document.getElementById('option_trashcan_checkbox').checked; + } // If using a grid, add all grid options. if (document.getElementById('option_grid_checkbox').checked) { - var grid = new Object(null); - grid['spacing'] = - parseInt(document.getElementById('gridOption_spacing_text').value); - grid['length'] = - parseInt(document.getElementById('gridOption_length_text').value); + var grid = Object.create(null); + var spacingValue = + document.getElementById('gridOption_spacing_number').value; + grid['spacing'] = typeof spacingValue == 'string' ? + parseInt(spacingValue) : spacingValue; + var lengthValue = document.getElementById('gridOption_length_number').value; + grid['length'] = typeof lengthValue == 'string' ? + parseInt(lengthValue) : lengthValue; grid['colour'] = document.getElementById('gridOption_colour_text').value; grid['snap'] = document.getElementById('gridOption_snap_checkbox').checked; optionsObj['grid'] = grid; @@ -1113,26 +1146,31 @@ WorkspaceFactoryController.prototype.generateNewOptions = function() { // If using zoom, add all zoom options. if (document.getElementById('option_zoom_checkbox').checked) { - var zoom = new Object(null); + var zoom = Object.create(null); zoom['controls'] = document.getElementById('zoomOption_controls_checkbox').checked; zoom['wheel'] = document.getElementById('zoomOption_wheel_checkbox').checked; - zoom['startScale'] = - parseInt(document.getElementById('zoomOption_startScale_text').value); - zoom['maxScale'] = - parseInt(document.getElementById('zoomOption_maxScale_text').value); - zoom['minScale'] = - parseInt(document.getElementById('zoomOption_minScale_text').value); - zoom['scaleSpeed'] = - parseInt(document.getElementById('zoomOption_scaleSpeed_text').value); + var startScaleValue = + document.getElementById('zoomOption_startScale_number').value; + zoom['startScale'] = typeof startScaleValue == 'string' ? + parseFloat(startScaleValue) : startScaleValue; + var maxScaleValue = + document.getElementById('zoomOption_maxScale_number').value; + zoom['maxcale'] = typeof maxScaleValue == 'string' ? + parseFloat(maxScaleValue) : maxScaleValue; + var minScaleValue = + document.getElementById('zoomOption_minScale_number').value; + zoom['minScale'] = typeof minScaleValue == 'string' ? + parseFloat(minScaleValue) : minScaleValue; + var scaleSpeedValue = + document.getElementById('zoomOption_scaleSpeed_number').value; + zoom['startScale'] = typeof startScaleValue == 'string' ? + parseFloat(scaleSpeedValue) : scaleSpeedValue; optionsObj['zoom'] = zoom; } - this.model.setOptions(optionsObj); - - this.reinjectPreview(Blockly.Options.parseToolboxTree - (this.generator.generateToolboxXml())); + return optionsObj; }; /** @@ -1156,7 +1194,8 @@ WorkspaceFactoryController.prototype.importBlocks = reader.onload = function() { try { // Define blocks using block types from file. - var blockTypes = FactoryUtils.defineAndGetBlockTypes(reader.result, format); + var blockTypes = FactoryUtils.defineAndGetBlockTypes(reader.result, + format); var blocks = controller.generator.getDefinedBlocks(blockTypes); // Generate category XML and append to toolbox. diff --git a/demos/blocklyfactory/workspacefactory/wfactory_generator.js b/demos/blocklyfactory/workspacefactory/wfactory_generator.js index 37725b0c2..8c2fa91d7 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_generator.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_generator.js @@ -118,6 +118,8 @@ WorkspaceFactoryGenerator.prototype.generateToolboxXml = function() { * it includes XY and ID attributes). Uses a workspace and converts user * generated shadow blocks to actual shadow blocks. * + * @return {!Element} XML element representing toolbox or flyout corresponding + * to toolbox workspace. */ WorkspaceFactoryGenerator.prototype.generateWorkspaceXml = function() { // Load workspace XML to hidden workspace with user-generated shadow blocks @@ -133,6 +135,38 @@ WorkspaceFactoryGenerator.prototype.generateWorkspaceXml = function() { return generatedXml; }; +/** + * Generates a string representation of the options object for injecting the + * workspace. + * + * @return {!string} String representation of options object. + */ +WorkspaceFactoryGenerator.prototype.generateOptionsString = function() { + + var addAttributes = function(obj, tabChar) { + if (!obj) { + return '{}\n'; + } + var str = ''; + for (var key in obj) { + if (key == 'grid' || key == 'zoom') { + var temp = tabChar + key + ' : {\n' + addAttributes(obj[key], + tabChar + '\t') + tabChar + '}, \n'; + } else if (typeof obj[key] == 'string') { + var temp = tabChar + key + ' : \'' + obj[key] + '\', \n'; + } else { + var temp = tabChar + key + ' : ' + obj[key] + ', \n'; + } + str = str.concat(temp); + } + var lastCommaIndex = str.lastIndexOf(','); + str = str.slice(0, lastCommaIndex) + '\n'; + return str; + }; + + return 'var options = { \n' + addAttributes(this.model.options, '\t') + '};'; +} + /** * Loads the given XML to the hidden workspace and sets any user-generated * shadow blocks to be actual shadow blocks. diff --git a/demos/blocklyfactory/workspacefactory/wfactory_init.js b/demos/blocklyfactory/workspacefactory/wfactory_init.js index db10c2163..212c3e04f 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_init.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_init.js @@ -351,6 +351,11 @@ document.getElementById('button_importBlocks').addEventListener ('click', function() { controller.setStandardOptionsAndUpdate(); }); + + document.getElementById('button_optionsHelp').addEventListener + ('click', function() { + open('https://developers.google.com/blockly/guides/get-started/web'); + }); }; /** @@ -578,6 +583,20 @@ WorkspaceFactoryInit.addWorkspaceFactoryOptionsListeners_ = 'block' : 'none'; }); + document.getElementById('option_readOnly_checkbox').addEventListener('change', + function(e) { + document.getElementById('trashcan_option').style.display = + document.getElementById('option_readOnly_checkbox').checked ? + 'none' : 'block'; + }); + + document.getElementById('option_infiniteBlocks_checkbox').addEventListener('change', + function(e) { + document.getElementById('maxBlockNumber_option').style.display = + document.getElementById('option_infiniteBlocks_checkbox').checked ? + 'none' : 'block'; + }); + // Generate new options every time an options input is updated. var optionsElements = document.getElementsByClassName('optionsInput'); for (var i = 0; i < optionsElements.length; i++) { diff --git a/demos/blocklyfactory/workspacefactory/wfactory_model.js b/demos/blocklyfactory/workspacefactory/wfactory_model.js index 8e552d838..ec059354c 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_model.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_model.js @@ -420,16 +420,6 @@ WorkspaceFactoryModel.prototype.setOptions = function(options) { this.options = options; }; -/** - * Sets an attribute of the options object. - * - * @param {!string} name Name of the attribute to add. - * @param {Object} value The value of the attribute to add. - */ -WorkspaceFactoryModel.prototype.setOptionsAttribute = function(name, value) { - this.options[name] = value; -}; - /* * Returns an array of all the block types currently being used in the toolbox * and the pre-loaded blocks. No duplicates. diff --git a/demos/blocklyfactory/workspacefactory/wfactory_view.js b/demos/blocklyfactory/workspacefactory/wfactory_view.js index ae8d79dc5..6bfb8f9e8 100644 --- a/demos/blocklyfactory/workspacefactory/wfactory_view.js +++ b/demos/blocklyfactory/workspacefactory/wfactory_view.js @@ -407,7 +407,8 @@ WorkspaceFactoryView.prototype.updateHelpText = function(mode) { WorkspaceFactoryView.prototype.setBaseOptions = function() { // Set basic options. document.getElementById('option_css_checkbox').checked = true; - document.getElementById('option_maxBlocks_text').value = Infinity; + document.getElementById('option_infiniteBlocks_checkbox').checked = true; + document.getElementById('option_maxBlocks_number').value = 100; document.getElementById('option_media_text').value = 'https://blockly-demo.appspot.com/static/media/'; document.getElementById('option_readOnly_checkbox').checked = false; @@ -421,18 +422,18 @@ WorkspaceFactoryView.prototype.setBaseOptions = function() { document.getElementById('zoom_options').style.display = 'none'; // Set grid options. - document.getElementById('gridOption_spacing_text').value = 0; - document.getElementById('gridOption_length_text').value = 1; + document.getElementById('gridOption_spacing_number').value = 0; + document.getElementById('gridOption_length_number').value = 1; document.getElementById('gridOption_colour_text').value = '#888'; document.getElementById('gridOption_snap_checkbox').checked = false; // Set zoom options. - document.getElementById('zoomOption_controls_checkbox').checked = false; - document.getElementById('zoomOption_wheel_checkbox').checked = false; - document.getElementById('zoomOption_startScale_text').value = 1.0; - document.getElementById('zoomOption_maxScale_text').value = 3; - document.getElementById('zoomOption_minScale_text').value = 0.3; - document.getElementById('zoomOption_scaleSpeed_text').value = 1.2; + document.getElementById('zoomOption_controls_checkbox').checked = true; + document.getElementById('zoomOption_wheel_checkbox').checked = true; + document.getElementById('zoomOption_startScale_number').value = 1.0; + document.getElementById('zoomOption_maxScale_number').value = 3; + document.getElementById('zoomOption_minScale_number').value = 0.3; + document.getElementById('zoomOption_scaleSpeed_number').value = 1.2; }; /**