Merge branch 'develop'

This commit is contained in:
Katelyn Mann
2016-10-11 15:25:51 -07:00
115 changed files with 4826 additions and 3766 deletions
+13 -8
View File
@@ -33,20 +33,25 @@ 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: [],
mediaPathPrefix: null
mediaPathPrefix: null,
toolbarButtonConfig: []
};
The value corresponding to 'toolbarButtonConfig' can be modified by adding
objects representing buttons on the toolbar. Each of these objects should have
two keys:
The value of mediaPathPrefix should be the location of the accessible/media
folder.
The value of 'toolbarButtonConfig' should be a list of objects, each
representing buttons on the toolbar. Each of these objects should have four
keys:
- 'text' (the text to display on the button)
- 'ariaDescribedBy' (the value of the button's aria-describedby attribute)
- 'onClickNotification' (a notification that the screenreader should read
when the button is clicked)
- 'isHidden' (a function that returns true if the button should not be
displayed, and false otherwise)
- '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
-----------
+7 -16
View File
@@ -29,28 +29,19 @@ blocklyApp.AppView = ng.core
.Component({
selector: 'blockly-app',
template: `
<div aria-hidden="true">
Status: <span aria-live="polite" role="status">{{getStatusMessage()}}</span>
<div *ngIf="getStatusMessage()" aria-hidden="true" class="blocklyAriaLiveStatus">
<span aria-live="polite" role="status">{{getStatusMessage()}}</span>
</div>
<div>
<blockly-toolbox>{{'TOOLBOX_LOAD'|translate}}</blockly-toolbox>
<blockly-workspace>{{'WORKSPACE_LOAD'|translate}}</blockly-workspace>
<blockly-toolbox></blockly-toolbox>
<blockly-workspace></blockly-workspace>
</div>
<label aria-hidden="true" hidden id="blockly-argument-block-menu">{{'ARGUMENT_BLOCK_ACTION_LIST'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-argument-input">{{'ARGUMENT_INPUT'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-argument-menu">{{'ARGUMENT_OPTIONS_LIST'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-argument-text">{{'TEXT'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-block-menu">{{'BLOCK_ACTION_LIST'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-block-summary">{{'BLOCK_SUMMARY'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-more-options">{{'MORE_OPTIONS'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-submenu-indicator">{{'SUBMENU_INDICATOR'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-toolbox-block">{{'TOOLBOX_BLOCK'|translate}} {{'SUBMENU_INDICATOR'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-workspace-block">{{'WORKSPACE_BLOCK'|translate}} {{'SUBMENU_INDICATOR'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-button">{{'BUTTON'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-disabled">{{'DISABLED'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-menu">{{'OPTION_LIST'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-more-options">{{'MORE_OPTIONS'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-toolbox-block">{{'TOOLBOX_BLOCK'|translate}}</label>
<label aria-hidden="true" hidden id="blockly-workspace-block">{{'WORKSPACE_BLOCK'|translate}}</label>
`,
directives: [blocklyApp.ToolboxComponent, blocklyApp.WorkspaceComponent],
pipes: [blocklyApp.TranslatePipe],
+3
View File
@@ -61,6 +61,9 @@ blocklyApp.ClipboardService = ng.core
return this.markedConnection_.getSourceBlock();
}
},
isAnyConnectionMarked: function() {
return Boolean(this.markedConnection_);
},
isMovableToMarkedConnection: function(block) {
// It should not be possible to move any ancestor of the block containing
// the marked spot to the marked spot.
+167
View File
@@ -0,0 +1,167 @@
/**
* 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 Component that renders a "field segment" (a group
* of non-editable Blockly.Field followed by 0 or 1 editable Blockly.Field)
* in a block. Also handles any interactions with the field.
* @author madeeha@google.com (Madeeha Ghori)
*/
blocklyApp.FieldSegmentComponent = ng.core
.Component({
selector: 'blockly-field-segment',
template: `
<template [ngIf]="!mainField">
<label [id]="mainFieldId">{{getPrefixText()}}</label>
</template>
<template [ngIf]="mainField">
<template [ngIf]="isTextInput()">
{{getPrefixText()}}
<input [id]="mainFieldId" type="text" [disabled]="disabled"
[ngModel]="mainField.getValue()" (ngModelChange)="mainField.setValue($event)"
[attr.aria-label]="getFieldDescription() + (disabled ? 'Disabled text field' : 'Press Enter to edit text')"
tabindex="-1">
</template>
<template [ngIf]="isNumberInput()">
{{getPrefixText()}}
<input [id]="mainFieldId" type="number" [disabled]="disabled"
[ngModel]="mainField.getValue()" (ngModelChange)="setNumberValue($event)"
[attr.aria-label]="getFieldDescription() + (disabled ? 'Disabled number field' : 'Press Enter to edit number')"
tabindex="-1">
</template>
<template [ngIf]="isDropdown()">
<label [id]="mainFieldId" [attr.aria-label]="getFieldDescription() + ' Move right to view submenu'">
{{getFieldDescription()}}
</label>
<ol role="group">
<li [id]="idMap[optionValue]" role="treeitem" *ngFor="#optionValue of getOptions()"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[optionValue + 'Button'], 'blockly-button')"
[attr.aria-level]="level" [attr.aria-selected]="mainField.getValue() == optionValue"
class="blocklyDropdownListItem">
<button [id]="idMap[optionValue + 'Button']" (click)="handleDropdownChange(mainField, optionValue)"
[disabled]="disabled" tabindex="-1"
[attr.aria-label]="optionText[optionValue] + ' Press Enter to select this value'">
{{optionText[optionValue]}}
</button>
</li>
</ol>
</template>
</template>
`,
inputs: ['prefixFields', 'mainField', 'mainFieldId', 'level'],
pipes: [blocklyApp.TranslatePipe]
})
.Class({
constructor: [
blocklyApp.NotificationsService, blocklyApp.UtilsService,
function(_notificationsService, _utilsService) {
this.optionText = {
keys: []
};
this.notificationsService = _notificationsService;
this.utilsService = _utilsService;
}],
ngOnInit: function() {
var elementsNeedingIds = this.generateElementNames(this.mainField);
// Warning: this assumes that the elements returned by
// this.generateElementNames() are unique.
this.idMap = this.utilsService.generateIds(elementsNeedingIds);
},
getPrefixText: function() {
var prefixTexts = this.prefixFields.map(function(prefixField) {
return prefixField.getText();
});
return prefixTexts.join(' ');
},
getFieldDescription: function() {
var description = this.mainField.getText();
if (this.prefixFields.length > 0) {
description = this.getPrefixText() + ': ' + description;
}
return description;
},
setNumberValue: function(newValue) {
// Do not permit a residual value of NaN after a backspace event.
this.mainField.setValue(newValue || 0);
},
generateAriaLabelledByAttr: function(mainLabel, secondLabel) {
return mainLabel + ' ' + secondLabel;
},
generateElementNames: function() {
var elementNames = [];
if (this.isDropdown()) {
var keys = this.getOptions();
for (var i = 0; i < keys.length; i++){
elementNames.push(keys[i], keys[i] + 'Button');
}
}
return elementNames;
},
isNumberInput: function() {
return this.mainField instanceof Blockly.FieldNumber;
},
isTextInput: function() {
return this.mainField instanceof Blockly.FieldTextInput &&
!(this.mainField instanceof Blockly.FieldNumber);
},
isDropdown: function() {
return this.mainField instanceof Blockly.FieldDropdown;
},
isCheckbox: function() {
return this.mainField instanceof Blockly.FieldCheckbox;
},
isTextField: function() {
return !(this.mainField instanceof Blockly.FieldTextInput) &&
!(this.mainField instanceof Blockly.FieldDropdown) &&
!(this.mainField instanceof Blockly.FieldCheckbox);
},
hasVisibleText: function() {
var text = this.mainField.getText().trim();
return !!text;
},
getOptions: function() {
if (this.optionText.keys.length) {
return this.optionText.keys;
}
var options = this.mainField.getOptions_();
for (var i = 0; i < options.length; i++) {
var tuple = options[i];
this.optionText[tuple[1]] = tuple[0];
this.optionText.keys.push(tuple[1]);
}
return this.optionText.keys;
},
handleDropdownChange: function(field, optionValue) {
if (optionValue == 'NO_ACTION') {
return;
}
if (this.mainField instanceof Blockly.FieldVariable) {
Blockly.FieldVariable.dropdownChange.call(this.mainField, optionValue);
} else {
this.mainField.setValue(optionValue);
}
this.notificationsService.setStatusMessage(
'Selected option ' + this.optionText[optionValue]);
}
});
-143
View File
@@ -1,143 +0,0 @@
/**
* 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 Component that details how a Blockly.Field is
* rendered in the toolbox in AccessibleBlockly. Also handles any interactions
* with the field.
* @author madeeha@google.com (Madeeha Ghori)
*/
blocklyApp.FieldComponent = ng.core
.Component({
selector: 'blockly-field',
template: `
<input *ngIf="isTextInput()" [id]="mainFieldId" type="text" [disabled]="disabled"
[ngModel]="field.getValue()" (ngModelChange)="field.setValue($event)"
[attr.aria-label]="disabled ? 'Disabled text field' : 'Press Enter to edit text: ' + field.getValue()"
tabindex="-1">
<input *ngIf="isNumberInput()" [id]="mainFieldId" type="number" [disabled]="disabled"
[ngModel]="field.getValue()" (ngModelChange)="setNumberValue($event)"
[attr.aria-label]="disabled ? 'Disabled number field' : 'Press Enter to edit number: ' + field.getValue()"
tabindex="-1">
<div *ngIf="isDropdown()">
<label [id]="mainFieldId" [attr.aria-label]="('CURRENT_ARGUMENT_VALUE'|translate) + ' ' + field.getText() + ' Move right to view submenu'">
{{'CURRENT_ARGUMENT_VALUE'|translate}} {{field.getText()}}
</label>
<ol role="group">
<li [id]="idMap[optionValue]" role="treeitem" *ngFor="#optionValue of getOptions()"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[optionValue + 'Button'], 'blockly-button')"
[attr.aria-level]="level" [attr.aria-selected]="field.getValue() == optionValue">
<button [id]="idMap[optionValue + 'Button']" (click)="handleDropdownChange(field, optionValue)"
[disabled]="disabled" tabindex="-1"
[attr.aria-label]="optionText[optionValue] + ' Press Enter to select this value'">
{{optionText[optionValue]}}
</button>
</li>
</ol>
</div>
<div *ngIf="isCheckbox()">
// Checkboxes are not currently supported.
</div>
<label [id]="mainFieldId" *ngIf="isTextField() && hasVisibleText()">
{{field.getText()}}
</label>
`,
inputs: ['field', 'index', 'parentId', 'disabled', 'mainFieldId', 'level'],
pipes: [blocklyApp.TranslatePipe]
})
.Class({
constructor: [blocklyApp.UtilsService, function(_utilsService) {
this.optionText = {
keys: []
};
this.utilsService = _utilsService;
}],
ngOnInit: function() {
var elementsNeedingIds = this.generateElementNames(this.field);
// Warning: this assumes that the elements returned by
// this.generateElementNames() are unique.
this.idMap = this.utilsService.generateIds(elementsNeedingIds);
},
setNumberValue: function(newValue) {
// Do not permit a residual value of NaN after a backspace event.
this.field.setValue(newValue || 0);
},
generateAriaLabelledByAttr: function(mainLabel, secondLabel) {
return mainLabel + ' ' + secondLabel;
},
generateElementNames: function() {
var elementNames = [];
if (this.isDropdown()) {
var keys = this.getOptions();
for (var i = 0; i < keys.length; i++){
elementNames.push(keys[i], keys[i] + 'Button');
}
}
return elementNames;
},
isNumberInput: function() {
return this.field instanceof Blockly.FieldNumber;
},
isTextInput: function() {
return this.field instanceof Blockly.FieldTextInput &&
!(this.field instanceof Blockly.FieldNumber);
},
isDropdown: function() {
return this.field instanceof Blockly.FieldDropdown;
},
isCheckbox: function() {
return this.field instanceof Blockly.FieldCheckbox;
},
isTextField: function() {
return !(this.field instanceof Blockly.FieldTextInput) &&
!(this.field instanceof Blockly.FieldDropdown) &&
!(this.field instanceof Blockly.FieldCheckbox);
},
hasVisibleText: function() {
var text = this.field.getText().trim();
return !!text;
},
getOptions: function() {
if (this.optionText.keys.length) {
return this.optionText.keys;
}
var options = this.field.getOptions_();
for (var i = 0; i < options.length; i++) {
var tuple = options[i];
this.optionText[tuple[1]] = tuple[0];
this.optionText.keys.push(tuple[1]);
}
return this.optionText.keys;
},
handleDropdownChange: function(field, text) {
if (text == 'NO_ACTION') {
return;
}
if (this.field instanceof Blockly.FieldVariable) {
Blockly.FieldVariable.dropdownChange.call(this.field, text);
} else {
this.field.setValue(text);
}
}
});
+18 -4
View File
@@ -8,16 +8,30 @@
}
.blocklyToolbarColumn {
float: left;
margin-left: 10px;
margin-top: 20px;
width: 200px;
width: 150px;
}
.blocklyAriaLiveStatus {
background: #c8f7be;
border-radius: 10px;
bottom: 80px;
left: 20px;
max-width: 275px;
padding: 10px;
position: fixed;
}
.blocklyTree .blocklyActiveDescendant > label,
.blocklyTree .blocklyActiveDescendant > div > label,
.blocklyActiveDescendant > button,
.blocklyActiveDescendant > input,
.blocklyActiveDescendant > blockly-field > label,
.blocklyActiveDescendant > blockly-field > input,
.blocklyActiveDescendant > blockly-field > div > label {
.blocklyActiveDescendant > blockly-field-segment > label,
.blocklyActiveDescendant > blockly-field-segment > input {
outline: 2px dotted #00f;
}
.blocklyDropdownListItem[aria-selected="true"] button {
font-weight: bold;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+29 -42
View File
@@ -19,55 +19,42 @@
*/
/**
* @fileoverview Accessible strings.
* @fileoverview Translatable string constants for Accessible Blockly.
* @author madeeha@google.com (Madeeha Ghori)
*/
'use strict';
// The following are all Accessible Blockly strings.
// None of the alert messages have periods on them. This is because the user
// will have their punctuation setting set to 'all', which will result in any
// punctuation being read out to them.
Blockly.Msg.RUN_CODE = 'Run Code';
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_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';
Blockly.Msg.PASTE = 'paste';
Blockly.Msg.TOOLBOX_LOAD_MSG = 'Loading Toolbox…';
Blockly.Msg.WORKSPACE_LOAD_MSG = 'Loading Workspace…';
Blockly.Msg.BLOCK_SUMMARY = 'block summary';
Blockly.Msg.OPTION_LIST = 'option list';
Blockly.Msg.ARGUMENT_OPTIONS_LIST = 'argument options list';
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.DISABLED = 'disabled';
Blockly.Msg.CURRENT_ARGUMENT_VALUE = 'current argument value:';
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.TOOLBOX_BLOCK = 'toolbox block. Move right to view submenu.';
Blockly.Msg.WORKSPACE_BLOCK = 'workspace block. Move right to view submenu.';
Blockly.Msg.CLEAR_WORKSPACE = 'Erase Workspace';
Blockly.Msg.COPY_TO_CLIPBOARD = 'Copy to clipboard.';
Blockly.Msg.COPY_TO_MARKED_SPOT = 'Attach to link.';
Blockly.Msg.COPY_TO_WORKSPACE = 'Add to workspace.';
Blockly.Msg.COPY_BLOCK = 'Copy block.';
Blockly.Msg.DELETE = 'Delete block.';
Blockly.Msg.MARK_SPOT_BEFORE = 'Add link before.';
Blockly.Msg.MARK_SPOT_AFTER = 'Add link after.';
Blockly.Msg.MARK_THIS_SPOT = 'Add link inside.';
Blockly.Msg.MOVE_TO_MARKED_SPOT = 'Attach to link.';
Blockly.Msg.PASTE_AFTER = 'Paste after.';
Blockly.Msg.PASTE_BEFORE = 'Paste before.';
Blockly.Msg.PASTE_INSIDE = 'Paste inside.';
Blockly.Msg.ANY = 'any';
Blockly.Msg.BLOCK = 'block';
Blockly.Msg.BUTTON = 'Button.';
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';
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_OPTIONS = 'Block options: ';
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.BLOCK_OPTIONS = 'Block options';
Blockly.Msg.COPIED_BLOCK_MSG = 'copied. ';
Blockly.Msg.MARKED_SPOT_MSG = 'Marked spot. ';
Blockly.Msg.PASTED_BLOCK_FROM_CLIPBOARD_MSG = 'pasted. ';
Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG = 'moved to marked spot. ';
+34 -19
View File
@@ -30,13 +30,21 @@ blocklyApp.ToolboxTreeComponent = ng.core
template: `
<li [id]="idMap['toolboxBlockRoot']" role="treeitem"
[ngClass]="{blocklyHasChildren: displayBlockMenu}"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['blockSummaryLabel'], 'blockly-toolbox-block')"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['toolboxBlockSummary'], 'blockly-toolbox-block')"
[attr.aria-level]="level">
<label #blockSummaryLabel [id]="idMap['blockSummaryLabel']">{{getBlockDescription()}}</label>
<label #toolboxBlockSummary [id]="idMap['toolboxBlockSummary']">{{getBlockDescription()}}</label>
<ol role="group" *ngIf="displayBlockMenu">
<li [id]="idMap['blockCopy']" role="treeitem" *ngIf="!isWorkspaceEmpty()"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['blockCopyButton'], 'blockly-button')"
[attr.aria-level]="level + 1">
<button [id]="idMap['blockCopyButton']" (click)="copyToClipboard()" tabindex="-1">
{{'COPY_TO_CLIPBOARD'|translate}}
</button>
</li>
<li [id]="idMap['sendToSelected']" role="treeitem" *ngIf="!isWorkspaceEmpty()"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['sendToSelectedButton'], 'blockly-button', !canBeCopiedToMarkedConnection())"
[attr.aria-level]="level + 2">
[attr.aria-label]="getAriaLabelForCopyToMarkedSpotButton()"
[attr.aria-level]="level + 1"
[attr.aria-disabled]="!canBeCopiedToMarkedConnection()">
<button [id]="idMap['sendToSelectedButton']" (click)="copyToMarkedSpot()"
[disabled]="!canBeCopiedToMarkedConnection()" tabindex="-1">
{{'COPY_TO_MARKED_SPOT'|translate}}
@@ -44,21 +52,15 @@ blocklyApp.ToolboxTreeComponent = ng.core
</li>
<li [id]="idMap['workspaceCopy']" role="treeitem"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['workspaceCopyButton'], 'blockly-button')"
[attr.aria-level]="level + 2">
[attr.aria-level]="level + 1">
<button [id]="idMap['workspaceCopyButton']" (click)="copyToWorkspace()" tabindex="-1">
{{'COPY_TO_WORKSPACE'|translate}}
</button>
</li>
</ol>
</li>
<blockly-toolbox-tree *ngIf= "block.nextConnection && block.nextConnection.targetBlock()"
[level]="level"
[block]="block.nextConnection.targetBlock()"
[displayBlockMenu]="false">
</blockly-toolbox-tree>
`,
directives: [blocklyApp.FieldComponent, ng.core.forwardRef(function() {
directives: [ng.core.forwardRef(function() {
return blocklyApp.ToolboxTreeComponent;
})],
inputs: [
@@ -78,11 +80,11 @@ blocklyApp.ToolboxTreeComponent = ng.core
this.utilsService = _utilsService;
}],
ngOnInit: function() {
var idKeys = ['toolboxBlockRoot', 'blockSummaryLabel'];
var idKeys = ['toolboxBlockRoot', 'toolboxBlockSummary'];
if (this.displayBlockMenu) {
idKeys = idKeys.concat([
'workspaceCopy', 'workspaceCopyButton', 'sendToSelected',
'sendToSelectedButton']);
'sendToSelectedButton', 'blockCopy', 'blockCopyButton']);
}
this.idMap = {};
@@ -103,19 +105,32 @@ blocklyApp.ToolboxTreeComponent = ng.core
});
}
},
getAriaLabelForCopyToMarkedSpotButton: function() {
// TODO(sll): Find a way to make this more like the other buttons.
var ariaLabel = 'Attach to link button';
if (!this.clipboardService.isAnyConnectionMarked()) {
ariaLabel += ', unavailable. Add a link in the workspace first.';
}
return ariaLabel;
},
isWorkspaceEmpty: function() {
return this.utilsService.isWorkspaceEmpty();
},
getBlockDescription: function() {
return this.utilsService.getBlockDescription(this.block);
},
generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) {
generateAriaLabelledByAttr: function(mainLabel, secondLabel) {
return this.utilsService.generateAriaLabelledByAttr(
mainLabel, secondLabel, isDisabled);
mainLabel, secondLabel);
},
canBeCopiedToMarkedConnection: function() {
return this.clipboardService.canBeCopiedToMarkedConnection(this.block);
},
copyToClipboard: function() {
this.clipboardService.copy(this.block);
this.notificationsService.setStatusMessage(
this.getBlockDescription() + ' ' + Blockly.Msg.COPIED_BLOCK_MSG);
},
copyToWorkspace: function() {
var blockDescription = this.getBlockDescription();
var xml = Blockly.Xml.blockToDom(this.block);
@@ -125,8 +140,8 @@ blocklyApp.ToolboxTreeComponent = ng.core
setTimeout(function() {
that.treeService.focusOnBlock(newBlockId);
that.notificationsService.setStatusMessage(
blockDescription + ' copied to new workspace group. ' +
'Now on copied block in workspace.');
blockDescription + ' added to workspace. ' +
'Now on added block in workspace.');
});
},
copyToMarkedSpot: function() {
@@ -155,7 +170,7 @@ blocklyApp.ToolboxTreeComponent = ng.core
}
that.notificationsService.setStatusMessage(
blockDescription + ' copied to marked spot. ' +
blockDescription + ' connected. ' +
'Now on copied block in workspace.');
});
}
+6 -5
View File
@@ -28,7 +28,7 @@ blocklyApp.ToolboxComponent = ng.core
selector: 'blockly-toolbox',
template: `
<div class="blocklyToolboxColumn">
<h3 #toolboxTitle id="blockly-toolbox-title">Toolbox</h3>
<h3 #toolboxTitle id="blockly-toolbox-title">{{'TOOLBOX'|translate}}</h3>
<ol #tree
id="blockly-toolbox-tree" role="tree" class="blocklyTree"
*ngIf="toolboxCategories && toolboxCategories.length > 0" tabindex="0"
@@ -40,7 +40,7 @@ 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-level="0"
[attr.aria-label]="getCategoryAriaLabel(category)">
<div *ngIf="category && category.attributes">
<label [id]="idMap['Label' + i]" #name>
@@ -48,7 +48,7 @@ blocklyApp.ToolboxComponent = ng.core
</label>
<ol role="group" *ngIf="getToolboxWorkspace(category).topBlocks_.length > 0">
<blockly-toolbox-tree *ngFor="#block of getToolboxWorkspace(category).topBlocks_"
[level]="2" [block]="block"
[level]="1" [block]="block"
[displayBlockMenu]="true"
[tree]="tree">
</blockly-toolbox-tree>
@@ -59,7 +59,7 @@ blocklyApp.ToolboxComponent = ng.core
<div *ngIf="!xmlHasCategories">
<blockly-toolbox-tree *ngFor="#block of getToolboxWorkspace(toolboxCategories[0]).topBlocks_; #i=index"
role="treeitem" [level]="1" [block]="block"
role="treeitem" [level]="0" [block]="block"
[tree]="tree" [displayBlockMenu]="true"
[isFirstToolboxTree]="i === 0">
</blockly-toolbox-tree>
@@ -67,7 +67,8 @@ blocklyApp.ToolboxComponent = ng.core
</ol>
</div>
`,
directives: [blocklyApp.ToolboxTreeComponent]
directives: [blocklyApp.ToolboxTreeComponent],
pipes: [blocklyApp.TranslatePipe]
})
.Class({
constructor: [
+37 -18
View File
@@ -440,23 +440,26 @@ blocklyApp.TreeService = ng.core
// Outside an input field, Enter, Tab and navigation keys are all
// recognized.
if (e.keyCode == 13) {
// Enter key. The user wants to interact with a button or an input
// field.
// Enter key. The user wants to interact with a button, interact with
// an input field, or move down one level.
// 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 found = false;
var dfsStack = Array.from(activeDesc.children);
while (dfsStack.length) {
var currentNode = dfsStack.shift();
if (currentNode.tagName == 'BUTTON') {
this.moveActiveDescToParent(treeId);
this.moveUpOneLevel_(treeId);
currentNode.click();
found = true;
break;
} else if (currentNode.tagName == 'INPUT') {
currentNode.focus();
currentNode.select();
this.notificationsService.setStatusMessage(
'Type a value, then press Escape to exit');
found = true;
break;
} else if (currentNode.tagName == 'LI') {
continue;
@@ -469,6 +472,12 @@ blocklyApp.TreeService = ng.core
});
}
}
// If we cannot find a field to interact with, we try moving down a
// level instead.
if (!found) {
this.moveDownOneLevel_(treeId);
}
} else if (e.keyCode == 9) {
// Tab key. Note that allowing the event to propagate through is
// intentional.
@@ -478,6 +487,8 @@ blocklyApp.TreeService = ng.core
if (destinationTreeId) {
this.notifyUserAboutCurrentTree_(destinationTreeId);
}
} else if (e.keyCode == 27) {
this.moveUpOneLevel_(treeId);
} else if (e.keyCode >= 35 && e.keyCode <= 40) {
// End, home, and arrow keys.
if (e.keyCode == 35) {
@@ -494,22 +505,22 @@ blocklyApp.TreeService = ng.core
}
} else if (e.keyCode == 37) {
// Left arrow key. Go up a level, if possible.
this.moveActiveDescToParent(treeId);
this.moveUpOneLevel_(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');
var statusMessage = 'Reached top of list.';
if (this.getParentListElement_(activeDesc)) {
statusMessage += ' Press left to go to parent list.';
}
this.notificationsService.setStatusMessage(statusMessage);
}
} else if (e.keyCode == 39) {
// Right arrow key. Go down a level, if possible.
var firstChild = this.getFirstChild(activeDesc);
if (firstChild) {
this.setActiveDesc(firstChild.id, treeId);
}
this.moveDownOneLevel_(treeId);
} else if (e.keyCode == 40) {
// Down arrow key. Go to the next sibling, if possible.
var nextSibling = this.getNextSibling(activeDesc);
@@ -517,7 +528,7 @@ blocklyApp.TreeService = ng.core
this.setActiveDesc(nextSibling.id, treeId);
} else {
this.notificationsService.setStatusMessage(
'Reached bottom of list');
'Reached bottom of list.');
}
}
@@ -526,19 +537,27 @@ blocklyApp.TreeService = ng.core
}
}
},
moveActiveDescToParent: function(treeId) {
moveDownOneLevel_: 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;
var firstChild = this.getFirstChild(activeDesc);
if (firstChild) {
this.setActiveDesc(firstChild.id, treeId);
}
},
moveUpOneLevel_: function(treeId) {
var activeDesc = document.getElementById(this.getActiveDescId(treeId));
var nextNode = this.getParentListElement_(activeDesc);
if (nextNode) {
this.setActiveDesc(nextNode.id, treeId);
}
},
getParentListElement_: function(element) {
var nextNode = element.parentNode;
while (nextNode && nextNode.tagName != 'LI') {
nextNode = nextNode.parentNode;
}
return nextNode;
},
getFirstChild: function(element) {
if (!element) {
return element;
+3 -7
View File
@@ -40,12 +40,8 @@ blocklyApp.UtilsService = ng.core
}
return idMap;
},
generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) {
var attrValue = mainLabel + (secondLabel ? ' ' + secondLabel : '');
if (isDisabled) {
attrValue += ' blockly-disabled';
}
return attrValue;
generateAriaLabelledByAttr: function(mainLabel, secondLabel) {
return mainLabel + (secondLabel ? ' ' + secondLabel : '');
},
getInputTypeLabel: function(connection) {
// Returns the input type name, or 'any' if any official input type
@@ -58,7 +54,7 @@ blocklyApp.UtilsService = ng.core
},
getBlockTypeLabel: function(inputBlock) {
if (inputBlock.type == Blockly.NEXT_STATEMENT) {
return Blockly.Msg.STATEMENT;
return Blockly.Msg.BLOCK;
} else {
return Blockly.Msg.VALUE;
}
+208 -119
View File
@@ -29,38 +29,42 @@ blocklyApp.WorkspaceTreeComponent = ng.core
selector: 'blockly-workspace-tree',
template: `
<li [id]="idMap['blockRoot']" role="treeitem" class="blocklyHasChildren"
[attr.aria-label]="getBlockDescription() + ' ' + ('WORKSPACE_BLOCK'|translate) + ' ' + ('SUBMENU_INDICATOR'|translate)"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['blockSummary'], 'blockly-workspace-block')"
[attr.aria-level]="level">
<label [id]="idMap['blockSummary']">{{getBlockDescription()}}</label>
<ol role="group">
<template ngFor #inputBlock [ngForOf]="block.inputList" #i="index">
<li role="treeitem" [id]="idMap['listItem' + i]" [attr.aria-level]="level + 1" *ngIf="inputBlock.fieldRow.length"
<template ngFor #blockInput [ngForOf]="block.inputList" #i="index">
<li role="treeitem" [id]="idMap['listItem' + i]" [attr.aria-level]="level + 1" *ngIf="blockInput.fieldRow.length"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['fieldLabel' + i])">
<blockly-field *ngFor="#field of inputBlock.fieldRow" [field]="field" [mainFieldId]="idMap['fieldLabel' + i]"
[level]="level + 2">
</blockly-field>
<blockly-field-segment *ngFor="#fieldSegment of inputListAsFieldSegments[i]"
[prefixFields]="fieldSegment.prefixFields"
[mainField]="fieldSegment.mainField"
[mainFieldId]="idMap['fieldLabel' + i]"
[level]="level + 2">
</blockly-field-segment>
</li>
<blockly-workspace-tree *ngIf="inputBlock.connection && inputBlock.connection.targetBlock()"
[block]="inputBlock.connection.targetBlock()" [level]="level + 1"
<blockly-workspace-tree *ngIf="blockInput.connection && blockInput.connection.targetBlock()"
[block]="blockInput.connection.targetBlock()" [level]="level + 1"
[tree]="tree">
</blockly-workspace-tree>
<li #inputList [id]="idMap['inputList' + i]" role="treeitem"
*ngIf="inputBlock.connection && !inputBlock.connection.targetBlock()"
*ngIf="blockInput.connection && !blockInput.connection.targetBlock()"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['inputMenuLabel' + i], 'blockly-submenu-indicator')"
[attr.aria-level]="level + 1">
<label [id]="idMap['inputMenuLabel' + i]">
{{utilsService.getInputTypeLabel(inputBlock.connection)}} {{utilsService.getBlockTypeLabel(inputBlock)}} needed:
{{utilsService.getInputTypeLabel(blockInput.connection)}} {{utilsService.getBlockTypeLabel(blockInput)}} needed:
</label>
<ol role="group">
<li *ngFor="#fieldButtonInfo of fieldButtonsInfo"
[id]="idMap[fieldButtonInfo.baseIdKey + i]" role="treeitem"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[fieldButtonInfo.baseIdKey + 'Button' + i], 'blockly-button', fieldButtonInfo.isDisabled(inputBlock.connection))"
[attr.aria-level]="level + 2">
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[fieldButtonInfo.baseIdKey + 'Button' + i], 'blockly-button')"
[attr.aria-level]="level + 2"
[attr.aria-disabled]="fieldButtonInfo.isDisabled(blockInput.connection)">
<button [id]="idMap[fieldButtonInfo.baseIdKey + 'Button' + i]"
(click)="fieldButtonInfo.action(inputBlock.connection)"
[disabled]="fieldButtonInfo.isDisabled(inputBlock.connection)" tabindex="-1">
(click)="fieldButtonInfo.action(blockInput.connection)"
[disabled]="fieldButtonInfo.isDisabled(blockInput.connection)" tabindex="-1">
{{fieldButtonInfo.translationIdForText|translate}}
</button>
</li>
@@ -75,8 +79,9 @@ blocklyApp.WorkspaceTreeComponent = ng.core
<ol role="group">
<li *ngFor="#buttonInfo of actionButtonsInfo"
[id]="idMap[buttonInfo.baseIdKey]" role="treeitem"
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[buttonInfo.baseIdKey + 'Button'], 'blockly-button', buttonInfo.isDisabled())"
[attr.aria-level]="level + 2">
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[buttonInfo.baseIdKey + 'Button'], 'blockly-button')"
[attr.aria-level]="level + 2"
[attr.aria-disabled]="buttonInfo.isDisabled()">
<button [id]="idMap[buttonInfo.baseIdKey + 'Button']" (click)="buttonInfo.action()"
[disabled]="buttonInfo.isDisabled()" tabindex="-1">
{{buttonInfo.translationIdForText|translate}}
@@ -92,7 +97,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core
[level]="level" [tree]="tree">
</blockly-workspace-tree>
`,
directives: [blocklyApp.FieldComponent, ng.core.forwardRef(function() {
directives: [blocklyApp.FieldSegmentComponent, ng.core.forwardRef(function() {
return blocklyApp.WorkspaceTreeComponent;
})],
inputs: ['block', 'level', 'tree', 'isTopLevel'],
@@ -112,8 +117,191 @@ blocklyApp.WorkspaceTreeComponent = ng.core
this.utilsService = _utilsService;
this.audioService = _audioService;
}],
ngOnInit: function() {
var SUPPORTED_FIELDS = [
Blockly.FieldTextInput, Blockly.FieldDropdown,
Blockly.FieldCheckbox];
this.inputListAsFieldSegments = this.block.inputList.map(function(input) {
// Converts the input to a list of field segments. Each field segment
// represents a user-editable field, prefixed by any number of
// non-editable fields.
var fieldSegments = [];
var bufferedFields = [];
input.fieldRow.forEach(function(field) {
var fieldIsSupported = SUPPORTED_FIELDS.some(function(fieldType) {
return (field instanceof fieldType);
});
if (fieldIsSupported) {
var fieldSegment = {
prefixFields: [],
mainField: field
};
bufferedFields.forEach(function(bufferedField) {
fieldSegment.prefixFields.push(bufferedField);
});
fieldSegments.push(fieldSegment);
bufferedFields = [];
} else {
bufferedFields.push(field);
}
});
// Handle leftover text at the end.
if (bufferedFields.length) {
fieldSegments.push({
prefixFields: bufferedFields,
mainField: null
});
}
return fieldSegments;
});
// Generate a list of action buttons.
var that = this;
this.actionButtonsInfo = [{
baseIdKey: 'copy',
translationIdForText: 'COPY_BLOCK',
action: function() {
that.clipboardService.copy(that.block);
that.notificationsService.setStatusMessage(
that.getBlockDescription() + ' ' + Blockly.Msg.COPIED_BLOCK_MSG);
},
isDisabled: function() {
return false;
}
}, {
baseIdKey: 'pasteBefore',
translationIdForText: 'PASTE_BEFORE',
action: function() {
that.treeService.pasteToConnection(
that.block, that.block.previousConnection);
},
isDisabled: function() {
return Boolean(
!that.block.previousConnection ||
!that.isCompatibleWithClipboard(that.block.previousConnection));
}
}, {
baseIdKey: 'pasteAfter',
translationIdForText: 'PASTE_AFTER',
action: function() {
that.treeService.pasteToConnection(
that.block, that.block.nextConnection);
},
isDisabled: function() {
return Boolean(
!that.block.nextConnection ||
!that.isCompatibleWithClipboard(that.block.nextConnection));
}
}, {
baseIdKey: 'markBefore',
translationIdForText: 'MARK_SPOT_BEFORE',
action: that.markSpotBefore_.bind(that),
isDisabled: function() {
return !that.block.previousConnection;
}
}, {
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.moveToMarkedSpot_.bind(that),
isDisabled: function() {
return !that.clipboardService.isMovableToMarkedConnection(
that.block);
}
}, {
baseIdKey: 'delete',
translationIdForText: 'DELETE',
action: that.deleteBlock_.bind(that),
isDisabled: function() {
return false;
}
}];
// 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_INSIDE',
action: function(connection) {
that.treeService.pasteToConnection(that.block, 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 blockInput = this.block.inputList[i];
that.idKeys.push(
'inputList' + i, 'inputMenuLabel' + i, 'listItem' + i,
'fieldLabel' + i);
}
},
ngDoCheck: function() {
// Generate a unique id for each id key. This needs to be done every time
// changes happen, but after the first ng-init, in order to force the
// element ids to change in cases where, e.g., a block is inserted in the
// middle of a sequence of blocks.
this.idMap = {};
for (var i = 0; i < this.idKeys.length; i++) {
this.idMap[this.idKeys[i]] = this.block.id + this.idKeys[i];
}
},
ngAfterViewInit: function() {
// If this is a top-level tree in the workspace, set its id and active
// descendant. (Note that a timeout is needed here in order to trigger
// Angular change detection.)
var that = this;
setTimeout(function() {
if (that.tree && that.isTopLevel && !that.tree.id) {
that.tree.id = that.utilsService.generateUniqueId();
}
if (that.tree && that.isTopLevel &&
!that.treeService.getActiveDescId(that.tree.id)) {
that.treeService.setActiveDesc(that.idMap['blockRoot'], that.tree.id);
}
});
},
getBlockDescription: function() {
return this.utilsService.getBlockDescription(this.block);
var blockDescription = this.utilsService.getBlockDescription(this.block);
var parentBlock = this.block.getSurroundParent();
if (parentBlock) {
var fullDescription = blockDescription + ' inside ' +
this.utilsService.getBlockDescription(parentBlock);
return fullDescription;
} else {
return blockDescription;
}
},
removeBlockAndSetFocus_: function(block, deleteBlockFunc) {
this.treeService.removeBlockAndSetFocus(
@@ -179,108 +367,9 @@ blocklyApp.WorkspaceTreeComponent = ng.core
markSpotAfter_: function() {
this.clipboardService.markConnection(this.block.nextConnection);
},
ngOnInit: function() {
var that = this;
// Generate a list of action buttons.
this.actionButtonsInfo = [{
baseIdKey: 'markBefore',
translationIdForText: 'MARK_SPOT_BEFORE',
action: that.markSpotBefore_.bind(that),
isDisabled: function() {
return !that.block.previousConnection;
}
}, {
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.moveToMarkedSpot_.bind(that),
isDisabled: function() {
return !that.clipboardService.isMovableToMarkedConnection(
that.block);
}
}, {
baseIdKey: 'delete',
translationIdForText: 'DELETE',
action: that.deleteBlock_.bind(that),
isDisabled: function() {
return false;
}
}];
// 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.treeService.pasteToConnection(that.block, 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];
that.idKeys.push(
'inputList' + i, 'inputMenuLabel' + i, 'listItem' + i,
'fieldLabel' + i);
}
},
ngDoCheck: function() {
// Generate a unique id for each id key. This needs to be done every time
// changes happen, but after the first ng-init, in order to force the
// element ids to change in cases where, e.g., a block is inserted in the
// middle of a sequence of blocks.
this.idMap = {};
for (var i = 0; i < this.idKeys.length; i++) {
this.idMap[this.idKeys[i]] = this.block.id + this.idKeys[i];
}
},
ngAfterViewInit: function() {
// If this is a top-level tree in the workspace, set its id and active
// descendant. (Note that a timeout is needed here in order to trigger
// Angular change detection.)
var that = this;
setTimeout(function() {
if (that.tree && that.isTopLevel && !that.tree.id) {
that.tree.id = that.utilsService.generateUniqueId();
}
if (that.tree && that.isTopLevel &&
!that.treeService.getActiveDescId(that.tree.id)) {
that.treeService.setActiveDesc(that.idMap['blockRoot'], that.tree.id);
}
});
},
generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) {
generateAriaLabelledByAttr: function(mainLabel, secondLabel) {
return this.utilsService.generateAriaLabelledByAttr(
mainLabel, secondLabel, isDisabled);
mainLabel, secondLabel);
},
isCompatibleWithClipboard: function(connection) {
return this.clipboardService.isCompatibleWithClipboard(connection);
+19 -4
View File
@@ -36,16 +36,22 @@ blocklyApp.WorkspaceComponent = ng.core
[attr.aria-activedescendant]="getActiveDescId(tree.id)"
[attr.aria-labelledby]="workspaceTitle.id"
(keydown)="onKeypress($event, tree)">
<blockly-workspace-tree [level]=1 [block]="block" [tree]="tree" [isTopLevel]="true">
<blockly-workspace-tree [level]="0" [block]="block" [tree]="tree" [isTopLevel]="true">
</blockly-workspace-tree>
</ol>
<span *ngIf="workspace.topBlocks_.length === 0">
<i>Workspace is empty.</i>
</span>
</div>
</div>
<div class="blocklyToolbarColumn">
<div id="blockly-workspace-toolbar" (keydown)="onWorkspaceToolbarKeypress($event)">
<span *ngFor="#buttonConfig of toolbarButtonConfig">
<button (click)="buttonConfig.action()"
<button *ngIf="!buttonConfig.isHidden()"
(click)="handleButtonClick(buttonConfig)"
[attr.aria-describedby]="buttonConfig.ariaDescribedBy"
class="blocklyTree blocklyWorkspaceToolbarButton">
{{buttonConfig.text}}
</button>
@@ -63,8 +69,9 @@ blocklyApp.WorkspaceComponent = ng.core
})
.Class({
constructor: [
blocklyApp.TreeService, blocklyApp.UtilsService,
function(_treeService, _utilsService) {
blocklyApp.NotificationsService, blocklyApp.TreeService,
blocklyApp.UtilsService,
function(_notificationsService, _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'.
@@ -74,6 +81,7 @@ blocklyApp.WorkspaceComponent = ng.core
ACCESSIBLE_GLOBALS && ACCESSIBLE_GLOBALS.toolbarButtonConfig ?
ACCESSIBLE_GLOBALS.toolbarButtonConfig : [];
this.workspace = blocklyApp.workspace;
this.notificationsService = _notificationsService;
this.treeService = _treeService;
this.utilsService = _utilsService;
}],
@@ -83,6 +91,13 @@ blocklyApp.WorkspaceComponent = ng.core
getActiveDescId: function(treeId) {
return this.treeService.getActiveDescId(treeId);
},
handleButtonClick: function(buttonConfig) {
buttonConfig.action();
if (buttonConfig.onClickNotification) {
this.notificationsService.setStatusMessage(
buttonConfig.onClickNotification);
}
},
onWorkspaceToolbarKeypress: function(e) {
this.treeService.onWorkspaceToolbarKeypress(
e, document.activeElement.id);
+169 -160
View File
@@ -76,7 +76,9 @@ goog.asserts.assertString=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isS
goog.asserts.assertObject=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isObject(a)&&goog.asserts.doAssertFailure_("Expected object but got %s: %s.",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};goog.asserts.assertArray=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isArray(a)&&goog.asserts.doAssertFailure_("Expected array but got %s: %s.",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};
goog.asserts.assertBoolean=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isBoolean(a)&&goog.asserts.doAssertFailure_("Expected boolean but got %s: %s.",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};goog.asserts.assertElement=function(a,b,c){!goog.asserts.ENABLE_ASSERTS||goog.isObject(a)&&a.nodeType==goog.dom.NodeType.ELEMENT||goog.asserts.doAssertFailure_("Expected Element but got %s: %s.",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};
goog.asserts.assertInstanceof=function(a,b,c,d){!goog.asserts.ENABLE_ASSERTS||a instanceof b||goog.asserts.doAssertFailure_("Expected instanceof %s but got %s.",[goog.asserts.getType_(b),goog.asserts.getType_(a)],c,Array.prototype.slice.call(arguments,3));return a};goog.asserts.assertObjectPrototypeIsIntact=function(){for(var a in Object.prototype)goog.asserts.fail(a+" should not be enumerable in Object.prototype.")};
goog.asserts.getType_=function(a){return a instanceof Function?a.displayName||a.name||"unknown type name":a instanceof Object?a.constructor.displayName||a.constructor.name||Object.prototype.toString.call(a):null===a?"null":typeof a};goog.array={};goog.NATIVE_ARRAY_PROTOTYPES=goog.TRUSTED_SITE;goog.array.ASSUME_NATIVE_FUNCTIONS=!1;goog.array.peek=function(a){return a[a.length-1]};goog.array.last=goog.array.peek;
goog.asserts.getType_=function(a){return a instanceof Function?a.displayName||a.name||"unknown type name":a instanceof Object?a.constructor.displayName||a.constructor.name||Object.prototype.toString.call(a):null===a?"null":typeof a};goog.debug.entryPointRegistry={};goog.debug.EntryPointMonitor=function(){};goog.debug.entryPointRegistry.refList_=[];goog.debug.entryPointRegistry.monitors_=[];goog.debug.entryPointRegistry.monitorsMayExist_=!1;goog.debug.entryPointRegistry.register=function(a){goog.debug.entryPointRegistry.refList_[goog.debug.entryPointRegistry.refList_.length]=a;if(goog.debug.entryPointRegistry.monitorsMayExist_)for(var b=goog.debug.entryPointRegistry.monitors_,c=0;c<b.length;c++)a(goog.bind(b[c].wrap,b[c]))};
goog.debug.entryPointRegistry.monitorAll=function(a){goog.debug.entryPointRegistry.monitorsMayExist_=!0;for(var b=goog.bind(a.wrap,a),c=0;c<goog.debug.entryPointRegistry.refList_.length;c++)goog.debug.entryPointRegistry.refList_[c](b);goog.debug.entryPointRegistry.monitors_.push(a)};
goog.debug.entryPointRegistry.unmonitorAllIfPossible=function(a){var b=goog.debug.entryPointRegistry.monitors_;goog.asserts.assert(a==b[b.length-1],"Only the most recent monitor can be unwrapped.");a=goog.bind(a.unwrap,a);for(var c=0;c<goog.debug.entryPointRegistry.refList_.length;c++)goog.debug.entryPointRegistry.refList_[c](a);b.length--};goog.array={};goog.NATIVE_ARRAY_PROTOTYPES=goog.TRUSTED_SITE;goog.array.ASSUME_NATIVE_FUNCTIONS=!1;goog.array.peek=function(a){return a[a.length-1]};goog.array.last=goog.array.peek;
goog.array.indexOf=goog.NATIVE_ARRAY_PROTOTYPES&&(goog.array.ASSUME_NATIVE_FUNCTIONS||Array.prototype.indexOf)?function(a,b,c){goog.asserts.assert(null!=a.length);return Array.prototype.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(goog.isString(a))return goog.isString(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1};
goog.array.lastIndexOf=goog.NATIVE_ARRAY_PROTOTYPES&&(goog.array.ASSUME_NATIVE_FUNCTIONS||Array.prototype.lastIndexOf)?function(a,b,c){goog.asserts.assert(null!=a.length);return Array.prototype.lastIndexOf.call(a,b,null==c?a.length-1:c)}:function(a,b,c){c=null==c?a.length-1:c;0>c&&(c=Math.max(0,a.length+c));if(goog.isString(a))return goog.isString(b)&&1==b.length?a.lastIndexOf(b,c):-1;for(;0<=c;c--)if(c in a&&a[c]===b)return c;return-1};
goog.array.forEach=goog.NATIVE_ARRAY_PROTOTYPES&&(goog.array.ASSUME_NATIVE_FUNCTIONS||Array.prototype.forEach)?function(a,b,c){goog.asserts.assert(null!=a.length);Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)};goog.array.forEachRight=function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(""):a,d=d-1;0<=d;--d)d in e&&b.call(c,e[d],d,a)};
@@ -101,13 +103,7 @@ goog.array.binaryInsert=function(a,b,c){c=goog.array.binarySearch(a,b,c);return
goog.array.range=function(a,b,c){var d=[],e=0,f=a;c=c||1;void 0!==b&&(e=a,f=b);if(0>c*(f-e))return[];if(0<c)for(a=e;a<f;a+=c)d.push(a);else for(a=e;a>f;a+=c)d.push(a);return d};goog.array.repeat=function(a,b){for(var c=[],d=0;d<b;d++)c[d]=a;return c};goog.array.flatten=function(a){for(var b=[],c=0;c<arguments.length;c++){var d=arguments[c];if(goog.isArray(d))for(var e=0;e<d.length;e+=8192)for(var f=goog.array.slice(d,e,e+8192),f=goog.array.flatten.apply(null,f),g=0;g<f.length;g++)b.push(f[g]);else b.push(d)}return b};
goog.array.rotate=function(a,b){goog.asserts.assert(null!=a.length);a.length&&(b%=a.length,0<b?Array.prototype.unshift.apply(a,a.splice(-b,b)):0>b&&Array.prototype.push.apply(a,a.splice(0,-b)));return a};goog.array.moveItem=function(a,b,c){goog.asserts.assert(0<=b&&b<a.length);goog.asserts.assert(0<=c&&c<a.length);b=Array.prototype.splice.call(a,b,1);Array.prototype.splice.call(a,c,0,b[0])};
goog.array.zip=function(a){if(!arguments.length)return[];for(var b=[],c=arguments[0].length,d=1;d<arguments.length;d++)arguments[d].length<c&&(c=arguments[d].length);for(d=0;d<c;d++){for(var e=[],f=0;f<arguments.length;f++)e.push(arguments[f][d]);b.push(e)}return b};goog.array.shuffle=function(a,b){for(var c=b||Math.random,d=a.length-1;0<d;d--){var e=Math.floor(c()*(d+1)),f=a[d];a[d]=a[e];a[e]=f}};goog.array.copyByIndex=function(a,b){var c=[];goog.array.forEach(b,function(b){c.push(a[b])});return c};
goog.array.concatMap=function(a,b,c){return goog.array.concat.apply([],goog.array.map(a,b,c))};goog.math={};goog.math.randomInt=function(a){return Math.floor(Math.random()*a)};goog.math.uniformRandom=function(a,b){return a+Math.random()*(b-a)};goog.math.clamp=function(a,b,c){return Math.min(Math.max(a,b),c)};goog.math.modulo=function(a,b){var c=a%b;return 0>c*b?c+b:c};goog.math.lerp=function(a,b,c){return a+c*(b-a)};goog.math.nearlyEquals=function(a,b,c){return Math.abs(a-b)<=(c||1E-6)};goog.math.standardAngle=function(a){return goog.math.modulo(a,360)};
goog.math.standardAngleInRadians=function(a){return goog.math.modulo(a,2*Math.PI)};goog.math.toRadians=function(a){return a*Math.PI/180};goog.math.toDegrees=function(a){return 180*a/Math.PI};goog.math.angleDx=function(a,b){return b*Math.cos(goog.math.toRadians(a))};goog.math.angleDy=function(a,b){return b*Math.sin(goog.math.toRadians(a))};goog.math.angle=function(a,b,c,d){return goog.math.standardAngle(goog.math.toDegrees(Math.atan2(d-b,c-a)))};
goog.math.angleDifference=function(a,b){var c=goog.math.standardAngle(b)-goog.math.standardAngle(a);180<c?c-=360:-180>=c&&(c=360+c);return c};goog.math.sign=Math.sign||function(a){return 0<a?1:0>a?-1:a};
goog.math.longestCommonSubsequence=function(a,b,c,d){c=c||function(a,b){return a==b};d=d||function(b,c){return a[b]};for(var e=a.length,f=b.length,g=[],h=0;h<e+1;h++)g[h]=[],g[h][0]=0;for(var k=0;k<f+1;k++)g[0][k]=0;for(h=1;h<=e;h++)for(k=1;k<=f;k++)c(a[h-1],b[k-1])?g[h][k]=g[h-1][k-1]+1:g[h][k]=Math.max(g[h-1][k],g[h][k-1]);for(var m=[],h=e,k=f;0<h&&0<k;)c(a[h-1],b[k-1])?(m.unshift(d(h-1,k-1)),h--,k--):g[h-1][k]>g[h][k-1]?h--:k--;return m};
goog.math.sum=function(a){return goog.array.reduce(arguments,function(a,c){return a+c},0)};goog.math.average=function(a){return goog.math.sum.apply(null,arguments)/arguments.length};goog.math.sampleVariance=function(a){var b=arguments.length;if(2>b)return 0;var c=goog.math.average.apply(null,arguments);return goog.math.sum.apply(null,goog.array.map(arguments,function(a){return Math.pow(a-c,2)}))/(b-1)};goog.math.standardDeviation=function(a){return Math.sqrt(goog.math.sampleVariance.apply(null,arguments))};
goog.math.isInt=function(a){return isFinite(a)&&0==a%1};goog.math.isFiniteNumber=function(a){return isFinite(a)&&!isNaN(a)};goog.math.isNegativeZero=function(a){return 0==a&&0>1/a};goog.math.log10Floor=function(a){if(0<a){var b=Math.round(Math.log(a)*Math.LOG10E);return b-(parseFloat("1e"+b)>a?1:0)}return 0==a?-Infinity:NaN};goog.math.safeFloor=function(a,b){goog.asserts.assert(!goog.isDef(b)||0<b);return Math.floor(a+(b||2E-15))};
goog.math.safeCeil=function(a,b){goog.asserts.assert(!goog.isDef(b)||0<b);return Math.ceil(a-(b||2E-15))};goog.labs={};goog.labs.userAgent={};goog.labs.userAgent.util={};goog.labs.userAgent.util.getNativeUserAgentString_=function(){var a=goog.labs.userAgent.util.getNavigator_();return a&&(a=a.userAgent)?a:""};goog.labs.userAgent.util.getNavigator_=function(){return goog.global.navigator};goog.labs.userAgent.util.userAgent_=goog.labs.userAgent.util.getNativeUserAgentString_();goog.labs.userAgent.util.setUserAgent=function(a){goog.labs.userAgent.util.userAgent_=a||goog.labs.userAgent.util.getNativeUserAgentString_()};
goog.array.concatMap=function(a,b,c){return goog.array.concat.apply([],goog.array.map(a,b,c))};goog.labs={};goog.labs.userAgent={};goog.labs.userAgent.util={};goog.labs.userAgent.util.getNativeUserAgentString_=function(){var a=goog.labs.userAgent.util.getNavigator_();return a&&(a=a.userAgent)?a:""};goog.labs.userAgent.util.getNavigator_=function(){return goog.global.navigator};goog.labs.userAgent.util.userAgent_=goog.labs.userAgent.util.getNativeUserAgentString_();goog.labs.userAgent.util.setUserAgent=function(a){goog.labs.userAgent.util.userAgent_=a||goog.labs.userAgent.util.getNativeUserAgentString_()};
goog.labs.userAgent.util.getUserAgent=function(){return goog.labs.userAgent.util.userAgent_};goog.labs.userAgent.util.matchUserAgent=function(a){var b=goog.labs.userAgent.util.getUserAgent();return goog.string.contains(b,a)};goog.labs.userAgent.util.matchUserAgentIgnoreCase=function(a){var b=goog.labs.userAgent.util.getUserAgent();return goog.string.caseInsensitiveContains(b,a)};
goog.labs.userAgent.util.extractVersionTuples=function(a){for(var b=RegExp("(\\w[\\w ]+)/([^\\s]+)\\s*(?:\\((.*?)\\))?","g"),c=[],d;d=b.exec(a);)c.push([d[1],d[2],d[3]||void 0]);return c};goog.object={};goog.object.is=function(a,b){return a===b?0!==a||1/a===1/b:a!==a&&b!==b};goog.object.forEach=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)};goog.object.filter=function(a,b,c){var d={},e;for(e in a)b.call(c,a[e],e,a)&&(d[e]=a[e]);return d};goog.object.map=function(a,b,c){var d={},e;for(e in a)d[e]=b.call(c,a[e],e,a);return d};goog.object.some=function(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return!0;return!1};
goog.object.every=function(a,b,c){for(var d in a)if(!b.call(c,a[d],d,a))return!1;return!0};goog.object.getCount=function(a){var b=0,c;for(c in a)b++;return b};goog.object.getAnyKey=function(a){for(var b in a)return b};goog.object.getAnyValue=function(a){for(var b in a)return a[b]};goog.object.contains=function(a,b){return goog.object.containsValue(a,b)};goog.object.getValues=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b};
@@ -142,7 +138,54 @@ goog.userAgent.ANDROID=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_ANDR
goog.userAgent.determineVersion_=function(){var a="",b=goog.userAgent.getVersionRegexResult_();b&&(a=b?b[1]:"");return goog.userAgent.IE&&(b=goog.userAgent.getDocumentMode_(),null!=b&&b>parseFloat(a))?String(b):a};
goog.userAgent.getVersionRegexResult_=function(){var a=goog.userAgent.getUserAgentString();if(goog.userAgent.GECKO)return/rv\:([^\);]+)(\)|;)/.exec(a);if(goog.userAgent.EDGE)return/Edge\/([\d\.]+)/.exec(a);if(goog.userAgent.IE)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(goog.userAgent.WEBKIT)return/WebKit\/(\S+)/.exec(a);if(goog.userAgent.OPERA)return/(?:Version)[ \/]?(\S+)/.exec(a)};goog.userAgent.getDocumentMode_=function(){var a=goog.global.document;return a?a.documentMode:void 0};
goog.userAgent.VERSION=goog.userAgent.determineVersion_();goog.userAgent.compare=function(a,b){return goog.string.compareVersions(a,b)};goog.userAgent.isVersionOrHigherCache_={};goog.userAgent.isVersionOrHigher=function(a){return goog.userAgent.ASSUME_ANY_VERSION||goog.reflect.cache(goog.userAgent.isVersionOrHigherCache_,a,function(){return 0<=goog.string.compareVersions(goog.userAgent.VERSION,a)})};goog.userAgent.isVersion=goog.userAgent.isVersionOrHigher;
goog.userAgent.isDocumentModeOrHigher=function(a){return Number(goog.userAgent.DOCUMENT_MODE)>=a};goog.userAgent.isDocumentMode=goog.userAgent.isDocumentModeOrHigher;goog.userAgent.DOCUMENT_MODE=function(){var a=goog.global.document,b=goog.userAgent.getDocumentMode_();return a&&goog.userAgent.IE?b||("CSS1Compat"==a.compatMode?parseInt(goog.userAgent.VERSION,10):5):void 0}();goog.dom.BrowserFeature={CAN_ADD_NAME_OR_TYPE_ATTRIBUTES:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),CAN_USE_CHILDREN_ATTRIBUTE:!goog.userAgent.GECKO&&!goog.userAgent.IE||goog.userAgent.IE&&goog.userAgent.isDocumentModeOrHigher(9)||goog.userAgent.GECKO&&goog.userAgent.isVersionOrHigher("1.9.1"),CAN_USE_INNER_TEXT:goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher("9"),CAN_USE_PARENT_ELEMENT_PROPERTY:goog.userAgent.IE||goog.userAgent.OPERA||goog.userAgent.WEBKIT,INNER_HTML_NEEDS_SCOPED_ELEMENT:goog.userAgent.IE,
goog.userAgent.isDocumentModeOrHigher=function(a){return Number(goog.userAgent.DOCUMENT_MODE)>=a};goog.userAgent.isDocumentMode=goog.userAgent.isDocumentModeOrHigher;goog.userAgent.DOCUMENT_MODE=function(){var a=goog.global.document,b=goog.userAgent.getDocumentMode_();return a&&goog.userAgent.IE?b||("CSS1Compat"==a.compatMode?parseInt(goog.userAgent.VERSION,10):5):void 0}();goog.events={};
goog.events.BrowserFeature={HAS_W3C_BUTTON:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),HAS_W3C_EVENT_SUPPORT:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),SET_KEY_CODE_TO_PREVENT_DEFAULT:goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher("9"),HAS_NAVIGATOR_ONLINE_PROPERTY:!goog.userAgent.WEBKIT||goog.userAgent.isVersionOrHigher("528"),HAS_HTML5_NETWORK_EVENT_SUPPORT:goog.userAgent.GECKO&&goog.userAgent.isVersionOrHigher("1.9b")||goog.userAgent.IE&&goog.userAgent.isVersionOrHigher("8")||
goog.userAgent.OPERA&&goog.userAgent.isVersionOrHigher("9.5")||goog.userAgent.WEBKIT&&goog.userAgent.isVersionOrHigher("528"),HTML5_NETWORK_EVENTS_FIRE_ON_BODY:goog.userAgent.GECKO&&!goog.userAgent.isVersionOrHigher("8")||goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher("9"),TOUCH_ENABLED:"ontouchstart"in goog.global||!!(goog.global.document&&document.documentElement&&"ontouchstart"in document.documentElement)||!(!goog.global.navigator||!goog.global.navigator.msMaxTouchPoints)};goog.disposable={};goog.disposable.IDisposable=function(){};goog.disposable.IDisposable.prototype.dispose=goog.abstractMethod;goog.disposable.IDisposable.prototype.isDisposed=goog.abstractMethod;goog.Disposable=function(){goog.Disposable.MONITORING_MODE!=goog.Disposable.MonitoringMode.OFF&&(goog.Disposable.INCLUDE_STACK_ON_CREATION&&(this.creationStack=Error().stack),goog.Disposable.instances_[goog.getUid(this)]=this);this.disposed_=this.disposed_;this.onDisposeCallbacks_=this.onDisposeCallbacks_};goog.Disposable.MonitoringMode={OFF:0,PERMANENT:1,INTERACTIVE:2};goog.Disposable.MONITORING_MODE=0;goog.Disposable.INCLUDE_STACK_ON_CREATION=!0;goog.Disposable.instances_={};
goog.Disposable.getUndisposedObjects=function(){var a=[],b;for(b in goog.Disposable.instances_)goog.Disposable.instances_.hasOwnProperty(b)&&a.push(goog.Disposable.instances_[Number(b)]);return a};goog.Disposable.clearUndisposedObjects=function(){goog.Disposable.instances_={}};goog.Disposable.prototype.disposed_=!1;goog.Disposable.prototype.isDisposed=function(){return this.disposed_};goog.Disposable.prototype.getDisposed=goog.Disposable.prototype.isDisposed;
goog.Disposable.prototype.dispose=function(){if(!this.disposed_&&(this.disposed_=!0,this.disposeInternal(),goog.Disposable.MONITORING_MODE!=goog.Disposable.MonitoringMode.OFF)){var a=goog.getUid(this);if(goog.Disposable.MONITORING_MODE==goog.Disposable.MonitoringMode.PERMANENT&&!goog.Disposable.instances_.hasOwnProperty(a))throw Error(this+" did not call the goog.Disposable base constructor or was disposed of after a clearUndisposedObjects call");delete goog.Disposable.instances_[a]}};
goog.Disposable.prototype.registerDisposable=function(a){this.addOnDisposeCallback(goog.partial(goog.dispose,a))};goog.Disposable.prototype.addOnDisposeCallback=function(a,b){this.disposed_?goog.isDef(b)?a.call(b):a():(this.onDisposeCallbacks_||(this.onDisposeCallbacks_=[]),this.onDisposeCallbacks_.push(goog.isDef(b)?goog.bind(a,b):a))};goog.Disposable.prototype.disposeInternal=function(){if(this.onDisposeCallbacks_)for(;this.onDisposeCallbacks_.length;)this.onDisposeCallbacks_.shift()()};
goog.Disposable.isDisposed=function(a){return a&&"function"==typeof a.isDisposed?a.isDisposed():!1};goog.dispose=function(a){a&&"function"==typeof a.dispose&&a.dispose()};goog.disposeAll=function(a){for(var b=0,c=arguments.length;b<c;++b){var d=arguments[b];goog.isArrayLike(d)?goog.disposeAll.apply(null,d):goog.dispose(d)}};goog.events.EventId=function(a){this.id=a};goog.events.EventId.prototype.toString=function(){return this.id};goog.events.Event=function(a,b){this.type=a instanceof goog.events.EventId?String(a):a;this.currentTarget=this.target=b;this.defaultPrevented=this.propagationStopped_=!1;this.returnValue_=!0};goog.events.Event.prototype.stopPropagation=function(){this.propagationStopped_=!0};goog.events.Event.prototype.preventDefault=function(){this.defaultPrevented=!0;this.returnValue_=!1};goog.events.Event.stopPropagation=function(a){a.stopPropagation()};goog.events.Event.preventDefault=function(a){a.preventDefault()};goog.events.getVendorPrefixedName_=function(a){return goog.userAgent.WEBKIT?"webkit"+a:goog.userAgent.OPERA?"o"+a.toLowerCase():a.toLowerCase()};
goog.events.EventType={CLICK:"click",RIGHTCLICK:"rightclick",DBLCLICK:"dblclick",MOUSEDOWN:"mousedown",MOUSEUP:"mouseup",MOUSEOVER:"mouseover",MOUSEOUT:"mouseout",MOUSEMOVE:"mousemove",MOUSEENTER:"mouseenter",MOUSELEAVE:"mouseleave",SELECTSTART:"selectstart",WHEEL:"wheel",KEYPRESS:"keypress",KEYDOWN:"keydown",KEYUP:"keyup",BLUR:"blur",FOCUS:"focus",DEACTIVATE:"deactivate",FOCUSIN:goog.userAgent.IE?"focusin":"DOMFocusIn",FOCUSOUT:goog.userAgent.IE?"focusout":"DOMFocusOut",CHANGE:"change",RESET:"reset",
SELECT:"select",SUBMIT:"submit",INPUT:"input",PROPERTYCHANGE:"propertychange",DRAGSTART:"dragstart",DRAG:"drag",DRAGENTER:"dragenter",DRAGOVER:"dragover",DRAGLEAVE:"dragleave",DROP:"drop",DRAGEND:"dragend",TOUCHSTART:"touchstart",TOUCHMOVE:"touchmove",TOUCHEND:"touchend",TOUCHCANCEL:"touchcancel",BEFOREUNLOAD:"beforeunload",CONSOLEMESSAGE:"consolemessage",CONTEXTMENU:"contextmenu",DOMCONTENTLOADED:"DOMContentLoaded",ERROR:"error",HELP:"help",LOAD:"load",LOSECAPTURE:"losecapture",ORIENTATIONCHANGE:"orientationchange",
READYSTATECHANGE:"readystatechange",RESIZE:"resize",SCROLL:"scroll",UNLOAD:"unload",CANPLAY:"canplay",CANPLAYTHROUGH:"canplaythrough",DURATIONCHANGE:"durationchange",EMPTIED:"emptied",ENDED:"ended",LOADEDDATA:"loadeddata",LOADEDMETADATA:"loadedmetadata",PAUSE:"pause",PLAY:"play",PLAYING:"playing",RATECHANGE:"ratechange",SEEKED:"seeked",SEEKING:"seeking",STALLED:"stalled",SUSPEND:"suspend",TIMEUPDATE:"timeupdate",VOLUMECHANGE:"volumechange",WAITING:"waiting",HASHCHANGE:"hashchange",PAGEHIDE:"pagehide",
PAGESHOW:"pageshow",POPSTATE:"popstate",COPY:"copy",PASTE:"paste",CUT:"cut",BEFORECOPY:"beforecopy",BEFORECUT:"beforecut",BEFOREPASTE:"beforepaste",ONLINE:"online",OFFLINE:"offline",MESSAGE:"message",CONNECT:"connect",ANIMATIONSTART:goog.events.getVendorPrefixedName_("AnimationStart"),ANIMATIONEND:goog.events.getVendorPrefixedName_("AnimationEnd"),ANIMATIONITERATION:goog.events.getVendorPrefixedName_("AnimationIteration"),TRANSITIONEND:goog.events.getVendorPrefixedName_("TransitionEnd"),POINTERDOWN:"pointerdown",
POINTERUP:"pointerup",POINTERCANCEL:"pointercancel",POINTERMOVE:"pointermove",POINTEROVER:"pointerover",POINTEROUT:"pointerout",POINTERENTER:"pointerenter",POINTERLEAVE:"pointerleave",GOTPOINTERCAPTURE:"gotpointercapture",LOSTPOINTERCAPTURE:"lostpointercapture",MSGESTURECHANGE:"MSGestureChange",MSGESTUREEND:"MSGestureEnd",MSGESTUREHOLD:"MSGestureHold",MSGESTURESTART:"MSGestureStart",MSGESTURETAP:"MSGestureTap",MSGOTPOINTERCAPTURE:"MSGotPointerCapture",MSINERTIASTART:"MSInertiaStart",MSLOSTPOINTERCAPTURE:"MSLostPointerCapture",
MSPOINTERCANCEL:"MSPointerCancel",MSPOINTERDOWN:"MSPointerDown",MSPOINTERENTER:"MSPointerEnter",MSPOINTERHOVER:"MSPointerHover",MSPOINTERLEAVE:"MSPointerLeave",MSPOINTERMOVE:"MSPointerMove",MSPOINTEROUT:"MSPointerOut",MSPOINTEROVER:"MSPointerOver",MSPOINTERUP:"MSPointerUp",TEXT:"text",TEXTINPUT:"textInput",COMPOSITIONSTART:"compositionstart",COMPOSITIONUPDATE:"compositionupdate",COMPOSITIONEND:"compositionend",EXIT:"exit",LOADABORT:"loadabort",LOADCOMMIT:"loadcommit",LOADREDIRECT:"loadredirect",LOADSTART:"loadstart",
LOADSTOP:"loadstop",RESPONSIVE:"responsive",SIZECHANGED:"sizechanged",UNRESPONSIVE:"unresponsive",VISIBILITYCHANGE:"visibilitychange",STORAGE:"storage",DOMSUBTREEMODIFIED:"DOMSubtreeModified",DOMNODEINSERTED:"DOMNodeInserted",DOMNODEREMOVED:"DOMNodeRemoved",DOMNODEREMOVEDFROMDOCUMENT:"DOMNodeRemovedFromDocument",DOMNODEINSERTEDINTODOCUMENT:"DOMNodeInsertedIntoDocument",DOMATTRMODIFIED:"DOMAttrModified",DOMCHARACTERDATAMODIFIED:"DOMCharacterDataModified",BEFOREPRINT:"beforeprint",AFTERPRINT:"afterprint"};goog.events.BrowserEvent=function(a,b){goog.events.Event.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.state=null;this.platformModifierKey=!1;this.event_=null;a&&this.init(a,b)};goog.inherits(goog.events.BrowserEvent,goog.events.Event);
goog.events.BrowserEvent.MouseButton={LEFT:0,MIDDLE:1,RIGHT:2};goog.events.BrowserEvent.IEButtonMap=[1,4,2];
goog.events.BrowserEvent.prototype.init=function(a,b){var c=this.type=a.type,d=a.changedTouches?a.changedTouches[0]:null;this.target=a.target||a.srcElement;this.currentTarget=b;var e=a.relatedTarget;e?goog.userAgent.GECKO&&(goog.reflect.canAccessProperty(e,"nodeName")||(e=null)):c==goog.events.EventType.MOUSEOVER?e=a.fromElement:c==goog.events.EventType.MOUSEOUT&&(e=a.toElement);this.relatedTarget=e;goog.isNull(d)?(this.offsetX=goog.userAgent.WEBKIT||void 0!==a.offsetX?a.offsetX:a.layerX,this.offsetY=
goog.userAgent.WEBKIT||void 0!==a.offsetY?a.offsetY:a.layerY,this.clientX=void 0!==a.clientX?a.clientX:a.pageX,this.clientY=void 0!==a.clientY?a.clientY:a.pageY,this.screenX=a.screenX||0,this.screenY=a.screenY||0):(this.clientX=void 0!==d.clientX?d.clientX:d.pageX,this.clientY=void 0!==d.clientY?d.clientY:d.pageY,this.screenX=d.screenX||0,this.screenY=d.screenY||0);this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=
a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.platformModifierKey=goog.userAgent.MAC?a.metaKey:a.ctrlKey;this.state=a.state;this.event_=a;a.defaultPrevented&&this.preventDefault()};goog.events.BrowserEvent.prototype.isButton=function(a){return goog.events.BrowserFeature.HAS_W3C_BUTTON?this.event_.button==a:"click"==this.type?a==goog.events.BrowserEvent.MouseButton.LEFT:!!(this.event_.button&goog.events.BrowserEvent.IEButtonMap[a])};
goog.events.BrowserEvent.prototype.isMouseActionButton=function(){return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT)&&!(goog.userAgent.WEBKIT&&goog.userAgent.MAC&&this.ctrlKey)};goog.events.BrowserEvent.prototype.stopPropagation=function(){goog.events.BrowserEvent.superClass_.stopPropagation.call(this);this.event_.stopPropagation?this.event_.stopPropagation():this.event_.cancelBubble=!0};
goog.events.BrowserEvent.prototype.preventDefault=function(){goog.events.BrowserEvent.superClass_.preventDefault.call(this);var a=this.event_;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};goog.events.BrowserEvent.prototype.getBrowserEvent=function(){return this.event_};goog.events.Listenable=function(){};goog.events.Listenable.IMPLEMENTED_BY_PROP="closure_listenable_"+(1E6*Math.random()|0);goog.events.Listenable.addImplementation=function(a){a.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP]=!0};goog.events.Listenable.isImplementedBy=function(a){return!(!a||!a[goog.events.Listenable.IMPLEMENTED_BY_PROP])};goog.events.ListenableKey=function(){};goog.events.ListenableKey.counter_=0;goog.events.ListenableKey.reserveKey=function(){return++goog.events.ListenableKey.counter_};goog.events.Listener=function(a,b,c,d,e,f){goog.events.Listener.ENABLE_MONITORING&&(this.creationStack=Error().stack);this.listener=a;this.proxy=b;this.src=c;this.type=d;this.capture=!!e;this.handler=f;this.key=goog.events.ListenableKey.reserveKey();this.removed=this.callOnce=!1};goog.events.Listener.ENABLE_MONITORING=!1;goog.events.Listener.prototype.markAsRemoved=function(){this.removed=!0;this.handler=this.src=this.proxy=this.listener=null};goog.events.ListenerMap=function(a){this.src=a;this.listeners={};this.typeCount_=0};goog.events.ListenerMap.prototype.getTypeCount=function(){return this.typeCount_};goog.events.ListenerMap.prototype.getListenerCount=function(){var a=0,b;for(b in this.listeners)a+=this.listeners[b].length;return a};
goog.events.ListenerMap.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.listeners[f];a||(a=this.listeners[f]=[],this.typeCount_++);var g=goog.events.ListenerMap.findListenerIndex_(a,b,d,e);-1<g?(b=a[g],c||(b.callOnce=!1)):(b=new goog.events.Listener(b,null,this.src,f,!!d,e),b.callOnce=c,a.push(b));return b};
goog.events.ListenerMap.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.listeners))return!1;var e=this.listeners[a];b=goog.events.ListenerMap.findListenerIndex_(e,b,c,d);return-1<b?(e[b].markAsRemoved(),goog.array.removeAt(e,b),0==e.length&&(delete this.listeners[a],this.typeCount_--),!0):!1};
goog.events.ListenerMap.prototype.removeByKey=function(a){var b=a.type;if(!(b in this.listeners))return!1;var c=goog.array.remove(this.listeners[b],a);c&&(a.markAsRemoved(),0==this.listeners[b].length&&(delete this.listeners[b],this.typeCount_--));return c};goog.events.ListenerMap.prototype.removeAll=function(a){a=a&&a.toString();var b=0,c;for(c in this.listeners)if(!a||c==a){for(var d=this.listeners[c],e=0;e<d.length;e++)++b,d[e].markAsRemoved();delete this.listeners[c];this.typeCount_--}return b};
goog.events.ListenerMap.prototype.getListeners=function(a,b){var c=this.listeners[a.toString()],d=[];if(c)for(var e=0;e<c.length;++e){var f=c[e];f.capture==b&&d.push(f)}return d};goog.events.ListenerMap.prototype.getListener=function(a,b,c,d){a=this.listeners[a.toString()];var e=-1;a&&(e=goog.events.ListenerMap.findListenerIndex_(a,b,c,d));return-1<e?a[e]:null};
goog.events.ListenerMap.prototype.hasListener=function(a,b){var c=goog.isDef(a),d=c?a.toString():"",e=goog.isDef(b);return goog.object.some(this.listeners,function(a,g){for(var h=0;h<a.length;++h)if(!(c&&a[h].type!=d||e&&a[h].capture!=b))return!0;return!1})};goog.events.ListenerMap.findListenerIndex_=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.listener==b&&f.capture==!!c&&f.handler==d)return e}return-1};goog.events.LISTENER_MAP_PROP_="closure_lm_"+(1E6*Math.random()|0);goog.events.onString_="on";goog.events.onStringMap_={};goog.events.CaptureSimulationMode={OFF_AND_FAIL:0,OFF_AND_SILENT:1,ON:2};goog.events.CAPTURE_SIMULATION_MODE=2;goog.events.listenerCountEstimate_=0;
goog.events.listen=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.listen(a,b[f],c,d,e);return null}c=goog.events.wrapListener(c);return goog.events.Listenable.isImplementedBy(a)?a.listen(b,c,d,e):goog.events.listen_(a,b,c,!1,d,e)};
goog.events.listen_=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var g=!!e;if(g&&!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT){if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.OFF_AND_FAIL)return goog.asserts.fail("Can not register capture listener in IE8-."),null;if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.OFF_AND_SILENT)return null}var h=goog.events.getListenerMap_(a);h||(a[goog.events.LISTENER_MAP_PROP_]=h=new goog.events.ListenerMap(a));
c=h.add(b,c,d,e,f);if(c.proxy)return c;d=goog.events.getProxy();c.proxy=d;d.src=a;d.listener=c;if(a.addEventListener)a.addEventListener(b.toString(),d,g);else if(a.attachEvent)a.attachEvent(goog.events.getOnString_(b.toString()),d);else throw Error("addEventListener and attachEvent are unavailable.");goog.events.listenerCountEstimate_++;return c};
goog.events.getProxy=function(){var a=goog.events.handleBrowserEvent_,b=goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b};
goog.events.listenOnce=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.listenOnce(a,b[f],c,d,e);return null}c=goog.events.wrapListener(c);return goog.events.Listenable.isImplementedBy(a)?a.listenOnce(b,c,d,e):goog.events.listen_(a,b,c,!0,d,e)};goog.events.listenWithWrapper=function(a,b,c,d,e){b.listen(a,c,d,e)};
goog.events.unlisten=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.unlisten(a,b[f],c,d,e);return null}c=goog.events.wrapListener(c);if(goog.events.Listenable.isImplementedBy(a))return a.unlisten(b,c,d,e);if(!a)return!1;d=!!d;if(a=goog.events.getListenerMap_(a))if(b=a.getListener(b,c,d,e))return goog.events.unlistenByKey(b);return!1};
goog.events.unlistenByKey=function(a){if(goog.isNumber(a)||!a||a.removed)return!1;var b=a.src;if(goog.events.Listenable.isImplementedBy(b))return b.unlistenByKey(a);var c=a.type,d=a.proxy;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(goog.events.getOnString_(c),d);goog.events.listenerCountEstimate_--;(c=goog.events.getListenerMap_(b))?(c.removeByKey(a),0==c.getTypeCount()&&(c.src=null,b[goog.events.LISTENER_MAP_PROP_]=null)):a.markAsRemoved();return!0};
goog.events.unlistenWithWrapper=function(a,b,c,d,e){b.unlisten(a,c,d,e)};goog.events.removeAll=function(a,b){if(!a)return 0;if(goog.events.Listenable.isImplementedBy(a))return a.removeAllListeners(b);var c=goog.events.getListenerMap_(a);if(!c)return 0;var d=0,e=b&&b.toString(),f;for(f in c.listeners)if(!e||f==e)for(var g=c.listeners[f].concat(),h=0;h<g.length;++h)goog.events.unlistenByKey(g[h])&&++d;return d};
goog.events.getListeners=function(a,b,c){return goog.events.Listenable.isImplementedBy(a)?a.getListeners(b,c):a?(a=goog.events.getListenerMap_(a))?a.getListeners(b,c):[]:[]};goog.events.getListener=function(a,b,c,d,e){c=goog.events.wrapListener(c);d=!!d;return goog.events.Listenable.isImplementedBy(a)?a.getListener(b,c,d,e):a?(a=goog.events.getListenerMap_(a))?a.getListener(b,c,d,e):null:null};
goog.events.hasListener=function(a,b,c){if(goog.events.Listenable.isImplementedBy(a))return a.hasListener(b,c);a=goog.events.getListenerMap_(a);return!!a&&a.hasListener(b,c)};goog.events.expose=function(a){var b=[],c;for(c in a)a[c]&&a[c].id?b.push(c+" = "+a[c]+" ("+a[c].id+")"):b.push(c+" = "+a[c]);return b.join("\n")};goog.events.getOnString_=function(a){return a in goog.events.onStringMap_?goog.events.onStringMap_[a]:goog.events.onStringMap_[a]=goog.events.onString_+a};
goog.events.fireListeners=function(a,b,c,d){return goog.events.Listenable.isImplementedBy(a)?a.fireListeners(b,c,d):goog.events.fireListeners_(a,b,c,d)};goog.events.fireListeners_=function(a,b,c,d){var e=!0;if(a=goog.events.getListenerMap_(a))if(b=a.listeners[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.removed&&(f=goog.events.fireListener(f,d),e=e&&!1!==f)}return e};
goog.events.fireListener=function(a,b){var c=a.listener,d=a.handler||a.src;a.callOnce&&goog.events.unlistenByKey(a);return c.call(d,b)};goog.events.getTotalListenerCount=function(){return goog.events.listenerCountEstimate_};goog.events.dispatchEvent=function(a,b){goog.asserts.assert(goog.events.Listenable.isImplementedBy(a),"Can not use goog.events.dispatchEvent with non-goog.events.Listenable instance.");return a.dispatchEvent(b)};
goog.events.protectBrowserEventEntryPoint=function(a){goog.events.handleBrowserEvent_=a.protectEntryPoint(goog.events.handleBrowserEvent_)};
goog.events.handleBrowserEvent_=function(a,b){if(a.removed)return!0;if(!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT){var c=b||goog.getObjectByName("window.event"),d=new goog.events.BrowserEvent(c,this),e=!0;if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.ON){if(!goog.events.isMarkedIeEvent_(c)){goog.events.markIeEvent_(c);for(var c=[],f=d.currentTarget;f;f=f.parentNode)c.push(f);for(var f=a.type,g=c.length-1;!d.propagationStopped_&&0<=g;g--){d.currentTarget=c[g];var h=
goog.events.fireListeners_(c[g],f,!0,d),e=e&&h}for(g=0;!d.propagationStopped_&&g<c.length;g++)d.currentTarget=c[g],h=goog.events.fireListeners_(c[g],f,!1,d),e=e&&h}}else e=goog.events.fireListener(a,d);return e}return goog.events.fireListener(a,new goog.events.BrowserEvent(b,this))};goog.events.markIeEvent_=function(a){var b=!1;if(0==a.keyCode)try{a.keyCode=-1;return}catch(c){b=!0}if(b||void 0==a.returnValue)a.returnValue=!0};goog.events.isMarkedIeEvent_=function(a){return 0>a.keyCode||void 0!=a.returnValue};
goog.events.uniqueIdCounter_=0;goog.events.getUniqueId=function(a){return a+"_"+goog.events.uniqueIdCounter_++};goog.events.getListenerMap_=function(a){a=a[goog.events.LISTENER_MAP_PROP_];return a instanceof goog.events.ListenerMap?a:null};goog.events.LISTENER_WRAPPER_PROP_="__closure_events_fn_"+(1E9*Math.random()>>>0);
goog.events.wrapListener=function(a){goog.asserts.assert(a,"Listener can not be null.");if(goog.isFunction(a))return a;goog.asserts.assert(a.handleEvent,"An object listener must have handleEvent method.");a[goog.events.LISTENER_WRAPPER_PROP_]||(a[goog.events.LISTENER_WRAPPER_PROP_]=function(b){return a.handleEvent(b)});return a[goog.events.LISTENER_WRAPPER_PROP_]};goog.debug.entryPointRegistry.register(function(a){goog.events.handleBrowserEvent_=a(goog.events.handleBrowserEvent_)});goog.math={};goog.math.randomInt=function(a){return Math.floor(Math.random()*a)};goog.math.uniformRandom=function(a,b){return a+Math.random()*(b-a)};goog.math.clamp=function(a,b,c){return Math.min(Math.max(a,b),c)};goog.math.modulo=function(a,b){var c=a%b;return 0>c*b?c+b:c};goog.math.lerp=function(a,b,c){return a+c*(b-a)};goog.math.nearlyEquals=function(a,b,c){return Math.abs(a-b)<=(c||1E-6)};goog.math.standardAngle=function(a){return goog.math.modulo(a,360)};
goog.math.standardAngleInRadians=function(a){return goog.math.modulo(a,2*Math.PI)};goog.math.toRadians=function(a){return a*Math.PI/180};goog.math.toDegrees=function(a){return 180*a/Math.PI};goog.math.angleDx=function(a,b){return b*Math.cos(goog.math.toRadians(a))};goog.math.angleDy=function(a,b){return b*Math.sin(goog.math.toRadians(a))};goog.math.angle=function(a,b,c,d){return goog.math.standardAngle(goog.math.toDegrees(Math.atan2(d-b,c-a)))};
goog.math.angleDifference=function(a,b){var c=goog.math.standardAngle(b)-goog.math.standardAngle(a);180<c?c-=360:-180>=c&&(c=360+c);return c};goog.math.sign=Math.sign||function(a){return 0<a?1:0>a?-1:a};
goog.math.longestCommonSubsequence=function(a,b,c,d){c=c||function(a,b){return a==b};d=d||function(b,c){return a[b]};for(var e=a.length,f=b.length,g=[],h=0;h<e+1;h++)g[h]=[],g[h][0]=0;for(var k=0;k<f+1;k++)g[0][k]=0;for(h=1;h<=e;h++)for(k=1;k<=f;k++)c(a[h-1],b[k-1])?g[h][k]=g[h-1][k-1]+1:g[h][k]=Math.max(g[h-1][k],g[h][k-1]);for(var m=[],h=e,k=f;0<h&&0<k;)c(a[h-1],b[k-1])?(m.unshift(d(h-1,k-1)),h--,k--):g[h-1][k]>g[h][k-1]?h--:k--;return m};
goog.math.sum=function(a){return goog.array.reduce(arguments,function(a,c){return a+c},0)};goog.math.average=function(a){return goog.math.sum.apply(null,arguments)/arguments.length};goog.math.sampleVariance=function(a){var b=arguments.length;if(2>b)return 0;var c=goog.math.average.apply(null,arguments);return goog.math.sum.apply(null,goog.array.map(arguments,function(a){return Math.pow(a-c,2)}))/(b-1)};goog.math.standardDeviation=function(a){return Math.sqrt(goog.math.sampleVariance.apply(null,arguments))};
goog.math.isInt=function(a){return isFinite(a)&&0==a%1};goog.math.isFiniteNumber=function(a){return isFinite(a)&&!isNaN(a)};goog.math.isNegativeZero=function(a){return 0==a&&0>1/a};goog.math.log10Floor=function(a){if(0<a){var b=Math.round(Math.log(a)*Math.LOG10E);return b-(parseFloat("1e"+b)>a?1:0)}return 0==a?-Infinity:NaN};goog.math.safeFloor=function(a,b){goog.asserts.assert(!goog.isDef(b)||0<b);return Math.floor(a+(b||2E-15))};
goog.math.safeCeil=function(a,b){goog.asserts.assert(!goog.isDef(b)||0<b);return Math.ceil(a-(b||2E-15))};goog.dom.BrowserFeature={CAN_ADD_NAME_OR_TYPE_ATTRIBUTES:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),CAN_USE_CHILDREN_ATTRIBUTE:!goog.userAgent.GECKO&&!goog.userAgent.IE||goog.userAgent.IE&&goog.userAgent.isDocumentModeOrHigher(9)||goog.userAgent.GECKO&&goog.userAgent.isVersionOrHigher("1.9.1"),CAN_USE_INNER_TEXT:goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher("9"),CAN_USE_PARENT_ELEMENT_PROPERTY:goog.userAgent.IE||goog.userAgent.OPERA||goog.userAgent.WEBKIT,INNER_HTML_NEEDS_SCOPED_ELEMENT:goog.userAgent.IE,
LEGACY_IE_RANGES:goog.userAgent.IE&&!goog.userAgent.isDocumentModeOrHigher(9)};goog.dom.TagName={A:"A",ABBR:"ABBR",ACRONYM:"ACRONYM",ADDRESS:"ADDRESS",APPLET:"APPLET",AREA:"AREA",ARTICLE:"ARTICLE",ASIDE:"ASIDE",AUDIO:"AUDIO",B:"B",BASE:"BASE",BASEFONT:"BASEFONT",BDI:"BDI",BDO:"BDO",BIG:"BIG",BLOCKQUOTE:"BLOCKQUOTE",BODY:"BODY",BR:"BR",BUTTON:"BUTTON",CANVAS:"CANVAS",CAPTION:"CAPTION",CENTER:"CENTER",CITE:"CITE",CODE:"CODE",COL:"COL",COLGROUP:"COLGROUP",COMMAND:"COMMAND",DATA:"DATA",DATALIST:"DATALIST",DD:"DD",DEL:"DEL",DETAILS:"DETAILS",DFN:"DFN",DIALOG:"DIALOG",DIR:"DIR",DIV:"DIV",
DL:"DL",DT:"DT",EM:"EM",EMBED:"EMBED",FIELDSET:"FIELDSET",FIGCAPTION:"FIGCAPTION",FIGURE:"FIGURE",FONT:"FONT",FOOTER:"FOOTER",FORM:"FORM",FRAME:"FRAME",FRAMESET:"FRAMESET",H1:"H1",H2:"H2",H3:"H3",H4:"H4",H5:"H5",H6:"H6",HEAD:"HEAD",HEADER:"HEADER",HGROUP:"HGROUP",HR:"HR",HTML:"HTML",I:"I",IFRAME:"IFRAME",IMG:"IMG",INPUT:"INPUT",INS:"INS",ISINDEX:"ISINDEX",KBD:"KBD",KEYGEN:"KEYGEN",LABEL:"LABEL",LEGEND:"LEGEND",LI:"LI",LINK:"LINK",MAP:"MAP",MARK:"MARK",MATH:"MATH",MENU:"MENU",META:"META",METER:"METER",
NAV:"NAV",NOFRAMES:"NOFRAMES",NOSCRIPT:"NOSCRIPT",OBJECT:"OBJECT",OL:"OL",OPTGROUP:"OPTGROUP",OPTION:"OPTION",OUTPUT:"OUTPUT",P:"P",PARAM:"PARAM",PRE:"PRE",PROGRESS:"PROGRESS",Q:"Q",RP:"RP",RT:"RT",RUBY:"RUBY",S:"S",SAMP:"SAMP",SCRIPT:"SCRIPT",SECTION:"SECTION",SELECT:"SELECT",SMALL:"SMALL",SOURCE:"SOURCE",SPAN:"SPAN",STRIKE:"STRIKE",STRONG:"STRONG",STYLE:"STYLE",SUB:"SUB",SUMMARY:"SUMMARY",SUP:"SUP",SVG:"SVG",TABLE:"TABLE",TBODY:"TBODY",TD:"TD",TEMPLATE:"TEMPLATE",TEXTAREA:"TEXTAREA",TFOOT:"TFOOT",
@@ -361,50 +404,7 @@ goog.style.getFontSize=function(a){var b=goog.style.getStyle_(a,"fontSize"),c=go
"left","pixelLeft")}c=goog.dom.createDom("SPAN",{style:"visibility:hidden;position:absolute;line-height:0;padding:0;margin:0;border:0;height:1em;"});goog.dom.appendChild(a,c);b=c.offsetHeight;goog.dom.removeNode(c);return b};goog.style.parseStyleAttribute=function(a){var b={};goog.array.forEach(a.split(/\s*;\s*/),function(a){var d=a.match(/\s*([\w-]+)\s*\:(.+)/);d&&(a=d[1],d=goog.string.trim(d[2]),b[goog.string.toCamelCase(a.toLowerCase())]=d)});return b};
goog.style.toStyleAttribute=function(a){var b=[];goog.object.forEach(a,function(a,d){b.push(goog.string.toSelectorCase(d),":",a,";")});return b.join("")};goog.style.setFloat=function(a,b){a.style[goog.userAgent.IE?"styleFloat":"cssFloat"]=b};goog.style.getFloat=function(a){return a.style[goog.userAgent.IE?"styleFloat":"cssFloat"]||""};
goog.style.getScrollbarWidth=function(a){var b=goog.dom.createElement("DIV");a&&(b.className=a);b.style.cssText="overflow:auto;position:absolute;top:0;width:100px;height:100px";a=goog.dom.createElement("DIV");goog.style.setSize(a,"200px","200px");b.appendChild(a);goog.dom.appendChild(goog.dom.getDocument().body,b);a=b.offsetWidth-b.clientWidth;goog.dom.removeNode(b);return a};goog.style.MATRIX_TRANSLATION_REGEX_=/matrix\([0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, [0-9\.\-]+, ([0-9\.\-]+)p?x?, ([0-9\.\-]+)p?x?\)/;
goog.style.getCssTranslation=function(a){a=goog.style.getComputedTransform(a);return a?(a=a.match(goog.style.MATRIX_TRANSLATION_REGEX_))?new goog.math.Coordinate(parseFloat(a[1]),parseFloat(a[2])):new goog.math.Coordinate(0,0):new goog.math.Coordinate(0,0)};goog.debug.entryPointRegistry={};goog.debug.EntryPointMonitor=function(){};goog.debug.entryPointRegistry.refList_=[];goog.debug.entryPointRegistry.monitors_=[];goog.debug.entryPointRegistry.monitorsMayExist_=!1;goog.debug.entryPointRegistry.register=function(a){goog.debug.entryPointRegistry.refList_[goog.debug.entryPointRegistry.refList_.length]=a;if(goog.debug.entryPointRegistry.monitorsMayExist_)for(var b=goog.debug.entryPointRegistry.monitors_,c=0;c<b.length;c++)a(goog.bind(b[c].wrap,b[c]))};
goog.debug.entryPointRegistry.monitorAll=function(a){goog.debug.entryPointRegistry.monitorsMayExist_=!0;for(var b=goog.bind(a.wrap,a),c=0;c<goog.debug.entryPointRegistry.refList_.length;c++)goog.debug.entryPointRegistry.refList_[c](b);goog.debug.entryPointRegistry.monitors_.push(a)};
goog.debug.entryPointRegistry.unmonitorAllIfPossible=function(a){var b=goog.debug.entryPointRegistry.monitors_;goog.asserts.assert(a==b[b.length-1],"Only the most recent monitor can be unwrapped.");a=goog.bind(a.unwrap,a);for(var c=0;c<goog.debug.entryPointRegistry.refList_.length;c++)goog.debug.entryPointRegistry.refList_[c](a);b.length--};goog.events={};
goog.events.BrowserFeature={HAS_W3C_BUTTON:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),HAS_W3C_EVENT_SUPPORT:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),SET_KEY_CODE_TO_PREVENT_DEFAULT:goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher("9"),HAS_NAVIGATOR_ONLINE_PROPERTY:!goog.userAgent.WEBKIT||goog.userAgent.isVersionOrHigher("528"),HAS_HTML5_NETWORK_EVENT_SUPPORT:goog.userAgent.GECKO&&goog.userAgent.isVersionOrHigher("1.9b")||goog.userAgent.IE&&goog.userAgent.isVersionOrHigher("8")||
goog.userAgent.OPERA&&goog.userAgent.isVersionOrHigher("9.5")||goog.userAgent.WEBKIT&&goog.userAgent.isVersionOrHigher("528"),HTML5_NETWORK_EVENTS_FIRE_ON_BODY:goog.userAgent.GECKO&&!goog.userAgent.isVersionOrHigher("8")||goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher("9"),TOUCH_ENABLED:"ontouchstart"in goog.global||!!(goog.global.document&&document.documentElement&&"ontouchstart"in document.documentElement)||!(!goog.global.navigator||!goog.global.navigator.msMaxTouchPoints)};goog.disposable={};goog.disposable.IDisposable=function(){};goog.disposable.IDisposable.prototype.dispose=goog.abstractMethod;goog.disposable.IDisposable.prototype.isDisposed=goog.abstractMethod;goog.Disposable=function(){goog.Disposable.MONITORING_MODE!=goog.Disposable.MonitoringMode.OFF&&(goog.Disposable.INCLUDE_STACK_ON_CREATION&&(this.creationStack=Error().stack),goog.Disposable.instances_[goog.getUid(this)]=this);this.disposed_=this.disposed_;this.onDisposeCallbacks_=this.onDisposeCallbacks_};goog.Disposable.MonitoringMode={OFF:0,PERMANENT:1,INTERACTIVE:2};goog.Disposable.MONITORING_MODE=0;goog.Disposable.INCLUDE_STACK_ON_CREATION=!0;goog.Disposable.instances_={};
goog.Disposable.getUndisposedObjects=function(){var a=[],b;for(b in goog.Disposable.instances_)goog.Disposable.instances_.hasOwnProperty(b)&&a.push(goog.Disposable.instances_[Number(b)]);return a};goog.Disposable.clearUndisposedObjects=function(){goog.Disposable.instances_={}};goog.Disposable.prototype.disposed_=!1;goog.Disposable.prototype.isDisposed=function(){return this.disposed_};goog.Disposable.prototype.getDisposed=goog.Disposable.prototype.isDisposed;
goog.Disposable.prototype.dispose=function(){if(!this.disposed_&&(this.disposed_=!0,this.disposeInternal(),goog.Disposable.MONITORING_MODE!=goog.Disposable.MonitoringMode.OFF)){var a=goog.getUid(this);if(goog.Disposable.MONITORING_MODE==goog.Disposable.MonitoringMode.PERMANENT&&!goog.Disposable.instances_.hasOwnProperty(a))throw Error(this+" did not call the goog.Disposable base constructor or was disposed of after a clearUndisposedObjects call");delete goog.Disposable.instances_[a]}};
goog.Disposable.prototype.registerDisposable=function(a){this.addOnDisposeCallback(goog.partial(goog.dispose,a))};goog.Disposable.prototype.addOnDisposeCallback=function(a,b){this.disposed_?goog.isDef(b)?a.call(b):a():(this.onDisposeCallbacks_||(this.onDisposeCallbacks_=[]),this.onDisposeCallbacks_.push(goog.isDef(b)?goog.bind(a,b):a))};goog.Disposable.prototype.disposeInternal=function(){if(this.onDisposeCallbacks_)for(;this.onDisposeCallbacks_.length;)this.onDisposeCallbacks_.shift()()};
goog.Disposable.isDisposed=function(a){return a&&"function"==typeof a.isDisposed?a.isDisposed():!1};goog.dispose=function(a){a&&"function"==typeof a.dispose&&a.dispose()};goog.disposeAll=function(a){for(var b=0,c=arguments.length;b<c;++b){var d=arguments[b];goog.isArrayLike(d)?goog.disposeAll.apply(null,d):goog.dispose(d)}};goog.events.EventId=function(a){this.id=a};goog.events.EventId.prototype.toString=function(){return this.id};goog.events.Event=function(a,b){this.type=a instanceof goog.events.EventId?String(a):a;this.currentTarget=this.target=b;this.defaultPrevented=this.propagationStopped_=!1;this.returnValue_=!0};goog.events.Event.prototype.stopPropagation=function(){this.propagationStopped_=!0};goog.events.Event.prototype.preventDefault=function(){this.defaultPrevented=!0;this.returnValue_=!1};goog.events.Event.stopPropagation=function(a){a.stopPropagation()};goog.events.Event.preventDefault=function(a){a.preventDefault()};goog.events.getVendorPrefixedName_=function(a){return goog.userAgent.WEBKIT?"webkit"+a:goog.userAgent.OPERA?"o"+a.toLowerCase():a.toLowerCase()};
goog.events.EventType={CLICK:"click",RIGHTCLICK:"rightclick",DBLCLICK:"dblclick",MOUSEDOWN:"mousedown",MOUSEUP:"mouseup",MOUSEOVER:"mouseover",MOUSEOUT:"mouseout",MOUSEMOVE:"mousemove",MOUSEENTER:"mouseenter",MOUSELEAVE:"mouseleave",SELECTSTART:"selectstart",WHEEL:"wheel",KEYPRESS:"keypress",KEYDOWN:"keydown",KEYUP:"keyup",BLUR:"blur",FOCUS:"focus",DEACTIVATE:"deactivate",FOCUSIN:goog.userAgent.IE?"focusin":"DOMFocusIn",FOCUSOUT:goog.userAgent.IE?"focusout":"DOMFocusOut",CHANGE:"change",RESET:"reset",
SELECT:"select",SUBMIT:"submit",INPUT:"input",PROPERTYCHANGE:"propertychange",DRAGSTART:"dragstart",DRAG:"drag",DRAGENTER:"dragenter",DRAGOVER:"dragover",DRAGLEAVE:"dragleave",DROP:"drop",DRAGEND:"dragend",TOUCHSTART:"touchstart",TOUCHMOVE:"touchmove",TOUCHEND:"touchend",TOUCHCANCEL:"touchcancel",BEFOREUNLOAD:"beforeunload",CONSOLEMESSAGE:"consolemessage",CONTEXTMENU:"contextmenu",DOMCONTENTLOADED:"DOMContentLoaded",ERROR:"error",HELP:"help",LOAD:"load",LOSECAPTURE:"losecapture",ORIENTATIONCHANGE:"orientationchange",
READYSTATECHANGE:"readystatechange",RESIZE:"resize",SCROLL:"scroll",UNLOAD:"unload",CANPLAY:"canplay",CANPLAYTHROUGH:"canplaythrough",DURATIONCHANGE:"durationchange",EMPTIED:"emptied",ENDED:"ended",LOADEDDATA:"loadeddata",LOADEDMETADATA:"loadedmetadata",PAUSE:"pause",PLAY:"play",PLAYING:"playing",RATECHANGE:"ratechange",SEEKED:"seeked",SEEKING:"seeking",STALLED:"stalled",SUSPEND:"suspend",TIMEUPDATE:"timeupdate",VOLUMECHANGE:"volumechange",WAITING:"waiting",HASHCHANGE:"hashchange",PAGEHIDE:"pagehide",
PAGESHOW:"pageshow",POPSTATE:"popstate",COPY:"copy",PASTE:"paste",CUT:"cut",BEFORECOPY:"beforecopy",BEFORECUT:"beforecut",BEFOREPASTE:"beforepaste",ONLINE:"online",OFFLINE:"offline",MESSAGE:"message",CONNECT:"connect",ANIMATIONSTART:goog.events.getVendorPrefixedName_("AnimationStart"),ANIMATIONEND:goog.events.getVendorPrefixedName_("AnimationEnd"),ANIMATIONITERATION:goog.events.getVendorPrefixedName_("AnimationIteration"),TRANSITIONEND:goog.events.getVendorPrefixedName_("TransitionEnd"),POINTERDOWN:"pointerdown",
POINTERUP:"pointerup",POINTERCANCEL:"pointercancel",POINTERMOVE:"pointermove",POINTEROVER:"pointerover",POINTEROUT:"pointerout",POINTERENTER:"pointerenter",POINTERLEAVE:"pointerleave",GOTPOINTERCAPTURE:"gotpointercapture",LOSTPOINTERCAPTURE:"lostpointercapture",MSGESTURECHANGE:"MSGestureChange",MSGESTUREEND:"MSGestureEnd",MSGESTUREHOLD:"MSGestureHold",MSGESTURESTART:"MSGestureStart",MSGESTURETAP:"MSGestureTap",MSGOTPOINTERCAPTURE:"MSGotPointerCapture",MSINERTIASTART:"MSInertiaStart",MSLOSTPOINTERCAPTURE:"MSLostPointerCapture",
MSPOINTERCANCEL:"MSPointerCancel",MSPOINTERDOWN:"MSPointerDown",MSPOINTERENTER:"MSPointerEnter",MSPOINTERHOVER:"MSPointerHover",MSPOINTERLEAVE:"MSPointerLeave",MSPOINTERMOVE:"MSPointerMove",MSPOINTEROUT:"MSPointerOut",MSPOINTEROVER:"MSPointerOver",MSPOINTERUP:"MSPointerUp",TEXT:"text",TEXTINPUT:"textInput",COMPOSITIONSTART:"compositionstart",COMPOSITIONUPDATE:"compositionupdate",COMPOSITIONEND:"compositionend",EXIT:"exit",LOADABORT:"loadabort",LOADCOMMIT:"loadcommit",LOADREDIRECT:"loadredirect",LOADSTART:"loadstart",
LOADSTOP:"loadstop",RESPONSIVE:"responsive",SIZECHANGED:"sizechanged",UNRESPONSIVE:"unresponsive",VISIBILITYCHANGE:"visibilitychange",STORAGE:"storage",DOMSUBTREEMODIFIED:"DOMSubtreeModified",DOMNODEINSERTED:"DOMNodeInserted",DOMNODEREMOVED:"DOMNodeRemoved",DOMNODEREMOVEDFROMDOCUMENT:"DOMNodeRemovedFromDocument",DOMNODEINSERTEDINTODOCUMENT:"DOMNodeInsertedIntoDocument",DOMATTRMODIFIED:"DOMAttrModified",DOMCHARACTERDATAMODIFIED:"DOMCharacterDataModified",BEFOREPRINT:"beforeprint",AFTERPRINT:"afterprint"};goog.events.BrowserEvent=function(a,b){goog.events.Event.call(this,a?a.type:"");this.relatedTarget=this.currentTarget=this.target=null;this.charCode=this.keyCode=this.button=this.screenY=this.screenX=this.clientY=this.clientX=this.offsetY=this.offsetX=0;this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.state=null;this.platformModifierKey=!1;this.event_=null;a&&this.init(a,b)};goog.inherits(goog.events.BrowserEvent,goog.events.Event);
goog.events.BrowserEvent.MouseButton={LEFT:0,MIDDLE:1,RIGHT:2};goog.events.BrowserEvent.IEButtonMap=[1,4,2];
goog.events.BrowserEvent.prototype.init=function(a,b){var c=this.type=a.type,d=a.changedTouches?a.changedTouches[0]:null;this.target=a.target||a.srcElement;this.currentTarget=b;var e=a.relatedTarget;e?goog.userAgent.GECKO&&(goog.reflect.canAccessProperty(e,"nodeName")||(e=null)):c==goog.events.EventType.MOUSEOVER?e=a.fromElement:c==goog.events.EventType.MOUSEOUT&&(e=a.toElement);this.relatedTarget=e;goog.isNull(d)?(this.offsetX=goog.userAgent.WEBKIT||void 0!==a.offsetX?a.offsetX:a.layerX,this.offsetY=
goog.userAgent.WEBKIT||void 0!==a.offsetY?a.offsetY:a.layerY,this.clientX=void 0!==a.clientX?a.clientX:a.pageX,this.clientY=void 0!==a.clientY?a.clientY:a.pageY,this.screenX=a.screenX||0,this.screenY=a.screenY||0):(this.clientX=void 0!==d.clientX?d.clientX:d.pageX,this.clientY=void 0!==d.clientY?d.clientY:d.pageY,this.screenX=d.screenX||0,this.screenY=d.screenY||0);this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||("keypress"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=
a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.platformModifierKey=goog.userAgent.MAC?a.metaKey:a.ctrlKey;this.state=a.state;this.event_=a;a.defaultPrevented&&this.preventDefault()};goog.events.BrowserEvent.prototype.isButton=function(a){return goog.events.BrowserFeature.HAS_W3C_BUTTON?this.event_.button==a:"click"==this.type?a==goog.events.BrowserEvent.MouseButton.LEFT:!!(this.event_.button&goog.events.BrowserEvent.IEButtonMap[a])};
goog.events.BrowserEvent.prototype.isMouseActionButton=function(){return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT)&&!(goog.userAgent.WEBKIT&&goog.userAgent.MAC&&this.ctrlKey)};goog.events.BrowserEvent.prototype.stopPropagation=function(){goog.events.BrowserEvent.superClass_.stopPropagation.call(this);this.event_.stopPropagation?this.event_.stopPropagation():this.event_.cancelBubble=!0};
goog.events.BrowserEvent.prototype.preventDefault=function(){goog.events.BrowserEvent.superClass_.preventDefault.call(this);var a=this.event_;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};goog.events.BrowserEvent.prototype.getBrowserEvent=function(){return this.event_};goog.events.Listenable=function(){};goog.events.Listenable.IMPLEMENTED_BY_PROP="closure_listenable_"+(1E6*Math.random()|0);goog.events.Listenable.addImplementation=function(a){a.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP]=!0};goog.events.Listenable.isImplementedBy=function(a){return!(!a||!a[goog.events.Listenable.IMPLEMENTED_BY_PROP])};goog.events.ListenableKey=function(){};goog.events.ListenableKey.counter_=0;goog.events.ListenableKey.reserveKey=function(){return++goog.events.ListenableKey.counter_};goog.events.Listener=function(a,b,c,d,e,f){goog.events.Listener.ENABLE_MONITORING&&(this.creationStack=Error().stack);this.listener=a;this.proxy=b;this.src=c;this.type=d;this.capture=!!e;this.handler=f;this.key=goog.events.ListenableKey.reserveKey();this.removed=this.callOnce=!1};goog.events.Listener.ENABLE_MONITORING=!1;goog.events.Listener.prototype.markAsRemoved=function(){this.removed=!0;this.handler=this.src=this.proxy=this.listener=null};goog.events.ListenerMap=function(a){this.src=a;this.listeners={};this.typeCount_=0};goog.events.ListenerMap.prototype.getTypeCount=function(){return this.typeCount_};goog.events.ListenerMap.prototype.getListenerCount=function(){var a=0,b;for(b in this.listeners)a+=this.listeners[b].length;return a};
goog.events.ListenerMap.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.listeners[f];a||(a=this.listeners[f]=[],this.typeCount_++);var g=goog.events.ListenerMap.findListenerIndex_(a,b,d,e);-1<g?(b=a[g],c||(b.callOnce=!1)):(b=new goog.events.Listener(b,null,this.src,f,!!d,e),b.callOnce=c,a.push(b));return b};
goog.events.ListenerMap.prototype.remove=function(a,b,c,d){a=a.toString();if(!(a in this.listeners))return!1;var e=this.listeners[a];b=goog.events.ListenerMap.findListenerIndex_(e,b,c,d);return-1<b?(e[b].markAsRemoved(),goog.array.removeAt(e,b),0==e.length&&(delete this.listeners[a],this.typeCount_--),!0):!1};
goog.events.ListenerMap.prototype.removeByKey=function(a){var b=a.type;if(!(b in this.listeners))return!1;var c=goog.array.remove(this.listeners[b],a);c&&(a.markAsRemoved(),0==this.listeners[b].length&&(delete this.listeners[b],this.typeCount_--));return c};goog.events.ListenerMap.prototype.removeAll=function(a){a=a&&a.toString();var b=0,c;for(c in this.listeners)if(!a||c==a){for(var d=this.listeners[c],e=0;e<d.length;e++)++b,d[e].markAsRemoved();delete this.listeners[c];this.typeCount_--}return b};
goog.events.ListenerMap.prototype.getListeners=function(a,b){var c=this.listeners[a.toString()],d=[];if(c)for(var e=0;e<c.length;++e){var f=c[e];f.capture==b&&d.push(f)}return d};goog.events.ListenerMap.prototype.getListener=function(a,b,c,d){a=this.listeners[a.toString()];var e=-1;a&&(e=goog.events.ListenerMap.findListenerIndex_(a,b,c,d));return-1<e?a[e]:null};
goog.events.ListenerMap.prototype.hasListener=function(a,b){var c=goog.isDef(a),d=c?a.toString():"",e=goog.isDef(b);return goog.object.some(this.listeners,function(a,g){for(var h=0;h<a.length;++h)if(!(c&&a[h].type!=d||e&&a[h].capture!=b))return!0;return!1})};goog.events.ListenerMap.findListenerIndex_=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.listener==b&&f.capture==!!c&&f.handler==d)return e}return-1};goog.events.LISTENER_MAP_PROP_="closure_lm_"+(1E6*Math.random()|0);goog.events.onString_="on";goog.events.onStringMap_={};goog.events.CaptureSimulationMode={OFF_AND_FAIL:0,OFF_AND_SILENT:1,ON:2};goog.events.CAPTURE_SIMULATION_MODE=2;goog.events.listenerCountEstimate_=0;
goog.events.listen=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.listen(a,b[f],c,d,e);return null}c=goog.events.wrapListener(c);return goog.events.Listenable.isImplementedBy(a)?a.listen(b,c,d,e):goog.events.listen_(a,b,c,!1,d,e)};
goog.events.listen_=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var g=!!e;if(g&&!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT){if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.OFF_AND_FAIL)return goog.asserts.fail("Can not register capture listener in IE8-."),null;if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.OFF_AND_SILENT)return null}var h=goog.events.getListenerMap_(a);h||(a[goog.events.LISTENER_MAP_PROP_]=h=new goog.events.ListenerMap(a));
c=h.add(b,c,d,e,f);if(c.proxy)return c;d=goog.events.getProxy();c.proxy=d;d.src=a;d.listener=c;if(a.addEventListener)a.addEventListener(b.toString(),d,g);else if(a.attachEvent)a.attachEvent(goog.events.getOnString_(b.toString()),d);else throw Error("addEventListener and attachEvent are unavailable.");goog.events.listenerCountEstimate_++;return c};
goog.events.getProxy=function(){var a=goog.events.handleBrowserEvent_,b=goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b};
goog.events.listenOnce=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.listenOnce(a,b[f],c,d,e);return null}c=goog.events.wrapListener(c);return goog.events.Listenable.isImplementedBy(a)?a.listenOnce(b,c,d,e):goog.events.listen_(a,b,c,!0,d,e)};goog.events.listenWithWrapper=function(a,b,c,d,e){b.listen(a,c,d,e)};
goog.events.unlisten=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.unlisten(a,b[f],c,d,e);return null}c=goog.events.wrapListener(c);if(goog.events.Listenable.isImplementedBy(a))return a.unlisten(b,c,d,e);if(!a)return!1;d=!!d;if(a=goog.events.getListenerMap_(a))if(b=a.getListener(b,c,d,e))return goog.events.unlistenByKey(b);return!1};
goog.events.unlistenByKey=function(a){if(goog.isNumber(a)||!a||a.removed)return!1;var b=a.src;if(goog.events.Listenable.isImplementedBy(b))return b.unlistenByKey(a);var c=a.type,d=a.proxy;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(goog.events.getOnString_(c),d);goog.events.listenerCountEstimate_--;(c=goog.events.getListenerMap_(b))?(c.removeByKey(a),0==c.getTypeCount()&&(c.src=null,b[goog.events.LISTENER_MAP_PROP_]=null)):a.markAsRemoved();return!0};
goog.events.unlistenWithWrapper=function(a,b,c,d,e){b.unlisten(a,c,d,e)};goog.events.removeAll=function(a,b){if(!a)return 0;if(goog.events.Listenable.isImplementedBy(a))return a.removeAllListeners(b);var c=goog.events.getListenerMap_(a);if(!c)return 0;var d=0,e=b&&b.toString(),f;for(f in c.listeners)if(!e||f==e)for(var g=c.listeners[f].concat(),h=0;h<g.length;++h)goog.events.unlistenByKey(g[h])&&++d;return d};
goog.events.getListeners=function(a,b,c){return goog.events.Listenable.isImplementedBy(a)?a.getListeners(b,c):a?(a=goog.events.getListenerMap_(a))?a.getListeners(b,c):[]:[]};goog.events.getListener=function(a,b,c,d,e){c=goog.events.wrapListener(c);d=!!d;return goog.events.Listenable.isImplementedBy(a)?a.getListener(b,c,d,e):a?(a=goog.events.getListenerMap_(a))?a.getListener(b,c,d,e):null:null};
goog.events.hasListener=function(a,b,c){if(goog.events.Listenable.isImplementedBy(a))return a.hasListener(b,c);a=goog.events.getListenerMap_(a);return!!a&&a.hasListener(b,c)};goog.events.expose=function(a){var b=[],c;for(c in a)a[c]&&a[c].id?b.push(c+" = "+a[c]+" ("+a[c].id+")"):b.push(c+" = "+a[c]);return b.join("\n")};goog.events.getOnString_=function(a){return a in goog.events.onStringMap_?goog.events.onStringMap_[a]:goog.events.onStringMap_[a]=goog.events.onString_+a};
goog.events.fireListeners=function(a,b,c,d){return goog.events.Listenable.isImplementedBy(a)?a.fireListeners(b,c,d):goog.events.fireListeners_(a,b,c,d)};goog.events.fireListeners_=function(a,b,c,d){var e=!0;if(a=goog.events.getListenerMap_(a))if(b=a.listeners[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.removed&&(f=goog.events.fireListener(f,d),e=e&&!1!==f)}return e};
goog.events.fireListener=function(a,b){var c=a.listener,d=a.handler||a.src;a.callOnce&&goog.events.unlistenByKey(a);return c.call(d,b)};goog.events.getTotalListenerCount=function(){return goog.events.listenerCountEstimate_};goog.events.dispatchEvent=function(a,b){goog.asserts.assert(goog.events.Listenable.isImplementedBy(a),"Can not use goog.events.dispatchEvent with non-goog.events.Listenable instance.");return a.dispatchEvent(b)};
goog.events.protectBrowserEventEntryPoint=function(a){goog.events.handleBrowserEvent_=a.protectEntryPoint(goog.events.handleBrowserEvent_)};
goog.events.handleBrowserEvent_=function(a,b){if(a.removed)return!0;if(!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT){var c=b||goog.getObjectByName("window.event"),d=new goog.events.BrowserEvent(c,this),e=!0;if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.ON){if(!goog.events.isMarkedIeEvent_(c)){goog.events.markIeEvent_(c);for(var c=[],f=d.currentTarget;f;f=f.parentNode)c.push(f);for(var f=a.type,g=c.length-1;!d.propagationStopped_&&0<=g;g--){d.currentTarget=c[g];var h=
goog.events.fireListeners_(c[g],f,!0,d),e=e&&h}for(g=0;!d.propagationStopped_&&g<c.length;g++)d.currentTarget=c[g],h=goog.events.fireListeners_(c[g],f,!1,d),e=e&&h}}else e=goog.events.fireListener(a,d);return e}return goog.events.fireListener(a,new goog.events.BrowserEvent(b,this))};goog.events.markIeEvent_=function(a){var b=!1;if(0==a.keyCode)try{a.keyCode=-1;return}catch(c){b=!0}if(b||void 0==a.returnValue)a.returnValue=!0};goog.events.isMarkedIeEvent_=function(a){return 0>a.keyCode||void 0!=a.returnValue};
goog.events.uniqueIdCounter_=0;goog.events.getUniqueId=function(a){return a+"_"+goog.events.uniqueIdCounter_++};goog.events.getListenerMap_=function(a){a=a[goog.events.LISTENER_MAP_PROP_];return a instanceof goog.events.ListenerMap?a:null};goog.events.LISTENER_WRAPPER_PROP_="__closure_events_fn_"+(1E9*Math.random()>>>0);
goog.events.wrapListener=function(a){goog.asserts.assert(a,"Listener can not be null.");if(goog.isFunction(a))return a;goog.asserts.assert(a.handleEvent,"An object listener must have handleEvent method.");a[goog.events.LISTENER_WRAPPER_PROP_]||(a[goog.events.LISTENER_WRAPPER_PROP_]=function(b){return a.handleEvent(b)});return a[goog.events.LISTENER_WRAPPER_PROP_]};goog.debug.entryPointRegistry.register(function(a){goog.events.handleBrowserEvent_=a(goog.events.handleBrowserEvent_)});goog.Thenable=function(){};goog.Thenable.prototype.then=function(a,b,c){};goog.Thenable.IMPLEMENTED_BY_PROP="$goog_Thenable";goog.Thenable.addImplementation=function(a){goog.exportProperty(a.prototype,"then",a.prototype.then);COMPILED?a.prototype[goog.Thenable.IMPLEMENTED_BY_PROP]=!0:a.prototype.$goog_Thenable=!0};goog.Thenable.isImplementedBy=function(a){if(!a)return!1;try{return COMPILED?!!a[goog.Thenable.IMPLEMENTED_BY_PROP]:!!a.$goog_Thenable}catch(b){return!1}};goog.async={};goog.async.FreeList=function(a,b,c){this.limit_=c;this.create_=a;this.reset_=b;this.occupants_=0;this.head_=null};goog.async.FreeList.prototype.get=function(){var a;0<this.occupants_?(this.occupants_--,a=this.head_,this.head_=a.next,a.next=null):a=this.create_();return a};goog.async.FreeList.prototype.put=function(a){this.reset_(a);this.occupants_<this.limit_&&(this.occupants_++,a.next=this.head_,this.head_=a)};goog.async.FreeList.prototype.occupants=function(){return this.occupants_};goog.async.WorkQueue=function(){this.workTail_=this.workHead_=null};goog.async.WorkQueue.DEFAULT_MAX_UNUSED=100;goog.async.WorkQueue.freelist_=new goog.async.FreeList(function(){return new goog.async.WorkItem},function(a){a.reset()},goog.async.WorkQueue.DEFAULT_MAX_UNUSED);goog.async.WorkQueue.prototype.add=function(a,b){var c=this.getUnusedItem_();c.set(a,b);this.workTail_?this.workTail_.next=c:(goog.asserts.assert(!this.workHead_),this.workHead_=c);this.workTail_=c};
goog.style.getCssTranslation=function(a){a=goog.style.getComputedTransform(a);return a?(a=a.match(goog.style.MATRIX_TRANSLATION_REGEX_))?new goog.math.Coordinate(parseFloat(a[1]),parseFloat(a[2])):new goog.math.Coordinate(0,0):new goog.math.Coordinate(0,0)};goog.Thenable=function(){};goog.Thenable.prototype.then=function(a,b,c){};goog.Thenable.IMPLEMENTED_BY_PROP="$goog_Thenable";goog.Thenable.addImplementation=function(a){goog.exportProperty(a.prototype,"then",a.prototype.then);COMPILED?a.prototype[goog.Thenable.IMPLEMENTED_BY_PROP]=!0:a.prototype.$goog_Thenable=!0};goog.Thenable.isImplementedBy=function(a){if(!a)return!1;try{return COMPILED?!!a[goog.Thenable.IMPLEMENTED_BY_PROP]:!!a.$goog_Thenable}catch(b){return!1}};goog.async={};goog.async.FreeList=function(a,b,c){this.limit_=c;this.create_=a;this.reset_=b;this.occupants_=0;this.head_=null};goog.async.FreeList.prototype.get=function(){var a;0<this.occupants_?(this.occupants_--,a=this.head_,this.head_=a.next,a.next=null):a=this.create_();return a};goog.async.FreeList.prototype.put=function(a){this.reset_(a);this.occupants_<this.limit_&&(this.occupants_++,a.next=this.head_,this.head_=a)};goog.async.FreeList.prototype.occupants=function(){return this.occupants_};goog.async.WorkQueue=function(){this.workTail_=this.workHead_=null};goog.async.WorkQueue.DEFAULT_MAX_UNUSED=100;goog.async.WorkQueue.freelist_=new goog.async.FreeList(function(){return new goog.async.WorkItem},function(a){a.reset()},goog.async.WorkQueue.DEFAULT_MAX_UNUSED);goog.async.WorkQueue.prototype.add=function(a,b){var c=this.getUnusedItem_();c.set(a,b);this.workTail_?this.workTail_.next=c:(goog.asserts.assert(!this.workHead_),this.workHead_=c);this.workTail_=c};
goog.async.WorkQueue.prototype.remove=function(){var a=null;this.workHead_&&(a=this.workHead_,this.workHead_=this.workHead_.next,this.workHead_||(this.workTail_=null),a.next=null);return a};goog.async.WorkQueue.prototype.returnUnused=function(a){goog.async.WorkQueue.freelist_.put(a)};goog.async.WorkQueue.prototype.getUnusedItem_=function(){return goog.async.WorkQueue.freelist_.get()};goog.async.WorkItem=function(){this.next=this.scope=this.fn=null};
goog.async.WorkItem.prototype.set=function(a,b){this.fn=a;this.scope=b;this.next=null};goog.async.WorkItem.prototype.reset=function(){this.next=this.scope=this.fn=null};goog.functions={};goog.functions.constant=function(a){return function(){return a}};goog.functions.FALSE=goog.functions.constant(!1);goog.functions.TRUE=goog.functions.constant(!0);goog.functions.NULL=goog.functions.constant(null);goog.functions.identity=function(a,b){return a};goog.functions.error=function(a){return function(){throw Error(a);}};goog.functions.fail=function(a){return function(){throw a;}};
goog.functions.lock=function(a,b){b=b||0;return function(){return a.apply(this,Array.prototype.slice.call(arguments,0,b))}};goog.functions.nth=function(a){return function(){return arguments[a]}};goog.functions.partialRight=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=Array.prototype.slice.call(arguments);b.push.apply(b,c);return a.apply(this,b)}};goog.functions.withReturnValue=function(a,b){return goog.functions.sequence(a,goog.functions.constant(b))};
@@ -873,42 +873,51 @@ goog.ui.tree.TreeControl.prototype.handleMouseEvent_=function(a){goog.log.fine(t
goog.ui.tree.TreeControl.prototype.handleKeyEvent=function(a){var b;(b=this.typeAhead_.handleNavigation(a)||this.selectedItem_&&this.selectedItem_.onKeyDown(a)||this.typeAhead_.handleTypeAheadChar(a))&&a.preventDefault();return b};goog.ui.tree.TreeControl.prototype.getNodeFromEvent_=function(a){for(var b=a.target;null!=b;){if(a=goog.ui.tree.BaseNode.allNodes[b.id])return a;if(b==this.getElement())break;b=b.parentNode}return null};
goog.ui.tree.TreeControl.prototype.createNode=function(a){return new goog.ui.tree.TreeNode(a||goog.html.SafeHtml.EMPTY,this.getConfig(),this.getDomHelper())};goog.ui.tree.TreeControl.prototype.setNode=function(a){this.typeAhead_.setNodeInMap(a)};goog.ui.tree.TreeControl.prototype.removeNode=function(a){this.typeAhead_.removeNodeFromMap(a)};goog.ui.tree.TreeControl.prototype.clearTypeAhead=function(){this.typeAhead_.clear()};goog.ui.tree.TreeControl.defaultConfig=goog.ui.tree.BaseNode.defaultConfig;
// Copyright 2013 Google Inc. Apache License 2.0
var Blockly={Blocks:{}};Blockly.Blocks.ONE_BASED_INDEXING=!0;
var Blockly={Blocks:{}};
// Copyright 2016 Google Inc. Apache License 2.0
Blockly.Touch={};Blockly.Touch.touchIdentifier_=null;Blockly.Touch.onTouchUpWrapper_=null;Blockly.Touch.TOUCH_MAP={};goog.events.BrowserFeature.TOUCH_ENABLED&&(Blockly.Touch.TOUCH_MAP={mousedown:["touchstart"],mousemove:["touchmove"],mouseup:["touchend","touchcancel"]});Blockly.longPid_=0;Blockly.longStart_=function(a,b){Blockly.longStop_();Blockly.longPid_=setTimeout(function(){a.button=2;b.onMouseDown_(a)},Blockly.LONGPRESS)};
Blockly.longStop_=function(){Blockly.longPid_&&(clearTimeout(Blockly.longPid_),Blockly.longPid_=0)};
Blockly.onMouseUp_=function(a){a=Blockly.getMainWorkspace();a.dragMode_!=Blockly.DRAG_NONE&&(Blockly.Touch.clearTouchIdentifier(),Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN),a.dragMode_=Blockly.DRAG_NONE,Blockly.Touch.onTouchUpWrapper_&&(Blockly.unbindEvent_(Blockly.Touch.onTouchUpWrapper_),Blockly.Touch.onTouchUpWrapper_=null),Blockly.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.onMouseMoveWrapper_),Blockly.onMouseMoveWrapper_=null))};
Blockly.onMouseMove_=function(a){var b=Blockly.getMainWorkspace();if(b.dragMode_!=Blockly.DRAG_NONE){var c=a.clientX-b.startDragMouseX,d=a.clientY-b.startDragMouseY,e=b.startDragMetrics,f=b.startScrollX+c,g=b.startScrollY+d,f=Math.min(f,-e.contentLeft),g=Math.min(g,-e.contentTop),f=Math.max(f,e.viewWidth-e.contentLeft-e.contentWidth),g=Math.max(g,e.viewHeight-e.contentTop-e.contentHeight);b.scrollbar.set(-f-e.contentLeft,-g-e.contentTop);Math.sqrt(c*c+d*d)>Blockly.DRAG_RADIUS&&(Blockly.longStop_(),
b.dragMode_=Blockly.DRAG_FREE);a.stopPropagation();a.preventDefault()}};Blockly.Touch.clearTouchIdentifier=function(){Blockly.Touch.touchIdentifier_=null};Blockly.Touch.shouldHandleEvent=function(a){return!Blockly.Touch.isMouseOrTouchEvent(a)||Blockly.Touch.checkTouchIdentifier(a)};
Blockly.Touch.checkTouchIdentifier=function(a){var b=a.changedTouches&&a.changedTouches[0]&&void 0!=a.changedTouches[0].identifier&&null!=a.changedTouches[0].identifier?a.changedTouches[0].identifier:"mouse";return void 0!=Blockly.Touch.touchIdentifier_&&null!=Blockly.Touch.touchIdentifier_?Blockly.Touch.touchIdentifier_==b:"mousedown"==a.type||"touchstart"==a.type?(Blockly.Touch.touchIdentifier_=b,!0):!1};
Blockly.Touch.setClientFromTouch=function(a){if(goog.string.startsWith(a.type,"touch")){var b=a.changedTouches[0];a.clientX=b.clientX;a.clientY=b.clientY}};Blockly.Touch.isMouseOrTouchEvent=function(a){return goog.string.startsWith(a.type,"touch")||goog.string.startsWith(a.type,"mouse")};
Blockly.Touch.splitEventByTouches=function(a){var b=[];if(a.changedTouches)for(var c=0;c<a.changedTouches.length;c++)b[c]={type:a.type,changedTouches:[a.changedTouches[c]],target:a.target,stopPropagation:function(){a.stopPropagation()},preventDefault:function(){a.preventDefault()}};else b.push(a);return b};
// Copyright 2012 Google Inc. Apache License 2.0
Blockly.Workspace=function(a){this.id=Blockly.genUid();Blockly.Workspace.WorkspaceDB_[this.id]=this;this.options=a||{};this.RTL=!!this.options.RTL;this.horizontalLayout=!!this.options.horizontalLayout;this.toolboxPosition=this.options.toolboxPosition;this.topBlocks_=[];this.listeners_=[];this.undoStack_=[];this.redoStack_=[];this.blockDB_=Object.create(null);this.variableList=[]};Blockly.Workspace.prototype.rendered=!1;Blockly.Workspace.prototype.MAX_UNDO=1024;
Blockly.Workspace.prototype.dispose=function(){this.listeners_.length=0;this.clear();delete Blockly.Workspace.WorkspaceDB_[this.id]};Blockly.Workspace.SCAN_ANGLE=3;Blockly.Workspace.prototype.addTopBlock=function(a){this.topBlocks_.push(a);if(this.isFlyout){a=Blockly.Variables.allUsedVariables(a);for(var b=0;b<a.length;b++)-1==this.variableList.indexOf(a[b])&&this.variableList.push(a[b])}};
Blockly.Workspace.prototype.removeTopBlock=function(a){for(var b=!1,c,d=0;c=this.topBlocks_[d];d++)if(c==a){this.topBlocks_.splice(d,1);b=!0;break}if(!b)throw"Block not present in workspace's list of top-most blocks.";};
Blockly.Workspace.prototype.getTopBlocks=function(a){var b=[].concat(this.topBlocks_);if(a&&1<b.length){var c=Math.sin(goog.math.toRadians(Blockly.Workspace.SCAN_ANGLE));this.RTL&&(c*=-1);b.sort(function(a,b){var f=a.getRelativeToSurfaceXY(),g=b.getRelativeToSurfaceXY();return f.y+c*f.x-(g.y+c*g.x)})}return b};Blockly.Workspace.prototype.getAllBlocks=function(){for(var a=this.getTopBlocks(!1),b=0;b<a.length;b++)a.push.apply(a,a[b].getChildren());return a};
Blockly.Workspace.prototype.clear=function(){var a=Blockly.Events.getGroup();for(a||Blockly.Events.setGroup(!0);this.topBlocks_.length;)this.topBlocks_[0].dispose();a||Blockly.Events.setGroup(!1);this.variableList.length=0};Blockly.Workspace.prototype.updateVariableList=function(a){if(!this.isFlyout){a&&(this.variableList.length=0);a=Blockly.Variables.allUsedVariables(this);for(var b=0;b<a.length;b++)this.createVariable(a[b])}};
Blockly.Workspace.prototype.removeTopBlock=function(a){if(!goog.array.remove(this.topBlocks_,a))throw"Block not present in workspace's list of top-most blocks.";};Blockly.Workspace.prototype.getTopBlocks=function(a){var b=[].concat(this.topBlocks_);if(a&&1<b.length){var c=Math.sin(goog.math.toRadians(Blockly.Workspace.SCAN_ANGLE));this.RTL&&(c*=-1);b.sort(function(a,b){var f=a.getRelativeToSurfaceXY(),g=b.getRelativeToSurfaceXY();return f.y+c*f.x-(g.y+c*g.x)})}return b};
Blockly.Workspace.prototype.getAllBlocks=function(){for(var a=this.getTopBlocks(!1),b=0;b<a.length;b++)a.push.apply(a,a[b].getChildren());return a};Blockly.Workspace.prototype.clear=function(){var a=Blockly.Events.getGroup();for(a||Blockly.Events.setGroup(!0);this.topBlocks_.length;)this.topBlocks_[0].dispose();a||Blockly.Events.setGroup(!1);this.variableList.length=0};
Blockly.Workspace.prototype.updateVariableList=function(a){if(!this.isFlyout){a&&(this.variableList.length=0);a=Blockly.Variables.allUsedVariables(this);for(var b=0;b<a.length;b++)this.createVariable(a[b])}};
Blockly.Workspace.prototype.renameVariable=function(a,b){var c=this.variableIndexOf(a),d=this.variableIndexOf(b);if(-1!=d&&this.variableList[d]!=b)var e=this.variableList[d];Blockly.Events.setGroup(!0);for(var f=this.getAllBlocks(),g=0;g<f.length;g++)f[g].renameVar(a,b),e&&f[g].renameVar(e,b);Blockly.Events.setGroup(!1);c==d||-1!=c&&-1==d?this.variableList[c]=b:-1!=c&&-1!=d?(this.variableList.splice(c,1),this.variableList[d]=b):(this.variableList.push(b),console.log("Tried to rename an non-existent variable."))};
Blockly.Workspace.prototype.createVariable=function(a){-1==this.variableIndexOf(a)&&this.variableList.push(a)};Blockly.Workspace.prototype.getVariableUses=function(a){for(var b=[],c=this.getAllBlocks(),d=0;d<c.length;d++){var e=c[d].getVars();if(e)for(var f=0;f<e.length;f++){var g=e[f];g&&Blockly.Names.equals(g,a)&&b.push(c[d])}}return b};
Blockly.Workspace.prototype.deleteVariable=function(a){var b=this.variableIndexOf(a);if(-1!=b){var c=this.getVariableUses(a);if(1<c.length){for(var d=0,e;e=c[d];d++)if("procedures_defnoreturn"==e.type||"procedures_defreturn"==e.type){b=e.getFieldValue("NAME");window.alert(Blockly.Msg.CANNOT_DELETE_VARIABLE_PROCEDURE.replace("%1",a).replace("%2",b));return}if(!window.confirm(Blockly.Msg.DELETE_VARIABLE_CONFIRMATION.replace("%1",c.length).replace("%2",a)))return}Blockly.Events.setGroup(!0);for(d=0;d<
c.length;d++)c[d].dispose(!0,!1);Blockly.Events.setGroup(!1);this.variableList.splice(b,1)}};Blockly.Workspace.prototype.variableIndexOf=function(a){for(var b=0,c;c=this.variableList[b];b++)if(Blockly.Names.equals(c,a))return b;return-1};Blockly.Workspace.prototype.getWidth=function(){return 0};Blockly.Workspace.prototype.newBlock=function(a,b){return new Blockly.Block(this,a,b)};
Blockly.Workspace.prototype.remainingCapacity=function(){return isNaN(this.options.maxBlocks)?Infinity:this.options.maxBlocks-this.getAllBlocks().length};
Blockly.Workspace.prototype.undo=function(a){var b=a?this.redoStack_:this.undoStack_,c=a?this.undoStack_:this.redoStack_,d=b.pop();if(d){for(var e=[d];b.length&&d.group&&d.group==b[b.length-1].group;)e.push(b.pop());for(b=0;d=e[b];b++)c.push(d);e=Blockly.Events.filter(e,a);Blockly.Events.recordUndo=!1;for(b=0;d=e[b];b++)d.run(a);Blockly.Events.recordUndo=!0}};Blockly.Workspace.prototype.clearUndo=function(){this.undoStack_.length=0;this.redoStack_.length=0;Blockly.Events.clearPendingUndo()};
Blockly.Workspace.prototype.addChangeListener=function(a){this.listeners_.push(a);return a};Blockly.Workspace.prototype.removeChangeListener=function(a){a=this.listeners_.indexOf(a);-1!=a&&this.listeners_.splice(a,1)};Blockly.Workspace.prototype.fireChangeListener=function(a){a.recordUndo&&(this.undoStack_.push(a),this.redoStack_.length=0,this.undoStack_.length>this.MAX_UNDO&&this.undoStack_.unshift());for(var b=0,c;c=this.listeners_[b];b++)c(a)};
Blockly.Workspace.prototype.addChangeListener=function(a){this.listeners_.push(a);return a};Blockly.Workspace.prototype.removeChangeListener=function(a){goog.array.remove(this.listeners_,a)};Blockly.Workspace.prototype.fireChangeListener=function(a){a.recordUndo&&(this.undoStack_.push(a),this.redoStack_.length=0,this.undoStack_.length>this.MAX_UNDO&&this.undoStack_.unshift());for(var b=0,c;c=this.listeners_[b];b++)c(a)};
Blockly.Workspace.prototype.getBlockById=function(a){return this.blockDB_[a]||null};Blockly.Workspace.WorkspaceDB_=Object.create(null);Blockly.Workspace.getById=function(a){return Blockly.Workspace.WorkspaceDB_[a]||null};Blockly.Workspace.prototype.clear=Blockly.Workspace.prototype.clear;Blockly.Workspace.prototype.clearUndo=Blockly.Workspace.prototype.clearUndo;Blockly.Workspace.prototype.addChangeListener=Blockly.Workspace.prototype.addChangeListener;
Blockly.Workspace.prototype.removeChangeListener=Blockly.Workspace.prototype.removeChangeListener;Blockly.Bubble=function(a,b,c,d,e,f){this.workspace_=a;this.content_=b;this.shape_=c;c=Blockly.Bubble.ARROW_ANGLE;this.workspace_.RTL&&(c=-c);this.arrow_radians_=goog.math.toRadians(c);a.getBubbleCanvas().appendChild(this.createDom_(b,!(!e||!f)));this.setAnchorLocation(d);e&&f||(b=this.content_.getBBox(),e=b.width+2*Blockly.Bubble.BORDER_WIDTH,f=b.height+2*Blockly.Bubble.BORDER_WIDTH);this.setBubbleSize(e,f);this.positionBubble_();this.renderArrow_();this.rendered_=!0;a.options.readOnly||(Blockly.bindEvent_(this.bubbleBack_,
"mousedown",this,this.bubbleMouseDown_),this.resizeGroup_&&Blockly.bindEvent_(this.resizeGroup_,"mousedown",this,this.resizeMouseDown_))};Blockly.Bubble.BORDER_WIDTH=6;Blockly.Bubble.ARROW_THICKNESS=10;Blockly.Bubble.ARROW_ANGLE=20;Blockly.Bubble.ARROW_BEND=4;Blockly.Bubble.ANCHOR_RADIUS=8;Blockly.Bubble.onMouseUpWrapper_=null;Blockly.Bubble.onMouseMoveWrapper_=null;Blockly.Bubble.prototype.resizeCallback_=null;
Blockly.Bubble.unbindDragEvents_=function(){Blockly.Bubble.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseUpWrapper_),Blockly.Bubble.onMouseUpWrapper_=null);Blockly.Bubble.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseMoveWrapper_),Blockly.Bubble.onMouseMoveWrapper_=null)};Blockly.Bubble.prototype.rendered_=!1;Blockly.Bubble.prototype.anchorXY_=null;Blockly.Bubble.prototype.relativeLeft_=0;Blockly.Bubble.prototype.relativeTop_=0;Blockly.Bubble.prototype.width_=0;
Blockly.Bubble.prototype.height_=0;Blockly.Bubble.prototype.autoLayout_=!0;
Blockly.Workspace.prototype.removeChangeListener=Blockly.Workspace.prototype.removeChangeListener;Blockly.Bubble=function(a,b,c,d,e,f){this.workspace_=a;this.content_=b;this.shape_=c;c=Blockly.Bubble.ARROW_ANGLE;this.workspace_.RTL&&(c=-c);this.arrow_radians_=goog.math.toRadians(c);a.getBubbleCanvas().appendChild(this.createDom_(b,!(!e||!f)));this.setAnchorLocation(d);e&&f||(b=this.content_.getBBox(),e=b.width+2*Blockly.Bubble.BORDER_WIDTH,f=b.height+2*Blockly.Bubble.BORDER_WIDTH);this.setBubbleSize(e,f);this.positionBubble_();this.renderArrow_();this.rendered_=!0;a.options.readOnly||(Blockly.bindEventWithChecks_(this.bubbleBack_,
"mousedown",this,this.bubbleMouseDown_),this.resizeGroup_&&Blockly.bindEventWithChecks_(this.resizeGroup_,"mousedown",this,this.resizeMouseDown_))};Blockly.Bubble.BORDER_WIDTH=6;Blockly.Bubble.ARROW_THICKNESS=5;Blockly.Bubble.ARROW_ANGLE=20;Blockly.Bubble.ARROW_BEND=4;Blockly.Bubble.ANCHOR_RADIUS=8;Blockly.Bubble.onMouseUpWrapper_=null;Blockly.Bubble.onMouseMoveWrapper_=null;Blockly.Bubble.prototype.resizeCallback_=null;
Blockly.Bubble.unbindDragEvents_=function(){Blockly.Bubble.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseUpWrapper_),Blockly.Bubble.onMouseUpWrapper_=null);Blockly.Bubble.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseMoveWrapper_),Blockly.Bubble.onMouseMoveWrapper_=null)};Blockly.Bubble.bubbleMouseUp_=function(){Blockly.Touch.clearTouchIdentifier();Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);Blockly.Bubble.unbindDragEvents_()};
Blockly.Bubble.prototype.rendered_=!1;Blockly.Bubble.prototype.anchorXY_=null;Blockly.Bubble.prototype.relativeLeft_=0;Blockly.Bubble.prototype.relativeTop_=0;Blockly.Bubble.prototype.width_=0;Blockly.Bubble.prototype.height_=0;Blockly.Bubble.prototype.autoLayout_=!0;
Blockly.Bubble.prototype.createDom_=function(a,b){this.bubbleGroup_=Blockly.createSvgElement("g",{},null);var c={filter:"url(#"+this.workspace_.options.embossFilterId+")"};-1!=goog.userAgent.getUserAgentString().indexOf("JavaFX")&&(c={});c=Blockly.createSvgElement("g",c,this.bubbleGroup_);this.bubbleArrow_=Blockly.createSvgElement("path",{},c);this.bubbleBack_=Blockly.createSvgElement("rect",{"class":"blocklyDraggable",x:0,y:0,rx:Blockly.Bubble.BORDER_WIDTH,ry:Blockly.Bubble.BORDER_WIDTH},c);b?(this.resizeGroup_=
Blockly.createSvgElement("g",{"class":this.workspace_.RTL?"blocklyResizeSW":"blocklyResizeSE"},this.bubbleGroup_),c=2*Blockly.Bubble.BORDER_WIDTH,Blockly.createSvgElement("polygon",{points:"0,x x,x x,0".replace(/x/g,c.toString())},this.resizeGroup_),Blockly.createSvgElement("line",{"class":"blocklyResizeLine",x1:c/3,y1:c-1,x2:c-1,y2:c/3},this.resizeGroup_),Blockly.createSvgElement("line",{"class":"blocklyResizeLine",x1:2*c/3,y1:c-1,x2:c-1,y2:2*c/3},this.resizeGroup_)):this.resizeGroup_=null;this.bubbleGroup_.appendChild(a);
return this.bubbleGroup_};
Blockly.Bubble.prototype.bubbleMouseDown_=function(a){this.promote_();Blockly.Bubble.unbindDragEvents_();Blockly.isRightButton(a)?a.stopPropagation():Blockly.isTargetInput_(a)||(Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED),this.workspace_.startDrag(a,new goog.math.Coordinate(this.workspace_.RTL?-this.relativeLeft_:this.relativeLeft_,this.relativeTop_)),Blockly.Bubble.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,Blockly.Bubble.unbindDragEvents_),Blockly.Bubble.onMouseMoveWrapper_=Blockly.bindEvent_(document,
"mousemove",this,this.bubbleMouseMove_),Blockly.hideChaff(),a.stopPropagation())};Blockly.Bubble.prototype.bubbleMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.relativeLeft_=this.workspace_.RTL?-a.x:a.x;this.relativeTop_=a.y;this.positionBubble_();this.renderArrow_()};
Blockly.Bubble.prototype.resizeMouseDown_=function(a){this.promote_();Blockly.Bubble.unbindDragEvents_();Blockly.isRightButton(a)||(Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED),this.workspace_.startDrag(a,new goog.math.Coordinate(this.workspace_.RTL?-this.width_:this.width_,this.height_)),Blockly.Bubble.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,Blockly.Bubble.unbindDragEvents_),Blockly.Bubble.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.resizeMouseMove_),
Blockly.hideChaff());a.stopPropagation()};Blockly.Bubble.prototype.resizeMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.setBubbleSize(this.workspace_.RTL?-a.x:a.x,a.y);this.workspace_.RTL&&this.positionBubble_()};Blockly.Bubble.prototype.registerResizeEvent=function(a){this.resizeCallback_=a};Blockly.Bubble.prototype.promote_=function(){this.bubbleGroup_.parentNode.appendChild(this.bubbleGroup_)};
Blockly.Bubble.prototype.bubbleMouseDown_=function(a){this.promote_();Blockly.Bubble.unbindDragEvents_();Blockly.isRightButton(a)?a.stopPropagation():Blockly.isTargetInput_(a)||(Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED),this.workspace_.startDrag(a,new goog.math.Coordinate(this.workspace_.RTL?-this.relativeLeft_:this.relativeLeft_,this.relativeTop_)),Blockly.Bubble.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,Blockly.Bubble.bubbleMouseUp_),Blockly.Bubble.onMouseMoveWrapper_=
Blockly.bindEventWithChecks_(document,"mousemove",this,this.bubbleMouseMove_),Blockly.hideChaff(),a.stopPropagation())};Blockly.Bubble.prototype.bubbleMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.relativeLeft_=this.workspace_.RTL?-a.x:a.x;this.relativeTop_=a.y;this.positionBubble_();this.renderArrow_()};
Blockly.Bubble.prototype.resizeMouseDown_=function(a){this.promote_();Blockly.Bubble.unbindDragEvents_();Blockly.isRightButton(a)||(Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED),this.workspace_.startDrag(a,new goog.math.Coordinate(this.workspace_.RTL?-this.width_:this.width_,this.height_)),Blockly.Bubble.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,Blockly.Bubble.bubbleMouseUp_),Blockly.Bubble.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",this,
this.resizeMouseMove_),Blockly.hideChaff());a.stopPropagation()};Blockly.Bubble.prototype.resizeMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.setBubbleSize(this.workspace_.RTL?-a.x:a.x,a.y);this.workspace_.RTL&&this.positionBubble_()};Blockly.Bubble.prototype.registerResizeEvent=function(a){this.resizeCallback_=a};Blockly.Bubble.prototype.promote_=function(){this.bubbleGroup_.parentNode.appendChild(this.bubbleGroup_)};
Blockly.Bubble.prototype.setAnchorLocation=function(a){this.anchorXY_=a;this.rendered_&&this.positionBubble_()};
Blockly.Bubble.prototype.layoutBubble_=function(){var a=-this.width_/4,b=-this.height_-Blockly.BlockSvg.MIN_BLOCK_Y,c=this.workspace_.getMetrics();c.viewWidth/=this.workspace_.scale;c.viewLeft/=this.workspace_.scale;var d=this.anchorXY_.x;this.workspace_.RTL?d-c.viewLeft-a-this.width_<Blockly.Scrollbar.scrollbarThickness?a=d-c.viewLeft-this.width_-Blockly.Scrollbar.scrollbarThickness:d-c.viewLeft-a>c.viewWidth&&(a=d-c.viewLeft-c.viewWidth):d+a<c.viewLeft?a=c.viewLeft-d:c.viewLeft+c.viewWidth<d+a+
this.width_+Blockly.BlockSvg.SEP_SPACE_X+Blockly.Scrollbar.scrollbarThickness&&(a=c.viewLeft+c.viewWidth-d-this.width_-Blockly.Scrollbar.scrollbarThickness);this.anchorXY_.y+b<c.viewTop&&(b=this.shape_.getBBox().height);this.relativeLeft_=a;this.relativeTop_=b};
Blockly.Bubble.prototype.positionBubble_=function(){var a=this.anchorXY_.x,a=this.workspace_.RTL?a-(this.relativeLeft_+this.width_):a+this.relativeLeft_;this.bubbleGroup_.setAttribute("transform","translate("+a+","+(this.relativeTop_+this.anchorXY_.y)+")")};Blockly.Bubble.prototype.getBubbleSize=function(){return{width:this.width_,height:this.height_}};
Blockly.Bubble.prototype.setBubbleSize=function(a,b){var c=2*Blockly.Bubble.BORDER_WIDTH;a=Math.max(a,c+45);b=Math.max(b,c+20);this.width_=a;this.height_=b;this.bubbleBack_.setAttribute("width",a);this.bubbleBack_.setAttribute("height",b);this.resizeGroup_&&(this.workspace_.RTL?this.resizeGroup_.setAttribute("transform","translate("+2*Blockly.Bubble.BORDER_WIDTH+","+(b-c)+") scale(-1 1)"):this.resizeGroup_.setAttribute("transform","translate("+(a-c)+","+(b-c)+")"));this.rendered_&&(this.autoLayout_&&
this.layoutBubble_(),this.positionBubble_(),this.renderArrow_());this.resizeCallback_&&this.resizeCallback_()};
Blockly.Bubble.prototype.renderArrow_=function(){var a=[],b=this.width_/2,c=this.height_/2,d=-this.relativeLeft_,e=-this.relativeTop_;if(b==d&&c==e)a.push("M "+b+","+c);else{e-=c;d-=b;this.workspace_.RTL&&(d*=-1);var f=Math.sqrt(e*e+d*d),g=Math.acos(d/f);0>e&&(g=2*Math.PI-g);var h=g+Math.PI/2;h>2*Math.PI&&(h-=2*Math.PI);var k=Math.sin(h),m=Math.cos(h),p=this.getBubbleSize(),h=(p.width+p.height)/Blockly.Bubble.ARROW_THICKNESS,h=Math.min(h,p.width,p.height)/2,p=1-Blockly.Bubble.ANCHOR_RADIUS/f,d=b+
Blockly.Bubble.prototype.renderArrow_=function(){var a=[],b=this.width_/2,c=this.height_/2,d=-this.relativeLeft_,e=-this.relativeTop_;if(b==d&&c==e)a.push("M "+b+","+c);else{e-=c;d-=b;this.workspace_.RTL&&(d*=-1);var f=Math.sqrt(e*e+d*d),g=Math.acos(d/f);0>e&&(g=2*Math.PI-g);var h=g+Math.PI/2;h>2*Math.PI&&(h-=2*Math.PI);var k=Math.sin(h),m=Math.cos(h),p=this.getBubbleSize(),h=(p.width+p.height)/Blockly.Bubble.ARROW_THICKNESS,h=Math.min(h,p.width,p.height)/4,p=1-Blockly.Bubble.ANCHOR_RADIUS/f,d=b+
p*d,e=c+p*e,p=b+h*m,l=c+h*k,b=b-h*m,c=c-h*k,k=g+this.arrow_radians_;k>2*Math.PI&&(k-=2*Math.PI);g=Math.sin(k)*f/Blockly.Bubble.ARROW_BEND;f=Math.cos(k)*f/Blockly.Bubble.ARROW_BEND;a.push("M"+p+","+l);a.push("C"+(p+f)+","+(l+g)+" "+d+","+e+" "+d+","+e);a.push("C"+d+","+e+" "+(b+f)+","+(c+g)+" "+b+","+c)}a.push("z");this.bubbleArrow_.setAttribute("d",a.join(" "))};Blockly.Bubble.prototype.setColour=function(a){this.bubbleBack_.setAttribute("fill",a);this.bubbleArrow_.setAttribute("fill",a)};
Blockly.Bubble.prototype.dispose=function(){Blockly.Bubble.unbindDragEvents_();goog.dom.removeNode(this.bubbleGroup_);this.shape_=this.content_=this.workspace_=this.resizeGroup_=this.bubbleBack_=this.bubbleArrow_=this.bubbleGroup_=null};Blockly.Icon=function(a){this.block_=a};Blockly.Icon.prototype.collapseHidden=!0;Blockly.Icon.prototype.SIZE=17;Blockly.Icon.prototype.bubble_=null;Blockly.Icon.prototype.iconXY_=null;
Blockly.Icon.prototype.createIcon=function(){this.iconGroup_||(this.iconGroup_=Blockly.createSvgElement("g",{"class":"blocklyIconGroup"},null),this.block_.isInFlyout&&Blockly.addClass_(this.iconGroup_,"blocklyIconGroupReadonly"),this.drawIcon_(this.iconGroup_),this.block_.getSvgRoot().appendChild(this.iconGroup_),Blockly.bindEvent_(this.iconGroup_,"mouseup",this,this.iconClick_),this.updateEditable())};
Blockly.Icon.prototype.createIcon=function(){this.iconGroup_||(this.iconGroup_=Blockly.createSvgElement("g",{"class":"blocklyIconGroup"},null),this.block_.isInFlyout&&Blockly.addClass_(this.iconGroup_,"blocklyIconGroupReadonly"),this.drawIcon_(this.iconGroup_),this.block_.getSvgRoot().appendChild(this.iconGroup_),Blockly.bindEventWithChecks_(this.iconGroup_,"mouseup",this,this.iconClick_),this.updateEditable())};
Blockly.Icon.prototype.dispose=function(){goog.dom.removeNode(this.iconGroup_);this.iconGroup_=null;this.setVisible(!1);this.block_=null};Blockly.Icon.prototype.updateEditable=function(){};Blockly.Icon.prototype.isVisible=function(){return!!this.bubble_};Blockly.Icon.prototype.iconClick_=function(a){this.block_.workspace.isDragging()||this.block_.isInFlyout||Blockly.isRightButton(a)||this.setVisible(!this.isVisible())};Blockly.Icon.prototype.updateColour=function(){this.isVisible()&&this.bubble_.setColour(this.block_.getColour())};
Blockly.Icon.prototype.renderIcon=function(a){if(this.collapseHidden&&this.block_.isCollapsed())return this.iconGroup_.setAttribute("display","none"),a;this.iconGroup_.setAttribute("display","block");var b=this.SIZE;this.block_.RTL&&(a-=b);this.iconGroup_.setAttribute("transform","translate("+a+",5)");this.computeIconLocation();return a=this.block_.RTL?a-Blockly.BlockSvg.SEP_SPACE_X:a+(b+Blockly.BlockSvg.SEP_SPACE_X)};
Blockly.Icon.prototype.setIconLocation=function(a){this.iconXY_=a;this.isVisible()&&this.bubble_.setAnchorLocation(a)};Blockly.Icon.prototype.computeIconLocation=function(){var a=this.block_.getRelativeToSurfaceXY(),b=Blockly.getRelativeXY_(this.iconGroup_),a=new goog.math.Coordinate(a.x+b.x+this.SIZE/2,a.y+b.y+this.SIZE/2);goog.math.Coordinate.equals(this.getIconLocation(),a)||this.setIconLocation(a)};Blockly.Icon.prototype.getIconLocation=function(){return this.iconXY_};
@@ -916,8 +925,8 @@ Blockly.Icon.prototype.setIconLocation=function(a){this.iconXY_=a;this.isVisible
Blockly.Comment=function(a){Blockly.Comment.superClass_.constructor.call(this,a);this.createIcon()};goog.inherits(Blockly.Comment,Blockly.Icon);Blockly.Comment.prototype.text_="";Blockly.Comment.prototype.width_=160;Blockly.Comment.prototype.height_=80;
Blockly.Comment.prototype.drawIcon_=function(a){Blockly.createSvgElement("circle",{"class":"blocklyIconShape",r:"8",cx:"8",cy:"8"},a);Blockly.createSvgElement("path",{"class":"blocklyIconSymbol",d:"m6.8,10h2c0.003,-0.617 0.271,-0.962 0.633,-1.266 2.875,-2.405 0.607,-5.534 -3.765,-3.874v1.7c3.12,-1.657 3.698,0.118 2.336,1.25 -1.201,0.998 -1.201,1.528 -1.204,2.19z"},a);Blockly.createSvgElement("rect",{"class":"blocklyIconSymbol",x:"6.8",y:"10.78",height:"2",width:"2"},a)};
Blockly.Comment.prototype.createEditor_=function(){this.foreignObject_=Blockly.createSvgElement("foreignObject",{x:Blockly.Bubble.BORDER_WIDTH,y:Blockly.Bubble.BORDER_WIDTH},null);var a=document.createElementNS(Blockly.HTML_NS,"body");a.setAttribute("xmlns",Blockly.HTML_NS);a.className="blocklyMinimalBody";var b=document.createElementNS(Blockly.HTML_NS,"textarea");b.className="blocklyCommentTextarea";b.setAttribute("dir",this.block_.RTL?"RTL":"LTR");a.appendChild(b);this.textarea_=b;this.foreignObject_.appendChild(a);
Blockly.bindEvent_(b,"mouseup",this,this.textareaFocus_);Blockly.bindEvent_(b,"wheel",this,function(a){a.stopPropagation()});Blockly.bindEvent_(b,"change",this,function(a){this.text_!=b.value&&(Blockly.Events.fire(new Blockly.Events.Change(this.block_,"comment",null,this.text_,b.value)),this.text_=b.value)});setTimeout(function(){b.focus()},0);return this.foreignObject_};Blockly.Comment.prototype.updateEditable=function(){this.isVisible()&&(this.setVisible(!1),this.setVisible(!0));Blockly.Icon.prototype.updateEditable.call(this)};
Blockly.Comment.prototype.resizeBubble_=function(){if(this.isVisible()){var a=this.bubble_.getBubbleSize(),b=2*Blockly.Bubble.BORDER_WIDTH;this.foreignObject_.setAttribute("width",a.width-b);this.foreignObject_.setAttribute("height",a.height-b);this.textarea_.style.width=a.width-b-4+"px";this.textarea_.style.height=a.height-b-4+"px"}};
Blockly.bindEventWithChecks_(b,"mouseup",this,this.textareaFocus_);Blockly.bindEventWithChecks_(b,"wheel",this,function(a){a.stopPropagation()});Blockly.bindEventWithChecks_(b,"change",this,function(a){this.text_!=b.value&&(Blockly.Events.fire(new Blockly.Events.Change(this.block_,"comment",null,this.text_,b.value)),this.text_=b.value)});setTimeout(function(){b.focus()},0);return this.foreignObject_};
Blockly.Comment.prototype.updateEditable=function(){this.isVisible()&&(this.setVisible(!1),this.setVisible(!0));Blockly.Icon.prototype.updateEditable.call(this)};Blockly.Comment.prototype.resizeBubble_=function(){if(this.isVisible()){var a=this.bubble_.getBubbleSize(),b=2*Blockly.Bubble.BORDER_WIDTH;this.foreignObject_.setAttribute("width",a.width-b);this.foreignObject_.setAttribute("height",a.height-b);this.textarea_.style.width=a.width-b-4+"px";this.textarea_.style.height=a.height-b-4+"px"}};
Blockly.Comment.prototype.setVisible=function(a){if(a!=this.isVisible())if(Blockly.Events.fire(new Blockly.Events.Ui(this.block_,"commentOpen",!a,a)),!this.block_.isEditable()&&!this.textarea_||goog.userAgent.IE)Blockly.Warning.prototype.setVisible.call(this,a);else{var b=this.getText(),c=this.getBubbleSize();a?(this.bubble_=new Blockly.Bubble(this.block_.workspace,this.createEditor_(),this.block_.svgPath_,this.iconXY_,this.width_,this.height_),this.bubble_.registerResizeEvent(this.resizeBubble_.bind(this)),
this.updateColour()):(this.bubble_.dispose(),this.foreignObject_=this.textarea_=this.bubble_=null);this.setText(b);this.setBubbleSize(c.width,c.height)}};Blockly.Comment.prototype.textareaFocus_=function(a){this.bubble_.promote_();this.textarea_.focus()};Blockly.Comment.prototype.getBubbleSize=function(){return this.isVisible()?this.bubble_.getBubbleSize():{width:this.width_,height:this.height_}};
Blockly.Comment.prototype.setBubbleSize=function(a,b){this.textarea_?this.bubble_.setBubbleSize(a,b):(this.width_=a,this.height_=b)};Blockly.Comment.prototype.getText=function(){return this.textarea_?this.textarea_.value:this.text_};Blockly.Comment.prototype.setText=function(a){this.text_!=a&&(Blockly.Events.fire(new Blockly.Events.Change(this.block_,"comment",null,this.text_,a)),this.text_=a);this.textarea_&&(this.textarea_.value=a)};
@@ -942,7 +951,7 @@ Blockly.Connection.prototype.checkType_=function(a){if(!this.check_||!a.check_)r
Blockly.Connection.prototype.setShadowDom=function(a){this.shadowDom_=a};Blockly.Connection.prototype.getShadowDom=function(){return this.shadowDom_};Blockly.Field=function(a,b){this.size_=new goog.math.Size(0,25);this.setValue(a);this.setValidator(b)};Blockly.Field.cacheWidths_=null;Blockly.Field.cacheReference_=0;Blockly.Field.prototype.name=void 0;Blockly.Field.prototype.maxDisplayLength=50;Blockly.Field.prototype.text_="";Blockly.Field.prototype.sourceBlock_=null;Blockly.Field.prototype.visible_=!0;Blockly.Field.prototype.validator_=null;Blockly.Field.NBSP="\u00a0";Blockly.Field.prototype.EDITABLE=!0;
Blockly.Field.prototype.setSourceBlock=function(a){goog.asserts.assert(!this.sourceBlock_,"Field already bound to a block.");this.sourceBlock_=a};
Blockly.Field.prototype.init=function(){this.fieldGroup_||(this.fieldGroup_=Blockly.createSvgElement("g",{},null),this.visible_||(this.fieldGroup_.style.display="none"),this.borderRect_=Blockly.createSvgElement("rect",{rx:4,ry:4,x:-Blockly.BlockSvg.SEP_SPACE_X/2,y:0,height:16},this.fieldGroup_,this.sourceBlock_.workspace),this.textElement_=Blockly.createSvgElement("text",{"class":"blocklyText",y:this.size_.height-12.5},this.fieldGroup_),this.updateEditable(),this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_),
this.mouseUpWrapper_=Blockly.bindEvent_(this.fieldGroup_,"mouseup",this,this.onMouseUp_),this.updateTextNode_())};Blockly.Field.prototype.dispose=function(){this.mouseUpWrapper_&&(Blockly.unbindEvent_(this.mouseUpWrapper_),this.mouseUpWrapper_=null);this.sourceBlock_=null;goog.dom.removeNode(this.fieldGroup_);this.validator_=this.borderRect_=this.textElement_=this.fieldGroup_=null};
this.mouseUpWrapper_=Blockly.bindEventWithChecks_(this.fieldGroup_,"mouseup",this,this.onMouseUp_),this.updateTextNode_())};Blockly.Field.prototype.dispose=function(){this.mouseUpWrapper_&&(Blockly.unbindEvent_(this.mouseUpWrapper_),this.mouseUpWrapper_=null);this.sourceBlock_=null;goog.dom.removeNode(this.fieldGroup_);this.validator_=this.borderRect_=this.textElement_=this.fieldGroup_=null};
Blockly.Field.prototype.updateEditable=function(){var a=this.fieldGroup_;this.EDITABLE&&a&&(this.sourceBlock_.isEditable()?(Blockly.addClass_(a,"blocklyEditableText"),Blockly.removeClass_(a,"blocklyNonEditableText"),this.fieldGroup_.style.cursor=this.CURSOR):(Blockly.addClass_(a,"blocklyNonEditableText"),Blockly.removeClass_(a,"blocklyEditableText"),this.fieldGroup_.style.cursor=""))};Blockly.Field.prototype.isVisible=function(){return this.visible_};
Blockly.Field.prototype.setVisible=function(a){if(this.visible_!=a){this.visible_=a;var b=this.getSvgRoot();b&&(b.style.display=a?"block":"none",this.render_())}};Blockly.Field.prototype.setValidator=function(a){this.validator_=a};Blockly.Field.prototype.getValidator=function(){return this.validator_};Blockly.Field.prototype.classValidator=function(a){return a};
Blockly.Field.prototype.callValidator=function(a){var b=this.classValidator(a);if(null===b)return null;void 0!==b&&(a=b);if(b=this.getValidator()){b=b.call(this,a);if(null===b)return null;void 0!==b&&(a=b)}return a};Blockly.Field.prototype.getSvgRoot=function(){return this.fieldGroup_};
@@ -952,7 +961,7 @@ Blockly.Field.prototype.getScaledBBox_=function(){var a=this.borderRect_.getBBox
Blockly.Field.prototype.updateTextNode_=function(){if(this.textElement_){var a=this.text_;a.length>this.maxDisplayLength&&(a=a.substring(0,this.maxDisplayLength-2)+"\u2026");goog.dom.removeChildren(this.textElement_);a=a.replace(/\s/g,Blockly.Field.NBSP);this.sourceBlock_.RTL&&a&&(a+="\u200f");a||(a=Blockly.Field.NBSP);a=document.createTextNode(a);this.textElement_.appendChild(a);this.size_.width=0}};Blockly.Field.prototype.getValue=function(){return this.getText()};
Blockly.Field.prototype.setValue=function(a){if(null!==a){var b=this.getValue();b!=a&&(this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,b,a)),this.setText(a))}};
Blockly.Field.prototype.onMouseUp_=function(a){if(!goog.userAgent.IPHONE&&!goog.userAgent.IPAD||goog.userAgent.isVersionOrHigher("537.51.2")||0===a.layerX||0===a.layerY)Blockly.isRightButton(a)||this.sourceBlock_.workspace.isDragging()||this.sourceBlock_.isEditable()&&this.showEditor_()};Blockly.Field.prototype.setTooltip=function(a){};Blockly.Field.prototype.getAbsoluteXY_=function(){return goog.style.getPageOffset(this.borderRect_)};Blockly.Tooltip={};Blockly.Tooltip.visible=!1;Blockly.Tooltip.LIMIT=50;Blockly.Tooltip.mouseOutPid_=0;Blockly.Tooltip.showPid_=0;Blockly.Tooltip.lastX_=0;Blockly.Tooltip.lastY_=0;Blockly.Tooltip.element_=null;Blockly.Tooltip.poisonedElement_=null;Blockly.Tooltip.OFFSET_X=0;Blockly.Tooltip.OFFSET_Y=10;Blockly.Tooltip.RADIUS_OK=10;Blockly.Tooltip.HOVER_MS=750;Blockly.Tooltip.MARGINS=5;Blockly.Tooltip.DIV=null;
Blockly.Tooltip.createDom=function(){Blockly.Tooltip.DIV||(Blockly.Tooltip.DIV=goog.dom.createDom("DIV","blocklyTooltipDiv"),document.body.appendChild(Blockly.Tooltip.DIV))};Blockly.Tooltip.bindMouseEvents=function(a){Blockly.bindEvent_(a,"mouseover",null,Blockly.Tooltip.onMouseOver_);Blockly.bindEvent_(a,"mouseout",null,Blockly.Tooltip.onMouseOut_);Blockly.bindEvent_(a,"mousemove",null,Blockly.Tooltip.onMouseMove_)};
Blockly.Tooltip.createDom=function(){Blockly.Tooltip.DIV||(Blockly.Tooltip.DIV=goog.dom.createDom("DIV","blocklyTooltipDiv"),document.body.appendChild(Blockly.Tooltip.DIV))};Blockly.Tooltip.bindMouseEvents=function(a){Blockly.bindEvent_(a,"mouseover",null,Blockly.Tooltip.onMouseOver_);Blockly.bindEvent_(a,"mouseout",null,Blockly.Tooltip.onMouseOut_);a.addEventListener("mousemove",Blockly.Tooltip.onMouseMove_,!1)};
Blockly.Tooltip.onMouseOver_=function(a){for(a=a.target;!goog.isString(a.tooltip)&&!goog.isFunction(a.tooltip);)a=a.tooltip;Blockly.Tooltip.element_!=a&&(Blockly.Tooltip.hide(),Blockly.Tooltip.poisonedElement_=null,Blockly.Tooltip.element_=a);clearTimeout(Blockly.Tooltip.mouseOutPid_)};Blockly.Tooltip.onMouseOut_=function(a){Blockly.Tooltip.mouseOutPid_=setTimeout(function(){Blockly.Tooltip.element_=null;Blockly.Tooltip.poisonedElement_=null;Blockly.Tooltip.hide()},1);clearTimeout(Blockly.Tooltip.showPid_)};
Blockly.Tooltip.onMouseMove_=function(a){if(Blockly.Tooltip.element_&&Blockly.Tooltip.element_.tooltip&&Blockly.dragMode_==Blockly.DRAG_NONE&&!Blockly.WidgetDiv.isVisible())if(Blockly.Tooltip.visible){var b=Blockly.Tooltip.lastX_-a.pageX;a=Blockly.Tooltip.lastY_-a.pageY;Math.sqrt(b*b+a*a)>Blockly.Tooltip.RADIUS_OK&&Blockly.Tooltip.hide()}else Blockly.Tooltip.poisonedElement_!=Blockly.Tooltip.element_&&(clearTimeout(Blockly.Tooltip.showPid_),Blockly.Tooltip.lastX_=a.pageX,Blockly.Tooltip.lastY_=a.pageY,
Blockly.Tooltip.showPid_=setTimeout(Blockly.Tooltip.show_,Blockly.Tooltip.HOVER_MS))};Blockly.Tooltip.hide=function(){Blockly.Tooltip.visible&&(Blockly.Tooltip.visible=!1,Blockly.Tooltip.DIV&&(Blockly.Tooltip.DIV.style.display="none"));clearTimeout(Blockly.Tooltip.showPid_)};
@@ -969,13 +978,11 @@ Blockly.ConnectionDB.prototype.findConnection=function(a){if(!this.length)return
Blockly.ConnectionDB.prototype.findPositionForConnection_=function(a){if(!this.length)return 0;for(var b=0,c=this.length;b<c;){var d=Math.floor((b+c)/2);if(this[d].y_<a.y_)b=d+1;else if(this[d].y_>a.y_)c=d;else{b=d;break}}return b};Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw"Connection not in database.";var b=this.findConnection(a);if(-1==b)throw"Unable to find connection in connectionDB.";a.inDB_=!1;this.splice(b,1)};
Blockly.ConnectionDB.prototype.getNeighbours=function(a,b){function c(a){var c=e-d[a].x_,g=f-d[a].y_;Math.sqrt(c*c+g*g)<=b&&m.push(d[a]);return g<b}for(var d=this,e=a.x_,f=a.y_,g=0,h=d.length-2,k=h;g<k;)d[k].y_<f?g=k:h=k,k=Math.floor((g+h)/2);var m=[],h=g=k;if(d.length){for(;0<=g&&c(g);)g--;do h++;while(h<d.length&&c(h))}return m};Blockly.ConnectionDB.prototype.isInYRange_=function(a,b,c){return Math.abs(this[a].y_-b)<=c};
Blockly.ConnectionDB.prototype.searchForClosest=function(a,b,c){if(!this.length)return{connection:null,radius:b};var d=a.y_,e=a.x_;a.x_=e+c.x;a.y_=d+c.y;var f=this.findPositionForConnection_(a);c=null;for(var g=b,h,k=f-1;0<=k&&this.isInYRange_(k,a.y_,b);)h=this[k],a.isConnectionAllowed(h,g)&&(c=h,g=h.distanceFrom(a)),k--;for(;f<this.length&&this.isInYRange_(f,a.y_,b);)h=this[f],a.isConnectionAllowed(h,g)&&(c=h,g=h.distanceFrom(a)),f++;a.x_=e;a.y_=d;return{connection:c,radius:g}};
Blockly.ConnectionDB.init=function(a){var b=[];b[Blockly.INPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.OUTPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.NEXT_STATEMENT]=new Blockly.ConnectionDB;b[Blockly.PREVIOUS_STATEMENT]=new Blockly.ConnectionDB;a.connectionDBList=b};
// Copyright 2016 Google Inc. Apache License 2.0
Blockly.constants={};Blockly.DRAG_RADIUS=5;Blockly.SNAP_RADIUS=20;Blockly.BUMP_DELAY=250;Blockly.COLLAPSE_CHARS=30;Blockly.LONGPRESS=750;Blockly.SOUND_LIMIT=100;Blockly.HSV_SATURATION=.45;Blockly.HSV_VALUE=.65;Blockly.SPRITE={width:96,height:124,url:"sprites.png"};Blockly.SVG_NS="http://www.w3.org/2000/svg";Blockly.HTML_NS="http://www.w3.org/1999/xhtml";Blockly.INPUT_VALUE=1;Blockly.OUTPUT_VALUE=2;Blockly.NEXT_STATEMENT=3;Blockly.PREVIOUS_STATEMENT=4;Blockly.DUMMY_INPUT=5;Blockly.ALIGN_LEFT=-1;
Blockly.ConnectionDB.init=function(a){var b=[];b[Blockly.INPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.OUTPUT_VALUE]=new Blockly.ConnectionDB;b[Blockly.NEXT_STATEMENT]=new Blockly.ConnectionDB;b[Blockly.PREVIOUS_STATEMENT]=new Blockly.ConnectionDB;a.connectionDBList=b};Blockly.constants={};Blockly.DRAG_RADIUS=5;Blockly.SNAP_RADIUS=20;Blockly.BUMP_DELAY=250;Blockly.COLLAPSE_CHARS=30;Blockly.LONGPRESS=750;Blockly.SOUND_LIMIT=100;Blockly.HSV_SATURATION=.45;Blockly.HSV_VALUE=.65;Blockly.SPRITE={width:96,height:124,url:"sprites.png"};Blockly.SVG_NS="http://www.w3.org/2000/svg";Blockly.HTML_NS="http://www.w3.org/1999/xhtml";Blockly.INPUT_VALUE=1;Blockly.OUTPUT_VALUE=2;Blockly.NEXT_STATEMENT=3;Blockly.PREVIOUS_STATEMENT=4;Blockly.DUMMY_INPUT=5;Blockly.ALIGN_LEFT=-1;
Blockly.ALIGN_CENTRE=0;Blockly.ALIGN_RIGHT=1;Blockly.DRAG_NONE=0;Blockly.DRAG_STICKY=1;Blockly.DRAG_BEGIN=1;Blockly.DRAG_FREE=2;Blockly.OPPOSITE_TYPE=[];Blockly.OPPOSITE_TYPE[Blockly.INPUT_VALUE]=Blockly.OUTPUT_VALUE;Blockly.OPPOSITE_TYPE[Blockly.OUTPUT_VALUE]=Blockly.INPUT_VALUE;Blockly.OPPOSITE_TYPE[Blockly.NEXT_STATEMENT]=Blockly.PREVIOUS_STATEMENT;Blockly.OPPOSITE_TYPE[Blockly.PREVIOUS_STATEMENT]=Blockly.NEXT_STATEMENT;Blockly.TOOLBOX_AT_TOP=0;Blockly.TOOLBOX_AT_BOTTOM=1;
Blockly.TOOLBOX_AT_LEFT=2;Blockly.TOOLBOX_AT_RIGHT=3;Blockly.Options=function(a){var b=!!a.readOnly;if(b)var c=null,d=!1,e=!1,f=!1,g=!1,h=!1,k=!1;else c=Blockly.Options.parseToolboxTree(a.toolbox),d=!(!c||!c.getElementsByTagName("category").length),e=a.trashcan,void 0===e&&(e=d),f=a.collapse,void 0===f&&(f=d),g=a.comments,void 0===g&&(g=d),h=a.disable,void 0===h&&(h=d),k=a.sounds,void 0===k&&(k=!0);var m=!!a.rtl,p=a.horizontalLayout;void 0===p&&(p=!1);var l=a.toolboxPosition,l="end"===l?!1:!0,l=p?l?Blockly.TOOLBOX_AT_TOP:Blockly.TOOLBOX_AT_BOTTOM:l==
m?Blockly.TOOLBOX_AT_RIGHT:Blockly.TOOLBOX_AT_LEFT,n=a.scrollbars;void 0===n&&(n=d);var q=a.css;void 0===q&&(q=!0);var t="https://blockly-demo.appspot.com/static/media/";a.media?t=a.media:a.path&&(t=a.path+"media/");this.RTL=m;this.collapse=f;this.comments=g;this.disable=h;this.readOnly=b;this.maxBlocks=a.maxBlocks||Infinity;this.pathToMedia=t;this.hasCategories=d;this.hasScrollbars=n;this.hasTrashcan=e;this.hasSounds=k;this.hasCss=q;this.horizontalLayout=p;this.languageTree=c;this.gridOptions=Blockly.Options.parseGridOptions_(a);
this.zoomOptions=Blockly.Options.parseZoomOptions_(a);this.toolboxPosition=l};Blockly.Options.prototype.parentWorkspace=null;Blockly.Options.prototype.setMetrics=null;Blockly.Options.prototype.getMetrics=null;
m?Blockly.TOOLBOX_AT_RIGHT:Blockly.TOOLBOX_AT_LEFT,n=a.scrollbars;void 0===n&&(n=d);var q=a.css;void 0===q&&(q=!0);var t="https://blockly-demo.appspot.com/static/media/";a.media?t=a.media:a.path&&(t=a.path+"media/");var r=!a.oneBasedIndex;this.RTL=m;this.oneBasedIndex=r;this.collapse=f;this.comments=g;this.disable=h;this.readOnly=b;this.maxBlocks=a.maxBlocks||Infinity;this.pathToMedia=t;this.hasCategories=d;this.hasScrollbars=n;this.hasTrashcan=e;this.hasSounds=k;this.hasCss=q;this.horizontalLayout=
p;this.languageTree=c;this.gridOptions=Blockly.Options.parseGridOptions_(a);this.zoomOptions=Blockly.Options.parseZoomOptions_(a);this.toolboxPosition=l};Blockly.Options.prototype.parentWorkspace=null;Blockly.Options.prototype.setMetrics=null;Blockly.Options.prototype.getMetrics=null;
Blockly.Options.parseZoomOptions_=function(a){a=a.zoom||{};var b={};b.controls=void 0===a.controls?!1:!!a.controls;b.wheel=void 0===a.wheel?!1:!!a.wheel;b.startScale=void 0===a.startScale?1:parseFloat(a.startScale);b.maxScale=void 0===a.maxScale?3:parseFloat(a.maxScale);b.minScale=void 0===a.minScale?.3:parseFloat(a.minScale);b.scaleSpeed=void 0===a.scaleSpeed?1.2:parseFloat(a.scaleSpeed);return b};
Blockly.Options.parseGridOptions_=function(a){a=a.grid||{};var b={};b.spacing=parseFloat(a.spacing)||0;b.colour=a.colour||"#888";b.length=parseFloat(a.length)||1;b.snap=0<b.spacing&&!!a.snap;return b};Blockly.Options.parseToolboxTree=function(a){a?("string"!=typeof a&&("undefined"==typeof XSLTProcessor&&a.outerHTML?a=a.outerHTML:a instanceof Element||(a=null)),"string"==typeof a&&(a=Blockly.Xml.textToDom(a))):a=null;return a};Blockly.ScrollbarPair=function(a){this.workspace_=a;this.hScroll=new Blockly.Scrollbar(a,!0,!0);this.vScroll=new Blockly.Scrollbar(a,!1,!0);this.corner_=Blockly.createSvgElement("rect",{height:Blockly.Scrollbar.scrollbarThickness,width:Blockly.Scrollbar.scrollbarThickness,"class":"blocklyScrollbarBackground"},null);Blockly.Scrollbar.insertAfter_(this.corner_,a.getBubbleCanvas())};Blockly.ScrollbarPair.prototype.oldHostMetrics_=null;
Blockly.ScrollbarPair.prototype.dispose=function(){goog.dom.removeNode(this.corner_);this.oldHostMetrics_=this.workspace_=this.corner_=null;this.hScroll.dispose();this.hScroll=null;this.vScroll.dispose();this.vScroll=null};
@@ -983,10 +990,10 @@ Blockly.ScrollbarPair.prototype.resize=function(){var a=this.workspace_.getMetri
this.oldHostMetrics_.contentHeight==a.contentHeight&&this.oldHostMetrics_.viewTop==a.viewTop&&this.oldHostMetrics_.contentTop==a.contentTop||(c=!0)):c=b=!0;b&&this.hScroll.resize(a);c&&this.vScroll.resize(a);this.oldHostMetrics_&&this.oldHostMetrics_.viewWidth==a.viewWidth&&this.oldHostMetrics_.absoluteLeft==a.absoluteLeft||this.corner_.setAttribute("x",this.vScroll.position_.x);this.oldHostMetrics_&&this.oldHostMetrics_.viewHeight==a.viewHeight&&this.oldHostMetrics_.absoluteTop==a.absoluteTop||this.corner_.setAttribute("y",
this.hScroll.position_.y);this.oldHostMetrics_=a}};Blockly.ScrollbarPair.prototype.set=function(a,b){var c={},d=a*this.hScroll.ratio_,e=b*this.vScroll.ratio_,f=this.vScroll.scrollViewSize_;c.x=this.getRatio_(d,this.hScroll.scrollViewSize_);c.y=this.getRatio_(e,f);this.workspace_.setMetrics(c);this.hScroll.setHandlePosition(d);this.vScroll.setHandlePosition(e)};Blockly.ScrollbarPair.prototype.getRatio_=function(a,b){var c=a/b;return isNaN(c)?0:c};
Blockly.Scrollbar=function(a,b,c){this.workspace_=a;this.pair_=c||!1;this.horizontal_=b;this.oldHostMetrics_=null;this.createDom_();this.position_=new goog.math.Coordinate(0,0);b?(this.svgBackground_.setAttribute("height",Blockly.Scrollbar.scrollbarThickness),this.svgHandle_.setAttribute("height",Blockly.Scrollbar.scrollbarThickness-5),this.svgHandle_.setAttribute("y",2.5),this.lengthAttribute_="width",this.positionAttribute_="x"):(this.svgBackground_.setAttribute("width",Blockly.Scrollbar.scrollbarThickness),
this.svgHandle_.setAttribute("width",Blockly.Scrollbar.scrollbarThickness-5),this.svgHandle_.setAttribute("x",2.5),this.lengthAttribute_="height",this.positionAttribute_="y");this.onMouseDownBarWrapper_=Blockly.bindEvent_(this.svgBackground_,"mousedown",this,this.onMouseDownBar_);this.onMouseDownHandleWrapper_=Blockly.bindEvent_(this.svgHandle_,"mousedown",this,this.onMouseDownHandle_)};Blockly.Scrollbar.prototype.scrollViewSize_=0;Blockly.Scrollbar.prototype.handleLength_=0;
this.svgHandle_.setAttribute("width",Blockly.Scrollbar.scrollbarThickness-5),this.svgHandle_.setAttribute("x",2.5),this.lengthAttribute_="height",this.positionAttribute_="y");this.onMouseDownBarWrapper_=Blockly.bindEventWithChecks_(this.svgBackground_,"mousedown",this,this.onMouseDownBar_);this.onMouseDownHandleWrapper_=Blockly.bindEventWithChecks_(this.svgHandle_,"mousedown",this,this.onMouseDownHandle_)};Blockly.Scrollbar.prototype.scrollViewSize_=0;Blockly.Scrollbar.prototype.handleLength_=0;
Blockly.Scrollbar.prototype.handlePosition_=0;Blockly.Scrollbar.prototype.isVisible_=!0;Blockly.Scrollbar.scrollbarThickness=15;goog.events.BrowserFeature.TOUCH_ENABLED&&(Blockly.Scrollbar.scrollbarThickness=25);
Blockly.Scrollbar.metricsAreEquivalent_=function(a,b){return a&&b&&a.viewWidth==b.viewWidth&&a.viewHeight==b.viewHeight&&a.viewLeft==b.viewLeft&&a.viewTop==b.viewTop&&a.absoluteTop==b.absoluteTop&&a.absoluteLeft==b.absoluteLeft&&a.contentWidth==b.contentWidth&&a.contentHeight==b.contentHeight&&a.contentLeft==b.contentLeft&&a.contentTop==b.contentTop?!0:!1};
Blockly.Scrollbar.prototype.dispose=function(){this.onMouseUpHandle_();Blockly.unbindEvent_(this.onMouseDownBarWrapper_);this.onMouseDownBarWrapper_=null;Blockly.unbindEvent_(this.onMouseDownHandleWrapper_);this.onMouseDownHandleWrapper_=null;goog.dom.removeNode(this.svgGroup_);this.workspace_=this.svgHandle_=this.svgBackground_=this.svgGroup_=null};Blockly.Scrollbar.prototype.setHandleLength_=function(a){this.handleLength_=a;this.svgHandle_.setAttribute(this.lengthAttribute_,this.handleLength_)};
Blockly.Scrollbar.prototype.dispose=function(){this.cleanUp_();Blockly.unbindEvent_(this.onMouseDownBarWrapper_);this.onMouseDownBarWrapper_=null;Blockly.unbindEvent_(this.onMouseDownHandleWrapper_);this.onMouseDownHandleWrapper_=null;goog.dom.removeNode(this.svgGroup_);this.workspace_=this.svgHandle_=this.svgBackground_=this.svgGroup_=null};Blockly.Scrollbar.prototype.setHandleLength_=function(a){this.handleLength_=a;this.svgHandle_.setAttribute(this.lengthAttribute_,this.handleLength_)};
Blockly.Scrollbar.prototype.setHandlePosition=function(a){this.handlePosition_=a;this.svgHandle_.setAttribute(this.positionAttribute_,this.handlePosition_)};Blockly.Scrollbar.prototype.setScrollViewSize_=function(a){this.scrollViewSize_=a;this.svgBackground_.setAttribute(this.lengthAttribute_,this.scrollViewSize_)};Blockly.Scrollbar.prototype.setPosition=function(a,b){this.position_.x=a;this.position_.y=b;this.svgGroup_.setAttribute("transform","translate("+this.position_.x+","+this.position_.y+")")};
Blockly.Scrollbar.prototype.resize=function(a){if(!a&&(a=this.workspace_.getMetrics(),!a))return;Blockly.Scrollbar.metricsAreEquivalent_(a,this.oldHostMetrics_)||(this.oldHostMetrics_=a,this.horizontal_?this.resizeHorizontal_(a):this.resizeVertical_(a),this.onScroll_())};Blockly.Scrollbar.prototype.resizeHorizontal_=function(a){this.resizeViewHorizontal(a)};
Blockly.Scrollbar.prototype.resizeViewHorizontal=function(a){var b=a.viewWidth-1;this.pair_&&(b-=Blockly.Scrollbar.scrollbarThickness);this.setScrollViewSize_(Math.max(0,b));b=a.absoluteLeft+.5;this.pair_&&this.workspace_.RTL&&(b+=Blockly.Scrollbar.scrollbarThickness);this.setPosition(b,a.absoluteTop+a.viewHeight-Blockly.Scrollbar.scrollbarThickness-.5);this.resizeContentHorizontal(a)};
@@ -995,26 +1002,27 @@ Blockly.Scrollbar.prototype.resizeViewVertical=function(a){var b=a.viewHeight-1;
Blockly.Scrollbar.prototype.resizeContentVertical=function(a){this.pair_||this.setVisible(this.scrollViewSize_<a.contentHeight);this.ratio_=this.scrollViewSize_/a.contentHeight;if(-Infinity==this.ratio_||Infinity==this.ratio_||isNaN(this.ratio_))this.ratio_=0;this.setHandleLength_(Math.max(0,a.viewHeight*this.ratio_));this.setHandlePosition(this.constrainHandle_((a.viewTop-a.contentTop)*this.ratio_))};
Blockly.Scrollbar.prototype.createDom_=function(){this.svgGroup_=Blockly.createSvgElement("g",{"class":"blocklyScrollbar"+(this.horizontal_?"Horizontal":"Vertical")},null);this.svgBackground_=Blockly.createSvgElement("rect",{"class":"blocklyScrollbarBackground"},this.svgGroup_);var a=Math.floor((Blockly.Scrollbar.scrollbarThickness-5)/2);this.svgHandle_=Blockly.createSvgElement("rect",{"class":"blocklyScrollbarHandle",rx:a,ry:a},this.svgGroup_);Blockly.Scrollbar.insertAfter_(this.svgGroup_,this.workspace_.getBubbleCanvas())};
Blockly.Scrollbar.prototype.isVisible=function(){return this.isVisible_};Blockly.Scrollbar.prototype.setVisible=function(a){if(a!=this.isVisible()){if(this.pair_)throw"Unable to toggle visibility of paired scrollbars.";(this.isVisible_=a)?this.svgGroup_.setAttribute("display","block"):(this.workspace_.setMetrics({x:0,y:0}),this.svgGroup_.setAttribute("display","none"))}};
Blockly.Scrollbar.prototype.onMouseDownBar_=function(a){this.onMouseUpHandle_();if(Blockly.isRightButton(a))a.stopPropagation();else{var b=Blockly.mouseToSvg(a,this.workspace_.getParentSvg(),this.workspace_.getInverseScreenCTM()),b=this.horizontal_?b.x:b.y,c=Blockly.getSvgXY_(this.svgHandle_,this.workspace_),c=this.horizontal_?c.x:c.y,d=this.handlePosition_,e=.95*this.handleLength_;b<=c?d-=e:b>=c+this.handleLength_&&(d+=e);this.setHandlePosition(this.constrainHandle_(d));this.onScroll_();a.stopPropagation();
a.preventDefault()}};Blockly.Scrollbar.prototype.onMouseDownHandle_=function(a){this.onMouseUpHandle_();Blockly.isRightButton(a)?a.stopPropagation():(this.startDragHandle=this.handlePosition_,this.startDragMouse=this.horizontal_?a.clientX:a.clientY,Blockly.Scrollbar.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,this.onMouseUpHandle_),Blockly.Scrollbar.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.onMouseMoveHandle_),a.stopPropagation(),a.preventDefault())};
Blockly.Scrollbar.prototype.onMouseMoveHandle_=function(a){this.setHandlePosition(this.constrainHandle_(this.startDragHandle+((this.horizontal_?a.clientX:a.clientY)-this.startDragMouse)));this.onScroll_()};
Blockly.Scrollbar.prototype.onMouseUpHandle_=function(){Blockly.hideChaff(!0);Blockly.Scrollbar.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_),Blockly.Scrollbar.onMouseUpWrapper_=null);Blockly.Scrollbar.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseMoveWrapper_),Blockly.Scrollbar.onMouseMoveWrapper_=null)};
Blockly.Scrollbar.prototype.onMouseDownBar_=function(a){Blockly.Touch.clearTouchIdentifier();this.cleanUp_();if(Blockly.isRightButton(a))a.stopPropagation();else{var b=Blockly.mouseToSvg(a,this.workspace_.getParentSvg(),this.workspace_.getInverseScreenCTM()),b=this.horizontal_?b.x:b.y,c=Blockly.getSvgXY_(this.svgHandle_,this.workspace_),c=this.horizontal_?c.x:c.y,d=this.handlePosition_,e=.95*this.handleLength_;b<=c?d-=e:b>=c+this.handleLength_&&(d+=e);this.setHandlePosition(this.constrainHandle_(d));
this.onScroll_();a.stopPropagation();a.preventDefault()}};
Blockly.Scrollbar.prototype.onMouseDownHandle_=function(a){this.cleanUp_();Blockly.isRightButton(a)?a.stopPropagation():(this.startDragHandle=this.handlePosition_,this.startDragMouse=this.horizontal_?a.clientX:a.clientY,Blockly.Scrollbar.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,this.onMouseUpHandle_),Blockly.Scrollbar.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",this,this.onMouseMoveHandle_),a.stopPropagation(),a.preventDefault())};
Blockly.Scrollbar.prototype.onMouseMoveHandle_=function(a){this.setHandlePosition(this.constrainHandle_(this.startDragHandle+((this.horizontal_?a.clientX:a.clientY)-this.startDragMouse)));this.onScroll_()};Blockly.Scrollbar.prototype.onMouseUpHandle_=function(){Blockly.Touch.clearTouchIdentifier();this.cleanUp_()};
Blockly.Scrollbar.prototype.cleanUp_=function(){Blockly.hideChaff(!0);Blockly.Scrollbar.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_),Blockly.Scrollbar.onMouseUpWrapper_=null);Blockly.Scrollbar.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseMoveWrapper_),Blockly.Scrollbar.onMouseMoveWrapper_=null)};
Blockly.Scrollbar.prototype.constrainHandle_=function(a){return a=0>=a||isNaN(a)||this.scrollViewSize_<this.handleLength_?0:Math.min(a,this.scrollViewSize_-this.handleLength_)};Blockly.Scrollbar.prototype.onScroll_=function(){var a=this.handlePosition_/this.scrollViewSize_;isNaN(a)&&(a=0);var b={};this.horizontal_?b.x=a:b.y=a;this.workspace_.setMetrics(b)};Blockly.Scrollbar.prototype.set=function(a){this.setHandlePosition(this.constrainHandle_(a*this.ratio_));this.onScroll_()};
Blockly.Scrollbar.insertAfter_=function(a,b){var c=b.nextSibling,d=b.parentNode;if(!d)throw"Reference node has no parent.";c?d.insertBefore(a,c):d.appendChild(a)};Blockly.Trashcan=function(a){this.workspace_=a};Blockly.Trashcan.prototype.WIDTH_=47;Blockly.Trashcan.prototype.BODY_HEIGHT_=44;Blockly.Trashcan.prototype.LID_HEIGHT_=16;Blockly.Trashcan.prototype.MARGIN_BOTTOM_=20;Blockly.Trashcan.prototype.MARGIN_SIDE_=20;Blockly.Trashcan.prototype.MARGIN_HOTSPOT_=10;Blockly.Trashcan.prototype.SPRITE_LEFT_=0;Blockly.Trashcan.prototype.SPRITE_TOP_=32;Blockly.Trashcan.prototype.isOpen=!1;Blockly.Trashcan.prototype.svgGroup_=null;
Blockly.Trashcan.prototype.svgLid_=null;Blockly.Trashcan.prototype.lidTask_=0;Blockly.Trashcan.prototype.lidOpen_=0;Blockly.Trashcan.prototype.left_=0;Blockly.Trashcan.prototype.top_=0;
Blockly.Trashcan.prototype.createDom=function(){this.svgGroup_=Blockly.createSvgElement("g",{"class":"blocklyTrash"},null);var a=String(Math.random()).substring(2),b=Blockly.createSvgElement("clipPath",{id:"blocklyTrashBodyClipPath"+a},this.svgGroup_);Blockly.createSvgElement("rect",{width:this.WIDTH_,height:this.BODY_HEIGHT_,y:this.LID_HEIGHT_},b);Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,x:-this.SPRITE_LEFT_,height:Blockly.SPRITE.height,y:-this.SPRITE_TOP_,"clip-path":"url(#blocklyTrashBodyClipPath"+
a+")"},this.svgGroup_).setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",this.workspace_.options.pathToMedia+Blockly.SPRITE.url);b=Blockly.createSvgElement("clipPath",{id:"blocklyTrashLidClipPath"+a},this.svgGroup_);Blockly.createSvgElement("rect",{width:this.WIDTH_,height:this.LID_HEIGHT_},b);this.svgLid_=Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,x:-this.SPRITE_LEFT_,height:Blockly.SPRITE.height,y:-this.SPRITE_TOP_,"clip-path":"url(#blocklyTrashLidClipPath"+a+")"},this.svgGroup_);
this.svgLid_.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",this.workspace_.options.pathToMedia+Blockly.SPRITE.url);Blockly.bindEvent_(this.svgGroup_,"mouseup",this,this.click);this.animateLid_();return this.svgGroup_};Blockly.Trashcan.prototype.init=function(a){this.bottom_=this.MARGIN_BOTTOM_+a;this.setOpen_(!1);return this.bottom_+this.BODY_HEIGHT_+this.LID_HEIGHT_};
this.svgLid_.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",this.workspace_.options.pathToMedia+Blockly.SPRITE.url);Blockly.bindEventWithChecks_(this.svgGroup_,"mouseup",this,this.click);this.animateLid_();return this.svgGroup_};Blockly.Trashcan.prototype.init=function(a){this.bottom_=this.MARGIN_BOTTOM_+a;this.setOpen_(!1);return this.bottom_+this.BODY_HEIGHT_+this.LID_HEIGHT_};
Blockly.Trashcan.prototype.dispose=function(){this.svgGroup_&&(goog.dom.removeNode(this.svgGroup_),this.svgGroup_=null);this.workspace_=this.svgLid_=null;goog.Timer.clear(this.lidTask_)};
Blockly.Trashcan.prototype.position=function(){var a=this.workspace_.getMetrics();a&&(this.workspace_.RTL?(this.left_=this.MARGIN_SIDE_+Blockly.Scrollbar.scrollbarThickness,a.toolboxPosition==Blockly.TOOLBOX_AT_LEFT&&(this.left_+=a.flyoutWidth,this.workspace_.toolbox_&&(this.left_+=a.absoluteLeft))):(this.left_=a.viewWidth+a.absoluteLeft-this.WIDTH_-this.MARGIN_SIDE_-Blockly.Scrollbar.scrollbarThickness,a.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT&&(this.left_-=a.flyoutWidth)),this.top_=a.viewHeight+
a.absoluteTop-(this.BODY_HEIGHT_+this.LID_HEIGHT_)-this.bottom_,a.toolboxPosition==Blockly.TOOLBOX_AT_BOTTOM&&(this.top_-=a.flyoutHeight),this.svgGroup_.setAttribute("transform","translate("+this.left_+","+this.top_+")"))};
Blockly.Trashcan.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect();return new goog.math.Rect(a.left+this.SPRITE_LEFT_-this.MARGIN_HOTSPOT_,a.top+this.SPRITE_TOP_-this.MARGIN_HOTSPOT_,this.WIDTH_+2*this.MARGIN_HOTSPOT_,this.LID_HEIGHT_+this.BODY_HEIGHT_+2*this.MARGIN_HOTSPOT_)};Blockly.Trashcan.prototype.setOpen_=function(a){this.isOpen!=a&&(goog.Timer.clear(this.lidTask_),this.isOpen=a,this.animateLid_())};
Blockly.Trashcan.prototype.animateLid_=function(){this.lidOpen_+=this.isOpen?.2:-.2;this.lidOpen_=goog.math.clamp(this.lidOpen_,0,1);var a=45*this.lidOpen_;this.svgLid_.setAttribute("transform","rotate("+(this.workspace_.RTL?-a:a)+","+(this.workspace_.RTL?4:this.WIDTH_-4)+","+(this.LID_HEIGHT_-2)+")");a=goog.math.lerp(.4,.8,this.lidOpen_);this.svgGroup_.style.opacity=a;0<this.lidOpen_&&1>this.lidOpen_&&(this.lidTask_=goog.Timer.callOnce(this.animateLid_,20,this))};
Blockly.Trashcan.prototype.close=function(){this.setOpen_(!1)};Blockly.Trashcan.prototype.click=function(){var a=this.workspace_.startScrollX-this.workspace_.scrollX,b=this.workspace_.startScrollY-this.workspace_.scrollY;Math.sqrt(a*a+b*b)>Blockly.DRAG_RADIUS||console.log("TODO: Inspect trash.")};Blockly.Xml={};Blockly.Xml.workspaceToDom=function(a){var b=goog.dom.createDom("xml");a=a.getTopBlocks(!0);for(var c=0,d;d=a[c];c++)b.appendChild(Blockly.Xml.blockToDomWithXY(d));return b};Blockly.Xml.blockToDomWithXY=function(a){var b;a.workspace.RTL&&(b=a.workspace.getWidth());var c=Blockly.Xml.blockToDom(a),d=a.getRelativeToSurfaceXY();c.setAttribute("x",Math.round(a.workspace.RTL?b-d.x:d.x));c.setAttribute("y",Math.round(d.y));return c};
Blockly.Xml.blockToDom=function(a){var b=goog.dom.createDom(a.isShadow()?"shadow":"block");b.setAttribute("type",a.type);b.setAttribute("id",a.id);if(a.mutationToDom){var c=a.mutationToDom();c&&(c.hasChildNodes()||c.hasAttributes())&&b.appendChild(c)}for(var c=0,d;d=a.inputList[c];c++)for(var e=0,f;f=d.fieldRow[e];e++)if(f.name&&f.EDITABLE){var g=goog.dom.createDom("field",null,f.getValue());g.setAttribute("name",f.name);b.appendChild(g)}if(c=a.getCommentText())c=goog.dom.createDom("comment",null,
c),"object"==typeof a.comment&&(c.setAttribute("pinned",a.comment.isVisible()),d=a.comment.getBubbleSize(),c.setAttribute("h",d.height),c.setAttribute("w",d.width)),b.appendChild(c);a.data&&(c=goog.dom.createDom("data",null,a.data),b.appendChild(c));for(c=0;d=a.inputList[c];c++){var h;f=!0;d.type!=Blockly.DUMMY_INPUT&&(g=d.connection.targetBlock(),d.type==Blockly.INPUT_VALUE?h=goog.dom.createDom("value"):d.type==Blockly.NEXT_STATEMENT&&(h=goog.dom.createDom("statement")),e=d.connection.getShadowDom(),
!e||g&&g.isShadow()||h.appendChild(Blockly.Xml.cloneShadow_(e)),g&&(h.appendChild(Blockly.Xml.blockToDom(g)),f=!1),h.setAttribute("name",d.name),f||b.appendChild(h))}a.inputsInlineDefault!=a.inputsInline&&b.setAttribute("inline",a.inputsInline);a.isCollapsed()&&b.setAttribute("collapsed",!0);a.disabled&&b.setAttribute("disabled",!0);a.isDeletable()||a.isShadow()||b.setAttribute("deletable",!1);a.isMovable()||a.isShadow()||b.setAttribute("movable",!1);a.isEditable()||b.setAttribute("editable",!1);
if(c=a.getNextBlock())h=goog.dom.createDom("next",null,Blockly.Xml.blockToDom(c)),b.appendChild(h);e=a.nextConnection&&a.nextConnection.getShadowDom();!e||c&&c.isShadow()||h.appendChild(Blockly.Xml.cloneShadow_(e));return b};
Blockly.Trashcan.prototype.close=function(){this.setOpen_(!1)};Blockly.Trashcan.prototype.click=function(){var a=this.workspace_.startScrollX-this.workspace_.scrollX,b=this.workspace_.startScrollY-this.workspace_.scrollY;Math.sqrt(a*a+b*b)>Blockly.DRAG_RADIUS||console.log("TODO: Inspect trash.")};Blockly.Xml={};Blockly.Xml.workspaceToDom=function(a,b){for(var c=goog.dom.createDom("xml"),d=a.getTopBlocks(!0),e=0,f;f=d[e];e++)c.appendChild(Blockly.Xml.blockToDomWithXY(f,b));return c};Blockly.Xml.blockToDomWithXY=function(a,b){var c;a.workspace.RTL&&(c=a.workspace.getWidth());var d=Blockly.Xml.blockToDom(a,b),e=a.getRelativeToSurfaceXY();d.setAttribute("x",Math.round(a.workspace.RTL?c-e.x:e.x));d.setAttribute("y",Math.round(e.y));return d};
Blockly.Xml.blockToDom=function(a,b){var c=goog.dom.createDom(a.isShadow()?"shadow":"block");c.setAttribute("type",a.type);b||c.setAttribute("id",a.id);if(a.mutationToDom){var d=a.mutationToDom();d&&(d.hasChildNodes()||d.hasAttributes())&&c.appendChild(d)}for(var d=0,e;e=a.inputList[d];d++)for(var f=0,g;g=e.fieldRow[f];f++)if(g.name&&g.EDITABLE){var h=goog.dom.createDom("field",null,g.getValue());h.setAttribute("name",g.name);c.appendChild(h)}if(d=a.getCommentText())d=goog.dom.createDom("comment",
null,d),"object"==typeof a.comment&&(d.setAttribute("pinned",a.comment.isVisible()),e=a.comment.getBubbleSize(),d.setAttribute("h",e.height),d.setAttribute("w",e.width)),c.appendChild(d);a.data&&(d=goog.dom.createDom("data",null,a.data),c.appendChild(d));for(d=0;e=a.inputList[d];d++){var k;g=!0;e.type!=Blockly.DUMMY_INPUT&&(h=e.connection.targetBlock(),e.type==Blockly.INPUT_VALUE?k=goog.dom.createDom("value"):e.type==Blockly.NEXT_STATEMENT&&(k=goog.dom.createDom("statement")),f=e.connection.getShadowDom(),
!f||h&&h.isShadow()||k.appendChild(Blockly.Xml.cloneShadow_(f)),h&&(k.appendChild(Blockly.Xml.blockToDom(h,b)),g=!1),k.setAttribute("name",e.name),g||c.appendChild(k))}a.inputsInlineDefault!=a.inputsInline&&c.setAttribute("inline",a.inputsInline);a.isCollapsed()&&c.setAttribute("collapsed",!0);a.disabled&&c.setAttribute("disabled",!0);a.isDeletable()||a.isShadow()||c.setAttribute("deletable",!1);a.isMovable()||a.isShadow()||c.setAttribute("movable",!1);a.isEditable()||c.setAttribute("editable",!1);
if(d=a.getNextBlock())k=goog.dom.createDom("next",null,Blockly.Xml.blockToDom(d,b)),c.appendChild(k);f=a.nextConnection&&a.nextConnection.getShadowDom();!f||d&&d.isShadow()||k.appendChild(Blockly.Xml.cloneShadow_(f));return c};
Blockly.Xml.cloneShadow_=function(a){for(var b=a=a.cloneNode(!0),c;b;)if(b.firstChild)b=b.firstChild;else{for(;b&&!b.nextSibling;)c=b,b=b.parentNode,3==c.nodeType&&""==c.data.trim()&&b.firstChild!=c&&goog.dom.removeNode(c);b&&(c=b,b=b.nextSibling,3==c.nodeType&&""==c.data.trim()&&goog.dom.removeNode(c))}return a};Blockly.Xml.domToText=function(a){return(new XMLSerializer).serializeToString(a)};
Blockly.Xml.domToPrettyText=function(a){a=Blockly.Xml.domToText(a).split("<");for(var b="",c=1;c<a.length;c++){var d=a[c];"/"==d[0]&&(b=b.substring(2));a[c]=b+"<"+d;"/"!=d[0]&&"/>"!=d.slice(-2)&&(b+=" ")}a=a.join("\n");a=a.replace(/(<(\w+)\b[^>]*>[^\n]*)\n *<\/\2>/g,"$1</$2>");return a.replace(/^\n/,"")};
Blockly.Xml.textToDom=function(a){(a=(new DOMParser).parseFromString(a,"text/xml"))&&a.firstChild&&"xml"==a.firstChild.nodeName.toLowerCase()&&a.firstChild===a.lastChild||goog.asserts.fail("Blockly.Xml.textToDom did not obtain a valid XML tree.");return a.firstChild};
@@ -1032,9 +1040,9 @@ Blockly.Xml.deleteNext=function(a){for(var b=0,c;c=a.childNodes[b];b++)if("next"
Blockly.ZoomControls=function(a){this.workspace_=a};Blockly.ZoomControls.prototype.WIDTH_=32;Blockly.ZoomControls.prototype.HEIGHT_=110;Blockly.ZoomControls.prototype.MARGIN_BOTTOM_=20;Blockly.ZoomControls.prototype.MARGIN_SIDE_=20;Blockly.ZoomControls.prototype.svgGroup_=null;Blockly.ZoomControls.prototype.left_=0;Blockly.ZoomControls.prototype.top_=0;
Blockly.ZoomControls.prototype.createDom=function(){var a=this.workspace_;this.svgGroup_=Blockly.createSvgElement("g",{"class":"blocklyZoom"},null);var b=String(Math.random()).substring(2),c=Blockly.createSvgElement("clipPath",{id:"blocklyZoomoutClipPath"+b},this.svgGroup_);Blockly.createSvgElement("rect",{width:32,height:32,y:77},c);var d=Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,height:Blockly.SPRITE.height,x:-64,y:-15,"clip-path":"url(#blocklyZoomoutClipPath"+b+")"},this.svgGroup_);
d.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a.options.pathToMedia+Blockly.SPRITE.url);c=Blockly.createSvgElement("clipPath",{id:"blocklyZoominClipPath"+b},this.svgGroup_);Blockly.createSvgElement("rect",{width:32,height:32,y:43},c);var e=Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,height:Blockly.SPRITE.height,x:-32,y:-49,"clip-path":"url(#blocklyZoominClipPath"+b+")"},this.svgGroup_);e.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a.options.pathToMedia+
Blockly.SPRITE.url);c=Blockly.createSvgElement("clipPath",{id:"blocklyZoomresetClipPath"+b},this.svgGroup_);Blockly.createSvgElement("rect",{width:32,height:32},c);b=Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,height:Blockly.SPRITE.height,y:-92,"clip-path":"url(#blocklyZoomresetClipPath"+b+")"},this.svgGroup_);b.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a.options.pathToMedia+Blockly.SPRITE.url);Blockly.bindEvent_(b,"mousedown",null,function(b){a.setScale(1);a.scrollCenter();
b.stopPropagation();b.preventDefault()});Blockly.bindEvent_(e,"mousedown",null,function(b){a.zoomCenter(1);b.stopPropagation();b.preventDefault()});Blockly.bindEvent_(d,"mousedown",null,function(b){a.zoomCenter(-1);b.stopPropagation();b.preventDefault()});return this.svgGroup_};Blockly.ZoomControls.prototype.init=function(a){this.bottom_=this.MARGIN_BOTTOM_+a;return this.bottom_+this.HEIGHT_};
Blockly.ZoomControls.prototype.dispose=function(){this.svgGroup_&&(goog.dom.removeNode(this.svgGroup_),this.svgGroup_=null);this.workspace_=null};
Blockly.SPRITE.url);c=Blockly.createSvgElement("clipPath",{id:"blocklyZoomresetClipPath"+b},this.svgGroup_);Blockly.createSvgElement("rect",{width:32,height:32},c);b=Blockly.createSvgElement("image",{width:Blockly.SPRITE.width,height:Blockly.SPRITE.height,y:-92,"clip-path":"url(#blocklyZoomresetClipPath"+b+")"},this.svgGroup_);b.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",a.options.pathToMedia+Blockly.SPRITE.url);Blockly.bindEventWithChecks_(b,"mousedown",null,function(b){a.setScale(a.options.zoomOptions.startScale);
a.scrollCenter();Blockly.Touch.clearTouchIdentifier();b.stopPropagation();b.preventDefault()});Blockly.bindEventWithChecks_(e,"mousedown",null,function(b){a.zoomCenter(1);Blockly.Touch.clearTouchIdentifier();b.stopPropagation();b.preventDefault()});Blockly.bindEventWithChecks_(d,"mousedown",null,function(b){a.zoomCenter(-1);Blockly.Touch.clearTouchIdentifier();b.stopPropagation();b.preventDefault()});return this.svgGroup_};
Blockly.ZoomControls.prototype.init=function(a){this.bottom_=this.MARGIN_BOTTOM_+a;return this.bottom_+this.HEIGHT_};Blockly.ZoomControls.prototype.dispose=function(){this.svgGroup_&&(goog.dom.removeNode(this.svgGroup_),this.svgGroup_=null);this.workspace_=null};
Blockly.ZoomControls.prototype.position=function(){var a=this.workspace_.getMetrics();a&&(this.workspace_.RTL?(this.left_=this.MARGIN_SIDE_+Blockly.Scrollbar.scrollbarThickness,a.toolboxPosition==Blockly.TOOLBOX_AT_LEFT&&(this.left_+=a.flyoutWidth,this.workspace_.toolbox_&&(this.left_+=a.absoluteLeft))):(this.left_=a.viewWidth+a.absoluteLeft-this.WIDTH_-this.MARGIN_SIDE_-Blockly.Scrollbar.scrollbarThickness,a.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT&&(this.left_-=a.flyoutWidth)),this.top_=a.viewHeight+
a.absoluteTop-this.HEIGHT_-this.bottom_,a.toolboxPosition==Blockly.TOOLBOX_AT_BOTTOM&&(this.top_-=a.flyoutHeight),this.svgGroup_.setAttribute("transform","translate("+this.left_+","+this.top_+")"))};
// Copyright 2014 Google Inc. Apache License 2.0
@@ -1042,26 +1050,28 @@ Blockly.WorkspaceSvg=function(a){Blockly.WorkspaceSvg.superClass_.constructor.ca
Blockly.WorkspaceSvg.prototype.isFlyout=!1;Blockly.WorkspaceSvg.prototype.isMutator=!1;Blockly.WorkspaceSvg.prototype.dragMode_=Blockly.DRAG_NONE;Blockly.WorkspaceSvg.prototype.scrollX=0;Blockly.WorkspaceSvg.prototype.scrollY=0;Blockly.WorkspaceSvg.prototype.startScrollX=0;Blockly.WorkspaceSvg.prototype.startScrollY=0;Blockly.WorkspaceSvg.prototype.dragDeltaXY_=null;Blockly.WorkspaceSvg.prototype.scale=1;Blockly.WorkspaceSvg.prototype.trashcan=null;Blockly.WorkspaceSvg.prototype.scrollbar=null;
Blockly.WorkspaceSvg.prototype.lastSound_=null;Blockly.WorkspaceSvg.prototype.lastRecordedPageScroll_=null;Blockly.WorkspaceSvg.prototype.inverseScreenCTM_=null;Blockly.WorkspaceSvg.prototype.getInverseScreenCTM=function(){return this.inverseScreenCTM_};Blockly.WorkspaceSvg.prototype.updateInverseScreenCTM=function(){this.inverseScreenCTM_=this.getParentSvg().getScreenCTM().inverse()};Blockly.WorkspaceSvg.prototype.setResizeHandlerWrapper=function(a){this.resizeHandlerWrapper_=a};
Blockly.WorkspaceSvg.prototype.createDom=function(a){this.svgGroup_=Blockly.createSvgElement("g",{"class":"blocklyWorkspace"},null);a&&(this.svgBackground_=Blockly.createSvgElement("rect",{height:"100%",width:"100%","class":a},this.svgGroup_),"blocklyMainBackground"==a&&(this.svgBackground_.style.fill="url(#"+this.options.gridPattern.id+")"));this.svgBlockCanvas_=Blockly.createSvgElement("g",{"class":"blocklyBlockCanvas"},this.svgGroup_,this);this.svgBubbleCanvas_=Blockly.createSvgElement("g",{"class":"blocklyBubbleCanvas"},
this.svgGroup_,this);a=Blockly.Scrollbar.scrollbarThickness;this.options.hasTrashcan&&(a=this.addTrashcan_(a));this.options.zoomOptions&&this.options.zoomOptions.controls&&(a=this.addZoomControls_(a));if(!this.isFlyout){Blockly.bindEvent_(this.svgGroup_,"mousedown",this,this.onMouseDown_);var b=this;Blockly.bindEvent_(this.svgGroup_,"touchstart",null,function(a){Blockly.longStart_(a,b)});this.options.zoomOptions&&this.options.zoomOptions.wheel&&Blockly.bindEvent_(this.svgGroup_,"wheel",this,this.onMouseWheel_)}this.options.hasCategories?
this.toolbox_=new Blockly.Toolbox(this):this.options.languageTree&&this.addFlyout_();this.updateGridPattern_();this.recordDeleteAreas();return this.svgGroup_};
this.svgGroup_,this);a=Blockly.Scrollbar.scrollbarThickness;this.options.hasTrashcan&&(a=this.addTrashcan_(a));this.options.zoomOptions&&this.options.zoomOptions.controls&&(a=this.addZoomControls_(a));if(!this.isFlyout){Blockly.bindEventWithChecks_(this.svgGroup_,"mousedown",this,this.onMouseDown_);var b=this;Blockly.bindEventWithChecks_(this.svgGroup_,"touchstart",null,function(a){Blockly.longStart_(a,b)});this.options.zoomOptions&&this.options.zoomOptions.wheel&&Blockly.bindEventWithChecks_(this.svgGroup_,
"wheel",this,this.onMouseWheel_)}this.options.hasCategories?this.toolbox_=new Blockly.Toolbox(this):this.options.languageTree&&this.addFlyout_();this.updateGridPattern_();this.recordDeleteAreas();return this.svgGroup_};
Blockly.WorkspaceSvg.prototype.dispose=function(){this.rendered=!1;Blockly.WorkspaceSvg.superClass_.dispose.call(this);this.svgGroup_&&(goog.dom.removeNode(this.svgGroup_),this.svgGroup_=null);this.svgBubbleCanvas_=this.svgBlockCanvas_=null;this.toolbox_&&(this.toolbox_.dispose(),this.toolbox_=null);this.flyout_&&(this.flyout_.dispose(),this.flyout_=null);this.trashcan&&(this.trashcan.dispose(),this.trashcan=null);this.scrollbar&&(this.scrollbar.dispose(),this.scrollbar=null);this.zoomControls_&&
(this.zoomControls_.dispose(),this.zoomControls_=null);this.options.parentWorkspace||goog.dom.removeNode(this.getParentSvg().parentNode);this.resizeHandlerWrapper_&&(Blockly.unbindEvent_(this.resizeHandlerWrapper_),this.resizeHandlerWrapper_=null)};Blockly.WorkspaceSvg.prototype.newBlock=function(a,b){return new Blockly.BlockSvg(this,a,b)};
Blockly.WorkspaceSvg.prototype.addTrashcan_=function(a){this.trashcan=new Blockly.Trashcan(this);var b=this.trashcan.createDom();this.svgGroup_.insertBefore(b,this.svgBlockCanvas_);return this.trashcan.init(a)};Blockly.WorkspaceSvg.prototype.addZoomControls_=function(a){this.zoomControls_=new Blockly.ZoomControls(this);var b=this.zoomControls_.createDom();this.svgGroup_.appendChild(b);return this.zoomControls_.init(a)};
Blockly.WorkspaceSvg.prototype.addFlyout_=function(){this.flyout_=new Blockly.Flyout({disabledPatternId:this.options.disabledPatternId,parentWorkspace:this,RTL:this.RTL,horizontalLayout:this.horizontalLayout,toolboxPosition:this.options.toolboxPosition});this.flyout_.autoClose=!1;var a=this.flyout_.createDom();this.svgGroup_.insertBefore(a,this.svgBlockCanvas_)};Blockly.WorkspaceSvg.prototype.updateScreenCalculations_=function(){this.updateInverseScreenCTM();this.recordDeleteAreas()};
Blockly.WorkspaceSvg.prototype.resizeContents=function(){this.scrollbar&&this.scrollbar.resize();this.updateInverseScreenCTM()};Blockly.WorkspaceSvg.prototype.resize=function(){this.toolbox_&&this.toolbox_.position();this.flyout_&&this.flyout_.position();this.trashcan&&this.trashcan.position();this.zoomControls_&&this.zoomControls_.position();this.scrollbar&&this.scrollbar.resize();this.updateScreenCalculations_()};
Blockly.WorkspaceSvg.prototype.updateScreenCalculationsIfScrolled=function(){var a=goog.dom.getDocumentScroll();goog.math.Coordinate.equals(this.lastRecordedPageScroll_,a)||(this.lastRecordedPageScroll_=a,this.updateScreenCalculations_())};Blockly.WorkspaceSvg.prototype.getCanvas=function(){return this.svgBlockCanvas_};Blockly.WorkspaceSvg.prototype.getBubbleCanvas=function(){return this.svgBubbleCanvas_};
Blockly.WorkspaceSvg.prototype.getParentSvg=function(){if(this.cachedParentSvg_)return this.cachedParentSvg_;for(var a=this.svgGroup_;a;){if("svg"==a.tagName)return this.cachedParentSvg_=a;a=a.parentNode}return null};Blockly.WorkspaceSvg.prototype.translate=function(a,b){var c="translate("+a+","+b+") scale("+this.scale+")";this.svgBlockCanvas_.setAttribute("transform",c);this.svgBubbleCanvas_.setAttribute("transform",c)};
Blockly.WorkspaceSvg.prototype.getWidth=function(){var a=this.getMetrics();return a?a.viewWidth/this.scale:0};Blockly.WorkspaceSvg.prototype.setVisible=function(a){this.getParentSvg().style.display=a?"block":"none";this.toolbox_&&(this.toolbox_.HtmlDiv.style.display=a?"block":"none");a?(this.render(),this.toolbox_&&this.toolbox_.position()):Blockly.hideChaff(!0)};Blockly.WorkspaceSvg.prototype.render=function(){for(var a=this.getAllBlocks(),b=a.length-1;0<=b;b--)a[b].render(!1)};
Blockly.WorkspaceSvg.prototype.traceOn=function(a){this.traceOn_=a;this.traceWrapper_&&(Blockly.unbindEvent_(this.traceWrapper_),this.traceWrapper_=null);a&&(this.traceWrapper_=Blockly.bindEvent_(this.svgBlockCanvas_,"blocklySelectChange",this,function(){this.traceOn_=!1}))};
Blockly.WorkspaceSvg.prototype.addFlyout_=function(){this.flyout_=new Blockly.Flyout({disabledPatternId:this.options.disabledPatternId,parentWorkspace:this,RTL:this.RTL,oneBasedIndex:this.options.oneBasedIndex,horizontalLayout:this.horizontalLayout,toolboxPosition:this.options.toolboxPosition});this.flyout_.autoClose=!1;var a=this.flyout_.createDom();this.svgGroup_.insertBefore(a,this.svgBlockCanvas_)};
Blockly.WorkspaceSvg.prototype.updateScreenCalculations_=function(){this.updateInverseScreenCTM();this.recordDeleteAreas()};Blockly.WorkspaceSvg.prototype.resizeContents=function(){this.scrollbar&&this.scrollbar.resize();this.updateInverseScreenCTM()};
Blockly.WorkspaceSvg.prototype.resize=function(){this.toolbox_&&this.toolbox_.position();this.flyout_&&this.flyout_.position();this.trashcan&&this.trashcan.position();this.zoomControls_&&this.zoomControls_.position();this.scrollbar&&this.scrollbar.resize();this.updateScreenCalculations_()};Blockly.WorkspaceSvg.prototype.updateScreenCalculationsIfScrolled=function(){var a=goog.dom.getDocumentScroll();goog.math.Coordinate.equals(this.lastRecordedPageScroll_,a)||(this.lastRecordedPageScroll_=a,this.updateScreenCalculations_())};
Blockly.WorkspaceSvg.prototype.getCanvas=function(){return this.svgBlockCanvas_};Blockly.WorkspaceSvg.prototype.getBubbleCanvas=function(){return this.svgBubbleCanvas_};Blockly.WorkspaceSvg.prototype.getParentSvg=function(){if(this.cachedParentSvg_)return this.cachedParentSvg_;for(var a=this.svgGroup_;a;){if("svg"==a.tagName)return this.cachedParentSvg_=a;a=a.parentNode}return null};
Blockly.WorkspaceSvg.prototype.translate=function(a,b){var c="translate("+a+","+b+") scale("+this.scale+")";this.svgBlockCanvas_.setAttribute("transform",c);this.svgBubbleCanvas_.setAttribute("transform",c)};Blockly.WorkspaceSvg.prototype.getWidth=function(){var a=this.getMetrics();return a?a.viewWidth/this.scale:0};
Blockly.WorkspaceSvg.prototype.setVisible=function(a){this.getParentSvg().style.display=a?"block":"none";this.toolbox_&&(this.toolbox_.HtmlDiv.style.display=a?"block":"none");a?(this.render(),this.toolbox_&&this.toolbox_.position()):Blockly.hideChaff(!0)};Blockly.WorkspaceSvg.prototype.render=function(){for(var a=this.getAllBlocks(),b=a.length-1;0<=b;b--)a[b].render(!1)};
Blockly.WorkspaceSvg.prototype.traceOn=function(a){this.traceOn_=a;this.traceWrapper_&&(Blockly.unbindEvent_(this.traceWrapper_),this.traceWrapper_=null);a&&(this.traceWrapper_=Blockly.bindEventWithChecks_(this.svgBlockCanvas_,"blocklySelectChange",this,function(){this.traceOn_=!1}))};
Blockly.WorkspaceSvg.prototype.highlightBlock=function(a){this.traceOn_&&Blockly.dragMode_!=Blockly.DRAG_NONE&&this.traceOn(!1);if(this.traceOn_){var b=null;if(a&&(b=this.getBlockById(a),!b))return;this.traceOn(!1);b?b.select():Blockly.selected&&Blockly.selected.unselect();var c=this;setTimeout(function(){c.traceOn(!0)},1)}};
Blockly.WorkspaceSvg.prototype.paste=function(a){if(this.rendered&&!(a.getElementsByTagName("block").length>=this.remainingCapacity())){Blockly.terminateDrag_();Blockly.Events.disable();try{var b=Blockly.Xml.domToBlock(a,this),c=parseInt(a.getAttribute("x"),10),d=parseInt(a.getAttribute("y"),10);if(!isNaN(c)&&!isNaN(d)){this.RTL&&(c=-c);do{a=!1;for(var e=this.getAllBlocks(),f=0,g;g=e[f];f++){var h=g.getRelativeToSurfaceXY();if(1>=Math.abs(c-h.x)&&1>=Math.abs(d-h.y)){a=!0;break}}if(!a)for(var k=b.getConnections_(!1),
f=0,m;m=k[f];f++)if(m.closest(Blockly.SNAP_RADIUS,new goog.math.Coordinate(c,d)).connection){a=!0;break}a&&(c=this.RTL?c-Blockly.SNAP_RADIUS:c+Blockly.SNAP_RADIUS,d+=2*Blockly.SNAP_RADIUS)}while(a);b.moveBy(c,d)}}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!b.isShadow()&&Blockly.Events.fire(new Blockly.Events.Create(b));b.select()}};
Blockly.WorkspaceSvg.prototype.createVariable=function(a){Blockly.WorkspaceSvg.superClass_.createVariable.call(this,a);this.toolbox_&&this.toolbox_.flyout_&&this.toolbox_.refreshSelection()};Blockly.WorkspaceSvg.prototype.recordDeleteAreas=function(){this.deleteAreaTrash_=this.trashcan?this.trashcan.getClientRect():null;this.deleteAreaToolbox_=this.flyout_?this.flyout_.getClientRect():this.toolbox_?this.toolbox_.getClientRect():null};
Blockly.WorkspaceSvg.prototype.createVariable=function(a){Blockly.WorkspaceSvg.superClass_.createVariable.call(this,a);this.toolbox_&&this.toolbox_.flyout_&&!Blockly.Flyout.startFlyout_&&this.toolbox_.refreshSelection()};Blockly.WorkspaceSvg.prototype.recordDeleteAreas=function(){this.deleteAreaTrash_=this.trashcan?this.trashcan.getClientRect():null;this.deleteAreaToolbox_=this.flyout_?this.flyout_.getClientRect():this.toolbox_?this.toolbox_.getClientRect():null};
Blockly.WorkspaceSvg.prototype.isDeleteArea=function(a){a=new goog.math.Coordinate(a.clientX,a.clientY);if(this.deleteAreaTrash_){if(this.deleteAreaTrash_.contains(a))return this.trashcan.setOpen_(!0),Blockly.Css.setCursor(Blockly.Css.Cursor.DELETE),!0;this.trashcan.setOpen_(!1)}if(this.deleteAreaToolbox_&&this.deleteAreaToolbox_.contains(a))return Blockly.Css.setCursor(Blockly.Css.Cursor.DELETE),!0;Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);return!1};
Blockly.WorkspaceSvg.prototype.onMouseDown_=function(a){this.markFocused();Blockly.isTargetInput_(a)||(Blockly.terminateDrag_(),Blockly.hideChaff(),a.target&&a.target.nodeName&&("svg"==a.target.nodeName.toLowerCase()||a.target==this.svgBackground_)&&Blockly.selected&&!this.options.readOnly&&Blockly.selected.unselect(),Blockly.isRightButton(a)?this.showContextMenu_(a):this.scrollbar&&(this.dragMode_=Blockly.DRAG_BEGIN,this.startDragMouseX=a.clientX,this.startDragMouseY=a.clientY,this.startDragMetrics=
this.getMetrics(),this.startScrollX=this.scrollX,this.startScrollY=this.scrollY,"mouseup"in Blockly.bindEvent_.TOUCH_MAP&&(Blockly.onTouchUpWrapper_=Blockly.onTouchUpWrapper_||[],Blockly.onTouchUpWrapper_=Blockly.onTouchUpWrapper_.concat(Blockly.bindEvent_(document,"mouseup",null,Blockly.onMouseUp_))),Blockly.onMouseMoveWrapper_=Blockly.onMouseMoveWrapper_||[],Blockly.onMouseMoveWrapper_=Blockly.onMouseMoveWrapper_.concat(Blockly.bindEvent_(document,"mousemove",null,Blockly.onMouseMove_))),a.stopPropagation(),
a.preventDefault())};Blockly.WorkspaceSvg.prototype.startDrag=function(a,b){var c=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());c.x/=this.scale;c.y/=this.scale;this.dragDeltaXY_=goog.math.Coordinate.difference(b,c)};Blockly.WorkspaceSvg.prototype.moveDrag=function(a){a=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());a.x/=this.scale;a.y/=this.scale;return goog.math.Coordinate.sum(this.dragDeltaXY_,a)};
Blockly.WorkspaceSvg.prototype.isDragging=function(){return Blockly.dragMode_==Blockly.DRAG_FREE||Blockly.Flyout.startFlyout_&&Blockly.Flyout.startFlyout_.dragMode_==Blockly.DRAG_FREE||this.dragMode_==Blockly.DRAG_FREE};Blockly.WorkspaceSvg.prototype.onMouseWheel_=function(a){Blockly.terminateDrag_();var b=0<a.deltaY?-1:1,c=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());this.zoom(c.x,c.y,b);a.preventDefault()};
Blockly.WorkspaceSvg.prototype.onMouseDown_=function(a){this.markFocused();Blockly.isTargetInput_(a)?Blockly.Touch.clearTouchIdentifier():(Blockly.terminateDrag_(),Blockly.hideChaff(),a.target&&a.target.nodeName&&("svg"==a.target.nodeName.toLowerCase()||a.target==this.svgBackground_)&&Blockly.selected&&!this.options.readOnly&&Blockly.selected.unselect(),Blockly.isRightButton(a)?(this.showContextMenu_(a),Blockly.Touch.clearTouchIdentifier()):this.scrollbar&&(this.dragMode_=Blockly.DRAG_BEGIN,this.startDragMouseX=
a.clientX,this.startDragMouseY=a.clientY,this.startDragMetrics=this.getMetrics(),this.startScrollX=this.scrollX,this.startScrollY=this.scrollY,"mouseup"in Blockly.Touch.TOUCH_MAP&&(Blockly.Touch.onTouchUpWrapper_=Blockly.Touch.onTouchUpWrapper_||[],Blockly.Touch.onTouchUpWrapper_=Blockly.Touch.onTouchUpWrapper_.concat(Blockly.bindEventWithChecks_(document,"mouseup",null,Blockly.onMouseUp_))),Blockly.onMouseMoveWrapper_=Blockly.onMouseMoveWrapper_||[],Blockly.onMouseMoveWrapper_=Blockly.onMouseMoveWrapper_.concat(Blockly.bindEventWithChecks_(document,
"mousemove",null,Blockly.onMouseMove_))),a.stopPropagation(),a.preventDefault())};Blockly.WorkspaceSvg.prototype.startDrag=function(a,b){var c=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());c.x/=this.scale;c.y/=this.scale;this.dragDeltaXY_=goog.math.Coordinate.difference(b,c)};
Blockly.WorkspaceSvg.prototype.moveDrag=function(a){a=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());a.x/=this.scale;a.y/=this.scale;return goog.math.Coordinate.sum(this.dragDeltaXY_,a)};Blockly.WorkspaceSvg.prototype.isDragging=function(){return Blockly.dragMode_==Blockly.DRAG_FREE||Blockly.Flyout.startFlyout_&&Blockly.Flyout.startFlyout_.dragMode_==Blockly.DRAG_FREE||this.dragMode_==Blockly.DRAG_FREE};
Blockly.WorkspaceSvg.prototype.onMouseWheel_=function(a){Blockly.terminateDrag_();var b=0<a.deltaY?-1:1,c=Blockly.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());this.zoom(c.x,c.y,b);a.preventDefault()};
Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox=function(){var a=this.getTopBlocks(!1);if(!a.length)return{x:0,y:0,width:0,height:0};for(var b=a[0].getBoundingRectangle(),c=1;c<a.length;c++){var d=a[c].getBoundingRectangle();d.topLeft.x<b.topLeft.x&&(b.topLeft.x=d.topLeft.x);d.bottomRight.x>b.bottomRight.x&&(b.bottomRight.x=d.bottomRight.x);d.topLeft.y<b.topLeft.y&&(b.topLeft.y=d.topLeft.y);d.bottomRight.y>b.bottomRight.y&&(b.bottomRight.y=d.bottomRight.y)}return{x:b.topLeft.x,y:b.topLeft.y,width:b.bottomRight.x-
b.topLeft.x,height:b.bottomRight.y-b.topLeft.y}};Blockly.WorkspaceSvg.prototype.cleanUp=function(){Blockly.Events.setGroup(!0);for(var a=this.getTopBlocks(!0),b=0,c=0,d;d=a[c];c++){var e=d.getRelativeToSurfaceXY();d.moveBy(-e.x,b-e.y);d.snapToGrid();b=d.getRelativeToSurfaceXY().y+d.getHeightWidth().height+Blockly.BlockSvg.MIN_BLOCK_Y}Blockly.Events.setGroup(!1);this.resizeContents()};
Blockly.WorkspaceSvg.prototype.showContextMenu_=function(a){function b(a){if(a.isDeletable())l=l.concat(a.getDescendants());else{a=a.getChildren();for(var c=0;c<a.length;c++)b(a[c])}}function c(){Blockly.Events.setGroup(f);var a=l.shift();a&&(a.workspace?(a.dispose(!1,!0),setTimeout(c,10)):c());Blockly.Events.setGroup(!1)}if(!this.options.readOnly&&!this.isFlyout){var d=[],e=this.getTopBlocks(!0),f=Blockly.genUid(),g={};g.text=Blockly.Msg.UNDO;g.enabled=0<this.undoStack_.length;g.callback=this.undo.bind(this,
@@ -1110,8 +1120,8 @@ Blockly.Block.prototype.getConnections_=function(){var a=[];this.outputConnectio
Blockly.Block.prototype.bumpNeighbours_=function(){if(this.workspace&&Blockly.dragMode_==Blockly.DRAG_NONE){var a=this.getRootBlock();if(!a.isInFlyout)for(var b=this.getConnections_(!1),c=0,d;d=b[c];c++){d.isConnected()&&d.isSuperior()&&d.targetBlock().bumpNeighbours_();for(var e=d.neighbours_(Blockly.SNAP_RADIUS),f=0,g;g=e[f];f++)d.isConnected()&&g.isConnected()||g.getSourceBlock().getRootBlock()!=a&&(d.isSuperior()?g.bumpAwayFrom_(d):d.bumpAwayFrom_(g))}}};Blockly.Block.prototype.getParent=function(){return this.parentBlock_};
Blockly.Block.prototype.getInputWithBlock=function(a){for(var b=0,c;c=this.inputList[b];b++)if(c.connection&&c.connection.targetBlock()==a)return c;return null};Blockly.Block.prototype.getSurroundParent=function(){var a=this;do{var b=a,a=a.getParent();if(!a)return null}while(a.getNextBlock()==b);return a};Blockly.Block.prototype.getNextBlock=function(){return this.nextConnection&&this.nextConnection.targetBlock()};
Blockly.Block.prototype.getRootBlock=function(){var a,b=this;do a=b,b=a.parentBlock_;while(b);return a};Blockly.Block.prototype.getChildren=function(){return this.childBlocks_};
Blockly.Block.prototype.setParent=function(a){if(a!=this.parentBlock_){if(this.parentBlock_){for(var b=this.parentBlock_.childBlocks_,c,d=0;c=b[d];d++)if(c==this){b.splice(d,1);break}if(this.previousConnection&&this.previousConnection.isConnected())throw"Still connected to previous block.";if(this.outputConnection&&this.outputConnection.isConnected())throw"Still connected to parent block.";this.parentBlock_=null}else this.workspace.removeTopBlock(this);(this.parentBlock_=a)?a.childBlocks_.push(this):
this.workspace.addTopBlock(this)}};Blockly.Block.prototype.getDescendants=function(){for(var a=[this],b,c=0;b=this.childBlocks_[c];c++)a.push.apply(a,b.getDescendants());return a};Blockly.Block.prototype.isDeletable=function(){return this.deletable_&&!this.isShadow_&&!(this.workspace&&this.workspace.options.readOnly)};Blockly.Block.prototype.setDeletable=function(a){this.deletable_=a};Blockly.Block.prototype.isMovable=function(){return this.movable_&&!this.isShadow_&&!(this.workspace&&this.workspace.options.readOnly)};
Blockly.Block.prototype.setParent=function(a){if(a!=this.parentBlock_){if(this.parentBlock_){goog.array.remove(this.parentBlock_.childBlocks_,this);if(this.previousConnection&&this.previousConnection.isConnected())throw"Still connected to previous block.";if(this.outputConnection&&this.outputConnection.isConnected())throw"Still connected to parent block.";this.parentBlock_=null}else this.workspace.removeTopBlock(this);(this.parentBlock_=a)?a.childBlocks_.push(this):this.workspace.addTopBlock(this)}};
Blockly.Block.prototype.getDescendants=function(){for(var a=[this],b,c=0;b=this.childBlocks_[c];c++)a.push.apply(a,b.getDescendants());return a};Blockly.Block.prototype.isDeletable=function(){return this.deletable_&&!this.isShadow_&&!(this.workspace&&this.workspace.options.readOnly)};Blockly.Block.prototype.setDeletable=function(a){this.deletable_=a};Blockly.Block.prototype.isMovable=function(){return this.movable_&&!this.isShadow_&&!(this.workspace&&this.workspace.options.readOnly)};
Blockly.Block.prototype.setMovable=function(a){this.movable_=a};Blockly.Block.prototype.isShadow=function(){return this.isShadow_};Blockly.Block.prototype.setShadow=function(a){this.isShadow_=a};Blockly.Block.prototype.isEditable=function(){return this.editable_&&!(this.workspace&&this.workspace.options.readOnly)};Blockly.Block.prototype.setEditable=function(a){this.editable_=a;a=0;for(var b;b=this.inputList[a];a++)for(var c=0,d;d=b.fieldRow[c];c++)d.updateEditable()};
Blockly.Block.prototype.setConnectionsHidden=function(a){if(!a&&this.isCollapsed()){if(this.outputConnection&&this.outputConnection.setHidden(a),this.previousConnection&&this.previousConnection.setHidden(a),this.nextConnection){this.nextConnection.setHidden(a);var b=this.nextConnection.targetBlock();b&&b.setConnectionsHidden(a)}}else for(var c=this.getConnections_(!0),d=0;b=c[d];d++)b.setHidden(a),b.isSuperior()&&(b=b.targetBlock())&&b.setConnectionsHidden(a)};
Blockly.Block.prototype.setHelpUrl=function(a){this.helpUrl=a};Blockly.Block.prototype.setTooltip=function(a){this.tooltip=a};Blockly.Block.prototype.getColour=function(){return this.colour_};Blockly.Block.prototype.setColour=function(a){var b=parseFloat(a);if(isNaN(b))if(goog.isString(a)&&a.match(/^#[0-9a-fA-F]{6}$/))this.colour_=a;else throw"Invalid colour: "+a;else this.colour_=Blockly.hueToRgb(b)};
@@ -1129,9 +1139,9 @@ Blockly.Block.prototype.toString=function(a,b){var c=[],d=b||"?";if(this.collaps
Blockly.Block.prototype.appendValueInput=function(a){return this.appendInput_(Blockly.INPUT_VALUE,a)};Blockly.Block.prototype.appendStatementInput=function(a){return this.appendInput_(Blockly.NEXT_STATEMENT,a)};Blockly.Block.prototype.appendDummyInput=function(a){return this.appendInput_(Blockly.DUMMY_INPUT,a||"")};
Blockly.Block.prototype.jsonInit=function(a){goog.asserts.assert(void 0==a.output||void 0==a.previousStatement,"Must not have both an output and a previousStatement.");void 0!==a.colour&&this.setColour(a.colour);for(var b=0;void 0!==a["message"+b];)this.interpolate_(a["message"+b],a["args"+b]||[],a["lastDummyAlign"+b]),b++;void 0!==a.inputsInline&&this.setInputsInline(a.inputsInline);void 0!==a.output&&this.setOutput(!0,a.output);void 0!==a.previousStatement&&this.setPreviousStatement(!0,a.previousStatement);
void 0!==a.nextStatement&&this.setNextStatement(!0,a.nextStatement);void 0!==a.tooltip&&this.setTooltip(a.tooltip);void 0!==a.helpUrl&&this.setHelpUrl(a.helpUrl)};
Blockly.Block.prototype.interpolate_=function(a,b,c){var d=Blockly.utils.tokenizeInterpolation(a),e=[],f=0;a=[];for(var g=0;g<d.length;g++){var h=d[g];"number"==typeof h?(goog.asserts.assert(0<h&&h<=b.length,'Message index "%s" out of range.',h),goog.asserts.assert(!e[h],'Message index "%s" duplicated.',h),e[h]=!0,f++,a.push(b[h-1])):(h=h.trim())&&a.push(h)}goog.asserts.assert(f==b.length,"Message does not reference all %s arg(s).",b.length);!a.length||"string"!=typeof a[a.length-1]&&0!=a[a.length-
1].type.indexOf("field_")||(g={type:"input_dummy"},c&&(g.align=c),a.push(g));c={LEFT:Blockly.ALIGN_LEFT,RIGHT:Blockly.ALIGN_RIGHT,CENTRE:Blockly.ALIGN_CENTRE};b=[];for(g=0;g<a.length;g++)if(e=a[g],"string"==typeof e)b.push([e,void 0]);else{d=f=null;do if(h=!1,"string"==typeof e)f=new Blockly.FieldLabel(e);else switch(e.type){case "input_value":d=this.appendValueInput(e.name);break;case "input_statement":d=this.appendStatementInput(e.name);break;case "input_dummy":d=this.appendDummyInput(e.name);break;
case "field_label":f=new Blockly.FieldLabel(e.text,e["class"]);break;case "field_input":f=new Blockly.FieldTextInput(e.text);"boolean"==typeof e.spellcheck&&f.setSpellcheck(e.spellcheck);break;case "field_angle":f=new Blockly.FieldAngle(e.angle);break;case "field_checkbox":f=new Blockly.FieldCheckbox(e.checked?"TRUE":"FALSE");break;case "field_colour":f=new Blockly.FieldColour(e.colour);break;case "field_variable":f=new Blockly.FieldVariable(e.variable);break;case "field_dropdown":f=new Blockly.FieldDropdown(e.options);
Blockly.Block.prototype.interpolate_=function(a,b,c){var d=Blockly.utils.tokenizeInterpolation(a),e=[],f=0;a=[];for(var g=0;g<d.length;g++){var h=d[g];"number"==typeof h?(goog.asserts.assert(0<h&&h<=b.length,'Message index "%s" out of range.',h),goog.asserts.assert(!e[h],'Message index "%s" duplicated.',h),e[h]=!0,f++,a.push(b[h-1])):(h=h.trim())&&a.push(h)}goog.asserts.assert(f==b.length,"Message does not reference all %s arg(s).",b.length);a.length&&("string"==typeof a[a.length-1]||goog.string.startsWith(a[a.length-
1].type,"field_"))&&(g={type:"input_dummy"},c&&(g.align=c),a.push(g));c={LEFT:Blockly.ALIGN_LEFT,RIGHT:Blockly.ALIGN_RIGHT,CENTRE:Blockly.ALIGN_CENTRE};b=[];for(g=0;g<a.length;g++)if(e=a[g],"string"==typeof e)b.push([e,void 0]);else{d=f=null;do if(h=!1,"string"==typeof e)f=new Blockly.FieldLabel(e);else switch(e.type){case "input_value":d=this.appendValueInput(e.name);break;case "input_statement":d=this.appendStatementInput(e.name);break;case "input_dummy":d=this.appendDummyInput(e.name);break;case "field_label":f=
new Blockly.FieldLabel(e.text,e["class"]);break;case "field_input":f=new Blockly.FieldTextInput(e.text);"boolean"==typeof e.spellcheck&&f.setSpellcheck(e.spellcheck);break;case "field_angle":f=new Blockly.FieldAngle(e.angle);break;case "field_checkbox":f=new Blockly.FieldCheckbox(e.checked?"TRUE":"FALSE");break;case "field_colour":f=new Blockly.FieldColour(e.colour);break;case "field_variable":f=new Blockly.FieldVariable(e.variable);break;case "field_dropdown":f=new Blockly.FieldDropdown(e.options);
break;case "field_image":f=new Blockly.FieldImage(e.src,e.width,e.height,e.alt);break;case "field_number":f=new Blockly.FieldNumber(e.value,e.min,e.max,e.precision);break;case "field_date":if(Blockly.FieldDate){f=new Blockly.FieldDate(e.date);break}default:e.alt&&(e=e.alt,h=!0)}while(h);if(f)b.push([f,e.name]);else if(d){e.check&&d.setCheck(e.check);e.align&&d.setAlign(c[e.align]);for(e=0;e<b.length;e++)d.appendField(b[e][0],b[e][1]);b.length=0}}};
Blockly.Block.prototype.appendInput_=function(a,b){var c=null;if(a==Blockly.INPUT_VALUE||a==Blockly.NEXT_STATEMENT)c=this.makeConnection_(a);c=new Blockly.Input(a,b,this,c);this.inputList.push(c);return c};
Blockly.Block.prototype.moveInputBefore=function(a,b){if(a!=b){for(var c=-1,d=b?-1:this.inputList.length,e=0,f;f=this.inputList[e];e++)if(f.name==a){if(c=e,-1!=d)break}else if(b&&f.name==b&&(d=e,-1!=c))break;goog.asserts.assert(-1!=c,'Named input "%s" not found.',a);goog.asserts.assert(-1!=d,'Reference input "%s" not found.',b);this.moveNumberedInputBefore(c,d)}};
@@ -1140,7 +1150,7 @@ Blockly.Block.prototype.removeInput=function(a,b){for(var c=0,d;d=this.inputList
Blockly.Block.prototype.getInputTargetBlock=function(a){return(a=this.getInput(a))&&a.connection&&a.connection.targetBlock()};Blockly.Block.prototype.getCommentText=function(){return this.comment||""};Blockly.Block.prototype.setCommentText=function(a){this.comment!=a&&(Blockly.Events.fire(new Blockly.Events.Change(this,"comment",null,this.comment,a||"")),this.comment=a)};Blockly.Block.prototype.setWarningText=function(a){};Blockly.Block.prototype.setMutator=function(a){};
Blockly.Block.prototype.getRelativeToSurfaceXY=function(){return this.xy_};Blockly.Block.prototype.moveBy=function(a,b){goog.asserts.assert(!this.parentBlock_,"Block has parent.");var c=new Blockly.Events.Move(this);this.xy_.translate(a,b);c.recordNew();Blockly.Events.fire(c)};Blockly.Block.prototype.makeConnection_=function(a){return new Blockly.Connection(this,a)};Blockly.ContextMenu={};Blockly.ContextMenu.currentBlock=null;
Blockly.ContextMenu.show=function(a,b,c){Blockly.WidgetDiv.show(Blockly.ContextMenu,c,null);if(b.length){var d=new goog.ui.Menu;d.setRightToLeft(c);for(var e=0,f;f=b[e];e++){var g=new goog.ui.MenuItem(f.text);g.setRightToLeft(c);d.addChild(g,!0);g.setEnabled(f.enabled);f.enabled&&goog.events.listen(g,goog.ui.Component.EventType.ACTION,f.callback)}goog.events.listen(d,goog.ui.Component.EventType.ACTION,Blockly.ContextMenu.hide);b=goog.dom.getViewportSize();e=goog.style.getViewportPageOffset(document);
d.render(Blockly.WidgetDiv.DIV);var h=d.getElement();Blockly.addClass_(h,"blocklyContextMenu");Blockly.bindEvent_(h,"contextmenu",null,Blockly.noEvent);f=goog.style.getSize(h);var g=a.clientX+e.x,k=a.clientY+e.y;a.clientY+f.height>=b.height&&(k-=f.height);c?f.width>=a.clientX&&(g+=f.width):a.clientX+f.width>=b.width&&(g-=f.width);Blockly.WidgetDiv.position(g,k,b,e,c);d.setAllowAutoFocus(!0);setTimeout(function(){h.focus()},1);Blockly.ContextMenu.currentBlock=null}else Blockly.ContextMenu.hide()};
d.render(Blockly.WidgetDiv.DIV);var h=d.getElement();Blockly.addClass_(h,"blocklyContextMenu");Blockly.bindEventWithChecks_(h,"contextmenu",null,Blockly.noEvent);f=goog.style.getSize(h);var g=a.clientX+e.x,k=a.clientY+e.y;a.clientY+f.height>=b.height&&(k-=f.height);c?f.width>=a.clientX&&(g+=f.width):a.clientX+f.width>=b.width&&(g-=f.width);Blockly.WidgetDiv.position(g,k,b,e,c);d.setAllowAutoFocus(!0);setTimeout(function(){h.focus()},1);Blockly.ContextMenu.currentBlock=null}else Blockly.ContextMenu.hide()};
Blockly.ContextMenu.hide=function(){Blockly.WidgetDiv.hideIfOwner(Blockly.ContextMenu);Blockly.ContextMenu.currentBlock=null};
Blockly.ContextMenu.callbackFactory=function(a,b){return function(){Blockly.Events.disable();try{var c=Blockly.Xml.domToBlock(b,a.workspace),d=a.getRelativeToSurfaceXY();d.x=a.RTL?d.x-Blockly.SNAP_RADIUS:d.x+Blockly.SNAP_RADIUS;d.y+=2*Blockly.SNAP_RADIUS;c.moveBy(d.x,d.y)}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!c.isShadow()&&Blockly.Events.fire(new Blockly.Events.Create(c));c.select()}};Blockly.RenderedConnection=function(a,b){Blockly.RenderedConnection.superClass_.constructor.call(this,a,b);this.offsetInBlock_=new goog.math.Coordinate(0,0)};goog.inherits(Blockly.RenderedConnection,Blockly.Connection);Blockly.RenderedConnection.prototype.distanceFrom=function(a){var b=this.x_-a.x_;a=this.y_-a.y_;return Math.sqrt(b*b+a*a)};
Blockly.RenderedConnection.prototype.bumpAwayFrom_=function(a){if(Blockly.dragMode_==Blockly.DRAG_NONE){var b=this.sourceBlock_.getRootBlock();if(!b.isInFlyout){var c=!1;if(!b.isMovable()){b=a.getSourceBlock().getRootBlock();if(!b.isMovable())return;a=this;c=!0}var d=Blockly.selected==b;d||b.addSelect();var e=a.x_+Blockly.SNAP_RADIUS-this.x_;a=a.y_+Blockly.SNAP_RADIUS-this.y_;c&&(a=-a);b.RTL&&(e=-e);b.moveBy(e,a);d||b.removeSelect()}}};
@@ -1155,8 +1165,8 @@ Blockly.RenderedConnection.prototype.disconnectInternal_=function(a,b){Blockly.R
Blockly.RenderedConnection.prototype.respawnShadow_=function(){var a=this.getSourceBlock(),b=this.getShadowDom();if(a.workspace&&b&&Blockly.Events.recordUndo){Blockly.RenderedConnection.superClass_.respawnShadow_.call(this);b=this.targetBlock();if(!b)throw"Couldn't respawn the shadow block that should exist here.";b.initSvg();b.render(!1);a.rendered&&a.render()}};Blockly.RenderedConnection.prototype.neighbours_=function(a){return this.dbOpposite_.getNeighbours(this,a)};
Blockly.RenderedConnection.prototype.connect_=function(a){Blockly.RenderedConnection.superClass_.connect_.call(this,a);var b=this.getSourceBlock();a=a.getSourceBlock();b.rendered&&b.updateDisabled();a.rendered&&a.updateDisabled();b.rendered&&a.rendered&&(this.type==Blockly.NEXT_STATEMENT||this.type==Blockly.PREVIOUS_STATEMENT?a.render():b.render())};Blockly.BlockSvg=function(a,b,c){this.svgGroup_=Blockly.createSvgElement("g",{},null);this.svgPathDark_=Blockly.createSvgElement("path",{"class":"blocklyPathDark",transform:"translate(1,1)"},this.svgGroup_);this.svgPath_=Blockly.createSvgElement("path",{"class":"blocklyPath"},this.svgGroup_);this.svgPathLight_=Blockly.createSvgElement("path",{"class":"blocklyPathLight"},this.svgGroup_);this.svgPath_.tooltip=this;this.rendered=!1;Blockly.Tooltip.bindMouseEvents(this.svgPath_);Blockly.BlockSvg.superClass_.constructor.call(this,
a,b,c)};goog.inherits(Blockly.BlockSvg,Blockly.Block);Blockly.BlockSvg.prototype.height=0;Blockly.BlockSvg.prototype.width=0;Blockly.BlockSvg.prototype.dragStartXY_=null;Blockly.BlockSvg.INLINE=-1;
Blockly.BlockSvg.prototype.initSvg=function(){goog.asserts.assert(this.workspace.rendered,"Workspace is headless.");for(var a=0,b;b=this.inputList[a];a++)b.init();b=this.getIcons();for(a=0;a<b.length;a++)b[a].createIcon();this.updateColour();this.updateMovable();if(!this.workspace.options.readOnly&&!this.eventsInit_){Blockly.bindEvent_(this.getSvgRoot(),"mousedown",this,this.onMouseDown_);var c=this;Blockly.bindEvent_(this.getSvgRoot(),"touchstart",null,function(a){Blockly.longStart_(a,c)})}this.eventsInit_=
!0;this.getSvgRoot().parentNode||this.workspace.getCanvas().appendChild(this.getSvgRoot())};
Blockly.BlockSvg.prototype.initSvg=function(){goog.asserts.assert(this.workspace.rendered,"Workspace is headless.");for(var a=0,b;b=this.inputList[a];a++)b.init();b=this.getIcons();for(a=0;a<b.length;a++)b[a].createIcon();this.updateColour();this.updateMovable();if(!this.workspace.options.readOnly&&!this.eventsInit_){Blockly.bindEventWithChecks_(this.getSvgRoot(),"mousedown",this,this.onMouseDown_);var c=this;Blockly.bindEventWithChecks_(this.getSvgRoot(),"touchstart",null,function(a){Blockly.longStart_(a,
c)})}this.eventsInit_=!0;this.getSvgRoot().parentNode||this.workspace.getCanvas().appendChild(this.getSvgRoot())};
Blockly.BlockSvg.prototype.select=function(){if(this.isShadow()&&this.getParent())this.getParent().select();else if(Blockly.selected!=this){var a=null;if(Blockly.selected){a=Blockly.selected.id;Blockly.Events.disable();try{Blockly.selected.unselect()}finally{Blockly.Events.enable()}}a=new Blockly.Events.Ui(null,"selected",a,this.id);a.workspaceId=this.workspace.id;Blockly.Events.fire(a);Blockly.selected=this;this.addSelect()}};
Blockly.BlockSvg.prototype.unselect=function(){if(Blockly.selected==this){var a=new Blockly.Events.Ui(null,"selected",this.id,null);a.workspaceId=this.workspace.id;Blockly.Events.fire(a);Blockly.selected=null;this.removeSelect()}};Blockly.BlockSvg.prototype.mutator=null;Blockly.BlockSvg.prototype.comment=null;Blockly.BlockSvg.prototype.warning=null;
Blockly.BlockSvg.prototype.getIcons=function(){var a=[];this.mutator&&a.push(this.mutator);this.comment&&a.push(this.comment);this.warning&&a.push(this.warning);return a};Blockly.BlockSvg.onMouseUpWrapper_=null;Blockly.BlockSvg.onMouseMoveWrapper_=null;
@@ -1171,11 +1181,11 @@ Blockly.BlockSvg.prototype.getHeightWidth=function(){var a=this.height,b=this.wi
Blockly.BlockSvg.prototype.getBoundingRectangle=function(){var a=this.getRelativeToSurfaceXY(this),b=this.outputConnection?Blockly.BlockSvg.TAB_WIDTH:0,c=this.getHeightWidth(),d;this.RTL?(d=new goog.math.Coordinate(a.x-(c.width-b),a.y),a=new goog.math.Coordinate(a.x+b,a.y+c.height)):(d=new goog.math.Coordinate(a.x-b,a.y),a=new goog.math.Coordinate(a.x+c.width-b,a.y+c.height));return{topLeft:d,bottomRight:a}};
Blockly.BlockSvg.prototype.setCollapsed=function(a){if(this.collapsed_!=a){for(var b=[],c=0,d;d=this.inputList[c];c++)b.push.apply(b,d.setVisible(!a));if(a){d=this.getIcons();for(c=0;c<d.length;c++)d[c].setVisible(!1);c=this.toString(Blockly.COLLAPSE_CHARS);this.appendDummyInput("_TEMP_COLLAPSED_INPUT").appendField(c).init()}else this.removeInput("_TEMP_COLLAPSED_INPUT"),this.setWarningText(null);Blockly.BlockSvg.superClass_.setCollapsed.call(this,a);b.length||(b[0]=this);if(this.rendered)for(c=0;a=
b[c];c++)a.render()}};Blockly.BlockSvg.prototype.tab=function(a,b){for(var c=[],d=0,e;e=this.inputList[d];d++){for(var f=0,g;g=e.fieldRow[f];f++)g instanceof Blockly.FieldTextInput&&c.push(g);e.connection&&(e=e.connection.targetBlock())&&c.push(e)}d=c.indexOf(a);-1==d&&(d=b?-1:c.length);(c=c[b?d+1:d-1])?c instanceof Blockly.Field?c.showEditor_():c.tab(null,b):(c=this.getParent())&&c.tab(this,b)};
Blockly.BlockSvg.prototype.onMouseDown_=function(a){if(!this.workspace.options.readOnly&&!this.isInFlyout){this.isInMutator&&this.workspace.resize();this.workspace.updateScreenCalculationsIfScrolled();this.workspace.markFocused();Blockly.terminateDrag_();this.select();Blockly.hideChaff();if(Blockly.isRightButton(a))this.showContextMenu_(a);else if(this.isMovable()){Blockly.Events.getGroup()||Blockly.Events.setGroup(!0);Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);this.dragStartXY_=this.getRelativeToSurfaceXY();
this.workspace.startDrag(a,this.dragStartXY_);Blockly.dragMode_=Blockly.DRAG_STICKY;Blockly.BlockSvg.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,this.onMouseUp_);Blockly.BlockSvg.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.onMouseMove_);this.draggedBubbles_=[];for(var b=this.getDescendants(),c=0,d;d=b[c];c++){d=d.getIcons();for(var e=0;e<d.length;e++){var f=d[e].getIconLocation();f.bubble=d[e];this.draggedBubbles_.push(f)}}}else return;a.stopPropagation();
a.preventDefault()}};
Blockly.BlockSvg.prototype.onMouseUp_=function(a){Blockly.dragMode_==Blockly.DRAG_FREE||Blockly.WidgetDiv.isVisible()||Blockly.Events.fire(new Blockly.Events.Ui(this,"click",void 0,void 0));Blockly.terminateDrag_();Blockly.selected&&Blockly.highlightedConnection_?(Blockly.localConnection_.connect(Blockly.highlightedConnection_),this.rendered&&(Blockly.localConnection_.isSuperior()?Blockly.highlightedConnection_:Blockly.localConnection_).getSourceBlock().connectionUiEffect(),this.workspace.trashcan&&this.workspace.trashcan.close()):
!this.getParent()&&Blockly.selected.isDeletable()&&this.workspace.isDeleteArea(a)&&((a=this.workspace.trashcan)&&goog.Timer.callOnce(a.close,100,a),Blockly.selected.dispose(!1,!0));Blockly.highlightedConnection_&&(Blockly.highlightedConnection_.unhighlight(),Blockly.highlightedConnection_=null);Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);Blockly.WidgetDiv.isVisible()||Blockly.Events.setGroup(!1)};
Blockly.BlockSvg.prototype.onMouseDown_=function(a){if(!this.workspace.options.readOnly)if(this.isInFlyout)"touchstart"==a.type&&Blockly.isRightButton(a)&&(Blockly.Flyout.blockRightClick_(a,this),a.stopPropagation(),a.preventDefault());else{this.isInMutator&&this.workspace.resize();this.workspace.updateScreenCalculationsIfScrolled();this.workspace.markFocused();Blockly.terminateDrag_();this.select();Blockly.hideChaff();if(Blockly.isRightButton(a))this.showContextMenu_(a),Blockly.Touch.clearTouchIdentifier();
else if(this.isMovable()){Blockly.Events.getGroup()||Blockly.Events.setGroup(!0);Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);this.dragStartXY_=this.getRelativeToSurfaceXY();this.workspace.startDrag(a,this.dragStartXY_);Blockly.dragMode_=Blockly.DRAG_STICKY;Blockly.BlockSvg.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,this.onMouseUp_);Blockly.BlockSvg.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",this,this.onMouseMove_);this.draggedBubbles_=[];
for(var b=this.getDescendants(),c=0,d;d=b[c];c++){d=d.getIcons();for(var e=0;e<d.length;e++){var f=d[e].getIconLocation();f.bubble=d[e];this.draggedBubbles_.push(f)}}}else return;a.stopPropagation();a.preventDefault()}};
Blockly.BlockSvg.prototype.onMouseUp_=function(a){Blockly.Touch.clearTouchIdentifier();Blockly.dragMode_==Blockly.DRAG_FREE||Blockly.WidgetDiv.isVisible()||Blockly.Events.fire(new Blockly.Events.Ui(this,"click",void 0,void 0));Blockly.terminateDrag_();Blockly.selected&&Blockly.highlightedConnection_?(Blockly.localConnection_.connect(Blockly.highlightedConnection_),this.rendered&&(Blockly.localConnection_.isSuperior()?Blockly.highlightedConnection_:Blockly.localConnection_).getSourceBlock().connectionUiEffect(),
this.workspace.trashcan&&this.workspace.trashcan.close()):!this.getParent()&&Blockly.selected.isDeletable()&&this.workspace.isDeleteArea(a)&&((a=this.workspace.trashcan)&&goog.Timer.callOnce(a.close,100,a),Blockly.selected.dispose(!1,!0));Blockly.highlightedConnection_&&(Blockly.highlightedConnection_.unhighlight(),Blockly.highlightedConnection_=null);Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);Blockly.WidgetDiv.isVisible()||Blockly.Events.setGroup(!1)};
Blockly.BlockSvg.prototype.showHelp_=function(){var a=goog.isFunction(this.helpUrl)?this.helpUrl():this.helpUrl;a&&window.open(a)};
Blockly.BlockSvg.prototype.showContextMenu_=function(a){if(!this.workspace.options.readOnly&&this.contextMenu){var b=this,c=[];if(this.isDeletable()&&this.isMovable()&&!b.isInFlyout){var d={text:Blockly.Msg.DUPLICATE_BLOCK,enabled:!0,callback:function(){Blockly.duplicate_(b)}};this.getDescendants().length>this.workspace.remainingCapacity()&&(d.enabled=!1);c.push(d);this.isEditable()&&!this.collapsed_&&this.workspace.options.comments&&(d={enabled:!goog.userAgent.IE},this.comment?(d.text=Blockly.Msg.REMOVE_COMMENT,
d.callback=function(){b.setCommentText(null)}):(d.text=Blockly.Msg.ADD_COMMENT,d.callback=function(){b.setCommentText("")}),c.push(d));if(!this.collapsed_)for(d=1;d<this.inputList.length;d++)if(this.inputList[d-1].type!=Blockly.NEXT_STATEMENT&&this.inputList[d].type!=Blockly.NEXT_STATEMENT){var d={enabled:!0},e=this.getInputsInline();d.text=e?Blockly.Msg.EXTERNAL_INPUTS:Blockly.Msg.INLINE_INPUTS;d.callback=function(){b.setInputsInline(!e)};c.push(d);break}this.workspace.options.collapse&&(this.collapsed_?
@@ -1268,8 +1278,8 @@ Blockly.Events.Ui.prototype.toJson=function(){var a=Blockly.Events.Ui.superClass
Blockly.Events.disableOrphans=function(a){if(a.type==Blockly.Events.MOVE||a.type==Blockly.Events.CREATE){Blockly.Events.disable();if(a=Blockly.Workspace.getById(a.workspaceId).getBlockById(a.blockId))if(a.getParent()&&!a.getParent().disabled){a=a.getDescendants();for(var b=0,c;c=a[b];b++)c.setDisabled(!1)}else if((a.outputConnection||a.previousConnection)&&Blockly.dragMode_==Blockly.DRAG_NONE){do a.setDisabled(!0),a=a.getNextBlock();while(a)}Blockly.Events.enable()}};Blockly.Msg={};goog.getMsgOrig=goog.getMsg;goog.getMsg=function(a,b){var c=goog.getMsg.blocklyMsgMap[a];c&&(a=Blockly.Msg[c]);return goog.getMsgOrig(a,b)};goog.getMsg.blocklyMsgMap={Today:"TODAY"};Blockly.FieldTextInput=function(a,b){Blockly.FieldTextInput.superClass_.constructor.call(this,a,b)};goog.inherits(Blockly.FieldTextInput,Blockly.Field);Blockly.FieldTextInput.FONTSIZE=11;Blockly.FieldTextInput.prototype.CURSOR="text";Blockly.FieldTextInput.prototype.spellcheck_=!0;Blockly.FieldTextInput.prototype.dispose=function(){Blockly.WidgetDiv.hideIfOwner(this);Blockly.FieldTextInput.superClass_.dispose.call(this)};
Blockly.FieldTextInput.prototype.setValue=function(a){if(null!==a){if(this.sourceBlock_){var b=this.callValidator(a);null!==b&&(a=b)}Blockly.Field.prototype.setValue.call(this,a)}};Blockly.FieldTextInput.prototype.setSpellcheck=function(a){this.spellcheck_=a};
Blockly.FieldTextInput.prototype.showEditor_=function(a){this.workspace_=this.sourceBlock_.workspace;a=a||!1;if(!a&&(goog.userAgent.MOBILE||goog.userAgent.ANDROID||goog.userAgent.IPAD))a=window.prompt(Blockly.Msg.CHANGE_VALUE_TITLE,this.text_),this.sourceBlock_&&(a=this.callValidator(a)),this.setValue(a);else{Blockly.WidgetDiv.show(this,this.sourceBlock_.RTL,this.widgetDispose_());var b=Blockly.WidgetDiv.DIV,c=goog.dom.createDom("INPUT","blocklyHtmlInput");c.setAttribute("spellcheck",this.spellcheck_);
var d=Blockly.FieldTextInput.FONTSIZE*this.workspace_.scale+"pt";b.style.fontSize=d;c.style.fontSize=d;Blockly.FieldTextInput.htmlInput_=c;b.appendChild(c);c.value=c.defaultValue=this.text_;c.oldValue_=null;this.validate_();this.resizeEditor_();a||(c.focus(),c.select());c.onKeyDownWrapper_=Blockly.bindEvent_(c,"keydown",this,this.onHtmlInputKeyDown_);c.onKeyUpWrapper_=Blockly.bindEvent_(c,"keyup",this,this.onHtmlInputChange_);c.onKeyPressWrapper_=Blockly.bindEvent_(c,"keypress",this,this.onHtmlInputChange_);
c.onWorkspaceChangeWrapper_=this.resizeEditor_.bind(this);this.workspace_.addChangeListener(c.onWorkspaceChangeWrapper_)}};Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_=function(a){var b=Blockly.FieldTextInput.htmlInput_;13==a.keyCode?Blockly.WidgetDiv.hide():27==a.keyCode?(b.value=b.defaultValue,Blockly.WidgetDiv.hide()):9==a.keyCode&&(Blockly.WidgetDiv.hide(),this.sourceBlock_.tab(this,!a.shiftKey),a.preventDefault())};
var d=Blockly.FieldTextInput.FONTSIZE*this.workspace_.scale+"pt";b.style.fontSize=d;c.style.fontSize=d;Blockly.FieldTextInput.htmlInput_=c;b.appendChild(c);c.value=c.defaultValue=this.text_;c.oldValue_=null;this.validate_();this.resizeEditor_();a||(c.focus(),c.select());c.onKeyDownWrapper_=Blockly.bindEventWithChecks_(c,"keydown",this,this.onHtmlInputKeyDown_);c.onKeyUpWrapper_=Blockly.bindEventWithChecks_(c,"keyup",this,this.onHtmlInputChange_);c.onKeyPressWrapper_=Blockly.bindEventWithChecks_(c,
"keypress",this,this.onHtmlInputChange_);c.onWorkspaceChangeWrapper_=this.resizeEditor_.bind(this);this.workspace_.addChangeListener(c.onWorkspaceChangeWrapper_)}};Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_=function(a){var b=Blockly.FieldTextInput.htmlInput_;13==a.keyCode?Blockly.WidgetDiv.hide():27==a.keyCode?(b.value=b.defaultValue,Blockly.WidgetDiv.hide()):9==a.keyCode&&(Blockly.WidgetDiv.hide(),this.sourceBlock_.tab(this,!a.shiftKey),a.preventDefault())};
Blockly.FieldTextInput.prototype.onHtmlInputChange_=function(a){a=Blockly.FieldTextInput.htmlInput_;var b=a.value;b!==a.oldValue_?(a.oldValue_=b,this.setValue(b),this.validate_()):goog.userAgent.WEBKIT&&this.sourceBlock_.render();this.resizeEditor_();Blockly.svgResize(this.sourceBlock_.workspace)};
Blockly.FieldTextInput.prototype.validate_=function(){var a=!0;goog.asserts.assertObject(Blockly.FieldTextInput.htmlInput_);var b=Blockly.FieldTextInput.htmlInput_;this.sourceBlock_&&(a=this.callValidator(b.value));null===a?Blockly.addClass_(b,"blocklyInvalidInput"):Blockly.removeClass_(b,"blocklyInvalidInput")};
Blockly.FieldTextInput.prototype.resizeEditor_=function(){var a=Blockly.WidgetDiv.DIV,b=this.fieldGroup_.getBBox();a.style.width=b.width*this.workspace_.scale+"px";a.style.height=b.height*this.workspace_.scale+"px";b=this.getAbsoluteXY_();if(this.sourceBlock_.RTL){var c=this.getScaledBBox_();b.x+=c.width;b.x-=a.offsetWidth}b.y+=1;goog.userAgent.GECKO&&Blockly.WidgetDiv.DIV.style.top&&(--b.x,--b.y);goog.userAgent.WEBKIT&&(b.y-=3);a.style.left=b.x+"px";a.style.top=b.y+"px"};
@@ -1286,7 +1296,7 @@ Blockly.FieldAngle.prototype.updateGraph_=function(){if(this.gauge_){var a=Numbe
Blockly.FieldAngle.RADIUS;b=Math.abs(Math.floor((b-e)/Math.PI)%2);Blockly.FieldAngle.CLOCKWISE&&(b=1-b);a.push(" l ",f,",",g," A ",Blockly.FieldAngle.RADIUS,",",Blockly.FieldAngle.RADIUS," 0 ",b," ",Number(Blockly.FieldAngle.CLOCKWISE)," ",c,",",d," z")}this.gauge_.setAttribute("d",a.join(""));this.line_.setAttribute("x2",c);this.line_.setAttribute("y2",d)}};
Blockly.FieldAngle.prototype.classValidator=function(a){if(null===a)return null;a=parseFloat(a||0);if(isNaN(a))return null;a%=360;0>a&&(a+=360);a>Blockly.FieldAngle.WRAP&&(a-=360);return String(a)};Blockly.FieldCheckbox=function(a,b){Blockly.FieldCheckbox.superClass_.constructor.call(this,"",b);this.setValue(a)};goog.inherits(Blockly.FieldCheckbox,Blockly.Field);Blockly.FieldCheckbox.CHECK_CHAR="\u2713";Blockly.FieldCheckbox.prototype.CURSOR="default";
Blockly.FieldCheckbox.prototype.init=function(){if(!this.fieldGroup_){Blockly.FieldCheckbox.superClass_.init.call(this);this.checkElement_=Blockly.createSvgElement("text",{"class":"blocklyText blocklyCheckbox",x:-3,y:14},this.fieldGroup_);var a=document.createTextNode(Blockly.FieldCheckbox.CHECK_CHAR);this.checkElement_.appendChild(a);this.checkElement_.style.display=this.state_?"block":"none"}};Blockly.FieldCheckbox.prototype.getValue=function(){return String(this.state_).toUpperCase()};
Blockly.FieldCheckbox.prototype.setValue=function(a){a="TRUE"==a;this.state_!==a&&(this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,this.state_,a)),this.state_=a,this.checkElement_&&(this.checkElement_.style.display=a?"block":"none"))};Blockly.FieldCheckbox.prototype.showEditor_=function(){var a=!this.state_;this.sourceBlock_&&(a=this.callValidator(a));null!==a&&this.setValue(String(a).toUpperCase())};Blockly.FieldColour=function(a,b){Blockly.FieldColour.superClass_.constructor.call(this,a,b);this.setText(Blockly.Field.NBSP+Blockly.Field.NBSP+Blockly.Field.NBSP)};goog.inherits(Blockly.FieldColour,Blockly.Field);Blockly.FieldColour.prototype.colours_=null;Blockly.FieldColour.prototype.columns_=0;Blockly.FieldColour.prototype.init=function(){Blockly.FieldColour.superClass_.init.call(this);this.borderRect_.style.fillOpacity=1;this.setValue(this.getValue())};Blockly.FieldColour.prototype.CURSOR="default";
Blockly.FieldCheckbox.prototype.setValue=function(a){a="TRUE"==a.toUpperCase();this.state_!==a&&(this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,this.state_,a)),this.state_=a,this.checkElement_&&(this.checkElement_.style.display=a?"block":"none"))};Blockly.FieldCheckbox.prototype.showEditor_=function(){var a=!this.state_;this.sourceBlock_&&(a=this.callValidator(a));null!==a&&this.setValue(String(a).toUpperCase())};Blockly.FieldColour=function(a,b){Blockly.FieldColour.superClass_.constructor.call(this,a,b);this.setText(Blockly.Field.NBSP+Blockly.Field.NBSP+Blockly.Field.NBSP)};goog.inherits(Blockly.FieldColour,Blockly.Field);Blockly.FieldColour.prototype.colours_=null;Blockly.FieldColour.prototype.columns_=0;Blockly.FieldColour.prototype.init=function(){Blockly.FieldColour.superClass_.init.call(this);this.borderRect_.style.fillOpacity=1;this.setValue(this.getValue())};Blockly.FieldColour.prototype.CURSOR="default";
Blockly.FieldColour.prototype.dispose=function(){Blockly.WidgetDiv.hideIfOwner(this);Blockly.FieldColour.superClass_.dispose.call(this)};Blockly.FieldColour.prototype.getValue=function(){return this.colour_};Blockly.FieldColour.prototype.setValue=function(a){this.sourceBlock_&&Blockly.Events.isEnabled()&&this.colour_!=a&&Blockly.Events.fire(new Blockly.Events.Change(this.sourceBlock_,"field",this.name,this.colour_,a));this.colour_=a;this.borderRect_&&(this.borderRect_.style.fill=a)};
Blockly.FieldColour.prototype.getText=function(){var a=this.colour_,b=a.match(/^#(.)\1(.)\2(.)\3$/);b&&(a="#"+b[1]+b[2]+b[3]);return a};Blockly.FieldColour.COLOURS=goog.ui.ColorPicker.SIMPLE_GRID_COLORS;Blockly.FieldColour.COLUMNS=7;Blockly.FieldColour.prototype.setColours=function(a){this.colours_=a;return this};Blockly.FieldColour.prototype.setColumns=function(a){this.columns_=a;return this};
Blockly.FieldColour.prototype.showEditor_=function(){Blockly.WidgetDiv.show(this,this.sourceBlock_.RTL,Blockly.FieldColour.widgetDispose_);var a=new goog.ui.ColorPicker;a.setSize(this.columns_||Blockly.FieldColour.COLUMNS);a.setColors(this.colours_||Blockly.FieldColour.COLOURS);var b=goog.dom.getViewportSize(),c=goog.style.getViewportPageOffset(document),d=this.getAbsoluteXY_(),e=this.getScaledBBox_();a.render(Blockly.WidgetDiv.DIV);a.setSelectedColor(this.getValue());var f=goog.style.getSize(a.getElement());
@@ -1317,11 +1327,11 @@ Blockly.FieldVariable.dropdownCreate=function(){var a=this.sourceBlock_&&this.so
Blockly.FieldVariable.prototype.classValidator=function(a){var b=this.sourceBlock_.workspace;if(a==Blockly.Msg.RENAME_VARIABLE){var c=this.getText();Blockly.hideChaff();(a=Blockly.Variables.promptName(Blockly.Msg.RENAME_VARIABLE_TITLE.replace("%1",c),c))&&b.renameVariable(c,a);return null}if(a==Blockly.Msg.DELETE_VARIABLE.replace("%1",this.getText()))return b.deleteVariable(this.getText()),null};Blockly.Generator=function(a){this.name_=a;this.FUNCTION_NAME_PLACEHOLDER_REGEXP_=new RegExp(this.FUNCTION_NAME_PLACEHOLDER_,"g")};Blockly.Generator.NAME_TYPE="generated_function";Blockly.Generator.prototype.INFINITE_LOOP_TRAP=null;Blockly.Generator.prototype.STATEMENT_PREFIX=null;Blockly.Generator.prototype.INDENT=" ";Blockly.Generator.prototype.COMMENT_WRAP=60;Blockly.Generator.prototype.ORDER_OVERRIDES=[];
Blockly.Generator.prototype.workspaceToCode=function(a){a||(console.warn("No workspace specified in workspaceToCode call. Guessing."),a=Blockly.getMainWorkspace());var b=[];this.init(a);a=a.getTopBlocks(!0);for(var c=0,d;d=a[c];c++){var e=this.blockToCode(d);goog.isArray(e)&&(e=e[0]);e&&(d.outputConnection&&this.scrubNakedValue&&(e=this.scrubNakedValue(e)),b.push(e))}b=b.join("\n");b=this.finish(b);b=b.replace(/^\s+\n/,"");b=b.replace(/\n\s+$/,"\n");return b=b.replace(/[ \t]+\n/g,"\n")};
Blockly.Generator.prototype.prefixLines=function(a,b){return b+a.replace(/(?!\n$)\n/g,"\n"+b)};Blockly.Generator.prototype.allNestedComments=function(a){var b=[];a=a.getDescendants();for(var c=0;c<a.length;c++){var d=a[c].getCommentText();d&&b.push(d)}b.length&&b.push("");return b.join("\n")};
Blockly.Generator.prototype.blockToCode=function(a){if(!a)return"";if(a.disabled)return this.blockToCode(a.getNextBlock());var b=this[a.type];goog.asserts.assertFunction(b,'Language "%s" does not know how to generate code for block type "%s".',this.name_,a.type);b=b.call(a,a);if(goog.isArray(b))return goog.asserts.assert(a.outputConnection,'Expecting string from statement block "%s".',a.type),[this.scrub_(a,b[0]),b[1]];if(goog.isString(b))return this.STATEMENT_PREFIX&&(b=this.STATEMENT_PREFIX.replace(/%1/g,
"'"+a.id+"'")+b),this.scrub_(a,b);if(null===b)return"";goog.asserts.fail("Invalid code generated: %s",b)};
Blockly.Generator.prototype.blockToCode=function(a){if(!a)return"";if(a.disabled)return this.blockToCode(a.getNextBlock());var b=this[a.type];goog.asserts.assertFunction(b,'Language "%s" does not know how to generate code for block type "%s".',this.name_,a.type);b=b.call(a,a);if(goog.isArray(b))return goog.asserts.assert(a.outputConnection,'Expecting string from statement block "%s".',a.type),[this.scrub_(a,b[0]),b[1]];if(goog.isString(b)){var c=a.id.replace(/\$/g,"$$$$");this.STATEMENT_PREFIX&&(b=
this.STATEMENT_PREFIX.replace(/%1/g,"'"+c+"'")+b);return this.scrub_(a,b)}if(null===b)return"";goog.asserts.fail("Invalid code generated: %s",b)};
Blockly.Generator.prototype.valueToCode=function(a,b,c){isNaN(c)&&goog.asserts.fail('Expecting valid order from block "%s".',a.type);var d=a.getInputTargetBlock(b);if(!d)return"";b=this.blockToCode(d);if(""===b)return"";goog.asserts.assertArray(b,'Expecting tuple from value block "%s".',d.type);a=b[0];b=b[1];isNaN(b)&&goog.asserts.fail('Expecting valid order from value block "%s".',d.type);if(!a)return"";var d=!1,e=Math.floor(c),f=Math.floor(b);if(e<=f&&(e!=f||0!=e&&99!=e))for(d=!0,e=0;e<this.ORDER_OVERRIDES.length;e++)if(this.ORDER_OVERRIDES[e][0]==
c&&this.ORDER_OVERRIDES[e][1]==b){d=!1;break}d&&(a="("+a+")");return a};Blockly.Generator.prototype.statementToCode=function(a,b){var c=a.getInputTargetBlock(b),d=this.blockToCode(c);goog.asserts.assertString(d,'Expecting code from statement block "%s".',c&&c.type);d&&(d=this.prefixLines(d,this.INDENT));return d};
Blockly.Generator.prototype.addLoopTrap=function(a,b){this.INFINITE_LOOP_TRAP&&(a=this.INFINITE_LOOP_TRAP.replace(/%1/g,"'"+b+"'")+a);this.STATEMENT_PREFIX&&(a+=this.prefixLines(this.STATEMENT_PREFIX.replace(/%1/g,"'"+b+"'"),this.INDENT));return a};Blockly.Generator.prototype.RESERVED_WORDS_="";Blockly.Generator.prototype.addReservedWords=function(a){this.RESERVED_WORDS_+=a+","};Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_="{leCUI8hutHZI4480Dc}";
Blockly.Generator.prototype.addLoopTrap=function(a,b){b=b.replace(/\$/g,"$$$$");this.INFINITE_LOOP_TRAP&&(a=this.INFINITE_LOOP_TRAP.replace(/%1/g,"'"+b+"'")+a);this.STATEMENT_PREFIX&&(a+=this.prefixLines(this.STATEMENT_PREFIX.replace(/%1/g,"'"+b+"'"),this.INDENT));return a};Blockly.Generator.prototype.RESERVED_WORDS_="";Blockly.Generator.prototype.addReservedWords=function(a){this.RESERVED_WORDS_+=a+","};Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_="{leCUI8hutHZI4480Dc}";
Blockly.Generator.prototype.provideFunction_=function(a,b){if(!this.definitions_[a]){var c=this.variableDB_.getDistinctName(a,Blockly.Procedures.NAME_TYPE);this.functionNames_[a]=c;for(var c=b.join("\n").replace(this.FUNCTION_NAME_PLACEHOLDER_REGEXP_,c),d;d!=c;)d=c,c=c.replace(/^(( )*) /gm,"$1"+this.INDENT);this.definitions_[a]=c}return this.functionNames_[a]};Blockly.Names=function(a,b){this.variablePrefix_=b||"";this.reservedDict_=Object.create(null);if(a)for(var c=a.split(","),d=0;d<c.length;d++)this.reservedDict_[c[d]]=!0;this.reset()};Blockly.Names.prototype.reset=function(){this.db_=Object.create(null);this.dbReverse_=Object.create(null)};
Blockly.Names.prototype.getName=function(a,b){var c=a.toLowerCase()+"_"+b,d=b==Blockly.Variables.NAME_TYPE?this.variablePrefix_:"";if(c in this.db_)return d+this.db_[c];var e=this.getDistinctName(a,b);this.db_[c]=e.substr(d.length);return e};Blockly.Names.prototype.getDistinctName=function(a,b){for(var c=this.safeName_(a),d="";this.dbReverse_[c+d]||c+d in this.reservedDict_;)d=d?d+1:2;c+=d;this.dbReverse_[c]=!0;return(b==Blockly.Variables.NAME_TYPE?this.variablePrefix_:"")+c};
Blockly.Names.prototype.safeName_=function(a){a?(a=encodeURI(a.replace(/ /g,"_")).replace(/[^\w]/g,"_"),-1!="0123456789".indexOf(a[0])&&(a="my_"+a)):a="unnamed";return a};Blockly.Names.equals=function(a,b){return a.toLowerCase()==b.toLowerCase()};Blockly.Procedures={};Blockly.Procedures.NAME_TYPE="PROCEDURE";Blockly.Procedures.allProcedures=function(a){a=a.getAllBlocks();for(var b=[],c=[],d=0;d<a.length;d++)if(a[d].getProcedureDef){var e=a[d].getProcedureDef();e&&(e[2]?b.push(e):c.push(e))}c.sort(Blockly.Procedures.procTupleComparator_);b.sort(Blockly.Procedures.procTupleComparator_);return[c,b]};Blockly.Procedures.procTupleComparator_=function(a,b){return a[0].toLowerCase().localeCompare(b[0].toLowerCase())};
@@ -1338,7 +1348,7 @@ Blockly.FlyoutButton.prototype.updateTransform_=function(){this.svgGroup_.setAtt
Blockly.FlyoutButton.prototype.onMouseUp=function(a){a.preventDefault();a.stopPropagation();Blockly.Flyout.terminateDrag_();Blockly.Variables.createVariable(this.targetWorkspace_)};Blockly.Flyout=function(a){a.getMetrics=this.getMetrics_.bind(this);a.setMetrics=this.setMetrics_.bind(this);this.workspace_=new Blockly.WorkspaceSvg(a);this.workspace_.isFlyout=!0;this.RTL=!!a.RTL;this.horizontalLayout_=a.horizontalLayout;this.toolboxPosition_=a.toolboxPosition;this.eventWrappers_=[];this.backgroundButtons_=[];this.buttons_=[];this.listeners_=[];this.permanentlyDisabled_=[];this.startDragMouseX_=this.startDragMouseY_=0};Blockly.Flyout.startFlyout_=null;
Blockly.Flyout.startDownEvent_=null;Blockly.Flyout.startBlock_=null;Blockly.Flyout.onMouseUpWrapper_=null;Blockly.Flyout.onMouseMoveWrapper_=null;Blockly.Flyout.onMouseMoveBlockWrapper_=null;Blockly.Flyout.prototype.autoClose=!0;Blockly.Flyout.prototype.CORNER_RADIUS=8;Blockly.Flyout.prototype.DRAG_RADIUS=10;Blockly.Flyout.prototype.MARGIN=Blockly.Flyout.prototype.CORNER_RADIUS;Blockly.Flyout.prototype.GAP_X=3*Blockly.Flyout.prototype.MARGIN;Blockly.Flyout.prototype.GAP_Y=3*Blockly.Flyout.prototype.MARGIN;
Blockly.Flyout.prototype.SCROLLBAR_PADDING=2;Blockly.Flyout.prototype.width_=0;Blockly.Flyout.prototype.height_=0;Blockly.Flyout.prototype.dragMode_=Blockly.DRAG_NONE;Blockly.Flyout.prototype.dragAngleRange_=70;Blockly.Flyout.prototype.createDom=function(){this.svgGroup_=Blockly.createSvgElement("g",{"class":"blocklyFlyout"},null);this.svgBackground_=Blockly.createSvgElement("path",{"class":"blocklyFlyoutBackground"},this.svgGroup_);this.svgGroup_.appendChild(this.workspace_.createDom());return this.svgGroup_};
Blockly.Flyout.prototype.init=function(a){this.targetWorkspace_=a;this.workspace_.targetWorkspace=a;this.scrollbar_=new Blockly.Scrollbar(this.workspace_,this.horizontalLayout_,!1);this.hide();Array.prototype.push.apply(this.eventWrappers_,Blockly.bindEvent_(this.svgGroup_,"wheel",this,this.wheel_));this.autoClose||(this.filterWrapper_=this.filterForCapacity_.bind(this),this.targetWorkspace_.addChangeListener(this.filterWrapper_));Array.prototype.push.apply(this.eventWrappers_,Blockly.bindEvent_(this.svgGroup_,
Blockly.Flyout.prototype.init=function(a){this.targetWorkspace_=a;this.workspace_.targetWorkspace=a;this.scrollbar_=new Blockly.Scrollbar(this.workspace_,this.horizontalLayout_,!1);this.hide();Array.prototype.push.apply(this.eventWrappers_,Blockly.bindEventWithChecks_(this.svgGroup_,"wheel",this,this.wheel_));this.autoClose||(this.filterWrapper_=this.filterForCapacity_.bind(this),this.targetWorkspace_.addChangeListener(this.filterWrapper_));Array.prototype.push.apply(this.eventWrappers_,Blockly.bindEventWithChecks_(this.svgGroup_,
"mousedown",this,this.onMouseDown_))};
Blockly.Flyout.prototype.dispose=function(){this.hide();Blockly.unbindEvent_(this.eventWrappers_);this.filterWrapper_&&(this.targetWorkspace_.removeChangeListener(this.filterWrapper_),this.filterWrapper_=null);this.scrollbar_&&(this.scrollbar_.dispose(),this.scrollbar_=null);this.workspace_&&(this.workspace_.targetWorkspace=null,this.workspace_.dispose(),this.workspace_=null);this.svgGroup_&&(goog.dom.removeNode(this.svgGroup_),this.svgGroup_=null);this.targetWorkspace_=this.svgBackground_=null};
Blockly.Flyout.prototype.getWidth=function(){return this.width_};Blockly.Flyout.prototype.getHeight=function(){return this.height_};
@@ -1354,18 +1364,19 @@ Blockly.Flyout.prototype.setBackgroundPathHorizontal_=function(a,b){var c=this.t
Blockly.Flyout.prototype.wheel_=function(a){var b=this.horizontalLayout_?a.deltaX:a.deltaY;if(b){goog.userAgent.GECKO&&(b*=10);var c=this.getMetrics_(),b=this.horizontalLayout_?c.viewLeft+b:c.viewTop+b,b=Math.min(b,this.horizontalLayout_?c.contentWidth-c.viewWidth:c.contentHeight-c.viewHeight),b=Math.max(b,0);this.scrollbar_.set(b)}a.preventDefault();a.stopPropagation()};Blockly.Flyout.prototype.isVisible=function(){return this.svgGroup_&&"block"==this.svgGroup_.style.display};
Blockly.Flyout.prototype.hide=function(){if(this.isVisible()){this.svgGroup_.style.display="none";for(var a=0,b;b=this.listeners_[a];a++)Blockly.unbindEvent_(b);this.listeners_.length=0;this.reflowWrapper_&&(this.workspace_.removeChangeListener(this.reflowWrapper_),this.reflowWrapper_=null)}};
Blockly.Flyout.prototype.show=function(a){this.hide();this.clearOldBlocks_();a==Blockly.Variables.NAME_TYPE?a=Blockly.Variables.flyoutCategory(this.workspace_.targetWorkspace):a==Blockly.Procedures.NAME_TYPE&&(a=Blockly.Procedures.flyoutCategory(this.workspace_.targetWorkspace));this.svgGroup_.style.display="block";for(var b=[],c=[],d=this.permanentlyDisabled_.length=0,e;e=a[d];d++)if(e.tagName){var f=e.tagName.toUpperCase(),g=this.horizontalLayout_?this.GAP_X:this.GAP_Y;"BLOCK"==f?(f=Blockly.Xml.domToBlock(e,
this.workspace_),f.disabled&&this.permanentlyDisabled_.push(f),b.push({type:"block",block:f}),e=parseInt(e.getAttribute("gap"),10),c.push(isNaN(e)?g:e)):"SEP"==e.tagName.toUpperCase()?(e=parseInt(e.getAttribute("gap"),10),!isNaN(e)&&0<c.length?c[c.length-1]=e:c.push(g)):"BUTTON"==f&&(e=e.getAttribute("text"),e=new Blockly.FlyoutButton(this.workspace_,this.targetWorkspace_,e),b.push({type:"button",button:e}),c.push(g))}this.layout_(b,c);this.listeners_.push(Blockly.bindEvent_(this.svgBackground_,"mouseover",
this,function(){for(var a=this.workspace_.getTopBlocks(!1),b=0,c;c=a[b];b++)c.removeSelect()}));this.horizontalLayout_?this.height_=0:this.width_=0;this.reflow();this.filterForCapacity_();this.position();this.reflowWrapper_=this.reflow.bind(this);this.workspace_.addChangeListener(this.reflowWrapper_)};
this.workspace_),f.disabled&&this.permanentlyDisabled_.push(f),b.push({type:"block",block:f}),e=parseInt(e.getAttribute("gap"),10),c.push(isNaN(e)?g:e)):"SEP"==e.tagName.toUpperCase()?(e=parseInt(e.getAttribute("gap"),10),!isNaN(e)&&0<c.length?c[c.length-1]=e:c.push(g)):"BUTTON"==f&&(e=e.getAttribute("text"),e=new Blockly.FlyoutButton(this.workspace_,this.targetWorkspace_,e),b.push({type:"button",button:e}),c.push(g))}this.layout_(b,c);this.listeners_.push(Blockly.bindEventWithChecks_(this.svgBackground_,
"mouseover",this,function(){for(var a=this.workspace_.getTopBlocks(!1),b=0,c;c=a[b];b++)c.removeSelect()}));this.horizontalLayout_?this.height_=0:this.width_=0;this.reflow();this.filterForCapacity_();this.position();this.reflowWrapper_=this.reflow.bind(this);this.workspace_.addChangeListener(this.reflowWrapper_)};
Blockly.Flyout.prototype.layout_=function(a,b){this.workspace_.scale=this.targetWorkspace_.scale;var c=this.MARGIN,d=this.RTL?c:c+Blockly.BlockSvg.TAB_WIDTH;this.horizontalLayout_&&this.RTL&&(a=a.reverse());for(var e=0,f;f=a[e];e++)if("block"==f.type){f=f.block;for(var g=f.getDescendants(),h=0,k;k=g[h];h++)k.isInFlyout=!0;f.render();g=f.getSvgRoot();h=f.getHeightWidth();k=f.outputConnection?Blockly.BlockSvg.TAB_WIDTH:0;this.horizontalLayout_&&(d+=k);f.moveBy(this.horizontalLayout_&&this.RTL?d+h.width-
k:d,c);this.horizontalLayout_?d+=h.width+b[e]-k:c+=h.height+b[e];h=Blockly.createSvgElement("rect",{"fill-opacity":0},null);h.tooltip=f;Blockly.Tooltip.bindMouseEvents(h);this.workspace_.getCanvas().insertBefore(h,f.getSvgRoot());f.flyoutRect_=h;this.backgroundButtons_[e]=h;this.addBlockListeners_(g,f,h)}else"button"==f.type&&(f=f.button,g=f.createDom(),f.moveTo(d,c),f.show(),Blockly.bindEvent_(g,"mouseup",f,f.onMouseUp),this.buttons_.push(f),this.horizontalLayout_?d+=f.width+b[e]:c+=f.height+b[e])};
Blockly.Flyout.prototype.clearOldBlocks_=function(){for(var a=this.workspace_.getTopBlocks(!1),b=0,c;c=a[b];b++)c.workspace==this.workspace_&&c.dispose(!1,!1);for(b=0;a=this.backgroundButtons_[b];b++)goog.dom.removeNode(a);for(b=this.backgroundButtons_.length=0;a=this.buttons_[b];b++)a.dispose();this.buttons_.length=0};
Blockly.Flyout.prototype.addBlockListeners_=function(a,b,c){this.listeners_.push(Blockly.bindEvent_(a,"mousedown",null,this.blockMouseDown_(b)));this.listeners_.push(Blockly.bindEvent_(c,"mousedown",null,this.blockMouseDown_(b)));this.listeners_.push(Blockly.bindEvent_(a,"mouseover",b,b.addSelect));this.listeners_.push(Blockly.bindEvent_(a,"mouseout",b,b.removeSelect));this.listeners_.push(Blockly.bindEvent_(c,"mouseover",b,b.addSelect));this.listeners_.push(Blockly.bindEvent_(c,"mouseout",b,b.removeSelect))};
Blockly.Flyout.prototype.blockMouseDown_=function(a){var b=this;return function(c){Blockly.terminateDrag_();Blockly.hideChaff(!0);Blockly.isRightButton(c)?a.showContextMenu_(c):(Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED),b.startDragMouseY_=c.clientY,b.startDragMouseX_=c.clientX,Blockly.Flyout.startDownEvent_=c,Blockly.Flyout.startBlock_=a,Blockly.Flyout.startFlyout_=b,Blockly.Flyout.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",b,b.onMouseUp_),Blockly.Flyout.onMouseMoveBlockWrapper_=
Blockly.bindEvent_(document,"mousemove",b,b.onMouseMoveBlock_));c.stopPropagation();c.preventDefault()}};
Blockly.Flyout.prototype.onMouseDown_=function(a){Blockly.isRightButton(a)||(Blockly.hideChaff(!0),this.dragMode_=Blockly.DRAG_FREE,this.startDragMouseY_=a.clientY,this.startDragMouseX_=a.clientX,Blockly.Flyout.startFlyout_=this,Blockly.Flyout.onMouseMoveWrapper_=Blockly.bindEvent_(document,"mousemove",this,this.onMouseMove_),Blockly.Flyout.onMouseUpWrapper_=Blockly.bindEvent_(document,"mouseup",this,Blockly.Flyout.terminateDrag_),a.preventDefault(),a.stopPropagation())};
Blockly.Flyout.prototype.onMouseUp_=function(a){this.workspace_.isDragging()||(this.autoClose?this.createBlockFunc_(Blockly.Flyout.startBlock_)(Blockly.Flyout.startDownEvent_):Blockly.WidgetDiv.isVisible()||Blockly.Events.fire(new Blockly.Events.Ui(Blockly.Flyout.startBlock_,"click",void 0,void 0)));Blockly.terminateDrag_()};
k:d,c);this.horizontalLayout_?d+=h.width+b[e]-k:c+=h.height+b[e];h=Blockly.createSvgElement("rect",{"fill-opacity":0},null);h.tooltip=f;Blockly.Tooltip.bindMouseEvents(h);this.workspace_.getCanvas().insertBefore(h,f.getSvgRoot());f.flyoutRect_=h;this.backgroundButtons_[e]=h;this.addBlockListeners_(g,f,h)}else"button"==f.type&&(f=f.button,g=f.createDom(),f.moveTo(d,c),f.show(),Blockly.bindEventWithChecks_(g,"mouseup",f,f.onMouseUp),this.buttons_.push(f),this.horizontalLayout_?d+=f.width+b[e]:c+=f.height+
b[e])};Blockly.Flyout.prototype.clearOldBlocks_=function(){for(var a=this.workspace_.getTopBlocks(!1),b=0,c;c=a[b];b++)c.workspace==this.workspace_&&c.dispose(!1,!1);for(b=0;a=this.backgroundButtons_[b];b++)goog.dom.removeNode(a);for(b=this.backgroundButtons_.length=0;a=this.buttons_[b];b++)a.dispose();this.buttons_.length=0};
Blockly.Flyout.prototype.addBlockListeners_=function(a,b,c){this.listeners_.push(Blockly.bindEventWithChecks_(a,"mousedown",null,this.blockMouseDown_(b)));this.listeners_.push(Blockly.bindEventWithChecks_(c,"mousedown",null,this.blockMouseDown_(b)));this.listeners_.push(Blockly.bindEvent_(a,"mouseover",b,b.addSelect));this.listeners_.push(Blockly.bindEvent_(a,"mouseout",b,b.removeSelect));this.listeners_.push(Blockly.bindEvent_(c,"mouseover",b,b.addSelect));this.listeners_.push(Blockly.bindEvent_(c,
"mouseout",b,b.removeSelect))};Blockly.Flyout.blockRightClick_=function(a,b){Blockly.terminateDrag_();Blockly.hideChaff(!0);b.showContextMenu_(a);Blockly.Touch.clearTouchIdentifier()};
Blockly.Flyout.prototype.blockMouseDown_=function(a){var b=this;return function(c){Blockly.isRightButton(c)?Blockly.Flyout.blockRightClick_(c,a):(Blockly.terminateDrag_(),Blockly.hideChaff(!0),Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED),b.startDragMouseY_=c.clientY,b.startDragMouseX_=c.clientX,Blockly.Flyout.startDownEvent_=c,Blockly.Flyout.startBlock_=a,Blockly.Flyout.startFlyout_=b,Blockly.Flyout.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",b,b.onMouseUp_),Blockly.Flyout.onMouseMoveBlockWrapper_=
Blockly.bindEventWithChecks_(document,"mousemove",b,b.onMouseMoveBlock_));c.stopPropagation();c.preventDefault()}};
Blockly.Flyout.prototype.onMouseDown_=function(a){Blockly.isRightButton(a)?Blockly.Touch.clearTouchIdentifier():(Blockly.hideChaff(!0),this.dragMode_=Blockly.DRAG_FREE,this.startDragMouseY_=a.clientY,this.startDragMouseX_=a.clientX,Blockly.Flyout.startFlyout_=this,Blockly.Flyout.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",this,this.onMouseMove_),Blockly.Flyout.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,Blockly.Flyout.terminateDrag_),a.preventDefault(),
a.stopPropagation())};Blockly.Flyout.prototype.onMouseUp_=function(a){this.workspace_.isDragging()||(Blockly.Touch.clearTouchIdentifier(),this.autoClose?this.createBlockFunc_(Blockly.Flyout.startBlock_)(Blockly.Flyout.startDownEvent_):Blockly.WidgetDiv.isVisible()||Blockly.Events.fire(new Blockly.Events.Ui(Blockly.Flyout.startBlock_,"click",void 0,void 0)));Blockly.terminateDrag_()};
Blockly.Flyout.prototype.onMouseMove_=function(a){var b=this.getMetrics_();if(this.horizontalLayout_){if(!(0>b.contentWidth-b.viewWidth)){var c=a.clientX-this.startDragMouseX_;this.startDragMouseX_=a.clientX;a=b.viewLeft-c;a=goog.math.clamp(a,0,b.contentWidth-b.viewWidth);this.scrollbar_.set(a)}}else 0>b.contentHeight-b.viewHeight||(c=a.clientY-this.startDragMouseY_,this.startDragMouseY_=a.clientY,a=b.viewTop-c,a=goog.math.clamp(a,0,b.contentHeight-b.viewHeight),this.scrollbar_.set(a))};
Blockly.Flyout.prototype.onMouseMoveBlock_=function(a){if(!("mousemove"==a.type&&1>=a.clientX&&0==a.clientY&&0==a.button))if(this.determineDragIntention_(a.clientX-Blockly.Flyout.startDownEvent_.clientX,a.clientY-Blockly.Flyout.startDownEvent_.clientY))this.createBlockFunc_(Blockly.Flyout.startBlock_)(Blockly.Flyout.startDownEvent_);else if(this.dragMode_==Blockly.DRAG_FREE)this.onMouseMove_(a);a.stopPropagation()};
Blockly.Flyout.prototype.onMouseMoveBlock_=function(a){"mousemove"==a.type&&1>=a.clientX&&0==a.clientY&&0==a.button||(this.determineDragIntention_(a.clientX-Blockly.Flyout.startDownEvent_.clientX,a.clientY-Blockly.Flyout.startDownEvent_.clientY)?(Blockly.longStop_(),this.createBlockFunc_(Blockly.Flyout.startBlock_)(Blockly.Flyout.startDownEvent_)):this.dragMode_==Blockly.DRAG_FREE&&(Blockly.longStop_(),this.onMouseMove_(a)));a.stopPropagation()};
Blockly.Flyout.prototype.determineDragIntention_=function(a,b){if(this.dragMode_==Blockly.DRAG_FREE)return!1;if(Math.sqrt(a*a+b*b)<this.DRAG_RADIUS)return this.dragMode_=Blockly.DRAG_STICKY,!1;if(this.isDragTowardWorkspace_(a,b)||!this.scrollbar_.isVisible())return!0;this.dragMode_=Blockly.DRAG_FREE;return!1};
Blockly.Flyout.prototype.isDragTowardWorkspace_=function(a,b){var c=Math.atan2(b,a)/Math.PI*180,d=!1,e=this.dragAngleRange_;if(this.horizontalLayout_)this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP?c<90+e&&c>90-e&&(d=!0):c>-90-e&&c<-90+e&&(d=!0);else if(this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT)c<e&&c>-e&&(d=!0);else if(c<-180+e||c>180-e)d=!0;return d};
Blockly.Flyout.prototype.createBlockFunc_=function(a){var b=this;return function(c){if(!Blockly.isRightButton(c)&&!a.disabled){Blockly.Events.disable();try{var d=b.placeNewBlock_(a)}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&(Blockly.Events.setGroup(!0),Blockly.Events.fire(new Blockly.Events.Create(d)));b.autoClose?b.hide():b.filterForCapacity_();d.onMouseDown_(c);Blockly.dragMode_=Blockly.DRAG_FREE;d.setDragging_(!0)}}};
@@ -1373,17 +1384,17 @@ Blockly.Flyout.prototype.placeNewBlock_=function(a){var b=this.targetWorkspace_,
c.y+=d/e-d);a=Blockly.Xml.blockToDom(a);a=Blockly.Xml.domToBlock(a,b);e=a.getSvgRoot();if(!e)throw"block is not rendered.";e=Blockly.getSvgXY_(e,b);e.x+=b.scrollX/b.scale-b.scrollX;e.y+=b.scrollY/b.scale-b.scrollY;b.toolbox_&&!b.scrollbar&&(e.x+=b.toolbox_.getWidth()/b.scale,e.y+=b.toolbox_.getHeight()/b.scale);a.moveBy(c.x-e.x,c.y-e.y);return a};
Blockly.Flyout.prototype.filterForCapacity_=function(){for(var a=this.targetWorkspace_.remainingCapacity(),b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)if(-1==this.permanentlyDisabled_.indexOf(d)){var e=d.getDescendants();d.setDisabled(e.length>a)}};
Blockly.Flyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.left,c=a.top,d=a.width,a=a.height;return this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP?new goog.math.Rect(-1E9,c-1E9,2E9,1E9+a):this.toolboxPosition_==Blockly.TOOLBOX_AT_BOTTOM?new goog.math.Rect(-1E9,c,2E9,1E9+a):this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT?new goog.math.Rect(b-1E9,-1E9,1E9+d,2E9):new goog.math.Rect(b,-1E9,1E9+d,2E9)};
Blockly.Flyout.terminateDrag_=function(){Blockly.Flyout.startFlyout_&&(Blockly.Flyout.startFlyout_.dragMode_=Blockly.DRAG_NONE);Blockly.Flyout.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_),Blockly.Flyout.onMouseUpWrapper_=null);Blockly.Flyout.onMouseMoveBlockWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveBlockWrapper_),Blockly.Flyout.onMouseMoveBlockWrapper_=null);Blockly.Flyout.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveWrapper_),
Blockly.Flyout.onMouseMoveWrapper_=null);Blockly.Flyout.startDownEvent_=null;Blockly.Flyout.startBlock_=null;Blockly.Flyout.startFlyout_=null};
Blockly.Flyout.terminateDrag_=function(){Blockly.Flyout.startFlyout_&&(Blockly.Flyout.startFlyout_.dragMode_==Blockly.DRAG_FREE&&Blockly.Touch.clearTouchIdentifier(),Blockly.Flyout.startFlyout_.dragMode_=Blockly.DRAG_NONE,Blockly.Flyout.startFlyout_=null);Blockly.Flyout.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_),Blockly.Flyout.onMouseUpWrapper_=null);Blockly.Flyout.onMouseMoveBlockWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveBlockWrapper_),Blockly.Flyout.onMouseMoveBlockWrapper_=
null);Blockly.Flyout.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Flyout.onMouseMoveWrapper_),Blockly.Flyout.onMouseMoveWrapper_=null);Blockly.Flyout.startDownEvent_=null;Blockly.Flyout.startBlock_=null};
Blockly.Flyout.prototype.reflowHorizontal=function(a){this.workspace_.scale=this.targetWorkspace_.scale;for(var b=0,c=0,d;d=a[c];c++)b=Math.max(b,d.getHeightWidth().height);b+=1.5*this.MARGIN;b*=this.workspace_.scale;b+=Blockly.Scrollbar.scrollbarThickness;if(this.height_!=b){for(c=0;d=a[c];c++){var e=d.getHeightWidth();if(d.flyoutRect_){d.flyoutRect_.setAttribute("width",e.width);d.flyoutRect_.setAttribute("height",e.height);var f=d.outputConnection?Blockly.BlockSvg.TAB_WIDTH:0,g=d.getRelativeToSurfaceXY();
d.flyoutRect_.setAttribute("y",g.y);d.flyoutRect_.setAttribute("x",this.RTL?g.x-e.width+f:g.x-f);(e=d.startHat_?Blockly.BlockSvg.START_HAT_HEIGHT:0)&&d.moveBy(0,e);d.flyoutRect_.setAttribute("y",g.y)}}this.height_=b;this.targetWorkspace_.resize()}};
Blockly.Flyout.prototype.reflowVertical=function(a){this.workspace_.scale=this.targetWorkspace_.scale;for(var b=0,c=0,d;d=a[c];c++){var e=d.getHeightWidth().width;d.outputConnection&&(e-=Blockly.BlockSvg.TAB_WIDTH);b=Math.max(b,e)}for(c=0;d=this.buttons_[c];c++)b=Math.max(b,d.width);b+=1.5*this.MARGIN+Blockly.BlockSvg.TAB_WIDTH;b*=this.workspace_.scale;b+=Blockly.Scrollbar.scrollbarThickness;if(this.width_!=b){for(c=0;d=a[c];c++){e=d.getHeightWidth();if(this.RTL){var f=d.getRelativeToSurfaceXY().x,
g=b/this.workspace_.scale-this.MARGIN,g=g-Blockly.BlockSvg.TAB_WIDTH;d.moveBy(g-f,0)}d.flyoutRect_&&(d.flyoutRect_.setAttribute("width",e.width),d.flyoutRect_.setAttribute("height",e.height),g=d.outputConnection?Blockly.BlockSvg.TAB_WIDTH:0,f=d.getRelativeToSurfaceXY(),d.flyoutRect_.setAttribute("x",this.RTL?f.x-e.width+g:f.x-g),(e=d.startHat_?Blockly.BlockSvg.START_HAT_HEIGHT:0)&&d.moveBy(0,e),d.flyoutRect_.setAttribute("y",f.y))}this.width_=b;this.targetWorkspace_.resize()}};
Blockly.Flyout.prototype.reflow=function(){this.reflowWrapper_&&this.workspace_.removeChangeListener(this.reflowWrapper_);var a=this.workspace_.getTopBlocks(!1);this.horizontalLayout_?this.reflowHorizontal(a):this.reflowVertical(a);this.reflowWrapper_&&this.workspace_.addChangeListener(this.reflowWrapper_)};Blockly.Toolbox=function(a){this.workspace_=a;this.RTL=a.options.RTL;this.horizontalLayout_=a.options.horizontalLayout;this.toolboxPosition=a.options.toolboxPosition;this.config_={indentWidth:19,cssRoot:"blocklyTreeRoot",cssHideRoot:"blocklyHidden",cssItem:"",cssTreeRow:"blocklyTreeRow",cssItemLabel:"blocklyTreeLabel",cssTreeIcon:"blocklyTreeIcon",cssExpandedFolderIcon:"blocklyTreeIconOpen",cssFileIcon:"blocklyTreeIconNone",cssSelectedRow:"blocklyTreeSelected"};this.treeSeparatorConfig_={cssTreeRow:"blocklyTreeSeparator"};
this.horizontalLayout_&&(this.config_.cssTreeRow+=a.RTL?" blocklyHorizontalTreeRtl":" blocklyHorizontalTree",this.treeSeparatorConfig_.cssTreeRow="blocklyTreeSeparatorHorizontal "+(a.RTL?"blocklyHorizontalTreeRtl":"blocklyHorizontalTree"),this.config_.cssTreeIcon="")};Blockly.Toolbox.prototype.width=0;Blockly.Toolbox.prototype.height=0;Blockly.Toolbox.prototype.selectedOption_=null;Blockly.Toolbox.prototype.lastCategory_=null;
Blockly.Toolbox.prototype.init=function(){var a=this.workspace_,b=this.workspace_.getParentSvg();this.HtmlDiv=goog.dom.createDom("DIV","blocklyToolboxDiv");this.HtmlDiv.setAttribute("dir",a.RTL?"RTL":"LTR");b.parentNode.insertBefore(this.HtmlDiv,b);Blockly.bindEvent_(this.HtmlDiv,"mousedown",this,function(a){Blockly.isRightButton(a)||a.target==this.HtmlDiv?Blockly.hideChaff(!1):Blockly.hideChaff(!0)});this.flyout_=new Blockly.Flyout({disabledPatternId:a.options.disabledPatternId,parentWorkspace:a,
RTL:a.RTL,horizontalLayout:a.horizontalLayout,toolboxPosition:a.options.toolboxPosition});goog.dom.insertSiblingAfter(this.flyout_.createDom(),a.svgGroup_);this.flyout_.init(a);this.config_.cleardotPath=a.options.pathToMedia+"1x1.gif";this.config_.cssCollapsedFolderIcon="blocklyTreeIconClosed"+(a.RTL?"Rtl":"Ltr");this.tree_=b=new Blockly.Toolbox.TreeControl(this,this.config_);b.setShowRootNode(!1);b.setShowLines(!1);b.setShowExpandIcons(!1);b.setSelectedItem(null);a=this.populate_(a.options.languageTree);
b.render(this.HtmlDiv);a&&b.setSelectedItem(a);this.addColour_();this.position()};Blockly.Toolbox.prototype.dispose=function(){this.flyout_.dispose();this.tree_.dispose();goog.dom.removeNode(this.HtmlDiv);this.lastCategory_=this.workspace_=null};Blockly.Toolbox.prototype.getWidth=function(){return this.width};Blockly.Toolbox.prototype.getHeight=function(){return this.height};
Blockly.Toolbox.prototype.init=function(){var a=this.workspace_,b=this.workspace_.getParentSvg();this.HtmlDiv=goog.dom.createDom("DIV","blocklyToolboxDiv");this.HtmlDiv.setAttribute("dir",a.RTL?"RTL":"LTR");b.parentNode.insertBefore(this.HtmlDiv,b);Blockly.bindEventWithChecks_(this.HtmlDiv,"mousedown",this,function(a){Blockly.isRightButton(a)||a.target==this.HtmlDiv?Blockly.hideChaff(!1):Blockly.hideChaff(!0);Blockly.Touch.clearTouchIdentifier()});this.flyout_=new Blockly.Flyout({disabledPatternId:a.options.disabledPatternId,
parentWorkspace:a,RTL:a.RTL,oneBasedIndex:a.options.oneBasedIndex,horizontalLayout:a.horizontalLayout,toolboxPosition:a.options.toolboxPosition});goog.dom.insertSiblingAfter(this.flyout_.createDom(),a.svgGroup_);this.flyout_.init(a);this.config_.cleardotPath=a.options.pathToMedia+"1x1.gif";this.config_.cssCollapsedFolderIcon="blocklyTreeIconClosed"+(a.RTL?"Rtl":"Ltr");this.tree_=b=new Blockly.Toolbox.TreeControl(this,this.config_);b.setShowRootNode(!1);b.setShowLines(!1);b.setShowExpandIcons(!1);
b.setSelectedItem(null);a=this.populate_(a.options.languageTree);b.render(this.HtmlDiv);a&&b.setSelectedItem(a);this.addColour_();this.position()};Blockly.Toolbox.prototype.dispose=function(){this.flyout_.dispose();this.tree_.dispose();goog.dom.removeNode(this.HtmlDiv);this.lastCategory_=this.workspace_=null};Blockly.Toolbox.prototype.getWidth=function(){return this.width};Blockly.Toolbox.prototype.getHeight=function(){return this.height};
Blockly.Toolbox.prototype.position=function(){var a=this.HtmlDiv;if(a){var b=this.workspace_.getParentSvg();goog.style.getPageOffset(b);b=Blockly.svgSize(b);this.horizontalLayout_?(a.style.left="0",a.style.height="auto",a.style.width=b.width+"px",this.height=a.offsetHeight,this.toolboxPosition==Blockly.TOOLBOX_AT_TOP?a.style.top="0":a.style.bottom="0"):(this.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?a.style.right="0":a.style.left="0",a.style.height=b.height+"px",this.width=a.offsetWidth);this.flyout_.position()}};
Blockly.Toolbox.prototype.populate_=function(a){this.tree_.removeChildren();this.tree_.blocks=[];this.hasColours_=!1;a=this.syncTrees_(a,this.tree_,this.workspace_.options.pathToMedia);if(this.tree_.blocks.length)throw"Toolbox cannot have both blocks and categories in the root level.";this.workspace_.resizeContents();return a};
Blockly.Toolbox.prototype.syncTrees_=function(a,b,c){for(var d=null,e=null,f=0,g;g=a.childNodes[f];f++)if(g.tagName)switch(g.tagName.toUpperCase()){case "CATEGORY":e=this.tree_.createNode(g.getAttribute("name"));e.blocks=[];b.add(e);var h=g.getAttribute("custom");h?e.blocks=h:(h=this.syncTrees_(g,e,c))&&(d=h);h=g.getAttribute("colour");goog.isString(h)?(h.match(/^#[0-9a-fA-F]{6}$/)?e.hexColour=h:e.hexColour=Blockly.hueToRgb(h),this.hasColours_=!0):e.hexColour="";"true"==g.getAttribute("expanded")?
@@ -1391,7 +1402,7 @@ Blockly.Toolbox.prototype.syncTrees_=function(a,b,c){for(var d=null,e=null,f=0,g
Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren();for(var b=0,c;c=a[b];b++){var d=c.getRowElement();if(d){var e=this.hasColours_?"8px solid "+(c.hexColour||"#ddd"):"none";this.workspace_.RTL?d.style.borderRight=e:d.style.borderLeft=e}this.addColour_(c)}};Blockly.Toolbox.prototype.clearSelection=function(){this.tree_.setSelectedItem(null)};
Blockly.Toolbox.prototype.getClientRect=function(){if(!this.HtmlDiv)return null;var a=this.HtmlDiv.getBoundingClientRect(),b=a.left,c=a.top,d=a.width,a=a.height;return this.toolboxPosition==Blockly.TOOLBOX_AT_LEFT?new goog.math.Rect(-1E7,-1E7,1E7+b+d,2E7):this.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?new goog.math.Rect(b,-1E7,1E7+d,2E7):this.toolboxPosition==Blockly.TOOLBOX_AT_TOP?new goog.math.Rect(-1E7,-1E7,2E7,1E7+c+a):new goog.math.Rect(0,c,2E7,1E7+d)};
Blockly.Toolbox.prototype.refreshSelection=function(){var a=this.tree_.getSelectedItem();a&&a.blocks&&this.flyout_.show(a.blocks)};Blockly.Toolbox.TreeControl=function(a,b){this.toolbox_=a;goog.ui.tree.TreeControl.call(this,goog.html.SafeHtml.EMPTY,b)};goog.inherits(Blockly.Toolbox.TreeControl,goog.ui.tree.TreeControl);
Blockly.Toolbox.TreeControl.prototype.enterDocument=function(){Blockly.Toolbox.TreeControl.superClass_.enterDocument.call(this);if(goog.events.BrowserFeature.TOUCH_ENABLED){var a=this.getElement();Blockly.bindEvent_(a,goog.events.EventType.TOUCHSTART,this,this.handleTouchEvent_)}};Blockly.Toolbox.TreeControl.prototype.handleTouchEvent_=function(a){a.preventDefault();var b=this.getNodeFromEvent_(a);b&&a.type===goog.events.EventType.TOUCHSTART&&setTimeout(function(){b.onMouseDown(a)},1)};
Blockly.Toolbox.TreeControl.prototype.enterDocument=function(){Blockly.Toolbox.TreeControl.superClass_.enterDocument.call(this);var a=this.getElement();goog.events.BrowserFeature.TOUCH_ENABLED&&Blockly.bindEventWithChecks_(a,goog.events.EventType.TOUCHSTART,this,this.handleTouchEvent_)};Blockly.Toolbox.TreeControl.prototype.handleTouchEvent_=function(a){a.preventDefault();var b=this.getNodeFromEvent_(a);b&&a.type===goog.events.EventType.TOUCHSTART&&setTimeout(function(){b.onMouseDown(a)},1)};
Blockly.Toolbox.TreeControl.prototype.createNode=function(a){return new Blockly.Toolbox.TreeNode(this.toolbox_,a?goog.html.SafeHtml.htmlEscape(a):goog.html.SafeHtml.EMPTY,this.getConfig(),this.getDomHelper())};
Blockly.Toolbox.TreeControl.prototype.setSelectedItem=function(a){var b=this.toolbox_;if(a!=this.selectedItem_&&a!=b.tree_){b.lastCategory_&&(b.lastCategory_.getRowElement().style.backgroundColor="");if(a){var c=a.hexColour||"#57e";a.getRowElement().style.backgroundColor=c;b.addColour_(a)}c=this.getSelectedItem();goog.ui.tree.TreeControl.prototype.setSelectedItem.call(this,a);a&&a.blocks&&a.blocks.length?(b.flyout_.show(a.blocks),b.lastCategory_!=a&&b.flyout_.scrollToStart()):b.flyout_.hide();c!=
a&&c!=this&&(c=new Blockly.Events.Ui(null,"category",c&&c.getHtml(),a&&a.getHtml()),c.workspaceId=b.workspace_.id,Blockly.Events.fire(c));a&&(b.lastCategory_=a)}};Blockly.Toolbox.TreeNode=function(a,b,c,d){goog.ui.tree.TreeNode.call(this,b,c,d);a&&(this.horizontalLayout_=a.horizontalLayout_,b=function(){Blockly.svgResize(a.workspace_)},goog.events.listen(a.tree_,goog.ui.tree.BaseNode.EventType.EXPAND,b),goog.events.listen(a.tree_,goog.ui.tree.BaseNode.EventType.COLLAPSE,b))};
@@ -1404,7 +1415,7 @@ Blockly.Css.CONTENT=[".blocklySvg {","background-color: #fff;","outline: none;",
"color: #000;","display: none;","font-family: sans-serif;","font-size: 9pt;","opacity: 0.9;","padding: 2px;","position: absolute;","z-index: 100000;","}",".blocklyResizeSE {","cursor: se-resize;","fill: #aaa;","}",".blocklyResizeSW {","cursor: sw-resize;","fill: #aaa;","}",".blocklyResizeLine {","stroke: #888;","stroke-width: 1;","}",".blocklyHighlightedConnectionPath {","fill: none;","stroke: #fc3;","stroke-width: 4px;","}",".blocklyPathLight {","fill: none;","stroke-linecap: round;","stroke-width: 1;",
"}",".blocklySelected>.blocklyPath {","stroke: #fc3;","stroke-width: 3px;","}",".blocklySelected>.blocklyPathLight {","display: none;","}",".blocklyDragging>.blocklyPath,",".blocklyDragging>.blocklyPathLight {","fill-opacity: .8;","stroke-opacity: .8;","}",".blocklyDragging>.blocklyPathDark {","display: none;","}",".blocklyDisabled>.blocklyPath {","fill-opacity: .5;","stroke-opacity: .5;","}",".blocklyDisabled>.blocklyPathLight,",".blocklyDisabled>.blocklyPathDark {","display: none;","}",".blocklyText {",
"cursor: default;","fill: #fff;","font-family: sans-serif;","font-size: 11pt;","}",".blocklyNonEditableText>text {","pointer-events: none;","}",".blocklyNonEditableText>rect,",".blocklyEditableText>rect {","fill: #fff;","fill-opacity: .6;","}",".blocklyNonEditableText>text,",".blocklyEditableText>text {","fill: #000;","}",".blocklyEditableText:hover>rect {","stroke: #fff;","stroke-width: 2;","}",".blocklyBubbleText {","fill: #000;","}",".blocklyFlyoutButton {","fill: #888;","cursor: default;","}",
".blocklyFlyoutButtonShadow {","fill: #444;","}",".blocklyFlyoutButton:hover {","fill: #aaa;","}",".blocklySvg text {","user-select: none;","-moz-user-select: none;","-webkit-user-select: none;","cursor: inherit;","}",".blocklyHidden {","display: none;","}",".blocklyFieldDropdown:not(.blocklyHidden) {","display: block;","}",".blocklyIconGroup {","cursor: default;","}",".blocklyIconGroup:not(:hover),",".blocklyIconGroupReadonly {","opacity: .6;","}",".blocklyIconShape {","fill: #00f;","stroke: #fff;",
".blocklyFlyoutButtonShadow {","fill: #666;","}",".blocklyFlyoutButton:hover {","fill: #aaa;","}",".blocklySvg text {","user-select: none;","-moz-user-select: none;","-webkit-user-select: none;","cursor: inherit;","}",".blocklyHidden {","display: none;","}",".blocklyFieldDropdown:not(.blocklyHidden) {","display: block;","}",".blocklyIconGroup {","cursor: default;","}",".blocklyIconGroup:not(:hover),",".blocklyIconGroupReadonly {","opacity: .6;","}",".blocklyIconShape {","fill: #00f;","stroke: #fff;",
"stroke-width: 1px;","}",".blocklyIconSymbol {","fill: #fff;","}",".blocklyMinimalBody {","margin: 0;","padding: 0;","}",".blocklyCommentTextarea {","background-color: #ffc;","border: 0;","margin: 0;","padding: 2px;","resize: none;","}",".blocklyHtmlInput {","border: none;","border-radius: 4px;","font-family: sans-serif;","height: 100%;","margin: 0;","outline: none;","padding: 0 1px;","width: 100%","}",".blocklyMainBackground {","stroke-width: 1;","stroke: #c6c6c6;","}",".blocklyMutatorBackground {",
"fill: #fff;","stroke: #ddd;","stroke-width: 1;","}",".blocklyFlyoutBackground {","fill: #ddd;","fill-opacity: .8;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarHandle {","fill: #ccc;","}",".blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,",".blocklyScrollbarHandle:hover {","fill: #bbb;","}",".blocklyZoom>image {","opacity: .4;","}",".blocklyZoom>image:hover {","opacity: .6;","}",".blocklyZoom>image:active {","opacity: .8;","}",".blocklyFlyout .blocklyScrollbarHandle {",
"fill: #bbb;","}",".blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,",".blocklyFlyout .blocklyScrollbarHandle:hover {","fill: #aaa;","}",".blocklyInvalidInput {","background: #faa;","}",".blocklyAngleCircle {","stroke: #444;","stroke-width: 1;","fill: #ddd;","fill-opacity: .8;","}",".blocklyAngleMarks {","stroke: #444;","stroke-width: 1;","}",".blocklyAngleGauge {","fill: #f88;","fill-opacity: .8;","}",".blocklyAngleLine {","stroke: #f00;","stroke-width: 2;","stroke-linecap: round;",
@@ -1421,38 +1432,36 @@ Blockly.Css.CONTENT=[".blocklySvg {","background-color: #fff;","outline: none;",
"text-align: left;","}",".blocklyWidgetDiv .goog-menuitem-mnemonic-hint {","text-decoration: underline;","}",".blocklyWidgetDiv .goog-menuitem-mnemonic-separator {","color: #999;","font-size: 12px;","padding-left: 4px;","}",".blocklyWidgetDiv .goog-menuseparator {","border-top: 1px solid #ccc;","margin: 4px 0;","padding: 0;","}",""];Blockly.WidgetDiv={};Blockly.WidgetDiv.DIV=null;Blockly.WidgetDiv.owner_=null;Blockly.WidgetDiv.dispose_=null;Blockly.WidgetDiv.createDom=function(){Blockly.WidgetDiv.DIV||(Blockly.WidgetDiv.DIV=goog.dom.createDom("DIV","blocklyWidgetDiv"),document.body.appendChild(Blockly.WidgetDiv.DIV))};
Blockly.WidgetDiv.show=function(a,b,c){Blockly.WidgetDiv.hide();Blockly.WidgetDiv.owner_=a;Blockly.WidgetDiv.dispose_=c;a=goog.style.getViewportPageOffset(document);Blockly.WidgetDiv.DIV.style.top=a.y+"px";Blockly.WidgetDiv.DIV.style.direction=b?"rtl":"ltr";Blockly.WidgetDiv.DIV.style.display="block"};
Blockly.WidgetDiv.hide=function(){Blockly.WidgetDiv.owner_&&(Blockly.WidgetDiv.owner_=null,Blockly.WidgetDiv.DIV.style.display="none",Blockly.WidgetDiv.DIV.style.left="",Blockly.WidgetDiv.DIV.style.top="",Blockly.WidgetDiv.dispose_&&Blockly.WidgetDiv.dispose_(),Blockly.WidgetDiv.dispose_=null,goog.dom.removeChildren(Blockly.WidgetDiv.DIV))};Blockly.WidgetDiv.isVisible=function(){return!!Blockly.WidgetDiv.owner_};Blockly.WidgetDiv.hideIfOwner=function(a){Blockly.WidgetDiv.owner_==a&&Blockly.WidgetDiv.hide()};
Blockly.WidgetDiv.position=function(a,b,c,d,e){b<d.y&&(b=d.y);e?a>c.width+d.x&&(a=c.width+d.x):a<d.x&&(a=d.x);Blockly.WidgetDiv.DIV.style.left=a+"px";Blockly.WidgetDiv.DIV.style.top=b+"px";Blockly.WidgetDiv.DIV.style.height=c.height+"px"};Blockly.inject=function(a,b){goog.isString(a)&&(a=document.getElementById(a)||document.querySelector(a));if(!goog.dom.contains(document,a))throw"Error: container is not in current document.";var c=new Blockly.Options(b||{}),d=goog.dom.createDom("div","injectionDiv");a.appendChild(d);d=Blockly.createDom_(d,c);c=Blockly.createMainWorkspace_(d,c);Blockly.init_(c);c.markFocused();Blockly.bindEvent_(d,"focus",c,c.markFocused);Blockly.svgResize(c);return c};
Blockly.WidgetDiv.position=function(a,b,c,d,e){b<d.y&&(b=d.y);e?a>c.width+d.x&&(a=c.width+d.x):a<d.x&&(a=d.x);Blockly.WidgetDiv.DIV.style.left=a+"px";Blockly.WidgetDiv.DIV.style.top=b+"px";Blockly.WidgetDiv.DIV.style.height=c.height+"px"};Blockly.inject=function(a,b){goog.isString(a)&&(a=document.getElementById(a)||document.querySelector(a));if(!goog.dom.contains(document,a))throw"Error: container is not in current document.";var c=new Blockly.Options(b||{}),d=goog.dom.createDom("div","injectionDiv");a.appendChild(d);d=Blockly.createDom_(d,c);c=Blockly.createMainWorkspace_(d,c);Blockly.init_(c);c.markFocused();Blockly.bindEventWithChecks_(d,"focus",c,c.markFocused);Blockly.svgResize(c);return c};
Blockly.createDom_=function(a,b){a.setAttribute("dir","LTR");goog.ui.Component.setDefaultRightToLeft(b.RTL);Blockly.Css.inject(b.hasCss,b.pathToMedia);var c=Blockly.createSvgElement("svg",{xmlns:"http://www.w3.org/2000/svg","xmlns:html":"http://www.w3.org/1999/xhtml","xmlns:xlink":"http://www.w3.org/1999/xlink",version:"1.1","class":"blocklySvg"},a),d=Blockly.createSvgElement("defs",{},c),e=String(Math.random()).substring(2),f=Blockly.createSvgElement("filter",{id:"blocklyEmbossFilter"+e},d);Blockly.createSvgElement("feGaussianBlur",
{"in":"SourceAlpha",stdDeviation:1,result:"blur"},f);var g=Blockly.createSvgElement("feSpecularLighting",{"in":"blur",surfaceScale:1,specularConstant:.5,specularExponent:10,"lighting-color":"white",result:"specOut"},f);Blockly.createSvgElement("fePointLight",{x:-5E3,y:-1E4,z:2E4},g);Blockly.createSvgElement("feComposite",{"in":"specOut",in2:"SourceAlpha",operator:"in",result:"specOut"},f);Blockly.createSvgElement("feComposite",{"in":"SourceGraphic",in2:"specOut",operator:"arithmetic",k1:0,k2:1,k3:1,
k4:0},f);b.embossFilterId=f.id;f=Blockly.createSvgElement("pattern",{id:"blocklyDisabledPattern"+e,patternUnits:"userSpaceOnUse",width:10,height:10},d);Blockly.createSvgElement("rect",{width:10,height:10,fill:"#aaa"},f);Blockly.createSvgElement("path",{d:"M 0 0 L 10 10 M 10 0 L 0 10",stroke:"#cc0"},f);b.disabledPatternId=f.id;d=Blockly.createSvgElement("pattern",{id:"blocklyGridPattern"+e,patternUnits:"userSpaceOnUse"},d);0<b.gridOptions.length&&0<b.gridOptions.spacing&&(Blockly.createSvgElement("line",
{stroke:b.gridOptions.colour},d),1<b.gridOptions.length&&Blockly.createSvgElement("line",{stroke:b.gridOptions.colour},d));b.gridPattern=d;return c};
Blockly.createMainWorkspace_=function(a,b){b.parentWorkspace=null;var c=new Blockly.WorkspaceSvg(b);c.scale=b.zoomOptions.startScale;a.appendChild(c.createDom("blocklyMainBackground"));c.translate(0,0);c.markFocused();b.readOnly||b.hasScrollbars||c.addChangeListener(function(){if(Blockly.dragMode_==Blockly.DRAG_NONE){var a=c.getMetrics(),e=a.viewLeft+a.absoluteLeft,f=a.viewTop+a.absoluteTop;if(a.contentTop<f||a.contentTop+a.contentHeight>a.viewHeight+f||a.contentLeft<(b.RTL?a.viewLeft:e)||a.contentLeft+
a.contentWidth>(b.RTL?a.viewWidth:a.viewWidth+e))for(var g=c.getTopBlocks(!1),h=0,k;k=g[h];h++){var m=k.getRelativeToSurfaceXY(),p=k.getHeightWidth(),l=f+25-p.height-m.y;0<l&&k.moveBy(0,l);l=f+a.viewHeight-25-m.y;0>l&&k.moveBy(0,l);l=25+e-m.x-(b.RTL?0:p.width);0<l&&k.moveBy(l,0);m=e+a.viewWidth-25-m.x+(b.RTL?p.width:0);0>m&&k.moveBy(m,0)}}});Blockly.svgResize(c);Blockly.WidgetDiv.createDom();Blockly.Tooltip.createDom();return c};
Blockly.init_=function(a){var b=a.options,c=a.getParentSvg();Blockly.bindEvent_(c,"contextmenu",null,function(a){Blockly.isTargetInput_(a)||a.preventDefault()});c=Blockly.bindEvent_(window,"resize",null,function(){Blockly.hideChaff(!0);Blockly.svgResize(a)});a.setResizeHandlerWrapper(c);Blockly.inject.bindDocumentEvents_();b.languageTree&&(a.toolbox_?a.toolbox_.init(a):a.flyout_&&(a.flyout_.init(a),a.flyout_.show(b.languageTree.childNodes),a.flyout_.scrollToStart(),a.scrollX=a.flyout_.width_,b.toolboxPosition==
Blockly.TOOLBOX_AT_RIGHT&&(a.scrollX*=-1),a.translate(a.scrollX,0)));b.hasScrollbars&&(a.scrollbar=new Blockly.ScrollbarPair(a),a.scrollbar.resize());b.hasSounds&&Blockly.inject.loadSounds_(b.pathToMedia,a)};
Blockly.inject.bindDocumentEvents_=function(){Blockly.documentEventsBound_||(Blockly.bindEvent_(document,"keydown",null,Blockly.onKeyDown_),Blockly.bindEvent_(document,"touchend",null,Blockly.longStop_),Blockly.bindEvent_(document,"touchcancel",null,Blockly.longStop_),document.addEventListener("mouseup",Blockly.onMouseUp_,!1),goog.userAgent.IPAD&&Blockly.bindEvent_(window,"orientationchange",document,function(){Blockly.svgResize(Blockly.getMainWorkspace())}));Blockly.documentEventsBound_=!0};
Blockly.inject.loadSounds_=function(a,b){b.loadAudio_([a+"click.mp3",a+"click.wav",a+"click.ogg"],"click");b.loadAudio_([a+"disconnect.wav",a+"disconnect.mp3",a+"disconnect.ogg"],"disconnect");b.loadAudio_([a+"delete.mp3",a+"delete.ogg",a+"delete.wav"],"delete");var c=[],d=function(){for(;c.length;)Blockly.unbindEvent_(c.pop());b.preloadAudio_()};c.push(Blockly.bindEvent_(document,"mousemove",null,d));c.push(Blockly.bindEvent_(document,"touchstart",null,d))};
Blockly.init_=function(a){var b=a.options,c=a.getParentSvg();Blockly.bindEventWithChecks_(c,"contextmenu",null,function(a){Blockly.isTargetInput_(a)||a.preventDefault()});c=Blockly.bindEventWithChecks_(window,"resize",null,function(){Blockly.hideChaff(!0);Blockly.svgResize(a)});a.setResizeHandlerWrapper(c);Blockly.inject.bindDocumentEvents_();b.languageTree&&(a.toolbox_?a.toolbox_.init(a):a.flyout_&&(a.flyout_.init(a),a.flyout_.show(b.languageTree.childNodes),a.flyout_.scrollToStart(),a.scrollX=a.flyout_.width_,
b.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT&&(a.scrollX*=-1),a.translate(a.scrollX,0)));b.hasScrollbars&&(a.scrollbar=new Blockly.ScrollbarPair(a),a.scrollbar.resize());b.hasSounds&&Blockly.inject.loadSounds_(b.pathToMedia,a)};
Blockly.inject.bindDocumentEvents_=function(){Blockly.documentEventsBound_||(Blockly.bindEventWithChecks_(document,"keydown",null,Blockly.onKeyDown_),Blockly.bindEventWithChecks_(document,"touchend",null,Blockly.longStop_),Blockly.bindEventWithChecks_(document,"touchcancel",null,Blockly.longStop_),document.addEventListener("mouseup",Blockly.onMouseUp_,!1),goog.userAgent.IPAD&&Blockly.bindEventWithChecks_(window,"orientationchange",document,function(){Blockly.svgResize(Blockly.getMainWorkspace())}));
Blockly.documentEventsBound_=!0};
Blockly.inject.loadSounds_=function(a,b){b.loadAudio_([a+"click.mp3",a+"click.wav",a+"click.ogg"],"click");b.loadAudio_([a+"disconnect.wav",a+"disconnect.mp3",a+"disconnect.ogg"],"disconnect");b.loadAudio_([a+"delete.mp3",a+"delete.ogg",a+"delete.wav"],"delete");var c=[],d=function(){for(;c.length;)Blockly.unbindEvent_(c.pop());b.preloadAudio_()};c.push(Blockly.bindEventWithChecks_(document,"mousemove",null,d,!0));c.push(Blockly.bindEventWithChecks_(document,"touchstart",null,d,!0))};
Blockly.updateToolbox=function(a){console.warn("Deprecated call to Blockly.updateToolbox, use workspace.updateToolbox instead.");Blockly.getMainWorkspace().updateToolbox(a)};Blockly.utils={};Blockly.addClass_=function(a,b){var c=a.getAttribute("class")||"";-1==(" "+c+" ").indexOf(" "+b+" ")&&(c&&(c+=" "),a.setAttribute("class",c+b))};Blockly.removeClass_=function(a,b){var c=a.getAttribute("class");if(-1!=(" "+c+" ").indexOf(" "+b+" ")){for(var c=c.split(/\s+/),d=0;d<c.length;d++)c[d]&&c[d]!=b||(c.splice(d,1),d--);c.length?a.setAttribute("class",c.join(" ")):a.removeAttribute("class")}};
Blockly.hasClass_=function(a,b){return-1!=(" "+a.getAttribute("class")+" ").indexOf(" "+b+" ")};Blockly.bindEvent_=function(a,b,c,d){var e=c?function(a){d.call(c,a)}:d;a.addEventListener(b,e,!1);var f=[[a,b,e]];if(b in Blockly.bindEvent_.TOUCH_MAP)for(var e=function(a){if(1==a.changedTouches.length){var b=a.changedTouches[0];a.clientX=b.clientX;a.clientY=b.clientY}d.call(c,a);a.preventDefault()},g=0,h;h=Blockly.bindEvent_.TOUCH_MAP[b][g];g++)a.addEventListener(h,e,!1),f.push([a,h,e]);return f};
Blockly.bindEvent_.TOUCH_MAP={};goog.events.BrowserFeature.TOUCH_ENABLED&&(Blockly.bindEvent_.TOUCH_MAP={mousedown:["touchstart"],mousemove:["touchmove"],mouseup:["touchend","touchcancel"]});Blockly.unbindEvent_=function(a){for(;a.length;){var b=a.pop(),c=b[2];b[0].removeEventListener(b[1],c,!1)}return c};Blockly.noEvent=function(a){a.preventDefault();a.stopPropagation()};
Blockly.isTargetInput_=function(a){return"textarea"==a.target.type||"text"==a.target.type||"number"==a.target.type||"email"==a.target.type||"password"==a.target.type||"search"==a.target.type||"tel"==a.target.type||"url"==a.target.type||a.target.isContentEditable};
Blockly.hasClass_=function(a,b){return-1!=(" "+a.getAttribute("class")+" ").indexOf(" "+b+" ")};
Blockly.bindEventWithChecks_=function(a,b,c,d,e){var f=!1,g=function(a){var b=!e;a=Blockly.Touch.splitEventByTouches(a);for(var g=0,h;h=a[g];g++)if(!b||Blockly.Touch.shouldHandleEvent(h))Blockly.Touch.setClientFromTouch(h),c?d.call(c,h):d(h),f=!0};a.addEventListener(b,g,!1);var h=[[a,b,g]];if(b in Blockly.Touch.TOUCH_MAP)for(var k=function(a){g(a);f&&a.preventDefault()},m=0,p;p=Blockly.Touch.TOUCH_MAP[b][m];m++)a.addEventListener(p,k,!1),h.push([a,p,k]);return h};
Blockly.bindEvent_=function(a,b,c,d){var e=function(a){c?d.call(c,a):d(a)};a.addEventListener(b,e,!1);var f=[[a,b,e]];if(b in Blockly.Touch.TOUCH_MAP)for(var g=function(a){if(1==a.changedTouches.length){var b=a.changedTouches[0];a.clientX=b.clientX;a.clientY=b.clientY}e(a);a.preventDefault()},h=0,k;k=Blockly.Touch.TOUCH_MAP[b][h];h++)a.addEventListener(k,g,!1),f.push([a,k,g]);return f};Blockly.unbindEvent_=function(a){for(;a.length;){var b=a.pop(),c=b[2];b[0].removeEventListener(b[1],c,!1)}return c};
Blockly.noEvent=function(a){a.preventDefault();a.stopPropagation()};Blockly.isTargetInput_=function(a){return"textarea"==a.target.type||"text"==a.target.type||"number"==a.target.type||"email"==a.target.type||"password"==a.target.type||"search"==a.target.type||"tel"==a.target.type||"url"==a.target.type||a.target.isContentEditable};
Blockly.getRelativeXY_=function(a){var b=new goog.math.Coordinate(0,0),c=a.getAttribute("x");c&&(b.x=parseInt(c,10));if(c=a.getAttribute("y"))b.y=parseInt(c,10);if(a=(a=a.getAttribute("transform"))&&a.match(Blockly.getRelativeXY_.XY_REGEXP_))b.x+=parseFloat(a[1]),a[3]&&(b.y+=parseFloat(a[3]));return b};Blockly.getRelativeXY_.XY_REGEXP_=/translate\(\s*([-+\d.e]+)([ ,]\s*([-+\d.e]+)\s*\))?/;
Blockly.getSvgXY_=function(a,b){var c=0,d=0,e=1;if(goog.dom.contains(b.getCanvas(),a)||goog.dom.contains(b.getBubbleCanvas(),a))e=b.scale;do{var f=Blockly.getRelativeXY_(a);if(a==b.getCanvas()||a==b.getBubbleCanvas())e=1;c+=f.x*e;d+=f.y*e;a=a.parentNode}while(a&&a!=b.getParentSvg());return new goog.math.Coordinate(c,d)};
Blockly.createSvgElement=function(a,b,c,d){a=document.createElementNS(Blockly.SVG_NS,a);for(var e in b)a.setAttribute(e,b[e]);document.body.runtimeStyle&&(a.runtimeStyle=a.currentStyle=a.style);c&&c.appendChild(a);return a};Blockly.isRightButton=function(a){return a.ctrlKey&&goog.userAgent.MAC?!0:2==a.button};Blockly.mouseToSvg=function(a,b,c){var d=b.createSVGPoint();d.x=a.clientX;d.y=a.clientY;c||(c=b.getScreenCTM().inverse());return d.matrixTransform(c)};
Blockly.shortestStringLength=function(a){if(!a.length)return 0;for(var b=a[0].length,c=1;c<a.length;c++)b=Math.min(b,a[c].length);return b};Blockly.commonWordPrefix=function(a,b){if(!a.length)return 0;if(1==a.length)return a[0].length;for(var c=0,d=b||Blockly.shortestStringLength(a),e=0;e<d;e++){for(var f=a[0][e],g=1;g<a.length;g++)if(f!=a[g][e])return c;" "==f&&(c=e+1)}for(g=1;g<a.length;g++)if((f=a[g][e])&&" "!=f)return c;return d};
Blockly.commonWordSuffix=function(a,b){if(!a.length)return 0;if(1==a.length)return a[0].length;for(var c=0,d=b||Blockly.shortestStringLength(a),e=0;e<d;e++){for(var f=a[0].substr(-e-1,1),g=1;g<a.length;g++)if(f!=a[g].substr(-e-1,1))return c;" "==f&&(c=e+1)}for(g=1;g<a.length;g++)if((f=a[g].charAt(a[g].length-e-1))&&" "!=f)return c;return d};Blockly.isNumber=function(a){return!!a.match(/^\s*-?\d+(\.\d+)?\s*$/)};
Blockly.utils.tokenizeInterpolation=function(a){var b=[];a=a.split("");a.push("");for(var c=0,d=[],e=null,f=0;f<a.length;f++){var g=a[f];0==c?"%"==g?c=1:d.push(g):1==c?"%"==g?(d.push(g),c=0):"0"<=g&&"9">=g?(c=2,e=g,(g=d.join(""))&&b.push(g),d.length=0):(d.push("%",g),c=0):2==c&&("0"<=g&&"9">=g?e+=g:(b.push(parseInt(e,10)),f--,c=0))}(g=d.join(""))&&b.push(g);return b};
Blockly.genUid=function(){for(var a=Blockly.genUid.soup_.length,b=[],c=0;20>c;c++)b[c]=Blockly.genUid.soup_.charAt(Math.random()*a);return b.join("")};Blockly.genUid.soup_="!#%()*+,-./:;=?@[]^_`{|}~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Blockly.utils.wrap=function(a,b){for(var c=a.split("\n"),d=0;d<c.length;d++)c[d]=Blockly.utils.wrap_line_(c[d],b);return c.join("\n")};
Blockly.genUid=function(){for(var a=Blockly.genUid.soup_.length,b=[],c=0;20>c;c++)b[c]=Blockly.genUid.soup_.charAt(Math.random()*a);return b.join("")};Blockly.genUid.soup_="!#$%()*+,-./:;=?@[]^_`{|}~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Blockly.utils.wrap=function(a,b){for(var c=a.split("\n"),d=0;d<c.length;d++)c[d]=Blockly.utils.wrap_line_(c[d],b);return c.join("\n")};
Blockly.utils.wrap_line_=function(a,b){if(a.length<=b)return a;for(var c=a.trim().split(/\s+/),d=0;d<c.length;d++)c[d].length>b&&(b=c[d].length);var e,d=-Infinity,f,g=1;do{e=d;f=a;for(var h=[],k=c.length/g,m=1,d=0;d<c.length-1;d++)m<(d+1.5)/k?(m++,h[d]=!0):h[d]=!1;h=Blockly.utils.wrapMutate_(c,h,b);d=Blockly.utils.wrapScore_(c,h,b);a=Blockly.utils.wrapToText_(c,h);g++}while(d>e);return f};
Blockly.utils.wrapScore_=function(a,b,c){for(var d=[0],e=[],f=0;f<a.length;f++)d[d.length-1]+=a[f].length,!0===b[f]?(d.push(0),e.push(a[f].charAt(a[f].length-1))):!1===b[f]&&d[d.length-1]++;a=Math.max.apply(Math,d);for(f=b=0;f<d.length;f++)b-=2*Math.pow(Math.abs(c-d[f]),1.5),b-=Math.pow(a-d[f],1.5),-1!=".?!".indexOf(e[f])?b+=c/3:-1!=",;)]}".indexOf(e[f])&&(b+=c/4);1<d.length&&d[d.length-1]<=d[d.length-2]&&(b+=.5);return b};
Blockly.utils.wrapMutate_=function(a,b,c){for(var d=Blockly.utils.wrapScore_(a,b,c),e,f=0;f<b.length-1;f++)if(b[f]!=b[f+1]){var g=[].concat(b);g[f]=!g[f];g[f+1]=!g[f+1];var h=Blockly.utils.wrapScore_(a,g,c);h>d&&(d=h,e=g)}return e?Blockly.utils.wrapMutate_(a,e,c):b};Blockly.utils.wrapToText_=function(a,b){for(var c=[],d=0;d<a.length;d++)c.push(a[d]),void 0!==b[d]&&c.push(b[d]?"\n":" ");return c.join("")};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.highlightedConnection_=null;Blockly.localConnection_=null;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.dragMode_=Blockly.DRAG_NONE;Blockly.onTouchUpWrapper_=null;Blockly.hueToRgb=function(a){return goog.color.hsvToHex(a,Blockly.HSV_SATURATION,255*Blockly.HSV_VALUE)};Blockly.svgSize=function(a){return{width:a.cachedWidth_,height:a.cachedHeight_}};
Blockly.resizeSvgContents=function(a){a.resizeContents()};Blockly.svgResize=function(a){for(;a.options.parentWorkspace;)a=a.options.parentWorkspace;var b=a.getParentSvg(),c=b.parentNode;if(c){var d=c.offsetWidth,c=c.offsetHeight;b.cachedWidth_!=d&&(b.setAttribute("width",d+"px"),b.cachedWidth_=d);b.cachedHeight_!=c&&(b.setAttribute("height",c+"px"),b.cachedHeight_=c);a.resize()}};
Blockly.onMouseUp_=function(a){a=Blockly.getMainWorkspace();Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);a.dragMode_=Blockly.DRAG_NONE;Blockly.onTouchUpWrapper_&&(Blockly.unbindEvent_(Blockly.onTouchUpWrapper_),Blockly.onTouchUpWrapper_=null);Blockly.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.onMouseMoveWrapper_),Blockly.onMouseMoveWrapper_=null)};
Blockly.onMouseMove_=function(a){if(!(a.touches&&2<=a.touches.length)){var b=Blockly.getMainWorkspace();if(b.dragMode_!=Blockly.DRAG_NONE){var c=a.clientX-b.startDragMouseX,d=a.clientY-b.startDragMouseY,e=b.startDragMetrics,f=b.startScrollX+c,g=b.startScrollY+d,f=Math.min(f,-e.contentLeft),g=Math.min(g,-e.contentTop),f=Math.max(f,e.viewWidth-e.contentLeft-e.contentWidth),g=Math.max(g,e.viewHeight-e.contentTop-e.contentHeight);b.scrollbar.set(-f-e.contentLeft,-g-e.contentTop);Math.sqrt(c*c+d*d)>Blockly.DRAG_RADIUS&&
(Blockly.longStop_(),b.dragMode_=Blockly.DRAG_FREE);a.stopPropagation();a.preventDefault()}}};
Blockly.utils.wrapMutate_=function(a,b,c){for(var d=Blockly.utils.wrapScore_(a,b,c),e,f=0;f<b.length-1;f++)if(b[f]!=b[f+1]){var g=[].concat(b);g[f]=!g[f];g[f+1]=!g[f+1];var h=Blockly.utils.wrapScore_(a,g,c);h>d&&(d=h,e=g)}return e?Blockly.utils.wrapMutate_(a,e,c):b};Blockly.utils.wrapToText_=function(a,b){for(var c=[],d=0;d<a.length;d++)c.push(a[d]),void 0!==b[d]&&c.push(b[d]?"\n":" ");return c.join("")};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.highlightedConnection_=null;Blockly.localConnection_=null;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.dragMode_=Blockly.DRAG_NONE;Blockly.hueToRgb=function(a){return goog.color.hsvToHex(a,Blockly.HSV_SATURATION,255*Blockly.HSV_VALUE)};Blockly.svgSize=function(a){return{width:a.cachedWidth_,height:a.cachedHeight_}};Blockly.resizeSvgContents=function(a){a.resizeContents()};
Blockly.svgResize=function(a){for(;a.options.parentWorkspace;)a=a.options.parentWorkspace;var b=a.getParentSvg(),c=b.parentNode;if(c){var d=c.offsetWidth,c=c.offsetHeight;b.cachedWidth_!=d&&(b.setAttribute("width",d+"px"),b.cachedWidth_=d);b.cachedHeight_!=c&&(b.setAttribute("height",c+"px"),b.cachedHeight_=c);a.resize()}};
Blockly.onKeyDown_=function(a){if(!Blockly.mainWorkspace.options.readOnly&&!Blockly.isTargetInput_(a)){var b=!1;if(27==a.keyCode)Blockly.hideChaff();else if(8==a.keyCode||46==a.keyCode)a.preventDefault(),Blockly.selected&&Blockly.selected.isDeletable()&&(b=!0);else if(a.altKey||a.ctrlKey||a.metaKey)Blockly.selected&&Blockly.selected.isDeletable()&&Blockly.selected.isMovable()&&(67==a.keyCode?(Blockly.hideChaff(),Blockly.copy_(Blockly.selected)):88==a.keyCode&&(Blockly.copy_(Blockly.selected),b=!0)),
86==a.keyCode?Blockly.clipboardXml_&&(Blockly.Events.setGroup(!0),Blockly.clipboardSource_.paste(Blockly.clipboardXml_),Blockly.Events.setGroup(!1)):90==a.keyCode&&(Blockly.hideChaff(),Blockly.mainWorkspace.undo(a.shiftKey));b&&(Blockly.Events.setGroup(!0),Blockly.hideChaff(),Blockly.selected.dispose(Blockly.dragMode_!=Blockly.DRAG_FREE,!0),Blockly.highlightedConnection_&&(Blockly.highlightedConnection_.unhighlight(),Blockly.highlightedConnection_=null),Blockly.Events.setGroup(!1))}};
Blockly.terminateDrag_=function(){Blockly.BlockSvg.terminateDrag();Blockly.Flyout.terminateDrag_()};Blockly.longPid_=0;Blockly.longStart_=function(a,b){Blockly.longStop_();Blockly.longPid_=setTimeout(function(){a.button=2;b.onMouseDown_(a)},Blockly.LONGPRESS)};Blockly.longStop_=function(){Blockly.longPid_&&(clearTimeout(Blockly.longPid_),Blockly.longPid_=0)};
Blockly.copy_=function(a){var b=Blockly.Xml.blockToDom(a);Blockly.dragMode_!=Blockly.DRAG_FREE&&Blockly.Xml.deleteNext(b);var c=a.getRelativeToSurfaceXY();b.setAttribute("x",a.RTL?-c.x:c.x);b.setAttribute("y",c.y);Blockly.clipboardXml_=b;Blockly.clipboardSource_=a.workspace};Blockly.duplicate_=function(a){var b=Blockly.clipboardXml_,c=Blockly.clipboardSource_;Blockly.copy_(a);a.workspace.paste(Blockly.clipboardXml_);Blockly.clipboardXml_=b;Blockly.clipboardSource_=c};
Blockly.onContextMenu_=function(a){Blockly.isTargetInput_(a)||a.preventDefault()};Blockly.hideChaff=function(a){Blockly.Tooltip.hide();Blockly.WidgetDiv.hide();a||(a=Blockly.getMainWorkspace(),a.toolbox_&&a.toolbox_.flyout_&&a.toolbox_.flyout_.autoClose&&a.toolbox_.clearSelection())};Blockly.addChangeListener=function(a){console.warn("Deprecated call to Blockly.addChangeListener, use workspace.addChangeListener instead.");return Blockly.getMainWorkspace().addChangeListener(a)};
Blockly.getMainWorkspace=function(){return Blockly.mainWorkspace};goog.global.console||(goog.global.console={log:function(){},warn:function(){}});goog.global.Blockly||(goog.global.Blockly={});goog.global.Blockly.getMainWorkspace=Blockly.getMainWorkspace;goog.global.Blockly.addChangeListener=Blockly.addChangeListener;
Blockly.terminateDrag_=function(){Blockly.BlockSvg.terminateDrag();Blockly.Flyout.terminateDrag_()};Blockly.copy_=function(a){var b=Blockly.Xml.blockToDom(a);Blockly.dragMode_!=Blockly.DRAG_FREE&&Blockly.Xml.deleteNext(b);var c=a.getRelativeToSurfaceXY();b.setAttribute("x",a.RTL?-c.x:c.x);b.setAttribute("y",c.y);Blockly.clipboardXml_=b;Blockly.clipboardSource_=a.workspace};
Blockly.duplicate_=function(a){var b=Blockly.clipboardXml_,c=Blockly.clipboardSource_;Blockly.copy_(a);a.workspace.paste(Blockly.clipboardXml_);Blockly.clipboardXml_=b;Blockly.clipboardSource_=c};Blockly.onContextMenu_=function(a){Blockly.isTargetInput_(a)||a.preventDefault()};Blockly.hideChaff=function(a){Blockly.Tooltip.hide();Blockly.WidgetDiv.hide();a||(a=Blockly.getMainWorkspace(),a.toolbox_&&a.toolbox_.flyout_&&a.toolbox_.flyout_.autoClose&&a.toolbox_.clearSelection())};
Blockly.addChangeListener=function(a){console.warn("Deprecated call to Blockly.addChangeListener, use workspace.addChangeListener instead.");return Blockly.getMainWorkspace().addChangeListener(a)};Blockly.getMainWorkspace=function(){return Blockly.mainWorkspace};goog.global.console||(goog.global.console={log:function(){},warn:function(){}});goog.global.Blockly||(goog.global.Blockly={});goog.global.Blockly.getMainWorkspace=Blockly.getMainWorkspace;goog.global.Blockly.addChangeListener=Blockly.addChangeListener;
+17 -15
View File
@@ -40,17 +40,17 @@ window.BLOCKLY_BOOT = function() {
}
goog.addDependency("../../../" + dir + "/core/block.js", ['Blockly.Block'], ['Blockly.Blocks', 'Blockly.Comment', 'Blockly.Connection', 'Blockly.Input', 'Blockly.Mutator', 'Blockly.Warning', 'Blockly.Workspace', 'Blockly.Xml', 'goog.array', 'goog.asserts', 'goog.math.Coordinate', 'goog.string']);
goog.addDependency("../../../" + dir + "/core/block_render_svg.js", ['Blockly.BlockSvg.render'], ['Blockly.BlockSvg']);
goog.addDependency("../../../" + dir + "/core/block_svg.js", ['Blockly.BlockSvg'], ['Blockly.Block', 'Blockly.ContextMenu', 'Blockly.RenderedConnection', 'goog.Timer', 'goog.asserts', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/blockly.js", ['Blockly'], ['Blockly.BlockSvg.render', 'Blockly.Events', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldTextInput', 'Blockly.FieldNumber', 'Blockly.FieldVariable', 'Blockly.Generator', 'Blockly.Msg', 'Blockly.Procedures', 'Blockly.Toolbox', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.constants', 'Blockly.inject', 'Blockly.utils', 'goog.color', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/block_svg.js", ['Blockly.BlockSvg'], ['Blockly.Block', 'Blockly.ContextMenu', 'Blockly.Touch', 'Blockly.RenderedConnection', 'goog.Timer', 'goog.asserts', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/blockly.js", ['Blockly'], ['Blockly.BlockSvg.render', 'Blockly.Events', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldTextInput', 'Blockly.FieldNumber', 'Blockly.FieldVariable', 'Blockly.Generator', 'Blockly.Msg', 'Blockly.Procedures', 'Blockly.Toolbox', 'Blockly.Touch', 'Blockly.WidgetDiv', 'Blockly.WorkspaceSvg', 'Blockly.constants', 'Blockly.inject', 'Blockly.utils', 'goog.color', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/blocks.js", ['Blockly.Blocks'], []);
goog.addDependency("../../../" + dir + "/core/bubble.js", ['Blockly.Bubble'], ['Blockly.Workspace', 'goog.dom', 'goog.math', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/bubble.js", ['Blockly.Bubble'], ['Blockly.Touch', 'Blockly.Workspace', 'goog.dom', 'goog.math', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/comment.js", ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Icon', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/connection.js", ['Blockly.Connection'], ['goog.asserts', 'goog.dom']);
goog.addDependency("../../../" + dir + "/core/connection_db.js", ['Blockly.ConnectionDB'], ['Blockly.Connection']);
goog.addDependency("../../../" + dir + "/core/constants.js", ['Blockly.constants'], []);
goog.addDependency("../../../" + dir + "/core/contextmenu.js", ['Blockly.ContextMenu'], ['goog.dom', 'goog.events', 'goog.style', 'goog.ui.Menu', 'goog.ui.MenuItem']);
goog.addDependency("../../../" + dir + "/core/css.js", ['Blockly.Css'], []);
goog.addDependency("../../../" + dir + "/core/events.js", ['Blockly.Events'], ['goog.math.Coordinate']);
goog.addDependency("../../../" + dir + "/core/events.js", ['Blockly.Events'], ['goog.array', 'goog.math.Coordinate']);
goog.addDependency("../../../" + dir + "/core/field.js", ['Blockly.Field'], ['goog.asserts', 'goog.dom', 'goog.math.Size', 'goog.style', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/field_angle.js", ['Blockly.FieldAngle'], ['Blockly.FieldTextInput', 'goog.math', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/field_checkbox.js", ['Blockly.FieldCheckbox'], ['Blockly.Field']);
@@ -62,7 +62,7 @@ goog.addDependency("../../../" + dir + "/core/field_label.js", ['Blockly.FieldLa
goog.addDependency("../../../" + dir + "/core/field_number.js", ['Blockly.FieldNumber'], ['Blockly.FieldTextInput', 'goog.math']);
goog.addDependency("../../../" + dir + "/core/field_textinput.js", ['Blockly.FieldTextInput'], ['Blockly.Field', 'Blockly.Msg', 'goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/field_variable.js", ['Blockly.FieldVariable'], ['Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.Variables', 'goog.string']);
goog.addDependency("../../../" + dir + "/core/flyout.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Comment', 'Blockly.Events', 'Blockly.FlyoutButton', 'Blockly.WorkspaceSvg', 'goog.dom', 'goog.events', 'goog.math.Rect', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/flyout.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Comment', 'Blockly.Events', 'Blockly.FlyoutButton', 'Blockly.Touch', 'Blockly.WorkspaceSvg', 'goog.dom', 'goog.events', 'goog.math.Rect', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/flyout_button.js", ['Blockly.FlyoutButton'], ['goog.dom', 'goog.math.Coordinate']);
goog.addDependency("../../../" + dir + "/core/generator.js", ['Blockly.Generator'], ['Blockly.Block', 'goog.asserts']);
goog.addDependency("../../../" + dir + "/core/icon.js", ['Blockly.Icon'], ['goog.dom', 'goog.math.Coordinate']);
@@ -75,17 +75,18 @@ goog.addDependency("../../../" + dir + "/core/options.js", ['Blockly.Options'],
goog.addDependency("../../../" + dir + "/core/procedures.js", ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.Field', 'Blockly.Names', 'Blockly.Workspace']);
goog.addDependency("../../../" + dir + "/core/rendered_connection.js", ['Blockly.RenderedConnection'], ['Blockly.Connection']);
goog.addDependency("../../../" + dir + "/core/scrollbar.js", ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['goog.dom', 'goog.events']);
goog.addDependency("../../../" + dir + "/core/toolbox.js", ['Blockly.Toolbox'], ['Blockly.Flyout', 'goog.dom', 'goog.dom.TagName', 'goog.events', 'goog.events.BrowserFeature', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.math.Rect', 'goog.style', 'goog.ui.tree.TreeControl', 'goog.ui.tree.TreeNode']);
goog.addDependency("../../../" + dir + "/core/toolbox.js", ['Blockly.Toolbox'], ['Blockly.Flyout', 'Blockly.Touch', 'goog.dom', 'goog.dom.TagName', 'goog.events', 'goog.events.BrowserFeature', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.math.Rect', 'goog.style', 'goog.ui.tree.TreeControl', 'goog.ui.tree.TreeNode']);
goog.addDependency("../../../" + dir + "/core/tooltip.js", ['Blockly.Tooltip'], ['goog.dom', 'goog.dom.TagName']);
goog.addDependency("../../../" + dir + "/core/touch.js", ['Blockly.Touch'], ['goog.events', 'goog.events.BrowserFeature', 'goog.string']);
goog.addDependency("../../../" + dir + "/core/trashcan.js", ['Blockly.Trashcan'], ['goog.Timer', 'goog.dom', 'goog.math', 'goog.math.Rect']);
goog.addDependency("../../../" + dir + "/core/utils.js", ['Blockly.utils'], ['goog.dom', 'goog.events.BrowserFeature', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/utils.js", ['Blockly.utils'], ['Blockly.Touch', 'goog.dom', 'goog.events.BrowserFeature', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/variables.js", ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.Workspace', 'goog.string']);
goog.addDependency("../../../" + dir + "/core/warning.js", ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Icon']);
goog.addDependency("../../../" + dir + "/core/widgetdiv.js", ['Blockly.WidgetDiv'], ['Blockly.Css', 'goog.dom', 'goog.dom.TagName', 'goog.style']);
goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['goog.math']);
goog.addDependency("../../../" + dir + "/core/workspace_svg.js", ['Blockly.WorkspaceSvg'], ['Blockly.ConnectionDB', 'Blockly.constants', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Trashcan', 'Blockly.Workspace', 'Blockly.Xml', 'Blockly.ZoomControls', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['goog.array', 'goog.math']);
goog.addDependency("../../../" + dir + "/core/workspace_svg.js", ['Blockly.WorkspaceSvg'], ['Blockly.ConnectionDB', 'Blockly.constants', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Touch', 'Blockly.Trashcan', 'Blockly.Workspace', 'Blockly.Xml', 'Blockly.ZoomControls', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
goog.addDependency("../../../" + dir + "/core/xml.js", ['Blockly.Xml'], ['goog.asserts', 'goog.dom']);
goog.addDependency("../../../" + dir + "/core/zoom_controls.js", ['Blockly.ZoomControls'], ['goog.dom']);
goog.addDependency("../../../" + dir + "/core/zoom_controls.js", ['Blockly.ZoomControls'], ['Blockly.Touch', 'goog.dom']);
goog.addDependency("../../alltests.js", [], []);
goog.addDependency("../../browser_capabilities.js", [], []);
goog.addDependency("../../protractor.conf.js", [], []);
@@ -594,11 +595,11 @@ goog.addDependency("html/uncheckedconversions.js", ['goog.html.uncheckedconversi
goog.addDependency("html/uncheckedconversions_test.js", ['goog.html.uncheckedconversionsTest'], ['goog.html.SafeHtml', 'goog.html.SafeScript', 'goog.html.SafeStyle', 'goog.html.SafeStyleSheet', 'goog.html.SafeUrl', 'goog.html.TrustedResourceUrl', 'goog.html.uncheckedconversions', 'goog.i18n.bidi.Dir', 'goog.string.Const', 'goog.testing.jsunit']);
goog.addDependency("html/utils.js", ['goog.html.utils'], ['goog.string']);
goog.addDependency("html/utils_test.js", ['goog.html.UtilsTest'], ['goog.array', 'goog.dom.TagName', 'goog.html.utils', 'goog.object', 'goog.testing.jsunit']);
goog.addDependency("html/sanitizer/attributewhitelist.js", ['goog.html.sanitizer.AttributeWhitelist'], []);
goog.addDependency("html/sanitizer/attributewhitelist.js", ['goog.html.sanitizer.AttributeSanitizedWhitelist', 'goog.html.sanitizer.AttributeWhitelist'], []);
goog.addDependency("html/sanitizer/csssanitizer.js", ['goog.html.sanitizer.CssSanitizer'], ['goog.array', 'goog.html.SafeStyle', 'goog.html.uncheckedconversions', 'goog.object', 'goog.string']);
goog.addDependency("html/sanitizer/csssanitizer_test.js", [], ['goog.array', 'goog.html.SafeStyle', 'goog.html.SafeUrl', 'goog.html.sanitizer.CssSanitizer', 'goog.string', 'goog.testing.jsunit', 'goog.userAgent', 'goog.userAgent.product']);
goog.addDependency("html/sanitizer/htmlsanitizer.js", ['goog.html.sanitizer.HtmlSanitizer', 'goog.html.sanitizer.HtmlSanitizer.Builder', 'goog.html.sanitizer.HtmlSanitizerPolicy', 'goog.html.sanitizer.HtmlSanitizerPolicyContext', 'goog.html.sanitizer.HtmlSanitizerPolicyHints'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.functions', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.html.SafeUrl', 'goog.html.sanitizer.AttributeWhitelist', 'goog.html.sanitizer.CssSanitizer', 'goog.html.sanitizer.TagBlacklist', 'goog.html.sanitizer.TagWhitelist', 'goog.html.uncheckedconversions', 'goog.object', 'goog.string', 'goog.string.Const', 'goog.userAgent']);
goog.addDependency("html/sanitizer/htmlsanitizer_test.js", [], ['goog.dom', 'goog.html.SafeHtml', 'goog.html.SafeUrl', 'goog.html.sanitizer.HtmlSanitizer', 'goog.html.sanitizer.TagWhitelist', 'goog.html.sanitizer.unsafe', 'goog.html.testing', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent']);
goog.addDependency("html/sanitizer/htmlsanitizer.js", ['goog.html.sanitizer.HtmlSanitizer', 'goog.html.sanitizer.HtmlSanitizer.Builder', 'goog.html.sanitizer.HtmlSanitizerPolicy', 'goog.html.sanitizer.HtmlSanitizerPolicyContext', 'goog.html.sanitizer.HtmlSanitizerPolicyHints'], ['goog.array', 'goog.asserts', 'goog.dom', 'goog.dom.NodeType', 'goog.functions', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.html.SafeUrl', 'goog.html.sanitizer.AttributeSanitizedWhitelist', 'goog.html.sanitizer.AttributeWhitelist', 'goog.html.sanitizer.CssSanitizer', 'goog.html.sanitizer.TagBlacklist', 'goog.html.sanitizer.TagWhitelist', 'goog.html.uncheckedconversions', 'goog.object', 'goog.string', 'goog.string.Const', 'goog.userAgent']);
goog.addDependency("html/sanitizer/htmlsanitizer_test.js", [], ['goog.html.sanitizer.HtmlSanitizer', 'goog.html.sanitizer.TagWhitelist', 'goog.html.sanitizer.unsafe', 'goog.dom', 'goog.html.SafeHtml', 'goog.html.SafeUrl', 'goog.html.testing', 'goog.testing.dom', 'goog.testing.jsunit', 'goog.userAgent']);
goog.addDependency("html/sanitizer/tagblacklist.js", ['goog.html.sanitizer.TagBlacklist'], []);
goog.addDependency("html/sanitizer/tagwhitelist.js", ['goog.html.sanitizer.TagWhitelist'], []);
goog.addDependency("html/sanitizer/unsafe.js", ['goog.html.sanitizer.unsafe'], ['goog.asserts', 'goog.html.sanitizer.HtmlSanitizer.Builder', 'goog.string', 'goog.string.Const']);
@@ -688,7 +689,7 @@ goog.addDependency("labs/net/webchanneltransport.js", ['goog.net.WebChannelTrans
goog.addDependency("labs/net/webchanneltransportfactory.js", ['goog.net.createWebChannelTransport'], ['goog.functions', 'goog.labs.net.webChannel.WebChannelBaseTransport']);
goog.addDependency("labs/net/xhr.js", ['goog.labs.net.xhr', 'goog.labs.net.xhr.Error', 'goog.labs.net.xhr.HttpError', 'goog.labs.net.xhr.Options', 'goog.labs.net.xhr.PostData', 'goog.labs.net.xhr.ResponseType', 'goog.labs.net.xhr.TimeoutError'], ['goog.Promise', 'goog.asserts', 'goog.debug.Error', 'goog.json', 'goog.net.HttpStatus', 'goog.net.XmlHttp', 'goog.object', 'goog.string', 'goog.uri.utils', 'goog.userAgent']);
goog.addDependency("labs/net/xhr_test.js", ['goog.labs.net.xhrTest'], ['goog.Promise', 'goog.events', 'goog.events.EventType', 'goog.labs.net.xhr', 'goog.net.WrapperXmlHttpFactory', 'goog.net.XmlHttp', 'goog.testing.MockClock', 'goog.testing.TestCase', 'goog.testing.jsunit', 'goog.userAgent']);
goog.addDependency("labs/net/webchannel/basetestchannel.js", ['goog.labs.net.webChannel.BaseTestChannel'], ['goog.labs.net.webChannel.Channel', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Stat']);
goog.addDependency("labs/net/webchannel/basetestchannel.js", ['goog.labs.net.webChannel.BaseTestChannel'], ['goog.labs.net.webChannel.Channel', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.net.WebChannel']);
goog.addDependency("labs/net/webchannel/channel.js", ['goog.labs.net.webChannel.Channel'], []);
goog.addDependency("labs/net/webchannel/channelrequest.js", ['goog.labs.net.webChannel.ChannelRequest'], ['goog.Timer', 'goog.async.Throttle', 'goog.events.EventHandler', 'goog.labs.net.webChannel.Channel', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.ServerReachability', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.net.ErrorCode', 'goog.net.EventType', 'goog.net.XmlHttp', 'goog.object', 'goog.userAgent']);
goog.addDependency("labs/net/webchannel/channelrequest_test.js", ['goog.labs.net.webChannel.channelRequestTest'], ['goog.Uri', 'goog.functions', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.ServerReachability', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit', 'goog.testing.net.XhrIo', 'goog.testing.recordFunction']);
@@ -699,7 +700,7 @@ goog.addDependency("labs/net/webchannel/netutils.js", ['goog.labs.net.webChannel
goog.addDependency("labs/net/webchannel/requeststats.js", ['goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Event', 'goog.labs.net.webChannel.requestStats.ServerReachability', 'goog.labs.net.webChannel.requestStats.ServerReachabilityEvent', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.labs.net.webChannel.requestStats.StatEvent', 'goog.labs.net.webChannel.requestStats.TimingEvent'], ['goog.events.Event', 'goog.events.EventTarget']);
goog.addDependency("labs/net/webchannel/webchannelbase.js", ['goog.labs.net.webChannel.WebChannelBase'], ['goog.Uri', 'goog.array', 'goog.asserts', 'goog.debug.TextFormatter', 'goog.json', 'goog.labs.net.webChannel.BaseTestChannel', 'goog.labs.net.webChannel.Channel', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.ConnectionState', 'goog.labs.net.webChannel.ForwardChannelRequestPool', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.Wire', 'goog.labs.net.webChannel.WireV8', 'goog.labs.net.webChannel.netUtils', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.log', 'goog.net.XhrIo', 'goog.object', 'goog.string', 'goog.structs', 'goog.structs.CircularBuffer']);
goog.addDependency("labs/net/webchannel/webchannelbase_test.js", ['goog.labs.net.webChannel.webChannelBaseTest'], ['goog.Timer', 'goog.array', 'goog.dom', 'goog.functions', 'goog.json', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.ForwardChannelRequestPool', 'goog.labs.net.webChannel.WebChannelBase', 'goog.labs.net.webChannel.WebChannelBaseTransport', 'goog.labs.net.webChannel.WebChannelDebug', 'goog.labs.net.webChannel.Wire', 'goog.labs.net.webChannel.netUtils', 'goog.labs.net.webChannel.requestStats', 'goog.labs.net.webChannel.requestStats.Stat', 'goog.structs.Map', 'goog.testing.MockClock', 'goog.testing.PropertyReplacer', 'goog.testing.asserts', 'goog.testing.jsunit']);
goog.addDependency("labs/net/webchannel/webchannelbasetransport.js", ['goog.labs.net.webChannel.WebChannelBaseTransport'], ['goog.asserts', 'goog.events.EventTarget', 'goog.json', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.WebChannelBase', 'goog.log', 'goog.net.WebChannel', 'goog.net.WebChannelTransport', 'goog.object', 'goog.string.path']);
goog.addDependency("labs/net/webchannel/webchannelbasetransport.js", ['goog.labs.net.webChannel.WebChannelBaseTransport'], ['goog.asserts', 'goog.events.EventTarget', 'goog.json', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.WebChannelBase', 'goog.log', 'goog.net.WebChannel', 'goog.net.WebChannelTransport', 'goog.object', 'goog.string', 'goog.string.path']);
goog.addDependency("labs/net/webchannel/webchannelbasetransport_test.js", ['goog.labs.net.webChannel.webChannelBaseTransportTest'], ['goog.events', 'goog.functions', 'goog.json', 'goog.labs.net.webChannel.ChannelRequest', 'goog.labs.net.webChannel.WebChannelBase', 'goog.labs.net.webChannel.WebChannelBaseTransport', 'goog.net.WebChannel', 'goog.testing.PropertyReplacer', 'goog.testing.jsunit']);
goog.addDependency("labs/net/webchannel/webchanneldebug.js", ['goog.labs.net.webChannel.WebChannelDebug'], ['goog.json', 'goog.log']);
goog.addDependency("labs/net/webchannel/wire.js", ['goog.labs.net.webChannel.Wire'], []);
@@ -1646,6 +1647,7 @@ goog.require('Blockly.Scrollbar');
goog.require('Blockly.ScrollbarPair');
goog.require('Blockly.Toolbox');
goog.require('Blockly.Tooltip');
goog.require('Blockly.Touch');
goog.require('Blockly.Trashcan');
goog.require('Blockly.Variables');
goog.require('Blockly.Warning');
+13 -6
View File
@@ -300,9 +300,12 @@ Blockly.Blocks['lists_indexOf'] = {
this.appendValueInput('FIND')
.appendField(new Blockly.FieldDropdown(OPERATORS), 'END');
this.setInputsInline(true);
var tooltip = Blockly.Msg.LISTS_INDEX_OF_TOOLTIP
.replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '0' : '-1');
this.setTooltip(tooltip);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
return Blockly.Msg.LISTS_INDEX_OF_TOOLTIP.replace('%1',
this.workspace.options.oneBasedIndex ? '0' : '-1');
});
}
};
@@ -390,8 +393,11 @@ Blockly.Blocks['lists_getIndex'] = {
break;
}
if (where == 'FROM_START' || where == 'FROM_END') {
tooltip += ' ' + Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP
.replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '#1' : '#0');
var msg = (where == 'FROM_START') ?
Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP :
Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP;
tooltip += ' ' + msg.replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
}
return tooltip;
});
@@ -551,7 +557,8 @@ Blockly.Blocks['lists_setIndex'] = {
}
if (where == 'FROM_START' || where == 'FROM_END') {
tooltip += ' ' + Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP
.replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '#1' : '#0');
.replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
}
return tooltip;
});
+8 -2
View File
@@ -49,7 +49,10 @@ Blockly.Blocks['procedures_defnoreturn'] = {
.appendField(nameField, 'NAME')
.appendField('', 'PARAMS');
this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
if (Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT) {
if ((this.workspace.options.comments ||
(this.workspace.options.parentWorkspace &&
this.workspace.options.parentWorkspace.options.comments)) &&
Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT) {
this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);
}
this.setColour(Blockly.Blocks.procedures.HUE);
@@ -343,7 +346,10 @@ Blockly.Blocks['procedures_defreturn'] = {
.setAlign(Blockly.ALIGN_RIGHT)
.appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);
this.setMutator(new Blockly.Mutator(['procedures_mutatorarg']));
if (Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT) {
if ((this.workspace.options.comments ||
(this.workspace.options.parentWorkspace &&
this.workspace.options.parentWorkspace.options.comments)) &&
Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT) {
this.setCommentText(Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT);
}
this.setColour(Blockly.Blocks.procedures.HUE);
+11 -5
View File
@@ -322,9 +322,12 @@ Blockly.Blocks['text_indexOf'] = {
this.appendDummyInput().appendField(Blockly.Msg.TEXT_INDEXOF_TAIL);
}
this.setInputsInline(true);
var tooltip = Blockly.Msg.TEXT_INDEXOF_TOOLTIP
.replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '0' : '-1');
this.setTooltip(tooltip);
// Assign 'this' to a variable for use in the tooltip closure below.
var thisBlock = this;
this.setTooltip(function() {
return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
});
}
};
@@ -355,8 +358,11 @@ Blockly.Blocks['text_charAt'] = {
var where = thisBlock.getFieldValue('WHERE');
var tooltip = Blockly.Msg.TEXT_CHARAT_TOOLTIP;
if (where == 'FROM_START' || where == 'FROM_END') {
tooltip += ' ' + Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP
.replace('%1', Blockly.Blocks.ONE_BASED_INDEXING ? '#1' : '#0');
var msg = (where == 'FROM_START') ?
Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP :
Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP;
tooltip += ' ' + msg.replace('%1',
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
}
return tooltip;
});
+24 -22
View File
@@ -18,19 +18,19 @@ Blockly.Blocks.lists_create_with_item={init:function(){this.setColour(Blockly.Bl
Blockly.Blocks.lists_repeat={init:function(){this.jsonInit({message0:Blockly.Msg.LISTS_REPEAT_TITLE,args0:[{type:"input_value",name:"ITEM"},{type:"input_value",name:"NUM",check:"Number"}],output:"Array",colour:Blockly.Blocks.lists.HUE,tooltip:Blockly.Msg.LISTS_REPEAT_TOOLTIP,helpUrl:Blockly.Msg.LISTS_REPEAT_HELPURL})}};
Blockly.Blocks.lists_length={init:function(){this.jsonInit({message0:Blockly.Msg.LISTS_LENGTH_TITLE,args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",colour:Blockly.Blocks.lists.HUE,tooltip:Blockly.Msg.LISTS_LENGTH_TOOLTIP,helpUrl:Blockly.Msg.LISTS_LENGTH_HELPURL})}};
Blockly.Blocks.lists_isEmpty={init:function(){this.jsonInit({message0:Blockly.Msg.LISTS_ISEMPTY_TITLE,args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",colour:Blockly.Blocks.lists.HUE,tooltip:Blockly.Msg.LISTS_ISEMPTY_TOOLTIP,helpUrl:Blockly.Msg.LISTS_ISEMPTY_HELPURL})}};
Blockly.Blocks.lists_indexOf={init:function(){var a=[[Blockly.Msg.LISTS_INDEX_OF_FIRST,"FIRST"],[Blockly.Msg.LISTS_INDEX_OF_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.LISTS_INDEX_OF_HELPURL);this.setColour(Blockly.Blocks.lists.HUE);this.setOutput(!0,"Number");this.appendValueInput("VALUE").setCheck("Array").appendField(Blockly.Msg.LISTS_INDEX_OF_INPUT_IN_LIST);this.appendValueInput("FIND").appendField(new Blockly.FieldDropdown(a),"END");this.setInputsInline(!0);a=Blockly.Msg.LISTS_INDEX_OF_TOOLTIP.replace("%1",
Blockly.Blocks.ONE_BASED_INDEXING?"0":"-1");this.setTooltip(a)}};
Blockly.Blocks.lists_indexOf={init:function(){var a=[[Blockly.Msg.LISTS_INDEX_OF_FIRST,"FIRST"],[Blockly.Msg.LISTS_INDEX_OF_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.LISTS_INDEX_OF_HELPURL);this.setColour(Blockly.Blocks.lists.HUE);this.setOutput(!0,"Number");this.appendValueInput("VALUE").setCheck("Array").appendField(Blockly.Msg.LISTS_INDEX_OF_INPUT_IN_LIST);this.appendValueInput("FIND").appendField(new Blockly.FieldDropdown(a),"END");this.setInputsInline(!0);this.setTooltip(function(){return Blockly.Msg.LISTS_INDEX_OF_TOOLTIP.replace("%1",
this.workspace.options.oneBasedIndex?"0":"-1")})}};
Blockly.Blocks.lists_getIndex={init:function(){var a=[[Blockly.Msg.LISTS_GET_INDEX_GET,"GET"],[Blockly.Msg.LISTS_GET_INDEX_GET_REMOVE,"GET_REMOVE"],[Blockly.Msg.LISTS_GET_INDEX_REMOVE,"REMOVE"]];this.WHERE_OPTIONS=[[Blockly.Msg.LISTS_GET_INDEX_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_INDEX_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_INDEX_FIRST,"FIRST"],[Blockly.Msg.LISTS_GET_INDEX_LAST,"LAST"],[Blockly.Msg.LISTS_GET_INDEX_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.LISTS_GET_INDEX_HELPURL);
this.setColour(Blockly.Blocks.lists.HUE);a=new Blockly.FieldDropdown(a,function(a){this.sourceBlock_.updateStatement_("REMOVE"==a)});this.appendValueInput("VALUE").setCheck("Array").appendField(Blockly.Msg.LISTS_GET_INDEX_INPUT_IN_LIST);this.appendDummyInput().appendField(a,"MODE").appendField("","SPACE");this.appendDummyInput("AT");Blockly.Msg.LISTS_GET_INDEX_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.LISTS_GET_INDEX_TAIL);this.setInputsInline(!0);this.setOutput(!0);this.updateAt_(!0);
var b=this;this.setTooltip(function(){var a=b.getFieldValue("MODE"),e=b.getFieldValue("WHERE"),d="";switch(a+" "+e){case "GET FROM_START":case "GET FROM_END":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FROM;break;case "GET FIRST":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_FIRST;break;case "GET LAST":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_LAST;break;case "GET RANDOM":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_RANDOM;break;case "GET_REMOVE FROM_START":case "GET_REMOVE FROM_END":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM;
break;case "GET_REMOVE FIRST":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST;break;case "GET_REMOVE LAST":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST;break;case "GET_REMOVE RANDOM":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM;break;case "REMOVE FROM_START":case "REMOVE FROM_END":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM;break;case "REMOVE FIRST":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST;break;case "REMOVE LAST":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST;
break;case "REMOVE RANDOM":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM}if("FROM_START"==e||"FROM_END"==e)d+=" "+Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP.replace("%1",Blockly.Blocks.ONE_BASED_INDEXING?"#1":"#0");return d})},mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("statement",!this.outputConnection);var b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){var b="true"==a.getAttribute("statement");
this.updateStatement_(b);a="false"!=a.getAttribute("at");this.updateAt_(a)},updateStatement_:function(a){a!=!this.outputConnection&&(this.unplug(!0,!0),a?(this.setOutput(!1),this.setPreviousStatement(!0),this.setNextStatement(!0)):(this.setPreviousStatement(!1),this.setNextStatement(!1),this.setOutput(!0)))},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
this.appendDummyInput("AT");var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var e="FROM_START"==b||"FROM_END"==b;if(e!=a){var d=this.sourceBlock_;d.updateAt_(e);d.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE");Blockly.Msg.LISTS_GET_INDEX_TAIL&&this.moveInputBefore("TAIL",null)}};
break;case "REMOVE RANDOM":d=Blockly.Msg.LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM}if("FROM_START"==e||"FROM_END"==e)d+=" "+("FROM_START"==e?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP).replace("%1",b.workspace.options.oneBasedIndex?"#1":"#0");return d})},mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("statement",!this.outputConnection);var b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){var b=
"true"==a.getAttribute("statement");this.updateStatement_(b);a="false"!=a.getAttribute("at");this.updateAt_(a)},updateStatement_:function(a){a!=!this.outputConnection&&(this.unplug(!0,!0),a?(this.setOutput(!1),this.setPreviousStatement(!0),this.setNextStatement(!0)):(this.setPreviousStatement(!1),this.setNextStatement(!1),this.setOutput(!0)))},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&
this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var e="FROM_START"==b||"FROM_END"==b;if(e!=a){var d=this.sourceBlock_;d.updateAt_(e);d.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE");Blockly.Msg.LISTS_GET_INDEX_TAIL&&this.moveInputBefore("TAIL",null)}};
Blockly.Blocks.lists_setIndex={init:function(){var a=[[Blockly.Msg.LISTS_SET_INDEX_SET,"SET"],[Blockly.Msg.LISTS_SET_INDEX_INSERT,"INSERT"]];this.WHERE_OPTIONS=[[Blockly.Msg.LISTS_GET_INDEX_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_INDEX_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_INDEX_FIRST,"FIRST"],[Blockly.Msg.LISTS_GET_INDEX_LAST,"LAST"],[Blockly.Msg.LISTS_GET_INDEX_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.LISTS_SET_INDEX_HELPURL);this.setColour(Blockly.Blocks.lists.HUE);this.appendValueInput("LIST").setCheck("Array").appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_IN_LIST);
this.appendDummyInput().appendField(new Blockly.FieldDropdown(a),"MODE").appendField("","SPACE");this.appendDummyInput("AT");this.appendValueInput("TO").appendField(Blockly.Msg.LISTS_SET_INDEX_INPUT_TO);this.setInputsInline(!0);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setTooltip(Blockly.Msg.LISTS_SET_INDEX_TOOLTIP);this.updateAt_(!0);var b=this;this.setTooltip(function(){var a=b.getFieldValue("MODE"),e=b.getFieldValue("WHERE"),d="";switch(a+" "+e){case "SET FROM_START":case "SET FROM_END":d=
Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FROM;break;case "SET FIRST":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_FIRST;break;case "SET LAST":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_LAST;break;case "SET RANDOM":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM;break;case "INSERT FROM_START":case "INSERT FROM_END":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FROM;break;case "INSERT FIRST":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST;break;case "INSERT LAST":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_LAST;
break;case "INSERT RANDOM":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM}if("FROM_START"==e||"FROM_END"==e)d+=" "+Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP.replace("%1",Blockly.Blocks.ONE_BASED_INDEXING?"#1":"#0");return d})},mutationToDom:function(){var a=document.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");
break;case "INSERT RANDOM":d=Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM}if("FROM_START"==e||"FROM_END"==e)d+=" "+Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP.replace("%1",b.workspace.options.oneBasedIndex?"#1":"#0");return d})},mutationToDom:function(){var a=document.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");
this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var e="FROM_START"==b||"FROM_END"==b;if(e!=a){var d=this.sourceBlock_;d.updateAt_(e);d.setFieldValue(b,"WHERE");return null}});this.moveInputBefore("AT","TO");this.getInput("ORDINAL")&&this.moveInputBefore("ORDINAL",
"TO");this.getInput("AT").appendField(b,"WHERE")}};
Blockly.Blocks.lists_getSublist={init:function(){this.WHERE_OPTIONS_1=[[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_SUBLIST_START_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_SUBLIST_START_FIRST,"FIRST"]];this.WHERE_OPTIONS_2=[[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_START,"FROM_START"],[Blockly.Msg.LISTS_GET_SUBLIST_END_FROM_END,"FROM_END"],[Blockly.Msg.LISTS_GET_SUBLIST_END_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.LISTS_GET_SUBLIST_HELPURL);this.setColour(Blockly.Blocks.lists.HUE);
@@ -87,18 +87,20 @@ Blockly.Blocks.math_modulo={init:function(){this.jsonInit({message0:Blockly.Msg.
Blockly.Blocks.math_constrain={init:function(){this.jsonInit({message0:Blockly.Msg.MATH_CONSTRAIN_TITLE,args0:[{type:"input_value",name:"VALUE",check:"Number"},{type:"input_value",name:"LOW",check:"Number"},{type:"input_value",name:"HIGH",check:"Number"}],inputsInline:!0,output:"Number",colour:Blockly.Blocks.math.HUE,tooltip:Blockly.Msg.MATH_CONSTRAIN_TOOLTIP,helpUrl:Blockly.Msg.MATH_CONSTRAIN_HELPURL})}};
Blockly.Blocks.math_random_int={init:function(){this.jsonInit({message0:Blockly.Msg.MATH_RANDOM_INT_TITLE,args0:[{type:"input_value",name:"FROM",check:"Number"},{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",colour:Blockly.Blocks.math.HUE,tooltip:Blockly.Msg.MATH_RANDOM_INT_TOOLTIP,helpUrl:Blockly.Msg.MATH_RANDOM_INT_HELPURL})}};
Blockly.Blocks.math_random_float={init:function(){this.jsonInit({message0:Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM,output:"Number",colour:Blockly.Blocks.math.HUE,tooltip:Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP,helpUrl:Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL})}};Blockly.Blocks.procedures={};Blockly.Blocks.procedures.HUE=290;
Blockly.Blocks.procedures_defnoreturn={init:function(){var a=new Blockly.FieldTextInput(Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE,Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);this.setColour(Blockly.Blocks.procedures.HUE);
this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:function(a){this.hasStatements_!==a&&(a?(this.appendStatementInput("STACK").appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO),this.getInput("RETURN")&&this.moveInputBefore("STACK","RETURN")):this.removeInput("STACK",!0),this.hasStatements_=a)},updateParams_:function(){for(var a=!1,b={},c=0;c<
this.arguments_.length;c++){if(b["arg_"+this.arguments_[c].toLowerCase()]){a=!0;break}b["arg_"+this.arguments_[c].toLowerCase()]=!0}a?this.setWarningText(Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING):this.setWarningText(null);a="";this.arguments_.length&&(a=Blockly.Msg.PROCEDURES_BEFORE_PARAMS+" "+this.arguments_.join(", "));Blockly.Events.disable();try{this.setFieldValue(a,"PARAMS")}finally{Blockly.Events.enable()}},mutationToDom:function(a){var b=document.createElement("mutation");a&&b.setAttribute("name",
this.getFieldValue("NAME"));for(var c=0;c<this.arguments_.length;c++){var e=document.createElement("arg");e.setAttribute("name",this.arguments_[c]);a&&this.paramIds_&&e.setAttribute("paramId",this.paramIds_[c]);b.appendChild(e)}this.hasStatements_||b.setAttribute("statements","false");return b},domToMutation:function(a){this.arguments_=[];for(var b=0,c;c=a.childNodes[b];b++)"arg"==c.nodeName.toLowerCase()&&this.arguments_.push(c.getAttribute("name"));this.updateParams_();Blockly.Procedures.mutateCallers(this);
this.setStatements_("false"!==a.getAttribute("statements"))},decompose:function(a){var b=a.newBlock("procedures_mutatorcontainer");b.initSvg();this.getInput("RETURN")?b.setFieldValue(this.hasStatements_?"TRUE":"FALSE","STATEMENTS"):b.getInput("STATEMENT_INPUT").setVisible(!1);for(var c=b.getInput("STACK").connection,e=0;e<this.arguments_.length;e++){var d=a.newBlock("procedures_mutatorarg");d.initSvg();d.setFieldValue(this.arguments_[e],"NAME");d.oldLocation=e;c.connect(d.previousConnection);c=d.nextConnection}Blockly.Procedures.mutateCallers(this);
return b},compose:function(a){this.arguments_=[];this.paramIds_=[];for(var b=a.getInputTargetBlock("STACK");b;)this.arguments_.push(b.getFieldValue("NAME")),this.paramIds_.push(b.id),b=b.nextConnection&&b.nextConnection.targetBlock();this.updateParams_();Blockly.Procedures.mutateCallers(this);a=a.getFieldValue("STATEMENTS");if(null!==a&&(a="TRUE"==a,this.hasStatements_!=a))if(a)this.setStatements_(!0),Blockly.Mutator.reconnect(this.statementConnection_,this,"STACK"),this.statementConnection_=null;
else{a=this.getInput("STACK").connection;if(this.statementConnection_=a.targetConnection)a=a.targetBlock(),a.unplug(),a.bumpNeighbours_();this.setStatements_(!1)}},getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!1]},getVars:function(){return this.arguments_},renameVar:function(a,b){for(var c=!1,e=0;e<this.arguments_.length;e++)Blockly.Names.equals(a,this.arguments_[e])&&(this.arguments_[e]=b,c=!0);if(c&&(this.updateParams_(),this.mutator.isVisible()))for(var c=this.mutator.workspace_.getAllBlocks(),
e=0,d;d=c[e];e++)"procedures_mutatorarg"==d.type&&Blockly.Names.equals(a,d.getFieldValue("NAME"))&&d.setFieldValue(b,"NAME")},customContextMenu:function(a){var b={enabled:!0},c=this.getFieldValue("NAME");b.text=Blockly.Msg.PROCEDURES_CREATE_DO.replace("%1",c);var e=goog.dom.createDom("mutation");e.setAttribute("name",c);for(var d=0;d<this.arguments_.length;d++)c=goog.dom.createDom("arg"),c.setAttribute("name",this.arguments_[d]),e.appendChild(c);e=goog.dom.createDom("block",null,e);e.setAttribute("type",
this.callType_);b.callback=Blockly.ContextMenu.callbackFactory(this,e);a.push(b);if(!this.isCollapsed())for(d=0;d<this.arguments_.length;d++)b={enabled:!0},c=this.arguments_[d],b.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c),e=goog.dom.createDom("field",null,c),e.setAttribute("name","VAR"),e=goog.dom.createDom("block",null,e),e.setAttribute("type","variables_get"),b.callback=Blockly.ContextMenu.callbackFactory(this,e),a.push(b)},callType_:"procedures_callnoreturn"};
Blockly.Blocks.procedures_defreturn={init:function(){var a=new Blockly.FieldTextInput(Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE,Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.appendValueInput("RETURN").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT&&
this.setCommentText(Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT);this.setColour(Blockly.Blocks.procedures.HUE);this.setTooltip(Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL);this.arguments_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:Blockly.Blocks.procedures_defnoreturn.setStatements_,updateParams_:Blockly.Blocks.procedures_defnoreturn.updateParams_,mutationToDom:Blockly.Blocks.procedures_defnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_defnoreturn.domToMutation,
decompose:Blockly.Blocks.procedures_defnoreturn.decompose,compose:Blockly.Blocks.procedures_defnoreturn.compose,getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!0]},getVars:Blockly.Blocks.procedures_defnoreturn.getVars,renameVar:Blockly.Blocks.procedures_defnoreturn.renameVar,customContextMenu:Blockly.Blocks.procedures_defnoreturn.customContextMenu,callType_:"procedures_callreturn"};
Blockly.Blocks.procedures_defnoreturn={init:function(){var a=new Blockly.FieldTextInput(Blockly.Msg.PROCEDURES_DEFNORETURN_PROCEDURE,Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&this.workspace.options.parentWorkspace.options.comments)&&
Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);this.setColour(Blockly.Blocks.procedures.HUE);this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:function(a){this.hasStatements_!==a&&(a?(this.appendStatementInput("STACK").appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO),this.getInput("RETURN")&&
this.moveInputBefore("STACK","RETURN")):this.removeInput("STACK",!0),this.hasStatements_=a)},updateParams_:function(){for(var a=!1,b={},c=0;c<this.arguments_.length;c++){if(b["arg_"+this.arguments_[c].toLowerCase()]){a=!0;break}b["arg_"+this.arguments_[c].toLowerCase()]=!0}a?this.setWarningText(Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING):this.setWarningText(null);a="";this.arguments_.length&&(a=Blockly.Msg.PROCEDURES_BEFORE_PARAMS+" "+this.arguments_.join(", "));Blockly.Events.disable();try{this.setFieldValue(a,
"PARAMS")}finally{Blockly.Events.enable()}},mutationToDom:function(a){var b=document.createElement("mutation");a&&b.setAttribute("name",this.getFieldValue("NAME"));for(var c=0;c<this.arguments_.length;c++){var e=document.createElement("arg");e.setAttribute("name",this.arguments_[c]);a&&this.paramIds_&&e.setAttribute("paramId",this.paramIds_[c]);b.appendChild(e)}this.hasStatements_||b.setAttribute("statements","false");return b},domToMutation:function(a){this.arguments_=[];for(var b=0,c;c=a.childNodes[b];b++)"arg"==
c.nodeName.toLowerCase()&&this.arguments_.push(c.getAttribute("name"));this.updateParams_();Blockly.Procedures.mutateCallers(this);this.setStatements_("false"!==a.getAttribute("statements"))},decompose:function(a){var b=a.newBlock("procedures_mutatorcontainer");b.initSvg();this.getInput("RETURN")?b.setFieldValue(this.hasStatements_?"TRUE":"FALSE","STATEMENTS"):b.getInput("STATEMENT_INPUT").setVisible(!1);for(var c=b.getInput("STACK").connection,e=0;e<this.arguments_.length;e++){var d=a.newBlock("procedures_mutatorarg");
d.initSvg();d.setFieldValue(this.arguments_[e],"NAME");d.oldLocation=e;c.connect(d.previousConnection);c=d.nextConnection}Blockly.Procedures.mutateCallers(this);return b},compose:function(a){this.arguments_=[];this.paramIds_=[];for(var b=a.getInputTargetBlock("STACK");b;)this.arguments_.push(b.getFieldValue("NAME")),this.paramIds_.push(b.id),b=b.nextConnection&&b.nextConnection.targetBlock();this.updateParams_();Blockly.Procedures.mutateCallers(this);a=a.getFieldValue("STATEMENTS");if(null!==a&&(a=
"TRUE"==a,this.hasStatements_!=a))if(a)this.setStatements_(!0),Blockly.Mutator.reconnect(this.statementConnection_,this,"STACK"),this.statementConnection_=null;else{a=this.getInput("STACK").connection;if(this.statementConnection_=a.targetConnection)a=a.targetBlock(),a.unplug(),a.bumpNeighbours_();this.setStatements_(!1)}},getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!1]},getVars:function(){return this.arguments_},renameVar:function(a,b){for(var c=!1,e=0;e<this.arguments_.length;e++)Blockly.Names.equals(a,
this.arguments_[e])&&(this.arguments_[e]=b,c=!0);if(c&&(this.updateParams_(),this.mutator.isVisible()))for(var c=this.mutator.workspace_.getAllBlocks(),e=0,d;d=c[e];e++)"procedures_mutatorarg"==d.type&&Blockly.Names.equals(a,d.getFieldValue("NAME"))&&d.setFieldValue(b,"NAME")},customContextMenu:function(a){var b={enabled:!0},c=this.getFieldValue("NAME");b.text=Blockly.Msg.PROCEDURES_CREATE_DO.replace("%1",c);var e=goog.dom.createDom("mutation");e.setAttribute("name",c);for(var d=0;d<this.arguments_.length;d++)c=
goog.dom.createDom("arg"),c.setAttribute("name",this.arguments_[d]),e.appendChild(c);e=goog.dom.createDom("block",null,e);e.setAttribute("type",this.callType_);b.callback=Blockly.ContextMenu.callbackFactory(this,e);a.push(b);if(!this.isCollapsed())for(d=0;d<this.arguments_.length;d++)b={enabled:!0},c=this.arguments_[d],b.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c),e=goog.dom.createDom("field",null,c),e.setAttribute("name","VAR"),e=goog.dom.createDom("block",null,e),e.setAttribute("type",
"variables_get"),b.callback=Blockly.ContextMenu.callbackFactory(this,e),a.push(b)},callType_:"procedures_callnoreturn"};
Blockly.Blocks.procedures_defreturn={init:function(){var a=new Blockly.FieldTextInput(Blockly.Msg.PROCEDURES_DEFRETURN_PROCEDURE,Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.appendValueInput("RETURN").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||
this.workspace.options.parentWorkspace&&this.workspace.options.parentWorkspace.options.comments)&&Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFRETURN_COMMENT);this.setColour(Blockly.Blocks.procedures.HUE);this.setTooltip(Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFRETURN_HELPURL);this.arguments_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:Blockly.Blocks.procedures_defnoreturn.setStatements_,
updateParams_:Blockly.Blocks.procedures_defnoreturn.updateParams_,mutationToDom:Blockly.Blocks.procedures_defnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_defnoreturn.domToMutation,decompose:Blockly.Blocks.procedures_defnoreturn.decompose,compose:Blockly.Blocks.procedures_defnoreturn.compose,getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!0]},getVars:Blockly.Blocks.procedures_defnoreturn.getVars,renameVar:Blockly.Blocks.procedures_defnoreturn.renameVar,
customContextMenu:Blockly.Blocks.procedures_defnoreturn.customContextMenu,callType_:"procedures_callreturn"};
Blockly.Blocks.procedures_mutatorcontainer={init:function(){this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TITLE);this.appendStatementInput("STACK");this.appendDummyInput("STATEMENT_INPUT").appendField(Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS).appendField(new Blockly.FieldCheckbox("TRUE"),"STATEMENTS");this.setColour(Blockly.Blocks.procedures.HUE);this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORCONTAINER_TOOLTIP);this.contextMenu=!1}};
Blockly.Blocks.procedures_mutatorarg={init:function(){var a=new Blockly.FieldTextInput("x",this.validator_);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_MUTATORARG_TITLE).appendField(a,"NAME");this.setPreviousStatement(!0);this.setNextStatement(!0);this.setColour(Blockly.Blocks.procedures.HUE);this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP);this.contextMenu=!1;a.onFinishEditing_=this.createNewVar_;a.onFinishEditing_("x")},validator_:function(a){return(a=a.replace(/[\s\xa0]+/g,
" ").replace(/^ | $/g,""))||null},createNewVar_:function(a){var b=this.sourceBlock_;b&&b.workspace&&b.workspace.options&&b.workspace.options.parentWorkspace&&b.workspace.options.parentWorkspace.createVariable(a)}};
@@ -128,11 +130,11 @@ Blockly.Blocks.text_append={init:function(){this.setHelpUrl(Blockly.Msg.TEXT_APP
Blockly.Blocks.text_length={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_LENGTH_TITLE,args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",colour:Blockly.Blocks.texts.HUE,tooltip:Blockly.Msg.TEXT_LENGTH_TOOLTIP,helpUrl:Blockly.Msg.TEXT_LENGTH_HELPURL})}};
Blockly.Blocks.text_isEmpty={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_ISEMPTY_TITLE,args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",colour:Blockly.Blocks.texts.HUE,tooltip:Blockly.Msg.TEXT_ISEMPTY_TOOLTIP,helpUrl:Blockly.Msg.TEXT_ISEMPTY_HELPURL})}};
Blockly.Blocks.text_indexOf={init:function(){var a=[[Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST,"FIRST"],[Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.TEXT_INDEXOF_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.setOutput(!0,"Number");this.appendValueInput("VALUE").setCheck("String").appendField(Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT);this.appendValueInput("FIND").setCheck("String").appendField(new Blockly.FieldDropdown(a),"END");Blockly.Msg.TEXT_INDEXOF_TAIL&&this.appendDummyInput().appendField(Blockly.Msg.TEXT_INDEXOF_TAIL);
this.setInputsInline(!0);a=Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace("%1",Blockly.Blocks.ONE_BASED_INDEXING?"0":"-1");this.setTooltip(a)}};
this.setInputsInline(!0);var b=this;this.setTooltip(function(){return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace("%1",b.workspace.options.oneBasedIndex?"0":"-1")})}};
Blockly.Blocks.text_charAt={init:function(){this.WHERE_OPTIONS=[[Blockly.Msg.TEXT_CHARAT_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_CHARAT_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_CHARAT_FIRST,"FIRST"],[Blockly.Msg.TEXT_CHARAT_LAST,"LAST"],[Blockly.Msg.TEXT_CHARAT_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.TEXT_CHARAT_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.setOutput(!0,"String");this.appendValueInput("VALUE").setCheck("String").appendField(Blockly.Msg.TEXT_CHARAT_INPUT_INTEXT);this.appendDummyInput("AT");
this.setInputsInline(!0);this.updateAt_(!0);var a=this;this.setTooltip(function(){var b=a.getFieldValue("WHERE"),c=Blockly.Msg.TEXT_CHARAT_TOOLTIP;if("FROM_START"==b||"FROM_END"==b)c+=" "+Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP.replace("%1",Blockly.Blocks.ONE_BASED_INDEXING?"#1":"#0");return c})},mutationToDom:function(){var a=document.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");
this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");Blockly.Msg.TEXT_CHARAT_TAIL&&(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_CHARAT_TAIL));var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var e="FROM_START"==
b||"FROM_END"==b;if(e!=a){var d=this.sourceBlock_;d.updateAt_(e);d.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE")}};
this.setInputsInline(!0);this.updateAt_(!0);var a=this;this.setTooltip(function(){var b=a.getFieldValue("WHERE"),c=Blockly.Msg.TEXT_CHARAT_TOOLTIP;if("FROM_START"==b||"FROM_END"==b)c+=" "+("FROM_START"==b?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP).replace("%1",a.workspace.options.oneBasedIndex?"#1":"#0");return c})},mutationToDom:function(){var a=document.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},
domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");Blockly.Msg.TEXT_CHARAT_TAIL&&(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_CHARAT_TAIL));var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,
function(b){var e="FROM_START"==b||"FROM_END"==b;if(e!=a){var d=this.sourceBlock_;d.updateAt_(e);d.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE")}};
Blockly.Blocks.text_getSubstring={init:function(){this.WHERE_OPTIONS_1=[[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FIRST,"FIRST"]];this.WHERE_OPTIONS_2=[[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.TEXT_GET_SUBSTRING_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);
this.appendValueInput("STRING").setCheck("String").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_INPUT_IN_TEXT);this.appendDummyInput("AT1");this.appendDummyInput("AT2");Blockly.Msg.TEXT_GET_SUBSTRING_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL);this.setInputsInline(!0);this.setOutput(!0,"String");this.updateAt_(1,!0);this.updateAt_(2,!0);this.setTooltip(Blockly.Msg.TEXT_GET_SUBSTRING_TOOLTIP)},mutationToDom:function(){var a=document.createElement("mutation"),
b=this.getInput("AT1").type==Blockly.INPUT_VALUE;a.setAttribute("at1",b);b=this.getInput("AT2").type==Blockly.INPUT_VALUE;a.setAttribute("at2",b);return a},domToMutation:function(a){var b="true"==a.getAttribute("at1");a="true"==a.getAttribute("at2");this.updateAt_(1,b);this.updateAt_(2,a)},updateAt_:function(a,b){this.removeInput("AT"+a);this.removeInput("ORDINAL"+a,!0);b?(this.appendValueInput("AT"+a).setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL"+a).appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
+5 -10
View File
@@ -46,7 +46,7 @@ goog.require('goog.string');
* @param {!Blockly.Workspace} workspace The block's workspace.
* @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @constructor
*/
@@ -454,13 +454,7 @@ Blockly.Block.prototype.setParent = function(newParent) {
}
if (this.parentBlock_) {
// Remove this block from the old parent's child list.
var children = this.parentBlock_.childBlocks_;
for (var child, x = 0; child = children[x]; x++) {
if (child == this) {
children.splice(x, 1);
break;
}
}
goog.array.remove(this.parentBlock_.childBlocks_, this);
// Disconnect from superior blocks.
if (this.previousConnection && this.previousConnection.isConnected()) {
@@ -1029,7 +1023,7 @@ Blockly.Block.prototype.jsonInit = function(json) {
* @param {string} message Text contains interpolation tokens (%1, %2, ...)
* that match with fields or inputs defined in the args array.
* @param {!Array} args Array of arguments to be interpolated.
* @param {=string} lastDummyAlign If a dummy input is added at the end,
* @param {string=} lastDummyAlign If a dummy input is added at the end,
* how should it be aligned?
* @private
*/
@@ -1060,7 +1054,8 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) {
'Message does not reference all %s arg(s).', args.length);
// Add last dummy input if needed.
if (elements.length && (typeof elements[elements.length - 1] == 'string' ||
elements[elements.length - 1]['type'].indexOf('field_') == 0)) {
goog.string.startsWith(elements[elements.length - 1]['type'],
'field_'))) {
var dummyInput = {type: 'input_dummy'};
if (lastDummyAlign) {
dummyInput['align'] = lastDummyAlign;
+22 -7
View File
@@ -28,6 +28,7 @@ goog.provide('Blockly.BlockSvg');
goog.require('Blockly.Block');
goog.require('Blockly.ContextMenu');
goog.require('Blockly.Touch');
goog.require('Blockly.RenderedConnection');
goog.require('goog.Timer');
goog.require('goog.asserts');
@@ -42,7 +43,7 @@ goog.require('goog.userAgent');
* @param {!Blockly.Workspace} workspace The block's workspace.
* @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @extends {Blockly.Block}
* @constructor
@@ -126,10 +127,10 @@ Blockly.BlockSvg.prototype.initSvg = function() {
this.updateColour();
this.updateMovable();
if (!this.workspace.options.readOnly && !this.eventsInit_) {
Blockly.bindEvent_(this.getSvgRoot(), 'mousedown', this,
Blockly.bindEventWithChecks_(this.getSvgRoot(), 'mousedown', this,
this.onMouseDown_);
var thisBlock = this;
Blockly.bindEvent_(this.getSvgRoot(), 'touchstart', null,
Blockly.bindEventWithChecks_(this.getSvgRoot(), 'touchstart', null,
function(e) {Blockly.longStart_(e, thisBlock);});
}
this.eventsInit_ = true;
@@ -523,7 +524,7 @@ Blockly.BlockSvg.prototype.tab = function(start, forward) {
/**
* Handle a mouse-down on an SVG block.
* @param {!Event} e Mouse down event.
* @param {!Event} e Mouse down event or touch start event.
* @private
*/
Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
@@ -531,6 +532,17 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
return;
}
if (this.isInFlyout) {
// longStart's simulation of right-clicks for longpresses on touch devices
// calls the onMouseDown_ function defined on the prototype of the object
// the was longpressed (in this case, a Blockly.BlockSvg). In this case
// that behaviour is wrong, because Blockly.Flyout.prototype.blockMouseDown
// should be called for a mousedown on a block in the flyout, which blocks
// execution of the block's onMouseDown_ function.
if (e.type == 'touchstart' && Blockly.isRightButton(e)) {
Blockly.Flyout.blockRightClick_(e, this);
e.stopPropagation();
e.preventDefault();
}
return;
}
if (this.isInMutator) {
@@ -547,6 +559,8 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
if (Blockly.isRightButton(e)) {
// Right-click.
this.showContextMenu_(e);
// Click, not drag, so stop waiting for other touches from this identifier.
Blockly.Touch.clearTouchIdentifier();
} else if (!this.isMovable()) {
// Allow immovable blocks to be selected and context menued, but not
// dragged. Let this event bubble up to document, so the workspace may be
@@ -563,10 +577,10 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
this.workspace.startDrag(e, this.dragStartXY_);
Blockly.dragMode_ = Blockly.DRAG_STICKY;
Blockly.BlockSvg.onMouseUpWrapper_ = Blockly.bindEvent_(document,
Blockly.BlockSvg.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
'mouseup', this, this.onMouseUp_);
Blockly.BlockSvg.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
'mousemove', this, this.onMouseMove_);
Blockly.BlockSvg.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(
document, 'mousemove', this, this.onMouseMove_);
// Build a list of bubbles that need to be moved and where they started.
this.draggedBubbles_ = [];
var descendants = this.getDescendants();
@@ -592,6 +606,7 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
* @private
*/
Blockly.BlockSvg.prototype.onMouseUp_ = function(e) {
Blockly.Touch.clearTouchIdentifier();
if (Blockly.dragMode_ != Blockly.DRAG_FREE &&
!Blockly.WidgetDiv.isVisible()) {
Blockly.Events.fire(
+5 -101
View File
@@ -24,7 +24,10 @@
*/
'use strict';
// Top level object for Blockly.
/**
* The top level namespace used to access the Blockly library.
* @namespace Blockly
*/
goog.provide('Blockly');
goog.require('Blockly.BlockSvg.render');
@@ -44,6 +47,7 @@ goog.require('Blockly.Generator');
goog.require('Blockly.Msg');
goog.require('Blockly.Procedures');
goog.require('Blockly.Toolbox');
goog.require('Blockly.Touch');
goog.require('Blockly.WidgetDiv');
goog.require('Blockly.WorkspaceSvg');
goog.require('Blockly.constants');
@@ -113,13 +117,6 @@ Blockly.clipboardSource_ = null;
*/
Blockly.dragMode_ = Blockly.DRAG_NONE;
/**
* Wrapper function called when a touch mouseUp occurs during a drag operation.
* @type {Array.<!Array>}
* @private
*/
Blockly.onTouchUpWrapper_ = null;
/**
* Convert a hue (HSV model) into an RGB hex triplet.
* @param {number} hue Hue on a colour wheel (0-360).
@@ -181,62 +178,6 @@ Blockly.svgResize = function(workspace) {
mainWorkspace.resize();
};
/**
* Handle a mouse-up anywhere on the page.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.onMouseUp_ = function(e) {
var workspace = Blockly.getMainWorkspace();
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
workspace.dragMode_ = Blockly.DRAG_NONE;
// Unbind the touch event if it exists.
if (Blockly.onTouchUpWrapper_) {
Blockly.unbindEvent_(Blockly.onTouchUpWrapper_);
Blockly.onTouchUpWrapper_ = null;
}
if (Blockly.onMouseMoveWrapper_) {
Blockly.unbindEvent_(Blockly.onMouseMoveWrapper_);
Blockly.onMouseMoveWrapper_ = null;
}
};
/**
* Handle a mouse-move on SVG drawing surface.
* @param {!Event} e Mouse move event.
* @private
*/
Blockly.onMouseMove_ = function(e) {
if (e.touches && e.touches.length >= 2) {
return; // Multi-touch gestures won't have e.clientX.
}
var workspace = Blockly.getMainWorkspace();
if (workspace.dragMode_ != Blockly.DRAG_NONE) {
var dx = e.clientX - workspace.startDragMouseX;
var dy = e.clientY - workspace.startDragMouseY;
var metrics = workspace.startDragMetrics;
var x = workspace.startScrollX + dx;
var y = workspace.startScrollY + dy;
x = Math.min(x, -metrics.contentLeft);
y = Math.min(y, -metrics.contentTop);
x = Math.max(x, metrics.viewWidth - metrics.contentLeft -
metrics.contentWidth);
y = Math.max(y, metrics.viewHeight - metrics.contentTop -
metrics.contentHeight);
// Move the scrollbars and the page will scroll automatically.
workspace.scrollbar.set(-x - metrics.contentLeft,
-y - metrics.contentTop);
// Cancel the long-press if the drag has moved too far.
if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) {
Blockly.longStop_();
workspace.dragMode_ = Blockly.DRAG_FREE;
}
e.stopPropagation();
e.preventDefault();
}
};
/**
* Handle a key-down on SVG drawing surface.
* @param {!Event} e Key down event.
@@ -310,43 +251,6 @@ Blockly.terminateDrag_ = function() {
Blockly.Flyout.terminateDrag_();
};
/**
* PID of queued long-press task.
* @private
*/
Blockly.longPid_ = 0;
/**
* Context menus on touch devices are activated using a long-press.
* Unfortunately the contextmenu touch event is currently (2015) only suported
* by Chrome. This function is fired on any touchstart event, queues a task,
* which after about a second opens the context menu. The tasks is killed
* if the touch event terminates early.
* @param {!Event} e Touch start event.
* @param {!Blockly.Block|!Blockly.WorkspaceSvg} uiObject The block or workspace
* under the touchstart event.
* @private
*/
Blockly.longStart_ = function(e, uiObject) {
Blockly.longStop_();
Blockly.longPid_ = setTimeout(function() {
e.button = 2; // Simulate a right button click.
uiObject.onMouseDown_(e);
}, Blockly.LONGPRESS);
};
/**
* Nope, that's not a long-press. Either touchend or touchcancel was fired,
* or a drag hath begun. Kill the queued long-press task.
* @private
*/
Blockly.longStop_ = function() {
if (Blockly.longPid_) {
clearTimeout(Blockly.longPid_);
Blockly.longPid_ = 0;
}
};
/**
* Copy a block onto the local clipboard.
* @param {!Blockly.Block} block Block to be copied.
-6
View File
@@ -25,9 +25,3 @@
'use strict';
goog.provide('Blockly.Blocks');
/**
* Allow for switching between one and zero based indexing for lists and text,
* one based by default.
*/
Blockly.Blocks.ONE_BASED_INDEXING = true;
+22 -10
View File
@@ -26,6 +26,7 @@
goog.provide('Blockly.Bubble');
goog.require('Blockly.Touch');
goog.require('Blockly.Workspace');
goog.require('goog.dom');
goog.require('goog.math');
@@ -74,10 +75,10 @@ Blockly.Bubble = function(workspace, content, shape, anchorXY,
this.rendered_ = true;
if (!workspace.options.readOnly) {
Blockly.bindEvent_(this.bubbleBack_, 'mousedown', this,
Blockly.bindEventWithChecks_(this.bubbleBack_, 'mousedown', this,
this.bubbleMouseDown_);
if (this.resizeGroup_) {
Blockly.bindEvent_(this.resizeGroup_, 'mousedown', this,
Blockly.bindEventWithChecks_(this.resizeGroup_, 'mousedown', this,
this.resizeMouseDown_);
}
}
@@ -92,7 +93,7 @@ Blockly.Bubble.BORDER_WIDTH = 6;
* Determines the thickness of the base of the arrow in relation to the size
* of the bubble. Higher numbers result in thinner arrows.
*/
Blockly.Bubble.ARROW_THICKNESS = 10;
Blockly.Bubble.ARROW_THICKNESS = 5;
/**
* The number of degrees that the arrow bends counter-clockwise.
@@ -144,6 +145,17 @@ Blockly.Bubble.unbindDragEvents_ = function() {
}
};
/*
* Handle a mouse-up event while dragging a bubble's border or resize handle.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.Bubble.bubbleMouseUp_ = function(/*e*/) {
Blockly.Touch.clearTouchIdentifier();
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
Blockly.Bubble.unbindDragEvents_();
};
/**
* Flag to stop incremental rendering during construction.
* @private
@@ -274,9 +286,9 @@ Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) {
this.workspace_.RTL ? -this.relativeLeft_ : this.relativeLeft_,
this.relativeTop_));
Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEvent_(document,
'mouseup', this, Blockly.Bubble.unbindDragEvents_);
Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
'mouseup', this, Blockly.Bubble.bubbleMouseUp_);
Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document,
'mousemove', this, this.bubbleMouseMove_);
Blockly.hideChaff();
// This event has been handled. No need to bubble up to the document.
@@ -316,9 +328,9 @@ Blockly.Bubble.prototype.resizeMouseDown_ = function(e) {
this.workspace_.startDrag(e, new goog.math.Coordinate(
this.workspace_.RTL ? -this.width_ : this.width_, this.height_));
Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEvent_(document,
'mouseup', this, Blockly.Bubble.unbindDragEvents_);
Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
'mouseup', this, Blockly.Bubble.bubbleMouseUp_);
Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document,
'mousemove', this, this.resizeMouseMove_);
Blockly.hideChaff();
// This event has been handled. No need to bubble up to the document.
@@ -518,7 +530,7 @@ Blockly.Bubble.prototype.renderArrow_ = function() {
var bubbleSize = this.getBubbleSize();
var thickness = (bubbleSize.width + bubbleSize.height) /
Blockly.Bubble.ARROW_THICKNESS;
thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 2;
thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 4;
// Back the tip of the arrow off of the anchor.
var backoffRatio = 1 - Blockly.Bubble.ANCHOR_RADIUS / hypotenuse;
+3 -3
View File
@@ -112,12 +112,12 @@ Blockly.Comment.prototype.createEditor_ = function() {
body.appendChild(textarea);
this.textarea_ = textarea;
this.foreignObject_.appendChild(body);
Blockly.bindEvent_(textarea, 'mouseup', this, this.textareaFocus_);
Blockly.bindEventWithChecks_(textarea, 'mouseup', this, this.textareaFocus_);
// Don't zoom with mousewheel.
Blockly.bindEvent_(textarea, 'wheel', this, function(e) {
Blockly.bindEventWithChecks_(textarea, 'wheel', this, function(e) {
e.stopPropagation();
});
Blockly.bindEvent_(textarea, 'change', this, function(e) {
Blockly.bindEventWithChecks_(textarea, 'change', this, function(e) {
if (this.text_ != textarea.value) {
Blockly.Events.fire(new Blockly.Events.Change(
this.block_, 'comment', null, this.text_, textarea.value));
+1 -1
View File
@@ -78,7 +78,7 @@ Blockly.ContextMenu.show = function(e, options, rtl) {
var menuDom = menu.getElement();
Blockly.addClass_(menuDom, 'blocklyContextMenu');
// Prevent system context menu when right-clicking a Blockly context menu.
Blockly.bindEvent_(menuDom, 'contextmenu', null, Blockly.noEvent);
Blockly.bindEventWithChecks_(menuDom, 'contextmenu', null, Blockly.noEvent);
// Record menuSize after adding menu.
var menuSize = goog.style.getSize(menuDom);
+1 -1
View File
@@ -263,7 +263,7 @@ Blockly.Css.CONTENT = [
'}',
'.blocklyFlyoutButtonShadow {',
'fill: #444;',
'fill: #666;',
'}',
'.blocklyFlyoutButton:hover {',
+5
View File
@@ -24,8 +24,13 @@
*/
'use strict';
/**
* Events fired as a result of actions in Blockly's editor.
* @namespace Blockly.Events
*/
goog.provide('Blockly.Events');
goog.require('goog.array');
goog.require('goog.math.Coordinate');
+5 -1
View File
@@ -153,7 +153,8 @@ Blockly.Field.prototype.init = function() {
this.updateEditable();
this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
this.mouseUpWrapper_ =
Blockly.bindEvent_(this.fieldGroup_, 'mouseup', this, this.onMouseUp_);
Blockly.bindEventWithChecks_(this.fieldGroup_, 'mouseup', this,
this.onMouseUp_);
// Force a render.
this.updateTextNode_();
};
@@ -472,6 +473,9 @@ Blockly.Field.prototype.onMouseUp_ = function(e) {
} else if (this.sourceBlock_.isEditable()) {
// Non-abstract sub-classes must define a showEditor_ method.
this.showEditor_();
// The field is handling the touch, but we also want the blockSvg onMouseUp
// handler to fire, so we will leave the touch identifier as it is.
// The next onMouseUp is responsible for nulling it out.
}
};
+7 -1
View File
@@ -167,12 +167,18 @@ Blockly.FieldAngle.prototype.showEditor_ = function() {
}, svg);
}
svg.style.marginLeft = (15 - Blockly.FieldAngle.RADIUS) + 'px';
// The angle picker is different from other fields in that it updates on
// mousemove even if it's not in the middle of a drag. In future we may
// change this behavior. For now, using bindEvent_ instead of
// bindEventWithChecks_ allows it to work without a mousedown/touchstart.
this.clickWrapper_ =
Blockly.bindEvent_(svg, 'click', this, Blockly.WidgetDiv.hide);
this.moveWrapper1_ =
Blockly.bindEvent_(circle, 'mousemove', this, this.onMouseMove);
this.moveWrapper2_ =
Blockly.bindEvent_(this.gauge_, 'mousemove', this, this.onMouseMove);
Blockly.bindEvent_(this.gauge_, 'mousemove', this,
this.onMouseMove);
this.updateGraph_();
};
+1 -1
View File
@@ -88,7 +88,7 @@ Blockly.FieldCheckbox.prototype.getValue = function() {
* @param {string} strBool New state.
*/
Blockly.FieldCheckbox.prototype.setValue = function(strBool) {
var newState = (strBool == 'TRUE');
var newState = (strBool.toUpperCase() == 'TRUE');
if (this.state_ !== newState) {
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
Blockly.Events.fire(new Blockly.Events.Change(
+6 -3
View File
@@ -147,13 +147,16 @@ Blockly.FieldTextInput.prototype.showEditor_ = function(opt_quietInput) {
// Bind to keydown -- trap Enter without IME and Esc to hide.
htmlInput.onKeyDownWrapper_ =
Blockly.bindEvent_(htmlInput, 'keydown', this, this.onHtmlInputKeyDown_);
Blockly.bindEventWithChecks_(htmlInput, 'keydown', this,
this.onHtmlInputKeyDown_);
// Bind to keyup -- trap Enter; resize after every keystroke.
htmlInput.onKeyUpWrapper_ =
Blockly.bindEvent_(htmlInput, 'keyup', this, this.onHtmlInputChange_);
Blockly.bindEventWithChecks_(htmlInput, 'keyup', this,
this.onHtmlInputChange_);
// Bind to keyPress -- repeatedly resize when holding down a key.
htmlInput.onKeyPressWrapper_ =
Blockly.bindEvent_(htmlInput, 'keypress', this, this.onHtmlInputChange_);
Blockly.bindEventWithChecks_(htmlInput, 'keypress', this,
this.onHtmlInputChange_);
htmlInput.onWorkspaceChangeWrapper_ = this.resizeEditor_.bind(this);
this.workspace_.addChangeListener(htmlInput.onWorkspaceChangeWrapper_);
};
+61 -27
View File
@@ -30,6 +30,7 @@ goog.require('Blockly.Block');
goog.require('Blockly.Comment');
goog.require('Blockly.Events');
goog.require('Blockly.FlyoutButton');
goog.require('Blockly.Touch');
goog.require('Blockly.WorkspaceSvg');
goog.require('goog.dom');
goog.require('goog.events');
@@ -110,13 +111,15 @@ Blockly.Flyout = function(workspaceOptions) {
/**
* y coordinate of mousedown - used to calculate scroll distances.
* @private {number}
* @type {number}
* @private
*/
this.startDragMouseY_ = 0;
/**
* x coordinate of mousedown - used to calculate scroll distances.
* @private {number}
* @type {number}
* @private
*/
this.startDragMouseX_ = 0;
};
@@ -124,40 +127,46 @@ Blockly.Flyout = function(workspaceOptions) {
/**
* When a flyout drag is in progress, this is a reference to the flyout being
* dragged. This is used by Flyout.terminateDrag_ to reset dragMode_.
* @private {Blockly.Flyout}
* @type {Blockly.Flyout}
* @private
*/
Blockly.Flyout.startFlyout_ = null;
/**
* Event that started a drag. Used to determine the drag distance/direction and
* also passed to BlockSvg.onMouseDown_() after creating a new block.
* @private {Event}
* @type {Event}
* @private
*/
Blockly.Flyout.startDownEvent_ = null;
/**
* Flyout block where the drag/click was initiated. Used to fire click events or
* create a new block.
* @private {Event}
* @type {Event}
* @private
*/
Blockly.Flyout.startBlock_ = null;
/**
* Wrapper function called when a mouseup occurs during a background or block
* drag operation.
* @private {Array.<!Array>}
* @type {Array.<!Array>}
* @private
*/
Blockly.Flyout.onMouseUpWrapper_ = null;
/**
* Wrapper function called when a mousemove occurs during a background drag.
* @private {Array.<!Array>}
* @type {Array.<!Array>}
* @private
*/
Blockly.Flyout.onMouseMoveWrapper_ = null;
/**
* Wrapper function called when a mousemove occurs during a block drag.
* @private {Array.<!Array>}
* @type {Array.<!Array>}
* @private
*/
Blockly.Flyout.onMouseMoveBlockWrapper_ = null;
@@ -284,14 +293,15 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) {
this.hide();
Array.prototype.push.apply(this.eventWrappers_,
Blockly.bindEvent_(this.svgGroup_, 'wheel', this, this.wheel_));
Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this, this.wheel_));
if (!this.autoClose) {
this.filterWrapper_ = this.filterForCapacity_.bind(this);
this.targetWorkspace_.addChangeListener(this.filterWrapper_);
}
// Dragging the flyout up and down.
Array.prototype.push.apply(this.eventWrappers_,
Blockly.bindEvent_(this.svgGroup_, 'mousedown', this, this.onMouseDown_));
Blockly.bindEventWithChecks_(this.svgGroup_, 'mousedown', this,
this.onMouseDown_));
};
/**
@@ -709,8 +719,8 @@ Blockly.Flyout.prototype.show = function(xmlList) {
}
};
this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover',
this, deselectAll));
this.listeners_.push(Blockly.bindEventWithChecks_(this.svgBackground_,
'mouseover', this, deselectAll));
if (this.horizontalLayout_) {
this.height_ = 0;
@@ -785,7 +795,8 @@ Blockly.Flyout.prototype.layout_ = function(contents, gaps) {
var buttonSvg = button.createDom();
button.moveTo(cursorX, cursorY);
button.show();
Blockly.bindEvent_(buttonSvg, 'mouseup', button, button.onMouseUp);
Blockly.bindEventWithChecks_(buttonSvg, 'mouseup', button,
button.onMouseUp);
this.buttons_.push(button);
if (this.horizontalLayout_) {
@@ -830,9 +841,9 @@ Blockly.Flyout.prototype.clearOldBlocks_ = function() {
* @private
*/
Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) {
this.listeners_.push(Blockly.bindEvent_(root, 'mousedown', null,
this.listeners_.push(Blockly.bindEventWithChecks_(root, 'mousedown', null,
this.blockMouseDown_(block)));
this.listeners_.push(Blockly.bindEvent_(rect, 'mousedown', null,
this.listeners_.push(Blockly.bindEventWithChecks_(rect, 'mousedown', null,
this.blockMouseDown_(block)));
this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block,
block.addSelect));
@@ -844,6 +855,20 @@ Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) {
block.removeSelect));
};
/**
* Actions to take when a block in the flyout is right-clicked.
* @param {!Event} e Event that triggered the right-click. Could originate from
* a long-press in a touch environment.
* @param {Blockly.BlockSvg} block The block that was clicked.
*/
Blockly.Flyout.blockRightClick_ = function(e, block) {
Blockly.terminateDrag_();
Blockly.hideChaff(true);
block.showContextMenu_(e);
// This was a right-click, so end the gesture immediately.
Blockly.Touch.clearTouchIdentifier();
};
/**
* Handle a mouse-down on an SVG block in a non-closing flyout.
* @param {!Blockly.Block} block The flyout block to copy.
@@ -853,12 +878,11 @@ Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) {
Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
var flyout = this;
return function(e) {
Blockly.terminateDrag_();
Blockly.hideChaff(true);
if (Blockly.isRightButton(e)) {
// Right-click.
block.showContextMenu_(e);
Blockly.Flyout.blockRightClick_(e, block);
} else {
Blockly.terminateDrag_();
Blockly.hideChaff(true);
// Left-click (or middle click)
Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);
// Record the current mouse position.
@@ -867,10 +891,10 @@ Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
Blockly.Flyout.startDownEvent_ = e;
Blockly.Flyout.startBlock_ = block;
Blockly.Flyout.startFlyout_ = flyout;
Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEvent_(document,
Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
'mouseup', flyout, flyout.onMouseUp_);
Blockly.Flyout.onMouseMoveBlockWrapper_ = Blockly.bindEvent_(document,
'mousemove', flyout, flyout.onMouseMoveBlock_);
Blockly.Flyout.onMouseMoveBlockWrapper_ = Blockly.bindEventWithChecks_(
document, 'mousemove', flyout, flyout.onMouseMoveBlock_);
}
// This event has been handled. No need to bubble up to the document.
e.stopPropagation();
@@ -885,6 +909,8 @@ Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
*/
Blockly.Flyout.prototype.onMouseDown_ = function(e) {
if (Blockly.isRightButton(e)) {
// Don't start drags with right clicks.
Blockly.Touch.clearTouchIdentifier();
return;
}
Blockly.hideChaff(true);
@@ -892,10 +918,10 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) {
this.startDragMouseY_ = e.clientY;
this.startDragMouseX_ = e.clientX;
Blockly.Flyout.startFlyout_ = this;
Blockly.Flyout.onMouseMoveWrapper_ = Blockly.bindEvent_(document, 'mousemove',
this, this.onMouseMove_);
Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEvent_(document, 'mouseup',
this, Blockly.Flyout.terminateDrag_);
Blockly.Flyout.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document,
'mousemove', this, this.onMouseMove_);
Blockly.Flyout.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
'mouseup', this, Blockly.Flyout.terminateDrag_);
// This event has been handled. No need to bubble up to the document.
e.preventDefault();
e.stopPropagation();
@@ -910,6 +936,8 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) {
*/
Blockly.Flyout.prototype.onMouseUp_ = function(e) {
if (!this.workspace_.isDragging()) {
// This was a click, not a drag. End the gesture.
Blockly.Touch.clearTouchIdentifier();
if (this.autoClose) {
this.createBlockFunc_(Blockly.Flyout.startBlock_)(
Blockly.Flyout.startDownEvent_);
@@ -973,9 +1001,11 @@ Blockly.Flyout.prototype.onMouseMoveBlock_ = function(e) {
var createBlock = this.determineDragIntention_(dx, dy);
if (createBlock) {
Blockly.longStop_();
this.createBlockFunc_(Blockly.Flyout.startBlock_)(
Blockly.Flyout.startDownEvent_);
} else if (this.dragMode_ == Blockly.DRAG_FREE) {
Blockly.longStop_();
// Do a scroll.
this.onMouseMove_(e);
}
@@ -1225,7 +1255,12 @@ Blockly.Flyout.prototype.getClientRect = function() {
*/
Blockly.Flyout.terminateDrag_ = function() {
if (Blockly.Flyout.startFlyout_) {
// User was dragging the flyout background, and has stopped.
if (Blockly.Flyout.startFlyout_.dragMode_ == Blockly.DRAG_FREE) {
Blockly.Touch.clearTouchIdentifier();
}
Blockly.Flyout.startFlyout_.dragMode_ = Blockly.DRAG_NONE;
Blockly.Flyout.startFlyout_ = null;
}
if (Blockly.Flyout.onMouseUpWrapper_) {
Blockly.unbindEvent_(Blockly.Flyout.onMouseUpWrapper_);
@@ -1241,7 +1276,6 @@ Blockly.Flyout.terminateDrag_ = function() {
}
Blockly.Flyout.startDownEvent_ = null;
Blockly.Flyout.startBlock_ = null;
Blockly.Flyout.startFlyout_ = null;
};
/**
+3 -1
View File
@@ -187,8 +187,9 @@ Blockly.Generator.prototype.blockToCode = function(block) {
'Expecting string from statement block "%s".', block.type);
return [this.scrub_(block, code[0]), code[1]];
} else if (goog.isString(code)) {
var id = block.id.replace(/\$/g, '$$$$'); // Issue 251.
if (this.STATEMENT_PREFIX) {
code = this.STATEMENT_PREFIX.replace(/%1/g, '\'' + block.id + '\'') +
code = this.STATEMENT_PREFIX.replace(/%1/g, '\'' + id + '\'') +
code;
}
return this.scrub_(block, code);
@@ -297,6 +298,7 @@ Blockly.Generator.prototype.statementToCode = function(block, name) {
* @return {string} Loop contents, with infinite loop trap added.
*/
Blockly.Generator.prototype.addLoopTrap = function(branch, id) {
id = id.replace(/\$/g, '$$$$'); // Issue 251.
if (this.INFINITE_LOOP_TRAP) {
branch = this.INFINITE_LOOP_TRAP.replace(/%1/g, '\'' + id + '\'') + branch;
}
+2 -1
View File
@@ -85,7 +85,8 @@ Blockly.Icon.prototype.createIcon = function() {
this.drawIcon_(this.iconGroup_);
this.block_.getSvgRoot().appendChild(this.iconGroup_);
Blockly.bindEvent_(this.iconGroup_, 'mouseup', this, this.iconClick_);
Blockly.bindEventWithChecks_(this.iconGroup_, 'mouseup', this,
this.iconClick_);
this.updateEditable();
};
+27 -16
View File
@@ -57,7 +57,7 @@ Blockly.inject = function(container, opt_options) {
var workspace = Blockly.createMainWorkspace_(svg, options);
Blockly.init_(workspace);
workspace.markFocused();
Blockly.bindEvent_(svg, 'focus', workspace, workspace.markFocused);
Blockly.bindEventWithChecks_(svg, 'focus', workspace, workspace.markFocused);
Blockly.svgResize(workspace);
return workspace;
};
@@ -257,18 +257,19 @@ Blockly.init_ = function(mainWorkspace) {
var svg = mainWorkspace.getParentSvg();
// Supress the browser's context menu.
Blockly.bindEvent_(svg, 'contextmenu', null,
Blockly.bindEventWithChecks_(svg, 'contextmenu', null,
function(e) {
if (!Blockly.isTargetInput_(e)) {
e.preventDefault();
}
});
var workspaceResizeHandler = Blockly.bindEvent_(window, 'resize', null,
function() {
Blockly.hideChaff(true);
Blockly.svgResize(mainWorkspace);
});
var workspaceResizeHandler = Blockly.bindEventWithChecks_(window, 'resize',
null,
function() {
Blockly.hideChaff(true);
Blockly.svgResize(mainWorkspace);
});
mainWorkspace.setResizeHandlerWrapper(workspaceResizeHandler);
Blockly.inject.bindDocumentEvents_();
@@ -314,19 +315,21 @@ Blockly.init_ = function(mainWorkspace) {
*/
Blockly.inject.bindDocumentEvents_ = function() {
if (!Blockly.documentEventsBound_) {
Blockly.bindEvent_(document, 'keydown', null, Blockly.onKeyDown_);
Blockly.bindEvent_(document, 'touchend', null, Blockly.longStop_);
Blockly.bindEvent_(document, 'touchcancel', null, Blockly.longStop_);
Blockly.bindEventWithChecks_(document, 'keydown', null, Blockly.onKeyDown_);
Blockly.bindEventWithChecks_(document, 'touchend', null, Blockly.longStop_);
Blockly.bindEventWithChecks_(document, 'touchcancel', null,
Blockly.longStop_);
// Don't use bindEvent_ for document's mouseup since that would create a
// corresponding touch handler that would squeltch the ability to interact
// with non-Blockly elements.
document.addEventListener('mouseup', Blockly.onMouseUp_, false);
// Some iPad versions don't fire resize after portrait to landscape change.
if (goog.userAgent.IPAD) {
Blockly.bindEvent_(window, 'orientationchange', document, function() {
// TODO(#397): Fix for multiple blockly workspaces.
Blockly.svgResize(Blockly.getMainWorkspace());
});
Blockly.bindEventWithChecks_(window, 'orientationchange', document,
function() {
// TODO(#397): Fix for multiple blockly workspaces.
Blockly.svgResize(Blockly.getMainWorkspace());
});
}
}
Blockly.documentEventsBound_ = true;
@@ -360,11 +363,19 @@ Blockly.inject.loadSounds_ = function(pathToMedia, workspace) {
}
workspace.preloadAudio_();
};
// These are bound on mouse/touch events with Blockly.bindEventWithChecks_, so
// they restrict the touch identifier that will be recognized. But this is
// really something that happens on a click, not a drag, so that's not
// necessary.
// Android ignores any sound not loaded as a result of a user action.
soundBinds.push(
Blockly.bindEvent_(document, 'mousemove', null, unbindSounds));
Blockly.bindEventWithChecks_(document, 'mousemove', null, unbindSounds,
true));
soundBinds.push(
Blockly.bindEvent_(document, 'touchstart', null, unbindSounds));
Blockly.bindEventWithChecks_(document, 'touchstart', null, unbindSounds,
true));
};
/**
+4 -2
View File
@@ -104,8 +104,10 @@ Blockly.Options = function(options) {
// 'path' is a deprecated option which has been replaced by 'media'.
pathToMedia = options['path'] + 'media/';
}
var oneBasedIndex = !options['oneBasedIndex'];
this.RTL = rtl;
this.oneBasedIndex = oneBasedIndex;
this.collapse = hasCollapse;
this.comments = hasComments;
this.disable = hasDisable;
@@ -125,8 +127,8 @@ Blockly.Options = function(options) {
};
/**
* @type {Blockly.Workspace} the parent of the current workspace, or null if
* there is no parent workspace.
* The parent of the current workspace, or null if there is no parent workspace.
* @type {Blockly.Workspace}
**/
Blockly.Options.prototype.parentWorkspace = null;
+20 -9
View File
@@ -219,9 +219,9 @@ Blockly.Scrollbar = function(workspace, horizontal, opt_pair) {
this.positionAttribute_ = 'y';
}
var scrollbar = this;
this.onMouseDownBarWrapper_ = Blockly.bindEvent_(this.svgBackground_,
'mousedown', scrollbar, scrollbar.onMouseDownBar_);
this.onMouseDownHandleWrapper_ = Blockly.bindEvent_(this.svgHandle_,
this.onMouseDownBarWrapper_ = Blockly.bindEventWithChecks_(
this.svgBackground_, 'mousedown', scrollbar, scrollbar.onMouseDownBar_);
this.onMouseDownHandleWrapper_ = Blockly.bindEventWithChecks_(this.svgHandle_,
'mousedown', scrollbar, scrollbar.onMouseDownHandle_);
};
@@ -297,7 +297,7 @@ Blockly.Scrollbar.metricsAreEquivalent_ = function(first, second) {
* Unlink from all DOM elements to prevent memory leaks.
*/
Blockly.Scrollbar.prototype.dispose = function() {
this.onMouseUpHandle_();
this.cleanUp_();
Blockly.unbindEvent_(this.onMouseDownBarWrapper_);
this.onMouseDownBarWrapper_ = null;
Blockly.unbindEvent_(this.onMouseDownHandleWrapper_);
@@ -599,7 +599,8 @@ Blockly.Scrollbar.prototype.setVisible = function(visible) {
* @private
*/
Blockly.Scrollbar.prototype.onMouseDownBar_ = function(e) {
this.onMouseUpHandle_();
Blockly.Touch.clearTouchIdentifier(); // This is really a click.
this.cleanUp_();
if (Blockly.isRightButton(e)) {
// Right-click.
// Scrollbars have no context menu.
@@ -637,7 +638,7 @@ Blockly.Scrollbar.prototype.onMouseDownBar_ = function(e) {
* @private
*/
Blockly.Scrollbar.prototype.onMouseDownHandle_ = function(e) {
this.onMouseUpHandle_();
this.cleanUp_();
if (Blockly.isRightButton(e)) {
// Right-click.
// Scrollbars have no context menu.
@@ -648,9 +649,9 @@ Blockly.Scrollbar.prototype.onMouseDownHandle_ = function(e) {
this.startDragHandle = this.handlePosition_;
// Record the current mouse position.
this.startDragMouse = this.horizontal_ ? e.clientX : e.clientY;
Blockly.Scrollbar.onMouseUpWrapper_ = Blockly.bindEvent_(document,
Blockly.Scrollbar.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
'mouseup', this, this.onMouseUpHandle_);
Blockly.Scrollbar.onMouseMoveWrapper_ = Blockly.bindEvent_(document,
Blockly.Scrollbar.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document,
'mousemove', this, this.onMouseMoveHandle_);
e.stopPropagation();
e.preventDefault();
@@ -671,10 +672,20 @@ Blockly.Scrollbar.prototype.onMouseMoveHandle_ = function(e) {
};
/**
* Stop binding to the global mouseup and mousemove events.
* Release the scrollbar handle and reset state accordingly.
* @private
*/
Blockly.Scrollbar.prototype.onMouseUpHandle_ = function() {
Blockly.Touch.clearTouchIdentifier();
this.cleanUp_();
};
/**
* Hide chaff and stop binding to mouseup and mousemove events. Call this to
* wrap up lose ends associated with the scrollbar.
* @private
*/
Blockly.Scrollbar.prototype.cleanUp_ = function() {
Blockly.hideChaff(true);
if (Blockly.Scrollbar.onMouseUpWrapper_) {
Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_);
+6 -3
View File
@@ -27,6 +27,7 @@
goog.provide('Blockly.Toolbox');
goog.require('Blockly.Flyout');
goog.require('Blockly.Touch');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.events');
@@ -154,7 +155,7 @@ Blockly.Toolbox.prototype.init = function() {
svg.parentNode.insertBefore(this.HtmlDiv, svg);
// Clicking on toolbox closes popups.
Blockly.bindEvent_(this.HtmlDiv, 'mousedown', this,
Blockly.bindEventWithChecks_(this.HtmlDiv, 'mousedown', this,
function(e) {
if (Blockly.isRightButton(e) || e.target == this.HtmlDiv) {
// Close flyout.
@@ -163,11 +164,13 @@ Blockly.Toolbox.prototype.init = function() {
// Just close popups.
Blockly.hideChaff(true);
}
Blockly.Touch.clearTouchIdentifier(); // Don't block future drags.
});
var workspaceOptions = {
disabledPatternId: workspace.options.disabledPatternId,
parentWorkspace: workspace,
RTL: workspace.RTL,
oneBasedIndex: workspace.options.oneBasedIndex,
horizontalLayout: workspace.horizontalLayout,
toolboxPosition: workspace.options.toolboxPosition
};
@@ -468,10 +471,10 @@ goog.inherits(Blockly.Toolbox.TreeControl, goog.ui.tree.TreeControl);
Blockly.Toolbox.TreeControl.prototype.enterDocument = function() {
Blockly.Toolbox.TreeControl.superClass_.enterDocument.call(this);
var el = this.getElement();
// Add touch handler.
if (goog.events.BrowserFeature.TOUCH_ENABLED) {
var el = this.getElement();
Blockly.bindEvent_(el, goog.events.EventType.TOUCHSTART, this,
Blockly.bindEventWithChecks_(el, goog.events.EventType.TOUCHSTART, this,
this.handleTouchEvent_);
}
};
+9 -3
View File
@@ -131,9 +131,15 @@ Blockly.Tooltip.createDom = function() {
* @param {!Element} element SVG element onto which tooltip is to be bound.
*/
Blockly.Tooltip.bindMouseEvents = function(element) {
Blockly.bindEvent_(element, 'mouseover', null, Blockly.Tooltip.onMouseOver_);
Blockly.bindEvent_(element, 'mouseout', null, Blockly.Tooltip.onMouseOut_);
Blockly.bindEvent_(element, 'mousemove', null, Blockly.Tooltip.onMouseMove_);
Blockly.bindEvent_(element, 'mouseover', null,
Blockly.Tooltip.onMouseOver_);
Blockly.bindEvent_(element, 'mouseout', null,
Blockly.Tooltip.onMouseOut_);
// Don't use bindEvent_ for mousemove since that would create a
// corresponding touch handler, even though this only makes sense in the
// context of a mouseover/mouseout.
element.addEventListener('mousemove', Blockly.Tooltip.onMouseMove_, false);
};
/**
+265
View File
@@ -0,0 +1,265 @@
/**
* @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 Touch handling for Blockly.
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.Touch');
goog.require('goog.events');
goog.require('goog.events.BrowserFeature');
goog.require('goog.string');
/**
* Which touch events are we currently paying attention to?
* @type {DOMString}
* @private
*/
Blockly.Touch.touchIdentifier_ = null;
/**
* Wrapper function called when a touch mouseUp occurs during a drag operation.
* @type {Array.<!Array>}
* @private
*/
Blockly.Touch.onTouchUpWrapper_ = null;
/**
* The TOUCH_MAP lookup dictionary specifies additional touch events to fire,
* in conjunction with mouse events.
* @type {Object}
*/
Blockly.Touch.TOUCH_MAP = {};
if (goog.events.BrowserFeature.TOUCH_ENABLED) {
Blockly.Touch.TOUCH_MAP = {
'mousedown': ['touchstart'],
'mousemove': ['touchmove'],
'mouseup': ['touchend', 'touchcancel']
};
}
/**
* PID of queued long-press task.
* @private
*/
Blockly.longPid_ = 0;
/**
* Context menus on touch devices are activated using a long-press.
* Unfortunately the contextmenu touch event is currently (2015) only suported
* by Chrome. This function is fired on any touchstart event, queues a task,
* which after about a second opens the context menu. The tasks is killed
* if the touch event terminates early.
* @param {!Event} e Touch start event.
* @param {!Blockly.Block|!Blockly.WorkspaceSvg} uiObject The block or workspace
* under the touchstart event.
* @private
*/
Blockly.longStart_ = function(e, uiObject) {
Blockly.longStop_();
Blockly.longPid_ = setTimeout(function() {
e.button = 2; // Simulate a right button click.
uiObject.onMouseDown_(e);
}, Blockly.LONGPRESS);
};
/**
* Nope, that's not a long-press. Either touchend or touchcancel was fired,
* or a drag hath begun. Kill the queued long-press task.
* @private
*/
Blockly.longStop_ = function() {
if (Blockly.longPid_) {
clearTimeout(Blockly.longPid_);
Blockly.longPid_ = 0;
}
};
/**
* Handle a mouse-up anywhere on the page.
* @param {!Event} e Mouse up event.
* @private
*/
Blockly.onMouseUp_ = function(e) {
var workspace = Blockly.getMainWorkspace();
if (workspace.dragMode_ == Blockly.DRAG_NONE) {
return;
}
Blockly.Touch.clearTouchIdentifier();
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
workspace.dragMode_ = Blockly.DRAG_NONE;
// Unbind the touch event if it exists.
if (Blockly.Touch.onTouchUpWrapper_) {
Blockly.unbindEvent_(Blockly.Touch.onTouchUpWrapper_);
Blockly.Touch.onTouchUpWrapper_ = null;
}
if (Blockly.onMouseMoveWrapper_) {
Blockly.unbindEvent_(Blockly.onMouseMoveWrapper_);
Blockly.onMouseMoveWrapper_ = null;
}
};
/**
* Handle a mouse-move on SVG drawing surface.
* @param {!Event} e Mouse move event.
* @private
*/
Blockly.onMouseMove_ = function(e) {
var workspace = Blockly.getMainWorkspace();
if (workspace.dragMode_ != Blockly.DRAG_NONE) {
var dx = e.clientX - workspace.startDragMouseX;
var dy = e.clientY - workspace.startDragMouseY;
var metrics = workspace.startDragMetrics;
var x = workspace.startScrollX + dx;
var y = workspace.startScrollY + dy;
x = Math.min(x, -metrics.contentLeft);
y = Math.min(y, -metrics.contentTop);
x = Math.max(x, metrics.viewWidth - metrics.contentLeft -
metrics.contentWidth);
y = Math.max(y, metrics.viewHeight - metrics.contentTop -
metrics.contentHeight);
// Move the scrollbars and the page will scroll automatically.
workspace.scrollbar.set(-x - metrics.contentLeft,
-y - metrics.contentTop);
// Cancel the long-press if the drag has moved too far.
if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) {
Blockly.longStop_();
workspace.dragMode_ = Blockly.DRAG_FREE;
}
e.stopPropagation();
e.preventDefault();
}
};
/**
* Clear the touch identifier that tracks which touch stream to pay attention
* to. This ends the current drag/gesture and allows other pointers to be
* captured.
*/
Blockly.Touch.clearTouchIdentifier = function() {
Blockly.Touch.touchIdentifier_ = null;
};
/**
* Decide whether Blockly should handle or ignore this event.
* Mouse and touch events require special checks because we only want to deal
* with one touch stream at a time. All other events should always be handled.
* @param {!Event} e The event to check.
* @return {boolean} True if this event should be passed through to the
* registered handler; false if it should be blocked.
*/
Blockly.Touch.shouldHandleEvent = function(e) {
return !Blockly.Touch.isMouseOrTouchEvent(e) ||
Blockly.Touch.checkTouchIdentifier(e);
};
/**
* Check whether the touch identifier on the event matches the current saved
* identifier. If there is no identifier, that means it's a mouse event and
* we'll use the identifier "mouse". This means we won't deal well with
* multiple mice being used at the same time. That seems okay.
* If the current identifier was unset, save the identifier from the
* event. This starts a drag/gesture, during which touch events with other
* identifiers will be silently ignored.
* @param {!Event} e Mouse event or touch event.
* @return {boolean} Whether the identifier on the event matches the current
* saved identifier.
*/
Blockly.Touch.checkTouchIdentifier = function(e) {
var identifier = (e.changedTouches && e.changedTouches[0] &&
e.changedTouches[0].identifier != undefined &&
e.changedTouches[0].identifier != null) ?
e.changedTouches[0].identifier : 'mouse';
// if (Blockly.touchIdentifier_ )is insufficient because android touch
// identifiers may be zero.
if (Blockly.Touch.touchIdentifier_ != undefined &&
Blockly.Touch.touchIdentifier_ != null) {
// We're already tracking some touch/mouse event. Is this from the same
// source?
return Blockly.Touch.touchIdentifier_ == identifier;
}
if (e.type == 'mousedown' || e.type == 'touchstart') {
// No identifier set yet, and this is the start of a drag. Set it and
// return.
Blockly.Touch.touchIdentifier_ = identifier;
return true;
}
// There was no identifier yet, but this wasn't a start event so we're going
// to ignore it. This probably means that another drag finished while this
// pointer was down.
return false;
};
/**
* Set an event's clientX and clientY from its first changed touch. Use this to
* make a touch event work in a mouse event handler.
* @param {!Event} e A touch event.
*/
Blockly.Touch.setClientFromTouch = function(e) {
if (goog.string.startsWith(e.type, 'touch')) {
// Map the touch event's properties to the event.
var touchPoint = e.changedTouches[0];
e.clientX = touchPoint.clientX;
e.clientY = touchPoint.clientY;
}
};
/**
* Check whether a given event is a mouse or touch event.
* @param {!Event} e An event.
* @return {boolean} true if it is a mouse or touch event; false otherwise.
*/
Blockly.Touch.isMouseOrTouchEvent = function(e) {
return goog.string.startsWith(e.type, 'touch') ||
goog.string.startsWith(e.type, 'mouse');
};
/**
* Split an event into an array of events, one per changed touch or mouse
* point.
* @param {!Event} e A mouse event or a touch event with one or more changed
* touches.
* @return {!Array.<!Event>} An array of mouse or touch events. Each touch
* event will have exactly one changed touch.
*/
Blockly.Touch.splitEventByTouches = function(e) {
var events = [];
if (e.changedTouches) {
for (var i = 0; i < e.changedTouches.length; i++) {
var newEvent = {
type: e.type,
changedTouches: [e.changedTouches[i]],
target: e.target,
stopPropagation: function(){ e.stopPropagation(); },
preventDefault: function(){ e.preventDefault(); }
};
events[i] = newEvent;
}
} else {
events.push(e);
}
return events;
};
+1 -1
View File
@@ -195,7 +195,7 @@ Blockly.Trashcan.prototype.createDom = function() {
this.svgLid_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
this.workspace_.options.pathToMedia + Blockly.SPRITE.url);
Blockly.bindEvent_(this.svgGroup_, 'mouseup', this, this.click);
Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup', this, this.click);
this.animateLid_();
return this.svgGroup_;
};
+84 -31
View File
@@ -28,6 +28,7 @@
goog.provide('Blockly.utils');
goog.require('Blockly.Touch');
goog.require('goog.dom');
goog.require('goog.events.BrowserFeature');
goog.require('goog.math.Coordinate');
@@ -90,7 +91,69 @@ Blockly.hasClass_ = function(element, className) {
};
/**
* Bind an event to a function call.
* Bind an event to a function call. When calling the function, verifies that
* it belongs to the touch stream that is currently being processsed, and splits
* multitouch events into multiple events as needed.
* @param {!Node} node Node upon which to listen.
* @param {string} name Event name to listen to (e.g. 'mousedown').
* @param {Object} thisObject The value of 'this' in the function.
* @param {!Function} func Function to call when event is triggered.
* @param {boolean} opt_noCaptureIdentifier True if triggering on this event
* should not block execution of other event handlers on this touch or other
* simultaneous touches.
* @return {!Array.<!Array>} Opaque data that can be passed to unbindEvent_.
* @private
*/
Blockly.bindEventWithChecks_ = function(node, name, thisObject, func,
opt_noCaptureIdentifier) {
var handled = false;
var wrapFunc = function(e) {
var captureIdentifier = !opt_noCaptureIdentifier;
// Handle each touch point separately. If the event was a mouse event, this
// will hand back an array with one element, which we're fine handling.
var events = Blockly.Touch.splitEventByTouches(e);
for (var i = 0, event; event = events[i]; i++) {
if (captureIdentifier && !Blockly.Touch.shouldHandleEvent(event)) {
continue;
}
Blockly.Touch.setClientFromTouch(event);
if (thisObject) {
func.call(thisObject, event);
} else {
func(event);
}
handled = true;
}
};
node.addEventListener(name, wrapFunc, false);
var bindData = [[node, name, wrapFunc]];
// Add equivalent touch event.
if (name in Blockly.Touch.TOUCH_MAP) {
var touchWrapFunc = function(e) {
wrapFunc(e);
// Stop the browser from scrolling/zooming the page.
if (handled) {
e.preventDefault();
}
};
for (var i = 0, eventName;
eventName = Blockly.Touch.TOUCH_MAP[name][i]; i++) {
node.addEventListener(eventName, touchWrapFunc, false);
bindData.push([node, eventName, touchWrapFunc]);
}
}
return bindData;
};
/**
* Bind an event to a function call. Handles multitouch events by using the
* coordinates of the first changed touch, and doesn't do any safety checks for
* simultaneous event processing.
* @deprecated in favor of bindEventWithChecks_, but preserved for external
* users.
* @param {!Node} node Node upon which to listen.
* @param {string} name Event name to listen to (e.g. 'mousedown').
* @param {Object} thisObject The value of 'this' in the function.
@@ -99,18 +162,20 @@ Blockly.hasClass_ = function(element, className) {
* @private
*/
Blockly.bindEvent_ = function(node, name, thisObject, func) {
if (thisObject) {
var wrapFunc = function(e) {
var wrapFunc = function(e) {
if (thisObject) {
func.call(thisObject, e);
};
} else {
var wrapFunc = func;
}
} else {
func(e);
}
};
node.addEventListener(name, wrapFunc, false);
var bindData = [[node, name, wrapFunc]];
// Add equivalent touch event.
if (name in Blockly.bindEvent_.TOUCH_MAP) {
wrapFunc = function(e) {
if (name in Blockly.Touch.TOUCH_MAP) {
var touchWrapFunc = function(e) {
// Punt on multitouch events.
if (e.changedTouches.length == 1) {
// Map the touch event's properties to the event.
@@ -118,33 +183,20 @@ Blockly.bindEvent_ = function(node, name, thisObject, func) {
e.clientX = touchPoint.clientX;
e.clientY = touchPoint.clientY;
}
func.call(thisObject, e);
wrapFunc(e);
// Stop the browser from scrolling/zooming the page.
e.preventDefault();
};
for (var i = 0, eventName;
eventName = Blockly.bindEvent_.TOUCH_MAP[name][i]; i++) {
node.addEventListener(eventName, wrapFunc, false);
bindData.push([node, eventName, wrapFunc]);
eventName = Blockly.Touch.TOUCH_MAP[name][i]; i++) {
node.addEventListener(eventName, touchWrapFunc, false);
bindData.push([node, eventName, touchWrapFunc]);
}
}
return bindData;
};
/**
* The TOUCH_MAP lookup dictionary specifies additional touch events to fire,
* in conjunction with mouse events.
* @type {Object}
*/
Blockly.bindEvent_.TOUCH_MAP = {};
if (goog.events.BrowserFeature.TOUCH_ENABLED) {
Blockly.bindEvent_.TOUCH_MAP = {
'mousedown': ['touchstart'],
'mousemove': ['touchmove'],
'mouseup': ['touchend', 'touchcancel']
};
}
/**
* Unbind one or more events event from a function call.
* @param {!Array.<!Array>} bindData Opaque data from bindEvent_. This list is
@@ -491,12 +543,13 @@ Blockly.genUid = function() {
};
/**
* Legal characters for the unique ID.
* Should be all on a US keyboard. No XML special characters or control codes.
* Removed $ due to issue 251.
* Legal characters for the unique ID. Should be all on a US keyboard.
* No characters that conflict with XML or JSON. Requests to remove additional
* 'problematic' characters from this soup will be denied. That's your failure
* to properly escape in your own environment. Issues #251, #625, #682.
* @private
*/
Blockly.genUid.soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' +
Blockly.genUid.soup_ = '!#$%()*+,-./:;=?@[]^_`{|}~' +
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
/**
+8 -18
View File
@@ -26,6 +26,7 @@
goog.provide('Blockly.Workspace');
goog.require('goog.array');
goog.require('goog.math');
@@ -82,14 +83,14 @@ Blockly.Workspace = function(opt_options) {
};
/**
* Workspaces may be headless.
* @type {boolean} True if visible. False if headless.
* Returns `true` if the workspace is visible and `false` if it's headless.
* @type {boolean}
*/
Blockly.Workspace.prototype.rendered = false;
/**
* Maximum number of undo events in stack.
* @type {number} 0 to turn off undo, Infinity for unlimited.
* Maximum number of undo events in stack. `0` turns off undo, `Infinity` sets it to unlimited.
* @type {number}
*/
Blockly.Workspace.prototype.MAX_UNDO = 1024;
@@ -136,15 +137,7 @@ Blockly.Workspace.prototype.addTopBlock = function(block) {
* @param {!Blockly.Block} block Block to remove.
*/
Blockly.Workspace.prototype.removeTopBlock = function(block) {
var found = false;
for (var child, i = 0; child = this.topBlocks_[i]; i++) {
if (child == block) {
this.topBlocks_.splice(i, 1);
found = true;
break;
}
}
if (!found) {
if (!goog.array.remove(this.topBlocks_, block)) {
throw 'Block not present in workspace\'s list of top-most blocks.';
}
};
@@ -369,7 +362,7 @@ Blockly.Workspace.prototype.getWidth = function() {
* Obtain a newly created block.
* @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @return {!Blockly.Block} The created block.
*/
@@ -444,10 +437,7 @@ Blockly.Workspace.prototype.addChangeListener = function(func) {
* @param {Function} func Function to stop calling.
*/
Blockly.Workspace.prototype.removeChangeListener = function(func) {
var i = this.listeners_.indexOf(func);
if (i != -1) {
this.listeners_.splice(i, 1);
}
goog.array.remove(this.listeners_, func);
};
/**
+31 -17
View File
@@ -32,6 +32,7 @@ goog.require('Blockly.ConnectionDB');
goog.require('Blockly.constants');
goog.require('Blockly.Options');
goog.require('Blockly.ScrollbarPair');
goog.require('Blockly.Touch');
goog.require('Blockly.Trashcan');
goog.require('Blockly.Workspace');
goog.require('Blockly.Xml');
@@ -68,14 +69,15 @@ Blockly.WorkspaceSvg = function(options) {
goog.inherits(Blockly.WorkspaceSvg, Blockly.Workspace);
/**
* Wrapper function called when a resize event occurs.
* @type {Array.<!Array>} Data that can be passed to unbindEvent_
* A wrapper function called when a resize event occurs. You can pass the result to `unbindEvent_`.
* @type {Array.<!Array>}
*/
Blockly.WorkspaceSvg.prototype.resizeHandlerWrapper_ = null;
/**
* Svg workspaces are user-visible (as opposed to a headless workspace).
* @type {boolean} True if visible. False if headless.
* The render status of an SVG workspace.
* Returns `true` for visible workspaces and `false` for non-visible, or headless, workspaces.
* @type {boolean}
*/
Blockly.WorkspaceSvg.prototype.rendered = true;
@@ -240,13 +242,15 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) {
}
if (!this.isFlyout) {
Blockly.bindEvent_(this.svgGroup_, 'mousedown', this, this.onMouseDown_);
Blockly.bindEventWithChecks_(this.svgGroup_, 'mousedown', this,
this.onMouseDown_);
var thisWorkspace = this;
Blockly.bindEvent_(this.svgGroup_, 'touchstart', null,
Blockly.bindEventWithChecks_(this.svgGroup_, 'touchstart', null,
function(e) {Blockly.longStart_(e, thisWorkspace);});
if (this.options.zoomOptions && this.options.zoomOptions.wheel) {
// Mouse-wheel.
Blockly.bindEvent_(this.svgGroup_, 'wheel', this, this.onMouseWheel_);
Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this,
this.onMouseWheel_);
}
}
@@ -311,8 +315,8 @@ Blockly.WorkspaceSvg.prototype.dispose = function() {
* Obtain a newly created block.
* @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
* create a new ID.
* @return {!Blockly.BlockSvg} The created block.
*/
Blockly.WorkspaceSvg.prototype.newBlock = function(prototypeName, opt_id) {
@@ -356,6 +360,7 @@ Blockly.WorkspaceSvg.prototype.addFlyout_ = function() {
disabledPatternId: this.options.disabledPatternId,
parentWorkspace: this,
RTL: this.RTL,
oneBasedIndex: this.options.oneBasedIndex,
horizontalLayout: this.horizontalLayout,
toolboxPosition: this.options.toolboxPosition
};
@@ -535,7 +540,7 @@ Blockly.WorkspaceSvg.prototype.traceOn = function(armed) {
this.traceWrapper_ = null;
}
if (armed) {
this.traceWrapper_ = Blockly.bindEvent_(this.svgBlockCanvas_,
this.traceWrapper_ = Blockly.bindEventWithChecks_(this.svgBlockCanvas_,
'blocklySelectChange', this, function() {this.traceOn_ = false;});
}
};
@@ -647,7 +652,8 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) {
*/
Blockly.WorkspaceSvg.prototype.createVariable = function(name) {
Blockly.WorkspaceSvg.superClass_.createVariable.call(this, name);
if (this.toolbox_ && this.toolbox_.flyout_) {
// Don't refresh the toolbox if there's a drag in progress.
if (this.toolbox_ && this.toolbox_.flyout_ && !Blockly.Flyout.startFlyout_) {
this.toolbox_.refreshSelection();
}
};
@@ -704,6 +710,7 @@ Blockly.WorkspaceSvg.prototype.isDeleteArea = function(e) {
Blockly.WorkspaceSvg.prototype.onMouseDown_ = function(e) {
this.markFocused();
if (Blockly.isTargetInput_(e)) {
Blockly.Touch.clearTouchIdentifier();
return;
}
Blockly.terminateDrag_(); // In case mouse-up event was lost.
@@ -718,6 +725,8 @@ Blockly.WorkspaceSvg.prototype.onMouseDown_ = function(e) {
if (Blockly.isRightButton(e)) {
// Right-click.
this.showContextMenu_(e);
// Since this was a click, not a drag, end the gesture immediately.
Blockly.Touch.clearTouchIdentifier();
} else if (this.scrollbar) {
this.dragMode_ = Blockly.DRAG_BEGIN;
// Record the current mouse position.
@@ -731,14 +740,16 @@ Blockly.WorkspaceSvg.prototype.onMouseDown_ = function(e) {
// is turned off and double move events are not performed on a block.
// See comment in inject.js Blockly.init_ as to why mouseup events are
// bound to the document instead of the SVG's surface.
if ('mouseup' in Blockly.bindEvent_.TOUCH_MAP) {
Blockly.onTouchUpWrapper_ = Blockly.onTouchUpWrapper_ || [];
Blockly.onTouchUpWrapper_ = Blockly.onTouchUpWrapper_.concat(
Blockly.bindEvent_(document, 'mouseup', null, Blockly.onMouseUp_));
if ('mouseup' in Blockly.Touch.TOUCH_MAP) {
Blockly.Touch.onTouchUpWrapper_ = Blockly.Touch.onTouchUpWrapper_ || [];
Blockly.Touch.onTouchUpWrapper_ = Blockly.Touch.onTouchUpWrapper_.concat(
Blockly.bindEventWithChecks_(document, 'mouseup', null,
Blockly.onMouseUp_));
}
Blockly.onMouseMoveWrapper_ = Blockly.onMouseMoveWrapper_ || [];
Blockly.onMouseMoveWrapper_ = Blockly.onMouseMoveWrapper_.concat(
Blockly.bindEvent_(document, 'mousemove', null, Blockly.onMouseMove_));
Blockly.bindEventWithChecks_(document, 'mousemove', null,
Blockly.onMouseMove_));
}
// This event has been handled. No need to bubble up to the document.
e.stopPropagation();
@@ -1295,8 +1306,10 @@ Blockly.WorkspaceSvg.prototype.updateGridPattern_ = function() {
* .flyoutWidth: Width of the flyout if it is always open. Otherwise zero.
* .flyoutHeight: Height of flyout if it is always open. Otherwise zero.
* .toolboxPosition: Top, bottom, left or right.
* @return {Object} Contains size and position metrics of a top level workspace.
* @return {!Object} Contains size and position metrics of a top level
* workspace.
* @private
* @this Blockly.WorkspaceSvg
*/
Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_ = function() {
var svgSize = Blockly.svgSize(this.getParentSvg());
@@ -1372,6 +1385,7 @@ Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_ = function() {
* @param {!Object} xyRatio Contains an x and/or y property which is a float
* between 0 and 1 specifying the degree of scrolling.
* @private
* @this Blockly.WorkspaceSvg
*/
Blockly.WorkspaceSvg.setTopLevelWorkspaceMetrics_ = function(xyRatio) {
if (!this.scrollbar) {
+13 -8
View File
@@ -33,13 +33,14 @@ goog.require('goog.dom');
/**
* Encode a block tree as XML.
* @param {!Blockly.Workspace} workspace The workspace containing blocks.
* @param {boolean} opt_noId True if the encoder should skip the block ids.
* @return {!Element} XML document.
*/
Blockly.Xml.workspaceToDom = function(workspace) {
Blockly.Xml.workspaceToDom = function(workspace, opt_noId) {
var xml = goog.dom.createDom('xml');
var blocks = workspace.getTopBlocks(true);
for (var i = 0, block; block = blocks[i]; i++) {
xml.appendChild(Blockly.Xml.blockToDomWithXY(block));
xml.appendChild(Blockly.Xml.blockToDomWithXY(block, opt_noId));
}
return xml;
};
@@ -47,14 +48,15 @@ Blockly.Xml.workspaceToDom = function(workspace) {
/**
* Encode a block subtree as XML with XY coordinates.
* @param {!Blockly.Block} block The root block to encode.
* @param {boolean} opt_noId True if the encoder should skip the block id.
* @return {!Element} Tree of XML elements.
*/
Blockly.Xml.blockToDomWithXY = function(block) {
Blockly.Xml.blockToDomWithXY = function(block, opt_noId) {
var width; // Not used in LTR.
if (block.workspace.RTL) {
width = block.workspace.getWidth();
}
var element = Blockly.Xml.blockToDom(block);
var element = Blockly.Xml.blockToDom(block, opt_noId);
var xy = block.getRelativeToSurfaceXY();
element.setAttribute('x',
Math.round(block.workspace.RTL ? width - xy.x : xy.x));
@@ -65,12 +67,15 @@ Blockly.Xml.blockToDomWithXY = function(block) {
/**
* Encode a block subtree as XML.
* @param {!Blockly.Block} block The root block to encode.
* @param {boolean} opt_noId True if the encoder should skip the block id.
* @return {!Element} Tree of XML elements.
*/
Blockly.Xml.blockToDom = function(block) {
Blockly.Xml.blockToDom = function(block, opt_noId) {
var element = goog.dom.createDom(block.isShadow() ? 'shadow' : 'block');
element.setAttribute('type', block.type);
element.setAttribute('id', block.id);
if (!opt_noId) {
element.setAttribute('id', block.id);
}
if (block.mutationToDom) {
// Custom data for an advanced block.
var mutation = block.mutationToDom();
@@ -125,7 +130,7 @@ Blockly.Xml.blockToDom = function(block) {
container.appendChild(Blockly.Xml.cloneShadow_(shadow));
}
if (childBlock) {
container.appendChild(Blockly.Xml.blockToDom(childBlock));
container.appendChild(Blockly.Xml.blockToDom(childBlock, opt_noId));
empty = false;
}
}
@@ -156,7 +161,7 @@ Blockly.Xml.blockToDom = function(block) {
var nextBlock = block.getNextBlock();
if (nextBlock) {
var container = goog.dom.createDom('next', null,
Blockly.Xml.blockToDom(nextBlock));
Blockly.Xml.blockToDom(nextBlock, opt_noId));
element.appendChild(container);
}
var shadow = block.nextConnection && block.nextConnection.getShadowDom();
+8 -4
View File
@@ -26,6 +26,7 @@
goog.provide('Blockly.ZoomControls');
goog.require('Blockly.Touch');
goog.require('goog.dom');
@@ -162,19 +163,22 @@ Blockly.ZoomControls.prototype.createDom = function() {
workspace.options.pathToMedia + Blockly.SPRITE.url);
// Attach event listeners.
Blockly.bindEvent_(zoomresetSvg, 'mousedown', null, function(e) {
workspace.setScale(1);
Blockly.bindEventWithChecks_(zoomresetSvg, 'mousedown', null, function(e) {
workspace.setScale(workspace.options.zoomOptions.startScale);
workspace.scrollCenter();
Blockly.Touch.clearTouchIdentifier(); // Don't block future drags.
e.stopPropagation(); // Don't start a workspace scroll.
e.preventDefault(); // Stop double-clicking from selecting text.
});
Blockly.bindEvent_(zoominSvg, 'mousedown', null, function(e) {
Blockly.bindEventWithChecks_(zoominSvg, 'mousedown', null, function(e) {
workspace.zoomCenter(1);
Blockly.Touch.clearTouchIdentifier(); // Don't block future drags.
e.stopPropagation(); // Don't start a workspace scroll.
e.preventDefault(); // Stop double-clicking from selecting text.
});
Blockly.bindEvent_(zoomoutSvg, 'mousedown', null, function(e) {
Blockly.bindEventWithChecks_(zoomoutSvg, 'mousedown', null, function(e) {
workspace.zoomCenter(-1);
Blockly.Touch.clearTouchIdentifier(); // Don't block future drags.
e.stopPropagation(); // Don't start a workspace scroll.
e.preventDefault(); // Stop double-clicking from selecting text.
});
+5 -5
View File
@@ -5,14 +5,14 @@
// Copyright 2014 Google Inc. Apache License 2.0
Blockly.Dart=new Blockly.Generator("Dart");Blockly.Dart.addReservedWords("assert,break,case,catch,class,const,continue,default,do,else,enum,extends,false,final,finally,for,if,in,is,new,null,rethrow,return,super,switch,this,throw,true,try,var,void,while,with,print,identityHashCode,identical,BidirectionalIterator,Comparable,double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,ArgumentError,AssertionError,CastError,ConcurrentModificationError,CyclicInitializationError,Error,Exception,FallThroughError,FormatException,IntegerDivisionByZeroException,NoSuchMethodError,NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,StateError,TypeError,UnimplementedError,UnsupportedError");
Blockly.Dart.ORDER_ATOMIC=0;Blockly.Dart.ORDER_UNARY_POSTFIX=1;Blockly.Dart.ORDER_UNARY_PREFIX=2;Blockly.Dart.ORDER_MULTIPLICATIVE=3;Blockly.Dart.ORDER_ADDITIVE=4;Blockly.Dart.ORDER_SHIFT=5;Blockly.Dart.ORDER_BITWISE_AND=6;Blockly.Dart.ORDER_BITWISE_XOR=7;Blockly.Dart.ORDER_BITWISE_OR=8;Blockly.Dart.ORDER_RELATIONAL=9;Blockly.Dart.ORDER_EQUALITY=10;Blockly.Dart.ORDER_LOGICAL_AND=11;Blockly.Dart.ORDER_LOGICAL_OR=12;Blockly.Dart.ORDER_IF_NULL=13;Blockly.Dart.ORDER_CONDITIONAL=14;
Blockly.Dart.ORDER_CASCADE=15;Blockly.Dart.ORDER_ASSIGNMENT=16;Blockly.Dart.ORDER_NONE=99;Blockly.Dart.ONE_BASED_INDEXING=!0;
Blockly.Dart.ORDER_CASCADE=15;Blockly.Dart.ORDER_ASSIGNMENT=16;Blockly.Dart.ORDER_NONE=99;
Blockly.Dart.init=function(a){Blockly.Dart.definitions_=Object.create(null);Blockly.Dart.functionNames_=Object.create(null);Blockly.Dart.variableDB_?Blockly.Dart.variableDB_.reset():Blockly.Dart.variableDB_=new Blockly.Names(Blockly.Dart.RESERVED_WORDS_);var b=[];a=a.variableList;if(a.length){for(var c=0;c<a.length;c++)b[c]=Blockly.Dart.variableDB_.getName(a[c],Blockly.Variables.NAME_TYPE);Blockly.Dart.definitions_.variables="var "+b.join(", ")+";"}};
Blockly.Dart.finish=function(a){a&&(a=Blockly.Dart.prefixLines(a,Blockly.Dart.INDENT));a="main() {\n"+a+"}";var b=[],c=[],d;for(d in Blockly.Dart.definitions_){var e=Blockly.Dart.definitions_[d];e.match(/^import\s/)?b.push(e):c.push(e)}delete Blockly.Dart.definitions_;delete Blockly.Dart.functionNames_;Blockly.Dart.variableDB_.reset();return(b.join("\n")+"\n\n"+c.join("\n\n")).replace(/\n\n+/g,"\n\n").replace(/\n*$/,"\n\n\n")+a};Blockly.Dart.scrubNakedValue=function(a){return a+";\n"};
Blockly.Dart.quote_=function(a){a=a.replace(/\\/g,"\\\\").replace(/\n/g,"\\\n").replace(/\$/g,"\\$").replace(/'/g,"\\'");return"'"+a+"'"};
Blockly.Dart.scrub_=function(a,b){var c="";if(!a.outputConnection||!a.outputConnection.targetConnection){var d=a.getCommentText();(d=Blockly.utils.wrap(d,Blockly.Dart.COMMENT_WRAP-3))&&(c=a.getProcedureDef?c+Blockly.Dart.prefixLines(d+"\n","/// "):c+Blockly.Dart.prefixLines(d+"\n","// "));for(var e=0;e<a.inputList.length;e++)a.inputList[e].type==Blockly.INPUT_VALUE&&(d=a.inputList[e].connection.targetBlock())&&(d=Blockly.Dart.allNestedComments(d))&&(c+=Blockly.Dart.prefixLines(d,"// "))}e=a.nextConnection&&
a.nextConnection.targetBlock();e=Blockly.Dart.blockToCode(e);return c+b+e};
Blockly.Dart.getAdjusted=function(a,b,c,d,e){c=c||0;e=e||Blockly.Dart.ORDER_NONE;Blockly.Dart.ONE_BASED_INDEXING&&c--;var f=Blockly.Dart.ONE_BASED_INDEXING?"1":"0";a=c?Blockly.Dart.valueToCode(a,b,Blockly.Dart.ORDER_ADDITIVE)||f:d?Blockly.Dart.valueToCode(a,b,Blockly.Dart.ORDER_UNARY_PREFIX)||f:Blockly.Dart.valueToCode(a,b,e)||f;if(Blockly.isNumber(a))a=parseInt(a,10)+c,d&&(a=-a);else{if(0<c){a=a+" + "+c;var g=Blockly.Dart.ORDER_ADDITIVE}else 0>c&&(a=a+" - "+-c,g=Blockly.Dart.ORDER_ADDITIVE);d&&(a=
c?"-("+a+")":"-"+a,g=Blockly.Dart.ORDER_UNARY_PREFIX);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.Dart.colour={};Blockly.Dart.addReservedWords("Math");Blockly.Dart.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.Dart.ORDER_ATOMIC]};
Blockly.Dart.getAdjusted=function(a,b,c,d,e){c=c||0;e=e||Blockly.Dart.ORDER_NONE;a.workspace.options.oneBasedIndex&&c--;var f=a.workspace.options.oneBasedIndex?"1":"0";a=c?Blockly.Dart.valueToCode(a,b,Blockly.Dart.ORDER_ADDITIVE)||f:d?Blockly.Dart.valueToCode(a,b,Blockly.Dart.ORDER_UNARY_PREFIX)||f:Blockly.Dart.valueToCode(a,b,e)||f;if(Blockly.isNumber(a))a=parseInt(a,10)+c,d&&(a=-a);else{if(0<c){a=a+" + "+c;var g=Blockly.Dart.ORDER_ADDITIVE}else 0>c&&(a=a+" - "+-c,g=Blockly.Dart.ORDER_ADDITIVE);
d&&(a=c?"-("+a+")":"-"+a,g=Blockly.Dart.ORDER_UNARY_PREFIX);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.Dart.colour={};Blockly.Dart.addReservedWords("Math");Blockly.Dart.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.Dart.ORDER_ATOMIC]};
Blockly.Dart.colour_random=function(a){Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";return[Blockly.Dart.provideFunction_("colour_random",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"() {"," String hex = '0123456789abcdef';"," var rnd = new Math.Random();"," return '#${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}'"," '${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}'"," '${hex[rnd.nextInt(16)]}${hex[rnd.nextInt(16)]}';","}"])+"()",Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.colour_rgb=function(a){var b=Blockly.Dart.valueToCode(a,"RED",Blockly.Dart.ORDER_NONE)||0,c=Blockly.Dart.valueToCode(a,"GREEN",Blockly.Dart.ORDER_NONE)||0;a=Blockly.Dart.valueToCode(a,"BLUE",Blockly.Dart.ORDER_NONE)||0;Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";return[Blockly.Dart.provideFunction_("colour_rgb",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(num r, num g, num b) {"," num rn = (Math.max(Math.min(r, 1), 0) * 255).round();"," String rs = rn.toInt().toRadixString(16);",
" rs = '0$rs';"," rs = rs.substring(rs.length - 2);"," num gn = (Math.max(Math.min(g, 1), 0) * 255).round();"," String gs = gn.toInt().toRadixString(16);"," gs = '0$gs';"," gs = gs.substring(gs.length - 2);"," num bn = (Math.max(Math.min(b, 1), 0) * 255).round();"," String bs = bn.toInt().toRadixString(16);"," bs = '0$bs';"," bs = bs.substring(bs.length - 2);"," return '#$rs$gs$bs';","}"])+"("+b+", "+c+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};
@@ -21,7 +21,7 @@ Blockly.Dart.colour_blend=function(a){var b=Blockly.Dart.valueToCode(a,"COLOUR1"
" num bn = (b1 * (1 - ratio) + b2 * ratio).round();"," String bs = bn.toInt().toRadixString(16);"," rs = '0$rs';"," rs = rs.substring(rs.length - 2);"," gs = '0$gs';"," gs = gs.substring(gs.length - 2);"," bs = '0$bs';"," bs = bs.substring(bs.length - 2);"," return '#$rs$gs$bs';","}"])+"("+b+", "+c+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.lists={};Blockly.Dart.addReservedWords("Math");Blockly.Dart.lists_create_empty=function(a){return["[]",Blockly.Dart.ORDER_ATOMIC]};Blockly.Dart.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.Dart.valueToCode(a,"ADD"+c,Blockly.Dart.ORDER_NONE)||"null";return["["+b.join(", ")+"]",Blockly.Dart.ORDER_ATOMIC]};
Blockly.Dart.lists_repeat=function(a){var b=Blockly.Dart.valueToCode(a,"ITEM",Blockly.Dart.ORDER_NONE)||"null";return["new List.filled("+(Blockly.Dart.valueToCode(a,"NUM",Blockly.Dart.ORDER_NONE)||"0")+", "+b+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.lists_length=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+".length",Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.lists_isEmpty=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+".isEmpty",Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''";a=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+"."+b+"("+c+")";return Blockly.Dart.ONE_BASED_INDEXING?[a+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[a,Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''",b=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.lists_getIndex=function(a){function b(){var a=Blockly.Dart.variableDB_.getDistinctName("tmp_list",Blockly.Variables.NAME_TYPE),b="List "+a+" = "+e+";\n";e=a;return b}var c=a.getFieldValue("MODE")||"GET",d=a.getFieldValue("WHERE")||"FROM_START",e=Blockly.Dart.valueToCode(a,"VALUE","RANDOM"==d||"FROM_END"==d?Blockly.Dart.ORDER_NONE:Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]";if(("RANDOM"!=d||"REMOVE"!=c)&&"FROM_END"!=d||e.match(/^\w+$/))switch(d){case "FIRST":if("GET"==c)return[e+".first",
Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+".removeAt(0)",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeAt(0);\n";break;case "LAST":if("GET"==c)return[e+".last",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+".removeLast()",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeLast();\n";break;case "FROM_START":d=Blockly.Dart.getAdjusted(a,"AT");if("GET"==c)return[e+"["+d+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+
".removeAt("+d+")",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeAt("+d+");\n";break;case "FROM_END":d=Blockly.Dart.getAdjusted(a,"AT",1,!1,Blockly.Dart.ORDER_ADDITIVE);if("GET"==c)return[e+"["+e+".length - "+d+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c||"REMOVE"==c){a=e+".removeAt("+e+".length - "+d+")";if("GET_REMOVE"==c)return[a,Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return a+";\n"}break;case "RANDOM":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";
@@ -79,7 +79,7 @@ Blockly.Dart.procedures_ifreturn=function(a){var b="if ("+(Blockly.Dart.valueToC
Blockly.Dart.text_join=function(a){switch(a.itemCount_){case 0:return["''",Blockly.Dart.ORDER_ATOMIC];case 1:return[(Blockly.Dart.valueToCode(a,"ADD0",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".toString()",Blockly.Dart.ORDER_UNARY_POSTFIX];default:for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.Dart.valueToCode(a,"ADD"+c,Blockly.Dart.ORDER_NONE)||"''";a="["+b.join(",")+"].join()";return[a,Blockly.Dart.ORDER_UNARY_POSTFIX]}};
Blockly.Dart.text_append=function(a){var b=Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);a=Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_NONE)||"''";return b+" = ["+b+", "+a+"].join();\n"};Blockly.Dart.text_length=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".length",Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.text_isEmpty=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".isEmpty",Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''";a=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+"."+b+"("+c+")";return Blockly.Dart.ONE_BASED_INDEXING?[a+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[a,Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''",b=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};
Blockly.Dart.text_charAt=function(a){var b=a.getFieldValue("WHERE")||"FROM_START",c=Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''";switch(b){case "FIRST":return[c+"[0]",Blockly.Dart.ORDER_UNARY_POSTFIX];case "FROM_START":return a=Blockly.Dart.getAdjusted(a,"AT"),[c+"["+a+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];case "LAST":case "FROM_END":return a=Blockly.Dart.getAdjusted(a,"AT",1),b=Blockly.Dart.provideFunction_("text_get_from_end",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+
"(String text, num x) {"," return text[text.length - x];","}"]),[b+"("+c+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX];case "RANDOM":return Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;",b=Blockly.Dart.provideFunction_("text_random_letter",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(String text) {"," int x = new Math.Random().nextInt(text.length);"," return text[x];","}"]),[b+"("+c+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}throw"Unhandled option (text_charAt).";};
Blockly.Dart.text_getSubstring=function(a){var b=Blockly.Dart.valueToCode(a,"STRING",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"==c&&"LAST"==d)a=b;else if(b.match(/^'?\w+'?$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":var e=Blockly.Dart.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.Dart.getAdjusted(a,"AT1",1,!1,Blockly.Dart.ORDER_ADDITIVE);e=b+".length - "+e;break;case "FIRST":e="0";break;default:throw"Unhandled option (text_getSubstring).";
+4 -4
View File
@@ -25,7 +25,7 @@
<script src="../../accessible/tree.service.js"></script>
<script src="../../accessible/translate.pipe.js"></script>
<script src="../../accessible/field.component.js"></script>
<script src="../../accessible/field-segment.component.js"></script>
<script src="../../accessible/toolbox-tree.component.js"></script>
<script src="../../accessible/toolbox.component.js"></script>
<script src="../../accessible/workspace-tree.component.js"></script>
@@ -71,11 +71,11 @@
<script>
var ACCESSIBLE_GLOBALS = {
// Prefix of path to sound files.
mediaPathPrefix: '../../accessible/media/',
// Additional buttons for the workspace toolbar that
// go before the "Clear Workspace" button.
toolbarButtonConfig: [],
// Prefix of path to sound files.
mediaPathPrefix: '../../accessible/media/'
toolbarButtonConfig: []
};
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(blocklyApp.AppView);
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -36,6 +36,7 @@ goog.require('goog.string');
goog.require('goog.ui.PopupColorPicker');
goog.require('goog.ui.ColorPicker');
/**
* Controller for the Blockly Factory
* @constructor
@@ -78,7 +79,7 @@ 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.
* block type to its XML text representation.
*/
AppController.prototype.importBlockLibraryFromFile = function() {
var self = this;
@@ -90,14 +91,14 @@ AppController.prototype.importBlockLibraryFromFile = function() {
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
// 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.
// 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'
@@ -124,16 +125,16 @@ AppController.prototype.importBlockLibraryFromFile = function() {
/**
* Tied to the 'Export Block Library' button. Exports block library to file that
* contains JSON mapping each block type to its xml text representation.
* contains JSON mapping each block type to its XML text representation.
*/
AppController.prototype.exportBlockLibraryToFile = function() {
// Get map of block type to xml.
// Get map of block type to XML.
var blockLib = this.blockLibraryController.getBlockLibrary();
// Concatenate the xmls, each separated by a blank line.
// 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.');
'library.', 'library.xml');
// Download file if all necessary parameters are provided.
if (filename) {
FactoryUtils.createAndDownloadFile(blockLibText, filename, 'xml');
@@ -144,11 +145,10 @@ AppController.prototype.exportBlockLibraryToFile = function() {
};
/**
* Converts an object mapping block type to xml to text file for output.
* Converts an object mapping block type to XML to text file for output.
* @param {!Object} blockXmlMap - Object mapping block type to XML.
* @return {string} XML text containing the block XMLs.
* @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.
@@ -156,29 +156,28 @@ AppController.prototype.formatBlockLibraryForExport_ = function(blockXmlMap) {
'xmlns':"http://www.w3.org/1999/xhtml"
});
// Append each block node to xml dom.
// 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 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
* Converts imported block library to an object mapping block type to block XML.
* @param {string} xmlText String representation of an XML with each block as
* a child node.
* @return {!Object} object mapping block type to xml text.
* @return {!Object} Object mapping block type to XML text.
* @private
*/
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
// 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, '*');
@@ -190,7 +189,7 @@ AppController.prototype.formatBlockLibraryForImport_ = function(xmlText) {
// Populate map.
for (var i = 0, blockNode; blockNode = blockNodes[i]; i++) {
// Add outer xml tag to the block for proper injection in to the
// 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', {
@@ -198,7 +197,7 @@ AppController.prototype.formatBlockLibraryForImport_ = function(xmlText) {
});
xmlDom.appendChild(blockNode);
var xmlText = Blockly.Xml.domToText(xmlDom);
xmlText = Blockly.Xml.domToText(xmlDom);
// All block types should be lowercase.
var blockType = this.getBlockTypeFromXml_(xmlText).toLowerCase();
@@ -209,12 +208,11 @@ AppController.prototype.formatBlockLibraryForImport_ = function(xmlText) {
};
/**
* Extracts out block type from xml text, the kind that is saved in block
* Extracts out block type from XML text, the kind that is saved in block
* library storage.
* @param {string} xmlText A block's XML text.
* @return {string} The block type that corresponds to the provided XML text.
* @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);
@@ -233,8 +231,7 @@ AppController.prototype.getBlockTypeFromXml_ = function(xmlText) {
/**
* 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.
* @param {!Object} tabMap Map of tab name to div element that is the tab.
*/
AppController.prototype.addTabHandlers = function(tabMap) {
var self = this;
@@ -247,10 +244,9 @@ AppController.prototype.addTabHandlers = function(tabMap) {
/**
* Set the selected tab.
* @private
*
* @param {string} tabName AppController.BLOCK_FACTORY,
* AppController.WORKSPACE_FACTORY, or AppController.EXPORTER
* @private
*/
AppController.prototype.setSelected_ = function(tabName) {
this.lastSelectedTab = this.selectedTab;
@@ -259,11 +255,10 @@ AppController.prototype.setSelected_ = function(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.
* @return {!Function} The tab click handler.
* @private
*/
AppController.prototype.makeTabClickHandler_ = function(tabName) {
var self = this;
@@ -453,7 +448,6 @@ AppController.prototype.assignExporterChangeListeners = function() {
/**
* If given checkbox is checked, display given elements. Otherwise, hide.
*
* @param {!Element} checkbox - Input element of type checkbox.
* @param {!Array.<!Element>} elementArray - Array of elements to show when
* block is checked.
@@ -640,7 +634,21 @@ AppController.prototype.confirmLeavePage = function() {
* Initialize Blockly and layout. Called on page load.
*/
AppController.prototype.init = function() {
// Handle Blockly Storage with App Engine
// Blockly factory has a dependency on bits of Closure that core Blockly
// doesn't have. When you run this from file:// without a copy of Closure,
// it breaks it non-obvious ways. Warning about this for now until the
// dependency is broken.
// TODO: #668.
if (!window.goog.dom.xml) {
alert('Sorry: Closure dependency not found. We are working on removing ' +
'this dependency. In the meantime, you can use our hosted demo\n ' +
'https://blockly-demo.appspot.com/static/demos/blocklyfactory/index.html' +
'\nor use these instructions to continue running locally:\n' +
'https:developers.google.com/blockly/guides/modify/web/closure');
return;
}
// Handle Blockly Storage with App Engine.
if ('BlocklyStorage' in window) {
this.initializeBlocklyStorage();
}
@@ -684,5 +692,3 @@ AppController.prototype.init = function() {
// Workspace Factory init.
WorkspaceFactoryInit.initWorkspaceFactory(this.workspaceFactoryController);
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -37,11 +37,11 @@ goog.require('BlockExporterView');
goog.require('BlockExporterTools');
goog.require('goog.dom.xml');
/**
* BlockExporter Controller Class
* @constructor
*
* @param {!BlockLibrary.Storage} blockLibStorage - Block Library Storage.
* @constructor
*/
BlockExporterController = function(blockLibStorage) {
// BlockLibrary.Storage object containing user's saved blocks.
@@ -61,7 +61,6 @@ BlockExporterController = function(blockLibStorage) {
/**
* Set the block library storage object from which exporter exports.
*
* @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage object
* that stores the blocks.
*/
@@ -72,7 +71,6 @@ BlockExporterController.prototype.setBlockLibraryStorage =
/**
* Get the block library storage object from which exporter exports.
*
* @return {!BlockLibraryStorage} blockLibStorage - Block Library Storage object
* that stores the blocks.
*/
@@ -126,11 +124,7 @@ BlockExporterController.prototype.export = function() {
var genStubs = this.tools.getGeneratorCode(blockXmlMap,
language);
// Get the correct file extension.
if (language == 'JavaScript') {
var fileType = 'javascript';
} else {
var fileType = 'plain';
}
var fileType = (language == 'JavaScript') ? 'javascript' : 'plain';
// Download the file.
FactoryUtils.createAndDownloadFile(
genStubs, generatorStub_filename, fileType);
@@ -185,9 +179,8 @@ BlockExporterController.prototype.selectAllBlocks = function() {
};
/**
* Returns the category xml containing all blocks in the block library.
*
* @return {Element} Xml for a category to be used in toolbox.
* 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);
@@ -270,8 +263,7 @@ BlockExporterController.prototype.selectUsedBlocks = function() {
/**
* Set the array that holds the block types used in workspace factory.
*
* @param {!Array.<!string>} usedBlockTypes - Block types used in
* @param {!Array.<string>} usedBlockTypes - Block types used in
*/
BlockExporterController.prototype.setUsedBlockTypes =
function(usedBlockTypes) {
@@ -293,10 +285,9 @@ BlockExporterController.prototype.updatePreview = function() {
};
/**
* 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.
* 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();
@@ -305,8 +296,7 @@ BlockExporterController.prototype.getSelectedBlockXmlMap = function() {
/**
* Get block definition code in the selected format for selected blocks.
*
* @return {!string} The concatenation of each selected block's language code
* @return {string} The concatenation of each selected block's language code
* in the format specified in export settings.
*/
BlockExporterController.prototype.getBlockDefinitionsOfSelected = function() {
@@ -320,8 +310,7 @@ BlockExporterController.prototype.getBlockDefinitionsOfSelected = function() {
/**
* Get generator stubs in the selected language for selected blocks.
*
* @return {!string} The concatenation of each selected block's generator stub
* @return {string} The concatenation of each selected block's generator stub
* in the language specified in export settings.
*/
BlockExporterController.prototype.getGeneratorStubsOfSelected = function() {
@@ -332,4 +321,3 @@ BlockExporterController.prototype.getGeneratorStubsOfSelected = function() {
var language = document.getElementById('exportLanguage').value;
return this.tools.getGeneratorCode(blockXmlMap, language);
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -20,8 +20,8 @@
/**
* @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
* 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)
@@ -35,6 +35,7 @@ goog.require('BlockOption');
goog.require('goog.dom');
goog.require('goog.dom.xml');
/**
* Block Exporter Tools Class
* @constructor
@@ -58,17 +59,16 @@ BlockExporterTools = function() {
};
/**
* Get Blockly Block object from xml that encodes the blocks used to design
* 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
* @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.
* @private
*/
BlockExporterTools.prototype.getRootBlockFromXml_ = function(xml) {
// Render xml in hidden workspace.
// Render XML in hidden workspace.
this.hiddenWorkspace.clear();
Blockly.Xml.domToWorkspace(xml, this.hiddenWorkspace);
// Get root block.
@@ -78,9 +78,8 @@ BlockExporterTools.prototype.getRootBlockFromXml_ = function(xml) {
/**
* 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'
* @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.
*/
@@ -100,9 +99,9 @@ BlockExporterTools.prototype.getBlockDefinitions =
} 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.';
'. 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.');
'. Could not find root block in XML stored for this block.');
}
} else {
// Append warning comment and write to console.
@@ -113,14 +112,18 @@ BlockExporterTools.prototype.getBlockDefinitions =
}
blockCode.push(code);
}
// Surround json with [] and comma separate items.
if (definitionFormat == "JSON") {
return "[" + blockCode.join(",\n") + "]";
}
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',
* @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.
@@ -155,10 +158,9 @@ BlockExporterTools.prototype.getGeneratorCode =
/**
* 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
* 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.
* @param {!Object} blockXmlMap Map of block type to XML.
*/
BlockExporterTools.prototype.addBlockDefinitions = function(blockXmlMap) {
var blockDefs = this.getBlockDefinitions(blockXmlMap, 'JavaScript');
@@ -166,11 +168,10 @@ BlockExporterTools.prototype.addBlockDefinitions = function(blockXmlMap) {
};
/**
* Pulls information about all blocks in the block library to generate xml
* 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.
* @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
* @return {!Element} XML representation of the toolbox.
*/
BlockExporterTools.prototype.generateToolboxFromLibrary
= function(blockLibStorage) {
@@ -206,11 +207,10 @@ BlockExporterTools.prototype.generateToolboxFromLibrary
};
/**
* Generate xml for the workspace factory's category from imported block
* 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.
* @param {!BlockLibraryStorage} blockLibStorage Block Library Storage object.
* @return {!Element} XML representation of a category.
*/
BlockExporterTools.prototype.generateCategoryFromBlockLib =
function(blockLibStorage) {
@@ -236,9 +236,8 @@ BlockExporterTools.prototype.generateCategoryFromBlockLib =
* 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
* @param {string} blockSelectorID - ID of the div element that will contain
* the block options.
* @return {!Object} Map of block type to Block Option object.
*/
@@ -259,7 +258,7 @@ BlockExporterTools.prototype.createBlockSelectorFromLib =
// Append each block option's dom to the selector.
var blockOptions = Object.create(null);
for (var blockType in blockXmlMap) {
// Get preview block's xml.
// Get preview block's XML.
var block = FactoryUtils.getDefinedBlock(blockType, this.hiddenWorkspace);
var previewBlockXml = Blockly.Xml.workspaceToDom(this.hiddenWorkspace);
@@ -273,4 +272,3 @@ BlockExporterTools.prototype.createBlockSelectorFromLib =
}
return blockOptions;
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -33,11 +33,11 @@ goog.require('BlockExporterTools');
goog.require('BlockOption');
goog.require('goog.dom');
/**
* BlockExporter View Class
* @param {!Object} blockOptions Map of block types to BlockOption objects.
* @constructor
*
* @param {!Object} blockOptions - Map of block types to BlockOption objects.
*/
BlockExporterView = function(blockOptions) {
// Map of block types to BlockOption objects to select from.
@@ -47,8 +47,7 @@ BlockExporterView = function(blockOptions) {
/**
* Set the block options in the selector of this instance of
* BlockExporterView.
*
* @param {!Object} blockOptions - Map of block types to BlockOption objects.
* @param {!Object} blockOptions Map of block types to BlockOption objects.
*/
BlockExporterView.prototype.setBlockOptions = function(blockOptions) {
this.blockOptions = blockOptions;
@@ -56,9 +55,8 @@ BlockExporterView.prototype.setBlockOptions = function(blockOptions) {
/**
* Updates the helper text.
*
* @param {string} newText - New helper text.
* @param {boolean} opt_append - True if appending to helper Text, false if
* @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) {
@@ -81,8 +79,7 @@ BlockExporterView.prototype.listSelectedBlocks = function() {
/**
* Selects a given block type in the selector.
*
* @param {string} blockType - Type of block to selector.
* @param {string} blockType Type of block to selector.
*/
BlockExporterView.prototype.select = function(blockType) {
this.blockOptions[blockType].setSelected(true);
@@ -90,8 +87,7 @@ BlockExporterView.prototype.select = function(blockType) {
/**
* Deselects a block in the selector.
*
* @param {!Blockly.Block} block - Type of block to add to selector workspce.
* @param {!Blockly.Block} block Type of block to add to selector workspce.
*/
BlockExporterView.prototype.deselect = function(blockType) {
this.blockOptions[blockType].setSelected(false);
@@ -110,8 +106,7 @@ BlockExporterView.prototype.deselectAllBlocks = function() {
/**
* Given an array of selected blocks, selects these blocks in the view, marking
* the checkboxes accordingly.
*
* @param {Array.<Blockly.Block>} blockTypes - Array of block types to select.
* @param {Array.<Blockly.Block>} blockTypes Array of block types to select.
*/
BlockExporterView.prototype.setSelectedBlockTypes = function(blockTypes) {
for (var i = 0, blockType; blockType = blockTypes[i]; i++) {
@@ -121,8 +116,7 @@ BlockExporterView.prototype.setSelectedBlockTypes = function(blockTypes) {
/**
* Returns array of selected blocks.
*
* @return {!Array.<!string>} Array of all selected block types.
* @return {!Array.<string>} Array of all selected block types.
*/
BlockExporterView.prototype.getSelectedBlockTypes = function() {
var selectedTypes = [];
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -38,14 +38,14 @@ goog.require('BlockLibraryStorage');
goog.require('BlockLibraryView');
goog.require('BlockFactory');
/**
* Block Library Controller Class
* @constructor
*
* @param {string} blockLibraryName - Desired name of Block Library, also used
* @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
* @param {!BlockLibraryStorage} opt_blockLibraryStorage Optional storage
* object that allows user to import a block library.
* @constructor
*/
BlockLibraryController = function(blockLibraryName, opt_blockLibraryStorage) {
this.name = blockLibraryName;
@@ -58,9 +58,8 @@ BlockLibraryController = function(blockLibraryName, opt_blockLibraryStorage) {
/**
* Returns the block type of the block the user is building.
* @private
*
* @return {string} The current block's type.
* @private
*/
BlockLibraryController.prototype.getCurrentBlockType = function() {
var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
@@ -72,8 +71,7 @@ BlockLibraryController.prototype.getCurrentBlockType = function() {
/**
* Removes current block from Block Library and updates the save and delete
* buttons so that user may save block to library and but not delete.
*
* @param {string} blockType - Type of block.
* @param {string} blockType Type of block.
*/
BlockLibraryController.prototype.removeFromBlockLibrary = function() {
var blockType = this.getCurrentBlockType();
@@ -85,8 +83,7 @@ BlockLibraryController.prototype.removeFromBlockLibrary = function() {
/**
* Updates the workspace to show the block user selected from library
*
* @param {string} blockType - Block to edit on block factory.
* @param {string} blockType Block to edit on block factory.
*/
BlockLibraryController.prototype.openBlock = function(blockType) {
if (blockType) {
@@ -102,7 +99,6 @@ BlockLibraryController.prototype.openBlock = function(blockType) {
/**
* Returns type of block selected from library.
*
* @return {string} Type of block selected.
*/
BlockLibraryController.prototype.getSelectedBlockType = function() {
@@ -143,7 +139,7 @@ BlockLibraryController.prototype.saveToBlockLibrary = function() {
return;
}
// Create block xml.
// Create block XML.
var xmlElement = goog.dom.createDom('xml');
var block = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
xmlElement.appendChild(Blockly.Xml.blockToDomWithXY(block));
@@ -167,8 +163,7 @@ BlockLibraryController.prototype.saveToBlockLibrary = function() {
/**
* Checks to see if the given blockType is already in Block Library
*
* @param {string} blockType - Type of block.
* @param {string} blockType Type of block.
* @return {boolean} Boolean indicating whether or not block is in the library.
*/
BlockLibraryController.prototype.has = function(blockType) {
@@ -177,7 +172,7 @@ BlockLibraryController.prototype.has = function(blockType) {
};
/**
* Populates the dropdown menu.
* Populates the dropdown menu.
*/
BlockLibraryController.prototype.populateBlockLibrary = function() {
this.view.clearOptions();
@@ -190,19 +185,17 @@ BlockLibraryController.prototype.populateBlockLibrary = function() {
};
/**
* Return block library mapping block type to xml.
*
* @return {Object} Object mapping block type to xml text.
* 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();
};
/**
* Return stored xml of a given block type.
*
* @param {!string} blockType - The type of block.
* @return {!Element} Xml element of a given block type or null.
* Return stored XML of a given block type.
* @param {string} blockType The type of block.
* @return {!Element} XML element of a given block type or null.
*/
BlockLibraryController.prototype.getBlockXml = function(blockType) {
return this.storage.getBlockXml(blockType);
@@ -210,7 +203,6 @@ BlockLibraryController.prototype.getBlockXml = function(blockType) {
/**
* Set the block library storage object from which exporter exports.
*
* @param {!BlockLibraryStorage} blockLibStorage - Block Library Storage
* object.
*/
@@ -221,8 +213,7 @@ BlockLibraryController.prototype.setBlockLibraryStorage
/**
* Get the block library storage object from which exporter exports.
*
* @return {!BlockLibraryStorage} blockLibStorage - Block Library Storage object
* @return {!BlockLibraryStorage} blockLibStorage Block Library Storage object
* that stores the blocks.
*/
BlockLibraryController.prototype.getBlockLibraryStorage = function() {
@@ -231,7 +222,6 @@ BlockLibraryController.prototype.getBlockLibraryStorage = function() {
/**
* 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() {
@@ -240,8 +230,7 @@ BlockLibraryController.prototype.hasEmptyBlockLibrary = function() {
/**
* Get all block types stored in block library.
*
* @return {!Array<!string>} Array of block types.
* @return {!Array.<string>} Array of block types.
*/
BlockLibraryController.prototype.getStoredBlockTypes = function() {
return this.storage.getBlockTypes();
@@ -258,7 +247,6 @@ BlockLibraryController.prototype.setNoneSelected = function() {
* If there are unsaved changes to the block in open in Block Factory
* and the block is not the starter block, check if user wants to proceed,
* knowing that it will cause them to lose their changes.
*
* @return {boolean} Whether or not to proceed.
*/
BlockLibraryController.prototype.warnIfUnsavedChanges = function() {
@@ -272,9 +260,7 @@ BlockLibraryController.prototype.warnIfUnsavedChanges = function() {
/**
* Add select handler for an option of a given block type. The handler will to
* update the view and the selected block accordingly.
*
* @param {!string} blockType - The type of block represented by the option is
* for.
* @param {string} blockType The type of block represented by the option is for.
*/
BlockLibraryController.prototype.addOptionSelectHandler = function(blockType) {
var self = this;
@@ -327,7 +313,6 @@ BlockLibraryController.prototype.addOptionSelectHandlers = function() {
/**
* Update the save and delete buttons based on the current block type of the
* block the user is currently editing.
*
* @param {boolean} Whether changes to the block have been saved.
*/
BlockLibraryController.prototype.updateButtons = function(savedChanges) {
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -29,13 +29,13 @@
goog.provide('BlockLibraryStorage');
/**
* Represents a block library's storage.
* @constructor
*
* @param {string} blockLibraryName - Desired name of Block Library, also used
* @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.
* @param {Object} opt_blocks Object mapping block type to XML.
* @constructor
*/
BlockLibraryStorage = function(blockLibraryName, opt_blocks) {
// Add prefix to this.name to avoid collisions in local storage.
@@ -85,9 +85,8 @@ BlockLibraryStorage.prototype.clear = function() {
/**
* Saves block to block library.
*
* @param {string} blockType - Type of block.
* @param {Element} blockXML - The block's XML pulled from workspace.
* @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);
@@ -96,19 +95,17 @@ BlockLibraryStorage.prototype.addBlock = function(blockType, blockXML) {
/**
* Removes block from current block library (this.blocks).
*
* @param {string} blockType - Type of block.
* @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
* 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.
* @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;
@@ -120,11 +117,10 @@ BlockLibraryStorage.prototype.getBlockXml = function(blockType) {
/**
* Returns map of each block type to its corresponding xml stored in current
* Returns map of each block type to its corresponding XML stored in current
* block library (this.blocks).
*
* @param {Array.<!string>} blockTypes - Types of blocks.
* @return {!Object} Map of block type to corresponding xml.
* @param {!Array.<string>} blockTypes Types of blocks.
* @return {!Object} Map of block type to corresponding XML.
*/
BlockLibraryStorage.prototype.getBlockXmlMap = function(blockTypes) {
var blockXmlMap = {};
@@ -138,7 +134,6 @@ BlockLibraryStorage.prototype.getBlockXmlMap = function(blockTypes) {
/**
* Returns array of all block types stored in current block library.
*
* @return {!Array.<string>} Array of block types stored in library.
*/
BlockLibraryStorage.prototype.getBlockTypes = function() {
@@ -147,7 +142,6 @@ BlockLibraryStorage.prototype.getBlockTypes = function() {
/**
* Checks to see if block library is empty.
*
* @return {boolean} True if empty, false otherwise.
*/
BlockLibraryStorage.prototype.isEmpty = function() {
@@ -159,8 +153,7 @@ BlockLibraryStorage.prototype.isEmpty = function() {
/**
* Returns array of all block types stored in current block library.
*
* @return {!Array.<string>} Map of block type to corresponding xml text.
* @return {!Array.<string>} Map of block type to corresponding XML text.
*/
BlockLibraryStorage.prototype.getBlockXmlTextMap = function() {
return this.blocks;
@@ -169,10 +162,9 @@ BlockLibraryStorage.prototype.getBlockXmlTextMap = function() {
/**
* Returns boolean of whether or not a given blockType is stored in block
* library.
*
* @param {string} blockType - Type of block.
* @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;
return !!this.blocks[blockType];
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -32,6 +32,7 @@ goog.provide('BlockLibraryView');
goog.require('goog.dom');
goog.require('goog.dom.classlist');
/**
* BlockLibraryView Class
* @constructor
@@ -68,8 +69,7 @@ BlockLibraryView.prototype.hide = function() {
/**
* Creates a node of a given element type and appends to the node with given id.
*
* @param {!string} blockType - Type of block.
* @param {string} blockType - Type of block.
* @param {boolean} selected - Whether or not the option should be selected on
* the dropdown.
*/
@@ -93,7 +93,6 @@ BlockLibraryView.prototype.addOption = function(blockType, selected) {
/**
* Sets a given block type to selected and all other blocks to deselected.
* If null, deselects all blocks.
*
* @param {string} blockTypeToSelect - Type of block to select or null.
*/
BlockLibraryView.prototype.setSelectedBlockType = function(blockTypeToSelect) {
@@ -111,10 +110,9 @@ BlockLibraryView.prototype.setSelectedBlockType = function(blockTypeToSelect) {
/**
* Selects a given option.
* @private
*
* @param {!Element} option - HTML 'a' element in the dropdown that represents
* @param {!Element} option HTML 'a' element in the dropdown that represents
* a particular block type.
* @private
*/
BlockLibraryView.prototype.selectOption_ = function(option) {
goog.dom.classlist.add(option, 'dropdown-content-selected');
@@ -122,10 +120,9 @@ BlockLibraryView.prototype.selectOption_ = function(option) {
/**
* Deselects a given option.
* @private
*
* @param {!Element} option - HTML 'a' element in the dropdown that represents
* @param {!Element} option HTML 'a' element in the dropdown that represents
* a particular block type.
* @private
*/
BlockLibraryView.prototype.deselectOption_ = function(option) {
goog.dom.classlist.remove(option, 'dropdown-content-selected');
@@ -135,10 +132,9 @@ BlockLibraryView.prototype.deselectOption_ = function(option) {
* Updates the save and delete buttons to represent how the current block will
* be saved by including the block type in the button text as well as indicating
* whether the block is being saved or updated.
*
* @param {!string} blockType - The type of block being edited.
* @param {boolean} isInLibrary - Whether the block type is in the library.
* @param {boolean} savedChanges - Whether changes to block have been saved.
* @param {string} blockType The type of block being edited.
* @param {boolean} isInLibrary Whether the block type is in the library.
* @param {boolean} savedChanges Whether changes to block have been saved.
*/
BlockLibraryView.prototype.updateButtons =
function(blockType, isInLibrary, savedChanges) {
@@ -193,7 +189,6 @@ BlockLibraryView.prototype.removeSelectedOption = function() {
/**
* Returns block type of selected block.
*
* @return {string} Type of block selected.
*/
BlockLibraryView.prototype.getSelectedBlockType = function() {
@@ -204,7 +199,6 @@ BlockLibraryView.prototype.getSelectedBlockType = function() {
/**
* Returns selected option.
*
* @return {!Element} HTML 'a' element that is the option for a block type.
*/
BlockLibraryView.prototype.getSelectedOption = function() {
@@ -222,5 +216,3 @@ BlockLibraryView.prototype.clearOptions = function() {
}
}
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -30,16 +30,16 @@
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 {string} blockType - Type of block for which to create an option.
* @param {!Element} previewBlockXml - Xml element containing the preview block.
* @constructor
*/
var BlockOption = function(blockSelector, blockType, previewBlockXml) {
// The div to contain the block option.
@@ -65,7 +65,6 @@ var BlockOption = function(blockSelector, blockType, previewBlockXml) {
/**
* 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.
*/
@@ -157,8 +156,7 @@ BlockOption.prototype.centerBlock = function() {
/**
* Selects or deselects the block option.
*
* @param {!boolean} selected - True if selecting option, false if deselecting
* @param {!boolean} selected True if selecting option, false if deselecting
* option.
*/
BlockOption.prototype.setSelected = function(selected) {
@@ -170,12 +168,9 @@ BlockOption.prototype.setSelected = function(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;
};
+29 -5
View File
@@ -47,6 +47,8 @@ Blockly.Blocks['factory_base'] = {
['↓ bottom connection', 'BOTTOM']],
function(option) {
this.sourceBlock_.updateShape_(option);
// Connect a shadow block to this new input.
this.sourceBlock_.spawnOutputShadow_(option);
});
this.appendDummyInput()
.appendField(dropdown, 'CONNECTIONS');
@@ -67,6 +69,33 @@ Blockly.Blocks['factory_base'] = {
var connections = xmlElement.getAttribute('connections');
this.updateShape_(connections);
},
spawnOutputShadow_: function(option) {
// Helper method for deciding which type of outputs this block needs
// to attach shaddow blocks to.
switch (option) {
case 'LEFT':
this.connectOutputShadow_('OUTPUTTYPE');
break;
case 'TOP':
this.connectOutputShadow_('TOPTYPE');
break;
case 'BOTTOM':
this.connectOutputShadow_('BOTTOMTYPE');
break;
case 'BOTH':
this.connectOutputShadow_('TOPTYPE');
this.connectOutputShadow_('BOTTOMTYPE');
break;
}
},
connectOutputShadow_: function(outputType) {
// Helper method to create & connect shadow block.
var type = this.workspace.newBlock('type_null');
type.setShadow(true);
type.outputConnection.connect(this.getInput(outputType).connection);
type.initSvg();
type.render();
},
updateShape_: function(option) {
var outputExists = this.getInput('OUTPUTTYPE');
var topExists = this.getInput('TOPTYPE');
@@ -98,11 +127,6 @@ Blockly.Blocks['factory_base'] = {
.setCheck('Type')
.appendField(label);
this.moveInputBefore(name, 'COLOUR');
var type = this.workspace.newBlock('type_null');
type.setShadow(true);
type.outputConnection.connect(this.getInput(name).connection);
type.initSvg();
type.render();
}
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
+110 -695
View File
@@ -1,7 +1,8 @@
/**
* @license
* Blockly Demos: Block Factory
*
* Copyright 2012 Google Inc.
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,32 +19,58 @@
*/
/**
* @fileoverview JavaScript for Blockly's Block Factory application.
* @author fraser@google.com (Neil Fraser)
* @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}
*/
var mainWorkspace = null;
BlockFactory.mainWorkspace = null;
/**
* Workspace for preview of block.
* @type {Blockly.Workspace}
*/
var previewWorkspace = null;
BlockFactory.previewWorkspace = null;
/**
* Name of block if not named.
*/
var UNNAMED = 'unnamed';
BlockFactory.UNNAMED = 'unnamed';
/**
* Existing direction ('ltr' vs 'rtl') of preview.
*/
BlockFactory.oldDir = null;
/*
* The starting XML for the Block Factory main workspace. Contains the
* unmovable, undeletable factory_base block.
*/
BlockFactory.STARTER_BLOCK_XML_TEXT = '<xml><block type="factory_base" ' +
'deletable="false" movable="false"></block></xml>';
/**
* Change the language code format.
*/
function formatChange() {
BlockFactory.formatChange = function() {
var mask = document.getElementById('blocklyMask');
var languagePre = document.getElementById('languagePre');
var languageTA = document.getElementById('languageTA');
@@ -55,619 +82,63 @@ function formatChange() {
var code = languagePre.textContent.trim();
languageTA.value = code;
languageTA.focus();
updatePreview();
BlockFactory.updatePreview();
} else {
mask.style.display = 'none';
languageTA.style.display = 'none';
languagePre.style.display = 'block';
updateLanguage();
BlockFactory.updateLanguage();
}
disableEnableLink();
}
BlockFactory.disableEnableLink();
};
/**
* Update the language code based on constructs made in Blockly.
*/
function updateLanguage() {
var rootBlock = getRootBlock();
BlockFactory.updateLanguage = function() {
var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
if (!rootBlock) {
return;
}
var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
if (!blockType) {
blockType = UNNAMED;
blockType = BlockFactory.UNNAMED;
}
blockType = blockType.replace(/\W/g, '_').replace(/^(\d)/, '_\\1');
switch (document.getElementById('format').value) {
case 'JSON':
var code = formatJson_(blockType, rootBlock);
break;
case 'JavaScript':
var code = formatJavaScript_(blockType, rootBlock);
break;
}
injectCode(code, 'languagePre');
updatePreview();
}
/**
* 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
*/
function formatJson_(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 = 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(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 && 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(getOptTypesFrom(rootBlock, 'OUTPUTTYPE') || 'null');
break;
case 'BOTH':
JS.previousStatement =
JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
JS.nextStatement =
JSON.parse(getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
break;
case 'TOP':
JS.previousStatement =
JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
break;
case 'BOTTOM':
JS.nextStatement =
JSON.parse(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.
* @return {string} Generanted language code.
* @private
*/
function formatJavaScript_(blockType, rootBlock) {
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 = escapeString(contentsBlock.getFieldValue('INPUTNAME'));
}
code.push(' this.' + TYPES[contentsBlock.type] + '(' + name + ')');
var check = 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 = 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(connectionLineJs_('setOutput', 'OUTPUTTYPE'));
break;
case 'BOTH':
code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
break;
case 'TOP':
code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
break;
case 'BOTTOM':
code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
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.
* @return {string} Line of JavaScript code to create connection.
* @private
*/
function connectionLineJs_(functionName, typeName) {
var type = getOptTypesFrom(getRootBlock(), 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.<string>} Field strings.
* @private
*/
function getFieldsJs_(block) {
var fields = [];
while (block) {
if (!block.disabled && !block.getInheritedDisabled()) {
switch (block.type) {
case 'field_static':
// Result: 'hello'
fields.push(escapeString(block.getFieldValue('TEXT')));
break;
case 'field_input':
// Result: new Blockly.FieldTextInput('Hello'), 'GREET'
fields.push('new Blockly.FieldTextInput(' +
escapeString(block.getFieldValue('TEXT')) + '), ' +
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(', ') + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_angle':
// Result: new Blockly.FieldAngle(90), 'ANGLE'
fields.push('new Blockly.FieldAngle(' +
Number(block.getFieldValue('ANGLE')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_checkbox':
// Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
fields.push('new Blockly.FieldCheckbox(' +
escapeString(block.getFieldValue('CHECKED')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_colour':
// Result: new Blockly.FieldColour('#ff0000'), 'COLOUR'
fields.push('new Blockly.FieldColour(' +
escapeString(block.getFieldValue('COLOUR')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_date':
// Result: new Blockly.FieldDate('2015-02-04'), 'DATE'
fields.push('new Blockly.FieldDate(' +
escapeString(block.getFieldValue('DATE')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_variable':
// Result: new Blockly.FieldVariable('item'), 'VAR'
var varname = escapeString(block.getFieldValue('TEXT') || null);
fields.push('new Blockly.FieldVariable(' + varname + '), ' +
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] = '[' + escapeString(block.getFieldValue('USER' + i)) +
', ' + escapeString(block.getFieldValue('CPU' + i)) + ']';
}
if (options.length) {
fields.push('new Blockly.FieldDropdown([' +
options.join(', ') + ']), ' +
escapeString(block.getFieldValue('FIELDNAME')));
}
break;
case 'field_image':
// Result: new Blockly.FieldImage('http://...', 80, 60, '*')
var src = escapeString(block.getFieldValue('SRC'));
var width = Number(block.getFieldValue('WIDTH'));
var height = Number(block.getFieldValue('HEIGHT'));
var alt = 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.<string|!Object>} Array of static text and field configs.
* @private
*/
function getFieldsJson_(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;
}
/**
* Escape a string.
* @param {string} string String to escape.
* @return {string} Escaped string surrouned by quotes.
*/
function escapeString(string) {
return JSON.stringify(string);
}
/**
* 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.
*/
function getOptTypesFrom(block, name) {
var types = 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.<string>} List of types.
* @private
*/
function getTypesFrom_(block, name) {
var typeBlock = block.getInputTargetBlock(name);
var types;
if (!typeBlock || typeBlock.disabled) {
types = [];
} else if (typeBlock.type == 'type_other') {
types = [escapeString(typeBlock.getFieldValue('TYPE'))];
} else if (typeBlock.type == 'type_group') {
types = [];
for (var i = 0; i < typeBlock.typeCount_; i++) {
types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + i));
}
// 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 = [escapeString(typeBlock.valueType)];
}
return types;
}
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.
*/
function updateGenerator(block) {
function makeVar(root, name) {
name = name.toLowerCase().replace(/\W/g, '_');
return ' var ' + root + '_' + name;
}
BlockFactory.updateGenerator = function(block) {
var language = document.getElementById('language').value;
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("};");
injectCode(code.join('\n'), 'generatorPre');
}
/**
* Existing direction ('ltr' vs 'rtl') of preview.
*/
var oldDir = null;
var generatorStub = FactoryUtils.getGeneratorStub(block, language);
FactoryUtils.injectCode(generatorStub, 'generatorPre');
};
/**
* Update the preview display.
*/
function updatePreview() {
BlockFactory.updatePreview = function() {
// Toggle between LTR/RTL if needed (also used in first display).
var newDir = document.getElementById('direction').value;
if (oldDir != newDir) {
if (previewWorkspace) {
previewWorkspace.dispose();
if (BlockFactory.oldDir != newDir) {
if (BlockFactory.previewWorkspace) {
BlockFactory.previewWorkspace.dispose();
}
var rtl = newDir == 'rtl';
previewWorkspace = Blockly.inject('preview',
BlockFactory.previewWorkspace = Blockly.inject('preview',
{rtl: rtl,
media: '../../media/',
scrollbars: true});
oldDir = newDir;
BlockFactory.oldDir = newDir;
}
previewWorkspace.clear();
BlockFactory.previewWorkspace.clear();
// Fetch the code and determine its format (JSON or JavaScript).
var format = document.getElementById('format').value;
@@ -693,14 +164,14 @@ function updatePreview() {
var backupBlocks = Blockly.Blocks;
try {
// Make a shallow copy.
Blockly.Blocks = {};
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 || UNNAMED] = {
Blockly.Blocks[json.type || BlockFactory.UNNAMED] = {
init: function() {
this.jsonInit(json);
}
@@ -725,126 +196,70 @@ function updatePreview() {
}
// Create the preview block.
var previewBlock = previewWorkspace.newBlock(blockType);
var previewBlock = BlockFactory.previewWorkspace.newBlock(blockType);
previewBlock.initSvg();
previewBlock.render();
previewBlock.setMovable(false);
previewBlock.setDeletable(false);
previewBlock.moveBy(15, 10);
previewWorkspace.clearUndo();
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 core Blockly block already exists ' +
'under this name.');
} else if (blockType == 'block_type') {
// Warn user to let them know they can't save a block under the default
// name 'block_type'
rootBlock.setWarningText('You cannot save a block with the default ' +
'name, "block_type"');
} else {
rootBlock.setWarningText(null);
}
updateGenerator(previewBlock);
} finally {
Blockly.Blocks = backupBlocks;
}
}
};
/**
* 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 <pre> element to inject into.
* Disable link and save buttons if the format is 'Manual', enable otherwise.
*/
function injectCode(code, id) {
var pre = document.getElementById(id);
pre.textContent = code;
code = pre.innerHTML;
code = prettyPrintOne(code, 'js');
pre.innerHTML = code;
}
/**
* Return the uneditable container block that everything else attaches to.
* @return {Blockly.Block}
*/
function getRootBlock() {
var blocks = mainWorkspace.getTopBlocks(false);
for (var i = 0, block; block = blocks[i]; i++) {
if (block.type == 'factory_base') {
return block;
}
}
return null;
}
/**
* Disable the link button if the format is 'Manual', enable otherwise.
*/
function disableEnableLink() {
BlockFactory.disableEnableLink = function() {
var linkButton = document.getElementById('linkButton');
linkButton.disabled = document.getElementById('format').value == 'Manual';
}
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;
};
/**
* Initialize Blockly and layout. Called on page load.
* Render starter block (factory_base).
*/
function init() {
if ('BlocklyStorage' in window) {
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(mainWorkspace);});
disableEnableLink();
}
BlockFactory.showStarterBlock = function() {
BlockFactory.mainWorkspace.clear();
var xml = Blockly.Xml.textToDom(BlockFactory.STARTER_BLOCK_XML_TEXT);
Blockly.Xml.domToWorkspace(xml, BlockFactory.mainWorkspace);
};
document.getElementById('helpButton').addEventListener('click',
function() {
open('https://developers.google.com/blockly/guides/create-custom-blocks/block-factory',
'BlockFactoryHelp');
});
var expandList = [
document.getElementById('blockly'),
document.getElementById('blocklyMask'),
document.getElementById('preview'),
document.getElementById('languagePre'),
document.getElementById('languageTA'),
document.getElementById('generatorPre')
];
var onresize = function(e) {
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';
}
};
onresize();
window.addEventListener('resize', onresize);
var toolbox = document.getElementById('toolbox');
mainWorkspace = Blockly.inject('blockly',
{collapse: false,
toolbox: toolbox,
media: '../../media/'});
// Create the root block.
if ('BlocklyStorage' in window && window.location.hash.length > 1) {
BlocklyStorage.retrieveXml(window.location.hash.substring(1),
mainWorkspace);
} else {
var xml = '<xml><block type="factory_base" deletable="false" movable="false"></block></xml>';
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), mainWorkspace);
}
mainWorkspace.clearUndo();
mainWorkspace.addChangeListener(Blockly.Events.disableOrphans);
mainWorkspace.addChangeListener(updateLanguage);
document.getElementById('direction')
.addEventListener('change', updatePreview);
document.getElementById('languageTA')
.addEventListener('change', updatePreview);
document.getElementById('languageTA')
.addEventListener('keyup', updatePreview);
document.getElementById('format')
.addEventListener('change', formatChange);
document.getElementById('language')
.addEventListener('change', updatePreview);
}
window.addEventListener('load', init);
/**
* Returns whether or not the current block open is the starter block.
*/
BlockFactory.isStarterBlock = function() {
var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
// The starter block does not have blocks nested into the factory_base block.
return !(rootBlock.getChildren().length > 0 ||
// The starter block's name is the default, 'block_type'.
rootBlock.getFieldValue('NAME').trim().toLowerCase() != 'block_type' ||
// The starter block has no connections.
rootBlock.getFieldValue('CONNECTIONS') != 'NONE' ||
// The starter block has automatic inputs.
rootBlock.getFieldValue('INLINE') != 'AUTO');
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -33,9 +33,9 @@
*/
goog.provide('FactoryUtils');
/**
* 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.
@@ -58,10 +58,9 @@ FactoryUtils.getBlockDefinition = function(blockType, rootBlock, format, workspa
/**
* 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'.
* @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) {
@@ -261,7 +260,6 @@ FactoryUtils.formatJson_ = function(blockType, rootBlock) {
* @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
*/
@@ -642,7 +640,6 @@ FactoryUtils.escapeString = function(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
*/
@@ -661,8 +658,7 @@ FactoryUtils.getRootBlock = function(workspace) {
/**
* Hides element so that it's invisible and doesn't take up space.
*
* @param {string} elementID - ID of element to hide.
* @param {string} elementID ID of element to hide.
*/
FactoryUtils.hide = function(elementID) {
document.getElementById(elementID).style.display = 'none';
@@ -670,8 +666,7 @@ FactoryUtils.hide = function(elementID) {
/**
* Un-hides an element.
*
* @param {string} elementID - ID of element to hide.
* @param {string} elementID ID of element to hide.
*/
FactoryUtils.show = function(elementID) {
document.getElementById(elementID).style.display = 'block';
@@ -679,8 +674,7 @@ FactoryUtils.show = function(elementID) {
/**
* Hides element so that it's invisible but still takes up space.
*
* @param {string} elementID - ID of element to hide.
* @param {string} elementID ID of element to hide.
*/
FactoryUtils.makeInvisible = function(elementID) {
document.getElementById(elementID).visibility = 'hidden';
@@ -688,8 +682,7 @@ FactoryUtils.makeInvisible = function(elementID) {
/**
* Makes element visible.
*
* @param {string} elementID - ID of element to hide.
* @param {string} elementID ID of element to hide.
*/
FactoryUtils.makeVisible = function(elementID) {
document.getElementById(elementID).visibility = 'visible';
@@ -697,9 +690,9 @@ FactoryUtils.makeVisible = function(elementID) {
/**
* 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.
* @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});
@@ -718,11 +711,10 @@ FactoryUtils.createAndDownloadFile = function(contents, filename, fileType) {
/**
* 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
* @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.
* @return {!Blockly.Block} The Blockly.Block of desired type.
*/
FactoryUtils.getDefinedBlock = function(blockType, workspace) {
workspace.clear();
@@ -731,8 +723,7 @@ FactoryUtils.getDefinedBlock = function(blockType, workspace) {
/**
* Parses a block definition get the type of the block it defines.
*
* @param {!string} blockDef - A single block definition.
* @param {string} blockDef A single block definition.
* @return {string} Type of block defined by the given definition.
*/
FactoryUtils.getBlockTypeFromJsDefinition = function(blockDef) {
@@ -748,10 +739,9 @@ FactoryUtils.getBlockTypeFromJsDefinition = function(blockDef) {
/**
* Generates a category containing blocks of the specified block types.
*
* @param {!Array.<Blockly.Block>} 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.
* @param {!Array.<!Blockly.Block>} 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.
@@ -774,9 +764,8 @@ FactoryUtils.generateCategoryXml = function(blocks, categoryName) {
/**
* 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.<string>} - Array of block definitions.
* @param {string} blockDefsString JavaScript block definition(s).
* @return {!Array.<string>} Array of block definitions.
*/
FactoryUtils.parseJsBlockDefinitions = function(blockDefsString) {
var blockDefArray = [];
@@ -800,10 +789,9 @@ FactoryUtils.parseJsBlockDefinitions = function(blockDefsString) {
* 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
* @param {string} blockDefsString String containing JSON block
* definition(s).
* @return {!Array.<string>} - Array of block definitions.
* @return {!Array.<string>} Array of block definitions.
*/
FactoryUtils.parseJsonBlockDefinitions = function(blockDefsString) {
var blockDefArray = [];
@@ -831,10 +819,9 @@ FactoryUtils.parseJsonBlockDefinitions = function(blockDefsString) {
/**
* Define blocks from imported block definitions.
*
* @param {!string} blockDefsString - Block definition(s).
* @param {!string} format - Block definition format ('JSON' or 'JavaScript').
* @return {!Array<!Element>} Array of block types defined.
* @param {string} blockDefsString Block definition(s).
* @param {string} format Block definition format ('JSON' or 'JavaScript').
* @return {!Array.<!Element>} Array of block types defined.
*/
FactoryUtils.defineAndGetBlockTypes = function(blockDefsString, format) {
var blockTypes = [];
@@ -886,48 +873,46 @@ FactoryUtils.injectCode = function(code, id) {
};
/**
* Returns whether or not two blocks are the same based on their xml. Expects
* xml with a single child node that is a factory_base block, the xml found on
* Returns whether or not two blocks are the same based on their XML. Expects
* XML with a single child node that is a factory_base block, the XML found on
* Block Factory's main workspace.
*
* @param {!Element} blockXml1 - An xml element with a single child node that
* @param {!Element} blockXml1 An XML element with a single child node that
* is a factory_base block.
* @param {!Element} blockXml2 - An xml element with a single child node that
* @param {!Element} blockXml2 An XML element with a single child node that
* is a factory_base block.
* @return {boolean} Whether or not two blocks are the same based on their xml.
* @return {boolean} Whether or not two blocks are the same based on their XML.
*/
FactoryUtils.sameBlockXml = function(blockXml1, blockXml2) {
// Each xml element should contain a single child element with a 'block' tag
if (goog.string.caseInsensitiveCompare(blockXml1.tagName, 'xml') ||
goog.string.caseInsensitiveCompare(blockXml2.tagName, 'xml')) {
throw new Error('Expected two xml elements, recieved elements with tag ' +
'names: ' + blockXml1.tagName + ' and ' + blockXml2.tagName + '.');
}
// Each XML element should contain a single child element with a 'block' tag
if (goog.string.caseInsensitiveCompare(blockXml1.tagName, 'xml') ||
goog.string.caseInsensitiveCompare(blockXml2.tagName, 'xml')) {
throw new Error('Expected two XML elements, recieved elements with tag ' +
'names: ' + blockXml1.tagName + ' and ' + blockXml2.tagName + '.');
}
// Compare the block elements directly. The xml tags may include other meta
// information we want to igrore.
var blockElement1 = blockXml1.getElementsByTagName('block')[0];
var blockElement2 = blockXml2.getElementsByTagName('block')[0];
// Compare the block elements directly. The XML tags may include other meta
// information we want to igrore.
var blockElement1 = blockXml1.getElementsByTagName('block')[0];
var blockElement2 = blockXml2.getElementsByTagName('block')[0];
if (!(blockElement1 && blockElement2)) {
throw new Error('Could not get find block element in xml.');
}
if (!(blockElement1 && blockElement2)) {
throw new Error('Could not get find block element in XML.');
}
var blockXmlText1 = Blockly.Xml.domToText(blockElement1);
var blockXmlText2 = Blockly.Xml.domToText(blockElement2);
var blockXmlText1 = Blockly.Xml.domToText(blockElement1);
var blockXmlText2 = Blockly.Xml.domToText(blockElement2);
// Strip white space.
blockXmlText1 = blockXmlText1.replace(/\s+/g, '');
blockXmlText2 = blockXmlText2.replace(/\s+/g, '');
// Strip white space.
blockXmlText1 = blockXmlText1.replace(/\s+/g, '');
blockXmlText2 = blockXmlText2.replace(/\s+/g, '');
// Return whether or not changes have been saved.
return blockXmlText1 == blockXmlText2;
// Return whether or not changes have been saved.
return blockXmlText1 == blockXmlText2;
};
/*
* 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.
*/
@@ -935,23 +920,15 @@ FactoryUtils.hasVariableField = function(block) {
if (!block) {
return false;
}
for (var i = 0, input; input = block.inputList[i]; i++) {
for (var j = 0, field; field = input.fieldRow[j]; j++) {
if (field.name == 'VAR') {
return true;
}
}
}
return false;
return block.getVars().length > 0;
};
/**
* Checks if a block is a procedures block. If procedures block names are
* ever updated or expanded, this function should be updated as well (no
* other known markers for procedure blocks beyond name).
*
* @param {Blockly.Block} block The block to check.
* @return {boolean} True if hte block is a procedure block, false otherwise.
* @return {boolean} True if the block is a procedure block, false otherwise.
*/
FactoryUtils.isProcedureBlock = function(block) {
return block &&
@@ -966,7 +943,6 @@ FactoryUtils.isProcedureBlock = function(block) {
* Returns whether or not a modified block's changes has been saved to the
* Block Library.
* TODO(quachtina96): move into the Block Factory Controller once made.
*
* @param {!BlockLibraryController} blockLibraryController - Block Library
* Controller storing custom blocks.
* @return {boolean} True if all changes made to the block have been saved to
@@ -986,4 +962,3 @@ FactoryUtils.savedBlockChanges = function(blockLibraryController) {
}
return false;
};
+612 -102
View File
@@ -1,107 +1,311 @@
<!-- TODO(quachtina96): move the CSS out to a separate file -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="target-densitydpi=device-dpi, height=660, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Blockly Demo: Block Factory</title>
<script src="/storage.js"></script>
<script src="factory.js"></script>
<title>Blockly Demo: Blockly Factory</title>
<script src="../../blockly_compressed.js"></script>
<script src="../../javascript_compressed.js"></script>
<script src="../../msg/messages.js"></script>
<script src="../../blocks_compressed.js"></script>
<script src="workspacefactory/wfactory_model.js"></script>
<script src="workspacefactory/wfactory_controller.js"></script>
<script src="workspacefactory/wfactory_view.js"></script>
<script src="workspacefactory/wfactory_generator.js"></script>
<script src="workspacefactory/wfactory_init.js"></script>
<script src="standard_categories.js"></script>
<script src="/storage.js"></script>
<script src="../../../closure-library/closure/goog/base.js"></script>
<script src="factory_utils.js"></script>
<script src="block_option.js"></script>
<script src="factory.js"></script>
<script src="block_library_view.js"></script>
<script src="block_library_storage.js"></script>
<script src="block_library_controller.js"></script>
<script src="block_exporter_tools.js"></script>
<script src="block_exporter_view.js"></script>
<script src="block_exporter_controller.js"></script>
<script src="blocks.js"></script>
<style>
html, body {
height: 100%;
}
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 {
height: 100%;
width: 100%;
}
td {
vertical-align: top;
padding: 0;
}
#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-family: monospace;
font-size: 10pt;
}
button {
border-radius: 4px;
border: 1px solid #ddd;
background-color: #eee;
color: #000;
padding: 10px;
margin: 0 5px;
font-size: large;
}
button:hover:not(:disabled) {
box-shadow: 2px 2px 5px #888;
}
button:disabled {
opacity: 0.6;
}
button>* {
opacity: 0.6;
vertical-align: text-bottom;
}
button:hover:not(:disabled)>* {
opacity: 1;
}
#linkButton {
display: none;
}
</style>
<script src="app_controller.js"></script>
<link rel="stylesheet" href="factory.css">
<link rel="stylesheet" href="../prettify.css">
<script src="../prettify.js"></script>
<script>
var blocklyFactory;
var init = function() {
blocklyFactory = new AppController();
blocklyFactory.init();
};
window.addEventListener('load', init);
</script>
</head>
<body>
<table>
<tr>
<td width="50%" height="5%">
<h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
<a href="../index.html">Demos</a> &gt; Block Factory</h1>
</td>
<body onbeforeunload="return blocklyFactory.confirmLeavePage()">
<h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
<a href="../index.html">Demos</a> &gt; Blockly Factory
<button id="helpButton" title="View documentation in new window.">
<span>Help</span>
</button>
</h1>
<div id="tabContainer">
<div id="blockFactory_tab" class="tab tabon"> Block Factory</div>
<div id="blocklibraryExporter_tab" class="tab taboff"> Block Exporter</div>
<div id="workspaceFactory_tab" class="tab taboff"> Workspace Factory</div>
</div>
<!-- Exporter tab -->
<div id="blockLibraryExporter">
<br>
<p id="helperText"> First, select blocks from your block library by clicking on them. Then, use the Export Settings form to download starter code for selected blocks.
</p>
<div id="exportSelector">
<br>
<h3> Block Selector </h3>
<div class='dropdown'>
<button id="button_setBlocks">Select</button>
<div id="dropdownDiv_setBlocks" class="dropdown-content">
<a id='dropdown_addAllFromLib' title="Select all block library blocks.">All Stored in Block Library</a>
<a id='dropdown_addAllUsed' title="Select all block library blocks used in workspace factory.">All Used in Workspace Factory</a>
</div>
<button id='clearSelectedButton' title="Clear selected blocks.">Clear Selected</a>
</div>
<div id="blockSelector"></div>
</div>
<!-- Users may customize export settings through this form -->
<div id="exportSettings">
<br>
<h3> Export Settings </h3>
<form id="exportSettingsForm">
<div id="selectedBlocksTextContainer">
<p>Currently Selected:</p>
<p id="selectedBlocksText"></p>
</div>
<input type="checkbox" id="blockDefCheck">Block Definition(s)<br>
<div id="blockDefSettings" class="subsettings">
Format:
<select id="exportFormat">
<option value="JSON">JSON</option>
<option value="JavaScript">JavaScript</option>
</select>
<br>
File Name:
<br>
<input type="text" id="blockDef_filename">
</div>
<br>
<input type="checkbox" id="genStubCheck">Generator Stub(s)<br>
<div id="genStubSettings" class="subsettings">
Language:
<select id="exportLanguage">
<option value="JavaScript">JavaScript</option>
<option value="Python">Python</option>
<option value="PHP">PHP</option>
<option value="Lua">Lua</option>
<option value="Dart">Dart</option>
</select>
<br>
File Name:
<br>
<input type="text" id="generatorStub_filename"><br>
</div>
<br>
</form>
<button id="exporterSubmitButton" title="Download block starter code as specified in export settings."> Export </button>
</div>
<div id="exportPreview">
<br>
<h3>Export Preview </h3>
<div id="blockDefs" class="exportPreviewTextArea">
<p id="blockDefs_label">Block Definitions:</p>
<pre id="blockDefs_textArea"></pre>
</div>
<div id="genStubs" class="exportPreviewTextArea">
<p id="genStubs_label">Generator Stubs:</p>
<pre id="genStubs_textArea"></pre>
</div>
</div>
</div>
<!-- Workspace Factory tab -->
<div id="workspaceFactoryContent">
<div id="factoryHeader">
<p>
<div class="dropdown">
<button id="button_importBlocks">Import Custom Blocks</button>
<div id="dropdownDiv_importBlocks" class="dropdown-content">
<input type="file" id="input_importBlocksJson" accept=".js, .json, .txt" class="inputfile"</input>
<label for="input_importBlocksJson">From JSON</label>
<input type="file" id="input_importBlocksJs" accept=".js, .txt" class="inputfile"</input>
<label for="input_importBlocksJs">From Javascript</label>
</div>
</div>
<div class="dropdown">
<button id="button_load">Load to Edit</button>
<div id="dropdownDiv_load" class="dropdown-content">
<input type="file" id="input_loadToolbox" accept=".xml" class="inputfile"></input>
<label for="input_loadToolbox">Toolbox</label>
<input type="file" id="input_loadPreload" accept=".xml" class="inputfile"</input>
<label for="input_loadPreload">Workspace Blocks</label>
</div>
</div>
<div class="dropdown">
<button id="button_export">Export</button>
<div id="dropdownDiv_export" class="dropdown-content">
<a id='dropdown_exportOptions'>Starter Code</a>
<a id='dropdown_exportToolbox'>Toolbox</a>
<a id='dropdown_exportPreload'>Workspace Blocks</a>
<a id='dropdown_exportAll'>All</a>
</div>
</div>
<button id="button_clear">Clear</button>
</p>
</div>
<section id="createDiv">
<div id="createHeader">
<h3>Edit</h3>
<p id="editHelpText">Drag blocks into the workspace to configure the toolbox in your custom workspace.</p>
</div>
<table id='workspaceTabs' style="width:auto; height:auto">
<td id="tab_toolbox" class="tabon">Toolbox</td>
<td id="tab_preload" class="taboff">Workspace</td>
</table>
<section id="toolbox_section">
<div id="toolbox_blocks"></div>
</section>
<aside id="toolbox_div">
<p id="categoryHeader">Your categories will appear here</p>
<table id="categoryTable" style="width:auto; height:auto">
</table>
<p>&nbsp;</p>
<div class='dropdown'>
<button id="button_add" class="large">+</button>
<div id="dropdownDiv_add" class="dropdown-content">
<a id='dropdown_newCategory'>New Category</a>
<a id='dropdown_loadCategory'>Standard Category</a>
<a id='dropdown_separator'>Separator</a>
<a id='dropdown_loadStandardToolbox'>Standard Toolbox</a>
</div>
</div>
<button id="button_remove" class="large">-</button>
<button id="button_up" class="large">&#8593;</button>
<button id="button_down" class="large">&#8595;</button>
<br>
<div class='dropdown'>
<button id="button_editCategory">Edit Category</button>
<div id="dropdownDiv_editCategory" class="dropdown-content">
<a id='dropdown_name'>Name</a>
<a id='dropdown_color'>Color</a>
</div>
</div>
</aside>
<button id='button_addShadow' style='display:none'>Make Shadow</button>
<button id='button_removeShadow' style='display:none'>Remove Shadow</button>
<aside id='preload_div' style='display:none'>
<div id="preloadHelp">
<p>Configure the options for your Blockly inject call.</p>
<button id="button_optionsHelp">Help</button>
<button class="small" id="button_standardOptions">Reset to Default</button>
</div>
<div id="workspace_options">
<input type="checkbox" id="option_readOnly_checkbox" class="optionsInput">Read Only<br>
<input type="checkbox" id="option_collapse_checkbox" class="optionsInput">Collapsible Blocks<br>
<input type="checkbox" id="option_comments_checkbox" class="optionsInput">Comments for Blocks<br>
<input type="checkbox" id="option_css_checkbox" class="optionsInput">Use Blockly CSS<br>
<input type="checkbox" id="option_disable_checkbox" class="optionsInput">Disabled Blocks<br>
<input type="checkbox" id="option_grid_checkbox" class="optionsInput">Use Grid<br>
<div id="grid_options" name="grid" style="display:none">
Spacing <input type="number" id="gridOption_spacing_number" class="optionsInput" value="0"><br>
Length <input type="number" id="gridOption_length_number" class="optionsInput" value="1"><br>
Color <input type="text" id="gridOption_colour_text" class="optionsInput" value="#888"><br>
<input type="checkbox" id="gridOption_snap_checkbox" class="optionsInput" value="grid_snap_checkbox">Snap<br>
</div>
<input type="checkbox" id="option_infiniteBlocks_checkbox" class="optionsInput" value="checked">Infinite Blocks<br>
<div id="maxBlockNumber_option" style="display:none">
Max Blocks <input type="number" id="option_maxBlocks_number" class="optionsInput" value=100><br>
</div>
Path to Blockly Media <input type="text" id="option_media_text" class="optionsInput"><br>
<input type="checkbox" id="option_rtl_checkbox" class="optionsInput">Layout with RTL<br>
<input type="checkbox" id="option_scrollbars_checkbox" class="optionsInput">Scrollbars<br>
<input type="checkbox" id="option_sounds_checkbox" class="optionsInput">Sounds<br>
<div id="trashcan_option">
<input type="checkbox" id="option_trashcan_checkbox" class="optionsInput">Trashcan<br>
</div>
<input type="checkbox" id="option_zoom_checkbox" class="optionsInput">Zoom<br>
<div id="zoom_options" name="zoom" style="display:none">
<input type="checkbox" id="zoomOption_controls_checkbox" class="optionsInput">Zoom Controls<br>
<input type="checkbox" id="zoomOption_wheel_checkbox" class="optionsInput">Zoom Wheel<br>
Start Scale <input type="number" id="zoomOption_startScale_number" class="optionsInput" name="startScale" value="1.0"><br>
Max Scale <input type="number" id="zoomOption_maxScale_number" class="optionsInput" value="3"><br>
Min Scale <input type="number" id="zoomOption_minScale_number" class="optionsInput" value="0.3"><br>
Scale Speed <input type="number" id="zoomOption_scaleSpeed_number" class="optionsInput" value="1.2"><br>
</div>
</div>
</aside>
</section>
<aside id="previewDiv">
<div id="previewBorder">
<div id="previewHelp">
<h3>Preview</h3>
<p>This is what your custom workspace will look like.</p>
</div>
<div id="preview_blocks" class="content"></div>
</div>
</aside>
</div>
<!-- Blockly Factory Tab -->
<table id="blockFactoryContent">
<tr width="100%" height="10%">
<td width="50%" height="5%">
<table>
<tr id="blockLibrary">
<td id="blockLibraryContainer">
<span>
<div class='dropdown'>
<button id="button_blockLib">Block Library</button>
<div id="dropdownDiv_blockLib" class="dropdown-content">
<a id='createNewBlockButton'>Create New Block</a>
</div>
</div>
<select id="blockLibraryDropdown" style="display:none">
</select>
</span>
</td>
<td id="blockLibraryControls">
<button id="saveToBlockLibraryButton" title="Save block to Block Library.">
Save "block_type"
</button>
<button id="removeBlockFromLibraryButton" title="Remove block from Block Library.">
Delete "block_type"
</button>
</td>
</tr>
</table>
</td>
<td height="5%">
<table id="blockFactoryPreview">
<tr>
<td style="vertical-align: bottom;">
<td id="previewContainer">
<h3>Preview:
<select id="direction">
<option value="ltr">LTR</option>
@@ -109,28 +313,33 @@
</select>
</h3>
</td>
<td style="vertical-align: middle; text-align: right;">
<td id="buttonContainer">
<button id="linkButton" title="Save and link to blocks.">
<img src="link.png" height="21" width="21">
</button>
<button id="linkButton" title="Save and link to blocks.">
<img src="link.png" height="21" width="21">
<button id="clearBlockLibraryButton" title="Clear Block Library.">
<span>Clear Library</span>
</button>
<button id="helpButton" title="View documentation in new window.">
<span>Help</span>
<label for="files" class="buttonStyle">
<span class=>Import Block Library</span>
</label>
<input id="files" type="file" name="files"
accept="application/xml">
<button id="localSaveButton" title="Save block library XML to a local file.">
<span>Download Block Library</span>
</button>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td width="50%" height="95%" style="padding: 2px;">
<tr height="80%">
<td id="blocklyWorkspaceContainer">
<div id="blockly"></div>
<div id="blocklyMask"></div>
</td>
<td width="50%" height="95%">
<table>
<td width="50%">
<table id="blocklyPreviewContainer">
<tr>
<td height="30%">
<div id="preview"></div>
@@ -138,7 +347,7 @@
</tr>
<tr>
<td height="5%">
<h3>Language code:
<h3>Block Definition:
<select id="format">
<option value="JSON">JSON</option>
<option value="JavaScript">JavaScript</option>
@@ -173,9 +382,10 @@
</tr>
</table>
</td>
</tr>
</tr>
</table>
<xml id="toolbox" style="display: none">
<xml id="blockfactory_toolbox" class="toolbox">
<category name="Input">
<block type="input_value">
<value name="TYPE">
@@ -226,5 +436,305 @@
<block type="colour_hue"><mutation colour="330"></mutation><field name="HUE">330</field></block>
</category>
</xml>
<xml id="workspacefactory_toolbox" class="toolbox">
<category name="Logic" colour="210">
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_negate"></block>
<block type="logic_boolean"></block>
<block type="logic_null"></block>
<block type="logic_ternary"></block>
</category>
<category name="Loops" colour="120">
<block type="controls_repeat_ext">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
<value name="BY">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="controls_forEach"></block>
<block type="controls_flow_statements"></block>
</category>
<category name="Math" colour="230">
<block type="math_number"></block>
<block type="math_arithmetic">
<value name="A">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="B">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="math_single">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">9</field>
</shadow>
</value>
</block>
<block type="math_trig">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">45</field>
</shadow>
</value>
</block>
<block type="math_constant"></block>
<block type="math_number_property">
<value name="NUMBER_TO_CHECK">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="math_round">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">3.1</field>
</shadow>
</value>
</block>
<block type="math_on_list"></block>
<block type="math_modulo">
<value name="DIVIDEND">
<shadow type="math_number">
<field name="NUM">64</field>
</shadow>
</value>
<value name="DIVISOR">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="math_constrain">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">50</field>
</shadow>
</value>
<value name="LOW">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="HIGH">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_int">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_float"></block>
</category>
<category name="Text" colour="160">
<block type="text"></block>
<block type="text_join"></block>
<block type="text_append">
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<block type="text_length">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_isEmpty">
<value name="VALUE">
<shadow type="text">
<field name="TEXT"></field>
</shadow>
</value>
</block>
<block type="text_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
<value name="FIND">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_charAt">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
</block>
<block type="text_getSubstring">
<value name="STRING">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
</block>
<block type="text_changeCase">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_trim">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_prompt_ext">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
</category>
<category name="Lists" colour="260">
<block type="lists_create_with">
<mutation items="0"></mutation>
</block>
<block type="lists_create_with"></block>
<block type="lists_repeat">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="lists_length"></block>
<block type="lists_isEmpty"></block>
<block type="lists_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_getIndex">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_setIndex">
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_getSublist">
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_split">
<value name="DELIM">
<shadow type="text">
<field name="TEXT">,</field>
</shadow>
</value>
</block>
<block type="lists_sort"></block>
</category>
<category name="Colour" colour="20">
<block type="colour_picker"></block>
<block type="colour_random"></block>
<block type="colour_rgb">
<value name="RED">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
<value name="GREEN">
<shadow type="math_number">
<field name="NUM">50</field>
</shadow>
</value>
<value name="BLUE">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="colour_blend">
<value name="COLOUR1">
<shadow type="colour_picker">
<field name="COLOUR">#ff0000</field>
</shadow>
</value>
<value name="COLOUR2">
<shadow type="colour_picker">
<field name="COLOUR">#3333ff</field>
</shadow>
</value>
<value name="RATIO">
<shadow type="math_number">
<field name="NUM">0.5</field>
</shadow>
</value>
</block>
</category>
<sep></sep>
<category name="Variables" colour="330" custom="VARIABLE"></category>
<category name="Functions" colour="290" custom="PROCEDURE"></category>
<sep></sep>
<category name="Block Library" colour="260" id="blockLibCategory"></category>
</xml>
</body>
</html>
@@ -1,6 +1,6 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
@@ -34,6 +34,7 @@
*/
goog.provide('StandardCategories');
// Map of standard category information necessary to add a standard category
// to the toolbox.
StandardCategories.categoryMap = Object.create(null);
@@ -392,4 +393,3 @@ StandardCategories.coreBlockTypes = ["controls_if", "logic_compare",
"lists_getSublist", "lists_split", "lists_sort", "variables_set",
"procedures_defreturn", "procedures_ifreturn", "procedures_defnoreturn",
"procedures_callreturn"];
@@ -454,70 +454,6 @@ goog.require('goog.ui.ColorPicker');
<category name="Functions" colour="290" custom="PROCEDURE"></category>
</xml>
<script type="text/javascript">
// 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 empty workspace for configuring workspace.
var toolboxWorkspace = Blockly.inject('toolbox_blocks',
{grid:
@@ -726,20 +662,6 @@ goog.require('goog.ui.ColorPicker');
}
});
// 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");
});
// Disable category editing buttons until categories are created.
document.getElementById('button_remove').disabled = true;
document.getElementById('button_up').disabled = true;
@@ -1,6 +1,6 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
@@ -37,13 +37,13 @@
goog.require('FactoryUtils');
goog.require('StandardCategories');
/**
* Class for a WorkspaceFactoryController
* @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.
* @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.
@@ -108,8 +108,8 @@ WorkspaceFactoryController.prototype.addCategory = function() {
// After possibly creating a category, check again if it's the first category.
var isFirstCategory = !this.model.hasElements();
// Get name from user.
name = this.promptForNewCategoryName('Enter the name of your new category: ');
if (!name) { //Exit if cancelled.
name = this.promptForNewCategoryName('Enter the name of your new category:');
if (!name) { // Exit if cancelled.
return;
}
// Create category.
@@ -117,7 +117,7 @@ WorkspaceFactoryController.prototype.addCategory = function() {
// Switch to category.
this.switchElement(this.model.getCategoryIdByName(name));
// Sets the default options for injecting the workspace
// Sets the default options for injecting the workspace
// when there are categories if adding the first category.
if (isFirstCategory) {
this.view.setCategoryOptions(this.model.hasElements());
@@ -131,9 +131,8 @@ WorkspaceFactoryController.prototype.addCategory = function() {
* 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 {string} name Name of category being added.
* @param {string} id The ID of the category being added.
*/
WorkspaceFactoryController.prototype.createCategory = function(name) {
// Create empty category
@@ -148,13 +147,12 @@ WorkspaceFactoryController.prototype.createCategory = function(name) {
* 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.
* @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
var clickFunction = function(id) { // Keep this in scope for switchElement.
return function() {
self.switchElement(id);
};
@@ -192,7 +190,6 @@ WorkspaceFactoryController.prototype.transferFlyoutBlocksToCategory =
* 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.
@@ -239,33 +236,34 @@ WorkspaceFactoryController.prototype.removeElement = function() {
/**
* Gets a valid name for a new category from the user.
*
* @param {!string} promptString Prompt for the user to enter a name.
* @param {string} promptString Prompt for the user to enter a name.
* @param {string=} opt_oldName The current name.
* @return {string} Valid name for a new category, or null if cancelled.
*/
WorkspaceFactoryController.prototype.promptForNewCategoryName =
function(promptString) {
function(promptString, opt_oldName) {
var defaultName = opt_oldName;
do {
var name = prompt(promptString);
var name = prompt(promptString, defaultName);
if (!name) { // If cancelled.
return null;
}
defaultName = name;
} 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.
* @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.
// 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);
@@ -279,8 +277,7 @@ WorkspaceFactoryController.prototype.switchElement = function(id) {
/**
* 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
* @param {string} id ID of category to load
*/
WorkspaceFactoryController.prototype.clearAndLoadElement = function(id) {
// Unselect current tab if switching to and from an element.
@@ -314,32 +311,32 @@ WorkspaceFactoryController.prototype.clearAndLoadElement = function(id) {
/**
* 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
* 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) {
// Get file name.
var fileName = prompt('File Name for ' + (exportMode ==
WorkspaceFactoryController.MODE_TOOLBOX ? 'toolbox XML: ' :
'pre-loaded workspace XML: '));
if (!fileName) { // If cancelled
// Get file name.
if (exportMode == WorkspaceFactoryController.MODE_TOOLBOX) {
var fileName = prompt('File Name for toolbox XML:', 'toolbox.xml');
} else {
var fileName = prompt('File Name for pre-loaded workspace XML:',
'workspace.xml');
}
if (!fileName) { // If cancelled.
return;
}
// Generate XML.
if (exportMode == WorkspaceFactoryController.MODE_TOOLBOX) {
// Export the toolbox XML.
var configXml = Blockly.Xml.domToPrettyText
(this.generator.generateToolboxXml());
this.hasUnsavedToolboxChanges = false;
} else if (exportMode == WorkspaceFactoryController.MODE_PRELOAD) {
// Export the pre-loaded block XML.
var configXml = Blockly.Xml.domToPrettyText
(this.generator.generateWorkspaceXml());
this.hasUnsavedPreloadChanges = false;
@@ -358,7 +355,8 @@ WorkspaceFactoryController.prototype.exportXmlFile = function(exportMode) {
* file name from the user and downloads the options object to that file.
*/
WorkspaceFactoryController.prototype.exportInjectFile = function() {
var fileName = prompt('File Name for starter Blockly workspace code: ');
var fileName = prompt('File Name for starter Blockly workspace code:',
'workspace.js');
if (!fileName) { // If cancelled.
return;
}
@@ -400,8 +398,8 @@ WorkspaceFactoryController.prototype.updatePreview = function() {
// 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());
var tree = Blockly.Options.parseToolboxTree(
this.generator.generateToolboxXml());
// No categories, creates a simple flyout.
if (tree.getElementsByTagName('category').length == 0) {
@@ -456,8 +454,8 @@ WorkspaceFactoryController.prototype.saveStateFromWorkspace = function() {
this.hasUnsavedPreloadChanges = true;
}
this.model.savePreloadXml
(Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
this.model.savePreloadXml(
Blockly.Xml.workspaceToDom(this.toolboxWorkspace));
}
};
@@ -465,8 +463,7 @@ WorkspaceFactoryController.prototype.saveStateFromWorkspace = function() {
* 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
* @param {!Element} Tree of XML elements
* @package
*/
WorkspaceFactoryController.prototype.reinjectPreview = function(tree) {
@@ -484,18 +481,20 @@ WorkspaceFactoryController.prototype.reinjectPreview = function(tree) {
* currently in use, exits if user presses cancel.
*/
WorkspaceFactoryController.prototype.changeCategoryName = function() {
var selected = this.model.getSelected();
// Return if a category is not selected.
if (this.model.getSelected().type != ListElement.TYPE_CATEGORY) {
if (selected.type != ListElement.TYPE_CATEGORY) {
return;
}
// Get new name from user.
window.foo = selected;
var newName = this.promptForNewCategoryName('What do you want to change this'
+ ' category\'s name to?');
if (!newName) { // If cancelled.
+ ' category\'s name to?', selected.name);
if (!newName) { // If cancelled.
return;
}
// Change category name.
this.model.getSelected().changeName(newName);
selected.changeName(newName);
this.view.updateCategoryName(newName, this.model.getSelectedId());
// Update preview.
this.updatePreview();
@@ -506,8 +505,7 @@ WorkspaceFactoryController.prototype.changeCategoryName = function() {
* 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
* @param {number} 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.
*/
@@ -534,10 +532,9 @@ WorkspaceFactoryController.prototype.moveElement = function(offset) {
/**
* 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.
* @param {number} newIndex The index to insert the element at.
* @param {number} oldIndex The index the element is currently at.
*/
WorkspaceFactoryController.prototype.moveElementToIndex = function(element,
newIndex, oldIndex) {
@@ -548,8 +545,7 @@ WorkspaceFactoryController.prototype.moveElementToIndex = function(element,
/**
* 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
* @param {string} color The color to change the selected category. Must be
* a valid CSS string.
*/
WorkspaceFactoryController.prototype.changeSelectedCategoryColor =
@@ -575,7 +571,7 @@ WorkspaceFactoryController.prototype.loadCategory = function() {
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.
return; // Exit if cancelled.
}
} while (!this.isStandardCategoryName(name));
@@ -586,7 +582,6 @@ WorkspaceFactoryController.prototype.loadCategory = function() {
/**
* Loads a Standard Category by name and switches to it. Leverages
* StandardCategories. Returns if cannot load standard category.
*
* @param {string} name Name of the standard category to load.
*/
WorkspaceFactoryController.prototype.loadCategoryByName = function(name) {
@@ -659,12 +654,11 @@ WorkspaceFactoryController.prototype.loadStandardToolbox = function() {
this.addSeparator();
this.loadCategoryByName('Variables');
this.loadCategoryByName('Functions');
}
};
/**
* 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.
@@ -706,12 +700,11 @@ WorkspaceFactoryController.prototype.addSeparator = function() {
* 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).
* 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.
@@ -735,10 +728,10 @@ WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
// Confirm that the user wants to override their current toolbox.
var hasToolboxElements = controller.model.hasElements() ||
controller.getAllBlocks().length > 0;
controller.toolboxWorkspace.getAllBlocks().length > 0;
if (hasToolboxElements &&
!confirm('Are you sure you want to import? You will lose your '
+ 'current toolbox. ')) {
!confirm('Are you sure you want to import? You will lose your ' +
'current toolbox.')) {
return;
}
// Import toolbox XML.
@@ -750,8 +743,8 @@ WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
// Confirm that the user wants to override their current blocks.
if (controller.toolboxWorkspace.getAllBlocks().length > 0 &&
!confirm('Are you sure you want to import? You will lose your '
+ 'current workspace blocks. ')) {
!confirm('Are you sure you want to import? You will lose your ' +
'current workspace blocks.')) {
return;
}
@@ -761,12 +754,12 @@ WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
// Throw error if invalid mode.
throw new Error("Unknown import mode: " + importMode);
}
} catch(e) {
alert('Cannot load XML from file.');
console.log(e);
} finally {
} catch(e) {
alert('Cannot load XML from file.');
console.log(e);
} finally {
Blockly.Events.enable();
}
}
}
// Read the file asynchronously.
@@ -777,9 +770,8 @@ WorkspaceFactoryController.prototype.importFile = function(file, importMode) {
* 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.
* @private
*/
WorkspaceFactoryController.prototype.importToolboxFromTree_ = function(tree) {
// Clear current editing area.
@@ -853,45 +845,42 @@ WorkspaceFactoryController.prototype.importToolboxFromTree_ = function(tree) {
* 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.
* 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.
* 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.
* 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
@@ -925,7 +914,6 @@ WorkspaceFactoryController.prototype.clearAll = function() {
* 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.
@@ -946,10 +934,9 @@ WorkspaceFactoryController.prototype.addShadow = function() {
/**
* 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.
* @private
*/
WorkspaceFactoryController.prototype.addShadowForBlockAndChildren_ =
function(block) {
@@ -972,7 +959,6 @@ WorkspaceFactoryController.prototype.addShadowForBlockAndChildren_ =
* 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.
@@ -992,14 +978,13 @@ WorkspaceFactoryController.prototype.removeShadow = function() {
/**
* 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.
* @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
@@ -1030,8 +1015,7 @@ WorkspaceFactoryController.prototype.convertShadowBlocks = function() {
* Sets the currently selected mode that determines what the toolbox workspace
* is being used to edit. Updates the view and then saves and loads XML
* to and from the toolbox and updates the help text.
*
* @param {!string} tab The type of tab being switched to
* @param {string} tab The type of tab being switched to
* (WorkspaceFactoryController.MODE_TOOLBOX or
* WorkspaceFactoryController.MODE_PRELOAD).
*/
@@ -1076,7 +1060,6 @@ WorkspaceFactoryController.prototype.setMode = function(mode) {
* 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) {
@@ -1097,7 +1080,7 @@ WorkspaceFactoryController.prototype.setStandardOptionsAndUpdate = function() {
this.view.setBaseOptions();
this.view.setCategoryOptions(this.model.hasElements());
this.generateNewOptions();
};
};
/**
* Generates a new options object for injecting a Blockly workspace based
@@ -1114,9 +1097,8 @@ WorkspaceFactoryController.prototype.generateNewOptions = function() {
/**
* Generates a new options object for injecting a Blockly workspace based on
* user input.
* @private
*
* @return {!Object} Blockly injection options object.
* @private
*/
WorkspaceFactoryController.prototype.readOptions_ = function() {
var optionsObj = Object.create(null);
@@ -1199,13 +1181,11 @@ WorkspaceFactoryController.prototype.readOptions_ = function() {
* 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
* @param {string} format The format of the file to import, either 'JSON' or
* 'JavaScript'.
*/
WorkspaceFactoryController.prototype.importBlocks =
function(file, format) {
WorkspaceFactoryController.prototype.importBlocks = function(file, format) {
// Generate category name from file name.
var categoryName = file.name;
@@ -1254,9 +1234,8 @@ WorkspaceFactoryController.prototype.importBlocks =
/*
* Updates the block library category in the toolbox workspace toolbox.
*
* @param {!Element} categoryXml XML for the block library category.
* @param {!Array<!string>} libBlockTypes Array of block types from the block
* @param {!Array.<string>} libBlockTypes Array of block types from the block
* library.
*/
WorkspaceFactoryController.prototype.setBlockLibCategory =
@@ -1283,8 +1262,7 @@ WorkspaceFactoryController.prototype.setBlockLibCategory =
/**
* Return the block types used in the custom toolbox and pre-loaded workspace.
*
* @return {!Array.<!string>} Block types used in the custom toolbox and
* @return {!Array.<string>} Block types used in the custom toolbox and
* pre-loaded workspace.
*/
WorkspaceFactoryController.prototype.getAllUsedBlockTypes = function() {
@@ -1295,7 +1273,6 @@ WorkspaceFactoryController.prototype.getAllUsedBlockTypes = function() {
* 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) {
@@ -1318,7 +1295,6 @@ WorkspaceFactoryController.prototype.warnForUndefinedBlocks_ = function() {
/*
* Determines if a standard variable category is in the custom toolbox.
*
* @return {boolean} True if a variables category is in use, false otherwise.
*/
WorkspaceFactoryController.prototype.hasVariablesCategory = function() {
@@ -1327,7 +1303,6 @@ WorkspaceFactoryController.prototype.hasVariablesCategory = function() {
/**
* Determines if a standard procedures category is in the custom toolbox.
*
* @return {boolean} True if a procedures category is in use, false otherwise.
*/
WorkspaceFactoryController.prototype.hasProceduresCategory = function() {
@@ -1336,7 +1311,6 @@ WorkspaceFactoryController.prototype.hasProceduresCategory = function() {
/**
* Determines if there are any unsaved changes in workspace factory.
*
* @return {boolean} True if there are unsaved changes, false otherwise.
*/
WorkspaceFactoryController.prototype.hasUnsavedChanges = function() {
@@ -1,6 +1,6 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
@@ -19,7 +19,7 @@
*/
/**
* @fileoverview Generates the configuration xml used to update the preview
* @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
@@ -30,6 +30,7 @@
goog.require('FactoryUtils');
/**
* Class for a WorkspaceFactoryGenerator
* @constructor
@@ -49,11 +50,10 @@ WorkspaceFactoryGenerator = function(model) {
};
/**
* Generates the xml for the toolbox or flyout with information from
* 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
@@ -117,7 +117,6 @@ WorkspaceFactoryGenerator.prototype.generateToolboxXml = function() {
* 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.
*
* @return {!Element} XML element representing toolbox or flyout corresponding
* to toolbox workspace.
*/
@@ -133,16 +132,14 @@ WorkspaceFactoryGenerator.prototype.generateWorkspaceXml = function() {
generatedXml.setAttribute('id', 'workspaceBlocks');
generatedXml.setAttribute('style', 'display:none');
return generatedXml;
};
};
/**
* Generates a string representation of the options object for injecting the
* workspace and starter code.
*
* @return {!string} String representation of starter code for injecting.
* @return {string} String representation of starter code for injecting.
*/
WorkspaceFactoryGenerator.prototype.generateInjectString = function() {
var addAttributes = function(obj, tabChar) {
if (!obj) {
return '{}\n';
@@ -183,35 +180,32 @@ WorkspaceFactoryGenerator.prototype.generateInjectString = function() {
'/* Load blocks to workspace. */\n' +
'Blockly.Xml.domToWorkspace(workspace, workspaceBlocks);';
return finalStr;
}
};
/**
* 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.
* @private
*/
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');
var blockChild = Blockly.Xml.blockToDom(block, /* opt_noId */ true);
xmlDom.appendChild(blockChild);
}
};
@@ -221,7 +215,6 @@ WorkspaceFactoryGenerator.prototype.appendHiddenWorkspaceToDom_ =
* actual shadow blocks. This is done so that blockToDom records them as
* shadow blocks instead of regular blocks.
* @private
*
*/
WorkspaceFactoryGenerator.prototype.setShadowBlocksInHiddenWorkspace_ =
function() {
@@ -236,9 +229,8 @@ WorkspaceFactoryGenerator.prototype.setShadowBlocksInHiddenWorkspace_ =
/**
* Given a set of block types, gets the Blockly.Block objects for each block
* type.
*
* @param {!Array<!Element>} blockTypes Array of blocks that have been defined.
* @return {!Array<!Blockly.Block>} Array of Blockly.Block objects corresponding
* @param {!Array.<!Element>} blockTypes Array of blocks that have been defined.
* @return {!Array.<!Blockly.Block>} Array of Blockly.Block objects corresponding
* to the array of blockTypes.
*/
WorkspaceFactoryGenerator.prototype.getDefinedBlocks = function(blockTypes) {
@@ -249,4 +241,3 @@ WorkspaceFactoryGenerator.prototype.getDefinedBlocks = function(blockTypes) {
}
return blocks;
};
@@ -1,12 +1,12 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* 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 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
@@ -37,7 +37,6 @@ WorkspaceFactoryInit = {};
/**
* Initialization for workspace factory tab.
*
* @param {!FactoryController} controller The controller for the workspace
* factory tab.
*/
@@ -59,75 +58,28 @@ WorkspaceFactoryInit.initWorkspaceFactory = function(controller) {
/**
* Initialize the color picker in workspace factory.
* @private
*
* @param {!FactoryController} controller The controller for the workspace
* factory tab.
* @private
*/
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'];
// 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();
@@ -141,16 +93,15 @@ WorkspaceFactoryInit.initColorPicker_ = function(controller) {
goog.events.listen(popupPicker, 'change', function(e) {
controller.changeSelectedCategoryColor(popupPicker.getSelectedColor());
document.getElementById('dropdownDiv_editCategory').classList.remove
("show");
('show');
});
};
/**
* Assign click handlers for workspace factory.
* @private
*
* @param {!FactoryController} controller The controller for the workspace
* factory tab.
* @private
*/
WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
function(controller) {
@@ -169,35 +120,35 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
document.getElementById('button_add').addEventListener
('click',
function() {
document.getElementById('dropdownDiv_add').classList.toggle("show");
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('dropdownDiv_add').classList.remove('show');
});
document.getElementById('dropdown_loadCategory').addEventListener
('click',
function() {
controller.loadCategory();
document.getElementById('dropdownDiv_add').classList.remove("show");
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('dropdownDiv_add').classList.remove('show');
});
document.getElementById('dropdown_loadStandardToolbox').addEventListener
('click',
function() {
controller.loadStandardToolbox();
document.getElementById('dropdownDiv_add').classList.remove("show");
document.getElementById('dropdownDiv_add').classList.remove('show');
});
document.getElementById('button_remove').addEventListener
@@ -210,21 +161,21 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
('click',
function() {
controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX);
document.getElementById('dropdownDiv_export').classList.remove("show");
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('dropdownDiv_export').classList.remove('show');
});
document.getElementById('dropdown_exportOptions').addEventListener
('click',
function() {
controller.exportInjectFile();
document.getElementById('dropdownDiv_export').classList.remove("show");
document.getElementById('dropdownDiv_export').classList.remove('show');
});
document.getElementById('dropdown_exportAll').addEventListener
@@ -233,16 +184,16 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
controller.exportInjectFile();
controller.exportXmlFile(WorkspaceFactoryController.MODE_TOOLBOX);
controller.exportXmlFile(WorkspaceFactoryController.MODE_PRELOAD);
document.getElementById('dropdownDiv_export').classList.remove("show");
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_export').classList.toggle('show');
document.getElementById('dropdownDiv_load').classList.remove('show');
document.getElementById('dropdownDiv_importBlocks').classList.
remove("show");
remove('show');
});
document.getElementById('button_up').addEventListener
@@ -261,7 +212,7 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
('click',
function() {
document.getElementById('dropdownDiv_editCategory').classList.
toggle("show");
toggle('show');
});
document.getElementById('dropdown_name').addEventListener
@@ -269,25 +220,25 @@ WorkspaceFactoryInit.assignWorkspaceFactoryClickHandlers_ =
function() {
controller.changeCategoryName();
document.getElementById('dropdownDiv_editCategory').classList.
remove("show");
remove('show');
});
document.getElementById('button_importBlocks').addEventListener
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");
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_load').classList.toggle('show');
document.getElementById('dropdownDiv_export').classList.remove('show');
document.getElementById('dropdownDiv_importBlocks').classList.
remove("show");
remove('show');
});
document.getElementById('input_loadToolbox').addEventListener
@@ -295,7 +246,7 @@ document.getElementById('button_importBlocks').addEventListener
function() {
controller.importFile(event.target.files[0],
WorkspaceFactoryController.MODE_TOOLBOX);
document.getElementById('dropdownDiv_load').classList.remove("show");
document.getElementById('dropdownDiv_load').classList.remove('show');
});
document.getElementById('input_loadPreload').addEventListener
@@ -303,7 +254,7 @@ document.getElementById('button_importBlocks').addEventListener
function() {
controller.importFile(event.target.files[0],
WorkspaceFactoryController.MODE_PRELOAD);
document.getElementById('dropdownDiv_load').classList.remove("show");
document.getElementById('dropdownDiv_load').classList.remove('show');
});
document.getElementById('input_importBlocksJson').addEventListener
@@ -311,7 +262,7 @@ document.getElementById('button_importBlocks').addEventListener
function() {
controller.importBlocks(event.target.files[0],'JSON');
document.getElementById('dropdownDiv_importBlocks').classList.
remove("show");
remove('show');
});
document.getElementById('input_importBlocksJs').addEventListener
@@ -319,16 +270,16 @@ document.getElementById('button_importBlocks').addEventListener
function() {
controller.importBlocks(event.target.files[0],'JavaScript');
document.getElementById('dropdownDiv_importBlocks').classList.
remove("show");
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");
remove('show');
document.getElementById('dropdownDiv_export').classList.remove('show');
document.getElementById('dropdownDiv_load').classList.remove('show');
controller.clearAll();
});
@@ -367,10 +318,9 @@ document.getElementById('button_importBlocks').addEventListener
/**
* Add event listeners for workspace factory.
* @private
*
* @param {!FactoryController} controller The controller for the workspace
* factory tab.
* @private
*/
WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
// Use up and down arrow keys to move categories.
@@ -547,7 +497,6 @@ WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
/**
* Display or hide the add shadow button.
*
* @param {boolean} show True if the add shadow button should be shown, false
* otherwise.
*/
@@ -558,25 +507,22 @@ WorkspaceFactoryInit.displayAddShadow_ = function(show) {
/**
* Display or hide the remove shadow button.
*
* @param {boolean} show True if the remove shadow button should be shown, false
* otherwise.
*/
WorkspaceFactoryInit.displayRemoveShadow_ = function(show) {
document.getElementById('button_removeShadow').style.display =
show ? 'inline-block' : 'none';
}
};
/**
* Add listeners for workspace factory options input elements.
* @private
*
* @param {!FactoryController} controller The controller for the workspace
* factory tab.
* @private
*/
WorkspaceFactoryInit.addWorkspaceFactoryOptionsListeners_ =
function(controller) {
// Checking the grid checkbox displays grid options.
document.getElementById('option_grid_checkbox').addEventListener('change',
function(e) {
@@ -1,6 +1,6 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
@@ -63,7 +63,6 @@ WorkspaceFactoryModel = function() {
/**
* 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.
*/
@@ -79,7 +78,6 @@ WorkspaceFactoryModel.prototype.hasCategoryByName = function(name) {
/**
* Determines if a category with the 'VARIABLE' tag exists.
*
* @return {boolean} True if there exists a category with the Variables tag,
* false otherwise.
*/
@@ -89,7 +87,6 @@ WorkspaceFactoryModel.prototype.hasVariables = function() {
/**
* Determines if a category with the 'PROCEDURE' tag exists.
*
* @return {boolean} True if there exists a category with the Procedures tag,
* false otherwise.
*/
@@ -100,7 +97,6 @@ WorkspaceFactoryModel.prototype.hasProcedures = function() {
/**
* 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() {
@@ -109,7 +105,6 @@ WorkspaceFactoryModel.prototype.hasElements = function() {
/**
* 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) {
@@ -126,8 +121,7 @@ WorkspaceFactoryModel.prototype.addElementToList = function(element) {
/**
* Given an index, deletes a list element and all associated data.
*
* @param {int} index The index of the list element to delete.
* @param {number} index The index of the list element to delete.
*/
WorkspaceFactoryModel.prototype.deleteElementFromList = function(index) {
// Check if index is out of bounds.
@@ -148,24 +142,22 @@ WorkspaceFactoryModel.prototype.deleteElementFromList = function(index) {
* 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.
* @param {number} newIndex The index to insert the element at.
* @param {number} oldIndex The index the element is currently at.
*/
WorkspaceFactoryModel.prototype.moveElementToIndex = function(element, newIndex,
oldIndex) {
@@ -176,12 +168,11 @@ WorkspaceFactoryModel.prototype.moveElementToIndex = function(element, newIndex,
}
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() {
@@ -192,7 +183,6 @@ WorkspaceFactoryModel.prototype.getSelectedId = function() {
* 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() {
@@ -201,7 +191,6 @@ WorkspaceFactoryModel.prototype.getSelectedName = function() {
/**
* Returns the currently selected list element object.
*
* @return {ListElement} The currently selected ListElement
*/
WorkspaceFactoryModel.prototype.getSelected = function() {
@@ -210,7 +199,6 @@ WorkspaceFactoryModel.prototype.getSelected = function() {
/**
* Sets list element currently selected by id.
*
* @param {string} id ID of list element that should now be selected.
*/
WorkspaceFactoryModel.prototype.setSelectedById = function(id) {
@@ -220,12 +208,10 @@ WorkspaceFactoryModel.prototype.setSelectedById = function(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
* @param {string} id The ID of list element to search for.
* @return {number} 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) {
@@ -237,8 +223,7 @@ WorkspaceFactoryModel.prototype.getIndexByElementId = function(id) {
/**
* Given the ID of a list element, returns that ListElement object.
*
* @param {!string} id The ID of element to search for.
* @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.
*/
@@ -254,8 +239,7 @@ WorkspaceFactoryModel.prototype.getElementById = function(id) {
/**
* Given the index of a list element in toolboxList, returns that ListElement
* object.
*
* @param {int} index The index of the element to return.
* @param {number} index The index of the element to return.
* @return {ListElement} The corresponding ListElement object in toolboxList.
*/
WorkspaceFactoryModel.prototype.getElementByIndex = function(index) {
@@ -266,8 +250,7 @@ WorkspaceFactoryModel.prototype.getElementByIndex = function(index) {
};
/**
* Returns the xml to load the selected element.
*
* Returns the XML to load the selected element.
* @return {!Element} The XML of the selected element, or null if there is
* no selected element.
*/
@@ -277,8 +260,7 @@ WorkspaceFactoryModel.prototype.getSelectedXml = function() {
/**
* Return ordered list of ListElement objects.
*
* @return {!Array<!ListElement>} ordered list of ListElement objects
* @return {!Array.<!ListElement>} ordered list of ListElement objects
*/
WorkspaceFactoryModel.prototype.getToolboxList = function() {
return this.toolboxList;
@@ -286,9 +268,8 @@ WorkspaceFactoryModel.prototype.getToolboxList = function() {
/**
* Gets the ID of a category given its name.
*
* @param {string} name Name of category.
* @return {int} ID of category
* @return {number} ID of category
*/
WorkspaceFactoryModel.prototype.getCategoryIdByName = function(name) {
for (var i = 0; i < this.toolboxList.length; i++) {
@@ -313,8 +294,7 @@ WorkspaceFactoryModel.prototype.clearToolboxList = function() {
/**
* 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.
* @param {string} blockId The unique ID of block to be added.
*/
WorkspaceFactoryModel.prototype.addShadowBlock = function(blockId) {
this.shadowBlocks.push(blockId);
@@ -323,8 +303,7 @@ WorkspaceFactoryModel.prototype.addShadowBlock = function(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.
* @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++) {
@@ -337,8 +316,7 @@ WorkspaceFactoryModel.prototype.removeShadowBlock = function(blockId) {
/**
* Determines if a block is a shadow block given a unique block ID.
*
* @param {!string} blockId The unique ID of the block to examine.
* @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.
*/
@@ -354,10 +332,9 @@ WorkspaceFactoryModel.prototype.isShadowBlock = function(blockId) {
/**
* Given a set of blocks currently loaded, returns all blocks in the workspace
* that are user generated shadow blocks.
*
* @param {!<Blockly.Block>} blocks Array of blocks currently loaded.
* @return {!<Blockly.Block>} Array of user-generated shadow blocks currently
* loaded.
* loaded.
*/
WorkspaceFactoryModel.prototype.getShadowBlocksInWorkspace =
function(workspaceBlocks) {
@@ -373,9 +350,8 @@ WorkspaceFactoryModel.prototype.getShadowBlocksInWorkspace =
/**
* 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.
* @param {string} tag The custom tag to add to the category.
*/
WorkspaceFactoryModel.prototype.addCustomTag = function(category, tag) {
// Only update list elements that are categories.
@@ -395,7 +371,6 @@ WorkspaceFactoryModel.prototype.addCustomTag = function(category, tag) {
/**
* Have basic pre-loaded workspace working
* Saves XML as XML to be pre-loaded into the workspace.
*
* @param {!Element} xml The XML to be saved.
*/
WorkspaceFactoryModel.prototype.savePreloadXml = function(xml) {
@@ -404,7 +379,6 @@ WorkspaceFactoryModel.prototype.savePreloadXml = function(xml) {
/**
* Gets the XML to be pre-loaded into the workspace.
*
* @return {!Element} The XML for the workspace.
*/
WorkspaceFactoryModel.prototype.getPreloadXml = function() {
@@ -413,7 +387,6 @@ WorkspaceFactoryModel.prototype.getPreloadXml = function() {
/**
* 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) {
@@ -424,8 +397,7 @@ WorkspaceFactoryModel.prototype.setOptions = function(options) {
* 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<!string>} Array of block types currently being used.
* @return {!Array.<string>} Array of block types currently being used.
*/
WorkspaceFactoryModel.prototype.getAllUsedBlockTypes = function() {
var blockTypeList = [];
@@ -466,8 +438,7 @@ WorkspaceFactoryModel.prototype.getAllUsedBlockTypes = function() {
/**
* Adds new imported block types to the list of current imported block types.
*
* @param {!Array<!string>} blockTypes Array of block types imported.
* @param {!Array.<string>} blockTypes Array of block types imported.
*/
WorkspaceFactoryModel.prototype.addImportedBlockTypes = function(blockTypes) {
this.importedBlockTypes = this.importedBlockTypes.concat(blockTypes);
@@ -475,8 +446,7 @@ WorkspaceFactoryModel.prototype.addImportedBlockTypes = function(blockTypes) {
/**
* Updates block types in block library.
*
* @param {!Array<!string>} blockTypes Array of block types in block library.
* @param {!Array.<string>} blockTypes Array of block types in block library.
*/
WorkspaceFactoryModel.prototype.updateLibBlockTypes = function(blockTypes) {
this.libBlockTypes = blockTypes;
@@ -485,8 +455,7 @@ WorkspaceFactoryModel.prototype.updateLibBlockTypes = function(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.
* @param {string} blockType Block type to check.
* @return {boolean} True if blockType is defined, false otherwise.
*/
WorkspaceFactoryModel.prototype.isDefinedBlockType = function(blockType) {
@@ -499,8 +468,7 @@ WorkspaceFactoryModel.prototype.isDefinedBlockType = function(blockType) {
/**
* Checks if any of the block types are already defined.
*
* @param {!Array<!string>} blockTypes Array of block types.
* @param {!Array.<string>} blockTypes Array of block types.
* @return {boolean} True if a block type in the array is already defined,
* false if none of the blocks are already defined.
*/
@@ -511,7 +479,7 @@ WorkspaceFactoryModel.prototype.hasDefinedBlockTypes = function(blockTypes) {
}
}
return false;
}
};
/**
* Class for a ListElement.
@@ -539,7 +507,6 @@ 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.
*/
@@ -555,7 +522,6 @@ ListElement.prototype.saveFromWorkspace = function(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) {
@@ -569,8 +535,7 @@ ListElement.prototype.changeName = function (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.
* @param {string} color The color that should be used for that category.
*/
ListElement.prototype.changeColor = function (color) {
if (this.type != ListElement.TYPE_CATEGORY) {
@@ -582,7 +547,6 @@ ListElement.prototype.changeColor = function (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() {
@@ -1,6 +1,6 @@
/**
* @license
* Visual Blocks Editor
* Blockly Demos: Block Factory
*
* Copyright 2016 Google Inc.
* https://developers.google.com/blockly/
@@ -28,13 +28,12 @@
* @author Emma Dauterman (edauterman)
*/
goog.require('FactoryUtils');
/**
* Class for a WorkspaceFactoryView
* @constructor
*/
goog.require('FactoryUtils');
/**
* 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);
@@ -42,9 +41,8 @@ WorkspaceFactoryView = function() {
/**
* 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 {string} name The name of the category being created
* @param {string} id ID of category being created
* @return {!Element} DOM element created for tab
*/
WorkspaceFactoryView.prototype.addCategoryRow = function(name, id) {
@@ -70,9 +68,8 @@ WorkspaceFactoryView.prototype.addCategoryRow = function(name, id) {
/**
* 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.
* @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.
@@ -98,18 +95,16 @@ WorkspaceFactoryView.prototype.addEmptyCategoryMessage = function() {
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 {number} 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) {
@@ -117,20 +112,18 @@ WorkspaceFactoryView.prototype.updateState = function(selectedIndex, selected) {
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;
document.getElementById('button_up').disabled = selectedIndex <= 0;
var table = document.getElementById('categoryTable');
document.getElementById('button_down').disabled = selectedIndex >=
table.rows.length - 1 || selectedIndex < 0 ? true : false;
table.rows.length - 1 || selectedIndex < 0;
// 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
* @param {string} name Name of category
* @return {string} ID of category tab
*/
WorkspaceFactoryView.prototype.createCategoryIdName = function(name) {
return 'tab_' + name;
@@ -138,8 +131,7 @@ WorkspaceFactoryView.prototype.createCategoryIdName = function(name) {
/**
* Switches a tab on or off.
*
* @param {!string} id ID of the tab to switch 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.
*/
@@ -154,7 +146,6 @@ WorkspaceFactoryView.prototype.setCategoryTabSelection =
/**
* 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
*/
@@ -169,31 +160,28 @@ WorkspaceFactoryView.prototype.bindClick = function(el, func) {
/**
* 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 {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);
};
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
*
* @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;
@@ -204,15 +192,14 @@ WorkspaceFactoryView.prototype.updateCategoryName = function(newName, id) {
* 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.
* @param {string} id The ID of the category to move.
* @param {number} newIndex The index to move the category to.
* @param {number} 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
// 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.');
@@ -234,23 +221,21 @@ WorkspaceFactoryView.prototype.moveTabToIndex =
/**
* 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.
* @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.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 {string} id The ID of the separator.
* @param {!Element} The td DOM element representing the separator.
*/
WorkspaceFactoryView.prototype.addSeparatorTab = function(id) {
@@ -275,7 +260,6 @@ WorkspaceFactoryView.prototype.addSeparatorTab = function(id) {
* 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.
*/
@@ -293,7 +277,6 @@ WorkspaceFactoryView.prototype.disableWorkspace = function(disable) {
/**
* 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) {
@@ -302,7 +285,7 @@ WorkspaceFactoryView.prototype.shouldDisableWorkspace = function(category) {
category.custom == 'VARIABLE' || category.custom == 'PROCEDURE');
};
/*
/**
* Removes all categories and separators in the view. Clears the tabMap to
* reflect this.
*/
@@ -320,8 +303,7 @@ WorkspaceFactoryView.prototype.clearToolboxTabs = function() {
* 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 {!<Blockly.Block>} blocks Array of user-generated shadow blocks
* @param {!Array.<!Blockly.Block>} blocks Array of user-generated shadow blocks
* currently loaded.
*/
WorkspaceFactoryView.prototype.markShadowBlocks = function(blocks) {
@@ -334,9 +316,8 @@ WorkspaceFactoryView.prototype.markShadowBlocks = function(blocks) {
* 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).
* block (must be rendered).
*/
WorkspaceFactoryView.prototype.markShadowBlock = function(block) {
// Add Blockly CSS for user-generated shadow blocks.
@@ -346,7 +327,6 @@ WorkspaceFactoryView.prototype.markShadowBlock = function(block) {
block.setWarningText('Shadow blocks must be nested inside' +
' other blocks to be displayed.');
}
if (FactoryUtils.hasVariableField(block)) {
block.setWarningText('Cannot make variable blocks shadow blocks.');
}
@@ -354,9 +334,8 @@ WorkspaceFactoryView.prototype.markShadowBlock = function(block) {
/**
* 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).
* block (must be rendered).
*/
WorkspaceFactoryView.prototype.unmarkShadowBlock = function(block) {
// Remove Blockly CSS for user-generated shadow blocks.
@@ -368,9 +347,8 @@ WorkspaceFactoryView.prototype.unmarkShadowBlock = function(block) {
/**
* 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).
* @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 ==
@@ -385,17 +363,16 @@ WorkspaceFactoryView.prototype.setModeSelection = function(mode) {
/**
* 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).
* @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.';
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.'
var helpText = 'Drag blocks into the workspace to pre-load them in your ' +
'custom workspace.'
}
document.getElementById('editHelpText').textContent = helpText;
};
@@ -407,7 +384,7 @@ WorkspaceFactoryView.prototype.updateHelpText = function(mode) {
WorkspaceFactoryView.prototype.setBaseOptions = function() {
// Set basic options.
document.getElementById('option_css_checkbox').checked = true;
document.getElementById('option_infiniteBlocks_checkbox').checked = true;
document.getElementById('option_infiniteBlocks_checkbox').checked = true;
document.getElementById('option_maxBlocks_number').value = 100;
document.getElementById('option_media_text').value =
'https://blockly-demo.appspot.com/static/media/';
@@ -439,7 +416,6 @@ WorkspaceFactoryView.prototype.setBaseOptions = function() {
/**
* 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.
*/
@@ -449,4 +425,4 @@ WorkspaceFactoryView.prototype.setCategoryOptions = function(hasCategories) {
document.getElementById('option_disable_checkbox').checked = hasCategories;
document.getElementById('option_scrollbars_checkbox').checked = hasCategories;
document.getElementById('option_trashcan_checkbox').checked = hasCategories;
}
};
+826
View File
@@ -0,0 +1,826 @@
/**
* Blockly Demos: Block Factory Blocks
*
* Copyright 2012 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 Blocks for Blockly's Block Factory application.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
Blockly.Blocks['factory_base'] = {
// Base of new block.
init: function() {
this.setColour(120);
this.appendDummyInput()
.appendField('name')
.appendField(new Blockly.FieldTextInput('block_type'), 'NAME');
this.appendStatementInput('INPUTS')
.setCheck('Input')
.appendField('inputs');
var dropdown = new Blockly.FieldDropdown([
['automatic inputs', 'AUTO'],
['external inputs', 'EXT'],
['inline inputs', 'INT']]);
this.appendDummyInput()
.appendField(dropdown, 'INLINE');
dropdown = new Blockly.FieldDropdown([
['no connections', 'NONE'],
['← left output', 'LEFT'],
['↕ top+bottom connections', 'BOTH'],
['↑ top connection', 'TOP'],
['↓ bottom connection', 'BOTTOM']],
function(option) {
this.sourceBlock_.updateShape_(option);
// Connect a shadow block to this new input.
this.sourceBlock_.spawnOutputShadow_(option);
});
this.appendDummyInput()
.appendField(dropdown, 'CONNECTIONS');
this.appendValueInput('COLOUR')
.setCheck('Colour')
.appendField('colour');
this.setTooltip('Build a custom block by plugging\n' +
'fields, inputs and other blocks here.');
this.setHelpUrl(
'https://developers.google.com/blockly/guides/create-custom-blocks/block-factory');
},
mutationToDom: function() {
var container = document.createElement('mutation');
container.setAttribute('connections', this.getFieldValue('CONNECTIONS'));
return container;
},
domToMutation: function(xmlElement) {
var connections = xmlElement.getAttribute('connections');
this.updateShape_(connections);
},
spawnOutputShadow_: function(option) {
// Helper method for deciding which type of outputs this block needs
// to attach shaddow blocks to.
switch (option) {
case 'LEFT':
this.connectOutputShadow_('OUTPUTTYPE');
break;
case 'TOP':
this.connectOutputShadow_('TOPTYPE');
break;
case 'BOTTOM':
this.connectOutputShadow_('BOTTOMTYPE');
break;
case 'BOTH':
this.connectOutputShadow_('TOPTYPE');
this.connectOutputShadow_('BOTTOMTYPE');
break;
}
},
connectOutputShadow_: function(outputType) {
// Helper method to create & connect shadow block.
var type = this.workspace.newBlock('type_null');
type.setShadow(true);
type.outputConnection.connect(this.getInput(outputType).connection);
type.initSvg();
type.render();
},
updateShape_: function(option) {
var outputExists = this.getInput('OUTPUTTYPE');
var topExists = this.getInput('TOPTYPE');
var bottomExists = this.getInput('BOTTOMTYPE');
if (option == 'LEFT') {
if (!outputExists) {
this.addTypeInput_('OUTPUTTYPE', 'output type');
}
} else if (outputExists) {
this.removeInput('OUTPUTTYPE');
}
if (option == 'TOP' || option == 'BOTH') {
if (!topExists) {
this.addTypeInput_('TOPTYPE', 'top type');
}
} else if (topExists) {
this.removeInput('TOPTYPE');
}
if (option == 'BOTTOM' || option == 'BOTH') {
if (!bottomExists) {
this.addTypeInput_('BOTTOMTYPE', 'bottom type');
}
} else if (bottomExists) {
this.removeInput('BOTTOMTYPE');
}
},
addTypeInput_: function(name, label) {
this.appendValueInput(name)
.setCheck('Type')
.appendField(label);
this.moveInputBefore(name, 'COLOUR');
}
};
var FIELD_MESSAGE = 'fields %1 %2';
var FIELD_ARGS = [
{
"type": "field_dropdown",
"name": "ALIGN",
"options": [['left', 'LEFT'], ['right', 'RIGHT'], ['centre', 'CENTRE']],
},
{
"type": "input_statement",
"name": "FIELDS",
"check": "Field"
}
];
var TYPE_MESSAGE = 'type %1';
var TYPE_ARGS = [
{
"type": "input_value",
"name": "TYPE",
"check": "Type",
"align": "RIGHT"
}
];
Blockly.Blocks['input_value'] = {
// Value input.
init: function() {
this.jsonInit({
"message0": "value input %1 %2",
"args0": [
{
"type": "field_input",
"name": "INPUTNAME",
"text": "NAME"
},
{
"type": "input_dummy"
}
],
"message1": FIELD_MESSAGE,
"args1": FIELD_ARGS,
"message2": TYPE_MESSAGE,
"args2": TYPE_ARGS,
"previousStatement": "Input",
"nextStatement": "Input",
"colour": 210,
"tooltip": "A value socket for horizontal connections.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=71"
});
},
onchange: function() {
inputNameCheck(this);
}
};
Blockly.Blocks['input_statement'] = {
// Statement input.
init: function() {
this.jsonInit({
"message0": "statement input %1 %2",
"args0": [
{
"type": "field_input",
"name": "INPUTNAME",
"text": "NAME"
},
{
"type": "input_dummy"
},
],
"message1": FIELD_MESSAGE,
"args1": FIELD_ARGS,
"message2": TYPE_MESSAGE,
"args2": TYPE_ARGS,
"previousStatement": "Input",
"nextStatement": "Input",
"colour": 210,
"tooltip": "A statement socket for enclosed vertical stacks.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=246"
});
},
onchange: function() {
inputNameCheck(this);
}
};
Blockly.Blocks['input_dummy'] = {
// Dummy input.
init: function() {
this.jsonInit({
"message0": "dummy input",
"message1": FIELD_MESSAGE,
"args1": FIELD_ARGS,
"previousStatement": "Input",
"nextStatement": "Input",
"colour": 210,
"tooltip": "For adding fields on a separate row with no " +
"connections. Alignment options (left, right, centre) " +
"apply only to multi-line fields.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=293"
});
}
};
Blockly.Blocks['field_static'] = {
// Text value.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('text')
.appendField(new Blockly.FieldTextInput(''), 'TEXT');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Static text that serves as a label.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=88');
}
};
Blockly.Blocks['field_input'] = {
// Text input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('text input')
.appendField(new Blockly.FieldTextInput('default'), 'TEXT')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('An input field for the user to enter text.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_number'] = {
// Numeric input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('numeric input')
.appendField(new Blockly.FieldNumber(0), 'VALUE')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.appendDummyInput()
.appendField('min')
.appendField(new Blockly.FieldNumber(-Infinity), 'MIN')
.appendField('max')
.appendField(new Blockly.FieldNumber(Infinity), 'MAX')
.appendField('precision')
.appendField(new Blockly.FieldNumber(0, 0), 'PRECISION');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('An input field for the user to enter a number.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=319');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_angle'] = {
// Angle input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('angle input')
.appendField(new Blockly.FieldAngle('90'), 'ANGLE')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('An input field for the user to enter an angle.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=372');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_dropdown'] = {
// Dropdown menu.
init: function() {
this.appendDummyInput()
.appendField('dropdown')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.optionCount_ = 3;
this.updateShape_();
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setMutator(new Blockly.Mutator(['field_dropdown_option']));
this.setColour(160);
this.setTooltip('Dropdown menu with a list of options.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
},
mutationToDom: function(workspace) {
// Create XML to represent menu options.
var container = document.createElement('mutation');
container.setAttribute('options', this.optionCount_);
return container;
},
domToMutation: function(container) {
// Parse XML to restore the menu options.
this.optionCount_ = parseInt(container.getAttribute('options'), 10);
this.updateShape_();
},
decompose: function(workspace) {
// Populate the mutator's dialog with this block's components.
var containerBlock = workspace.newBlock('field_dropdown_container');
containerBlock.initSvg();
var connection = containerBlock.getInput('STACK').connection;
for (var i = 0; i < this.optionCount_; i++) {
var optionBlock = workspace.newBlock('field_dropdown_option');
optionBlock.initSvg();
connection.connect(optionBlock.previousConnection);
connection = optionBlock.nextConnection;
}
return containerBlock;
},
compose: function(containerBlock) {
// Reconfigure this block based on the mutator dialog's components.
var optionBlock = containerBlock.getInputTargetBlock('STACK');
// Count number of inputs.
var data = [];
while (optionBlock) {
data.push([optionBlock.userData_, optionBlock.cpuData_]);
optionBlock = optionBlock.nextConnection &&
optionBlock.nextConnection.targetBlock();
}
this.optionCount_ = data.length;
this.updateShape_();
// Restore any data.
for (var i = 0; i < this.optionCount_; i++) {
this.setFieldValue(data[i][0] || 'option', 'USER' + i);
this.setFieldValue(data[i][1] || 'OPTIONNAME', 'CPU' + i);
}
},
saveConnections: function(containerBlock) {
// Store names and values for each option.
var optionBlock = containerBlock.getInputTargetBlock('STACK');
var i = 0;
while (optionBlock) {
optionBlock.userData_ = this.getFieldValue('USER' + i);
optionBlock.cpuData_ = this.getFieldValue('CPU' + i);
i++;
optionBlock = optionBlock.nextConnection &&
optionBlock.nextConnection.targetBlock();
}
},
updateShape_: function() {
// Modify this block to have the correct number of options.
// Add new options.
for (var i = 0; i < this.optionCount_; i++) {
if (!this.getInput('OPTION' + i)) {
this.appendDummyInput('OPTION' + i)
.appendField(new Blockly.FieldTextInput('option'), 'USER' + i)
.appendField(',')
.appendField(new Blockly.FieldTextInput('OPTIONNAME'), 'CPU' + i);
}
}
// Remove deleted options.
while (this.getInput('OPTION' + i)) {
this.removeInput('OPTION' + i);
i++;
}
},
onchange: function() {
if (this.workspace && this.optionCount_ < 1) {
this.setWarningText('Drop down menu must\nhave at least one option.');
} else {
fieldNameCheck(this);
}
}
};
Blockly.Blocks['field_dropdown_container'] = {
// Container.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('add options');
this.appendStatementInput('STACK');
this.setTooltip('Add, remove, or reorder options\n' +
'to reconfigure this dropdown menu.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
this.contextMenu = false;
}
};
Blockly.Blocks['field_dropdown_option'] = {
// Add option.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('option');
this.setPreviousStatement(true);
this.setNextStatement(true);
this.setTooltip('Add a new option to the dropdown menu.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=386');
this.contextMenu = false;
}
};
Blockly.Blocks['field_checkbox'] = {
// Checkbox.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('checkbox')
.appendField(new Blockly.FieldCheckbox('TRUE'), 'CHECKED')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Checkbox field.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=485');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_colour'] = {
// Colour input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('colour')
.appendField(new Blockly.FieldColour('#ff0000'), 'COLOUR')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Colour input field.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=495');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_date'] = {
// Date input.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('date')
.appendField(new Blockly.FieldDate(), 'DATE')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Date input field.');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_variable'] = {
// Dropdown for variables.
init: function() {
this.setColour(160);
this.appendDummyInput()
.appendField('variable')
.appendField(new Blockly.FieldTextInput('item'), 'TEXT')
.appendField(',')
.appendField(new Blockly.FieldTextInput('NAME'), 'FIELDNAME');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Dropdown menu for variable names.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=510');
},
onchange: function() {
fieldNameCheck(this);
}
};
Blockly.Blocks['field_image'] = {
// Image.
init: function() {
this.setColour(160);
var src = 'https://www.gstatic.com/codesite/ph/images/star_on.gif';
this.appendDummyInput()
.appendField('image')
.appendField(new Blockly.FieldTextInput(src), 'SRC');
this.appendDummyInput()
.appendField('width')
.appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'WIDTH')
.appendField('height')
.appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT')
.appendField('alt text')
.appendField(new Blockly.FieldTextInput('*'), 'ALT');
this.setPreviousStatement(true, 'Field');
this.setNextStatement(true, 'Field');
this.setTooltip('Static image (JPEG, PNG, GIF, SVG, BMP).\n' +
'Retains aspect ratio regardless of height and width.\n' +
'Alt text is for when collapsed.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=567');
}
};
Blockly.Blocks['type_group'] = {
// Group of types.
init: function() {
this.typeCount_ = 2;
this.updateShape_();
this.setOutput(true, 'Type');
this.setMutator(new Blockly.Mutator(['type_group_item']));
this.setColour(230);
this.setTooltip('Allows more than one type to be accepted.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677');
},
mutationToDom: function(workspace) {
// Create XML to represent a group of types.
var container = document.createElement('mutation');
container.setAttribute('types', this.typeCount_);
return container;
},
domToMutation: function(container) {
// Parse XML to restore the group of types.
this.typeCount_ = parseInt(container.getAttribute('types'), 10);
this.updateShape_();
for (var i = 0; i < this.typeCount_; i++) {
this.removeInput('TYPE' + i);
}
for (var i = 0; i < this.typeCount_; i++) {
var input = this.appendValueInput('TYPE' + i)
.setCheck('Type');
if (i == 0) {
input.appendField('any of');
}
}
},
decompose: function(workspace) {
// Populate the mutator's dialog with this block's components.
var containerBlock = workspace.newBlock('type_group_container');
containerBlock.initSvg();
var connection = containerBlock.getInput('STACK').connection;
for (var i = 0; i < this.typeCount_; i++) {
var typeBlock = workspace.newBlock('type_group_item');
typeBlock.initSvg();
connection.connect(typeBlock.previousConnection);
connection = typeBlock.nextConnection;
}
return containerBlock;
},
compose: function(containerBlock) {
// Reconfigure this block based on the mutator dialog's components.
var typeBlock = containerBlock.getInputTargetBlock('STACK');
// Count number of inputs.
var connections = [];
while (typeBlock) {
connections.push(typeBlock.valueConnection_);
typeBlock = typeBlock.nextConnection &&
typeBlock.nextConnection.targetBlock();
}
// Disconnect any children that don't belong.
for (var i = 0; i < this.typeCount_; i++) {
var connection = this.getInput('TYPE' + i).connection.targetConnection;
if (connection && connections.indexOf(connection) == -1) {
connection.disconnect();
}
}
this.typeCount_ = connections.length;
this.updateShape_();
// Reconnect any child blocks.
for (var i = 0; i < this.typeCount_; i++) {
Blockly.Mutator.reconnect(connections[i], this, 'TYPE' + i);
}
},
saveConnections: function(containerBlock) {
// Store a pointer to any connected child blocks.
var typeBlock = containerBlock.getInputTargetBlock('STACK');
var i = 0;
while (typeBlock) {
var input = this.getInput('TYPE' + i);
typeBlock.valueConnection_ = input && input.connection.targetConnection;
i++;
typeBlock = typeBlock.nextConnection &&
typeBlock.nextConnection.targetBlock();
}
},
updateShape_: function() {
// Modify this block to have the correct number of inputs.
// Add new inputs.
for (var i = 0; i < this.typeCount_; i++) {
if (!this.getInput('TYPE' + i)) {
var input = this.appendValueInput('TYPE' + i);
if (i == 0) {
input.appendField('any of');
}
}
}
// Remove deleted inputs.
while (this.getInput('TYPE' + i)) {
this.removeInput('TYPE' + i);
i++;
}
}
};
Blockly.Blocks['type_group_container'] = {
// Container.
init: function() {
this.jsonInit({
"message0": "add types %1 %2",
"args0": [
{"type": "input_dummy"},
{"type": "input_statement", "name": "STACK"}
],
"colour": 230,
"tooltip": "Add, or remove allowed type.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
});
}
};
Blockly.Blocks['type_group_item'] = {
// Add type.
init: function() {
this.jsonInit({
"message0": "type",
"previousStatement": null,
"nextStatement": null,
"colour": 230,
"tooltip": "Add a new allowed type.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=677"
});
}
};
Blockly.Blocks['type_null'] = {
// Null type.
valueType: null,
init: function() {
this.jsonInit({
"message0": "any",
"output": "Type",
"colour": 230,
"tooltip": "Any type is allowed.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
});
}
};
Blockly.Blocks['type_boolean'] = {
// Boolean type.
valueType: 'Boolean',
init: function() {
this.jsonInit({
"message0": "Boolean",
"output": "Type",
"colour": 230,
"tooltip": "Booleans (true/false) are allowed.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
});
}
};
Blockly.Blocks['type_number'] = {
// Number type.
valueType: 'Number',
init: function() {
this.jsonInit({
"message0": "Number",
"output": "Type",
"colour": 230,
"tooltip": "Numbers (int/float) are allowed.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
});
}
};
Blockly.Blocks['type_string'] = {
// String type.
valueType: 'String',
init: function() {
this.jsonInit({
"message0": "String",
"output": "Type",
"colour": 230,
"tooltip": "Strings (text) are allowed.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
});
}
};
Blockly.Blocks['type_list'] = {
// List type.
valueType: 'Array',
init: function() {
this.jsonInit({
"message0": "Array",
"output": "Type",
"colour": 230,
"tooltip": "Arrays (lists) are allowed.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=602"
});
}
};
Blockly.Blocks['type_other'] = {
// Other type.
init: function() {
this.jsonInit({
"message0": "other %1",
"args0": [{"type": "field_input", "name": "TYPE", "text": ""}],
"output": "Type",
"colour": 230,
"tooltip": "Custom type to allow.",
"helpUrl": "https://www.youtube.com/watch?v=s2_xaEvcVI0#t=702"
});
}
};
Blockly.Blocks['colour_hue'] = {
// Set the colour of the block.
init: function() {
this.appendDummyInput()
.appendField('hue:')
.appendField(new Blockly.FieldAngle('0', this.validator), 'HUE');
this.setOutput(true, 'Colour');
this.setTooltip('Paint the block with this colour.');
this.setHelpUrl('https://www.youtube.com/watch?v=s2_xaEvcVI0#t=55');
},
validator: function(text) {
// Update the current block's colour to match.
var hue = parseInt(text, 10);
if (!isNaN(hue)) {
this.sourceBlock_.setColour(hue);
}
},
mutationToDom: function(workspace) {
var container = document.createElement('mutation');
container.setAttribute('colour', this.getColour());
return container;
},
domToMutation: function(container) {
this.setColour(container.getAttribute('colour'));
}
};
/**
* Check to see if more than one field has this name.
* Highly inefficient (On^2), but n is small.
* @param {!Blockly.Block} referenceBlock Block to check.
*/
function fieldNameCheck(referenceBlock) {
if (!referenceBlock.workspace) {
// Block has been deleted.
return;
}
var name = referenceBlock.getFieldValue('FIELDNAME').toLowerCase();
var count = 0;
var blocks = referenceBlock.workspace.getAllBlocks();
for (var i = 0, block; block = blocks[i]; i++) {
var otherName = block.getFieldValue('FIELDNAME');
if (!block.disabled && !block.getInheritedDisabled() &&
otherName && otherName.toLowerCase() == name) {
count++;
}
}
var msg = (count > 1) ?
'There are ' + count + ' field blocks\n with this name.' : null;
referenceBlock.setWarningText(msg);
}
/**
* Check to see if more than one input has this name.
* Highly inefficient (On^2), but n is small.
* @param {!Blockly.Block} referenceBlock Block to check.
*/
function inputNameCheck(referenceBlock) {
if (!referenceBlock.workspace) {
// Block has been deleted.
return;
}
var name = referenceBlock.getFieldValue('INPUTNAME').toLowerCase();
var count = 0;
var blocks = referenceBlock.workspace.getAllBlocks();
for (var i = 0, block; block = blocks[i]; i++) {
var otherName = block.getFieldValue('INPUTNAME');
if (!block.disabled && !block.getInheritedDisabled() &&
otherName && otherName.toLowerCase() == name) {
count++;
}
}
var msg = (count > 1) ?
'There are ' + count + ' input blocks\n with this name.' : null;
referenceBlock.setWarningText(msg);
}
+850
View File
@@ -0,0 +1,850 @@
/**
* Blockly Demos: Block Factory
*
* Copyright 2012 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.
* @author fraser@google.com (Neil Fraser)
*/
'use strict';
/**
* Workspace for user to build block.
* @type {Blockly.Workspace}
*/
var mainWorkspace = null;
/**
* Workspace for preview of block.
* @type {Blockly.Workspace}
*/
var previewWorkspace = null;
/**
* Name of block if not named.
*/
var UNNAMED = 'unnamed';
/**
* Change the language code format.
*/
function formatChange() {
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();
updatePreview();
} else {
mask.style.display = 'none';
languageTA.style.display = 'none';
languagePre.style.display = 'block';
updateLanguage();
}
disableEnableLink();
}
/**
* Update the language code based on constructs made in Blockly.
*/
function updateLanguage() {
var rootBlock = getRootBlock();
if (!rootBlock) {
return;
}
var blockType = rootBlock.getFieldValue('NAME').trim().toLowerCase();
if (!blockType) {
blockType = UNNAMED;
}
blockType = blockType.replace(/\W/g, '_').replace(/^(\d)/, '_\\1');
switch (document.getElementById('format').value) {
case 'JSON':
var code = formatJson_(blockType, rootBlock);
break;
case 'JavaScript':
var code = formatJavaScript_(blockType, rootBlock);
break;
}
injectCode(code, 'languagePre');
updatePreview();
}
/**
* 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
*/
function formatJson_(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 = 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(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 && 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(getOptTypesFrom(rootBlock, 'OUTPUTTYPE') || 'null');
break;
case 'BOTH':
JS.previousStatement =
JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
JS.nextStatement =
JSON.parse(getOptTypesFrom(rootBlock, 'BOTTOMTYPE') || 'null');
break;
case 'TOP':
JS.previousStatement =
JSON.parse(getOptTypesFrom(rootBlock, 'TOPTYPE') || 'null');
break;
case 'BOTTOM':
JS.nextStatement =
JSON.parse(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.
* @return {string} Generanted language code.
* @private
*/
function formatJavaScript_(blockType, rootBlock) {
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 = escapeString(contentsBlock.getFieldValue('INPUTNAME'));
}
code.push(' this.' + TYPES[contentsBlock.type] + '(' + name + ')');
var check = 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 = 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(connectionLineJs_('setOutput', 'OUTPUTTYPE'));
break;
case 'BOTH':
code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
break;
case 'TOP':
code.push(connectionLineJs_('setPreviousStatement', 'TOPTYPE'));
break;
case 'BOTTOM':
code.push(connectionLineJs_('setNextStatement', 'BOTTOMTYPE'));
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.
* @return {string} Line of JavaScript code to create connection.
* @private
*/
function connectionLineJs_(functionName, typeName) {
var type = getOptTypesFrom(getRootBlock(), 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.<string>} Field strings.
* @private
*/
function getFieldsJs_(block) {
var fields = [];
while (block) {
if (!block.disabled && !block.getInheritedDisabled()) {
switch (block.type) {
case 'field_static':
// Result: 'hello'
fields.push(escapeString(block.getFieldValue('TEXT')));
break;
case 'field_input':
// Result: new Blockly.FieldTextInput('Hello'), 'GREET'
fields.push('new Blockly.FieldTextInput(' +
escapeString(block.getFieldValue('TEXT')) + '), ' +
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(', ') + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_angle':
// Result: new Blockly.FieldAngle(90), 'ANGLE'
fields.push('new Blockly.FieldAngle(' +
Number(block.getFieldValue('ANGLE')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_checkbox':
// Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
fields.push('new Blockly.FieldCheckbox(' +
escapeString(block.getFieldValue('CHECKED')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_colour':
// Result: new Blockly.FieldColour('#ff0000'), 'COLOUR'
fields.push('new Blockly.FieldColour(' +
escapeString(block.getFieldValue('COLOUR')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_date':
// Result: new Blockly.FieldDate('2015-02-04'), 'DATE'
fields.push('new Blockly.FieldDate(' +
escapeString(block.getFieldValue('DATE')) + '), ' +
escapeString(block.getFieldValue('FIELDNAME')));
break;
case 'field_variable':
// Result: new Blockly.FieldVariable('item'), 'VAR'
var varname = escapeString(block.getFieldValue('TEXT') || null);
fields.push('new Blockly.FieldVariable(' + varname + '), ' +
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] = '[' + escapeString(block.getFieldValue('USER' + i)) +
', ' + escapeString(block.getFieldValue('CPU' + i)) + ']';
}
if (options.length) {
fields.push('new Blockly.FieldDropdown([' +
options.join(', ') + ']), ' +
escapeString(block.getFieldValue('FIELDNAME')));
}
break;
case 'field_image':
// Result: new Blockly.FieldImage('http://...', 80, 60, '*')
var src = escapeString(block.getFieldValue('SRC'));
var width = Number(block.getFieldValue('WIDTH'));
var height = Number(block.getFieldValue('HEIGHT'));
var alt = 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.<string|!Object>} Array of static text and field configs.
* @private
*/
function getFieldsJson_(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;
}
/**
* Escape a string.
* @param {string} string String to escape.
* @return {string} Escaped string surrouned by quotes.
*/
function escapeString(string) {
return JSON.stringify(string);
}
/**
* 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.
*/
function getOptTypesFrom(block, name) {
var types = 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.<string>} List of types.
* @private
*/
function getTypesFrom_(block, name) {
var typeBlock = block.getInputTargetBlock(name);
var types;
if (!typeBlock || typeBlock.disabled) {
types = [];
} else if (typeBlock.type == 'type_other') {
types = [escapeString(typeBlock.getFieldValue('TYPE'))];
} else if (typeBlock.type == 'type_group') {
types = [];
for (var i = 0; i < typeBlock.typeCount_; i++) {
types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + i));
}
// 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 = [escapeString(typeBlock.valueType)];
}
return types;
}
/**
* Update the generator code.
* @param {!Blockly.Block} block Rendered block in preview workspace.
*/
function updateGenerator(block) {
function makeVar(root, name) {
name = name.toLowerCase().replace(/\W/g, '_');
return ' var ' + root + '_' + name;
}
var language = document.getElementById('language').value;
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("};");
injectCode(code.join('\n'), 'generatorPre');
}
/**
* Existing direction ('ltr' vs 'rtl') of preview.
*/
var oldDir = null;
/**
* Update the preview display.
*/
function updatePreview() {
// Toggle between LTR/RTL if needed (also used in first display).
var newDir = document.getElementById('direction').value;
if (oldDir != newDir) {
if (previewWorkspace) {
previewWorkspace.dispose();
}
var rtl = newDir == 'rtl';
previewWorkspace = Blockly.inject('preview',
{rtl: rtl,
media: '../../media/',
scrollbars: true});
oldDir = newDir;
}
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 = {};
for (var prop in backupBlocks) {
Blockly.Blocks[prop] = backupBlocks[prop];
}
if (format == 'JSON') {
var json = JSON.parse(code);
Blockly.Blocks[json.type || 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 = previewWorkspace.newBlock(blockType);
previewBlock.initSvg();
previewBlock.render();
previewBlock.setMovable(false);
previewBlock.setDeletable(false);
previewBlock.moveBy(15, 10);
previewWorkspace.clearUndo();
updateGenerator(previewBlock);
} finally {
Blockly.Blocks = backupBlocks;
}
}
/**
* 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 <pre> element to inject into.
*/
function injectCode(code, id) {
var pre = document.getElementById(id);
pre.textContent = code;
code = pre.innerHTML;
code = prettyPrintOne(code, 'js');
pre.innerHTML = code;
}
/**
* Return the uneditable container block that everything else attaches to.
* @return {Blockly.Block}
*/
function getRootBlock() {
var blocks = mainWorkspace.getTopBlocks(false);
for (var i = 0, block; block = blocks[i]; i++) {
if (block.type == 'factory_base') {
return block;
}
}
return null;
}
/**
* Disable the link button if the format is 'Manual', enable otherwise.
*/
function disableEnableLink() {
var linkButton = document.getElementById('linkButton');
linkButton.disabled = document.getElementById('format').value == 'Manual';
}
/**
* Initialize Blockly and layout. Called on page load.
*/
function init() {
if ('BlocklyStorage' in window) {
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(mainWorkspace);});
disableEnableLink();
}
document.getElementById('helpButton').addEventListener('click',
function() {
open('https://developers.google.com/blockly/guides/create-custom-blocks/block-factory',
'BlockFactoryHelp');
});
var expandList = [
document.getElementById('blockly'),
document.getElementById('blocklyMask'),
document.getElementById('preview'),
document.getElementById('languagePre'),
document.getElementById('languageTA'),
document.getElementById('generatorPre')
];
var onresize = function(e) {
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';
}
};
onresize();
window.addEventListener('resize', onresize);
var toolbox = document.getElementById('toolbox');
mainWorkspace = Blockly.inject('blockly',
{collapse: false,
toolbox: toolbox,
media: '../../media/'});
// Create the root block.
if ('BlocklyStorage' in window && window.location.hash.length > 1) {
BlocklyStorage.retrieveXml(window.location.hash.substring(1),
mainWorkspace);
} else {
var xml = '<xml><block type="factory_base" deletable="false" movable="false"></block></xml>';
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), mainWorkspace);
}
mainWorkspace.clearUndo();
mainWorkspace.addChangeListener(Blockly.Events.disableOrphans);
mainWorkspace.addChangeListener(updateLanguage);
document.getElementById('direction')
.addEventListener('change', updatePreview);
document.getElementById('languageTA')
.addEventListener('change', updatePreview);
document.getElementById('languageTA')
.addEventListener('keyup', updatePreview);
document.getElementById('format')
.addEventListener('change', formatChange);
document.getElementById('language')
.addEventListener('change', updatePreview);
}
window.addEventListener('load', init);
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

+230
View File
@@ -0,0 +1,230 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="target-densitydpi=device-dpi, height=660, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Blockly Demo: Block Factory</title>
<script src="/storage.js"></script>
<script src="factory.js"></script>
<script src="../../blockly_compressed.js"></script>
<script src="../../msg/messages.js"></script>
<script src="blocks.js"></script>
<style>
html, body {
height: 100%;
}
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 {
height: 100%;
width: 100%;
}
td {
vertical-align: top;
padding: 0;
}
#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-family: monospace;
font-size: 10pt;
}
button {
border-radius: 4px;
border: 1px solid #ddd;
background-color: #eee;
color: #000;
padding: 10px;
margin: 0 5px;
font-size: large;
}
button:hover:not(:disabled) {
box-shadow: 2px 2px 5px #888;
}
button:disabled {
opacity: 0.6;
}
button>* {
opacity: 0.6;
vertical-align: text-bottom;
}
button:hover:not(:disabled)>* {
opacity: 1;
}
#linkButton {
display: none;
}
</style>
<link rel="stylesheet" href="../prettify.css">
<script src="../prettify.js"></script>
</head>
<body>
<table>
<tr>
<td width="50%" height="5%">
<h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
<a href="../index.html">Demos</a> &gt; Block Factory</h1>
</td>
<td width="50%" height="5%">
<table>
<tr>
<td style="vertical-align: bottom;">
<h3>Preview:
<select id="direction">
<option value="ltr">LTR</option>
<option value="rtl">RTL</option>
</select>
</h3>
</td>
<td style="vertical-align: middle; text-align: right;">
<button id="linkButton" title="Save and link to blocks.">
<img src="link.png" height="21" width="21">
</button>
<button id="linkButton" title="Save and link to blocks.">
<img src="link.png" height="21" width="21">
</button>
<button id="helpButton" title="View documentation in new window.">
<span>Help</span>
</button>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td width="50%" height="95%" style="padding: 2px;">
<div id="blockly"></div>
<div id="blocklyMask"></div>
</td>
<td width="50%" height="95%">
<table>
<tr>
<td height="30%">
<div id="preview"></div>
</td>
</tr>
<tr>
<td height="5%">
<h3>Language code:
<select id="format">
<option value="JSON">JSON</option>
<option value="JavaScript">JavaScript</option>
<option value="Manual">Manual edit&hellip;</option>
</select>
</h3>
</td>
</tr>
<tr>
<td height="30%">
<pre id="languagePre"></pre>
<textarea id="languageTA"></textarea>
</td>
</tr>
<tr>
<td height="5%">
<h3>Generator stub:
<select id="language">
<option value="JavaScript">JavaScript</option>
<option value="Python">Python</option>
<option value="PHP">PHP</option>
<option value="Lua">Lua</option>
<option value="Dart">Dart</option>
</select>
</h3>
</td>
</tr>
<tr>
<td height="30%">
<pre id="generatorPre"></pre>
</td>
</tr>
</table>
</td>
</tr>
</table>
<xml id="toolbox" style="display: none">
<category name="Input">
<block type="input_value">
<value name="TYPE">
<shadow type="type_null"></shadow>
</value>
</block>
<block type="input_statement">
<value name="TYPE">
<shadow type="type_null"></shadow>
</value>
</block>
<block type="input_dummy"></block>
</category>
<category name="Field">
<block type="field_static"></block>
<block type="field_input"></block>
<block type="field_number"></block>
<block type="field_angle"></block>
<block type="field_dropdown"></block>
<block type="field_checkbox"></block>
<block type="field_colour"></block>
<!--
Date picker commented out since it increases footprint by 60%.
Add it only if you need it. See also goog.require in blockly.js.
<block type="field_date"></block>
-->
<block type="field_variable"></block>
<block type="field_image"></block>
</category>
<category name="Type">
<block type="type_group"></block>
<block type="type_null"></block>
<block type="type_boolean"></block>
<block type="type_number"></block>
<block type="type_string"></block>
<block type="type_list"></block>
<block type="type_other"></block>
</category>
<category name="Colour" id="colourCategory">
<block type="colour_hue"><mutation colour="20"></mutation><field name="HUE">20</field></block>
<block type="colour_hue"><mutation colour="65"></mutation><field name="HUE">65</field></block>
<block type="colour_hue"><mutation colour="120"></mutation><field name="HUE">120</field></block>
<block type="colour_hue"><mutation colour="160"></mutation><field name="HUE">160</field></block>
<block type="colour_hue"><mutation colour="210"></mutation><field name="HUE">210</field></block>
<block type="colour_hue"><mutation colour="230"></mutation><field name="HUE">230</field></block>
<block type="colour_hue"><mutation colour="260"></mutation><field name="HUE">260</field></block>
<block type="colour_hue"><mutation colour="290"></mutation><field name="HUE">290</field></block>
<block type="colour_hue"><mutation colour="330"></mutation><field name="HUE">330</field></block>
</category>
</xml>
</body>
</html>

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

-265
View File
@@ -1,265 +0,0 @@
/**
* @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;
/*
* The starting xml for the Block Factory main workspace. Contains the
* unmovable, undeletable factory_base block.
*/
BlockFactory.STARTER_BLOCK_XML_TEXT = '<xml><block type="factory_base" ' +
'deletable="false" movable="false"></block></xml>';
/**
* 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 core Blockly block already exists ' +
'under this name.');
} else if (blockType == 'block_type') {
// Warn user to let them know they can't save a block under the default
// name 'block_type'
rootBlock.setWarningText('You cannot save a block with the default ' +
'name, "block_type"');
} 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.textToDom(BlockFactory.STARTER_BLOCK_XML_TEXT);
Blockly.Xml.domToWorkspace(xml, BlockFactory.mainWorkspace);
};
/**
* Returns whether or not the current block open is the starter block.
*/
BlockFactory.isStarterBlock = function() {
var rootBlock = FactoryUtils.getRootBlock(BlockFactory.mainWorkspace);
// The starter block does not have blocks nested into the factory_base block.
return !(rootBlock.getChildren().length > 0 ||
// The starter block's name is the default, 'block_type'.
rootBlock.getFieldValue('NAME').trim().toLowerCase() != 'block_type' ||
// The starter block has no connections.
rootBlock.getFieldValue('CONNECTIONS') != 'NONE' ||
// The starter block has automatic inputs.
rootBlock.getFieldValue('INLINE') != 'AUTO');
};
-740
View File
@@ -1,740 +0,0 @@
<!-- TODO(quachtina96): move the CSS out to a separate file -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="target-densitydpi=device-dpi, height=660, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Blockly Demo: Blockly Factory</title>
<script src="../../blockly_compressed.js"></script>
<script src="../../javascript_compressed.js"></script>
<script src="../../msg/messages.js"></script>
<script src="../../blocks_compressed.js"></script>
<script src="workspacefactory/wfactory_model.js"></script>
<script src="workspacefactory/wfactory_controller.js"></script>
<script src="workspacefactory/wfactory_view.js"></script>
<script src="workspacefactory/wfactory_generator.js"></script>
<script src="workspacefactory/wfactory_init.js"></script>
<script src="standard_categories.js"></script>
<script src="/storage.js"></script>
<script src="../../../closure-library/closure/goog/base.js"></script>
<script src="factory_utils.js"></script>
<script src="block_option.js"></script>
<script src="factory.js"></script>
<script src="block_library_view.js"></script>
<script src="block_library_storage.js"></script>
<script src="block_library_controller.js"></script>
<script src="block_exporter_tools.js"></script>
<script src="block_exporter_view.js"></script>
<script src="block_exporter_controller.js"></script>
<script src="../blockfactory/blocks.js"></script>
<script src="app_controller.js"></script>
<link rel="stylesheet" href="factory.css">
<link rel="stylesheet" href="../prettify.css">
<script src="../prettify.js"></script>
<script>
var blocklyFactory;
var init = function() {
blocklyFactory = new AppController();
blocklyFactory.init();
};
window.addEventListener('load', init);
</script>
</head>
<body onbeforeunload="return blocklyFactory.confirmLeavePage()">
<h1><a href="https://developers.google.com/blockly/">Blockly</a> &gt;
<a href="../index.html">Demos</a> &gt; Blockly Factory
<button id="helpButton" title="View documentation in new window.">
<span>Help</span>
</button>
</h1>
<div id="tabContainer">
<div id="blockFactory_tab" class="tab tabon"> Block Factory</div>
<div id="blocklibraryExporter_tab" class="tab taboff"> Block Exporter</div>
<div id="workspaceFactory_tab" class="tab taboff"> Workspace Factory</div>
</div>
<!-- Exporter tab -->
<div id="blockLibraryExporter">
<br>
<p id="helperText"> First, select blocks from your block library by clicking on them. Then, use the Export Settings form to download starter code for selected blocks.
</p>
<div id="exportSelector">
<br>
<h3> Block Selector </h3>
<div class='dropdown'>
<button id="button_setBlocks">Select</button>
<div id="dropdownDiv_setBlocks" class="dropdown-content">
<a id='dropdown_addAllFromLib' title="Select all block library blocks.">All Stored in Block Library</a>
<a id='dropdown_addAllUsed' title="Select all block library blocks used in workspace factory.">All Used in Workspace Factory</a>
</div>
<button id='clearSelectedButton' title="Clear selected blocks.">Clear Selected</a>
</div>
<div id="blockSelector"></div>
</div>
<!-- Users may customize export settings through this form -->
<div id="exportSettings">
<br>
<h3> Export Settings </h3>
<form id="exportSettingsForm">
<div id="selectedBlocksTextContainer">
<p>Currently Selected:</p>
<p id="selectedBlocksText"></p>
</div>
<input type="checkbox" id="blockDefCheck">Block Definition(s)<br>
<div id="blockDefSettings" class="subsettings">
Format:
<select id="exportFormat">
<option value="JSON">JSON</option>
<option value="JavaScript">JavaScript</option>
</select>
<br>
File Name:
<br>
<input type="text" id="blockDef_filename">
</div>
<br>
<input type="checkbox" id="genStubCheck">Generator Stub(s)<br>
<div id="genStubSettings" class="subsettings">
Language:
<select id="exportLanguage">
<option value="JavaScript">JavaScript</option>
<option value="Python">Python</option>
<option value="PHP">PHP</option>
<option value="Lua">Lua</option>
<option value="Dart">Dart</option>
</select>
<br>
File Name:
<br>
<input type="text" id="generatorStub_filename"><br>
</div>
<br>
</form>
<button id="exporterSubmitButton" title="Download block starter code as specified in export settings."> Export </button>
</div>
<div id="exportPreview">
<br>
<h3>Export Preview </h3>
<div id="blockDefs" class="exportPreviewTextArea">
<p id="blockDefs_label">Block Definitions:</p>
<pre id="blockDefs_textArea"></pre>
</div>
<div id="genStubs" class="exportPreviewTextArea">
<p id="genStubs_label">Generator Stubs:</p>
<pre id="genStubs_textArea"></pre>
</div>
</div>
</div>
<!-- Workspace Factory tab -->
<div id="workspaceFactoryContent">
<div id="factoryHeader">
<p>
<div class="dropdown">
<button id="button_importBlocks">Import Custom Blocks</button>
<div id="dropdownDiv_importBlocks" class="dropdown-content">
<input type="file" id="input_importBlocksJson" accept=".js, .json, .txt" class="inputfile"</input>
<label for="input_importBlocksJson">From JSON</label>
<input type="file" id="input_importBlocksJs" accept=".js, .txt" class="inputfile"</input>
<label for="input_importBlocksJs">From Javascript</label>
</div>
</div>
<div class="dropdown">
<button id="button_load">Load to Edit</button>
<div id="dropdownDiv_load" class="dropdown-content">
<input type="file" id="input_loadToolbox" accept=".xml" class="inputfile"></input>
<label for="input_loadToolbox">Toolbox</label>
<input type="file" id="input_loadPreload" accept=".xml" class="inputfile"</input>
<label for="input_loadPreload">Workspace Blocks</label>
</div>
</div>
<div class="dropdown">
<button id="button_export">Export</button>
<div id="dropdownDiv_export" class="dropdown-content">
<a id='dropdown_exportOptions'>Starter Code</a>
<a id='dropdown_exportToolbox'>Toolbox</a>
<a id='dropdown_exportPreload'>Workspace Blocks</a>
<a id='dropdown_exportAll'>All</a>
</div>
</div>
<button id="button_clear">Clear</button>
</p>
</div>
<section id="createDiv">
<div id="createHeader">
<h3>Edit</h3>
<p id="editHelpText">Drag blocks into the workspace to configure the toolbox in your custom workspace.</p>
</div>
<table id='workspaceTabs' style="width:auto; height:auto">
<td id="tab_toolbox" class="tabon">Toolbox</td>
<td id="tab_preload" class="taboff">Workspace</td>
</table>
<section id="toolbox_section">
<div id="toolbox_blocks"></div>
</section>
<aside id="toolbox_div">
<p id="categoryHeader">Your categories will appear here</p>
<table id="categoryTable" style="width:auto; height:auto">
</table>
<p>&nbsp;</p>
<div class='dropdown'>
<button id="button_add" class="large">+</button>
<div id="dropdownDiv_add" class="dropdown-content">
<a id='dropdown_newCategory'>New Category</a>
<a id='dropdown_loadCategory'>Standard Category</a>
<a id='dropdown_separator'>Separator</a>
<a id='dropdown_loadStandardToolbox'>Standard Toolbox</a>
</div>
</div>
<button id="button_remove" class="large">-</button>
<button id="button_up" class="large">&#8593;</button>
<button id="button_down" class="large">&#8595;</button>
<br>
<div class='dropdown'>
<button id="button_editCategory">Edit Category</button>
<div id="dropdownDiv_editCategory" class="dropdown-content">
<a id='dropdown_name'>Name</a>
<a id='dropdown_color'>Color</a>
</div>
</div>
</aside>
<button id='button_addShadow' style='display:none'>Make Shadow</button>
<button id='button_removeShadow' style='display:none'>Remove Shadow</button>
<aside id='preload_div' style='display:none'>
<div id="preloadHelp">
<p>Configure the options for your Blockly inject call.</p>
<button id="button_optionsHelp">Help</button>
<button class="small" id="button_standardOptions">Reset to Default</button>
</div>
<div id="workspace_options">
<input type="checkbox" id="option_readOnly_checkbox" class="optionsInput">Read Only<br>
<input type="checkbox" id="option_collapse_checkbox" class="optionsInput">Collapsible Blocks<br>
<input type="checkbox" id="option_comments_checkbox" class="optionsInput">Comments for Blocks<br>
<input type="checkbox" id="option_css_checkbox" class="optionsInput">Use Blockly CSS<br>
<input type="checkbox" id="option_disable_checkbox" class="optionsInput">Disabled Blocks<br>
<input type="checkbox" id="option_grid_checkbox" class="optionsInput">Use Grid<br>
<div id="grid_options" name="grid" style="display:none">
Spacing <input type="number" id="gridOption_spacing_number" class="optionsInput" value="0"><br>
Length <input type="number" id="gridOption_length_number" class="optionsInput" value="1"><br>
Color <input type="text" id="gridOption_colour_text" class="optionsInput" value="#888"><br>
<input type="checkbox" id="gridOption_snap_checkbox" class="optionsInput" value="grid_snap_checkbox">Snap<br>
</div>
<input type="checkbox" id="option_infiniteBlocks_checkbox" class="optionsInput" value="checked">Infinite Blocks<br>
<div id="maxBlockNumber_option" style="display:none">
Max Blocks <input type="number" id="option_maxBlocks_number" class="optionsInput" value=100><br>
</div>
Path to Blockly Media <input type="text" id="option_media_text" class="optionsInput"><br>
<input type="checkbox" id="option_rtl_checkbox" class="optionsInput">Layout with RTL<br>
<input type="checkbox" id="option_scrollbars_checkbox" class="optionsInput">Scrollbars<br>
<input type="checkbox" id="option_sounds_checkbox" class="optionsInput">Sounds<br>
<div id="trashcan_option">
<input type="checkbox" id="option_trashcan_checkbox" class="optionsInput">Trashcan<br>
</div>
<input type="checkbox" id="option_zoom_checkbox" class="optionsInput">Zoom<br>
<div id="zoom_options" name="zoom" style="display:none">
<input type="checkbox" id="zoomOption_controls_checkbox" class="optionsInput">Zoom Controls<br>
<input type="checkbox" id="zoomOption_wheel_checkbox" class="optionsInput">Zoom Wheel<br>
Start Scale <input type="number" id="zoomOption_startScale_number" class="optionsInput" name="startScale" value="1.0"><br>
Max Scale <input type="number" id="zoomOption_maxScale_number" class="optionsInput" value="3"><br>
Min Scale <input type="number" id="zoomOption_minScale_number" class="optionsInput" value="0.3"><br>
Scale Speed <input type="number" id="zoomOption_scaleSpeed_number" class="optionsInput" value="1.2"><br>
</div>
</div>
</aside>
</section>
<aside id="previewDiv">
<div id="previewBorder">
<div id="previewHelp">
<h3>Preview</h3>
<p>This is what your custom workspace will look like.</p>
</div>
<div id="preview_blocks" class="content"></div>
</div>
</aside>
</div>
<!-- Blockly Factory Tab -->
<table id="blockFactoryContent">
<tr width="100%" height="10%">
<td width="50%" height="5%">
<table>
<tr id="blockLibrary">
<td id="blockLibraryContainer">
<span>
<div class='dropdown'>
<button id="button_blockLib">Block Library</button>
<div id="dropdownDiv_blockLib" class="dropdown-content">
<a id='createNewBlockButton'>Create New Block</a>
</div>
</div>
<select id="blockLibraryDropdown" style="display:none">
</select>
</span>
</td>
<td id="blockLibraryControls">
<button id="saveToBlockLibraryButton" title="Save block to Block Library.">
Save "block_type"
</button>
<button id="removeBlockFromLibraryButton" title="Remove block from Block Library.">
Delete "block_type"
</button>
</td>
</tr>
</table>
</td>
<td height="5%">
<table id="blockFactoryPreview">
<tr>
<td id="previewContainer">
<h3>Preview:
<select id="direction">
<option value="ltr">LTR</option>
<option value="rtl">RTL</option>
</select>
</h3>
</td>
<td id="buttonContainer">
<button id="linkButton" title="Save and link to blocks.">
<img src="link.png" height="21" width="21">
</button>
<button id="clearBlockLibraryButton" title="Clear Block Library.">
<span>Clear Library</span>
</button>
<label for="files" class="buttonStyle">
<span class=>Import Block Library</span>
</label>
<input id="files" type="file" name="files"
accept="application/xml">
<button id="localSaveButton" title="Save block library xml to a local file.">
<span>Download Block Library</span>
</button>
</td>
</tr>
</table>
</td>
</tr>
<tr height="80%">
<td id="blocklyWorkspaceContainer">
<div id="blockly"></div>
<div id="blocklyMask"></div>
</td>
<td width="50%">
<table id="blocklyPreviewContainer">
<tr>
<td height="30%">
<div id="preview"></div>
</td>
</tr>
<tr>
<td height="5%">
<h3>Block Definition:
<select id="format">
<option value="JSON">JSON</option>
<option value="JavaScript">JavaScript</option>
<option value="Manual">Manual edit&hellip;</option>
</select>
</h3>
</td>
</tr>
<tr>
<td height="30%">
<pre id="languagePre"></pre>
<textarea id="languageTA"></textarea>
</td>
</tr>
<tr>
<td height="5%">
<h3>Generator stub:
<select id="language">
<option value="JavaScript">JavaScript</option>
<option value="Python">Python</option>
<option value="PHP">PHP</option>
<option value="Lua">Lua</option>
<option value="Dart">Dart</option>
</select>
</h3>
</td>
</tr>
<tr>
<td height="30%">
<pre id="generatorPre"></pre>
</td>
</tr>
</table>
</td>
</tr>
</table>
<xml id="blockfactory_toolbox" class="toolbox">
<category name="Input">
<block type="input_value">
<value name="TYPE">
<shadow type="type_null"></shadow>
</value>
</block>
<block type="input_statement">
<value name="TYPE">
<shadow type="type_null"></shadow>
</value>
</block>
<block type="input_dummy"></block>
</category>
<category name="Field">
<block type="field_static"></block>
<block type="field_input"></block>
<block type="field_number"></block>
<block type="field_angle"></block>
<block type="field_dropdown"></block>
<block type="field_checkbox"></block>
<block type="field_colour"></block>
<!--
Date picker commented out since it increases footprint by 60%.
Add it only if you need it. See also goog.require in blockly.js.
<block type="field_date"></block>
-->
<block type="field_variable"></block>
<block type="field_image"></block>
</category>
<category name="Type">
<block type="type_group"></block>
<block type="type_null"></block>
<block type="type_boolean"></block>
<block type="type_number"></block>
<block type="type_string"></block>
<block type="type_list"></block>
<block type="type_other"></block>
</category>
<category name="Colour" id="colourCategory">
<block type="colour_hue"><mutation colour="20"></mutation><field name="HUE">20</field></block>
<block type="colour_hue"><mutation colour="65"></mutation><field name="HUE">65</field></block>
<block type="colour_hue"><mutation colour="120"></mutation><field name="HUE">120</field></block>
<block type="colour_hue"><mutation colour="160"></mutation><field name="HUE">160</field></block>
<block type="colour_hue"><mutation colour="210"></mutation><field name="HUE">210</field></block>
<block type="colour_hue"><mutation colour="230"></mutation><field name="HUE">230</field></block>
<block type="colour_hue"><mutation colour="260"></mutation><field name="HUE">260</field></block>
<block type="colour_hue"><mutation colour="290"></mutation><field name="HUE">290</field></block>
<block type="colour_hue"><mutation colour="330"></mutation><field name="HUE">330</field></block>
</category>
</xml>
<xml id="workspacefactory_toolbox" class="toolbox">
<category name="Logic" colour="210">
<block type="controls_if"></block>
<block type="logic_compare"></block>
<block type="logic_operation"></block>
<block type="logic_negate"></block>
<block type="logic_boolean"></block>
<block type="logic_null"></block>
<block type="logic_ternary"></block>
</category>
<category name="Loops" colour="120">
<block type="controls_repeat_ext">
<value name="TIMES">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="controls_whileUntil"></block>
<block type="controls_for">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
<value name="BY">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="controls_forEach"></block>
<block type="controls_flow_statements"></block>
</category>
<category name="Math" colour="230">
<block type="math_number"></block>
<block type="math_arithmetic">
<value name="A">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="B">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
</block>
<block type="math_single">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">9</field>
</shadow>
</value>
</block>
<block type="math_trig">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">45</field>
</shadow>
</value>
</block>
<block type="math_constant"></block>
<block type="math_number_property">
<value name="NUMBER_TO_CHECK">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="math_round">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">3.1</field>
</shadow>
</value>
</block>
<block type="math_on_list"></block>
<block type="math_modulo">
<value name="DIVIDEND">
<shadow type="math_number">
<field name="NUM">64</field>
</shadow>
</value>
<value name="DIVISOR">
<shadow type="math_number">
<field name="NUM">10</field>
</shadow>
</value>
</block>
<block type="math_constrain">
<value name="VALUE">
<shadow type="math_number">
<field name="NUM">50</field>
</shadow>
</value>
<value name="LOW">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="HIGH">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_int">
<value name="FROM">
<shadow type="math_number">
<field name="NUM">1</field>
</shadow>
</value>
<value name="TO">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
</block>
<block type="math_random_float"></block>
</category>
<category name="Text" colour="160">
<block type="text"></block>
<block type="text_join"></block>
<block type="text_append">
<value name="TEXT">
<shadow type="text"></shadow>
</value>
</block>
<block type="text_length">
<value name="VALUE">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_isEmpty">
<value name="VALUE">
<shadow type="text">
<field name="TEXT"></field>
</shadow>
</value>
</block>
<block type="text_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
<value name="FIND">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_charAt">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
</block>
<block type="text_getSubstring">
<value name="STRING">
<block type="variables_get">
<field name="VAR">text</field>
</block>
</value>
</block>
<block type="text_changeCase">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_trim">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_print">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
<block type="text_prompt_ext">
<value name="TEXT">
<shadow type="text">
<field name="TEXT">abc</field>
</shadow>
</value>
</block>
</category>
<category name="Lists" colour="260">
<block type="lists_create_with">
<mutation items="0"></mutation>
</block>
<block type="lists_create_with"></block>
<block type="lists_repeat">
<value name="NUM">
<shadow type="math_number">
<field name="NUM">5</field>
</shadow>
</value>
</block>
<block type="lists_length"></block>
<block type="lists_isEmpty"></block>
<block type="lists_indexOf">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_getIndex">
<value name="VALUE">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_setIndex">
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_getSublist">
<value name="LIST">
<block type="variables_get">
<field name="VAR">list</field>
</block>
</value>
</block>
<block type="lists_split">
<value name="DELIM">
<shadow type="text">
<field name="TEXT">,</field>
</shadow>
</value>
</block>
<block type="lists_sort"></block>
</category>
<category name="Colour" colour="20">
<block type="colour_picker"></block>
<block type="colour_random"></block>
<block type="colour_rgb">
<value name="RED">
<shadow type="math_number">
<field name="NUM">100</field>
</shadow>
</value>
<value name="GREEN">
<shadow type="math_number">
<field name="NUM">50</field>
</shadow>
</value>
<value name="BLUE">
<shadow type="math_number">
<field name="NUM">0</field>
</shadow>
</value>
</block>
<block type="colour_blend">
<value name="COLOUR1">
<shadow type="colour_picker">
<field name="COLOUR">#ff0000</field>
</shadow>
</value>
<value name="COLOUR2">
<shadow type="colour_picker">
<field name="COLOUR">#3333ff</field>
</shadow>
</value>
<value name="RATIO">
<shadow type="math_number">
<field name="NUM">0.5</field>
</shadow>
</value>
</block>
</category>
<sep></sep>
<category name="Variables" colour="330" custom="VARIABLE"></category>
<category name="Functions" colour="290" custom="PROCEDURE"></category>
<sep></sep>
<category name="Block Library" colour="260" id="blockLibCategory"></category>
</xml>
</body>
</html>
File diff suppressed because one or more lines are too long
+2 -8
View File
@@ -84,12 +84,6 @@ Blockly.Dart.ORDER_CASCADE = 15; // ..
Blockly.Dart.ORDER_ASSIGNMENT = 16; // = *= /= ~/= %= += -= <<= >>= &= ^= |=
Blockly.Dart.ORDER_NONE = 99; // (...)
/**
* Allow for switching between one and zero based indexing for lists and text,
* one based by default.
*/
Blockly.Dart.ONE_BASED_INDEXING = true;
/**
* Initialise the database of variable names.
* @param {!Blockly.Workspace} workspace Workspace to generate code from.
@@ -232,10 +226,10 @@ Blockly.Dart.getAdjusted = function(block, atId, opt_delta, opt_negate,
opt_order) {
var delta = opt_delta || 0;
var order = opt_order || Blockly.Dart.ORDER_NONE;
if (Blockly.Dart.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
delta--;
}
var defaultAtIndex = Blockly.Dart.ONE_BASED_INDEXING ? '1' : '0';
var defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0';
if (delta) {
var at = Blockly.Dart.valueToCode(block, atId,
Blockly.Dart.ORDER_ADDITIVE) || defaultAtIndex;
+1 -1
View File
@@ -80,7 +80,7 @@ Blockly.Dart['lists_indexOf'] = function(block) {
var list = Blockly.Dart.valueToCode(block, 'VALUE',
Blockly.Dart.ORDER_UNARY_POSTFIX) || '[]';
var code = list + '.' + operator + '(' + item + ')';
if (Blockly.Dart.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
return [code + ' + 1', Blockly.Dart.ORDER_ADDITIVE];
}
return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
+1 -1
View File
@@ -90,7 +90,7 @@ Blockly.Dart['text_indexOf'] = function(block) {
var text = Blockly.Dart.valueToCode(block, 'VALUE',
Blockly.Dart.ORDER_UNARY_POSTFIX) || '\'\'';
var code = text + '.' + operator + '(' + substring + ')';
if (Blockly.Dart.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
return [code + ' + 1', Blockly.Dart.ORDER_ADDITIVE];
}
return [code, Blockly.Dart.ORDER_UNARY_POSTFIX];
+2 -8
View File
@@ -134,12 +134,6 @@ Blockly.JavaScript.ORDER_OVERRIDES = [
[Blockly.JavaScript.ORDER_LOGICAL_OR, Blockly.JavaScript.ORDER_LOGICAL_OR]
];
/**
* Allow for switching between one and zero based indexing for lists and text,
* one based by default.
*/
Blockly.JavaScript.ONE_BASED_INDEXING = true;
/**
* Initialise the database of variable names.
* @param {!Blockly.Workspace} workspace Workspace to generate code from.
@@ -272,10 +266,10 @@ Blockly.JavaScript.getAdjusted = function(block, atId, opt_delta, opt_negate,
opt_order) {
var delta = opt_delta || 0;
var order = opt_order || Blockly.JavaScript.ORDER_NONE;
if (Blockly.JavaScript.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
delta--;
}
var defaultAtIndex = Blockly.JavaScript.ONE_BASED_INDEXING ? '1' : '0';
var defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0';
if (delta > 0) {
var at = Blockly.JavaScript.valueToCode(block, atId,
Blockly.JavaScript.ORDER_ADDITION) || defaultAtIndex;
+1 -1
View File
@@ -88,7 +88,7 @@ Blockly.JavaScript['lists_indexOf'] = function(block) {
var list = Blockly.JavaScript.valueToCode(block, 'VALUE',
Blockly.JavaScript.ORDER_MEMBER) || '[]';
var code = list + '.' + operator + '(' + item + ')';
if (Blockly.JavaScript.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
return [code + ' + 1', Blockly.JavaScript.ORDER_ADDITION];
}
return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
+1 -1
View File
@@ -96,7 +96,7 @@ Blockly.JavaScript['text_indexOf'] = function(block) {
Blockly.JavaScript.ORDER_MEMBER) || '\'\'';
var code = text + '.' + operator + '(' + substring + ')';
// Adjust index if using one-based indices.
if (Blockly.JavaScript.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
return [code + ' + 1', Blockly.JavaScript.ORDER_ADDITION];
}
return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL];
+3 -3
View File
@@ -87,9 +87,9 @@ Blockly.Lua.ORDER_OR = 9; // or
Blockly.Lua.ORDER_NONE = 99;
/**
* Lua is not supporting zero-indexing since the language itself is one-indexed,
* so there is not flag for ONE_BASED_INDEXING to indicate which indexing is
* used for lists and text.
* Note: Lua is not supporting zero-indexing since the language itself is
* one-indexed, so the generator does not repoct the oneBasedIndex configuration
* option used for lists and text.
*/
/**
+2 -8
View File
@@ -131,12 +131,6 @@ Blockly.PHP.ORDER_OVERRIDES = [
[Blockly.PHP.ORDER_LOGICAL_OR, Blockly.PHP.ORDER_LOGICAL_OR]
];
/**
* Allow for switching between one and zero based indexing for lists and text,
* one based by default.
*/
Blockly.PHP.ONE_BASED_INDEXING = true;
/**
* Initialise the database of variable names.
* @param {!Blockly.Workspace} workspace Workspace to generate code from.
@@ -257,10 +251,10 @@ Blockly.PHP.getAdjusted = function(block, atId, opt_delta, opt_negate,
opt_order) {
var delta = opt_delta || 0;
var order = opt_order || Blockly.PHP.ORDER_NONE;
if (Blockly.PHP.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
delta--;
}
var defaultAtIndex = Blockly.PHP.ONE_BASED_INDEXING ? '1' : '0';
var defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0';
if (delta > 0) {
var at = Blockly.PHP.valueToCode(block, atId,
Blockly.PHP.ORDER_ADDITION) || defaultAtIndex;
+1 -1
View File
@@ -105,7 +105,7 @@ Blockly.PHP['lists_indexOf'] = function(block) {
Blockly.PHP.ORDER_NONE) || '\'\'';
var argument1 = Blockly.PHP.valueToCode(block, 'VALUE',
Blockly.PHP.ORDER_MEMBER) || '[]';
if (Blockly.PHP.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
var errorIndex = ' 0';
var indexAdjustment = ' + 1';
} else {
+8 -12
View File
@@ -30,18 +30,14 @@ 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 = block.workspace.variableList;
for (var i = globals.length - 1; i >= 0; i--) {
var varName = globals[i];
if (block.arguments_.indexOf(varName) == -1) {
globals[i] = Blockly.PHP.variableDB_.getName(varName,
Blockly.Variables.NAME_TYPE);
} else {
// This variable is actually a parameter name. Do not include it in
// the list of globals, thus allowing it be of local scope.
globals.splice(i, 1);
}
// First, add a 'global' statement for every variable that is not shadowed by
// a local parameter.
var globals = [];
for (var i = 0, varName; varName = block.workspace.variableList[i]; i++) {
if (block.arguments_.indexOf(varName) == -1) {
globals.push(Blockly.PHP.variableDB_.getName(varName,
Blockly.Variables.NAME_TYPE));
}
}
globals = globals.length ? ' global ' + globals.join(', ') + ';\n' : '';
+1 -1
View File
@@ -102,7 +102,7 @@ Blockly.PHP['text_indexOf'] = function(block) {
Blockly.PHP.ORDER_NONE) || '\'\'';
var text = Blockly.PHP.valueToCode(block, 'VALUE',
Blockly.PHP.ORDER_NONE) || '\'\'';
if (Blockly.PHP.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
var errorIndex = ' 0';
var indexAdjustment = ' + 1';
} else {
+2 -9
View File
@@ -90,12 +90,6 @@ Blockly.Python.ORDER_CONDITIONAL = 15; // if else
Blockly.Python.ORDER_LAMBDA = 16; // lambda
Blockly.Python.ORDER_NONE = 99; // (...)
/**
* Allow for switching between one and zero based indexing for lists and text,
* one based by default.
*/
Blockly.Python.ONE_BASED_INDEXING = true;
/**
* List of outer-inner pairings that do NOT require parentheses.
* @type {!Array.<!Array.<number>>}
@@ -258,10 +252,10 @@ Blockly.Python.scrub_ = function(block, code) {
*/
Blockly.Python.getAdjustedInt = function(block, atId, opt_delta, opt_negate) {
var delta = opt_delta || 0;
if (Blockly.Python.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
delta--;
}
var defaultAtIndex = Blockly.Python.ONE_BASED_INDEXING ? '1' : '0';
var defaultAtIndex = block.workspace.options.oneBasedIndex ? '1' : '0';
var atOrder = delta ? Blockly.Python.ORDER_ADDITIVE :
Blockly.Python.ORDER_NONE;
var at = Blockly.Python.valueToCode(block, atId, atOrder) || defaultAtIndex;
@@ -287,4 +281,3 @@ Blockly.Python.getAdjustedInt = function(block, atId, opt_delta, opt_negate) {
}
return at;
};
+1 -1
View File
@@ -76,7 +76,7 @@ Blockly.Python['lists_indexOf'] = function(block) {
Blockly.Python.ORDER_NONE) || '[]';
var list = Blockly.Python.valueToCode(block, 'VALUE',
Blockly.Python.ORDER_NONE) || '\'\'';
if (Blockly.Python.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
var errorIndex = ' 0';
var firstIndexAdjustment = ' + 1';
var lastIndexAdjustment = '';
+6 -10
View File
@@ -31,17 +31,13 @@ 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 = block.workspace.variableList;
for (var i = globals.length - 1; i >= 0; i--) {
var varName = globals[i];
// First, add a 'global' statement for every variable that is not shadowed by
// a local parameter.
var globals = [];
for (var i = 0, varName; varName = block.workspace.variableList[i]; i++) {
if (block.arguments_.indexOf(varName) == -1) {
globals[i] = Blockly.Python.variableDB_.getName(varName,
Blockly.Variables.NAME_TYPE);
} else {
// This variable is actually a parameter name. Do not include it in
// the list of globals, thus allowing it be of local scope.
globals.splice(i, 1);
globals.push(Blockly.Python.variableDB_.getName(varName,
Blockly.Variables.NAME_TYPE));
}
}
globals = globals.length ? ' global ' + globals.join(', ') + '\n' : '';
+1 -1
View File
@@ -103,7 +103,7 @@ Blockly.Python['text_indexOf'] = function(block) {
var text = Blockly.Python.valueToCode(block, 'VALUE',
Blockly.Python.ORDER_MEMBER) || '\'\'';
var code = text + '.' + operator + '(' + substring + ')';
if (Blockly.Python.ONE_BASED_INDEXING) {
if (block.workspace.options.oneBasedIndex) {
return [code + ' + 1', Blockly.Python.ORDER_ADDITIVE];
}
return [code, Blockly.Python.ORDER_FUNCTION_CALL];
+5 -5
View File
@@ -8,13 +8,13 @@ Blockly.JavaScript.ORDER_ATOMIC=0;Blockly.JavaScript.ORDER_NEW=1.1;Blockly.JavaS
Blockly.JavaScript.ORDER_DIVISION=5.1;Blockly.JavaScript.ORDER_MULTIPLICATION=5.2;Blockly.JavaScript.ORDER_MODULUS=5.3;Blockly.JavaScript.ORDER_SUBTRACTION=6.1;Blockly.JavaScript.ORDER_ADDITION=6.2;Blockly.JavaScript.ORDER_BITWISE_SHIFT=7;Blockly.JavaScript.ORDER_RELATIONAL=8;Blockly.JavaScript.ORDER_IN=8;Blockly.JavaScript.ORDER_INSTANCEOF=8;Blockly.JavaScript.ORDER_EQUALITY=9;Blockly.JavaScript.ORDER_BITWISE_AND=10;Blockly.JavaScript.ORDER_BITWISE_XOR=11;Blockly.JavaScript.ORDER_BITWISE_OR=12;
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.ORDER_ADDITION],[Blockly.JavaScript.ORDER_LOGICAL_AND,Blockly.JavaScript.ORDER_LOGICAL_AND],[Blockly.JavaScript.ORDER_LOGICAL_OR,Blockly.JavaScript.ORDER_LOGICAL_OR]];
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=a.variableList;if(a.length){for(var c=0;c<a.length;c++)b[c]=Blockly.JavaScript.variableDB_.getName(a[c],Blockly.Variables.NAME_TYPE);Blockly.JavaScript.definitions_.variables="var "+b.join(", ")+
";"}};Blockly.JavaScript.finish=function(a){var b=[],c;for(c in Blockly.JavaScript.definitions_)b.push(Blockly.JavaScript.definitions_[c]);delete Blockly.JavaScript.definitions_;delete Blockly.JavaScript.functionNames_;Blockly.JavaScript.variableDB_.reset();return b.join("\n\n")+"\n\n\n"+a};Blockly.JavaScript.scrubNakedValue=function(a){return a+";\n"};Blockly.JavaScript.quote_=function(a){a=a.replace(/\\/g,"\\\\").replace(/\n/g,"\\\n").replace(/'/g,"\\'");return"'"+a+"'"};
Blockly.JavaScript.scrub_=function(a,b){var c="";if(!a.outputConnection||!a.outputConnection.targetConnection){var d=a.getCommentText();(d=Blockly.utils.wrap(d,Blockly.JavaScript.COMMENT_WRAP-3))&&(c=a.getProcedureDef?c+("/**\n"+Blockly.JavaScript.prefixLines(d+"\n"," * ")+" */\n"):c+Blockly.JavaScript.prefixLines(d+"\n","// "));for(var e=0;e<a.inputList.length;e++)a.inputList[e].type==Blockly.INPUT_VALUE&&(d=a.inputList[e].connection.targetBlock())&&(d=Blockly.JavaScript.allNestedComments(d))&&(c+=
Blockly.JavaScript.prefixLines(d,"// "))}e=a.nextConnection&&a.nextConnection.targetBlock();e=Blockly.JavaScript.blockToCode(e);return c+b+e};
Blockly.JavaScript.getAdjusted=function(a,b,c,d,e){c=c||0;e=e||Blockly.JavaScript.ORDER_NONE;Blockly.JavaScript.ONE_BASED_INDEXING&&c--;var f=Blockly.JavaScript.ONE_BASED_INDEXING?"1":"0";a=0<c?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_ADDITION)||f:0>c?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)+
c,d&&(a=-a);else{if(0<c){a=a+" + "+c;var g=Blockly.JavaScript.ORDER_ADDITION}else 0>c&&(a=a+" - "+-c,g=Blockly.JavaScript.ORDER_SUBTRACTION);d&&(a=c?"-("+a+")":"-"+a,g=Blockly.JavaScript.ORDER_UNARY_NEGATION);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.JavaScript.colour={};Blockly.JavaScript.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.colour_random=function(a){return[Blockly.JavaScript.provideFunction_("colourRandom",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"() {"," var num = Math.floor(Math.random() * Math.pow(2, 24));"," return '#' + ('00000' + num.toString(16)).substr(-6);","}"])+"()",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.getAdjusted=function(a,b,c,d,e){c=c||0;e=e||Blockly.JavaScript.ORDER_NONE;a.workspace.options.oneBasedIndex&&c--;var f=a.workspace.options.oneBasedIndex?"1":"0";a=0<c?Blockly.JavaScript.valueToCode(a,b,Blockly.JavaScript.ORDER_ADDITION)||f:0>c?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)+c,
d&&(a=-a);else{if(0<c){a=a+" + "+c;var g=Blockly.JavaScript.ORDER_ADDITION}else 0>c&&(a=a+" - "+-c,g=Blockly.JavaScript.ORDER_SUBTRACTION);d&&(a=c?"-("+a+")":"-"+a,g=Blockly.JavaScript.ORDER_UNARY_NEGATION);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.JavaScript.colour={};Blockly.JavaScript.colour_picker=function(a){return["'"+a.getFieldValue("COLOUR")+"'",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.colour_random=function(a){return[Blockly.JavaScript.provideFunction_("colourRandom",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"() {"," var num = Math.floor(Math.random() * Math.pow(2, 24));"," return '#' + ('00000' + num.toString(16)).substr(-6);","}"])+"()",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.colour_rgb=function(a){var b=Blockly.JavaScript.valueToCode(a,"RED",Blockly.JavaScript.ORDER_COMMA)||0,c=Blockly.JavaScript.valueToCode(a,"GREEN",Blockly.JavaScript.ORDER_COMMA)||0;a=Blockly.JavaScript.valueToCode(a,"BLUE",Blockly.JavaScript.ORDER_COMMA)||0;return[Blockly.JavaScript.provideFunction_("colourRgb",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(r, g, b) {"," r = Math.max(Math.min(Number(r), 100), 0) * 2.55;"," g = Math.max(Math.min(Number(g), 100), 0) * 2.55;",
" b = Math.max(Math.min(Number(b), 100), 0) * 2.55;"," r = ('0' + (Math.round(r) || 0).toString(16)).slice(-2);"," g = ('0' + (Math.round(g) || 0).toString(16)).slice(-2);"," b = ('0' + (Math.round(b) || 0).toString(16)).slice(-2);"," return '#' + r + g + b;","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.colour_blend=function(a){var b=Blockly.JavaScript.valueToCode(a,"COLOUR1",Blockly.JavaScript.ORDER_COMMA)||"'#000000'",c=Blockly.JavaScript.valueToCode(a,"COLOUR2",Blockly.JavaScript.ORDER_COMMA)||"'#000000'";a=Blockly.JavaScript.valueToCode(a,"RATIO",Blockly.JavaScript.ORDER_COMMA)||.5;return[Blockly.JavaScript.provideFunction_("colourBlend",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(c1, c2, ratio) {"," ratio = Math.max(Math.min(Number(ratio), 1), 0);"," var r1 = parseInt(c1.substring(1, 3), 16);",
@@ -22,7 +22,7 @@ Blockly.JavaScript.colour_blend=function(a){var b=Blockly.JavaScript.valueToCode
" return '#' + r + g + b;","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.lists={};Blockly.JavaScript.lists_create_empty=function(a){return["[]",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.JavaScript.valueToCode(a,"ADD"+c,Blockly.JavaScript.ORDER_COMMA)||"null";return["["+b.join(", ")+"]",Blockly.JavaScript.ORDER_ATOMIC]};
Blockly.JavaScript.lists_repeat=function(a){var b=Blockly.JavaScript.provideFunction_("listsRepeat",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(value, n) {"," var array = [];"," for (var i = 0; i < n; i++) {"," array[i] = value;"," }"," return array;","}"]),c=Blockly.JavaScript.valueToCode(a,"ITEM",Blockly.JavaScript.ORDER_COMMA)||"null";a=Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_COMMA)||"0";return[b+"("+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_length=function(a){return[(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+".length",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.lists_isEmpty=function(a){return["!"+(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+".length",Blockly.JavaScript.ORDER_LOGICAL_NOT]};
Blockly.JavaScript.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''";a=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+"."+b+"("+c+")";return Blockly.JavaScript.ONE_BASED_INDEXING?[a+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[a,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''",b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.lists_getIndex=function(a){var b=a.getFieldValue("MODE")||"GET",c=a.getFieldValue("WHERE")||"FROM_START",d=Blockly.JavaScript.valueToCode(a,"VALUE","RANDOM"==c?Blockly.JavaScript.ORDER_COMMA:Blockly.JavaScript.ORDER_MEMBER)||"[]";switch(c){case "FIRST":if("GET"==b)return[d+"[0]",Blockly.JavaScript.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".shift()",Blockly.JavaScript.ORDER_MEMBER];if("REMOVE"==b)return d+".shift();\n";break;case "LAST":if("GET"==b)return[d+".slice(-1)[0]",Blockly.JavaScript.ORDER_MEMBER];
if("GET_REMOVE"==b)return[d+".pop()",Blockly.JavaScript.ORDER_MEMBER];if("REMOVE"==b)return d+".pop();\n";break;case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT");if("GET"==b)return[d+"["+a+"]",Blockly.JavaScript.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".splice("+a+", 1)[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".splice("+a+", 1);\n";break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT",1,!0);if("GET"==b)return[d+".slice("+a+")[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];
if("GET_REMOVE"==b)return[d+".splice("+a+", 1)[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".splice("+a+", 1);";break;case "RANDOM":d=Blockly.JavaScript.provideFunction_("listsGetRandomItem",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(list, remove) {"," var x = Math.floor(Math.random() * list.length);"," if (remove) {"," return list.splice(x, 1)[0];"," } else {"," return list[x];"," }","}"])+"("+d+", "+("GET"!=b)+")";if("GET"==b||"GET_REMOVE"==b)return[d,
@@ -78,7 +78,7 @@ Blockly.JavaScript.procedures_ifreturn=function(a){var b="if ("+(Blockly.JavaScr
Blockly.JavaScript.text_join=function(a){switch(a.itemCount_){case 0:return["''",Blockly.JavaScript.ORDER_ATOMIC];case 1:return["String("+(Blockly.JavaScript.valueToCode(a,"ADD0",Blockly.JavaScript.ORDER_NONE)||"''")+")",Blockly.JavaScript.ORDER_FUNCTION_CALL];case 2:var b=Blockly.JavaScript.valueToCode(a,"ADD0",Blockly.JavaScript.ORDER_NONE)||"''";a=Blockly.JavaScript.valueToCode(a,"ADD1",Blockly.JavaScript.ORDER_NONE)||"''";return["String("+b+") + String("+a+")",Blockly.JavaScript.ORDER_ADDITION];
default:for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.JavaScript.valueToCode(a,"ADD"+c,Blockly.JavaScript.ORDER_COMMA)||"''";a="["+b.join(",")+"].join('')";return[a,Blockly.JavaScript.ORDER_FUNCTION_CALL]}};Blockly.JavaScript.text_append=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);a=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''";return b+" = String("+b+") + String("+a+");\n"};
Blockly.JavaScript.text_length=function(a){return[(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"''")+".length",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.text_isEmpty=function(a){return["!"+(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+".length",Blockly.JavaScript.ORDER_LOGICAL_NOT]};
Blockly.JavaScript.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''";a=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+"."+b+"("+c+")";return Blockly.JavaScript.ONE_BASED_INDEXING?[a+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[a,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''",b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
Blockly.JavaScript.text_charAt=function(a){var b=a.getFieldValue("WHERE")||"FROM_START",c=Blockly.JavaScript.valueToCode(a,"VALUE","RANDOM"==b?Blockly.JavaScript.ORDER_NONE:Blockly.JavaScript.ORDER_MEMBER)||"''";switch(b){case "FIRST":return[c+".charAt(0)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "LAST":return[c+".slice(-1)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "FROM_START":return a=Blockly.JavaScript.getAdjusted(a,"AT"),[c+".charAt("+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "FROM_END":return a=
Blockly.JavaScript.getAdjusted(a,"AT",1,!0),[c+".slice("+a+").charAt(0)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "RANDOM":return[Blockly.JavaScript.provideFunction_("textRandomLetter",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(text) {"," var x = Math.floor(Math.random() * text.length);"," return text[x];","}"])+"("+c+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]}throw"Unhandled option (text_charAt).";};
Blockly.JavaScript.text.getIndex_=function(a,b,c){return"FIRST"==b?"0":"FROM_END"==b?a+".length - 1 - "+c:"LAST"==b?a+".length - 1":c};
+12 -12
View File
@@ -62,8 +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_VARIABLE = "Изтриване на променливата \"%1\"";
Blockly.Msg.DELETE_VARIABLE_CONFIRMATION = "Изтриване на %1 използване на променлива '%2'?";
Blockly.Msg.DELETE_X_BLOCKS = "Изтрий %1 блока";
Blockly.Msg.DISABLE_BLOCK = "Деактивирай блок";
Blockly.Msg.DUPLICATE_BLOCK = "Копирай";
@@ -143,7 +143,7 @@ Blockly.Msg.LISTS_SET_INDEX_TOOLTIP_SET_RANDOM = "Променя случаен
Blockly.Msg.LISTS_SORT_HELPURL = "https://github.com/google/blockly/wiki/Lists#sorting-a-list";
Blockly.Msg.LISTS_SORT_ORDER_ASCENDING = "възходящо";
Blockly.Msg.LISTS_SORT_ORDER_DESCENDING = "низходящо";
Blockly.Msg.LISTS_SORT_TITLE = "sort %1 %2 %3"; // untranslated
Blockly.Msg.LISTS_SORT_TITLE = "Сортирай по %1 %2 %3";
Blockly.Msg.LISTS_SORT_TOOLTIP = "Подреди копие на списъка.";
Blockly.Msg.LISTS_SORT_TYPE_IGNORECASE = "по азбучен ред, без отчитане на малки и главни букви";
Blockly.Msg.LISTS_SORT_TYPE_NUMERIC = "в числов ред";
@@ -180,7 +180,7 @@ Blockly.Msg.LOGIC_TERNARY_CONDITION = "тест";
Blockly.Msg.LOGIC_TERNARY_HELPURL = "https://en.wikipedia.org/wiki/%3F:"; // untranslated
Blockly.Msg.LOGIC_TERNARY_IF_FALSE = "Ако е невярно";
Blockly.Msg.LOGIC_TERNARY_IF_TRUE = "Ако е вярно";
Blockly.Msg.LOGIC_TERNARY_TOOLTIP = "Провери исловието в \"тест\". Ако условието е истина, върни \"ако е истина\" стойността, иначе върни \"ако е лъжа\" стойността.";
Blockly.Msg.LOGIC_TERNARY_TOOLTIP = "Провери условието в \"тест\". Ако условието е истина, върни стойността \"ако е вярно\", иначе върни стойността \"ако е невярно\".";
Blockly.Msg.MATH_ADDITION_SYMBOL = "+"; // untranslated
Blockly.Msg.MATH_ARITHMETIC_HELPURL = "https://bg.wikipedia.org/wiki/Аритметика";
Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD = "Върни сумата на двете числа.";
@@ -203,7 +203,7 @@ Blockly.Msg.MATH_IS_NEGATIVE = "е отрицателно";
Blockly.Msg.MATH_IS_ODD = "е нечетно";
Blockly.Msg.MATH_IS_POSITIVE = "е положително";
Blockly.Msg.MATH_IS_PRIME = "е просто";
Blockly.Msg.MATH_IS_TOOLTIP = "Проверете дали дадено число е четно, нечетно, просто, цяло, положително, отрицателно или дали се дели на друго число. Връща истина или лъжа.";
Blockly.Msg.MATH_IS_TOOLTIP = "Проверете дали дадено число е четно, нечетно, просто, цяло, положително, отрицателно или дали се дели на друго число. Връща вярно или невярно.";
Blockly.Msg.MATH_IS_WHOLE = "е цяло";
Blockly.Msg.MATH_MODULO_HELPURL = "http://bg.wikipedia.org/wiki/Остатък";
Blockly.Msg.MATH_MODULO_TITLE = "остатъка от делението на %1 на %2";
@@ -216,7 +216,7 @@ Blockly.Msg.MATH_ONLIST_OPERATOR_AVERAGE = "средната стойност н
Blockly.Msg.MATH_ONLIST_OPERATOR_MAX = "най-голямата стойност в списъка";
Blockly.Msg.MATH_ONLIST_OPERATOR_MEDIAN = "медианата на списък";
Blockly.Msg.MATH_ONLIST_OPERATOR_MIN = "най-малката стойност в списъка";
Blockly.Msg.MATH_ONLIST_OPERATOR_MODE = "мода на списъка";
Blockly.Msg.MATH_ONLIST_OPERATOR_MODE = "режими на списъка";
Blockly.Msg.MATH_ONLIST_OPERATOR_RANDOM = "случаен елемент от списъка";
Blockly.Msg.MATH_ONLIST_OPERATOR_STD_DEV = "стандартно отклонение на списък";
Blockly.Msg.MATH_ONLIST_OPERATOR_SUM = "сумирай списъка";
@@ -264,7 +264,7 @@ Blockly.Msg.MATH_TRIG_TOOLTIP_ATAN = "Върни аркустангенс от
Blockly.Msg.MATH_TRIG_TOOLTIP_COS = "Върни косинус от ъгъл в градуси (не в радиани)";
Blockly.Msg.MATH_TRIG_TOOLTIP_SIN = "Върни синус от ъгъл в градуси (не в радиани)";
Blockly.Msg.MATH_TRIG_TOOLTIP_TAN = "Върни тангенс от ъгъл в градуси (не в радиани)";
Blockly.Msg.NEW_VARIABLE = "Нова променлива...";
Blockly.Msg.NEW_VARIABLE = "Създаване на променлива...";
Blockly.Msg.NEW_VARIABLE_TITLE = "Ново име на променливата:";
Blockly.Msg.ORDINAL_NUMBER_SUFFIX = ""; // untranslated
Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS = "позволи операциите";
@@ -287,7 +287,7 @@ Blockly.Msg.PROCEDURES_DEFRETURN_TOOLTIP = "Създава функция, ко
Blockly.Msg.PROCEDURES_DEF_DUPLICATE_WARNING = "Предупреждение: Тази функция има дублиращи се параметри.";
Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF = "Покажи дефиницията на функцията";
Blockly.Msg.PROCEDURES_IFRETURN_HELPURL = "http://c2.com/cgi/wiki?GuardClause";
Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP = "Ако стойността е истина, върни втората стойност.";
Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP = "Ако стойността е вярна, върни втората стойност.";
Blockly.Msg.PROCEDURES_IFRETURN_WARNING = "Предупреждение: Този блок може да се използва само във функция.";
Blockly.Msg.PROCEDURES_MUTATORARG_TITLE = "име на параметър";
Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP = "Добавяне на параметър към функцията.";
@@ -300,7 +300,7 @@ Blockly.Msg.RENAME_VARIABLE_TITLE = "Преименувай всички '%1' п
Blockly.Msg.TEXT_APPEND_APPENDTEXT = "добави текста";
Blockly.Msg.TEXT_APPEND_HELPURL = "https://github.com/google/blockly/wiki/Text#text-modification"; // untranslated
Blockly.Msg.TEXT_APPEND_TO = "към";
Blockly.Msg.TEXT_APPEND_TOOLTIP = "Добави текста към променливата \"%1\".";
Blockly.Msg.TEXT_APPEND_TOOLTIP = "Добави текст към променливата \"%1\".";
Blockly.Msg.TEXT_CHANGECASE_HELPURL = "https://github.com/google/blockly/wiki/Text#adjusting-text-case"; // untranslated
Blockly.Msg.TEXT_CHANGECASE_OPERATOR_LOWERCASE = "с малки букви";
Blockly.Msg.TEXT_CHANGECASE_OPERATOR_TITLECASE = "с Главна Буква На Всяка Дума";
@@ -322,7 +322,7 @@ Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_END = "со буква № (броено
Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_START = "до буква №";
Blockly.Msg.TEXT_GET_SUBSTRING_END_LAST = "со последната буква.";
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 = "В текста";
Blockly.Msg.TEXT_GET_SUBSTRING_INPUT_IN_TEXT = "в текста";
Blockly.Msg.TEXT_GET_SUBSTRING_START_FIRST = "вземи текста от първата буква";
Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_END = "вземи текста от буква № (броено отзад-напред)";
Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_START = "вземи текста от буква №";
@@ -336,7 +336,7 @@ Blockly.Msg.TEXT_INDEXOF_TAIL = ""; // 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 е празен";
Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "Връща истина, ако текста е празен.";
Blockly.Msg.TEXT_ISEMPTY_TOOLTIP = "Връща вярно, ако текста е празен.";
Blockly.Msg.TEXT_JOIN_HELPURL = "https://github.com/google/blockly/wiki/Text#text-creation"; // untranslated
Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH = "създай текст с";
Blockly.Msg.TEXT_JOIN_TOOLTIP = "Създай текст като съчетаеш няколко елемента.";
@@ -368,7 +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.VARIABLE_ALREADY_EXISTS = "Променлива с име '%1' вече съществува.";
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;
+1 -1
View File
@@ -79,7 +79,7 @@ Blockly.Msg.LISTS_CREATE_EMPTY_TOOLTIP = "Returns a list, of length 0, containin
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_INPUT_WITH = "Liste ya vıraz";
Blockly.Msg.LISTS_CREATE_WITH_ITEM_TOOLTIP = "Yew nesne dekerê lista miyan";
Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP = "Create a list with any number of items."; // untranslated
Blockly.Msg.LISTS_GET_INDEX_FIRST = "verên";
+2 -2
View File
@@ -264,7 +264,7 @@ Blockly.Msg.MATH_TRIG_TOOLTIP_ATAN = "Palauttaa luvun arkustangentin.";
Blockly.Msg.MATH_TRIG_TOOLTIP_COS = "Palauttaa asteluvun (ei radiaanin) kosinin.";
Blockly.Msg.MATH_TRIG_TOOLTIP_SIN = "Palauttaa asteluvun (ei radiaanin) sinin.";
Blockly.Msg.MATH_TRIG_TOOLTIP_TAN = "Palauttaa asteluvun (ei radiaanin) tangentin.";
Blockly.Msg.NEW_VARIABLE = "Uusi muuttuja...";
Blockly.Msg.NEW_VARIABLE = "Luo muuttuja...";
Blockly.Msg.NEW_VARIABLE_TITLE = "Uuden muuttujan nimi:";
Blockly.Msg.ORDINAL_NUMBER_SUFFIX = ""; // untranslated
Blockly.Msg.PROCEDURES_ALLOW_STATEMENTS = "salli kommentit";
@@ -368,7 +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.VARIABLE_ALREADY_EXISTS = "Muuttuja nimeltään '%1' jo olemassa.";
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;

Some files were not shown because too many files have changed in this diff Show More