diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index b20da09f8d3..00000000000 --- a/.eslintrc +++ /dev/null @@ -1,68 +0,0 @@ -{ - "rules": { - "curly": ["error"], - "eol-last": ["error"], - "indent": [ - "error", 2, # Blockly/Google use 2-space indents - # Blockly/Google uses +4 space indents for line continuations. - { - "SwitchCase": 1, - "MemberExpression": 2, - "ObjectExpression": 1, - "FunctionDeclaration": { - "body": 1, - "parameters": 2 - }, - "FunctionExpression": { - "body": 1, - "parameters": 2 - }, - "CallExpression": { - "arguments": 2 - }, - # Ignore default rules for ternary expressions. - "ignoredNodes": ["ConditionalExpression"] - } - ], - "keyword-spacing": ["error"], - "linebreak-style": ["error", "unix"], - "max-len": [ - "error", - { - "code": 100, - "tabWidth": 4, - "ignoreStrings": true, - "ignoreRegExpLiterals": true - } - ], - "no-trailing-spaces": ["error", { "skipBlankLines": true }], - "no-unused-vars": [ - "error", - { - "args": "after-used", - # Ignore vars starting with an underscore. - "varsIgnorePattern": "^_", - # Ignore arguments starting with an underscore. - "argsIgnorePattern": "^_" - } - ], - "no-use-before-define": ["error"], - "quotes": ["off"], # Blockly uses single quotes except for JSON blobs, which must use double quotes. - "semi": ["error", "always"], - "space-before-function-paren": ["error", "never"], # Blockly doesn't have space before function paren - "space-infix-ops": ["error"], - "strict": ["off"], # Blockly uses 'use strict' in files - "no-cond-assign": ["off"], # Blockly often uses cond-assignment in loops - "no-redeclare": ["off"], # Closure style allows redeclarations - "valid-jsdoc": ["error", {"requireReturn": false}], - "no-console": ["off"] - }, - "env": { - "browser": true - }, - "globals": { - "Blockly": true, # Blockly global - "goog": true # goog closure libraries/includes - }, - "extends": "eslint:recommended" -} diff --git a/.gitignore b/.gitignore index c546855b886..bcfe7f40602 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ npm-debug.log tests/compile/main_compressed.js tests/compile/*compiler*.jar local_build/*compiler*.jar -local_build/local_blockly_compressed.js \ No newline at end of file +local_build/local_*_compressed.js diff --git a/appengine/app.yaml b/appengine/app.yaml index 8938830f849..e111840b46c 100644 --- a/appengine/app.yaml +++ b/appengine/app.yaml @@ -1,5 +1,5 @@ application: blockly-demo -version: 1 +version: 20190215 runtime: python27 api_version: 1 threadsafe: no diff --git a/blockly_accessible_compressed.js b/blockly_accessible_compressed.js index 4ac17008013..ea695160bc6 100644 --- a/blockly_accessible_compressed.js +++ b/blockly_accessible_compressed.js @@ -17,7 +17,7 @@ COMPILED||(goog.isProvided_=function(a){return a in goog.loadedModules_||!goog.i goog.addDependency=function(a,b,c,d){!COMPILED&&goog.DEPENDENCIES_ENABLED&&goog.debugLoader_.addDependency(a,b,c,d)};goog.ENABLE_DEBUG_LOADER=!0;goog.logToConsole_=function(a){goog.global.console&&goog.global.console.error(a)}; goog.require=function(a){if(!COMPILED){goog.ENABLE_DEBUG_LOADER&&goog.debugLoader_.requested(a);if(goog.isProvided_(a)){if(goog.isInModuleLoader_())return goog.module.getInternal_(a)}else if(goog.ENABLE_DEBUG_LOADER){var b=goog.moduleLoaderState_;goog.moduleLoaderState_=null;try{goog.debugLoader_.load_(a)}finally{goog.moduleLoaderState_=b}}return null}};goog.requireType=function(a){return{}};goog.basePath="";goog.nullFunction=function(){}; goog.abstractMethod=function(){throw Error("unimplemented abstract method");};goog.addSingletonGetter=function(a){a.instance_=void 0;a.getInstance=function(){if(a.instance_)return a.instance_;goog.DEBUG&&(goog.instantiatedSingletons_[goog.instantiatedSingletons_.length]=a);return a.instance_=new a}};goog.instantiatedSingletons_=[];goog.LOAD_MODULE_USING_EVAL=!0;goog.SEAL_MODULE_EXPORTS=goog.DEBUG;goog.loadedModules_={};goog.DEPENDENCIES_ENABLED=!COMPILED&&goog.ENABLE_DEBUG_LOADER;goog.TRANSPILE="detect"; -goog.TRANSPILE_TO_LANGUAGE="";goog.TRANSPILER="transpile.js";goog.hasBadLetScoping=null;goog.useSafari10Workaround=function(){if(null==goog.hasBadLetScoping){try{var a=!eval('"use strict";let x = 1; function f() { return typeof x; };f() == "number";')}catch(b){a=!1}goog.hasBadLetScoping=a}return goog.hasBadLetScoping};goog.workaroundSafari10EvalBug=function(a){return"(function(){"+a+"\n;})();\n"}; +goog.ASSUME_ES_MODULES_TRANSPILED=!1;goog.TRANSPILE_TO_LANGUAGE="";goog.TRANSPILER="transpile.js";goog.hasBadLetScoping=null;goog.useSafari10Workaround=function(){if(null==goog.hasBadLetScoping){try{var a=!eval('"use strict";let x = 1; function f() { return typeof x; };f() == "number";')}catch(b){a=!1}goog.hasBadLetScoping=a}return goog.hasBadLetScoping};goog.workaroundSafari10EvalBug=function(a){return"(function(){"+a+"\n;})();\n"}; goog.loadModule=function(a){var b=goog.moduleLoaderState_;try{goog.moduleLoaderState_={moduleName:"",declareLegacyNamespace:!1,type:goog.ModuleType.GOOG};if(goog.isFunction(a))var c=a.call(void 0,{});else if(goog.isString(a))goog.useSafari10Workaround()&&(a=goog.workaroundSafari10EvalBug(a)),c=goog.loadModuleFromSource_.call(void 0,a);else throw Error("Invalid module definition");var d=goog.moduleLoaderState_.moduleName;if(goog.isString(d)&&d)goog.moduleLoaderState_.declareLegacyNamespace?goog.constructNamespace_(d, c):goog.SEAL_MODULE_EXPORTS&&Object.seal&&"object"==typeof c&&null!=c&&Object.seal(c),goog.loadedModules_[d]={exports:c,type:goog.ModuleType.GOOG,moduleId:goog.moduleLoaderState_.moduleName};else throw Error('Invalid module name "'+d+'"');}finally{goog.moduleLoaderState_=b}};goog.loadModuleFromSource_=function(a){eval(a);return{}};goog.normalizePath_=function(a){a=a.split("/");for(var b=0;bNumber(a[1])?!1:b('(()=>{"use strict";class X{constructor(){if(new.target!=String)throw 1;this.x=42}}let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof String))throw 1;for(const a of[2,3]){if(a==2)continue;function f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()==3}})()')}); +var c="es3",d={es3:!1},e=!1,f=goog.global.navigator&&goog.global.navigator.userAgent?goog.global.navigator.userAgent:"";a("es5",function(){return b("[1,].length==1")});a("es6",function(){return f.match(/Edge\/(\d+)(\.\d)*/i)?!1:b('(()=>{"use strict";class X{constructor(){if(new.target!=String)throw 1;this.x=42}}let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof String))throw 1;for(const a of[2,3]){if(a==2)continue;function f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()==3}})()')}); a("es6-impl",function(){return!0});a("es7",function(){return b("2 ** 2 == 4")});a("es8",function(){return b("async () => 1, true")});a("es9",function(){return b("({...rest} = {}), true")});a("es_next",function(){return!1});return{target:c,map:d}},goog.Transpiler.prototype.needsTranspile=function(a,b){if("always"==goog.TRANSPILE)return!0;if("never"==goog.TRANSPILE)return!1;if(!this.requiresTranspilation_){var c=this.createRequiresTranspilation_();this.requiresTranspilation_=c.map;this.transpilationTarget_= this.transpilationTarget_||c.target}if(a in this.requiresTranspilation_)return this.requiresTranspilation_[a]?!0:!goog.inHtmlDocument_()||"es6"!=b||"noModule"in goog.global.document.createElement("script")?!1:!0;throw Error("Unknown language mode: "+a);},goog.Transpiler.prototype.transpile=function(a,b){return goog.transpile_(a,b,this.transpilationTarget_)},goog.transpiler_=new goog.Transpiler,goog.protectScriptTag_=function(a){return a.replace(/<\/(SCRIPT)/ig,"\\x3c/$1")},goog.DebugLoader_=function(){this.dependencies_= {};this.idToPath_={};this.written_={};this.loadingDeps_=[];this.depsToLoad_=[];this.paused_=!1;this.factory_=new goog.DependencyFactory(goog.transpiler_);this.deferredCallbacks_={};this.deferredQueue_=[]},goog.DebugLoader_.prototype.bootstrap=function(a,b){function c(){d&&(goog.global.setTimeout(d,0),d=null)}var d=b;if(a.length){b=[];for(var e=0;e'+goog.protectScriptTag_('goog.Dependency.callback_("'+b+'");')+"\x3c/script>")}var e=this;if(goog.global.CLOSURE_IMPORT_SCRIPT)b(),this.contents_&& goog.global.CLOSURE_IMPORT_SCRIPT("",this.contents_)?(this.contents_=null,a.loaded()):a.pause();else{var f=this.loadFlags.module==goog.ModuleType.ES6;this.lazyFetch_||b();var g=1>16,a>>8&255,a&255]}; +goog.color.rgbToHex=function(a,b,c){a=Number(a);b=Number(b);c=Number(c);if(a!=(a&255)||b!=(b&255)||c!=(c&255))throw Error('"('+a+","+b+","+c+'") is not a valid RGB color');b=a<<16|b<<8|c;return 16>a?"#"+(16777216|b).toString(16).substr(1):"#"+b.toString(16)};goog.color.rgbArrayToHex=function(a){return goog.color.rgbToHex(a[0],a[1],a[2])}; goog.color.rgbToHsl=function(a,b,c){a/=255;b/=255;c/=255;var d=Math.max(a,b,c),e=Math.min(a,b,c),f=0,g=0,h=.5*(d+e);d!=e&&(d==a?f=60*(b-c)/(d-e):d==b?f=60*(c-a)/(d-e)+120:d==c&&(f=60*(a-b)/(d-e)+240),g=0=h?(d-e)/(2*h):(d-e)/(2-2*h));return[Math.round(f+360)%360,g,h]};goog.color.rgbArrayToHsl=function(a){return goog.color.rgbToHsl(a[0],a[1],a[2])};goog.color.hueToRgb_=function(a,b,c){0>c?c+=1:16*c?a+6*(b-a)*c:1>2*c?b:2>3*c?a+(b-a)*(2/3-c)*6:a}; goog.color.hslToRgb=function(a,b,c){a/=360;if(0==b)c=b=a=255*c;else{var d=.5>c?c*(1+b):c+b-b*c;var e=2*c-d;c=255*goog.color.hueToRgb_(e,d,a+1/3);b=255*goog.color.hueToRgb_(e,d,a);a=255*goog.color.hueToRgb_(e,d,a-1/3)}return[Math.round(c),Math.round(b),Math.round(a)]};goog.color.hslArrayToRgb=function(a){return goog.color.hslToRgb(a[0],a[1],a[2])};goog.color.validHexColorRe_=/^#(?:[0-9a-f]{3}){1,2}$/i;goog.color.isValidHexColor_=function(a){return goog.color.validHexColorRe_.test(a)}; -goog.color.normalizedHexColorRe_=/^#[0-9a-f]{6}$/;goog.color.isNormalizedHexColor_=function(a){return goog.color.normalizedHexColorRe_.test(a)};goog.color.rgbColorRe_=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;goog.color.isValidRgbColor_=function(a){var b=a.match(goog.color.rgbColorRe_);if(b){a=Number(b[1]);var c=Number(b[2]);b=Number(b[3]);if(0<=a&&255>=a&&0<=c&&255>=c&&0<=b&&255>=b)return[a,c,b]}return[]}; -goog.color.prependZeroIfNecessaryHelper=function(a){return 1==a.length?"0"+a:a};goog.color.prependHashIfNecessaryHelper=function(a){return"#"==a.charAt(0)?a:"#"+a};goog.color.rgbStyle_=function(a){return"rgb("+a.join(",")+")"}; -goog.color.hsvToRgb=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=c;else{var g=Math.floor(a/60),h=a/60-g;a=c*(1-b);var k=c*(1-b*h);b=c*(1-b*(1-h));switch(g){case 1:d=k;e=c;f=a;break;case 2:d=a;e=c;f=b;break;case 3:d=a;e=k;f=c;break;case 4:d=b;e=a;f=c;break;case 5:d=c;e=a;f=k;break;case 6:case 0:d=c,e=b,f=a}}return[Math.floor(d),Math.floor(e),Math.floor(f)]}; +goog.color.rgbColorRe_=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;goog.color.isValidRgbColor_=function(a){var b=a.match(goog.color.rgbColorRe_);if(b){a=Number(b[1]);var c=Number(b[2]);b=Number(b[3]);if(0<=a&&255>=a&&0<=c&&255>=c&&0<=b&&255>=b)return[a,c,b]}return[]};goog.color.prependZeroIfNecessaryHelper=function(a){return 1==a.length?"0"+a:a};goog.color.prependHashIfNecessaryHelper=function(a){return"#"==a.charAt(0)?a:"#"+a}; +goog.color.rgbStyle_=function(a){return"rgb("+a.join(",")+")"};goog.color.hsvToRgb=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=c;else{var g=Math.floor(a/60),h=a/60-g;a=c*(1-b);var k=c*(1-b*h);b=c*(1-b*(1-h));switch(g){case 1:d=k;e=c;f=a;break;case 2:d=a;e=c;f=b;break;case 3:d=a;e=k;f=c;break;case 4:d=b;e=a;f=c;break;case 5:d=c;e=a;f=k;break;case 6:case 0:d=c,e=b,f=a}}return[Math.floor(d),Math.floor(e),Math.floor(f)]}; goog.color.rgbToHsv=function(a,b,c){var d=Math.max(Math.max(a,b),c),e=Math.min(Math.min(a,b),c);if(e==d)e=a=0;else{var f=d-e;e=f/d;a=60*(a==d?(b-c)/f:b==d?2+(c-a)/f:4+(a-b)/f);0>a&&(a+=360);360=a[2]?a[1]*a[2]:a[1]*(1-a[2]);var d=.5>=b[2]?b[1]*b[2]:b[1]*(1-b[2]);return(a[2]-b[2])*(a[2]-b[2])+c*c+d*d-2*c*d*Math.cos(2*(a[0]/360-b[0]/360)*Math.PI)};goog.color.blend=function(a,b,c){c=goog.math.clamp(c,0,1);return[Math.round(b[0]+c*(a[0]-b[0])),Math.round(b[1]+c*(a[1]-b[1])),Math.round(b[2]+c*(a[2]-b[2]))]};goog.color.darken=function(a,b){return goog.color.blend([0,0,0],a,b)}; @@ -168,9 +168,10 @@ goog.object.unsafeClone=function(a){var b=goog.typeOf(a);if("object"==b||"array" goog.object.extend=function(a,b){for(var c,d,e=1;e=goog.debug.MAX_STACK_DEPTH){b.push("[...long stack...]");break}}a&&d>=a?b.push("[...reached max depth limit...]"):b.push("[end]");return b.join("")}; goog.debug.MAX_STACK_DEPTH=50;goog.debug.getNativeStackTrace_=function(a){var b=Error();if(Error.captureStackTrace)return Error.captureStackTrace(b,a),String(b.stack);try{throw b;}catch(c){b=c}return(a=b.stack)?String(a):null};goog.debug.getStacktrace=function(a){var b;goog.debug.FORCE_SLOPPY_STACKS||(b=goog.debug.getNativeStackTrace_(a||goog.debug.getStacktrace));b||(b=goog.debug.getStacktraceHelper_(a||arguments.callee.caller,[]));return b}; goog.debug.getStacktraceHelper_=function(a,b){var c=[];if(goog.array.contains(b,a))c.push("[...circular reference...]");else if(a&&b.lengtha.length?"&":"")+encodeURIComponent(d)+"="+encodeURIComponent(String(g)))}}return b};goog.html.SafeUrl=function(){this.privateDoNotAccessOrElseSafeHtmlWrappedValue_="";this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeUrl.INNOCUOUS_STRING="about:invalid#zClosurez";goog.html.SafeUrl.prototype.implementsGoogStringTypedString=!0;goog.html.SafeUrl.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_}; -goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString=!0;goog.html.SafeUrl.prototype.getDirection=function(){return goog.i18n.bidi.Dir.LTR};goog.DEBUG&&(goog.html.SafeUrl.prototype.toString=function(){return"SafeUrl{"+this.privateDoNotAccessOrElseSafeHtmlWrappedValue_+"}"}); -goog.html.SafeUrl.unwrap=function(a){if(a instanceof goog.html.SafeUrl&&a.constructor===goog.html.SafeUrl&&a.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_)return a.privateDoNotAccessOrElseSafeHtmlWrappedValue_;goog.asserts.fail("expected object of type SafeUrl, got '"+a+"' of type "+goog.typeOf(a));return"type_error:SafeUrl"};goog.html.SafeUrl.fromConstant=function(a){return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(goog.string.Const.unwrap(a))}; +goog.html.TrustedResourceUrl.stringifyParams_=function(a,b,c){if(null==c)return b;if(goog.isString(c))return c?a+encodeURIComponent(c):"";for(var d in c){var e=c[d];e=goog.isArray(e)?e:[e];for(var f=0;fa.length?"&":"")+encodeURIComponent(d)+"="+encodeURIComponent(String(g)))}}return b};goog.html.SafeUrl=function(){this.privateDoNotAccessOrElseSafeUrlWrappedValue_="";this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeUrl.INNOCUOUS_STRING="about:invalid#zClosurez";goog.html.SafeUrl.prototype.implementsGoogStringTypedString=!0;goog.html.SafeUrl.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeUrlWrappedValue_}; +goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString=!0;goog.html.SafeUrl.prototype.getDirection=function(){return goog.i18n.bidi.Dir.LTR};goog.DEBUG&&(goog.html.SafeUrl.prototype.toString=function(){return"SafeUrl{"+this.privateDoNotAccessOrElseSafeUrlWrappedValue_+"}"}); +goog.html.SafeUrl.unwrap=function(a){if(a instanceof goog.html.SafeUrl&&a.constructor===goog.html.SafeUrl&&a.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_)return a.privateDoNotAccessOrElseSafeUrlWrappedValue_;goog.asserts.fail("expected object of type SafeUrl, got '"+a+"' of type "+goog.typeOf(a));return"type_error:SafeUrl"};goog.html.SafeUrl.fromConstant=function(a){return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(goog.string.Const.unwrap(a))}; goog.html.SAFE_MIME_TYPE_PATTERN_=/^(?:audio\/(?:3gpp2|3gpp|aac|L16|midi|mp3|mp4|mpeg|oga|ogg|opus|x-m4a|x-wav|wav|webm)|image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|text\/csv|video\/(?:mpeg|mp4|ogg|webm|quicktime))$/i;goog.html.SafeUrl.fromBlob=function(a){a=goog.html.SAFE_MIME_TYPE_PATTERN_.test(a.type)?goog.fs.url.createObjectUrl(a):goog.html.SafeUrl.INNOCUOUS_STRING;return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)};goog.html.DATA_URL_PATTERN_=/^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i; goog.html.SafeUrl.fromDataUrl=function(a){a=a.replace(/(%0A|%0D)/g,"");var b=a.match(goog.html.DATA_URL_PATTERN_);b=b&&goog.html.SAFE_MIME_TYPE_PATTERN_.test(b[1]);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(b?a:goog.html.SafeUrl.INNOCUOUS_STRING)};goog.html.SafeUrl.fromTelUrl=function(a){goog.string.caseInsensitiveStartsWith(a,"tel:")||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)}; goog.html.SIP_URL_PATTERN_=/^sip[s]?:[+a-z0-9_.!$%&'*\/=^`{|}~-]+@([a-z0-9-]+\.)+[a-z0-9]{2,63}$/i;goog.html.SafeUrl.fromSipUrl=function(a){goog.html.SIP_URL_PATTERN_.test(decodeURIComponent(a))||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)};goog.html.SafeUrl.fromFacebookMessengerUrl=function(a){goog.string.caseInsensitiveStartsWith(a,"fb-messenger://share")||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)}; @@ -332,7 +334,7 @@ goog.html.SafeUrl.sanitizeChromeExtensionUrl=function(a,b){return goog.html.Safe goog.html.SafeUrl.sanitizeExtensionUrl_=function(a,b,c){(a=a.exec(b))?(a=a[1],-1==(c instanceof goog.string.Const?[goog.string.Const.unwrap(c)]:c.map(function(a){return goog.string.Const.unwrap(a)})).indexOf(a)&&(b=goog.html.SafeUrl.INNOCUOUS_STRING)):b=goog.html.SafeUrl.INNOCUOUS_STRING;return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(b)};goog.html.SafeUrl.fromTrustedResourceUrl=function(a){return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(goog.html.TrustedResourceUrl.unwrap(a))}; goog.html.SAFE_URL_PATTERN_=/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;goog.html.SafeUrl.SAFE_URL_PATTERN=goog.html.SAFE_URL_PATTERN_;goog.html.SafeUrl.sanitize=function(a){if(a instanceof goog.html.SafeUrl)return a;a="object"==typeof a&&a.implementsGoogStringTypedString?a.getTypedStringValue():String(a);goog.html.SAFE_URL_PATTERN_.test(a)||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)}; goog.html.SafeUrl.sanitizeAssertUnchanged=function(a){if(a instanceof goog.html.SafeUrl)return a;a="object"==typeof a&&a.implementsGoogStringTypedString?a.getTypedStringValue():String(a);goog.asserts.assert(goog.html.SAFE_URL_PATTERN_.test(a),"%s does not match the safe URL pattern",a)||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)};goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_={}; -goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse=function(a){var b=new goog.html.SafeUrl;b.privateDoNotAccessOrElseSafeHtmlWrappedValue_=a;return b};goog.html.SafeUrl.ABOUT_BLANK=goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse("about:blank");goog.html.SafeStyle=function(){this.privateDoNotAccessOrElseSafeStyleWrappedValue_="";this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeStyle.prototype.implementsGoogStringTypedString=!0;goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_={}; +goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse=function(a){var b=new goog.html.SafeUrl;b.privateDoNotAccessOrElseSafeUrlWrappedValue_=a;return b};goog.html.SafeUrl.ABOUT_BLANK=goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse("about:blank");goog.html.SafeStyle=function(){this.privateDoNotAccessOrElseSafeStyleWrappedValue_="";this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeStyle.prototype.implementsGoogStringTypedString=!0;goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_={}; goog.html.SafeStyle.fromConstant=function(a){a=goog.string.Const.unwrap(a);if(0===a.length)return goog.html.SafeStyle.EMPTY;goog.html.SafeStyle.checkStyle_(a);goog.asserts.assert(goog.string.endsWith(a,";"),"Last character of style string is not ';': "+a);goog.asserts.assert(goog.string.contains(a,":"),"Style string must contain at least one ':', to specify a \"name: value\" pair: "+a);return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(a)}; goog.html.SafeStyle.checkStyle_=function(a){goog.asserts.assert(!/[<>]/.test(a),"Forbidden characters in style string: "+a)};goog.html.SafeStyle.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeStyleWrappedValue_};goog.DEBUG&&(goog.html.SafeStyle.prototype.toString=function(){return"SafeStyle{"+this.privateDoNotAccessOrElseSafeStyleWrappedValue_+"}"}); goog.html.SafeStyle.unwrap=function(a){if(a instanceof goog.html.SafeStyle&&a.constructor===goog.html.SafeStyle&&a.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_)return a.privateDoNotAccessOrElseSafeStyleWrappedValue_;goog.asserts.fail("expected object of type SafeStyle, got '"+a+"' of type "+goog.typeOf(a));return"type_error:SafeStyle"};goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse=function(a){return(new goog.html.SafeStyle).initSecurityPrivateDoNotAccessOrElse_(a)}; @@ -383,7 +385,8 @@ goog.dom.safe.setFrameSrc=function(a,b){goog.dom.asserts.assertIsHTMLFrameElemen goog.dom.safe.setLinkHrefAndRel=function(a,b,c){goog.dom.asserts.assertIsHTMLLinkElement(a);a.rel=c;goog.string.caseInsensitiveContains(c,"stylesheet")?(goog.asserts.assert(b instanceof goog.html.TrustedResourceUrl,'URL must be TrustedResourceUrl because "rel" contains "stylesheet"'),a.href=goog.html.TrustedResourceUrl.unwrap(b)):a.href=b instanceof goog.html.TrustedResourceUrl?goog.html.TrustedResourceUrl.unwrap(b):b instanceof goog.html.SafeUrl?goog.html.SafeUrl.unwrap(b):goog.html.SafeUrl.sanitizeAssertUnchanged(b).getTypedStringValue()}; goog.dom.safe.setObjectData=function(a,b){goog.dom.asserts.assertIsHTMLObjectElement(a);a.data=goog.html.TrustedResourceUrl.unwrap(b)};goog.dom.safe.setScriptSrc=function(a,b){goog.dom.asserts.assertIsHTMLScriptElement(a);a.src=goog.html.TrustedResourceUrl.unwrap(b);(b=goog.getScriptNonce())&&a.setAttribute("nonce",b)};goog.dom.safe.setScriptContent=function(a,b){goog.dom.asserts.assertIsHTMLScriptElement(a);a.text=goog.html.SafeScript.unwrap(b);(b=goog.getScriptNonce())&&a.setAttribute("nonce",b)}; goog.dom.safe.setLocationHref=function(a,b){goog.dom.asserts.assertIsLocation(a);b=b instanceof goog.html.SafeUrl?b:goog.html.SafeUrl.sanitizeAssertUnchanged(b);a.href=goog.html.SafeUrl.unwrap(b)};goog.dom.safe.assignLocation=function(a,b){goog.dom.asserts.assertIsLocation(a);b=b instanceof goog.html.SafeUrl?b:goog.html.SafeUrl.sanitizeAssertUnchanged(b);a.assign(goog.html.SafeUrl.unwrap(b))}; -goog.dom.safe.replaceLocation=function(a,b){goog.dom.asserts.assertIsLocation(a);b=b instanceof goog.html.SafeUrl?b:goog.html.SafeUrl.sanitizeAssertUnchanged(b);a.replace(goog.html.SafeUrl.unwrap(b))};goog.dom.safe.openInWindow=function(a,b,c,d,e){a=a instanceof goog.html.SafeUrl?a:goog.html.SafeUrl.sanitizeAssertUnchanged(a);return(b||window).open(goog.html.SafeUrl.unwrap(a),c?goog.string.Const.unwrap(c):"",d,e)};goog.html.uncheckedconversions={};goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract=function(a,b,c){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(b,c||null)}; +goog.dom.safe.replaceLocation=function(a,b){goog.dom.asserts.assertIsLocation(a);b=b instanceof goog.html.SafeUrl?b:goog.html.SafeUrl.sanitizeAssertUnchanged(b);a.replace(goog.html.SafeUrl.unwrap(b))};goog.dom.safe.openInWindow=function(a,b,c,d,e){a=a instanceof goog.html.SafeUrl?a:goog.html.SafeUrl.sanitizeAssertUnchanged(a);return(b||window).open(goog.html.SafeUrl.unwrap(a),c?goog.string.Const.unwrap(c):"",d,e)}; +goog.dom.safe.parseFromStringHtml=function(a,b){return a.parseFromString(goog.html.SafeHtml.unwrap(b),"text/html")};goog.dom.safe.createImageFromBlob=function(a){if(!/^image\/.*/g.test(a.type))throw Error("goog.dom.safe.createImageFromBlob only accepts MIME type image/.*.");var b=window.URL.createObjectURL(a);a=new Image;a.onload=function(){window.URL.revokeObjectURL(b)};a.src=b;return a};goog.html.uncheckedconversions={};goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract=function(a,b,c){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(b,c||null)}; goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract=function(a,b){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(b)}; goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract=function(a,b){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(b)}; goog.html.uncheckedconversions.safeStyleSheetFromStringKnownToSatisfyTypeContract=function(a,b){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(b)}; @@ -594,7 +597,7 @@ d?63232<=d&&d in goog.events.KeyHandler.safariKey_?f=goog.events.KeyHandler.safa e,a,b),b.altKey=c,this.dispatchEvent(b))};goog.events.KeyHandler.prototype.getElement=function(){return this.element_}; goog.events.KeyHandler.prototype.attach=function(a,b){this.keyUpKey_&&this.detach();this.element_=a;this.keyPressKey_=goog.events.listen(this.element_,goog.events.EventType.KEYPRESS,this,b);this.keyDownKey_=goog.events.listen(this.element_,goog.events.EventType.KEYDOWN,this.handleKeyDown_,b,this);this.keyUpKey_=goog.events.listen(this.element_,goog.events.EventType.KEYUP,this.handleKeyup_,b,this)}; goog.events.KeyHandler.prototype.detach=function(){this.keyPressKey_&&(goog.events.unlistenByKey(this.keyPressKey_),goog.events.unlistenByKey(this.keyDownKey_),goog.events.unlistenByKey(this.keyUpKey_),this.keyUpKey_=this.keyDownKey_=this.keyPressKey_=null);this.element_=null;this.keyCode_=this.lastKey_=-1};goog.events.KeyHandler.prototype.disposeInternal=function(){goog.events.KeyHandler.superClass_.disposeInternal.call(this);this.detach()}; -goog.events.KeyEvent=function(a,b,c,d){goog.events.BrowserEvent.call(this,d);this.type=goog.events.KeyHandler.EventType.KEY;this.keyCode=a;this.charCode=b;this.repeat=c};goog.inherits(goog.events.KeyEvent,goog.events.BrowserEvent);goog.ui.ComponentUtil={};goog.ui.ComponentUtil.getMouseEventType=function(a){return a.pointerEventsEnabled()?goog.events.PointerAsMouseEventType:goog.events.EventType};goog.dom.classlist={};goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST=!1;goog.dom.classlist.get=function(a){if(goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList)return a.classList;a=a.className;return goog.isString(a)&&a.match(/\S+/g)||[]};goog.dom.classlist.set=function(a,b){a.className=b};goog.dom.classlist.contains=function(a,b){return goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList?a.classList.contains(b):goog.array.contains(goog.dom.classlist.get(a),b)}; +goog.events.KeyEvent=function(a,b,c,d){goog.events.BrowserEvent.call(this,d);this.type=goog.events.KeyHandler.EventType.KEY;this.keyCode=a;this.charCode=b;this.repeat=c};goog.inherits(goog.events.KeyEvent,goog.events.BrowserEvent);goog.ui.ComponentUtil={};goog.ui.ComponentUtil.getMouseEventType=function(a){return a.pointerEventsEnabled()?goog.events.PointerAsMouseEventType:goog.events.MouseAsMouseEventType};goog.dom.classlist={};goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST=!1;goog.dom.classlist.get=function(a){if(goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList)return a.classList;a=a.className;return goog.isString(a)&&a.match(/\S+/g)||[]};goog.dom.classlist.set=function(a,b){a.className=b};goog.dom.classlist.contains=function(a,b){return goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList?a.classList.contains(b):goog.array.contains(goog.dom.classlist.get(a),b)}; goog.dom.classlist.add=function(a,b){goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList?a.classList.add(b):goog.dom.classlist.contains(a,b)||(a.className+=0 document.");return a.documentElement};Blockly.Xml.clearWorkspaceAndLoadFromXml=function(a,b){b.setResizesEnabled(!1);b.clear();a=Blockly.Xml.domToWorkspace(a,b);b.setResizesEnabled(!0);return a}; Blockly.Xml.domToWorkspace=function(a,b){if(a instanceof Blockly.Workspace){var c=a;a=b;b=c;console.warn("Deprecated call to Blockly.Xml.domToWorkspace, swap the arguments.")}var d;b.RTL&&(d=b.getWidth());c=[];Blockly.Field.startCache();var e=a.childNodes.length,f=Blockly.Events.getGroup();f||Blockly.Events.setGroup(!0);b.setResizesEnabled&&b.setResizesEnabled(!1);var g=!0;try{for(var h=0;hc.viewWidth&&(a=d-c.viewLeft-c.viewWidth):d+aa.viewWidth)return b;var c=this.anchorXY_.x;this.workspace_.RTL?c-a.viewLeft-b-this.width_a.viewWidth&&(b=c-a.viewLeft-a.viewWidth):c+ba.viewHeight)return b;var c=this.anchorXY_.y;c+be&&(g=2*Math.PI-g);var h=g+Math.PI/2;h>2*Math.PI&&(h-=2*Math.PI);var k=Math.sin(h),l=Math.cos(h),n=this.getBubbleSize();h=(n.width+n.height)/Blockly.Bubble.ARROW_THICKNESS;h=Math.min(h,n.width,n.height)/4;n=1-Blockly.Bubble.ANCHOR_RADIUS/f;d=b+ @@ -1050,7 +1058,7 @@ Blockly.Connection.CAN_CONNECT:Blockly.Connection.REASON_CHECKS_FAILED}; Blockly.Connection.prototype.checkConnection_=function(a){switch(this.canConnectWithReason_(a)){case Blockly.Connection.CAN_CONNECT:break;case Blockly.Connection.REASON_SELF_CONNECTION:throw Error("Attempted to connect a block to itself.");case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:throw Error("Blocks not on same workspace.");case Blockly.Connection.REASON_WRONG_TYPE:throw Error("Attempt to connect incompatible types.");case Blockly.Connection.REASON_TARGET_NULL:throw Error("Target connection is null."); case Blockly.Connection.REASON_CHECKS_FAILED:throw Error("Connection checks failed. "+(this+" expected "+this.check_+", found "+a.check_));case Blockly.Connection.REASON_SHADOW_PARENT:throw Error("Connecting non-shadow to shadow block.");default:throw Error("Unknown connection failure: this should never happen!");}}; Blockly.Connection.prototype.canConnectToPrevious_=function(a){if(this.targetConnection||-1!=Blockly.draggingConnections_.indexOf(a))return!1;if(!a.targetConnection)return!0;a=a.targetBlock();return a.isInsertionMarker()?!a.getPreviousBlock():!1}; -Blockly.Connection.prototype.isConnectionAllowed=function(a){if(a.sourceBlock_.isInsertionMarker()||this.canConnectWithReason_(a)!=Blockly.Connection.CAN_CONNECT)return!1;switch(a.type){case Blockly.PREVIOUS_STATEMENT:return this.canConnectToPrevious_(a);case Blockly.OUTPUT_VALUE:if(a.isConnected()||this.isConnected())return!1;break;case Blockly.INPUT_VALUE:if(a.isConnected()&&!a.targetBlock().isMovable()&&!a.targetBlock().isShadow())return!1;break;case Blockly.NEXT_STATEMENT:if(a.isConnected()&& +Blockly.Connection.prototype.isConnectionAllowed=function(a){if(a.sourceBlock_.isInsertionMarker()||this.canConnectWithReason_(a)!=Blockly.Connection.CAN_CONNECT)return!1;switch(a.type){case Blockly.PREVIOUS_STATEMENT:return this.canConnectToPrevious_(a);case Blockly.OUTPUT_VALUE:if(a.isConnected()&&!a.targetBlock().isInsertionMarker()||this.isConnected())return!1;break;case Blockly.INPUT_VALUE:if(a.isConnected()&&!a.targetBlock().isMovable()&&!a.targetBlock().isShadow())return!1;break;case Blockly.NEXT_STATEMENT:if(a.isConnected()&& !this.sourceBlock_.nextConnection&&!a.targetBlock().isShadow()&&a.targetBlock().nextConnection)return!1;break;default:throw Error("Unknown connection type in isConnectionAllowed");}return-1!=Blockly.draggingConnections_.indexOf(a)?!1:!0};Blockly.Connection.prototype.connect=function(a){this.targetConnection!=a&&(this.checkConnection_(a),this.isSuperior()?this.connect_(a):a.connect_(this))}; Blockly.Connection.connectReciprocally_=function(a,b){if(!a||!b)throw Error("Cannot connect null connections.");a.targetConnection=b;b.targetConnection=a};Blockly.Connection.singleConnection_=function(a,b){for(var c=!1,d=0;d=this.length)return-1;for(var c=a.y_,d=b;0<=d&&this[d].y_==c;){if(this[d]==a)return d;d--}for(;ba.y_)c=d;else{b=d;break}}return b};Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw Error("Connection not in database.");var b=this.findConnection(a);if(-1==b)throw Error("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&&k.push(d[a]);return g=this.connections_.length)return-1;for(var c=a.y_,d=b;0<=d&&this.connections_[d].y_==c;){if(this.connections_[d]==a)return d;d--}for(;ba.y_)c=d;else{b=d;break}}return b}; +Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw Error("Connection not in database.");var b=this.findConnection(a);if(-1==b)throw Error("Unable to find connection in connectionDB.");a.inDB_=!1;this.connections_.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&&k.push(d[a]);return gb-Blockly.CURRENT_CONNECTION_PREFERENCE)}if(this.localConnection_||this.closestConnection_)console.error("Only one of localConnection_ and closestConnection_ was set."); +else return!0}else return!(!this.localConnection_||!this.closestConnection_);console.error("Returning true from shouldUpdatePreviews, but it's not clear why.");return!0};Blockly.InsertionMarkerManager.prototype.getCandidate_=function(a){for(var b=this.getStartRadius_(),c=null,d=null,e=0;e(this.flyout_?Blockly.FLYOUT_DRAG_RADIUS:Blockly.DRAG_RADIUS)}; -Blockly.Gesture.prototype.updateIsDraggingFromFlyout_=function(){return this.targetBlock_.disabled?!1:!this.flyout_.isScrollable()||this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)?(this.startWorkspace_=this.flyout_.targetWorkspace_,this.startWorkspace_.updateScreenCalculationsIfScrolled(),Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.startBlock_=null,this.targetBlock_=this.flyout_.createBlock(this.targetBlock_),this.targetBlock_.select(),!0):!1}; +Blockly.Gesture.prototype.updateIsDraggingFromFlyout_=function(){return this.flyout_.isBlockCreatable_(this.targetBlock_)?!this.flyout_.isScrollable()||this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)?(this.startWorkspace_=this.flyout_.targetWorkspace_,this.startWorkspace_.updateScreenCalculationsIfScrolled(),Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.startBlock_=null,this.targetBlock_=this.flyout_.createBlock(this.targetBlock_),this.targetBlock_.select(),!0):!1:!1}; Blockly.Gesture.prototype.updateIsDraggingBubble_=function(){if(!this.startBubble_)return!1;this.isDraggingBubble_=!0;this.startDraggingBubble_();return!0};Blockly.Gesture.prototype.updateIsDraggingBlock_=function(){if(!this.targetBlock_)return!1;this.flyout_?this.isDraggingBlock_=this.updateIsDraggingFromFlyout_():this.targetBlock_.isMovable()&&(this.isDraggingBlock_=!0);return this.isDraggingBlock_?(this.startDraggingBlock_(),!0):!1}; Blockly.Gesture.prototype.updateIsDraggingWorkspace_=function(){if(this.flyout_?this.flyout_.isScrollable():this.startWorkspace_&&this.startWorkspace_.isDraggable())this.workspaceDragger_=this.flyout_?new Blockly.FlyoutDragger(this.flyout_):new Blockly.WorkspaceDragger(this.startWorkspace_),this.isDraggingWorkspace_=!0,this.workspaceDragger_.startDrag()}; Blockly.Gesture.prototype.updateIsDragging_=function(){if(this.calledUpdateIsDragging_)throw Error("updateIsDragging_ should only be called once per gesture.");this.calledUpdateIsDragging_=!0;this.updateIsDraggingBubble_()||this.updateIsDraggingBlock_()||this.updateIsDraggingWorkspace_()}; @@ -1160,12 +1185,15 @@ Blockly.Gesture.prototype.doBlockClick_=function(){this.flyout_&&this.flyout_.au Blockly.Gesture.prototype.bringBlockToFront_=function(){this.targetBlock_&&!this.flyout_&&this.targetBlock_.bringToFront()};Blockly.Gesture.prototype.setStartField=function(a){if(this.hasStarted_)throw Error("Tried to call gesture.setStartField, but the gesture had already been started.");this.startField_||(this.startField_=a)};Blockly.Gesture.prototype.setStartBubble=function(a){this.startBubble_||(this.startBubble_=a)}; Blockly.Gesture.prototype.setStartBlock=function(a){this.startBlock_||this.startBubble_||(this.startBlock_=a,a.isInFlyout&&a!=a.getRootBlock()?this.setTargetBlock_(a.getRootBlock()):this.setTargetBlock_(a))};Blockly.Gesture.prototype.setTargetBlock_=function(a){a.isShadow()?this.setTargetBlock_(a.getParent()):this.targetBlock_=a};Blockly.Gesture.prototype.setStartWorkspace_=function(a){this.startWorkspace_||(this.startWorkspace_=a)}; Blockly.Gesture.prototype.setStartFlyout_=function(a){this.flyout_||(this.flyout_=a)};Blockly.Gesture.prototype.isBubbleClick_=function(){return!!this.startBubble_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isBlockClick_=function(){return!!this.startBlock_&&!this.hasExceededDragRadius_&&!this.isFieldClick_()};Blockly.Gesture.prototype.isFieldClick_=function(){return(this.startField_?this.startField_.isCurrentlyEditable():!1)&&!this.hasExceededDragRadius_&&(!this.flyout_||!this.flyout_.autoClose)}; -Blockly.Gesture.prototype.isWorkspaceClick_=function(){return!this.startBlock_&&!this.startBubble_&&!this.startField_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_||this.isDraggingBlock_||this.isDraggingBubble_};Blockly.Gesture.prototype.hasStarted=function(){return this.hasStarted_};Blockly.Grid=function(a,b){this.gridPattern_=a;this.spacing_=b.spacing;this.length_=b.length;this.line2_=(this.line1_=a.firstChild)&&this.line1_.nextSibling;this.snapToGrid_=b.snap};Blockly.Grid.prototype.scale_=1;Blockly.Grid.prototype.dispose=function(){this.gridPattern_=null};Blockly.Grid.prototype.shouldSnap=function(){return this.snapToGrid_};Blockly.Grid.prototype.getSpacing=function(){return this.spacing_};Blockly.Grid.prototype.getPatternId=function(){return this.gridPattern_.id}; +Blockly.Gesture.prototype.isWorkspaceClick_=function(){return!this.startBlock_&&!this.startBubble_&&!this.startField_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_||this.isDraggingBlock_||this.isDraggingBubble_};Blockly.Gesture.prototype.hasStarted=function(){return this.hasStarted_};Blockly.Gesture.prototype.getInsertionMarkers=function(){return this.blockDragger_?this.blockDragger_.getInsertionMarkers():[]};Blockly.Grid=function(a,b){this.gridPattern_=a;this.spacing_=b.spacing;this.length_=b.length;this.line2_=(this.line1_=a.firstChild)&&this.line1_.nextSibling;this.snapToGrid_=b.snap};Blockly.Grid.prototype.scale_=1;Blockly.Grid.prototype.dispose=function(){this.gridPattern_=null};Blockly.Grid.prototype.shouldSnap=function(){return this.snapToGrid_};Blockly.Grid.prototype.getSpacing=function(){return this.spacing_};Blockly.Grid.prototype.getPatternId=function(){return this.gridPattern_.id}; Blockly.Grid.prototype.update=function(a){this.scale_=a;var b=this.spacing_*a||100;this.gridPattern_.setAttribute("width",b);this.gridPattern_.setAttribute("height",b);b=Math.floor(this.spacing_/2)+.5;var c=b-this.length_/2,d=b+this.length_/2;b*=a;c*=a;d*=a;this.setLineAttributes_(this.line1_,a,c,d,b,b);this.setLineAttributes_(this.line2_,a,b,b,c,d)}; Blockly.Grid.prototype.setLineAttributes_=function(a,b,c,d,e,f){a&&(a.setAttribute("stroke-width",b),a.setAttribute("x1",c),a.setAttribute("y1",e),a.setAttribute("x2",d),a.setAttribute("y2",f))};Blockly.Grid.prototype.moveTo=function(a,b){this.gridPattern_.setAttribute("x",a);this.gridPattern_.setAttribute("y",b);(goog.userAgent.IE||goog.userAgent.EDGE)&&this.update(this.scale_)}; -Blockly.Grid.createDom=function(a,b,c){a=Blockly.utils.createSvgElement("pattern",{id:"blocklyGridPattern"+a,patternUnits:"userSpaceOnUse"},c);0this.previousScale_){var c=b-this.previousScale_;c=0Object.keys(this.cachedPoints_).length&&(this.cachedPoints_={},this.previousScale_=0)}; -Blockly.TouchGesture.prototype.getTouchPoint=function(a){return this.startWorkspace_?new goog.math.Coordinate(a.pageX?a.pageX:a.changedTouches[0].pageX,a.pageY?a.pageY:a.changedTouches[0].pageY):null};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.utils.createSvgElement("g",{"class":"blocklyTrash"},null);var a=String(Math.random()).substring(2);var b=Blockly.utils.createSvgElement("clipPath",{id:"blocklyTrashBodyClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.BODY_HEIGHT_,y:this.LID_HEIGHT_},b);Blockly.utils.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.utils.createSvgElement("clipPath",{id:"blocklyTrashLidClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.LID_HEIGHT_},b);this.svgLid_=Blockly.utils.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.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.TouchGesture.prototype.getTouchPoint=function(a){return this.startWorkspace_?new goog.math.Coordinate(a.pageX?a.pageX:a.changedTouches[0].pageX,a.pageY?a.pageY:a.changedTouches[0].pageY):null};Blockly.Trashcan=function(a){this.workspace_=a;this.hasBlocks_=!1;this.contents_=[];0>=this.workspace_.options.maxTrashcanContents||(a={scrollbars:!0,disabledPatternId:this.workspace_.options.disabledPatternId,parentWorkspace:this.workspace_,RTL:this.workspace_.RTL,oneBasedIndex:this.workspace_.options.oneBasedIndex},this.workspace_.horizontalLayout?(a.toolboxPosition=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_TOP?Blockly.TOOLBOX_AT_BOTTOM:Blockly.TOOLBOX_AT_TOP,this.flyout_=new Blockly.HorizontalFlyout(a)): +(a.toolboxPosition=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?Blockly.TOOLBOX_AT_LEFT:Blockly.TOOLBOX_AT_RIGHT,this.flyout_=new Blockly.VerticalFlyout(a)),this.workspace_.addChangeListener(this.onDelete_()))};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.HAS_BLOCKS_LID_ANGLE=.1;Blockly.Trashcan.prototype.isOpen=!1;Blockly.Trashcan.prototype.minOpenness_=0;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.utils.createSvgElement("g",{"class":"blocklyTrash"},null);var a=String(Math.random()).substring(2);var b=Blockly.utils.createSvgElement("clipPath",{id:"blocklyTrashBodyClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.BODY_HEIGHT_,y:this.LID_HEIGHT_},b);var c=Blockly.utils.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_);c.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",this.workspace_.options.pathToMedia+Blockly.SPRITE.url);b=Blockly.utils.createSvgElement("clipPath",{id:"blocklyTrashLidClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.LID_HEIGHT_},b);this.svgLid_=Blockly.utils.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.bindEventWithChecks_(this.svgGroup_,"mouseup",this,this.click);Blockly.bindEvent_(c,"mouseover",this,this.mouseOver_);Blockly.bindEvent_(c,"mouseout",this,this.mouseOut_);this.animateLid_();return this.svgGroup_}; +Blockly.Trashcan.prototype.init=function(a){0this.lidOpen_&&(this.lidTask_=setTimeout(this.animateLid_.bind(this),20))};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.VariableModel=function(a,b,c,d){this.workspace=a;this.name=b;this.type=c||"";this.id_=d||Blockly.utils.genUid();Blockly.Events.fire(new Blockly.Events.VarCreate(this))};Blockly.VariableModel.prototype.getId=function(){return this.id_};Blockly.VariableModel.compareByName=function(a,b){a=a.name.toLowerCase();b=b.name.toLowerCase();return athis.minOpenness_&&1>this.lidOpen_&&(this.lidTask_=setTimeout(this.animateLid_.bind(this),20))}; +Blockly.Trashcan.prototype.setLidAngle_=function(a){var b=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT||this.workspace_.horizontalLayout&&this.workspace_.RTL;this.svgLid_.setAttribute("transform","rotate("+(b?-a:a)+","+(b?4:this.WIDTH_-4)+","+(this.LID_HEIGHT_-2)+")")};Blockly.Trashcan.prototype.close=function(){this.setOpen_(!1)};Blockly.Trashcan.prototype.click=function(){if(this.hasBlocks_){for(var a=[],b=0,c;c=this.contents_[b];b++)a[b]=Blockly.Xml.textToDom(c).firstChild;this.flyout_.show(a)}}; +Blockly.Trashcan.prototype.mouseOver_=function(){this.hasBlocks_&&this.setOpen_(!0)};Blockly.Trashcan.prototype.mouseOut_=function(){this.setOpen_(!1)}; +Blockly.Trashcan.prototype.onDelete_=function(){var a=this;return function(b){0>=a.workspace_.options.maxTrashcanContents||b.type!=Blockly.Events.BLOCK_DELETE||(b=a.cleanBlockXML_(b.oldXml),-1==a.contents_.indexOf(b)&&(a.contents_.unshift(b),a.contents_.length>a.workspace_.options.maxTrashcanContents&&a.contents_.splice(a.workspace_.options.maxTrashcanContents,a.contents_.length-a.workspace_.options.maxTrashcanContents),a.hasBlocks_=!0,a.minOpenness_=a.HAS_BLOCKS_LID_ANGLE,a.setLidAngle_(45*a.minOpenness_)))}}; +Blockly.Trashcan.prototype.cleanBlockXML_=function(a){for(var b=a=a.cloneNode(!0);b;){b.removeAttribute&&(b.removeAttribute("x"),b.removeAttribute("y"),b.removeAttribute("id"));var c=b.firstChild||b.nextSibling;if(!c)for(c=b.parentNode;c;){if(c.nextSibling){c=c.nextSibling;break}c=c.parentNode}b=c}return""+Blockly.Xml.domToText(a)+""};Blockly.VariableModel=function(a,b,c,d){this.workspace=a;this.name=b;this.type=c||"";this.id_=d||Blockly.utils.genUid();Blockly.Events.fire(new Blockly.Events.VarCreate(this))};Blockly.VariableModel.prototype.getId=function(){return this.id_};Blockly.VariableModel.compareByName=function(a,b){a=a.name.toLowerCase();b=b.name.toLowerCase();return a=this.remainingCapacity()||(this.currentGesture_&&this.currentGesture_.cancel(),"comment"==a.tagName.toLowerCase()?this.pasteWorkspaceComment_(a):this.pasteBlock_(a))}; Blockly.WorkspaceSvg.prototype.pasteBlock_=function(a){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(!1),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){var k=b.getConnections_(!1);f=0;for(var l;l=k[f];f++)if(l.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.BlockCreate(b));b.select()}; @@ -1335,8 +1366,9 @@ for(var b=0,c;c=a[b];b++)c.render();this.rootBlock_.setMovable(!1);this.rootBloc this.workspace_.addChangeListener(this.workspaceChanged_.bind(this));this.updateColour()}else this.svgDialog_=null,this.workspace_.dispose(),this.rootBlock_=this.workspace_=null,this.bubble_.dispose(),this.bubble_=null,this.workspaceHeight_=this.workspaceWidth_=0,this.sourceListener_&&(this.block_.workspace.removeChangeListener(this.sourceListener_),this.sourceListener_=null)}; Blockly.Mutator.prototype.workspaceChanged_=function(){if(!this.workspace_.isDragging())for(var a=this.workspace_.getTopBlocks(!1),b=0,c;c=a[b];b++){var d=c.getRelativeToSurfaceXY(),e=c.getHeightWidth();20>d.y+e.height&&c.moveBy(0,20-e.height-d.y)}if(this.rootBlock_.workspace==this.workspace_){Blockly.Events.setGroup(!0);c=this.block_;a=(a=c.mutationToDom())&&Blockly.Xml.domToText(a);b=c.rendered;c.rendered=!1;c.compose(this.rootBlock_);c.rendered=b;c.initSvg();b=(b=c.mutationToDom())&&Blockly.Xml.domToText(b); if(a!=b){Blockly.Events.fire(new Blockly.Events.BlockChange(c,"mutation",null,a,b));var f=Blockly.Events.getGroup();setTimeout(function(){Blockly.Events.setGroup(f);c.bumpNeighbours_();Blockly.Events.setGroup(!1)},Blockly.BUMP_DELAY)}c.rendered&&c.render();this.workspace_.isDragging()||this.resizeBubble_();Blockly.Events.setGroup(!1)}};Blockly.Mutator.prototype.getFlyoutMetrics_=function(){return{viewHeight:this.workspaceHeight_,viewWidth:this.workspaceWidth_,absoluteTop:0,absoluteLeft:0}}; -Blockly.Mutator.prototype.dispose=function(){this.block_.mutator=null;Blockly.Icon.prototype.dispose.call(this)};Blockly.Mutator.reconnect=function(a,b,c){if(!a||!a.getSourceBlock().workspace)return!1;c=b.getInput(c).connection;var d=a.targetBlock();return d&&d!=b||c.targetConnection==a?!1:(c.isConnected()&&c.disconnect(),c.connect(a),!0)}; -Blockly.Mutator.findParentWs=function(a){var b=null;if(a&&a.options){var c=a.options.parentWorkspace;a.isFlyout?c&&c.options&&(b=c.options.parentWorkspace):c&&(b=c)}return b};goog.global.Blockly||(goog.global.Blockly={});goog.global.Blockly.Mutator||(goog.global.Blockly.Mutator={});goog.global.Blockly.Mutator.reconnect=Blockly.Mutator.reconnect;Blockly.Extensions={};Blockly.Extensions.ALL_={};Blockly.Extensions.register=function(a,b){if("string"!=typeof a||""==a.trim())throw Error('Error: Invalid extension name "'+a+'"');if(Blockly.Extensions.ALL_[a])throw Error('Error: Extension "'+a+'" is already registered.');if("function"!=typeof b)throw Error('Error: Extension "'+a+'" must be a function');Blockly.Extensions.ALL_[a]=b}; +Blockly.Mutator.prototype.dispose=function(){this.block_.mutator=null;Blockly.Icon.prototype.dispose.call(this)};Blockly.Mutator.prototype.updateBlockStyle=function(){var a=this.workspace_;if(a&&a.getAllBlocks()){for(var b=a.getAllBlocks(),c=0;c=c)this.hue_=c,this.colour_=Blockly.hueToRgb(c);else if("string"==typeof b&&/^#[0-9a-fA-F]{6}$/.test(b))this.colour_=b,this.hue_=null;else throw c='Invalid colour: "'+b+'"',a!=b&&(c+=' (from "'+a+'")'),c;}; +Blockly.Block.prototype.setStyle=function(a){var b=Blockly.getTheme();if(!b)throw Error("Trying to set block style to "+a+" before theme was defined via Blockly.setTheme().");b=b.getBlockStyle(a);this.styleName_=a;if(b)this.colourSecondary_=b.colourSecondary,this.colourTertiary_=b.colourTertiary,this.hat=b.hat,this.setColour(b.colourPrimary);else throw Error("Invalid style name: "+a);}; Blockly.Block.prototype.setOnChange=function(a){if(a&&"function"!=typeof a)throw Error("onchange must be a function.");this.onchangeWrapper_&&this.workspace.removeChangeListener(this.onchangeWrapper_);if(this.onchange=a)this.onchangeWrapper_=a.bind(this),this.workspace.addChangeListener(this.onchangeWrapper_)};Blockly.Block.prototype.getField=function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)if(e.name===a)return e;return null}; Blockly.Block.prototype.getVars=function(){for(var a=[],b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)e.referencesVariables()&&a.push(e.getValue());return a};Blockly.Block.prototype.getVarModels=function(){for(var a=[],b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)e.referencesVariables()&&(e=this.workspace.getVariableById(e.getValue()))&&a.push(e);return a}; Blockly.Block.prototype.updateVarName=function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)e.referencesVariables()&&a.getId()==e.getValue()&&e.setText(a.name)};Blockly.Block.prototype.renameVarById=function(a,b){for(var c=0,d;d=this.inputList[c];c++)for(var e=0,f;f=d.fieldRow[e];e++)f.referencesVariables()&&a==f.getValue()&&f.setValue(b)};Blockly.Block.prototype.getFieldValue=function(a){return(a=this.getField(a))?a.getValue():null}; @@ -1402,9 +1437,10 @@ Blockly.Block.prototype.setDisabled=function(a){this.disabled!=a&&(Blockly.Event Blockly.Block.prototype.setCollapsed=function(a){this.collapsed_!=a&&(Blockly.Events.fire(new Blockly.Events.BlockChange(this,"collapsed",null,this.collapsed_,a)),this.collapsed_=a)}; Blockly.Block.prototype.toString=function(a,b){var c=[],d=b||"?";if(this.collapsed_)c.push(this.getInput("_TEMP_COLLAPSED_INPUT").fieldRow[0].text_);else for(var e=0,f;f=this.inputList[e];e++){for(var g=0,h;h=f.fieldRow[g];g++)h instanceof Blockly.FieldDropdown&&!h.getValue()?c.push(d):c.push(h.getText());f.connection&&((f=f.connection.targetBlock())?c.push(f.toString(void 0,b)):c.push(d))}c=c.join(" ").trim()||"???";a&&c.length>a&&(c=c.substring(0,a-3)+"...");return c}; 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){var b=a.type?'Block "'+a.type+'": ':"";if(a.output&&a.previousStatement)throw Error(b+"Must not have both an output and a previousStatement.");this.jsonInitColour_(a,b);for(var c=0;void 0!==a["message"+c];)this.interpolate_(a["message"+c],a["args"+c]||[],a["lastDummyAlign"+c]),c++;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&&(c=a.tooltip,c=Blockly.utils.replaceMessageReferences(c),this.setTooltip(c));void 0!==a.enableContextMenu&&(c=a.enableContextMenu,this.contextMenu=!!c);void 0!==a.helpUrl&&(c=a.helpUrl,c=Blockly.utils.replaceMessageReferences(c),this.setHelpUrl(c));"string"==typeof a.extensions&&(console.warn(b+"JSON attribute 'extensions' should be an array of strings. Found raw string in JSON for '"+a.type+"' block."),a.extensions= -[a.extensions]);void 0!==a.mutator&&Blockly.Extensions.apply(a.mutator,this,!0);if(Array.isArray(a.extensions))for(a=a.extensions,b=0;b=h||h>b.length)throw Error('Block "'+this.type+'": Message index %'+h+" out of range.");if(e[h])throw Error('Block "'+this.type+'": Message index %'+h+" duplicated.");e[h]=!0;f++;a.push(b[h-1])}else(h=h.trim())&&a.push(h)}if(f!=b.length)throw Error('Block "'+this.type+'": Message does not reference all '+b.length+" arg(s)."); a.length&&("string"==typeof a[a.length-1]||Blockly.utils.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;g90-b||a>-90-b&&a<-90+b?!0:!1}; Blockly.HorizontalFlyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.top;a=a.height;if(this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP)return new goog.math.Rect(-1E9,b-1E9,2E9,1E9+a);if(this.toolboxPosition_==Blockly.TOOLBOX_AT_BOTTOM)return new goog.math.Rect(-1E9,b,2E9,1E9+a)}; -Blockly.HorizontalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace_.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)a=Math.max(a,d.getHeightWidth().height);a+=1.5*this.MARGIN;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.height_!=a){for(c=0;d=b[c];c++)d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);this.height_=a;this.targetWorkspace_.resize()}};Blockly.VerticalFlyout=function(a){a.getMetrics=this.getMetrics_.bind(this);a.setMetrics=this.setMetrics_.bind(this);Blockly.VerticalFlyout.superClass_.constructor.call(this,a);this.horizontalLayout_=!1};goog.inherits(Blockly.VerticalFlyout,Blockly.Flyout); +Blockly.HorizontalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace_.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)a=Math.max(a,d.getHeightWidth().height);a+=1.5*this.MARGIN;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.height_!=a){for(c=0;d=b[c];c++)d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);this.height_=a;this.position()}};Blockly.VerticalFlyout=function(a){a.getMetrics=this.getMetrics_.bind(this);a.setMetrics=this.setMetrics_.bind(this);Blockly.VerticalFlyout.superClass_.constructor.call(this,a);this.horizontalLayout_=!1};goog.inherits(Blockly.VerticalFlyout,Blockly.Flyout); Blockly.VerticalFlyout.prototype.getMetrics_=function(){if(!this.isVisible())return null;try{var a=this.workspace_.getCanvas().getBBox()}catch(e){a={height:0,y:0,width:0,x:0}}var b=this.SCROLLBAR_PADDING,c=this.height_-2*this.SCROLLBAR_PADDING,d=this.width_;this.RTL||(d-=this.SCROLLBAR_PADDING);return{viewHeight:c,viewWidth:d,contentHeight:a.height*this.workspace_.scale+2*this.MARGIN,contentWidth:a.width*this.workspace_.scale+2*this.MARGIN,viewTop:-this.workspace_.scrollY+a.y,viewLeft:-this.workspace_.scrollX, contentTop:a.y,contentLeft:a.x,absoluteTop:b,absoluteLeft:0}};Blockly.VerticalFlyout.prototype.setMetrics_=function(a){var b=this.getMetrics_();b&&("number"==typeof a.y&&(this.workspace_.scrollY=-b.contentHeight*a.y),this.workspace_.translate(this.workspace_.scrollX+b.absoluteLeft,this.workspace_.scrollY+b.absoluteTop))}; Blockly.VerticalFlyout.prototype.position=function(){if(this.isVisible()){var a=this.targetWorkspace_.getMetrics();if(a){this.height_=a.viewHeight;this.setBackgroundPath_(this.width_-this.CORNER_RADIUS,a.viewHeight-2*this.CORNER_RADIUS);var b=a.absoluteTop,c=a.absoluteLeft;this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT&&(this.leftEdge_=c+=a.viewWidth-this.width_);this.positionAt_(this.width_,this.height_,c,b)}}}; @@ -1641,7 +1679,7 @@ f.button.height+b[e])};Blockly.VerticalFlyout.prototype.isDragTowardWorkspace=fu Blockly.VerticalFlyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.left;a=a.width;if(this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT)return new goog.math.Rect(b-1E9,-1E9,1E9+a,2E9);if(goog.userAgent.GECKO&&this.targetWorkspace_&&this.targetWorkspace_.isMutator){var c=this.targetWorkspace_.svgGroup_.getBoundingClientRect().x;10>Math.abs(c-b)&&(b+=this.leftEdge_*this.targetWorkspace_.options.parentWorkspace.scale)}return new goog.math.Rect(b, -1E9,1E9+a,2E9)}; Blockly.VerticalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace_.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++){var e=d.getHeightWidth().width;d.outputConnection&&(e-=Blockly.BlockSvg.TAB_WIDTH);a=Math.max(a,e)}for(c=0;d=this.buttons_[c];c++)a=Math.max(a,d.width);a+=1.5*this.MARGIN+Blockly.BlockSvg.TAB_WIDTH;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.width_!=a){for(c=0;d=b[c];c++)this.RTL&&(e=d.getRelativeToSurfaceXY().x, -d.moveBy(a/this.workspace_.scale-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH-e,0)),d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);if(this.RTL)for(c=0;d=this.buttons_[c];c++)b=d.getPosition().y,d.moveTo(a/this.workspace_.scale-d.width-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH,b);this.width_=a;this.targetWorkspace_.resize()}};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"}; +d.moveBy(a/this.workspace_.scale-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH-e,0)),d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);if(this.RTL)for(c=0;d=this.buttons_[c];c++)b=d.getPosition().y,d.moveTo(a/this.workspace_.scale-d.width-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH,b);this.width_=a;this.position()}};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=document.createElement("div");this.HtmlDiv.className="blocklyToolboxDiv";this.HtmlDiv.setAttribute("dir",a.RTL?"RTL":"LTR");b.parentNode.insertBefore(this.HtmlDiv,b);Blockly.bindEventWithChecks_(this.HtmlDiv,"mousedown",this,function(a){Blockly.utils.isRightButton(a)||a.target==this.HtmlDiv?Blockly.hideChaff(!1):Blockly.hideChaff(!0);Blockly.Touch.clearTouchIdentifier()},!1,!0);b={disabledPatternId:a.options.disabledPatternId, parentWorkspace:a,RTL:a.RTL,oneBasedIndex:a.options.oneBasedIndex,horizontalLayout:a.horizontalLayout,toolboxPosition:a.options.toolboxPosition};this.flyout_=null;this.flyout_=a.horizontalLayout?new Blockly.HorizontalFlyout(b):new Blockly.VerticalFlyout(b);Blockly.utils.insertAfter(this.flyout_.createDom("svg"),this.workspace_.getParentSvg());this.flyout_.init(a);this.config_.cleardotPath=a.options.pathToMedia+"1x1.gif";this.config_.cssCollapsedFolderIcon="blocklyTreeIconClosed"+(a.RTL?"Rtl":"Ltr"); @@ -1649,10 +1687,13 @@ this.tree_=b=new Blockly.Toolbox.TreeControl(this,this.config_);b.setShowRootNod Blockly.Toolbox.prototype.getHeight=function(){return this.height}; Blockly.Toolbox.prototype.position=function(){var a=this.HtmlDiv;if(a){var b=this.workspace_.getParentSvg();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 Error("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=Blockly.utils.replaceMessageReferences(g.getAttribute("name"));var h=this.tree_.createNode(e);h.blocks=[];b.add(h);var k=g.getAttribute("custom");k?h.blocks=k:(k=this.syncTrees_(g,h,c))&&(d=k);k=Blockly.utils.replaceMessageReferences(g.getAttribute("colour"));null===k||""===k?h.hexColour="":/^#[0-9a-fA-F]{6}$/.test(k)?(h.hexColour=k, -this.hasColours_=!0):"number"===typeof k||"string"===typeof k&&!isNaN(Number(k))?(h.hexColour=Blockly.hueToRgb(Number(k)),this.hasColours_=!0):(h.hexColour="",console.warn('Toolbox category "'+e+'" has unrecognized colour attribute: '+k));"true"==g.getAttribute("expanded")?(h.blocks.length&&(d=h),h.setExpanded(!0)):h.setExpanded(!1);e=g;break;case "SEP":e&&("CATEGORY"==e.tagName.toUpperCase()?b.add(new Blockly.Toolbox.TreeSeparator(this.treeSeparatorConfig_)):(g=parseFloat(g.getAttribute("gap")), -!isNaN(g)&&e&&e.setAttribute("gap",g)));break;case "BLOCK":case "SHADOW":case "LABEL":case "BUTTON":b.blocks.push(g),e=g}return d};Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren(!1);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.addStyle=function(a){Blockly.utils.addClass(this.HtmlDiv,a)};Blockly.Toolbox.prototype.removeStyle=function(a){Blockly.utils.removeClass(this.HtmlDiv,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=Blockly.utils.replaceMessageReferences(g.getAttribute("name"));var h=this.tree_.createNode(e);h.blocks=[];b.add(h);var k=g.getAttribute("custom");k?h.blocks=k:(k=this.syncTrees_(g,h,c))&&(d=k);k=g.getAttribute("style");var l=g.getAttribute("colour");l&&k?(h.hexColour="",console.warn('Toolbox category "'+e+'" can not have both a style and a colour')): +k?this.setColourFromStyle_(k,h,e):this.setColour_(l,h,e);"true"==g.getAttribute("expanded")?(h.blocks.length&&(d=h),h.setExpanded(!0)):h.setExpanded(!1);e=g;break;case "SEP":e&&("CATEGORY"==e.tagName.toUpperCase()?b.add(new Blockly.Toolbox.TreeSeparator(this.treeSeparatorConfig_)):(g=parseFloat(g.getAttribute("gap")),!isNaN(g)&&e&&e.setAttribute("gap",g)));break;case "BLOCK":case "SHADOW":case "LABEL":case "BUTTON":b.blocks.push(g),e=g}return d}; +Blockly.Toolbox.prototype.setColour_=function(a,b,c){a=Blockly.utils.replaceMessageReferences(a);null===a||""===a?b.hexColour="":/^#[0-9a-fA-F]{6}$/.test(a)?(b.hexColour=a,this.hasColours_=!0):"number"===typeof a||"string"===typeof a&&!isNaN(Number(a))?(b.hexColour=Blockly.hueToRgb(Number(a)),this.hasColours_=!0):(b.hexColour="",console.warn('Toolbox category "'+c+'" has unrecognized colour attribute: '+a))}; +Blockly.Toolbox.prototype.setColourFromStyle_=function(a,b,c){if((b.styleName=a)&&Blockly.getTheme()){var d=Blockly.getTheme().getCategoryStyle(a);d&&d.colour?this.setColour_(d.colour,b,c):console.warn('Style "'+a+'" must exist and contain a colour value')}};Blockly.Toolbox.prototype.updateColourFromTheme_=function(a){if(a=a||this.tree_){a=a.getChildren(!1);for(var b=0,c;c=a[b];b++)c.styleName&&(this.setColourFromStyle_(c.styleName,c,""),this.addColour_()),this.updateColourFromTheme_(c)}}; +Blockly.Toolbox.prototype.updateColourFromTheme=function(){var a=this.tree_;a&&(this.updateColourFromTheme_(a),this.updateSelectedItemColour_(a))};Blockly.Toolbox.prototype.updateSelectedItemColour_=function(a){var b=a.selectedItem_;if(b){var c=b.hexColour||"#57e";b.getRowElement().style.backgroundColor=c;a.toolbox_.addColour_(b)}}; +Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren(!1);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.addStyle=function(a){Blockly.utils.addClass(this.HtmlDiv,a)}; +Blockly.Toolbox.prototype.removeStyle=function(a){Blockly.utils.removeClass(this.HtmlDiv,a)}; 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.bindEventWithChecks_(a,goog.events.EventType.TOUCHEND,this,this.handleTouchEvent_)}};Blockly.Toolbox.TreeControl.prototype.handleTouchEvent_=function(a){var b=this.getNodeFromEvent_(a);b&&a.type===goog.events.EventType.TOUCHEND&&setTimeout(function(){b.onClick_(a)},1)}; @@ -1677,44 +1718,46 @@ Blockly.Css.CONTENT=[".blocklySvg {","background-color: #fff;","outline: none;", "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;","}",".blocklyTransparentBackground {","opacity: 0;","}",".blocklyMainWorkspaceScrollbar {","z-index: 20;","}",".blocklyFlyoutScrollbar {","z-index: 30;", "}",".blocklyScrollbarHorizontal, .blocklyScrollbarVertical {","position: absolute;","outline: none;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarHandle {","fill: #ccc;","}",".blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,",".blocklyScrollbarHandle:hover {","fill: #bbb;","}",".blocklyZoom>image, .blocklyZoom>svg>image {","opacity: .4;","}",".blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {","opacity: .6;","}",".blocklyZoom>image:active, .blocklyZoom>svg>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;","pointer-events: none;","}",".blocklyContextMenu {","border-radius: 4px;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(<<>>/sprites.png) no-repeat -48px -16px !important;","}",".blocklyToolboxDiv {","background-color: #ddd;","overflow-x: visible;","overflow-y: auto;","position: absolute;", -"user-select: none;","-moz-user-select: none;","-ms-user-select: none;","-webkit-user-select: none;","z-index: 70;","-webkit-tap-highlight-color: transparent;","}",".blocklyTreeRoot {","padding: 4px 0;","}",".blocklyTreeRoot:focus {","outline: none;","}",".blocklyTreeRow {","height: 22px;","line-height: 22px;","margin-bottom: 3px;","padding-right: 8px;","white-space: nowrap;","}",".blocklyHorizontalTree {","float: left;","margin: 1px 5px 8px 0;","}",".blocklyHorizontalTreeRtl {","float: right;","margin: 1px 0 8px 5px;", -"}",'.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',"margin-left: 8px;","}",".blocklyTreeRow:not(.blocklyTreeSelected):hover {","background-color: #e4e4e4;","}",".blocklyTreeSeparator {","border-bottom: solid #e5e5e5 1px;","height: 0;","margin: 5px 0;","}",".blocklyTreeSeparatorHorizontal {","border-right: solid #e5e5e5 1px;","width: 0;","padding: 5px 0;","margin: 0 5px;","}",".blocklyTreeIcon {","background-image: url(<<>>/sprites.png);","height: 16px;","vertical-align: middle;","width: 16px;", -"}",".blocklyTreeIconClosedLtr {","background-position: -32px -1px;","}",".blocklyTreeIconClosedRtl {","background-position: 0 -1px;","}",".blocklyTreeIconOpen {","background-position: -16px -1px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedLtr {","background-position: -32px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedRtl {","background-position: 0 -17px;","}",".blocklyTreeSelected>.blocklyTreeIconOpen {","background-position: -16px -17px;","}",".blocklyTreeIconNone,",".blocklyTreeSelected>.blocklyTreeIconNone {", -"background-position: -48px -1px;","}",".blocklyTreeLabel {","cursor: default;","font-family: sans-serif;","font-size: 16px;","padding: 0 3px;","vertical-align: middle;","}",".blocklyToolboxDelete .blocklyTreeLabel {",'cursor: url("<<>>/handdelete.cur"), auto;',"}",".blocklyTreeSelected .blocklyTreeLabel {","color: #fff;","}",".blocklyColourTable {","border-collapse: collapse;","}",".blocklyColourTable>tr>td {","border: 1px solid #666;","padding: 0;","}",".blocklyColourTable>tr>td>div {","border: 1px solid #666;", -"height: 13px;","width: 15px;","}",".blocklyColourTable>tr>td>div:hover {","border: 1px solid #fff;","}",".blocklyColourSelected, .blocklyColourSelected:hover {","border: 1px solid #000 !important;","}",".blocklyWidgetDiv .goog-menu {","background: #fff;","border-color: #ccc #666 #666 #ccc;","border-style: solid;","border-width: 1px;","cursor: default;","font: normal 13px Arial, sans-serif;","margin: 0;","outline: none;","padding: 4px 0;","position: absolute;","overflow-y: auto;","overflow-x: hidden;", -"max-height: 100%;","z-index: 20000;","}",".blocklyWidgetDiv .goog-menuitem {","color: #000;","font: normal 13px Arial, sans-serif;","list-style: none;","margin: 0;","padding: 4px 7em 4px 28px;","white-space: nowrap;","}",".blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl {","padding-left: 7em;","padding-right: 28px;","}",".blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,",".blocklyWidgetDiv .goog-menu-noicon .goog-menuitem {","padding-left: 12px;","}",".blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem {", -"padding-right: 20px;","}",".blocklyWidgetDiv .goog-menuitem-content {","color: #000;","font: normal 13px Arial, sans-serif;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel,",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content {","color: #ccc !important;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon {","opacity: 0.3;","filter: alpha(opacity=30);","}",".blocklyWidgetDiv .goog-menuitem-highlight,",".blocklyWidgetDiv .goog-menuitem-hover {","background-color: #d6e9f8;", -"border-color: #d6e9f8;","border-style: dotted;","border-width: 1px 0;","padding-bottom: 3px;","padding-top: 3px;","}",".blocklyWidgetDiv .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-icon {","background-repeat: no-repeat;","height: 16px;","left: 6px;","position: absolute;","right: auto;","vertical-align: middle;","width: 16px;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon {","left: auto;","right: 6px;", -"}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0;","}",".blocklyWidgetDiv .goog-menuitem-accel {","color: #999;","direction: ltr;","left: auto;","padding: 0 6px;","position: absolute;","right: 0;","text-align: right;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel {","left: 0;","right: auto;","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=document.createElement("div"),Blockly.WidgetDiv.DIV.className="blocklyWidgetDiv",document.body.appendChild(Blockly.WidgetDiv.DIV))}; +"stroke: #f00;","stroke-width: 2;","stroke-linecap: round;","pointer-events: none;","}",".blocklyContextMenu {","border-radius: 4px;","max-height: 100%;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(<<>>/sprites.png) no-repeat -48px -16px !important;","}",".blocklyToolboxDiv {","background-color: #ddd;","overflow-x: visible;","overflow-y: auto;", +"position: absolute;","user-select: none;","-moz-user-select: none;","-ms-user-select: none;","-webkit-user-select: none;","z-index: 70;","-webkit-tap-highlight-color: transparent;","}",".blocklyTreeRoot {","padding: 4px 0;","}",".blocklyTreeRoot:focus {","outline: none;","}",".blocklyTreeRow {","height: 22px;","line-height: 22px;","margin-bottom: 3px;","padding-right: 8px;","white-space: nowrap;","}",".blocklyHorizontalTree {","float: left;","margin: 1px 5px 8px 0;","}",".blocklyHorizontalTreeRtl {", +"float: right;","margin: 1px 0 8px 5px;","}",'.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',"margin-left: 8px;","}",".blocklyTreeRow:not(.blocklyTreeSelected):hover {","background-color: #e4e4e4;","}",".blocklyTreeSeparator {","border-bottom: solid #e5e5e5 1px;","height: 0;","margin: 5px 0;","}",".blocklyTreeSeparatorHorizontal {","border-right: solid #e5e5e5 1px;","width: 0;","padding: 5px 0;","margin: 0 5px;","}",".blocklyTreeIcon {","background-image: url(<<>>/sprites.png);","height: 16px;", +"vertical-align: middle;","width: 16px;","}",".blocklyTreeIconClosedLtr {","background-position: -32px -1px;","}",".blocklyTreeIconClosedRtl {","background-position: 0 -1px;","}",".blocklyTreeIconOpen {","background-position: -16px -1px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedLtr {","background-position: -32px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedRtl {","background-position: 0 -17px;","}",".blocklyTreeSelected>.blocklyTreeIconOpen {","background-position: -16px -17px;", +"}",".blocklyTreeIconNone,",".blocklyTreeSelected>.blocklyTreeIconNone {","background-position: -48px -1px;","}",".blocklyTreeLabel {","cursor: default;","font-family: sans-serif;","font-size: 16px;","padding: 0 3px;","vertical-align: middle;","}",".blocklyToolboxDelete .blocklyTreeLabel {",'cursor: url("<<>>/handdelete.cur"), auto;',"}",".blocklyTreeSelected .blocklyTreeLabel {","color: #fff;","}",".blocklyColourTable {","border-collapse: collapse;","}",".blocklyColourTable>tr>td {","border: 1px solid #666;", +"padding: 0;","}",".blocklyColourTable>tr>td>div {","border: 1px solid #666;","height: 13px;","width: 15px;","}",".blocklyColourTable>tr>td>div:hover {","border: 1px solid #fff;","}",".blocklyColourSelected, .blocklyColourSelected:hover {","border: 1px solid #000 !important;","}",".blocklyWidgetDiv .goog-menu {","background: #fff;","border-color: #ccc #666 #666 #ccc;","border-style: solid;","border-width: 1px;","cursor: default;","font: normal 13px Arial, sans-serif;","margin: 0;","outline: none;", +"padding: 4px 0;","position: absolute;","overflow-y: auto;","overflow-x: hidden;","max-height: 100%;","z-index: 20000;","}",".blocklyWidgetDiv .goog-menuitem {","color: #000;","font: normal 13px Arial, sans-serif;","list-style: none;","margin: 0;","padding: 4px 7em 4px 28px;","white-space: nowrap;","}",".blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl {","padding-left: 7em;","padding-right: 28px;","}",".blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,",".blocklyWidgetDiv .goog-menu-noicon .goog-menuitem {", +"padding-left: 12px;","}",".blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem {","padding-right: 20px;","}",".blocklyWidgetDiv .goog-menuitem-content {","color: #000;","font: normal 13px Arial, sans-serif;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel,",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content {","color: #ccc !important;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon {","opacity: 0.3;","filter: alpha(opacity=30);","}",".blocklyWidgetDiv .goog-menuitem-highlight,", +".blocklyWidgetDiv .goog-menuitem-hover {","background-color: #d6e9f8;","border-color: #d6e9f8;","border-style: dotted;","border-width: 1px 0;","padding-bottom: 3px;","padding-top: 3px;","}",".blocklyWidgetDiv .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-icon {","background-repeat: no-repeat;","height: 16px;","left: 6px;","position: absolute;","right: auto;","vertical-align: middle;","width: 16px;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon {", +"left: auto;","right: 6px;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0;","}",".blocklyWidgetDiv .goog-menuitem-accel {","color: #999;","direction: ltr;","left: auto;","padding: 0 6px;","position: absolute;","right: 0;","text-align: right;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel {","left: 0;","right: auto;", +"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=document.createElement("div"),Blockly.WidgetDiv.DIV.className="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,Blockly.WidgetDiv.DIV.innerHTML="")};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){bc.width+d.x&&(a=c.width+d.x):a=a.bottom?b.top-c.height:b.bottom};Blockly.BlockDragSurfaceSvg=function(a){this.container_=a;this.createDom()};Blockly.BlockDragSurfaceSvg.prototype.SVG_=null;Blockly.BlockDragSurfaceSvg.prototype.dragGroup_=null;Blockly.BlockDragSurfaceSvg.prototype.container_=null;Blockly.BlockDragSurfaceSvg.prototype.scale_=1;Blockly.BlockDragSurfaceSvg.prototype.surfaceXY_=null; +Blockly.WidgetDiv.positionWithAnchor=function(a,b,c,d){var e=Blockly.WidgetDiv.calculateY_(a,b,c);a=Blockly.WidgetDiv.calculateX_(a,b,c,d);0>e?Blockly.WidgetDiv.positionInternal_(a,0,c.height+e):Blockly.WidgetDiv.positionInternal_(a,e,c.height)};Blockly.WidgetDiv.calculateX_=function(a,b,c,d){if(d)return b=Math.max(b.right-c.width,a.left),Math.min(b,a.right-c.width);b=Math.min(b.left,a.right-c.width);return Math.max(b,a.left)}; +Blockly.WidgetDiv.calculateY_=function(a,b,c){return b.bottom+c.height>=a.bottom?b.top-c.height:b.bottom};Blockly.BlockDragSurfaceSvg=function(a){this.container_=a;this.createDom()};Blockly.BlockDragSurfaceSvg.prototype.SVG_=null;Blockly.BlockDragSurfaceSvg.prototype.dragGroup_=null;Blockly.BlockDragSurfaceSvg.prototype.container_=null;Blockly.BlockDragSurfaceSvg.prototype.scale_=1;Blockly.BlockDragSurfaceSvg.prototype.surfaceXY_=null; Blockly.BlockDragSurfaceSvg.prototype.createDom=function(){this.SVG_||(this.SVG_=Blockly.utils.createSvgElement("svg",{xmlns:Blockly.SVG_NS,"xmlns:html":Blockly.HTML_NS,"xmlns:xlink":"http://www.w3.org/1999/xlink",version:"1.1","class":"blocklyBlockDragSurface"},this.container_),this.dragGroup_=Blockly.utils.createSvgElement("g",{},this.SVG_))}; Blockly.BlockDragSurfaceSvg.prototype.setBlocksAndShow=function(a){if(this.dragGroup_.childNodes.length)throw Error("Already dragging a block.");this.dragGroup_.appendChild(a);this.SVG_.style.display="block";this.surfaceXY_=new goog.math.Coordinate(0,0)};Blockly.BlockDragSurfaceSvg.prototype.translateAndScaleGroup=function(a,b,c){this.scale_=c;a=a.toFixed(0);b=b.toFixed(0);this.dragGroup_.setAttribute("transform","translate("+a+","+b+") scale("+c+")")}; Blockly.BlockDragSurfaceSvg.prototype.translateSurfaceInternal_=function(){var a=this.surfaceXY_.x,b=this.surfaceXY_.y;a=a.toFixed(0);b=b.toFixed(0);this.SVG_.style.display="block";Blockly.utils.setCssTransform(this.SVG_,"translate3d("+a+"px, "+b+"px, 0px)")};Blockly.BlockDragSurfaceSvg.prototype.translateSurface=function(a,b){this.surfaceXY_=new goog.math.Coordinate(a*this.scale_,b*this.scale_);this.translateSurfaceInternal_()}; Blockly.BlockDragSurfaceSvg.prototype.getSurfaceTranslation=function(){var a=Blockly.utils.getRelativeXY(this.SVG_);return new goog.math.Coordinate(a.x/this.scale_,a.y/this.scale_)};Blockly.BlockDragSurfaceSvg.prototype.getGroup=function(){return this.dragGroup_};Blockly.BlockDragSurfaceSvg.prototype.getCurrentBlock=function(){return this.dragGroup_.firstChild}; -Blockly.BlockDragSurfaceSvg.prototype.clearAndHide=function(a){a?a.appendChild(this.getCurrentBlock()):this.dragGroup_.removeChild(this.getCurrentBlock());this.SVG_.style.display="none";if(this.dragGroup_.childNodes.length)throw Error("Drag group was not cleared.");this.surfaceXY_=null};Blockly.inject=function(a,b){Blockly.checkBlockColourConstants();"string"==typeof a&&(a=document.getElementById(a)||document.querySelector(a));if(!Blockly.utils.containsNode(document,a))throw Error("Error: container is not in current document.");b=new Blockly.Options(b||{});var c=document.createElement("div");c.className="injectionDiv";a.appendChild(c);a=Blockly.createDom_(c,b);var d=new Blockly.BlockDragSurfaceSvg(c);c=new Blockly.WorkspaceDragSurfaceSvg(c);b=Blockly.createMainWorkspace_(a,b,d,c); -Blockly.init_(b);Blockly.mainWorkspace=b;Blockly.svgResize(b);return b}; +Blockly.BlockDragSurfaceSvg.prototype.clearAndHide=function(a){a?a.appendChild(this.getCurrentBlock()):this.dragGroup_.removeChild(this.getCurrentBlock());this.SVG_.style.display="none";if(this.dragGroup_.childNodes.length)throw Error("Drag group was not cleared.");this.surfaceXY_=null};Blockly.inject=function(a,b){Blockly.checkBlockColourConstants();"string"==typeof a&&(a=document.getElementById(a)||document.querySelector(a));if(!Blockly.utils.containsNode(document,a))throw Error("Error: container is not in current document.");b=new Blockly.Options(b||{});var c=document.createElement("div");c.className="injectionDiv";a.appendChild(c);a=Blockly.createDom_(c,b);var d=new Blockly.BlockDragSurfaceSvg(c);c=new Blockly.WorkspaceDragSurfaceSvg(c);c=Blockly.createMainWorkspace_(a,b,d,c); +Blockly.setTheme(b.theme);Blockly.init_(c);Blockly.mainWorkspace=c;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);a=Blockly.utils.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);var c=Blockly.utils.createSvgElement("defs",{},a),d=String(Math.random()).substring(2),e=Blockly.utils.createSvgElement("filter",{id:"blocklyEmbossFilter"+ d},c);Blockly.utils.createSvgElement("feGaussianBlur",{"in":"SourceAlpha",stdDeviation:1,result:"blur"},e);var f=Blockly.utils.createSvgElement("feSpecularLighting",{"in":"blur",surfaceScale:1,specularConstant:.5,specularExponent:10,"lighting-color":"white",result:"specOut"},e);Blockly.utils.createSvgElement("fePointLight",{x:-5E3,y:-1E4,z:2E4},f);Blockly.utils.createSvgElement("feComposite",{"in":"specOut",in2:"SourceAlpha",operator:"in",result:"specOut"},e);Blockly.utils.createSvgElement("feComposite", {"in":"SourceGraphic",in2:"specOut",operator:"arithmetic",k1:0,k2:1,k3:1,k4:0},e);b.embossFilterId=e.id;e=Blockly.utils.createSvgElement("pattern",{id:"blocklyDisabledPattern"+d,patternUnits:"userSpaceOnUse",width:10,height:10},c);Blockly.utils.createSvgElement("rect",{width:10,height:10,fill:"#aaa"},e);Blockly.utils.createSvgElement("path",{d:"M 0 0 L 10 10 M 10 0 L 0 10",stroke:"#cc0"},e);b.disabledPatternId=e.id;b.gridPattern=Blockly.Grid.createDom(d,b.gridOptions,c);return a}; -Blockly.createMainWorkspace_=function(a,b,c,d){b.parentWorkspace=null;var e=new Blockly.WorkspaceSvg(b,c,d);e.scale=b.zoomOptions.startScale;a.appendChild(e.createDom("blocklyMainBackground"));!b.hasCategories&&b.languageTree&&(c=e.addFlyout_("svg"),Blockly.utils.insertAfter(c,a));e.translate(0,0);Blockly.mainWorkspace=e;b.readOnly||b.hasScrollbars||e.addChangeListener(function(a){if(!e.isDragging()){var c=e.getMetrics(),d=c.viewLeft+c.absoluteLeft,f=c.viewTop+c.absoluteTop;if(c.contentTopc.viewHeight+f||c.contentLeft<(b.RTL?c.viewLeft:d)||c.contentLeft+c.contentWidth>(b.RTL?c.viewWidth:c.viewWidth+d)){var l=e.getTopBlocks(!1),n=null;a&&(n=Blockly.Events.getGroup(),Blockly.Events.setGroup(a.group));for(var m=!1,p=0,q;q=l[p];p++){var r=q.getRelativeToSurfaceXY(),t=q.getHeightWidth(),u=f+25-t.height-r.y;0u&&(q.moveBy(0,u),m=!0);u=25+d-r.x-(b.RTL?0:t.width);0r&&(q.moveBy(r,0),m=!0)}a&&(!a.group&&m&&console.log("WARNING: Moved blocks in bounds but there was no event group. This may break undo."),Blockly.Events.setGroup(n))}}});Blockly.svgResize(e);Blockly.WidgetDiv.createDom();Blockly.Tooltip.createDom();return e}; +Blockly.createMainWorkspace_=function(a,b,c,d){b.parentWorkspace=null;var e=new Blockly.WorkspaceSvg(b,c,d);e.scale=b.zoomOptions.startScale;a.appendChild(e.createDom("blocklyMainBackground"));!b.hasCategories&&b.languageTree&&(c=e.addFlyout_("svg"),Blockly.utils.insertAfter(c,a));b.hasTrashcan&&e.addTrashcan();b.zoomOptions&&b.zoomOptions.controls&&e.addZoomControls();e.translate(0,0);Blockly.mainWorkspace=e;b.readOnly||b.hasScrollbars||e.addChangeListener(function(a){if(!e.isDragging()){var c=e.getMetrics(), +d=c.viewLeft+c.absoluteLeft,f=c.viewTop+c.absoluteTop;if(c.contentTopc.viewHeight+f||c.contentLeft<(b.RTL?c.viewLeft:d)||c.contentLeft+c.contentWidth>(b.RTL?c.viewWidth:c.viewWidth+d)){var l=e.getTopBlocks(!1),n=null;a&&(n=Blockly.Events.getGroup(),Blockly.Events.setGroup(a.group));for(var m=!1,p=0,q;q=l[p];p++){var t=q.getRelativeToSurfaceXY(),r=q.getHeightWidth(),u=f+25-r.height-t.y;0u&&(q.moveBy(0,u),m=!0);u=25+ +d-t.x-(b.RTL?0:r.width);0t&&(q.moveBy(t,0),m=!0)}a&&(!a.group&&m&&console.log("WARNING: Moved blocks in bounds but there was no event group. This may break undo."),Blockly.Events.setGroup(n))}}});Blockly.svgResize(e);Blockly.WidgetDiv.createDom();Blockly.Tooltip.createDom();return e}; Blockly.init_=function(a){var b=a.options,c=a.getParentSvg();Blockly.bindEventWithChecks_(c.parentNode,"contextmenu",null,function(a){Blockly.utils.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)}; +a.scrollX=a.flyout_.width_,b.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT&&(a.scrollX*=-1),a.translate(a.scrollX,0)));c=Blockly.Scrollbar.scrollbarThickness;b.hasTrashcan&&(c=a.trashcan.init(c));b.zoomOptions&&b.zoomOptions.controls&&a.zoomControls_.init(c);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.bindEvent_(document,"touchend",null,Blockly.longStop_),Blockly.bindEvent_(document,"touchcancel",null,Blockly.longStop_),goog.userAgent.IPAD&&Blockly.bindEventWithChecks_(window,"orientationchange",document,function(){Blockly.svgResize(Blockly.getMainWorkspace())}));Blockly.documentEventsBound_=!0}; Blockly.inject.loadSounds_=function(a,b){var c=b.getAudioManager();c.load([a+"click.mp3",a+"click.wav",a+"click.ogg"],"click");c.load([a+"disconnect.wav",a+"disconnect.mp3",a+"disconnect.ogg"],"disconnect");c.load([a+"delete.mp3",a+"delete.ogg",a+"delete.wav"],"delete");var d=[];a=function(){for(;d.length;)Blockly.unbindEvent_(d.pop());c.preload()};d.push(Blockly.bindEventWithChecks_(document,"mousemove",null,a,!0));d.push(Blockly.bindEventWithChecks_(document,"touchstart",null,a,!0))}; -Blockly.updateToolbox=function(a){console.warn("Deprecated call to Blockly.updateToolbox, use workspace.updateToolbox instead.");Blockly.getMainWorkspace().updateToolbox(a)};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.clipboardTypeCounts_=null;Blockly.cache3dSupported_=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.updateToolbox=function(a){console.warn("Deprecated call to Blockly.updateToolbox, use workspace.updateToolbox instead.");Blockly.getMainWorkspace().updateToolbox(a)};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.clipboardTypeCounts_=null;Blockly.cache3dSupported_=null;Blockly.theme_=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.onKeyDown_=function(a){var b=Blockly.mainWorkspace;if(!(b.options.readOnly||Blockly.utils.isTargetInput(a)||b.rendered&&!b.isVisible())){var c=!1;if(27==a.keyCode)Blockly.hideChaff();else if(8==a.keyCode||46==a.keyCode){a.preventDefault();if(b.isDragging())return;Blockly.selected&&Blockly.selected.isDeletable()&&(c=!0)}else if(a.altKey||a.ctrlKey||a.metaKey){if(b.isDragging())return;Blockly.selected&&Blockly.selected.isDeletable()&&Blockly.selected.isMovable()&&(67==a.keyCode?(Blockly.hideChaff(), Blockly.copy_(Blockly.selected)):88!=a.keyCode||Blockly.selected.workspace.isFlyout||(Blockly.copy_(Blockly.selected),c=!0));86==a.keyCode?Blockly.clipboardXml_&&(b=Blockly.clipboardSource_,b.isFlyout&&(b=b.targetWorkspace),Blockly.clipboardTypeCounts_&&b.isCapacityAvailable(Blockly.clipboardTypeCounts_)&&(Blockly.Events.setGroup(!0),b.paste(Blockly.clipboardXml_),Blockly.Events.setGroup(!1))):90==a.keyCode&&(Blockly.hideChaff(),b.undo(a.shiftKey))}c&&!Blockly.selected.workspace.isFlyout&&(Blockly.Events.setGroup(!0), Blockly.hideChaff(),Blockly.selected.dispose(!0,!0),Blockly.Events.setGroup(!1))}};Blockly.copy_=function(a){if(a.isComment)var b=a.toXmlWithXY();else{b=Blockly.Xml.blockToDom(a);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.clipboardTypeCounts_=a.isComment?null:Blockly.utils.getBlockTypeCounts(a,!0)}; -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.utils.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};Blockly.alert=function(a,b){window.alert(a);b&&b()};Blockly.confirm=function(a,b){b(window.confirm(a))};Blockly.prompt=function(a,b,c){c(window.prompt(a,b))};Blockly.jsonInitFactory_=function(a){return function(){this.jsonInit(a)}}; +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.utils.isTargetInput(a)||a.preventDefault()}; +Blockly.hideChaff=function(a){Blockly.Tooltip.hide();Blockly.WidgetDiv.hide();var b=Blockly.getMainWorkspace();b.trashcan&&b.trashcan.flyout_&&b.trashcan.flyout_.hide();a||b.toolbox_&&b.toolbox_.flyout_&&b.toolbox_.flyout_.autoClose&&b.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}; +Blockly.alert=function(a,b){window.alert(a);b&&b()};Blockly.confirm=function(a,b){b(window.confirm(a))};Blockly.prompt=function(a,b,c){c(window.prompt(a,b))};Blockly.jsonInitFactory_=function(a){return function(){this.jsonInit(a)}}; Blockly.defineBlocksWithJsonArray=function(a){for(var b=0;bNumber(a[1])?!1:b('(()=>{"use strict";class X{constructor(){if(new.target!=String)throw 1;this.x=42}}let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof String))throw 1;for(const a of[2,3]){if(a==2)continue;function f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()==3}})()')}); +var c="es3",d={es3:!1},e=!1,f=goog.global.navigator&&goog.global.navigator.userAgent?goog.global.navigator.userAgent:"";a("es5",function(){return b("[1,].length==1")});a("es6",function(){return f.match(/Edge\/(\d+)(\.\d)*/i)?!1:b('(()=>{"use strict";class X{constructor(){if(new.target!=String)throw 1;this.x=42}}let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof String))throw 1;for(const a of[2,3]){if(a==2)continue;function f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()==3}})()')}); a("es6-impl",function(){return!0});a("es7",function(){return b("2 ** 2 == 4")});a("es8",function(){return b("async () => 1, true")});a("es9",function(){return b("({...rest} = {}), true")});a("es_next",function(){return!1});return{target:c,map:d}},goog.Transpiler.prototype.needsTranspile=function(a,b){if("always"==goog.TRANSPILE)return!0;if("never"==goog.TRANSPILE)return!1;if(!this.requiresTranspilation_){var c=this.createRequiresTranspilation_();this.requiresTranspilation_=c.map;this.transpilationTarget_= this.transpilationTarget_||c.target}if(a in this.requiresTranspilation_)return this.requiresTranspilation_[a]?!0:!goog.inHtmlDocument_()||"es6"!=b||"noModule"in goog.global.document.createElement("script")?!1:!0;throw Error("Unknown language mode: "+a);},goog.Transpiler.prototype.transpile=function(a,b){return goog.transpile_(a,b,this.transpilationTarget_)},goog.transpiler_=new goog.Transpiler,goog.protectScriptTag_=function(a){return a.replace(/<\/(SCRIPT)/ig,"\\x3c/$1")},goog.DebugLoader_=function(){this.dependencies_= {};this.idToPath_={};this.written_={};this.loadingDeps_=[];this.depsToLoad_=[];this.paused_=!1;this.factory_=new goog.DependencyFactory(goog.transpiler_);this.deferredCallbacks_={};this.deferredQueue_=[]},goog.DebugLoader_.prototype.bootstrap=function(a,b){function c(){d&&(goog.global.setTimeout(d,0),d=null)}var d=b;if(a.length){for(var e=[],f=0;f'+goog.protectScriptTag_('goog.Dependency.callback_("'+b+'");')+"\x3c/script>")}var e=this;if(goog.global.CLOSURE_IMPORT_SCRIPT)b(),this.contents_&& goog.global.CLOSURE_IMPORT_SCRIPT("",this.contents_)?(this.contents_=null,a.loaded()):a.pause();else{var f=this.loadFlags.module==goog.ModuleType.ES6;this.lazyFetch_||b();var g=1>16,a>>8&255,a&255]}; +goog.color.rgbToHex=function(a,b,c){a=Number(a);b=Number(b);c=Number(c);if(a!=(a&255)||b!=(b&255)||c!=(c&255))throw Error('"('+a+","+b+","+c+'") is not a valid RGB color');b=a<<16|b<<8|c;return 16>a?"#"+(16777216|b).toString(16).substr(1):"#"+b.toString(16)};goog.color.rgbArrayToHex=function(a){return goog.color.rgbToHex(a[0],a[1],a[2])}; goog.color.rgbToHsl=function(a,b,c){a/=255;b/=255;c/=255;var d=Math.max(a,b,c),e=Math.min(a,b,c),f=0,g=0,h=.5*(d+e);d!=e&&(d==a?f=60*(b-c)/(d-e):d==b?f=60*(c-a)/(d-e)+120:d==c&&(f=60*(a-b)/(d-e)+240),g=0=h?(d-e)/(2*h):(d-e)/(2-2*h));return[Math.round(f+360)%360,g,h]};goog.color.rgbArrayToHsl=function(a){return goog.color.rgbToHsl(a[0],a[1],a[2])};goog.color.hueToRgb_=function(a,b,c){0>c?c+=1:16*c?a+6*(b-a)*c:1>2*c?b:2>3*c?a+(b-a)*(2/3-c)*6:a}; goog.color.hslToRgb=function(a,b,c){a/=360;if(0==b)c=b=a=255*c;else{var d=.5>c?c*(1+b):c+b-b*c;var e=2*c-d;c=255*goog.color.hueToRgb_(e,d,a+1/3);b=255*goog.color.hueToRgb_(e,d,a);a=255*goog.color.hueToRgb_(e,d,a-1/3)}return[Math.round(c),Math.round(b),Math.round(a)]};goog.color.hslArrayToRgb=function(a){return goog.color.hslToRgb(a[0],a[1],a[2])};goog.color.validHexColorRe_=/^#(?:[0-9a-f]{3}){1,2}$/i;goog.color.isValidHexColor_=function(a){return goog.color.validHexColorRe_.test(a)}; -goog.color.normalizedHexColorRe_=/^#[0-9a-f]{6}$/;goog.color.isNormalizedHexColor_=function(a){return goog.color.normalizedHexColorRe_.test(a)};goog.color.rgbColorRe_=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;goog.color.isValidRgbColor_=function(a){var b=a.match(goog.color.rgbColorRe_);if(b){a=Number(b[1]);var c=Number(b[2]);b=Number(b[3]);if(0<=a&&255>=a&&0<=c&&255>=c&&0<=b&&255>=b)return[a,c,b]}return[]}; -goog.color.prependZeroIfNecessaryHelper=function(a){return 1==a.length?"0"+a:a};goog.color.prependHashIfNecessaryHelper=function(a){return"#"==a.charAt(0)?a:"#"+a};goog.color.rgbStyle_=function(a){return"rgb("+a.join(",")+")"}; -goog.color.hsvToRgb=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=c;else{var g=Math.floor(a/60),h=a/60-g;a=c*(1-b);var k=c*(1-b*h);b=c*(1-b*(1-h));switch(g){case 1:d=k;e=c;f=a;break;case 2:d=a;e=c;f=b;break;case 3:d=a;e=k;f=c;break;case 4:d=b;e=a;f=c;break;case 5:d=c;e=a;f=k;break;case 6:case 0:d=c,e=b,f=a}}return[Math.floor(d),Math.floor(e),Math.floor(f)]}; +goog.color.rgbColorRe_=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;goog.color.isValidRgbColor_=function(a){var b=a.match(goog.color.rgbColorRe_);if(b){a=Number(b[1]);var c=Number(b[2]);b=Number(b[3]);if(0<=a&&255>=a&&0<=c&&255>=c&&0<=b&&255>=b)return[a,c,b]}return[]};goog.color.prependZeroIfNecessaryHelper=function(a){return 1==a.length?"0"+a:a};goog.color.prependHashIfNecessaryHelper=function(a){return"#"==a.charAt(0)?a:"#"+a}; +goog.color.rgbStyle_=function(a){return"rgb("+a.join(",")+")"};goog.color.hsvToRgb=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=c;else{var g=Math.floor(a/60),h=a/60-g;a=c*(1-b);var k=c*(1-b*h);b=c*(1-b*(1-h));switch(g){case 1:d=k;e=c;f=a;break;case 2:d=a;e=c;f=b;break;case 3:d=a;e=k;f=c;break;case 4:d=b;e=a;f=c;break;case 5:d=c;e=a;f=k;break;case 6:case 0:d=c,e=b,f=a}}return[Math.floor(d),Math.floor(e),Math.floor(f)]}; goog.color.rgbToHsv=function(a,b,c){var d=Math.max(Math.max(a,b),c),e=Math.min(Math.min(a,b),c);if(e==d)e=a=0;else{var f=d-e;e=f/d;a=60*(a==d?(b-c)/f:b==d?2+(c-a)/f:4+(a-b)/f);0>a&&(a+=360);360=a[2]?a[1]*a[2]:a[1]*(1-a[2]);var d=.5>=b[2]?b[1]*b[2]:b[1]*(1-b[2]);return(a[2]-b[2])*(a[2]-b[2])+c*c+d*d-2*c*d*Math.cos(2*(a[0]/360-b[0]/360)*Math.PI)};goog.color.blend=function(a,b,c){c=goog.math.clamp(c,0,1);return[Math.round(b[0]+c*(a[0]-b[0])),Math.round(b[1]+c*(a[1]-b[1])),Math.round(b[2]+c*(a[2]-b[2]))]};goog.color.darken=function(a,b){return goog.color.blend([0,0,0],a,b)}; @@ -168,9 +168,10 @@ goog.object.unsafeClone=function(a){var b=goog.typeOf(a);if("object"==b||"array" goog.object.extend=function(a,b){for(var c,d,e=1;e=goog.debug.MAX_STACK_DEPTH){b.push("[...long stack...]");break}}a&&d>=a?b.push("[...reached max depth limit...]"):b.push("[end]");return b.join("")}; goog.debug.MAX_STACK_DEPTH=50;goog.debug.getNativeStackTrace_=function(a){var b=Error();if(Error.captureStackTrace)return Error.captureStackTrace(b,a),String(b.stack);try{throw b;}catch(c){b=c}return(a=b.stack)?String(a):null};goog.debug.getStacktrace=function(a){var b;goog.debug.FORCE_SLOPPY_STACKS||(b=goog.debug.getNativeStackTrace_(a||goog.debug.getStacktrace));b||(b=goog.debug.getStacktraceHelper_(a||arguments.callee.caller,[]));return b}; goog.debug.getStacktraceHelper_=function(a,b){var c=[];if(goog.array.contains(b,a))c.push("[...circular reference...]");else if(a&&b.lengtha.length?"&":"")+encodeURIComponent(d)+"="+encodeURIComponent(String(g)))}}return b};goog.html.SafeUrl=function(){this.privateDoNotAccessOrElseSafeHtmlWrappedValue_="";this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeUrl.INNOCUOUS_STRING="about:invalid#zClosurez";goog.html.SafeUrl.prototype.implementsGoogStringTypedString=!0;goog.html.SafeUrl.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_}; -goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString=!0;goog.html.SafeUrl.prototype.getDirection=function(){return goog.i18n.bidi.Dir.LTR};goog.DEBUG&&(goog.html.SafeUrl.prototype.toString=function(){return"SafeUrl{"+this.privateDoNotAccessOrElseSafeHtmlWrappedValue_+"}"}); -goog.html.SafeUrl.unwrap=function(a){if(a instanceof goog.html.SafeUrl&&a.constructor===goog.html.SafeUrl&&a.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_)return a.privateDoNotAccessOrElseSafeHtmlWrappedValue_;goog.asserts.fail("expected object of type SafeUrl, got '"+a+"' of type "+goog.typeOf(a));return"type_error:SafeUrl"};goog.html.SafeUrl.fromConstant=function(a){return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(goog.string.Const.unwrap(a))}; +goog.html.TrustedResourceUrl.stringifyParams_=function(a,b,c){if(null==c)return b;if(goog.isString(c))return c?a+encodeURIComponent(c):"";for(var d in c){var e=c[d];e=goog.isArray(e)?e:[e];for(var f=0;fa.length?"&":"")+encodeURIComponent(d)+"="+encodeURIComponent(String(g)))}}return b};goog.html.SafeUrl=function(){this.privateDoNotAccessOrElseSafeUrlWrappedValue_="";this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeUrl.INNOCUOUS_STRING="about:invalid#zClosurez";goog.html.SafeUrl.prototype.implementsGoogStringTypedString=!0;goog.html.SafeUrl.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeUrlWrappedValue_}; +goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString=!0;goog.html.SafeUrl.prototype.getDirection=function(){return goog.i18n.bidi.Dir.LTR};goog.DEBUG&&(goog.html.SafeUrl.prototype.toString=function(){return"SafeUrl{"+this.privateDoNotAccessOrElseSafeUrlWrappedValue_+"}"}); +goog.html.SafeUrl.unwrap=function(a){if(a instanceof goog.html.SafeUrl&&a.constructor===goog.html.SafeUrl&&a.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_)return a.privateDoNotAccessOrElseSafeUrlWrappedValue_;goog.asserts.fail("expected object of type SafeUrl, got '"+a+"' of type "+goog.typeOf(a));return"type_error:SafeUrl"};goog.html.SafeUrl.fromConstant=function(a){return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(goog.string.Const.unwrap(a))}; goog.html.SAFE_MIME_TYPE_PATTERN_=/^(?:audio\/(?:3gpp2|3gpp|aac|L16|midi|mp3|mp4|mpeg|oga|ogg|opus|x-m4a|x-wav|wav|webm)|image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|text\/csv|video\/(?:mpeg|mp4|ogg|webm|quicktime))$/i;goog.html.SafeUrl.fromBlob=function(a){a=goog.html.SAFE_MIME_TYPE_PATTERN_.test(a.type)?goog.fs.url.createObjectUrl(a):goog.html.SafeUrl.INNOCUOUS_STRING;return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)};goog.html.DATA_URL_PATTERN_=/^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i; goog.html.SafeUrl.fromDataUrl=function(a){a=a.replace(/(%0A|%0D)/g,"");var b=a.match(goog.html.DATA_URL_PATTERN_);b=b&&goog.html.SAFE_MIME_TYPE_PATTERN_.test(b[1]);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(b?a:goog.html.SafeUrl.INNOCUOUS_STRING)};goog.html.SafeUrl.fromTelUrl=function(a){goog.string.caseInsensitiveStartsWith(a,"tel:")||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)}; goog.html.SIP_URL_PATTERN_=/^sip[s]?:[+a-z0-9_.!$%&'*\/=^`{|}~-]+@([a-z0-9-]+\.)+[a-z0-9]{2,63}$/i;goog.html.SafeUrl.fromSipUrl=function(a){goog.html.SIP_URL_PATTERN_.test(decodeURIComponent(a))||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)};goog.html.SafeUrl.fromFacebookMessengerUrl=function(a){goog.string.caseInsensitiveStartsWith(a,"fb-messenger://share")||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)}; @@ -332,7 +334,7 @@ goog.html.SafeUrl.sanitizeChromeExtensionUrl=function(a,b){return goog.html.Safe goog.html.SafeUrl.sanitizeExtensionUrl_=function(a,b,c){(a=a.exec(b))?(a=a[1],-1==(c instanceof goog.string.Const?[goog.string.Const.unwrap(c)]:c.map(function(a){return goog.string.Const.unwrap(a)})).indexOf(a)&&(b=goog.html.SafeUrl.INNOCUOUS_STRING)):b=goog.html.SafeUrl.INNOCUOUS_STRING;return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(b)};goog.html.SafeUrl.fromTrustedResourceUrl=function(a){return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(goog.html.TrustedResourceUrl.unwrap(a))}; goog.html.SAFE_URL_PATTERN_=/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;goog.html.SafeUrl.SAFE_URL_PATTERN=goog.html.SAFE_URL_PATTERN_;goog.html.SafeUrl.sanitize=function(a){if(a instanceof goog.html.SafeUrl)return a;a="object"==typeof a&&a.implementsGoogStringTypedString?a.getTypedStringValue():String(a);goog.html.SAFE_URL_PATTERN_.test(a)||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)}; goog.html.SafeUrl.sanitizeAssertUnchanged=function(a){if(a instanceof goog.html.SafeUrl)return a;a="object"==typeof a&&a.implementsGoogStringTypedString?a.getTypedStringValue():String(a);goog.asserts.assert(goog.html.SAFE_URL_PATTERN_.test(a),"%s does not match the safe URL pattern",a)||(a=goog.html.SafeUrl.INNOCUOUS_STRING);return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(a)};goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_={}; -goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse=function(a){var b=new goog.html.SafeUrl;b.privateDoNotAccessOrElseSafeHtmlWrappedValue_=a;return b};goog.html.SafeUrl.ABOUT_BLANK=goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse("about:blank");goog.html.SafeStyle=function(){this.privateDoNotAccessOrElseSafeStyleWrappedValue_="";this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeStyle.prototype.implementsGoogStringTypedString=!0;goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_={}; +goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse=function(a){var b=new goog.html.SafeUrl;b.privateDoNotAccessOrElseSafeUrlWrappedValue_=a;return b};goog.html.SafeUrl.ABOUT_BLANK=goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse("about:blank");goog.html.SafeStyle=function(){this.privateDoNotAccessOrElseSafeStyleWrappedValue_="";this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_=goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_};goog.html.SafeStyle.prototype.implementsGoogStringTypedString=!0;goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_={}; goog.html.SafeStyle.fromConstant=function(a){a=goog.string.Const.unwrap(a);if(0===a.length)return goog.html.SafeStyle.EMPTY;goog.html.SafeStyle.checkStyle_(a);goog.asserts.assert(goog.string.endsWith(a,";"),"Last character of style string is not ';': "+a);goog.asserts.assert(goog.string.contains(a,":"),"Style string must contain at least one ':', to specify a \"name: value\" pair: "+a);return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(a)}; goog.html.SafeStyle.checkStyle_=function(a){goog.asserts.assert(!/[<>]/.test(a),"Forbidden characters in style string: "+a)};goog.html.SafeStyle.prototype.getTypedStringValue=function(){return this.privateDoNotAccessOrElseSafeStyleWrappedValue_};goog.DEBUG&&(goog.html.SafeStyle.prototype.toString=function(){return"SafeStyle{"+this.privateDoNotAccessOrElseSafeStyleWrappedValue_+"}"}); goog.html.SafeStyle.unwrap=function(a){if(a instanceof goog.html.SafeStyle&&a.constructor===goog.html.SafeStyle&&a.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_===goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_)return a.privateDoNotAccessOrElseSafeStyleWrappedValue_;goog.asserts.fail("expected object of type SafeStyle, got '"+a+"' of type "+goog.typeOf(a));return"type_error:SafeStyle"};goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse=function(a){return(new goog.html.SafeStyle).initSecurityPrivateDoNotAccessOrElse_(a)}; @@ -384,7 +386,8 @@ goog.dom.safe.setLinkHrefAndRel=function(a,b,c){goog.dom.asserts.assertIsHTMLLin goog.dom.safe.setObjectData=function(a,b){goog.dom.asserts.assertIsHTMLObjectElement(a);a.data=goog.html.TrustedResourceUrl.unwrap(b)};goog.dom.safe.setScriptSrc=function(a,b){goog.dom.asserts.assertIsHTMLScriptElement(a);a.src=goog.html.TrustedResourceUrl.unwrap(b);var c=goog.getScriptNonce();c&&a.setAttribute("nonce",c)}; goog.dom.safe.setScriptContent=function(a,b){goog.dom.asserts.assertIsHTMLScriptElement(a);a.text=goog.html.SafeScript.unwrap(b);var c=goog.getScriptNonce();c&&a.setAttribute("nonce",c)};goog.dom.safe.setLocationHref=function(a,b){goog.dom.asserts.assertIsLocation(a);var c=b instanceof goog.html.SafeUrl?b:goog.html.SafeUrl.sanitizeAssertUnchanged(b);a.href=goog.html.SafeUrl.unwrap(c)}; goog.dom.safe.assignLocation=function(a,b){goog.dom.asserts.assertIsLocation(a);var c=b instanceof goog.html.SafeUrl?b:goog.html.SafeUrl.sanitizeAssertUnchanged(b);a.assign(goog.html.SafeUrl.unwrap(c))};goog.dom.safe.replaceLocation=function(a,b){goog.dom.asserts.assertIsLocation(a);var c=b instanceof goog.html.SafeUrl?b:goog.html.SafeUrl.sanitizeAssertUnchanged(b);a.replace(goog.html.SafeUrl.unwrap(c))}; -goog.dom.safe.openInWindow=function(a,b,c,d,e){a=a instanceof goog.html.SafeUrl?a:goog.html.SafeUrl.sanitizeAssertUnchanged(a);return(b||window).open(goog.html.SafeUrl.unwrap(a),c?goog.string.Const.unwrap(c):"",d,e)};goog.html.uncheckedconversions={};goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract=function(a,b,c){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(b,c||null)}; +goog.dom.safe.openInWindow=function(a,b,c,d,e){a=a instanceof goog.html.SafeUrl?a:goog.html.SafeUrl.sanitizeAssertUnchanged(a);return(b||window).open(goog.html.SafeUrl.unwrap(a),c?goog.string.Const.unwrap(c):"",d,e)};goog.dom.safe.parseFromStringHtml=function(a,b){return a.parseFromString(goog.html.SafeHtml.unwrap(b),"text/html")}; +goog.dom.safe.createImageFromBlob=function(a){if(!/^image\/.*/g.test(a.type))throw Error("goog.dom.safe.createImageFromBlob only accepts MIME type image/.*.");var b=window.URL.createObjectURL(a);a=new Image;a.onload=function(){window.URL.revokeObjectURL(b)};a.src=b;return a};goog.html.uncheckedconversions={};goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract=function(a,b,c){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(b,c||null)}; goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract=function(a,b){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(b)}; goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract=function(a,b){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(b)}; goog.html.uncheckedconversions.safeStyleSheetFromStringKnownToSatisfyTypeContract=function(a,b){goog.asserts.assertString(goog.string.Const.unwrap(a),"must provide justification");goog.asserts.assert(!goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(a)),"must provide non-empty justification");return goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(b)}; @@ -597,7 +600,7 @@ d?63232<=d&&d in goog.events.KeyHandler.safariKey_?f=goog.events.KeyHandler.safa e,a,b),b.altKey=c,this.dispatchEvent(b))};goog.events.KeyHandler.prototype.getElement=function(){return this.element_}; goog.events.KeyHandler.prototype.attach=function(a,b){this.keyUpKey_&&this.detach();this.element_=a;this.keyPressKey_=goog.events.listen(this.element_,goog.events.EventType.KEYPRESS,this,b);this.keyDownKey_=goog.events.listen(this.element_,goog.events.EventType.KEYDOWN,this.handleKeyDown_,b,this);this.keyUpKey_=goog.events.listen(this.element_,goog.events.EventType.KEYUP,this.handleKeyup_,b,this)}; goog.events.KeyHandler.prototype.detach=function(){this.keyPressKey_&&(goog.events.unlistenByKey(this.keyPressKey_),goog.events.unlistenByKey(this.keyDownKey_),goog.events.unlistenByKey(this.keyUpKey_),this.keyUpKey_=this.keyDownKey_=this.keyPressKey_=null);this.element_=null;this.keyCode_=this.lastKey_=-1};goog.events.KeyHandler.prototype.disposeInternal=function(){goog.events.KeyHandler.superClass_.disposeInternal.call(this);this.detach()}; -goog.events.KeyEvent=function(a,b,c,d){goog.events.BrowserEvent.call(this,d);this.type=goog.events.KeyHandler.EventType.KEY;this.keyCode=a;this.charCode=b;this.repeat=c};goog.inherits(goog.events.KeyEvent,goog.events.BrowserEvent);goog.ui.ComponentUtil={};goog.ui.ComponentUtil.getMouseEventType=function(a){return a.pointerEventsEnabled()?goog.events.PointerAsMouseEventType:goog.events.EventType};goog.dom.classlist={};goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST=!1;goog.dom.classlist.get=function(a){if(goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList)return a.classList;a=a.className;return goog.isString(a)&&a.match(/\S+/g)||[]};goog.dom.classlist.set=function(a,b){a.className=b};goog.dom.classlist.contains=function(a,b){return goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList?a.classList.contains(b):goog.array.contains(goog.dom.classlist.get(a),b)}; +goog.events.KeyEvent=function(a,b,c,d){goog.events.BrowserEvent.call(this,d);this.type=goog.events.KeyHandler.EventType.KEY;this.keyCode=a;this.charCode=b;this.repeat=c};goog.inherits(goog.events.KeyEvent,goog.events.BrowserEvent);goog.ui.ComponentUtil={};goog.ui.ComponentUtil.getMouseEventType=function(a){return a.pointerEventsEnabled()?goog.events.PointerAsMouseEventType:goog.events.MouseAsMouseEventType};goog.dom.classlist={};goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST=!1;goog.dom.classlist.get=function(a){if(goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList)return a.classList;a=a.className;return goog.isString(a)&&a.match(/\S+/g)||[]};goog.dom.classlist.set=function(a,b){a.className=b};goog.dom.classlist.contains=function(a,b){return goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList?a.classList.contains(b):goog.array.contains(goog.dom.classlist.get(a),b)}; goog.dom.classlist.add=function(a,b){goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST||a.classList?a.classList.add(b):goog.dom.classlist.contains(a,b)||(a.className+=0 document.");return a.documentElement};Blockly.Xml.clearWorkspaceAndLoadFromXml=function(a,b){b.setResizesEnabled(!1);b.clear();var c=Blockly.Xml.domToWorkspace(a,b);b.setResizesEnabled(!0);return c}; Blockly.Xml.domToWorkspace=function(a,b){if(a instanceof Blockly.Workspace){var c=a;a=b;b=c;console.warn("Deprecated call to Blockly.Xml.domToWorkspace, swap the arguments.")}var d;b.RTL&&(d=b.getWidth());c=[];Blockly.Field.startCache();var e=a.childNodes.length,f=Blockly.Events.getGroup();f||Blockly.Events.setGroup(!0);b.setResizesEnabled&&b.setResizesEnabled(!1);var g=!0;try{for(var h=0;hc.viewWidth&&(a=d-c.viewLeft-c.viewWidth):d+aa.viewWidth)return b;var c=this.anchorXY_.x;this.workspace_.RTL?c-a.viewLeft-b-this.width_a.viewWidth&&(b=c-a.viewLeft-a.viewWidth):c+ba.viewHeight)return b;var c=this.anchorXY_.y;c+be&&(g=2*Math.PI-g);var h=g+Math.PI/2;h>2*Math.PI&&(h-=2*Math.PI);var k=Math.sin(h),l=Math.cos(h),n=this.getBubbleSize();h=(n.width+n.height)/Blockly.Bubble.ARROW_THICKNESS;h=Math.min(h,n.width,n.height)/4;n=1-Blockly.Bubble.ANCHOR_RADIUS/f;d=b+ @@ -1054,7 +1062,7 @@ Blockly.Connection.CAN_CONNECT:Blockly.Connection.REASON_CHECKS_FAILED}; Blockly.Connection.prototype.checkConnection_=function(a){switch(this.canConnectWithReason_(a)){case Blockly.Connection.CAN_CONNECT:break;case Blockly.Connection.REASON_SELF_CONNECTION:throw Error("Attempted to connect a block to itself.");case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:throw Error("Blocks not on same workspace.");case Blockly.Connection.REASON_WRONG_TYPE:throw Error("Attempt to connect incompatible types.");case Blockly.Connection.REASON_TARGET_NULL:throw Error("Target connection is null."); case Blockly.Connection.REASON_CHECKS_FAILED:throw Error("Connection checks failed. "+(this+" expected "+this.check_+", found "+a.check_));case Blockly.Connection.REASON_SHADOW_PARENT:throw Error("Connecting non-shadow to shadow block.");default:throw Error("Unknown connection failure: this should never happen!");}}; Blockly.Connection.prototype.canConnectToPrevious_=function(a){if(this.targetConnection||-1!=Blockly.draggingConnections_.indexOf(a))return!1;if(!a.targetConnection)return!0;a=a.targetBlock();return a.isInsertionMarker()?!a.getPreviousBlock():!1}; -Blockly.Connection.prototype.isConnectionAllowed=function(a){if(a.sourceBlock_.isInsertionMarker()||this.canConnectWithReason_(a)!=Blockly.Connection.CAN_CONNECT)return!1;switch(a.type){case Blockly.PREVIOUS_STATEMENT:return this.canConnectToPrevious_(a);case Blockly.OUTPUT_VALUE:if(a.isConnected()||this.isConnected())return!1;break;case Blockly.INPUT_VALUE:if(a.isConnected()&&!a.targetBlock().isMovable()&&!a.targetBlock().isShadow())return!1;break;case Blockly.NEXT_STATEMENT:if(a.isConnected()&& +Blockly.Connection.prototype.isConnectionAllowed=function(a){if(a.sourceBlock_.isInsertionMarker()||this.canConnectWithReason_(a)!=Blockly.Connection.CAN_CONNECT)return!1;switch(a.type){case Blockly.PREVIOUS_STATEMENT:return this.canConnectToPrevious_(a);case Blockly.OUTPUT_VALUE:if(a.isConnected()&&!a.targetBlock().isInsertionMarker()||this.isConnected())return!1;break;case Blockly.INPUT_VALUE:if(a.isConnected()&&!a.targetBlock().isMovable()&&!a.targetBlock().isShadow())return!1;break;case Blockly.NEXT_STATEMENT:if(a.isConnected()&& !this.sourceBlock_.nextConnection&&!a.targetBlock().isShadow()&&a.targetBlock().nextConnection)return!1;break;default:throw Error("Unknown connection type in isConnectionAllowed");}return-1!=Blockly.draggingConnections_.indexOf(a)?!1:!0};Blockly.Connection.prototype.connect=function(a){this.targetConnection!=a&&(this.checkConnection_(a),this.isSuperior()?this.connect_(a):a.connect_(this))}; Blockly.Connection.connectReciprocally_=function(a,b){if(!a||!b)throw Error("Cannot connect null connections.");a.targetConnection=b;b.targetConnection=a};Blockly.Connection.singleConnection_=function(a,b){for(var c=!1,d=0;d=this.length)return-1;for(var c=a.y_,d=b;0<=d&&this[d].y_==c;){if(this[d]==a)return d;d--}for(;ba.y_)c=d;else{b=d;break}}return b};Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw Error("Connection not in database.");var b=this.findConnection(a);if(-1==b)throw Error("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&&l.push(d[a]);return g=this.connections_.length)return-1;for(var c=a.y_,d=b;0<=d&&this.connections_[d].y_==c;){if(this.connections_[d]==a)return d;d--}for(;ba.y_)c=d;else{b=d;break}}return b}; +Blockly.ConnectionDB.prototype.removeConnection_=function(a){if(!a.inDB_)throw Error("Connection not in database.");var b=this.findConnection(a);if(-1==b)throw Error("Unable to find connection in connectionDB.");a.inDB_=!1;this.connections_.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&&l.push(d[a]);return gc-Blockly.CURRENT_CONNECTION_PREFERENCE)}if(this.localConnection_||this.closestConnection_)console.error("Only one of localConnection_ and closestConnection_ was set."); +else return!0}else return!(!this.localConnection_||!this.closestConnection_);console.error("Returning true from shouldUpdatePreviews, but it's not clear why.");return!0};Blockly.InsertionMarkerManager.prototype.getCandidate_=function(a){for(var b=this.getStartRadius_(),c=null,d=null,e=0;e(this.flyout_?Blockly.FLYOUT_DRAG_RADIUS:Blockly.DRAG_RADIUS)}; -Blockly.Gesture.prototype.updateIsDraggingFromFlyout_=function(){return this.targetBlock_.disabled?!1:!this.flyout_.isScrollable()||this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)?(this.startWorkspace_=this.flyout_.targetWorkspace_,this.startWorkspace_.updateScreenCalculationsIfScrolled(),Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.startBlock_=null,this.targetBlock_=this.flyout_.createBlock(this.targetBlock_),this.targetBlock_.select(),!0):!1}; +Blockly.Gesture.prototype.updateIsDraggingFromFlyout_=function(){return this.flyout_.isBlockCreatable_(this.targetBlock_)?!this.flyout_.isScrollable()||this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)?(this.startWorkspace_=this.flyout_.targetWorkspace_,this.startWorkspace_.updateScreenCalculationsIfScrolled(),Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.startBlock_=null,this.targetBlock_=this.flyout_.createBlock(this.targetBlock_),this.targetBlock_.select(),!0):!1:!1}; Blockly.Gesture.prototype.updateIsDraggingBubble_=function(){if(!this.startBubble_)return!1;this.isDraggingBubble_=!0;this.startDraggingBubble_();return!0};Blockly.Gesture.prototype.updateIsDraggingBlock_=function(){if(!this.targetBlock_)return!1;this.flyout_?this.isDraggingBlock_=this.updateIsDraggingFromFlyout_():this.targetBlock_.isMovable()&&(this.isDraggingBlock_=!0);return this.isDraggingBlock_?(this.startDraggingBlock_(),!0):!1}; Blockly.Gesture.prototype.updateIsDraggingWorkspace_=function(){if(this.flyout_?this.flyout_.isScrollable():this.startWorkspace_&&this.startWorkspace_.isDraggable())this.workspaceDragger_=this.flyout_?new Blockly.FlyoutDragger(this.flyout_):new Blockly.WorkspaceDragger(this.startWorkspace_),this.isDraggingWorkspace_=!0,this.workspaceDragger_.startDrag()}; Blockly.Gesture.prototype.updateIsDragging_=function(){if(this.calledUpdateIsDragging_)throw Error("updateIsDragging_ should only be called once per gesture.");this.calledUpdateIsDragging_=!0;this.updateIsDraggingBubble_()||this.updateIsDraggingBlock_()||this.updateIsDraggingWorkspace_()}; @@ -1164,12 +1189,15 @@ Blockly.Gesture.prototype.doBlockClick_=function(){this.flyout_&&this.flyout_.au Blockly.Gesture.prototype.bringBlockToFront_=function(){this.targetBlock_&&!this.flyout_&&this.targetBlock_.bringToFront()};Blockly.Gesture.prototype.setStartField=function(a){if(this.hasStarted_)throw Error("Tried to call gesture.setStartField, but the gesture had already been started.");this.startField_||(this.startField_=a)};Blockly.Gesture.prototype.setStartBubble=function(a){this.startBubble_||(this.startBubble_=a)}; Blockly.Gesture.prototype.setStartBlock=function(a){this.startBlock_||this.startBubble_||(this.startBlock_=a,a.isInFlyout&&a!=a.getRootBlock()?this.setTargetBlock_(a.getRootBlock()):this.setTargetBlock_(a))};Blockly.Gesture.prototype.setTargetBlock_=function(a){a.isShadow()?this.setTargetBlock_(a.getParent()):this.targetBlock_=a};Blockly.Gesture.prototype.setStartWorkspace_=function(a){this.startWorkspace_||(this.startWorkspace_=a)}; Blockly.Gesture.prototype.setStartFlyout_=function(a){this.flyout_||(this.flyout_=a)};Blockly.Gesture.prototype.isBubbleClick_=function(){return!!this.startBubble_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isBlockClick_=function(){return!!this.startBlock_&&!this.hasExceededDragRadius_&&!this.isFieldClick_()};Blockly.Gesture.prototype.isFieldClick_=function(){return(this.startField_?this.startField_.isCurrentlyEditable():!1)&&!this.hasExceededDragRadius_&&(!this.flyout_||!this.flyout_.autoClose)}; -Blockly.Gesture.prototype.isWorkspaceClick_=function(){return!this.startBlock_&&!this.startBubble_&&!this.startField_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_||this.isDraggingBlock_||this.isDraggingBubble_};Blockly.Gesture.prototype.hasStarted=function(){return this.hasStarted_};Blockly.Grid=function(a,b){this.gridPattern_=a;this.spacing_=b.spacing;this.length_=b.length;this.line2_=(this.line1_=a.firstChild)&&this.line1_.nextSibling;this.snapToGrid_=b.snap};Blockly.Grid.prototype.scale_=1;Blockly.Grid.prototype.dispose=function(){this.gridPattern_=null};Blockly.Grid.prototype.shouldSnap=function(){return this.snapToGrid_};Blockly.Grid.prototype.getSpacing=function(){return this.spacing_};Blockly.Grid.prototype.getPatternId=function(){return this.gridPattern_.id}; +Blockly.Gesture.prototype.isWorkspaceClick_=function(){return!this.startBlock_&&!this.startBubble_&&!this.startField_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_||this.isDraggingBlock_||this.isDraggingBubble_};Blockly.Gesture.prototype.hasStarted=function(){return this.hasStarted_};Blockly.Gesture.prototype.getInsertionMarkers=function(){return this.blockDragger_?this.blockDragger_.getInsertionMarkers():[]};Blockly.Grid=function(a,b){this.gridPattern_=a;this.spacing_=b.spacing;this.length_=b.length;this.line2_=(this.line1_=a.firstChild)&&this.line1_.nextSibling;this.snapToGrid_=b.snap};Blockly.Grid.prototype.scale_=1;Blockly.Grid.prototype.dispose=function(){this.gridPattern_=null};Blockly.Grid.prototype.shouldSnap=function(){return this.snapToGrid_};Blockly.Grid.prototype.getSpacing=function(){return this.spacing_};Blockly.Grid.prototype.getPatternId=function(){return this.gridPattern_.id}; Blockly.Grid.prototype.update=function(a){this.scale_=a;var b=this.spacing_*a||100;this.gridPattern_.setAttribute("width",b);this.gridPattern_.setAttribute("height",b);b=Math.floor(this.spacing_/2)+.5;var c=b-this.length_/2,d=b+this.length_/2;b*=a;c*=a;d*=a;this.setLineAttributes_(this.line1_,a,c,d,b,b);this.setLineAttributes_(this.line2_,a,b,b,c,d)}; Blockly.Grid.prototype.setLineAttributes_=function(a,b,c,d,e,f){a&&(a.setAttribute("stroke-width",b),a.setAttribute("x1",c),a.setAttribute("y1",e),a.setAttribute("x2",d),a.setAttribute("y2",f))};Blockly.Grid.prototype.moveTo=function(a,b){this.gridPattern_.setAttribute("x",a);this.gridPattern_.setAttribute("y",b);(goog.userAgent.IE||goog.userAgent.EDGE)&&this.update(this.scale_)}; -Blockly.Grid.createDom=function(a,b,c){a=Blockly.utils.createSvgElement("pattern",{id:"blocklyGridPattern"+a,patternUnits:"userSpaceOnUse"},c);0this.previousScale_){var c=b-this.previousScale_;c=0Object.keys(this.cachedPoints_).length&&(this.cachedPoints_={},this.previousScale_=0)}; -Blockly.TouchGesture.prototype.getTouchPoint=function(a){return this.startWorkspace_?new goog.math.Coordinate(a.pageX?a.pageX:a.changedTouches[0].pageX,a.pageY?a.pageY:a.changedTouches[0].pageY):null};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.utils.createSvgElement("g",{"class":"blocklyTrash"},null);var a=String(Math.random()).substring(2);var b=Blockly.utils.createSvgElement("clipPath",{id:"blocklyTrashBodyClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.BODY_HEIGHT_,y:this.LID_HEIGHT_},b);Blockly.utils.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.utils.createSvgElement("clipPath",{id:"blocklyTrashLidClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.LID_HEIGHT_},b);this.svgLid_=Blockly.utils.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.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.TouchGesture.prototype.getTouchPoint=function(a){return this.startWorkspace_?new goog.math.Coordinate(a.pageX?a.pageX:a.changedTouches[0].pageX,a.pageY?a.pageY:a.changedTouches[0].pageY):null};Blockly.Trashcan=function(a){this.workspace_=a;this.hasBlocks_=!1;this.contents_=[];0>=this.workspace_.options.maxTrashcanContents||(a={scrollbars:!0,disabledPatternId:this.workspace_.options.disabledPatternId,parentWorkspace:this.workspace_,RTL:this.workspace_.RTL,oneBasedIndex:this.workspace_.options.oneBasedIndex},this.workspace_.horizontalLayout?(a.toolboxPosition=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_TOP?Blockly.TOOLBOX_AT_BOTTOM:Blockly.TOOLBOX_AT_TOP,this.flyout_=new Blockly.HorizontalFlyout(a)): +(a.toolboxPosition=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?Blockly.TOOLBOX_AT_LEFT:Blockly.TOOLBOX_AT_RIGHT,this.flyout_=new Blockly.VerticalFlyout(a)),this.workspace_.addChangeListener(this.onDelete_()))};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.HAS_BLOCKS_LID_ANGLE=.1;Blockly.Trashcan.prototype.isOpen=!1;Blockly.Trashcan.prototype.minOpenness_=0;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.utils.createSvgElement("g",{"class":"blocklyTrash"},null);var a=String(Math.random()).substring(2);var b=Blockly.utils.createSvgElement("clipPath",{id:"blocklyTrashBodyClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.BODY_HEIGHT_,y:this.LID_HEIGHT_},b);var c=Blockly.utils.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_);c.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",this.workspace_.options.pathToMedia+Blockly.SPRITE.url);b=Blockly.utils.createSvgElement("clipPath",{id:"blocklyTrashLidClipPath"+a},this.svgGroup_);Blockly.utils.createSvgElement("rect",{width:this.WIDTH_,height:this.LID_HEIGHT_},b);this.svgLid_=Blockly.utils.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.bindEventWithChecks_(this.svgGroup_,"mouseup",this,this.click);Blockly.bindEvent_(c,"mouseover",this,this.mouseOver_);Blockly.bindEvent_(c,"mouseout",this,this.mouseOut_);this.animateLid_();return this.svgGroup_}; +Blockly.Trashcan.prototype.init=function(a){0this.lidOpen_&&(this.lidTask_=setTimeout(this.animateLid_.bind(this),20))};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.VariableModel=function(a,b,c,d){this.workspace=a;this.name=b;this.type=c||"";this.id_=d||Blockly.utils.genUid();Blockly.Events.fire(new Blockly.Events.VarCreate(this))};Blockly.VariableModel.prototype.getId=function(){return this.id_};Blockly.VariableModel.compareByName=function(a,b){var c=a.name.toLowerCase(),d=b.name.toLowerCase();return cthis.minOpenness_&&1>this.lidOpen_&&(this.lidTask_=setTimeout(this.animateLid_.bind(this),20))}; +Blockly.Trashcan.prototype.setLidAngle_=function(a){var b=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT||this.workspace_.horizontalLayout&&this.workspace_.RTL;this.svgLid_.setAttribute("transform","rotate("+(b?-a:a)+","+(b?4:this.WIDTH_-4)+","+(this.LID_HEIGHT_-2)+")")};Blockly.Trashcan.prototype.close=function(){this.setOpen_(!1)};Blockly.Trashcan.prototype.click=function(){if(this.hasBlocks_){for(var a=[],b=0,c;c=this.contents_[b];b++)a[b]=Blockly.Xml.textToDom(c).firstChild;this.flyout_.show(a)}}; +Blockly.Trashcan.prototype.mouseOver_=function(){this.hasBlocks_&&this.setOpen_(!0)};Blockly.Trashcan.prototype.mouseOut_=function(){this.setOpen_(!1)}; +Blockly.Trashcan.prototype.onDelete_=function(){var a=this;return function(b){0>=a.workspace_.options.maxTrashcanContents||b.type!=Blockly.Events.BLOCK_DELETE||(b=a.cleanBlockXML_(b.oldXml),-1==a.contents_.indexOf(b)&&(a.contents_.unshift(b),a.contents_.length>a.workspace_.options.maxTrashcanContents&&a.contents_.splice(a.workspace_.options.maxTrashcanContents,a.contents_.length-a.workspace_.options.maxTrashcanContents),a.hasBlocks_=!0,a.minOpenness_=a.HAS_BLOCKS_LID_ANGLE,a.setLidAngle_(45*a.minOpenness_)))}}; +Blockly.Trashcan.prototype.cleanBlockXML_=function(a){for(var b=a=a.cloneNode(!0);b;){b.removeAttribute&&(b.removeAttribute("x"),b.removeAttribute("y"),b.removeAttribute("id"));var c=b.firstChild||b.nextSibling;if(!c)for(c=b.parentNode;c;){if(c.nextSibling){c=c.nextSibling;break}c=c.parentNode}b=c}return""+Blockly.Xml.domToText(a)+""};Blockly.VariableModel=function(a,b,c,d){this.workspace=a;this.name=b;this.type=c||"";this.id_=d||Blockly.utils.genUid();Blockly.Events.fire(new Blockly.Events.VarCreate(this))};Blockly.VariableModel.prototype.getId=function(){return this.id_};Blockly.VariableModel.compareByName=function(a,b){var c=a.name.toLowerCase(),d=b.name.toLowerCase();return c=this.remainingCapacity()||(this.currentGesture_&&this.currentGesture_.cancel(),"comment"==a.tagName.toLowerCase()?this.pasteWorkspaceComment_(a):this.pasteBlock_(a))}; Blockly.WorkspaceSvg.prototype.pasteBlock_=function(a){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(!1),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){var k=b.getConnections_(!1);f=0;for(var l;l=k[f];f++)if(l.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.BlockCreate(b));b.select()}; @@ -1339,8 +1370,9 @@ for(var b=0,c;c=a[b];b++)c.render();this.rootBlock_.setMovable(!1);this.rootBloc this.workspace_.addChangeListener(this.workspaceChanged_.bind(this));this.updateColour()}else this.svgDialog_=null,this.workspace_.dispose(),this.rootBlock_=this.workspace_=null,this.bubble_.dispose(),this.bubble_=null,this.workspaceHeight_=this.workspaceWidth_=0,this.sourceListener_&&(this.block_.workspace.removeChangeListener(this.sourceListener_),this.sourceListener_=null)}; Blockly.Mutator.prototype.workspaceChanged_=function(){if(!this.workspace_.isDragging())for(var a=this.workspace_.getTopBlocks(!1),b=0,c;c=a[b];b++){var d=c.getRelativeToSurfaceXY(),e=c.getHeightWidth();20>d.y+e.height&&c.moveBy(0,20-e.height-d.y)}if(this.rootBlock_.workspace==this.workspace_){Blockly.Events.setGroup(!0);c=this.block_;a=(a=c.mutationToDom())&&Blockly.Xml.domToText(a);b=c.rendered;c.rendered=!1;c.compose(this.rootBlock_);c.rendered=b;c.initSvg();b=(b=c.mutationToDom())&&Blockly.Xml.domToText(b); if(a!=b){Blockly.Events.fire(new Blockly.Events.BlockChange(c,"mutation",null,a,b));var f=Blockly.Events.getGroup();setTimeout(function(){Blockly.Events.setGroup(f);c.bumpNeighbours_();Blockly.Events.setGroup(!1)},Blockly.BUMP_DELAY)}c.rendered&&c.render();this.workspace_.isDragging()||this.resizeBubble_();Blockly.Events.setGroup(!1)}};Blockly.Mutator.prototype.getFlyoutMetrics_=function(){return{viewHeight:this.workspaceHeight_,viewWidth:this.workspaceWidth_,absoluteTop:0,absoluteLeft:0}}; -Blockly.Mutator.prototype.dispose=function(){this.block_.mutator=null;Blockly.Icon.prototype.dispose.call(this)};Blockly.Mutator.reconnect=function(a,b,c){if(!a||!a.getSourceBlock().workspace)return!1;c=b.getInput(c).connection;var d=a.targetBlock();return d&&d!=b||c.targetConnection==a?!1:(c.isConnected()&&c.disconnect(),c.connect(a),!0)}; -Blockly.Mutator.findParentWs=function(a){var b=null;if(a&&a.options){var c=a.options.parentWorkspace;a.isFlyout?c&&c.options&&(b=c.options.parentWorkspace):c&&(b=c)}return b};goog.global.Blockly||(goog.global.Blockly={});goog.global.Blockly.Mutator||(goog.global.Blockly.Mutator={});goog.global.Blockly.Mutator.reconnect=Blockly.Mutator.reconnect;Blockly.Extensions={};Blockly.Extensions.ALL_={};Blockly.Extensions.register=function(a,b){if("string"!=typeof a||""==a.trim())throw Error('Error: Invalid extension name "'+a+'"');if(Blockly.Extensions.ALL_[a])throw Error('Error: Extension "'+a+'" is already registered.');if("function"!=typeof b)throw Error('Error: Extension "'+a+'" must be a function');Blockly.Extensions.ALL_[a]=b}; +Blockly.Mutator.prototype.dispose=function(){this.block_.mutator=null;Blockly.Icon.prototype.dispose.call(this)};Blockly.Mutator.prototype.updateBlockStyle=function(){var a=this.workspace_;if(a&&a.getAllBlocks()){for(var b=a.getAllBlocks(),c=0;c=c)this.hue_=c,this.colour_=Blockly.hueToRgb(c);else if("string"==typeof b&&/^#[0-9a-fA-F]{6}$/.test(b))this.colour_=b,this.hue_=null;else throw c='Invalid colour: "'+b+'"',a!=b&&(c+=' (from "'+a+'")'),c;}; +Blockly.Block.prototype.setStyle=function(a){var b=Blockly.getTheme();if(!b)throw Error("Trying to set block style to "+a+" before theme was defined via Blockly.setTheme().");b=b.getBlockStyle(a);this.styleName_=a;if(b)this.colourSecondary_=b.colourSecondary,this.colourTertiary_=b.colourTertiary,this.hat=b.hat,this.setColour(b.colourPrimary);else throw Error("Invalid style name: "+a);}; Blockly.Block.prototype.setOnChange=function(a){if(a&&"function"!=typeof a)throw Error("onchange must be a function.");this.onchangeWrapper_&&this.workspace.removeChangeListener(this.onchangeWrapper_);if(this.onchange=a)this.onchangeWrapper_=a.bind(this),this.workspace.addChangeListener(this.onchangeWrapper_)};Blockly.Block.prototype.getField=function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)if(e.name===a)return e;return null}; Blockly.Block.prototype.getVars=function(){for(var a=[],b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)e.referencesVariables()&&a.push(e.getValue());return a};Blockly.Block.prototype.getVarModels=function(){for(var a=[],b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)e.referencesVariables()&&(e=this.workspace.getVariableById(e.getValue()))&&a.push(e);return a}; Blockly.Block.prototype.updateVarName=function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)e.referencesVariables()&&a.getId()==e.getValue()&&e.setText(a.name)};Blockly.Block.prototype.renameVarById=function(a,b){for(var c=0,d;d=this.inputList[c];c++)for(var e=0,f;f=d.fieldRow[e];e++)f.referencesVariables()&&a==f.getValue()&&f.setValue(b)};Blockly.Block.prototype.getFieldValue=function(a){return(a=this.getField(a))?a.getValue():null}; @@ -1406,9 +1441,10 @@ Blockly.Block.prototype.setDisabled=function(a){this.disabled!=a&&(Blockly.Event Blockly.Block.prototype.setCollapsed=function(a){this.collapsed_!=a&&(Blockly.Events.fire(new Blockly.Events.BlockChange(this,"collapsed",null,this.collapsed_,a)),this.collapsed_=a)}; Blockly.Block.prototype.toString=function(a,b){var c=[],d=b||"?";if(this.collapsed_)c.push(this.getInput("_TEMP_COLLAPSED_INPUT").fieldRow[0].text_);else for(var e=0,f;f=this.inputList[e];e++){for(var g=0,h;h=f.fieldRow[g];g++)h instanceof Blockly.FieldDropdown&&!h.getValue()?c.push(d):c.push(h.getText());f.connection&&((f=f.connection.targetBlock())?c.push(f.toString(void 0,b)):c.push(d))}c=c.join(" ").trim()||"???";a&&c.length>a&&(c=c.substring(0,a-3)+"...");return c}; 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){var b=a.type?'Block "'+a.type+'": ':"";if(a.output&&a.previousStatement)throw Error(b+"Must not have both an output and a previousStatement.");this.jsonInitColour_(a,b);for(var c=0;void 0!==a["message"+c];)this.interpolate_(a["message"+c],a["args"+c]||[],a["lastDummyAlign"+c]),c++;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&&(c=a.tooltip,c=Blockly.utils.replaceMessageReferences(c),this.setTooltip(c));void 0!==a.enableContextMenu&&(c=a.enableContextMenu,this.contextMenu=!!c);void 0!==a.helpUrl&&(c=a.helpUrl,c=Blockly.utils.replaceMessageReferences(c),this.setHelpUrl(c));"string"==typeof a.extensions&&(console.warn(b+"JSON attribute 'extensions' should be an array of strings. Found raw string in JSON for '"+a.type+"' block."),a.extensions= -[a.extensions]);void 0!==a.mutator&&Blockly.Extensions.apply(a.mutator,this,!0);if(Array.isArray(a.extensions))for(a=a.extensions,b=0;b=h||h>b.length)throw Error('Block "'+this.type+'": Message index %'+h+" out of range.");if(e[h])throw Error('Block "'+this.type+'": Message index %'+h+" duplicated.");e[h]=!0;f++;a.push(b[h-1])}else(h=h.trim())&&a.push(h)}if(f!=b.length)throw Error('Block "'+this.type+'": Message does not reference all '+b.length+" arg(s)."); a.length&&("string"==typeof a[a.length-1]||Blockly.utils.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;g90-b||a>-90-b&&a<-90+b?!0:!1}; Blockly.HorizontalFlyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.top;a=a.height;if(this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP)return new goog.math.Rect(-1E9,b-1E9,2E9,1E9+a);if(this.toolboxPosition_==Blockly.TOOLBOX_AT_BOTTOM)return new goog.math.Rect(-1E9,b,2E9,1E9+a)}; -Blockly.HorizontalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace_.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)a=Math.max(a,d.getHeightWidth().height);a+=1.5*this.MARGIN;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.height_!=a){for(c=0;d=b[c];c++)d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);this.height_=a;this.targetWorkspace_.resize()}};Blockly.VerticalFlyout=function(a){a.getMetrics=this.getMetrics_.bind(this);a.setMetrics=this.setMetrics_.bind(this);Blockly.VerticalFlyout.superClass_.constructor.call(this,a);this.horizontalLayout_=!1};goog.inherits(Blockly.VerticalFlyout,Blockly.Flyout); +Blockly.HorizontalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace_.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)a=Math.max(a,d.getHeightWidth().height);a+=1.5*this.MARGIN;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.height_!=a){for(c=0;d=b[c];c++)d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);this.height_=a;this.position()}};Blockly.VerticalFlyout=function(a){a.getMetrics=this.getMetrics_.bind(this);a.setMetrics=this.setMetrics_.bind(this);Blockly.VerticalFlyout.superClass_.constructor.call(this,a);this.horizontalLayout_=!1};goog.inherits(Blockly.VerticalFlyout,Blockly.Flyout); Blockly.VerticalFlyout.prototype.getMetrics_=function(){if(!this.isVisible())return null;try{var a=this.workspace_.getCanvas().getBBox()}catch(e){a={height:0,y:0,width:0,x:0}}var b=this.SCROLLBAR_PADDING,c=this.height_-2*this.SCROLLBAR_PADDING,d=this.width_;this.RTL||(d-=this.SCROLLBAR_PADDING);return{viewHeight:c,viewWidth:d,contentHeight:a.height*this.workspace_.scale+2*this.MARGIN,contentWidth:a.width*this.workspace_.scale+2*this.MARGIN,viewTop:-this.workspace_.scrollY+a.y,viewLeft:-this.workspace_.scrollX, contentTop:a.y,contentLeft:a.x,absoluteTop:b,absoluteLeft:0}};Blockly.VerticalFlyout.prototype.setMetrics_=function(a){var b=this.getMetrics_();b&&("number"==typeof a.y&&(this.workspace_.scrollY=-b.contentHeight*a.y),this.workspace_.translate(this.workspace_.scrollX+b.absoluteLeft,this.workspace_.scrollY+b.absoluteTop))}; Blockly.VerticalFlyout.prototype.position=function(){if(this.isVisible()){var a=this.targetWorkspace_.getMetrics();if(a){this.height_=a.viewHeight;this.setBackgroundPath_(this.width_-this.CORNER_RADIUS,a.viewHeight-2*this.CORNER_RADIUS);var b=a.absoluteTop,c=a.absoluteLeft;this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT&&(this.leftEdge_=c+=a.viewWidth-this.width_);this.positionAt_(this.width_,this.height_,c,b)}}}; @@ -1650,7 +1688,7 @@ f.button.height+b[e])};Blockly.VerticalFlyout.prototype.isDragTowardWorkspace=fu Blockly.VerticalFlyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.left;a=a.width;if(this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT)return new goog.math.Rect(b-1E9,-1E9,1E9+a,2E9);if(goog.userAgent.GECKO&&this.targetWorkspace_&&this.targetWorkspace_.isMutator){var c=this.targetWorkspace_.svgGroup_.getBoundingClientRect().x;10>Math.abs(c-b)&&(b+=this.leftEdge_*this.targetWorkspace_.options.parentWorkspace.scale)}return new goog.math.Rect(b, -1E9,1E9+a,2E9)}; Blockly.VerticalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace_.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++){var e=d.getHeightWidth().width;d.outputConnection&&(e-=Blockly.BlockSvg.TAB_WIDTH);a=Math.max(a,e)}for(c=0;d=this.buttons_[c];c++)a=Math.max(a,d.width);a+=1.5*this.MARGIN+Blockly.BlockSvg.TAB_WIDTH;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.width_!=a){for(c=0;d=b[c];c++)this.RTL&&(e=d.getRelativeToSurfaceXY().x, -d.moveBy(a/this.workspace_.scale-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH-e,0)),d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);if(this.RTL)for(c=0;d=this.buttons_[c];c++)b=d.getPosition().y,d.moveTo(a/this.workspace_.scale-d.width-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH,b);this.width_=a;this.targetWorkspace_.resize()}};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"}; +d.moveBy(a/this.workspace_.scale-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH-e,0)),d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);if(this.RTL)for(c=0;d=this.buttons_[c];c++)b=d.getPosition().y,d.moveTo(a/this.workspace_.scale-d.width-this.MARGIN-Blockly.BlockSvg.TAB_WIDTH,b);this.width_=a;this.position()}};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=document.createElement("div");this.HtmlDiv.className="blocklyToolboxDiv";this.HtmlDiv.setAttribute("dir",a.RTL?"RTL":"LTR");b.parentNode.insertBefore(this.HtmlDiv,b);Blockly.bindEventWithChecks_(this.HtmlDiv,"mousedown",this,function(a){Blockly.utils.isRightButton(a)||a.target==this.HtmlDiv?Blockly.hideChaff(!1):Blockly.hideChaff(!0);Blockly.Touch.clearTouchIdentifier()},!1,!0);b={disabledPatternId:a.options.disabledPatternId, parentWorkspace:a,RTL:a.RTL,oneBasedIndex:a.options.oneBasedIndex,horizontalLayout:a.horizontalLayout,toolboxPosition:a.options.toolboxPosition};this.flyout_=null;this.flyout_=a.horizontalLayout?new Blockly.HorizontalFlyout(b):new Blockly.VerticalFlyout(b);Blockly.utils.insertAfter(this.flyout_.createDom("svg"),this.workspace_.getParentSvg());this.flyout_.init(a);this.config_.cleardotPath=a.options.pathToMedia+"1x1.gif";this.config_.cssCollapsedFolderIcon="blocklyTreeIconClosed"+(a.RTL?"Rtl":"Ltr"); @@ -1658,10 +1696,13 @@ this.tree_=b=new Blockly.Toolbox.TreeControl(this,this.config_);b.setShowRootNod Blockly.Toolbox.prototype.getHeight=function(){return this.height}; Blockly.Toolbox.prototype.position=function(){var a=this.HtmlDiv;if(a){var b=this.workspace_.getParentSvg();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 Error("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=Blockly.utils.replaceMessageReferences(g.getAttribute("name"));var h=this.tree_.createNode(e);h.blocks=[];b.add(h);var k=g.getAttribute("custom");k?h.blocks=k:(k=this.syncTrees_(g,h,c))&&(d=k);k=Blockly.utils.replaceMessageReferences(g.getAttribute("colour"));null===k||""===k?h.hexColour="":/^#[0-9a-fA-F]{6}$/.test(k)?(h.hexColour=k, -this.hasColours_=!0):"number"===typeof k||"string"===typeof k&&!isNaN(Number(k))?(h.hexColour=Blockly.hueToRgb(Number(k)),this.hasColours_=!0):(h.hexColour="",console.warn('Toolbox category "'+e+'" has unrecognized colour attribute: '+k));"true"==g.getAttribute("expanded")?(h.blocks.length&&(d=h),h.setExpanded(!0)):h.setExpanded(!1);e=g;break;case "SEP":e&&("CATEGORY"==e.tagName.toUpperCase()?b.add(new Blockly.Toolbox.TreeSeparator(this.treeSeparatorConfig_)):(g=parseFloat(g.getAttribute("gap")), -!isNaN(g)&&e&&e.setAttribute("gap",g)));break;case "BLOCK":case "SHADOW":case "LABEL":case "BUTTON":b.blocks.push(g),e=g}return d};Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren(!1);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.addStyle=function(a){Blockly.utils.addClass(this.HtmlDiv,a)};Blockly.Toolbox.prototype.removeStyle=function(a){Blockly.utils.removeClass(this.HtmlDiv,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=Blockly.utils.replaceMessageReferences(g.getAttribute("name"));var h=this.tree_.createNode(e);h.blocks=[];b.add(h);var k=g.getAttribute("custom");k?h.blocks=k:(k=this.syncTrees_(g,h,c))&&(d=k);k=g.getAttribute("style");var l=g.getAttribute("colour");l&&k?(h.hexColour="",console.warn('Toolbox category "'+e+'" can not have both a style and a colour')): +k?this.setColourFromStyle_(k,h,e):this.setColour_(l,h,e);"true"==g.getAttribute("expanded")?(h.blocks.length&&(d=h),h.setExpanded(!0)):h.setExpanded(!1);e=g;break;case "SEP":e&&("CATEGORY"==e.tagName.toUpperCase()?b.add(new Blockly.Toolbox.TreeSeparator(this.treeSeparatorConfig_)):(g=parseFloat(g.getAttribute("gap")),!isNaN(g)&&e&&e.setAttribute("gap",g)));break;case "BLOCK":case "SHADOW":case "LABEL":case "BUTTON":b.blocks.push(g),e=g}return d}; +Blockly.Toolbox.prototype.setColour_=function(a,b,c){a=Blockly.utils.replaceMessageReferences(a);null===a||""===a?b.hexColour="":/^#[0-9a-fA-F]{6}$/.test(a)?(b.hexColour=a,this.hasColours_=!0):"number"===typeof a||"string"===typeof a&&!isNaN(Number(a))?(b.hexColour=Blockly.hueToRgb(Number(a)),this.hasColours_=!0):(b.hexColour="",console.warn('Toolbox category "'+c+'" has unrecognized colour attribute: '+a))}; +Blockly.Toolbox.prototype.setColourFromStyle_=function(a,b,c){if((b.styleName=a)&&Blockly.getTheme()){var d=Blockly.getTheme().getCategoryStyle(a);d&&d.colour?this.setColour_(d.colour,b,c):console.warn('Style "'+a+'" must exist and contain a colour value')}};Blockly.Toolbox.prototype.updateColourFromTheme_=function(a){if(a=a||this.tree_){a=a.getChildren(!1);for(var b=0,c;c=a[b];b++)c.styleName&&(this.setColourFromStyle_(c.styleName,c,""),this.addColour_()),this.updateColourFromTheme_(c)}}; +Blockly.Toolbox.prototype.updateColourFromTheme=function(){var a=this.tree_;a&&(this.updateColourFromTheme_(a),this.updateSelectedItemColour_(a))};Blockly.Toolbox.prototype.updateSelectedItemColour_=function(a){var b=a.selectedItem_;if(b){var c=b.hexColour||"#57e";b.getRowElement().style.backgroundColor=c;a.toolbox_.addColour_(b)}}; +Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren(!1);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.addStyle=function(a){Blockly.utils.addClass(this.HtmlDiv,a)}; +Blockly.Toolbox.prototype.removeStyle=function(a){Blockly.utils.removeClass(this.HtmlDiv,a)}; 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.bindEventWithChecks_(a,goog.events.EventType.TOUCHEND,this,this.handleTouchEvent_)}};Blockly.Toolbox.TreeControl.prototype.handleTouchEvent_=function(a){var b=this.getNodeFromEvent_(a);b&&a.type===goog.events.EventType.TOUCHEND&&setTimeout(function(){b.onClick_(a)},1)}; @@ -1686,39 +1727,41 @@ Blockly.Css.CONTENT=[".blocklySvg {","background-color: #fff;","outline: none;", "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;","}",".blocklyTransparentBackground {","opacity: 0;","}",".blocklyMainWorkspaceScrollbar {","z-index: 20;","}",".blocklyFlyoutScrollbar {","z-index: 30;", "}",".blocklyScrollbarHorizontal, .blocklyScrollbarVertical {","position: absolute;","outline: none;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarHandle {","fill: #ccc;","}",".blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,",".blocklyScrollbarHandle:hover {","fill: #bbb;","}",".blocklyZoom>image, .blocklyZoom>svg>image {","opacity: .4;","}",".blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {","opacity: .6;","}",".blocklyZoom>image:active, .blocklyZoom>svg>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;","pointer-events: none;","}",".blocklyContextMenu {","border-radius: 4px;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(<<>>/sprites.png) no-repeat -48px -16px !important;","}",".blocklyToolboxDiv {","background-color: #ddd;","overflow-x: visible;","overflow-y: auto;","position: absolute;", -"user-select: none;","-moz-user-select: none;","-ms-user-select: none;","-webkit-user-select: none;","z-index: 70;","-webkit-tap-highlight-color: transparent;","}",".blocklyTreeRoot {","padding: 4px 0;","}",".blocklyTreeRoot:focus {","outline: none;","}",".blocklyTreeRow {","height: 22px;","line-height: 22px;","margin-bottom: 3px;","padding-right: 8px;","white-space: nowrap;","}",".blocklyHorizontalTree {","float: left;","margin: 1px 5px 8px 0;","}",".blocklyHorizontalTreeRtl {","float: right;","margin: 1px 0 8px 5px;", -"}",'.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',"margin-left: 8px;","}",".blocklyTreeRow:not(.blocklyTreeSelected):hover {","background-color: #e4e4e4;","}",".blocklyTreeSeparator {","border-bottom: solid #e5e5e5 1px;","height: 0;","margin: 5px 0;","}",".blocklyTreeSeparatorHorizontal {","border-right: solid #e5e5e5 1px;","width: 0;","padding: 5px 0;","margin: 0 5px;","}",".blocklyTreeIcon {","background-image: url(<<>>/sprites.png);","height: 16px;","vertical-align: middle;","width: 16px;", -"}",".blocklyTreeIconClosedLtr {","background-position: -32px -1px;","}",".blocklyTreeIconClosedRtl {","background-position: 0 -1px;","}",".blocklyTreeIconOpen {","background-position: -16px -1px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedLtr {","background-position: -32px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedRtl {","background-position: 0 -17px;","}",".blocklyTreeSelected>.blocklyTreeIconOpen {","background-position: -16px -17px;","}",".blocklyTreeIconNone,",".blocklyTreeSelected>.blocklyTreeIconNone {", -"background-position: -48px -1px;","}",".blocklyTreeLabel {","cursor: default;","font-family: sans-serif;","font-size: 16px;","padding: 0 3px;","vertical-align: middle;","}",".blocklyToolboxDelete .blocklyTreeLabel {",'cursor: url("<<>>/handdelete.cur"), auto;',"}",".blocklyTreeSelected .blocklyTreeLabel {","color: #fff;","}",".blocklyColourTable {","border-collapse: collapse;","}",".blocklyColourTable>tr>td {","border: 1px solid #666;","padding: 0;","}",".blocklyColourTable>tr>td>div {","border: 1px solid #666;", -"height: 13px;","width: 15px;","}",".blocklyColourTable>tr>td>div:hover {","border: 1px solid #fff;","}",".blocklyColourSelected, .blocklyColourSelected:hover {","border: 1px solid #000 !important;","}",".blocklyWidgetDiv .goog-menu {","background: #fff;","border-color: #ccc #666 #666 #ccc;","border-style: solid;","border-width: 1px;","cursor: default;","font: normal 13px Arial, sans-serif;","margin: 0;","outline: none;","padding: 4px 0;","position: absolute;","overflow-y: auto;","overflow-x: hidden;", -"max-height: 100%;","z-index: 20000;","}",".blocklyWidgetDiv .goog-menuitem {","color: #000;","font: normal 13px Arial, sans-serif;","list-style: none;","margin: 0;","padding: 4px 7em 4px 28px;","white-space: nowrap;","}",".blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl {","padding-left: 7em;","padding-right: 28px;","}",".blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,",".blocklyWidgetDiv .goog-menu-noicon .goog-menuitem {","padding-left: 12px;","}",".blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem {", -"padding-right: 20px;","}",".blocklyWidgetDiv .goog-menuitem-content {","color: #000;","font: normal 13px Arial, sans-serif;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel,",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content {","color: #ccc !important;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon {","opacity: 0.3;","filter: alpha(opacity=30);","}",".blocklyWidgetDiv .goog-menuitem-highlight,",".blocklyWidgetDiv .goog-menuitem-hover {","background-color: #d6e9f8;", -"border-color: #d6e9f8;","border-style: dotted;","border-width: 1px 0;","padding-bottom: 3px;","padding-top: 3px;","}",".blocklyWidgetDiv .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-icon {","background-repeat: no-repeat;","height: 16px;","left: 6px;","position: absolute;","right: auto;","vertical-align: middle;","width: 16px;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon {","left: auto;","right: 6px;", -"}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0;","}",".blocklyWidgetDiv .goog-menuitem-accel {","color: #999;","direction: ltr;","left: auto;","padding: 0 6px;","position: absolute;","right: 0;","text-align: right;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel {","left: 0;","right: auto;","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=document.createElement("div"),Blockly.WidgetDiv.DIV.className="blocklyWidgetDiv",document.body.appendChild(Blockly.WidgetDiv.DIV))}; +"stroke: #f00;","stroke-width: 2;","stroke-linecap: round;","pointer-events: none;","}",".blocklyContextMenu {","border-radius: 4px;","max-height: 100%;","}",".blocklyDropdownMenu {","padding: 0 !important;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(<<>>/sprites.png) no-repeat -48px -16px !important;","}",".blocklyToolboxDiv {","background-color: #ddd;","overflow-x: visible;","overflow-y: auto;", +"position: absolute;","user-select: none;","-moz-user-select: none;","-ms-user-select: none;","-webkit-user-select: none;","z-index: 70;","-webkit-tap-highlight-color: transparent;","}",".blocklyTreeRoot {","padding: 4px 0;","}",".blocklyTreeRoot:focus {","outline: none;","}",".blocklyTreeRow {","height: 22px;","line-height: 22px;","margin-bottom: 3px;","padding-right: 8px;","white-space: nowrap;","}",".blocklyHorizontalTree {","float: left;","margin: 1px 5px 8px 0;","}",".blocklyHorizontalTreeRtl {", +"float: right;","margin: 1px 0 8px 5px;","}",'.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',"margin-left: 8px;","}",".blocklyTreeRow:not(.blocklyTreeSelected):hover {","background-color: #e4e4e4;","}",".blocklyTreeSeparator {","border-bottom: solid #e5e5e5 1px;","height: 0;","margin: 5px 0;","}",".blocklyTreeSeparatorHorizontal {","border-right: solid #e5e5e5 1px;","width: 0;","padding: 5px 0;","margin: 0 5px;","}",".blocklyTreeIcon {","background-image: url(<<>>/sprites.png);","height: 16px;", +"vertical-align: middle;","width: 16px;","}",".blocklyTreeIconClosedLtr {","background-position: -32px -1px;","}",".blocklyTreeIconClosedRtl {","background-position: 0 -1px;","}",".blocklyTreeIconOpen {","background-position: -16px -1px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedLtr {","background-position: -32px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedRtl {","background-position: 0 -17px;","}",".blocklyTreeSelected>.blocklyTreeIconOpen {","background-position: -16px -17px;", +"}",".blocklyTreeIconNone,",".blocklyTreeSelected>.blocklyTreeIconNone {","background-position: -48px -1px;","}",".blocklyTreeLabel {","cursor: default;","font-family: sans-serif;","font-size: 16px;","padding: 0 3px;","vertical-align: middle;","}",".blocklyToolboxDelete .blocklyTreeLabel {",'cursor: url("<<>>/handdelete.cur"), auto;',"}",".blocklyTreeSelected .blocklyTreeLabel {","color: #fff;","}",".blocklyColourTable {","border-collapse: collapse;","}",".blocklyColourTable>tr>td {","border: 1px solid #666;", +"padding: 0;","}",".blocklyColourTable>tr>td>div {","border: 1px solid #666;","height: 13px;","width: 15px;","}",".blocklyColourTable>tr>td>div:hover {","border: 1px solid #fff;","}",".blocklyColourSelected, .blocklyColourSelected:hover {","border: 1px solid #000 !important;","}",".blocklyWidgetDiv .goog-menu {","background: #fff;","border-color: #ccc #666 #666 #ccc;","border-style: solid;","border-width: 1px;","cursor: default;","font: normal 13px Arial, sans-serif;","margin: 0;","outline: none;", +"padding: 4px 0;","position: absolute;","overflow-y: auto;","overflow-x: hidden;","max-height: 100%;","z-index: 20000;","}",".blocklyWidgetDiv .goog-menuitem {","color: #000;","font: normal 13px Arial, sans-serif;","list-style: none;","margin: 0;","padding: 4px 7em 4px 28px;","white-space: nowrap;","}",".blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl {","padding-left: 7em;","padding-right: 28px;","}",".blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,",".blocklyWidgetDiv .goog-menu-noicon .goog-menuitem {", +"padding-left: 12px;","}",".blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem {","padding-right: 20px;","}",".blocklyWidgetDiv .goog-menuitem-content {","color: #000;","font: normal 13px Arial, sans-serif;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel,",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content {","color: #ccc !important;","}",".blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon {","opacity: 0.3;","filter: alpha(opacity=30);","}",".blocklyWidgetDiv .goog-menuitem-highlight,", +".blocklyWidgetDiv .goog-menuitem-hover {","background-color: #d6e9f8;","border-color: #d6e9f8;","border-style: dotted;","border-width: 1px 0;","padding-bottom: 3px;","padding-top: 3px;","}",".blocklyWidgetDiv .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-icon {","background-repeat: no-repeat;","height: 16px;","left: 6px;","position: absolute;","right: auto;","vertical-align: middle;","width: 16px;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon {", +"left: auto;","right: 6px;","}",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,",".blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {","background: url(//ssl.gstatic.com/editor/editortoolbar.png) no-repeat -512px 0;","}",".blocklyWidgetDiv .goog-menuitem-accel {","color: #999;","direction: ltr;","left: auto;","padding: 0 6px;","position: absolute;","right: 0;","text-align: right;","}",".blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel {","left: 0;","right: auto;", +"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=document.createElement("div"),Blockly.WidgetDiv.DIV.className="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,Blockly.WidgetDiv.DIV.innerHTML="")};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){bc.width+d.x&&(a=c.width+d.x):a=a.bottom?b.top-c.height:b.bottom};Blockly.inject=function(a,b){Blockly.checkBlockColourConstants();"string"==typeof a&&(a=document.getElementById(a)||document.querySelector(a));if(!Blockly.utils.containsNode(document,a))throw Error("Error: container is not in current document.");var c=new Blockly.Options(b||{}),d=document.createElement("div");d.className="injectionDiv";a.appendChild(d);var e=Blockly.createDom_(d,c),f=new Blockly.BlockDragSurfaceSvg(d);d=new Blockly.WorkspaceDragSurfaceSvg(d);c=Blockly.createMainWorkspace_(e,c,f,d); -Blockly.init_(c);Blockly.mainWorkspace=c;Blockly.svgResize(c);return c}; +Blockly.WidgetDiv.positionWithAnchor=function(a,b,c,d){var e=Blockly.WidgetDiv.calculateY_(a,b,c);a=Blockly.WidgetDiv.calculateX_(a,b,c,d);0>e?Blockly.WidgetDiv.positionInternal_(a,0,c.height+e):Blockly.WidgetDiv.positionInternal_(a,e,c.height)};Blockly.WidgetDiv.calculateX_=function(a,b,c,d){if(d)return b=Math.max(b.right-c.width,a.left),Math.min(b,a.right-c.width);b=Math.min(b.left,a.right-c.width);return Math.max(b,a.left)}; +Blockly.WidgetDiv.calculateY_=function(a,b,c){return b.bottom+c.height>=a.bottom?b.top-c.height:b.bottom};Blockly.inject=function(a,b){Blockly.checkBlockColourConstants();"string"==typeof a&&(a=document.getElementById(a)||document.querySelector(a));if(!Blockly.utils.containsNode(document,a))throw Error("Error: container is not in current document.");var c=new Blockly.Options(b||{}),d=document.createElement("div");d.className="injectionDiv";a.appendChild(d);var e=Blockly.createDom_(d,c),f=new Blockly.BlockDragSurfaceSvg(d);d=new Blockly.WorkspaceDragSurfaceSvg(d);e=Blockly.createMainWorkspace_(e,c,f,d); +Blockly.setTheme(c.theme);Blockly.init_(e);Blockly.mainWorkspace=e;Blockly.svgResize(e);return e}; 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.utils.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.utils.createSvgElement("defs",{},c),e=String(Math.random()).substring(2),f=Blockly.utils.createSvgElement("filter",{id:"blocklyEmbossFilter"+ e},d);Blockly.utils.createSvgElement("feGaussianBlur",{"in":"SourceAlpha",stdDeviation:1,result:"blur"},f);var g=Blockly.utils.createSvgElement("feSpecularLighting",{"in":"blur",surfaceScale:1,specularConstant:.5,specularExponent:10,"lighting-color":"white",result:"specOut"},f);Blockly.utils.createSvgElement("fePointLight",{x:-5E3,y:-1E4,z:2E4},g);Blockly.utils.createSvgElement("feComposite",{"in":"specOut",in2:"SourceAlpha",operator:"in",result:"specOut"},f);Blockly.utils.createSvgElement("feComposite", {"in":"SourceGraphic",in2:"specOut",operator:"arithmetic",k1:0,k2:1,k3:1,k4:0},f);b.embossFilterId=f.id;f=Blockly.utils.createSvgElement("pattern",{id:"blocklyDisabledPattern"+e,patternUnits:"userSpaceOnUse",width:10,height:10},d);Blockly.utils.createSvgElement("rect",{width:10,height:10,fill:"#aaa"},f);Blockly.utils.createSvgElement("path",{d:"M 0 0 L 10 10 M 10 0 L 0 10",stroke:"#cc0"},f);b.disabledPatternId=f.id;b.gridPattern=Blockly.Grid.createDom(e,b.gridOptions,d);return c}; -Blockly.createMainWorkspace_=function(a,b,c,d){b.parentWorkspace=null;var e=new Blockly.WorkspaceSvg(b,c,d);e.scale=b.zoomOptions.startScale;a.appendChild(e.createDom("blocklyMainBackground"));!b.hasCategories&&b.languageTree&&(c=e.addFlyout_("svg"),Blockly.utils.insertAfter(c,a));e.translate(0,0);Blockly.mainWorkspace=e;b.readOnly||b.hasScrollbars||e.addChangeListener(function(a){if(!e.isDragging()){var c=e.getMetrics(),d=c.viewLeft+c.absoluteLeft,f=c.viewTop+c.absoluteTop;if(c.contentTopc.viewHeight+f||c.contentLeft<(b.RTL?c.viewLeft:d)||c.contentLeft+c.contentWidth>(b.RTL?c.viewWidth:c.viewWidth+d)){var l=e.getTopBlocks(!1),n=null;a&&(n=Blockly.Events.getGroup(),Blockly.Events.setGroup(a.group));for(var m=!1,p=0,q;q=l[p];p++){var r=q.getRelativeToSurfaceXY(),t=q.getHeightWidth(),u=f+25-t.height-r.y;0u&&(q.moveBy(0,u),m=!0);u=25+d-r.x-(b.RTL?0:t.width);0r&&(q.moveBy(r,0),m=!0)}a&&(!a.group&&m&&console.log("WARNING: Moved blocks in bounds but there was no event group. This may break undo."),Blockly.Events.setGroup(n))}}});Blockly.svgResize(e);Blockly.WidgetDiv.createDom();Blockly.Tooltip.createDom();return e}; +Blockly.createMainWorkspace_=function(a,b,c,d){b.parentWorkspace=null;var e=new Blockly.WorkspaceSvg(b,c,d);e.scale=b.zoomOptions.startScale;a.appendChild(e.createDom("blocklyMainBackground"));!b.hasCategories&&b.languageTree&&(c=e.addFlyout_("svg"),Blockly.utils.insertAfter(c,a));b.hasTrashcan&&e.addTrashcan();b.zoomOptions&&b.zoomOptions.controls&&e.addZoomControls();e.translate(0,0);Blockly.mainWorkspace=e;b.readOnly||b.hasScrollbars||e.addChangeListener(function(a){if(!e.isDragging()){var c=e.getMetrics(), +d=c.viewLeft+c.absoluteLeft,f=c.viewTop+c.absoluteTop;if(c.contentTopc.viewHeight+f||c.contentLeft<(b.RTL?c.viewLeft:d)||c.contentLeft+c.contentWidth>(b.RTL?c.viewWidth:c.viewWidth+d)){var l=e.getTopBlocks(!1),n=null;a&&(n=Blockly.Events.getGroup(),Blockly.Events.setGroup(a.group));for(var m=!1,p=0,q;q=l[p];p++){var t=q.getRelativeToSurfaceXY(),r=q.getHeightWidth(),u=f+25-r.height-t.y;0u&&(q.moveBy(0,u),m=!0);u=25+ +d-t.x-(b.RTL?0:r.width);0t&&(q.moveBy(t,0),m=!0)}a&&(!a.group&&m&&console.log("WARNING: Moved blocks in bounds but there was no event group. This may break undo."),Blockly.Events.setGroup(n))}}});Blockly.svgResize(e);Blockly.WidgetDiv.createDom();Blockly.Tooltip.createDom();return e}; Blockly.init_=function(a){var b=a.options,c=a.getParentSvg();Blockly.bindEventWithChecks_(c.parentNode,"contextmenu",null,function(a){Blockly.utils.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)}; +a.scrollX=a.flyout_.width_,b.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT&&(a.scrollX*=-1),a.translate(a.scrollX,0)));c=Blockly.Scrollbar.scrollbarThickness;b.hasTrashcan&&(c=a.trashcan.init(c));b.zoomOptions&&b.zoomOptions.controls&&a.zoomControls_.init(c);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.bindEvent_(document,"touchend",null,Blockly.longStop_),Blockly.bindEvent_(document,"touchcancel",null,Blockly.longStop_),goog.userAgent.IPAD&&Blockly.bindEventWithChecks_(window,"orientationchange",document,function(){Blockly.svgResize(Blockly.getMainWorkspace())}));Blockly.documentEventsBound_=!0}; Blockly.inject.loadSounds_=function(a,b){var c=b.getAudioManager();c.load([a+"click.mp3",a+"click.wav",a+"click.ogg"],"click");c.load([a+"disconnect.wav",a+"disconnect.mp3",a+"disconnect.ogg"],"disconnect");c.load([a+"delete.mp3",a+"delete.ogg",a+"delete.wav"],"delete");var d=[],e=function(){for(;d.length;)Blockly.unbindEvent_(d.pop());c.preload()};d.push(Blockly.bindEventWithChecks_(document,"mousemove",null,e,!0));d.push(Blockly.bindEventWithChecks_(document,"touchstart",null,e,!0))}; -Blockly.updateToolbox=function(a){console.warn("Deprecated call to Blockly.updateToolbox, use workspace.updateToolbox instead.");Blockly.getMainWorkspace().updateToolbox(a)};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.clipboardTypeCounts_=null;Blockly.cache3dSupported_=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.updateToolbox=function(a){console.warn("Deprecated call to Blockly.updateToolbox, use workspace.updateToolbox instead.");Blockly.getMainWorkspace().updateToolbox(a)};var CLOSURE_DEFINES={"goog.DEBUG":!1};Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.draggingConnections_=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.clipboardTypeCounts_=null;Blockly.cache3dSupported_=null;Blockly.theme_=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.onKeyDown_=function(a){var b=Blockly.mainWorkspace;if(!(b.options.readOnly||Blockly.utils.isTargetInput(a)||b.rendered&&!b.isVisible())){var c=!1;if(27==a.keyCode)Blockly.hideChaff();else if(8==a.keyCode||46==a.keyCode){a.preventDefault();if(b.isDragging())return;Blockly.selected&&Blockly.selected.isDeletable()&&(c=!0)}else if(a.altKey||a.ctrlKey||a.metaKey){if(b.isDragging())return;Blockly.selected&&Blockly.selected.isDeletable()&&Blockly.selected.isMovable()&&(67==a.keyCode?(Blockly.hideChaff(), Blockly.copy_(Blockly.selected)):88!=a.keyCode||Blockly.selected.workspace.isFlyout||(Blockly.copy_(Blockly.selected),c=!0));86==a.keyCode?Blockly.clipboardXml_&&(b=Blockly.clipboardSource_,b.isFlyout&&(b=b.targetWorkspace),Blockly.clipboardTypeCounts_&&b.isCapacityAvailable(Blockly.clipboardTypeCounts_)&&(Blockly.Events.setGroup(!0),b.paste(Blockly.clipboardXml_),Blockly.Events.setGroup(!1))):90==a.keyCode&&(Blockly.hideChaff(),b.undo(a.shiftKey))}c&&!Blockly.selected.workspace.isFlyout&&(Blockly.Events.setGroup(!0), Blockly.hideChaff(),Blockly.selected.dispose(!0,!0),Blockly.Events.setGroup(!1))}};Blockly.copy_=function(a){if(a.isComment)var b=a.toXmlWithXY();else{b=Blockly.Xml.blockToDom(a);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.clipboardTypeCounts_=a.isComment?null:Blockly.utils.getBlockTypeCounts(a,!0)}; -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.utils.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};Blockly.alert=function(a,b){window.alert(a);b&&b()};Blockly.confirm=function(a,b){b(window.confirm(a))};Blockly.prompt=function(a,b,c){c(window.prompt(a,b))};Blockly.jsonInitFactory_=function(a){return function(){this.jsonInit(a)}}; +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.utils.isTargetInput(a)||a.preventDefault()}; +Blockly.hideChaff=function(a){Blockly.Tooltip.hide();Blockly.WidgetDiv.hide();var b=Blockly.getMainWorkspace();b.trashcan&&b.trashcan.flyout_&&b.trashcan.flyout_.hide();a||b.toolbox_&&b.toolbox_.flyout_&&b.toolbox_.flyout_.autoClose&&b.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}; +Blockly.alert=function(a,b){window.alert(a);b&&b()};Blockly.confirm=function(a,b){b(window.confirm(a))};Blockly.prompt=function(a,b,c){c(window.prompt(a,b))};Blockly.jsonInitFactory_=function(a){return function(){this.jsonInit(a)}}; Blockly.defineBlocksWithJsonArray=function(a){for(var b=0;b","GT"],["\u200f\u2265","GTE"]]},{type:"input_value",name:"B"}],inputsInline:!0,output:"Boolean",colour:"%{BKY_LOGIC_HUE}",helpUrl:"%{BKY_LOGIC_COMPARE_HELPURL}",extensions:["logic_compare", -"logic_op_tooltip"]},{type:"logic_operation",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Boolean"},{type:"field_dropdown",name:"OP",options:[["%{BKY_LOGIC_OPERATION_AND}","AND"],["%{BKY_LOGIC_OPERATION_OR}","OR"]]},{type:"input_value",name:"B",check:"Boolean"}],inputsInline:!0,output:"Boolean",colour:"%{BKY_LOGIC_HUE}",helpUrl:"%{BKY_LOGIC_OPERATION_HELPURL}",extensions:["logic_op_tooltip"]},{type:"logic_negate",message0:"%{BKY_LOGIC_NEGATE_TITLE}",args0:[{type:"input_value",name:"BOOL", -check:"Boolean"}],output:"Boolean",colour:"%{BKY_LOGIC_HUE}",tooltip:"%{BKY_LOGIC_NEGATE_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NEGATE_HELPURL}"},{type:"logic_null",message0:"%{BKY_LOGIC_NULL}",output:null,colour:"%{BKY_LOGIC_HUE}",tooltip:"%{BKY_LOGIC_NULL_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NULL_HELPURL}"},{type:"logic_ternary",message0:"%{BKY_LOGIC_TERNARY_CONDITION} %1",args0:[{type:"input_value",name:"IF",check:"Boolean"}],message1:"%{BKY_LOGIC_TERNARY_IF_TRUE} %1",args1:[{type:"input_value",name:"THEN"}], -message2:"%{BKY_LOGIC_TERNARY_IF_FALSE} %1",args2:[{type:"input_value",name:"ELSE"}],output:null,colour:"%{BKY_LOGIC_HUE}",tooltip:"%{BKY_LOGIC_TERNARY_TOOLTIP}",helpUrl:"%{BKY_LOGIC_TERNARY_HELPURL}",extensions:["logic_ternary"]}]); -Blockly.defineBlocksWithJsonArray([{type:"controls_if_if",message0:"%{BKY_CONTROLS_IF_IF_TITLE_IF}",nextStatement:null,enableContextMenu:!1,colour:"%{BKY_LOGIC_HUE}",tooltip:"%{BKY_CONTROLS_IF_IF_TOOLTIP}"},{type:"controls_if_elseif",message0:"%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}",previousStatement:null,nextStatement:null,enableContextMenu:!1,colour:"%{BKY_LOGIC_HUE}",tooltip:"%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}"},{type:"controls_if_else",message0:"%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}",previousStatement:null, -enableContextMenu:!1,colour:"%{BKY_LOGIC_HUE}",tooltip:"%{BKY_CONTROLS_IF_ELSE_TOOLTIP}"}]);Blockly.Constants.Logic.TOOLTIPS_BY_OP={EQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}",NEQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}",LT:"%{BKY_LOGIC_COMPARE_TOOLTIP_LT}",LTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_LTE}",GT:"%{BKY_LOGIC_COMPARE_TOOLTIP_GT}",GTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_GTE}",AND:"%{BKY_LOGIC_OPERATION_TOOLTIP_AND}",OR:"%{BKY_LOGIC_OPERATION_TOOLTIP_OR}"}; +Blockly.defineBlocksWithJsonArray([{type:"logic_boolean",message0:"%1",args0:[{type:"field_dropdown",name:"BOOL",options:[["%{BKY_LOGIC_BOOLEAN_TRUE}","TRUE"],["%{BKY_LOGIC_BOOLEAN_FALSE}","FALSE"]]}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_BOOLEAN_TOOLTIP}",helpUrl:"%{BKY_LOGIC_BOOLEAN_HELPURL}"},{type:"controls_if",message0:"%{BKY_CONTROLS_IF_MSG_IF} %1",args0:[{type:"input_value",name:"IF0",check:"Boolean"}],message1:"%{BKY_CONTROLS_IF_MSG_THEN} %1",args1:[{type:"input_statement", +name:"DO0"}],previousStatement:null,nextStatement:null,style:"logic_blocks",helpUrl:"%{BKY_CONTROLS_IF_HELPURL}",mutator:"controls_if_mutator",extensions:["controls_if_tooltip"]},{type:"controls_ifelse",message0:"%{BKY_CONTROLS_IF_MSG_IF} %1",args0:[{type:"input_value",name:"IF0",check:"Boolean"}],message1:"%{BKY_CONTROLS_IF_MSG_THEN} %1",args1:[{type:"input_statement",name:"DO0"}],message2:"%{BKY_CONTROLS_IF_MSG_ELSE} %1",args2:[{type:"input_statement",name:"ELSE"}],previousStatement:null,nextStatement:null, +style:"logic_blocks",tooltip:"%{BKYCONTROLS_IF_TOOLTIP_2}",helpUrl:"%{BKY_CONTROLS_IF_HELPURL}",extensions:["controls_if_tooltip"]},{type:"logic_compare",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A"},{type:"field_dropdown",name:"OP",options:[["=","EQ"],["\u2260","NEQ"],["\u200f<","LT"],["\u200f\u2264","LTE"],["\u200f>","GT"],["\u200f\u2265","GTE"]]},{type:"input_value",name:"B"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_COMPARE_HELPURL}",extensions:["logic_compare", +"logic_op_tooltip"]},{type:"logic_operation",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Boolean"},{type:"field_dropdown",name:"OP",options:[["%{BKY_LOGIC_OPERATION_AND}","AND"],["%{BKY_LOGIC_OPERATION_OR}","OR"]]},{type:"input_value",name:"B",check:"Boolean"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_OPERATION_HELPURL}",extensions:["logic_op_tooltip"]},{type:"logic_negate",message0:"%{BKY_LOGIC_NEGATE_TITLE}",args0:[{type:"input_value",name:"BOOL", +check:"Boolean"}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_NEGATE_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NEGATE_HELPURL}"},{type:"logic_null",message0:"%{BKY_LOGIC_NULL}",output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_NULL_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NULL_HELPURL}"},{type:"logic_ternary",message0:"%{BKY_LOGIC_TERNARY_CONDITION} %1",args0:[{type:"input_value",name:"IF",check:"Boolean"}],message1:"%{BKY_LOGIC_TERNARY_IF_TRUE} %1",args1:[{type:"input_value",name:"THEN"}],message2:"%{BKY_LOGIC_TERNARY_IF_FALSE} %1", +args2:[{type:"input_value",name:"ELSE"}],output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_TERNARY_TOOLTIP}",helpUrl:"%{BKY_LOGIC_TERNARY_HELPURL}",extensions:["logic_ternary"]}]); +Blockly.defineBlocksWithJsonArray([{type:"controls_if_if",message0:"%{BKY_CONTROLS_IF_IF_TITLE_IF}",nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_IF_TOOLTIP}"},{type:"controls_if_elseif",message0:"%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}",previousStatement:null,nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}"},{type:"controls_if_else",message0:"%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}",previousStatement:null, +enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSE_TOOLTIP}"}]);Blockly.Constants.Logic.TOOLTIPS_BY_OP={EQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}",NEQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}",LT:"%{BKY_LOGIC_COMPARE_TOOLTIP_LT}",LTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_LTE}",GT:"%{BKY_LOGIC_COMPARE_TOOLTIP_GT}",GTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_GTE}",AND:"%{BKY_LOGIC_OPERATION_TOOLTIP_AND}",OR:"%{BKY_LOGIC_OPERATION_TOOLTIP_OR}"}; Blockly.Extensions.register("logic_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Logic.TOOLTIPS_BY_OP)); Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN={elseifCount_:0,elseCount_:0,mutationToDom:function(){if(!this.elseifCount_&&!this.elseCount_)return null;var a=document.createElement("mutation");this.elseifCount_&&a.setAttribute("elseif",this.elseifCount_);this.elseCount_&&a.setAttribute("else",1);return a},domToMutation:function(a){this.elseifCount_=parseInt(a.getAttribute("elseif"),10)||0;this.elseCount_=parseInt(a.getAttribute("else"),10)||0;this.rebuildShape_()},decompose:function(a){var b= a.newBlock("controls_if_if");b.initSvg();for(var c=b.nextConnection,d=1;d<=this.elseifCount_;d++){var e=a.newBlock("controls_if_elseif");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}this.elseCount_&&(a=a.newBlock("controls_if_else"),a.initSvg(),c.connect(a.previousConnection));return b},compose:function(a){a=a.nextConnection.targetBlock();this.elseCount_=this.elseifCount_=0;for(var b=[null],c=[null],d=null;a;){switch(a.type){case "controls_if_elseif":this.elseifCount_++;b.push(a.valueConnection_); @@ -59,28 +59,28 @@ Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN={onchange:function(a){this. this.bumpNeighbours_(),Blockly.Events.setGroup(!1));this.prevBlocks_[0]=this.getInputTargetBlock("A");this.prevBlocks_[1]=this.getInputTargetBlock("B")}};Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION=function(){this.mixin(Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN)};Blockly.Extensions.register("logic_compare",Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION); Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN={prevParentConnection_:null,onchange:function(a){var b=this.getInputTargetBlock("THEN"),c=this.getInputTargetBlock("ELSE"),d=this.outputConnection.targetConnection;if((b||c)&&d)for(var e=0;2>e;e++){var f=1==e?b:c;f&&!f.outputConnection.checkType_(d)&&(Blockly.Events.setGroup(a.group),d===this.prevParentConnection_?(this.unplug(),d.getSourceBlock().bumpNeighbours_()):(f.unplug(),f.bumpNeighbours_()),Blockly.Events.setGroup(!1))}this.prevParentConnection_= d}};Blockly.Extensions.registerMixin("logic_ternary",Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN);Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120; -Blockly.defineBlocksWithJsonArray([{type:"controls_repeat_ext",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"input_value",name:"TIMES",check:"Number"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,colour:"%{BKY_LOOPS_HUE}",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_repeat",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"field_number",name:"TIMES", -value:10,min:0,precision:1}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,colour:"%{BKY_LOOPS_HUE}",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_whileUntil",message0:"%1 %2",args0:[{type:"field_dropdown",name:"MODE",options:[["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}","WHILE"],["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}","UNTIL"]]},{type:"input_value",name:"BOOL", -check:"Boolean"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,colour:"%{BKY_LOOPS_HUE}",helpUrl:"%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",extensions:["controls_whileUntil_tooltip"]},{type:"controls_for",message0:"%{BKY_CONTROLS_FOR_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"FROM",check:"Number",align:"RIGHT"},{type:"input_value",name:"TO",check:"Number",align:"RIGHT"},{type:"input_value", -name:"BY",check:"Number",align:"RIGHT"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],inputsInline:!0,previousStatement:null,nextStatement:null,colour:"%{BKY_LOOPS_HUE}",helpUrl:"%{BKY_CONTROLS_FOR_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_for_tooltip"]},{type:"controls_forEach",message0:"%{BKY_CONTROLS_FOREACH_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"LIST",check:"Array"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", -args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,colour:"%{BKY_LOOPS_HUE}",helpUrl:"%{BKY_CONTROLS_FOREACH_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_forEach_tooltip"]},{type:"controls_flow_statements",message0:"%1",args0:[{type:"field_dropdown",name:"FLOW",options:[["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}","BREAK"],["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}","CONTINUE"]]}],previousStatement:null,colour:"%{BKY_LOOPS_HUE}", -helpUrl:"%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}",extensions:["controls_flow_tooltip","controls_flow_in_loop_check"]}]);Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS={WHILE:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}",UNTIL:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}"};Blockly.Extensions.register("controls_whileUntil_tooltip",Blockly.Extensions.buildTooltipForDropdown("MODE",Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS)); -Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS={BREAK:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}",CONTINUE:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}"};Blockly.Extensions.register("controls_flow_tooltip",Blockly.Extensions.buildTooltipForDropdown("FLOW",Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS)); +Blockly.defineBlocksWithJsonArray([{type:"controls_repeat_ext",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"input_value",name:"TIMES",check:"Number"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_repeat",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"field_number",name:"TIMES",value:10, +min:0,precision:1}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_whileUntil",message0:"%1 %2",args0:[{type:"field_dropdown",name:"MODE",options:[["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}","WHILE"],["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}","UNTIL"]]},{type:"input_value",name:"BOOL",check:"Boolean"}], +message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",extensions:["controls_whileUntil_tooltip"]},{type:"controls_for",message0:"%{BKY_CONTROLS_FOR_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"FROM",check:"Number",align:"RIGHT"},{type:"input_value",name:"TO",check:"Number",align:"RIGHT"},{type:"input_value",name:"BY", +check:"Number",align:"RIGHT"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],inputsInline:!0,previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOR_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_for_tooltip"]},{type:"controls_forEach",message0:"%{BKY_CONTROLS_FOREACH_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"LIST",check:"Array"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", +args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOREACH_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_forEach_tooltip"]},{type:"controls_flow_statements",message0:"%1",args0:[{type:"field_dropdown",name:"FLOW",options:[["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}","BREAK"],["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}","CONTINUE"]]}],previousStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}", +extensions:["controls_flow_tooltip","controls_flow_in_loop_check"]}]);Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS={WHILE:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}",UNTIL:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}"};Blockly.Extensions.register("controls_whileUntil_tooltip",Blockly.Extensions.buildTooltipForDropdown("MODE",Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS));Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS={BREAK:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}",CONTINUE:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}"}; +Blockly.Extensions.register("controls_flow_tooltip",Blockly.Extensions.buildTooltipForDropdown("FLOW",Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS)); Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){var b=this.getField("VAR").getVariable(),c=b.name;if(!this.isCollapsed()&&null!=c){var d={enabled:!0};d.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c);b=Blockly.Variables.generateVariableFieldDom(b);c=document.createElement("block");c.setAttribute("type","variables_get");c.appendChild(b);d.callback=Blockly.ContextMenu.callbackFactory(this,c);a.push(d)}}}}; Blockly.Extensions.registerMixin("contextMenu_newGetVariableBlock",Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);Blockly.Extensions.register("controls_for_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOR_TOOLTIP}","VAR"));Blockly.Extensions.register("controls_forEach_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOREACH_TOOLTIP}","VAR")); Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN={LOOP_TYPES:["controls_repeat","controls_repeat_ext","controls_forEach","controls_for","controls_whileUntil"],onchange:function(){if(this.workspace.isDragging&&!this.workspace.isDragging()){var a=!1,b=this;do{if(-1!=this.LOOP_TYPES.indexOf(b.type)){a=!0;break}b=b.getSurroundParent()}while(b);a?(this.setWarningText(null),this.isInFlyout||this.setDisabled(!1)):(this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING),this.isInFlyout|| this.getInheritedDisabled()||this.setDisabled(!0))}}};Blockly.Extensions.registerMixin("controls_flow_in_loop_check",Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);Blockly.Blocks.math={};Blockly.Constants.Math={};Blockly.Constants.Math.HUE=230; -Blockly.defineBlocksWithJsonArray([{type:"math_number",message0:"%1",args0:[{type:"field_number",name:"NUM",value:0}],output:"Number",colour:"%{BKY_MATH_HUE}",helpUrl:"%{BKY_MATH_NUMBER_HELPURL}",tooltip:"%{BKY_MATH_NUMBER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"math_arithmetic",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Number"},{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ADDITION_SYMBOL}","ADD"],["%{BKY_MATH_SUBTRACTION_SYMBOL}","MINUS"],["%{BKY_MATH_MULTIPLICATION_SYMBOL}", -"MULTIPLY"],["%{BKY_MATH_DIVISION_SYMBOL}","DIVIDE"],["%{BKY_MATH_POWER_SYMBOL}","POWER"]]},{type:"input_value",name:"B",check:"Number"}],inputsInline:!0,output:"Number",colour:"%{BKY_MATH_HUE}",helpUrl:"%{BKY_MATH_ARITHMETIC_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_single",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_SINGLE_OP_ROOT}","ROOT"],["%{BKY_MATH_SINGLE_OP_ABSOLUTE}","ABS"],["-","NEG"],["ln","LN"],["log10","LOG10"],["e^","EXP"],["10^","POW10"]]}, -{type:"input_value",name:"NUM",check:"Number"}],output:"Number",colour:"%{BKY_MATH_HUE}",helpUrl:"%{BKY_MATH_SINGLE_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_trig",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_TRIG_SIN}","SIN"],["%{BKY_MATH_TRIG_COS}","COS"],["%{BKY_MATH_TRIG_TAN}","TAN"],["%{BKY_MATH_TRIG_ASIN}","ASIN"],["%{BKY_MATH_TRIG_ACOS}","ACOS"],["%{BKY_MATH_TRIG_ATAN}","ATAN"]]},{type:"input_value",name:"NUM",check:"Number"}],output:"Number",colour:"%{BKY_MATH_HUE}", -helpUrl:"%{BKY_MATH_TRIG_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_constant",message0:"%1",args0:[{type:"field_dropdown",name:"CONSTANT",options:[["\u03c0","PI"],["e","E"],["\u03c6","GOLDEN_RATIO"],["sqrt(2)","SQRT2"],["sqrt(\u00bd)","SQRT1_2"],["\u221e","INFINITY"]]}],output:"Number",colour:"%{BKY_MATH_HUE}",tooltip:"%{BKY_MATH_CONSTANT_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTANT_HELPURL}"},{type:"math_number_property",message0:"%1 %2",args0:[{type:"input_value",name:"NUMBER_TO_CHECK",check:"Number"}, -{type:"field_dropdown",name:"PROPERTY",options:[["%{BKY_MATH_IS_EVEN}","EVEN"],["%{BKY_MATH_IS_ODD}","ODD"],["%{BKY_MATH_IS_PRIME}","PRIME"],["%{BKY_MATH_IS_WHOLE}","WHOLE"],["%{BKY_MATH_IS_POSITIVE}","POSITIVE"],["%{BKY_MATH_IS_NEGATIVE}","NEGATIVE"],["%{BKY_MATH_IS_DIVISIBLE_BY}","DIVISIBLE_BY"]]}],inputsInline:!0,output:"Boolean",colour:"%{BKY_MATH_HUE}",tooltip:"%{BKY_MATH_IS_TOOLTIP}",mutator:"math_is_divisibleby_mutator"},{type:"math_change",message0:"%{BKY_MATH_CHANGE_TITLE}",args0:[{type:"field_variable", -name:"VAR",variable:"%{BKY_MATH_CHANGE_TITLE_ITEM}"},{type:"input_value",name:"DELTA",check:"Number"}],previousStatement:null,nextStatement:null,colour:"%{BKY_VARIABLES_HUE}",helpUrl:"%{BKY_MATH_CHANGE_HELPURL}",extensions:["math_change_tooltip"]},{type:"math_round",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ROUND_OPERATOR_ROUND}","ROUND"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}","ROUNDUP"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}","ROUNDDOWN"]]},{type:"input_value", -name:"NUM",check:"Number"}],output:"Number",colour:"%{BKY_MATH_HUE}",helpUrl:"%{BKY_MATH_ROUND_HELPURL}",tooltip:"%{BKY_MATH_ROUND_TOOLTIP}"},{type:"math_on_list",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ONLIST_OPERATOR_SUM}","SUM"],["%{BKY_MATH_ONLIST_OPERATOR_MIN}","MIN"],["%{BKY_MATH_ONLIST_OPERATOR_MAX}","MAX"],["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}","AVERAGE"],["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}","MEDIAN"],["%{BKY_MATH_ONLIST_OPERATOR_MODE}","MODE"],["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}", -"STD_DEV"],["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}","RANDOM"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Number",colour:"%{BKY_MATH_HUE}",helpUrl:"%{BKY_MATH_ONLIST_HELPURL}",mutator:"math_modes_of_list_mutator",extensions:["math_op_tooltip"]},{type:"math_modulo",message0:"%{BKY_MATH_MODULO_TITLE}",args0:[{type:"input_value",name:"DIVIDEND",check:"Number"},{type:"input_value",name:"DIVISOR",check:"Number"}],inputsInline:!0,output:"Number",colour:"%{BKY_MATH_HUE}",tooltip:"%{BKY_MATH_MODULO_TOOLTIP}", -helpUrl:"%{BKY_MATH_MODULO_HELPURL}"},{type:"math_constrain",message0:"%{BKY_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:"%{BKY_MATH_HUE}",tooltip:"%{BKY_MATH_CONSTRAIN_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTRAIN_HELPURL}"},{type:"math_random_int",message0:"%{BKY_MATH_RANDOM_INT_TITLE}",args0:[{type:"input_value",name:"FROM",check:"Number"}, -{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",colour:"%{BKY_MATH_HUE}",tooltip:"%{BKY_MATH_RANDOM_INT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_INT_HELPURL}"},{type:"math_random_float",message0:"%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}",output:"Number",colour:"%{BKY_MATH_HUE}",tooltip:"%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_FLOAT_HELPURL}"},{type:"math_atan2",message0:"%{BKY_MATH_ATAN2_TITLE}",args0:[{type:"input_value",name:"X",check:"Number"},{type:"input_value", -name:"Y",check:"Number"}],inputsInline:!0,output:"Number",colour:"%{BKY_MATH_HUE}",tooltip:"%{BKY_MATH_ATAN2_TOOLTIP}",helpUrl:"%{BKY_MATH_ATAN2_HELPURL}"}]); +Blockly.defineBlocksWithJsonArray([{type:"math_number",message0:"%1",args0:[{type:"field_number",name:"NUM",value:0}],output:"Number",helpUrl:"%{BKY_MATH_NUMBER_HELPURL}",style:"math_blocks",tooltip:"%{BKY_MATH_NUMBER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"math_arithmetic",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Number"},{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ADDITION_SYMBOL}","ADD"],["%{BKY_MATH_SUBTRACTION_SYMBOL}","MINUS"],["%{BKY_MATH_MULTIPLICATION_SYMBOL}", +"MULTIPLY"],["%{BKY_MATH_DIVISION_SYMBOL}","DIVIDE"],["%{BKY_MATH_POWER_SYMBOL}","POWER"]]},{type:"input_value",name:"B",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ARITHMETIC_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_single",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_SINGLE_OP_ROOT}","ROOT"],["%{BKY_MATH_SINGLE_OP_ABSOLUTE}","ABS"],["-","NEG"],["ln","LN"],["log10","LOG10"],["e^","EXP"],["10^","POW10"]]}, +{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_SINGLE_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_trig",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_TRIG_SIN}","SIN"],["%{BKY_MATH_TRIG_COS}","COS"],["%{BKY_MATH_TRIG_TAN}","TAN"],["%{BKY_MATH_TRIG_ASIN}","ASIN"],["%{BKY_MATH_TRIG_ACOS}","ACOS"],["%{BKY_MATH_TRIG_ATAN}","ATAN"]]},{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks", +helpUrl:"%{BKY_MATH_TRIG_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_constant",message0:"%1",args0:[{type:"field_dropdown",name:"CONSTANT",options:[["\u03c0","PI"],["e","E"],["\u03c6","GOLDEN_RATIO"],["sqrt(2)","SQRT2"],["sqrt(\u00bd)","SQRT1_2"],["\u221e","INFINITY"]]}],output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTANT_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTANT_HELPURL}"},{type:"math_number_property",message0:"%1 %2",args0:[{type:"input_value",name:"NUMBER_TO_CHECK",check:"Number"}, +{type:"field_dropdown",name:"PROPERTY",options:[["%{BKY_MATH_IS_EVEN}","EVEN"],["%{BKY_MATH_IS_ODD}","ODD"],["%{BKY_MATH_IS_PRIME}","PRIME"],["%{BKY_MATH_IS_WHOLE}","WHOLE"],["%{BKY_MATH_IS_POSITIVE}","POSITIVE"],["%{BKY_MATH_IS_NEGATIVE}","NEGATIVE"],["%{BKY_MATH_IS_DIVISIBLE_BY}","DIVISIBLE_BY"]]}],inputsInline:!0,output:"Boolean",style:"math_blocks",tooltip:"%{BKY_MATH_IS_TOOLTIP}",mutator:"math_is_divisibleby_mutator"},{type:"math_change",message0:"%{BKY_MATH_CHANGE_TITLE}",args0:[{type:"field_variable", +name:"VAR",variable:"%{BKY_MATH_CHANGE_TITLE_ITEM}"},{type:"input_value",name:"DELTA",check:"Number"}],previousStatement:null,nextStatement:null,style:"variable_blocks",helpUrl:"%{BKY_MATH_CHANGE_HELPURL}",extensions:["math_change_tooltip"]},{type:"math_round",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ROUND_OPERATOR_ROUND}","ROUND"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}","ROUNDUP"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}","ROUNDDOWN"]]},{type:"input_value",name:"NUM", +check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ROUND_HELPURL}",tooltip:"%{BKY_MATH_ROUND_TOOLTIP}"},{type:"math_on_list",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ONLIST_OPERATOR_SUM}","SUM"],["%{BKY_MATH_ONLIST_OPERATOR_MIN}","MIN"],["%{BKY_MATH_ONLIST_OPERATOR_MAX}","MAX"],["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}","AVERAGE"],["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}","MEDIAN"],["%{BKY_MATH_ONLIST_OPERATOR_MODE}","MODE"],["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}", +"STD_DEV"],["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}","RANDOM"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ONLIST_HELPURL}",mutator:"math_modes_of_list_mutator",extensions:["math_op_tooltip"]},{type:"math_modulo",message0:"%{BKY_MATH_MODULO_TITLE}",args0:[{type:"input_value",name:"DIVIDEND",check:"Number"},{type:"input_value",name:"DIVISOR",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_MODULO_TOOLTIP}", +helpUrl:"%{BKY_MATH_MODULO_HELPURL}"},{type:"math_constrain",message0:"%{BKY_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",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTRAIN_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTRAIN_HELPURL}"},{type:"math_random_int",message0:"%{BKY_MATH_RANDOM_INT_TITLE}",args0:[{type:"input_value",name:"FROM",check:"Number"}, +{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_INT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_INT_HELPURL}"},{type:"math_random_float",message0:"%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}",output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_FLOAT_HELPURL}"},{type:"math_atan2",message0:"%{BKY_MATH_ATAN2_TITLE}",args0:[{type:"input_value",name:"X",check:"Number"},{type:"input_value", +name:"Y",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_ATAN2_TOOLTIP}",helpUrl:"%{BKY_MATH_ATAN2_HELPURL}"}]); Blockly.Constants.Math.TOOLTIPS_BY_OP={ADD:"%{BKY_MATH_ARITHMETIC_TOOLTIP_ADD}",MINUS:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MINUS}",MULTIPLY:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MULTIPLY}",DIVIDE:"%{BKY_MATH_ARITHMETIC_TOOLTIP_DIVIDE}",POWER:"%{BKY_MATH_ARITHMETIC_TOOLTIP_POWER}",ROOT:"%{BKY_MATH_SINGLE_TOOLTIP_ROOT}",ABS:"%{BKY_MATH_SINGLE_TOOLTIP_ABS}",NEG:"%{BKY_MATH_SINGLE_TOOLTIP_NEG}",LN:"%{BKY_MATH_SINGLE_TOOLTIP_LN}",LOG10:"%{BKY_MATH_SINGLE_TOOLTIP_LOG10}",EXP:"%{BKY_MATH_SINGLE_TOOLTIP_EXP}",POW10:"%{BKY_MATH_SINGLE_TOOLTIP_POW10}", SIN:"%{BKY_MATH_TRIG_TOOLTIP_SIN}",COS:"%{BKY_MATH_TRIG_TOOLTIP_COS}",TAN:"%{BKY_MATH_TRIG_TOOLTIP_TAN}",ASIN:"%{BKY_MATH_TRIG_TOOLTIP_ASIN}",ACOS:"%{BKY_MATH_TRIG_TOOLTIP_ACOS}",ATAN:"%{BKY_MATH_TRIG_TOOLTIP_ATAN}",SUM:"%{BKY_MATH_ONLIST_TOOLTIP_SUM}",MIN:"%{BKY_MATH_ONLIST_TOOLTIP_MIN}",MAX:"%{BKY_MATH_ONLIST_TOOLTIP_MAX}",AVERAGE:"%{BKY_MATH_ONLIST_TOOLTIP_AVERAGE}",MEDIAN:"%{BKY_MATH_ONLIST_TOOLTIP_MEDIAN}",MODE:"%{BKY_MATH_ONLIST_TOOLTIP_MODE}",STD_DEV:"%{BKY_MATH_ONLIST_TOOLTIP_STD_DEV}",RANDOM:"%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}"}; Blockly.Extensions.register("math_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Math.TOOLTIPS_BY_OP)); @@ -89,26 +89,27 @@ Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION=function(){this.getField(" Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN={updateType_:function(a){"MODE"==a?this.outputConnection.setCheck("Array"):this.outputConnection.setCheck("Number")},mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("op",this.getFieldValue("OP"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("op"))}};Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION=function(){this.getField("OP").setValidator(function(a){this.updateType_(a)}.bind(this))}; Blockly.Extensions.registerMutator("math_modes_of_list_mutator",Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN,Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION);Blockly.Blocks.procedures={}; Blockly.Blocks.procedures_defnoreturn={init:function(){var a=new Blockly.FieldTextInput("",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.Msg.PROCEDURES_HUE);this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];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} A possibly empty list of insertion + * marker blocks. + * @package + */ +Blockly.BlockDragger.prototype.getInsertionMarkers = function() { + // No insertion markers with the old style of dragged connection managers. + if (this.draggedConnectionManager_ && + this.draggedConnectionManager_.getInsertionMarkers) { + return this.draggedConnectionManager_.getInsertionMarkers(); + } + return []; +}; diff --git a/core/block_render_svg.js b/core/block_render_svg.js index a1d4af37e4b..6275d1aec7c 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -370,21 +370,30 @@ Blockly.BlockSvg.prototype.renderFields_ = function(fieldList, continue; } + var translateX; + var scale = ''; if (this.RTL) { cursorX -= field.renderSep + field.renderWidth; - root.setAttribute('transform', - 'translate(' + cursorX + ',' + cursorY + ')'); + translateX = cursorX; if (field.renderWidth) { cursorX -= Blockly.BlockSvg.SEP_SPACE_X; } } else { - root.setAttribute('transform', - 'translate(' + (cursorX + field.renderSep) + ',' + cursorY + ')'); + translateX = cursorX + field.renderSep; if (field.renderWidth) { cursorX += field.renderSep + field.renderWidth + Blockly.BlockSvg.SEP_SPACE_X; } } + if (this.RTL && + field instanceof Blockly.FieldImage && + field.getFlipRtl()) { + scale = 'scale(-1 1)'; + translateX += field.renderWidth; + } + root.setAttribute('transform', + 'translate(' + translateX + ',' + cursorY + ')' + scale); + // Fields are invisible on insertion marker. They still have to be rendered // so that the block can be sized correctly. if (this.isInsertionMarker()) { @@ -558,6 +567,9 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { this.squareTopLeftCorner_ = true; this.squareBottomLeftCorner_ = true; } else { + var renderCap = typeof this.hat !== undefined ? this.hat === 'cap' : + Blockly.BlockSvg.START_HAT; + this.squareTopLeftCorner_ = false; this.squareBottomLeftCorner_ = false; // If this block is in the middle of a stack, square the corners. @@ -566,7 +578,7 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { if (prevBlock && prevBlock.getNextBlock() == this) { this.squareTopLeftCorner_ = true; } - } else if (Blockly.BlockSvg.START_HAT) { + } else if (renderCap) { // No output or previous connection. this.squareTopLeftCorner_ = true; this.startHat_ = true; diff --git a/core/block_svg.js b/core/block_svg.js index 4cd15dde572..1bad283fd23 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -140,6 +140,15 @@ Blockly.BlockSvg.prototype.warningTextDb_ = null; */ Blockly.BlockSvg.INLINE = -1; +/** + * ID to give the "collapsed warnings" warning. Allows us to remove the + * "collapsed warnings" warning without removing any warnings that belong to + * the block. + * @type {string} + * @const + */ +Blockly.BlockSvg.COLLAPSED_WARNING_ID = 'TEMP_COLLAPSED_WARNING_'; + /** * Create and initialize the SVG representation of the block. * May be called more than once. @@ -509,10 +518,30 @@ Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) { } var text = this.toString(Blockly.COLLAPSE_CHARS); this.appendDummyInput(COLLAPSED_INPUT_NAME).appendField(text).init(); + + // Add any warnings on enclosed blocks to this block. + var descendants = this.getDescendants(true); + var nextBlock = this.getNextBlock(); + if (nextBlock) { + var index = descendants.indexOf(nextBlock); + descendants.splice(index, descendants.length - index); + } + for (var i = 1, block; block = descendants[i]; i++) { + if (block.warning) { + this.setWarningText(Blockly.Msg['COLLAPSED_WARNINGS_WARNING'], + Blockly.BlockSvg.COLLAPSED_WARNING_ID); + break; + } + } } else { this.removeInput(COLLAPSED_INPUT_NAME); // Clear any warnings inherited from enclosed blocks. - this.setWarningText(null); + if (this.warning) { + this.warning.setText('', Blockly.BlockSvg.COLLAPSED_WARNING_ID); + if (!Object.keys(this.warning.text_).length) { + this.setWarningText(null); + } + } } Blockly.BlockSvg.superClass_.setCollapsed.call(this, collapsed); @@ -864,16 +893,28 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { this.warningTextDb_ = null; } + // If the block is rendered we need to record the event before disposing of + // the icons to prevent losing information. + // TODO (#1969): Remove event generation/firing once comments are fixed. + this.unplug(healStack); + var deleteEvent; + if (Blockly.Events.isEnabled()) { + deleteEvent = new Blockly.Events.BlockDelete(this); + } Blockly.Events.disable(); try { var icons = this.getIcons(); for (var i = 0; i < icons.length; i++) { icons[i].dispose(); } + // TODO (#1969): Move out of disable block once comments are fixed. + Blockly.BlockSvg.superClass_.dispose.call(this, healStack); } finally { Blockly.Events.enable(); } - Blockly.BlockSvg.superClass_.dispose.call(this, healStack); + if (Blockly.Events.isEnabled() && deleteEvent) { + Blockly.Events.fire(deleteEvent); + } Blockly.utils.removeNode(this.svgGroup_); blockWorkspace.resizeContents(); @@ -894,18 +935,14 @@ Blockly.BlockSvg.prototype.updateColour = function() { return; } var hexColour = this.getColour(); + var colourSecondary = this.getColourSecondary(); + var colourTertiary = this.getColourTertiary(); var rgb = goog.color.hexToRgb(hexColour); + if (this.isShadow()) { - rgb = goog.color.lighten(rgb, 0.6); - hexColour = goog.color.rgbArrayToHex(rgb); - this.svgPathLight_.style.display = 'none'; - this.svgPathDark_.setAttribute('fill', hexColour); + hexColour = this.setShadowColour_(rgb, colourSecondary); } else { - this.svgPathLight_.style.display = ''; - var hexLight = goog.color.rgbArrayToHex(goog.color.lighten(rgb, 0.3)); - var hexDark = goog.color.rgbArrayToHex(goog.color.darken(rgb, 0.2)); - this.svgPathLight_.setAttribute('stroke', hexLight); - this.svgPathDark_.setAttribute('fill', hexDark); + this.setBorderColour_(rgb, colourTertiary); } this.svgPath_.setAttribute('fill', hexColour); @@ -923,6 +960,51 @@ Blockly.BlockSvg.prototype.updateColour = function() { } }; +/** + * Sets the colour of the border. + * Removes the light and dark paths if a tertiary colour is defined. + * @param {!string} rgb Colour of the block. + * @param {?string} colourTertiary Colour of the border. + */ +Blockly.BlockSvg.prototype.setBorderColour_ = function(rgb, colourTertiary) { + if (colourTertiary) { + this.svgPathLight_.setAttribute('stroke', 'none'); + this.svgPathDark_.setAttribute('fill', 'none'); + this.svgPath_.setAttribute('stroke', colourTertiary); + } else { + this.svgPathLight_.style.display = ''; + var hexLight = goog.color.rgbArrayToHex(goog.color.lighten(rgb, 0.3)); + var hexDark = goog.color.rgbArrayToHex(goog.color.darken(rgb, 0.2)); + this.svgPathLight_.setAttribute('stroke', hexLight); + this.svgPathDark_.setAttribute('fill', hexDark); + this.svgPath_.setAttribute('stroke', 'none'); + + } +}; + +/** + * Sets the colour of shadow blocks. + * @param {!string} rgb Primary colour of the block. + * @param {?string} colourSecondary Colour for shadow block. + * @return {!string} hexColour The background color of the block. + */ +Blockly.BlockSvg.prototype.setShadowColour_ = function( + rgb, colourSecondary) { + var hexColour; + if (colourSecondary) { + this.svgPathLight_.style.display = 'none'; + this.svgPathDark_.style.display = 'none'; + this.svgPath_.setAttribute('fill', colourSecondary); + hexColour = colourSecondary; + } else { + rgb = goog.color.lighten(rgb, 0.6); + hexColour = goog.color.rgbArrayToHex(rgb); + this.svgPathLight_.style.display = 'none'; + this.svgPathDark_.setAttribute('fill', hexColour); + } + return hexColour; +}; + /** * Enable or disable a block. */ @@ -1035,7 +1117,8 @@ Blockly.BlockSvg.prototype.setWarningText = function(text, opt_id) { parent = parent.getSurroundParent(); } if (collapsedParent) { - collapsedParent.setWarningText(text, 'collapsed ' + this.id + ' ' + id); + collapsedParent.setWarningText(Blockly.Msg['COLLAPSED_WARNINGS_WARNING'], + Blockly.BlockSvg.COLLAPSED_WARNING_ID); } var changedState = false; diff --git a/core/blockly.js b/core/blockly.js index 366e55fdf78..a7067caa57c 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -112,6 +112,13 @@ Blockly.clipboardTypeCounts_ = null; */ Blockly.cache3dSupported_ = null; +/** + * Holds all Blockly style attributes. + * @type {?Blockly.Theme} + * @private + */ +Blockly.theme_ = null; + /** * Convert a hue (HSV model) into an RGB hex triplet. * @param {number} hue Hue on a colour wheel (0-360). @@ -326,8 +333,14 @@ Blockly.onContextMenu_ = function(e) { Blockly.hideChaff = function(opt_allowToolbox) { Blockly.Tooltip.hide(); Blockly.WidgetDiv.hide(); + // For now the trashcan flyout always autocloses because it overlays the + // trashcan UI (no trashcan to click to close it) + var workspace = Blockly.getMainWorkspace(); + if (workspace.trashcan && + workspace.trashcan.flyout_) { + workspace.trashcan.flyout_.hide(); + } if (!opt_allowToolbox) { - var workspace = Blockly.getMainWorkspace(); if (workspace.toolbox_ && workspace.toolbox_.flyout_ && workspace.toolbox_.flyout_.autoClose) { @@ -667,6 +680,64 @@ Blockly.checkBlockColourConstant_ = function( } }; + +/** + * Sets the theme for blockly and refreshes all blocks in the toolbox and workspace. + * @param {Blockly.Theme} theme Theme for blockly. + */ +Blockly.setTheme = function(theme) { + this.theme_ = theme; + var ws = Blockly.getMainWorkspace(); + + //update all blocks in workspace that have a style name + this.updateBlockStyles_(ws.getAllBlocks().filter( + function(block){ + return block.getStyleName() !== undefined; + } + )); + + //update blocks in the flyout + if (!ws.toolbox_ && ws.flyout_ && ws.flyout_.workspace_) { + this.updateBlockStyles_(ws.flyout_.workspace_.getAllBlocks()); + } else { + ws.refreshToolboxSelection(); + } + + //update colours on the categories + if (ws.toolbox_) { + ws.toolbox_.updateColourFromTheme(); + } + + var event = new Blockly.Events.Ui(null, 'theme'); + event.workspaceId = ws.id; + Blockly.Events.fire(event); +}; + +/** + * Updates all the blocks with new style. + * @param {!Array.} blocks List of blocks to update the style on. + * @private + */ +Blockly.updateBlockStyles_ = function(blocks) { + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i]; + var blockStyleName = block.getStyleName(); + + block.setStyle(blockStyleName); + if (block.mutator) { + block.mutator.updateBlockStyle(blockStyleName); + } + } +}; + +/** + * Gets the theme. + * @return {?Blockly.Theme} theme Theme for blockly. + */ +Blockly.getTheme = function() { + return this.theme_; +}; + // IE9 does not have a console. Create a stub to stop errors. if (!goog.global['console']) { goog.global['console'] = { diff --git a/core/bubble.js b/core/bubble.js index a0a7d67c7ea..2605cbae507 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -401,22 +401,121 @@ Blockly.Bubble.prototype.setAnchorLocation = function(xy) { * @private */ Blockly.Bubble.prototype.layoutBubble_ = function() { - // Compute the preferred bubble location. - var relativeLeft = -this.width_ / 4; - var relativeTop = -this.height_ - Blockly.BlockSvg.MIN_BLOCK_Y; - // Prevent the bubble from being off-screen. var metrics = this.workspace_.getMetrics(); metrics.viewWidth /= this.workspace_.scale; metrics.viewLeft /= this.workspace_.scale; + var optimalLeft = this.getOptimalRelativeLeft_(metrics); + var optimalTop = this.getOptimalRelativeTop_(metrics); + var bbox = this.shape_.getBBox(); + + var topPosition = {x: optimalLeft, + y: -this.height_ - Blockly.BlockSvg.MIN_BLOCK_Y}; + var startPosition = {x: -this.width_ - 30, y: optimalTop}; + var endPosition = {x: bbox.width, y: optimalTop}; + var bottomPosition = {x: optimalLeft, y: bbox.height}; + + var closerPosition = bbox.width < bbox.height ? endPosition : bottomPosition; + var fartherPosition = bbox.width < bbox.height ? bottomPosition : endPosition; + + var topPositionOverlap = this.getOverlap_(topPosition, metrics); + var startPositionOverlap = this.getOverlap_(startPosition, metrics); + var closerPositionOverlap = this.getOverlap_(closerPosition, metrics); + var fartherPositionOverlap = this.getOverlap_(fartherPosition, metrics); + + // Set the position to whichever position shows the most of the bubble, + // with tiebreaks going in the order: top > start > close > far. + var mostOverlap = Math.max(topPositionOverlap, startPositionOverlap, + closerPositionOverlap, fartherPositionOverlap); + if (topPositionOverlap == mostOverlap) { + this.relativeLeft_ = topPosition.x; + this.relativeTop_ = topPosition.y; + return; + } + if (startPositionOverlap == mostOverlap) { + this.relativeLeft_ = startPosition.x; + this.relativeTop_ = startPosition.y; + return; + } + if (closerPositionOverlap == mostOverlap) { + this.relativeLeft_ = closerPosition.x; + this.relativeTop_ = closerPosition.y; + return; + } + this.relativeLeft_ = fartherPosition.x; + this.relativeTop_ = fartherPosition.y; +}; + +/** + * Calculate the what percentage of the bubble overlaps with the visible + * workspace (what percentage of the bubble is visible). + * @param {!Object} relativeMin The position of the top-left corner of the + * bubble relative to the anchor point. + * @param {!number} relativeMin.x The x-position of the relativeMin. + * @param {!number} relativeMin.y The y-position of the relativeMin. + * @param {!Object} metrics The metrics of the workspace the bubble will + * appear in. + * @returns {number} The percentage of the bubble that is visible. + * @private + */ +Blockly.Bubble.prototype.getOverlap_ = function(relativeMin, metrics) { + // The position of the top-start corner of the bubble in workspace units. + var bubbleMin = { + x: relativeMin.x + (this.workspace_.RTL ? + metrics.viewWidth - this.anchorXY_.x : + this.anchorXY_.x), + y: relativeMin.y + this.anchorXY_.y + }; + // The position of the bottom-end corner of the bubble in workspace units. + var bubbleMax = { + x: bubbleMin.x + this.width_, + y: bubbleMin.y + this.height_ + }; + // The position of the top-start corner of the workspace. + var workspaceMin = { + x: this.workspace_.RTL ? -metrics.viewLeft : metrics.viewLeft, + y: metrics.viewTop + }; + // The position of the bottom-end corner of the workspace. + var workspaceMax = { + x: workspaceMin.x + metrics.viewWidth, + y: workspaceMin.y + metrics.viewHeight + }; + + var overlapWidth = Math.min(bubbleMax.x, workspaceMax.x) - + Math.max(bubbleMin.x, workspaceMin.x); + var overlapHeight = Math.min(bubbleMax.y, workspaceMax.y) - + Math.max(bubbleMin.y, workspaceMin.y); + return Math.max(0, Math.min(1, + (overlapWidth * overlapHeight) / (this.width_ * this.height_))); +}; + +/** + * Calculate what the optimal horizontal position of the top-left corner of the + * bubble is (relative to the anchor point) so that the most area of the + * bubble is shown. + * @param {!Object} metrics The metrics of the workspace the bubble will + * appear in. + * @returns {number} The optimal horizontal position of the top-left corner + * of the bubble. + * @private + */ +Blockly.Bubble.prototype.getOptimalRelativeLeft_ = function(metrics) { + var relativeLeft = -this.width_ / 4; + + // No amount of sliding left or right will give us a better overlap. + if (this.width_ > metrics.viewWidth) { + return relativeLeft; + } + var anchorX = this.anchorXY_.x; if (this.workspace_.RTL) { if (anchorX - metrics.viewLeft - relativeLeft - this.width_ < Blockly.Scrollbar.scrollbarThickness) { // Slide the bubble right until it is onscreen. relativeLeft = anchorX - metrics.viewLeft - this.width_ - - Blockly.Scrollbar.scrollbarThickness; + Blockly.Scrollbar.scrollbarThickness; } else if (anchorX - metrics.viewLeft - relativeLeft > - metrics.viewWidth) { + metrics.viewWidth) { // Slide the bubble left until it is onscreen. relativeLeft = anchorX - metrics.viewLeft - metrics.viewWidth; } @@ -433,13 +532,42 @@ Blockly.Bubble.prototype.layoutBubble_ = function() { this.width_ - Blockly.Scrollbar.scrollbarThickness; } } - if (this.anchorXY_.y + relativeTop < metrics.viewTop) { - // Slide the bubble below the block. - var bBox = /** @type {SVGLocatable} */ (this.shape_).getBBox(); - relativeTop = bBox.height; + + return relativeLeft; +}; + +/** + * Calculate what the optimal vertical position of the top-left corner of + * the bubble is (relative to the anchor point) so that the most area of the + * bubble is shown. + * @param {!Object} metrics The metrics of the workspace the bubble will + * appear in. + * @returns {number} The optimal vertical position of the top-left corner + * of the bubble. + * @private + */ +Blockly.Bubble.prototype.getOptimalRelativeTop_ = function(metrics) { + var relativeTop = -this.height_ / 4; + + // No amount of sliding up or down will give us a better overlap. + if (this.height_ > metrics.viewHeight) { + return relativeTop; } - this.relativeLeft_ = relativeLeft; - this.relativeTop_ = relativeTop; + + var anchorY = this.anchorXY_.y; + if (anchorY + relativeTop < metrics.viewTop) { + // Slide the bubble down until it is onscreen. + relativeTop = metrics.viewTop - anchorY; + } else if (metrics.viewTop + metrics.viewHeight < + anchorY + relativeTop + this.height_ + + Blockly.BlockSvg.SEP_SPACE_Y + + Blockly.Scrollbar.scrollbarThickness) { + // Slide the bubble up until it is onscreen. + relativeTop = metrics.viewTop + metrics.viewHeight - anchorY - + this.height_ - Blockly.Scrollbar.scrollbarThickness; + } + + return relativeTop; }; /** diff --git a/core/connection.js b/core/connection.js index 6e978dedc82..76ccb40b9fb 100644 --- a/core/connection.js +++ b/core/connection.js @@ -392,7 +392,9 @@ Blockly.Connection.prototype.isConnectionAllowed = function(candidate) { case Blockly.OUTPUT_VALUE: { // Don't offer to connect an already connected left (male) value plug to // an available right (female) value plug. - if (candidate.isConnected() || this.isConnected()) { + if ((candidate.isConnected() && + !candidate.targetBlock().isInsertionMarker()) || + this.isConnected()) { return false; } break; diff --git a/core/connection_db.js b/core/connection_db.js index f1968f957c7..476b824bbc9 100644 --- a/core/connection_db.js +++ b/core/connection_db.js @@ -36,15 +36,14 @@ goog.require('Blockly.Connection'); * @constructor */ Blockly.ConnectionDB = function() { + /** + * Array of connections sorted by y coordinate. + * @type {!Array.} + * @private + */ + this.connections_ = []; }; -Blockly.ConnectionDB.prototype = new Array(); -/** - * Don't inherit the constructor from Array. - * @type {!Function} - */ -Blockly.ConnectionDB.constructor = Blockly.ConnectionDB; - /** * Add a connection to the database. Must not already exist in DB. * @param {!Blockly.Connection} connection The connection to be added. @@ -58,7 +57,7 @@ Blockly.ConnectionDB.prototype.addConnection = function(connection) { return; } var position = this.findPositionForConnection_(connection); - this.splice(position, 0, connection); + this.connections_.splice(position, 0, connection); connection.inDB_ = true; }; @@ -71,12 +70,12 @@ Blockly.ConnectionDB.prototype.addConnection = function(connection) { * not found. */ Blockly.ConnectionDB.prototype.findConnection = function(conn) { - if (!this.length) { + if (!this.connections_.length) { return -1; } var bestGuess = this.findPositionForConnection_(conn); - if (bestGuess >= this.length) { + if (bestGuess >= this.connections_.length) { // Not in list return -1; } @@ -85,15 +84,16 @@ Blockly.ConnectionDB.prototype.findConnection = function(conn) { // Walk forward and back on the y axis looking for the connection. var pointerMin = bestGuess; var pointerMax = bestGuess; - while (pointerMin >= 0 && this[pointerMin].y_ == yPos) { - if (this[pointerMin] == conn) { + while (pointerMin >= 0 && this.connections_[pointerMin].y_ == yPos) { + if (this.connections_[pointerMin] == conn) { return pointerMin; } pointerMin--; } - while (pointerMax < this.length && this[pointerMax].y_ == yPos) { - if (this[pointerMax] == conn) { + while (pointerMax < this.connections_.length && + this.connections_[pointerMax].y_ == yPos) { + if (this.connections_[pointerMax] == conn) { return pointerMax; } pointerMax++; @@ -111,16 +111,16 @@ Blockly.ConnectionDB.prototype.findConnection = function(conn) { */ Blockly.ConnectionDB.prototype.findPositionForConnection_ = function( connection) { - if (!this.length) { + if (!this.connections_.length) { return 0; } var pointerMin = 0; - var pointerMax = this.length; + var pointerMax = this.connections_.length; while (pointerMin < pointerMax) { var pointerMid = Math.floor((pointerMin + pointerMax) / 2); - if (this[pointerMid].y_ < connection.y_) { + if (this.connections_[pointerMid].y_ < connection.y_) { pointerMin = pointerMid + 1; - } else if (this[pointerMid].y_ > connection.y_) { + } else if (this.connections_[pointerMid].y_ > connection.y_) { pointerMax = pointerMid; } else { pointerMin = pointerMid; @@ -144,7 +144,7 @@ Blockly.ConnectionDB.prototype.removeConnection_ = function(connection) { throw Error('Unable to find connection in connectionDB.'); } connection.inDB_ = false; - this.splice(removalIndex, 1); + this.connections_.splice(removalIndex, 1); }; /** @@ -156,7 +156,7 @@ Blockly.ConnectionDB.prototype.removeConnection_ = function(connection) { * @return {!Array.} List of connections. */ Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) { - var db = this; + var db = this.connections_; var currentX = connection.x_; var currentY = connection.y_; @@ -218,7 +218,7 @@ Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) { * @private */ Blockly.ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) { - return (Math.abs(this[index].y_ - baseY) <= maxRadius); + return (Math.abs(this.connections_[index].y_ - baseY) <= maxRadius); }; /** @@ -235,7 +235,7 @@ Blockly.ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) { Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, dxy) { // Don't bother. - if (!this.length) { + if (!this.connections_.length) { return {connection: null, radius: maxRadius}; } @@ -258,7 +258,7 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, // Walk forward and back on the y axis looking for the closest x,y point. var pointerMin = closestIndex - 1; while (pointerMin >= 0 && this.isInYRange_(pointerMin, conn.y_, maxRadius)) { - temp = this[pointerMin]; + temp = this.connections_[pointerMin]; if (conn.isConnectionAllowed(temp, bestRadius)) { bestConnection = temp; bestRadius = temp.distanceFrom(conn); @@ -267,9 +267,9 @@ Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, } var pointerMax = closestIndex; - while (pointerMax < this.length && this.isInYRange_(pointerMax, conn.y_, - maxRadius)) { - temp = this[pointerMax]; + while (pointerMax < this.connections_.length && + this.isInYRange_(pointerMax, conn.y_, maxRadius)) { + temp = this.connections_[pointerMax]; if (conn.isConnectionAllowed(temp, bestRadius)) { bestConnection = temp; bestRadius = temp.distanceFrom(conn); diff --git a/core/css.js b/core/css.js index 94a75f723b9..66a789e9bdc 100644 --- a/core/css.js +++ b/core/css.js @@ -584,6 +584,7 @@ Blockly.Css.CONTENT = [ '.blocklyContextMenu {', 'border-radius: 4px;', + 'max-height: 100%;', '}', '.blocklyDropdownMenu {', diff --git a/core/events.js b/core/events.js index 8fac899025c..4dd4158ee7f 100644 --- a/core/events.js +++ b/core/events.js @@ -149,6 +149,11 @@ Blockly.Events.COMMENT_CHANGE = 'comment_change'; */ Blockly.Events.COMMENT_MOVE = 'comment_move'; +/** + * Name of event that records a workspace load. + */ +Blockly.Events.FINISHED_LOADING = 'finished_loading'; + /** * List of events queued for firing. * @private diff --git a/core/field.js b/core/field.js index a45ad938d34..d6eacaf3740 100644 --- a/core/field.js +++ b/core/field.js @@ -204,8 +204,6 @@ Blockly.Field.prototype.init = function() { this.mouseDownWrapper_ = Blockly.bindEventWithChecks_( this.fieldGroup_, 'mousedown', this, this.onMouseDown_); - // Force a render. - this.render_(); }; /** @@ -375,7 +373,7 @@ Blockly.Field.prototype.render_ = function() { }; /** - * Updates thw width of the field. This calls getCachedWidth which won't cache + * Updates the width of the field. This calls getCachedWidth which won't cache * the approximated width on IE/Edge when `getComputedTextLength` fails. Once * it eventually does succeed, the result will be cached. */ diff --git a/core/field_image.js b/core/field_image.js index 01ffb23d0d0..e32bf7c1c9b 100644 --- a/core/field_image.js +++ b/core/field_image.js @@ -40,10 +40,12 @@ goog.require('goog.math.Size'); * @param {string=} opt_alt Optional alt text for when block is collapsed. * @param {Function=} opt_onClick Optional function to be called when the image * is clicked. If opt_onClick is defined, opt_alt must also be defined. + * @param {boolean=} opt_flipRtl Whether to flip the icon in RTL. * @extends {Blockly.Field} * @constructor */ -Blockly.FieldImage = function(src, width, height, opt_alt, opt_onClick) { +Blockly.FieldImage = function(src, width, height, + opt_alt, opt_onClick, opt_flipRtl) { this.sourceBlock_ = null; // Ensure height and width are numbers. Strings are bad at math. @@ -51,9 +53,10 @@ Blockly.FieldImage = function(src, width, height, opt_alt, opt_onClick) { this.width_ = Number(width); this.size_ = new goog.math.Size(this.width_, this.height_ + 2 * Blockly.BlockSvg.INLINE_PADDING_Y); - this.text_ = opt_alt || ''; + this.flipRtl_ = opt_flipRtl; this.tooltip_ = ''; this.setValue(src); + this.setText(opt_alt); if (typeof opt_onClick == 'function') { this.clickHandler_ = opt_onClick; @@ -64,8 +67,8 @@ goog.inherits(Blockly.FieldImage, Blockly.Field); /** * Construct a FieldImage from a JSON arg object, * dereferencing any string table references. - * @param {!Object} options A JSON object with options (src, width, height, and - * alt). + * @param {!Object} options A JSON object with options (src, width, height, + * alt, and flipRtl). * @returns {!Blockly.FieldImage} The new field instance. * @package * @nocollapse @@ -76,7 +79,8 @@ Blockly.FieldImage.fromJson = function(options) { var height = Number(Blockly.utils.replaceMessageReferences(options['height'])); var alt = Blockly.utils.replaceMessageReferences(options['alt']); - return new Blockly.FieldImage(src, width, height, alt); + var flipRtl = !!options['flipRtl']; + return new Blockly.FieldImage(src, width, height, alt, null, flipRtl); }; /** @@ -107,6 +111,7 @@ Blockly.FieldImage.prototype.init = function() { }, this.fieldGroup_); this.setValue(this.src_); + this.setText(this.text_); this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); if (this.tooltip_) { @@ -182,6 +187,14 @@ Blockly.FieldImage.prototype.setValue = function(src) { } }; +/** + * Get whether to flip this image in RTL + * @return {boolean} True if we should flip in RTL. + */ +Blockly.FieldImage.prototype.getFlipRtl = function() { + return this.flipRtl_; +}; + /** * Set the alt text of this image. * @param {?string} alt New alt text. @@ -193,6 +206,9 @@ Blockly.FieldImage.prototype.setText = function(alt) { return; } this.text_ = alt; + if (this.imageElement_) { + this.imageElement_.setAttribute('alt', alt || ''); + } }; /** diff --git a/core/flyout_base.js b/core/flyout_base.js index 5be9802b75b..f1ad80f978a 100644 --- a/core/flyout_base.js +++ b/core/flyout_base.js @@ -388,6 +388,10 @@ Blockly.Flyout.prototype.positionAt_ = function(width, height, x, y) { // Set the scrollbars origin to be the top left of the flyout. this.scrollbar_.setOrigin(x, y); this.scrollbar_.resize(); + // Set the position again so that if the metrics were the same (and the + // resize failed) our position is still updated. + this.scrollbar_.setPosition_( + this.scrollbar_.position_.x, this.scrollbar_.position_.y); } }; @@ -595,6 +599,18 @@ Blockly.Flyout.prototype.onMouseDown_ = function(e) { } }; +/** + * Does this flyout allow you to create a new instance of the given block? + * Used for deciding if a block can be "dragged out of" the flyout. + * @param {!Blockly.BlockSvg} block The block to copy from the flyout. + * @return {!boolean} True if you can create a new instance of the block, false + * otherwise. + * @package + */ +Blockly.Flyout.prototype.isBlockCreatable_ = function(block) { + return !block.disabled; +}; + /** * Create a copy of this block on the workspace. * @param {!Blockly.BlockSvg} originalBlock The block to copy from the flyout. @@ -728,8 +744,12 @@ Blockly.Flyout.prototype.filterForCapacity_ = function() { var blocks = this.workspace_.getTopBlocks(false); for (var i = 0, block; block = blocks[i]; i++) { if (this.permanentlyDisabled_.indexOf(block) == -1) { - block.setDisabled(!this.targetWorkspace_ - .isCapacityAvailable(Blockly.utils.getBlockTypeCounts(block))); + var disable = !this.targetWorkspace_ + .isCapacityAvailable(Blockly.utils.getBlockTypeCounts(block)); + while (block) { + block.setDisabled(disable); + block = block.getNextBlock(); + } } } }; diff --git a/core/flyout_button.js b/core/flyout_button.js index c81e86ed217..4cd12fab944 100644 --- a/core/flyout_button.js +++ b/core/flyout_button.js @@ -75,21 +75,11 @@ Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) { this.isLabel_ = isLabel; /** - * Function to call when this button is clicked. - * @type {function(!Blockly.FlyoutButton)} + * The key to the function called when this button is clicked. + * @type {string} * @private */ - this.callback_ = null; - - var callbackKey = xml.getAttribute('callbackKey'); - if (this.isLabel_ && callbackKey) { - console.warn('Labels should not have callbacks. Label text: ' + this.text_); - } else if (!this.isLabel_ && - !(callbackKey && targetWorkspace.getButtonCallback(callbackKey))) { - console.warn('Buttons should have callbacks. Button text: ' + this.text_); - } else { - this.callback_ = targetWorkspace.getButtonCallback(callbackKey); - } + this.callbackKey_ = xml.getAttribute('callbackKey'); /** * If specified, a CSS class to add to this button. @@ -257,8 +247,12 @@ Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) { gesture.cancel(); } - // Call the callback registered to this button. - if (this.callback_) { - this.callback_(this); + if (this.isLabel_ && this.callbackKey_) { + console.warn('Labels should not have callbacks. Label text: ' + this.text_); + } else if (!this.isLabel_ && !(this.callbackKey_ && + this.targetWorkspace_.getButtonCallback(this.callbackKey_))) { + console.warn('Buttons should have callbacks. Button text: ' + this.text_); + } else if (!this.isLabel_) { + this.targetWorkspace_.getButtonCallback(this.callbackKey_)(this); } }; diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js index 3129e538612..ac3c3dc1f6a 100644 --- a/core/flyout_horizontal.js +++ b/core/flyout_horizontal.js @@ -103,8 +103,8 @@ Blockly.HorizontalFlyout.prototype.getMetrics_ = function() { contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale, viewTop: -this.workspace_.scrollY, viewLeft: -this.workspace_.scrollX, - contentTop: optionBox.y, - contentLeft: optionBox.x, + contentTop: 0, + contentLeft: 0, absoluteTop: absoluteTop, absoluteLeft: absoluteLeft }; @@ -217,7 +217,7 @@ Blockly.HorizontalFlyout.prototype.scrollToStart = function() { * @private */ Blockly.HorizontalFlyout.prototype.wheel_ = function(e) { - var delta = e.deltaX; + var delta = e.deltaX || e.deltaY; if (delta) { // Firefox's mouse wheel deltas are a tenth that of Chrome/Safari. @@ -225,7 +225,6 @@ Blockly.HorizontalFlyout.prototype.wheel_ = function(e) { if (goog.userAgent.GECKO && (e.deltaMode === 1)) { delta *= 10; } - // TODO: #1093 var metrics = this.getMetrics_(); var pos = metrics.viewLeft + delta; var limit = metrics.contentWidth - metrics.viewWidth; @@ -367,8 +366,6 @@ Blockly.HorizontalFlyout.prototype.reflowInternal_ = function() { } // Record the height for .getMetrics_ and .position. this.height_ = flyoutHeight; - // Call this since it is possible the trash and zoom buttons need - // to move. e.g. on a bottom positioned flyout when zoom is clicked. - this.targetWorkspace_.resize(); + this.position(); } }; diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js index 2f6e79de888..65886f384ec 100644 --- a/core/flyout_vertical.js +++ b/core/flyout_vertical.js @@ -392,8 +392,6 @@ Blockly.VerticalFlyout.prototype.reflowInternal_ = function() { } // Record the width for .getMetrics_ and .position. this.width_ = flyoutWidth; - // Call this since it is possible the trash and zoom buttons need - // to move. e.g. on a bottom positioned flyout when zoom is clicked. - this.targetWorkspace_.resize(); + this.position(); } }; diff --git a/core/gesture.js b/core/gesture.js index 0f8c6782054..a8e1177e0cb 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -327,8 +327,7 @@ Blockly.Gesture.prototype.updateDragDelta_ = function(currentXY) { * @private */ Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() { - // Disabled blocks may not be dragged from the flyout. - if (this.targetBlock_.disabled) { + if (!this.flyout_.isBlockCreatable_(this.targetBlock_)) { return false; } if (!this.flyout_.isScrollable() || @@ -941,3 +940,17 @@ Blockly.Gesture.prototype.isDragging = function() { Blockly.Gesture.prototype.hasStarted = function() { return this.hasStarted_; }; + +/** + * Get a list of the insertion markers that currently exist. Block drags have + * 0, 1, or 2 insertion markers. + * @return {!Array.} A possibly empty list of insertion + * marker blocks. + * @package + */ +Blockly.Gesture.prototype.getInsertionMarkers = function() { + if (this.blockDragger_) { + return this.blockDragger_.getInsertionMarkers(); + } + return []; +}; diff --git a/core/inject.js b/core/inject.js index df7b481798a..2be8fff630a 100644 --- a/core/inject.js +++ b/core/inject.js @@ -69,10 +69,13 @@ Blockly.inject = function(container, opt_options) { var workspace = Blockly.createMainWorkspace_(svg, options, blockDragSurface, workspaceDragSurface); + Blockly.setTheme(options.theme); + Blockly.init_(workspace); Blockly.mainWorkspace = workspace; Blockly.svgResize(workspace); + return workspace; }; @@ -218,6 +221,12 @@ Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface, var flyout = mainWorkspace.addFlyout_('svg'); Blockly.utils.insertAfter(flyout, svg); } + if (options.hasTrashcan) { + mainWorkspace.addTrashcan(); + } + if (options.zoomOptions && options.zoomOptions.controls) { + mainWorkspace.addZoomControls(); + } // A null translation will also apply the correct initial scale. mainWorkspace.translate(0, 0); @@ -339,6 +348,14 @@ Blockly.init_ = function(mainWorkspace) { } } + var verticalSpacing = Blockly.Scrollbar.scrollbarThickness; + if (options.hasTrashcan) { + verticalSpacing = mainWorkspace.trashcan.init(verticalSpacing); + } + if (options.zoomOptions && options.zoomOptions.controls) { + mainWorkspace.zoomControls_.init(verticalSpacing); + } + if (options.hasScrollbars) { mainWorkspace.scrollbar = new Blockly.ScrollbarPair(mainWorkspace); mainWorkspace.scrollbar.resize(); diff --git a/core/insertion_marker_manager.js b/core/insertion_marker_manager.js index f94677c8d99..367fff46ac6 100644 --- a/core/insertion_marker_manager.js +++ b/core/insertion_marker_manager.js @@ -696,3 +696,21 @@ Blockly.InsertionMarkerManager.prototype.connectMarker_ = function() { }; /**** End insertion marker display functions ****/ + +/** + * Get a list of the insertion markers that currently exist. Drags have 0, 1, + * or 2 insertion markers. + * @return {!Array.} A possibly empty list of insertion + * marker blocks. + * @package + */ +Blockly.InsertionMarkerManager.prototype.getInsertionMarkers = function() { + var result = []; + if (this.firstMarker_) { + result.push(this.firstMarker_); + } + if (this.lastMarker_) { + result.push(this.lastMarker_); + } + return result; +}; diff --git a/core/mutator.js b/core/mutator.js index b1367749cf2..004be699685 100644 --- a/core/mutator.js +++ b/core/mutator.js @@ -397,6 +397,28 @@ Blockly.Mutator.prototype.dispose = function() { Blockly.Icon.prototype.dispose.call(this); }; +/** + * Update the styles on all blocks in the mutator. + * @public + */ +Blockly.Mutator.prototype.updateBlockStyle = function() { + var ws = this.workspace_; + + if (ws && ws.getAllBlocks()){ + var workspaceBlocks = ws.getAllBlocks(); + for (var i = 0; i < workspaceBlocks.length; i++) { + var block = workspaceBlocks[i]; + block.setStyle(block.getStyleName()); + } + + var flyoutBlocks = ws.flyout_.workspace_.getAllBlocks(); + for (var i = 0; i < flyoutBlocks.length; i++) { + var block = flyoutBlocks[i]; + block.setStyle(block.getStyleName()); + } + } +}; + /** * Reconnect an block to a mutated input. * @param {Blockly.Connection} connectionChild Connection on child block. diff --git a/core/options.js b/core/options.js index 0b91d9d647e..d6503d3a714 100644 --- a/core/options.js +++ b/core/options.js @@ -27,6 +27,7 @@ goog.provide('Blockly.Options'); goog.require('Blockly.Xml'); +goog.require('Blockly.Themes.Classic'); /** @@ -54,6 +55,14 @@ Blockly.Options = function(options) { if (hasTrashcan === undefined) { hasTrashcan = hasCategories; } + var maxTrashcanContents = options['maxTrashcanContents']; + if (hasTrashcan) { + if (maxTrashcanContents === undefined) { + maxTrashcanContents = 32; + } + } else { + maxTrashcanContents = 0; + } var hasCollapse = options['collapse']; if (hasCollapse === undefined) { hasCollapse = hasCategories; @@ -111,6 +120,10 @@ Blockly.Options = function(options) { } else { var oneBasedIndex = !!options['oneBasedIndex']; } + var theme = options['theme']; + if (theme === undefined) { + theme = Blockly.Themes.Classic; + } this.RTL = rtl; this.oneBasedIndex = oneBasedIndex; @@ -124,6 +137,7 @@ Blockly.Options = function(options) { this.hasCategories = hasCategories; this.hasScrollbars = hasScrollbars; this.hasTrashcan = hasTrashcan; + this.maxTrashcanContents = maxTrashcanContents; this.hasSounds = hasSounds; this.hasCss = hasCss; this.horizontalLayout = horizontalLayout; @@ -131,6 +145,7 @@ Blockly.Options = function(options) { this.gridOptions = Blockly.Options.parseGridOptions_(options); this.zoomOptions = Blockly.Options.parseZoomOptions_(options); this.toolboxPosition = toolboxPosition; + this.theme = theme; }; /** diff --git a/core/theme.js b/core/theme.js new file mode 100644 index 00000000000..993e59bc41c --- /dev/null +++ b/core/theme.js @@ -0,0 +1,93 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview The class representing a theme. + */ +'use strict'; + +goog.provide('Blockly.Theme'); +/** + * Class for a theme. + * @param {?Object.} blockStyles A map from style + * names (strings) to objects with style attributes relating to blocks. + * @param {?Object.} categoryStyles A map from style + * names (strings) to objects with style attributes relating to categories. + * @constructor + */ +Blockly.Theme = function(blockStyles, categoryStyles) { + this.blockStyles_ = blockStyles; + this.categoryStyles_ = categoryStyles; +}; + +/** + * Overrides or adds all values from blockStyles to blockStyles_ + * @param {Object.} blockStyles List of + * block styles. + */ +Blockly.Theme.prototype.setAllBlockStyles = function(blockStyles) { + for (var key in blockStyles) { + this.setBlockStyle(key, blockStyles[key]); + } +}; + +/** + * Gets a list of all the block style names. + * @return{Array.} styleName List of blockstyle names. + */ +Blockly.Theme.prototype.getAllBlockStyles = function() { + return this.blockStyles_; +}; + +/** + * Gets the BlockStyle for the given block style name. + * @param{String} blockStyleName The name of the block style. + * @return {Blockly.BlockStyle} The style with the block style name. + */ +Blockly.Theme.prototype.getBlockStyle = function(blockStyleName) { + return this.blockStyles_[blockStyleName]; +}; + +/** + * Overrides or adds a style to the blockStyles map. + * @param{String} blockStyleName The name of the block style. + * @param{Blockly.BlockStyle} blockStyle The block style +*/ +Blockly.Theme.prototype.setBlockStyle = function(blockStyleName, blockStyle) { + this.blockStyles_[blockStyleName] = blockStyle; +}; + +/** + * Gets the CategoryStyle for the given category style name. + * @param{String} categoryStyleName The name of the block style. + * @return {Blockly.CategoryStyle} The style with the block style name. + */ +Blockly.Theme.prototype.getCategoryStyle = function(categoryStyleName) { + return this.categoryStyles_[categoryStyleName]; +}; + +/** + * Overrides or adds a style to the categoryStyles map. + * @param{String} categoryStyleName The name of the category style. + * @param{Blockly.CategoryStyle} categoryStyle The category style +*/ +Blockly.Theme.prototype.setCategoryStyle = function(categoryStyleName, categoryStyle) { + this.categoryStyles_[categoryStyleName] = categoryStyle; +}; diff --git a/core/theme/classic.js b/core/theme/classic.js new file mode 100644 index 00000000000..522e444409c --- /dev/null +++ b/core/theme/classic.js @@ -0,0 +1,96 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 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 Classic theme. + * Contains multi colored border to create shadow effect. + */ + +'use strict'; + +goog.provide('Blockly.Themes.Classic'); + +goog.require('Blockly.Theme'); + +var defaultBlockStyles = { + "colour_blocks":{ + "colourPrimary": "20" + }, + "list_blocks": { + "colourPrimary": "260" + }, + "logic_blocks": { + "colourPrimary": "210" + }, + "loop_blocks": { + "colourPrimary": "120" + }, + "math_blocks": { + "colourPrimary": "230" + }, + "procedure_blocks": { + "colourPrimary": "290" + }, + "text_blocks": { + "colourPrimary": "160" + }, + "variable_blocks": { + "colourPrimary": "330" + }, + "variable_dynamic_blocks":{ + "colourPrimary": "310" + }, + "hat_blocks":{ + "colourPrimary":"330", + "hat":"cap" + } +}; + +var categoryStyles = { + "colour_category":{ + "colour": "20" + }, + "list_category": { + "colour": "260" + }, + "logic_category": { + "colour": "210" + }, + "loop_category": { + "colour": "120" + }, + "math_category": { + "colour": "230" + }, + "procedure_category": { + "colour": "290" + }, + "text_category": { + "colour": "160" + }, + "variable_category": { + "colour": "330" + }, + "variable_dynamic_category":{ + "colour": "310" + } +}; + +Blockly.Themes.Classic = new Blockly.Theme(defaultBlockStyles, categoryStyles); diff --git a/core/theme/highcontrast.js b/core/theme/highcontrast.js new file mode 100644 index 00000000000..93c88ecf95c --- /dev/null +++ b/core/theme/highcontrast.js @@ -0,0 +1,116 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 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 High contrast theme. + * Darker colors to contrast the white font. + */ +'use strict'; + +goog.provide('Blockly.Themes.HighContrast'); + +goog.require('Blockly.Theme'); + +var defaultBlockStyles = { + "colour_blocks":{ + "colourPrimary": "#a52714", + "colourSecondary":"#FB9B8C", + "colourTertiary":"#FBE1DD" + }, + "list_blocks": { + "colourPrimary": "#4a148c", + "colourSecondary":"#AD7BE9", + "colourTertiary":"#CDB6E9" + }, + "logic_blocks": { + "colourPrimary": "#01579b", + "colourSecondary":"#64C7FF", + "colourTertiary":"#C5EAFF" + }, + "loop_blocks": { + "colourPrimary": "#33691e", + "colourSecondary":"#9AFF78", + "colourTertiary":"#E1FFD7" + }, + "math_blocks": { + "colourPrimary": "#1a237e", + "colourSecondary":"#8A9EFF", + "colourTertiary":"#DCE2FF" + }, + "procedure_blocks": { + "colourPrimary": "#006064", + "colourSecondary":"#77E6EE", + "colourTertiary":"#CFECEE" + }, + "text_blocks": { + "colourPrimary": "#004d40", + "colourSecondary":"#5ae27c", + "colourTertiary":"#D2FFDD" + }, + "variable_blocks": { + "colourPrimary": "#880e4f", + "colourSecondary":"#FF73BE", + "colourTertiary":"#FFD4EB" + }, + "variable_dynamic_blocks": { + "colourPrimary": "#880e4f", + "colourSecondary":"#FF73BE", + "colourTertiary":"#FFD4EB" + }, + "hat_blocks" : { + "colourPrimary": "#880e4f", + "colourSecondary":"#FF73BE", + "colourTertiary":"#FFD4EB", + "hat": "cap" + } +}; + +var categoryStyles = { + "colour_category":{ + "colour": "#a52714", + }, + "list_category": { + "colour": "#4a148c", + }, + "logic_category": { + "colour": "#01579b", + }, + "loop_category": { + "colour": "#33691e", + }, + "math_category": { + "colour": "#1a237e", + }, + "procedure_category": { + "colour": "#006064", + }, + "text_category": { + "colour": "#004d40", + }, + "variable_category": { + "colour": "#880e4f", + }, + "variable_dynamic_category":{ + "colour": "#880e4f", + } +}; + +//This style is still being fleshed out and may change. +Blockly.Themes.HighContrast = new Blockly.Theme(defaultBlockStyles, categoryStyles); diff --git a/core/theme/modern.js b/core/theme/modern.js new file mode 100644 index 00000000000..d7e06cf1a79 --- /dev/null +++ b/core/theme/modern.js @@ -0,0 +1,116 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 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 Modern theme. + * Same colors as classic, but single colored border. + */ +'use strict'; + +goog.provide('Blockly.Themes.Modern'); + +goog.require('Blockly.Theme'); + +var defaultBlockStyles = { + "colour_blocks": { + "colourPrimary": "#a5745b", + "colourSecondary": "#dbc7bd", + "colourTertiary": "#845d49" + }, + "list_blocks": { + "colourPrimary": "#745ba5", + "colourSecondary": "#c7bddb", + "colourTertiary": "#5d4984" + }, + "logic_blocks": { + "colourPrimary": "#5b80a5", + "colourSecondary": "#bdccdb", + "colourTertiary": "#496684" + }, + "loop_blocks": { + "colourPrimary": "#5ba55b", + "colourSecondary": "#bddbbd", + "colourTertiary": "#498449" + }, + "math_blocks": { + "colourPrimary": "#5b67a5", + "colourSecondary": "#bdc2db", + "colourTertiary": "#495284" + }, + "procedure_blocks": { + "colourPrimary": "#995ba5", + "colourSecondary": "#d6bddb", + "colourTertiary": "#7a4984" + }, + "text_blocks": { + "colourPrimary": "#5ba58c", + "colourSecondary": "#bddbd1", + "colourTertiary": "#498470" + }, + "variable_blocks": { + "colourPrimary": "#a55b99", + "colourSecondary": "#dbbdd6", + "colourTertiary": "#84497a" + }, + "variable_dynamic_blocks": { + "colourPrimary": "#a55b99", + "colourSecondary": "#dbbdd6", + "colourTertiary": "#84497a" + }, + "hat_blocks": { + "colourPrimary": "#a55b99", + "colourSecondary": "#dbbdd6", + "colourTertiary": "#84497a", + "hat": "cap" + } +}; + +var categoryStyles = { + "colour_category":{ + "colour": "#a5745b", + }, + "list_category": { + "colour": "#745ba5", + }, + "logic_category": { + "colour": "#5b80a5", + }, + "loop_category": { + "colour": "#5ba55b", + }, + "math_category": { + "colour": "#5b67a5", + }, + "procedure_category": { + "colour": "#995ba5", + }, + "text_category": { + "colour": "#5ba58c", + }, + "variable_category": { + "colour": "#a55b99", + }, + "variable_dynamic_category":{ + "colour": "#a55b99", + } +}; + +//This style is still being fleshed out and may change. +Blockly.Themes.Modern = new Blockly.Theme(defaultBlockStyles, categoryStyles); diff --git a/core/toolbox.js b/core/toolbox.js index 685b9be0eb4..8c49c360ce2 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -332,25 +332,22 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { openNode = newOpenNode; } } - // Decode the colour for any potential message references - // (eg. `%{BKY_MATH_HUE}`). - var colour = Blockly.utils.replaceMessageReferences( - childIn.getAttribute('colour')); - if (colour === null || colour === '') { - // No attribute. No colour. - childOut.hexColour = ''; - } else if (/^#[0-9a-fA-F]{6}$/.test(colour)) { - childOut.hexColour = colour; - this.hasColours_ = true; - } else if (typeof colour === 'number' || - (typeof colour === 'string' && !isNaN(Number(colour)))) { - childOut.hexColour = Blockly.hueToRgb(Number(colour)); - this.hasColours_ = true; - } else { + + var styleName = childIn.getAttribute('style'); + var colour = childIn.getAttribute('colour'); + + if (colour && styleName) { childOut.hexColour = ''; console.warn('Toolbox category "' + categoryName + - '" has unrecognized colour attribute: ' + colour); + '" can not have both a style and a colour'); + } + else if (styleName) { + this.setColourFromStyle_(styleName, childOut, categoryName); + } + else { + this.setColour_(colour, childOut, categoryName); } + if (childIn.getAttribute('expanded') == 'true') { if (childOut.blocks.length) { // This is a category that directly contains blocks. @@ -395,6 +392,103 @@ Blockly.Toolbox.prototype.syncTrees_ = function(treeIn, treeOut, pathToMedia) { return openNode; }; +/** + * Sets the colour on the category. + * @param {number|string} colourValue HSV hue value (0 to 360), #RRGGBB string, + * or a message reference string pointing to one of those two values. + * @param {Blockly.Toolbox.TreeNode} childOut The child to set the hexColour on. + * @param {string} categoryName Name of the toolbox category. + * @private + */ +Blockly.Toolbox.prototype.setColour_ = function(colourValue, childOut, categoryName){ + // Decode the colour for any potential message references + // (eg. `%{BKY_MATH_HUE}`). + var colour = Blockly.utils.replaceMessageReferences(colourValue); + if (colour === null || colour === '') { + // No attribute. No colour. + childOut.hexColour = ''; + } else if (/^#[0-9a-fA-F]{6}$/.test(colour)) { + childOut.hexColour = colour; + this.hasColours_ = true; + } else if (typeof colour === 'number' || + (typeof colour === 'string' && !isNaN(Number(colour)))) { + childOut.hexColour = Blockly.hueToRgb(Number(colour)); + this.hasColours_ = true; + } else { + childOut.hexColour = ''; + console.warn('Toolbox category "' + categoryName + + '" has unrecognized colour attribute: ' + colour); + } +}; + +/** + * Retrieves and sets the colour for the category using the style name. + * The category colour is set from the colour style attribute. + * @param {string} styleName Name of the style. + * @param {!Blockly.Toolbox.TreeNode} childOut The child to set the hexColour on. + * @param {string} categoryName Name of the toolbox category. + */ +Blockly.Toolbox.prototype.setColourFromStyle_ = function( + styleName, childOut, categoryName){ + childOut.styleName = styleName; + if (styleName && Blockly.getTheme()) { + var style = Blockly.getTheme().getCategoryStyle(styleName); + if (style && style.colour) { + this.setColour_(style.colour, childOut, categoryName); + } + else { + console.warn('Style "' + styleName + '" must exist and contain a colour value'); + } + } +}; + +/** + * Recursively updates all the category colours using the category style name. + * @param {Blockly.Toolbox.TreeNode=} opt_tree Starting point of tree. + * Defaults to the root node. + * @private + */ +Blockly.Toolbox.prototype.updateColourFromTheme_ = function(opt_tree) { + var tree = opt_tree || this.tree_; + if (tree) { + var children = tree.getChildren(false); + for (var i = 0, child; child = children[i]; i++) { + if (child.styleName) { + this.setColourFromStyle_(child.styleName, child, ''); + this.addColour_(); + } + this.updateColourFromTheme_(child); + } + } +}; + +/** + * Updates the category colours and background colour of selected categories. + */ +Blockly.Toolbox.prototype.updateColourFromTheme = function() { + var tree = this.tree_; + if (tree) { + this.updateColourFromTheme_(tree); + this.updateSelectedItemColour_(tree); + } +}; + +/** + * Updates the background colour of the selected category. + * @param {!Blockly.Toolbox.TreeNode} tree Starting point of tree. + * Defaults to the root node. + * @private + */ +Blockly.Toolbox.prototype.updateSelectedItemColour_ = function(tree) { + var selectedItem = tree.selectedItem_; + if (selectedItem) { + var hexColour = selectedItem.hexColour || '#57e'; + selectedItem.getRowElement().style.backgroundColor = hexColour; + tree.toolbox_.addColour_(selectedItem); + } +}; + + /** * Recursively add colours to this toolbox. * @param {Blockly.Toolbox.TreeNode=} opt_tree Starting point of tree. diff --git a/core/trashcan.js b/core/trashcan.js index bd6a709543f..4bebbf59f23 100644 --- a/core/trashcan.js +++ b/core/trashcan.js @@ -37,7 +37,52 @@ goog.require('goog.math.Rect'); * @constructor */ Blockly.Trashcan = function(workspace) { + /** + * The workspace the trashcan sits in. + * @type {!Blockly.Workspace} + * @private + */ this.workspace_ = workspace; + + /** + * True if the trashcan contains blocks, otherwise false. + * @type {boolean} + * @private + */ + this.hasBlocks_ = false; + + /** + * A list of Xml (stored as strings) representing blocks "inside" the trashcan. + * @type {Array} + * @private + */ + this.contents_ = []; + + + if (this.workspace_.options.maxTrashcanContents <= 0) { + return; + } + // Create flyout options. + var flyoutWorkspaceOptions = { + scrollbars: true, + disabledPatternId: this.workspace_.options.disabledPatternId, + parentWorkspace: this.workspace_, + RTL: this.workspace_.RTL, + oneBasedIndex: this.workspace_.options.oneBasedIndex, + }; + // Create vertical or horizontal flyout. + if (this.workspace_.horizontalLayout) { + flyoutWorkspaceOptions.toolboxPosition = + this.workspace_.toolboxPosition == Blockly.TOOLBOX_AT_TOP ? + Blockly.TOOLBOX_AT_BOTTOM : Blockly.TOOLBOX_AT_TOP; + this.flyout_ = new Blockly.HorizontalFlyout(flyoutWorkspaceOptions); + } else { + flyoutWorkspaceOptions.toolboxPosition = + this.workspace_.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT ? + Blockly.TOOLBOX_AT_LEFT : Blockly.TOOLBOX_AT_RIGHT; + this.flyout_ = new Blockly.VerticalFlyout(flyoutWorkspaceOptions); + } + this.workspace_.addChangeListener(this.onDelete_()); }; /** @@ -96,12 +141,28 @@ Blockly.Trashcan.prototype.SPRITE_LEFT_ = 0; */ Blockly.Trashcan.prototype.SPRITE_TOP_ = 32; +/** + * The openness of the lid when the trashcan contains blocks. + * (0.0 = closed, 1.0 = open) + * @type {number} + * @private + */ +Blockly.Trashcan.prototype.HAS_BLOCKS_LID_ANGLE = 0.1; + /** * Current open/close state of the lid. * @type {boolean} */ Blockly.Trashcan.prototype.isOpen = false; +/** + * The minimum openness of the lid. Used to indicate if the trashcan contains + * blocks. + * @type {number} + * @private + */ +Blockly.Trashcan.prototype.minOpenness_ = 0; + /** * The SVG group containing the trash can. * @type {Element} @@ -207,19 +268,37 @@ Blockly.Trashcan.prototype.createDom = function() { this.workspace_.options.pathToMedia + Blockly.SPRITE.url); Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup', this, this.click); + // bindEventWithChecks_ quashes events too aggressively. See: + // https://groups.google.com/forum/#!topic/blockly/QF4yB9Wx00s + // Bind to body instead of this.svgGroup_ so that we don't get lid jitters + Blockly.bindEvent_(body, 'mouseover', this, this.mouseOver_); + Blockly.bindEvent_(body, 'mouseout', this, this.mouseOut_); this.animateLid_(); return this.svgGroup_; }; /** * Initialize the trash can. - * @param {number} bottom Distance from workspace bottom to bottom of trashcan. - * @return {number} Distance from workspace bottom to the top of trashcan. + * @param {number} verticalSpacing Vertical distance from workspace edge to the + * same edge of the trashcan. + * @return {number} Vertical distance from workspace edge to the opposite + * edge of the trashcan. */ -Blockly.Trashcan.prototype.init = function(bottom) { - this.bottom_ = this.MARGIN_BOTTOM_ + bottom; +Blockly.Trashcan.prototype.init = function(verticalSpacing) { + if (this.workspace_.options.maxTrashcanContents > 0) { + Blockly.utils.insertAfter(this.flyout_.createDom('svg'), + this.workspace_.getParentSvg()); + this.flyout_.init(this.workspace_); + this.flyout_.isBlockCreatable_ = function() { + // All blocks, including disabled ones, can be dragged from the + // trashcan flyout. + return true; + }; + } + + this.verticalSpacing_ = this.MARGIN_BOTTOM_ + verticalSpacing; this.setOpen_(false); - return this.bottom_ + this.BODY_HEIGHT_ + this.LID_HEIGHT_; + return this.verticalSpacing_ + this.BODY_HEIGHT_ + this.LID_HEIGHT_; }; /** @@ -237,36 +316,37 @@ Blockly.Trashcan.prototype.dispose = function() { }; /** - * Move the trash can to the bottom-right corner. + * Position the trashcan. + * It is positioned in the opposite corner to the corner the + * categories/toolbox starts at. */ Blockly.Trashcan.prototype.position = function() { + // Not yet initialized. + if (!this.verticalSpacing_) { + return; + } var metrics = this.workspace_.getMetrics(); if (!metrics) { // There are no metrics available (workspace is probably not visible). return; } - if (this.workspace_.RTL) { - this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - this.left_ += metrics.flyoutWidth; - if (this.workspace_.toolbox_) { - this.left_ += metrics.absoluteLeft; - } - } - } else { + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT + || (this.workspace_.horizontalLayout && !this.workspace_.RTL)) { + // Toolbox starts in the left corner. this.left_ = metrics.viewWidth + metrics.absoluteLeft - - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; - - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - this.left_ -= metrics.flyoutWidth; - } + this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; + } else { + // Toolbox starts in the right corner. + this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; } - this.top_ = metrics.viewHeight + metrics.absoluteTop - - (this.BODY_HEIGHT_ + this.LID_HEIGHT_) - this.bottom_; if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { - this.top_ -= metrics.flyoutHeight; + this.top_ = this.verticalSpacing_; + } else { + this.top_ = metrics.viewHeight + metrics.absoluteTop - + (this.BODY_HEIGHT_ + this.LID_HEIGHT_) - this.verticalSpacing_; } + this.svgGroup_.setAttribute('transform', 'translate(' + this.left_ + ',' + this.top_ + ')'); }; @@ -309,20 +389,30 @@ Blockly.Trashcan.prototype.setOpen_ = function(state) { */ Blockly.Trashcan.prototype.animateLid_ = function() { this.lidOpen_ += this.isOpen ? 0.2 : -0.2; - this.lidOpen_ = Math.min(Math.max(this.lidOpen_, 0), 1); - var lidAngle = this.lidOpen_ * 45; - this.svgLid_.setAttribute('transform', 'rotate(' + - (this.workspace_.RTL ? -lidAngle : lidAngle) + ',' + - (this.workspace_.RTL ? 4 : this.WIDTH_ - 4) + ',' + - (this.LID_HEIGHT_ - 2) + ')'); + this.lidOpen_ = Math.min(Math.max(this.lidOpen_, this.minOpenness_), 1); + this.setLidAngle_(this.lidOpen_ * 45); // Linear interpolation between 0.4 and 0.8. var opacity = 0.4 + this.lidOpen_ * (0.8 - 0.4); this.svgGroup_.style.opacity = opacity; - if (this.lidOpen_ > 0 && this.lidOpen_ < 1) { + if (this.lidOpen_ > this.minOpenness_ && this.lidOpen_ < 1) { this.lidTask_ = setTimeout(this.animateLid_.bind(this), 20); } }; +/** + * Set the angle of the trashcan's lid. + * @param {!number} lidAngle The angle at which to set the lid. + * @private + */ +Blockly.Trashcan.prototype.setLidAngle_ = function(lidAngle) { + var openAtRight = this.workspace_.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT + || (this.workspace_.horizontalLayout && this.workspace_.RTL); + this.svgLid_.setAttribute('transform', 'rotate(' + + (openAtRight ? -lidAngle : lidAngle) + ',' + + (openAtRight ? 4 : this.WIDTH_ - 4) + ',' + + (this.LID_HEIGHT_ - 2) + ')'); +}; + /** * Flip the lid shut. * Called externally after a drag. @@ -335,10 +425,110 @@ Blockly.Trashcan.prototype.close = function() { * Inspect the contents of the trash. */ Blockly.Trashcan.prototype.click = function() { - var dx = this.workspace_.startScrollX - this.workspace_.scrollX; - var dy = this.workspace_.startScrollY - this.workspace_.scrollY; - if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) { + if (!this.hasBlocks_) { return; } - console.log('TODO: Inspect trash.'); + + var xml = []; + for (var i = 0, text; text = this.contents_[i]; i++) { + xml[i] = Blockly.Xml.textToDom(text).firstChild; + } + this.flyout_.show(xml); +}; + +/** + * Indicate that the trashcan can be clicked (by opening it) if it has blocks. + * @private + */ +Blockly.Trashcan.prototype.mouseOver_ = function() { + if (!this.hasBlocks_) { + return; + } + this.setOpen_(true); +}; + +/** + * Close the lid of the trashcan if it was open (Vis. it was indicating it had + * blocks). + * @private + */ +Blockly.Trashcan.prototype.mouseOut_ = function() { + // No need to do a .hasBlocks check here because if it doesn't the trashcan + // wont be open in the first place, and setOpen_ won't run. + this.setOpen_(false); +}; + +/** + * Handle a BLOCK_DELETE event. Adds deleted blocks oldXml to the content array. + * @returns {!Function} Function to call when a block is deleted. + * @private + */ +Blockly.Trashcan.prototype.onDelete_ = function() { + var trashcan = this; + return function(event) { + if (trashcan.workspace_.options.maxTrashcanContents <= 0) { + return; + } + if (event.type == Blockly.Events.BLOCK_DELETE) { + var cleanedXML = trashcan.cleanBlockXML_(event.oldXml); + if (trashcan.contents_.indexOf(cleanedXML) != -1) { + return; + } + trashcan.contents_.unshift(cleanedXML); + if (trashcan.contents_.length > + trashcan.workspace_.options.maxTrashcanContents) { + trashcan.contents_.splice( + trashcan.workspace_.options.maxTrashcanContents, + trashcan.contents_.length - + trashcan.workspace_.options.maxTrashcanContents); + } + + trashcan.hasBlocks_ = true; + trashcan.minOpenness_ = trashcan.HAS_BLOCKS_LID_ANGLE; + trashcan.setLidAngle_(trashcan.minOpenness_ * 45); + } + }; +}; + +/** + * Converts xml representing a block into text that can be stored in the + * content array. + * @param {!Element} xml An XML tree defining the block and any + * connected child blocks. + * @returns {!string} Text representing the XML tree, cleaned of all unnecessary + * attributes. + * @private + */ +Blockly.Trashcan.prototype.cleanBlockXML_ = function(xml) { + var xmlBlock = xml.cloneNode(true); + var node = xmlBlock; + while (node) { + // Things like text inside tags are still treated as nodes, but they + // don't have attributes (or the removeAttribute function) so we can + // skip removing attributes from them. + if (node.removeAttribute) { + node.removeAttribute('x'); + node.removeAttribute('y'); + node.removeAttribute('id'); + } + + // Try to go down the tree + var nextNode = node.firstChild || node.nextSibling; + // If we can't go down, try to go back up the tree. + if (!nextNode) { + nextNode = node.parentNode; + while (nextNode) { + // We are valid again! + if (nextNode.nextSibling) { + nextNode = nextNode.nextSibling; + break; + } + // Try going up again. If parentNode is null that means we have + // reached the top, and we will break out of both loops. + nextNode = nextNode.parentNode; + } + } + node = nextNode; + } + return '' + Blockly.Xml.domToText(xmlBlock) + ''; }; diff --git a/core/widgetdiv.js b/core/widgetdiv.js index 8edad91ffd5..b51dc5e8dfe 100644 --- a/core/widgetdiv.js +++ b/core/widgetdiv.js @@ -186,7 +186,12 @@ Blockly.WidgetDiv.positionWithAnchor = function(viewportBBox, anchorBBox, var x = Blockly.WidgetDiv.calculateX_(viewportBBox, anchorBBox, widgetSize, rtl); - Blockly.WidgetDiv.positionInternal_(x, y, widgetSize.height); + if (y < 0) { + Blockly.WidgetDiv.positionInternal_(x, 0, widgetSize.height + y); + } + else { + Blockly.WidgetDiv.positionInternal_(x, y, widgetSize.height); + } }; /** diff --git a/core/workspace.js b/core/workspace.js index 8d58ff87abb..bf9adc2b6a7 100644 --- a/core/workspace.js +++ b/core/workspace.js @@ -328,7 +328,14 @@ Blockly.Workspace.prototype.getAllBlocks = function(ordered) { blocks.push.apply(blocks, blocks[i].getChildren(false)); } } - return blocks; + + // Insertion markers exist on the workspace for rendering reasons, but aren't + // "real" blocks from a developer perspective. + var filtered = blocks.filter(function(block) { + return !block.isInsertionMarker(); + }); + + return filtered; }; /** @@ -513,6 +520,7 @@ Blockly.Workspace.prototype.remainingCapacity = function() { if (isNaN(this.options.maxBlocks)) { return Infinity; } + return this.options.maxBlocks - this.getAllBlocks().length; }; diff --git a/core/workspace_events.js b/core/workspace_events.js new file mode 100644 index 00000000000..b4c78e2defc --- /dev/null +++ b/core/workspace_events.js @@ -0,0 +1,91 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 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 Class for a finished loading workspace event. + * @author BeksOmega + */ +'use strict'; + +goog.provide('Blockly.Events.FinishedLoading'); + +goog.require('Blockly.Events'); +goog.require('Blockly.Events.Abstract'); + +/** + * Class for a finished loading event. + * Used to notify the developer when the workspace has finished loading (i.e + * domToWorkspace). + * Finished loading events do not record undo or redo. + * @param {!Blockly.Workspace} workspace The workspace that has finished + * loading. + * @constructor + */ +Blockly.Events.FinishedLoading = function(workspace) { + /** + * The workspace identifier for this event. + * @type {string} + */ + this.workspaceId = workspace.id; + + /** + * The event group id for the group this event belongs to. Groups define + * events that should be treated as an single action from the user's + * perspective, and should be undone together. + * @type {string} + */ + this.group = Blockly.Events.group_; + + // Workspace events do not undo or redo. + this.recordUndo = false; +}; +goog.inherits(Blockly.Events.FinishedLoading, Blockly.Events.Abstract); + +/** + * Type of this event. + * @type {string} + */ +Blockly.Events.FinishedLoading.prototype.type = Blockly.Events.FINISHED_LOADING; + +/** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ +Blockly.Events.FinishedLoading.prototype.toJson = function() { + var json = { + 'type': this.type, + }; + if (this.group) { + json['group'] = this.group; + } + if (this.workspaceId) { + json['workspaceId'] = this.workspaceId; + } + return json; +}; + +/** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ +Blockly.Events.FinishedLoading.prototype.fromJson = function(json) { + this.workspaceId = json['workspaceId']; + this.group = json['group']; +}; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 7484650e89a..34bad2df46c 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -466,13 +466,6 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) { /** @type {SVGElement} */ this.svgBubbleCanvas_ = Blockly.utils.createSvgElement('g', {'class': 'blocklyBubbleCanvas'}, this.svgGroup_); - var bottom = Blockly.Scrollbar.scrollbarThickness; - if (this.options.hasTrashcan) { - bottom = this.addTrashcan_(bottom); - } - if (this.options.zoomOptions && this.options.zoomOptions.controls) { - this.addZoomControls_(bottom); - } if (!this.isFlyout) { Blockly.bindEventWithChecks_(this.svgGroup_, 'mousedown', this, @@ -582,30 +575,24 @@ Blockly.WorkspaceSvg.prototype.newBlock = function(prototypeName, opt_id) { /** * Add a trashcan. - * @param {number} bottom Distance from workspace bottom to bottom of trashcan. - * @return {number} Distance from workspace bottom to the top of trashcan. - * @private + * @package */ -Blockly.WorkspaceSvg.prototype.addTrashcan_ = function(bottom) { +Blockly.WorkspaceSvg.prototype.addTrashcan = function() { /** @type {Blockly.Trashcan} */ this.trashcan = new Blockly.Trashcan(this); var svgTrashcan = this.trashcan.createDom(); this.svgGroup_.insertBefore(svgTrashcan, this.svgBlockCanvas_); - return this.trashcan.init(bottom); }; /** * Add zoom controls. - * @param {number} bottom Distance from workspace bottom to bottom of controls. - * @return {number} Distance from workspace bottom to the top of controls. - * @private + * @package */ -Blockly.WorkspaceSvg.prototype.addZoomControls_ = function(bottom) { +Blockly.WorkspaceSvg.prototype.addZoomControls = function() { /** @type {Blockly.ZoomControls} */ this.zoomControls_ = new Blockly.ZoomControls(this); var svgZoomControls = this.zoomControls_.createDom(); this.svgGroup_.appendChild(svgZoomControls); - return this.zoomControls_.init(bottom); }; /** @@ -913,6 +900,13 @@ Blockly.WorkspaceSvg.prototype.render = function() { for (var i = blocks.length - 1; i >= 0; i--) { blocks[i].render(false); } + + if (this.currentGesture_) { + var imList = this.currentGesture_.getInsertionMarkers(); + for (var i = 0; i < imList.length; i++) { + imList[i].render(false); + } + } }; /** diff --git a/core/xml.js b/core/xml.js index 6a8ba6e158b..9941ddbebd6 100644 --- a/core/xml.js +++ b/core/xml.js @@ -31,6 +31,7 @@ goog.provide('Blockly.Xml'); goog.require('Blockly.Events.BlockCreate'); +goog.require('Blockly.Events.FinishedLoading'); goog.require('Blockly.Events.VarCreate'); goog.require('Blockly.Xml.utils'); @@ -467,6 +468,7 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { if (workspace.setResizesEnabled) { workspace.setResizesEnabled(true); } + Blockly.Events.fire(new Blockly.Events.FinishedLoading(workspace)); return newBlockIds; }; diff --git a/core/zoom_controls.js b/core/zoom_controls.js index 3c8dd8a5207..5103d15cb69 100644 --- a/core/zoom_controls.js +++ b/core/zoom_controls.js @@ -94,7 +94,7 @@ Blockly.ZoomControls.prototype.top_ = 0; */ Blockly.ZoomControls.prototype.createDom = function() { this.svgGroup_ = - Blockly.utils.createSvgElement('g', {'class': 'blocklyZoom'}, null); + Blockly.utils.createSvgElement('g', {}, null); // Each filter/pattern needs a unique ID for the case of multiple Blockly // instances on a page. Browser behaviour becomes undefined otherwise. @@ -108,12 +108,14 @@ Blockly.ZoomControls.prototype.createDom = function() { /** * Initialize the zoom controls. - * @param {number} bottom Distance from workspace bottom to bottom of controls. - * @return {number} Distance from workspace bottom to the top of controls. + * @param {number} verticalSpacing Vertical distances from workspace edge to the + * same edge of the controls. + * @return {number} Vertical distance from workspace edge to the opposite + * edge of the controls. */ -Blockly.ZoomControls.prototype.init = function(bottom) { - this.bottom_ = this.MARGIN_BOTTOM_ + bottom; - return this.bottom_ + this.HEIGHT_; +Blockly.ZoomControls.prototype.init = function(verticalSpacing) { + this.verticalSpacing_ = this.MARGIN_BOTTOM_ + verticalSpacing; + return this.verticalSpacing_ + this.HEIGHT_; }; /** @@ -129,35 +131,41 @@ Blockly.ZoomControls.prototype.dispose = function() { }; /** - * Move the zoom controls to the bottom-right corner. + * Position the zoom controls. + * It is positioned in the opposite corner to the corner the + * categories/toolbox starts at. */ Blockly.ZoomControls.prototype.position = function() { + // Not yet initialized. + if (!this.verticalSpacing_) { + return; + } var metrics = this.workspace_.getMetrics(); if (!metrics) { // There are no metrics available (workspace is probably not visible). return; } - if (this.workspace_.RTL) { - this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - this.left_ += metrics.flyoutWidth; - if (this.workspace_.toolbox_) { - this.left_ += metrics.absoluteLeft; - } - } - } else { + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT + || (this.workspace_.horizontalLayout && !this.workspace_.RTL)) { + // Toolbox starts in the left corner. this.left_ = metrics.viewWidth + metrics.absoluteLeft - - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; - - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - this.left_ -= metrics.flyoutWidth; - } + this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; + } else { + // Toolbox starts in the right corner. + this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; } - this.top_ = metrics.viewHeight + metrics.absoluteTop - - this.HEIGHT_ - this.bottom_; + if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { - this.top_ -= metrics.flyoutHeight; + this.top_ = this.verticalSpacing_; + this.zoomInGroup_.setAttribute('transform', 'translate(0, 34)'); + this.zoomResetGroup_.setAttribute('transform', 'translate(0, 77)'); + } else { + this.top_ = metrics.viewHeight + metrics.absoluteTop - + this.HEIGHT_ - this.verticalSpacing_; + this.zoomInGroup_.setAttribute('transform', 'translate(0, 43)'); + this.zoomOutGroup_.setAttribute('transform', 'translate(0, 77)'); } + this.svgGroup_.setAttribute('transform', 'translate(' + this.left_ + ',' + this.top_ + ')'); }; @@ -170,43 +178,38 @@ Blockly.ZoomControls.prototype.position = function() { * @private */ Blockly.ZoomControls.prototype.createZoomOutSvg_ = function(rnd) { - /* This markup will be generated and added to the "blocklyZoom" group: - - - - - + + - + */ var ws = this.workspace_; - - var svgHolder = Blockly.utils.createSvgElement('svg', - { - "id": "svg" + rnd - }, - this.svgGroup_); - + this.zoomOutGroup_ = Blockly.utils.createSvgElement('g', + {'class': 'blocklyZoom'}, this.svgGroup_); var clip = Blockly.utils.createSvgElement('clipPath', { 'id': 'blocklyZoomoutClipPath' + rnd }, - svgHolder); + this.zoomOutGroup_); Blockly.utils.createSvgElement('rect', { 'width': 32, 'height': 32, - 'y': 77 }, clip); var zoomoutSvg = Blockly.utils.createSvgElement('image', { 'width': Blockly.SPRITE.width, - 'height': Blockly.SPRITE.height, 'x': -64, - 'y': -15, + 'height': Blockly.SPRITE.height, + 'x': -64, + 'y': -92, 'clip-path': 'url(#blocklyZoomoutClipPath' + rnd + ')' }, - svgHolder); + this.zoomOutGroup_); zoomoutSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', ws.options.pathToMedia + Blockly.SPRITE.url); @@ -228,32 +231,27 @@ Blockly.ZoomControls.prototype.createZoomOutSvg_ = function(rnd) { * @private */ Blockly.ZoomControls.prototype.createZoomInSvg_ = function(rnd) { - /* This markup will be generated and added to the "blocklyZoom" group: - - - - - + + + + - + */ - var ws = this.workspace_; - var svgHolder = Blockly.utils.createSvgElement('svg', - { - "id": "svg" + rnd - }, - this.svgGroup_); + this.zoomInGroup_ = Blockly.utils.createSvgElement('g', + {'class': 'blocklyZoom'}, this.svgGroup_); var clip = Blockly.utils.createSvgElement('clipPath', { 'id': 'blocklyZoominClipPath' + rnd }, - svgHolder); + this.zoomInGroup_); Blockly.utils.createSvgElement('rect', { 'width': 32, 'height': 32, - 'y': 43 }, clip); var zoominSvg = Blockly.utils.createSvgElement('image', @@ -261,10 +259,10 @@ Blockly.ZoomControls.prototype.createZoomInSvg_ = function(rnd) { 'width': Blockly.SPRITE.width, 'height': Blockly.SPRITE.height, 'x': -32, - 'y': -49, + 'y': -92, 'clip-path': 'url(#blocklyZoominClipPath' + rnd + ')' }, - svgHolder); + this.zoomInGroup_); zoominSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', ws.options.pathToMedia + Blockly.SPRITE.url); @@ -286,26 +284,23 @@ Blockly.ZoomControls.prototype.createZoomInSvg_ = function(rnd) { * @private */ Blockly.ZoomControls.prototype.createZoomResetSvg_ = function(rnd) { - /* This markup will be generated and added to the "blocklyZoom" group: - - + /* This markup will be generated and added to the .svgGroup_: + + - - + - + */ var ws = this.workspace_; - var svgHolder = Blockly.utils.createSvgElement('svg', - { - "id": "svg" + rnd - }, - this.svgGroup_); + this.zoomResetGroup_ = Blockly.utils.createSvgElement('g', + {'class': 'blocklyZoom'}, this.svgGroup_); var clip = Blockly.utils.createSvgElement('clipPath', { 'id': 'blocklyZoomresetClipPath' + rnd }, - svgHolder); + this.zoomResetGroup_); Blockly.utils.createSvgElement('rect', { 'width': 32, @@ -315,10 +310,11 @@ Blockly.ZoomControls.prototype.createZoomResetSvg_ = function(rnd) { var zoomresetSvg = Blockly.utils.createSvgElement('image', { 'width': Blockly.SPRITE.width, - 'height': Blockly.SPRITE.height, 'y': -92, + 'height': Blockly.SPRITE.height, + 'y': -92, 'clip-path': 'url(#blocklyZoomresetClipPath' + rnd + ')' }, - svgHolder); + this.zoomResetGroup_); zoomresetSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', ws.options.pathToMedia + Blockly.SPRITE.url); diff --git a/demos/blockfactory/blocks.js b/demos/blockfactory/blocks.js index 4f10b091a79..15a7f2a41a2 100644 --- a/demos/blockfactory/blocks.js +++ b/demos/blockfactory/blocks.js @@ -603,7 +603,9 @@ Blockly.Blocks['field_image'] = { .appendField('height') .appendField(new Blockly.FieldNumber('15', 0, NaN, 1), 'HEIGHT') .appendField('alt text') - .appendField(new Blockly.FieldTextInput('*'), 'ALT'); + .appendField(new Blockly.FieldTextInput('*'), 'ALT') + .appendField('flip RTL') + .appendField(new Blockly.FieldCheckbox('false'), 'FLIP_RTL'); this.setPreviousStatement(true, 'Field'); this.setNextStatement(true, 'Field'); this.setTooltip('Static image (JPEG, PNG, GIF, SVG, BMP).\n' + diff --git a/demos/blockfactory/factory_utils.js b/demos/blockfactory/factory_utils.js index 17a26ac56f9..4006d43087d 100644 --- a/demos/blockfactory/factory_utils.js +++ b/demos/blockfactory/factory_utils.js @@ -593,7 +593,8 @@ FactoryUtils.getFieldsJson_ = function(block) { src: block.getFieldValue('SRC'), width: Number(block.getFieldValue('WIDTH')), height: Number(block.getFieldValue('HEIGHT')), - alt: block.getFieldValue('ALT') + alt: block.getFieldValue('ALT'), + flipRtl: block.getFieldValue('FLIP_RTL') == 'TRUE' }); break; } diff --git a/demos/plane/README.txt b/demos/plane/README.txt index 38bbc031330..944448fd6da 100644 --- a/demos/plane/README.txt +++ b/demos/plane/README.txt @@ -13,11 +13,11 @@ This generates xlf/extracted_msgs.xlf, which may then be used by any XLIFF-compatible translation console to generate a set of files with the translated strings. These should be placed in the xlf directory. -Finally, generate all the language versions wih this command: +Finally, generate all the language versions with this command: java -jar soy/SoyToJsSrcCompiler.jar --locales ar,be-tarask,br,ca,da,de,el,en,es,fa,fr,he,hrx,hu,ia,is,it,ja,ko,ms,nb,nl,pl,pms,pt-br,ro,ru,sc,sv,th,tr,uk,vi,zh-hans,zh-hant --messageFilePathFormat xlf/translated_msgs_{LOCALE}.xlf --outputPathFormat "generated/{LOCALE}.js" template.soy -This is the process that Google uses for maintaining Blockly Games in 40+ +This is the process that Google uses for maintaining Blockly Games in 50+ languages. The XLIFF format is simple enough that it is trivial to write a Python script to reformat it into some other format (such as JSON) for compatibility with other translation consoles. diff --git a/demos/plane/generated/ar.js b/demos/plane/generated/ar.js index 13109490495..ac760fb11d8 100644 --- a/demos/plane/generated/ar.js +++ b/demos/plane/generated/ar.js @@ -1,37 +1,50 @@ // This file was automatically generated from template.soy. // Please don't edit this file by hand. +/** + * @fileoverview Templates in namespace planepage. + */ + if (typeof planepage == 'undefined') { var planepage = {}; } planepage.messages = function(opt_data, opt_ignored, opt_ijData) { - return '
الصفوف: %1الصفوف (%1)صفوف الطبقة الأولى: %1صفوف الطبقة الأولى (%1)صفوف الفئة الثانية: %1صفوف الفئة الثانية: (%1)المقاعد: %1؟المقاعد =
'; + return '
\u0627\u0644\u0635\u0641\u0648\u0641: %1\u0627\u0644\u0635\u0641\u0648\u0641 (%1)\u0635\u0641\u0648\u0641 \u0627\u0644\u0637\u0628\u0642\u0629 \u0627\u0644\u0623\u0648\u0644\u0649: %1\u0635\u0641\u0648\u0641 \u0627\u0644\u0637\u0628\u0642\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 (%1)\u0635\u0641\u0648\u0641 \u0627\u0644\u0641\u0626\u0629 \u0627\u0644\u062B\u0627\u0646\u064A\u0629: %1\u0635\u0641\u0648\u0641 \u0627\u0644\u0641\u0626\u0629 \u0627\u0644\u062B\u0627\u0646\u064A\u0629: (%1)\u0627\u0644\u0645\u0642\u0627\u0639\u062F: %1\u061F\u0627\u0644\u0645\u0642\u0627\u0639\u062F =
'; }; +if (goog.DEBUG) { + planepage.messages.soyTemplateName = 'planepage.messages'; +} planepage.start = function(opt_data, opt_ignored, opt_ijData) { - var output = planepage.messages(null, null, opt_ijData) + '

Blockly‏ > Demos‏ > آلة حاسبة لمقعد الطائرة   '; - var iLimit37 = opt_ijData.maxLevel + 1; - for (var i37 = 1; i37 < iLimit37; i37++) { - output += ' ' + ((i37 == opt_ijData.level) ? '' + soy.$$escapeHtml(i37) + '' : (i37 < opt_ijData.level) ? '' : '' + soy.$$escapeHtml(i37) + ''); + var output = planepage.messages(null, null, opt_ijData) + '

Blockly‏ > Demos‏ > \u0622\u0644\u0629 \u062D\u0627\u0633\u0628\u0629 \u0644\u0645\u0642\u0639\u062F \u0627\u0644\u0637\u0627\u0626\u0631\u0629   '; + var iLimit47 = opt_ijData.maxLevel + 1; + for (var i47 = 1; i47 < iLimit47; i47++) { + output += ' ' + ((i47 == opt_ijData.level) ? '' + soy.$$escapeHtml(i47) + '' : (i47 < opt_ijData.level) ? '' : '' + soy.$$escapeHtml(i47) + ''); } output += '

+ diff --git a/tests/jsunit/theme_test.js b/tests/jsunit/theme_test.js new file mode 100644 index 00000000000..1600964c940 --- /dev/null +++ b/tests/jsunit/theme_test.js @@ -0,0 +1,154 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 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 Tests for Blockly.Style + * @author aschmiedt@google.com (Abby Schmiedt) + */ +'use strict'; + +function defineThemeTestBlocks() { + Blockly.defineBlocksWithJsonArray([{ + "type": "stack_block", + "message0": "", + "previousStatement": null, + "nextStatement": null + }, + { + "type": "row_block", + "message0": "%1", + "args0": [ + { + "type": "input_value", + "name": "INPUT" + } + ], + "output": null + }]); +}; + +function undefineThemeTestBlocks() { + delete Blockly.Blocks['stack_block']; + delete Blockly.Blocks['row_block']; +} + + +function createBlockStyles() { + return { + "styleOne": { + "colourPrimary": "colour1", + "colourSecondary":"colour2", + "colourTertiary":"colour3" + } + }; +} + +function createMultipleBlockStyles() { + return { + "styleOne": { + "colourPrimary": "colour1", + "colourSecondary":"colour2", + "colourTertiary":"colour3" + }, + "styleTwo": { + "colourPrimary": "colour1", + "colourSecondary":"colour2", + "colourTertiary":"colour3" + } + }; +} + +function test_setAllBlockStyles() { + var theme = new Blockly.Theme(createBlockStyles()); + stringifyAndCompare(createBlockStyles(), theme.blockStyles_); + theme.setAllBlockStyles(createMultipleBlockStyles()); + stringifyAndCompare(createMultipleBlockStyles(), theme.blockStyles_); +} + +function test_getAllBlockStyles() { + var theme = new Blockly.Theme(createMultipleBlockStyles()); + var allBlocks = theme.getAllBlockStyles(); + stringifyAndCompare(createMultipleBlockStyles(), allBlocks); + +} + +function test_getBlockStyles() { + var theme = new Blockly.Theme(createBlockStyles()); + var blockStyle = theme.getBlockStyle('styleOne'); + + stringifyAndCompare(blockStyle, createBlockStyles().styleOne); +} + +function test_setBlockStyleUpdate() { + var theme = new Blockly.Theme(createBlockStyles()); + var blockStyle = createBlockStyles(); + blockStyle.styleOne.colourPrimary = 'somethingElse'; + + theme.setBlockStyle('styleOne', blockStyle.styleOne); + + stringifyAndCompare(theme.blockStyles_, blockStyle); +} + +function test_setBlockStyleAdd() { + var theme = new Blockly.Theme(createBlockStyles()); + var blockStyle = createMultipleBlockStyles(); + + theme.setBlockStyle('styleTwo', blockStyle.styleTwo); + + stringifyAndCompare(theme.blockStyles_, blockStyle); +} + +function test_setTheme() { + defineThemeTestBlocks(); + var blockStyles = createBlockStyles(); + var workspace = new Blockly.WorkspaceSvg({}); + var blockA = workspace.newBlock('stack_block'); + var blocks = [blockA]; + + blockA.setStyle = function() {this.styleName_ = 'styleTwo'}; + var callCount = 1; + workspace.refreshToolboxSelection = function() { + return ++callCount; + }; + blockA.styleName_ = 'styleOne'; + + setUpMockMethod(mockControl_, Blockly, 'getMainWorkspace', null, [workspace]); + + Blockly.setTheme(blockStyles); + + //Checks that the theme was set correctly on Blockly namespace + stringifyAndCompare(Blockly.getTheme(), blockStyles); + + //Checks that the setTheme function was called on the block + assertEquals(blockA.getStyleName(), 'styleTwo'); + + //check that the toolbox refreshed method was called + assertEquals(workspace.refreshToolboxSelection(), 3); + + assertEquals(Blockly.Events.FIRE_QUEUE_.pop().element, 'theme'); + + undefineThemeTestBlocks(); +} + +function stringifyAndCompare(val1, val2) { + var stringVal1 = JSON.stringify(val1); + var stringVal2 = JSON.stringify(val2); + assertEquals(stringVal1, stringVal2); +} diff --git a/tests/media/arrow.png b/tests/media/arrow.png new file mode 100644 index 00000000000..d310bd2a61b Binary files /dev/null and b/tests/media/arrow.png differ diff --git a/tests/mocha/.eslintrc.json b/tests/mocha/.eslintrc.json new file mode 100644 index 00000000000..c887877f64b --- /dev/null +++ b/tests/mocha/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "env": { + "browser": true, + "mocha": true + }, + "globals": { + "chai": false, + "sinon": false, + "assertNull": true, + "assertNotNull": true, + "assertEquals": true, + "assertTrue": true, + "assertFalse": true, + "isEqualArrays": true, + "assertUndefined": true, + "assertNotUndefined": true + + }, + "extends": "eslint:recommended" +} diff --git a/tests/mocha/block_test.js b/tests/mocha/block_test.js new file mode 100644 index 00000000000..5613d40ec82 --- /dev/null +++ b/tests/mocha/block_test.js @@ -0,0 +1,176 @@ + + +suite('Blocks', function() { + + suite('Unplug', function() { + function assertUnpluggedNoheal(blocks) { + // A has nothing connected to it. + assertEquals(0, blocks.A.getChildren().length); + // B and C are still connected. + assertEquals(blocks.B, blocks.C.getParent()); + // B is the top of its stack. + assertNull(blocks.B.getParent()); + } + + function assertUnpluggedHealed(blocks) { + // A and C are connected. + assertEquals(1, blocks.A.getChildren().length); + assertEquals(blocks.A, blocks.C.getParent()); + // B has nothing connected to it. + assertEquals(0, blocks.B.getChildren().length); + // B is the top of its stack. + assertNull(blocks.B.getParent()); + } + + setup(function() { + Blockly.defineBlocksWithJsonArray([{ + "type": "stack_block", + "message0": "", + "previousStatement": null, + "nextStatement": null + }, + { + "type": "row_block", + "message0": "%1", + "args0": [ + { + "type": "input_value", + "name": "INPUT" + } + ], + "output": null + }]); + + this.workspace = new Blockly.Workspace(); + }); + + teardown(function() { + delete Blockly.Blocks['stack_block']; + delete Blockly.Blocks['row_block']; + + this.workspace.dispose(); + }); + + suite('Row', function() { + setup(function() { + var blockA = this.workspace.newBlock('row_block'); + var blockB = this.workspace.newBlock('row_block'); + var blockC = this.workspace.newBlock('row_block'); + + blockA.inputList[0].connection.connect(blockB.outputConnection); + blockB.inputList[0].connection.connect(blockC.outputConnection); + + assertEquals(blockB, blockC.getParent()); + + this.blocks = { + A: blockA, + B: blockB, + C: blockC + }; + }); + + test('Don\'t heal', function() { + this.blocks.B.unplug(false); + assertUnpluggedNoheal(this.blocks); + }); + + test('Heal', function() { + this.blocks.B.unplug(true); + // Each block has only one input, and the types work. + assertUnpluggedHealed(this.blocks); + }); + + test('Heal with bad checks', function() { + var blocks = this.blocks; + + // A and C can't connect, but both can connect to B. + blocks.A.inputList[0].connection.setCheck('type1'); + blocks.C.outputConnection.setCheck('type2'); + + // Each block has only one input, but the types don't work. + blocks.B.unplug(true); + assertUnpluggedNoheal(blocks); + }); + + test('Parent has multiple inputs', function() { + var blocks = this.blocks; + // Add extra input to parent + blocks.A.appendValueInput("INPUT").setCheck(null); + blocks.B.unplug(true); + assertUnpluggedHealed(blocks); + }); + + test('Middle block has multiple inputs', function() { + var blocks = this.blocks; + // Add extra input to middle block + blocks.B.appendValueInput("INPUT").setCheck(null); + blocks.B.unplug(true); + assertUnpluggedNoheal(blocks); + }); + + test('Child block has multiple inputs', function() { + var blocks = this.blocks; + // Add extra input to child block + blocks.C.appendValueInput("INPUT").setCheck(null); + // Child block input count doesn't matter. + blocks.B.unplug(true); + assertUnpluggedHealed(blocks); + }); + }); + + + suite('Stack', function() { + setup(function() { + var blockA = this.workspace.newBlock('stack_block'); + var blockB = this.workspace.newBlock('stack_block'); + var blockC = this.workspace.newBlock('stack_block'); + + blockA.nextConnection.connect(blockB.previousConnection); + blockB.nextConnection.connect(blockC.previousConnection); + + assertEquals(blockB, blockC.getParent()); + + this.blocks = { + A: blockA, + B: blockB, + C: blockC + }; + }); + + test('Don\'t heal', function() { + this.blocks.B.unplug(); + assertUnpluggedNoheal(this.blocks); + }); + + test('Heal', function() { + this.blocks.B.unplug(true); + assertUnpluggedHealed(this.blocks); + }); + + test('Heal with bad checks', function() { + var blocks = this.blocks; + // A and C can't connect, but both can connect to B. + blocks.A.nextConnection.setCheck('type1'); + blocks.C.previousConnection.setCheck('type2'); + + // The types don't work. + blocks.B.unplug(true); + + // Stack blocks unplug before checking whether the types match. + // TODO (#1994): Check types before unplugging. + // A has nothing connected to it. + assertEquals(0, blocks.A.getChildren().length); + // B has nothing connected to it. + assertEquals(0, blocks.B.getChildren().length); + // C has nothing connected to it. + assertEquals(0, blocks.C.getChildren().length); + // A is the top of its stack. + assertNull(blocks.A.getParent()); + // B is the top of its stack. + assertNull(blocks.B.getParent()); + // C is the top of its stack. + assertNull(blocks.C.getParent()); + }); + }); + }); +}); diff --git a/tests/mocha/event_test.js b/tests/mocha/event_test.js new file mode 100644 index 00000000000..e88d379a8b6 --- /dev/null +++ b/tests/mocha/event_test.js @@ -0,0 +1,379 @@ + +suite('Events', function() { + setup(function() { + this.workspace = new Blockly.Workspace(); + Blockly.defineBlocksWithJsonArray([{ + 'type': 'field_variable_test_block', + 'message0': '%1', + 'args0': [ + { + 'type': 'field_variable', + 'name': 'VAR', + 'variable': 'item' + } + ], + }, + { + 'type': 'simple_test_block', + 'message0': 'simple test block' + }]); + }); + + teardown(function() { + delete Blockly.Blocks['field_variable_test_block']; + delete Blockly.Blocks['simple_test_block']; + this.workspace.dispose(); + + // Clear Blockly.Event state. + Blockly.Events.setGroup(false); + Blockly.Events.disabled_ = 0; + }); + + function checkExactEventValues(event, values) { + var keys = Object.keys(values); + for (var i = 0; i < keys.length; i++) { + var field = keys[i] + assertEquals(values[field], event[field]); + } + } + + function checkCreateEventValues(event, block, ids, type) { + var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block)); + var result_xml = Blockly.Xml.domToText(event.xml); + assertEquals(expected_xml, result_xml); + isEqualArrays(ids, event.ids); + assertEquals(type, event.type); + } + + function checkDeleteEventValues(event, block, ids, type) { + var expected_xml = Blockly.Xml.domToText(Blockly.Xml.blockToDom(block)); + var result_xml = Blockly.Xml.domToText(event.oldXml); + assertEquals(expected_xml, result_xml); + isEqualArrays(ids, event.ids); + assertEquals(type, event.type); + } + + suite('Constructors', function() { + test('Abstract', function() { + var event = new Blockly.Events.Abstract(); + assertUndefined(event.blockId); + assertUndefined(event.workspaceId); + assertUndefined(event.varId); + checkExactEventValues(event, {'group': '', 'recordUndo': true}); + }); + + test('UI event without block', function() { + Blockly.Events.setGroup('testGroup'); + var event = new Blockly.Events.Ui(null, 'foo', 'bar', 'baz'); + checkExactEventValues(event, + { + 'blockId': null, + 'workspaceId': null, + 'type': 'ui', + 'oldValue': 'bar', + 'newValue': 'baz', + 'element': 'foo', + 'recordUndo': false, + 'group': 'testGroup' + }); + }); + + suite('With simple blocks', function() { + setup(function() { + this.FAKE_ID = 'hedgehog'; + sinon.stub(Blockly.utils, "genUid").returns(this.FAKE_ID); + + // Disable events while constructing the block: this is a test of the + // Blockly.Event constructors, not the block constructor. + Blockly.Events.disable(); + this.block = new Blockly.Block(this.workspace, 'simple_test_block'); + Blockly.Events.enable(); + sinon.restore(); + }); + + teardown(function() { + }); + + test('Block base', function() { + var event = new Blockly.Events.BlockBase(this.block); + assertUndefined(event.varId); + checkExactEventValues(event, + { + 'blockId': this.FAKE_ID, + 'workspaceId': this.workspace.id, + 'group': '', + 'recordUndo': true + }); + }); + + test('Create', function() { + var event = new Blockly.Events.Create(this.block); + checkCreateEventValues(event, this.block, [this.FAKE_ID], 'create'); + }); + + test('Block create', function() { + var event = new Blockly.Events.BlockCreate(this.block); + checkCreateEventValues(event, this.block, [this.FAKE_ID], 'create'); + }); + + test('Delete', function() { + var event = new Blockly.Events.Delete(this.block); + checkDeleteEventValues(event, this.block, [this.FAKE_ID], 'delete'); + }); + + test('Block delete', function() { + var event = new Blockly.Events.BlockDelete(this.block); + checkDeleteEventValues(event, this.block, [this.FAKE_ID], 'delete'); + }); + + test('UI event with block', function() { + Blockly.Events.setGroup('testGroup'); + var event = new Blockly.Events.Ui(this.block, 'foo', 'bar', 'baz'); + checkExactEventValues(event, + { + 'blockId': this.FAKE_ID, + 'workspaceId': this.workspace.id, + 'type': 'ui', + 'oldValue': 'bar', + 'newValue': 'baz', + 'element': 'foo', + 'recordUndo': false, + 'group': 'testGroup' + }); + }); + + suite('Move', function() { + test('Move by coordinate', function() { + var coordinate = new goog.math.Coordinate(3, 4); + this.block.xy_ = coordinate; + + var event = new Blockly.Events.Move(this.block); + checkExactEventValues(event, + {'oldCoordinate': coordinate, 'type': 'move'}); + }); + + test('Block move by coordinate', function() { + var coordinate = new goog.math.Coordinate(3, 4); + this.block.xy_ = coordinate; + + var event = new Blockly.Events.BlockMove(this.block); + checkExactEventValues(event, + {'oldCoordinate': coordinate, 'type': 'move'}); + }); + + suite('Move by parent', function() { + setup(function() { + sinon.stub(Blockly.utils, "genUid").returns("parent"); + Blockly.Events.disable(); + this.parentBlock = new Blockly.Block(this.workspace, 'simple_test_block'); + Blockly.Events.enable(); + sinon.restore(); + + this.block.parentBlock_ = this.parentBlock; + this.block.xy_ = new goog.math.Coordinate(3, 4); + }); + + teardown(function() { + this.block.parentBlock_ = null; + }); + + test('Move by parent', function() { + // Expect the oldParentId to be set but not the oldCoordinate to be set. + var event = new Blockly.Events.Move(this.block); + checkExactEventValues(event, {'oldCoordinate': undefined, + 'oldParentId': 'parent', 'type': 'move'}); + }); + + test('Block move by parent', function() { + // Expect the oldParentId to be set but not the oldCoordinate to be set. + var event = new Blockly.Events.BlockMove(this.block); + checkExactEventValues(event, {'oldCoordinate': undefined, + 'oldParentId': 'parent', 'type': 'move'}); + }); + }); + }); + }); + + suite('With variable getter blocks', function() { + setup(function() { + // Disable events while constructing the block: this is a test of the + // Blockly.Event constructors, not the block constructor. + Blockly.Events.disable(); + this.block = new Blockly.Block(this.workspace, 'field_variable_test_block'); + Blockly.Events.enable(); + }); + + teardown(function() { + + }); + + test('Change', function() { + var event = + new Blockly.Events.Change(this.block, 'field', 'VAR', 'id1', 'id2'); + checkExactEventValues(event, {'element': 'field', 'name': 'VAR', + 'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'}); + }); + + test('Block change', function() { + var event = new Blockly.Events.BlockChange( + this.block, 'field', 'VAR', 'id1', 'id2'); + checkExactEventValues(event, {'element': 'field', 'name': 'VAR', + 'oldValue': 'id1', 'newValue': 'id2', 'type': 'change'}); + }); + }); + }); + + suite('Variable events', function() { + setup(function() { + this.variable = this.workspace.createVariable('name1', 'type1', 'id1'); + }); + + /** + * Check if a variable with the given values exists. + * @param {Blockly.Workspace|Blockly.VariableMap} container The workspace or + * variableMap the checked variable belongs to. + * @param {!string} name The expected name of the variable. + * @param {!string} type The expected type of the variable. + * @param {!string} id The expected id of the variable. + */ + function checkVariableValues(container, name, type, id) { + var variable = container.getVariableById(id); + assertNotUndefined(variable); + assertEquals(name, variable.name); + assertEquals(type, variable.type); + assertEquals(id, variable.getId()); + } + suite('Constructors', function() { + test('Var base', function() { + var event = new Blockly.Events.VarBase(this.variable); + assertUndefined(event.blockId); + checkExactEventValues(event, {'varId': 'id1', + 'workspaceId': this.workspace.id, 'group': '', 'recordUndo': true}); + }); + + test('Var create', function() { + var event = new Blockly.Events.VarCreate(this.variable); + checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1', + 'type': 'var_create'}); + }); + + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + checkExactEventValues(event, {'varName': 'name1', 'varType': 'type1', + 'varId':'id1', 'type': 'var_delete'}); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + checkExactEventValues(event, {'varId': 'id1', 'oldName': 'name1', + 'newName': 'name2', 'type': 'var_rename'}); + }); + }); + + suite('fromJson', function() { + test('Var create', function() { + var event = new Blockly.Events.VarCreate(this.variable); + var event2 = new Blockly.Events.VarCreate(null); + var json = event.toJson(); + event2.fromJson(json); + + assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson())); + }); + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + var event2 = new Blockly.Events.VarDelete(null); + var json = event.toJson(); + event2.fromJson(json); + + assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson())); + }); + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, ''); + var event2 = new Blockly.Events.VarRename(null); + var json = event.toJson(); + event2.fromJson(json); + + assertEquals(JSON.stringify(json), JSON.stringify(event2.toJson())); + }); + }); + + suite('toJson', function() { + test('Var create', function() { + var variable = this.workspace.createVariable('name1', 'type1', 'id1'); + var event = new Blockly.Events.VarCreate(variable); + var json = event.toJson(); + var expectedJson = ({type: "var_create", varId: "id1", varType: "type1", + varName: "name1"}); + + assertEquals(JSON.stringify(expectedJson), JSON.stringify(json)); + }); + + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + var json = event.toJson(); + var expectedJson = ({type: "var_delete", varId: "id1", varType: "type1", + varName: "name1"}); + + assertEquals(JSON.stringify(expectedJson), JSON.stringify(json)); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + var json = event.toJson(); + var expectedJson = ({type: "var_rename", varId: "id1", oldName: "name1", + newName: "name2"}); + + assertEquals(JSON.stringify(expectedJson), JSON.stringify(json)); + }); + }); + + suite.skip('Run Forward', function() { + test('Var create', function() { + var json = {type: "var_create", varId: "id1", varType: "type1", + varName: "name1"}; + var event = Blockly.Events.fromJson(json, this.workspace); + assertNull(this.workspace.getVariableById('id1')); + event.run(true); + checkVariableValues(this.workspace, 'name1', 'type1', 'id1'); + }); + + test('Var delete', function() { + var event = new Blockly.Events.VarDelete(this.variable); + assertNotNull(this.workspace.getVariableById('id1')); + event.run(true); + assertNull(this.workspace.getVariableById('id1')); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + event.run(true); + assertNull(this.workspace.getVariable('name1')); + checkVariableValues(this.workspace, 'name2', 'type1', 'id1'); + }); + }); + suite.skip('Run Backward', function() { + test('Var create', function() { + var variable = this.workspace.createVariable('name1', 'type1', 'id1'); + var event = new Blockly.Events.VarCreate(variable); + assertNotNull(this.workspace.getVariableById('id1')); + event.run(false); + }); + + test('Var delete', function() { + var json = {type: "var_delete", varId: "id1", varType: "type1", + varName: "name1"}; + var event = Blockly.Events.fromJson(json, this.workspace); + assertNull(this.workspace.getVariableById('id1')); + event.run(false); + checkVariableValues(this.workspace, 'name1', 'type1', 'id1'); + }); + + test('Var rename', function() { + var event = new Blockly.Events.VarRename(this.variable, 'name2'); + event.run(false); + assertNull(this.workspace.getVariable('name2')); + checkVariableValues(this.workspace, 'name1', 'type1', 'id1'); + }); + }); + }); +}); diff --git a/tests/mocha/field_variable_test.js b/tests/mocha/field_variable_test.js new file mode 100644 index 00000000000..da8338c388e --- /dev/null +++ b/tests/mocha/field_variable_test.js @@ -0,0 +1,154 @@ + +suite('Variable Fields', function() { + function getMockBlock(workspace) { + return { + 'workspace': workspace, + 'isShadow': function() { + return false; + } + }; + } + function fieldVariable_createAndInitField(workspace) { + var fieldVariable = new Blockly.FieldVariable('name1'); + var mockBlock = getMockBlock(workspace); + fieldVariable.setSourceBlock(mockBlock); + // No view to initialize, but still need to init the model. + fieldVariable.initModel(); + return fieldVariable; + } + + setup(function() { + this.workspace = new Blockly.Workspace(); + }); + teardown(function() { + this.workspace.dispose(); + }); + + test('Constructor', function() { + var fieldVariable = new Blockly.FieldVariable('name1'); + // The field does not have a variable until after init() is called. + assertEquals('', fieldVariable.getText()); + assertNull(fieldVariable.getValue()); + }); + + test('Set value by ID', function() { + this.workspace.createVariable('name2', null, 'id2'); + + var fieldVariable = fieldVariable_createAndInitField(this.workspace); + var oldId = fieldVariable.getValue(); + + fieldVariable.setValue('id2'); + // Setting value by ID gives us the right text as well. + assertEquals('name2', fieldVariable.getText()); + assertEquals('id2', fieldVariable.getValue()); + chai.assert.notEqual(oldId, fieldVariable.getValue()); + }); + + test('Set value: variable does not exist', function() { + var fieldVariable = fieldVariable_createAndInitField(this.workspace); + chai.assert.throws(function() { + fieldVariable.setValue('id1'); + }); + }); + + test('Dropdown contains variables', function() { + // Expect that the dropdown options will contain the variables that exist + this.workspace.createVariable('name1', '', 'id1'); + this.workspace.createVariable('name2', '', 'id2'); + var fieldVariable = fieldVariable_createAndInitField(this.workspace); + + // Expect that variables created after field creation will show up too. + this.workspace.createVariable('name3', '', 'id3'); + + var result_options = Blockly.FieldVariable.dropdownCreate.call( + fieldVariable); + + // Expect three variable options and a rename option. + assertEquals(result_options.length, 4); + isEqualArrays(result_options[0], ['name1', 'id1']); + isEqualArrays(result_options[1], ['name2', 'id2']); + isEqualArrays(result_options[2], ['name3', 'id3']); + + }); + + suite('Get variable types', function() { + setup(function() { + this.workspace.createVariable('name1', 'type1'); + this.workspace.createVariable('name2', 'type2'); + }); + + test('variableTypes is undefined', function() { + // Expect that since variableTypes is undefined, only type empty string + // will be returned (regardless of what types are available on the workspace). + var fieldVariable = new Blockly.FieldVariable('name1'); + var resultTypes = fieldVariable.getVariableTypes_(); + isEqualArrays(resultTypes, ['']); + }); + + test('variableTypes is explicit', function() { + // Expect that since variableTypes is defined, it will be the return + // value, regardless of what types are available on the workspace. + var fieldVariable = new Blockly.FieldVariable( + 'name1', null, ['type1', 'type2'], 'type1'); + var resultTypes = fieldVariable.getVariableTypes_(); + isEqualArrays(resultTypes, ['type1', 'type2']); + assertEquals('Default type was wrong', 'type1', + fieldVariable.defaultType_); + }); + + test('variableTypes is null', function() { + // Expect all variable types to be returned. + // The field does not need to be initialized to do this--it just needs + // a pointer to the workspace. + var fieldVariable = new Blockly.FieldVariable('name1'); + var mockBlock = getMockBlock(this.workspace); + fieldVariable.setSourceBlock(mockBlock); + fieldVariable.variableTypes = null; + + var resultTypes = fieldVariable.getVariableTypes_(); + // The empty string is always one of the options. + isEqualArrays(resultTypes, ['type1', 'type2', '']); + }); + + test('variableTypes is the empty list', function() { + var fieldVariable = new Blockly.FieldVariable('name1'); + var mockBlock = getMockBlock(this.workspace); + fieldVariable.setSourceBlock(mockBlock); + fieldVariable.variableTypes = []; + + chai.assert.throws(function() { + fieldVariable.getVariableTypes_(); + }); + }); + }); + + suite('Default types', function() { + test('Default type exists', function() { + var fieldVariable = new Blockly.FieldVariable(null, null, ['b'], 'b'); + assertEquals('The variable field\'s default type should be "b"', + 'b', fieldVariable.defaultType_); + }); + + test('No default type', function() { + var fieldVariable = new Blockly.FieldVariable(null); + assertEquals('The variable field\'s default type should be the empty string', + '', fieldVariable.defaultType_); + assertNull('The variable field\'s allowed types should be null', + fieldVariable.variableTypes); + }); + + test('Default type mismatch', function() { + // Invalid default type when creating a variable field. + chai.assert.throws(function() { + var _fieldVariable = new Blockly.FieldVariable(null, null, ['a'], 'b'); + }); + }); + + test('Default type mismatch with empty array', function() { + // Invalid default type when creating a variable field. + chai.assert.throws(function() { + var _fieldVariable = new Blockly.FieldVariable(null, null, ['a']); + }); + }); + }); +}); diff --git a/tests/mocha/index.html b/tests/mocha/index.html new file mode 100644 index 00000000000..6dcef5ce6dd --- /dev/null +++ b/tests/mocha/index.html @@ -0,0 +1,31 @@ + + + + + Mocha Tests for Blockly + + + + + + +
+ + + + + + + + + + + + + diff --git a/tests/mocha/mocha.opts b/tests/mocha/mocha.opts new file mode 100644 index 00000000000..b0986b14169 --- /dev/null +++ b/tests/mocha/mocha.opts @@ -0,0 +1,3 @@ +--ui tdd +--file ../blockly_uncompressed.js +--reporter landing diff --git a/tests/mocha/test_helpers.js b/tests/mocha/test_helpers.js new file mode 100644 index 00000000000..deea338e892 --- /dev/null +++ b/tests/mocha/test_helpers.js @@ -0,0 +1,108 @@ +/* exported assertEquals, assertTrue, assertFalse, assertNull, assertNotNull, + isEqualArrays, assertUndefined, assertNotUndefined */ +function _argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) { + return args.length == expectedNumberOfNonCommentArgs + 1; +} + +function _commentArg(expectedNumberOfNonCommentArgs, args) { + if (_argumentsIncludeComments(expectedNumberOfNonCommentArgs, args)) { + return args[0]; + } + + return null; +} + +function _nonCommentArg(desiredNonCommentArgIndex, expectedNumberOfNonCommentArgs, args) { + return _argumentsIncludeComments(expectedNumberOfNonCommentArgs, args) ? + args[desiredNonCommentArgIndex] : + args[desiredNonCommentArgIndex - 1]; +} + +function _validateArguments(expectedNumberOfNonCommentArgs, args) { + if (!( args.length == expectedNumberOfNonCommentArgs || + (args.length == expectedNumberOfNonCommentArgs + 1 && (typeof(args[0]) == 'string') || args[0] == null))) { + throw new Error('Incorrect arguments passed to assert function'); + } +} +/** + * Converts from JSUnit assertEquals to chai.assert.equal. + */ +function assertEquals() { + _validateArguments(2, arguments); + var var1 = _nonCommentArg(1, 2, arguments); + var var2 = _nonCommentArg(2, 2, arguments); + var comment = _commentArg(2, arguments); + chai.assert.equal(var1, var2, comment); +} + +/** + * Converts from JSUnit assertTrue to chai.assert.isTrue. + */ +function assertTrue() { + _validateArguments(1, arguments); + var commentArg = _commentArg(1, arguments); + var booleanValue = _nonCommentArg(1, 1, arguments); + if (typeof(booleanValue) != 'boolean') { + throw new Error('Bad argument to assertTrue(boolean)'); + } + + chai.assert.isTrue(booleanValue, commentArg); +} + +/** + * Converts from JSUnit assertFalse to chai.assert.isNotTrue. + */ +function assertFalse() { + _validateArguments(1, arguments); + var commentArg = _commentArg(1, arguments); + var booleanValue = _nonCommentArg(1, 1, arguments); + + if (typeof(booleanValue) != 'boolean') { + throw new Error('Bad argument to assertFalse(boolean)'); + } + + chai.assert.isNotTrue(booleanValue, commentArg); +} + +/** + * Converts from JSUnit assertNull to chai.assert.isNull. + */ +function assertNull() { + _validateArguments(1, arguments); + var commentArg = _commentArg(1, arguments); + var val = _nonCommentArg(1, 1, arguments); + chai.assert.isNull(val, commentArg); +} + +function assertNotNull() { + _validateArguments(1, arguments); + var commentArg = _commentArg(1, arguments); + var val = _nonCommentArg(1, 1, arguments); + chai.assert.isNotNull(val, commentArg); +} + +/** + * Check that two arrays have the same content. + * @param {!Array.} array1 The first array. + * @param {!Array.} array2 The second array. + */ +function isEqualArrays(array1, array2) { + assertEquals(array1.length, array2.length); + for (var i = 0; i < array1.length; i++) { + assertEquals(array1[i], array2[i]); + } +} + +function assertUndefined() { + _validateArguments(1, arguments); + var commentArg = _commentArg(1, arguments); + var val = _nonCommentArg(1, 1, arguments); + chai.assert.isUndefined(val, commentArg); +} + +function assertNotUndefined() { + _validateArguments(1, arguments); + var commentArg = _commentArg(1, arguments); + var val = _nonCommentArg(1, 1, arguments); + chai.assert.isNotUndefined(val, commentArg); +} diff --git a/tests/mocha/utils_test.js b/tests/mocha/utils_test.js new file mode 100644 index 00000000000..05fce6f3ac8 --- /dev/null +++ b/tests/mocha/utils_test.js @@ -0,0 +1,86 @@ + +suite('Utils', function() { + test('genUid', function() { + var uuids = {}; + chai.assert.equal([1,2,3].indexOf(4), -1); + for (var i = 0; i < 1000; i++) { + var uuid = Blockly.utils.genUid(); + chai.assert.isFalse(uuid in uuids, 'UUID different: ' + uuid); + uuids[uuid] = true; + } + }); + test('addClass', function() { + var p = document.createElement('p'); + Blockly.utils.addClass(p, 'one'); + assertEquals('Adding "one"', 'one', p.className); + Blockly.utils.addClass(p, 'one'); + assertEquals('Adding duplicate "one"', 'one', p.className); + Blockly.utils.addClass(p, 'two'); + assertEquals('Adding "two"', 'one two', p.className); + Blockly.utils.addClass(p, 'two'); + assertEquals('Adding duplicate "two"', 'one two', p.className); + Blockly.utils.addClass(p, 'three'); + assertEquals('Adding "three"', 'one two three', p.className); + }); + + test('hasClass', function() { + var p = document.createElement('p'); + p.className = ' one three two three '; + assertTrue('Has "one"', Blockly.utils.hasClass(p, 'one')); + assertTrue('Has "two"', Blockly.utils.hasClass(p, 'two')); + assertTrue('Has "three"', Blockly.utils.hasClass(p, 'three')); + assertFalse('Has no "four"', Blockly.utils.hasClass(p, 'four')); + assertFalse('Has no "t"', Blockly.utils.hasClass(p, 't')); + }); + + test('removeClass', function() { + var p = document.createElement('p'); + p.className = ' one three two three '; + Blockly.utils.removeClass(p, 'two'); + assertEquals('Removing "two"', 'one three three', p.className); + Blockly.utils.removeClass(p, 'four'); + assertEquals('Removing "four"', 'one three three', p.className); + Blockly.utils.removeClass(p, 'three'); + assertEquals('Removing "three"', 'one', p.className); + Blockly.utils.removeClass(p, 'ne'); + assertEquals('Removing "ne"', 'one', p.className); + Blockly.utils.removeClass(p, 'one'); + assertEquals('Removing "one"', '', p.className); + Blockly.utils.removeClass(p, 'zero'); + assertEquals('Removing "zero"', '', p.className); + }); + + test('shortest string length', function() { + var len = Blockly.utils.shortestStringLength('one,two,three,four,five'.split(',')); + assertEquals('Length of "one"', 3, len); + len = Blockly.utils.shortestStringLength('one,two,three,four,five,'.split(',')); + assertEquals('Length of ""', 0, len); + len = Blockly.utils.shortestStringLength(['Hello World']); + assertEquals('List of one', 11, len); + len = Blockly.utils.shortestStringLength([]); + assertEquals('Empty list', 0, len); + }); + + test('comment word prefix', function() { + var len = Blockly.utils.commonWordPrefix('one,two,three,four,five'.split(',')); + assertEquals('No prefix', 0, len); + len = Blockly.utils.commonWordPrefix('Xone,Xtwo,Xthree,Xfour,Xfive'.split(',')); + assertEquals('No word prefix', 0, len); + len = Blockly.utils.commonWordPrefix('abc de,abc de,abc de,abc de'.split(',')); + assertEquals('Full equality', 6, len); + len = Blockly.utils.commonWordPrefix('abc deX,abc deY'.split(',')); + assertEquals('One word prefix', 4, len); + len = Blockly.utils.commonWordPrefix('abc de,abc deY'.split(',')); + assertEquals('Overflow no', 4, len); + len = Blockly.utils.commonWordPrefix('abc de,abc de Y'.split(',')); + assertEquals('Overflow yes', 6, len); + len = Blockly.utils.commonWordPrefix(['Hello World']); + assertEquals('List of one', 11, len); + len = Blockly.utils.commonWordPrefix([]); + assertEquals('Empty list', 0, len); + len = Blockly.utils.commonWordPrefix('turn left,turn right'.split(',')); + assertEquals('No prefix due to &nbsp;', 0, len); + len = Blockly.utils.commonWordPrefix('turn\u00A0left,turn\u00A0right'.split(',')); + assertEquals('No prefix due to \\u00A0', 0, len); + }); +}); diff --git a/tests/playground.html b/tests/playground.html index d2f5d50a749..364ca0468fa 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -65,6 +65,7 @@ +