diff --git a/accessible/README b/accessible/README index 21c2e202..37ea6787 100644 --- a/accessible/README +++ b/accessible/README @@ -5,10 +5,8 @@ Google's Blockly is a web-based, visual programming editor that is accessible to blind users. The code in this directory renders a version of the Blockly toolbox and -workspace that is fully keyboard-navigable, and compatible with JAWS/NVDA -screen readers on Firefox for Windows. (We chose this combination because JAWS -and NVDA are the most robust screen readers, and are compatible with many more -aria tags than other screen readers.) +workspace that is fully keyboard-navigable, and compatible with most screen +readers. In the future, Accessible Blockly may be modified to suit accessibility needs other than visual impairments. Note that deaf users are expected to continue @@ -27,15 +25,16 @@ the main component to be loaded. This will usually be blocklyApp.AppView, but if you have another component that wraps it, use that one instead. -Customizing the Toolbar ------------------------ +Customizing the Toolbar and Audio +--------------------------------- The Accessible Blockly workspace comes with a customizable toolbar. To customize the toolbar, you will need to declare an ACCESSIBLE_GLOBALS object in the global scope that looks like this: var ACCESSIBLE_GLOBALS = { - toolbarButtonConfig: [] + toolbarButtonConfig: [], + mediaPathPrefix: null }; The value corresponding to 'toolbarButtonConfig' can be modified by adding @@ -45,6 +44,9 @@ two keys: - 'text' (the text to display on the button) - 'action' (the function that gets run when the button is clicked) +In addition, if you want audio to be played, set mediaPathPrefix to the +location of the accessible/media folder. + Limitations ----------- diff --git a/accessible/app.component.js b/accessible/app.component.js index 763a13c1..e1c4329d 100644 --- a/accessible/app.component.js +++ b/accessible/app.component.js @@ -29,6 +29,9 @@ blocklyApp.AppView = ng.core .Component({ selector: 'blockly-app', template: ` +
@@ -46,19 +49,29 @@ blocklyApp.AppView = ng.core + + + + - + `, directives: [blocklyApp.ToolboxComponent, blocklyApp.WorkspaceComponent], pipes: [blocklyApp.TranslatePipe], - // The clipboard, tree and utils services are declared here, so that all - // components in the application use the same instance of the service. + // All services are declared here, so that all components in the + // application use the same instance of the service. // https://www.sitepoint.com/angular-2-components-providers-classes-factories-values/ providers: [ - blocklyApp.ClipboardService, blocklyApp.TreeService, - blocklyApp.UtilsService] + blocklyApp.ClipboardService, blocklyApp.NotificationsService, + blocklyApp.TreeService, blocklyApp.UtilsService, + blocklyApp.AudioService] }) .Class({ - constructor: [function() {}] + constructor: [blocklyApp.NotificationsService, function(_notificationsService) { + this.notificationsService = _notificationsService; + }], + getStatusMessage: function() { + return this.notificationsService.getStatusMessage(); + } }); diff --git a/accessible/audio.service.js b/accessible/audio.service.js new file mode 100644 index 00000000..c358c083 --- /dev/null +++ b/accessible/audio.service.js @@ -0,0 +1,57 @@ +/** + * AccessibleBlockly + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Angular2 Service that plays audio files. + * @author sll@google.com (Sean Lip) + */ + +blocklyApp.AudioService = ng.core + .Class({ + constructor: [function() { + // We do not play any audio unless a media path prefix is specified. + this.canPlayAudio = false; + if (ACCESSIBLE_GLOBALS.hasOwnProperty('mediaPathPrefix')) { + this.canPlayAudio = true; + var mediaPathPrefix = ACCESSIBLE_GLOBALS['mediaPathPrefix']; + this.AUDIO_PATHS_ = { + 'connect': mediaPathPrefix + 'click.mp3', + 'delete': mediaPathPrefix + 'delete.mp3' + }; + } + + // TODO(sll): Add ogg and mp3 fallbacks. + this.cachedAudioFiles_ = {}; + }], + play_: function(audioId) { + if (this.canPlayAudio) { + if (!this.cachedAudioFiles_.hasOwnProperty(audioId)) { + this.cachedAudioFiles_[audioId] = new Audio( + this.AUDIO_PATHS_[audioId]); + } + this.cachedAudioFiles_[audioId].play(); + } + }, + playConnectSound: function() { + this.play_('connect'); + }, + playDeleteSound: function() { + this.play_('delete'); + } + }); diff --git a/accessible/clipboard.service.js b/accessible/clipboard.service.js index db65e2d9..05b0afe6 100644 --- a/accessible/clipboard.service.js +++ b/accessible/clipboard.service.js @@ -24,12 +24,19 @@ blocklyApp.ClipboardService = ng.core .Class({ - constructor: function() { + constructor: [ + blocklyApp.NotificationsService, blocklyApp.UtilsService, + blocklyApp.AudioService, + function(_notificationsService, _utilsService, _audioService) { this.clipboardBlockXml_ = null; - this.clipboardBlockSuperiorConnection_ = null; + this.clipboardBlockPreviousConnection_ = null; this.clipboardBlockNextConnection_ = null; + this.clipboardBlockOutputConnection_ = null; this.markedConnection_ = null; - }, + this.notificationsService = _notificationsService; + this.utilsService = _utilsService; + this.audioService = _audioService; + }], areConnectionsCompatible_: function(blockConnection, connection) { // Check that both connections exist, that it's the right kind of // connection, and that the types match. @@ -39,11 +46,20 @@ blocklyApp.ClipboardService = ng.core connection.checkType_(blockConnection)); }, isCompatibleWithClipboard: function(connection) { - var superiorConnection = this.clipboardBlockSuperiorConnection_; + var previousConnection = this.clipboardBlockPreviousConnection_; var nextConnection = this.clipboardBlockNextConnection_; + var outputConnection = this.clipboardBlockOutputConnection_; return Boolean( - this.areConnectionsCompatible_(connection, superiorConnection) || - this.areConnectionsCompatible_(connection, nextConnection)); + this.areConnectionsCompatible_(connection, previousConnection) || + this.areConnectionsCompatible_(connection, nextConnection) || + this.areConnectionsCompatible_(connection, outputConnection)); + }, + getMarkedConnectionBlock: function() { + if (!this.markedConnection_) { + return null; + } else { + return this.markedConnection_.getSourceBlock(); + } }, isMovableToMarkedConnection: function(block) { // It should not be possible to move any ancestor of the block containing @@ -52,7 +68,7 @@ blocklyApp.ClipboardService = ng.core return false; } - var markedSpotAncestorBlock = this.markedConnection_.getSourceBlock(); + var markedSpotAncestorBlock = this.getMarkedConnectionBlock(); while (markedSpotAncestorBlock) { if (markedSpotAncestorBlock.id == block.id) { return false; @@ -82,24 +98,28 @@ blocklyApp.ClipboardService = ng.core }, markConnection: function(connection) { this.markedConnection_ = connection; - alert(Blockly.Msg.MARKED_SPOT_MSG); + this.notificationsService.setStatusMessage(Blockly.Msg.MARKED_SPOT_MSG); }, cut: function(block) { - var blockSummary = block.toString(); - this.copy(block, false); + this.copy(block); block.dispose(true); - alert(Blockly.Msg.CUT_BLOCK_MSG + blockSummary); }, - copy: function(block, announce) { + copy: function(block) { this.clipboardBlockXml_ = Blockly.Xml.blockToDom(block); - this.clipboardBlockSuperiorConnection_ = block.outputConnection || - block.previousConnection; + this.clipboardBlockPreviousConnection_ = block.previousConnection; this.clipboardBlockNextConnection_ = block.nextConnection; - if (announce) { - alert(Blockly.Msg.COPIED_BLOCK_MSG + block.toString()); - } + this.clipboardBlockOutputConnection_ = block.outputConnection; }, - pasteFromClipboard: function(connection) { + pasteFromClipboard: function(inputConnection) { + var connection = inputConnection; + // If the connection is a 'previousConnection' and that connection is + // already joined to something, use the 'nextConnection' of the + // previous block instead in order to do an insertion. + if (inputConnection.type == Blockly.PREVIOUS_STATEMENT && + inputConnection.isConnected()) { + connection = inputConnection.targetConnection; + } + var reconstitutedBlock = Blockly.Xml.domToBlock(blocklyApp.workspace, this.clipboardBlockXml_); switch (connection.type) { @@ -112,11 +132,13 @@ blocklyApp.ClipboardService = ng.core default: connection.connect(reconstitutedBlock.outputConnection); } - alert( - Blockly.Msg.PASTED_BLOCK_FROM_CLIPBOARD_MSG + - reconstitutedBlock.toString()); + this.audioService.playConnectSound(); + this.notificationsService.setStatusMessage( + this.utilsService.getBlockDescription(reconstitutedBlock) + ' ' + + Blockly.Msg.PASTED_BLOCK_FROM_CLIPBOARD_MSG); + return reconstitutedBlock.id; }, - pasteToMarkedConnection: function(block, announce) { + pasteToMarkedConnection: function(block) { var xml = Blockly.Xml.blockToDom(block); var reconstitutedBlock = Blockly.Xml.domToBlock( blocklyApp.workspace, xml); @@ -132,6 +154,7 @@ blocklyApp.ClipboardService = ng.core if (this.areConnectionsCompatible_( this.markedConnection_, potentialConnections[i])) { this.markedConnection_.connect(potentialConnections[i]); + this.audioService.playConnectSound(); connectionSuccessful = true; break; } @@ -142,12 +165,8 @@ blocklyApp.ClipboardService = ng.core return; } - if (announce) { - alert( - Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG + - reconstitutedBlock.toString()); - } - this.markedConnection_ = null; + + return reconstitutedBlock.id; } }); diff --git a/accessible/field.component.js b/accessible/field.component.js index 9b1a3514..e10323db 100644 --- a/accessible/field.component.js +++ b/accessible/field.component.js @@ -28,40 +28,41 @@ blocklyApp.FieldComponent = ng.core .Component({ selector: 'blockly-field', template: ` -
  • - -
  • -
  • - -
      + + + + +
      + +
      1. + [attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[optionValue + 'Button'], 'blockly-button')">
      - -
    1. +
    2. + +
      // Checkboxes are not currently supported. - -
    3. - -
    4. +
      + + `, - inputs: ['field', 'level', 'index', 'parentId', 'disabled'], + inputs: ['field', 'index', 'parentId', 'disabled', 'mainFieldId'], pipes: [blocklyApp.TranslatePipe] }) .Class({ @@ -81,22 +82,21 @@ blocklyApp.FieldComponent = ng.core return mainLabel + ' ' + secondLabel; }, generateElementNames: function() { - var elementNames = ['listItem']; - if (this.isTextInput()) { - elementNames.push('input'); - } else if (this.isDropdown()) { - elementNames.push('label'); + var elementNames = []; + if (this.isDropdown()) { var keys = this.getOptions(); for (var i = 0; i < keys.length; i++){ elementNames.push(keys[i], keys[i] + 'Button'); } - } else if (this.isTextField() && this.hasVisibleText()) { - elementNames.push('label'); } return elementNames; }, + isNumberInput: function() { + return this.field instanceof Blockly.FieldNumber; + }, isTextInput: function() { - return this.field instanceof Blockly.FieldTextInput; + return this.field instanceof Blockly.FieldTextInput && + !(this.field instanceof Blockly.FieldNumber); }, isDropdown: function() { return this.field instanceof Blockly.FieldDropdown; diff --git a/media/accessible.css b/accessible/media/accessible.css similarity index 51% rename from media/accessible.css rename to accessible/media/accessible.css index 651c7432..5a68527c 100644 --- a/media/accessible.css +++ b/accessible/media/accessible.css @@ -4,6 +4,9 @@ .blocklyTree .blocklyActiveDescendant > label, .blocklyTree .blocklyActiveDescendant > div > label, .blocklyActiveDescendant > button, -.blocklyActiveDescendant > input { +.blocklyActiveDescendant > input, +.blocklyActiveDescendant > blockly-field > label, +.blocklyActiveDescendant > blockly-field > input, +.blocklyActiveDescendant > blockly-field > div > label { outline: 2px dotted #00f; } diff --git a/accessible/media/click.mp3 b/accessible/media/click.mp3 new file mode 100644 index 00000000..4534b0dd Binary files /dev/null and b/accessible/media/click.mp3 differ diff --git a/accessible/media/click.ogg b/accessible/media/click.ogg new file mode 100644 index 00000000..e8ae42a6 Binary files /dev/null and b/accessible/media/click.ogg differ diff --git a/accessible/media/click.wav b/accessible/media/click.wav new file mode 100644 index 00000000..41a50cd7 Binary files /dev/null and b/accessible/media/click.wav differ diff --git a/accessible/media/delete.mp3 b/accessible/media/delete.mp3 new file mode 100644 index 00000000..442bd9c1 Binary files /dev/null and b/accessible/media/delete.mp3 differ diff --git a/accessible/media/delete.ogg b/accessible/media/delete.ogg new file mode 100644 index 00000000..67f84ac1 Binary files /dev/null and b/accessible/media/delete.ogg differ diff --git a/accessible/media/delete.wav b/accessible/media/delete.wav new file mode 100644 index 00000000..18debcf9 Binary files /dev/null and b/accessible/media/delete.wav differ diff --git a/accessible/messages.js b/accessible/messages.js index 8014beee..681b5d53 100644 --- a/accessible/messages.js +++ b/accessible/messages.js @@ -33,10 +33,10 @@ Blockly.Msg.CLEAR_WORKSPACE = 'Clear Workspace'; Blockly.Msg.BLOCK_ACTION_LIST = 'block action list'; Blockly.Msg.CUT_BLOCK = 'cut block'; Blockly.Msg.COPY_BLOCK = 'copy block'; -Blockly.Msg.PASTE_BELOW = 'paste below'; -Blockly.Msg.PASTE_ABOVE = 'paste above'; -Blockly.Msg.MARK_SPOT_BELOW = 'mark spot below'; -Blockly.Msg.MARK_SPOT_ABOVE = 'mark spot above'; +Blockly.Msg.PASTE_BEFORE = 'paste before'; +Blockly.Msg.PASTE_AFTER = 'paste after'; +Blockly.Msg.MARK_SPOT_BEFORE = 'mark spot before'; +Blockly.Msg.MARK_SPOT_AFTER = 'mark spot after'; Blockly.Msg.MOVE_TO_MARKED_SPOT = 'move to marked spot'; Blockly.Msg.DELETE = 'delete'; Blockly.Msg.MARK_THIS_SPOT = 'mark this spot'; @@ -50,19 +50,24 @@ Blockly.Msg.ARGUMENT_INPUT = 'argument input'; Blockly.Msg.ARGUMENT_BLOCK_ACTION_LIST = 'argument block action list'; Blockly.Msg.TEXT = 'text'; Blockly.Msg.BUTTON = 'button'; -Blockly.Msg.UNAVAILABLE = 'unavailable'; +Blockly.Msg.DISABLED = 'disabled'; Blockly.Msg.CURRENT_ARGUMENT_VALUE = 'current argument value:'; -Blockly.Msg.COPY_TO_WORKSPACE = 'copy to workspace'; +Blockly.Msg.COPY_TO_WORKSPACE = 'create new group with this block'; Blockly.Msg.COPY_TO_CLIPBOARD = 'copy to clipboard'; Blockly.Msg.COPY_TO_MARKED_SPOT = 'copy to marked spot'; Blockly.Msg.TOOLBOX = 'Toolbox'; Blockly.Msg.WORKSPACE = 'Workspace'; Blockly.Msg.ANY = 'any'; +Blockly.Msg.FOR = 'for'; Blockly.Msg.STATEMENT = 'statement'; Blockly.Msg.VALUE = 'value'; Blockly.Msg.CUT_BLOCK_MSG = 'Cut block: '; -Blockly.Msg.COPIED_BLOCK_MSG = 'Copied block to clipboard: '; -Blockly.Msg.PASTED_BLOCK_FROM_CLIPBOARD_MSG = 'Pasted block from clipboard: '; -Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG = 'Pasted block to marked spot: '; +Blockly.Msg.COPIED_BLOCK_MSG = 'copied'; +Blockly.Msg.PASTED_BLOCK_FROM_CLIPBOARD_MSG = 'pasted'; +Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG = 'moved to marked spot'; Blockly.Msg.MARKED_SPOT_MSG = 'Marked spot'; Blockly.Msg.BLOCK_MOVED_TO_MARKED_SPOT_MSB = 'Block moved to marked spot: '; +Blockly.Msg.TOOLBOX_BLOCK = 'toolbox block'; +Blockly.Msg.WORKSPACE_BLOCK = 'workspace block'; +Blockly.Msg.SUBMENU_INDICATOR = 'move right to view submenu'; +Blockly.Msg.MORE_OPTIONS = 'More options'; diff --git a/accessible/notifications.service.js b/accessible/notifications.service.js new file mode 100644 index 00000000..ad5af84c --- /dev/null +++ b/accessible/notifications.service.js @@ -0,0 +1,46 @@ +/** + * AccessibleBlockly + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Angular2 Service that notifies the user about actions that + * they have taken, by updating an ARIA live region. + * @author sll@google.com (Sean Lip) + */ + +blocklyApp.NotificationsService = ng.core + .Class({ + constructor: [function() { + this.statusMessage_ = ''; + }], + getStatusMessage: function() { + return this.statusMessage_; + }, + setStatusMessage: function(newMessage) { + // Introduce a temporary status message, so that if, e.g., two "copy" + // operations are done in succession, both messages will be read. + this.statusMessage_ = ''; + + // We need a non-zero timeout here, otherwise NVDA does not read the + // notification messages properly. + var that = this; + setTimeout(function() { + that.statusMessage_ = newMessage; + }, 20); + } + }); diff --git a/accessible/toolbox-tree.component.js b/accessible/toolbox-tree.component.js index 15dfa220..e5bbb75b 100644 --- a/accessible/toolbox-tree.component.js +++ b/accessible/toolbox-tree.component.js @@ -29,70 +29,36 @@ blocklyApp.ToolboxTreeComponent = ng.core selector: 'blockly-toolbox-tree', template: `
    5. - -
        -
      1. - -
          -
        1. - -
        2. -
        3. - -
        4. -
        5. - -
        6. -
        + +
          +
        1. + +
        2. +
        3. + +
        4. +
        5. +
        6. -
          - - - - -
        7. - - -
        8. -
      2. + Toolbox
          @@ -39,15 +39,15 @@ blocklyApp.ToolboxComponent = ng.core [id]="idMap['Parent' + i]" role="treeitem" [ngClass]="{blocklyHasChildren: true, blocklyActiveDescendant: tree.getAttribute('aria-activedescendant') == idMap['Parent' + i]}" *ngFor="#category of toolboxCategories; #i=index" - aria-level="1" aria-selected=false - [attr.aria-label]="category.attributes.name.value"> + aria-level="1" + [attr.aria-label]="getCategoryAriaLabel(category)">
            @@ -57,7 +57,7 @@ blocklyApp.ToolboxComponent = ng.core
            0; i--) { if (trees[i].id == treeId) { - trees[i - 1].focus(); - return true; + return trees[i - 1].id; } } - return false; + return null; }, getActiveDescId: function(treeId) { return this.activeDescendantIds_[treeId] || ''; @@ -107,13 +107,11 @@ blocklyApp.TreeService = ng.core var activeDesc = document.getElementById(activeDescId); if (activeDesc) { activeDesc.classList.remove('blocklyActiveDescendant'); - activeDesc.setAttribute('aria-selected', 'false'); } }, markActiveDesc_: function(activeDescId) { var newActiveDesc = document.getElementById(activeDescId); newActiveDesc.classList.add('blocklyActiveDescendant'); - newActiveDesc.setAttribute('aria-selected', 'true'); }, // Runs the given function while preserving the focus and active descendant // for the given tree. @@ -133,22 +131,68 @@ blocklyApp.TreeService = ng.core document.getElementById(treeId).focus(); }, 0); }, + // This clears the active descendant of the given tree. It is used just + // before the tree is deleted. + clearActiveDesc: function(treeId) { + this.unmarkActiveDesc_(this.getActiveDescId(treeId)); + delete this.activeDescendantIds_[treeId]; + }, // Make a given node the active descendant of a given tree. setActiveDesc: function(newActiveDescId, treeId) { this.unmarkActiveDesc_(this.getActiveDescId(treeId)); this.markActiveDesc_(newActiveDescId); this.activeDescendantIds_[treeId] = newActiveDescId; + + // Scroll the new active desc into view, if needed. This has no effect + // for blind users, but is helpful for sighted onlookers. + var activeDescNode = document.getElementById(newActiveDescId); + var documentNode = document.body || document.documentElement; + if (activeDescNode.offsetTop < documentNode.scrollTop || + activeDescNode.offsetTop > + documentNode.scrollTop + window.innerHeight) { + window.scrollTo(0, activeDescNode.offsetTop); + } + }, + initActiveDesc: function(treeId) { + // Set the active desc to the first child in this tree. + var tree = document.getElementById(treeId); + this.setActiveDesc(this.getFirstChild(tree).id, treeId); + }, + getTreeIdForBlock: function(blockId) { + // Walk up the DOM until we get to the root node of the tree. + var domNode = document.getElementById(blockId + 'blockRoot'); + while (!domNode.classList.contains('blocklyTree')) { + domNode = domNode.parentNode; + } + return domNode.id; + }, + focusOnBlock: function(blockId) { + // Set focus to the tree containing the given block, and set the active + // desc for this tree to the given block. + var domNode = document.getElementById(blockId + 'blockRoot'); + // Walk up the DOM until we get to the root node of the tree. + while (!domNode.classList.contains('blocklyTree')) { + domNode = domNode.parentNode; + } + domNode.focus(); + + // We need to wait a while to set the active desc, because domNode takes + // a while to be given an ID if a new tree has just been created. + // TODO(sll): Make this more deterministic. + var that = this; + setTimeout(function() { + that.setActiveDesc(blockId + 'blockRoot', domNode.id); + }, 100); }, onWorkspaceToolbarKeypress: function(e, treeId) { if (e.keyCode == 9) { // Tab key. - if (e.shiftKey) { - this.focusOnPreviousTree_(treeId); - } else { - this.focusOnNextTree_(treeId); + var destinationTreeId = + e.shiftKey ? this.getIdOfPreviousTree_(treeId) : + this.getIdOfNextTree_(treeId); + if (destinationTreeId) { + this.notifyUserAboutCurrentTree_(destinationTreeId); } - e.preventDefault(); - e.stopPropagation(); } }, isButtonOrFieldNode_: function(node) { @@ -181,42 +225,52 @@ blocklyApp.TreeService = ng.core // in the first place. console.error('Could not handle deletion of block.' + blockRootNode); }, + notifyUserAboutCurrentTree_: function(treeId) { + if (this.getToolboxTreeNode_().id == treeId) { + this.notificationsService.setStatusMessage('Now in toolbox.'); + } else { + var workspaceTreeNodes = this.getWorkspaceTreeNodes_(); + for (var i = 0; i < workspaceTreeNodes.length; i++) { + if (workspaceTreeNodes[i].id == treeId) { + this.notificationsService.setStatusMessage( + 'Now in workspace group ' + (i + 1) + ' of ' + + workspaceTreeNodes.length); + } + } + } + }, onKeypress: function(e, tree) { var treeId = tree.id; var activeDesc = document.getElementById(this.getActiveDescId(treeId)); if (!activeDesc) { console.error('ERROR: no active descendant for current tree.'); - - // TODO(sll): Generalize this to other trees (outside the workspace). - var workspaceTreeNodes = this.getWorkspaceTreeNodes_(); - for (var i = 0; i < workspaceTreeNodes.length; i++) { - if (workspaceTreeNodes[i].id == treeId) { - // Set the active desc to the first child in this tree. - this.setActiveDesc( - this.getFirstChild(workspaceTreeNodes[i]).id, treeId); - break; - } - } + this.initActiveDesc(treeId); return; } - if (document.activeElement.tagName == 'INPUT') { + if (e.altKey || e.ctrlKey) { + // Do not intercept combinations such as Alt+Home. + return; + } else if (document.activeElement.tagName == 'INPUT') { // For input fields, only Esc and Tab keystrokes are handled specially. if (e.keyCode == 27 || e.keyCode == 9) { // For Esc and Tab keys, the focus is removed from the input field. this.focusOnCurrentTree_(treeId); - // In addition, for Tab keys, the user tabs to the previous/next tree. if (e.keyCode == 9) { - if (e.shiftKey) { - this.focusOnPreviousTree_(treeId); - } else { - this.focusOnNextTree_(treeId); + var destinationTreeId = + e.shiftKey ? this.getIdOfPreviousTree_(treeId) : + this.getIdOfNextTree_(treeId); + if (destinationTreeId) { + this.notifyUserAboutCurrentTree_(destinationTreeId); } } - e.preventDefault(); - e.stopPropagation(); + // Allow Tab keypresses to go through. + if (e.keyCode == 27) { + e.preventDefault(); + e.stopPropagation(); + } } } else { // Outside an input field, Enter, Tab and navigation keys are all @@ -224,23 +278,41 @@ blocklyApp.TreeService = ng.core if (e.keyCode == 13) { // Enter key. The user wants to interact with a button or an input // field. - if (activeDesc.children.length == 1) { - var child = activeDesc.children[0]; - if (child.tagName == 'BUTTON') { - child.click(); - } else if (child.tagName == 'INPUT') { - child.focus(); + // Algorithm to find the field: do a DFS through the children until + // we find an INPUT or BUTTON element (in which case we use it). + // Truncate the search at child LI elements. + var dfsStack = Array.from(activeDesc.children); + while (dfsStack.length) { + var currentNode = dfsStack.shift(); + if (currentNode.tagName == 'BUTTON') { + this.moveActiveDescToParent(treeId); + currentNode.click(); + break; + } else if (currentNode.tagName == 'INPUT') { + currentNode.focus(); + this.notificationsService.setStatusMessage( + 'Type a value, then press Escape to exit'); + break; + } else if (currentNode.tagName == 'LI') { + continue; + } + + if (currentNode.children) { + var reversedChildren = Array.from(currentNode.children).reverse(); + reversedChildren.forEach(function(childNode) { + dfsStack.unshift(childNode); + }); } } } else if (e.keyCode == 9) { - // Tab key. - if (e.shiftKey) { - this.focusOnPreviousTree_(treeId); - } else { - this.focusOnNextTree_(treeId); + // Tab key. Note that allowing the event to propagate through is + // intentional. + var destinationTreeId = + e.shiftKey ? this.getIdOfPreviousTree_(treeId) : + this.getIdOfNextTree_(treeId); + if (destinationTreeId) { + this.notifyUserAboutCurrentTree_(destinationTreeId); } - e.preventDefault(); - e.stopPropagation(); } else if (e.keyCode >= 35 && e.keyCode <= 40) { // End, home, and arrow keys. if (e.keyCode == 35) { @@ -257,21 +329,15 @@ blocklyApp.TreeService = ng.core } } else if (e.keyCode == 37) { // Left arrow key. Go up a level, if possible. - var nextNode = activeDesc.parentNode; - if (this.isButtonOrFieldNode_(activeDesc)) { - nextNode = nextNode.parentNode; - } - while (nextNode && nextNode.tagName != 'LI') { - nextNode = nextNode.parentNode; - } - if (nextNode) { - this.setActiveDesc(nextNode.id, treeId); - } + this.moveActiveDescToParent(treeId); } else if (e.keyCode == 38) { // Up arrow key. Go to the previous sibling, if possible. var prevSibling = this.getPreviousSibling(activeDesc); if (prevSibling) { this.setActiveDesc(prevSibling.id, treeId); + } else { + this.notificationsService.setStatusMessage( + 'Reached top of list'); } } else if (e.keyCode == 39) { // Right arrow key. Go down a level, if possible. @@ -284,6 +350,9 @@ blocklyApp.TreeService = ng.core var nextSibling = this.getNextSibling(activeDesc); if (nextSibling) { this.setActiveDesc(nextSibling.id, treeId); + } else { + this.notificationsService.setStatusMessage( + 'Reached bottom of list'); } } @@ -292,6 +361,19 @@ blocklyApp.TreeService = ng.core } } }, + moveActiveDescToParent: function(treeId) { + var activeDesc = document.getElementById(this.getActiveDescId(treeId)); + var nextNode = activeDesc.parentNode; + if (this.isButtonOrFieldNode_(activeDesc)) { + nextNode = nextNode.parentNode; + } + while (nextNode && nextNode.tagName != 'LI') { + nextNode = nextNode.parentNode; + } + if (nextNode) { + this.setActiveDesc(nextNode.id, treeId); + } + }, getFirstChild: function(element) { if (!element) { return element; diff --git a/accessible/utils.service.js b/accessible/utils.service.js index 5afd8969..3a7eef79 100644 --- a/accessible/utils.service.js +++ b/accessible/utils.service.js @@ -41,20 +41,17 @@ blocklyApp.UtilsService = ng.core return idMap; }, generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) { - var attrValue = mainLabel + ' ' + secondLabel; + var attrValue = mainLabel + (secondLabel ? ' ' + secondLabel : ''); if (isDisabled) { attrValue += ' blockly-disabled'; } return attrValue; }, getInputTypeLabel: function(connection) { - // Returns an upper case string in the case of official input type names. - // Returns the lower case string 'any' if any official input type qualifies. - // The differentiation between upper and lower case signifies the difference - // between an input type (BOOLEAN, LIST, etc) and the colloquial english term - // 'any'. + // Returns the input type name, or 'any' if any official input type + // qualifies. if (connection.check_) { - return connection.check_.join(', ').toUpperCase(); + return connection.check_.join(', '); } else { return Blockly.Msg.ANY; } @@ -65,5 +62,13 @@ blocklyApp.UtilsService = ng.core } else { return Blockly.Msg.VALUE; } + }, + getBlockDescription: function(block) { + // We use 'BLANK' instead of the default '?' so that the string is read + // out. (By default, screen readers tend to ignore punctuation.) + return block.toString(undefined, 'BLANK'); + }, + isWorkspaceEmpty: function() { + return !blocklyApp.workspace.topBlocks_.length; } }); diff --git a/accessible/workspace-tree.component.js b/accessible/workspace-tree.component.js index 83dda119..04ec09b1 100644 --- a/accessible/workspace-tree.component.js +++ b/accessible/workspace-tree.component.js @@ -29,56 +29,61 @@ blocklyApp.WorkspaceTreeComponent = ng.core selector: 'blockly-workspace-tree', template: `
          1. - + [attr.aria-label]="getBlockDescription() + ' ' + ('WORKSPACE_BLOCK'|translate) + ' ' + ('SUBMENU_INDICATOR'|translate)" + [attr.aria-level]="level"> + + +
              + -
              1. - -
                  + [attr.aria-labelledBy]="generateAriaLabelledByAttr('blockly-more-options', 'blockly-submenu-indicator')" + [attr.aria-level]="level + 1"> + +
                  1. + [attr.aria-level]="level + 2">
                  - -
                  - - - -
                1. - - -
                    -
                  1. - -
                  2. -
                  3. - -
                  4. -
                  -
                2. -
              2. @@ -95,23 +100,31 @@ blocklyApp.WorkspaceTreeComponent = ng.core }) .Class({ constructor: [ - blocklyApp.ClipboardService, blocklyApp.TreeService, - blocklyApp.UtilsService, - function(_clipboardService, _treeService, _utilsService) { - this.infoBlocks = Object.create(null); + blocklyApp.ClipboardService, blocklyApp.NotificationsService, + blocklyApp.TreeService, blocklyApp.UtilsService, + blocklyApp.AudioService, + function( + _clipboardService, _notificationsService, _treeService, + _utilsService, _audioService) { this.clipboardService = _clipboardService; + this.notificationsService = _notificationsService; this.treeService = _treeService; this.utilsService = _utilsService; + this.audioService = _audioService; }], + getBlockDescription: function() { + return this.utilsService.getBlockDescription(this.block); + }, isIsolatedTopLevelBlock_: function(block) { // Returns whether the given block is at the top level, and has no // siblings. - return Boolean( - !block.nextConnection.targetConnection && - !block.previousConnection.targetConnection && - blocklyApp.workspace.topBlocks_.some(function(topBlock) { - return topBlock.id == block.id; - })); + var blockIsAtTopLevel = !block.getParent(); + var blockHasNoSiblings = ( + (!block.nextConnection || + !block.nextConnection.targetConnection) && + (!block.previousConnection || + !block.previousConnection.targetConnection)); + return blockIsAtTopLevel && blockHasNoSiblings; }, removeBlockAndSetFocus_: function(block, deleteBlockFunc) { // This method runs the given function and then does one of two things: @@ -121,8 +134,13 @@ blocklyApp.WorkspaceTreeComponent = ng.core if (this.isIsolatedTopLevelBlock_(block)) { var nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted(this.tree.id); + + this.treeService.clearActiveDesc(this.tree.id); deleteBlockFunc(); - nextNodeToFocusOn.focus(); + // Invoke a digest cycle, so that the DOM settles. + setTimeout(function() { + nextNodeToFocusOn.focus(); + }); } else { var blockRootNode = document.getElementById(this.idMap['blockRoot']); var nextActiveDesc = @@ -133,41 +151,105 @@ blocklyApp.WorkspaceTreeComponent = ng.core } }, cutBlock_: function() { + var blockDescription = this.getBlockDescription(); + var that = this; this.removeBlockAndSetFocus_(this.block, function() { that.clipboardService.cut(that.block); }); + + setTimeout(function() { + if (that.utilsService.isWorkspaceEmpty()) { + that.notificationsService.setStatusMessage( + blockDescription + ' cut. Workspace is empty.'); + } else { + that.notificationsService.setStatusMessage( + blockDescription + ' cut. Now on workspace.'); + } + }); }, deleteBlock_: function() { + var blockDescription = this.getBlockDescription(); + var that = this; this.removeBlockAndSetFocus_(this.block, function() { that.block.dispose(true); + that.audioService.playDeleteSound(); + }); + + setTimeout(function() { + if (that.utilsService.isWorkspaceEmpty()) { + that.notificationsService.setStatusMessage( + blockDescription + ' deleted. Workspace is empty.'); + } else { + that.notificationsService.setStatusMessage( + blockDescription + ' deleted. Now on workspace.'); + } }); }, pasteToConnection_: function(connection) { + var destinationTreeId = this.treeService.getTreeIdForBlock( + connection.getSourceBlock().id); + this.treeService.clearActiveDesc(destinationTreeId); + + var newBlockId = this.clipboardService.pasteFromClipboard(connection); + + // Invoke a digest cycle, so that the DOM settles. var that = this; - this.treeService.runWhilePreservingFocus(function() { - // If the connection is a 'previousConnection' and that connection is - // already joined to something, use the 'nextConnection' of the - // previous block instead in order to do an insertion. - if (connection.type == Blockly.PREVIOUS_STATEMENT && - connection.isConnected()) { - that.clipboardService.pasteFromClipboard( - connection.targetConnection); - } else { - that.clipboardService.pasteFromClipboard(connection); - } - }, this.tree.id); + setTimeout(function() { + that.treeService.focusOnBlock(newBlockId); + }); }, - sendToMarkedSpot_: function() { - this.clipboardService.pasteToMarkedConnection(this.block, false); + moveToMarkedSpot_: function() { + var blockDescription = this.getBlockDescription(); + var oldDestinationTreeId = this.treeService.getTreeIdForBlock( + this.clipboardService.getMarkedConnectionBlock().id); + this.treeService.clearActiveDesc(oldDestinationTreeId); + + var newBlockId = this.clipboardService.pasteToMarkedConnection( + this.block); var that = this; this.removeBlockAndSetFocus_(this.block, function() { that.block.dispose(true); }); - alert('Block moved to marked spot: ' + this.block.toString()); + // Invoke a digest cycle, so that the DOM settles. + setTimeout(function() { + that.treeService.focusOnBlock(newBlockId); + + var newDestinationTreeId = that.treeService.getTreeIdForBlock( + newBlockId); + if (newDestinationTreeId != oldDestinationTreeId) { + // It is possible for the tree ID for the pasted block to change + // after the paste operation, e.g. when inserting a block between two + // existing blocks that are joined together. In this case, we need to + // also reset the active desc for the old destination tree. + that.treeService.initActiveDesc(oldDestinationTreeId); + } + + that.notificationsService.setStatusMessage( + blockDescription + ' ' + + Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG + + '. Now on moved block in workspace.'); + }); + }, + copyBlock_: function() { + this.clipboardService.copy(this.block); + this.notificationsService.setStatusMessage( + this.getBlockDescription() + ' ' + Blockly.Msg.COPIED_BLOCK_MSG); + }, + markSpotBefore_: function() { + this.clipboardService.markConnection(this.block.previousConnection); + }, + markSpotAfter_: function() { + this.clipboardService.markConnection(this.block.nextConnection); + }, + pasteToNextConnection_: function() { + this.pasteToConnection_(this.block.nextConnection); + }, + pasteToPreviousConnection_: function() { + this.pasteToConnection_(this.block.previousConnection); }, ngOnInit: function() { var that = this; @@ -183,50 +265,46 @@ blocklyApp.WorkspaceTreeComponent = ng.core }, { baseIdKey: 'copy', translationIdForText: 'COPY_BLOCK', - action: that.clipboardService.copy.bind( - that.clipboardService, that.block, true), + action: that.copyBlock_.bind(that), isDisabled: function() { return false; } }, { - baseIdKey: 'pasteBelow', - translationIdForText: 'PASTE_BELOW', - action: that.pasteToConnection_.bind(that, that.block.nextConnection), - isDisabled: function() { - return Boolean( - !that.block.nextConnection || - !that.isCompatibleWithClipboard(that.block.nextConnection)); - } - }, { - baseIdKey: 'pasteAbove', - translationIdForText: 'PASTE_ABOVE', - action: that.pasteToConnection_.bind( - that, that.block.previousConnection), + baseIdKey: 'pasteBefore', + translationIdForText: 'PASTE_BEFORE', + action: that.pasteToPreviousConnection_.bind(that), isDisabled: function() { return Boolean( !that.block.previousConnection || !that.isCompatibleWithClipboard(that.block.previousConnection)); } }, { - baseIdKey: 'markBelow', - translationIdForText: 'MARK_SPOT_BELOW', - action: that.clipboardService.markConnection.bind( - that.clipboardService, that.block.nextConnection), + baseIdKey: 'pasteAfter', + translationIdForText: 'PASTE_AFTER', + action: that.pasteToNextConnection_.bind(that), isDisabled: function() { - return !that.block.nextConnection; + return Boolean( + !that.block.nextConnection || + !that.isCompatibleWithClipboard(that.block.nextConnection)); } }, { - baseIdKey: 'markAbove', - translationIdForText: 'MARK_SPOT_ABOVE', - action: that.clipboardService.markConnection.bind( - that.clipboardService, that.block.previousConnection), + baseIdKey: 'markBefore', + translationIdForText: 'MARK_SPOT_BEFORE', + action: that.markSpotBefore_.bind(that), isDisabled: function() { return !that.block.previousConnection; } }, { - baseIdKey: 'sendToMarkedSpot', + baseIdKey: 'markAfter', + translationIdForText: 'MARK_SPOT_AFTER', + action: that.markSpotAfter_.bind(that), + isDisabled: function() { + return !that.block.nextConnection; + } + }, { + baseIdKey: 'moveToMarkedSpot', translationIdForText: 'MOVE_TO_MARKED_SPOT', - action: that.sendToMarkedSpot_.bind(that), + action: that.moveToMarkedSpot_.bind(that), isDisabled: function() { return !that.clipboardService.isMovableToMarkedConnection( that.block); @@ -240,18 +318,43 @@ blocklyApp.WorkspaceTreeComponent = ng.core } }]; + // Generate a list of action buttons. + this.fieldButtonsInfo = [{ + baseIdKey: 'markSpot', + translationIdForText: 'MARK_THIS_SPOT', + action: function(connection) { + that.clipboardService.markConnection(connection); + }, + isDisabled: function() { + return false; + } + }, { + baseIdKey: 'paste', + translationIdForText: 'PASTE', + action: function(connection) { + that.pasteToConnection_(connection); + }, + isDisabled: function(connection) { + return !that.isCompatibleWithClipboard(connection); + } + }]; + // Make a list of all the id keys. this.idKeys = ['blockRoot', 'blockSummary', 'listItem', 'label']; this.actionButtonsInfo.forEach(function(buttonInfo) { that.idKeys.push(buttonInfo.baseIdKey, buttonInfo.baseIdKey + 'Button'); }); + this.fieldButtonsInfo.forEach(function(buttonInfo) { + for (var i = 0; i < that.block.inputList.length; i++) { + that.idKeys.push( + buttonInfo.baseIdKey + i, buttonInfo.baseIdKey + 'Button' + i); + } + }); for (var i = 0; i < this.block.inputList.length; i++) { var inputBlock = this.block.inputList[i]; - if (inputBlock.connection && !inputBlock.connection.targetBlock()) { - that.idKeys.push( - 'inputList' + i, 'inputMenuLabel' + i, 'markSpot' + i, - 'markSpotButton' + i, 'paste' + i, 'pasteButton' + i); - } + that.idKeys.push( + 'inputList' + i, 'inputMenuLabel' + i, 'listItem' + i, + 'fieldLabel' + i); } }, ngDoCheck: function() { diff --git a/accessible/workspace.component.js b/accessible/workspace.component.js index 42eb84d4..25573502 100644 --- a/accessible/workspace.component.js +++ b/accessible/workspace.component.js @@ -47,7 +47,7 @@ blocklyApp.WorkspaceComponent = ng.core
                  @@ -60,7 +60,9 @@ blocklyApp.WorkspaceComponent = ng.core pipes: [blocklyApp.TranslatePipe] }) .Class({ - constructor: [blocklyApp.TreeService, function(_treeService) { + constructor: [ + blocklyApp.TreeService, blocklyApp.UtilsService, + function(_treeService, _utilsService) { // ACCESSIBLE_GLOBALS is a global variable defined by the containing // page. It should contain a key, toolbarButtonConfig, whose // corresponding value is an Array with two keys: 'text' and 'action'. @@ -71,6 +73,7 @@ blocklyApp.WorkspaceComponent = ng.core ACCESSIBLE_GLOBALS.toolbarButtonConfig : []; this.workspace = blocklyApp.workspace; this.treeService = _treeService; + this.utilsService = _utilsService; }], clearWorkspace: function() { this.workspace.clear(); @@ -86,6 +89,6 @@ blocklyApp.WorkspaceComponent = ng.core this.treeService.onKeypress(e, tree); }, isWorkspaceEmpty: function() { - return !this.workspace.topBlocks_.length; + return this.utilsService.isWorkspaceEmpty(); } }); diff --git a/blockly_compressed.js b/blockly_compressed.js index 4fefd9d1..9632cab2 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -11,22 +11,23 @@ goog.setTestOnly=function(a){if(goog.DISALLOW_TEST_ONLY_CODE)throw a=a||"",Error goog.getObjectByName=function(a,b){for(var c=a.split("."),d=b||goog.global,e;e=c.shift();)if(goog.isDefAndNotNull(d[e]))d=d[e];else return null;return d};goog.globalize=function(a,b){var c=b||goog.global,d;for(d in a)c[d]=a[d]}; goog.addDependency=function(a,b,c,d){if(goog.DEPENDENCIES_ENABLED){var e;a=a.replace(/\\/g,"/");var f=goog.dependencies_;d&&"boolean"!==typeof d||(d=d?{module:"goog"}:{});for(var g=0;e=b[g];g++)f.nameToPath[e]=a,f.loadFlags[a]=d;for(d=0;b=c[d];d++)a in f.requires||(f.requires[a]={}),f.requires[a][b]=!0}};goog.ENABLE_DEBUG_LOADER=!0;goog.logToConsole_=function(a){goog.global.console&&goog.global.console.error(a)}; goog.require=function(a){if(!COMPILED){goog.ENABLE_DEBUG_LOADER&&goog.IS_OLD_IE_&&goog.maybeProcessDeferredDep_(a);if(goog.isProvided_(a))return goog.isInModuleLoader_()?goog.module.getInternal_(a):null;if(goog.ENABLE_DEBUG_LOADER){var b=goog.getPathFromDeps_(a);if(b)return goog.writeScripts_(b),null}a="goog.require could not find: "+a;goog.logToConsole_(a);throw Error(a);}};goog.basePath="";goog.nullFunction=function(){}; -goog.abstractMethod=function(){throw Error("unimplemented abstract method");};goog.addSingletonGetter=function(a){a.getInstance=function(){if(a.instance_)return a.instance_;goog.DEBUG&&(goog.instantiatedSingletons_[goog.instantiatedSingletons_.length]=a);return a.instance_=new a}};goog.instantiatedSingletons_=[];goog.LOAD_MODULE_USING_EVAL=!0;goog.SEAL_MODULE_EXPORTS=goog.DEBUG;goog.loadedModules_={};goog.DEPENDENCIES_ENABLED=!COMPILED&&goog.ENABLE_DEBUG_LOADER;goog.ALWAYS_TRANSPILE=!1; -goog.NEVER_TRANSPILE=!1; +goog.abstractMethod=function(){throw Error("unimplemented abstract method");};goog.addSingletonGetter=function(a){a.getInstance=function(){if(a.instance_)return a.instance_;goog.DEBUG&&(goog.instantiatedSingletons_[goog.instantiatedSingletons_.length]=a);return a.instance_=new a}};goog.instantiatedSingletons_=[];goog.LOAD_MODULE_USING_EVAL=!0;goog.SEAL_MODULE_EXPORTS=goog.DEBUG;goog.loadedModules_={};goog.DEPENDENCIES_ENABLED=!COMPILED&&goog.ENABLE_DEBUG_LOADER;goog.TRANSPILE="detect"; +goog.TRANSPILER="transpile.js"; goog.DEPENDENCIES_ENABLED&&(goog.dependencies_={loadFlags:{},nameToPath:{},requires:{},visited:{},written:{},deferred:{}},goog.inHtmlDocument_=function(){var a=goog.global.document;return null!=a&&"write"in a},goog.findBasePath_=function(){if(goog.isDef(goog.global.CLOSURE_BASE_PATH))goog.basePath=goog.global.CLOSURE_BASE_PATH;else if(goog.inHtmlDocument_())for(var a=goog.global.document.getElementsByTagName("SCRIPT"),b=a.length-1;0<=b;--b){var c=a[b].src,d=c.lastIndexOf("?"),d=-1==d?c.length:d;if("base.js"== c.substr(d-7,7)){goog.basePath=c.substr(0,d-7);break}}},goog.importScript_=function(a,b){(goog.global.CLOSURE_IMPORT_SCRIPT||goog.writeScriptTag_)(a,b)&&(goog.dependencies_.written[a]=!0)},goog.IS_OLD_IE_=!(goog.global.atob||!goog.global.document||!goog.global.document.all),goog.importProcessedScript_=function(a,b,c){goog.importScript_("",'goog.retrieveAndExec_("'+a+'", '+b+", "+c+");")},goog.queuedModules_=[],goog.wrapModule_=function(a,b){return goog.LOAD_MODULE_USING_EVAL&&goog.isDef(goog.global.JSON)? "goog.loadModule("+goog.global.JSON.stringify(b+"\n//# sourceURL="+a+"\n")+");":'goog.loadModule(function(exports) {"use strict";'+b+"\n;return exports});\n//# sourceURL="+a+"\n"},goog.loadQueuedModules_=function(){var a=goog.queuedModules_.length;if(0\x3c/script>')},goog.appendScriptSrcNode_=function(a){var b=goog.global.document, -c=b.createElement("script");c.type="text/javascript";c.src=a;c.defer=!1;c.async=!1;b.head.appendChild(c)},goog.writeScriptTag_=function(a,b){if(goog.inHtmlDocument_()){var c=goog.global.document;if(!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING&&"complete"==c.readyState){if(/\bdeps.js$/.test(a))return!1;throw Error('Cannot write "'+a+'" after document load');}if(void 0===b)if(goog.IS_OLD_IE_){var d=" onreadystatechange='goog.onScriptLoad_(this, "+ ++goog.lastNonModuleScriptIndex_+")' ";c.write(' + + @@ -30,7 +32,7 @@ - + -

                  Blockly > - Demos > Accessible Blockly

                  +

                  + Blockly > + Demos > Accessible Blockly +

                  This is a simple demo of a version of Blockly designed for screen readers.

                  +

                  + In Blockly, you can move blocks from the toolbox to the workspace and join + them to create programs. To navigate between components, use Tab or + Shift-Tab. When you're on a block, move right to access its submenus, and + move up or down to go to the next or previous block in the sequence. +

                  + @@ -58,7 +73,9 @@ var ACCESSIBLE_GLOBALS = { // Additional buttons for the workspace toolbar that // go before the "Clear Workspace" button. - toolbarButtonConfig: [] + toolbarButtonConfig: [], + // Prefix of path to sound files. + mediaPathPrefix: '../../accessible/media/' }; document.addEventListener('DOMContentLoaded', function() { ng.platform.browser.bootstrap(blocklyApp.AppView); @@ -72,7 +89,6 @@ - @@ -83,7 +99,6 @@ - @@ -326,9 +341,6 @@ - - - diff --git a/demos/blockfactory/index.html b/demos/blockfactory/index.html index ce87f88f..449b9a5e 100644 --- a/demos/blockfactory/index.html +++ b/demos/blockfactory/index.html @@ -140,8 +140,8 @@
  • Language code:

    diff --git a/demos/blocklyfactory/app_controller.js b/demos/blocklyfactory/app_controller.js new file mode 100644 index 00000000..8a6ba837 --- /dev/null +++ b/demos/blocklyfactory/app_controller.js @@ -0,0 +1,635 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview The AppController Class brings together the Block + * Factory, Block Library, and Block Exporter functionality into a single web + * app. + * + * @author quachtina96 (Tina Quach) + */ +goog.provide('AppController'); + +goog.require('BlockFactory'); +goog.require('FactoryUtils'); +goog.require('BlockLibraryController'); +goog.require('BlockExporterController'); +goog.require('goog.dom.classlist'); +goog.require('goog.string'); +goog.require('goog.ui.PopupColorPicker'); +goog.require('goog.ui.ColorPicker'); + +/** + * Controller for the Blockly Factory + * @constructor + */ +AppController = function() { + // Initialize Block Library + this.blockLibraryName = 'blockLibrary'; + this.blockLibraryController = + new BlockLibraryController(this.blockLibraryName); + this.blockLibraryController.populateBlockLibrary(); + + // Construct Workspace Factory Controller. + this.workspaceFactoryController = new WorkspaceFactoryController + ('workspacefactory_toolbox', 'toolbox_blocks', 'preview_blocks'); + + // Initialize Block Exporter + this.exporter = + new BlockExporterController(this.blockLibraryController.storage); + + // Map of tab type to the div element for the tab. + this.tabMap = Object.create(null); + this.tabMap[AppController.BLOCK_FACTORY] = + goog.dom.getElement('blockFactory_tab'); + this.tabMap[AppController.WORKSPACE_FACTORY] = + goog.dom.getElement('workspaceFactory_tab'); + this.tabMap[AppController.EXPORTER] = + goog.dom.getElement('blocklibraryExporter_tab'); + + // Selected tab. + this.selectedTab = AppController.BLOCK_FACTORY; +}; + +// Constant values representing the three tabs in the controller. +AppController.BLOCK_FACTORY = 'BLOCK_FACTORY'; +AppController.WORKSPACE_FACTORY = 'WORKSPACE_FACTORY'; +AppController.EXPORTER = 'EXPORTER'; + +/** + * Tied to the 'Import Block Library' button. Imports block library from file to + * Block Factory. Expects user to upload a single file of JSON mapping each + * block type to its xml text representation. + */ +AppController.prototype.importBlockLibraryFromFile = function() { + var self = this; + var files = document.getElementById('files'); + // If the file list is empty, the user likely canceled in the dialog. + if (files.files.length > 0) { + // The input tag doesn't have the "multiple" attribute + // so the user can only choose 1 file. + var file = files.files[0]; + var fileReader = new FileReader(); + + // Create a map of block type to xml text from the file when it has been + // read. + fileReader.addEventListener('load', function(event) { + var fileContents = event.target.result; + // Create empty object to hold the read block library information. + var blockXmlTextMap = Object.create(null); + try { + // Parse the file to get map of block type to xml text. + blockXmlTextMap = self.formatBlockLibraryForImport_(fileContents); + } catch (e) { + var message = 'Could not load your block library file.\n' + window.alert(message + '\nFile Name: ' + file.name); + return; + } + + // Create a new block library storage object with inputted block library. + var blockLibStorage = new BlockLibraryStorage( + self.blockLibraryName, blockXmlTextMap); + + // Update block library controller with the new block library + // storage. + self.blockLibraryController.setBlockLibraryStorage(blockLibStorage); + // Update the block library dropdown. + self.blockLibraryController.populateBlockLibrary(); + // Update the exporter's block library storage. + self.exporter.setBlockLibraryStorage(blockLibStorage); + }); + // Read the file. + fileReader.readAsText(file); + } +}; + +/** + * Tied to the 'Export Block Library' button. Exports block library to file that + * contains JSON mapping each block type to its xml text representation. + */ +AppController.prototype.exportBlockLibraryToFile = function() { + // Get map of block type to xml. + var blockLib = this.blockLibraryController.getBlockLibrary(); + // Concatenate the xmls, each separated by a blank line. + var blockLibText = this.formatBlockLibraryForExport_(blockLib); + // Get file name. + var filename = prompt('Enter the file name under which to save your block ' + + 'library.'); + // Download file if all necessary parameters are provided. + if (filename) { + FactoryUtils.createAndDownloadFile(blockLibText, filename, 'xml'); + } else { + alert('Could not export Block Library without file name under which to ' + + 'save library.'); + } +}; + +/** + * Converts an object mapping block type to xml to text file for output. + * @private + * + * @param {!Object} blockXmlMap - Object mapping block type to xml. + * @return {string} Xml text containing the block xmls. + */ +AppController.prototype.formatBlockLibraryForExport_ = function(blockXmlMap) { + // Create DOM for XML. + var xmlDom = goog.dom.createDom('xml', { + 'xmlns':"http://www.w3.org/1999/xhtml" + }); + + // Append each block node to xml dom. + for (var blockType in blockXmlMap) { + var blockXmlDom = Blockly.Xml.textToDom(blockXmlMap[blockType]); + var blockNode = blockXmlDom.firstElementChild; + xmlDom.appendChild(blockNode); + } + + // Return the xml text. + return Blockly.Xml.domToText(xmlDom); +}; + +/** + * Converts imported block library to an object mapping block type to block xml. + * @private + * + * @param {string} xmlText - String representation of an xml with each block as + * a child node. + * @return {!Object} object mapping block type to xml text. + */ +AppController.prototype.formatBlockLibraryForImport_ = function(xmlText) { + var xmlDom = Blockly.Xml.textToDom(xmlText); + + // Get array of xmls. Use an asterisk (*) instead of a tag name for the XPath + // selector, to match all elements at that level and get all factory_base + // blocks. + var blockNodes = goog.dom.xml.selectNodes(xmlDom, '*'); + + // Create empty map. The line below creates a truly empy object. It doesn't + // have built-in attributes/functions such as length or toString. + var blockXmlTextMap = Object.create(null); + + // Populate map. + for (var i = 0, blockNode; blockNode = blockNodes[i]; i++) { + + // Add outer xml tag to the block for proper injection in to the + // main workspace. + // Create DOM for XML. + var xmlDom = goog.dom.createDom('xml', { + 'xmlns':"http://www.w3.org/1999/xhtml" + }); + xmlDom.appendChild(blockNode); + + var xmlText = Blockly.Xml.domToText(xmlDom); + // All block types should be lowercase. + var blockType = this.getBlockTypeFromXml_(xmlText).toLowerCase(); + + blockXmlTextMap[blockType] = xmlText; + } + + return blockXmlTextMap; +}; + +/** + * Extracts out block type from xml text, the kind that is saved in block + * library storage. + * @private + * + * @param {!string} xmlText - A block's xml text. + * @return {string} The block type that corresponds to the provided xml text. + */ +AppController.prototype.getBlockTypeFromXml_ = function(xmlText) { + var xmlDom = Blockly.Xml.textToDom(xmlText); + // Find factory base block. + var factoryBaseBlockXml = xmlDom.getElementsByTagName('block')[0]; + // Get field elements from factory base. + var fields = factoryBaseBlockXml.getElementsByTagName('field'); + for (var i = 0; i < fields.length; i++) { + // The field whose name is 'NAME' holds the block type as its value. + if (fields[i].getAttribute('name') == 'NAME') { + return fields[i].childNodes[0].nodeValue; + } + } +}; + +/** + * Updates the Block Factory tab to show selected block when user selects a + * different block in the block library dropdown. Tied to block library dropdown + * in index.html. + * + * @param {!Element} blockLibraryDropdown - HTML select element from which the + * user selects a block to work on. + */ +AppController.prototype.onSelectedBlockChanged + = function(blockLibraryDropdown) { + // Get selected block type. + var blockType = this.blockLibraryController.getSelectedBlockType( + blockLibraryDropdown); + // Update Block Factory page by showing the selected block. + this.blockLibraryController.openBlock(blockType); +}; + +/** + * Add click handlers to each tab to allow switching between the Block Factory, + * Workspace Factory, and Block Exporter tab. + * + * @param {!Object} tabMap - Map of tab name to div element that is the tab. + */ +AppController.prototype.addTabHandlers = function(tabMap) { + var self = this; + for (var tabName in tabMap) { + var tab = tabMap[tabName]; + // Use an additional closure to correctly assign the tab callback. + tab.addEventListener('click', self.makeTabClickHandler_(tabName)); + } +}; + +/** + * Set the selected tab. + * @private + * + * @param {string} tabName AppController.BLOCK_FACTORY, + * AppController.WORKSPACE_FACTORY, or AppController.EXPORTER + */ +AppController.prototype.setSelected_ = function(tabName) { + this.selectedTab = tabName; +}; + +/** + * Creates the tab click handler specific to the tab specified. + * @private + * + * @param {string} tabName AppController.BLOCK_FACTORY, + * AppController.WORKSPACE_FACTORY, or AppController.EXPORTER + * @return {Function} The tab click handler. + */ +AppController.prototype.makeTabClickHandler_ = function(tabName) { + var self = this; + return function() { + self.setSelected_(tabName); + self.onTab(); + }; +}; + +/** + * Called on each tab click. Hides and shows specific content based on which tab + * (Block Factory, Workspace Factory, or Exporter) is selected. + */ +AppController.prototype.onTab = function() { + // Get tab div elements. + var blockFactoryTab = this.tabMap[AppController.BLOCK_FACTORY]; + var exporterTab = this.tabMap[AppController.EXPORTER]; + var workspaceFactoryTab = this.tabMap[AppController.WORKSPACE_FACTORY]; + + // Turn selected tab on and other tabs off. + this.styleTabs_(); + + // Only enable key events in workspace factory if workspace factory tab is + // selected. + this.workspaceFactoryController.keyEventsEnabled = + this.selectedTab == AppController.WORKSPACE_FACTORY; + + if (this.selectedTab == AppController.EXPORTER) { + // Show container of exporter. + FactoryUtils.show('blockLibraryExporter'); + FactoryUtils.hide('workspaceFactoryContent'); + + // Need accurate state in order to know which blocks are used in workspace + // factory. + this.workspaceFactoryController.saveStateFromWorkspace(); + + // Update exporter's list of the types of blocks used in workspace factory. + var usedBlockTypes = this.workspaceFactoryController.getAllUsedBlockTypes(); + this.exporter.setUsedBlockTypes(usedBlockTypes); + + // Update exporter's block selector to reflect current block library. + this.exporter.updateSelector(); + + // Update the preview to reflect any changes made to the blocks. + this.exporter.updatePreview(); + + } else if (this.selectedTab == AppController.BLOCK_FACTORY) { + // Hide container of exporter. + FactoryUtils.hide('blockLibraryExporter'); + FactoryUtils.hide('workspaceFactoryContent'); + + } else if (this.selectedTab == AppController.WORKSPACE_FACTORY) { + // Hide container of exporter. + FactoryUtils.hide('blockLibraryExporter'); + // Show workspace factory container. + FactoryUtils.show('workspaceFactoryContent'); + // Update block library category. + var categoryXml = this.exporter.getBlockLibraryCategory(); + var blockTypes = this.blockLibraryController.getStoredBlockTypes(); + this.workspaceFactoryController.setBlockLibCategory(categoryXml, + blockTypes); + } + + // Resize to render workspaces' toolboxes correctly for all tabs. + window.dispatchEvent(new Event('resize')); +}; + +/** + * Called on each tab click. Styles the tabs to reflect which tab is selected. + * @private + */ +AppController.prototype.styleTabs_ = function() { + for (var tabName in this.tabMap) { + if (this.selectedTab == tabName) { + goog.dom.classlist.addRemove(this.tabMap[tabName], 'taboff', 'tabon'); + } else { + goog.dom.classlist.addRemove(this.tabMap[tabName], 'tabon', 'taboff'); + } + } +}; + +/** + * Assign button click handlers for the exporter. + */ +AppController.prototype.assignExporterClickHandlers = function() { + var self = this; + document.getElementById('button_setBlocks').addEventListener('click', + function() { + document.getElementById('dropdownDiv_setBlocks').classList.toggle("show"); + }); + + document.getElementById('dropdown_addAllUsed').addEventListener('click', + function() { + self.exporter.selectUsedBlocks(); + self.exporter.updatePreview(); + document.getElementById('dropdownDiv_setBlocks').classList.remove("show"); + }); + + document.getElementById('dropdown_clearSelected').addEventListener('click', + function() { + self.exporter.clearSelectedBlocks(); + self.exporter.updatePreview(); + document.getElementById('dropdownDiv_setBlocks').classList.remove("show"); + }); + + document.getElementById('dropdown_addAllFromLib').addEventListener('click', + function() { + self.exporter.selectAllBlocks(); + self.exporter.updatePreview(); + document.getElementById('dropdownDiv_setBlocks').classList.remove("show"); + }); + + // Export blocks when the user submits the export settings. + document.getElementById('exporterSubmitButton').addEventListener('click', + function() { + self.exporter.export(); + }); +}; + +/** + * Assign change listeners for the exporter. These allow for the dynamic update + * of the exporter preview. + */ +AppController.prototype.assignExporterChangeListeners = function() { + var self = this; + + var blockDefCheck = document.getElementById('blockDefCheck'); + var genStubCheck = document.getElementById('genStubCheck'); + + var blockDefs = document.getElementById('blockDefs'); + var blockDefSettings = document.getElementById('blockDefSettings'); + var blockDefElements = [blockDefs, blockDefSettings]; + + var genStubs = document.getElementById('genStubs'); + var genStubSettings = document.getElementById('genStubSettings'); + var genStubElements = [genStubs, genStubSettings]; + + // Select the block definitions and generator stubs on default. + blockDefCheck.checked = true; + genStubCheck.checked = true; + + // Checking the block definitions checkbox displays preview of code to export. + document.getElementById('blockDefCheck').addEventListener('change', + function(e) { + self.ifCheckedDisplay(blockDefCheck, blockDefElements); + }); + + // Preview updates when user selects different block definition format. + document.getElementById('exportFormat').addEventListener('change', + function(e) { + self.exporter.updatePreview(); + }); + + // Checking the generator stub checkbox displays preview of code to export. + document.getElementById('genStubCheck').addEventListener('change', + function(e) { + self.ifCheckedDisplay(genStubCheck, genStubElements); + }); + + // Preview updates when user selects different generator stub language. + document.getElementById('exportLanguage').addEventListener('change', + function(e) { + self.exporter.updatePreview(); + }); +}; + +/** + * If given checkbox is checked, display given elements. Otherwise, hide. + * + * @param {!Element} checkbox - Input element of type checkbox. + * @param {!Array.} elementArray - Array of elements to show when + * block is checked. + */ +AppController.prototype.ifCheckedDisplay = function(checkbox, elementArray) { + for (var i = 0, element; element = elementArray[i]; i++) { + element.style.display = checkbox.checked ? 'block' : 'none'; + } +}; + +/** + * Assign button click handlers for the block library. + */ +AppController.prototype.assignLibraryClickHandlers = function() { + var self = this; + // Assign button click handlers for Block Library. + document.getElementById('saveToBlockLibraryButton').addEventListener('click', + function() { + self.blockLibraryController.saveToBlockLibrary(); + }); + + document.getElementById('removeBlockFromLibraryButton').addEventListener( + 'click', + function() { + self.blockLibraryController.removeFromBlockLibrary(); + }); + + document.getElementById('clearBlockLibraryButton').addEventListener('click', + function() { + self.blockLibraryController.clearBlockLibrary(); + }); + + var dropdown = document.getElementById('blockLibraryDropdown'); + dropdown.addEventListener('change', + function() { + self.onSelectedBlockChanged(dropdown); + }); +}; + +/** + * Assign button click handlers for the block factory. + */ +AppController.prototype.assignBlockFactoryClickHandlers = function() { + var self = this; + // Assign button event handlers for Block Factory. + document.getElementById('localSaveButton') + .addEventListener('click', function() { + self.exportBlockLibraryToFile(); + }); + + document.getElementById('helpButton').addEventListener('click', + function() { + open('https://developers.google.com/blockly/custom-blocks/block-factory', + 'BlockFactoryHelp'); + }); + + document.getElementById('files').addEventListener('change', + function() { + // Warn user. + var replace = confirm('This imported block library will ' + + 'replace your current block library.'); + if (replace) { + self.importBlockLibraryFromFile(); + // Clear this so that the change event still fires even if the + // same file is chosen again. If the user re-imports a file, we + // want to reload the workspace with its contents. + this.value = null; + } + }); + + document.getElementById('createNewBlockButton') + .addEventListener('click', function() { + BlockFactory.showStarterBlock(); + BlockLibraryView.selectDefaultOption('blockLibraryDropdown'); + }); +}; + +/** + * Add event listeners for the block factory. + */ +AppController.prototype.addBlockFactoryEventListeners = function() { + BlockFactory.mainWorkspace.addChangeListener(BlockFactory.updateLanguage); + document.getElementById('direction') + .addEventListener('change', BlockFactory.updatePreview); + document.getElementById('languageTA') + .addEventListener('change', BlockFactory.updatePreview); + document.getElementById('languageTA') + .addEventListener('keyup', BlockFactory.updatePreview); + document.getElementById('format') + .addEventListener('change', BlockFactory.formatChange); + document.getElementById('language') + .addEventListener('change', BlockFactory.updatePreview); +}; + +/** + * Handle Blockly Storage with App Engine. + */ +AppController.prototype.initializeBlocklyStorage = function() { + BlocklyStorage.HTTPREQUEST_ERROR = + 'There was a problem with the request.\n'; + BlocklyStorage.LINK_ALERT = + 'Share your blocks with this link:\n\n%1'; + BlocklyStorage.HASH_ERROR = + 'Sorry, "%1" doesn\'t correspond with any saved Blockly file.'; + BlocklyStorage.XML_ERROR = 'Could not load your saved file.\n' + + 'Perhaps it was created with a different version of Blockly?'; + var linkButton = document.getElementById('linkButton'); + linkButton.style.display = 'inline-block'; + linkButton.addEventListener('click', + function() { + BlocklyStorage.link(BlockFactory.mainWorkspace);}); + BlockFactory.disableEnableLink(); +}; + +/** + * Handle resizing of elements. + */ +AppController.prototype.onresize = function(event) { + // Handle resizing of Block Factory elements. + var expandList = [ + document.getElementById('blockly'), + document.getElementById('blocklyMask'), + document.getElementById('preview'), + document.getElementById('languagePre'), + document.getElementById('languageTA'), + document.getElementById('generatorPre') + ]; + for (var i = 0, expand; expand = expandList[i]; i++) { + expand.style.width = (expand.parentNode.offsetWidth - 2) + 'px'; + expand.style.height = (expand.parentNode.offsetHeight - 2) + 'px'; + } + + // Handle resize of Exporter block options. + this.exporter.view.centerPreviewBlocks(); +}; + +/** + * Initialize Blockly and layout. Called on page load. + */ +AppController.prototype.init = function() { + // Handle Blockly Storage with App Engine + if ('BlocklyStorage' in window) { + this.initializeBlocklyStorage(); + } + + // Assign click handlers. + this.assignExporterClickHandlers(); + this.assignLibraryClickHandlers(); + this.assignBlockFactoryClickHandlers(); + + this.onresize(); + self = this; + window.addEventListener('resize', function() { + self.onresize(); + }); + + // Inject Block Factory Main Workspace. + var toolbox = document.getElementById('blockfactory_toolbox'); + BlockFactory.mainWorkspace = Blockly.inject('blockly', + {collapse: false, + toolbox: toolbox, + media: '../../media/'}); + + // Add tab handlers for switching between Block Factory and Block Exporter. + this.addTabHandlers(this.tabMap); + + // Assign exporter change listeners. + this.assignExporterChangeListeners(); + + // Create the root block on Block Factory main workspace. + if ('BlocklyStorage' in window && window.location.hash.length > 1) { + BlocklyStorage.retrieveXml(window.location.hash.substring(1), + BlockFactory.mainWorkspace); + } else { + BlockFactory.showStarterBlock(); + } + BlockFactory.mainWorkspace.clearUndo(); + + // Add Block Factory event listeners. + this.addBlockFactoryEventListeners(); + + // Workspace Factory init. + WorkspaceFactoryInit.initWorkspaceFactory(this.workspaceFactoryController); +}; + + diff --git a/demos/blocklyfactory/block_exporter_controller.js b/demos/blocklyfactory/block_exporter_controller.js new file mode 100644 index 00000000..e0c192fb --- /dev/null +++ b/demos/blocklyfactory/block_exporter_controller.js @@ -0,0 +1,335 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Javascript for the Block Exporter Controller class. Allows + * users to export block definitions and generator stubs of their saved blocks + * easily using a visual interface. Depends on Block Exporter View and Block + * Exporter Tools classes. Interacts with Export Settings in the index.html. + * + * @author quachtina96 (Tina Quach) + */ + +'use strict'; + +goog.provide('BlockExporterController'); + +goog.require('FactoryUtils'); +goog.require('StandardCategories'); +goog.require('BlockExporterView'); +goog.require('BlockExporterTools'); +goog.require('goog.dom.xml'); + +/** + * BlockExporter Controller Class + * @constructor + * + * @param {!BlockLibrary.Storage} blockLibStorage - Block Library Storage. + */ +BlockExporterController = function(blockLibStorage) { + // BlockLibrary.Storage object containing user's saved blocks. + this.blockLibStorage = blockLibStorage; + // Utils for generating code to export. + this.tools = new BlockExporterTools(); + // The ID of the block selector, a div element that will be populated with the + // block options. + this.selectorID = 'blockSelector'; + // Map of block types stored in block library to their corresponding Block + // Option objects. + this.blockOptions = this.tools.createBlockSelectorFromLib( + this.blockLibStorage, this.selectorID); + // View provides the block selector and export settings UI. + this.view = new BlockExporterView(this.blockOptions); +}; + +/** + * Set the block library storage object from which exporter exports. + * + * @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object + * that stores the blocks. + */ +BlockExporterController.prototype.setBlockLibraryStorage = + function(blockLibStorage) { + this.blockLibStorage = blockLibStorage; +}; + +/** + * Get the block library storage object from which exporter exports. + * + * @return {!BlockLibraryStorage} blockLibStorage - Block Library Storage object + * that stores the blocks. + */ +BlockExporterController.prototype.getBlockLibraryStorage = + function(blockLibStorage) { + return this.blockLibStorage; +}; + +/** + * Get selected blocks from block selector, pulls info from the Export + * Settings form in Block Exporter, and downloads code accordingly. + */ +BlockExporterController.prototype.export = function() { + // Get selected blocks' information. + var blockTypes = this.view.getSelectedBlockTypes(); + var blockXmlMap = this.blockLibStorage.getBlockXmlMap(blockTypes); + + // Pull block definition(s) settings from the Export Settings form. + var wantBlockDef = document.getElementById('blockDefCheck').checked; + var definitionFormat = document.getElementById('exportFormat').value; + var blockDef_filename = document.getElementById('blockDef_filename').value; + + // Pull block generator stub(s) settings from the Export Settings form. + var wantGenStub = document.getElementById('genStubCheck').checked; + var language = document.getElementById('exportLanguage').value; + var generatorStub_filename = document.getElementById( + 'generatorStub_filename').value; + + if (wantBlockDef) { + // User wants to export selected blocks' definitions. + if (!blockDef_filename) { + // User needs to enter filename. + alert('Please enter a filename for your block definition(s) download.'); + } else { + // Get block definition code in the selected format for the blocks. + var blockDefs = this.tools.getBlockDefinitions(blockXmlMap, + definitionFormat); + // Download the file, using .js file ending for JSON or Javascript. + FactoryUtils.createAndDownloadFile( + blockDefs, blockDef_filename, 'javascript'); + } + } + + if (wantGenStub) { + // User wants to export selected blocks' generator stubs. + if (!generatorStub_filename) { + // User needs to enter filename. + alert('Please enter a filename for your generator stub(s) download.'); + } else { + // Get generator stub code in the selected language for the blocks. + var genStubs = this.tools.getGeneratorCode(blockXmlMap, + language); + // Get the correct file extension. + if (language == 'JavaScript') { + var fileType = 'javascript'; + } else { + var fileType = 'plain'; + } + // Download the file. + FactoryUtils.createAndDownloadFile( + genStubs, generatorStub_filename, fileType); + } + } + +}; + +/** + * Update the Exporter's block selector with block options generated from blocks + * stored in block library. + */ +BlockExporterController.prototype.updateSelector = function() { + // Get previously selected block types. + var oldSelectedTypes = this.view.getSelectedBlockTypes(); + + // Generate options from block library and assign to view. + this.blockOptions = this.tools.createBlockSelectorFromLib( + this.blockLibStorage, this.selectorID); + this.addBlockOptionSelectHandlers(); + this.view.setBlockOptions(this.blockOptions); + + // Select all previously selected blocks. + for (var i = 0, blockType; blockType = oldSelectedTypes[i]; i++) { + if (this.blockOptions[blockType]) { + this.view.select(blockType); + } + } + + this.view.listSelectedBlocks(); +}; + +/** + * Tied to the 'Clear Selected Blocks' button in the Block Exporter. + * Deselects all blocks in the selector and updates text accordingly. + */ +BlockExporterController.prototype.clearSelectedBlocks = function() { + this.view.deselectAllBlocks(); + this.view.listSelectedBlocks(); +}; + +/** + * Tied to the 'All Stored' button in the Block Exporter 'Select' dropdown. + * Selects all blocks stored in block library for export. + */ +BlockExporterController.prototype.selectAllBlocks = function() { + var allBlockTypes = this.blockLibStorage.getBlockTypes(); + for (var i = 0, blockType; blockType = allBlockTypes[i]; i++) { + this.view.select(blockType); + } + this.view.listSelectedBlocks(); +}; + +/** + * Returns the category xml containing all blocks in the block library. + * + * @return {Element} Xml for a category to be used in toolbox. + */ +BlockExporterController.prototype.getBlockLibraryCategory = function() { + return this.tools.generateCategoryFromBlockLib(this.blockLibStorage); +}; + +/** + * Add select handlers to each block option to update the view and the selected + * blocks accordingly. + */ +BlockExporterController.prototype.addBlockOptionSelectHandlers = function() { + var self = this; + + // Click handler for a block option. Toggles whether or not it's selected and + // updates helper text accordingly. + var updateSelectedBlockTypes_ = function(blockOption) { + // Toggle selected. + blockOption.setSelected(!blockOption.isSelected()); + + // Show currently selected blocks in helper text. + self.view.listSelectedBlocks(); + }; + + // Returns a block option select handler. + var makeBlockOptionSelectHandler_ = function(blockOption) { + return function() { + updateSelectedBlockTypes_(blockOption); + self.updatePreview(); + }; + }; + + // Assign a click handler to each block option. + for (var blockType in this.blockOptions) { + var blockOption = this.blockOptions[blockType]; + // Use an additional closure to correctly assign the tab callback. + blockOption.dom.addEventListener( + 'click', makeBlockOptionSelectHandler_(blockOption)); + } +}; + +/** + * Tied to the 'All Used' button in the Block Exporter's 'Select' button. + * Selects all blocks stored in block library and used in workspace factory. + */ +BlockExporterController.prototype.selectUsedBlocks = function() { + // Deselect all blocks. + this.view.deselectAllBlocks(); + + // Get list of block types that are in block library and used in workspace + // factory. + var storedBlockTypes = this.blockLibStorage.getBlockTypes(); + var sharedBlockTypes = []; + // Keep list of custom block types used but not in library. + var unstoredCustomBlockTypes = []; + + for (var i = 0, blockType; blockType = this.usedBlockTypes[i]; i++) { + if (storedBlockTypes.indexOf(blockType) != -1) { + sharedBlockTypes.push(blockType); + } else if (StandardCategories.coreBlockTypes.indexOf(blockType) == -1) { + unstoredCustomBlockTypes.push(blockType); + } + } + + // Select each shared block type. + for (var i = 0, blockType; blockType = sharedBlockTypes[i]; i++) { + this.view.select(blockType); + } + this.view.listSelectedBlocks(); + + if (unstoredCustomBlockTypes.length > 0){ + // Warn user to import block defifnitions and generator code for blocks + // not in their Block Library nor Blockly's standard library. + var blockTypesText = unstoredCustomBlockTypes.join(', '); + var customWarning = 'Custom blocks used in workspace factory but not ' + + 'stored in block library:\n ' + blockTypesText + + '\n\nDon\'t forget to include block definitions and generator code ' + + 'for these blocks.'; + alert(customWarning); + } +}; + +/** + * Set the array that holds the block types used in workspace factory. + * + * @param {!Array.} usedBlockTypes - Block types used in + */ +BlockExporterController.prototype.setUsedBlockTypes = + function(usedBlockTypes) { + this.usedBlockTypes = usedBlockTypes; +}; + +/** + * Updates preview code (block definitions and generator stubs) in the exporter + * preview to reflect selected blocks. + */ +BlockExporterController.prototype.updatePreview = function() { + // Generate preview code for selected blocks. + var blockDefs = this.getBlockDefinitionsOfSelected(); + var genStubs = this.getGeneratorStubsOfSelected(); + + // Update the text areas containing the code. + FactoryUtils.injectCode(blockDefs, 'blockDefs_textArea'); + FactoryUtils.injectCode(genStubs, 'genStubs_textArea'); +}; + +/** + * Returns a map of each selected block's type to its corresponding xml. + * + * @return {!Object} a map of each selected block's type (a string) to its + * corresponding xml element. + */ +BlockExporterController.prototype.getSelectedBlockXmlMap = function() { + var blockTypes = this.view.getSelectedBlockTypes(); + return this.blockLibStorage.getBlockXmlMap(blockTypes); +}; + +/** + * Get block definition code in the selected format for selected blocks. + * + * @return {!string} The concatenation of each selected block's language code + * in the format specified in export settings. + */ +BlockExporterController.prototype.getBlockDefinitionsOfSelected = function() { + // Get selected blocks' information. + var blockXmlMap = this.getSelectedBlockXmlMap(); + + // Get block definition code in the selected format for the blocks. + var definitionFormat = document.getElementById('exportFormat').value; + return this.tools.getBlockDefinitions(blockXmlMap, definitionFormat); +}; + +/** + * Get generator stubs in the selected language for selected blocks. + * + * @return {!string} The concatenation of each selected block's generator stub + * in the language specified in export settings. + */ +BlockExporterController.prototype.getGeneratorStubsOfSelected = function() { + // Get selected blocks' information. + var blockXmlMap = this.getSelectedBlockXmlMap(); + + // Get generator stub code in the selected language for the blocks. + var language = document.getElementById('exportLanguage').value; + return this.tools.getGeneratorCode(blockXmlMap, language); +}; + diff --git a/demos/blocklyfactory/block_exporter_tools.js b/demos/blocklyfactory/block_exporter_tools.js new file mode 100644 index 00000000..1396800a --- /dev/null +++ b/demos/blocklyfactory/block_exporter_tools.js @@ -0,0 +1,276 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Javascript for the BlockExporter Tools class, which generates + * block definitions and generator stubs for given block types. Also generates + * toolbox xml for the exporter's workspace. Depends on the FactoryUtils for + * its code generation functions. + * + * @author quachtina96 (Tina Quach) + */ +'use strict'; + +goog.provide('BlockExporterTools'); + +goog.require('FactoryUtils'); +goog.require('BlockOption'); +goog.require('goog.dom'); +goog.require('goog.dom.xml'); + +/** +* Block Exporter Tools Class +* @constructor +*/ +BlockExporterTools = function() { + // Create container for hidden workspace. + this.container = goog.dom.createDom('div', { + 'id': 'blockExporterTools_hiddenWorkspace' + }, ''); // Empty quotes for empty div. + // Hide hidden workspace. + this.container.style.display = 'none'; + goog.dom.appendChild(document.body, this.container); + /** + * Hidden workspace for the Block Exporter that holds pieces that make + * up the block + * @type {Blockly.Workspace} + */ + this.hiddenWorkspace = Blockly.inject(this.container.id, + {collapse: false, + media: '../../media/'}); +}; + +/** + * Get Blockly Block object from xml that encodes the blocks used to design + * the block. + * @private + * + * @param {!Element} xml - Xml element that encodes the blocks used to design + * the block. For example, the block xmls saved in block library. + * @return {!Blockly.Block} - Root block (factory_base block) which contains + * all information needed to generate block definition or null. + */ +BlockExporterTools.prototype.getRootBlockFromXml_ = function(xml) { + // Render xml in hidden workspace. + this.hiddenWorkspace.clear(); + Blockly.Xml.domToWorkspace(xml, this.hiddenWorkspace); + // Get root block. + var rootBlock = this.hiddenWorkspace.getTopBlocks()[0] || null; + return rootBlock; +}; + +/** + * Return the given language code of each block type in an array. + * + * @param {!Object} blockXmlMap - Map of block type to xml. + * @param {string} definitionFormat - 'JSON' or 'JavaScript' + * @return {string} The concatenation of each block's language code in the + * desired format. + */ +BlockExporterTools.prototype.getBlockDefinitions = + function(blockXmlMap, definitionFormat) { + var blockCode = []; + for (var blockType in blockXmlMap) { + var xml = blockXmlMap[blockType]; + if (xml) { + // Render and get block from hidden workspace. + var rootBlock = this.getRootBlockFromXml_(xml); + if (rootBlock) { + // Generate the block's definition. + var code = FactoryUtils.getBlockDefinition(blockType, rootBlock, + definitionFormat, this.hiddenWorkspace); + // Add block's definition to the definitions to return. + } else { + // Append warning comment and write to console. + var code = '// No block definition generated for ' + blockType + + '. Could not find root block in xml stored for this block.'; + console.log('No block definition generated for ' + blockType + + '. Could not find root block in xml stored for this block.'); + } + } else { + // Append warning comment and write to console. + var code = '// No block definition generated for ' + blockType + + '. Block was not found in Block Library Storage.'; + console.log('No block definition generated for ' + blockType + + '. Block was not found in Block Library Storage.'); + } + blockCode.push(code); + } + return blockCode.join("\n\n"); +}; + +/** + * Return the generator code of each block type in an array in a given language. + * + * @param {!Object} blockXmlMap - Map of block type to xml. + * @param {string} generatorLanguage - e.g.'JavaScript', 'Python', 'PHP', 'Lua', + * 'Dart' + * @return {string} The concatenation of each block's generator code in the + * desired format. + */ +BlockExporterTools.prototype.getGeneratorCode = + function(blockXmlMap, generatorLanguage) { + var multiblockCode = []; + // Define the custom blocks in order to be able to create instances of + // them in the exporter workspace. + this.addBlockDefinitions(blockXmlMap); + + for (var blockType in blockXmlMap) { + var xml = blockXmlMap[blockType]; + if (xml) { + // Render the preview block in the hidden workspace. + var tempBlock = + FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace); + // Get generator stub for the given block and add to generator code. + var blockGenCode = + FactoryUtils.getGeneratorStub(tempBlock, generatorLanguage); + } else { + // Append warning comment and write to console. + var blockGenCode = '// No generator stub generated for ' + blockType + + '. Block was not found in Block Library Storage.'; + console.log('No block generator stub generated for ' + blockType + + '. Block was not found in Block Library Storage.'); + } + multiblockCode.push(blockGenCode); + } + return multiblockCode.join("\n\n"); +}; + +/** + * Evaluates block definition code of each block in given object mapping + * block type to xml. Called in order to be able to create instances of the + * blocks in the exporter workspace. + * + * @param {!Object} blockXmlMap - Map of block type to xml. + */ +BlockExporterTools.prototype.addBlockDefinitions = function(blockXmlMap) { + var blockDefs = this.getBlockDefinitions(blockXmlMap, 'JavaScript'); + eval(blockDefs); +}; + +/** + * Pulls information about all blocks in the block library to generate xml + * for the selector workpace's toolbox. + * + * @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object. + * @return {!Element} Xml representation of the toolbox. + */ +BlockExporterTools.prototype.generateToolboxFromLibrary + = function(blockLibStorage) { + // Create DOM for XML. + var xmlDom = goog.dom.createDom('xml', { + 'id' : 'blockExporterTools_toolbox', + 'style' : 'display:none' + }); + + var allBlockTypes = blockLibStorage.getBlockTypes(); + // Object mapping block type to XML. + var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes); + + // Define the custom blocks in order to be able to create instances of + // them in the exporter workspace. + this.addBlockDefinitions(blockXmlMap); + + for (var blockType in blockXmlMap) { + // Get block. + var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace); + var category = FactoryUtils.generateCategoryXml([block], blockType); + xmlDom.appendChild(category); + } + + // If there are no blocks in library and the map is empty, append dummy + // category. + if (Object.keys(blockXmlMap).length == 0) { + var category = goog.dom.createDom('category'); + category.setAttribute('name','Next Saved Block'); + xmlDom.appendChild(category); + } + return xmlDom; +}; + +/** + * Generate xml for the workspace factory's category from imported block + * definitions. + * + * @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object. + * @return {!Element} Xml representation of a category. + */ +BlockExporterTools.prototype.generateCategoryFromBlockLib = + function(blockLibStorage) { + var allBlockTypes = blockLibStorage.getBlockTypes(); + // Object mapping block type to XML. + var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes); + + // Define the custom blocks in order to be able to create instances of + // them in the exporter workspace. + this.addBlockDefinitions(blockXmlMap); + + // Get array of defined blocks. + var blocks = []; + for (var blockType in blockXmlMap) { + var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace); + blocks.push(block); + } + + return FactoryUtils.generateCategoryXml(blocks,'Block Library'); +}; + +/** + * Generate selector dom from block library storage. For each block in the + * library, it has a block option, which consists of a checkbox, a label, + * and a fixed size preview workspace. + * + * @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object. + * @param {!string} blockSelectorID - ID of the div element that will contain + * the block options. + * @return {!Object} Map of block type to Block Option object. + */ +BlockExporterTools.prototype.createBlockSelectorFromLib = + function(blockLibStorage, blockSelectorID) { + // Object mapping each stored block type to XML. + var allBlockTypes = blockLibStorage.getBlockTypes(); + var blockXmlMap = blockLibStorage.getBlockXmlMap(allBlockTypes); + + // Define the custom blocks in order to be able to create instances of + // them in the exporter workspace. + this.addBlockDefinitions(blockXmlMap); + + var blockSelector = goog.dom.getElement(blockSelectorID); + // Clear the block selector. + goog.dom.removeChildren(blockSelector); + + // Append each block option's dom to the selector. + var blockOptions = Object.create(null); + for (var blockType in blockXmlMap) { + // Get preview block's xml. + var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace); + var previewBlockXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace); + + // Create block option, inject block into preview workspace, and append + // option to block selector. + var blockOpt = new BlockOption(blockSelector, blockType, previewBlockXml); + blockOpt.createDom(); + goog.dom.appendChild(blockSelector, blockOpt.dom); + blockOpt.showPreviewBlock(); + blockOptions[blockType] = blockOpt; + } + return blockOptions; +}; + diff --git a/demos/blocklyfactory/block_exporter_view.js b/demos/blocklyfactory/block_exporter_view.js new file mode 100644 index 00000000..5b703f38 --- /dev/null +++ b/demos/blocklyfactory/block_exporter_view.js @@ -0,0 +1,145 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Javascript for the Block Exporter View class. Reads from and + * manages a block selector through which users select blocks to export. + * + * @author quachtina96 (Tina Quach) + */ + +'use strict'; + +goog.provide('BlockExporterView'); + +goog.require('BlockExporterTools'); +goog.require('BlockOption'); +goog.require('goog.dom'); + +/** + * BlockExporter View Class + * @constructor + * + * @param {!Object} blockOptions - Map of block types to BlockOption objects. + */ +BlockExporterView = function(blockOptions) { + // Map of block types to BlockOption objects to select from. + this.blockOptions = blockOptions; +}; + +/** + * Set the block options in the selector of this instance of + * BlockExporterView. + * + * @param {!Object} blockOptions - Map of block types to BlockOption objects. + */ +BlockExporterView.prototype.setBlockOptions = function(blockOptions) { + this.blockOptions = blockOptions; +}; + +/** + * Updates the helper text. + * + * @param {string} newText - New helper text. + * @param {boolean} opt_append - True if appending to helper Text, false if + * replacing. + */ +BlockExporterView.prototype.updateHelperText = function(newText, opt_append) { + if (opt_append) { + goog.dom.getElement('helperText').textContent = + goog.dom.getElement('helperText').textContent + newText; + } else { + goog.dom.getElement('helperText').textContent = newText; + } +}; + +/** + * Updates the helper text to show list of currently selected blocks. + */ +BlockExporterView.prototype.listSelectedBlocks = function() { + + var selectedBlocksText = this.getSelectedBlockTypes().join(",\n "); + goog.dom.getElement('selectedBlocksText').textContent = selectedBlocksText; +}; + +/** + * Selects a given block type in the selector. + * + * @param {string} blockType - Type of block to selector. + */ +BlockExporterView.prototype.select = function(blockType) { + this.blockOptions[blockType].setSelected(true); +}; + +/** + * Deselects a block in the selector. + * + * @param {!Blockly.Block} block - Type of block to add to selector workspce. + */ +BlockExporterView.prototype.deselect = function(blockType) { + this.blockOptions[blockType].setSelected(false); +}; + + +/** + * Deselects all blocks. + */ +BlockExporterView.prototype.deselectAllBlocks = function() { + for (var blockType in this.blockOptions) { + this.deselect(blockType); + } +}; + +/** + * Given an array of selected blocks, selects these blocks in the view, marking + * the checkboxes accordingly. + * + * @param {Array.} blockTypes - Array of block types to select. + */ +BlockExporterView.prototype.setSelectedBlockTypes = function(blockTypes) { + for (var i = 0, blockType; blockType = blockTypes[i]; i++) { + this.select(blockType); + } +}; + +/** + * Returns array of selected blocks. + * + * @return {!Array.} Array of all selected block types. + */ +BlockExporterView.prototype.getSelectedBlockTypes = function() { + var selectedTypes = []; + for (var blockType in this.blockOptions) { + var blockOption = this.blockOptions[blockType]; + if (blockOption.isSelected()) { + selectedTypes.push(blockType); + } + } + return selectedTypes; +}; + +/** + * Centers the preview block of each block option in the exporter selector. + */ +BlockExporterView.prototype.centerPreviewBlocks = function() { + for (var blockType in this.blockOptions) { + this.blockOptions[blockType].centerBlock(); + } +}; diff --git a/demos/blocklyfactory/block_library_controller.js b/demos/blocklyfactory/block_library_controller.js new file mode 100644 index 00000000..1df8121a --- /dev/null +++ b/demos/blocklyfactory/block_library_controller.js @@ -0,0 +1,239 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Contains the code for Block Library Controller, which + * depends on Block Library Storage and Block Library UI. Provides the + * interfaces for the user to + * - save their blocks to the browser + * - re-open and edit saved blocks + * - delete blocks + * - clear their block library + * Depends on BlockFactory functions defined in factory.js. + * + * @author quachtina96 (Tina Quach) + */ +'use strict'; + +goog.provide('BlockLibraryController'); + +goog.require('BlockLibraryStorage'); +goog.require('BlockLibraryView'); +goog.require('BlockFactory'); + +/** + * Block Library Controller Class + * @constructor + * + * @param {string} blockLibraryName - Desired name of Block Library, also used + * to create the key for where it's stored in local storage. + * @param {!BlockLibraryStorage} opt_blockLibraryStorage - optional storage + * object that allows user to import a block library. + */ +BlockLibraryController = function(blockLibraryName, opt_blockLibraryStorage) { + this.name = blockLibraryName; + // Create a new, empty Block Library Storage object, or load existing one. + this.storage = opt_blockLibraryStorage || new BlockLibraryStorage(this.name); +}; + +/** + * Returns the block type of the block the user is building. + * @private + * + * @return {string} The current block's type. + */ +BlockLibraryController.prototype.getCurrentBlockType_ = function() { + var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace); + var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase(); + // Replace white space with underscores + return blockType.replace(/\W/g, '_').replace(/^(\d)/, '_\\1'); +}; + +/** + * Removes current block from Block Library + * + * @param {string} blockType - Type of block. + */ +BlockLibraryController.prototype.removeFromBlockLibrary = function() { + var blockType = this.getCurrentBlockType_(); + this.storage.removeBlock(blockType); + this.storage.saveToLocalStorage(); + this.populateBlockLibrary(); + // Show default block. + BlockFactory.showStarterBlock(); +}; + +/** + * Updates the workspace to show the block user selected from library + * + * @param {string} blockType - Block to edit on block factory. + */ +BlockLibraryController.prototype.openBlock = function(blockType) { + var xml = this.storage.getBlockXml(blockType); + BlockFactory.mainWorkspace.clear(); + Blockly.Xml.domToWorkspace(xml, BlockFactory.mainWorkspace); +}; + +/** + * Returns type of block selected from library. + * + * @param {Element} blockLibraryDropdown - The block library dropdown. + * @return {string} Type of block selected. + */ +BlockLibraryController.prototype.getSelectedBlockType = + function(blockLibraryDropdown) { + return BlockLibraryView.getSelected(blockLibraryDropdown); +}; + +/** + * Confirms with user before clearing the block library in local storage and + * updating the dropdown and displaying the starter block (factory_base). + */ +BlockLibraryController.prototype.clearBlockLibrary = function() { + var check = confirm( + 'Click OK to clear your block library.'); + if (check) { + // Clear Block Library Storage. + this.storage.clear(); + this.storage.saveToLocalStorage(); + // Update dropdown. + BlockLibraryView.clearOptions('blockLibraryDropdown'); + // Add a default, blank option to dropdown for when no block from library is + // selected. + BlockLibraryView.addDefaultOption('blockLibraryDropdown'); + // Show default block. + BlockFactory.showStarterBlock(); + } +}; + +/** + * Saves current block to local storage and updates dropdown. + */ +BlockLibraryController.prototype.saveToBlockLibrary = function() { + var blockType = this.getCurrentBlockType_(); + // If block under that name already exists, confirm that user wants to replace + // saved block. + if (this.isInBlockLibrary(blockType)) { + var replace = confirm('You already have a block called ' + blockType + + ' in your library. Click OK to replace.'); + if (!replace) { + // Do not save if user doesn't want to replace the saved block. + return; + } + } + + // Create block xml. + var xmlElement = goog.dom.createDom('xml'); + var block = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace); + xmlElement.appendChild(Blockly.Xml.blockToDomWithXY(block)); + + // Save block. + this.storage.addBlock(blockType, xmlElement); + this.storage.saveToLocalStorage(); + + // Show saved block without other stray blocks sitting in Block Factory's + // main workspace. + this.openBlock(blockType); + + // Do not add another option to dropdown if replacing. + if (replace) { + return; + } + BlockLibraryView.addOption( + blockType, blockType, 'blockLibraryDropdown', true, true); +}; + +/** + * Checks to see if the given blockType is already in Block Library + * + * @param {string} blockType - Type of block. + * @return {boolean} Boolean indicating whether or not block is in the library. + */ +BlockLibraryController.prototype.isInBlockLibrary = function(blockType) { + var blockLibrary = this.storage.blocks; + return (blockType in blockLibrary && blockLibrary[blockType] != null); +}; + +/** + * Populates the dropdown menu. + */ +BlockLibraryController.prototype.populateBlockLibrary = function() { + BlockLibraryView.clearOptions('blockLibraryDropdown'); + // Add a default, blank option to dropdown for when no block from library is + // selected. + BlockLibraryView.addDefaultOption('blockLibraryDropdown'); + // Add option for each saved block. + var blockLibrary = this.storage.blocks; + for (var block in blockLibrary) { + // Make sure the block wasn't deleted. + if (blockLibrary[block] != null) { + BlockLibraryView.addOption( + block, block, 'blockLibraryDropdown', false, true); + } + } +}; + +/** + * Return block library mapping block type to xml. + * + * @return {Object} Object mapping block type to xml text. + */ +BlockLibraryController.prototype.getBlockLibrary = function() { + return this.storage.getBlockXmlTextMap(); +}; + +/** + * Set the block library storage object from which exporter exports. + * + * @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage + * object. + */ +BlockLibraryController.prototype.setBlockLibraryStorage + = function(blockLibStorage) { + this.storage = blockLibStorage; +}; + +/** + * Get the block library storage object from which exporter exports. + * + * @return {!BlockLibraryStorage} blockLibStorage - Block Library Storage object + * that stores the blocks. + */ +BlockLibraryController.prototype.getBlockLibraryStorage = function() { + return this.blockLibStorage; +}; + +/** + * Get the block library storage object from which exporter exports. + * + * @return {boolean} True if the Block Library is empty, false otherwise. + */ +BlockLibraryController.prototype.hasEmptyBlockLibrary = function() { + return this.storage.isEmpty(); +}; + +/** + * Get all block types stored in block library. + * + * @return {!Array} Array of block types. + */ +BlockLibraryController.prototype.getStoredBlockTypes = function() { + return this.storage.getBlockTypes(); +}; diff --git a/demos/blocklyfactory/block_library_storage.js b/demos/blocklyfactory/block_library_storage.js new file mode 100644 index 00000000..c4361959 --- /dev/null +++ b/demos/blocklyfactory/block_library_storage.js @@ -0,0 +1,178 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Javascript for Block Library's Storage Class. + * Depends on Block Library for its namespace. + * + * @author quachtina96 (Tina Quach) + */ + +'use strict'; + +goog.provide('BlockLibraryStorage'); + +/** + * Represents a block library's storage. + * @constructor + * + * @param {string} blockLibraryName - Desired name of Block Library, also used + * to create the key for where it's stored in local storage. + * @param {Object} opt_blocks - Object mapping block type to xml. + */ +BlockLibraryStorage = function(blockLibraryName, opt_blocks) { + // Add prefix to this.name to avoid collisions in local storage. + this.name = 'BlockLibraryStorage.' + blockLibraryName; + if (!opt_blocks) { + // Initialize this.blocks by loading from local storage. + this.loadFromLocalStorage(); + if (this.blocks == null) { + this.blocks = Object.create(null); + // The line above is equivalent of {} except that this object is TRULY + // empty. It doesn't have built-in attributes/functions such as length or + // toString. + this.saveToLocalStorage(); + } + } else { + this.blocks = opt_blocks; + this.saveToLocalStorage(); + } +}; + +/** + * Reads the named block library from local storage and saves it in this.blocks. + */ +BlockLibraryStorage.prototype.loadFromLocalStorage = function() { + // goog.global is synonymous to window, and allows for flexibility + // between browsers. + var object = goog.global.localStorage[this.name]; + this.blocks = object ? JSON.parse(object) : null; +}; + +/** + * Writes the current block library (this.blocks) to local storage. + */ +BlockLibraryStorage.prototype.saveToLocalStorage = function() { + goog.global.localStorage[this.name] = JSON.stringify(this.blocks); +}; + +/** + * Clears the current block library. + */ +BlockLibraryStorage.prototype.clear = function() { + this.blocks = Object.create(null); + // The line above is equivalent of {} except that this object is TRULY + // empty. It doesn't have built-in attributes/functions such as length or + // toString. +}; + +/** + * Saves block to block library. + * + * @param {string} blockType - Type of block. + * @param {Element} blockXML - The block's XML pulled from workspace. + */ +BlockLibraryStorage.prototype.addBlock = function(blockType, blockXML) { + var prettyXml = Blockly.Xml.domToPrettyText(blockXML); + this.blocks[blockType] = prettyXml; +}; + +/** + * Removes block from current block library (this.blocks). + * + * @param {string} blockType - Type of block. + */ +BlockLibraryStorage.prototype.removeBlock = function(blockType) { + delete this.blocks[blockType]; +}; + +/** + * Returns the xml of given block type stored in current block library + * (this.blocks). + * + * @param {string} blockType - Type of block. + * @return {Element} The xml that represents the block type or null. + */ +BlockLibraryStorage.prototype.getBlockXml = function(blockType) { + var xml = this.blocks[blockType] || null; + if (xml) { + var xml = Blockly.Xml.textToDom(xml); + } + return xml; +}; + + +/** + * Returns map of each block type to its corresponding xml stored in current + * block library (this.blocks). + * + * @param {Array.} blockTypes - Types of blocks. + * @return {!Object} Map of block type to corresponding xml. + */ +BlockLibraryStorage.prototype.getBlockXmlMap = function(blockTypes) { + var blockXmlMap = {}; + for (var i = 0; i < blockTypes.length; i++) { + var blockType = blockTypes[i]; + var xml = this.getBlockXml(blockType); + blockXmlMap[blockType] = xml; + } + return blockXmlMap; +}; + +/** + * Returns array of all block types stored in current block library. + * + * @return {!Array.} Array of block types stored in library. + */ +BlockLibraryStorage.prototype.getBlockTypes = function() { + return Object.keys(this.blocks); +}; + +/** + * Checks to see if block library is empty. + * + * @return {boolean} True if empty, false otherwise. + */ +BlockLibraryStorage.prototype.isEmpty = function() { + for (var blockType in this.blocks) { + return false; + } + return true; +}; + +/** + * Returns array of all block types stored in current block library. + * + * @return {!Array.} Map of block type to corresponding xml text. + */ +BlockLibraryStorage.prototype.getBlockXmlTextMap = function() { + return this.blocks; +}; + +/** + * Returns boolean of whether or not a given blockType is stored in block + * library. + * + * @param {string} blockType - Type of block. + * @return {boolean} Whether or not blockType is stored in block library. + */ +BlockLibraryStorage.prototype.has = function(blockType) { + return this.blocks[blockType] ? true : false; +}; diff --git a/demos/blocklyfactory/block_library_view.js b/demos/blocklyfactory/block_library_view.js new file mode 100644 index 00000000..151055a4 --- /dev/null +++ b/demos/blocklyfactory/block_library_view.js @@ -0,0 +1,117 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Javascript for Block Library's UI for pulling blocks from the + * Block Library's storage to edit in Block Factory. + * + * @author quachtina96 (Tina Quach) + */ + +'use strict'; + +goog.provide('BlockLibraryView'); + +/** + * Creates a node of a given element type and appends to the node with given id. + * + * @param {string} optionIdentifier - String used to identify option. + * @param {string} optionText - Text to display in the dropdown for the option. + * @param {string} dropdownID - ID for HTML select element. + * @param {boolean} selected - Whether or not the option should be selected on + * the dropdown. + * @param {boolean} enabled - Whether or not the option should be enabled. + */ +BlockLibraryView.addOption + = function(optionIdentifier, optionText, dropdownID, selected, enabled) { + var dropdown = document.getElementById(dropdownID); + var option = document.createElement('option'); + // The value attribute of a dropdown's option is not visible in the UI, but is + // useful for identifying different options that may have the same text. + option.value = optionIdentifier; + // The text attribute is what the user sees in the dropdown for the option. + option.text = optionText; + option.selected = selected; + option.disabled = !enabled; + dropdown.add(option); +}; + +/** + * Adds a default, blank option to dropdown for when no block from library is + * selected. + * + * @param {string} dropdownID - ID of HTML select element + */ +BlockLibraryView.addDefaultOption = function(dropdownID) { + BlockLibraryView.addOption( + 'BLOCK_LIBRARY_DEFAULT_BLANK', '', dropdownID, true, false); +}; + +/** + * Selects the default, blank option in dropdown identified by given ID. + * + * @param {string} dropdownID - ID of HTML select element + */ +BlockLibraryView.selectDefaultOption = function(dropdownID) { + var dropdown = document.getElementById(dropdownID); + // Deselect currently selected option. + var index = dropdown.selectedIndex; + dropdown.options[index].selected = false; + // Select default option, always the first in the dropdown. + var defaultOption = dropdown.options[0]; + defaultOption.selected = true; +}; + +/** + * Returns block type of selected block. + * + * @param {Element} dropdown - HTML select element. + * @return {string} Type of block selected. + */ +BlockLibraryView.getSelected = function(dropdown) { + var index = dropdown.selectedIndex; + return dropdown.options[index].value; +}; + +/** + * Removes option currently selected in dropdown from dropdown menu. + * + * @param {string} dropdownID - ID of HTML select element within which to find + * the selected option. + */ +BlockLibraryView.removeSelectedOption = function(dropdownID) { + var dropdown = document.getElementById(dropdownID); + if (dropdown) { + dropdown.remove(dropdown.selectedIndex); + } +}; + +/** + * Removes all options from dropdown. + * + * @param {string} dropdownID - ID of HTML select element to clear options of. + */ +BlockLibraryView.clearOptions = function(dropdownID) { + var dropdown = document.getElementById(dropdownID); + while (dropdown.length > 0) { + dropdown.remove(dropdown.length - 1); + } +}; + diff --git a/demos/blocklyfactory/block_option.js b/demos/blocklyfactory/block_option.js new file mode 100644 index 00000000..265de70c --- /dev/null +++ b/demos/blocklyfactory/block_option.js @@ -0,0 +1,181 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Javascript for the BlockOption class, used to represent each of + * the various blocks that you may select. Each block option has a checkbox, + * a label, and a preview workspace through which to view the block. + * + * @author quachtina96 (Tina Quach) + */ +'use strict'; + +goog.provide('BlockOption'); +goog.require('goog.dom'); + + /** + * BlockOption Class + * A block option includes checkbox, label, and div element that shows a preview + * of the block. + * @constructor + * + * @param {!Element} blockSelector - Scrollable div that will contain the + * block options for the selector. + * @param {!string} blockType - Type of block for which to create an option. + * @param {!Element} previewBlockXml - Xml element containing the preview block. + */ +var BlockOption = function(blockSelector, blockType, previewBlockXml) { + // The div to contain the block option. + this.blockSelector = blockSelector; + // The type of block represented by the option. + this.blockType = blockType; + // The checkbox for the option. Set in createDom. + this.checkbox = null; + // The dom for the option. Set in createDom. + this.dom = null; + // Xml element containing the preview block. + this.previewBlockXml = previewBlockXml; + // Workspace containing preview of block. Set upon injection of workspace in + // showPreviewBlock. + this.previewWorkspace = null; + // Whether or not block the option is selected. + this.selected = false; + // Using this.selected rather than this.checkbox.checked allows for proper + // handling of click events on the block option; Without this, clicking + // directly on the checkbox does not toggle selection. +}; + +/** + * Creates the dom for a single block option. Includes checkbox, label, and div + * in which to inject the preview block. + * + * @return {!Element} Root node of the selector dom which consists of a + * checkbox, a label, and a fixed size preview workspace per block. + */ +BlockOption.prototype.createDom = function() { + // Create the div for the block option. + var blockOptContainer = goog.dom.createDom('div', { + 'id': this.blockType, + 'class': 'blockOption' + }, ''); // Empty quotes for empty div. + + // Create and append div in which to inject the workspace for viewing the + // block option. + var blockOptionPreview = goog.dom.createDom('div', { + 'id' : this.blockType + '_workspace', + 'class': 'blockOption_preview' + }, ''); + goog.dom.appendChild(blockOptContainer,blockOptionPreview); + + // Create and append container to hold checkbox and label. + var checkLabelContainer = goog.dom.createDom('div', { + 'class': 'blockOption_checkLabel' + }, ''); + goog.dom.appendChild(blockOptContainer,checkLabelContainer); + + // Create and append container for checkbox. + var checkContainer = goog.dom.createDom('div', { + 'class': 'blockOption_check' + }, ''); + goog.dom.appendChild(checkLabelContainer, checkContainer); + + // Create and append checkbox. + this.checkbox = goog.dom.createDom('input', { + 'type': 'checkbox', + 'id': this.blockType + '_check' + }, ''); + goog.dom.appendChild(checkContainer, this.checkbox); + + // Create and append container for block label. + var labelContainer = goog.dom.createDom('div', { + 'class': 'blockOption_label' + }, ''); + goog.dom.appendChild(checkLabelContainer, labelContainer); + + // Create and append text node for the label. + var labelText = goog.dom.createDom('p', { + 'id': this.blockType + '_text' + }, this.blockType); + goog.dom.appendChild(labelContainer, labelText); + + this.dom = blockOptContainer; + return this.dom; +}; + +/** + * Injects a workspace containing the block into the block option's preview div. + */ +BlockOption.prototype.showPreviewBlock = function() { + // Get ID of preview workspace. + var blockOptPreviewID = this.dom.id + '_workspace'; + + // Inject preview block. + var workspace = Blockly.inject(blockOptPreviewID, {readOnly:true}); + Blockly.Xml.domToWorkspace(this.previewBlockXml, workspace); + this.previewWorkspace = workspace; + + // Center the preview block in the workspace. + this.centerBlock(); +}; + +/** + * Centers the preview block in the workspace. + */ +BlockOption.prototype.centerBlock = function() { + // Get metrics. + var block = this.previewWorkspace.getTopBlocks()[0]; + var blockMetrics = block.getHeightWidth(); + var blockCoordinates = block.getRelativeToSurfaceXY(); + var workspaceMetrics = this.previewWorkspace.getMetrics(); + + // Calculate new coordinates. + var x = workspaceMetrics.viewWidth/2 - blockMetrics['width']/2 - + blockCoordinates.x; + var y = workspaceMetrics.viewHeight/2 - blockMetrics['height']/2 - + blockCoordinates.y; + + // Move block. + block.moveBy(x, y); +}; + +/** + * Selects or deselects the block option. + * + * @param {!boolean} selected - True if selecting option, false if deselecting + * option. + */ +BlockOption.prototype.setSelected = function(selected) { + this.selected = selected; + if (this.checkbox) { + this.checkbox.checked = selected; + } +}; + +/** + * Returns boolean telling whether or not block is selected. + * + * @return {!boolean} True if selecting option, false if deselecting + * option. + */ +BlockOption.prototype.isSelected = function() { + return this.selected; +}; + + diff --git a/demos/blocklyfactory/factory.css b/demos/blocklyfactory/factory.css new file mode 100644 index 00000000..810b4741 --- /dev/null +++ b/demos/blocklyfactory/factory.css @@ -0,0 +1,518 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +html, body { + height: 100%; + min-height: 375px; +} + +body { + background-color: #fff; + font-family: sans-serif; + margin: 0 5px; + overflow: hidden +} + +h1 { + font-weight: normal; + font-size: 140%; +} + +h3 { + margin-top: 5px; + margin-bottom: 0; +} + +table { + border: none; + border-collapse: collapse; + height: 100%; + margin: 0; + padding: 0; + width: 100%; +} + +td { + vertical-align: top; + padding: 0; +} + +p { + display: block; + -webkit-margin-before: 0em; + -webkit-margin-after: 0em; + -webkit-margin-start: 0px; + -webkit-margin-end: 0px; + padding: 5px 0px; +} + +#blockly { + position: fixed; +} + +#blocklyMask { + background-color: #000; + cursor: not-allowed; + display: none; + position: fixed; + opacity: 0.2; + z-index: 9; +} + +#preview { + position: absolute; +} + +pre, +#languageTA { + border: #ddd 1px solid; + margin-top: 0; + position: absolute; + overflow: scroll; +} + +#languageTA { + display: none; + font: 10pt monospace; +} + +.downloadButton { + padding: 5px; +} + +button:disabled, .buttonStyle:disabled { + opacity: 0.6; +} + +button>*, .buttonStyle>* { + opacity: 1; + vertical-align: text-bottom; +} + +button, .buttonStyle { + border-radius: 4px; + border: 1px solid #ddd; + background-color: #eee; + color: #000; + padding: 10px; + margin: 10px 5px; + font-size: small; +} + +.buttonStyle:hover:not(:disabled), button:hover:not(:disabled) { + box-shadow: 2px 2px 5px #888; +} + +.buttonStyle:hover:not(:disabled)>*, button:hover:not(:disabled)>* { + opacity: 1; +} + +#linkButton { + display: none; +} + +#helpButton { + float: right; +} + +#blockFactoryContent { + height: 87%; + width: 100%; +} + +#blockFactoryPreview { + height: 100%; + width: 100%; +} + +#blockLibraryContainer { + vertical-align: bottom; +} + +#blockLibraryControls { + text-align: right; + vertical-align: middle; +} + +#previewContainer { + vertical-align: bottom; +} + +#buttonContainer { + text-align: right; + vertical-align: middle; +} + +#files { + position: absolute; + visibility: hidden; +} + +.toolbox { + display: none; +} + +#blocklyWorkspaceContainer { + height: 95%; + padding: 2px; + width: 50%; +} + +#workspaceFactoryContent { + clear: both; + display: none; + height: 100%; +} + +/* Exporter */ + +#blockLibraryExporter { + clear: both; + display: none; + height: 100%; +} + +#exportSelector { + display: inline-block; + float: left; + height: 70%; + width: 30%; +} + +#exportSettings { + float: left; + overflow: hidden; + padding-left: 16px; + width: 20%; +} + +#selectedBlocksTextContainer { + max-height: 200px; + overflow-y: scroll; + padding-bottom: 2em; +} + +::-webkit-scrollbar { + -webkit-appearance: none; + width: 7px; +} + +::-webkit-scrollbar-thumb { + border-radius: 4px; + background-color: #ccc; + -webkit-box-shadow: 0 0 1px rgba(255,255,255,.5); +} + +.subsettings { + margin: 0px 25px; +} + +#exporterHiddenWorkspace { + display: none; +} + +#exportPreview { + float: right; + height: 90%; + overflow: hidden; + width: 45%; +} + +.exportPreviewTextArea { + display: block; + float: right; + height: 40%; + width: 100%; +} + +#genStubs_textArea, #blockDefs_textArea { + display: block; + height: 80%; + margin-right: 20px; + max-height: 300px; + overflow: scroll; + position: static; +} + +#blockDefs_label, #genStubs_label { + display: block; +} + +#blockSelector { + background-color: #eee; + border: 1px solid lightgrey; + width: 80%; + height: 90%; + overflow-y: scroll; + position: relative; +} + +/* Exporter Block Option */ + +.blockOption { + background-color: #eee; + padding: 15px 20px; + width: 95%; +} + +.blockOption_check_label { + position: relative; +} + +.blockOption_check { + float: left; + padding: 4px; +} + +.blockOption_label { + float: left; + max-width: inherit; + overflow-y: scroll; + word-wrap: break-word; +} + +.blockOption_preview { + height: 100px; + padding-top: 10px; + width: 90%; +} + +/* Tabs */ + +.tab { + float: left; + padding: 5px 19px; +} + +.tab:hover:not(.tabon){ + background-color: #e8e8e8; +} + +.tab.tabon { + background-color: #ccc; +} + +.tab.taboff { + cursor: pointer; +} + +#tabContainer { + background-color: #f8f8f8; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + display: table; + width: 100%; +} + +/* Workspace Factory */ + +section { + float: left; +} + +aside { + float: right; +} + +#categoryTable>table { + border: 1px solid #ccc; + border-bottom: none; + width: auto; +} + +td.tabon { + background-color: #ccc; + border-bottom-color: #ccc; + padding: 5px 19px; +} + +td.taboff { + cursor: pointer; + padding: 5px 19px; +} + +td.taboff:hover { + background-color: #eee; +} + +.large { + font-size: large; +} + +td { + padding: 0; + vertical-align: top; +} + +.inputfile { + height: 0; + opacity: 0; + overflow: hidden; + position: absolute; + width: 0; + z-index: -1; +} + +#workspaceTabs { + background-color: #f8f8f8; + border: 1px solid #ccc; + display: table; +} + +#toolbox_section { + height: 405px; + width: 60%; + position: relative; +} + +#toolbox_blocks { + height: 100%; + width: 100%; +} + +#preview_blocks { + height: 395px; + width: 100%; +} + +#createDiv { + padding: 0.5%; + width: 60%; +} + +#previewDiv { + border: 10px solid #eee; + margin-right: 0.5%; + width: 35%; +} + +#previewBorder { + border: 5px solid #ddd; + padding: 2%; +} + +#toolbox_div, #preload_div { + margin-right: 5%; + width: 35%; +} + +#workspace_options { + max-height: 250px; + overflow-y: scroll; +} + +#disable_div { + background-color: white; + height: 100%; + left: 0; + opacity: .5; + position: absolute; + top: 0; + width: 100%; + z-index: -1; /* Start behind workspace */ +} + +/* 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 #000000; + font-size: 1px; +} + +.goog-palette-colorswatch { + border: 1px solid #000000; + 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; +} + +/* The container
    - needed to position the dropdown content */ +.dropdown { + position: relative; + display: inline-block; +} + +/* 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; +} + +/* Links inside the dropdown */ +.dropdown-content a, .dropdown-content label { + color: black; + display: block; + font-size: small; + padding: 12px 16px; + text-decoration: none; +} + +/* Change color of dropdown links on hover. */ +.dropdown-content a:hover, .dropdown-content label:hover { + background-color: #f1f1f1 +} + +/* Show the dropdown menu */ +.show { + display: block; +} + +.shadowBlock>.blocklyPath { + fill-opacity: .5; + stroke-opacity: .5; +} + +.shadowBlock>.blocklyPathLight, +.shadowBlock>.blocklyPathDark { + display: none; +} diff --git a/demos/blocklyfactory/factory.js b/demos/blocklyfactory/factory.js new file mode 100644 index 00000000..be096f8f --- /dev/null +++ b/demos/blocklyfactory/factory.js @@ -0,0 +1,252 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview JavaScript for Blockly's Block Factory application through + * which users can build blocks using a visual interface and dynamically + * generate a preview block and starter code for the block (block definition and + * generator stub. Uses the Block Factory namespace. Depends on the FactoryUtils + * for its code generation functions. + * + * @author fraser@google.com (Neil Fraser), quachtina96 (Tina Quach) + */ +'use strict'; + +/** + * Namespace for Block Factory. + */ +goog.provide('BlockFactory'); + +goog.require('FactoryUtils'); +goog.require('StandardCategories'); + + +/** + * Workspace for user to build block. + * @type {Blockly.Workspace} + */ +BlockFactory.mainWorkspace = null; + +/** + * Workspace for preview of block. + * @type {Blockly.Workspace} + */ +BlockFactory.previewWorkspace = null; + +/** + * Name of block if not named. + */ +BlockFactory.UNNAMED = 'unnamed'; + +/** + * Existing direction ('ltr' vs 'rtl') of preview. + */ +BlockFactory.oldDir = null; + +/** + * Inject code into a pre tag, with syntax highlighting. + * Safe from HTML/script injection. + * @param {string} code Lines of code. + * @param {string} id ID of
     element to inject into.
    + */
    +FactoryUtils.injectCode = function(code, id) {
    +  var pre = document.getElementById(id);
    +  pre.textContent = code;
    +  code = pre.innerHTML;
    +  code = prettyPrintOne(code, 'js');
    +  pre.innerHTML = code;
    +};
    +
    +/**
    + * Change the language code format.
    + */
    +BlockFactory.formatChange = function() {
    +  var mask = document.getElementById('blocklyMask');
    +  var languagePre = document.getElementById('languagePre');
    +  var languageTA = document.getElementById('languageTA');
    +  if (document.getElementById('format').value == 'Manual') {
    +    Blockly.hideChaff();
    +    mask.style.display = 'block';
    +    languagePre.style.display = 'none';
    +    languageTA.style.display = 'block';
    +    var code = languagePre.textContent.trim();
    +    languageTA.value = code;
    +    languageTA.focus();
    +    BlockFactory.updatePreview();
    +  } else {
    +    mask.style.display = 'none';
    +    languageTA.style.display = 'none';
    +    languagePre.style.display = 'block';
    +    BlockFactory.updateLanguage();
    +  }
    +  BlockFactory.disableEnableLink();
    +};
    +
    +/**
    + * Update the language code based on constructs made in Blockly.
    + */
    +BlockFactory.updateLanguage = function() {
    +  var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
    +  if (!rootBlock) {
    +    return;
    +  }
    +  var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
    +  if (!blockType) {
    +    blockType = BlockFactory.UNNAMED;
    +  }
    +  var format = document.getElementById('format').value;
    +  var code = FactoryUtils.getBlockDefinition(blockType, rootBlock, format,
    +      BlockFactory.mainWorkspace);
    +  FactoryUtils.injectCode(code, 'languagePre');
    +  BlockFactory.updatePreview();
    +};
    +
    +/**
    + * Update the generator code.
    + * @param {!Blockly.Block} block Rendered block in preview workspace.
    + */
    +BlockFactory.updateGenerator = function(block) {
    +  var language = document.getElementById('language').value;
    +  var generatorStub = FactoryUtils.getGeneratorStub(block, language);
    +  FactoryUtils.injectCode(generatorStub, 'generatorPre');
    +};
    +
    +/**
    + * Update the preview display.
    + */
    +BlockFactory.updatePreview = function() {
    +  // Toggle between LTR/RTL if needed (also used in first display).
    +  var newDir = document.getElementById('direction').value;
    +  if (BlockFactory.oldDir != newDir) {
    +    if (BlockFactory.previewWorkspace) {
    +      BlockFactory.previewWorkspace.dispose();
    +    }
    +    var rtl = newDir == 'rtl';
    +    BlockFactory.previewWorkspace = Blockly.inject('preview',
    +        {rtl: rtl,
    +         media: '../../media/',
    +         scrollbars: true});
    +    BlockFactory.oldDir = newDir;
    +  }
    +  BlockFactory.previewWorkspace.clear();
    +
    +  // Fetch the code and determine its format (JSON or JavaScript).
    +  var format = document.getElementById('format').value;
    +  if (format == 'Manual') {
    +    var code = document.getElementById('languageTA').value;
    +    // If the code is JSON, it will parse, otherwise treat as JS.
    +    try {
    +      JSON.parse(code);
    +      format = 'JSON';
    +    } catch (e) {
    +      format = 'JavaScript';
    +    }
    +  } else {
    +    var code = document.getElementById('languagePre').textContent;
    +  }
    +  if (!code.trim()) {
    +    // Nothing to render.  Happens while cloud storage is loading.
    +    return;
    +  }
    +
    +  // Backup Blockly.Blocks object so that main workspace and preview don't
    +  // collide if user creates a 'factory_base' block, for instance.
    +  var backupBlocks = Blockly.Blocks;
    +  try {
    +    // Make a shallow copy.
    +    Blockly.Blocks = Object.create(null);
    +    for (var prop in backupBlocks) {
    +      Blockly.Blocks[prop] = backupBlocks[prop];
    +    }
    +
    +    if (format == 'JSON') {
    +      var json = JSON.parse(code);
    +      Blockly.Blocks[json.type || BlockFactory.UNNAMED] = {
    +        init: function() {
    +          this.jsonInit(json);
    +        }
    +      };
    +    } else if (format == 'JavaScript') {
    +      eval(code);
    +    } else {
    +      throw 'Unknown format: ' + format;
    +    }
    +
    +    // Look for a block on Blockly.Blocks that does not match the backup.
    +    var blockType = null;
    +    for (var type in Blockly.Blocks) {
    +      if (typeof Blockly.Blocks[type].init == 'function' &&
    +          Blockly.Blocks[type] != backupBlocks[type]) {
    +        blockType = type;
    +        break;
    +      }
    +    }
    +    if (!blockType) {
    +      return;
    +    }
    +
    +    // Create the preview block.
    +    var previewBlock = BlockFactory.previewWorkspace.newBlock(blockType);
    +    previewBlock.initSvg();
    +    previewBlock.render();
    +    previewBlock.setMovable(false);
    +    previewBlock.setDeletable(false);
    +    previewBlock.moveBy(15, 10);
    +    BlockFactory.previewWorkspace.clearUndo();
    +    BlockFactory.updateGenerator(previewBlock);
    +
    +    // Warn user only if their block type is already exists in Blockly's
    +    // standard library.
    +    var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
    +    if (StandardCategories.coreBlockTypes.indexOf(blockType) != -1) {
    +      rootBlock.setWarningText('A standard Blockly.Block already exists ' +
    +          'under this name.');
    +    } else {
    +      rootBlock.setWarningText(null);
    +    }
    +  } finally {
    +    Blockly.Blocks = backupBlocks;
    +  }
    +};
    +
    +/**
    + * Disable link and save buttons if the format is 'Manual', enable otherwise.
    + */
    +BlockFactory.disableEnableLink = function() {
    +  var linkButton = document.getElementById('linkButton');
    +  var saveBlockButton = document.getElementById('localSaveButton');
    +  var saveToLibButton = document.getElementById('saveToBlockLibraryButton');
    +  var disabled = document.getElementById('format').value == 'Manual';
    +  linkButton.disabled = disabled;
    +  saveBlockButton.disabled = disabled;
    +  saveToLibButton.disabled = disabled;
    +};
    +
    +/**
    + * Render starter block (factory_base).
    + */
    +BlockFactory.showStarterBlock = function() {
    +    BlockFactory.mainWorkspace.clear();
    +    var xml = '';
    +    Blockly.Xml.domToWorkspace(
    +        Blockly.Xml.textToDom(xml), BlockFactory.mainWorkspace);
    +};
    +
    diff --git a/demos/blocklyfactory/factory_utils.js b/demos/blocklyfactory/factory_utils.js
    new file mode 100644
    index 00000000..1dabc018
    --- /dev/null
    +++ b/demos/blocklyfactory/factory_utils.js
    @@ -0,0 +1,888 @@
    +/**
    + * @license
    + * Visual Blocks Editor
    + *
    + * Copyright 2016 Google Inc.
    + * https://developers.google.com/blockly/
    + *
    + * 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.
    + */
    +
    +/**
    + * @fileoverview FactoryUtils is a namespace that holds block starter code
    + * generation functions shared by the Block Factory, Workspace Factory, and
    + * Exporter applications within Blockly Factory. Holds functions to generate
    + * block definitions and generator stubs and to create and download files.
    + *
    + * @author fraser@google.com (Neil Fraser), quachtina96 (Tina Quach)
    + */
    + 'use strict';
    +
    +/**
    + * Namespace for FactoryUtils.
    + */
    +goog.provide('FactoryUtils');
    +
    +goog.require('goog.dom.classes');
    +
    +/**
    + * Get block definition code for the current block.
    + *
    + * @param {string} blockType - Type of block.
    + * @param {!Blockly.Block} rootBlock - RootBlock from main workspace in which
    + *    user uses Block Factory Blocks to create a custom block.
    + * @param {string} format - 'JSON' or 'JavaScript'.
    + * @param {!Blockly.Workspace} workspace - Where the root block lives.
    + * @return {string} Block definition.
    + */
    +FactoryUtils.getBlockDefinition = function(blockType, rootBlock, format, workspace) {
    +  blockType = blockType.replace(/\W/g, '_').replace(/^(\d)/, '_\\1');
    +  switch (format) {
    +    case 'JSON':
    +      var code = FactoryUtils.formatJson_(blockType, rootBlock);
    +      break;
    +    case 'JavaScript':
    +      var code = FactoryUtils.formatJavaScript_(blockType, rootBlock, workspace);
    +      break;
    +  }
    +  return code;
    +};
    +
    +/**
    + * Get the generator code for a given block.
    + *
    + * @param {!Blockly.Block} block - Rendered block in preview workspace.
    + * @param {string} generatorLanguage - 'JavaScript', 'Python', 'PHP', 'Lua',
    + *     'Dart'.
    + * @return {string} Generator code for multiple blocks.
    + */
    +FactoryUtils.getGeneratorStub = function(block, generatorLanguage) {
    +  function makeVar(root, name) {
    +    name = name.toLowerCase().replace(/\W/g, '_');
    +    return '  var ' + root + '_' + name;
    +  }
    +  // The makevar function lives in the original update generator.
    +  var language = generatorLanguage;
    +  var code = [];
    +  code.push("Blockly." + language + "['" + block.type +
    +            "'] = function(block) {");
    +
    +  // Generate getters for any fields or inputs.
    +  for (var i = 0, input; input = block.inputList[i]; i++) {
    +    for (var j = 0, field; field = input.fieldRow[j]; j++) {
    +      var name = field.name;
    +      if (!name) {
    +        continue;
    +      }
    +      if (field instanceof Blockly.FieldVariable) {
    +        // Subclass of Blockly.FieldDropdown, must test first.
    +        code.push(makeVar('variable', name) +
    +                  " = Blockly." + language +
    +                  ".variableDB_.getName(block.getFieldValue('" + name +
    +                  "'), Blockly.Variables.NAME_TYPE);");
    +      } else if (field instanceof Blockly.FieldAngle) {
    +        // Subclass of Blockly.FieldTextInput, must test first.
    +        code.push(makeVar('angle', name) +
    +                  " = block.getFieldValue('" + name + "');");
    +      } else if (Blockly.FieldDate && field instanceof Blockly.FieldDate) {
    +        // Blockly.FieldDate may not be compiled into Blockly.
    +        code.push(makeVar('date', name) +
    +                  " = block.getFieldValue('" + name + "');");
    +      } else if (field instanceof Blockly.FieldColour) {
    +        code.push(makeVar('colour', name) +
    +                  " = block.getFieldValue('" + name + "');");
    +      } else if (field instanceof Blockly.FieldCheckbox) {
    +        code.push(makeVar('checkbox', name) +
    +                  " = block.getFieldValue('" + name + "') == 'TRUE';");
    +      } else if (field instanceof Blockly.FieldDropdown) {
    +        code.push(makeVar('dropdown', name) +
    +                  " = block.getFieldValue('" + name + "');");
    +      } else if (field instanceof Blockly.FieldNumber) {
    +        code.push(makeVar('number', name) +
    +                  " = block.getFieldValue('" + name + "');");
    +      } else if (field instanceof Blockly.FieldTextInput) {
    +        code.push(makeVar('text', name) +
    +                  " = block.getFieldValue('" + name + "');");
    +      }
    +    }
    +    var name = input.name;
    +    if (name) {
    +      if (input.type == Blockly.INPUT_VALUE) {
    +        code.push(makeVar('value', name) +
    +                  " = Blockly." + language + ".valueToCode(block, '" + name +
    +                  "', Blockly." + language + ".ORDER_ATOMIC);");
    +      } else if (input.type == Blockly.NEXT_STATEMENT) {
    +        code.push(makeVar('statements', name) +
    +                  " = Blockly." + language + ".statementToCode(block, '" +
    +                  name + "');");
    +      }
    +    }
    +  }
    +  // Most languages end lines with a semicolon.  Python does not.
    +  var lineEnd = {
    +    'JavaScript': ';',
    +    'Python': '',
    +    'PHP': ';',
    +    'Dart': ';'
    +  };
    +  code.push("  // TODO: Assemble " + language + " into code variable.");
    +  if (block.outputConnection) {
    +    code.push("  var code = '...';");
    +    code.push("  // TODO: Change ORDER_NONE to the correct strength.");
    +    code.push("  return [code, Blockly." + language + ".ORDER_NONE];");
    +  } else {
    +    code.push("  var code = '..." + (lineEnd[language] || '') + "\\n';");
    +    code.push("  return code;");
    +  }
    +  code.push("};");
    +
    +  return code.join('\n');
    +};
    +
    +/**
    + * Update the language code as JSON.
    + * @param {string} blockType Name of block.
    + * @param {!Blockly.Block} rootBlock Factory_base block.
    + * @return {string} Generanted language code.
    + * @private
    + */
    +FactoryUtils.formatJson_ = function(blockType, rootBlock) {
    +  var JS = {};
    +  // Type is not used by Blockly, but may be used by a loader.
    +  JS.type = blockType;
    +  // Generate inputs.
    +  var message = [];
    +  var args = [];
    +  var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
    +  var lastInput = null;
    +  while (contentsBlock) {
    +    if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
    +      var fields = FactoryUtils.getFieldsJson_(
    +          contentsBlock.getInputTargetBlock('FIELDS'));
    +      for (var i = 0; i < fields.length; i++) {
    +        if (typeof fields[i] == 'string') {
    +          message.push(fields[i].replace(/%/g, '%%'));
    +        } else {
    +          args.push(fields[i]);
    +          message.push('%' + args.length);
    +        }
    +      }
    +
    +      var input = {type: contentsBlock.type};
    +      // Dummy inputs don't have names.  Other inputs do.
    +      if (contentsBlock.type != 'input_dummy') {
    +        input.name = contentsBlock.getFieldValue('INPUTNAME');
    +      }
    +      var check = JSON.parse(
    +          FactoryUtils.getOptTypesFrom(contentsBlock, 'TYPE') || 'null');
    +      if (check) {
    +        input.check = check;
    +      }
    +      var align = contentsBlock.getFieldValue('ALIGN');
    +      if (align != 'LEFT') {
    +        input.align = align;
    +      }
    +      args.push(input);
    +      message.push('%' + args.length);
    +      lastInput = contentsBlock;
    +    }
    +    contentsBlock = contentsBlock.nextConnection &&
    +        contentsBlock.nextConnection.targetBlock();
    +  }
    +  // Remove last input if dummy and not empty.
    +  if (lastInput && lastInput.type == 'input_dummy') {
    +    var fields = lastInput.getInputTargetBlock('FIELDS');
    +    if (fields && FactoryUtils.getFieldsJson_(fields).join('').trim() != '') {
    +      var align = lastInput.getFieldValue('ALIGN');
    +      if (align != 'LEFT') {
    +        JS.lastDummyAlign0 = align;
    +      }
    +      args.pop();
    +      message.pop();
    +    }
    +  }
    +  JS.message0 = message.join(' ');
    +  if (args.length) {
    +    JS.args0 = args;
    +  }
    +  // Generate inline/external switch.
    +  if (rootBlock.getFieldValue('INLINE') == 'EXT') {
    +    JS.inputsInline = false;
    +  } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
    +    JS.inputsInline = true;
    +  }
    +  // Generate output, or next/previous connections.
    +  switch (rootBlock.getFieldValue('CONNECTIONS')) {
    +    case 'LEFT':
    +      JS.output =
    +          JSON.parse(
    +              FactoryUtils.getOptTypesFrom(rootBlock, 'OUTPUTTYPE') || 'null');
    +      break;
    +    case 'BOTH':
    +      JS.previousStatement =
    +          JSON.parse(
    +              FactoryUtils.getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
    +      JS.nextStatement =
    +          JSON.parse(
    +              FactoryUtils.getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
    +      break;
    +    case 'TOP':
    +      JS.previousStatement =
    +          JSON.parse(
    +              FactoryUtils.getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
    +      break;
    +    case 'BOTTOM':
    +      JS.nextStatement =
    +          JSON.parse(
    +              FactoryUtils.getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
    +      break;
    +  }
    +  // Generate colour.
    +  var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
    +  if (colourBlock && !colourBlock.disabled) {
    +    var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
    +    JS.colour = hue;
    +  }
    +  JS.tooltip = '';
    +  JS.helpUrl = 'http://www.example.com/';
    +  return JSON.stringify(JS, null, '  ');
    +};
    +
    +/**
    + * Update the language code as JavaScript.
    + * @param {string} blockType Name of block.
    + * @param {!Blockly.Block} rootBlock Factory_base block.
    + * @param {!Blockly.Workspace} workspace - Where the root block lives.
    +
    + * @return {string} Generated language code.
    + * @private
    + */
    +FactoryUtils.formatJavaScript_ = function(blockType, rootBlock, workspace) {
    +  var code = [];
    +  code.push("Blockly.Blocks['" + blockType + "'] = {");
    +  code.push("  init: function() {");
    +  // Generate inputs.
    +  var TYPES = {'input_value': 'appendValueInput',
    +               'input_statement': 'appendStatementInput',
    +               'input_dummy': 'appendDummyInput'};
    +  var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
    +  while (contentsBlock) {
    +    if (!contentsBlock.disabled && !contentsBlock.getInheritedDisabled()) {
    +      var name = '';
    +      // Dummy inputs don't have names.  Other inputs do.
    +      if (contentsBlock.type != 'input_dummy') {
    +        name =
    +            FactoryUtils.escapeString(contentsBlock.getFieldValue('INPUTNAME'));
    +      }
    +      code.push('    this.' + TYPES[contentsBlock.type] + '(' + name + ')');
    +      var check = FactoryUtils.getOptTypesFrom(contentsBlock, 'TYPE');
    +      if (check) {
    +        code.push('        .setCheck(' + check + ')');
    +      }
    +      var align = contentsBlock.getFieldValue('ALIGN');
    +      if (align != 'LEFT') {
    +        code.push('        .setAlign(Blockly.ALIGN_' + align + ')');
    +      }
    +      var fields = FactoryUtils.getFieldsJs_(
    +          contentsBlock.getInputTargetBlock('FIELDS'));
    +      for (var i = 0; i < fields.length; i++) {
    +        code.push('        .appendField(' + fields[i] + ')');
    +      }
    +      // Add semicolon to last line to finish the statement.
    +      code[code.length - 1] += ';';
    +    }
    +    contentsBlock = contentsBlock.nextConnection &&
    +        contentsBlock.nextConnection.targetBlock();
    +  }
    +  // Generate inline/external switch.
    +  if (rootBlock.getFieldValue('INLINE') == 'EXT') {
    +    code.push('    this.setInputsInline(false);');
    +  } else if (rootBlock.getFieldValue('INLINE') == 'INT') {
    +    code.push('    this.setInputsInline(true);');
    +  }
    +  // Generate output, or next/previous connections.
    +  switch (rootBlock.getFieldValue('CONNECTIONS')) {
    +    case 'LEFT':
    +      code.push(FactoryUtils.connectionLineJs_('setOutput', 'OUTPUTTYPE', workspace));
    +      break;
    +    case 'BOTH':
    +      code.push(
    +          FactoryUtils.connectionLineJs_('setPreviousStatement', 'TOPTYPE', workspace));
    +      code.push(
    +          FactoryUtils.connectionLineJs_('setNextStatement', 'BOTTOMTYPE', workspace));
    +      break;
    +    case 'TOP':
    +      code.push(
    +          FactoryUtils.connectionLineJs_('setPreviousStatement', 'TOPTYPE', workspace));
    +      break;
    +    case 'BOTTOM':
    +      code.push(
    +          FactoryUtils.connectionLineJs_('setNextStatement', 'BOTTOMTYPE', workspace));
    +      break;
    +  }
    +  // Generate colour.
    +  var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
    +  if (colourBlock && !colourBlock.disabled) {
    +    var hue = parseInt(colourBlock.getFieldValue('HUE'), 10);
    +    if (!isNaN(hue)) {
    +      code.push('    this.setColour(' + hue + ');');
    +    }
    +  }
    +  code.push("    this.setTooltip('');");
    +  code.push("    this.setHelpUrl('http://www.example.com/');");
    +  code.push('  }');
    +  code.push('};');
    +  return code.join('\n');
    +};
    +
    +/**
    + * Create JS code required to create a top, bottom, or value connection.
    + * @param {string} functionName JavaScript function name.
    + * @param {string} typeName Name of type input.
    + * @param {!Blockly.Workspace} workspace - Where the root block lives.
    + * @return {string} Line of JavaScript code to create connection.
    + * @private
    + */
    +FactoryUtils.connectionLineJs_ = function(functionName, typeName, workspace) {
    +  var type = FactoryUtils.getOptTypesFrom(
    +      FactoryUtils.getRootBlock(workspace), typeName);
    +  if (type) {
    +    type = ', ' + type;
    +  } else {
    +    type = '';
    +  }
    +  return '    this.' + functionName + '(true' + type + ');';
    +};
    +
    +/**
    + * Returns field strings and any config.
    + * @param {!Blockly.Block} block Input block.
    + * @return {!Array.} Field strings.
    + * @private
    + */
    +FactoryUtils.getFieldsJs_ = function(block) {
    +  var fields = [];
    +  while (block) {
    +    if (!block.disabled && !block.getInheritedDisabled()) {
    +      switch (block.type) {
    +        case 'field_static':
    +          // Result: 'hello'
    +          fields.push(FactoryUtils.escapeString(block.getFieldValue('TEXT')));
    +          break;
    +        case 'field_input':
    +          // Result: new Blockly.FieldTextInput('Hello'), 'GREET'
    +          fields.push('new Blockly.FieldTextInput(' +
    +              FactoryUtils.escapeString(block.getFieldValue('TEXT')) + '), ' +
    +              FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          break;
    +        case 'field_number':
    +          // Result: new Blockly.FieldNumber(10, 0, 100, 1), 'NUMBER'
    +          var args = [
    +            Number(block.getFieldValue('VALUE')),
    +            Number(block.getFieldValue('MIN')),
    +            Number(block.getFieldValue('MAX')),
    +            Number(block.getFieldValue('PRECISION'))
    +          ];
    +          // Remove any trailing arguments that aren't needed.
    +          if (args[3] == 0) {
    +            args.pop();
    +            if (args[2] == Infinity) {
    +              args.pop();
    +              if (args[1] == -Infinity) {
    +                args.pop();
    +              }
    +            }
    +          }
    +          fields.push('new Blockly.FieldNumber(' + args.join(', ') + '), ' +
    +              FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          break;
    +        case 'field_angle':
    +          // Result: new Blockly.FieldAngle(90), 'ANGLE'
    +          fields.push('new Blockly.FieldAngle(' +
    +              parseFloat(block.getFieldValue('ANGLE')) + '), ' +
    +              FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          break;
    +        case 'field_checkbox':
    +          // Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
    +          fields.push('new Blockly.FieldCheckbox(' +
    +              FactoryUtils.escapeString(block.getFieldValue('CHECKED')) +
    +               '), ' +
    +              FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          break;
    +        case 'field_colour':
    +          // Result: new Blockly.FieldColour('#ff0000'), 'COLOUR'
    +          fields.push('new Blockly.FieldColour(' +
    +              FactoryUtils.escapeString(block.getFieldValue('COLOUR')) +
    +              '), ' +
    +              FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          break;
    +        case 'field_date':
    +          // Result: new Blockly.FieldDate('2015-02-04'), 'DATE'
    +          fields.push('new Blockly.FieldDate(' +
    +              FactoryUtils.escapeString(block.getFieldValue('DATE')) + '), ' +
    +              FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          break;
    +        case 'field_variable':
    +          // Result: new Blockly.FieldVariable('item'), 'VAR'
    +          var varname
    +              = FactoryUtils.escapeString(block.getFieldValue('TEXT') || null);
    +          fields.push('new Blockly.FieldVariable(' + varname + '), ' +
    +              FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          break;
    +        case 'field_dropdown':
    +          // Result:
    +          // new Blockly.FieldDropdown([['yes', '1'], ['no', '0']]), 'TOGGLE'
    +          var options = [];
    +          for (var i = 0; i < block.optionCount_; i++) {
    +            options[i] = '[' +
    +                FactoryUtils.escapeString(block.getFieldValue('USER' + i)) +
    +                ', ' +
    +                FactoryUtils.escapeString(block.getFieldValue('CPU' + i)) + ']';
    +          }
    +          if (options.length) {
    +            fields.push('new Blockly.FieldDropdown([' +
    +                options.join(', ') + ']), ' +
    +                FactoryUtils.escapeString(block.getFieldValue('FIELDNAME')));
    +          }
    +          break;
    +        case 'field_image':
    +          // Result: new Blockly.FieldImage('http://...', 80, 60)
    +          var src = FactoryUtils.escapeString(block.getFieldValue('SRC'));
    +          var width = Number(block.getFieldValue('WIDTH'));
    +          var height = Number(block.getFieldValue('HEIGHT'));
    +          var alt = FactoryUtils.escapeString(block.getFieldValue('ALT'));
    +          fields.push('new Blockly.FieldImage(' +
    +              src + ', ' + width + ', ' + height + ', ' + alt + ')');
    +          break;
    +      }
    +    }
    +    block = block.nextConnection && block.nextConnection.targetBlock();
    +  }
    +  return fields;
    +};
    +
    +/**
    + * Returns field strings and any config.
    + * @param {!Blockly.Block} block Input block.
    + * @return {!Array.} Array of static text and field configs.
    + * @private
    + */
    +FactoryUtils.getFieldsJson_ = function(block) {
    +  var fields = [];
    +  while (block) {
    +    if (!block.disabled && !block.getInheritedDisabled()) {
    +      switch (block.type) {
    +        case 'field_static':
    +          // Result: 'hello'
    +          fields.push(block.getFieldValue('TEXT'));
    +          break;
    +        case 'field_input':
    +          fields.push({
    +            type: block.type,
    +            name: block.getFieldValue('FIELDNAME'),
    +            text: block.getFieldValue('TEXT')
    +          });
    +          break;
    +        case 'field_number':
    +          var obj = {
    +            type: block.type,
    +            name: block.getFieldValue('FIELDNAME'),
    +            value: parseFloat(block.getFieldValue('VALUE'))
    +          };
    +          var min = parseFloat(block.getFieldValue('MIN'));
    +          if (min > -Infinity) {
    +            obj.min = min;
    +          }
    +          var max = parseFloat(block.getFieldValue('MAX'));
    +          if (max < Infinity) {
    +            obj.max = max;
    +          }
    +          var precision = parseFloat(block.getFieldValue('PRECISION'));
    +          if (precision) {
    +            obj.precision = precision;
    +          }
    +          fields.push(obj);
    +          break;
    +        case 'field_angle':
    +          fields.push({
    +            type: block.type,
    +            name: block.getFieldValue('FIELDNAME'),
    +            angle: Number(block.getFieldValue('ANGLE'))
    +          });
    +          break;
    +        case 'field_checkbox':
    +          fields.push({
    +            type: block.type,
    +            name: block.getFieldValue('FIELDNAME'),
    +            checked: block.getFieldValue('CHECKED') == 'TRUE'
    +          });
    +          break;
    +        case 'field_colour':
    +          fields.push({
    +            type: block.type,
    +            name: block.getFieldValue('FIELDNAME'),
    +            colour: block.getFieldValue('COLOUR')
    +          });
    +          break;
    +        case 'field_date':
    +          fields.push({
    +            type: block.type,
    +            name: block.getFieldValue('FIELDNAME'),
    +            date: block.getFieldValue('DATE')
    +          });
    +          break;
    +        case 'field_variable':
    +          fields.push({
    +            type: block.type,
    +            name: block.getFieldValue('FIELDNAME'),
    +            variable: block.getFieldValue('TEXT') || null
    +          });
    +          break;
    +        case 'field_dropdown':
    +          var options = [];
    +          for (var i = 0; i < block.optionCount_; i++) {
    +            options[i] = [block.getFieldValue('USER' + i),
    +                block.getFieldValue('CPU' + i)];
    +          }
    +          if (options.length) {
    +            fields.push({
    +              type: block.type,
    +              name: block.getFieldValue('FIELDNAME'),
    +              options: options
    +            });
    +          }
    +          break;
    +        case 'field_image':
    +          fields.push({
    +            type: block.type,
    +            src: block.getFieldValue('SRC'),
    +            width: Number(block.getFieldValue('WIDTH')),
    +            height: Number(block.getFieldValue('HEIGHT')),
    +            alt: block.getFieldValue('ALT')
    +          });
    +          break;
    +      }
    +    }
    +    block = block.nextConnection && block.nextConnection.targetBlock();
    +  }
    +  return fields;
    +};
    +
    +/**
    + * Fetch the type(s) defined in the given input.
    + * Format as a string for appending to the generated code.
    + * @param {!Blockly.Block} block Block with input.
    + * @param {string} name Name of the input.
    + * @return {?string} String defining the types.
    + */
    +FactoryUtils.getOptTypesFrom = function(block, name) {
    +  var types = FactoryUtils.getTypesFrom_(block, name);
    +  if (types.length == 0) {
    +    return undefined;
    +  } else if (types.indexOf('null') != -1) {
    +    return 'null';
    +  } else if (types.length == 1) {
    +    return types[0];
    +  } else {
    +    return '[' + types.join(', ') + ']';
    +  }
    +};
    +
    +
    +/**
    + * Fetch the type(s) defined in the given input.
    + * @param {!Blockly.Block} block Block with input.
    + * @param {string} name Name of the input.
    + * @return {!Array.} List of types.
    + * @private
    + */
    +FactoryUtils.getTypesFrom_ = function(block, name) {
    +  var typeBlock = block.getInputTargetBlock(name);
    +  var types;
    +  if (!typeBlock || typeBlock.disabled) {
    +    types = [];
    +  } else if (typeBlock.type == 'type_other') {
    +    types = [FactoryUtils.escapeString(typeBlock.getFieldValue('TYPE'))];
    +  } else if (typeBlock.type == 'type_group') {
    +    types = [];
    +    for (var n = 0; n < typeBlock.typeCount_; n++) {
    +      types = types.concat(FactoryUtils.getTypesFrom_(typeBlock, 'TYPE' + n));
    +    }
    +    // Remove duplicates.
    +    var hash = Object.create(null);
    +    for (var n = types.length - 1; n >= 0; n--) {
    +      if (hash[types[n]]) {
    +        types.splice(n, 1);
    +      }
    +      hash[types[n]] = true;
    +    }
    +  } else {
    +    types = [FactoryUtils.escapeString(typeBlock.valueType)];
    +  }
    +  return types;
    +};
    +
    +/**
    + * Escape a string.
    + * @param {string} string String to escape.
    + * @return {string} Escaped string surrouned by quotes.
    + */
    +FactoryUtils.escapeString = function(string) {
    +  return JSON.stringify(string);
    +};
    +
    +/**
    + * Return the uneditable container block that everything else attaches to in
    + * given workspace
    + *
    + * @param {!Blockly.Workspace} workspace - where the root block lives
    + * @return {Blockly.Block} root block
    + */
    +FactoryUtils.getRootBlock = function(workspace) {
    +  var blocks = workspace.getTopBlocks(false);
    +  for (var i = 0, block; block = blocks[i]; i++) {
    +    if (block.type == 'factory_base') {
    +      return block;
    +    }
    +  }
    +  return null;
    +};
    +
    +// TODO(quachtina96): Move hide, show, makeInvisible, and makeVisible to a new
    +// AppView namespace.
    +
    +/**
    + * Hides element so that it's invisible and doesn't take up space.
    + *
    + * @param {string} elementID - ID of element to hide.
    + */
    +FactoryUtils.hide = function(elementID) {
    +  document.getElementById(elementID).style.display = 'none';
    +};
    +
    +/**
    + * Un-hides an element.
    + *
    + * @param {string} elementID - ID of element to hide.
    + */
    +FactoryUtils.show = function(elementID) {
    +  document.getElementById(elementID).style.display = 'block';
    +};
    +
    +/**
    + * Hides element so that it's invisible but still takes up space.
    + *
    + * @param {string} elementID - ID of element to hide.
    + */
    +FactoryUtils.makeInvisible = function(elementID) {
    +  document.getElementById(elementID).visibility = 'hidden';
    +};
    +
    +/**
    + * Makes element visible.
    + *
    + * @param {string} elementID - ID of element to hide.
    + */
    +FactoryUtils.makeVisible = function(elementID) {
    +  document.getElementById(elementID).visibility = 'visible';
    +};
    +
    +/**
    + * Create a file with the given attributes and download it.
    + * @param {string} contents - The contents of the file.
    + * @param {string} filename - The name of the file to save to.
    + * @param {string} fileType - The type of the file to save.
    + */
    +FactoryUtils.createAndDownloadFile = function(contents, filename, fileType) {
    +  var data = new Blob([contents], {type: 'text/' + fileType});
    +  var clickEvent = new MouseEvent("click", {
    +    "view": window,
    +    "bubbles": true,
    +    "cancelable": false
    +  });
    +
    +  var a = document.createElement('a');
    +  a.href = window.URL.createObjectURL(data);
    +  a.download = filename;
    +  a.textContent = 'Download file!';
    +  a.dispatchEvent(clickEvent);
    +};
    +
    +/**
    + * Get Blockly Block by rendering pre-defined block in workspace.
    + *
    + * @param {!Element} blockType - Type of block that has already been defined.
    + * @param {!Blockly.Workspace} workspace - Workspace on which to render
    + *    the block.
    + * @return {!Blockly.Block} the Blockly.Block of desired type.
    + */
    +FactoryUtils.getDefinedBlock = function(blockType, workspace) {
    +  workspace.clear();
    +  return workspace.newBlock(blockType);
    +};
    +
    +/**
    + * Parses a block definition get the type of the block it defines.
    + *
    + * @param {!string} blockDef - A single block definition.
    + * @return {string} Type of block defined by the given definition.
    + */
    +FactoryUtils.getBlockTypeFromJsDefinition = function(blockDef) {
    +  var indexOfStartBracket = blockDef.indexOf('[\'');
    +  var indexOfEndBracket = blockDef.indexOf('\']');
    +  if (indexOfStartBracket != -1 && indexOfEndBracket != -1) {
    +    return blockDef.substring(indexOfStartBracket + 2, indexOfEndBracket);
    +  } else {
    +    throw new Error ('Could not parse block type out of JavaScript block ' +
    +        'definition. Brackets normally enclosing block type not found.');
    +  }
    +};
    +
    +/**
    + * Generates a category containing blocks of the specified block types.
    + *
    + * @param {!Array.} blocks - Blocks to include in the category.
    + * @param {string} categoryName - Name to use for the generated category.
    + * @return {Element} - Category xml containing the given block types.
    + */
    +FactoryUtils.generateCategoryXml = function(blocks, categoryName) {
    +  // Create category DOM element.
    +  var categoryElement = goog.dom.createDom('category');
    +  categoryElement.setAttribute('name', categoryName);
    +
    +  // For each block, add block element to category.
    +  for (var i = 0, block; block = blocks[i]; i++) {
    +
    +    // Get preview block XML.
    +    var blockXml = Blockly.Xml.blockToDom(block);
    +    blockXml.removeAttribute('id');
    +
    +    // Add block to category and category to XML.
    +    categoryElement.appendChild(blockXml);
    +  }
    +  return categoryElement;
    +};
    +
    +/**
    + * Parses a string containing JavaScript block definition(s) to create an array
    + * in which each element is a single block definition.
    + *
    + * @param {!string} blockDefsString - JavaScript block definition(s).
    + * @return {!Array.} - Array of block definitions.
    + */
    +FactoryUtils.parseJsBlockDefinitions = function(blockDefsString) {
    +  var blockDefArray = [];
    +  var defStart = blockDefsString.indexOf('Blockly.Blocks');
    +
    +  while (blockDefsString.indexOf('Blockly.Blocks', defStart) != -1) {
    +    var nextStart = blockDefsString.indexOf('Blockly.Blocks', defStart + 1);
    +    if (nextStart == -1) {
    +      // This is the last block definition.
    +      nextStart = blockDefsString.length;
    +    }
    +    var blockDef = blockDefsString.substring(defStart, nextStart);
    +    blockDefArray.push(blockDef);
    +    defStart = nextStart;
    +  }
    +  return blockDefArray;
    +};
    +
    +/**
    + * Parses a string containing JSON block definition(s) to create an array
    + * in which each element is a single block definition. Expected input is
    + * one or more block definitions in the form of concatenated, stringified
    + * JSON objects.
    + *
    + * @param {!string} blockDefsString - String containing JSON block
    + *    definition(s).
    + * @return {!Array.} - Array of block definitions.
    + */
    +FactoryUtils.parseJsonBlockDefinitions = function(blockDefsString) {
    +  var blockDefArray = [];
    +  var unbalancedBracketCount = 0;
    +  var defStart = 0;
    +  // Iterate through the blockDefs string. Keep track of whether brackets
    +  // are balanced.
    +  for (var i = 0; i < blockDefsString.length; i++) {
    +    var currentChar = blockDefsString[i];
    +    if (currentChar == '{') {
    +      unbalancedBracketCount++;
    +    }
    +    else if (currentChar == '}') {
    +      unbalancedBracketCount--;
    +      if (unbalancedBracketCount == 0 && i > 0) {
    +        // The brackets are balanced. We've got a complete block defintion.
    +        var blockDef = blockDefsString.substring(defStart, i + 1);
    +        blockDefArray.push(blockDef);
    +        defStart = i + 1;
    +      }
    +    }
    +  }
    +  return blockDefArray;
    +};
    +
    +/**
    + * Define blocks from imported block definitions.
    + *
    + * @param {!string} blockDefsString - Block definition(s).
    + * @param {!string} format - Block definition format ('JSON' or 'JavaScript').
    + * @return {!Array} Array of block types defined.
    + */
    +FactoryUtils.defineAndGetBlockTypes = function(blockDefsString, format) {
    +  var blockTypes = [];
    +
    +  // Define blocks and get block types.
    +  if (format == 'JSON') {
    +    var blockDefArray = FactoryUtils.parseJsonBlockDefinitions(blockDefsString);
    +
    +    // Populate array of blocktypes and define each block.
    +    for (var i = 0, blockDef; blockDef = blockDefArray[i]; i++) {
    +      var json = JSON.parse(blockDef);
    +      blockTypes.push(json.type);
    +
    +      // Define the block.
    +      Blockly.Blocks[json.type] = {
    +        init: function() {
    +          this.jsonInit(json);
    +        }
    +      };
    +    }
    +  } else if (format == 'JavaScript') {
    +    var blockDefArray = FactoryUtils.parseJsBlockDefinitions(blockDefsString);
    +
    +    // Populate array of block types.
    +    for (var i = 0, blockDef; blockDef = blockDefArray[i]; i++) {
    +      var blockType = FactoryUtils.getBlockTypeFromJsDefinition(blockDef);
    +      blockTypes.push(blockType);
    +    }
    +
    +    // Define all blocks.
    +    eval(blockDefsString);
    +  }
    +
    +  return blockTypes;
    +};
    +
    +/**
    + * Inject code into a pre tag, with syntax highlighting.
    + * Safe from HTML/script injection.
    + * @param {string} code Lines of code.
    + * @param {string} id ID of 
     element to inject into.
    + */
    +FactoryUtils.injectCode = function(code, id) {
    +  var pre = document.getElementById(id);
    +  pre.textContent = code;
    +  code = pre.innerHTML;
    +  code = prettyPrintOne(code, 'js');
    +  pre.innerHTML = code;
    +};
    diff --git a/demos/blocklyfactory/index.html b/demos/blocklyfactory/index.html
    new file mode 100644
    index 00000000..1bd3d7d0
    --- /dev/null
    +++ b/demos/blocklyfactory/index.html
    @@ -0,0 +1,729 @@
    +
    +
    +
    +
    +
    +  
    +  
    +  Blockly Demo: Blockly Factory
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +

    Blockly > + Demos > Blockly Factory + +

    +
    +
    Block Factory
    +
    Workspace Factory
    +
    Block Exporter
    +
    + + +
    +
    +

    First, select blocks from your block library by dragging them into your workspace. Then, use the Export Settings form to download starter code for selected blocks. +

    +
    +
    +

    Block Selector

    + +
    +
    + + +
    +
    +

    Export Settings

    +
    + +
    +

    Currently Selected:

    +

    +
    + Block Definition(s)
    +
    + Format: + +
    + File Name: +
    + +
    +
    + Generator Stub(s)
    +
    + Language: + +
    + File Name: +
    +
    +
    +
    +
    + +
    +
    +
    +

    Export Preview

    +
    +

    Block Definitions:

    +
    
    +        
    +
    +

    Generator Stubs:

    +
    
    +        
    +
    +
    + + + +
    +

    +

    + + + + + + + +

    + +
    +

    Edit

    +

    Drag blocks into the workspace to configure the toolbox in your custom workspace.

    + + + +
    ToolboxWorkspace
    +
    +
    +
    +
    + + + + + + + +
    + + +
    + + + + + + + + + + +
    + + + + + +
    + +

    Block Library:

    + +
    +
    + + + +
    +
    + + + + + +
    +

    Preview: + +

    +
    + + + + + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +
    +
    +
    +

    Language code: + +

    +
    +
    
    +              
    +            
    +

    Generator stub: + +

    +
    +
    
    +            
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20 + 65 + 120 + 160 + 210 + 230 + 260 + 290 + 330 + + + + + + + + + + + + + + + + + + 10 + + + + + + + + 1 + + + + + 10 + + + + + 1 + + + + + + + + + + + + 1 + + + + + 1 + + + + + + + 9 + + + + + + + 45 + + + + + + + + 0 + + + + + + + 3.1 + + + + + + + + 64 + + + + + 10 + + + + + + + 50 + + + + + 1 + + + + + 100 + + + + + + + 1 + + + + + 100 + + + + + + + + + + + + + + + + + abc + + + + + + + + + + + + + + text + + + + + abc + + + + + + + text + + + + + + + text + + + + + + + abc + + + + + + + abc + + + + + + + abc + + + + + + + abc + + + + + + + + + + + + + 5 + + + + + + + + + list + + + + + + + list + + + + + + + list + + + + + + + list + + + + + + + , + + + + + + + + + + + + 100 + + + + + 50 + + + + + 0 + + + + + + + #ff0000 + + + + + #3333ff + + + + + 0.5 + + + + + + + + + + + + + diff --git a/demos/blocklyfactory/link.png b/demos/blocklyfactory/link.png new file mode 100644 index 00000000..11dfd828 Binary files /dev/null and b/demos/blocklyfactory/link.png differ diff --git a/demos/blocklyfactory/standard_categories.js b/demos/blocklyfactory/standard_categories.js new file mode 100644 index 00000000..ef102fc9 --- /dev/null +++ b/demos/blocklyfactory/standard_categories.js @@ -0,0 +1,395 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Contains a map of standard Blockly categories used to load + * standard Blockly categories into the user's toolbox. The map is keyed by + * the lower case name of the category, and contains the Category object for + * that particular category. Also has a list of core block types provided + * by Blockly. + * + * @author Emma Dauterman (evd2014) + */ + 'use strict'; + +/** + * Namespace for StandardCategories + */ +goog.provide('StandardCategories'); + +// Map of standard category information necessary to add a standard category +// to the toolbox. +StandardCategories.categoryMap = Object.create(null); + +StandardCategories.categoryMap['logic'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Logic'); +StandardCategories.categoryMap['logic'].xml = + Blockly.Xml.textToDom( + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''); +StandardCategories.categoryMap['logic'].color ='#5C81A6'; + +StandardCategories.categoryMap['loops'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Loops'); +StandardCategories.categoryMap['loops'].xml = + Blockly.Xml.textToDom( + '' + + '' + + '' + + '' + + '10' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '1' + + '' + + '' + + '' + + '' + + '10' + + '' + + '' + + '' + + '' + + '1' + + '' + + '' + + '' + + '' + + '' + + ''); +StandardCategories.categoryMap['loops'].color = '#5CA65C'; + +StandardCategories.categoryMap['math'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Math'); +StandardCategories.categoryMap['math'].xml = + Blockly.Xml.textToDom( + '' + + '' + + '' + + '' + + '' + + '1' + + '' + + '' + + '' + + '' + + '1' + + '' + + '' + + '' + + '' + + '' + + '' + + '9' + + '' + + '' + + '' + + '' + + '' + + '' + + '45' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '0' + + '' + + '' + + '' + + '' + + '' + + '' + + '3.1' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '64' + + '' + + '' + + '' + + '' + + '10'+ + '' + + '' + + '' + + '' + + '' + + '' + + '50' + + '' + + '' + + '' + + '' + + '1' + + '' + + '' + + '' + + '' + + '100' + + '' + + '' + + '' + + '' + + '' + + '' + + '1' + + '' + + '' + + '' + + '' + + '100' + + '' + + '' + + '' + + '' + + ''); +StandardCategories.categoryMap['math'].color = '#5C68A6'; + +StandardCategories.categoryMap['text'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Text'); +StandardCategories.categoryMap['text'].xml = + Blockly.Xml.textToDom( + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + 'abc' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + 'text' + + '' + + '' + + '' + + '' + + 'abc' + + '' + + '' + + '' + + '' + + '' + + '' + + 'text' + + '' + + '' + + '' + + '' + + '' + + '' + + 'text' + + '' + + '' + + '' + + '' + + '' + + '' + + 'abc' + + '' + + '' + + '' + + '' + + '' + + '' + + 'abc' + + '' + + '' + + '' + + '' + + '' + + '' + + 'abc' + + '' + + '' + + '' + + '' + + '' + + '' + + 'abc' + + '' + + '' + + '' + + ''); +StandardCategories.categoryMap['text'].color = '#5CA68D'; + +StandardCategories.categoryMap['lists'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Lists'); +StandardCategories.categoryMap['lists'].xml = + Blockly.Xml.textToDom( + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '5' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + 'list' + + '' + + '' + + '' + + '' + + '' + + '' + + 'list' + + '' + + '' + + '' + + '' + + '' + + '' + + 'list' + + '' + + '' + + '' + + '' + + '' + + '' + + 'list' + + '' + + '' + + '' + + '' + + '' + + '' + + ',' + + '' + + '' + + '' + + '' + + ''); +StandardCategories.categoryMap['lists'].color = '#745CA6'; + +StandardCategories.categoryMap['colour'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Colour'); +StandardCategories.categoryMap['colour'].xml = + Blockly.Xml.textToDom( + '' + + '' + + '' + + '' + + '' + + '' + + '100' + + '' + + '' + + '' + + '' + + '50' + + '' + + '' + + '' + + '' + + '0' + + '' + + '' + + '' + + '' + + '' + + '' + + '#ff0000' + + '' + + '' + + '' + + '' + + '#3333ff' + + '' + + '' + + '' + + '' + + '0.5' + + '' + + '' + + '' + + ''); +StandardCategories.categoryMap['colour'].color = '#A6745C'; + +StandardCategories.categoryMap['functions'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Functions'); +StandardCategories.categoryMap['functions'].color = '#9A5CA6' +StandardCategories.categoryMap['functions'].custom = 'PROCEDURE'; + +StandardCategories.categoryMap['variables'] = + new ListElement(ListElement.TYPE_CATEGORY, 'Variables'); +StandardCategories.categoryMap['variables'].color = '#A65C81'; +StandardCategories.categoryMap['variables'].custom = 'VARIABLE'; + +// All standard block types in provided in Blockly core. +StandardCategories.coreBlockTypes = ["controls_if", "logic_compare", + "logic_operation", "logic_negate", "logic_boolean", "logic_null", + "logic_ternary", "controls_repeat_ext", "controls_whileUntil", + "controls_for", "controls_forEach", "controls_flow_statements", + "math_number", "math_arithmetic", "math_single", "math_trig", + "math_constant", "math_number_property", "math_change", "math_round", + "math_on_list", "math_modulo", "math_constrain", "math_random_int", + "math_random_float", "text", "text_join", "text_append", "text_length", + "text_isEmpty", "text_indexOf", "variables_get", "text_charAt", + "text_getSubstring", "text_changeCase", "text_trim", "text_print", + "text_prompt_ext", "colour_picker", "colour_random", "colour_rgb", + "colour_blend", "lists_create_with", "lists_repeat", "lists_length", + "lists_isEmpty", "lists_indexOf", "lists_getIndex", "lists_setIndex", + "lists_getSublist", "lists_split", "lists_sort", "variables_set", + "procedures_defreturn", "procedures_ifreturn", "procedures_defnoreturn", + "procedures_callreturn"]; + diff --git a/demos/blocklyfactory/workspacefactory/index.html b/demos/blocklyfactory/workspacefactory/index.html new file mode 100644 index 00000000..c45c449a --- /dev/null +++ b/demos/blocklyfactory/workspacefactory/index.html @@ -0,0 +1,847 @@ + + +Blockly Workspace Factory + + + + + + + + + + + + + + + + + + + + + +
    +

    Blockly‏ > + Demos‏ > + Workspace Factory +

    +
    +

    +

    + + + + + +

    +
    + +
    +

    Drag blocks into your toolbox:

    + + + +
    ToolboxWorkspace
    +
    +
    +
    +
    + + + + + + + +
    + + + + + + diff --git a/demos/blocklyfactory/workspacefactory/style.css b/demos/blocklyfactory/workspacefactory/style.css new file mode 100644 index 00000000..3acb3216 --- /dev/null +++ b/demos/blocklyfactory/workspacefactory/style.css @@ -0,0 +1,224 @@ +body { + background-color: #fff; + font-family: sans-serif; +} + +h1 { + font-weight: normal; + font-size: 140%; +} + +section { + float: left; +} + +aside { + float: right; +} + +#categoryTable>table { + border: 1px solid #ccc; + border-bottom: none; +} + +#workspaceTabs>table { + float: right; +} + +td.tabon { + border-bottom-color: #ddd !important; + background-color: #ddd; + padding: 5px 19px; +} + +td.taboff { + cursor: pointer; + padding: 5px 19px; +} + +td.taboff:hover { + background-color: #eee; +} + +button { + border-radius: 4px; + border: 1px solid #ddd; + background-color: #eee; + color: #000; + font-size: large; + margin: 0 5px; + padding: 10px; +} + +button:hover:not(:disabled) { + box-shadow: 2px 2px 5px #888; +} + +button:disabled { + opacity: .6; +} + +button>* { + opacity: .6; + vertical-align: text-bottom; +} + +button:hover:not(:disabled)>* { + opacity: 1; +} + +button.small { + font-size: small; +} + +table { + border: none; + border-collapse: collapse; + margin: 0; + padding: 0; +} + +td { + padding: 0; + vertical-align: top; +} + +.inputfile { + height: 0; + opacity: 0; + overflow: hidden; + position: absolute; + width: 0; + z-index: -1; +} + +#toolbox_section { + height: 480px; + width: 80%; + position: relative; +} + +#toolbox_blocks { + height: 100%; + width: 100%; +} + +#preview_blocks { + height: 300px; + width: 100%; +} + +#createDiv { + width: 70%; +} + +#previewDiv { + width: 30%; +} + +#toolbox_div { + width: 20%; +} + +#preload_div { + width: 20%; +} + +#disable_div { + background-color: white; + height: 100%; + left: 0; + opacity: .5; + position: absolute; + top: 0; + width: 100%; + z-index: -1; /* Start behind workspace */ +} + +/* 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 #000000; + font-size: 1px; +} + +.goog-palette-colorswatch { + border: 1px solid #000000; + 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; +} + +/* The container
    - needed to position the dropdown content */ +.dropdown { + position: relative; + display: inline-block; +} + +/* 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; +} + +/* Links inside the dropdown */ +.dropdown-content a, .dropdown-content label { + color: black; + display: block; + font-size: small; + padding: 12px 16px; + text-decoration: none; +} + +/* Change color of dropdown links on hover. */ +.dropdown-content a:hover, .dropdown-content label:hover { + background-color: #f1f1f1 +} + +/* Show the dropdown menu */ +.show { + display: block; +} + +.shadowBlock>.blocklyPath { + fill-opacity: .5; + stroke-opacity: .5; +} + +.shadowBlock>.blocklyPathLight, +.shadowBlock>.blocklyPathDark { + display: none; +} diff --git a/demos/blocklyfactory/workspacefactory/wfactory_controller.js b/demos/blocklyfactory/workspacefactory/wfactory_controller.js new file mode 100644 index 00000000..d3fb168b --- /dev/null +++ b/demos/blocklyfactory/workspacefactory/wfactory_controller.js @@ -0,0 +1,1233 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Contains the controller code for workspace factory. Depends + * on the model and view objects (created as internal variables) and interacts + * with previewWorkspace and toolboxWorkspace (internal references stored to + * both). Also depends on standard_categories.js for standard Blockly + * categories. Provides the functionality for the actions the user can initiate: + * - adding and removing categories + * - switching between categories + * - printing and downloading configuration xml + * - updating the preview workspace + * - changing a category name + * - moving the position of a category. + * + * @author Emma Dauterman (evd2014) + */ + + goog.require('FactoryUtils'); + goog.require('StandardCategories'); + +/** + * Class for a WorkspaceFactoryController + * @constructor + * + * @param {!string} toolboxName Name of workspace toolbox XML. + * @param {!string} toolboxDiv Name of div to inject toolbox workspace in. + * @param {!string} previewDiv Name of div to inject preview workspace in. + */ +WorkspaceFactoryController = function(toolboxName, toolboxDiv, previewDiv) { + // Toolbox XML element for the editing workspace. + this.toolbox = document.getElementById(toolboxName); + + // Workspace for user to drag blocks in for a certain category. + this.toolboxWorkspace = Blockly.inject(toolboxDiv, + {grid: + {spacing: 25, + length: 3, + colour: '#ccc', + snap: true}, + media: '../../media/', + toolbox: this.toolbox, + }); + + // Workspace for user to preview their changes. + this.previewWorkspace = Blockly.inject(previewDiv, + {grid: + {spacing: 25, + length: 3, + colour: '#ccc', + snap: true}, + media: '../../media/', + toolbox: '', + zoom: + {controls: true, + wheel: true} + }); + + // Model to keep track of categories and blocks. + this.model = new WorkspaceFactoryModel(); + // Updates the category tabs. + this.view = new WorkspaceFactoryView(); + // Generates XML for categories. + this.generator = new WorkspaceFactoryGenerator(this.model); + // Tracks which editing mode the user is in. Toolbox mode on start. + this.selectedMode = WorkspaceFactoryController.MODE_TOOLBOX; + // True if key events are enabled, false otherwise. + this.keyEventsEnabled = true; +}; + +// Toolbox editing mode. Changes the user makes to the workspace updates the +// toolbox. +WorkspaceFactoryController.MODE_TOOLBOX = 'toolbox'; +// Pre-loaded workspace editing mode. Changes the user makes to the workspace +// udpates the pre-loaded blocks. +WorkspaceFactoryController.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. + */ +WorkspaceFactoryController.prototype.addCategory = function() { + // 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); + // Allow user to use the default options for injecting with categories. + this.allowToSetDefaultOptions(); + // Update preview here in case exit early. + this.updatePreview(); + } + } + + // After possibly creating a category, check again if it's the first category. + 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, 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(); +}; + +/** + * Helper method for addCategory. Adds a category to the view given a name, ID, + * and a boolean for if it's the first category created. Assumes the category + * has already been created in the model. Does not switch to category. + * + * @param {!string} name Name of category being added. + * @param {!string} id The ID of the category being added. + * @param {boolean} isFirstCategory True if it's the first category created, + * false otherwise. + */ +WorkspaceFactoryController.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, isFirstCategory); + this.addClickToSwitch(tab, category.id); +}; + +/** + * Given a tab and a ID to be associated to that tab, adds a listener to + * that tab so that when the user clicks on the tab, it switches to the + * element associated with that ID. + * + * @param {!Element} tab The DOM element to add the listener to. + * @param {!string} id The ID of the element to switch to when tab is clicked. + */ +WorkspaceFactoryController.prototype.addClickToSwitch = function(tab, id) { + var self = this; + var clickFunction = function(id) { // Keep this in scope for switchElement + return function() { + self.switchElement(id); + }; + }; + this.view.bindClick(tab, clickFunction(id)); +}; + +/** + * Attached to "-" button. Checks if the user wants to delete + * the current element. Removes the element and switches to another element. + * When the last element is removed, it switches to a single flyout mode. + * + */ +WorkspaceFactoryController.prototype.removeElement = function() { + // Check that there is a currently selected category to remove. + 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.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(); +}; + +/** + * Gets a valid name for a new category from the user. + * + * @param {!string} promptString Prompt for the user to enter a name. + * @return {string} Valid name for a new category, or null if cancelled. + */ +WorkspaceFactoryController.prototype.promptForNewCategoryName = + function(promptString) { + do { + var name = prompt(promptString); + if (!name) { // If cancelled. + return null; + } + } while (this.model.hasCategoryByName(name)); + return name; +} + +/** + * Switches to a new tab for the element given by ID. Stores XML and blocks + * to reload later, updates selected accordingly, and clears the workspace + * and clears undo, then loads the new element. + * + * @param {!string} id ID of tab to be opened, must be valid element ID. + */ +WorkspaceFactoryController.prototype.switchElement = function(id) { + // Disables events while switching so that Blockly delete and create events + // don't update the preview repeatedly. + Blockly.Events.disable(); + // Caches information to reload or generate xml if switching to/from element. + // Only saves if a category is selected. + if (this.model.getSelectedId() != null && id != null) { + this.model.getSelected().saveFromWorkspace(this.toolboxWorkspace); + } + // Load element. + this.clearAndLoadElement(id); + // Enable Blockly events again. + Blockly.Events.enable(); +}; + +/** + * Switches to a new tab for the element by ID. Helper for switchElement. + * Updates selected, clears the workspace and clears undo, loads a new element. + * + * @param {!string} id ID of category to load + */ +WorkspaceFactoryController.prototype.clearAndLoadElement = function(id) { + // Unselect current tab if switching to and from an element. + if (this.model.getSelectedId() != null && id != null) { + this.view.setCategoryTabSelection(this.model.getSelectedId(), false); + } + + // If switching to another category, set category selection in the model and + // view. + if (id != null) { + // Set next category. + this.model.setSelectedById(id); + + // Clears workspace and loads next category. + this.clearAndLoadXml_(this.model.getSelectedXml()); + + // Selects the next tab. + this.view.setCategoryTabSelection(id, true); + + // Order blocks as shown in flyout. + this.toolboxWorkspace.cleanUp(); + + // Update category editing buttons. + this.view.updateState(this.model.getIndexByElementId + (this.model.getSelectedId()), this.model.getSelected()); + } else { + // Update category editing buttons for no categories. + this.view.updateState(-1, null); + } +}; + +/** + * 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 + * (WorkspaceFactoryController.MODE_TOOLBOX for the toolbox configuration, + * and WorkspaceFactoryController.MODE_PRELOAD for the pre-loaded workspace + * configuration) + */ +WorkspaceFactoryController.prototype.exportXmlFile = function(exportMode) { + // Generate XML. + if (exportMode == WorkspaceFactoryController.MODE_TOOLBOX) { + // Export the toolbox XML. + + var configXml = Blockly.Xml.domToPrettyText + (this.generator.generateToolboxXml()); + } else if (exportMode == WorkspaceFactoryController.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 for ' + (exportMode == + WorkspaceFactoryController.MODE_TOOLBOX ? 'toolbox XML: ' : + 'pre-loaded workspace XML: ')); + if (!fileName) { // If cancelled + return; + } + // Download file. + var data = new Blob([configXml], {type: 'text/xml'}); + this.view.createAndDownloadFile(fileName, data); + }; + +/** + * 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. + */ +WorkspaceFactoryController.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/javascript'}); + this.view.createAndDownloadFile(fileName, data); +}; + +/** + * Tied to "Print" button. Mainly used for debugging purposes. Prints + * the configuration XML to the console. + */ +WorkspaceFactoryController.prototype.printConfig = function() { + // Capture any changes made by user before generating XML. + this.saveStateFromWorkspace(); + // Print XML. + window.console.log(Blockly.Xml.domToPrettyText + (this.generator.generateToolboxXml())); +}; + +/** + * Updates the preview workspace based on the toolbox workspace. If switching + * from no categories to categories or categories to no categories, reinjects + * 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. Make sure + * that no changes have been made to the workspace since updating the model + * (if this might be the case, call saveStateFromWorkspace). + */ +WorkspaceFactoryController.prototype.updatePreview = function() { + // Disable events to stop updatePreview from recursively calling itself + // through event handlers. + Blockly.Events.disable(); + + // Only update the toolbox if not in read only mode. + if (!this.model.options['readOnly']) { + // 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, expensive. + } else { + this.previewWorkspace.updateToolbox(tree); + } + } else { + // Uses categories, creates a toolbox. + if (!this.previewWorkspace.toolbox_) { + this.reinjectPreview(tree); // Create a toolbox, expensive. + } else { + this.previewWorkspace.updateToolbox(tree); + } + } + } + + // Update pre-loaded blocks in the preview 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. + */ +WorkspaceFactoryController.prototype.saveStateFromWorkspace = function() { + if (this.selectedMode == WorkspaceFactoryController.MODE_TOOLBOX) { + // If currently editing the toolbox. + this.model.getSelected().saveFromWorkspace(this.toolboxWorkspace); + } else if (this.selectedMode == WorkspaceFactoryController.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 + * flyout. More expensive than simply updating the flyout or toolbox. + * + * @param {!Element} tree of xml elements + * @package + */ +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); + Blockly.Xml.domToWorkspace(this.generator.generateWorkspaceXml(), + this.previewWorkspace); +}; + +/** + * 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. + */ +WorkspaceFactoryController.prototype.changeCategoryName = function() { + // Return if a category is not selected. + if (this.model.getSelected().type != ListElement.TYPE_CATEGORY) { + return; + } + // Get new name from user. + var newName = this.promptForNewCategoryName('What do you want to change this' + + ' category\'s name to?'); + if (!newName) { // If cancelled. + return; + } + // Change category name. + this.model.getSelected().changeName(newName); + this.view.updateCategoryName(newName, this.model.getSelectedId()); + // Update preview. + this.updatePreview(); +}; + +/** + * Tied to arrow up and arrow down buttons. Swaps with the element above or + * below the currently selected element (offset categories away from the + * current element). Updates state to enable the correct element editing + * buttons. + * + * @param {int} offset The index offset from the currently selected element + * to swap with. Positive if the element to be swapped with is below, negative + * if the element to be swapped with is above. + */ +WorkspaceFactoryController.prototype.moveElement = function(offset) { + var curr = this.model.getSelected(); + if (!curr) { // Return if no selected element. + return; + } + var currIndex = this.model.getIndexByElementId(curr.id); + var swapIndex = this.model.getIndexByElementId(curr.id) + offset; + var swap = this.model.getElementByIndex(swapIndex); + if (!swap) { // Return if cannot swap in that direction. + return; + } + // Move currently selected element to index of other element. + // Indexes must be valid because confirmed that curr and swap exist. + this.moveElementToIndex(curr, swapIndex, currIndex); + // Update element editing buttons. + this.view.updateState(swapIndex, this.model.getSelected()); + // Update preview. + this.updatePreview(); +}; + +/** + * Moves a element to a specified index and updates the model and view + * accordingly. Helper functions throw an error if indexes are out of bounds. + * + * @param {!Element} element The element to move. + * @param {int} newIndex The index to insert the element at. + * @param {int} oldIndex The index the element is currently at. + */ +WorkspaceFactoryController.prototype.moveElementToIndex = function(element, + newIndex, oldIndex) { + this.model.moveElementToIndex(element, newIndex, oldIndex); + 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 + * loads it as a new category and switches to it. Leverages StandardCategories. + */ +WorkspaceFactoryController.prototype.loadCategory = function() { + // Prompt user for the name of the standard category to load. + do { + var name = prompt('Enter the name of the category you would like to import ' + + '(Logic, Loops, Math, Text, Lists, Colour, Variables, or Functions)'); + if (!name) { + return; // Exit if cancelled. + } + } while (!this.isStandardCategoryName(name)); + + // Check if the user can create that standard category. + if (this.model.hasVariables() && name.toLowerCase() == 'variables') { + alert('A Variables category already exists. You cannot create multiple' + + ' variables categories.'); + return; + } + if (this.model.hasProcedures() && name.toLowerCase() == 'functions') { + alert('A Functions category already exists. You cannot create multiple' + + ' functions categories.'); + return; + } + // Check if the user can create a category with that name. + var standardCategory = StandardCategories.categoryMap[name.toLowerCase()] + if (this.model.hasCategoryByName(standardCategory.name)) { + alert('You already have a category with the name ' + standardCategory.name + + '. Rename your category and try again.'); + return; + } + + var isFirstCategory = !this.model.hasElements(); + // Copy the standard category in the model. + var copy = standardCategory.copy(); + + // Add it to the model. + this.model.addElementToList(copy); + + // 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) { + this.view.setBorderColor(copy.id, copy.color); + } + // Switch to loaded category. + this.switchElement(copy.id); + // Convert actual shadow blocks to user-generated shadow blocks. + 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(); +}; + +/** + * Given the name of a category, determines if it's the name of a standard + * category (case insensitive). + * + * @param {string} name The name of the category that should be checked if it's + * in StandardCategories categoryMap + * @return {boolean} True if name is a standard category name, false otherwise. + */ +WorkspaceFactoryController.prototype.isStandardCategoryName = function(name) { + for (var category in StandardCategories.categoryMap) { + if (name.toLowerCase() == category) { + return true; + } + } + return false; +}; + +/** + * Connected to the "add separator" dropdown option. If categories already + * exist, adds a separator to the model and view. Does not switch to select + * the separator, and updates the preview. + */ +WorkspaceFactoryController.prototype.addSeparator = function() { + // Don't allow the user to add a separator if a category has not been created. + if (!this.model.hasElements()) { + alert('Add a category before adding a separator.'); + return; + } + // Create the separator in the model. + var separator = new ListElement(ListElement.TYPE_SEPARATOR); + this.model.addElementToList(separator); + // Create the separator in the view. + var tab = this.view.addSeparatorTab(separator.id); + this.addClickToSwitch(tab, separator.id); + // Switch to the separator and update the preview. + this.switchElement(separator.id); + this.updatePreview(); +}; + +/** + * Connected to the import button. Given the file path inputted by the user + * 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 (WorkspaceFactoryController.MODE_TOOLBOX or + * WorkspaceFactoryController.MODE_PRELOAD). + */ +WorkspaceFactoryController.prototype.importFile = function(file, importMode) { + // Exit if cancelled. + if (!file) { + return; + } + + Blockly.Events.disable(); + var controller = this; + 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); + if (importMode == WorkspaceFactoryController.MODE_TOOLBOX) { + // Switch mode and import toolbox XML. + controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX); + controller.importToolboxFromTree_(tree); + } else if (importMode == WorkspaceFactoryController.MODE_PRELOAD) { + // Switch mode and import pre-loaded workspace XML. + controller.setMode(WorkspaceFactoryController.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); + } finally { + Blockly.Events.enable(); + } + } + + // 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. Assumes that the mode is MODE_TOOLBOX. + * @private + * + * @param {!Element} tree XML tree to be loaded to toolbox editing area. + */ +WorkspaceFactoryController.prototype.importToolboxFromTree_ = function(tree) { + // Clear current editing area. + this.model.clearToolboxList(); + this.view.clearToolboxTabs(); + + if (tree.getElementsByTagName('category').length == 0) { + // No categories present. + // Load all the blocks into a single category evenly spaced. + Blockly.Xml.domToWorkspace(tree, this.toolboxWorkspace); + this.toolboxWorkspace.cleanUp(); + + // Convert actual shadow blocks to user-generated shadow blocks. + this.convertShadowBlocks(); + + // 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); + var category = this.model.getElementByIndex(i); + this.switchElement(category.id); + + // Load all blocks in that category to the workspace to be evenly + // spaced and saved to that category. + for (var j = 0, blockXml; blockXml = item.children[j]; j++) { + Blockly.Xml.domToBlock(blockXml, this.toolboxWorkspace); + } + + // Evenly space the blocks. + this.toolboxWorkspace.cleanUp(); + + // Convert actual shadow blocks to user-generated shadow blocks. + this.convertShadowBlocks(); + + // Set category color. + if (item.getAttribute('colour')) { + category.changeColor(item.getAttribute('colour')); + this.view.setBorderColor(category.id, category.color); + } + // Set any custom tags. + if (item.getAttribute('custom')) { + this.model.addCustomTag(category, item.getAttribute('custom')); + } + } else { + // If the element is a separator, add the separator and switch to it. + this.addSeparator(); + this.switchElement(this.model.getElementByIndex(i).id); + } + } + } + this.view.updateState(this.model.getIndexByElementId + (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(); + + 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. + */ +WorkspaceFactoryController.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. + */ +WorkspaceFactoryController.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. + */ +WorkspaceFactoryController.prototype.importPreloadFromTree_ = function(tree) { + this.clearAndLoadXml_(tree); + this.model.savePreloadXml(tree); + this.saveStateFromWorkspace(); + this.updatePreview(); +} + +/** + * Clears the editing area completely, deleting all categories and all + * blocks in the model and view and all pre-loaded blocks. Tied to the + * "Clear" button. + */ +WorkspaceFactoryController.prototype.clearAll = function() { + var hasCategories = this.model.hasElements(); + this.model.clearToolboxList(); + this.view.clearToolboxTabs(); + this.model.savePreloadXml(Blockly.Xml.textToDom('')); + this.view.addEmptyCategoryMessage(); + this.view.updateState(-1, null); + this.toolboxWorkspace.clear(); + this.toolboxWorkspace.clearUndo(); + this.saveStateFromWorkspace(); + if (hasCategories) { + this.allowToSetDefaultOptions(); + } + this.updatePreview(); +}; + +/* + * Makes the currently selected block a user-generated shadow block. These + * blocks are not made into real shadow blocks, but recorded in the model + * and visually marked as shadow blocks, allowing the user to move and edit + * them (which would be impossible with actual shadow blocks). Updates the + * preview when done. + * + */ +WorkspaceFactoryController.prototype.addShadow = function() { + // No block selected to make a shadow block. + if (!Blockly.selected) { + return; + } + // Clear any previous warnings on the block (would only have warnings on + // a non-shadow block if it was nested inside another shadow block). + Blockly.selected.setWarningText(null); + // Set selected block and all children as shadow blocks. + this.addShadowForBlockAndChildren_(Blockly.selected); + + // Save and update the preview. + this.saveStateFromWorkspace(); + this.updatePreview(); +}; + +/** + * Sets a block and all of its children to be user-generated shadow blocks, + * both in the model and view. + * @private + * + * @param {!Blockly.Block} block The block to be converted to a user-generated + * shadow block. + */ +WorkspaceFactoryController.prototype.addShadowForBlockAndChildren_ = + function(block) { + // Convert to shadow block. + this.view.markShadowBlock(block); + this.model.addShadowBlock(block.id); + + if (this.hasVariableField(block)) { + block.setWarningText('Cannot make variable blocks shadow blocks.'); + } + + // Convert all children to shadow blocks recursively. + var children = block.getChildren(); + for (var i = 0; i < children.length; i++) { + this.addShadowForBlockAndChildren_(children[i]); + } +}; + +/** + * Checks if a block has a variable field. Blocks with variable fields cannot + * be shadow blocks. + * + * @param {Blockly.Block} block The block to check if a variable field exists. + * @return {boolean} True if the block has a variable field, false otherwise. + */ +WorkspaceFactoryController.prototype.hasVariableField = function(block) { + if (!block) { + return false; + } + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].fieldRow.length > 0 && + block.inputList[i].fieldRow[0].name == 'VAR') { + return true; + } + } + return false; +}; + +/** + * If the currently selected block is a user-generated shadow block, this + * function makes it a normal block again, removing it from the list of + * shadow blocks and loading the workspace again. Updates the preview again. + * + */ +WorkspaceFactoryController.prototype.removeShadow = function() { + // No block selected to modify. + if (!Blockly.selected) { + return; + } + this.model.removeShadowBlock(Blockly.selected.id); + this.view.unmarkShadowBlock(Blockly.selected); + + // If turning invalid shadow block back to normal block, remove warning. + Blockly.selected.setWarningText(null); + + this.saveStateFromWorkspace(); + this.updatePreview(); +}; + +/** + * Given a unique block ID, uses the model to determine if a block is a + * user-generated shadow block. + * + * @param {!string} blockId The unique ID of the block to examine. + * @return {boolean} True if the block is a user-generated shadow block, false + * otherwise. + */ +WorkspaceFactoryController.prototype.isUserGenShadowBlock = function(blockId) { + return this.model.isShadowBlock(blockId); +} + +/** + * Call when importing XML containing real shadow blocks. This function turns + * all real shadow blocks loaded in the workspace into user-generated shadow + * blocks, meaning they are marked as shadow blocks by the model and appear as + * shadow blocks in the view but are still editable and movable. + */ +WorkspaceFactoryController.prototype.convertShadowBlocks = function() { + var blocks = this.toolboxWorkspace.getAllBlocks(); + for (var i = 0, block; block = blocks[i]; i++) { + if (block.isShadow()) { + block.setShadow(false); + this.model.addShadowBlock(block.id); + this.view.markShadowBlock(block); + } + } +}; + +/** + * 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 + * (WorkspaceFactoryController.MODE_TOOLBOX or + * WorkspaceFactoryController.MODE_PRELOAD). + */ +WorkspaceFactoryController.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 == WorkspaceFactoryController.MODE_TOOLBOX) { + // Open the toolbox editing space. + 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. + 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. + */ +WorkspaceFactoryController.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())); + this.warnForUndefinedBlocks_(); +}; + +/** + * Sets the standard default options for the options object and updates + * the preview workspace. The default values depends on if categories are + * present. + */ +WorkspaceFactoryController.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. + */ +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); + + // 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())); +}; + +/** + * Imports blocks from a file, generating a category in the toolbox workspace + * to allow the user to use imported blocks in the toolbox and in pre-loaded + * blocks. + * + * @param {!File} file File object for the blocks to import. + * @param {!string} format The format of the file to import, either 'JSON' or + * 'JavaScript'. + */ +WorkspaceFactoryController.prototype.importBlocks = + function(file, format) { + // Generate category name from file name. + var categoryName = file.name + ' blocks'; + + var controller = this; + var reader = new FileReader(); + + // To be executed when the reader has read the file. + reader.onload = function() { + try { + // Define blocks using block types from file. + var blockTypes = FactoryUtils.defineAndGetBlockTypes(reader.result, format); + var blocks = controller.generator.getDefinedBlocks(blockTypes); + + // Generate category XML and append to toolbox. + var categoryXml = FactoryUtils.generateCategoryXml(blocks, categoryName); + // Get random color for category between 0 and 360. Gives each imported + // category a different color. + var randomColor = Math.floor(Math.random() * 360); + categoryXml.setAttribute('colour', randomColor); + controller.toolbox.appendChild(categoryXml); + controller.toolboxWorkspace.updateToolbox(controller.toolbox); + // Update imported block types. + this.model.addImportedBlocks(blockTypes); + // Reload current category to possibly reflect any newly defined blocks. + this.clearAndLoadXml_(Blockly.Xml.workspaceToDom(this.toolboxWorkspace)); + } catch (e) { + alert('Cannot read blocks from file.'); + window.console.log(e); + } + } + + // Read the file asynchronously. + reader.readAsText(file); +}; + +/* + * Updates the block library category in the toolbox workspace toolbox. + * + * @param {!Element} categoryXml XML for the block library category. + * @param {!Array} libBlockTypes Array of block types from the block + * library. + */ +WorkspaceFactoryController.prototype.setBlockLibCategory = + function(categoryXml, libBlockTypes) { + var blockLibCategory = document.getElementById('blockLibCategory'); + + // Set category id so that it can be easily replaced, and set a standard, + // arbitrary block library color. + categoryXml.setAttribute('id', 'blockLibCategory'); + categoryXml.setAttribute('colour', 260); + + // Update the toolbox and toolboxWorkspace. + this.toolbox.replaceChild(categoryXml, blockLibCategory); + this.toolboxWorkspace.updateToolbox(this.toolbox); + + // Update the block library types. + this.model.updateLibBlockTypes(libBlockTypes); + + // Reload XML on page to account for blocks now defined or undefined in block + // library. + this.clearAndLoadXml_(Blockly.Xml.workspaceToDom(this.toolboxWorkspace)); +}; + +/** + * Return the block types used in the custom toolbox and pre-loaded workspace. + * + * @return {!Array.} Block types used in the custom toolbox and + * pre-loaded workspace. + */ +WorkspaceFactoryController.prototype.getAllUsedBlockTypes = function() { + return this.model.getAllUsedBlockTypes(); +}; + +/** + * Determines if a block loaded in the workspace has a definition (if it + * is a standard block, is defined in the block library, or has a definition + * imported). + * + * @param {!Blockly.Block} block The block to examine. + */ +WorkspaceFactoryController.prototype.isDefinedBlock = function(block) { + return this.model.isDefinedBlockType(block.type); +}; + +/** + * Sets a warning on blocks loaded to the workspace that are not defined. + * @private + */ +WorkspaceFactoryController.prototype.warnForUndefinedBlocks_ = function() { + var blocks = this.toolboxWorkspace.getAllBlocks(); + for (var i = 0, block; block = blocks[i]; i++) { + if (!this.isDefinedBlock(block)) { + block.setWarningText(block.type + ' is not defined (it is not a standard ' + + 'block, \nin your block library, or an imported block)'); + } + } +}; diff --git a/demos/blocklyfactory/workspacefactory/wfactory_generator.js b/demos/blocklyfactory/workspacefactory/wfactory_generator.js new file mode 100644 index 00000000..37725b0c --- /dev/null +++ b/demos/blocklyfactory/workspacefactory/wfactory_generator.js @@ -0,0 +1,200 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Generates the configuration xml used to update the preview + * workspace or print to the console or download to a file. Leverages + * Blockly.Xml and depends on information in the model (holds a reference). + * Depends on a hidden workspace created in the generator to load saved XML in + * order to generate toolbox XML. + * + * @author Emma Dauterman (evd2014) + */ + +goog.require('FactoryUtils'); + +/** + * Class for a WorkspaceFactoryGenerator + * @constructor + */ +WorkspaceFactoryGenerator = function(model) { + // Model to share information about categories and shadow blocks. + this.model = model; + // Create hidden workspace to load saved XML to generate toolbox XML. + var hiddenBlocks = document.createElement('div'); + // Generate a globally unique ID for the hidden div element to avoid + // collisions. + var hiddenBlocksId = Blockly.genUid(); + hiddenBlocks.id = hiddenBlocksId; + hiddenBlocks.style.display = 'none'; + document.body.appendChild(hiddenBlocks); + this.hiddenWorkspace = Blockly.inject(hiddenBlocksId); +}; + +/** + * 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. + */ +WorkspaceFactoryGenerator.prototype.generateToolboxXml = function() { + // Create DOM for XML. + var xmlDom = goog.dom.createDom('xml', + { + 'id' : 'toolbox', + 'style' : 'display:none' + }); + if (!this.model.hasElements()) { + // Toolbox has no categories. Use XML directly from workspace. + this.loadToHiddenWorkspace_(this.model.getSelectedXml()); + this.appendHiddenWorkspaceToDom_(xmlDom); + } else { + // Toolbox has categories. + // Assert that selected != null + if (!this.model.getSelected()) { + throw new Error('Selected is null when the toolbox is empty.'); + } + + var xml = this.model.getSelectedXml(); + var toolboxList = this.model.getToolboxList(); + + // Iterate through each category to generate XML for each using the + // hidden workspace. Load each category to the hidden workspace to make sure + // that all the blocks that are not top blocks are also captured as block + // groups in the flyout. + for (var i = 0; i < toolboxList.length; i++) { + var element = toolboxList[i]; + if (element.type == ListElement.TYPE_SEPARATOR) { + // If the next element is a separator. + var nextElement = goog.dom.createDom('sep'); + } 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); + // Add a colour attribute if one exists. + if (element.color != null) { + nextElement.setAttribute('colour', element.color); + } + // Add a custom attribute if one exists. + if (element.custom != null) { + nextElement.setAttribute('custom', element.custom); + } + // Load that category to hidden workspace, setting user-generated shadow + // blocks as real shadow blocks. + this.loadToHiddenWorkspace_(element.xml); + this.appendHiddenWorkspaceToDom_(nextElement); + } + xmlDom.appendChild(nextElement); + } + } + 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. + * + */ +WorkspaceFactoryGenerator.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; + }; + +/** + * Loads the given XML to the hidden workspace and sets any user-generated + * shadow blocks to be actual shadow blocks. + * @private + * + * @param {!Element} xml The XML to be loaded to the hidden workspace. + */ +WorkspaceFactoryGenerator.prototype.loadToHiddenWorkspace_ = function(xml) { + this.hiddenWorkspace.clear(); + Blockly.Xml.domToWorkspace(xml, this.hiddenWorkspace); + this.setShadowBlocksInHiddenWorkspace_(); +} + + /** + * Encodes blocks in the hidden workspace in a XML DOM element. Very + * similar to workspaceToDom, but doesn't capture IDs. Uses the top-level + * blocks loaded in hiddenWorkspace. + * @private + * + * @param {!Element} xmlDom Tree of XML elements to be appended to. + */ +WorkspaceFactoryGenerator.prototype.appendHiddenWorkspaceToDom_ = + function(xmlDom) { + var blocks = this.hiddenWorkspace.getTopBlocks(); + for (var i = 0, block; block = blocks[i]; i++) { + var blockChild = Blockly.Xml.blockToDom(block); + blockChild.removeAttribute('id'); + xmlDom.appendChild(blockChild); + } +}; + +/** + * Sets the user-generated shadow blocks loaded into hiddenWorkspace to be + * actual shadow blocks. This is done so that blockToDom records them as + * shadow blocks instead of regular blocks. + * @private + * + */ +WorkspaceFactoryGenerator.prototype.setShadowBlocksInHiddenWorkspace_ = + function() { + var blocks = this.hiddenWorkspace.getAllBlocks(); + for (var i = 0; i < blocks.length; i++) { + if (this.model.isShadowBlock(blocks[i].id)) { + blocks[i].setShadow(true); + } + } +}; + +/** + * Given a set of block types, gets the Blockly.Block objects for each block + * type. + * + * @param {!Array} blockTypes Array of blocks that have been defined. + * @return {!Array} Array of Blockly.Block objects corresponding + * to the array of blockTypes. + */ +WorkspaceFactoryGenerator.prototype.getDefinedBlocks = function(blockTypes) { + var blocks = []; + for (var i = 0; i < blockTypes.length ; i++) { + blocks.push(FactoryUtils.getDefinedBlock(blockTypes[i], + this.hiddenWorkspace)); + } + return blocks; +}; + diff --git a/demos/blocklyfactory/workspacefactory/wfactory_init.js b/demos/blocklyfactory/workspacefactory/wfactory_init.js new file mode 100644 index 00000000..4e286da8 --- /dev/null +++ b/demos/blocklyfactory/workspacefactory/wfactory_init.js @@ -0,0 +1,535 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Contains the init functions for the workspace factory tab. + * Adds click handlers to buttons and dropdowns, adds event listeners for + * keydown events and Blockly events, and configures the initial setup of + * the page. + * + * @author Emma Dauterman (evd2014) + */ + +/** + * Namespace for workspace factory initialization methods. + * @namespace + */ +WorkspaceFactoryInit = {}; + +/** + * Initialization for workspace factory tab. + * + * @param {!FactoryController} controller The controller for the workspace + * factory tab. + */ +WorkspaceFactoryInit.initWorkspaceFactory = function(controller) { + // Disable category editing buttons until categories are created. + document.getElementById('button_remove').disabled = true; + document.getElementById('button_up').disabled = true; + document.getElementById('button_down').disabled = true; + document.getElementById('button_editCategory').disabled = true; + document.getElementById('button_editShadow').disabled = true; + + this.initColorPicker_(controller); + this.addWorkspaceFactoryEventListeners_(controller); + this.assignWorkspaceFactoryClickHandlers_(controller); + this.addWorkspaceFactoryOptionsListeners_(controller); + + // Check standard options and apply the changes to update the view. + controller.setStandardOptionsAndUpdate(); +}; + +/** + * Initialize the color picker in workspace factory. + * @private + * + * @param {!FactoryController} controller The controller for the workspace + * factory tab. + */ +WorkspaceFactoryInit.initColorPicker_ = function(controller) { + // Array of Blockly category colors, variety of hues with saturation 45% + // and value 65% as specified in Blockly Developer documentation: + // https://developers.google.com/blockly/guides/create-custom-blocks/define-blocks + var colors = ['#A65C5C', + '#A6635C', + '#A66A5C', + '#A6725C', + '#A6795C', + '#A6815C', + '#A6885C', + '#A6905C', + '#A6975C', + '#A69F5C', + '#A6A65C', + '#9FA65C', + '#97A65C', + '#90A65C', + '#88A65C', + '#81A65C', + '#79A65C', + '#6FA65C', + '#66A65C', + '#5EA65C', + '#5CA661', + '#5CA668', + '#5CA66F', + '#5CA677', + '#5CA67E', + '#5CA686', + '#5CA68D', + '#5CA695', + '#5CA69C', + '#5CA6A4', + '#5CA1A6', + '#5C9AA6', + '#5C92A6', + '#5C8BA6', + '#5C83A6', + '#5C7CA6', + '#5C74A6', + '#5C6AA6', + '#5C61A6', + '#5E5CA6', + '#665CA6', + '#6D5CA6', + '#745CA6', + '#7C5CA6', + '#835CA6', + '#8B5CA6', + '#925CA6', + '#9A5CA6', + '#A15CA6', + '#A65CA4', + '#A65C9C', + '#A65C95', + '#A65C8D', + '#A65C86', + '#A65C7E', + '#A65C77', + '#A65C6F', + '#A65C66', + '#A65C61', + '#A65C5E']; + + // Create color picker with specific set of Blockly colors. + var colorPicker = new goog.ui.ColorPicker(); + colorPicker.setColors(colors); + + // Create and render the popup color picker and attach to button. + var popupPicker = new goog.ui.PopupColorPicker(null, colorPicker); + popupPicker.render(); + popupPicker.attach(document.getElementById('dropdown_color')); + popupPicker.setFocusable(true); + goog.events.listen(popupPicker, 'change', function(e) { + controller.changeSelectedCategoryColor(popupPicker.getSelectedColor()); + document.getElementById('dropdownDiv_editCategory').classList.remove + ("show"); + }); +}; + +/** + * Assign click handlers for workspace factory. + * @private + * + * @param {!FactoryController} controller The controller for the workspace + * factory tab. + */ +WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ = + function(controller) { + document.getElementById('tab_toolbox').addEventListener + ('click', + function() { + controller.setMode(WorkspaceFactoryController.MODE_TOOLBOX); + }); + + document.getElementById('tab_preload').addEventListener + ('click', + function() { + controller.setMode(WorkspaceFactoryController.MODE_PRELOAD); + }); + + document.getElementById('button_add').addEventListener + ('click', + function() { + document.getElementById('dropdownDiv_add').classList.toggle("show"); + }); + + document.getElementById('dropdown_newCategory').addEventListener + ('click', + function() { + controller.addCategory(); + document.getElementById('dropdownDiv_add').classList.remove("show"); + }); + + document.getElementById('dropdown_loadCategory').addEventListener + ('click', + function() { + controller.loadCategory(); + document.getElementById('dropdownDiv_add').classList.remove("show"); + }); + + document.getElementById('dropdown_separator').addEventListener + ('click', + function() { + controller.addSeparator(); + document.getElementById('dropdownDiv_add').classList.remove("show"); + }); + + document.getElementById('button_remove').addEventListener + ('click', + function() { + controller.removeElement(); + }); + + document.getElementById('dropdown_exportToolbox').addEventListener + ('click', + function() { + controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX); + document.getElementById('dropdownDiv_export').classList.remove("show"); + }); + + document.getElementById('dropdown_exportPreload').addEventListener + ('click', + function() { + controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD); + document.getElementById('dropdownDiv_export').classList.remove("show"); + }); + + document.getElementById('dropdown_exportOptions').addEventListener + ('click', + function() { + controller.exportOptionsFile(); + document.getElementById('dropdownDiv_export').classList.remove("show"); + }); + + document.getElementById('dropdown_exportAll').addEventListener + ('click', + function() { + controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX); + controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD); + controller.exportOptionsFile(); + document.getElementById('dropdownDiv_export').classList.remove("show"); + }); + + document.getElementById('button_export').addEventListener + ('click', + function() { + document.getElementById('dropdownDiv_export').classList.toggle("show"); + document.getElementById('dropdownDiv_load').classList.remove("show"); + document.getElementById('dropdownDiv_importBlocks').classList. + remove("show"); + }); + + document.getElementById('button_up').addEventListener + ('click', + function() { + controller.moveElement(-1); + }); + + document.getElementById('button_down').addEventListener + ('click', + function() { + controller.moveElement(1); + }); + + document.getElementById('button_editCategory').addEventListener + ('click', + function() { + document.getElementById('dropdownDiv_editCategory').classList. + toggle("show"); + }); + + document.getElementById('button_editShadow').addEventListener + ('click', + function() { + if (Blockly.selected) { + // Can only edit blocks when a block is selected. + + if (!controller.isUserGenShadowBlock(Blockly.selected.id) && + Blockly.selected.getSurroundParent() != null) { + // If a block is selected that could be a valid shadow block (not a + // shadow block, has a surrounding parent), let the user make it a + // shadow block. Use toggle instead of add so that the user can + // click the button again to make the dropdown disappear without + // clicking one of the options. + document.getElementById('dropdownDiv_editShadowRemove').classList. + remove("show"); + document.getElementById('dropdownDiv_editShadowAdd').classList. + toggle("show"); + } else { + // If the block is a shadow block, let the user make it a normal + // block. + document.getElementById('dropdownDiv_editShadowAdd').classList. + remove("show"); + document.getElementById('dropdownDiv_editShadowRemove').classList. + toggle("show"); + } + } + }); + + document.getElementById('dropdown_name').addEventListener + ('click', + function() { + controller.changeCategoryName(); + document.getElementById('dropdownDiv_editCategory').classList. + remove("show"); + }); + +document.getElementById('button_importBlocks').addEventListener + ('click', + function() { + document.getElementById('dropdownDiv_importBlocks').classList. + toggle("show"); + document.getElementById('dropdownDiv_export').classList.remove("show"); + document.getElementById('dropdownDiv_load').classList.remove("show"); + }); + + document.getElementById('button_load').addEventListener + ('click', + function() { + document.getElementById('dropdownDiv_load').classList.toggle("show"); + document.getElementById('dropdownDiv_export').classList.remove("show"); + document.getElementById('dropdownDiv_importBlocks').classList. + remove("show"); + }); + + document.getElementById('input_loadToolbox').addEventListener + ('change', + function() { + controller.importFile(event.target.files[0], + WorkspaceFactoryController.MODE_TOOLBOX); + document.getElementById('dropdownDiv_load').classList.remove("show"); + }); + + document.getElementById('input_loadPreload').addEventListener + ('change', + function() { + controller.importFile(event.target.files[0], + WorkspaceFactoryController.MODE_PRELOAD); + document.getElementById('dropdownDiv_load').classList.remove("show"); + }); + + document.getElementById('input_importBlocksJson').addEventListener + ('change', + function() { + controller.importBlocks(event.target.files[0],'JSON'); + document.getElementById('dropdownDiv_importBlocks').classList. + remove("show"); + }); + + document.getElementById('input_importBlocksJs').addEventListener + ('change', + function() { + controller.importBlocks(event.target.files[0],'JavaScript'); + document.getElementById('dropdownDiv_importBlocks').classList. + remove("show"); + }); + + document.getElementById('button_clear').addEventListener + ('click', + function() { + document.getElementById('dropdownDiv_importBlocks').classList. + remove("show"); + document.getElementById('dropdownDiv_export').classList.remove("show"); + document.getElementById('dropdownDiv_load').classList.remove("show"); + controller.clearAll(); + }); + + document.getElementById('dropdown_addShadow').addEventListener + ('click', + function() { + controller.addShadow(); + document.getElementById('dropdownDiv_editShadowAdd').classList. + remove("show"); + }); + + document.getElementById('dropdown_removeShadow').addEventListener + ('click', + function() { + controller.removeShadow(); + document.getElementById('dropdownDiv_editShadowRemove').classList. + remove("show"); + // Disable shadow editing button if turning invalid shadow block back + // to normal block. + if (!Blockly.selected.getSurroundParent()) { + document.getElementById('button_editShadow').disabled = true; + } + }); + + document.getElementById('button_standardOptions').addEventListener + ('click', function() { + controller.setStandardOptionsAndUpdate(); + }); +}; + +/** + * Add event listeners for workspace factory. + * @private + * + * @param {!FactoryController} controller The controller for the workspace + * factory tab. + */ +WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) { + // Use up and down arrow keys to move categories. + window.addEventListener('keydown', function(e) { + // Don't let arrow keys have any effect if not in Workspace Factory + // editing the toolbox. + if (!(controller.keyEventsEnabled && controller.selectedMode + == WorkspaceFactoryController.MODE_TOOLBOX)) { + return; + } + + if (e.keyCode == 38) { + // Arrow up. + controller.moveElement(-1); + } else if (e.keyCode == 40) { + // Arrow down. + controller.moveElement(1); + } + }); + + // Determines if a block breaks shadow block placement rules. + // Breaks rules if (1) a shadow block no longer has a valid + // parent, or (2) a normal block is inside of a shadow block. + var isInvalidBlockPlacement = function(block) { + return ((controller.isUserGenShadowBlock(block.id) && + !block.getSurroundParent()) || + (!controller.isUserGenShadowBlock(block.id) && block.getSurroundParent() + && controller.isUserGenShadowBlock(block.getSurroundParent().id))); + }; + + // Add change listeners for toolbox workspace in workspace factory. + controller.toolboxWorkspace.addChangeListener(function(e) { + // Listen for Blockly move and delete events to update preview. + // Not listening for Blockly create events because causes the user to drop + // 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 || + e.type == Blockly.Events.CHANGE) { + controller.saveStateFromWorkspace(); + controller.updatePreview(); + } + + // Listen for Blockly UI events to correctly enable the "Edit Block" button. + // Only enable "Edit Block" when a block is selected and it has a + // surrounding parent, meaning it is nested in another block (blocks that + // are not nested in parents cannot be shadow blocks). + if (e.type == Blockly.Events.MOVE || (e.type == Blockly.Events.UI && + e.element == 'selected')) { + var selected = Blockly.selected; + + if (selected != null && selected.getSurroundParent() != null && + !controller.isUserGenShadowBlock(selected.getSurroundParent().id)) { + // Selected block is a valid shadow block or could be a valid shadow + // block. + + // Enable block editing and remove warnings if the block is not a + // variable user-generated shadow block. + document.getElementById('button_editShadow').disabled = false; + if (!controller.hasVariableField(selected) && + controller.isDefinedBlock(selected)) { + selected.setWarningText(null); + } + } else { + // Selected block cannot be a valid shadow block. + + if (selected != null && isInvalidBlockPlacement(selected)) { + // Selected block breaks shadow block rules. + // Invalid shadow block if (1) a shadow block no longer has a valid + // parent, or (2) a normal block is inside of a shadow block. + + if (!controller.isUserGenShadowBlock(selected.id)) { + // Warn if a non-shadow block is nested inside a shadow block. + selected.setWarningText('Only shadow blocks can be nested inside ' + + 'other shadow blocks.'); + } else if (!controller.hasVariableField(selected)) { + // Warn if a shadow block is invalid only if not replacing + // warning for variables. + selected.setWarningText('Shadow blocks must be nested inside other' + + ' blocks.') + } + + // Give editing options so that the user can make an invalid shadow + // block a normal block. + document.getElementById('button_editShadow').disabled = false; + } else { + // Selected block does not break any shadow block rules, but cannot + // be a shadow block. + + // Remove possible 'invalid shadow block placement' warning. + if (selected != null && controller.isDefinedBlock(selected) && + (!controller.hasVariableField(selected) || + !controller.isUserGenShadowBlock(selected.id))) { + selected.setWarningText(null); + } + + // No block selected that is a shadow block or could be a valid shadow + // block. Disable block editing. + document.getElementById('button_editShadow').disabled = true; + document.getElementById('dropdownDiv_editShadowRemove').classList. + remove("show"); + document.getElementById('dropdownDiv_editShadowAdd').classList. + remove("show"); + } + } + } + + // Convert actual shadow blocks added from the toolbox to user-generated + // shadow blocks. + if (e.type == Blockly.Events.CREATE) { + controller.convertShadowBlocks(); + } + }); +}; + +/** + * Add listeners for workspace factory options input elements. + * @private + * + * @param {!FactoryController} controller The controller for the workspace + * factory tab. + */ +WorkspaceFactoryInit.addWorkspaceFactoryOptionsListeners_ = + function(controller) { + + // Checking the grid checkbox displays grid options. + document.getElementById('option_grid_checkbox').addEventListener('change', + function(e) { + document.getElementById('grid_options').style.display = + document.getElementById('option_grid_checkbox').checked ? + 'block' : 'none'; + }); + + // Checking the grid checkbox displays zoom options. + document.getElementById('option_zoom_checkbox').addEventListener('change', + function(e) { + document.getElementById('zoom_options').style.display = + document.getElementById('option_zoom_checkbox').checked ? + 'block' : 'none'; + }); + + // Generate new options every time an options input is updated. + var optionsElements = document.getElementsByClassName('optionsInput'); + for (var i = 0; i < optionsElements.length; i++) { + optionsElements[i].addEventListener('change', function() { + controller.generateNewOptions(); + }); + } +}; diff --git a/demos/blocklyfactory/workspacefactory/wfactory_model.js b/demos/blocklyfactory/workspacefactory/wfactory_model.js new file mode 100644 index 00000000..8e552d83 --- /dev/null +++ b/demos/blocklyfactory/workspacefactory/wfactory_model.js @@ -0,0 +1,593 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * @fileoverview Stores and updates information about state and categories + * in workspace factory. Each list element is either a separator or a category, + * and each category stores its name, XML to load that category, color, + * custom tags, and a unique ID making it possible to change category names and + * move categories easily. Keeps track of the currently selected list + * element. Also keeps track of all the user-created shadow blocks and + * manipulates them as necessary. + * + * @author Emma Dauterman (evd2014) + */ + +/** + * Class for a WorkspaceFactoryModel + * @constructor + */ +WorkspaceFactoryModel = function() { + // 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 = []; + // 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(''); + // Options object to be configured for Blockly inject call. + this.options = new Object(null); + // Block Library block types. + this.libBlockTypes = []; + // Imported block types. + this.importedBlockTypes = []; + // +}; + +/** + * Given a name, determines if it is the name of a category already present. + * Used when getting a valid category name from the user. + * + * @param {string} name String name to be compared against. + * @return {boolean} True if string is a used category name, false otherwise. + */ +WorkspaceFactoryModel.prototype.hasCategoryByName = function(name) { + for (var i = 0; i < this.toolboxList.length; i++) { + if (this.toolboxList[i].type == ListElement.TYPE_CATEGORY && + this.toolboxList[i].name == name) { + return true; + } + } + return false; +}; + +/** + * Determines if a category with the 'VARIABLE' tag exists. + * + * @return {boolean} True if there exists a category with the Variables tag, + * false otherwise. + */ +WorkspaceFactoryModel.prototype.hasVariables = function() { + return this.hasVariableCategory; +}; + +/** + * Determines if a category with the 'PROCEDURE' tag exists. + * + * @return {boolean} True if there exists a category with the Procedures tag, + * false otherwise. + */ +WorkspaceFactoryModel.prototype.hasProcedures = function() { + return this.hasFunctionCategory; +}; + +/** + * Determines if the user has any elements in the toolbox. Uses the length of + * toolboxList. + * + * @return {boolean} True if elements exist, false otherwise. + */ +WorkspaceFactoryModel.prototype.hasElements = function() { + return this.toolboxList.length > 0; +}; + +/** + * Given a ListElement, adds it to the toolbox list. + * + * @param {!ListElement} element The element to be added to the list. + */ +WorkspaceFactoryModel.prototype.addElementToList = function(element) { + // Update state if the copied category has a custom tag. + this.hasVariableCategory = element.custom == 'VARIABLE' ? true : + this.hasVariableCategory; + this.hasProcedureCategory = element.custom == 'PROCEDURE' ? true : + this.hasProcedureCategory; + // Add element to toolboxList. + this.toolboxList.push(element); + // Empty single flyout. + this.flyout = null; +}; + +/** + * Given an index, deletes a list element and all associated data. + * + * @param {int} index The index of the list element to delete. + */ +WorkspaceFactoryModel.prototype.deleteElementFromList = function(index) { + // Check if index is out of bounds. + if (index < 0 || index >= this.toolboxList.length) { + return; // No entry to delete. + } + // Check if need to update flags. + this.hasVariableCategory = this.toolboxList[index].custom == 'VARIABLE' ? + false : this.hasVariableCategory; + this.hasProcedureCategory = this.toolboxList[index].custom == 'PROCEDURE' ? + false : this.hasProcedureCategory; + // Remove element. + 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. + * + */ +WorkspaceFactoryModel.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 + * bounds (throws error if not), but assumes that oldIndex is the correct index + * for list element. + * + * @param {!ListElement} element The element to move in toolboxList. + * @param {int} newIndex The index to insert the element at. + * @param {int} oldIndex The index the element is currently at. + */ +WorkspaceFactoryModel.prototype.moveElementToIndex = function(element, newIndex, + oldIndex) { + // Check that indexes are in bounds. + if (newIndex < 0 || newIndex >= this.toolboxList.length || oldIndex < 0 || + oldIndex >= this.toolboxList.length) { + throw new Error('Index out of bounds when moving element in the model.'); + } + this.deleteElementFromList(oldIndex); + this.toolboxList.splice(newIndex, 0, element); +} + +/** + * Returns the ID of the currently selected element. Returns null if there are + * no categories (if selected == null). + * + * @return {string} The ID of the element currently selected. + */ +WorkspaceFactoryModel.prototype.getSelectedId = function() { + return this.selected ? this.selected.id : null; +}; + +/** + * Returns the name of the currently selected category. Returns null if there + * are no categories (if selected == null) or the selected element is not + * a category (in which case its name is null). + * + * @return {string} The name of the category currently selected. + */ +WorkspaceFactoryModel.prototype.getSelectedName = function() { + return this.selected ? this.selected.name : null; +}; + +/** + * Returns the currently selected list element object. + * + * @return {ListElement} The currently selected ListElement + */ +WorkspaceFactoryModel.prototype.getSelected = function() { + return this.selected; +}; + +/** + * Sets list element currently selected by id. + * + * @param {string} id ID of list element that should now be selected. + */ +WorkspaceFactoryModel.prototype.setSelectedById = function(id) { + this.selected = this.getElementById(id); +}; + +/** + * Given an ID of a list element, returns the index of that list element in + * toolboxList. Returns -1 if ID is not present. + * + * @param {!string} id The ID of list element to search for. + * @return {int} The index of the list element in toolboxList, or -1 if it + * doesn't exist. + */ + +WorkspaceFactoryModel.prototype.getIndexByElementId = function(id) { + for (var i = 0; i < this.toolboxList.length; i++) { + if (this.toolboxList[i].id == id) { + return i; + } + } + return -1; // ID not present in toolboxList. +}; + +/** + * Given the ID of a list element, returns that ListElement object. + * + * @param {!string} id The ID of element to search for. + * @return {ListElement} Corresponding ListElement object in toolboxList, or + * null if that element does not exist. + */ +WorkspaceFactoryModel.prototype.getElementById = function(id) { + for (var i = 0; i < this.toolboxList.length; i++) { + if (this.toolboxList[i].id == id) { + return this.toolboxList[i]; + } + } + return null; // ID not present in toolboxList. +}; + +/** + * Given the index of a list element in toolboxList, returns that ListElement + * object. + * + * @param {int} index The index of the element to return. + * @return {ListElement} The corresponding ListElement object in toolboxList. + */ +WorkspaceFactoryModel.prototype.getElementByIndex = function(index) { + if (index < 0 || index >= this.toolboxList.length) { + return null; + } + return this.toolboxList[index]; +}; + +/** + * Returns the xml to load the selected element. + * + * @return {!Element} The XML of the selected element, or null if there is + * no selected element. + */ +WorkspaceFactoryModel.prototype.getSelectedXml = function() { + return this.selected ? this.selected.xml : null; +}; + +/** + * Return ordered list of ListElement objects. + * + * @return {!Array} ordered list of ListElement objects + */ +WorkspaceFactoryModel.prototype.getToolboxList = function() { + return this.toolboxList; +}; + +/** + * Gets the ID of a category given its name. + * + * @param {string} name Name of category. + * @return {int} ID of category + */ +WorkspaceFactoryModel.prototype.getCategoryIdByName = function(name) { + for (var i = 0; i < this.toolboxList.length; i++) { + if (this.toolboxList[i].name == name) { + return this.toolboxList[i].id; + } + } + return null; // Name not present in toolboxList. +}; + +/** + * Clears the toolbox list, deleting all ListElements. + */ +WorkspaceFactoryModel.prototype.clearToolboxList = function() { + this.toolboxList = []; + this.hasVariableCategory = false; + this.hasProcedureCategory = false; + this.shadowBlocks = []; + this.selected.xml = Blockly.Xml.textToDom(''); +}; + +/** + * Class for a ListElement + * Adds a shadow block to the list of shadow blocks. + * + * @param {!string} blockId The unique ID of block to be added. + */ +WorkspaceFactoryModel.prototype.addShadowBlock = function(blockId) { + this.shadowBlocks.push(blockId); +}; + +/** + * Removes a shadow block ID from the list of shadow block IDs if that ID is + * in the list. + * + * @param {!string} blockId The unique ID of block to be removed. + */ +WorkspaceFactoryModel.prototype.removeShadowBlock = function(blockId) { + for (var i = 0; i < this.shadowBlocks.length; i++) { + if (this.shadowBlocks[i] == blockId) { + this.shadowBlocks.splice(i, 1); + return; + } + } +}; + +/** + * Determines if a block is a shadow block given a unique block ID. + * + * @param {!string} blockId The unique ID of the block to examine. + * @return {boolean} True if the block is a user-generated shadow block, false + * otherwise. + */ +WorkspaceFactoryModel.prototype.isShadowBlock = function(blockId) { + for (var i = 0; i < this.shadowBlocks.length; i++) { + if (this.shadowBlocks[i] == blockId) { + return true; + } + } + return false; +}; + +/** + * Given a set of blocks currently loaded, returns all blocks in the workspace + * that are user generated shadow blocks. + * + * @param {!} blocks Array of blocks currently loaded. + * @return {!} Array of user-generated shadow blocks currently + * loaded. + */ +WorkspaceFactoryModel.prototype.getShadowBlocksInWorkspace = + function(workspaceBlocks) { + var shadowsInWorkspace = []; + for (var i = 0; i < workspaceBlocks.length; i++) { + if (this.isShadowBlock(workspaceBlocks[i].id)) { + shadowsInWorkspace.push(workspaceBlocks[i]); + } + } + return shadowsInWorkspace; +}; + +/** + * Adds a custom tag to a category, updating state variables accordingly. + * Only accepts 'VARIABLE' and 'PROCEDURE' tags. + * + * @param {!ListElement} category The category to add the tag to. + * @param {!string} tag The custom tag to add to the category. + */ +WorkspaceFactoryModel.prototype.addCustomTag = function(category, tag) { + // Only update list elements that are categories. + if (category.type != ListElement.TYPE_CATEGORY) { + return; + } + // Only update the tag to be 'VARIABLE' or 'PROCEDURE'. + if (tag == 'VARIABLE') { + this.hasVariableCategory = true; + category.custom = 'VARIABLE'; + } else if (tag == 'PROCEDURE') { + this.hasProcedureCategory = true; + category.custom = 'PROCEDURE'; + } +}; + +/** + * 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. + */ +WorkspaceFactoryModel.prototype.savePreloadXml = function(xml) { + this.preloadXml = xml +}; + +/** + * Gets the XML to be pre-loaded into the workspace. + * + * @return {!Element} The XML for the workspace. + */ +WorkspaceFactoryModel.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. + */ +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. + * TODO(evd2014): Move pushBlockTypesToList to FactoryUtils. + * + * @return {!Array} Array of block types currently being used. + */ +WorkspaceFactoryModel.prototype.getAllUsedBlockTypes = function() { + var blockTypeList = []; + + // Given XML for the workspace, adds all block types included in the XML + // to the list, not including duplicates. + var pushBlockTypesToList = function(xml, list) { + // Get all block XML nodes. + var blocks = xml.getElementsByTagName('block'); + + // Add block types if not already in list. + for (var i = 0; i < blocks.length; i++) { + var type = blocks[i].getAttribute('type'); + if (list.indexOf(type) == -1) { + list.push(type); + } + } + }; + + if (this.flyout) { + // If has a single flyout, add block types for the single flyout. + pushBlockTypesToList(this.getSelectedXml(), blockTypeList); + } else { + // If has categories, add block types for each category. + + for (var i = 0, category; category = this.toolboxList[i]; i++) { + if (category.type == ListElement.TYPE_CATEGORY) { + pushBlockTypesToList(category.xml, blockTypeList); + } + } + } + + // Add the block types from any pre-loaded blocks. + pushBlockTypesToList(this.getPreloadXml(), blockTypeList); + + return blockTypeList; +}; + +/** + * Adds new imported block types to the list of current imported block types. + * + * @param {!Array} blockTypes Array of block types imported. + */ +WorkspaceFactoryModel.prototype.addImportedBlockTypes = function(blockTypes) { + this.importedBlockTypes = this.importedBlockTypes.concat(blockTypes); +}; + +/** + * Updates block types in block library. + * + * @param {!Array} blockTypes Array of block types in block library. + */ +WorkspaceFactoryModel.prototype.updateLibBlockTypes = function(blockTypes) { + this.libBlockTypes = blockTypes; +}; + +/** + * Determines if a block type is defined as a standard block, in the block + * library, or as an imported block. + * + * @param {!string} blockType Block type to check. + * @return {boolean} True if blockType is defined, false otherwise. + */ +WorkspaceFactoryModel.prototype.isDefinedBlockType = function(blockType) { + var isStandardBlock = StandardCategories.coreBlockTypes.indexOf(blockType) + != -1; + var isLibBlock = this.libBlockTypes.indexOf(blockType) != -1; + var isImportedBlock = this.importedBlockTypes.indexOf(blockType) != -1; + return (isStandardBlock || isLibBlock || isImportedBlock); +} + +/** + * Class for a ListElement. + * @constructor + */ +ListElement = function(type, opt_name) { + this.type = type; + // XML DOM element to load the element. + this.xml = Blockly.Xml.textToDom(''); + // Name of category. Can be changed by user. Null if separator. + this.name = opt_name ? opt_name : null; + // Unique ID of element. Does not change. + this.id = Blockly.genUid(); + // Color of category. Default is no color. Null if separator. + this.color = null; + // Stores a custom tag, if necessary. Null if no custom tag or separator. + this.custom = null; +}; + +// 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 + * elements that are not categories). + * + * @param {!Blockly.workspace} workspace The workspace to save category entry + * from. + */ +ListElement.prototype.saveFromWorkspace = function(workspace) { + // Only save XML for categories and flyouts. + if (this.type == ListElement.TYPE_FLYOUT || + this.type == ListElement.TYPE_CATEGORY) { + this.xml = Blockly.Xml.workspaceToDom(workspace); + } +}; + + +/** + * Changes the name of a category object given a new name. Returns if + * not a category. + * + * @param {string} name New name of category. + */ +ListElement.prototype.changeName = function (name) { + // Only update list elements that are categories. + if (this.type != ListElement.TYPE_CATEGORY) { + return; + } + this.name = name; +}; + +/** + * Sets the color of a category. If tries to set the color of something other + * than a category, returns. + * + * @param {!string} color The color that should be used for that category. + */ +ListElement.prototype.changeColor = function (color) { + if (this.type != ListElement.TYPE_CATEGORY) { + return; + } + this.color = color; +}; + +/** + * Makes a copy of the original element and returns it. Everything about the + * copy is identical except for its ID. + * + * @return {!ListElement} The copy of the ListElement. + */ +ListElement.prototype.copy = function() { + copy = new ListElement(this.type); + // Generate a unique ID for the element. + copy.id = Blockly.genUid(); + // Copy all attributes except ID. + copy.name = this.name; + copy.xml = this.xml; + copy.color = this.color; + copy.custom = this.custom; + // Return copy. + return copy; +}; diff --git a/demos/blocklyfactory/workspacefactory/wfactory_view.js b/demos/blocklyfactory/workspacefactory/wfactory_view.js new file mode 100644 index 00000000..5f713960 --- /dev/null +++ b/demos/blocklyfactory/workspacefactory/wfactory_view.js @@ -0,0 +1,435 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Google Inc. + * https://developers.google.com/blockly/ + * + * 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. + */ + +/** + * Controls the UI elements for workspace factory, mainly the category tabs. + * Also includes downloading files because that interacts directly with the DOM. + * Depends on WorkspaceFactoryController (for adding mouse listeners). Tabs for + * each category are stored in tab map, which associates a unique ID for a + * category with a particular tab. + * + * @author Emma Dauterman (edauterman) + */ + + /** + * Class for a WorkspaceFactoryView + * @constructor + */ + +WorkspaceFactoryView = function() { + // For each tab, maps ID of a ListElement to the td DOM element. + this.tabMap = Object.create(null); +}; + +/** + * Adds a category tab to the UI, and updates tabMap accordingly. + * + * @param {!string} name The name of the category being created + * @param {!string} id ID of category being created + * @param {boolean} firstCategory true if it's the first category, false + * otherwise + * @return {!Element} DOM element created for tab + */ +WorkspaceFactoryView.prototype.addCategoryRow = + function(name, id, firstCategory) { + var table = document.getElementById('categoryTable'); + // Delete help label and enable category buttons if it's the first category. + if (firstCategory) { + document.getElementById('categoryHeader').textContent = 'Your Categories:'; + } + // Create tab. + var count = table.rows.length; + var row = table.insertRow(count); + var nextEntry = row.insertCell(0); + // Configure tab. + nextEntry.id = this.createCategoryIdName(name); + nextEntry.textContent = name; + // Store tab. + this.tabMap[id] = table.rows[count].cells[0]; + // Return tab. + return nextEntry; +}; + +/** + * Deletes a category tab from the UI and updates tabMap accordingly. + * + * @param {!string} id ID of category to be deleted. + * @param {!string} name The name of the category to be deleted. + */ +WorkspaceFactoryView.prototype.deleteElementRow = function(id, index) { + // Delete tab entry. + delete this.tabMap[id]; + // Delete tab row. + var table = document.getElementById('categoryTable'); + var count = table.rows.length; + table.deleteRow(index); + + // If last category removed, add category help text and disable category + // buttons. + this.addEmptyCategoryMessage(); +}; + +/** + * If there are no toolbox elements created, adds a help message to show + * where categories will appear. Should be called when deleting list elements + * in case the last element is deleted. + */ +WorkspaceFactoryView.prototype.addEmptyCategoryMessage = function() { + var table = document.getElementById('categoryTable'); + if (table.rows.length == 0) { + document.getElementById('categoryHeader').textContent = + 'Your categories will appear here'; + } +} + +/** + * Given the index of the currently selected element, updates the state of + * the buttons that allow the user to edit the list elements. Updates the edit + * and arrow buttons. Should be called when adding or removing elements + * or when changing to a new element or when swapping to a different element. + * + * TODO(evd2014): Switch to using CSS to add/remove styles. + * + * @param {int} selectedIndex The index of the currently selected category, + * -1 if no categories created. + * @param {ListElement} selected The selected ListElement. + */ +WorkspaceFactoryView.prototype.updateState = function(selectedIndex, selected) { + // Disable/enable editing buttons as necessary. + document.getElementById('button_editCategory').disabled = selectedIndex < 0 || + selected.type != ListElement.TYPE_CATEGORY; + document.getElementById('button_remove').disabled = selectedIndex < 0; + document.getElementById('button_up').disabled = + selectedIndex <= 0 ? true : false; + var table = document.getElementById('categoryTable'); + document.getElementById('button_down').disabled = selectedIndex >= + table.rows.length - 1 || selectedIndex < 0 ? true : false; + // Disable/enable the workspace as necessary. + this.disableWorkspace(this.shouldDisableWorkspace(selected)); +}; + +/** + * Determines the DOM id for a category given its name. + * + * @param {!string} name Name of category + * @return {!string} ID of category tab + */ +WorkspaceFactoryView.prototype.createCategoryIdName = function(name) { + return 'tab_' + name; +}; + +/** + * Switches a tab on or off. + * + * @param {!string} id ID of the tab to switch on or off. + * @param {boolean} selected True if tab should be on, false if tab should be + * off. + */ +WorkspaceFactoryView.prototype.setCategoryTabSelection = + function(id, selected) { + if (!this.tabMap[id]) { + return; // Exit if tab does not exist. + } + this.tabMap[id].className = selected ? 'tabon' : 'taboff'; +}; + +/** + * Used to bind a click to a certain DOM element (used for category tabs). + * Taken directly from code.js + * + * @param {string|!Element} e1 tab element or corresponding id string + * @param {!Function} func Function to be executed on click + */ +WorkspaceFactoryView.prototype.bindClick = function(el, func) { + if (typeof el == 'string') { + el = document.getElementById(el); + } + el.addEventListener('click', func, true); + el.addEventListener('touchend', func, true); +}; + +/** + * Creates a file and downloads it. In some browsers downloads, and in other + * browsers, opens new tab with contents. + * + * @param {!string} filename Name of file + * @param {!Blob} data Blob containing contents to download + */ +WorkspaceFactoryView.prototype.createAndDownloadFile = + function(filename, data) { + var clickEvent = new MouseEvent("click", { + "view": window, + "bubbles": true, + "cancelable": false + }); + var a = document.createElement('a'); + a.href = window.URL.createObjectURL(data); + a.download = filename; + a.textContent = 'Download file!'; + a.dispatchEvent(clickEvent); + }; + +/** + * Given the ID of a certain category, updates the corresponding tab in + * the DOM to show a new name. + * + * @param {!string} newName Name of string to be displayed on tab + * @param {!string} id ID of category to be updated + * + */ +WorkspaceFactoryView.prototype.updateCategoryName = function(newName, id) { + this.tabMap[id].textContent = newName; + this.tabMap[id].id = this.createCategoryIdName(newName); +}; + +/** + * Moves a tab from one index to another. Adjusts index inserting before + * based on if inserting before or after. Checks that the indexes are in + * bounds, throws error if not. + * + * @param {!string} id The ID of the category to move. + * @param {int} newIndex The index to move the category to. + * @param {int} oldIndex The index the category is currently at. + */ +WorkspaceFactoryView.prototype.moveTabToIndex = + function(id, newIndex, oldIndex) { + var table = document.getElementById('categoryTable'); + // Check that indexes are in bounds + if (newIndex < 0 || newIndex >= table.rows.length || oldIndex < 0 || + oldIndex >= table.rows.length) { + throw new Error('Index out of bounds when moving tab in the view.'); + } + + if (newIndex < oldIndex) { + // Inserting before. + var row = table.insertRow(newIndex); + row.appendChild(this.tabMap[id]); + table.deleteRow(oldIndex + 1); + } else { + // Inserting after. + var row = table.insertRow(newIndex + 1); + row.appendChild(this.tabMap[id]); + table.deleteRow(oldIndex); + } +}; + +/** + * Given a category ID and color, use that color to color the left border of the + * tab for that category. + * + * @param {!string} id The ID of the category to color. + * @param {!string} color The color for to be used for the border of the tab. + * Must be a valid CSS string. + */ +WorkspaceFactoryView.prototype.setBorderColor = function(id, color) { + var tab = this.tabMap[id]; + tab.style.borderLeftWidth = "8px"; + tab.style.borderLeftStyle = "solid"; + tab.style.borderColor = color; +}; + +/** + * Given a separator ID, creates a corresponding tab in the view, updates + * tab map, and returns the tab. + * + * @param {!string} id The ID of the separator. + * @param {!Element} The td DOM element representing the separator. + */ +WorkspaceFactoryView.prototype.addSeparatorTab = function(id) { + // Create separator. + var table = document.getElementById('categoryTable'); + var count = table.rows.length; + var row = table.insertRow(count); + var nextEntry = row.insertCell(0); + // Configure separator. + nextEntry.style.height = '10px'; + // Store and return separator. + this.tabMap[id] = table.rows[count].cells[0]; + return nextEntry; +}; + +/** + * Disables or enables the workspace by putting a div over or under the + * toolbox workspace, depending on the value of disable. Used when switching + * to/from separators where the user shouldn't be able to drag blocks into + * the workspace. + * + * @param {boolean} disable True if the workspace should be disabled, false + * if it should be enabled. + */ +WorkspaceFactoryView.prototype.disableWorkspace = function(disable) { + document.getElementById('disable_div').style.zIndex = disable ? 1 : -1; +}; + +/** + * Determines if the workspace should be disabled. The workspace should be + * disabled if category is a separator or has VARIABLE or PROCEDURE tags. + * + * @return {boolean} True if the workspace should be disabled, false otherwise. + */ +WorkspaceFactoryView.prototype.shouldDisableWorkspace = function(category) { + return category != null && category.type != ListElement.TYPE_FLYOUT && + (category.type == ListElement.TYPE_SEPARATOR || + category.custom == 'VARIABLE' || category.custom == 'PROCEDURE'); +}; + +/* + * Removes all categories and separators in the view. Clears the tabMap to + * reflect this. + */ +WorkspaceFactoryView.prototype.clearToolboxTabs = function() { + this.tabMap = []; + var oldCategoryTable = document.getElementById('categoryTable'); + var newCategoryTable = document.createElement('table'); + newCategoryTable.id = 'categoryTable'; + newCategoryTable.style.width = 'auto'; + oldCategoryTable.parentElement.replaceChild(newCategoryTable, + oldCategoryTable); +}; + +/** + * Given a set of blocks currently loaded user-generated shadow blocks, visually + * marks them without making them actual shadow blocks (allowing them to still + * be editable and movable). + * + * @param {!} blocks Array of user-generated shadow blocks + * currently loaded. + */ +WorkspaceFactoryView.prototype.markShadowBlocks = function(blocks) { + for (var i = 0; i < blocks.length; i++) { + this.markShadowBlock(blocks[i]); + } +}; + +/** + * Visually marks a user-generated shadow block as a shadow block in the + * workspace without making the block an actual shadow block (allowing it + * to be moved and edited). + * + * @param {!Blockly.Block} block The block that should be marked as a shadow + * block (must be rendered). + */ +WorkspaceFactoryView.prototype.markShadowBlock = function(block) { + // Add Blockly CSS for user-generated shadow blocks. + Blockly.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' + + ' other blocks to be displayed.'); + } +}; + +/** + * Removes visual marking for a shadow block given a rendered block. + * + * @param {!Blockly.Block} block The block that should be unmarked as a shadow + * block (must be rendered). + */ +WorkspaceFactoryView.prototype.unmarkShadowBlock = function(block) { + // Remove Blockly CSS for user-generated shadow blocks. + if (Blockly.hasClass_(block.svgGroup_, 'shadowBlock')) { + 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 + * (WorkspaceFactoryController.MODE_TOOLBOX or WorkspaceFactoryController.MODE_PRELOAD). + */ +WorkspaceFactoryView.prototype.setModeSelection = function(mode) { + document.getElementById('tab_preload').className = mode == + WorkspaceFactoryController.MODE_PRELOAD ? 'tabon' : 'taboff'; + document.getElementById('preload_div').style.display = mode == + WorkspaceFactoryController.MODE_PRELOAD ? 'block' : 'none'; + document.getElementById('tab_toolbox').className = mode == + WorkspaceFactoryController.MODE_TOOLBOX ? 'tabon' : 'taboff'; + document.getElementById('toolbox_div').style.display = mode == + WorkspaceFactoryController.MODE_TOOLBOX ? 'block' : 'none'; +}; + +/** + * Updates the help text above the workspace depending on the selected mode. + * + * @param {!string} mode The selected mode (WorkspaceFactoryController.MODE_TOOLBOX or + * WorkspaceFactoryController.MODE_PRELOAD). + */ +WorkspaceFactoryView.prototype.updateHelpText = function(mode) { + if (mode == WorkspaceFactoryController.MODE_TOOLBOX) { + var helpText = 'Drag blocks into the workspace to configure the toolbox ' + + 'in your custom workspace.'; + } else { + var helpText = 'Drag blocks into the workspace to pre-load them in your ' + + 'custom 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. + */ +WorkspaceFactoryView.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. + */ +WorkspaceFactoryView.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; +} diff --git a/demos/code/code.js b/demos/code/code.js index 2dfebbbf..65a2621f 100644 --- a/demos/code/code.js +++ b/demos/code/code.js @@ -42,6 +42,7 @@ Code.LANGUAGE_NAME = { 'el': 'Ελληνικά', 'en': 'English', 'es': 'Español', + 'et': 'Eesti', 'fa': 'فارسی', 'fr': 'Français', 'he': 'עברית', diff --git a/demos/code/index.html b/demos/code/index.html index 54a59acd..3878ec07 100644 --- a/demos/code/index.html +++ b/demos/code/index.html @@ -148,13 +148,6 @@ - - - - 1 - - - diff --git a/demos/code/msg/et.js b/demos/code/msg/et.js new file mode 100644 index 00000000..320c3917 --- /dev/null +++ b/demos/code/msg/et.js @@ -0,0 +1,24 @@ +var MSG = { + title: "Kood", + blocks: "Plokid", + linkTooltip: "Salvesta ja tekita link plokkidele.", + runTooltip: "Käivita töölaual olevate plokkidega defineeritud programm.", + badCode: "Viga programmis:\n%1", + timeout: "Käivitatavate iteratsioonide maksimaalne arv on ületatud.", + trashTooltip: "Eemalda kõik plokid.", + catLogic: "Loogika", + catLoops: "Kordus", + catMath: "Matemaatika", + catText: "Tekst", + catLists: "Loendid", + catColour: "Värv", + catVariables: "Muutujad", + catFunctions: "Funktsioonid", + listVariable: "loend", + textVariable: "tekst", + httpRequestError: "Probleem päringuga.", + linkAlert: "Oma plokke saad jagada selle lingiga:\n\n%1", + hashError: "Vabandust, kuid '%1' ei vasta ühelegi salvestatud programmile.", + xmlError: "Su salvestatud faili ei õnnestunud laadida. Võibolla on see loodud mõne teise Blockly versiooniga?", + badXml: "Viga XML-i parsimisel:\n%1\n\nTehtud muudatustest loobumiseks vajuta 'OK', XML-i muudatuste tegemise jätkamiseks 'Katkesta'." +}; diff --git a/demos/plane/generated/et.js b/demos/plane/generated/et.js new file mode 100644 index 00000000..069c7cb2 --- /dev/null +++ b/demos/plane/generated/et.js @@ -0,0 +1,37 @@ +// This file was automatically generated from template.soy. +// Please don't edit this file by hand. + +if (typeof planepage == 'undefined') { var planepage = {}; } + + +planepage.messages = function(opt_data, opt_ignored, opt_ijData) { + return '
    Ridu: %1rows (%1)1. klassi ridu: %11. klassi ridu (%1)2. klassi ridu: %12. klassi ridu (%1)Istmeid: %1?istmete arv =
    '; +}; + + +planepage.start = function(opt_data, opt_ignored, opt_ijData) { + var output = planepage.messages(null, null, opt_ijData) + '

    Blockly‏ > Demos‏ > Lennukiistmete kalkulaator   '; + var iLimit37 = opt_ijData.maxLevel + 1; + for (var i37 = 1; i37 < iLimit37; i37++) { + output += ' ' + ((i37 == opt_ijData.level) ? '' + soy.$$escapeHtml(i37) + '' : (i37 < opt_ijData.level) ? '' : '' + soy.$$escapeHtml(i37) + ''); + } + output += '

    diff --git a/demos/rtl/index.html b/demos/rtl/index.html index f2ae162f..fd84febd 100644 --- a/demos/rtl/index.html +++ b/demos/rtl/index.html @@ -87,13 +87,6 @@ - - - - 1 - - - diff --git a/demos/toolbox/index.html b/demos/toolbox/index.html index a4e70b87..4e81c02e 100644 --- a/demos/toolbox/index.html +++ b/demos/toolbox/index.html @@ -84,13 +84,6 @@ - - - - 1 - - - diff --git a/generators/dart.js b/generators/dart.js index feec82a2..72c828ce 100644 --- a/generators/dart.js +++ b/generators/dart.js @@ -109,7 +109,7 @@ Blockly.Dart.init = function(workspace) { } var defvars = []; - var variables = Blockly.Variables.allVariables(workspace); + var variables = workspace.variableList; if (variables.length) { for (var i = 0; i < variables.length; i++) { defvars[i] = Blockly.Dart.variableDB_.getName(variables[i], diff --git a/generators/javascript.js b/generators/javascript.js index db095253..a1043abe 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -159,7 +159,7 @@ Blockly.JavaScript.init = function(workspace) { } var defvars = []; - var variables = Blockly.Variables.allVariables(workspace); + var variables = workspace.variableList; if (variables.length) { for (var i = 0; i < variables.length; i++) { defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i], diff --git a/generators/php/procedures.js b/generators/php/procedures.js index 0dc56d7c..2131bebd 100644 --- a/generators/php/procedures.js +++ b/generators/php/procedures.js @@ -31,7 +31,7 @@ goog.require('Blockly.PHP'); Blockly.PHP['procedures_defreturn'] = function(block) { // Define a procedure with a return value. // First, add a 'global' statement for every variable that is assigned. - var globals = Blockly.Variables.allVariables(block); + var globals = block.workspace.variableList; for (var i = globals.length - 1; i >= 0; i--) { var varName = globals[i]; if (block.arguments_.indexOf(varName) == -1) { diff --git a/generators/python.js b/generators/python.js index 3534aec2..aad95825 100644 --- a/generators/python.js +++ b/generators/python.js @@ -146,7 +146,7 @@ Blockly.Python.init = function(workspace) { } var defvars = []; - var variables = Blockly.Variables.allVariables(workspace); + var variables = workspace.variableList; for (var i = 0; i < variables.length; i++) { defvars[i] = Blockly.Python.variableDB_.getName(variables[i], Blockly.Variables.NAME_TYPE) + ' = None'; diff --git a/generators/python/procedures.js b/generators/python/procedures.js index b90f38d5..fa938ef0 100644 --- a/generators/python/procedures.js +++ b/generators/python/procedures.js @@ -32,7 +32,7 @@ goog.require('Blockly.Python'); Blockly.Python['procedures_defreturn'] = function(block) { // Define a procedure with a return value. // First, add a 'global' statement for every variable that is assigned. - var globals = Blockly.Variables.allVariables(block); + var globals = block.workspace.variableList; for (var i = globals.length - 1; i >= 0; i--) { var varName = globals[i]; if (block.arguments_.indexOf(varName) == -1) { diff --git a/javascript_compressed.js b/javascript_compressed.js index aaf4a156..586ca12c 100644 --- a/javascript_compressed.js +++ b/javascript_compressed.js @@ -9,8 +9,8 @@ Blockly.JavaScript.ORDER_DIVISION=5.1;Blockly.JavaScript.ORDER_MULTIPLICATION=5. Blockly.JavaScript.ORDER_LOGICAL_AND=13;Blockly.JavaScript.ORDER_LOGICAL_OR=14;Blockly.JavaScript.ORDER_CONDITIONAL=15;Blockly.JavaScript.ORDER_ASSIGNMENT=16;Blockly.JavaScript.ORDER_COMMA=17;Blockly.JavaScript.ORDER_NONE=99; Blockly.JavaScript.ORDER_OVERRIDES=[[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_LOGICAL_NOT,Blockly.JavaScript.ORDER_LOGICAL_NOT],[Blockly.JavaScript.ORDER_MULTIPLICATION,Blockly.JavaScript.ORDER_MULTIPLICATION],[Blockly.JavaScript.ORDER_ADDITION, Blockly.JavaScript.ORDER_ADDITION],[Blockly.JavaScript.ORDER_LOGICAL_AND,Blockly.JavaScript.ORDER_LOGICAL_AND],[Blockly.JavaScript.ORDER_LOGICAL_OR,Blockly.JavaScript.ORDER_LOGICAL_OR]];Blockly.JavaScript.ONE_BASED_INDEXING=!0; -Blockly.JavaScript.init=function(a){Blockly.JavaScript.definitions_=Object.create(null);Blockly.JavaScript.functionNames_=Object.create(null);Blockly.JavaScript.variableDB_?Blockly.JavaScript.variableDB_.reset():Blockly.JavaScript.variableDB_=new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);var b=[];a=Blockly.Variables.allVariables(a);if(a.length){for(var c=0;cc?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_SUBTRACTION)||f:d?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_UNARY_NEGATION)||f:Blockly.JavaScript.valueToCode(a,b,e)||f;if(Blockly.isNumber(a))a=parseFloat(a)+ diff --git a/msg/js/ar.js b/msg/js/ar.js index a4aa4edd..3846f10e 100644 --- a/msg/js/ar.js +++ b/msg/js/ar.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "بما ان القيمة خاط Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "بما ان القيمة صحيحة, نفّذ بعض الأوامر."; Blockly.Msg.DELETE_ALL_BLOCKS = "حذف كل مناعات %1؟"; Blockly.Msg.DELETE_BLOCK = "إحذف القطعة"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "إحذف قطع %1"; Blockly.Msg.DISABLE_BLOCK = "عطّل القطعة"; Blockly.Msg.DUPLICATE_BLOCK = "ادمج"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "تعيين %1 إلى %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "انشئ 'احصل على %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "تعيين هذا المتغير لتكون مساوية للقيمة المدخلة."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/az.js b/msg/js/az.js index 08e86acc..243e7e67 100644 --- a/msg/js/az.js +++ b/msg/js/az.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Hələ ki, qiymət \"yalan\"dı Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Hələ ki, qiymət \"doğru\"dur, bəzi əmrləri yerinə yetir."; Blockly.Msg.DELETE_ALL_BLOCKS = "Bütün %1 blok silinsin?"; Blockly.Msg.DELETE_BLOCK = "Bloku sil"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 bloku sil"; Blockly.Msg.DISABLE_BLOCK = "Bloku söndür"; Blockly.Msg.DUPLICATE_BLOCK = "Dublikat"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "%1 - i bu qiymət ilə təyin et: %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "'%1 - i götür' - ü yarat"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Bu dəyişəni daxil edilmiş qiymətə bərabər edir."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ba.js b/msg/js/ba.js index b185e11b..734e00c7 100644 --- a/msg/js/ba.js +++ b/msg/js/ba.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Мәғәнә ялған бул Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Мәғәнә дөрөҫ булғанда, командаларҙы ҡабатлай."; Blockly.Msg.DELETE_ALL_BLOCKS = "Бөтә %1 блоктарҙы юйырғамы?"; Blockly.Msg.DELETE_BLOCK = "Блокты юйҙырырға"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = " %1 блокты юйҙырырға"; Blockly.Msg.DISABLE_BLOCK = "Блокты һүндерергә"; Blockly.Msg.DUPLICATE_BLOCK = "Күсереп алырға"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; // untranslated Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/bcc.js b/msg/js/bcc.js index 229e131f..d4ff7cbe 100644 --- a/msg/js/bcc.js +++ b/msg/js/bcc.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "تا زمانی که یک مق Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "تا زمانی که یک مقدار صحیح است، چند عبارت را انجام بده."; Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "حذف بلوک"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "حذف بلوک‌های %1"; Blockly.Msg.DISABLE_BLOCK = "غیرفعال‌سازی بلوک"; Blockly.Msg.DUPLICATE_BLOCK = "تکراری"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "مجموعه %1 به %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "درست‌کردن «تنظیم %1»"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "متغیر برابر با خروجی را مشخص می‌کند."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/be-tarask.js b/msg/js/be-tarask.js index e2e51def..3fa73d5a 100644 --- a/msg/js/be-tarask.js +++ b/msg/js/be-tarask.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Пакуль значэньне Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Пакуль значэньне ісьціна, выконваць пэўныя апэрацыі."; Blockly.Msg.DELETE_ALL_BLOCKS = "Выдаліць усе блёкі %1?"; Blockly.Msg.DELETE_BLOCK = "Выдаліць блёк"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Выдаліць %1 блёкі"; Blockly.Msg.DISABLE_BLOCK = "Адключыць блёк"; Blockly.Msg.DUPLICATE_BLOCK = "Капіяваць"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "усталяваць %1 да %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Стварыць блёк «атрымаць %1»"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Надаць гэтай зьменнай значэньне ўстаўкі."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/bg.js b/msg/js/bg.js index 01fe4710..b31e9898 100644 --- a/msg/js/bg.js +++ b/msg/js/bg.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Докато стойностт Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Докато стойността е истина, изпълнявай командите."; Blockly.Msg.DELETE_ALL_BLOCKS = "Изтриване на всички 1% блокове?"; Blockly.Msg.DELETE_BLOCK = "Изтрий блок"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Изтрий %1 блока"; Blockly.Msg.DISABLE_BLOCK = "Деактивирай блок"; Blockly.Msg.DUPLICATE_BLOCK = "Копирай"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "нека %1 бъде %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Създай \"вземи стойността на %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Задава тази променлива да бъде равен на входа."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/bn.js b/msg/js/bn.js index cec003e8..739407dd 100644 --- a/msg/js/bn.js +++ b/msg/js/bn.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "While a value is false, then do Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "While a value is true, then do some statements."; // untranslated Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "ব্লকটি মুছে ফেল"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 ব্লক অপসারণ কর"; Blockly.Msg.DISABLE_BLOCK = "ব্লকটি বিকল কর"; Blockly.Msg.DUPLICATE_BLOCK = "প্রতিলিপি"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; // untranslated Blockly.Msg.VARIABLES_SET_CREATE_GET = "'%1 নিন' তৈরি করুন"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/br.js b/msg/js/br.js index 9b0d9b8d..6070696f 100644 --- a/msg/js/br.js +++ b/msg/js/br.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Keit ha ma vez faos un dalvoude Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Keit ha ma vez gwir un dalvoudenn, seveniñ urzhioù zo neuze."; Blockly.Msg.DELETE_ALL_BLOCKS = "Diverkañ an holl vloc'hoù %1 ?"; Blockly.Msg.DELETE_BLOCK = "Dilemel ar bloc'h"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Dilemel %1 bloc'h"; Blockly.Msg.DISABLE_BLOCK = "Diweredekaat ar bloc'h"; Blockly.Msg.DUPLICATE_BLOCK = "Eiladuriñ"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "termenañ %1 da %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Krouiñ 'kaout %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Termenañ a ra argemm-mañ evit ma vo par da dalvoudenn ar moned."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ca.js b/msg/js/ca.js index bb6fab35..7bd6ef6c 100644 --- a/msg/js/ca.js +++ b/msg/js/ca.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Mentre un valor sigui fals, lla Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Mentre un valor sigui cert, llavors executar unes sentències."; Blockly.Msg.DELETE_ALL_BLOCKS = "Esborrar els %1 blocs?"; Blockly.Msg.DELETE_BLOCK = "Esborra bloc"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Esborra %1 blocs"; Blockly.Msg.DISABLE_BLOCK = "Desactiva bloc"; Blockly.Msg.DUPLICATE_BLOCK = "Duplica"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "modifica %1 a %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Crear 'recupera %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Modifica aquesta variable al valor introduït."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/cs.js b/msg/js/cs.js index fe471427..1ca8da11 100644 --- a/msg/js/cs.js +++ b/msg/js/cs.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Dokud je hodnota nepravdivá, p Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Dokud je hodnota pravdivá, prováděj určité příkazy."; Blockly.Msg.DELETE_ALL_BLOCKS = "Smazat všech %1 bloků?"; Blockly.Msg.DELETE_BLOCK = "Smazat blok"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Smazat %1 bloků"; Blockly.Msg.DISABLE_BLOCK = "Deaktivovat blok"; Blockly.Msg.DUPLICATE_BLOCK = "Duplikovat"; @@ -90,11 +92,11 @@ Blockly.Msg.LISTS_GET_INDEX_RANDOM = "náhodné"; Blockly.Msg.LISTS_GET_INDEX_REMOVE = "odstranit"; Blockly.Msg.LISTS_GET_INDEX_TAIL = ""; // untranslated Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST = "Vrátí první položku v seznamu."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "Vrátí položku z určené pozice v seznamu."; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "Získá položku z určené pozice v seznamu."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST = "Vrátí poslední položku v seznamu."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM = "Vrátí náhodnou položku ze seznamu."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST = "Odstraní a vrátí první položku v seznamu."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM = "Odstraní a vrátí položku z určené pozice v seznamu."; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM = "Odstraní a získá položku z určené pozice v seznamu."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST = "Odstraní a vrátí poslední položku v seznamu."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM = "Odstraní a vrátí náhodnou položku v seznamu."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST = "Odstraní první položku v seznamu."; @@ -331,7 +333,7 @@ Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT = "v textu"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST = "najít první výskyt textu"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST = "najít poslední výskyt textu"; Blockly.Msg.TEXT_INDEXOF_TAIL = ""; // untranslated -Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "Vrátí index prvního/posledního výskytu prvního textu v druhém textu. Pokud text není nalezen, vrátí hodnotu %1."; +Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "Vrátí index prvního/posledního výskytu prvního textu v druhém textu. Pokud text není nalezen, vypíše %1."; Blockly.Msg.TEXT_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; // untranslated Blockly.Msg.TEXT_ISEMPTY_TITLE = "%1 je prázdný"; Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "Vrátí pravda pokud je zadaný text prázdný."; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "nastavit %1 na %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Vytvořit \"získat %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Nastaví tuto proměnnou, aby se rovnala vstupu."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/da.js b/msg/js/da.js index 026ddeb3..21e3e3e3 100644 --- a/msg/js/da.js +++ b/msg/js/da.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Udfør nogle kommandoer, sålæ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Udfør nogle kommandoer, sålænge en værdi er sand."; Blockly.Msg.DELETE_ALL_BLOCKS = "Slet alle %1 blokke?"; Blockly.Msg.DELETE_BLOCK = "Slet blok"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Slet %1 blokke"; Blockly.Msg.DISABLE_BLOCK = "Deaktivér blok"; Blockly.Msg.DUPLICATE_BLOCK = "Duplikér"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "sæt %1 til %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Opret 'hent %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sætter denne variabel til at være lig med input."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/de.js b/msg/js/de.js index 28a50d88..5d2249d6 100644 --- a/msg/js/de.js +++ b/msg/js/de.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Führt Anweisungen aus solange Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Führt Anweisungen aus solange die Bedingung wahr ist."; Blockly.Msg.DELETE_ALL_BLOCKS = "Alle %1 Bausteine löschen?"; Blockly.Msg.DELETE_BLOCK = "Baustein löschen"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Baustein %1 löschen"; Blockly.Msg.DISABLE_BLOCK = "Baustein deaktivieren"; Blockly.Msg.DUPLICATE_BLOCK = "Kopieren"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "setze %1 auf %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Erzeuge \"Lese %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://de.wikipedia.org/wiki/Variable_%28Programmierung%29"; Blockly.Msg.VARIABLES_SET_TOOLTIP = "Setzt den Wert einer Variable."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/diq.js b/msg/js/diq.js new file mode 100644 index 00000000..71e1aaf4 --- /dev/null +++ b/msg/js/diq.js @@ -0,0 +1,391 @@ +// This file was automatically generated. Do not modify. + +'use strict'; + +goog.provide('Blockly.Msg.diq'); + +goog.require('Blockly.Msg'); + +Blockly.Msg.ADD_COMMENT = "Tefsir cı ke"; +Blockly.Msg.CHANGE_VALUE_TITLE = "Erci bıvırne:"; +Blockly.Msg.CLEAN_UP = "Çengan pak ke"; +Blockly.Msg.COLLAPSE_ALL = "Çengi teng ke"; +Blockly.Msg.COLLAPSE_BLOCK = "Çengi teng ke"; +Blockly.Msg.COLOUR_BLEND_COLOUR1 = "reng 1"; +Blockly.Msg.COLOUR_BLEND_COLOUR2 = "reng 2"; +Blockly.Msg.COLOUR_BLEND_HELPURL = "http://meyerweb.com/eric/tools/color-blend/"; // untranslated +Blockly.Msg.COLOUR_BLEND_RATIO = "nisbet"; +Blockly.Msg.COLOUR_BLEND_TITLE = "tewde"; +Blockly.Msg.COLOUR_BLEND_TOOLTIP = "Blends two colours together with a given ratio (0.0 - 1.0)."; // untranslated +Blockly.Msg.COLOUR_PICKER_HELPURL = "https://diq.wikipedia.org/wiki/Reng"; +Blockly.Msg.COLOUR_PICKER_TOOLTIP = "Choose a colour from the palette."; // untranslated +Blockly.Msg.COLOUR_RANDOM_HELPURL = "http://randomcolour.com"; // untranslated +Blockly.Msg.COLOUR_RANDOM_TITLE = "rengo rastameye"; +Blockly.Msg.COLOUR_RANDOM_TOOLTIP = "Choose a colour at random."; // untranslated +Blockly.Msg.COLOUR_RGB_BLUE = "mawi"; +Blockly.Msg.COLOUR_RGB_GREEN = "kıho"; +Blockly.Msg.COLOUR_RGB_HELPURL = "http://www.december.com/html/spec/colorper.html"; // untranslated +Blockly.Msg.COLOUR_RGB_RED = "sur"; +Blockly.Msg.COLOUR_RGB_TITLE = "komponentên rengan"; +Blockly.Msg.COLOUR_RGB_TOOLTIP = "Create a colour with the specified amount of red, green, and blue. All values must be between 0 and 100."; // untranslated +Blockly.Msg.CONTROLS_FLOW_STATEMENTS_HELPURL = "https://github.com/google/blockly/wiki/Loops#loop-termination-blocks"; // untranslated +Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK = "break out of loop"; // untranslated +Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE = "continue with next iteration of loop"; // untranslated +Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK = "Break out of the containing loop."; // untranslated +Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE = "Skip the rest of this loop, and continue with the next iteration."; // untranslated +Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING = "Warning: This block may only be used within a loop."; // untranslated +Blockly.Msg.CONTROLS_FOREACH_HELPURL = "https://github.com/google/blockly/wiki/Loops#for-each"; // untranslated +Blockly.Msg.CONTROLS_FOREACH_TITLE = "for each item %1 in list %2"; // untranslated +Blockly.Msg.CONTROLS_FOREACH_TOOLTIP = "For each item in a list, set the variable '%1' to the item, and then do some statements."; // untranslated +Blockly.Msg.CONTROLS_FOR_HELPURL = "https://github.com/google/blockly/wiki/Loops#count-with"; // untranslated +Blockly.Msg.CONTROLS_FOR_TITLE = "count with %1 from %2 to %3 by %4"; // untranslated +Blockly.Msg.CONTROLS_FOR_TOOLTIP = "Have the variable '%1' take on the values from the start number to the end number, counting by the specified interval, and do the specified blocks."; // untranslated +Blockly.Msg.CONTROLS_IF_ELSEIF_TOOLTIP = "Add a condition to the if block."; // untranslated +Blockly.Msg.CONTROLS_IF_ELSE_TOOLTIP = "Add a final, catch-all condition to the if block."; // untranslated +Blockly.Msg.CONTROLS_IF_HELPURL = "https://github.com/google/blockly/wiki/IfElse"; // untranslated +Blockly.Msg.CONTROLS_IF_IF_TOOLTIP = "Add, remove, or reorder sections to reconfigure this if block."; // untranslated +Blockly.Msg.CONTROLS_IF_MSG_ELSE = "çıniyose"; +Blockly.Msg.CONTROLS_IF_MSG_ELSEIF = "niyose"; +Blockly.Msg.CONTROLS_IF_MSG_IF = "se"; +Blockly.Msg.CONTROLS_IF_TOOLTIP_1 = "If a value is true, then do some statements."; // untranslated +Blockly.Msg.CONTROLS_IF_TOOLTIP_2 = "If a value is true, then do the first block of statements. Otherwise, do the second block of statements."; // untranslated +Blockly.Msg.CONTROLS_IF_TOOLTIP_3 = "If the first value is true, then do the first block of statements. Otherwise, if the second value is true, do the second block of statements."; // untranslated +Blockly.Msg.CONTROLS_IF_TOOLTIP_4 = "If the first value is true, then do the first block of statements. Otherwise, if the second value is true, do the second block of statements. If none of the values are true, do the last block of statements."; // untranslated +Blockly.Msg.CONTROLS_REPEAT_HELPURL = "https://en.wikipedia.org/wiki/For_loop"; +Blockly.Msg.CONTROLS_REPEAT_INPUT_DO = "bık"; +Blockly.Msg.CONTROLS_REPEAT_TITLE = "repeat %1 times"; // untranslated +Blockly.Msg.CONTROLS_REPEAT_TOOLTIP = "Do some statements several times."; // untranslated +Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL = "https://github.com/google/blockly/wiki/Loops#repeat"; // untranslated +Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL = "hend tekrar ke"; +Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE = "Tekrar kerdış de"; +Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Yew erc xırabo se tay beyanati bıd"; +Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Yew erc raşto se yu beyanat bıd."; +Blockly.Msg.DELETE_ALL_BLOCKS = "Wa %1 çengey heme besteri yè?"; +Blockly.Msg.DELETE_BLOCK = "Bloki bestere"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated +Blockly.Msg.DELETE_X_BLOCKS = "%1 çengan bestern"; +Blockly.Msg.DISABLE_BLOCK = "Çengi devre ra vec"; +Blockly.Msg.DUPLICATE_BLOCK = "Zewnc"; +Blockly.Msg.ENABLE_BLOCK = "Çengi aktiv ke"; +Blockly.Msg.EXPAND_ALL = "Çengan hera ke"; +Blockly.Msg.EXPAND_BLOCK = "Çengi hera ke"; +Blockly.Msg.EXTERNAL_INPUTS = "Cıkewtışê xarıciy"; +Blockly.Msg.HELP = "Peşti"; +Blockly.Msg.INLINE_INPUTS = "Cıkerdışê xomiyani"; +Blockly.Msg.LISTS_CREATE_EMPTY_HELPURL = "https://github.com/google/blockly/wiki/Lists#create-empty-list"; // untranslated +Blockly.Msg.LISTS_CREATE_EMPTY_TITLE = "lista venge vıraze"; +Blockly.Msg.LISTS_CREATE_EMPTY_TOOLTIP = "Returns a list, of length 0, containing no data records"; // untranslated +Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TITLE_ADD = "liste"; +Blockly.Msg.LISTS_CREATE_WITH_CONTAINER_TOOLTIP = "Add, remove, or reorder sections to reconfigure this list block."; // untranslated +Blockly.Msg.LISTS_CREATE_WITH_HELPURL = "https://github.com/google/blockly/wiki/Lists#create-list-with"; // untranslated +Blockly.Msg.LISTS_CREATE_WITH_INPUT_WITH = "create list with"; // untranslated +Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP = "Add an item to the list."; // untranslated +Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP = "Create a list with any number of items."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_FIRST = "verên"; +Blockly.Msg.LISTS_GET_INDEX_FROM_END = "# peynira"; +Blockly.Msg.LISTS_GET_INDEX_FROM_START = "#"; // untranslated +Blockly.Msg.LISTS_GET_INDEX_GET = "bıgê"; +Blockly.Msg.LISTS_GET_INDEX_GET_REMOVE = "Bıgi u wedarne"; +Blockly.Msg.LISTS_GET_INDEX_LAST = "peyên"; +Blockly.Msg.LISTS_GET_INDEX_RANDOM = "raştameye"; +Blockly.Msg.LISTS_GET_INDEX_REMOVE = "wedare"; +Blockly.Msg.LISTS_GET_INDEX_TAIL = ""; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST = "Returns the first item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "Returns the item at the specified position in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST = "Returns the last item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM = "Returns a random item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST = "Removes and returns the first item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM = "Removes and returns the item at the specified position in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST = "Removes and returns the last item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM = "Removes and returns a random item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST = "Removes the first item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM = "Removes the item at the specified position in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST = "Removes the last item in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM = "Removes a random item in a list."; // untranslated +Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END = "Peyni # ra hetana"; +Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_START = "#'ya"; +Blockly.Msg.LISTS_GET_SUBLIST_END_LAST = "Hetana pey"; +Blockly.Msg.LISTS_GET_SUBLIST_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-a-sublist"; // untranslated +Blockly.Msg.LISTS_GET_SUBLIST_START_FIRST = "get sub-list from first"; // untranslated +Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END = "get sub-list from # from end"; // untranslated +Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START = "get sub-list from #"; // untranslated +Blockly.Msg.LISTS_GET_SUBLIST_TAIL = ""; // untranslated +Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP = "Creates a copy of the specified portion of a list."; // untranslated +Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1 objeyo peyên o"; +Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 objeyo sıfteyên o"; +Blockly.Msg.LISTS_INDEX_OF_FIRST = "find first occurrence of item"; // untranslated +Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated +Blockly.Msg.LISTS_INDEX_OF_LAST = "find last occurrence of item"; // untranslated +Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "Returns the index of the first/last occurrence of the item in the list. Returns %1 if item is not found."; // untranslated +Blockly.Msg.LISTS_INLIST = "lista de"; +Blockly.Msg.LISTS_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Lists#is-empty"; // untranslated +Blockly.Msg.LISTS_ISEMPTY_TITLE = "%1 vengo"; +Blockly.Msg.LISTS_ISEMPTY_TOOLTIP = "Returns true if the list is empty."; // untranslated +Blockly.Msg.LISTS_LENGTH_HELPURL = "https://github.com/google/blockly/wiki/Lists#length-of"; // untranslated +Blockly.Msg.LISTS_LENGTH_TITLE = "length of %1"; // untranslated +Blockly.Msg.LISTS_LENGTH_TOOLTIP = "Returns the length of a list."; // untranslated +Blockly.Msg.LISTS_REPEAT_HELPURL = "https://github.com/google/blockly/wiki/Lists#create-list-with"; // untranslated +Blockly.Msg.LISTS_REPEAT_TITLE = "create list with item %1 repeated %2 times"; // untranslated +Blockly.Msg.LISTS_REPEAT_TOOLTIP = "Creates a list consisting of the given value repeated the specified number of times."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_HELPURL = "https://github.com/google/blockly/wiki/Lists#in-list--set"; // untranslated +Blockly.Msg.LISTS_SET_INDEX_INPUT_TO = "zey"; +Blockly.Msg.LISTS_SET_INDEX_INSERT = "insert at"; // untranslated +Blockly.Msg.LISTS_SET_INDEX_SET = "ca ke"; +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST = "Inserts the item at the start of a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "Inserts the item at the specified position in a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST = "Append the item to the end of a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM = "Inserts the item randomly in a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST = "Sets the first item in a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM = "Sets the item at the specified position in a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST = "Sets the last item in a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM = "Sets a random item in a list."; // untranslated +Blockly.Msg.LISTS_SORT_HELPURL = "https://github.com/google/blockly/wiki/Lists#sorting-a-list"; +Blockly.Msg.LISTS_SORT_ORDER_ASCENDING = "zeydıyen"; +Blockly.Msg.LISTS_SORT_ORDER_DESCENDING = "Kemeyen"; +Blockly.Msg.LISTS_SORT_TITLE = "Kılm %1 %2 %3"; +Blockly.Msg.LISTS_SORT_TOOLTIP = "Sort a copy of a list."; // untranslated +Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE = "alphabetic, ignore case"; // untranslated +Blockly.Msg.LISTS_SORT_TYPE_NUMERIC = "Amoriyal"; +Blockly.Msg.LISTS_SORT_TYPE_TEXT = "Alfabetik"; +Blockly.Msg.LISTS_SPLIT_HELPURL = "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists"; // untranslated +Blockly.Msg.LISTS_SPLIT_LIST_FROM_TEXT = "make list from text"; // untranslated +Blockly.Msg.LISTS_SPLIT_TEXT_FROM_LIST = "make text from list"; // untranslated +Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN = "Join a list of texts into one text, separated by a delimiter."; // untranslated +Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT = "Split text into a list of texts, breaking at each delimiter."; // untranslated +Blockly.Msg.LISTS_SPLIT_WITH_DELIMITER = "with delimiter"; // untranslated +Blockly.Msg.LOGIC_BOOLEAN_FALSE = "ğelet"; +Blockly.Msg.LOGIC_BOOLEAN_HELPURL = "https://github.com/google/blockly/wiki/Logic#values"; // untranslated +Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP = "Returns either true or false."; // untranslated +Blockly.Msg.LOGIC_BOOLEAN_TRUE = "raşt"; +Blockly.Msg.LOGIC_COMPARE_HELPURL = "https://en.wikipedia.org/wiki/Inequality_(mathematics)"; // untranslated +Blockly.Msg.LOGIC_COMPARE_TOOLTIP_EQ = "Return true if both inputs equal each other."; // untranslated +Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GT = "Return true if the first input is greater than the second input."; // untranslated +Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GTE = "Return true if the first input is greater than or equal to the second input."; // untranslated +Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LT = "Return true if the first input is smaller than the second input."; // untranslated +Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LTE = "Return true if the first input is smaller than or equal to the second input."; // untranslated +Blockly.Msg.LOGIC_COMPARE_TOOLTIP_NEQ = "Return true if both inputs are not equal to each other."; // untranslated +Blockly.Msg.LOGIC_NEGATE_HELPURL = "https://github.com/google/blockly/wiki/Logic#not"; // untranslated +Blockly.Msg.LOGIC_NEGATE_TITLE = "%1 niyo"; +Blockly.Msg.LOGIC_NEGATE_TOOLTIP = "Returns true if the input is false. Returns false if the input is true."; // untranslated +Blockly.Msg.LOGIC_NULL = "veng"; +Blockly.Msg.LOGIC_NULL_HELPURL = "https://en.wikipedia.org/wiki/Nullable_type"; // untranslated +Blockly.Msg.LOGIC_NULL_TOOLTIP = "Veng çarneno ra."; +Blockly.Msg.LOGIC_OPERATION_AND = "û"; +Blockly.Msg.LOGIC_OPERATION_HELPURL = "https://github.com/google/blockly/wiki/Logic#logical-operations"; // untranslated +Blockly.Msg.LOGIC_OPERATION_OR = "ya zi"; +Blockly.Msg.LOGIC_OPERATION_TOOLTIP_AND = "Return true if both inputs are true."; // untranslated +Blockly.Msg.LOGIC_OPERATION_TOOLTIP_OR = "Return true if at least one of the inputs is true."; // untranslated +Blockly.Msg.LOGIC_TERNARY_CONDITION = "test"; +Blockly.Msg.LOGIC_TERNARY_HELPURL = "https://en.wikipedia.org/wiki/%3F:"; // untranslated +Blockly.Msg.LOGIC_TERNARY_IF_FALSE = "eke ğeleto"; +Blockly.Msg.LOGIC_TERNARY_IF_TRUE = "eke raşto"; +Blockly.Msg.LOGIC_TERNARY_TOOLTIP = "Check the condition in 'test'. If the condition is true, returns the 'if true' value; otherwise returns the 'if false' value."; // untranslated +Blockly.Msg.MATH_ADDITION_SYMBOL = "+"; // untranslated +Blockly.Msg.MATH_ARITHMETIC_HELPURL = "https://en.wikipedia.org/wiki/Aritmetik"; +Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD = "Return the sum of the two numbers."; // untranslated +Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE = "Return the quotient of the two numbers."; // untranslated +Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS = "Return the difference of the two numbers."; // untranslated +Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY = "Return the product of the two numbers."; // untranslated +Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER = "Return the first number raised to the power of the second number."; // untranslated +Blockly.Msg.MATH_CHANGE_HELPURL = "https://en.wikipedia.org/wiki/Programming_idiom#Incrementing_a_counter"; // untranslated +Blockly.Msg.MATH_CHANGE_TITLE = "%2, keno %1 vurneno"; +Blockly.Msg.MATH_CHANGE_TOOLTIP = "Add a number to variable '%1'."; // untranslated +Blockly.Msg.MATH_CONSTANT_HELPURL = "https://en.wikipedia.org/wiki/Mathematical_constant"; // untranslated +Blockly.Msg.MATH_CONSTANT_TOOLTIP = "Return one of the common constants: π (3.141…), e (2.718…), φ (1.618…), sqrt(2) (1.414…), sqrt(½) (0.707…), or ∞ (infinity)."; // untranslated +Blockly.Msg.MATH_CONSTRAIN_HELPURL = "https://en.wikipedia.org/wiki/Clamping_%28graphics%29"; // untranslated +Blockly.Msg.MATH_CONSTRAIN_TITLE = "constrain %1 low %2 high %3"; // untranslated +Blockly.Msg.MATH_CONSTRAIN_TOOLTIP = "Constrain a number to be between the specified limits (inclusive)."; // untranslated +Blockly.Msg.MATH_DIVISION_SYMBOL = "÷"; // untranslated +Blockly.Msg.MATH_IS_DIVISIBLE_BY = "is divisible by"; // untranslated +Blockly.Msg.MATH_IS_EVEN = "zewnco"; +Blockly.Msg.MATH_IS_NEGATIVE = "negatifo"; +Blockly.Msg.MATH_IS_ODD = "kıto"; +Blockly.Msg.MATH_IS_POSITIVE = "pozitifo"; +Blockly.Msg.MATH_IS_PRIME = "bıngehên"; +Blockly.Msg.MATH_IS_TOOLTIP = "Check if a number is an even, odd, prime, whole, positive, negative, or if it is divisible by certain number. Returns true or false."; // untranslated +Blockly.Msg.MATH_IS_WHOLE = "tamo"; +Blockly.Msg.MATH_MODULO_HELPURL = "https://en.wikipedia.org/wiki/Modulo_operation"; // untranslated +Blockly.Msg.MATH_MODULO_TITLE = "remainder of %1 ÷ %2"; // untranslated +Blockly.Msg.MATH_MODULO_TOOLTIP = "Return the remainder from dividing the two numbers."; // untranslated +Blockly.Msg.MATH_MULTIPLICATION_SYMBOL = "×"; // untranslated +Blockly.Msg.MATH_NUMBER_HELPURL = "https://diq.wikipedia.org/wiki/Numre"; +Blockly.Msg.MATH_NUMBER_TOOLTIP = "Yew numre."; +Blockly.Msg.MATH_ONLIST_HELPURL = ""; // untranslated +Blockly.Msg.MATH_ONLIST_OPERATOR_AVERAGE = "Averacê lista"; +Blockly.Msg.MATH_ONLIST_OPERATOR_MAX = "Tewr gırdê lista"; +Blockly.Msg.MATH_ONLIST_OPERATOR_MEDIAN = "Wertey lista"; +Blockly.Msg.MATH_ONLIST_OPERATOR_MIN = "Tewr qıcê lista"; +Blockly.Msg.MATH_ONLIST_OPERATOR_MODE = "listey modi"; +Blockly.Msg.MATH_ONLIST_OPERATOR_RANDOM = "random item of list"; // untranslated +Blockly.Msg.MATH_ONLIST_OPERATOR_STD_DEV = "standard deviation of list"; // untranslated +Blockly.Msg.MATH_ONLIST_OPERATOR_SUM = "koma liste"; +Blockly.Msg.MATH_ONLIST_TOOLTIP_AVERAGE = "Return the average (arithmetic mean) of the numeric values in the list."; // untranslated +Blockly.Msg.MATH_ONLIST_TOOLTIP_MAX = "Return the largest number in the list."; // untranslated +Blockly.Msg.MATH_ONLIST_TOOLTIP_MEDIAN = "Return the median number in the list."; // untranslated +Blockly.Msg.MATH_ONLIST_TOOLTIP_MIN = "Return the smallest number in the list."; // untranslated +Blockly.Msg.MATH_ONLIST_TOOLTIP_MODE = "Return a list of the most common item(s) in the list."; // untranslated +Blockly.Msg.MATH_ONLIST_TOOLTIP_RANDOM = "Return a random element from the list."; // untranslated +Blockly.Msg.MATH_ONLIST_TOOLTIP_STD_DEV = "Return the standard deviation of the list."; // untranslated +Blockly.Msg.MATH_ONLIST_TOOLTIP_SUM = "Return the sum of all the numbers in the list."; // untranslated +Blockly.Msg.MATH_POWER_SYMBOL = "^"; // untranslated +Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL = "https://en.wikipedia.org/wiki/Random_number_generation"; // untranslated +Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM = "Raştamaye nimande amor"; +Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP = "Return a random fraction between 0.0 (inclusive) and 1.0 (exclusive)."; // untranslated +Blockly.Msg.MATH_RANDOM_INT_HELPURL = "https://en.wikipedia.org/wiki/Random_number_generation"; // untranslated +Blockly.Msg.MATH_RANDOM_INT_TITLE = "random integer from %1 to %2"; // untranslated +Blockly.Msg.MATH_RANDOM_INT_TOOLTIP = "Return a random integer between the two specified limits, inclusive."; // untranslated +Blockly.Msg.MATH_ROUND_HELPURL = "https://en.wikipedia.org/wiki/Rounding"; +Blockly.Msg.MATH_ROUND_OPERATOR_ROUND = "gılor ke"; +Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDDOWN = "Loğê cêri ke"; +Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDUP = "Loğê cori ke"; +Blockly.Msg.MATH_ROUND_TOOLTIP = "Yu amorer loğê cêri yana cori ke"; +Blockly.Msg.MATH_SINGLE_HELPURL = "https://en.wikipedia.org/wiki/Square_root"; // untranslated +Blockly.Msg.MATH_SINGLE_OP_ABSOLUTE = "mutlaq"; +Blockly.Msg.MATH_SINGLE_OP_ROOT = "karekok"; +Blockly.Msg.MATH_SINGLE_TOOLTIP_ABS = "Return the absolute value of a number."; // untranslated +Blockly.Msg.MATH_SINGLE_TOOLTIP_EXP = "Return e to the power of a number."; // untranslated +Blockly.Msg.MATH_SINGLE_TOOLTIP_LN = "Return the natural logarithm of a number."; // untranslated +Blockly.Msg.MATH_SINGLE_TOOLTIP_LOG10 = "Return the base 10 logarithm of a number."; // untranslated +Blockly.Msg.MATH_SINGLE_TOOLTIP_NEG = "Return the negation of a number."; // untranslated +Blockly.Msg.MATH_SINGLE_TOOLTIP_POW10 = "Return 10 to the power of a number."; // untranslated +Blockly.Msg.MATH_SINGLE_TOOLTIP_ROOT = "Return the square root of a number."; // untranslated +Blockly.Msg.MATH_SUBTRACTION_SYMBOL = "-"; // untranslated +Blockly.Msg.MATH_TRIG_ACOS = "acos"; // untranslated +Blockly.Msg.MATH_TRIG_ASIN = "asin"; // untranslated +Blockly.Msg.MATH_TRIG_ATAN = "atan"; // untranslated +Blockly.Msg.MATH_TRIG_COS = "cos"; // untranslated +Blockly.Msg.MATH_TRIG_HELPURL = "https://en.wikipedia.org/wiki/Trigonometric_functions"; // untranslated +Blockly.Msg.MATH_TRIG_SIN = "sin"; // untranslated +Blockly.Msg.MATH_TRIG_TAN = "tan"; // untranslated +Blockly.Msg.MATH_TRIG_TOOLTIP_ACOS = "Return the arccosine of a number."; // untranslated +Blockly.Msg.MATH_TRIG_TOOLTIP_ASIN = "Return the arcsine of a number."; // untranslated +Blockly.Msg.MATH_TRIG_TOOLTIP_ATAN = "Return the arctangent of a number."; // untranslated +Blockly.Msg.MATH_TRIG_TOOLTIP_COS = "Return the cosine of a degree (not radian)."; // untranslated +Blockly.Msg.MATH_TRIG_TOOLTIP_SIN = "Return the sine of a degree (not radian)."; // untranslated +Blockly.Msg.MATH_TRIG_TOOLTIP_TAN = "Return the tangent of a degree (not radian)."; // untranslated +Blockly.Msg.NEW_VARIABLE = "Vuriyayeyo newe..."; +Blockly.Msg.NEW_VARIABLE_TITLE = "Namey vuriyayeyê newi:"; +Blockly.Msg.ORDINAL_NUMBER_SUFFIX = ""; // untranslated +Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS = "Çıyan rê mısafe bıd"; +Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "ebe:"; +Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; // untranslated +Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP = "Run the user-defined function '%1'."; // untranslated +Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; // untranslated +Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP = "Run the user-defined function '%1' and use its output."; // untranslated +Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS = "ebe:"; +Blockly.Msg.PROCEDURES_CREATE_DO = "'%1' vıraze"; +Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = "Nê fonksiyoni beyan ke..."; +Blockly.Msg.PROCEDURES_DEFNORETURN_DO = ""; // untranslated +Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; // untranslated +Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE = "Çıyê bık"; +Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE = "rê"; +Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP = "Yew fonksiyono çap nêdate vırazeno"; +Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; // untranslated +Blockly.Msg.PROCEDURES_DEFRETURN_RETURN = "peyser biya"; +Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP = "Yew fonksiyono çap daye vırazeno"; +Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING = "Warning: This function has duplicate parameters."; // untranslated +Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF = "Highlight function definition"; // untranslated +Blockly.Msg.PROCEDURES_IFRETURN_HELPURL = "http://c2.com/cgi/wiki?GuardClause"; // untranslated +Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP = "If a value is true, then return a second value."; // untranslated +Blockly.Msg.PROCEDURES_IFRETURN_WARNING = "Warning: This block may be used only within a function definition."; // untranslated +Blockly.Msg.PROCEDURES_MUTATORARG_TITLE = "nameyê cıkewtışi:"; +Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP = "Add an input to the function."; // untranslated +Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE = "cıkewtışi"; +Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP = "Add, remove, or reorder inputs to this function."; // untranslated +Blockly.Msg.REDO = "Anewe ke"; +Blockly.Msg.REMOVE_COMMENT = "Tefsiri Wedare"; +Blockly.Msg.RENAME_VARIABLE = "Vuriyayey fına name ke..."; +Blockly.Msg.RENAME_VARIABLE_TITLE = "Rename all '%1' variables to:"; // untranslated +Blockly.Msg.TEXT_APPEND_APPENDTEXT = "append text"; // untranslated +Blockly.Msg.TEXT_APPEND_HELPURL = "https://github.com/google/blockly/wiki/Text#text-modification"; // untranslated +Blockly.Msg.TEXT_APPEND_TO = "rê"; +Blockly.Msg.TEXT_APPEND_TOOLTIP = "Append some text to variable '%1'."; // untranslated +Blockly.Msg.TEXT_CHANGECASE_HELPURL = "https://github.com/google/blockly/wiki/Text#adjusting-text-case"; // untranslated +Blockly.Msg.TEXT_CHANGECASE_OPERATOR_LOWERCASE = "to lower case"; // untranslated +Blockly.Msg.TEXT_CHANGECASE_OPERATOR_TITLECASE = "to Title Case"; // untranslated +Blockly.Msg.TEXT_CHANGECASE_OPERATOR_UPPERCASE = "to UPPER CASE"; // untranslated +Blockly.Msg.TEXT_CHANGECASE_TOOLTIP = "Return a copy of the text in a different case."; // untranslated +Blockly.Msg.TEXT_CHARAT_FIRST = "Herfa sıfti bıgi"; +Blockly.Msg.TEXT_CHARAT_FROM_END = "# ra tepya herfan bıgi"; +Blockly.Msg.TEXT_CHARAT_FROM_START = "Herfa # bıgi"; +Blockly.Msg.TEXT_CHARAT_HELPURL = "https://github.com/google/blockly/wiki/Text#extracting-text"; // untranslated +Blockly.Msg.TEXT_CHARAT_INPUT_INTEXT = "metın de"; +Blockly.Msg.TEXT_CHARAT_LAST = "Herfa peyên bıgi"; +Blockly.Msg.TEXT_CHARAT_RANDOM = "Raştamaye yu herf bıgi"; +Blockly.Msg.TEXT_CHARAT_TAIL = ""; // untranslated +Blockly.Msg.TEXT_CHARAT_TOOLTIP = "Şınasnaye pozisyon de yu herfer çerğ keno"; +Blockly.Msg.TEXT_CREATE_JOIN_ITEM_TOOLTIP = "Add an item to the text."; // untranslated +Blockly.Msg.TEXT_CREATE_JOIN_TITLE_JOIN = "gıre de"; +Blockly.Msg.TEXT_CREATE_JOIN_TOOLTIP = "Add, remove, or reorder sections to reconfigure this text block."; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_END = "to letter # from end"; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_START = "to letter #"; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_END_LAST = "to last letter"; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_HELPURL = "https://github.com/google/blockly/wiki/Text#extracting-a-region-of-text"; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_INPUT_IN_TEXT = "metın de"; +Blockly.Msg.TEXT_GET_SUBSTRING_START_FIRST = "get substring from first letter"; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_END = "get substring from letter # from end"; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_START = "# ra substring gêno"; +Blockly.Msg.TEXT_GET_SUBSTRING_TAIL = ""; // untranslated +Blockly.Msg.TEXT_GET_SUBSTRING_TOOLTIP = "Tay letey metini çerğ keno"; +Blockly.Msg.TEXT_INDEXOF_HELPURL = "https://github.com/google/blockly/wiki/Text#finding-text"; // untranslated +Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT = "metın de"; +Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST = "find first occurrence of text"; // untranslated +Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST = "find last occurrence of text"; // untranslated +Blockly.Msg.TEXT_INDEXOF_TAIL = ""; // untranslated +Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "Returns the index of the first/last occurrence of the first text in the second text. Returns %1 if text is not found."; // untranslated +Blockly.Msg.TEXT_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; // untranslated +Blockly.Msg.TEXT_ISEMPTY_TITLE = "%1 vengo"; +Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "Returns true if the provided text is empty."; // untranslated +Blockly.Msg.TEXT_JOIN_HELPURL = "https://github.com/google/blockly/wiki/Text#text-creation"; // untranslated +Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH = "create text with"; // untranslated +Blockly.Msg.TEXT_JOIN_TOOLTIP = "Create a piece of text by joining together any number of items."; // untranslated +Blockly.Msg.TEXT_LENGTH_HELPURL = "https://github.com/google/blockly/wiki/Text#text-modification"; // untranslated +Blockly.Msg.TEXT_LENGTH_TITLE = "length of %1"; // untranslated +Blockly.Msg.TEXT_LENGTH_TOOLTIP = "Returns the number of letters (including spaces) in the provided text."; // untranslated +Blockly.Msg.TEXT_PRINT_HELPURL = "https://github.com/google/blockly/wiki/Text#printing-text"; // untranslated +Blockly.Msg.TEXT_PRINT_TITLE = "print %1"; // untranslated +Blockly.Msg.TEXT_PRINT_TOOLTIP = "Print the specified text, number or other value."; // untranslated +Blockly.Msg.TEXT_PROMPT_HELPURL = "https://github.com/google/blockly/wiki/Text#getting-input-from-the-user"; // untranslated +Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER = "Prompt for user for a number."; // untranslated +Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT = "Prompt for user for some text."; // untranslated +Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER = "prompt for number with message"; // untranslated +Blockly.Msg.TEXT_PROMPT_TYPE_TEXT = "prompt for text with message"; // untranslated +Blockly.Msg.TEXT_TEXT_HELPURL = "https://en.wikipedia.org/wiki/String_(computer_science)"; // untranslated +Blockly.Msg.TEXT_TEXT_TOOLTIP = "A letter, word, or line of text."; // untranslated +Blockly.Msg.TEXT_TRIM_HELPURL = "https://github.com/google/blockly/wiki/Text#trimming-removing-spaces"; // untranslated +Blockly.Msg.TEXT_TRIM_OPERATOR_BOTH = "trim spaces from both sides of"; // untranslated +Blockly.Msg.TEXT_TRIM_OPERATOR_LEFT = "trim spaces from left side of"; // untranslated +Blockly.Msg.TEXT_TRIM_OPERATOR_RIGHT = "trim spaces from right side of"; // untranslated +Blockly.Msg.TEXT_TRIM_TOOLTIP = "Return a copy of the text with spaces removed from one or both ends."; // untranslated +Blockly.Msg.TODAY = "Ewro"; +Blockly.Msg.UNDO = "Peyser biya"; +Blockly.Msg.VARIABLES_DEFAULT_NAME = "unsur"; +Blockly.Msg.VARIABLES_GET_CREATE_SET = "Create 'set %1'"; // untranslated +Blockly.Msg.VARIABLES_GET_HELPURL = "https://github.com/google/blockly/wiki/Variables#get"; // untranslated +Blockly.Msg.VARIABLES_GET_TOOLTIP = "Returns the value of this variable."; // untranslated +Blockly.Msg.VARIABLES_SET = "set %1 to %2"; // untranslated +Blockly.Msg.VARIABLES_SET_CREATE_GET = "'get %1' vıraz"; +Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated +Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated +Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; +Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; +Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; +Blockly.Msg.CONTROLS_IF_MSG_THEN = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; +Blockly.Msg.CONTROLS_IF_ELSE_TITLE_ELSE = Blockly.Msg.CONTROLS_IF_MSG_ELSE; +Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE = Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE; +Blockly.Msg.LISTS_GET_SUBLIST_INPUT_IN_LIST = Blockly.Msg.LISTS_INLIST; +Blockly.Msg.LISTS_GET_INDEX_INPUT_IN_LIST = Blockly.Msg.LISTS_INLIST; +Blockly.Msg.MATH_CHANGE_TITLE_ITEM = Blockly.Msg.VARIABLES_DEFAULT_NAME; +Blockly.Msg.PROCEDURES_DEFRETURN_DO = Blockly.Msg.PROCEDURES_DEFNORETURN_DO; +Blockly.Msg.CONTROLS_IF_ELSEIF_TITLE_ELSEIF = Blockly.Msg.CONTROLS_IF_MSG_ELSEIF; +Blockly.Msg.LISTS_GET_INDEX_HELPURL = Blockly.Msg.LISTS_INDEX_OF_HELPURL; +Blockly.Msg.CONTROLS_FOREACH_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; +Blockly.Msg.LISTS_SET_INDEX_INPUT_IN_LIST = Blockly.Msg.LISTS_INLIST; +Blockly.Msg.CONTROLS_FOR_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; +Blockly.Msg.LISTS_CREATE_WITH_ITEM_TITLE = Blockly.Msg.VARIABLES_DEFAULT_NAME; +Blockly.Msg.TEXT_APPEND_VARIABLE = Blockly.Msg.VARIABLES_DEFAULT_NAME; +Blockly.Msg.TEXT_CREATE_JOIN_ITEM_TITLE_ITEM = Blockly.Msg.VARIABLES_DEFAULT_NAME; +Blockly.Msg.LISTS_INDEX_OF_INPUT_IN_LIST = Blockly.Msg.LISTS_INLIST; +Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT = Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT; \ No newline at end of file diff --git a/msg/js/el.js b/msg/js/el.js index eb0259b8..ba527b71 100644 --- a/msg/js/el.js +++ b/msg/js/el.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Ενόσω μια τιμή εί Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Ενόσω μια τιμή είναι αληθής, τότε εκτελεί κάποιες εντολές."; Blockly.Msg.DELETE_ALL_BLOCKS = "Να διαγραφούν και τα %1 μπλοκ?"; Blockly.Msg.DELETE_BLOCK = "Διέγραψε Το Μπλοκ"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Διέγραψε %1 Μπλοκ"; Blockly.Msg.DISABLE_BLOCK = "Απενεργοποίησε Το Μπλοκ"; Blockly.Msg.DUPLICATE_BLOCK = "Διπλότυπο"; @@ -138,7 +140,7 @@ Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST = "Ορίζει το πρώτο σ Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM = "Ορίζει το στοιχείο στην καθορισμένη θέση σε μια λίστα."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST = "Ορίζει το τελευταίο στοιχείο σε μια λίστα."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM = "Ορίζει ένα τυχαίο στοιχείο σε μια λίστα."; -Blockly.Msg.LISTS_SORT_HELPURL = "https://github.com/google/blockly/wiki/Lists#sorting-a-list"; // untranslated +Blockly.Msg.LISTS_SORT_HELPURL = "https://github.com/google/blockly/wiki/Lists#sorting-a-list"; Blockly.Msg.LISTS_SORT_ORDER_ASCENDING = "ascending"; // untranslated Blockly.Msg.LISTS_SORT_ORDER_DESCENDING = "descending"; // untranslated Blockly.Msg.LISTS_SORT_TITLE = "sort %1 %2 %3"; // untranslated @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "όρισε %1 μέχρι το %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Δημιούργησε «πάρε %1»"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Ορίζει αυτή τη μεταβλητή να είναι ίση με την είσοδο."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/en.js b/msg/js/en.js index e15039cd..1020c64d 100644 --- a/msg/js/en.js +++ b/msg/js/en.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "While a value is false, then do Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "While a value is true, then do some statements."; Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; Blockly.Msg.DELETE_BLOCK = "Delete Block"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; Blockly.Msg.DELETE_X_BLOCKS = "Delete %1 Blocks"; Blockly.Msg.DISABLE_BLOCK = "Disable Block"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicate"; @@ -262,7 +264,7 @@ Blockly.Msg.MATH_TRIG_TOOLTIP_ATAN = "Return the arctangent of a number."; Blockly.Msg.MATH_TRIG_TOOLTIP_COS = "Return the cosine of a degree (not radian)."; Blockly.Msg.MATH_TRIG_TOOLTIP_SIN = "Return the sine of a degree (not radian)."; Blockly.Msg.MATH_TRIG_TOOLTIP_TAN = "Return the tangent of a degree (not radian)."; -Blockly.Msg.NEW_VARIABLE = "New variable..."; +Blockly.Msg.NEW_VARIABLE = "Create variable..."; Blockly.Msg.NEW_VARIABLE_TITLE = "New variable name:"; Blockly.Msg.ORDINAL_NUMBER_SUFFIX = ""; Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS = "allow statements"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/es.js b/msg/js/es.js index a6c685bf..92ebe924 100644 --- a/msg/js/es.js +++ b/msg/js/es.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Mientras un valor sea falso, en Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Mientras un valor sea verdadero, entonces hacer algunas declaraciones."; Blockly.Msg.DELETE_ALL_BLOCKS = "¿Eliminar todos los %1 bloques?"; Blockly.Msg.DELETE_BLOCK = "Eliminar bloque"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Eliminar %1 bloques"; Blockly.Msg.DISABLE_BLOCK = "Desactivar bloque"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicar"; @@ -90,15 +92,15 @@ Blockly.Msg.LISTS_GET_INDEX_RANDOM = "aleatorio"; Blockly.Msg.LISTS_GET_INDEX_REMOVE = "eliminar"; Blockly.Msg.LISTS_GET_INDEX_TAIL = ""; // untranslated Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST = "Devuelve el primer elemento de una lista."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "Devuelve el elemento en la posición especificada en la lista."; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "Devuelve el elemento en la posición especificada en una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST = "Devuelve el último elemento de una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM = "Devuelve un elemento aleatorio en una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST = "Elimina y devuelve el primer elemento de una lista."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM = "Elimina y devuelve el elemento en la posición especificada en la lista."; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM = "Elimina y devuelve el elemento en la posición especificada en una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST = "Elimina y devuelve el último elemento de una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM = "Elimina y devuelve un elemento aleatorio en una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST = "Elimina el primer elemento de una lista."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM = "Elimina el elemento en la posición especificada en la lista."; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM = "Elimina el elemento en la posición especificada en una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST = "Elimina el último elemento de una lista."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM = "Elimina un elemento aleatorio en una lista."; Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END = "hasta # del final"; @@ -131,7 +133,7 @@ Blockly.Msg.LISTS_SET_INDEX_INPUT_TO = "como"; Blockly.Msg.LISTS_SET_INDEX_INSERT = "insertar en"; Blockly.Msg.LISTS_SET_INDEX_SET = "establecer"; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST = "Inserta el elemento al inicio de una lista."; -Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "Inserta el elemento en la posición especificada en la lista."; +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "Inserta el elemento en la posición especificada en una lista."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST = "Añade el elemento al final de una lista."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM = "Inserta el elemento aleatoriamente en una lista."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST = "Establece el primer elemento de una lista."; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "establecer %1 a %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Crear 'obtener %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Establece esta variable para que sea igual a la entrada."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/fa.js b/msg/js/fa.js index 7f2c8ae6..35bcb74b 100644 --- a/msg/js/fa.js +++ b/msg/js/fa.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "تا زمانی که یک مق Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "تا زمانی که یک مقدار صحیح است، چند عبارت را انجام بده."; Blockly.Msg.DELETE_ALL_BLOCKS = "حذف همهٔ بلاک‌های %1؟"; Blockly.Msg.DELETE_BLOCK = "حذف بلوک"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "حذف بلوک‌های %1"; Blockly.Msg.DISABLE_BLOCK = "غیرفعال‌سازی بلوک"; Blockly.Msg.DUPLICATE_BLOCK = "تکراری"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "مجموعه %1 به %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "درست‌کردن «گرفتن %1»"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "متغیر برابر با خروجی را مشخص می‌کند."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/fi.js b/msg/js/fi.js index fdfcced3..9bbacb4f 100644 --- a/msg/js/fi.js +++ b/msg/js/fi.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Niin kauan kuin arvo on epätos Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Niin kauan kuin arvo on tosi, suorita joukko lausekkeita."; Blockly.Msg.DELETE_ALL_BLOCKS = "Poistetaanko kaikki %1 lohkoa?"; Blockly.Msg.DELETE_BLOCK = "Poista lohko"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Poista %1 lohkoa"; Blockly.Msg.DISABLE_BLOCK = "Passivoi lohko"; Blockly.Msg.DUPLICATE_BLOCK = "Kopioi"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "aseta %1 arvoksi %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Luo 'hae %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Asettaa muutujan arvoksi annetun syötteen."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/fr.js b/msg/js/fr.js index ededfe91..d662f371 100644 --- a/msg/js/fr.js +++ b/msg/js/fr.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Tant qu’une valeur est fausse Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Tant qu’une valeur est vraie, alors exécuter des instructions."; Blockly.Msg.DELETE_ALL_BLOCKS = "Supprimer ces %1 blocs ?"; Blockly.Msg.DELETE_BLOCK = "Supprimer le bloc"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Supprimer %1 blocs"; Blockly.Msg.DISABLE_BLOCK = "Désactiver le bloc"; Blockly.Msg.DUPLICATE_BLOCK = "Dupliquer"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "fixer %1 à %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Créer 'obtenir %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Fixe cette variable pour qu’elle soit égale à la valeur de l’entrée."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/he.js b/msg/js/he.js index 5b960bd8..4bbd18b2 100644 --- a/msg/js/he.js +++ b/msg/js/he.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "בזמן שהערך שווה ל Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "כל עוד הערך הוא אמת, לעשות כמה פעולות."; Blockly.Msg.DELETE_ALL_BLOCKS = "האם למחוק את כל %1 קטעי הקוד?"; Blockly.Msg.DELETE_BLOCK = "מחק קטע קוד"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "מחק %1 קטעי קוד"; Blockly.Msg.DISABLE_BLOCK = "נטרל קטע קוד"; Blockly.Msg.DUPLICATE_BLOCK = "שכפל"; @@ -115,7 +117,7 @@ Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 הוא הפריט הראשון. Blockly.Msg.LISTS_INDEX_OF_FIRST = "מחזירה את המיקום הראשון של פריט ברשימה"; Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated Blockly.Msg.LISTS_INDEX_OF_LAST = "מחזירה את המיקום האחרון של פריט ברשימה"; -Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "מחזירה את האינדקס של המופע ראשון/אחרון של הפריט ברשימה. מחזירה %1 אם הפריט אינו נמצא."; +Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "מחזירה את האינדקס של המופע הראשון/האחרון של הפריט ברשימה. מחזירה %1 אם הפריט אינו נמצא."; Blockly.Msg.LISTS_INLIST = "ברשימה"; Blockly.Msg.LISTS_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Lists#is-empty"; // untranslated Blockly.Msg.LISTS_ISEMPTY_TITLE = "%1 הוא ריק"; @@ -331,7 +333,7 @@ Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT = "in text"; // untranslated Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST = "find first occurrence of text"; // untranslated Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST = "find last occurrence of text"; // untranslated Blockly.Msg.TEXT_INDEXOF_TAIL = ""; // untranslated -Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "Returns the index of the first/last occurrence of the first text in the second text. Returns %1 if text is not found."; // untranslated +Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "מחזירה את האינדקס של המופע הראשון/האחרון בטקסט הראשון לתוך הטקסט השני. מחזירה %1 אם הטקסט אינו נמצא."; Blockly.Msg.TEXT_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; // untranslated Blockly.Msg.TEXT_ISEMPTY_TITLE = "%1 is empty"; // untranslated Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "Returns true if the provided text is empty."; // untranslated @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "הגדר %1 ל- %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "ליצור 'קרא %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "מגדיר משתנה זה להיות שווה לקלט."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/hi.js b/msg/js/hi.js index 0ddddea9..261a69cf 100644 --- a/msg/js/hi.js +++ b/msg/js/hi.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "जब तक मान फॉ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "जब तक मान ट्रू है, तब तक कुछ स्टेट्मेंट्स चलाएँ।"; Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "ब्लॉक हटाएँ"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 ब्लॉक हटाएँ"; Blockly.Msg.DISABLE_BLOCK = "ब्लॉक को अक्षम करें"; Blockly.Msg.DUPLICATE_BLOCK = "कॉपी करें"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "सेट करें %1 को %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "इस चर को इनपुट के बराबर सेट करता है।"; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/hrx.js b/msg/js/hrx.js index 0969d3b7..d2518399 100644 --- a/msg/js/hrx.js +++ b/msg/js/hrx.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Füahr die Oonweisung solang au Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Füahr die Oonweisung solang aus wie die Bedingung woahr (true) ist."; Blockly.Msg.DELETE_ALL_BLOCKS = "All %1 Bausten lösche?"; Blockly.Msg.DELETE_BLOCK = "Block lösche"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Block %1 lösche"; Blockly.Msg.DISABLE_BLOCK = "Block deaktivieren"; Blockly.Msg.DUPLICATE_BLOCK = "Kopieren"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "Schreib %1 zu %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Generier/erzeich \"Lese %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Setzt en Variable sei Weart."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/hu.js b/msg/js/hu.js index 1ed3c30c..c57c98b8 100644 --- a/msg/js/hu.js +++ b/msg/js/hu.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Amíg a feltétel hamis, végre Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Amíg a feltétel igaz, végrehajtja az utasításokat."; Blockly.Msg.DELETE_ALL_BLOCKS = "Az összes %1 blokk törlése?"; Blockly.Msg.DELETE_BLOCK = "Blokk törlése"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 blokk törlése"; Blockly.Msg.DISABLE_BLOCK = "Blokk letiltása"; Blockly.Msg.DUPLICATE_BLOCK = "Másolat"; @@ -115,7 +117,7 @@ Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 az első elemet jelenti."; Blockly.Msg.LISTS_INDEX_OF_FIRST = "listában első előfordulásaː"; Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated Blockly.Msg.LISTS_INDEX_OF_LAST = "listában utolsó előfordulásaː"; -Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "A megadott elem első vagy utolsó előfordulásával tér vissza. %1-val tér vissza, ha nem talál ilyen elemet."; +Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "A megadott elem első vagy utolsó előfordulásával tér vissza. Ha nem talál ilyen elemet, akkor %1 a visszatérési érték."; Blockly.Msg.LISTS_INLIST = "A(z)"; Blockly.Msg.LISTS_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Lists#is-empty"; // untranslated Blockly.Msg.LISTS_ISEMPTY_TITLE = "%1 üres lista?"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "%1 %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Készíts \"%1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "A változónak adhatunk értéket."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ia.js b/msg/js/ia.js index 58d10fd3..10766402 100644 --- a/msg/js/ia.js +++ b/msg/js/ia.js @@ -39,7 +39,7 @@ Blockly.Msg.CONTROLS_FOREACH_TITLE = "pro cata elemento %1 in lista %2"; Blockly.Msg.CONTROLS_FOREACH_TOOLTIP = "Pro cata elemento in un lista, mitter lo in le variabile '%1' e exequer certe instructiones."; Blockly.Msg.CONTROLS_FOR_HELPURL = "https://github.com/google/blockly/wiki/Loops#count-with"; // untranslated Blockly.Msg.CONTROLS_FOR_TITLE = "contar con %1 de %2 a %3 per %4"; -Blockly.Msg.CONTROLS_FOR_TOOLTIP = "Mitter in le variabile \"%1\" le valores ab le numero initial usque al numero final, con passos secundo le intervallo specificate, e exequer le blocos specificate."; +Blockly.Msg.CONTROLS_FOR_TOOLTIP = "Mitter in le variabile '%1' le valores ab le numero initial usque al numero final, con passos secundo le intervallo specificate, e exequer le blocos specificate."; Blockly.Msg.CONTROLS_IF_ELSEIF_TOOLTIP = "Adder un condition al bloco \"si\"."; Blockly.Msg.CONTROLS_IF_ELSE_TOOLTIP = "Adder un condition final de reserva al bloco \"si\"."; Blockly.Msg.CONTROLS_IF_HELPURL = "https://github.com/google/blockly/wiki/IfElse"; // untranslated @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Durante que un valor es false, Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Durante que un valor es ver, exequer certe instructiones."; Blockly.Msg.DELETE_ALL_BLOCKS = "Deler tote le %1 blocos?"; Blockly.Msg.DELETE_BLOCK = "Deler bloco"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Deler %1 blocos"; Blockly.Msg.DISABLE_BLOCK = "Disactivar bloco"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicar"; @@ -115,7 +117,7 @@ Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "№ %1 es le prime elemento."; Blockly.Msg.LISTS_INDEX_OF_FIRST = "cercar le prime occurrentia del elemento"; Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated Blockly.Msg.LISTS_INDEX_OF_LAST = "cercar le ultime occurrentia del elemento"; -Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "Retorna le indice del prime/ultime occurrentia del elemento in le lista. Retorna %1 si le texto non es trovate."; +Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "Retorna le indice del prime/ultime occurrentia del elemento in le lista. Retorna %1 si le elemento non es trovate."; Blockly.Msg.LISTS_INLIST = "in lista"; Blockly.Msg.LISTS_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Lists#is-empty"; // untranslated Blockly.Msg.LISTS_ISEMPTY_TITLE = "%1 es vacue"; @@ -139,18 +141,18 @@ Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM = "Defini le valor del elemento al Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST = "Defini le valor del ultime elemento in un lista."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM = "Defini le valor de un elemento aleatori in un lista."; Blockly.Msg.LISTS_SORT_HELPURL = "https://github.com/google/blockly/wiki/Lists#sorting-a-list"; // untranslated -Blockly.Msg.LISTS_SORT_ORDER_ASCENDING = "ascending"; // untranslated -Blockly.Msg.LISTS_SORT_ORDER_DESCENDING = "descending"; // untranslated -Blockly.Msg.LISTS_SORT_TITLE = "sort %1 %2 %3"; // untranslated -Blockly.Msg.LISTS_SORT_TOOLTIP = "Sort a copy of a list."; // untranslated -Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE = "alphabetic, ignore case"; // untranslated -Blockly.Msg.LISTS_SORT_TYPE_NUMERIC = "numeric"; // untranslated -Blockly.Msg.LISTS_SORT_TYPE_TEXT = "alphabetic"; // untranslated +Blockly.Msg.LISTS_SORT_ORDER_ASCENDING = "ascendente"; +Blockly.Msg.LISTS_SORT_ORDER_DESCENDING = "descendente"; +Blockly.Msg.LISTS_SORT_TITLE = "ordinamento %1 %2 %3"; +Blockly.Msg.LISTS_SORT_TOOLTIP = "Ordinar un copia de un lista."; +Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE = "alphabetic, ignorar majuscula/minuscula"; +Blockly.Msg.LISTS_SORT_TYPE_NUMERIC = "numeric"; +Blockly.Msg.LISTS_SORT_TYPE_TEXT = "alphabetic"; Blockly.Msg.LISTS_SPLIT_HELPURL = "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists"; // untranslated Blockly.Msg.LISTS_SPLIT_LIST_FROM_TEXT = "Crear un lista per un texto"; Blockly.Msg.LISTS_SPLIT_TEXT_FROM_LIST = "crear un texto per un lista"; -Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN = "Join a list of texts into one text, separated by a delimiter."; // untranslated -Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT = "Split text into a list of texts, breaking at each delimiter."; // untranslated +Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN = "Unir un lista de textos, separate per un delimitator, in un sol texto."; +Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT = "Divider texto in un lista de textos, separante lo a cata delimitator."; Blockly.Msg.LISTS_SPLIT_WITH_DELIMITER = "con delimitator"; Blockly.Msg.LOGIC_BOOLEAN_FALSE = "false"; Blockly.Msg.LOGIC_BOOLEAN_HELPURL = "https://github.com/google/blockly/wiki/Logic#values"; // untranslated @@ -273,7 +275,7 @@ Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL = "https://en.wikipedia.org/wiki/Proce Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP = "Executar le function '%1' definite per le usator e usar su resultato."; Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS = "con:"; Blockly.Msg.PROCEDURES_CREATE_DO = "Crear '%1'"; -Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = "Describe this function..."; // untranslated +Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT = "Describe iste function..."; Blockly.Msg.PROCEDURES_DEFNORETURN_DO = ""; // untranslated Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; // untranslated Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE = "facer qualcosa"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "mitter %1 a %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Crear 'prender %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Mitte iste variabile al valor del entrata."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/id.js b/msg/js/id.js index 8f43b7cd..419cdb49 100644 --- a/msg/js/id.js +++ b/msg/js/id.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Selagi nilainya salah, maka lak Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Selagi nilainya benar, maka lakukan beberapa perintah."; Blockly.Msg.DELETE_ALL_BLOCKS = "Hapus semua %1 blok?"; Blockly.Msg.DELETE_BLOCK = "Hapus Blok"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Hapus %1 Blok"; Blockly.Msg.DISABLE_BLOCK = "Nonaktifkan Blok"; Blockly.Msg.DUPLICATE_BLOCK = "Duplikat"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "tetapkan %1 untuk %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Buat 'get %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "tetapkan variabel ini dengan input yang sama."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/is.js b/msg/js/is.js index 4e6f84c5..19783f18 100644 --- a/msg/js/is.js +++ b/msg/js/is.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Endurtaka eitthvað á meðan g Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Endurtaka eitthvað á meðan gildi er satt."; Blockly.Msg.DELETE_ALL_BLOCKS = "Eyða öllum %1 kubbunum?"; Blockly.Msg.DELETE_BLOCK = "Eyða kubbi"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Eyða %1 kubbum"; Blockly.Msg.DISABLE_BLOCK = "Óvirkja kubb"; Blockly.Msg.DUPLICATE_BLOCK = "Afrita"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "stilla %1 á %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Búa til 'sækja %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; Blockly.Msg.VARIABLES_SET_TOOLTIP = "Stillir þessa breytu á innihald inntaksins."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/it.js b/msg/js/it.js index 181f8cff..c8f9a133 100644 --- a/msg/js/it.js +++ b/msg/js/it.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Finché un valore è falso, ese Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Finché un valore è vero, esegue alcune istruzioni."; Blockly.Msg.DELETE_ALL_BLOCKS = "Cancellare tutti i %1 blocchi?"; Blockly.Msg.DELETE_BLOCK = "Cancella blocco"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Cancella %1 blocchi"; Blockly.Msg.DISABLE_BLOCK = "Disattiva blocco"; Blockly.Msg.DUPLICATE_BLOCK = "Duplica"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "imposta %1 a %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Crea 'prendi %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Imposta questa variabile ad essere pari all'input."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ja.js b/msg/js/ja.js index 6b969135..4b4ecbc3 100644 --- a/msg/js/ja.js +++ b/msg/js/ja.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "値は false のあいだ、い Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "値は true のあいだ、いくつかのステートメントを行います。"; Blockly.Msg.DELETE_ALL_BLOCKS = "%1件のすべてのブロックを消しますか?"; Blockly.Msg.DELETE_BLOCK = "ブロックを消す"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 個のブロックを消す"; Blockly.Msg.DISABLE_BLOCK = "ブロックを無効にします。"; Blockly.Msg.DUPLICATE_BLOCK = "複製"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "セット %1 宛先 %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "'%1 を取得' を作成します。"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "この入力を変数と等しくなるように設定します。"; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ko.js b/msg/js/ko.js index 9029ab9d..6aac7ff4 100644 --- a/msg/js/ko.js +++ b/msg/js/ko.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "값이 거짓일 때, 몇 가 Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "값이 참일 때, 몇 가지 선언을 합니다."; Blockly.Msg.DELETE_ALL_BLOCKS = "모든 블록 %1개를 삭제하겠습니까?"; Blockly.Msg.DELETE_BLOCK = "블록 삭제"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "블록 %1 삭제"; Blockly.Msg.DISABLE_BLOCK = "블록 비활성화"; Blockly.Msg.DUPLICATE_BLOCK = "중복됨"; @@ -110,12 +112,12 @@ Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END = "마지막부터 # 번째 위치 Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START = "처음 # 번째 위치부터, 서브 리스트 추출"; Blockly.Msg.LISTS_GET_SUBLIST_TAIL = ""; Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP = "목록의 특정 부분에 대한 복사본을 만듭니다."; -Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1은 마지막 항목입니다."; +Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1은(는) 마지막 항목입니다."; Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1은 첫 번째 항목입니다."; Blockly.Msg.LISTS_INDEX_OF_FIRST = "처음으로 나타난 위치"; Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; Blockly.Msg.LISTS_INDEX_OF_LAST = "마지막으로 나타난 위치"; -Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "아이템이 나타난 처음 또는 마지막 위치를 찾아 돌려줍니다. 아이템이 없으면 %1이 반환됩니다."; +Blockly.Msg.LISTS_INDEX_OF_TOOLTIP = "목록에서 항목이 처음 또는 마지막으로 발생한 색인 위치를 반환합니다. 항목이 없으면 %1을 반환합니다."; Blockly.Msg.LISTS_INLIST = "리스트"; Blockly.Msg.LISTS_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Lists#is-empty"; Blockly.Msg.LISTS_ISEMPTY_TITLE = "%1이 비어 있습니다"; @@ -131,7 +133,7 @@ Blockly.Msg.LISTS_SET_INDEX_INPUT_TO = "에"; Blockly.Msg.LISTS_SET_INDEX_INSERT = "에서 원하는 위치에 삽입"; Blockly.Msg.LISTS_SET_INDEX_SET = "에서 설정"; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST = "항목을 목록의 처음 위치에 삽입합니다."; -Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "아이템을 리스트의 특정 위치에 삽입합니다."; +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "목록의 특정 위치에 항목을 삽입합니다."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST = "리스트의 마지막에 아이템을 추가합니다."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM = "목록에서 임의 위치에 아이템을 삽입합니다."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST = "첫 번째 위치의 아이템으로 설정합니다."; @@ -331,7 +333,7 @@ Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT = "문장"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST = "에서 다음 문장이 처음으로 나타난 위치 찾기 :"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST = "에서 다음 문장이 마지막으로 나타난 위치 찾기 :"; Blockly.Msg.TEXT_INDEXOF_TAIL = ""; -Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "어떤 문장이 가장 처음 나타난 위치 또는, 가장 마지막으로 나타난 위치를 찾아 돌려줍니다. 찾는 문장이 없는 경우는 %1 값을 돌려줌."; +Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "두 번째 텍스트에서 첫 번째 텍스트가 처음 또는 마지막으로 발생한 색인 위치를 반환합니다. 텍스트가 없으면 %1을 반환합니다."; Blockly.Msg.TEXT_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; Blockly.Msg.TEXT_ISEMPTY_TITLE = "%1이 비어 있습니다"; Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "입력된 문장이, 빈 문장(\"\")이면 참(true) 값을 돌려줍니다."; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "%1를 %2로 설정"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "'%1 값 읽기' 블럭 생성"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://ko.wikipedia.org/wiki/%EB%B3%80%EC%88%98_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)"; Blockly.Msg.VARIABLES_SET_TOOLTIP = "변수의 값을 입력한 값으로 변경해 줍니다."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/lb.js b/msg/js/lb.js index 933ad837..1a38e8b5 100644 --- a/msg/js/lb.js +++ b/msg/js/lb.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Féiert d'Uweisungen aus, soula Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Féiert d'Uweisungen aus, soulaang wéi de Wäert richteg ass"; Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "Block läschen"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 Bléck läschen"; Blockly.Msg.DISABLE_BLOCK = "Block desaktivéieren"; Blockly.Msg.DUPLICATE_BLOCK = "Eng Kopie maachen"; @@ -110,8 +112,8 @@ Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END = "get sub-list from # from end"; Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START = "get sub-list from #"; // untranslated Blockly.Msg.LISTS_GET_SUBLIST_TAIL = ""; // untranslated Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP = "Creates a copy of the specified portion of a list."; // untranslated -Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1 is the last item."; // untranslated -Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 is the first item."; // untranslated +Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1 ass dat éischt Element."; +Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 ass dat éischt Element."; Blockly.Msg.LISTS_INDEX_OF_FIRST = "find first occurrence of item"; // untranslated Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated Blockly.Msg.LISTS_INDEX_OF_LAST = "find last occurrence of item"; // untranslated @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; // untranslated Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/lki.js b/msg/js/lki.js index 417e4bae..0c4469e9 100644 --- a/msg/js/lki.js +++ b/msg/js/lki.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "تا زمانی که یک مق Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "تا زمانی که یک مقدار صحیح است، چند عبارت را انجام بده."; Blockly.Msg.DELETE_ALL_BLOCKS = "حةذف کؤل %1 بلاکةل?"; Blockly.Msg.DELETE_BLOCK = "پاک کردن بلاک"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "حةذف %1 بلاکةل"; Blockly.Msg.DISABLE_BLOCK = "إ کار کةتن(غیرفعال‌سازی) بلاک"; Blockly.Msg.DUPLICATE_BLOCK = "کؤپی کردن"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "مجموعه %1 به %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "درست‌کردن «گرفتن %1»"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "متغیر برابر با خروجی را مشخص می‌کند."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/lrc.js b/msg/js/lrc.js index d2537fbb..2731ca98 100644 --- a/msg/js/lrc.js +++ b/msg/js/lrc.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "While a value is false, then do Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "While a value is true, then do some statements."; // untranslated Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "پاکسا کردن برشت"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "پاکسا کردن%1 د برشتیا"; Blockly.Msg.DISABLE_BLOCK = "ناکشتگر کردن برشت"; Blockly.Msg.DUPLICATE_BLOCK = "کپی کردن"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "میزوکاری %1 سی %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/lt.js b/msg/js/lt.js index 79fdbb22..8ac37256 100644 --- a/msg/js/lt.js +++ b/msg/js/lt.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Kartoja veiksmus, kol bus pasie Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Kartoja veiksmus, kol sąlyga tenkinama."; Blockly.Msg.DELETE_ALL_BLOCKS = "Ištrinti visus %1 blokus?"; Blockly.Msg.DELETE_BLOCK = "Ištrinti bloką"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Ištrinti %1 blokus"; Blockly.Msg.DISABLE_BLOCK = "Išjungti bloką"; Blockly.Msg.DUPLICATE_BLOCK = "Kopijuoti"; @@ -90,7 +92,7 @@ Blockly.Msg.LISTS_GET_INDEX_RANDOM = "atsitiktinis"; Blockly.Msg.LISTS_GET_INDEX_REMOVE = "pašalinti"; Blockly.Msg.LISTS_GET_INDEX_TAIL = ""; // untranslated Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST = "Grąžina pirmąjį sąrašo elementą."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "Returns the item at the specified position in a list."; // untranslated +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "Gražina objektą į nurodyta poziciją sąraše."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST = "Grąžina paskutinį elementą iš sąrašo."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM = "Grąžina atsitiktinį elementą iš sąrašo."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST = "Removes and returns the first item in a list."; // untranslated @@ -110,8 +112,8 @@ Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END = "sąrašo dalis nuo # nuo galo"; Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START = "sąrašo dalis nuo #"; Blockly.Msg.LISTS_GET_SUBLIST_TAIL = ""; // untranslated Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP = "Creates a copy of the specified portion of a list."; // untranslated -Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1 is the last item."; // untranslated -Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 is the first item."; // untranslated +Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1 yra paskutinis objektas."; +Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 yra pirmasis objektas."; Blockly.Msg.LISTS_INDEX_OF_FIRST = "rask pirmą reikšmę"; Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated Blockly.Msg.LISTS_INDEX_OF_LAST = "rask paskutinę reikšmę"; @@ -131,7 +133,7 @@ Blockly.Msg.LISTS_SET_INDEX_INPUT_TO = "kaip"; Blockly.Msg.LISTS_SET_INDEX_INSERT = "įterpk į vietą"; Blockly.Msg.LISTS_SET_INDEX_SET = "priskirk elementui"; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST = "Inserts the item at the start of a list."; // untranslated -Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "Inserts the item at the specified position in a list."; // untranslated +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "Įterpią objektą į nurodytą poziciją sąraše."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST = "Append the item to the end of a list."; // untranslated Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM = "Inserts the item randomly in a list."; // untranslated Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST = "Sets the first item in a list."; // untranslated @@ -156,7 +158,7 @@ Blockly.Msg.LOGIC_BOOLEAN_FALSE = "klaidinga"; Blockly.Msg.LOGIC_BOOLEAN_HELPURL = "https://github.com/google/blockly/wiki/Logic#values"; // untranslated Blockly.Msg.LOGIC_BOOLEAN_TOOLTIP = "Reikšmė gali būti \"teisinga\"/\"Taip\" arba \"klaidinga\"/\"Ne\"."; Blockly.Msg.LOGIC_BOOLEAN_TRUE = "tiesa"; -Blockly.Msg.LOGIC_COMPARE_HELPURL = "https://en.wikipedia.org/wiki/Inequality_(mathematics)"; // untranslated +Blockly.Msg.LOGIC_COMPARE_HELPURL = "https://en.wikipedia.org/wiki/Inequality_(mathematics)"; Blockly.Msg.LOGIC_COMPARE_TOOLTIP_EQ = "Tenkinama, jei abu reiškiniai lygūs."; Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GT = "Return true if the first input is greater than the second input."; // untranslated Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GTE = "Return true if the first input is greater than or equal to the second input."; // untranslated @@ -186,10 +188,10 @@ Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE = "Grąžina dviejų skaičių dalmen Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS = "Grąžina dviejų skaičių skirtumą."; Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY = "Grąžina dviejų skaičių sandaugą."; Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER = "Grąžina pirmą skaičių pakeltą laipsniu pagal antrą skaičių."; -Blockly.Msg.MATH_CHANGE_HELPURL = "https://en.wikipedia.org/wiki/Programming_idiom#Incrementing_a_counter"; // untranslated +Blockly.Msg.MATH_CHANGE_HELPURL = "https://en.wikipedia.org/wiki/Programming_idiom#Incrementing_a_counter"; Blockly.Msg.MATH_CHANGE_TITLE = "padidink %1 (emptypage) %2"; Blockly.Msg.MATH_CHANGE_TOOLTIP = "Prideda skaičių prie kintamojo '%1'. Kai skaičius neigiamas - gaunasi atimtis."; -Blockly.Msg.MATH_CONSTANT_HELPURL = "https://en.wikipedia.org/wiki/Mathematical_constant"; // untranslated +Blockly.Msg.MATH_CONSTANT_HELPURL = "https://lt.wikipedia.org/wiki/Matematin%C4%97_konstanta"; Blockly.Msg.MATH_CONSTANT_TOOLTIP = "Return one of the common constants: π (3.141…), e (2.718…), φ (1.618…), sqrt(2) (1.414…), sqrt(½) (0.707…), or ∞ (infinity)."; // untranslated Blockly.Msg.MATH_CONSTRAIN_HELPURL = "https://en.wikipedia.org/wiki/Clamping_%28graphics%29"; // untranslated Blockly.Msg.MATH_CONSTRAIN_TITLE = "apribok %1 tarp %2 ir %3"; @@ -203,7 +205,7 @@ Blockly.Msg.MATH_IS_POSITIVE = "yra teigiamas"; Blockly.Msg.MATH_IS_PRIME = "yra pirminis"; Blockly.Msg.MATH_IS_TOOLTIP = "Patikrina skaičiaus savybę: (ne)lyginis/pirminis/sveikasis/teigiamas/neigiamas/dalus iš x."; Blockly.Msg.MATH_IS_WHOLE = "yra sveikasis"; -Blockly.Msg.MATH_MODULO_HELPURL = "https://en.wikipedia.org/wiki/Modulo_operation"; // untranslated +Blockly.Msg.MATH_MODULO_HELPURL = "https://en.wikipedia.org/wiki/Modulo_operation"; Blockly.Msg.MATH_MODULO_TITLE = "dalybos liekana %1 ÷ %2"; Blockly.Msg.MATH_MODULO_TOOLTIP = "Return the remainder from dividing the two numbers."; // untranslated Blockly.Msg.MATH_MULTIPLICATION_SYMBOL = "×"; // untranslated @@ -227,17 +229,17 @@ Blockly.Msg.MATH_ONLIST_TOOLTIP_RANDOM = "Grąžinti atsitiktinį elementą iš Blockly.Msg.MATH_ONLIST_TOOLTIP_STD_DEV = "Return the standard deviation of the list."; // untranslated Blockly.Msg.MATH_ONLIST_TOOLTIP_SUM = "didžiausia reikšmė"; Blockly.Msg.MATH_POWER_SYMBOL = "^"; // untranslated -Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL = "https://en.wikipedia.org/wiki/Random_number_generation"; // untranslated +Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL = "https://en.wikipedia.org/wiki/Random_number_generation"; Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM = "atsitiktinė trupmena"; Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP = "Atsitiktinė trupmena nuo 0 (imtinai) iki 1 (neimtinai)."; -Blockly.Msg.MATH_RANDOM_INT_HELPURL = "https://en.wikipedia.org/wiki/Random_number_generation"; // untranslated +Blockly.Msg.MATH_RANDOM_INT_HELPURL = "https://en.wikipedia.org/wiki/Random_number_generation"; Blockly.Msg.MATH_RANDOM_INT_TITLE = "atsitiktinis sveikas sk. nuo %1 iki %2"; Blockly.Msg.MATH_RANDOM_INT_TOOLTIP = "Return a random integer between the two specified limits, inclusive."; // untranslated -Blockly.Msg.MATH_ROUND_HELPURL = "https://en.wikipedia.org/wiki/Rounding"; // untranslated +Blockly.Msg.MATH_ROUND_HELPURL = "https://lt.wikipedia.org/wiki/Apvalinimas"; Blockly.Msg.MATH_ROUND_OPERATOR_ROUND = "apvalink"; Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDDOWN = "apvalink žemyn"; Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDUP = "apvalink aukštyn"; -Blockly.Msg.MATH_ROUND_TOOLTIP = "Round a number up or down."; // untranslated +Blockly.Msg.MATH_ROUND_TOOLTIP = "Suapvalinti skaičių į žemesnę ar aukštesnę reikšmę."; Blockly.Msg.MATH_SINGLE_HELPURL = "https://en.wikipedia.org/wiki/Square_root"; Blockly.Msg.MATH_SINGLE_OP_ABSOLUTE = "modulis"; Blockly.Msg.MATH_SINGLE_OP_ROOT = "kvadratinė šaknis"; @@ -267,9 +269,9 @@ Blockly.Msg.NEW_VARIABLE_TITLE = "Naujo kintamojo pavadinimas:"; Blockly.Msg.ORDINAL_NUMBER_SUFFIX = ""; // untranslated Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS = "leisti vidinius veiksmus"; Blockly.Msg.PROCEDURES_BEFORE_PARAMS = "pagal:"; -Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; // untranslated +Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP = "Vykdyti sukurtą komandą \"%1\"."; -Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; // untranslated +Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL = "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29"; Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP = "Įvykdyti komandą \"%1\" ir naudoti jos suskaičiuotą (atiduotą) reikšmę."; Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS = "su:"; Blockly.Msg.PROCEDURES_CREATE_DO = "Sukurti \"%1\""; @@ -291,7 +293,7 @@ Blockly.Msg.PROCEDURES_MUTATORARG_TITLE = "parametro pavadinimas:"; Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP = "Pridėti funkcijos parametrą (gaunamų duomenų pavadinimą)."; Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE = "gaunami duomenys (parametrai)"; Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP = "Tvarkyti komandos gaunamus duomenis (parametrus)."; -Blockly.Msg.REDO = "Redo"; // untranslated +Blockly.Msg.REDO = "Atkurti"; Blockly.Msg.REMOVE_COMMENT = "Pašalinti komentarą"; Blockly.Msg.RENAME_VARIABLE = "Pervardyti kintamajį..."; Blockly.Msg.RENAME_VARIABLE_TITLE = "Pervadinti visus '%1' kintamuosius į:"; @@ -357,7 +359,7 @@ Blockly.Msg.TEXT_TRIM_OPERATOR_LEFT = "išvalyk tarpus pradžioje"; Blockly.Msg.TEXT_TRIM_OPERATOR_RIGHT = "išvalyk tarpus pabaigoje"; Blockly.Msg.TEXT_TRIM_TOOLTIP = "Return a copy of the text with spaces removed from one or both ends."; // untranslated Blockly.Msg.TODAY = "Šiandien"; -Blockly.Msg.UNDO = "Undo"; // untranslated +Blockly.Msg.UNDO = "Anuliuoti"; Blockly.Msg.VARIABLES_DEFAULT_NAME = "elementas"; Blockly.Msg.VARIABLES_GET_CREATE_SET = "Sukurk \"priskirk %1\""; Blockly.Msg.VARIABLES_GET_HELPURL = "https://github.com/google/blockly/wiki/Variables#get"; // untranslated @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "priskirk %1 = %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Sukurti 'kintamasis %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/mk.js b/msg/js/mk.js index 9ea5c3de..8a9b2b32 100644 --- a/msg/js/mk.js +++ b/msg/js/mk.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Додека вредноста Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Додека вредноста е вистинита, исполнува наредби."; Blockly.Msg.DELETE_ALL_BLOCKS = "Да ги избришам сите %1 блокчиња?"; Blockly.Msg.DELETE_BLOCK = "Избриши блок"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Избриши %1 блока"; Blockly.Msg.DISABLE_BLOCK = "Исклучи блок"; Blockly.Msg.DUPLICATE_BLOCK = "Ископирај"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; // untranslated Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ms.js b/msg/js/ms.js index 179202ea..65c1c638 100644 --- a/msg/js/ms.js +++ b/msg/js/ms.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Lakukan beberapa perintah apabi Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Lakukan beberapa perintah apabila nilainya benar (true)."; Blockly.Msg.DELETE_ALL_BLOCKS = "Hapuskan kesemua %1 blok?"; Blockly.Msg.DELETE_BLOCK = "Hapuskan Blok"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Hapuskan %1 Blok"; Blockly.Msg.DISABLE_BLOCK = "Matikan Blok"; Blockly.Msg.DUPLICATE_BLOCK = "Pendua"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 ke %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Hasilkan 'set %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Set pembolehubah ini supaya sama dengan input."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/nb.js b/msg/js/nb.js index a12e0511..a6e1c556 100644 --- a/msg/js/nb.js +++ b/msg/js/nb.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Så lenge et utsagn ikke stemme Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Så lenge et utsagn stemmer, utfør noen instruksjoner."; Blockly.Msg.DELETE_ALL_BLOCKS = "Slett alle %1 blokker?"; Blockly.Msg.DELETE_BLOCK = "Slett blokk"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Slett %1 blokker"; Blockly.Msg.DISABLE_BLOCK = "Deaktiver blokk"; Blockly.Msg.DUPLICATE_BLOCK = "duplikat"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "sett %1 til %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Opprett 'hent %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Setter verdien av denne variablen lik parameteren."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/nl.js b/msg/js/nl.js index 0f0d6757..6574d34d 100644 --- a/msg/js/nl.js +++ b/msg/js/nl.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Terwijl een waarde onwaar is de Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Terwijl een waarde waar is de volgende opdrachten uitvoeren."; Blockly.Msg.DELETE_ALL_BLOCKS = "Alle %1 blokken verwijderen?"; Blockly.Msg.DELETE_BLOCK = "Blok verwijderen"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 blokken verwijderen"; Blockly.Msg.DISABLE_BLOCK = "Blok uitschakelen"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicaat"; @@ -143,14 +145,14 @@ Blockly.Msg.LISTS_SORT_ORDER_ASCENDING = "oplopend"; Blockly.Msg.LISTS_SORT_ORDER_DESCENDING = "aflopend"; Blockly.Msg.LISTS_SORT_TITLE = "sorteer %1 %2 %3"; Blockly.Msg.LISTS_SORT_TOOLTIP = "Sorteer een kopie van een lijst."; -Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE = "alfabetisch, negeer zaak"; +Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE = "alfabetisch, negeer hoofd-/kleine letters"; Blockly.Msg.LISTS_SORT_TYPE_NUMERIC = "numerieke"; Blockly.Msg.LISTS_SORT_TYPE_TEXT = "in alfabetische volgorde"; Blockly.Msg.LISTS_SPLIT_HELPURL = "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists"; // untranslated Blockly.Msg.LISTS_SPLIT_LIST_FROM_TEXT = "lijst maken van tekst"; Blockly.Msg.LISTS_SPLIT_TEXT_FROM_LIST = "tekst maken van lijst"; Blockly.Msg.LISTS_SPLIT_TOOLTIP_JOIN = "Lijst van tekstdelen samenvoegen in één stuk tekst, waarbij de tekstdelen gescheiden zijn door een scheidingsteken."; -Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT = "Tekst splitsen in een tekst van tekst op basis van een scheidingsteken."; +Blockly.Msg.LISTS_SPLIT_TOOLTIP_SPLIT = "Tekst splitsen in een lijst van teksten op basis van een scheidingsteken."; Blockly.Msg.LISTS_SPLIT_WITH_DELIMITER = "met scheidingsteken"; Blockly.Msg.LOGIC_BOOLEAN_FALSE = "onwaar"; Blockly.Msg.LOGIC_BOOLEAN_HELPURL = "https://github.com/google/blockly/wiki/Logic#values"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "stel %1 in op %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Maak 'opvragen van %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; Blockly.Msg.VARIABLES_SET_TOOLTIP = "Verandert de waarde van de variabele naar de waarde van de invoer."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/oc.js b/msg/js/oc.js index c565ee8a..5e682d07 100644 --- a/msg/js/oc.js +++ b/msg/js/oc.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "While a value is false, then do Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "While a value is true, then do some statements."; // untranslated Blockly.Msg.DELETE_ALL_BLOCKS = "Suprimir totes los %1 blòts ?"; Blockly.Msg.DELETE_BLOCK = "Suprimir lo blòt"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Suprimir %1 blòts"; Blockly.Msg.DISABLE_BLOCK = "Desactivar lo blòt"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicar"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "fixar %1 a %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/pl.js b/msg/js/pl.js index 57c7643f..44da10c7 100644 --- a/msg/js/pl.js +++ b/msg/js/pl.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Gdy wartość jest nieprawdziwa Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Gdy wartość jest prawdziwa, wykonaj kilka instrukcji."; Blockly.Msg.DELETE_ALL_BLOCKS = "Usunąć wszystkie %1 bloki(ów)?"; Blockly.Msg.DELETE_BLOCK = "Usuń blok"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Usuń %1 bloki(ów)"; Blockly.Msg.DISABLE_BLOCK = "Wyłącz blok"; Blockly.Msg.DUPLICATE_BLOCK = "Duplikuj"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "przypisz %1 wartość %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Utwórz blok 'pobierz %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Nadaj tej zmiennej wartość."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/pms.js b/msg/js/pms.js index 400ddc10..09c40c65 100644 --- a/msg/js/pms.js +++ b/msg/js/pms.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Cand un valor a l'é fàuss, es Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Cand un valor a l'é ver, eseguì chèiche anstrussion."; Blockly.Msg.DELETE_ALL_BLOCKS = "Scancelé tuti ij %1 blòch?"; Blockly.Msg.DELETE_BLOCK = "Scancelé ël blòch"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Scancelé %1 blòch"; Blockly.Msg.DISABLE_BLOCK = "Disativé ël blòch"; Blockly.Msg.DUPLICATE_BLOCK = "Dupliché"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "fissé %1 a %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Creé 'oten-e %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Fissé costa variàbil ugual al valor d'imission."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/pt-br.js b/msg/js/pt-br.js index 0e12ea63..ba41b5ef 100644 --- a/msg/js/pt-br.js +++ b/msg/js/pt-br.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Enquanto um valor for falso, en Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Enquanto um valor for verdadeiro, então faça algumas instruções."; Blockly.Msg.DELETE_ALL_BLOCKS = "Apagar todos os %1 blocos?"; Blockly.Msg.DELETE_BLOCK = "Remover bloco"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Remover %1 blocos"; Blockly.Msg.DISABLE_BLOCK = "Desabilitar bloco"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicar"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "definir %1 para %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Criar \"obter %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Define esta variável para o valor da entrada."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/pt.js b/msg/js/pt.js index 4fb72083..3242d851 100644 --- a/msg/js/pt.js +++ b/msg/js/pt.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Enquanto um valor for falso, en Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Enquanto um valor for verdadeiro, então faça algumas instruções."; Blockly.Msg.DELETE_ALL_BLOCKS = "Apagar todos os %1 blocos?"; Blockly.Msg.DELETE_BLOCK = "Remover Bloco"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Remover %1 Blocos"; Blockly.Msg.DISABLE_BLOCK = "Desabilitar Bloco"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicar"; @@ -331,7 +333,7 @@ Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT = "no texto"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST = "primeira ocorrência do texto"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST = "última ocorrência do texto"; Blockly.Msg.TEXT_INDEXOF_TAIL = ""; // untranslated -Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "Retorna a posição da primeira/última ocorrência do primeiro texto no segundo texto. Retorna %1 se o texto não for encontrado."; +Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "Retorna a posição da primeira/última ocorrência do primeiro texto no segundo texto. Retorna %1 se o texto não for encontrado."; Blockly.Msg.TEXT_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; // untranslated Blockly.Msg.TEXT_ISEMPTY_TITLE = "%1 está vazio"; Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "Retorna verdadeiro se o texto fornecido estiver vazio."; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "definir %1 para %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Criar \"obter %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Define esta variável para o valor inserido."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ro.js b/msg/js/ro.js index 93ab7988..3626c8c8 100644 --- a/msg/js/ro.js +++ b/msg/js/ro.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "În timp ce o valoare este adev Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "În timp ce o valoare este adevărat, atunci face unele declaraţii."; Blockly.Msg.DELETE_ALL_BLOCKS = "Ștergi toate cele %1 (de) blocuri?"; Blockly.Msg.DELETE_BLOCK = "Șterge Bloc"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Ștergeți %1 Blocuri"; Blockly.Msg.DISABLE_BLOCK = "Dezactivaţi bloc"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicati"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "seteaza %1 la %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Crează 'get %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Setează această variabilă sa fie egală la intrare."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ru.js b/msg/js/ru.js index 8a9a53eb..c98ee19c 100644 --- a/msg/js/ru.js +++ b/msg/js/ru.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Пока значение ло Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Пока значение истинно, выполняет команды."; Blockly.Msg.DELETE_ALL_BLOCKS = "Удалить все блоки (%1)?"; Blockly.Msg.DELETE_BLOCK = "Удалить блок"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Удалить %1 блоков"; Blockly.Msg.DISABLE_BLOCK = "Отключить блок"; Blockly.Msg.DUPLICATE_BLOCK = "Скопировать"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "присвоить %1 = %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Создать вставку %1"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Присваивает переменной значение вставки."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/sc.js b/msg/js/sc.js index 13931f37..bb4b2ff6 100644 --- a/msg/js/sc.js +++ b/msg/js/sc.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Interis su valori est frassu, t Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Interis su valori est berus, tandu fai pariga de cumandus."; Blockly.Msg.DELETE_ALL_BLOCKS = "Scancellu su %1 de is brocus?"; Blockly.Msg.DELETE_BLOCK = "Fùlia Blocu"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Fulia %1 Blocus"; Blockly.Msg.DISABLE_BLOCK = "Disabìlita Blocu"; Blockly.Msg.DUPLICATE_BLOCK = "Dùplica"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "imposta %1 a %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Fait 'piga %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Imposta custa variabili uguali a s'input."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/sd.js b/msg/js/sd.js index 01f746cb..ff2d4fbc 100644 --- a/msg/js/sd.js +++ b/msg/js/sd.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "While a value is false, then do Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "While a value is true, then do some statements."; // untranslated Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "بلاڪ ڊاهيو"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "1٪ بلاڪ ڊاهيو"; Blockly.Msg.DISABLE_BLOCK = "بلاڪ کي غيرفعال بڻايو"; Blockly.Msg.DUPLICATE_BLOCK = "نقل"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; // untranslated Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/shn.js b/msg/js/shn.js index 9aac8d65..630a99ba 100644 --- a/msg/js/shn.js +++ b/msg/js/shn.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "ပေႃးဝႃႈ ၵႃႈ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "ပေႃးဝႃႈ ၵႃႈၶၼ် (ၼမ်ႉၵတ်ႉ) မၢၼ်ႇမႅၼ်ႈယဝ်ႉၸိုင် ႁဵတ်းၶေႃႈၵဵပ်းထွၼ်ၵမ်ႈၽွင်ႈ"; Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "မွတ်ႇပလွၵ်ႉ"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "မွတ်ႇပလွၵ်ႉ %1"; Blockly.Msg.DISABLE_BLOCK = "ဢမ်ႇၸၢင်ႈပလွၵ်ႉ"; Blockly.Msg.DUPLICATE_BLOCK = "ထုတ်ႇ"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; // untranslated Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; // untranslated Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/sk.js b/msg/js/sk.js index 7aaf9287..d929eef2 100644 --- a/msg/js/sk.js +++ b/msg/js/sk.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Kým je hodnota nepravdivá, vy Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Kým je hodnota pravdivá, vykonávaj príkazy."; Blockly.Msg.DELETE_ALL_BLOCKS = "Zmazať všetkých %1 dielcov?"; Blockly.Msg.DELETE_BLOCK = "Odstrániť blok"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Odstrániť %1 blokov"; Blockly.Msg.DISABLE_BLOCK = "Vypnúť blok"; Blockly.Msg.DUPLICATE_BLOCK = "Duplikovať"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "nastaviť %1 na %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Vytvoriť \"získať %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Nastaví túto premennú, aby sa rovnala vstupu."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/sl.js b/msg/js/sl.js index 78aacaa5..adf537da 100644 --- a/msg/js/sl.js +++ b/msg/js/sl.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Kocke se izvajajo dokler je vre Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Kocke se izvajajo dokler je vrednost resnična."; Blockly.Msg.DELETE_ALL_BLOCKS = "Izbrišem vseh %1 kock?"; Blockly.Msg.DELETE_BLOCK = "Izbriši kocko"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Izbriši kocke"; Blockly.Msg.DISABLE_BLOCK = "Onemogoči kocko"; Blockly.Msg.DUPLICATE_BLOCK = "Podvoji"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "nastavi %1 na %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Ustvari 'vrni %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; Blockly.Msg.VARIABLES_SET_TOOLTIP = "Nastavi, da je vrednost spremenljivke enaka vnosu."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/sq.js b/msg/js/sq.js index 448ed062..c0c6e65a 100644 --- a/msg/js/sq.js +++ b/msg/js/sq.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Përderisa një vlerë është Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Përderisa një vlerë është e saktë, atëherë ekzekuto disa fjali."; Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "Fshij bllokun"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Fshij %1 blloqe"; Blockly.Msg.DISABLE_BLOCK = "Çaktivizo bllokun"; Blockly.Msg.DUPLICATE_BLOCK = "Kopjo"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "vendos %1 ne %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Krijo 'merr %1"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Vendos kete variable te jete e barabarte me te dhenat ne hyrje."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/sr.js b/msg/js/sr.js index c1e3a42e..e53417c3 100644 --- a/msg/js/sr.js +++ b/msg/js/sr.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Док вредност ниј Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Док је вредност тачна, онда извршите неке наредбе."; Blockly.Msg.DELETE_ALL_BLOCKS = "Обрисати %1 блокова?"; Blockly.Msg.DELETE_BLOCK = "Обриши блок"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Обриши %1 блокова"; Blockly.Msg.DISABLE_BLOCK = "Онемогући блок"; Blockly.Msg.DUPLICATE_BLOCK = "Дуплирај"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "постави %1 у %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Направи „преузми %1“"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Поставља променљиву тако да буде једнака улазу."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/sv.js b/msg/js/sv.js index dff16de1..0e6b0034 100644 --- a/msg/js/sv.js +++ b/msg/js/sv.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Medan ett värde är falskt, ut Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Medan ett värde är sant, utför några kommandon."; Blockly.Msg.DELETE_ALL_BLOCKS = "Radera alla %1 block?"; Blockly.Msg.DELETE_BLOCK = "Radera block"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Radera %1 block"; Blockly.Msg.DISABLE_BLOCK = "Inaktivera block"; Blockly.Msg.DUPLICATE_BLOCK = "Duplicera"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "välj %1 till %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Skapa 'hämta %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Gör så att den här variabeln blir lika med inputen."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/ta.js b/msg/js/ta.js index dfb2d3a0..669bc0da 100644 --- a/msg/js/ta.js +++ b/msg/js/ta.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "மாறி பொய் ஆ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "மாறி உண்மை ஆக உள்ள வரை, கட்டளைகளை இயக்கு"; Blockly.Msg.DELETE_ALL_BLOCKS = "அனைத்து %1 நிரல் துண்டுகளையும் அழிக்கவா??"; Blockly.Msg.DELETE_BLOCK = "உறுப்பை நீக்கு"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 உறுப்பை நீக்கு"; Blockly.Msg.DISABLE_BLOCK = "உறுப்பை இயங்காது செய்"; Blockly.Msg.DUPLICATE_BLOCK = "மறுநகல்"; @@ -331,7 +333,7 @@ Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT = "உரையில்"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST = "உரையில் முதல் தோற்ற இடத்தை பின்கொடு"; Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST = "உரையில் கடைசி தோற்ற இடத்தை பின்கொடு"; Blockly.Msg.TEXT_INDEXOF_TAIL = ""; // untranslated -Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "Returns the index of the first/last occurrence of the first text in the second text. Returns %1 if text is not found."; // untranslated +Blockly.Msg.TEXT_INDEXOF_TOOLTIP = "இரண்டாவது உரையில் முதல் உரையின் முதல்/கடை இருக்கை குறிஎண்ணை பின்கொடு."; Blockly.Msg.TEXT_ISEMPTY_HELPURL = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; // untranslated Blockly.Msg.TEXT_ISEMPTY_TITLE = "%1 காலியானது"; Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "காலியானது என்றால் மெய் மதிப்பை பின்கொடு"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "நியமி %1 இந்த மாறியி Blockly.Msg.VARIABLES_SET_CREATE_GET = "'எடு %1' உருவாக்கு"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "மாறியின் மதிப்பாய் உள்ளீட்டு மதிப்பை வை."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/tcy.js b/msg/js/tcy.js index d8eda3dc..47fbb0cb 100644 --- a/msg/js/tcy.js +++ b/msg/js/tcy.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "ಈ ತಿರ್ತ್‍ದ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "ಈ ತಿರ್ತ್‌ದ ಸರಿ ಇತ್ತ್ಂಡಲಾ, ಬುಕ್ಕೊದ ಕೆಲವು ಹೇಳಿಕೆಲೆನ್ ಮಲ್ಪುಲ"; Blockly.Msg.DELETE_ALL_BLOCKS = "ಮಾತ %1 ನಿರ್ಬಂದೊಲೆನ್ ದೆತ್ತ್ ಪಾಡ್ಲೆ ?"; Blockly.Msg.DELETE_BLOCK = "ಮಾಜಯರ ತಡೆಯಾತ್ಂಡ್"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "ಮಾಜಯರ ಶೇಕಡಾ ೧ ತಡೆಯಾತ್ಂಡ್"; Blockly.Msg.DISABLE_BLOCK = "ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಾದ್ ತಡೆಪತ್ತುನೆ"; Blockly.Msg.DUPLICATE_BLOCK = "ನಕಲ್"; @@ -90,15 +92,15 @@ Blockly.Msg.LISTS_GET_INDEX_RANDOM = "ಗೊತ್ತು ಗುರಿದಾಂ Blockly.Msg.LISTS_GET_INDEX_REMOVE = "ದೆಪ್ಪುಲೆ"; Blockly.Msg.LISTS_GET_INDEX_TAIL = ""; // untranslated Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST = "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಪಿರಕೊರು."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಪಿರಕೊರು"; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM = "ನಿರ್ದಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಪಿರಕೊರು"; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST = "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಪಿರಕೊರು."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM = "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಪಿರಕೊರು."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST = "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM = "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು"; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM = "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು"; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST = "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM = "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST = "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು."; -Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM = "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪುಲೆ"; +Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM = "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪುಲೆ"; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST = "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು."; Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM = "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು."; Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END = "ಡ್ದ್ # ಅಕೇರಿಗ್"; @@ -110,8 +112,8 @@ Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END = "ಉಪ-ಪಟ್ಯೊನು ದ Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START = "ಉಪ-ಪಟ್ಯೊನು ದೆತೊನು#"; Blockly.Msg.LISTS_GET_SUBLIST_TAIL = ""; // untranslated Blockly.Msg.LISTS_GET_SUBLIST_TOOLTIP = "ಪಟ್ಯೊದ ನಿರ್ದಿಷ್ಟ ಬಾಗೊದ ಪ್ರತಿನ್ ಸ್ರಸ್ಟಿಸವುಂಡು."; -Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1 ಅಕೇರಿತ ಅಂಸ"; -Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 ಸುರುತ ಅಂಸ"; +Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP = "%1 ಅಕೇರಿತ ಅಂಸೊ"; +Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP = "%1 ಸುರುತ ಅಂಸೊ"; Blockly.Msg.LISTS_INDEX_OF_FIRST = "ದುಂಬು ಕರಿನ ಪಟ್ಯೊನು ನಾಡ್‍ಲೆ"; Blockly.Msg.LISTS_INDEX_OF_HELPURL = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; // untranslated Blockly.Msg.LISTS_INDEX_OF_LAST = "ಅಕೆರಿಗ್ ಕರಿನ ಪಟ್ಯೊನು ನಾಡ್‍ಲೆ"; @@ -131,11 +133,11 @@ Blockly.Msg.LISTS_SET_INDEX_INPUT_TO = "ಅಂಚ"; Blockly.Msg.LISTS_SET_INDEX_INSERT = "ಸೇರಲ"; Blockly.Msg.LISTS_SET_INDEX_SET = "ಮಾಲ್ಪು"; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST = "ಸುರುತ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಸೇರಲ."; -Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಸೇರಲ"; +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM = "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಸೇರಲ"; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST = "ಪಟ್ಟಿದ ಅಕೇರಿಗ್ ಈ ಅಂಸೊಲೆನ್ ಸೇರಲ."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM = "ಪಟ್ಟಿಗ್ ಗೊತ್ತುಗುರಿದಾಂತೆ ಅಂಸೊಲೆನ್ ಸೇರಲ."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST = "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು."; -Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM = "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು"; +Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM = "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು"; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST = "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು."; Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM = "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು."; Blockly.Msg.LISTS_SORT_HELPURL = "https://github.com/google/blockly/wiki/Lists#sorting-a-list"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "%1 ಡ್ದು %2 ಮಲ್ಪುಲೆ"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "'%1' ರಚನೆ ಮಲ್ಪುಲೆ"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "ಉಲಯಿ ಬರ್ಪುನವು ಸಮಪಾಲ್ ಇಪ್ಪುನಂಚ ವ್ಯತ್ಯಾಸೊ ಮಾಲ್ಪು"; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/th.js b/msg/js/th.js index da7676ab..5aedca7c 100644 --- a/msg/js/th.js +++ b/msg/js/th.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "ตราบเท่าที Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "ตราบเท่าที่ค่าเป็นจริง ก็จะทำบางคำสั่ง"; Blockly.Msg.DELETE_ALL_BLOCKS = "ต้องการลบบล็อกทั้ง %1 บล็อกหรือไม่"; Blockly.Msg.DELETE_BLOCK = "ลบบล็อก"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "ลบ %1 บล็อก"; Blockly.Msg.DISABLE_BLOCK = "ปิดใช้งานบล็อก"; Blockly.Msg.DUPLICATE_BLOCK = "ทำซ้ำ"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "กำหนด %1 จนถึง %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "สร้าง \"get %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "กำหนดให้ตัวแปรนี้เท่ากับการป้อนข้อมูล"; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/tl.js b/msg/js/tl.js index 3e43d2bb..0151c926 100644 --- a/msg/js/tl.js +++ b/msg/js/tl.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Habang ang value ay false, gaga Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Habang ang value ay true, gagawin ang ibang statements."; Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated Blockly.Msg.DELETE_BLOCK = "burahin ang bloke"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "burahin %1 ng bloke"; Blockly.Msg.DISABLE_BLOCK = "Ipangwalang bisa ang Block"; Blockly.Msg.DUPLICATE_BLOCK = "Kaparehas"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "set %1 to %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Create 'get %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/tlh.js b/msg/js/tlh.js index bfea2948..fee40f3e 100644 --- a/msg/js/tlh.js +++ b/msg/js/tlh.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "While a value is false, then do Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "While a value is true, then do some statements."; // untranslated Blockly.Msg.DELETE_ALL_BLOCKS = "Hoch %1 ngoghmey Qaw'?"; Blockly.Msg.DELETE_BLOCK = "ngogh Qaw'"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 ngoghmey Qaw'"; Blockly.Msg.DISABLE_BLOCK = "ngogh Qotlh"; Blockly.Msg.DUPLICATE_BLOCK = "velqa' chenmoH"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "choH %1 %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "chel 'Suq %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Sets this variable to be equal to the input."; // untranslated +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/tr.js b/msg/js/tr.js index 7f7cce23..5b63f2db 100644 --- a/msg/js/tr.js +++ b/msg/js/tr.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Bir değer yanlış olduğunda Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Bir değer doğru olduğunda bazı beyanlarda bulun."; Blockly.Msg.DELETE_ALL_BLOCKS = "Tüm %1 blok silinsin mi?"; Blockly.Msg.DELETE_BLOCK = "Bloğu Sil"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "%1 Blokları Sil"; Blockly.Msg.DISABLE_BLOCK = "Bloğu Devre Dışı Bırak"; Blockly.Msg.DUPLICATE_BLOCK = "Çoğalt"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "Atamak %1 e %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "'get %1' oluştur"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Bu değişkeni girilen değere eşitler."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/uk.js b/msg/js/uk.js index 02c99fa5..832d6af6 100644 --- a/msg/js/uk.js +++ b/msg/js/uk.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Поки значення хи Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Поки значення істинне, виконувати певні дії."; Blockly.Msg.DELETE_ALL_BLOCKS = "Вилучити всі блоки %1?"; Blockly.Msg.DELETE_BLOCK = "Видалити блок"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Видалити %1 блоків"; Blockly.Msg.DISABLE_BLOCK = "Вимкнути блок"; Blockly.Msg.DUPLICATE_BLOCK = "Дублювати"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "встановити %1 до %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Створити 'отримати %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Задає цю змінну рівною входу."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/vi.js b/msg/js/vi.js index 4f9f2f4a..6bd63860 100644 --- a/msg/js/vi.js +++ b/msg/js/vi.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Miễn là điều kiện còn Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Miễn là điều kiện còn đúng, thì thực hiện các lệnh."; Blockly.Msg.DELETE_ALL_BLOCKS = "Xóa hết %1 mảnh?"; Blockly.Msg.DELETE_BLOCK = "Xóa Mảnh Này"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "Xóa %1 Mảnh"; Blockly.Msg.DISABLE_BLOCK = "Ngưng Tác Dụng"; Blockly.Msg.DUPLICATE_BLOCK = "Tạo Bản Sao"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "cho %1 bằng %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "Tạo mảnh \"lấy %1\""; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "Đặt giá trị của biến này thành..."; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/zh-hans.js b/msg/js/zh-hans.js index cd76b734..eef1e371 100644 --- a/msg/js/zh-hans.js +++ b/msg/js/zh-hans.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "只要值为假,执行一些 Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "只要值为真,执行一些语句。"; Blockly.Msg.DELETE_ALL_BLOCKS = "删除所有%1块吗?"; Blockly.Msg.DELETE_BLOCK = "删除块"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "删除 %1 块"; Blockly.Msg.DISABLE_BLOCK = "禁用块"; Blockly.Msg.DUPLICATE_BLOCK = "复制"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "赋值 %1 到 %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "创建“获得%1”"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "设置此变量,以使它和输入值相等。"; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/js/zh-hant.js b/msg/js/zh-hant.js index 31e404a3..c5eae5c2 100644 --- a/msg/js/zh-hant.js +++ b/msg/js/zh-hant.js @@ -62,6 +62,8 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "當值為否時,執行一些 Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "當值為真時,執行一些語句"; Blockly.Msg.DELETE_ALL_BLOCKS = "刪除共 %1 塊積木?"; Blockly.Msg.DELETE_BLOCK = "刪除積木"; +Blockly.Msg.DELETE_VARIABLE = "Delete the '%1' variable"; // untranslated +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Delete %1 uses of the '%2' variable?"; // untranslated Blockly.Msg.DELETE_X_BLOCKS = "刪除 %1 塊積木"; Blockly.Msg.DISABLE_BLOCK = "停用積木"; Blockly.Msg.DUPLICATE_BLOCK = "複製"; @@ -366,6 +368,7 @@ Blockly.Msg.VARIABLES_SET = "賦值 %1 到 %2"; Blockly.Msg.VARIABLES_SET_CREATE_GET = "建立 '取得 %1'"; Blockly.Msg.VARIABLES_SET_HELPURL = "https://github.com/google/blockly/wiki/Variables#set"; // untranslated Blockly.Msg.VARIABLES_SET_TOOLTIP = "設定此變數,好和輸入值相等。"; +Blockly.Msg.VARIABLE_ALREADY_EXISTS = "A variable named '%1' already exists."; // untranslated Blockly.Msg.PROCEDURES_DEFRETURN_TITLE = Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE; Blockly.Msg.CONTROLS_IF_IF_TITLE_IF = Blockly.Msg.CONTROLS_IF_MSG_IF; Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO = Blockly.Msg.CONTROLS_REPEAT_INPUT_DO; diff --git a/msg/json/cs.json b/msg/json/cs.json index 3d0827e5..e0cb6a41 100644 --- a/msg/json/cs.json +++ b/msg/json/cs.json @@ -195,7 +195,7 @@ "TEXT_LENGTH_TOOLTIP": "Vrátí počet písmen (včetně mezer) v zadaném textu.", "TEXT_ISEMPTY_TITLE": "%1 je prázdný", "TEXT_ISEMPTY_TOOLTIP": "Vrátí pravda pokud je zadaný text prázdný.", - "TEXT_INDEXOF_TOOLTIP": "Vrátí index prvního/posledního výskytu prvního textu v druhém textu. Pokud text není nalezen, vrátí hodnotu %1.", + "TEXT_INDEXOF_TOOLTIP": "Vrátí index prvního/posledního výskytu prvního textu v druhém textu. Pokud text není nalezen, vypíše %1.", "TEXT_INDEXOF_INPUT_INTEXT": "v textu", "TEXT_INDEXOF_OPERATOR_FIRST": "najít první výskyt textu", "TEXT_INDEXOF_OPERATOR_LAST": "najít poslední výskyt textu", @@ -255,11 +255,11 @@ "LISTS_GET_INDEX_RANDOM": "náhodné", "LISTS_INDEX_FROM_START_TOOLTIP": "%1 je první položka.", "LISTS_INDEX_FROM_END_TOOLTIP": "%1 je poslední položka.", - "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "Vrátí položku z určené pozice v seznamu.", + "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "Získá položku z určené pozice v seznamu.", "LISTS_GET_INDEX_TOOLTIP_GET_FIRST": "Vrátí první položku v seznamu.", "LISTS_GET_INDEX_TOOLTIP_GET_LAST": "Vrátí poslední položku v seznamu.", "LISTS_GET_INDEX_TOOLTIP_GET_RANDOM": "Vrátí náhodnou položku ze seznamu.", - "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM": "Odstraní a vrátí položku z určené pozice v seznamu.", + "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM": "Odstraní a získá položku z určené pozice v seznamu.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST": "Odstraní a vrátí první položku v seznamu.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST": "Odstraní a vrátí poslední položku v seznamu.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM": "Odstraní a vrátí náhodnou položku v seznamu.", diff --git a/msg/json/diq.json b/msg/json/diq.json new file mode 100644 index 00000000..409cd9c2 --- /dev/null +++ b/msg/json/diq.json @@ -0,0 +1,137 @@ +{ + "@metadata": { + "authors": [ + "Kumkumuk", + "Marmase", + "Mirzali" + ] + }, + "VARIABLES_DEFAULT_NAME": "unsur", + "TODAY": "Ewro", + "DUPLICATE_BLOCK": "Zewnc", + "ADD_COMMENT": "Tefsir cı ke", + "REMOVE_COMMENT": "Tefsiri Wedare", + "EXTERNAL_INPUTS": "Cıkewtışê xarıciy", + "INLINE_INPUTS": "Cıkerdışê xomiyani", + "DELETE_BLOCK": "Bloki bestere", + "DELETE_X_BLOCKS": "%1 çengan bestern", + "DELETE_ALL_BLOCKS": "Wa %1 çengey heme besteri yè?", + "CLEAN_UP": "Çengan pak ke", + "COLLAPSE_BLOCK": "Çengi teng ke", + "COLLAPSE_ALL": "Çengi teng ke", + "EXPAND_BLOCK": "Çengi hera ke", + "EXPAND_ALL": "Çengan hera ke", + "DISABLE_BLOCK": "Çengi devre ra vec", + "ENABLE_BLOCK": "Çengi aktiv ke", + "HELP": "Peşti", + "UNDO": "Peyser biya", + "REDO": "Anewe ke", + "CHANGE_VALUE_TITLE": "Erci bıvırne:", + "NEW_VARIABLE": "Vuriyayeyo newe...", + "NEW_VARIABLE_TITLE": "Namey vuriyayeyê newi:", + "RENAME_VARIABLE": "Vuriyayey fına name ke...", + "COLOUR_PICKER_HELPURL": "https://diq.wikipedia.org/wiki/Reng", + "COLOUR_RANDOM_TITLE": "rengo rastameye", + "COLOUR_RGB_TITLE": "komponentên rengan", + "COLOUR_RGB_RED": "sur", + "COLOUR_RGB_GREEN": "kıho", + "COLOUR_RGB_BLUE": "mawi", + "COLOUR_BLEND_TITLE": "tewde", + "COLOUR_BLEND_COLOUR1": "reng 1", + "COLOUR_BLEND_COLOUR2": "reng 2", + "COLOUR_BLEND_RATIO": "nisbet", + "CONTROLS_REPEAT_HELPURL": "https://en.wikipedia.org/wiki/For_loop", + "CONTROLS_REPEAT_INPUT_DO": "bık", + "CONTROLS_WHILEUNTIL_OPERATOR_WHILE": "Tekrar kerdış de", + "CONTROLS_WHILEUNTIL_OPERATOR_UNTIL": "hend tekrar ke", + "CONTROLS_WHILEUNTIL_TOOLTIP_WHILE": "Yew erc raşto se yu beyanat bıd.", + "CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL": "Yew erc xırabo se tay beyanati bıd", + "CONTROLS_IF_MSG_IF": "se", + "CONTROLS_IF_MSG_ELSEIF": "niyose", + "CONTROLS_IF_MSG_ELSE": "çıniyose", + "LOGIC_OPERATION_AND": "û", + "LOGIC_OPERATION_OR": "ya zi", + "LOGIC_NEGATE_TITLE": "%1 niyo", + "LOGIC_BOOLEAN_TRUE": "raşt", + "LOGIC_BOOLEAN_FALSE": "ğelet", + "LOGIC_NULL": "veng", + "LOGIC_NULL_TOOLTIP": "Veng çarneno ra.", + "LOGIC_TERNARY_CONDITION": "test", + "LOGIC_TERNARY_IF_TRUE": "eke raşto", + "LOGIC_TERNARY_IF_FALSE": "eke ğeleto", + "MATH_NUMBER_HELPURL": "https://diq.wikipedia.org/wiki/Numre", + "MATH_NUMBER_TOOLTIP": "Yew numre.", + "MATH_ARITHMETIC_HELPURL": "https://en.wikipedia.org/wiki/Aritmetik", + "MATH_SINGLE_OP_ROOT": "karekok", + "MATH_SINGLE_OP_ABSOLUTE": "mutlaq", + "MATH_IS_EVEN": "zewnco", + "MATH_IS_ODD": "kıto", + "MATH_IS_PRIME": "bıngehên", + "MATH_IS_WHOLE": "tamo", + "MATH_IS_POSITIVE": "pozitifo", + "MATH_IS_NEGATIVE": "negatifo", + "MATH_CHANGE_TITLE": "%2, keno %1 vurneno", + "MATH_ROUND_HELPURL": "https://en.wikipedia.org/wiki/Rounding", + "MATH_ROUND_TOOLTIP": "Yu amorer loğê cêri yana cori ke", + "MATH_ROUND_OPERATOR_ROUND": "gılor ke", + "MATH_ROUND_OPERATOR_ROUNDUP": "Loğê cori ke", + "MATH_ROUND_OPERATOR_ROUNDDOWN": "Loğê cêri ke", + "MATH_ONLIST_OPERATOR_SUM": "koma liste", + "MATH_ONLIST_OPERATOR_MIN": "Tewr qıcê lista", + "MATH_ONLIST_OPERATOR_MAX": "Tewr gırdê lista", + "MATH_ONLIST_OPERATOR_AVERAGE": "Averacê lista", + "MATH_ONLIST_OPERATOR_MEDIAN": "Wertey lista", + "MATH_ONLIST_OPERATOR_MODE": "listey modi", + "MATH_RANDOM_FLOAT_TITLE_RANDOM": "Raştamaye nimande amor", + "TEXT_CREATE_JOIN_TITLE_JOIN": "gıre de", + "TEXT_APPEND_TO": "rê", + "TEXT_ISEMPTY_TITLE": "%1 vengo", + "TEXT_INDEXOF_INPUT_INTEXT": "metın de", + "TEXT_CHARAT_INPUT_INTEXT": "metın de", + "TEXT_CHARAT_FROM_START": "Herfa # bıgi", + "TEXT_CHARAT_FROM_END": "# ra tepya herfan bıgi", + "TEXT_CHARAT_FIRST": "Herfa sıfti bıgi", + "TEXT_CHARAT_LAST": "Herfa peyên bıgi", + "TEXT_CHARAT_RANDOM": "Raştamaye yu herf bıgi", + "TEXT_CHARAT_TOOLTIP": "Şınasnaye pozisyon de yu herfer çerğ keno", + "TEXT_GET_SUBSTRING_TOOLTIP": "Tay letey metini çerğ keno", + "TEXT_GET_SUBSTRING_INPUT_IN_TEXT": "metın de", + "TEXT_GET_SUBSTRING_START_FROM_START": "# ra substring gêno", + "LISTS_CREATE_EMPTY_TITLE": "lista venge vıraze", + "LISTS_CREATE_WITH_CONTAINER_TITLE_ADD": "liste", + "LISTS_ISEMPTY_TITLE": "%1 vengo", + "LISTS_INLIST": "lista de", + "LISTS_GET_INDEX_GET": "bıgê", + "LISTS_GET_INDEX_GET_REMOVE": "Bıgi u wedarne", + "LISTS_GET_INDEX_REMOVE": "wedare", + "LISTS_GET_INDEX_FROM_END": "# peynira", + "LISTS_GET_INDEX_FIRST": "verên", + "LISTS_GET_INDEX_LAST": "peyên", + "LISTS_GET_INDEX_RANDOM": "raştameye", + "LISTS_INDEX_FROM_START_TOOLTIP": "%1 objeyo sıfteyên o", + "LISTS_INDEX_FROM_END_TOOLTIP": "%1 objeyo peyên o", + "LISTS_SET_INDEX_SET": "ca ke", + "LISTS_SET_INDEX_INPUT_TO": "zey", + "LISTS_GET_SUBLIST_END_FROM_START": "#'ya", + "LISTS_GET_SUBLIST_END_FROM_END": "Peyni # ra hetana", + "LISTS_GET_SUBLIST_END_LAST": "Hetana pey", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", + "LISTS_SORT_TITLE": "Kılm %1 %2 %3", + "LISTS_SORT_ORDER_ASCENDING": "zeydıyen", + "LISTS_SORT_ORDER_DESCENDING": "Kemeyen", + "LISTS_SORT_TYPE_NUMERIC": "Amoriyal", + "LISTS_SORT_TYPE_TEXT": "Alfabetik", + "VARIABLES_SET_CREATE_GET": "'get %1' vıraz", + "PROCEDURES_DEFNORETURN_TITLE": "rê", + "PROCEDURES_DEFNORETURN_PROCEDURE": "Çıyê bık", + "PROCEDURES_BEFORE_PARAMS": "ebe:", + "PROCEDURES_CALL_BEFORE_PARAMS": "ebe:", + "PROCEDURES_DEFNORETURN_TOOLTIP": "Yew fonksiyono çap nêdate vırazeno", + "PROCEDURES_DEFNORETURN_COMMENT": "Nê fonksiyoni beyan ke...", + "PROCEDURES_DEFRETURN_RETURN": "peyser biya", + "PROCEDURES_DEFRETURN_TOOLTIP": "Yew fonksiyono çap daye vırazeno", + "PROCEDURES_ALLOW_STATEMENTS": "Çıyan rê mısafe bıd", + "PROCEDURES_MUTATORCONTAINER_TITLE": "cıkewtışi", + "PROCEDURES_MUTATORARG_TITLE": "nameyê cıkewtışi:", + "PROCEDURES_CREATE_DO": "'%1' vıraze" +} diff --git a/msg/json/el.json b/msg/json/el.json index c6551121..191bfc17 100644 --- a/msg/json/el.json +++ b/msg/json/el.json @@ -8,7 +8,8 @@ "Sfyrakis", "Glavkos", "Gchr", - "아라" + "아라", + "Geraki" ] }, "VARIABLES_DEFAULT_NAME": "αντικείμενο", @@ -291,6 +292,7 @@ "LISTS_GET_SUBLIST_END_FROM_END": "έως # από το τέλος", "LISTS_GET_SUBLIST_END_LAST": "έως το τελευταίο", "LISTS_GET_SUBLIST_TOOLTIP": "Δημιουργεί ένα αντίγραφο του καθορισμένου τμήματος μιας λίστας.", + "LISTS_SORT_HELPURL": "https://github.com/google/blockly/wiki/Lists#sorting-a-list", "LISTS_SPLIT_LIST_FROM_TEXT": "κάνετε λίστα από το κείμενο", "LISTS_SPLIT_TEXT_FROM_LIST": "κάνετε κείμενο από τη λίστα", "LISTS_SPLIT_WITH_DELIMITER": "με διαχωριστικό", diff --git a/msg/json/en.json b/msg/json/en.json index 0e678864..d2c052a6 100644 --- a/msg/json/en.json +++ b/msg/json/en.json @@ -1,7 +1,7 @@ { "@metadata": { "author": "Ellen Spertus ", - "lastupdated": "2016-05-06 10:42:01.474947", + "lastupdated": "2016-08-17 16:08:04.888607", "locale": "en", "messagedocumentation" : "qqq" }, @@ -26,10 +26,13 @@ "UNDO": "Undo", "REDO": "Redo", "CHANGE_VALUE_TITLE": "Change value:", - "NEW_VARIABLE": "New variable...", - "NEW_VARIABLE_TITLE": "New variable name:", "RENAME_VARIABLE": "Rename variable...", "RENAME_VARIABLE_TITLE": "Rename all '%1' variables to:", + "NEW_VARIABLE": "Create variable...", + "NEW_VARIABLE_TITLE": "New variable name:", + "VARIABLE_ALREADY_EXISTS": "A variable named '%1' already exists.", + "DELETE_VARIABLE_CONFIRMATION": "Delete %1 uses of the '%2' variable?", + "DELETE_VARIABLE": "Delete the '%1' variable", "COLOUR_PICKER_HELPURL": "https://en.wikipedia.org/wiki/Color", "COLOUR_PICKER_TOOLTIP": "Choose a colour from the palette.", "COLOUR_RANDOM_HELPURL": "http://randomcolour.com", diff --git a/msg/json/es.json b/msg/json/es.json index d976c56f..15266374 100644 --- a/msg/json/es.json +++ b/msg/json/es.json @@ -244,15 +244,15 @@ "LISTS_GET_INDEX_RANDOM": "aleatorio", "LISTS_INDEX_FROM_START_TOOLTIP": "%1 es el primer elemento.", "LISTS_INDEX_FROM_END_TOOLTIP": "%1 es el último elemento.", - "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "Devuelve el elemento en la posición especificada en la lista.", + "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "Devuelve el elemento en la posición especificada en una lista.", "LISTS_GET_INDEX_TOOLTIP_GET_FIRST": "Devuelve el primer elemento de una lista.", "LISTS_GET_INDEX_TOOLTIP_GET_LAST": "Devuelve el último elemento de una lista.", "LISTS_GET_INDEX_TOOLTIP_GET_RANDOM": "Devuelve un elemento aleatorio en una lista.", - "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM": "Elimina y devuelve el elemento en la posición especificada en la lista.", + "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM": "Elimina y devuelve el elemento en la posición especificada en una lista.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST": "Elimina y devuelve el primer elemento de una lista.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST": "Elimina y devuelve el último elemento de una lista.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM": "Elimina y devuelve un elemento aleatorio en una lista.", - "LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM": "Elimina el elemento en la posición especificada en la lista.", + "LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM": "Elimina el elemento en la posición especificada en una lista.", "LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST": "Elimina el primer elemento de una lista.", "LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST": "Elimina el último elemento de una lista.", "LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM": "Elimina un elemento aleatorio en una lista.", @@ -263,7 +263,7 @@ "LISTS_SET_INDEX_TOOLTIP_SET_FIRST": "Establece el primer elemento de una lista.", "LISTS_SET_INDEX_TOOLTIP_SET_LAST": "Establece el último elemento de una lista.", "LISTS_SET_INDEX_TOOLTIP_SET_RANDOM": "Establece un elemento aleatorio en una lista.", - "LISTS_SET_INDEX_TOOLTIP_INSERT_FROM": "Inserta el elemento en la posición especificada en la lista.", + "LISTS_SET_INDEX_TOOLTIP_INSERT_FROM": "Inserta el elemento en la posición especificada en una lista.", "LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST": "Inserta el elemento al inicio de una lista.", "LISTS_SET_INDEX_TOOLTIP_INSERT_LAST": "Añade el elemento al final de una lista.", "LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM": "Inserta el elemento aleatoriamente en una lista.", diff --git a/msg/json/he.json b/msg/json/he.json index fe2705e0..7eb0aaeb 100644 --- a/msg/json/he.json +++ b/msg/json/he.json @@ -168,6 +168,7 @@ "TEXT_CREATE_JOIN_TITLE_JOIN": "צירוף", "TEXT_APPEND_TO": "אל", "TEXT_APPEND_APPENDTEXT": "הוספת טקסט", + "TEXT_INDEXOF_TOOLTIP": "מחזירה את האינדקס של המופע הראשון/האחרון בטקסט הראשון לתוך הטקסט השני. מחזירה %1 אם הטקסט אינו נמצא.", "TEXT_GET_SUBSTRING_END_FROM_START": "לאות #", "TEXT_GET_SUBSTRING_END_FROM_END": "לאות # מהסוף", "TEXT_CHANGECASE_OPERATOR_UPPERCASE": "לאותיות גדולות (עבור טקסט באנגלית)", @@ -199,7 +200,7 @@ "LISTS_INLIST": "ברשימה", "LISTS_INDEX_OF_FIRST": "מחזירה את המיקום הראשון של פריט ברשימה", "LISTS_INDEX_OF_LAST": "מחזירה את המיקום האחרון של פריט ברשימה", - "LISTS_INDEX_OF_TOOLTIP": "מחזירה את האינדקס של המופע ראשון/אחרון של הפריט ברשימה. מחזירה %1 אם הפריט אינו נמצא.", + "LISTS_INDEX_OF_TOOLTIP": "מחזירה את האינדקס של המופע הראשון/האחרון של הפריט ברשימה. מחזירה %1 אם הפריט אינו נמצא.", "LISTS_GET_INDEX_GET": "לקבל", "LISTS_GET_INDEX_GET_REMOVE": "קבל ומחק", "LISTS_GET_INDEX_REMOVE": "הסרה", diff --git a/msg/json/hu.json b/msg/json/hu.json index be22129d..2ef04a97 100644 --- a/msg/json/hu.json +++ b/msg/json/hu.json @@ -238,7 +238,7 @@ "LISTS_INLIST": "A(z)", "LISTS_INDEX_OF_FIRST": "listában első előfordulásaː", "LISTS_INDEX_OF_LAST": "listában utolsó előfordulásaː", - "LISTS_INDEX_OF_TOOLTIP": "A megadott elem első vagy utolsó előfordulásával tér vissza. %1-val tér vissza, ha nem talál ilyen elemet.", + "LISTS_INDEX_OF_TOOLTIP": "A megadott elem első vagy utolsó előfordulásával tér vissza. Ha nem talál ilyen elemet, akkor %1 a visszatérési érték.", "LISTS_GET_INDEX_GET": "listából értéke", "LISTS_GET_INDEX_GET_REMOVE": "listából kivétele", "LISTS_GET_INDEX_REMOVE": "listából törlése", diff --git a/msg/json/ia.json b/msg/json/ia.json index 8ae5b153..f38d01ec 100644 --- a/msg/json/ia.json +++ b/msg/json/ia.json @@ -52,7 +52,7 @@ "CONTROLS_WHILEUNTIL_OPERATOR_UNTIL": "repeter usque a", "CONTROLS_WHILEUNTIL_TOOLTIP_WHILE": "Durante que un valor es ver, exequer certe instructiones.", "CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL": "Durante que un valor es false, exequer certe instructiones.", - "CONTROLS_FOR_TOOLTIP": "Mitter in le variabile \"%1\" le valores ab le numero initial usque al numero final, con passos secundo le intervallo specificate, e exequer le blocos specificate.", + "CONTROLS_FOR_TOOLTIP": "Mitter in le variabile '%1' le valores ab le numero initial usque al numero final, con passos secundo le intervallo specificate, e exequer le blocos specificate.", "CONTROLS_FOR_TITLE": "contar con %1 de %2 a %3 per %4", "CONTROLS_FOREACH_TITLE": "pro cata elemento %1 in lista %2", "CONTROLS_FOREACH_TOOLTIP": "Pro cata elemento in un lista, mitter lo in le variabile '%1' e exequer certe instructiones.", @@ -218,7 +218,7 @@ "LISTS_INLIST": "in lista", "LISTS_INDEX_OF_FIRST": "cercar le prime occurrentia del elemento", "LISTS_INDEX_OF_LAST": "cercar le ultime occurrentia del elemento", - "LISTS_INDEX_OF_TOOLTIP": "Retorna le indice del prime/ultime occurrentia del elemento in le lista. Retorna %1 si le texto non es trovate.", + "LISTS_INDEX_OF_TOOLTIP": "Retorna le indice del prime/ultime occurrentia del elemento in le lista. Retorna %1 si le elemento non es trovate.", "LISTS_GET_INDEX_GET": "prender", "LISTS_GET_INDEX_GET_REMOVE": "prender e remover", "LISTS_GET_INDEX_REMOVE": "remover", @@ -258,9 +258,18 @@ "LISTS_GET_SUBLIST_END_FROM_END": "usque al № ab fin", "LISTS_GET_SUBLIST_END_LAST": "usque al ultime", "LISTS_GET_SUBLIST_TOOLTIP": "Crea un copia del parte specificate de un lista.", + "LISTS_SORT_TITLE": "ordinamento %1 %2 %3", + "LISTS_SORT_TOOLTIP": "Ordinar un copia de un lista.", + "LISTS_SORT_ORDER_ASCENDING": "ascendente", + "LISTS_SORT_ORDER_DESCENDING": "descendente", + "LISTS_SORT_TYPE_NUMERIC": "numeric", + "LISTS_SORT_TYPE_TEXT": "alphabetic", + "LISTS_SORT_TYPE_IGNORECASE": "alphabetic, ignorar majuscula/minuscula", "LISTS_SPLIT_LIST_FROM_TEXT": "Crear un lista per un texto", "LISTS_SPLIT_TEXT_FROM_LIST": "crear un texto per un lista", "LISTS_SPLIT_WITH_DELIMITER": "con delimitator", + "LISTS_SPLIT_TOOLTIP_SPLIT": "Divider texto in un lista de textos, separante lo a cata delimitator.", + "LISTS_SPLIT_TOOLTIP_JOIN": "Unir un lista de textos, separate per un delimitator, in un sol texto.", "VARIABLES_GET_TOOLTIP": "Retorna le valor de iste variabile.", "VARIABLES_GET_CREATE_SET": "Crea 'mitter %1'", "VARIABLES_SET": "mitter %1 a %2", @@ -271,6 +280,7 @@ "PROCEDURES_BEFORE_PARAMS": "con:", "PROCEDURES_CALL_BEFORE_PARAMS": "con:", "PROCEDURES_DEFNORETURN_TOOLTIP": "Crea un function que non retorna un valor.", + "PROCEDURES_DEFNORETURN_COMMENT": "Describe iste function...", "PROCEDURES_DEFRETURN_RETURN": "retornar", "PROCEDURES_DEFRETURN_TOOLTIP": "Crea un function que retorna un valor.", "PROCEDURES_ALLOW_STATEMENTS": "permitter declarationes", diff --git a/msg/json/ko.json b/msg/json/ko.json index c0de5c00..ea7dcefa 100644 --- a/msg/json/ko.json +++ b/msg/json/ko.json @@ -214,7 +214,7 @@ "TEXT_ISEMPTY_TITLE": "%1이 비어 있습니다", "TEXT_ISEMPTY_TOOLTIP": "입력된 문장이, 빈 문장(\"\")이면 참(true) 값을 돌려줍니다.", "TEXT_INDEXOF_HELPURL": "https://github.com/google/blockly/wiki/Text#finding-text", - "TEXT_INDEXOF_TOOLTIP": "어떤 문장이 가장 처음 나타난 위치 또는, 가장 마지막으로 나타난 위치를 찾아 돌려줍니다. 찾는 문장이 없는 경우는 %1 값을 돌려줌.", + "TEXT_INDEXOF_TOOLTIP": "두 번째 텍스트에서 첫 번째 텍스트가 처음 또는 마지막으로 발생한 색인 위치를 반환합니다. 텍스트가 없으면 %1을 반환합니다.", "TEXT_INDEXOF_INPUT_INTEXT": "문장", "TEXT_INDEXOF_OPERATOR_FIRST": "에서 다음 문장이 처음으로 나타난 위치 찾기 :", "TEXT_INDEXOF_OPERATOR_LAST": "에서 다음 문장이 마지막으로 나타난 위치 찾기 :", @@ -278,7 +278,7 @@ "LISTS_INDEX_OF_HELPURL": "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list", "LISTS_INDEX_OF_FIRST": "처음으로 나타난 위치", "LISTS_INDEX_OF_LAST": "마지막으로 나타난 위치", - "LISTS_INDEX_OF_TOOLTIP": "아이템이 나타난 처음 또는 마지막 위치를 찾아 돌려줍니다. 아이템이 없으면 %1이 반환됩니다.", + "LISTS_INDEX_OF_TOOLTIP": "목록에서 항목이 처음 또는 마지막으로 발생한 색인 위치를 반환합니다. 항목이 없으면 %1을 반환합니다.", "LISTS_GET_INDEX_GET": "가져오기", "LISTS_GET_INDEX_GET_REMOVE": "잘라 내기", "LISTS_GET_INDEX_REMOVE": "삭제", @@ -289,7 +289,7 @@ "LISTS_GET_INDEX_RANDOM": "임의로", "LISTS_GET_INDEX_TAIL": "", "LISTS_INDEX_FROM_START_TOOLTIP": "%1은 첫 번째 항목입니다.", - "LISTS_INDEX_FROM_END_TOOLTIP": "%1은 마지막 항목입니다.", + "LISTS_INDEX_FROM_END_TOOLTIP": "%1은(는) 마지막 항목입니다.", "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "목록에서 특정 위치의 항목을 반환합니다.", "LISTS_GET_INDEX_TOOLTIP_GET_FIRST": "첫 번째 아이템을 찾아 돌려줍니다.", "LISTS_GET_INDEX_TOOLTIP_GET_LAST": "마지막 아이템을 찾아 돌려줍니다.", @@ -310,7 +310,7 @@ "LISTS_SET_INDEX_TOOLTIP_SET_FIRST": "첫 번째 위치의 아이템으로 설정합니다.", "LISTS_SET_INDEX_TOOLTIP_SET_LAST": "마지막 아이템으로 설정합니다.", "LISTS_SET_INDEX_TOOLTIP_SET_RANDOM": "목록에서 임의 위치의 아이템을 설정합니다.", - "LISTS_SET_INDEX_TOOLTIP_INSERT_FROM": "아이템을 리스트의 특정 위치에 삽입합니다.", + "LISTS_SET_INDEX_TOOLTIP_INSERT_FROM": "목록의 특정 위치에 항목을 삽입합니다.", "LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST": "항목을 목록의 처음 위치에 삽입합니다.", "LISTS_SET_INDEX_TOOLTIP_INSERT_LAST": "리스트의 마지막에 아이템을 추가합니다.", "LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM": "목록에서 임의 위치에 아이템을 삽입합니다.", diff --git a/msg/json/lb.json b/msg/json/lb.json index ff82c489..129fba60 100644 --- a/msg/json/lb.json +++ b/msg/json/lb.json @@ -106,6 +106,8 @@ "LISTS_GET_INDEX_FIRST": "éischt", "LISTS_GET_INDEX_LAST": "lescht", "LISTS_GET_INDEX_RANDOM": "Zoufall", + "LISTS_INDEX_FROM_START_TOOLTIP": "%1 ass dat éischt Element.", + "LISTS_INDEX_FROM_END_TOOLTIP": "%1 ass dat éischt Element.", "LISTS_GET_INDEX_TOOLTIP_GET_RANDOM": "Schéckt en zoufällegt Element aus enger Lëscht zréck.", "LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST": "Hëlt dat lescht Element aus enger Lëscht eraus.", "LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM": "Hëlt en zoufällegt Element aus enger Lëscht eraus.", diff --git a/msg/json/lt.json b/msg/json/lt.json index 58ddf6f4..f8efcb73 100644 --- a/msg/json/lt.json +++ b/msg/json/lt.json @@ -24,6 +24,8 @@ "DISABLE_BLOCK": "Išjungti bloką", "ENABLE_BLOCK": "Įjungti bloką", "HELP": "Pagalba", + "UNDO": "Anuliuoti", + "REDO": "Atkurti", "CHANGE_VALUE_TITLE": "Keisti reikšmę:", "NEW_VARIABLE": "Naujas kintamasis...", "NEW_VARIABLE_TITLE": "Naujo kintamojo pavadinimas:", @@ -70,6 +72,7 @@ "CONTROLS_IF_IF_TOOLTIP": "Galite pridėt/pašalinti/pertvarkyti sąlygų \"šakas\".", "CONTROLS_IF_ELSEIF_TOOLTIP": "Pridėti sąlygą „jei“ blokui.", "CONTROLS_IF_ELSE_TOOLTIP": "Pridėti veiksmų vykdymo variantą/\"šaką\", kai netenkinama nė viena sąlyga.", + "LOGIC_COMPARE_HELPURL": "https://en.wikipedia.org/wiki/Inequality_(mathematics)", "LOGIC_COMPARE_TOOLTIP_EQ": "Tenkinama, jei abu reiškiniai lygūs.", "LOGIC_OPERATION_TOOLTIP_AND": "Bus teisinga, kai abi sąlygos bus tenkinamos.", "LOGIC_OPERATION_AND": "ir", @@ -107,6 +110,7 @@ "MATH_TRIG_TOOLTIP_ASIN": "Grąžinti skaičiaus arksinusą.", "MATH_TRIG_TOOLTIP_ACOS": "Grąžinti skaičiaus arkkosinusą.", "MATH_TRIG_TOOLTIP_ATAN": "Grąžinti skaičiaus arktangentą.", + "MATH_CONSTANT_HELPURL": "https://lt.wikipedia.org/wiki/Matematin%C4%97_konstanta", "MATH_IS_EVEN": "yra lyginis", "MATH_IS_ODD": "yra nelyginis", "MATH_IS_PRIME": "yra pirminis", @@ -115,8 +119,11 @@ "MATH_IS_NEGATIVE": "yra neigiamas", "MATH_IS_DIVISIBLE_BY": "yra dalus iš", "MATH_IS_TOOLTIP": "Patikrina skaičiaus savybę: (ne)lyginis/pirminis/sveikasis/teigiamas/neigiamas/dalus iš x.", + "MATH_CHANGE_HELPURL": "https://en.wikipedia.org/wiki/Programming_idiom#Incrementing_a_counter", "MATH_CHANGE_TITLE": "padidink %1 (emptypage) %2", "MATH_CHANGE_TOOLTIP": "Prideda skaičių prie kintamojo '%1'. Kai skaičius neigiamas - gaunasi atimtis.", + "MATH_ROUND_HELPURL": "https://lt.wikipedia.org/wiki/Apvalinimas", + "MATH_ROUND_TOOLTIP": "Suapvalinti skaičių į žemesnę ar aukštesnę reikšmę.", "MATH_ROUND_OPERATOR_ROUND": "apvalink", "MATH_ROUND_OPERATOR_ROUNDUP": "apvalink aukštyn", "MATH_ROUND_OPERATOR_ROUNDDOWN": "apvalink žemyn", @@ -132,9 +139,12 @@ "MATH_ONLIST_OPERATOR_STD_DEV": "standartinis nuokrypis sąraše", "MATH_ONLIST_OPERATOR_RANDOM": "atsitiktinis elementas iš sąrašo", "MATH_ONLIST_TOOLTIP_RANDOM": "Grąžinti atsitiktinį elementą iš sąrašo.", + "MATH_MODULO_HELPURL": "https://en.wikipedia.org/wiki/Modulo_operation", "MATH_MODULO_TITLE": "dalybos liekana %1 ÷ %2", "MATH_CONSTRAIN_TITLE": "apribok %1 tarp %2 ir %3", + "MATH_RANDOM_INT_HELPURL": "https://en.wikipedia.org/wiki/Random_number_generation", "MATH_RANDOM_INT_TITLE": "atsitiktinis sveikas sk. nuo %1 iki %2", + "MATH_RANDOM_FLOAT_HELPURL": "https://en.wikipedia.org/wiki/Random_number_generation", "MATH_RANDOM_FLOAT_TITLE_RANDOM": "atsitiktinė trupmena", "MATH_RANDOM_FLOAT_TOOLTIP": "Atsitiktinė trupmena nuo 0 (imtinai) iki 1 (neimtinai).", "TEXT_TEXT_TOOLTIP": "Tekstas (arba žodis, ar raidė)", @@ -195,12 +205,16 @@ "LISTS_GET_INDEX_FIRST": "pirmas", "LISTS_GET_INDEX_LAST": "paskutinis", "LISTS_GET_INDEX_RANDOM": "atsitiktinis", + "LISTS_INDEX_FROM_START_TOOLTIP": "%1 yra pirmasis objektas.", + "LISTS_INDEX_FROM_END_TOOLTIP": "%1 yra paskutinis objektas.", + "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "Gražina objektą į nurodyta poziciją sąraše.", "LISTS_GET_INDEX_TOOLTIP_GET_FIRST": "Grąžina pirmąjį sąrašo elementą.", "LISTS_GET_INDEX_TOOLTIP_GET_LAST": "Grąžina paskutinį elementą iš sąrašo.", "LISTS_GET_INDEX_TOOLTIP_GET_RANDOM": "Grąžina atsitiktinį elementą iš sąrašo.", "LISTS_SET_INDEX_SET": "priskirk elementui", "LISTS_SET_INDEX_INSERT": "įterpk į vietą", "LISTS_SET_INDEX_INPUT_TO": "kaip", + "LISTS_SET_INDEX_TOOLTIP_INSERT_FROM": "Įterpią objektą į nurodytą poziciją sąraše.", "LISTS_GET_SUBLIST_START_FROM_START": "sąrašo dalis nuo #", "LISTS_GET_SUBLIST_START_FROM_END": "sąrašo dalis nuo # nuo galo", "LISTS_GET_SUBLIST_START_FIRST": "sąrašo dalis nuo pradžios", @@ -219,7 +233,9 @@ "PROCEDURES_DEFRETURN_TOOLTIP": "Sukuria funkciją - komandą, kuri ne tik atlieka veiksmus bet ir pateikia (grąžina/duoda) rezultatą.", "PROCEDURES_ALLOW_STATEMENTS": "leisti vidinius veiksmus", "PROCEDURES_DEF_DUPLICATE_WARNING": "Ši komanda turi du vienodus gaunamų duomenų pavadinimus.", + "PROCEDURES_CALLNORETURN_HELPURL": "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29", "PROCEDURES_CALLNORETURN_TOOLTIP": "Vykdyti sukurtą komandą \"%1\".", + "PROCEDURES_CALLRETURN_HELPURL": "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29", "PROCEDURES_CALLRETURN_TOOLTIP": "Įvykdyti komandą \"%1\" ir naudoti jos suskaičiuotą (atiduotą) reikšmę.", "PROCEDURES_MUTATORCONTAINER_TITLE": "gaunami duomenys (parametrai)", "PROCEDURES_MUTATORCONTAINER_TOOLTIP": "Tvarkyti komandos gaunamus duomenis (parametrus).", diff --git a/msg/json/nl.json b/msg/json/nl.json index 828a4ee0..4f27ad85 100644 --- a/msg/json/nl.json +++ b/msg/json/nl.json @@ -320,11 +320,11 @@ "LISTS_SORT_ORDER_DESCENDING": "aflopend", "LISTS_SORT_TYPE_NUMERIC": "numerieke", "LISTS_SORT_TYPE_TEXT": "in alfabetische volgorde", - "LISTS_SORT_TYPE_IGNORECASE": "alfabetisch, negeer zaak", + "LISTS_SORT_TYPE_IGNORECASE": "alfabetisch, negeer hoofd-/kleine letters", "LISTS_SPLIT_LIST_FROM_TEXT": "lijst maken van tekst", "LISTS_SPLIT_TEXT_FROM_LIST": "tekst maken van lijst", "LISTS_SPLIT_WITH_DELIMITER": "met scheidingsteken", - "LISTS_SPLIT_TOOLTIP_SPLIT": "Tekst splitsen in een tekst van tekst op basis van een scheidingsteken.", + "LISTS_SPLIT_TOOLTIP_SPLIT": "Tekst splitsen in een lijst van teksten op basis van een scheidingsteken.", "LISTS_SPLIT_TOOLTIP_JOIN": "Lijst van tekstdelen samenvoegen in één stuk tekst, waarbij de tekstdelen gescheiden zijn door een scheidingsteken.", "VARIABLES_GET_HELPURL": "https://github.com/google/blockly/wiki/Variables#get", "VARIABLES_GET_TOOLTIP": "Geeft de waarde van deze variabele.", diff --git a/msg/json/pt.json b/msg/json/pt.json index 1098cf15..52c4ea31 100644 --- a/msg/json/pt.json +++ b/msg/json/pt.json @@ -198,7 +198,7 @@ "TEXT_LENGTH_TOOLTIP": "Devolve o número de letras (incluindo espaços) do texto fornecido.", "TEXT_ISEMPTY_TITLE": "%1 está vazio", "TEXT_ISEMPTY_TOOLTIP": "Retorna verdadeiro se o texto fornecido estiver vazio.", - "TEXT_INDEXOF_TOOLTIP": "Retorna a posição da primeira/última ocorrência do primeiro texto no segundo texto. Retorna %1 se o texto não for encontrado.", + "TEXT_INDEXOF_TOOLTIP": "Retorna a posição da primeira/última ocorrência do primeiro texto no segundo texto. Retorna %1 se o texto não for encontrado.", "TEXT_INDEXOF_INPUT_INTEXT": "no texto", "TEXT_INDEXOF_OPERATOR_FIRST": "primeira ocorrência do texto", "TEXT_INDEXOF_OPERATOR_LAST": "última ocorrência do texto", diff --git a/msg/json/qqq.json b/msg/json/qqq.json index 9b6c7d6d..f7ac9f42 100644 --- a/msg/json/qqq.json +++ b/msg/json/qqq.json @@ -1,11 +1,4 @@ { - "@metadata": { - "authors": [ - "Espertus", - "Liuxinyu970226", - "Shirayuki" - ] - }, "VARIABLES_DEFAULT_NAME": "default name - A simple, general default name for a variable, preferably short. For more context, see [[Translating:Blockly#infrequent_message_types]].\n{{Identical|Item}}", "TODAY": "button text - Button that sets a calendar to today's date.\n{{Identical|Today}}", "DUPLICATE_BLOCK": "context menu - Make a copy of the selected block (and any blocks it contains).\n{{Identical|Duplicate}}", @@ -27,10 +20,13 @@ "UNDO": "context menu - Undo the previous action.\n{{Identical|Undo}}", "REDO": "context menu - Undo the previous undo action.\n{{Identical|Redo}}", "CHANGE_VALUE_TITLE": "prompt - This message is only seen in the Opera browser. With most browsers, users can edit numeric values in blocks by just clicking and typing. Opera does not allows this, so we have to open a new window and prompt users with this message to chanage a value.", - "NEW_VARIABLE": "dropdown choice - When the user clicks on a variable block, this is one of the dropdown menu choices. It is used to define a new variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu].", - "NEW_VARIABLE_TITLE": "prompt - Prompts the user to enter the name for a new variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu].", "RENAME_VARIABLE": "dropdown choice - When the user clicks on a variable block, this is one of the dropdown menu choices. It is used to rename the current variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu].", "RENAME_VARIABLE_TITLE": "prompt - Prompts the user to enter the new name for the selected variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu].\n\nParameters:\n* %1 - the name of the variable to be renamed.", + "NEW_VARIABLE": "button text - Text on the button used to launch the variable creation dialogue.", + "NEW_VARIABLE_TITLE": "prompt - Prompts the user to enter the name for a new variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu].", + "VARIABLE_ALREADY_EXISTS": "alert - Tells the user that the name they entered is already in use.", + "DELETE_VARIABLE_CONFIRMATION": "confirm - Ask the user to confirm their deletion of multiple uses of a variable.", + "DELETE_VARIABLE": "alert - Tell the user that they can't delete a variable because it's part of the definition of a procedure. dropdown choice - Delete the currently selected variable.", "COLOUR_PICKER_HELPURL": "url - Information about colour.", "COLOUR_PICKER_TOOLTIP": "tooltip - See [https://github.com/google/blockly/wiki/Colour#picking-a-colour-from-a-palette https://github.com/google/blockly/wiki/Colour#picking-a-colour-from-a-palette].", "COLOUR_RANDOM_HELPURL": "url - A link that displays a random colour each time you visit it.", diff --git a/msg/json/ta.json b/msg/json/ta.json index bddb0967..58b755a6 100644 --- a/msg/json/ta.json +++ b/msg/json/ta.json @@ -176,6 +176,7 @@ "TEXT_LENGTH_TOOLTIP": "தொடரில் உள்ள எழுத்துக்களின் (இடைவெளிகளையும் சேர்த்து) எண்ணிகையை பின்கொடு.", "TEXT_ISEMPTY_TITLE": "%1 காலியானது", "TEXT_ISEMPTY_TOOLTIP": "காலியானது என்றால் மெய் மதிப்பை பின்கொடு", + "TEXT_INDEXOF_TOOLTIP": "இரண்டாவது உரையில் முதல் உரையின் முதல்/கடை இருக்கை குறிஎண்ணை பின்கொடு.", "TEXT_INDEXOF_INPUT_INTEXT": "உரையில்", "TEXT_INDEXOF_OPERATOR_FIRST": "உரையில் முதல் தோற்ற இடத்தை பின்கொடு", "TEXT_INDEXOF_OPERATOR_LAST": "உரையில் கடைசி தோற்ற இடத்தை பின்கொடு", diff --git a/msg/json/tcy.json b/msg/json/tcy.json index 8194895a..38ce9669 100644 --- a/msg/json/tcy.json +++ b/msg/json/tcy.json @@ -232,28 +232,28 @@ "LISTS_GET_INDEX_FIRST": "ಸುರುತ", "LISTS_GET_INDEX_LAST": "ಕಡೆತ", "LISTS_GET_INDEX_RANDOM": "ಗೊತ್ತು ಗುರಿದಾಂತಿನ", - "LISTS_INDEX_FROM_START_TOOLTIP": "%1 ಸುರುತ ಅಂಸ", - "LISTS_INDEX_FROM_END_TOOLTIP": "%1 ಅಕೇರಿತ ಅಂಸ", - "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಪಿರಕೊರು", + "LISTS_INDEX_FROM_START_TOOLTIP": "%1 ಸುರುತ ಅಂಸೊ", + "LISTS_INDEX_FROM_END_TOOLTIP": "%1 ಅಕೇರಿತ ಅಂಸೊ", + "LISTS_GET_INDEX_TOOLTIP_GET_FROM": "ನಿರ್ದಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಪಿರಕೊರು", "LISTS_GET_INDEX_TOOLTIP_GET_FIRST": "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಪಿರಕೊರು.", "LISTS_GET_INDEX_TOOLTIP_GET_LAST": "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಪಿರಕೊರು.", "LISTS_GET_INDEX_TOOLTIP_GET_RANDOM": "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಪಿರಕೊರು.", - "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM": "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು", + "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM": "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST": "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST": "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು.", "LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM": "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು ಅತ್ತಂಡ ಪಿರಕೊರು.", - "LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM": "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪುಲೆ", + "LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM": "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ದೆಪ್ಪುಲೆ", "LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST": "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು.", "LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST": "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು.", "LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM": "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ದೆಪ್ಪು.", "LISTS_SET_INDEX_SET": "ಮಾಲ್ಪು", "LISTS_SET_INDEX_INSERT": "ಸೇರಲ", "LISTS_SET_INDEX_INPUT_TO": "ಅಂಚ", - "LISTS_SET_INDEX_TOOLTIP_SET_FROM": "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು", + "LISTS_SET_INDEX_TOOLTIP_SET_FROM": "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು", "LISTS_SET_INDEX_TOOLTIP_SET_FIRST": "ಸುರುತ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು.", "LISTS_SET_INDEX_TOOLTIP_SET_LAST": "ಅಕೇರಿದ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು.", "LISTS_SET_INDEX_TOOLTIP_SET_RANDOM": "ಗೊತ್ತುಗುರಿದಾಂತಿನ ಅಂಸೊಲೆನ ಪಟ್ಟಿನ್ ಮಾಲ್ಪು.", - "LISTS_SET_INDEX_TOOLTIP_INSERT_FROM": "ನಿರ್ದಿಷ್ಟ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಸೇರಲ", + "LISTS_SET_INDEX_TOOLTIP_INSERT_FROM": "ನಿರ್ದಿಸ್ಟೊ ಜಾಗೆಡುಪ್ಪುನ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಸೇರಲ", "LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST": "ಸುರುತ ಅಂಸೊಲೆ ಪಟ್ಟಿನ್ ಸೇರಲ.", "LISTS_SET_INDEX_TOOLTIP_INSERT_LAST": "ಪಟ್ಟಿದ ಅಕೇರಿಗ್ ಈ ಅಂಸೊಲೆನ್ ಸೇರಲ.", "LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM": "ಪಟ್ಟಿಗ್ ಗೊತ್ತುಗುರಿದಾಂತೆ ಅಂಸೊಲೆನ್ ಸೇರಲ.", diff --git a/msg/messages.js b/msg/messages.js index 9b0464a3..f4b05591 100644 --- a/msg/messages.js +++ b/msg/messages.js @@ -96,15 +96,27 @@ Blockly.Msg.REDO = 'Redo'; // Variable renaming. /// prompt - This message is only seen in the Opera browser. With most browsers, users can edit numeric values in blocks by just clicking and typing. Opera does not allows this, so we have to open a new window and prompt users with this message to chanage a value. Blockly.Msg.CHANGE_VALUE_TITLE = 'Change value:'; -/// dropdown choice - When the user clicks on a variable block, this is one of the dropdown menu choices. It is used to define a new variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu]. -Blockly.Msg.NEW_VARIABLE = 'New variable...'; -/// prompt - Prompts the user to enter the name for a new variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu]. -Blockly.Msg.NEW_VARIABLE_TITLE = 'New variable name:'; /// dropdown choice - When the user clicks on a variable block, this is one of the dropdown menu choices. It is used to rename the current variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu]. Blockly.Msg.RENAME_VARIABLE = 'Rename variable...'; /// prompt - Prompts the user to enter the new name for the selected variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu].\n\nParameters:\n* %1 - the name of the variable to be renamed. Blockly.Msg.RENAME_VARIABLE_TITLE = 'Rename all "%1" variables to:'; +// Variable creation +/// button text - Text on the button used to launch the variable creation dialogue. +Blockly.Msg.NEW_VARIABLE = 'Create variable...'; +/// prompt - Prompts the user to enter the name for a new variable. See [https://github.com/google/blockly/wiki/Variables#dropdown-menu https://github.com/google/blockly/wiki/Variables#dropdown-menu]. +Blockly.Msg.NEW_VARIABLE_TITLE = 'New variable name:'; +/// alert - Tells the user that the name they entered is already in use. +Blockly.Msg.VARIABLE_ALREADY_EXISTS = 'A variable named "%1" already exists.' + +// Variable deletion. +/// confirm - Ask the user to confirm their deletion of multiple uses of a variable. +Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = 'Delete %1 uses of the "%2" variable?'; +/// alert - Tell the user that they can't delete a variable because it's part of the definition of a procedure. +Blockly.Msg.CANNOT_DELETE_VARIABLE_PROCEDURE = 'Can\'t delete the variable "%1" because it is part of the definition of the procedure "%2"'; +/// dropdown choice - Delete the currently selected variable. +Blockly.Msg.DELETE_VARIABLE = 'Delete the "%1" variable'; + // Colour Blocks. /// url - Information about colour. Blockly.Msg.COLOUR_PICKER_HELPURL = 'https://en.wikipedia.org/wiki/Color'; diff --git a/php_compressed.js b/php_compressed.js index d0df0e60..73ffa0b0 100644 --- a/php_compressed.js +++ b/php_compressed.js @@ -65,7 +65,7 @@ Blockly.PHP.math_on_list=function(a){var b=a.getFieldValue("OP");switch(b){case Blockly.PHP.math_constrain=function(a){var b=Blockly.PHP.valueToCode(a,"VALUE",Blockly.PHP.ORDER_COMMA)||"0",c=Blockly.PHP.valueToCode(a,"LOW",Blockly.PHP.ORDER_COMMA)||"0";a=Blockly.PHP.valueToCode(a,"HIGH",Blockly.PHP.ORDER_COMMA)||"Infinity";return["min(max("+b+", "+c+"), "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; Blockly.PHP.math_random_int=function(a){var b=Blockly.PHP.valueToCode(a,"FROM",Blockly.PHP.ORDER_COMMA)||"0";a=Blockly.PHP.valueToCode(a,"TO",Blockly.PHP.ORDER_COMMA)||"0";return[Blockly.PHP.provideFunction_("math_random_int",["function "+Blockly.PHP.FUNCTION_NAME_PLACEHOLDER_+"($a, $b) {"," if ($a > $b) {"," return rand($b, $a);"," }"," return rand($a, $b);","}"])+"("+b+", "+a+")",Blockly.PHP.ORDER_FUNCTION_CALL]}; Blockly.PHP.math_random_float=function(a){return["(float)rand()/(float)getrandmax()",Blockly.PHP.ORDER_FUNCTION_CALL]};Blockly.PHP.procedures={}; -Blockly.PHP.procedures_defreturn=function(a){for(var b=Blockly.Variables.allVariables(a),c=b.length-1;0<=c;c--){var d=b[c];-1==a.arguments_.indexOf(d)?b[c]=Blockly.PHP.variableDB_.getName(d,Blockly.Variables.NAME_TYPE):b.splice(c,1)}var b=b.length?" global "+b.join(", ")+";\n":"",d=Blockly.PHP.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE),e=Blockly.PHP.statementToCode(a,"STACK");Blockly.PHP.STATEMENT_PREFIX&&(e=Blockly.PHP.prefixLines(Blockly.PHP.STATEMENT_PREFIX.replace(/%1/g,"'"+ +Blockly.PHP.procedures_defreturn=function(a){for(var b=a.workspace.variableList,c=b.length-1;0<=c;c--){var d=b[c];-1==a.arguments_.indexOf(d)?b[c]=Blockly.PHP.variableDB_.getName(d,Blockly.Variables.NAME_TYPE):b.splice(c,1)}var b=b.length?" global "+b.join(", ")+";\n":"",d=Blockly.PHP.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE),e=Blockly.PHP.statementToCode(a,"STACK");Blockly.PHP.STATEMENT_PREFIX&&(e=Blockly.PHP.prefixLines(Blockly.PHP.STATEMENT_PREFIX.replace(/%1/g,"'"+ a.id+"'"),Blockly.PHP.INDENT)+e);Blockly.PHP.INFINITE_LOOP_TRAP&&(e=Blockly.PHP.INFINITE_LOOP_TRAP.replace(/%1/g,"'"+a.id+"'")+e);var g=Blockly.PHP.valueToCode(a,"RETURN",Blockly.PHP.ORDER_NONE)||"";g&&(g=" return "+g+";\n");for(var f=[],c=0;cc?"int("+a+" - "+-c+")":"int("+a+")",d&&(a="-"+a));return a};Blockly.Python.colour={};Blockly.Python.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.colour_random=function(a){Blockly.Python.definitions_.import_random="import random";return["'#%06x' % random.randint(0, 2**24 - 1)",Blockly.Python.ORDER_FUNCTION_CALL]}; @@ -60,7 +60,7 @@ Blockly.Python.math_on_list=function(a){var b=a.getFieldValue("OP");a=Blockly.Py Blockly.Python.math_modulo=function(a){var b=Blockly.Python.valueToCode(a,"DIVIDEND",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";a=Blockly.Python.valueToCode(a,"DIVISOR",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";return[b+" % "+a,Blockly.Python.ORDER_MULTIPLICATIVE]}; Blockly.Python.math_constrain=function(a){var b=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"0",c=Blockly.Python.valueToCode(a,"LOW",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"HIGH",Blockly.Python.ORDER_NONE)||"float('inf')";return["min(max("+b+", "+c+"), "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]}; Blockly.Python.math_random_int=function(a){Blockly.Python.definitions_.import_random="import random";var b=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0";return["random.randint("+b+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.math_random_float=function(a){Blockly.Python.definitions_.import_random="import random";return["random.random()",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.procedures={}; -Blockly.Python.procedures_defreturn=function(a){for(var b=Blockly.Variables.allVariables(a),c=b.length-1;0<=c;c--){var d=b[c];-1==a.arguments_.indexOf(d)?b[c]=Blockly.Python.variableDB_.getName(d,Blockly.Variables.NAME_TYPE):b.splice(c,1)}var b=b.length?" global "+b.join(", ")+"\n":"",d=Blockly.Python.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE),e=Blockly.Python.statementToCode(a,"STACK");Blockly.Python.STATEMENT_PREFIX&&(e=Blockly.Python.prefixLines(Blockly.Python.STATEMENT_PREFIX.replace(/%1/g,"'"+ +Blockly.Python.procedures_defreturn=function(a){for(var b=a.workspace.variableList,c=b.length-1;0<=c;c--){var d=b[c];-1==a.arguments_.indexOf(d)?b[c]=Blockly.Python.variableDB_.getName(d,Blockly.Variables.NAME_TYPE):b.splice(c,1)}var b=b.length?" global "+b.join(", ")+"\n":"",d=Blockly.Python.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE),e=Blockly.Python.statementToCode(a,"STACK");Blockly.Python.STATEMENT_PREFIX&&(e=Blockly.Python.prefixLines(Blockly.Python.STATEMENT_PREFIX.replace(/%1/g,"'"+ a.id+"'"),Blockly.Python.INDENT)+e);Blockly.Python.INFINITE_LOOP_TRAP&&(e=Blockly.Python.INFINITE_LOOP_TRAP.replace(/%1/g,'"'+a.id+'"')+e);var f=Blockly.Python.valueToCode(a,"RETURN",Blockly.Python.ORDER_NONE)||"";f?f=" return "+f+"\n":e||(e=Blockly.Python.PASS);for(var g=[],c=0;c
    - diff --git a/tests/jsunit/xml_test.js b/tests/jsunit/xml_test.js index 7487b6f3..25e2655e 100644 --- a/tests/jsunit/xml_test.js +++ b/tests/jsunit/xml_test.js @@ -59,6 +59,29 @@ function test_domToText() { text.replace(/\s+/g, '')); } +function test_domToWorkspace() { + Blockly.Blocks.test_block = { + init: function() { + this.jsonInit({ + message0: 'test', + }); + } + }; + + try { + var dom = Blockly.Xml.textToDom( + '' + + ' ' + + ' ' + + ''); + var workspace = new Blockly.Workspace(); + Blockly.Xml.domToWorkspace(dom, workspace); + assertEquals('Block count', 1, workspace.getAllBlocks().length); + } finally { + delete Blockly.Blocks.test_block; + } +} + function test_domToPrettyText() { var dom = Blockly.Xml.textToDom(XML_TEXT); var text = Blockly.Xml.domToPrettyText(dom); diff --git a/tests/multi_playground.html b/tests/multi_playground.html index f541d6f6..9c21323e 100644 --- a/tests/multi_playground.html +++ b/tests/multi_playground.html @@ -90,7 +90,7 @@ h1 { #octaweb td { width: 50%; } -#octaweb td div { +#octaweb td >div { height: 480px; width: 100%; } @@ -236,13 +236,6 @@ h1 { - - - - 1 - - - diff --git a/tests/playground.html b/tests/playground.html index e7e6e116..144b4f4e 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -472,13 +472,6 @@ h1 { - - - - 1 - - -