diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..5131e6201 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,10 @@ +*_compressed*.js +*_uncompressed*.js +/msg/* +/core/css.js +/tests/jsunit/* +/tests/generators/* +/generators/* +/demos/* +/accessible/* +/appengine/* \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..bba9e1ba2 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,28 @@ +{ + "rules": { + "curly": ["error", "multi-line"], + "eol-last": ["error"], + "indent": ["error", 2, {"SwitchCase": 1}], # Blockly/Google use 2-space indents + "linebreak-style": ["error", "unix"], + "max-len": ["error", 120, 4], + "no-trailing-spaces": ["error", { "skipBlankLines": true }], + "no-unused-vars": ["error", {"args": "after-used", "varsIgnorePattern": "^_"}], + "no-use-before-define": ["error"], + "quotes": ["off"], # Blockly mixes single and double quotes + "semi": ["error", "always"], + "space-before-function-paren": ["error", "never"], # Blockly doesn't have space before function paren + "strict": ["off"], # Blockly uses 'use strict' in files + "no-cond-assign": ["off"], # Blockly often uses cond-assignment in loops + "no-redeclare": ["off"], # Closure style allows redeclarations + "valid-jsdoc": ["error", {"requireReturn": false}], + "no-console": ["off"] + }, + "env": { + "browser": true + }, + "globals": { + "Blockly": true, # Blockly global + "goog": true # goog closure libraries/includes + }, + "extends": "eslint:recommended" +} diff --git a/blocks/lists.js b/blocks/lists.js index 301764564..af0f6624a 100644 --- a/blocks/lists.js +++ b/blocks/lists.js @@ -618,17 +618,19 @@ Blockly.Blocks['lists_getSublist'] = { } var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n], function(value) { - var newAt = (value == 'FROM_START') || (value == 'FROM_END'); - // The 'isAt' variable is available due to this function being a closure. - if (newAt != isAt) { - var block = this.sourceBlock_; - block.updateAt_(n, newAt); - // This menu has been destroyed and replaced. Update the replacement. - block.setFieldValue(value, 'WHERE' + n); - return null; - } - return undefined; - }); + var newAt = (value == 'FROM_START') || (value == 'FROM_END'); + // The 'isAt' variable is available due to this function being a + // closure. + if (newAt != isAt) { + var block = this.sourceBlock_; + block.updateAt_(n, newAt); + // This menu has been destroyed and replaced. + // Update the replacement. + block.setFieldValue(value, 'WHERE' + n); + return null; + } + return undefined; + }); this.getInput('AT' + n) .appendField(menu, 'WHERE' + n); if (n == 1) { diff --git a/blocks/logic.js b/blocks/logic.js index 5dab77d86..6d34cb14d 100644 --- a/blocks/logic.js +++ b/blocks/logic.js @@ -271,21 +271,23 @@ Blockly.Blocks['logic_compare'] = { * @this Blockly.Block */ init: function() { - var OPERATORS = this.RTL ? [ - ['=', 'EQ'], - ['\u2260', 'NEQ'], - ['>', 'LT'], - ['\u2265', 'LTE'], - ['<', 'GT'], - ['\u2264', 'GTE'] - ] : [ - ['=', 'EQ'], - ['\u2260', 'NEQ'], - ['<', 'LT'], - ['\u2264', 'LTE'], - ['>', 'GT'], - ['\u2265', 'GTE'] - ]; + var rtlOperators = [ + ['=', 'EQ'], + ['\u2260', 'NEQ'], + ['>', 'LT'], + ['\u2265', 'LTE'], + ['<', 'GT'], + ['\u2264', 'GTE'] + ]; + var ltrOperators = [ + ['=', 'EQ'], + ['\u2260', 'NEQ'], + ['<', 'LT'], + ['\u2264', 'LTE'], + ['>', 'GT'], + ['\u2265', 'GTE'] + ]; + var OPERATORS = this.RTL ? rtlOperators : ltrOperators; this.setHelpUrl(Blockly.Msg.LOGIC_COMPARE_HELPURL); this.setColour(Blockly.Blocks.logic.HUE); this.setOutput(true, 'Boolean'); diff --git a/blocks/procedures.js b/blocks/procedures.js index 0b7ad45e2..f6b25bcd6 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -64,7 +64,7 @@ Blockly.Blocks['procedures_defnoreturn'] = { * inconsistent as a result of the XML loading. * @this Blockly.Block */ - validate: function () { + validate: function() { var name = Blockly.Procedures.findLegalName( this.getFieldValue('NAME'), this); this.setFieldValue(name, 'NAME'); diff --git a/blocks/text.js b/blocks/text.js index f8b56d0d2..030940add 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -498,17 +498,20 @@ Blockly.Blocks['text_getSubstring'] = { } var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n], function(value) { - var newAt = (value == 'FROM_START') || (value == 'FROM_END'); - // The 'isAt' variable is available due to this function being a closure. - if (newAt != isAt) { - var block = this.sourceBlock_; - block.updateAt_(n, newAt); - // This menu has been destroyed and replaced. Update the replacement. - block.setFieldValue(value, 'WHERE' + n); - return null; - } - return undefined; - }); + var newAt = (value == 'FROM_START') || (value == 'FROM_END'); + // The 'isAt' variable is available due to this function being a + // closure. + if (newAt != isAt) { + var block = this.sourceBlock_; + block.updateAt_(n, newAt); + // This menu has been destroyed and replaced. + // Update the replacement. + block.setFieldValue(value, 'WHERE' + n); + return null; + } + return undefined; + }); + this.getInput('AT' + n) .appendField(menu, 'WHERE' + n); if (n == 1) { diff --git a/core/block_render_svg.js b/core/block_render_svg.js index 7535db4a6..aa17738c8 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -303,6 +303,7 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) { */ Blockly.BlockSvg.prototype.renderFields_ = function(fieldList, cursorX, cursorY) { + /* eslint-disable indent */ cursorY += Blockly.BlockSvg.INLINE_PADDING_Y; if (this.RTL) { cursorX = -cursorX; @@ -329,7 +330,7 @@ Blockly.BlockSvg.prototype.renderFields_ = } } return this.RTL ? -cursorX : cursorX; -}; +}; /* eslint-enable indent */ /** * Computes the height and widths for each row and field. @@ -502,7 +503,7 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { var prevBlock = this.previousConnection.targetBlock(); if (prevBlock && prevBlock.getNextBlock() == this) { this.squareTopLeftCorner_ = true; - } + } } else if (Blockly.BlockSvg.START_HAT) { // No output or previous connection. this.squareTopLeftCorner_ = true; @@ -534,7 +535,7 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { var cursorY = this.renderDrawRight_(steps, highlightSteps, inlineSteps, highlightInlineSteps, connectionsXY, inputRows, iconWidth); this.renderDrawBottom_(steps, highlightSteps, connectionsXY, cursorY); - this.renderDrawLeft_(steps, highlightSteps, connectionsXY, cursorY); + this.renderDrawLeft_(steps, highlightSteps, connectionsXY); var pathString = steps.join(' ') + '\n' + inlineSteps.join(' '); this.svgPath_.setAttribute('d', pathString); @@ -559,6 +560,7 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { */ Blockly.BlockSvg.prototype.renderDrawTop_ = function(steps, highlightSteps, connectionsXY, rightEdge) { + /* eslint-disable indent */ // Position the cursor at the top-left starting point. if (this.squareTopLeftCorner_) { steps.push('m 0,0'); @@ -595,7 +597,7 @@ Blockly.BlockSvg.prototype.renderDrawTop_ = steps.push('H', rightEdge); highlightSteps.push('H', rightEdge - 0.5); this.width = rightEdge; -}; +}; /* eslint-enable indent */ /** * Render the right edge of the block. @@ -871,6 +873,7 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, */ Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps, highlightSteps, connectionsXY, cursorY) { + /* eslint-disable indent */ this.height += cursorY + 1; // Add one for the shadow. if (this.nextConnection) { steps.push('H', (Blockly.BlockSvg.NOTCH_WIDTH + (this.RTL ? 0.5 : - 0.5)) + @@ -910,18 +913,18 @@ Blockly.BlockSvg.prototype.renderDrawBottom_ = '0.5,' + (cursorY - Blockly.BlockSvg.CORNER_RADIUS)); } } -}; +}; /* eslint-enable indent */ /** * Render the left edge of the block. * @param {!Array.} steps Path of block outline. * @param {!Array.} highlightSteps Path of block highlights. * @param {!Object} connectionsXY Location of block. - * @param {number} cursorY Height of block. * @private */ Blockly.BlockSvg.prototype.renderDrawLeft_ = - function(steps, highlightSteps, connectionsXY, cursorY) { + function(steps, highlightSteps, connectionsXY) { + /* eslint-disable indent */ if (this.outputConnection) { // Create output connection. this.outputConnection.moveTo(connectionsXY.x, connectionsXY.y); @@ -951,4 +954,4 @@ Blockly.BlockSvg.prototype.renderDrawLeft_ = } } steps.push('z'); -}; +}; /* eslint-enable indent */ diff --git a/core/block_svg.js b/core/block_svg.js index 2ae02fc5c..ef39321dc 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -263,14 +263,14 @@ Blockly.BlockSvg.terminateDrag_ = function() { // Ensure that any stap and bump are part of this move's event group. var group = Blockly.Events.getGroup(); setTimeout(function() { - Blockly.Events.setGroup(group); - selected.snapToGrid(); - Blockly.Events.setGroup(false); + Blockly.Events.setGroup(group); + selected.snapToGrid(); + Blockly.Events.setGroup(false); }, Blockly.BUMP_DELAY / 2); setTimeout(function() { - Blockly.Events.setGroup(group); - selected.bumpNeighbours_(); - Blockly.Events.setGroup(false); + Blockly.Events.setGroup(group); + selected.bumpNeighbours_(); + Blockly.Events.setGroup(false); }, Blockly.BUMP_DELAY); // Fire an event to allow scrollbars to resize. Blockly.asyncSvgResize(this.workspace); @@ -1461,6 +1461,7 @@ Blockly.BlockSvg.prototype.setColour = function(colour) { */ Blockly.BlockSvg.prototype.setPreviousStatement = function(newBoolean, opt_check) { + /* eslint-disable indent */ Blockly.BlockSvg.superClass_.setPreviousStatement.call(this, newBoolean, opt_check); @@ -1468,7 +1469,7 @@ Blockly.BlockSvg.prototype.setPreviousStatement = this.render(); this.bumpNeighbours_(); } -}; +}; /* eslint-enable indent */ /** * Set whether another block can chain onto the bottom of this block. diff --git a/core/blockly.js b/core/blockly.js index c528de945..3e9881e04 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -340,9 +340,9 @@ Blockly.longPid_ = 0; Blockly.longStart_ = function(e, uiObject) { Blockly.longStop_(); Blockly.longPid_ = setTimeout(function() { - e.button = 2; // Simulate a right button click. - uiObject.onMouseDown_(e); - }, Blockly.LONGPRESS); + e.button = 2; // Simulate a right button click. + uiObject.onMouseDown_(e); + }, Blockly.LONGPRESS); }; /** diff --git a/core/connection_db.js b/core/connection_db.js index 9d143e367..8b3c3008e 100644 --- a/core/connection_db.js +++ b/core/connection_db.js @@ -111,6 +111,7 @@ Blockly.ConnectionDB.prototype.findConnection = function(conn) { */ Blockly.ConnectionDB.prototype.findPositionForConnection_ = function(connection) { + /* eslint-disable indent */ if (!this.length) { return 0; } @@ -128,7 +129,7 @@ Blockly.ConnectionDB.prototype.findPositionForConnection_ = } } return pointerMin; -}; +}; /* eslint-enable indent */ /** * Remove a connection from the database. Must already exist in DB. diff --git a/core/flyout.js b/core/flyout.js index c7c0b05ed..5dce8223f 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -428,6 +428,7 @@ Blockly.Flyout.prototype.setBackgroundPathVertical_ = function(width, height) { */ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = function(width, height) { + /* eslint-disable indent */ var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP; // Start at top left. var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)]; @@ -460,7 +461,7 @@ Blockly.Flyout.prototype.setBackgroundPathHorizontal_ = path.push('z'); } this.svgBackground_.setAttribute('d', path.join(' ')); -}; +}; /* eslint-enable indent */ /** * Scroll the flyout to the top. @@ -570,12 +571,13 @@ Blockly.Flyout.prototype.show = function(xmlList) { // IE 11 is an incompetant browser that fails to fire mouseout events. // When the mouse is over the background, deselect all blocks. - var deselectAll = function(e) { + var deselectAll = function() { var topBlocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; block = topBlocks[i]; i++) { block.removeSelect(); } }; + this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', this, deselectAll)); diff --git a/core/mutator.js b/core/mutator.js index 358b90f36..a804b463f 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -306,9 +306,9 @@ Blockly.Mutator.prototype.workspaceChanged_ = function() { // Ensure that any bump is part of this mutation's event group. var group = Blockly.Events.getGroup(); setTimeout(function() { - Blockly.Events.setGroup(group); - block.bumpNeighbours_(); - Blockly.Events.setGroup(false); + Blockly.Events.setGroup(group); + block.bumpNeighbours_(); + Blockly.Events.setGroup(false); }, Blockly.BUMP_DELAY); } if (block.rendered) { diff --git a/core/options.js b/core/options.js index d8da08b6b..4254fe859 100644 --- a/core/options.js +++ b/core/options.js @@ -134,7 +134,7 @@ Blockly.Options.prototype.parentWorkspace = null; * If set, sets the translation of the workspace to match the scrollbars. * No-op if unset. */ -Blockly.Options.prototype.setMetrics = function(translation) { return; }; +Blockly.Options.prototype.setMetrics = function() { return; }; /** * Return an object with the metrics required to size the workspace, or null diff --git a/core/rendered_connection.js b/core/rendered_connection.js index 9c3d90cfc..113639e61 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -164,10 +164,7 @@ Blockly.RenderedConnection.prototype.closest = function(maxLimit, dx, dy) { Blockly.RenderedConnection.prototype.highlight = function() { var steps; if (this.type == Blockly.INPUT_VALUE || this.type == Blockly.OUTPUT_VALUE) { - var tabWidth = this.sourceBlock_.RTL ? -Blockly.BlockSvg.TAB_WIDTH : - Blockly.BlockSvg.TAB_WIDTH; steps = 'm 0,0 ' + Blockly.BlockSvg.TAB_PATH_DOWN + ' v 5'; - } else { steps = 'm -20,0 h 5 ' + Blockly.BlockSvg.NOTCH_PATH_LEFT + ' h 5'; } diff --git a/core/toolbox.js b/core/toolbox.js index d89d06fde..2eb9cbb54 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -583,6 +583,9 @@ Blockly.Toolbox.TreeNode.prototype.onDoubleClick_ = function(e) { /** * A blank separator node in the tree. + * @param {Object=} config The configuration for the tree. See + * goog.ui.tree.TreeControl.DefaultConfig. If not specified, a default config + * will be used. * @constructor * @extends {Blockly.Toolbox.TreeNode} */ diff --git a/core/tooltip.js b/core/tooltip.js index 6e520e65b..b4ded3772 100644 --- a/core/tooltip.js +++ b/core/tooltip.js @@ -167,10 +167,10 @@ Blockly.Tooltip.onMouseOut_ = function(e) { // event and kill it if a mouseOver is received immediately. // This way the task only fully executes if mousing into the void. Blockly.Tooltip.mouseOutPid_ = setTimeout(function() { - Blockly.Tooltip.element_ = null; - Blockly.Tooltip.poisonedElement_ = null; - Blockly.Tooltip.hide(); - }, 1); + Blockly.Tooltip.element_ = null; + Blockly.Tooltip.poisonedElement_ = null; + Blockly.Tooltip.hide(); + }, 1); clearTimeout(Blockly.Tooltip.showPid_); }; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index c2c46ad00..a1788868e 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -290,7 +290,7 @@ Blockly.WorkspaceSvg.prototype.addFlyout_ = function() { parentWorkspace: this, RTL: this.RTL, horizontalLayout: this.horizontalLayout, - toolboxPosition: this.options.toolboxPosition, + toolboxPosition: this.options.toolboxPosition }; /** @type {Blockly.Flyout} */ this.flyout_ = new Blockly.Flyout(workspaceOptions); @@ -820,18 +820,7 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) { for (var i = 0; i < topBlocks.length; i++) { addDeletableBlocks(topBlocks[i]); } - var deleteOption = { - text: deleteList.length == 1 ? Blockly.Msg.DELETE_BLOCK : - Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(deleteList.length)), - enabled: deleteList.length > 0, - callback: function() { - if (deleteList.length < 2 || - window.confirm(Blockly.Msg.DELETE_ALL_BLOCKS.replace('%1', - String(deleteList.length)))) { - deleteNext(); - } - } - }; + function deleteNext() { Blockly.Events.setGroup(eventGroup); var block = deleteList.shift(); @@ -845,6 +834,19 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) { } Blockly.Events.setGroup(false); } + + var deleteOption = { + text: deleteList.length == 1 ? Blockly.Msg.DELETE_BLOCK : + Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(deleteList.length)), + enabled: deleteList.length > 0, + callback: function() { + if (deleteList.length < 2 || + window.confirm(Blockly.Msg.DELETE_ALL_BLOCKS.replace('%1', + String(deleteList.length)))) { + deleteNext(); + } + } + }; menuOptions.push(deleteOption); Blockly.ContextMenu.show(e, menuOptions, this.RTL);