From 7e048f2e09957a59e74b8deba8074935008266f3 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 12 Jun 2019 17:28:27 -0700 Subject: [PATCH] Remove last Closure from Block Factory. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improves UI, but is still pretty clunky. For example there’s still not way to choose “no colour”. Closes #668. --- demos/blockfactory/1x1.gif | Bin 0 -> 43 bytes demos/blockfactory/app_controller.js | 16 -- demos/blockfactory/cp.js | 197 ++++++++++++++++++ demos/blockfactory/factory.css | 76 +++---- demos/blockfactory/index.html | 9 +- .../workspacefactory/wfactory_controller.js | 43 ++-- .../workspacefactory/wfactory_init.js | 115 +++------- .../workspacefactory/wfactory_view.js | 4 +- 8 files changed, 267 insertions(+), 193 deletions(-) create mode 100644 demos/blockfactory/1x1.gif create mode 100644 demos/blockfactory/cp.js diff --git a/demos/blockfactory/1x1.gif b/demos/blockfactory/1x1.gif new file mode 100644 index 0000000000000000000000000000000000000000..3085511236caea65b69ad30a68979c9b95f4a425 GIT binary patch literal 43 qcmZ?wbhEHbWMp7uXkY+=|Ns9h{$$}~01D`U_#hbuCMF+725SJojRy(< literal 0 HcmV?d00001 diff --git a/demos/blockfactory/app_controller.js b/demos/blockfactory/app_controller.js index eff016547..13333d16e 100644 --- a/demos/blockfactory/app_controller.js +++ b/demos/blockfactory/app_controller.js @@ -26,8 +26,6 @@ * @author quachtina96 (Tina Quach) */ -goog.require('goog.dom.xml'); // Used to detect Closure - /** * Controller for the Blockly Factory * @constructor @@ -678,20 +676,6 @@ AppController.prototype.modalName_ = null; * Initialize Blockly and layout. Called on page load. */ AppController.prototype.init = function() { - // Block Factory has a dependency on bits of Closure that core Blockly - // doesn't have. When you run this from file:// without a copy of Closure, - // it breaks it non-obvious ways. Warning about this for now until the - // dependency is broken. - // TODO: #668. - if (!window.goog.dom.xml) { - alert('Sorry: Closure dependency not found. We are working on removing ' + - 'this dependency. In the meantime, you can use our hosted demo\n ' + - 'https://blockly-demo.appspot.com/static/demos/blockfactory/index.html' + - '\nor use these instructions to continue running locally:\n' + - 'https://developers.google.com/blockly/guides/modify/web/closure'); - return; - } - var self = this; // Handle Blockly Storage with App Engine. if ('BlocklyStorage' in window) { diff --git a/demos/blockfactory/cp.js b/demos/blockfactory/cp.js new file mode 100644 index 000000000..dce9bdd13 --- /dev/null +++ b/demos/blockfactory/cp.js @@ -0,0 +1,197 @@ +/** + * Colour Picker v1.1 + * + * Copyright 2006 Neil Fraser + * https://neil.fraser.name/software/colourpicker/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Include at the top of your page: +// +// Call with: +// +// + +var cp_grid = [ + ['ffffff', 'ffcccc', 'ffcc99', 'ffff99', 'ffffcc', '99ff99', '99ffff', 'ccffff', 'ccccff', 'ffccff'], + ['cccccc', 'ff6666', 'ff9966', 'ffff66', 'ffff33', '66ff99', '33ffff', '66ffff', '9999ff', 'ff99ff'], + ['c0c0c0', 'ff0000', 'ff9900', 'ffcc66', 'ffff00', '33ff33', '66cccc', '33ccff', '6666cc', 'cc66cc'], + ['999999', 'cc0000', 'ff6600', 'ffcc33', 'ffcc00', '33cc00', '00cccc', '3366ff', '6633ff', 'cc33cc'], + ['666666', '990000', 'cc6600', 'cc9933', '999900', '009900', '339999', '3333ff', '6600cc', '993399'], + ['333333', '660000', '993300', '996633', '666600', '006600', '336666', '000099', '333399', '663366'], + ['000000', '330000', '663300', '663333', '333300', '003300', '003333', '000066', '330099', '330033']]; + +var cp_dom = null; +var cp_caller = null; +var cp_defaultcolour = 'ffffff'; +var cp_closePID = null; + +function cp_init(id) { + // Hide the form element, and replace it with a colour box. + var obj = document.getElementById(id); + if (!obj) { + alert('Colour picker can\'t find "' + id + '"'); + return; + } + if (!cp_hex2rgb(obj.value)) { + alert('Colour picker can\'t parse colour code in "' + id + '"'); + return; + } + if (!obj.cp_box) { + obj.type = 'hidden'; + // + var box = document.createElement('img'); + box.style.border = 'outset 3px #888'; + box.src = '1x1.gif'; + box.height = 20; + box.width = 30; + box.label = id; + box.onclick = new Function('cp_open(this)'); + box.onmouseover = cp_cancelclose; + box.onmouseout = cp_closesoon; + obj.parentNode.insertBefore(box, obj); + obj.cp_box = box; + } + obj.cp_box.style.backgroundColor = '#' + obj.value; +} + +function cp_open(caller) { + // Create a table of colours. + if (cp_dom) { + cp_close(); + return; + } + cp_caller = caller; + // alert(document.getElementById(caller.label)); + cp_defaultcolour = document.getElementById(caller.label).value; + var posX = 0; + var posY = caller.offsetHeight; + while (caller) { + posX += caller.offsetLeft; + posY += caller.offsetTop; + caller = caller.offsetParent; + } + cp_dom = document.createElement('div'); + cp_dom.id = 'colourpicker'; + var table = document.createElement('table'); + table.setAttribute('border', '1'); + table.style.backgroundColor = '#808080'; + table.onmouseover = cp_cancelclose; + table.onmouseout = cp_closesoon; + var tbody = document.createElement('tbody'); // IE 6 needs this. + var row, cell; + for (var y = 0; y < cp_grid.length; y++) { + row = document.createElement('tr'); + tbody.appendChild(row); + for (var x = 0; x < cp_grid[y].length; x++) { + cell = document.createElement('td'); + row.appendChild(cell); + cell.style.backgroundColor = '#' + cp_grid[y][x]; + cell.label = cp_grid[y][x]; + cell.style.border = 'solid 2px #' + cell.label; + cell.onmouseover = cp_onmouseover; + cell.onmouseout = cp_onmouseout; + cell.onclick = cp_onclick; + cell.innerHTML = ''; + if (cp_defaultcolour.toLowerCase() == cp_grid[y][x].toLowerCase()) { + cell.onmouseover(); + cell.onmouseout(); + } + } + } + table.appendChild(tbody); + cp_dom.appendChild(table); + + cp_dom.style.position = 'absolute'; + cp_dom.style.left = '0'; + cp_dom.style.top = '0'; + cp_dom.style.visibility = 'hidden'; + document.body.appendChild(cp_dom); + // Don't widen the screen. + if (posX + cp_dom.offsetWidth > document.body.offsetWidth) { + posX = document.body.offsetWidth - cp_dom.offsetWidth; + } + cp_dom.style.left = posX + 'px'; + cp_dom.style.top = posY + 'px'; + cp_dom.style.visibility = 'visible'; +} + +function cp_close() { + // Close the table now. + cp_cancelclose(); + if (cp_dom) + document.body.removeChild(cp_dom) + cp_dom = null; + cp_caller = null; +} + +function cp_closesoon() { + // Close the table a split-second from now. + cp_closePID = window.setTimeout('cp_close()', 250); +} + +function cp_cancelclose() { + // Don't close the colour table after all. + if (cp_closePID) + window.clearTimeout(cp_closePID); +} + +function cp_onclick() { + // Clicked on a colour. + // Close the table, set the colour, fire an onchange event. + cp_caller.style.backgroundColor = '#' + this.label; + var input = document.getElementById(cp_caller.label) + input.value = this.label; + cp_close(); + if (input.onchange) { + input.onchange(); + } +} + +function cp_onmouseover() { + // Place a black border on the cell if the contents are light, + // a white border if the contents are dark. + this.style.borderStyle = 'dotted'; + var rgb = cp_hex2rgb(this.label); + if (rgb[0] + rgb[1] + rgb[2] > 255 * 3 / 2) + this.style.borderColor = 'black'; + else + this.style.borderColor = 'white'; +} + +function cp_onmouseout() { + // Remove the border. + if (this.label == cp_defaultcolour) { + this.style.borderStyle = 'outset'; + } else { + this.style.border = 'solid 2px #' + this.label; + } +} + +function cp_hex2rgb(hexcode) { + // Parse '0088ff' and return the [r, g, b] ints. + var m = + hexcode.match(/^([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])$/); + if (m) { + var r = parseInt(m[1], 16); + var g = parseInt(m[2], 16); + var b = parseInt(m[3], 16); + return [r, g, b]; + } else { + return null; + } +} diff --git a/demos/blockfactory/factory.css b/demos/blockfactory/factory.css index 6661c139c..aca3d0d24 100644 --- a/demos/blockfactory/factory.css +++ b/demos/blockfactory/factory.css @@ -56,11 +56,11 @@ td { p { display: block; - -webkit-margin-before: 0em; - -webkit-margin-after: 0em; - -webkit-margin-start: 0px; - -webkit-margin-end: 0px; - padding: 5px 0px; + -webkit-margin-before: 0; + -webkit-margin-after: 0; + -webkit-margin-start: 0; + -webkit-margin-end: 0; + padding: 5px 0; } #factoryHeader { @@ -233,7 +233,7 @@ button, .buttonStyle { } .subsettings { - margin: 0px 25px; + margin: 0 25px; } #exporterHiddenWorkspace { @@ -511,53 +511,10 @@ td.taboff:hover { right: 0; bottom: 0; left: 0; - background: rgba(0, 0, 0, 0.05); + background: rgba(0, 0, 0, 0.1); z-index: 100; } -/* Rules for Closure popup color picker */ -.goog-palette { - outline: none; - cursor: default; -} - -.goog-palette-cell { - height: 13px; - width: 15px; - margin: 0; - border: 0; - text-align: center; - vertical-align: middle; - border-right: 1px solid #000; - font-size: 1px; -} - -.goog-palette-colorswatch { - border: 1px solid #000; - height: 13px; - position: relative; - width: 15px; -} - -.goog-palette-cell-hover .goog-palette-colorswatch { - border: 1px solid #fff; -} - -.goog-palette-cell-selected .goog-palette-colorswatch { - border: 1px solid #000; - color: #fff; -} - -.goog-palette-table { - border: 1px solid #000; - border-collapse: collapse; -} - -.goog-popupcolorpicker { - position: absolute; - z-index: 101; /* On top of the modal Shadow. */ -} - /* The container
- needed to position the dropdown content */ .dropdown { display: inline-block; @@ -566,7 +523,7 @@ td.taboff:hover { /* Dropdown Content (Hidden by Default) */ .dropdown-content { background-color: #fff; - box-shadow: 0px 8px 16px 0px rgba(0,0,0,.2); + box-shadow: 0 8px 16px 0 rgba(0,0,0,.2); display: none; min-width: 170px; opacity: 1; @@ -598,6 +555,23 @@ td.taboff:hover { display: block; } +#dropdownDiv_editCategory { + padding: 0 1ex; +} + +#dropdownDiv_editCategory>img { + vertical-align: middle; +} + +#colourpicker { + z-index: 999; +} + +#colourpicker>table { + border-collapse: separate; + z-index: 999; +} + .shadowBlock>.blocklyPath { fill-opacity: .5; stroke-opacity: .5; diff --git a/demos/blockfactory/index.html b/demos/blockfactory/index.html index afdfe01a8..b01ff2857 100644 --- a/demos/blockfactory/index.html +++ b/demos/blockfactory/index.html @@ -8,12 +8,12 @@ - + @@ -208,10 +208,11 @@
diff --git a/demos/blockfactory/workspacefactory/wfactory_controller.js b/demos/blockfactory/workspacefactory/wfactory_controller.js index 61c830870..43f9ae453 100644 --- a/demos/blockfactory/workspacefactory/wfactory_controller.js +++ b/demos/blockfactory/workspacefactory/wfactory_controller.js @@ -491,26 +491,25 @@ WorkspaceFactoryController.prototype.reinjectPreview = function(tree) { }; /** - * Tied to "change name" button. Changes the name of the selected category. - * Continues prompting the user until they input a category name that is not - * currently in use, exits if user presses cancel. + * Changes the name and colour of the selected category. + * Return if selected element is a separator. + * @param {string} name New name for selected category. + * @param {string} colour New colour for selected category. + * Must be a valid CSS string. */ -WorkspaceFactoryController.prototype.changeCategoryName = function() { +WorkspaceFactoryController.prototype.changeSelectedCategory = + function(name, colour) { var selected = this.model.getSelected(); // Return if a category is not selected. if (selected.type != ListElement.TYPE_CATEGORY) { return; } - // Get new name from user. - window.foo = selected; - var newName = this.promptForNewCategoryName('What do you want to change this' - + ' category\'s name to?', selected.name); - if (!newName) { // If cancelled. - return; - } + // Change colour of selected category. + selected.changeColor(colour); + this.view.setBorderColor(this.model.getSelectedId(), colour); // Change category name. - selected.changeName(newName); - this.view.updateCategoryName(newName, this.model.getSelectedId()); + selected.changeName(name); + this.view.updateCategoryName(name, this.model.getSelectedId()); // Update preview. this.updatePreview(); }; @@ -557,24 +556,6 @@ WorkspaceFactoryController.prototype.moveElementToIndex = function(element, this.view.moveTabToIndex(element.id, newIndex, oldIndex); }; -/** - * Changes the color of the selected category. Return if selected element is - * a separator. - * @param {string} color The color to change the selected category. Must be - * a valid CSS string. - */ -WorkspaceFactoryController.prototype.changeSelectedCategoryColor = - function(color) { - // Return if category is not selected. - if (this.model.getSelected().type != ListElement.TYPE_CATEGORY) { - return; - } - // Change color of selected category. - this.model.getSelected().changeColor(color); - this.view.setBorderColor(this.model.getSelectedId(), color); - this.updatePreview(); -}; - /** * Tied to the "Standard Category" dropdown option, this function prompts * the user for a name of a standard Blockly category (case insensitive) and diff --git a/demos/blockfactory/workspacefactory/wfactory_init.js b/demos/blockfactory/workspacefactory/wfactory_init.js index 36ca9fe27..84d420111 100644 --- a/demos/blockfactory/workspacefactory/wfactory_init.js +++ b/demos/blockfactory/workspacefactory/wfactory_init.js @@ -27,9 +27,6 @@ * @author Emma Dauterman (evd2014) */ -goog.require('goog.ui.PopupColorPicker'); -goog.require('goog.ui.ColorPicker'); - /** * Namespace for workspace factory initialization methods. * @namespace @@ -48,7 +45,7 @@ WorkspaceFactoryInit.initWorkspaceFactory = function(controller) { document.getElementById('button_down').disabled = true; document.getElementById('button_editCategory').disabled = true; - this.initColorPicker_(controller); + this.initColourPicker_(controller); this.addWorkspaceFactoryEventListeners_(controller); this.assignWorkspaceFactoryClickHandlers_(controller); this.addWorkspaceFactoryOptionsListeners_(controller); @@ -58,98 +55,25 @@ WorkspaceFactoryInit.initWorkspaceFactory = function(controller) { }; /** - * Initialize the color picker in workspace factory. + * Initialize the colour picker in workspace factory. * @param {!FactoryController} controller The controller for the workspace * factory tab. * @private */ -WorkspaceFactoryInit.initColorPicker_ = function(controller) { - // Array of Blockly category colours, consitent with the 15 degree default +WorkspaceFactoryInit.initColourPicker_ = function(controller) { + // Array of Blockly category colours, consistent with the 15 degree default // of the block factory's colour wheel. var colours = []; + var row = []; for (var hue = 0; hue < 360; hue += 15) { - colours.push(WorkspaceFactoryInit.hsvToHex_(hue, - Blockly.HSV_SATURATION, Blockly.HSV_VALUE)); - } - - // Create color picker with specific set of Blockly colours. - var colourPicker = new goog.ui.ColorPicker(); - colourPicker.setSize(6); - colourPicker.setColors(colours); - - // Create and render the popup colour picker and attach to button. - var popupPicker = new goog.ui.PopupColorPicker(null, colourPicker); - popupPicker.render(); - popupPicker.attach(document.getElementById('dropdown_color')); - popupPicker.setFocusable(true); - goog.events.listen(popupPicker, 'change', function(e) { - controller.changeSelectedCategoryColor(popupPicker.getSelectedColor()); - blocklyFactory.closeModal(); - }); -}; - -/** - * Converts from h,s,v values to a hex string - * @param {number} h Hue, in [0, 360]. - * @param {number} s Saturation, in [0, 1]. - * @param {number} v Value, in [0, 1]. - * @return {string} hex representation of the color. - * @private - */ -WorkspaceFactoryInit.hsvToHex_ = function(h, s, v) { - var brightness = v * 255; - var red = 0; - var green = 0; - var blue = 0; - if (s == 0) { - red = brightness; - green = brightness; - blue = brightness; - } else { - var sextant = Math.floor(h / 60); - var remainder = (h / 60) - sextant; - var val1 = brightness * (1 - s); - var val2 = brightness * (1 - (s * remainder)); - var val3 = brightness * (1 - (s * (1 - remainder))); - switch (sextant) { - case 1: - red = val2; - green = brightness; - blue = val1; - break; - case 2: - red = val1; - green = brightness; - blue = val3; - break; - case 3: - red = val1; - green = val2; - blue = brightness; - break; - case 4: - red = val3; - green = val1; - blue = brightness; - break; - case 5: - red = brightness; - green = val1; - blue = val2; - break; - case 6: - case 0: - red = brightness; - green = val3; - blue = val1; - break; + row.push(Blockly.hueToHex(hue).substring(1)); + if (row.length == 6) { + colours.push(row); + row = []; } } - - var hexR = ('0' + Math.floor(red).toString(16)).slice(-2); - var hexG = ('0' + Math.floor(green).toString(16)).slice(-2); - var hexB = ('0' + Math.floor(blue).toString(16)).slice(-2); - return '#' + hexR + hexG + hexB; + // Override the default colours. + cp_grid = colours; }; /** @@ -311,12 +235,25 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ = document.getElementById('button_editCategory').addEventListener ('click', function() { + var selected = controller.model.getSelected(); + // Return if a category is not selected. + if (selected.type != ListElement.TYPE_CATEGORY) { + return; + } + document.getElementById('categoryName').value = selected.name; + document.getElementById('categoryColour').value = selected.color ? + selected.color.substring(1).toLowerCase() : '000000'; + // Link the colour picker to the field. + cp_init('categoryColour'); blocklyFactory.openModal('dropdownDiv_editCategory'); }); - document.getElementById('dropdown_name').addEventListener + + document.getElementById('categorySave').addEventListener ('click', function() { - controller.changeCategoryName(); + var name = document.getElementById('categoryName').value.trim(); + var colour = '#' + document.getElementById('categoryColour').value; + controller.changeSelectedCategory(name, colour); blocklyFactory.closeModal(); }); diff --git a/demos/blockfactory/workspacefactory/wfactory_view.js b/demos/blockfactory/workspacefactory/wfactory_view.js index f36ae5c17..754b084d6 100644 --- a/demos/blockfactory/workspacefactory/wfactory_view.js +++ b/demos/blockfactory/workspacefactory/wfactory_view.js @@ -320,7 +320,7 @@ WorkspaceFactoryView.prototype.markShadowBlocks = function(blocks) { */ WorkspaceFactoryView.prototype.markShadowBlock = function(block) { // Add Blockly CSS for user-generated shadow blocks. - Blockly.utils.addClass(block.svgGroup_, 'shadowBlock'); + Blockly.utils.dom.addClass(block.svgGroup_, 'shadowBlock'); // If not a valid shadow block, add a warning message. if (!block.getSurroundParent()) { block.setWarningText('Shadow blocks must be nested inside' + @@ -338,7 +338,7 @@ WorkspaceFactoryView.prototype.markShadowBlock = function(block) { */ WorkspaceFactoryView.prototype.unmarkShadowBlock = function(block) { // Remove Blockly CSS for user-generated shadow blocks. - Blockly.utils.removeClass(block.svgGroup_, 'shadowBlock'); + Blockly.utils.dom.removeClass(block.svgGroup_, 'shadowBlock'); }; /**