not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2800 lines
107 KiB

  1. // Copyright (c) 2014-17 Walter Bender
  2. // Copyright (c) Yash Khandelwal, GSoC'15
  3. // Copyright (c) 2016 Tymon Radzik
  4. //
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the The GNU Affero General Public
  7. // License as published by the Free Software Foundation; either
  8. // version 3 of the License, or (at your option) any later version.
  9. //
  10. // You should have received a copy of the GNU Affero General Public
  11. // License along with this library; if not, write to the Free Software
  12. // Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
  13. //
  14. // Note: This code is inspired by the Python Turtle Blocks project
  15. // (https://github.com/walterbender/turtleart), but implemented from
  16. // scratch. -- Walter Bender, October 2014.
  17. const _THIS_IS_MUSIC_BLOCKS_ = false;
  18. const _THIS_IS_TURTLE_BLOCKS_ = !_THIS_IS_MUSIC_BLOCKS_;
  19. function facebookInit() {
  20. window.fbAsyncInit = function () {
  21. FB.init({
  22. appId: '1496189893985945',
  23. xfbml: true,
  24. version: 'v2.1'
  25. });
  26. // ADD ADDITIONAL FACEBOOK CODE HERE
  27. };
  28. };
  29. try {
  30. (function (d, s, id) {
  31. var js, fjs = d.getElementsByTagName(s)[0];
  32. if (d.getElementById(id)) {
  33. return;
  34. }
  35. js = d.createElement(s);
  36. js.id = id;
  37. js.src = "https://connect.facebook.net/en_US/sdk.js";
  38. fjs.parentNode.insertBefore(js, fjs);
  39. }(document, 'script', 'facebook-jssdk'));
  40. } catch (e) {
  41. };
  42. var lang = document.webL10n.getLanguage();
  43. if (lang.indexOf('-') !== -1) {
  44. lang = lang.slice(0, lang.indexOf("-"));
  45. document.webL10n.setLanguage(lang);
  46. }
  47. if (_THIS_IS_MUSIC_BLOCKS_) {
  48. MYDEFINES = ["activity/sugarizer-compatibility", 'activity/platformstyle', 'easeljs-0.8.2.min', 'tweenjs-0.6.2.min', 'preloadjs-0.6.2.min', 'Tone.min', 'howler', 'p5.min', 'p5.sound.min', 'p5.dom.min', 'mespeak', 'Chart', 'activity/utils', 'activity/artwork', 'activity/status', 'activity/munsell', 'activity/trash', 'activity/boundary', 'activity/turtle', 'activity/palette', 'activity/protoblocks', 'activity/blocks', 'activity/block', 'activity/turtledefs', 'activity/logo', 'activity/clearbox', 'activity/utilitybox', 'activity/samplesviewer', 'activity/basicblocks', 'activity/blockfactory', 'activity/analytics', 'activity/modewidget', 'activity/soundsamples', 'activity/pitchtimematrix', 'activity/pitchdrummatrix', 'activity/rhythmruler', 'activity/pitchstaircase', 'activity/tempo', 'activity/pitchslider', 'activity/macros', 'activity/musicutils', 'activity/lilypond', 'prefixfree.min'];
  49. } else {
  50. MYDEFINES = ["activity/sugarizer-compatibility", 'activity/platformstyle', 'easeljs-0.8.2.min', 'tweenjs-0.6.2.min', 'preloadjs-0.6.2.min', 'howler', 'p5.min', 'p5.sound.min', 'p5.dom.min', 'mespeak', 'Chart', 'activity/utils', 'activity/artwork', 'activity/status', 'activity/munsell', 'activity/trash', 'activity/boundary', 'activity/turtle', 'activity/palette', 'activity/protoblocks', 'activity/blocks', 'activity/block', 'activity/turtledefs', 'activity/logo', 'activity/clearbox', 'activity/savebox', 'activity/utilitybox', 'activity/samplesviewer', 'activity/basicblocks', 'activity/blockfactory', 'activity/analytics', 'activity/macros', 'activity/musicutils', 'activity/lilypond', 'prefixfree.min'];
  51. }
  52. define(MYDEFINES, function (compatibility) {
  53. // Manipulate the DOM only when it is ready.
  54. requirejs(['domReady!','activity/sugarizer-compatibility'], function (doc) {
  55. if (sugarizerCompatibility.isInsideSugarizer()) {
  56. window.addEventListener('localized', function() {
  57. sugarizerCompatibility.loadData(function () {
  58. domReady(doc);
  59. });
  60. });
  61. document.webL10n.setLanguage(sugarizerCompatibility.getLanguage());
  62. } else {
  63. domReady(doc);
  64. }
  65. });
  66. function domReady(doc) {
  67. createDefaultStack();
  68. createHelpContent();
  69. // facebookInit();
  70. window.scroll(0, 0);
  71. try {
  72. meSpeak.loadConfig('lib/mespeak_config.json');
  73. var lang = document.webL10n.getLanguage();
  74. if (sugarizerCompatibility.isInsideSugarizer()) {
  75. lang = sugarizerCompatibility.getLanguage();
  76. }
  77. if (['es', 'ca', 'de', 'el', 'eo', 'fi', 'fr', 'hu', 'it', 'kn', 'la', 'lv', 'nl', 'pl', 'pt', 'ro', 'sk', 'sv', 'tr', 'zh'].indexOf(lang) !== -1) {
  78. meSpeak.loadVoice('lib/voices/' + lang + '.json');
  79. } else {
  80. meSpeak.loadVoice('lib/voices/en/en.json');
  81. }
  82. } catch (e) {
  83. console.log(e);
  84. }
  85. var canvas = docById('myCanvas');
  86. var queue = new createjs.LoadQueue(false);
  87. // Check for the various File API support.
  88. if (window.File && window.FileReader && window.FileList && window.Blob) {
  89. var files = true;
  90. } else {
  91. alert('The File APIs are not fully supported in this browser.');
  92. var files = false;
  93. }
  94. // Set up a file chooser for the doOpen function.
  95. var fileChooser = docById('myOpenFile');
  96. // Set up a file chooser for the doOpenPlugin function.
  97. var pluginChooser = docById('myOpenPlugin');
  98. // The file chooser for all files.
  99. var allFilesChooser = docById('myOpenAll');
  100. // Are we running off of a server?
  101. var server = true;
  102. var turtleBlocksScale = 1;
  103. var stage;
  104. var turtles;
  105. var palettes;
  106. var blocks;
  107. var logo;
  108. var clearBox;
  109. var utilityBox;
  110. var thumbnails;
  111. var buttonsVisible = true;
  112. var headerContainer = null;
  113. var toolbarButtonsVisible = true;
  114. var menuButtonsVisible = true;
  115. var menuContainer = null;
  116. var scrollBlockContainer = false;
  117. var currentKey = '';
  118. var currentKeyCode = 0;
  119. var lastKeyCode = 0;
  120. var pasteContainer = null;
  121. var pasteImage = null;
  122. var chartBitmap = null;
  123. if (_THIS_IS_TURTLE_BLOCKS_) {
  124. var saveBox;
  125. }
  126. // Calculate the palette colors.
  127. for (var p in PALETTECOLORS) {
  128. PALETTEFILLCOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1], PALETTECOLORS[p][2]);
  129. PALETTESTROKECOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1] - 30, PALETTECOLORS[p][2]);
  130. PALETTEHIGHLIGHTCOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1] + 10, PALETTECOLORS[p][2]);
  131. HIGHLIGHTSTROKECOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1] - 50, PALETTECOLORS[p][2]);
  132. }
  133. pluginObjs = {
  134. 'PALETTEPLUGINS': {},
  135. 'PALETTEFILLCOLORS': {},
  136. 'PALETTESTROKECOLORS': {},
  137. 'PALETTEHIGHLIGHTCOLORS': {},
  138. 'FLOWPLUGINS': {},
  139. 'ARGPLUGINS': {},
  140. 'BLOCKPLUGINS': {},
  141. 'ONLOAD': {},
  142. 'ONSTART': {},
  143. 'ONSTOP': {}
  144. };
  145. // Stacks of blocks saved in local storage
  146. var macroDict = {};
  147. var stopTurtleContainer = null;
  148. var stopTurtleContainerX = 0;
  149. var stopTurtleContainerY = 0;
  150. var homeButtonContainers = [];
  151. var homeButtonContainersX = 0;
  152. var homeButtonContainersY = 0;
  153. var cameraID = null;
  154. var toLang = null;
  155. var fromLang = null;
  156. // initial scroll position
  157. var scrollX = 0;
  158. var scrollY = 0;
  159. // default values
  160. const DEFAULTDELAY = 500; // milleseconds
  161. const TURTLESTEP = -1; // Run in step-by-step mode
  162. const BLOCKSCALES = [1, 1.5, 2, 3, 4];
  163. var blockscale = BLOCKSCALES.indexOf(DEFAULTBLOCKSCALE);
  164. if (blockscale === -1) {
  165. blockscale = 1;
  166. }
  167. // Time when we hit run
  168. var time = 0;
  169. // Used by pause block
  170. var waitTime = {};
  171. // Used to track mouse state for mouse button block
  172. var stageMouseDown = false;
  173. var stageX = 0;
  174. var stageY = 0;
  175. var onXO = (screen.width === 1200 && screen.height === 900) || (screen.width === 900 && screen.height === 1200);
  176. var cellSize = 55;
  177. if (onXO) {
  178. cellSize = 75;
  179. }
  180. var onscreenButtons = [];
  181. var onscreenMenu = [];
  182. var utilityButton = null;
  183. var saveButton = null;
  184. var helpContainer = null;
  185. var helpIdx = 0;
  186. var firstRun = true;
  187. pluginsImages = {};
  188. // Sometimes (race condition?) Firefox does not properly
  189. // initialize strings in musicutils. These methods ensure that
  190. // the names are never null.
  191. console.log('initing i18n for music terms');
  192. initDrumI18N();
  193. initModeI18N();
  194. initVoiceI18N();
  195. window.onblur = function () {
  196. logo.doStopTurtle();
  197. };
  198. function _findBlocks() {
  199. logo.showBlocks();
  200. blocksContainer.x = 0;
  201. blocksContainer.y = 0;
  202. palettes.initial_x = 55;
  203. palettes.initial_y = 55;
  204. palettes.updatePalettes();
  205. var x = 100 * turtleBlocksScale;
  206. var y = 100 * turtleBlocksScale;
  207. for (var blk in blocks.blockList) {
  208. if (!blocks.blockList[blk].trash) {
  209. var myBlock = blocks.blockList[blk];
  210. if (myBlock.connections[0] == null) {
  211. var dx = x - myBlock.container.x;
  212. var dy = y - myBlock.container.y;
  213. blocks.moveBlockRelative(blk, dx, dy);
  214. blocks.findDragGroup(blk);
  215. if (blocks.dragGroup.length > 0) {
  216. for (var b = 0; b < blocks.dragGroup.length; b++) {
  217. var bblk = blocks.dragGroup[b];
  218. if (b !== 0) {
  219. blocks.moveBlockRelative(bblk, dx, dy);
  220. }
  221. }
  222. }
  223. x += 200 * turtleBlocksScale;
  224. if (x > (canvas.width - 100) / (turtleBlocksScale)) {
  225. x = 100 * turtleBlocksScale;
  226. y += 100 * turtleBlocksScale;
  227. }
  228. }
  229. }
  230. }
  231. // Blocks are all home, so reset go-home-button.
  232. homeButtonContainers[0].visible = false;
  233. homeButtonContainers[1].visible = true;
  234. boundary.hide();
  235. };
  236. function _allClear() {
  237. if (chartBitmap != null) {
  238. stage.removeChild(chartBitmap);
  239. chartBitmap = null;
  240. }
  241. logo.boxes = {};
  242. logo.time = 0;
  243. hideMsgs();
  244. logo.setBackgroundColor(-1);
  245. logo.lilypondOutput = LILYPONDHEADER;
  246. for (var turtle = 0; turtle < turtles.turtleList.length; turtle++) {
  247. logo.turtleHeaps[turtle] = [];
  248. logo.lilypondStaging[turtle] = [];
  249. turtles.turtleList[turtle].doClear(true, true);
  250. }
  251. blocksContainer.x = 0;
  252. blocksContainer.y = 0;
  253. // Code specific to cleaning up music blocks
  254. Element.prototype.remove = function () {
  255. this.parentElement.removeChild(this);
  256. };
  257. NodeList.prototype.remove = HTMLCollection.prototype.remove = function () {
  258. for (var i = 0, len = this.length; i < len; i++) {
  259. if(this[i] && this[i].parentElement) {
  260. this[i].parentElement.removeChild(this[i]);
  261. }
  262. }
  263. };
  264. var table = document.getElementById("myTable");
  265. if(table != null) {
  266. table.remove();
  267. }
  268. /*
  269. var canvas = document.getElementById("music");
  270. var context = canvas.getContext("2d");
  271. context.clearRect(0, 0, canvas.width, canvas.height);
  272. */
  273. };
  274. function _doFastButton(env) {
  275. var currentDelay = logo.turtleDelay;
  276. var playingWidget = false;
  277. logo.setTurtleDelay(0);
  278. if (_THIS_IS_MUSIC_BLOCKS_) {
  279. if (docById('ptmDiv').style.visibility === 'visible') {
  280. playingWidget = true;
  281. logo.pitchTimeMatrix.playAll();
  282. }
  283. if (docById('pscDiv').style.visibility === 'visible') {
  284. playingWidget = true;
  285. pitchstaircase.playUpAndDown();
  286. }
  287. if (docById('rulerDiv').style.visibility === 'visible') {
  288. // If the tempo widget is open, sync it up with the
  289. // rhythm ruler.
  290. if (docById('tempoDiv').style.visibility === 'visible') {
  291. if (tempo.isMoving) {
  292. tempo.pause();
  293. }
  294. tempo.resume();
  295. }
  296. playingWidget = true;
  297. rhythmruler.playAll();
  298. }
  299. // We were using the run button to play a widget, not
  300. // the turtles.
  301. if (playingWidget) {
  302. return;
  303. }
  304. // Restart tempo widget and run blocks.
  305. if (docById('tempoDiv').style.visibility === 'visible') {
  306. if (tempo.isMoving) {
  307. tempo.pause();
  308. }
  309. tempo.resume();
  310. }
  311. }
  312. if (!turtles.running()) {
  313. console.log('running');
  314. logo.runLogoCommands(null, env);
  315. } else {
  316. if (currentDelay !== 0) {
  317. // keep playing at full speep
  318. console.log('running from step');
  319. logo.step();
  320. } else {
  321. // stop and restart
  322. console.log('stopping...');
  323. logo.doStopTurtle();
  324. setTimeout(function () {
  325. console.log('and running');
  326. logo.runLogoCommands(null, env);
  327. }, 500);
  328. }
  329. }
  330. };
  331. function _doSlowButton() {
  332. logo.setTurtleDelay(DEFAULTDELAY);
  333. if (_THIS_IS_MUSIC_BLOCKS_ && docById('ptmDiv').style.visibility === 'visible') {
  334. logo.pitchTimeMatrix.playAll();
  335. } else if (!turtles.running()) {
  336. logo.runLogoCommands();
  337. } else {
  338. logo.step();
  339. }
  340. };
  341. function _doStepButton() {
  342. var turtleCount = 0;
  343. for (var turtle in logo.stepQueue) {
  344. turtleCount += 1;
  345. }
  346. if (turtleCount === 0 || logo.turtleDelay !== TURTLESTEP) {
  347. // Either we haven't set up a queue or we are
  348. // switching modes.
  349. logo.setTurtleDelay(TURTLESTEP);
  350. // Queue and take first step.
  351. if (!turtles.running()) {
  352. logo.runLogoCommands();
  353. }
  354. logo.step();
  355. } else {
  356. logo.setTurtleDelay(TURTLESTEP);
  357. logo.step();
  358. }
  359. };
  360. function _doSlowMusicButton() {
  361. logo.setNoteDelay(DEFAULTDELAY);
  362. if (docById('ptmDiv').style.visibility === 'visible') {
  363. logo.pitchTimeMatrix.playAll();
  364. } else if (!turtles.running()) {
  365. logo.runLogoCommands();
  366. } else {
  367. logo.stepNote();
  368. }
  369. };
  370. function _doStepMusicButton() {
  371. var turtleCount = 0;
  372. for (var turtle in logo.stepQueue) {
  373. turtleCount += 1;
  374. }
  375. if (turtleCount === 0 || logo.TurtleDelay !== TURTLESTEP) {
  376. // Either we haven't set up a queue or we are
  377. // switching modes.
  378. logo.setTurtleDelay(TURTLESTEP);
  379. // Queue and take first step.
  380. if (!turtles.running()) {
  381. logo.runLogoCommands();
  382. }
  383. logo.stepNote();
  384. } else {
  385. logo.setTurtleDelay(TURTLESTEP);
  386. logo.stepNote();
  387. }
  388. };
  389. var stopTurtle = false;
  390. function doStopButton() {
  391. logo.doStopTurtle();
  392. };
  393. var cartesianVisible = false;
  394. function _doCartesian() {
  395. if (cartesianVisible) {
  396. _hideCartesian();
  397. cartesianVisible = false;
  398. } else {
  399. _showCartesian();
  400. cartesianVisible = true;
  401. }
  402. };
  403. var polarVisible = false;
  404. function _doPolar() {
  405. if (polarVisible) {
  406. _hidePolar();
  407. polarVisible = false;
  408. } else {
  409. _showPolar();
  410. polarVisible = true;
  411. }
  412. };
  413. function toggleScroller() {
  414. scrollBlockContainer = !scrollBlockContainer;
  415. };
  416. function closeAnalytics(chartBitmap, ctx) {
  417. var button = this;
  418. button.x = (canvas.width / (2 * turtleBlocksScale)) + (300 / Math.sqrt(2));
  419. button.y = 300.00 - (300.00 / Math.sqrt(2));
  420. this.closeButton = _makeButton('cancel-button', _('Close'), button.x, button.y, 55, 0);
  421. this.closeButton.on('click', function (event) {
  422. console.log('Deleting Chart');
  423. button.closeButton.visible = false;
  424. stage.removeChild(chartBitmap);
  425. logo.showBlocks();
  426. update = true;
  427. ctx.clearRect(0, 0, 600, 600);
  428. });
  429. };
  430. function _isCanvasBlank(canvas) {
  431. var blank = document.createElement('canvas');
  432. blank.width = canvas.width;
  433. blank.height = canvas.height;
  434. return canvas.toDataURL() == blank.toDataURL();
  435. };
  436. function doAnalytics() {
  437. var myChart = docById('myChart');
  438. if(_isCanvasBlank(myChart) == false) {
  439. return ;
  440. }
  441. var ctx = myChart.getContext('2d');
  442. document.body.style.cursor = 'wait';
  443. var myRadarChart = null;
  444. var scores = analyzeProject(blocks);
  445. console.log(scores);
  446. var data = scoreToChartData(scores);
  447. var Analytics = this;
  448. Analytics.close = closeAnalytics;
  449. var __callback = function () {
  450. var imageData = myRadarChart.toBase64Image();
  451. var img = new Image();
  452. img.onload = function () {
  453. var chartBitmap = new createjs.Bitmap(img);
  454. stage.addChild(chartBitmap);
  455. chartBitmap.x = (canvas.width / (2 * turtleBlocksScale)) - (300);
  456. chartBitmap.y = 0;
  457. chartBitmap.scaleX = chartBitmap.scaleY = chartBitmap.scale = 600 / chartBitmap.image.width;
  458. logo.hideBlocks();
  459. update = true;
  460. document.body.style.cursor = 'default';
  461. Analytics.close(chartBitmap, ctx);
  462. };
  463. img.src = imageData;
  464. };
  465. var options = getChartOptions(__callback);
  466. console.log('creating new chart');
  467. myRadarChart = new Chart(ctx).Radar(data, options);
  468. };
  469. function doBiggerFont() {
  470. if (blockscale < BLOCKSCALES.length - 1) {
  471. blockscale += 1;
  472. blocks.setBlockScale(BLOCKSCALES[blockscale]);
  473. }
  474. };
  475. function doSmallerFont() {
  476. if (blockscale > 0) {
  477. blockscale -= 1;
  478. blocks.setBlockScale(BLOCKSCALES[blockscale]);
  479. }
  480. };
  481. // Do we need to update the stage?
  482. var update = true;
  483. // The dictionary of action name: block
  484. var actions = {};
  485. // The dictionary of box name: value
  486. var boxes = {};
  487. // Coordinate grid
  488. var cartesianBitmap = null;
  489. // Polar grid
  490. var polarBitmap = null;
  491. // Msg block
  492. var msgText = null;
  493. // ErrorMsg block
  494. var errorMsgText = null;
  495. var errorMsgArrow = null;
  496. var errorArtwork = {};
  497. const ERRORARTWORK = ['emptybox', 'emptyheap', 'negroot', 'noinput', 'zerodivide', 'notanumber', 'nostack', 'notastring', 'nomicrophone'];
  498. // Get things started
  499. init();
  500. function init() {
  501. docById('loader').className = 'loader';
  502. stage = new createjs.Stage(canvas);
  503. createjs.Touch.enable(stage);
  504. createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED;
  505. createjs.Ticker.setFPS(30);
  506. createjs.Ticker.addEventListener('tick', stage);
  507. createjs.Ticker.addEventListener('tick', __tick);
  508. _createMsgContainer('#ffffff', '#7a7a7a', function (text) {
  509. msgText = text;
  510. }, 55);
  511. _createMsgContainer('#ffcbc4', '#ff0031', function (text) {
  512. errorMsgText = text;
  513. }, 110);
  514. _createErrorContainers();
  515. /* Z-Order (top to bottom):
  516. * menus
  517. * palettes
  518. * blocks
  519. * trash
  520. * turtles
  521. * logo (drawing)
  522. */
  523. palettesContainer = new createjs.Container();
  524. blocksContainer = new createjs.Container();
  525. trashContainer = new createjs.Container();
  526. turtleContainer = new createjs.Container();
  527. stage.addChild(turtleContainer, trashContainer, blocksContainer, palettesContainer);
  528. _setupBlocksContainerEvents();
  529. trashcan = new Trashcan();
  530. trashcan
  531. .setCanvas(canvas)
  532. .setStage(trashContainer)
  533. .setSize(cellSize)
  534. .setRefreshCanvas(refreshCanvas)
  535. .init();
  536. turtles = new Turtles();
  537. turtles
  538. .setCanvas(canvas)
  539. .setStage(turtleContainer)
  540. .setRefreshCanvas(refreshCanvas);
  541. // Put the boundary in the blocks container so it scrolls
  542. // with the blocks.
  543. boundary = new Boundary();
  544. boundary
  545. .setStage(blocksContainer)
  546. .init();
  547. blocks = new Blocks();
  548. blocks
  549. .setCanvas(canvas)
  550. .setStage(blocksContainer)
  551. .setRefreshCanvas(refreshCanvas)
  552. .setTrashcan(trashcan)
  553. .setUpdateStage(stage.update)
  554. .setGetStageScale(getStageScale)
  555. .setTurtles(turtles)
  556. .setErrorMsg(errorMsg);
  557. blocks.makeCopyPasteButtons(_makeButton, updatePasteButton);
  558. turtles.setBlocks(blocks);
  559. palettes = new Palettes();
  560. palettes
  561. .setCanvas(canvas)
  562. .setStage(palettesContainer)
  563. .setRefreshCanvas(refreshCanvas)
  564. .setSize(cellSize)
  565. .setTrashcan(trashcan)
  566. .setBlocks(blocks)
  567. .init();
  568. initPalettes(palettes);
  569. logo = new Logo();
  570. logo
  571. .setCanvas(canvas)
  572. .setBlocks(blocks)
  573. .setTurtles(turtles)
  574. .setStage(turtleContainer)
  575. .setRefreshCanvas(refreshCanvas)
  576. .setTextMsg(textMsg)
  577. .setErrorMsg(errorMsg)
  578. .setHideMsgs(hideMsgs)
  579. .setOnStopTurtle(onStopTurtle)
  580. .setOnRunTurtle(onRunTurtle)
  581. .setGetStageX(getStageX)
  582. .setGetStageY(getStageY)
  583. .setGetStageMouseDown(getStageMouseDown)
  584. .setGetCurrentKeyCode(getCurrentKeyCode)
  585. .setClearCurrentKeyCode(clearCurrentKeyCode)
  586. .setMeSpeak(meSpeak)
  587. .setSaveLocally(saveLocally);
  588. blocks.setLogo(logo);
  589. // Set the default background color...
  590. logo.setBackgroundColor(-1);
  591. clearBox = new ClearBox();
  592. clearBox
  593. .setCanvas(canvas)
  594. .setStage(stage)
  595. .setRefreshCanvas(refreshCanvas)
  596. .setClear(sendAllToTrash);
  597. if (_THIS_IS_TURTLE_BLOCKS_) {
  598. saveBox = new SaveBox();
  599. saveBox
  600. .setCanvas(canvas)
  601. .setStage(stage)
  602. .setRefreshCanvas(refreshCanvas)
  603. .setSaveTB(doSaveTB)
  604. .setSaveSVG(doSaveSVG)
  605. .setSavePNG(doSavePNG)
  606. .setSavePlanet(doUploadToPlanet)
  607. .setSaveFB(doShareOnFacebook);
  608. }
  609. utilityBox = new UtilityBox();
  610. utilityBox
  611. .setStage(stage)
  612. .setRefreshCanvas(refreshCanvas)
  613. .setBigger(doBiggerFont)
  614. .setSmaller(doSmallerFont)
  615. .setPlugins(doOpenPlugin)
  616. .setStats(doAnalytics)
  617. .setScroller(toggleScroller);
  618. thumbnails = new SamplesViewer();
  619. thumbnails
  620. .setStage(stage)
  621. .setRefreshCanvas(refreshCanvas)
  622. .setClear(sendAllToTrash)
  623. .setLoad(loadProject)
  624. .setLoadRaw(loadRawProject)
  625. .init();
  626. initBasicProtoBlocks(palettes, blocks);
  627. // Load any macros saved in local storage.
  628. macroData = storage.macros;
  629. if (macroData != null) {
  630. processMacroData(macroData, palettes, blocks, macroDict);
  631. }
  632. // Blocks and palettes need access to the macros dictionary.
  633. blocks.setMacroDictionary(macroDict);
  634. palettes.setMacroDictionary(macroDict);
  635. // Load any plugins saved in local storage.
  636. pluginData = storage.plugins;
  637. if (pluginData != null) {
  638. var obj = processPluginData(pluginData, palettes, blocks, logo.evalFlowDict, logo.evalArgDict, logo.evalParameterDict, logo.evalSetterDict, logo.evalOnStartList, logo.evalOnStopList);
  639. updatePluginObj(obj);
  640. }
  641. // Load custom mode saved in local storage.
  642. var custommodeData = storage.custommode;
  643. if (custommodeData != undefined) {
  644. customMode = JSON.parse(custommodeData);
  645. console.log('restoring custom mode: ' + customMode);
  646. }
  647. fileChooser.addEventListener('click', function (event) {
  648. this.value = null;
  649. });
  650. fileChooser.addEventListener('change', function (event) {
  651. // Read file here.
  652. var reader = new FileReader();
  653. reader.onload = (function (theFile) {
  654. // Show busy cursor.
  655. document.body.style.cursor = 'wait';
  656. setTimeout(function () {
  657. var rawData = reader.result;
  658. var cleanData = rawData.replace('\n', ' ');
  659. var obj = JSON.parse(cleanData);
  660. // First, hide the palettes as they will need updating.
  661. for (var name in blocks.palettes.dict) {
  662. blocks.palettes.dict[name].hideMenu(true);
  663. }
  664. refreshCanvas();
  665. blocks.loadNewBlocks(obj);
  666. // Restore default cursor.
  667. document.body.style.cursor = 'default';
  668. }, 200);
  669. });
  670. reader.readAsText(fileChooser.files[0]);
  671. }, false);
  672. allFilesChooser.addEventListener('click', function (event) {
  673. this.value = null;
  674. });
  675. pluginChooser.addEventListener('click', function (event) {
  676. window.scroll(0, 0);
  677. this.value = null;
  678. });
  679. pluginChooser.addEventListener('change', function (event) {
  680. window.scroll(0, 0);
  681. // Read file here.
  682. var reader = new FileReader();
  683. reader.onload = (function (theFile) {
  684. // Show busy cursor.
  685. document.body.style.cursor = 'wait';
  686. setTimeout(function () {
  687. obj = processRawPluginData(reader.result, palettes, blocks, errorMsg, logo.evalFlowDict, logo.evalArgDict, logo.evalParameterDict, logo.evalSetterDict, logo.evalOnStartList, logo.evalOnStopList);
  688. // Save plugins to local storage.
  689. if (obj != null) {
  690. var pluginObj = preparePluginExports(obj);
  691. console.log(pluginObj);
  692. storage.plugins = pluginObj; // preparePluginExports(obj));
  693. }
  694. // Refresh the palettes.
  695. setTimeout(function () {
  696. if (palettes.visible) {
  697. palettes.hide();
  698. }
  699. palettes.show();
  700. palettes.bringToTop();
  701. }, 1000);
  702. // Restore default cursor.
  703. document.body.style.cursor = 'default';
  704. }, 200);
  705. });
  706. reader.readAsText(pluginChooser.files[0]);
  707. }, false);
  708. // Workaround to chrome security issues
  709. // createjs.LoadQueue(true, null, true);
  710. // Enable touch interactions if supported on the current device.
  711. // FIXME: voodoo
  712. // createjs.Touch.enable(stage, false, true);
  713. // Keep tracking the mouse even when it leaves the canvas.
  714. stage.mouseMoveOutside = true;
  715. // Enabled mouse over and mouse out events.
  716. stage.enableMouseOver(10); // default is 20
  717. cartesianBitmap = _createGrid('images/Cartesian.svg');
  718. polarBitmap = _createGrid('images/polar.svg');
  719. var URL = window.location.href;
  720. var projectName = null;
  721. var runProjectOnLoad = false;
  722. _setupAndroidToolbar();
  723. // Scale the canvas relative to the screen size.
  724. _onResize();
  725. var urlParts;
  726. var env = [];
  727. if (!sugarizerCompatibility.isInsideSugarizer() && URL.indexOf('?') > 0) {
  728. var urlParts = URL.split('?');
  729. if (urlParts[1].indexOf('&') > 0) {
  730. var newUrlParts = urlParts[1].split('&');
  731. for (var i = 0; i < newUrlParts.length; i++) {
  732. if (newUrlParts[i].indexOf('=') > 0) {
  733. var args = newUrlParts[i].split('=');
  734. switch (args[0].toLowerCase()) {
  735. case 'file':
  736. projectName = args[1];
  737. break;
  738. case 'run':
  739. if (args[1].toLowerCase() === 'true')
  740. runProjectOnLoad = true;
  741. break;
  742. case 'inurl':
  743. var url = args[1];
  744. var getJSON = function (url) {
  745. return new Promise(function (resolve, reject) {
  746. var xhr = new XMLHttpRequest();
  747. xhr.open('get', url, true);
  748. xhr.responseType = 'json';
  749. xhr.onload = function () {
  750. var status = xhr.status;
  751. if (status === 200) {
  752. resolve(xhr.response);
  753. } else {
  754. reject(status);
  755. }
  756. };
  757. xhr.send();
  758. });
  759. };
  760. getJSON(url).then(function (data) {
  761. console.log('Your Json result is: ' + data.arg); //you can comment this, i used it to debug
  762. n = data.arg;
  763. env.push(parseInt(n));
  764. }, function (status) { //error detection....
  765. alert('Something went wrong.');
  766. });
  767. break;
  768. case 'outurl':
  769. var url = args[1];
  770. break;
  771. default:
  772. errorMsg("Invalid parameters");
  773. }
  774. }
  775. }
  776. } else {
  777. if (urlParts[1].indexOf('=') > 0)
  778. var args = urlParts[1].split('=');
  779. //File is the only arg that can stand alone
  780. if (args[0].toLowerCase() === 'file') {
  781. projectName = args[1];
  782. }
  783. }
  784. }
  785. if (projectName != null) {
  786. setTimeout(function () {
  787. console.log('loading ' + projectName);
  788. loadStartWrapper(loadProject, projectName, runProjectOnLoad, env);
  789. }, 2000);
  790. } else {
  791. setTimeout(function () {
  792. loadStartWrapper(_loadStart);
  793. }, 2000);
  794. }
  795. document.addEventListener('mousewheel', scrollEvent, false);
  796. document.addEventListener('DOMMouseScroll', scrollEvent, false);
  797. this.document.onkeydown = __keyPressed;
  798. _hideStopButton();
  799. };
  800. function _setupBlocksContainerEvents() {
  801. var moving = false;
  802. stage.on('stagemousemove', function (event) {
  803. stageX = event.stageX;
  804. stageY = event.stageY;
  805. });
  806. stage.on('stagemousedown', function (event) {
  807. stageMouseDown = true;
  808. if (stage.getObjectUnderPoint() != null | turtles.running()) {
  809. stage.on('stagemouseup', function (event) {
  810. stageMouseDown = false;
  811. });
  812. return;
  813. }
  814. moving = true;
  815. lastCords = {
  816. x: event.stageX,
  817. y: event.stageY
  818. };
  819. stage.on('stagemousemove', function (event) {
  820. if (!moving) {
  821. return;
  822. }
  823. if (blocks.inLongPress) {
  824. blocks.saveStackButton.visible = false;
  825. blocks.dismissButton.visible = false;
  826. blocks.inLongPress = false;
  827. }
  828. if (scrollBlockContainer) {
  829. blocksContainer.x += event.stageX - lastCords.x;
  830. blocksContainer.y += event.stageY - lastCords.y;
  831. lastCords = {
  832. x: event.stageX,
  833. y: event.stageY
  834. };
  835. refreshCanvas();
  836. }
  837. });
  838. stage.on('stagemouseup', function (event) {
  839. stageMouseDown = false;
  840. moving = false;
  841. }, null, true); // once = true
  842. });
  843. };
  844. function scrollEvent(event) {
  845. var data = event.wheelDelta || -event.detail;
  846. var delta = Math.max(-1, Math.min(1, (data)));
  847. var scrollSpeed = 30;
  848. if (event.clientX < cellSize) {
  849. palettes.menuScrollEvent(delta, scrollSpeed);
  850. palettes.hidePaletteIconCircles();
  851. } else {
  852. palette = palettes.findPalette(event.clientX / turtleBlocksScale, event.clientY / turtleBlocksScale);
  853. if (palette) {
  854. palette.scrollEvent(delta, scrollSpeed);
  855. }
  856. }
  857. };
  858. function getStageScale() {
  859. return turtleBlocksScale;
  860. };
  861. function getStageX() {
  862. return turtles.screenX2turtleX(stageX / turtleBlocksScale);
  863. };
  864. function getStageY() {
  865. return turtles.screenY2turtleY(stageY / turtleBlocksScale);
  866. };
  867. function getStageMouseDown() {
  868. return stageMouseDown;
  869. };
  870. function setCameraID(id) {
  871. cameraID = id;
  872. };
  873. function _createGrid(imagePath) {
  874. var img = new Image();
  875. img.src = imagePath;
  876. var container = new createjs.Container();
  877. stage.addChild(container);
  878. var bitmap = new createjs.Bitmap(img);
  879. container.addChild(bitmap);
  880. bitmap.cache(0, 0, 1200, 900);
  881. bitmap.x = (canvas.width - 1200) / 2;
  882. bitmap.y = (canvas.height - 900) / 2;
  883. bitmap.scaleX = bitmap.scaleY = bitmap.scale = 1;
  884. bitmap.visible = false;
  885. bitmap.updateCache();
  886. return bitmap;
  887. };
  888. function _createMsgContainer(fillColor, strokeColor, callback, y) {
  889. var container = new createjs.Container();
  890. stage.addChild(container);
  891. container.x = (canvas.width - 1000) / 2;
  892. container.y = y;
  893. container.visible = false;
  894. var img = new Image();
  895. var svgData = MSGBLOCK.replace('fill_color', fillColor).replace(
  896. 'stroke_color', strokeColor);
  897. img.onload = function () {
  898. var msgBlock = new createjs.Bitmap(img);
  899. container.addChild(msgBlock);
  900. var text = new createjs.Text('your message here', '20px Arial', '#000000');
  901. container.addChild(text);
  902. text.textAlign = 'center';
  903. text.textBaseline = 'alphabetic';
  904. text.x = 500;
  905. text.y = 30;
  906. var bounds = container.getBounds();
  907. container.cache(bounds.x, bounds.y, bounds.width, bounds.height);
  908. var hitArea = new createjs.Shape();
  909. hitArea.graphics.beginFill('#FFF').drawRect(0, 0, 1000, 42);
  910. hitArea.x = 0;
  911. hitArea.y = 0;
  912. container.hitArea = hitArea;
  913. container.on('click', function (event) {
  914. container.visible = false;
  915. // On the possibility that there was an error
  916. // arrow associated with this container
  917. if (errorMsgArrow != null) {
  918. errorMsgArrow.removeAllChildren(); // Hide the error arrow.
  919. }
  920. update = true;
  921. });
  922. callback(text);
  923. blocks.setMsgText(text);
  924. };
  925. img.src = 'data:image/svg+xml;base64,' + window.btoa(
  926. unescape(encodeURIComponent(svgData)));
  927. };
  928. function _createErrorContainers() {
  929. // Some error messages have special artwork.
  930. for (var i = 0; i < ERRORARTWORK.length; i++) {
  931. var name = ERRORARTWORK[i];
  932. _makeErrorArtwork(name);
  933. }
  934. };
  935. function _makeErrorArtwork(name) {
  936. var container = new createjs.Container();
  937. stage.addChild(container);
  938. container.x = (canvas.width - 1000) / 2;
  939. container.y = 110;
  940. errorArtwork[name] = container;
  941. errorArtwork[name].name = name;
  942. errorArtwork[name].visible = false;
  943. var img = new Image();
  944. img.onload = function () {
  945. // console.log('creating error message artwork for ' + img.src);
  946. var artwork = new createjs.Bitmap(img);
  947. container.addChild(artwork);
  948. var text = new createjs.Text('', '20px Sans', '#000000');
  949. container.addChild(text);
  950. text.x = 70;
  951. text.y = 10;
  952. var bounds = container.getBounds();
  953. container.cache(bounds.x, bounds.y, bounds.width, bounds.height);
  954. var hitArea = new createjs.Shape();
  955. hitArea.graphics.beginFill('#FFF').drawRect(0, 0, bounds.width, bounds.height);
  956. hitArea.x = 0;
  957. hitArea.y = 0;
  958. container.hitArea = hitArea;
  959. container.on('click', function (event) {
  960. container.visible = false;
  961. // On the possibility that there was an error
  962. // arrow associated with this container
  963. if (errorMsgArrow != null) {
  964. errorMsgArrow.removeAllChildren(); // Hide the error arrow.
  965. }
  966. update = true;
  967. });
  968. };
  969. img.src = 'images/' + name + '.svg';
  970. };
  971. function __keyPressed(event) {
  972. if (docById('labelDiv').classList.contains('hasKeyboard')) {
  973. return;
  974. }
  975. if (_THIS_IS_MUSIC_BLOCKS_) {
  976. if (docById('BPMInput').classList.contains('hasKeyboard')) {
  977. return;
  978. }
  979. if (docById('musicratio1').classList.contains('hasKeyboard')) {
  980. return;
  981. }
  982. if (docById('musicratio2').classList.contains('hasKeyboard')) {
  983. return;
  984. }
  985. if (docById('dissectNumber').classList.contains('hasKeyboard')) {
  986. return;
  987. }
  988. }
  989. const BACKSPACE = 8;
  990. const TAB = 9;
  991. if (event.keyCode === TAB || event.keyCode === BACKSPACE) {
  992. // Prevent browser from grabbing TAB key
  993. event.preventDefault();
  994. }
  995. const ESC = 27;
  996. const ALT = 18;
  997. const CTRL = 17;
  998. const SHIFT = 16;
  999. const RETURN = 13;
  1000. const SPACE = 32;
  1001. const HOME = 36;
  1002. const PAGE_UP = 33;
  1003. const PAGE_DOWN = 34;
  1004. const KEYCODE_LEFT = 37;
  1005. const KEYCODE_RIGHT = 39;
  1006. const KEYCODE_UP = 38;
  1007. const KEYCODE_DOWN = 40;
  1008. if (event.altKey) {
  1009. switch (event.keyCode) {
  1010. case 69: // 'E'
  1011. _allClear();
  1012. break;
  1013. case 82: // 'R'
  1014. _doFastButton();
  1015. break;
  1016. case 83: // 'S'
  1017. logo.doStopTurtle();
  1018. break;
  1019. }
  1020. } else if (event.ctrlKey) {
  1021. } else {
  1022. switch (event.keyCode) {
  1023. case KEYCODE_UP:
  1024. if (blocks.activeBlock != null) {
  1025. blocks.moveStackRelative(blocks.activeBlock, 0, -STANDARDBLOCKHEIGHT / 2);
  1026. blocks.blockMoved(blocks.activeBlock);
  1027. blocks.adjustDocks(blocks.activeBlock, true);
  1028. } else if (palettes.mouseOver) {
  1029. palettes.menuScrollEvent(1, 10);
  1030. palettes.hidePaletteIconCircles();
  1031. } else if (palettes.activePalette != null) {
  1032. palettes.activePalette.scrollEvent(STANDARDBLOCKHEIGHT, 1);
  1033. } else if (scrollBlockContainer) {
  1034. blocksContainer.y -= 21;
  1035. }
  1036. break;
  1037. case KEYCODE_DOWN:
  1038. if (blocks.activeBlock != null) {
  1039. blocks.moveStackRelative(blocks.activeBlock, 0, STANDARDBLOCKHEIGHT / 2);
  1040. blocks.blockMoved(blocks.activeBlock);
  1041. blocks.adjustDocks(blocks.activeBlock, true);
  1042. } else if (palettes.mouseOver) {
  1043. palettes.menuScrollEvent(-1, 10);
  1044. palettes.hidePaletteIconCircles();
  1045. } else if (palettes.activePalette != null) {
  1046. palettes.activePalette.scrollEvent(-STANDARDBLOCKHEIGHT, 1);
  1047. } else if (scrollBlockContainer) {
  1048. blocksContainer.y += 21;
  1049. }
  1050. break;
  1051. case KEYCODE_LEFT:
  1052. if (blocks.activeBlock != null) {
  1053. blocks.moveStackRelative(blocks.activeBlock, -STANDARDBLOCKHEIGHT / 2, 0);
  1054. blocks.blockMoved(blocks.activeBlock);
  1055. blocks.adjustDocks(blocks.activeBlock, true);
  1056. } else if (scrollBlockContainer) {
  1057. blocksContainer.x -= 21;
  1058. }
  1059. break;
  1060. case KEYCODE_RIGHT:
  1061. if (blocks.activeBlock != null) {
  1062. blocks.moveStackRelative(blocks.activeBlock, STANDARDBLOCKHEIGHT / 2, 0);
  1063. blocks.blockMoved(blocks.activeBlock);
  1064. blocks.adjustDocks(blocks.activeBlock, true);
  1065. } else if (scrollBlockContainer) {
  1066. blocksContainer.x += 21;
  1067. }
  1068. break;
  1069. case HOME:
  1070. if (palettes.mouseOver) {
  1071. var dy = Math.max(55 - palettes.buttons['rhythm'].y, 0);
  1072. palettes.menuScrollEvent(1, dy);
  1073. palettes.hidePaletteIconCircles();
  1074. } else if (palettes.activePalette != null) {
  1075. palettes.activePalette.scrollEvent(-palettes.activePalette.scrollDiff, 1);
  1076. } else {
  1077. _findBlocks();
  1078. }
  1079. break;
  1080. case TAB:
  1081. break;
  1082. case ESC:
  1083. // toggle full screen
  1084. _toggleToolbar();
  1085. break;
  1086. case RETURN:
  1087. // toggle run
  1088. logo.runLogoCommands();
  1089. break;
  1090. default:
  1091. // currentKey = String.fromCharCode(event.keyCode);
  1092. // currentKeyCode = event.keyCode;
  1093. break;
  1094. }
  1095. // Always store current key so as not to mask it from
  1096. // the keyboard block.
  1097. currentKey = String.fromCharCode(event.keyCode);
  1098. currentKeyCode = event.keyCode;
  1099. }
  1100. };
  1101. function getCurrentKeyCode() {
  1102. return currentKeyCode;
  1103. };
  1104. function clearCurrentKeyCode() {
  1105. currentKey = '';
  1106. currentKeyCode = 0;
  1107. };
  1108. function _onResize() {
  1109. if (docById('labelDiv').classList.contains('hasKeyboard')) {
  1110. return;
  1111. }
  1112. if (!platform.androidWebkit) {
  1113. var w = window.innerWidth;
  1114. var h = window.innerHeight;
  1115. } else {
  1116. var w = window.outerWidth;
  1117. var h = window.outerHeight;
  1118. }
  1119. var smallSide = Math.min(w, h);
  1120. if (smallSide < cellSize * 11) {
  1121. var mobileSize = true;
  1122. if (w < cellSize * 10) {
  1123. turtleBlocksScale = smallSide / (cellSize * 11);
  1124. } else {
  1125. turtleBlocksScale = Math.max(smallSide / (cellSize * 11), 0.75);
  1126. }
  1127. } else {
  1128. var mobileSize = false;
  1129. if (w / 1200 > h / 900) {
  1130. turtleBlocksScale = w / 1200;
  1131. } else {
  1132. turtleBlocksScale = h / 900;
  1133. }
  1134. }
  1135. stage.scaleX = turtleBlocksScale;
  1136. stage.scaleY = turtleBlocksScale;
  1137. stage.canvas.width = w;
  1138. stage.canvas.height = h;
  1139. /*
  1140. console.log('Resize: scale ' + turtleBlocksScale +
  1141. ', windowW ' + w + ', windowH ' + h +
  1142. ', canvasW ' + canvas.width + ', canvasH ' + canvas.height +
  1143. ', screenW ' + screen.width + ', screenH ' + screen.height);
  1144. */
  1145. turtles.setScale(turtleBlocksScale);
  1146. blocks.setScale(turtleBlocksScale);
  1147. boundary.setScale(w, h, turtleBlocksScale);
  1148. palettes.setScale(turtleBlocksScale);
  1149. trashcan.resizeEvent(turtleBlocksScale);
  1150. _setupAndroidToolbar(mobileSize);
  1151. // Reposition coordinate grids.
  1152. cartesianBitmap.x = (canvas.width / (2 * turtleBlocksScale)) - (600);
  1153. cartesianBitmap.y = (canvas.height / (2 * turtleBlocksScale)) - (450);
  1154. polarBitmap.x = (canvas.width / (2 * turtleBlocksScale)) - (600);
  1155. polarBitmap.y = (canvas.height / (2 * turtleBlocksScale)) - (450);
  1156. update = true;
  1157. // Setup help now that we have calculated turtleBlocksScale.
  1158. _showHelp(true);
  1159. // Hide palette icons on mobile
  1160. if (mobileSize) {
  1161. palettes.setMobile(true);
  1162. palettes.hide();
  1163. } else {
  1164. palettes.setMobile(false);
  1165. palettes.show();
  1166. palettes.bringToTop();
  1167. }
  1168. for (var turtle = 0; turtle < turtles.turtleList.length; turtle++) {
  1169. turtles.turtleList[turtle].doClear(false, false);
  1170. }
  1171. var artcanvas = document.getElementById("overlayCanvas");
  1172. artcanvas.width = w;
  1173. artcanvas.height = h;
  1174. };
  1175. window.onresize = function () {
  1176. _onResize();
  1177. };
  1178. function _restoreTrash() {
  1179. // Restore last stack pushed to trashStack.
  1180. // First, hide the palettes as they will need updating.
  1181. for (var name in blocks.palettes.dict) {
  1182. blocks.palettes.dict[name].hideMenu(true);
  1183. }
  1184. refreshCanvas();
  1185. var dx = 0;
  1186. var dy = -cellSize * 3; // Reposition blocks about trash area.
  1187. if (blocks.trashStacks.length === 0) {
  1188. console.log('Trash is empty--nothing to do');
  1189. return;
  1190. }
  1191. var thisBlock = blocks.trashStacks.pop();
  1192. // Restore drag group in trash
  1193. blocks.findDragGroup(thisBlock);
  1194. for (var b = 0; b < blocks.dragGroup.length; b++) {
  1195. var blk = blocks.dragGroup[b];
  1196. // console.log('Restoring ' + blocks.blockList[blk].name + ' from the trash.');
  1197. blocks.blockList[blk].trash = false;
  1198. blocks.moveBlockRelative(blk, dx, dy);
  1199. blocks.blockList[blk].show();
  1200. }
  1201. blocks.raiseStackToTop(thisBlock);
  1202. if (blocks.blockList[thisBlock].name === 'start' || blocks.blockList[thisBlock].name === 'drum') {
  1203. var turtle = blocks.blockList[thisBlock].value;
  1204. turtles.turtleList[turtle].trash = false;
  1205. turtles.turtleList[turtle].container.visible = true;
  1206. } else if (blocks.blockList[thisBlock].name === 'action') {
  1207. // We need to add a palette entry for this action.
  1208. // But first we need to ensure we have a unqiue name,
  1209. // as the name could have been taken in the interim.
  1210. var actionArg = blocks.blockList[blocks.blockList[thisBlock].connections[1]];
  1211. if (actionArg != null) {
  1212. var oldName = actionArg.value;
  1213. // Mark the action block as still being in the
  1214. // trash so that its name won't be considered when
  1215. // looking for a unique name.
  1216. blocks.blockList[thisBlock].trash = true;
  1217. var uniqueName = blocks.findUniqueActionName(oldName);
  1218. blocks.blockList[thisBlock].trash = false;
  1219. if (uniqueName !== actionArg) {
  1220. console.log('renaming action when restoring from trash. old name: ' + oldName + ' unique name: ' + uniqueName);
  1221. actionArg.value = uniqueName;
  1222. var label = actionArg.value.toString();
  1223. if (label.length > 8) {
  1224. label = label.substr(0, 7) + '...';
  1225. }
  1226. actionArg.text.text = label;
  1227. if (actionArg.label != null) {
  1228. actionArg.label.value = uniqueName;
  1229. }
  1230. actionArg.container.updateCache();
  1231. // Check the drag group to ensure any do
  1232. // blocks are updated (in case of recursion).
  1233. for (var b = 0; b < blocks.dragGroup.length; b++) {
  1234. var me = blocks.blockList[blocks.dragGroup[b]];
  1235. if (['nameddo', 'nameddoArg', 'namedcalc', 'namedcalcArg'].indexOf(me.name) !== -1 && me.privateData === oldName) {
  1236. console.log('reassigning nameddo to ' + uniqueName);
  1237. me.privateData = uniqueName;
  1238. me.value = uniqueName;
  1239. var label = me.value.toString();
  1240. if (label.length > 8) {
  1241. label = label.substr(0, 7) + '...';
  1242. }
  1243. me.text.text = label;
  1244. me.overrideName = label;
  1245. me.regenerateArtwork();
  1246. me.container.updateCache();
  1247. }
  1248. }
  1249. }
  1250. var actionName = actionArg.value;
  1251. if (actionName !== _('action')) {
  1252. // blocks.checkPaletteEntries('action');
  1253. console.log('FIXME: Check for unique action name here');
  1254. }
  1255. }
  1256. }
  1257. blocks.refreshCanvas();
  1258. };
  1259. function _deleteBlocksBox() {
  1260. clearBox.show(turtleBlocksScale);
  1261. };
  1262. function _doUtilityBox() {
  1263. utilityBox.init(turtleBlocksScale, utilityButton.x - 27, utilityButton.y, _makeButton);
  1264. };
  1265. function sendAllToTrash(addStartBlock, doNotSave) {
  1266. // First, hide the palettes as they will need updating.
  1267. for (var name in blocks.palettes.dict) {
  1268. blocks.palettes.dict[name].hideMenu(true);
  1269. }
  1270. refreshCanvas();
  1271. var dx = 0;
  1272. var dy = cellSize * 3;
  1273. for (var blk in blocks.blockList) {
  1274. // If this block is at the top of a stack, push it
  1275. // onto the trashStacks list.
  1276. if (blocks.blockList[blk].connections[0] == null) {
  1277. blocks.trashStacks.push(blk);
  1278. }
  1279. if (blocks.blockList[blk].name === 'start' || blocks.blockList[blk].name === 'drum') {
  1280. console.log('start blk ' + blk + ' value is ' + blocks.blockList[blk].value)
  1281. var turtle = blocks.blockList[blk].value;
  1282. if (!blocks.blockList[blk].trash && turtle != null) {
  1283. console.log('sending turtle ' + turtle + ' to trash');
  1284. turtles.turtleList[turtle].trash = true;
  1285. turtles.turtleList[turtle].container.visible = false;
  1286. }
  1287. } else if (blocks.blockList[blk].name === 'action') {
  1288. if (!blocks.blockList[blk].trash) {
  1289. blocks.deleteActionBlock(blocks.blockList[blk]);
  1290. }
  1291. }
  1292. blocks.blockList[blk].trash = true;
  1293. blocks.moveBlockRelative(blk, dx, dy);
  1294. blocks.blockList[blk].hide();
  1295. }
  1296. if (addStartBlock) {
  1297. blocks.loadNewBlocks(DATAOBJS);
  1298. } else if (!doNotSave) {
  1299. // Overwrite session data too.
  1300. saveLocally();
  1301. }
  1302. update = true;
  1303. };
  1304. function _changePaletteVisibility() {
  1305. if (palettes.visible) {
  1306. palettes.hide();
  1307. } else {
  1308. palettes.show();
  1309. palettes.bringToTop();
  1310. }
  1311. };
  1312. function _changeBlockVisibility() {
  1313. if (blocks.visible) {
  1314. logo.hideBlocks();
  1315. palettes.hide();
  1316. } else {
  1317. if (chartBitmap != null) {
  1318. stage.removeChild(chartBitmap);
  1319. chartBitmap = null;
  1320. }
  1321. logo.showBlocks();
  1322. palettes.show();
  1323. palettes.bringToTop();
  1324. }
  1325. // Combine block and palette visibility into one button.
  1326. // _changePaletteVisibility();
  1327. };
  1328. function _toggleCollapsibleStacks() {
  1329. if (blocks.visible) {
  1330. console.log('calling toggleCollapsibles');
  1331. blocks.toggleCollapsibles();
  1332. }
  1333. };
  1334. function onStopTurtle() {
  1335. // TODO: plugin support
  1336. if (stopTurtleContainer.visible) {
  1337. _hideStopButton();
  1338. }
  1339. };
  1340. function onRunTurtle() {
  1341. // TODO: plugin support
  1342. // If the stop button is hidden, show it.
  1343. if (!stopTurtleContainer.visible) {
  1344. _showStopButton();
  1345. }
  1346. };
  1347. function refreshCanvas() {
  1348. update = true;
  1349. };
  1350. function __tick(event) {
  1351. // This set makes it so the stage only re-renders when an
  1352. // event handler indicates a change has happened.
  1353. if (update) {
  1354. update = false; // Only update once
  1355. stage.update(event);
  1356. }
  1357. };
  1358. function _doOpenSamples() {
  1359. if (_THIS_IS_MUSIC_BLOCKS_) {
  1360. localStorage.setItem('isMatrixHidden', docById('ptmDiv').style.visibility);
  1361. localStorage.setItem('isStaircaseHidden', docById('pscDiv').style.visibility);
  1362. localStorage.setItem('isPitchDrumMatrixHidden', docById('pdmDiv').style.visibility);
  1363. localStorage.setItem('isRhythmRulerHidden', docById('rulerDiv').style.visibility);
  1364. localStorage.setItem('isModeWidgetHidden', docById('modeDiv').style.visibility);
  1365. localStorage.setItem('isSliderHidden', docById('sliderDiv').style.visibility);
  1366. localStorage.setItem('isTempoHidden', docById('tempoDiv').style.visibility);
  1367. if (docById('ptmDiv').style.visibility !== 'hidden') {
  1368. docById('ptmDiv').style.visibility = 'hidden';
  1369. docById('ptmTableDiv').style.visibility = 'hidden';
  1370. docById('ptmButtonsDiv').style.visibility = 'hidden';
  1371. }
  1372. if (docById('pdmDiv').style.visibility !== 'hidden') {
  1373. docById('pdmDiv').style.visibility = 'hidden';
  1374. docById('pdmButtonsDiv').style.visibility = 'hidden';
  1375. docById('pdmTableDiv').style.visibility = 'hidden';
  1376. }
  1377. if (docById('rulerDiv').style.visibility !== 'hidden') {
  1378. docById('rulerDiv').style.visibility = 'hidden';
  1379. docById('rulerTableDiv').style.visibility = 'hidden';
  1380. docById('rulerButtonsDiv').style.visibility = 'hidden';
  1381. }
  1382. if (docById('pscDiv').style.visibility !== 'hidden') {
  1383. docById('pscDiv').style.visibility = 'hidden';
  1384. docById('pscTableDiv').style.visibility = 'hidden';
  1385. docById('pscButtonsDiv').style.visibility = 'hidden';
  1386. }
  1387. if (docById('statusDiv').style.visibility !== 'hidden') {
  1388. docById('statusDiv').style.visibility = 'hidden';
  1389. docById('statusButtonsDiv').style.visibility = 'hidden';
  1390. docById('statusTableDiv').style.visibility = 'hidden';
  1391. }
  1392. if (docById('sliderDiv').style.visibility !== 'hidden') {
  1393. docById('sliderDiv').style.visibility = 'hidden';
  1394. docById('sliderButtonsDiv').style.visibility = 'hidden';
  1395. docById('sliderTableDiv').style.visibility = 'hidden';
  1396. }
  1397. if (docById('modeDiv').style.visibility !== 'hidden') {
  1398. docById('modeDiv').style.visibility = 'hidden';
  1399. docById('modeButtonsDiv').style.visibility = 'hidden';
  1400. docById('modeTableDiv').style.visibility = 'hidden';
  1401. }
  1402. if (docById('tempoDiv').style.visibility !== 'hidden') {
  1403. if (logo.tempo != null) {
  1404. logo.tempo.hide();
  1405. }
  1406. }
  1407. }
  1408. localStorage.setItem('isStatusHidden', docById('statusDiv').style.visibility);
  1409. logo.doStopTurtle();
  1410. helpContainer.visible = false;
  1411. docById('helpElem').style.visibility = 'hidden';
  1412. console.log('save locally');
  1413. saveLocally();
  1414. thumbnails.show()
  1415. };
  1416. function doSave() {
  1417. if (_THIS_IS_MUSIC_BLOCKS_) {
  1418. console.log('Saving .tb file');
  1419. var name = 'My Project';
  1420. download(name + '.tb', 'data:text/plain;charset=utf-8,' + prepareExport());
  1421. } else {
  1422. saveBox.init(turtleBlocksScale, saveButton.x - 27, saveButton.y - 97, _makeButton);
  1423. }
  1424. };
  1425. function doSaveTB() {
  1426. var filename = prompt('Filename:', 'untitled.tb'); // default filename = untitled
  1427. if (filename != null) {
  1428. if (fileExt(filename) !== 'tb') {
  1429. filename += '.tb';
  1430. }
  1431. download(filename, 'data:text/plain;charset=utf-8,' + encodeURIComponent(prepareExport()));
  1432. }
  1433. };
  1434. function doSaveSVG() {
  1435. var filename = prompt('Filename:', 'untitled.svg');
  1436. if (filename != null) {
  1437. if (fileExt(filename) !== 'svg') {
  1438. filename += '.svg';
  1439. }
  1440. var svg = doSVG(logo.canvas, logo, logo.turtles, logo.canvas.width, logo.canvas.height, 1.0);
  1441. download(filename, 'data:image/svg+xml;utf8,' + svg, filename, '"width=' + logo.canvas.width + ', height=' + logo.canvas.height + '"');
  1442. }
  1443. };
  1444. function doSavePNG() {
  1445. alert("Unavailable at the moment");
  1446. //var filename = prompt('Filename:', 'untitled.png');
  1447. //if (fileExt(filename) !== 'png') {
  1448. // filename += '.png';
  1449. //}
  1450. //download(filename, 'data:text/plain;charset=utf-8,' + encodeURIComponent(prepareExport()));
  1451. };
  1452. function doUploadToPlanet() {
  1453. saveLocally();
  1454. thumbnails.show()
  1455. };
  1456. function doShareOnFacebook() {
  1457. alert("Facebook Sharing : disabled"); // remove when add fb share link
  1458. // add code for facebook share link
  1459. };
  1460. function doLoad() {
  1461. console.log('Loading .tb file');
  1462. document.querySelector('#myOpenFile').focus();
  1463. document.querySelector('#myOpenFile').click();
  1464. window.scroll(0, 0);
  1465. };
  1466. function _doLilypond() {
  1467. // Show busy cursor.
  1468. document.body.style.cursor = 'wait';
  1469. console.log('Saving .ly file');
  1470. // Suppress music and turtle output when generating
  1471. // Lilypond output.
  1472. logo.runningLilypond = true;
  1473. logo.lilypondOutput = LILYPONDHEADER;
  1474. logo.lilypondNotes = {};
  1475. for (var turtle = 0; turtle < turtles.turtleList.length; turtle++) {
  1476. logo.lilypondStaging[turtle] = [];
  1477. turtles.turtleList[turtle].doClear(true, true);
  1478. }
  1479. logo.runLogoCommands();
  1480. };
  1481. window.prepareExport = prepareExport;
  1482. window.saveLocally = saveLocally;
  1483. function saveLocally() {
  1484. if (sugarizerCompatibility.isInsideSugarizer()) {
  1485. //sugarizerCompatibility.data.blocks = prepareExport();
  1486. storage = sugarizerCompatibility.data;
  1487. } else {
  1488. storage = localStorage;
  1489. }
  1490. console.log('overwriting session data');
  1491. if (storage.currentProject === undefined) {
  1492. try {
  1493. storage.currentProject = 'My Project';
  1494. storage.allProjects = JSON.stringify(['My Project'])
  1495. } catch (e) {
  1496. // Edge case, eg. Firefox localSorage DB corrupted
  1497. console.log(e);
  1498. }
  1499. }
  1500. try {
  1501. var p = storage.currentProject;
  1502. storage['SESSION' + p] = prepareExport();
  1503. } catch (e) {
  1504. console.log(e);
  1505. }
  1506. // if (isSVGEmpty(turtles)) {
  1507. // We will use the music icon in these cases.
  1508. // return;
  1509. // }
  1510. var img = new Image();
  1511. var svgData = doSVG(canvas, logo, turtles, 320, 240, 320 / canvas.width);
  1512. img.onload = function () {
  1513. var bitmap = new createjs.Bitmap(img);
  1514. var bounds = bitmap.getBounds();
  1515. bitmap.cache(bounds.x, bounds.y, bounds.width, bounds.height);
  1516. try {
  1517. storage['SESSIONIMAGE' + p] = bitmap.getCacheDataURL();
  1518. } catch (e) {
  1519. console.log(e);
  1520. }
  1521. };
  1522. img.src = 'data:image/svg+xml;base64,' +
  1523. window.btoa(unescape(encodeURIComponent(svgData)));
  1524. // console.log(img.src);
  1525. if (sugarizerCompatibility.isInsideSugarizer()) {
  1526. sugarizerCompatibility.saveLocally();
  1527. }
  1528. };
  1529. function runProject(env){
  1530. console.log("Running Project from Event");
  1531. document.removeEventListener("finishedLoading", runProject);
  1532. setTimeout(function () {
  1533. console.log("Run");
  1534. _changeBlockVisibility();
  1535. _doFastButton(env);
  1536. }, 5000);
  1537. }
  1538. function loadProject(projectName, run, env) {
  1539. //set default value of run
  1540. run = typeof run !== 'undefined' ? run : false;
  1541. // Show busy cursor.
  1542. document.body.style.cursor = 'wait';
  1543. // palettes.updatePalettes();
  1544. setTimeout(function () {
  1545. if (fileExt(projectName) !== 'tb')
  1546. {
  1547. projectName += '.tb';
  1548. }
  1549. try {
  1550. try {
  1551. httpGet(null);
  1552. console.log('running from server or the user can access to examples.');
  1553. server = true;
  1554. } catch (e) {
  1555. console.log('running from filesystem or the connection isnt secure');
  1556. server = false;
  1557. }
  1558. if (server) {
  1559. var rawData = httpGet(projectName);
  1560. var cleanData = rawData.replace('\n', '');
  1561. }
  1562. // First, hide the palettes as they will need updating.
  1563. for (var name in blocks.palettes.dict) {
  1564. blocks.palettes.dict[name].hideMenu(true);
  1565. }
  1566. var obj = JSON.parse(cleanData);
  1567. blocks.loadNewBlocks(obj);
  1568. saveLocally();
  1569. } catch (e) {
  1570. console.log(e);
  1571. loadStartWrapper(_loadStart);
  1572. }
  1573. // Restore default cursor
  1574. document.body.style.cursor = 'default';
  1575. update = true;
  1576. }, 200);
  1577. if (run && firstRun) {
  1578. if (document.addEventListener) {
  1579. document.addEventListener('finishedLoading', function (){runProject(env);}, false);
  1580. } else {
  1581. document.attachEvent('finishedLoading', function (){runProject(env);});
  1582. }
  1583. }
  1584. firstRun = false;
  1585. };
  1586. function loadRawProject(data) {
  1587. console.log('loadRawProject ' + data);
  1588. document.body.style.cursor = 'wait';
  1589. _allClear();
  1590. // First, hide the palettes as they will need updating.
  1591. for (var name in blocks.palettes.dict) {
  1592. blocks.palettes.dict[name].hideMenu(true);
  1593. }
  1594. var obj = JSON.parse(data);
  1595. blocks.loadNewBlocks(obj);
  1596. document.body.style.cursor = 'default';
  1597. };
  1598. function saveProject(projectName) {
  1599. // palettes.updatePalettes();
  1600. // Show busy cursor.
  1601. document.body.style.cursor = 'wait';
  1602. setTimeout(function () {
  1603. var punctuationless = projectName.replace(/['!"#$%&\\'()\*+,\-\.\/:;<=>?@\[\\\]\^`{|}~']/g, '');
  1604. projectName = punctuationless.replace(/ /g, '_');
  1605. if (fileExt(projectName) !== 'tb') {
  1606. projectName += '.tb';
  1607. }
  1608. try {
  1609. // Post the project
  1610. var returnValue = httpPost('MusicBlocks_'+projectName, prepareExport());
  1611. errorMsg('Saved ' + projectName + ' to ' + window.location.host);
  1612. var img = new Image();
  1613. var svgData = doSVG(canvas, logo, turtles, 320, 240, 320 / canvas.width);
  1614. img.onload = function () {
  1615. var bitmap = new createjs.Bitmap(img);
  1616. var bounds = bitmap.getBounds();
  1617. bitmap.cache(bounds.x, bounds.y, bounds.width, bounds.height);
  1618. // and base64-encoded png
  1619. httpPost(('MusicBlocks_'+projectName).replace('.tb', '.b64'), bitmap.getCacheDataURL());
  1620. };
  1621. img.src = 'data:image/svg+xml;base64,' + window.btoa(
  1622. unescape(encodeURIComponent(svgData)));
  1623. // Restore default cursor
  1624. document.body.style.cursor = 'default';
  1625. return returnValue;
  1626. } catch (e) {
  1627. console.log(e);
  1628. // Restore default cursor
  1629. document.body.style.cursor = 'default';
  1630. return;
  1631. }
  1632. }, 200);
  1633. };
  1634. // Calculate time such that no matter how long it takes to
  1635. // load the program, the loading animation will cycle at least
  1636. // once.
  1637. function loadStartWrapper(func, arg1, arg2, arg3) {
  1638. var time1 = new Date();
  1639. func(arg1, arg2, arg3);
  1640. var time2 = new Date();
  1641. var elapsedTime = time2.getTime() - time1.getTime();
  1642. var timeLeft = Math.max(6000 - elapsedTime);
  1643. setTimeout(showContents, timeLeft);
  1644. };
  1645. // Hides the loading animation and unhides the background.
  1646. function showContents(){
  1647. docById('loading-image-container').style.display = 'none';
  1648. // docById('canvas').style.display = 'none';
  1649. docById('hideContents').style.display = 'block';
  1650. };
  1651. function _loadStart() {
  1652. // where to put this?
  1653. // palettes.updatePalettes();
  1654. console.log('LOAD START')
  1655. justLoadStart = function () {
  1656. console.log('loading start and a matrix');
  1657. blocks.loadNewBlocks(DATAOBJS);
  1658. };
  1659. if (sugarizerCompatibility.isInsideSugarizer()) {
  1660. storage = sugarizerCompatibility.data;
  1661. }
  1662. else {
  1663. storage = localStorage;
  1664. }
  1665. sessionData = null;
  1666. // Try restarting where we were when we hit save.
  1667. var currentProject = storage.currentProject;
  1668. sessionData = storage['SESSION' + currentProject];
  1669. if (sessionData) {
  1670. try {
  1671. if (sessionData === 'undefined' || sessionData === '[]') {
  1672. console.log('empty session found: loading start');
  1673. justLoadStart();
  1674. } else {
  1675. console.log('restoring session: ' + sessionData);
  1676. // First, hide the palettes as they will need updating.
  1677. for (var name in blocks.palettes.dict) {
  1678. blocks.palettes.dict[name].hideMenu(true);
  1679. }
  1680. blocks.loadNewBlocks(JSON.parse(sessionData));
  1681. }
  1682. } catch (e) {
  1683. console.log(e);
  1684. }
  1685. } else {
  1686. justLoadStart();
  1687. }
  1688. update = true;
  1689. };
  1690. function hideMsgs() {
  1691. errorMsgText.parent.visible = false;
  1692. if (errorMsgArrow != null) {
  1693. errorMsgArrow.removeAllChildren();
  1694. refreshCanvas();
  1695. }
  1696. msgText.parent.visible = false;
  1697. for (var i in errorArtwork) {
  1698. errorArtwork[i].visible = false;
  1699. }
  1700. };
  1701. function textMsg(msg) {
  1702. if (msgText == null) {
  1703. // The container may not be ready yet... so do nothing
  1704. return;
  1705. }
  1706. var msgContainer = msgText.parent;
  1707. msgContainer.visible = true;
  1708. msgText.text = msg;
  1709. msgContainer.updateCache();
  1710. stage.setChildIndex(msgContainer, stage.getNumChildren() - 1);
  1711. };
  1712. function errorMsg(msg, blk, text) {
  1713. _hideStopButton(); //Hide the button, as the program is going to be terminated
  1714. if (errorMsgText == null) {
  1715. // The container may not be ready yet... so do nothing
  1716. return;
  1717. }
  1718. if (blk !== undefined && blk != null && !blocks.blockList[blk].collapsed) {
  1719. var fromX = (canvas.width - 1000) / 2;
  1720. var fromY = 128;
  1721. var toX = blocks.blockList[blk].container.x + blocksContainer.x;
  1722. var toY = blocks.blockList[blk].container.y + blocksContainer.y;
  1723. if (errorMsgArrow == null) {
  1724. errorMsgArrow = new createjs.Container();
  1725. stage.addChild(errorMsgArrow);
  1726. }
  1727. var line = new createjs.Shape();
  1728. errorMsgArrow.addChild(line);
  1729. line.graphics.setStrokeStyle(4).beginStroke('#ff0031').moveTo(fromX, fromY).lineTo(toX, toY);
  1730. stage.setChildIndex(errorMsgArrow, stage.getNumChildren() - 1);
  1731. var angle = Math.atan2(toX - fromX, fromY - toY) / Math.PI * 180;
  1732. var head = new createjs.Shape();
  1733. errorMsgArrow.addChild(head);
  1734. head.graphics.setStrokeStyle(4).beginStroke('#ff0031').moveTo(-10, 18).lineTo(0, 0).lineTo(10, 18);
  1735. head.x = toX;
  1736. head.y = toY;
  1737. head.rotation = angle;
  1738. }
  1739. switch (msg) {
  1740. case NOMICERRORMSG:
  1741. errorArtwork['nomicrophone'].visible = true;
  1742. stage.setChildIndex(errorArtwork['nomicrophone'], stage.getNumChildren() - 1);
  1743. break;
  1744. case NOSTRINGERRORMSG:
  1745. errorArtwork['notastring'].visible = true;
  1746. stage.setChildIndex(errorArtwork['notastring'], stage.getNumChildren() - 1);
  1747. break;
  1748. case EMPTYHEAPERRORMSG:
  1749. errorArtwork['emptyheap'].visible = true;
  1750. stage.setChildIndex(errorArtwork['emptyheap'], stage.getNumChildren() - 1);
  1751. break;
  1752. case NOSQRTERRORMSG:
  1753. errorArtwork['negroot'].visible = true;
  1754. stage.setChildIndex(errorArtwork['negroot'], stage.getNumChildren() - 1);
  1755. break;
  1756. case NOACTIONERRORMSG:
  1757. if (text == null) {
  1758. text = 'foo';
  1759. }
  1760. errorArtwork['nostack'].children[1].text = text;
  1761. errorArtwork['nostack'].visible = true;
  1762. errorArtwork['nostack'].updateCache();
  1763. stage.setChildIndex(errorArtwork['nostack'], stage.getNumChildren() - 1);
  1764. break;
  1765. case NOBOXERRORMSG:
  1766. if (text == null) {
  1767. text = 'foo';
  1768. }
  1769. errorArtwork['emptybox'].children[1].text = text;
  1770. errorArtwork['emptybox'].visible = true;
  1771. errorArtwork['emptybox'].updateCache();
  1772. stage.setChildIndex(errorArtwork['emptybox'], stage.getNumChildren() - 1);
  1773. break;
  1774. case ZERODIVIDEERRORMSG:
  1775. errorArtwork['zerodivide'].visible = true;
  1776. stage.setChildIndex(errorArtwork['zerodivide'], stage.getNumChildren() - 1);
  1777. break;
  1778. case NANERRORMSG:
  1779. errorArtwork['notanumber'].visible = true;
  1780. stage.setChildIndex(errorArtwork['notanumber'], stage.getNumChildren() - 1);
  1781. break;
  1782. case NOINPUTERRORMSG:
  1783. errorArtwork['noinput'].visible = true;
  1784. stage.setChildIndex(errorArtwork['noinput'], stage.getNumChildren() - 1);
  1785. break;
  1786. default:
  1787. var errorMsgContainer = errorMsgText.parent;
  1788. errorMsgContainer.visible = true;
  1789. errorMsgText.text = msg;
  1790. stage.setChildIndex(errorMsgContainer, stage.getNumChildren() - 1);
  1791. errorMsgContainer.updateCache();
  1792. break;
  1793. }
  1794. update = true;
  1795. };
  1796. function _hideCartesian() {
  1797. cartesianBitmap.visible = false;
  1798. cartesianBitmap.updateCache();
  1799. update = true;
  1800. };
  1801. function _showCartesian() {
  1802. cartesianBitmap.visible = true;
  1803. cartesianBitmap.updateCache();
  1804. update = true;
  1805. };
  1806. function _hidePolar() {
  1807. polarBitmap.visible = false;
  1808. polarBitmap.updateCache();
  1809. update = true;
  1810. };
  1811. function _showPolar() {
  1812. polarBitmap.visible = true;
  1813. polarBitmap.updateCache();
  1814. update = true;
  1815. };
  1816. function pasteStack() {
  1817. blocks.pasteStack();
  1818. };
  1819. function prepareExport() {
  1820. // We don't save blocks in the trash, so we need to
  1821. // consolidate the block list and remap the connections.
  1822. var blockMap = [];
  1823. var hasMatrixDataBlock = false;
  1824. for (var blk = 0; blk < blocks.blockList.length; blk++) {
  1825. var myBlock = blocks.blockList[blk];
  1826. if (myBlock.trash) {
  1827. // Don't save blocks in the trash.
  1828. continue;
  1829. }
  1830. blockMap.push(blk);
  1831. }
  1832. var data = [];
  1833. for (var blk = 0; blk < blocks.blockList.length; blk++) {
  1834. var myBlock = blocks.blockList[blk];
  1835. if (myBlock.trash) {
  1836. // Don't save blocks in the trash.
  1837. continue;
  1838. }
  1839. if (myBlock.isValueBlock() || myBlock.name === 'loadFile') {
  1840. // FIX ME: scale image if it exceeds a maximum size.
  1841. var args = {
  1842. 'value': myBlock.value
  1843. };
  1844. } else if (myBlock.name === 'start' || myBlock.name === 'drum') {
  1845. // Find the turtle associated with this block.
  1846. var turtle = turtles.turtleList[myBlock.value];
  1847. if (turtle == null) {
  1848. var args = {
  1849. 'collapsed': false,
  1850. 'xcor': 0,
  1851. 'ycor': 0,
  1852. 'heading': 0,
  1853. 'color': 0,
  1854. 'shade': 50,
  1855. 'pensize': 5,
  1856. 'grey': 100
  1857. };
  1858. } else {
  1859. var args = {
  1860. 'collapsed': myBlock.collapsed,
  1861. 'xcor': turtle.x,
  1862. 'ycor': turtle.y,
  1863. 'heading': turtle.orientation,
  1864. 'color': turtle.color,
  1865. 'shade': turtle.value,
  1866. 'pensize': turtle.stroke,
  1867. 'grey': turtle.chroma
  1868. };
  1869. }
  1870. } else if (myBlock.name === 'action') {
  1871. var args = {
  1872. 'collapsed': myBlock.collapsed
  1873. }
  1874. } else if(myBlock.name === 'matrix') {
  1875. var args = {
  1876. 'collapsed' : myBlock.collapsed
  1877. }
  1878. } else if(myBlock.name === 'pitchdrummatrix') {
  1879. var args = {
  1880. 'collapsed' : myBlock.collapsed
  1881. }
  1882. } else if(myBlock.name === 'status') {
  1883. var args = {
  1884. 'collapsed' : myBlock.collapsed
  1885. }
  1886. } else if (myBlock.name === 'namedbox') {
  1887. var args = {
  1888. 'value': myBlock.privateData
  1889. }
  1890. } else if (myBlock.name === 'nameddo') {
  1891. var args = {
  1892. 'value': myBlock.privateData
  1893. }
  1894. } else if (myBlock.name === 'nameddoArg') {
  1895. var args = {
  1896. 'value': myBlock.privateData
  1897. }
  1898. } else if (myBlock.name === 'namedcalc') {
  1899. var args = {
  1900. 'value': myBlock.privateData
  1901. }
  1902. } else if (myBlock.name === 'namedcalcArg') {
  1903. var args = {
  1904. 'value': myBlock.privateData
  1905. }
  1906. } else if (myBlock.name === 'namedarg') {
  1907. var args = {
  1908. 'value': myBlock.privateData
  1909. }
  1910. } else if (myBlock.name === 'matrixData') {
  1911. var args = {
  1912. 'notes': window.savedMatricesNotes, 'count': window.savedMatricesCount
  1913. }
  1914. hasMatrixDataBlock = true;
  1915. } else {
  1916. var args = {}
  1917. }
  1918. connections = [];
  1919. for (var c = 0; c < myBlock.connections.length; c++) {
  1920. var mapConnection = blockMap.indexOf(myBlock.connections[c]);
  1921. if (myBlock.connections[c] == null || mapConnection === -1) {
  1922. connections.push(null);
  1923. } else {
  1924. connections.push(mapConnection);
  1925. }
  1926. }
  1927. data.push([blockMap.indexOf(blk), [myBlock.name, args], myBlock.container.x, myBlock.container.y, connections]);
  1928. }
  1929. return JSON.stringify(data);
  1930. };
  1931. function doOpenPlugin() {
  1932. // Click on the plugin open chooser in the DOM (.json).
  1933. pluginChooser.focus();
  1934. pluginChooser.click();
  1935. };
  1936. function saveToFile() {
  1937. var filename = prompt('Filename:');
  1938. if (fileExt(filename) !== 'tb') {
  1939. filename += '.tb';
  1940. }
  1941. download(filename, 'data:text/plain;charset=utf-8,' + encodeURIComponent(prepareExport()));
  1942. };
  1943. function _hideStopButton() {
  1944. // stopTurtleContainer.x = stopTurtleContainerX;
  1945. // stopTurtleContainer.y = stopTurtleContainerY;
  1946. stopTurtleContainer.visible = false;
  1947. };
  1948. function _showStopButton() {
  1949. // stopTurtleContainer.x = onscreenButtons[0].x;
  1950. // stopTurtleContainer.y = onscreenButtons[0].y;
  1951. stopTurtleContainer.visible = true;
  1952. };
  1953. function blinkPasteButton(bitmap) {
  1954. function handleComplete() {
  1955. createjs.Tween.get(bitmap).to({alpha:1, visible:true}, 500);
  1956. };
  1957. createjs.Tween.get(bitmap).to({alpha:0, visible:false}, 1000).call(
  1958. handleComplete);
  1959. };
  1960. function updatePasteButton() {
  1961. if (pasteImage === null) {
  1962. var img = new Image();
  1963. img.onload = function () {
  1964. var originalSize = 55; // this is the original svg size
  1965. var halfSize = Math.floor(cellSize / 2);
  1966. var bitmap = new createjs.Bitmap(img);
  1967. if (cellSize !== originalSize) {
  1968. bitmap.scaleX = cellSize / originalSize;
  1969. bitmap.scaleY = cellSize / originalSize;
  1970. }
  1971. bitmap.regX = halfSize / bitmap.scaleX;
  1972. bitmap.regY = halfSize / bitmap.scaleY;
  1973. pasteContainer.addChild(bitmap);
  1974. pasteImage = bitmap;
  1975. update = true;
  1976. };
  1977. img.src = 'header-icons/paste-button.svg';
  1978. } else {
  1979. blinkPasteButton(pasteImage);
  1980. }
  1981. };
  1982. function _setupAndroidToolbar(showPalettesPopover) {
  1983. if (headerContainer !== undefined) {
  1984. stage.removeChild(headerContainer);
  1985. for (var i in onscreenButtons) {
  1986. stage.removeChild(onscreenButtons[i]);
  1987. }
  1988. }
  1989. headerContainer = new createjs.Shape();
  1990. headerContainer.graphics.f(platformColor.header).r(0, 0, screen.width / turtleBlocksScale, cellSize);
  1991. if (platformColor.doHeaderShadow) {
  1992. headerContainer.shadow = new createjs.Shadow('#777', 0, 2, 2);
  1993. }
  1994. stage.addChild(headerContainer);
  1995. // Buttons used when running turtle programs
  1996. // name / onpress function / label / onlongpress function / onextralongpress function / onlongpress icon / onextralongpress icon
  1997. if (_THIS_IS_MUSIC_BLOCKS_) {
  1998. var buttonNames = [
  1999. ['run', _doFastButton, _('Run fast / long press to run slowly / extra-long press to run music slowly'), _doSlowButton, _doSlowMusicButton, 'slow-button', 'slow-music-button'],
  2000. ['step', _doStepButton, _('Run step by step'), null, null, null, null],
  2001. ['step-music', _doStepMusicButton, _('Run note by note'), null, null, null, null],
  2002. ['stop-turtle', doStopButton, _('Stop'), null, null, null, null],
  2003. ['clear', _allClear, _('Clean'), null, null, null, null],
  2004. // ['palette', _changePaletteVisibility, _('Show/hide palettes'), null, null, null, null],
  2005. ['hide-blocks', _changeBlockVisibility, _('Show/hide blocks'), null, null, null, null],
  2006. ['collapse-blocks', _toggleCollapsibleStacks, _('Expand/collapse collapsable blocks'), null, null, null, null],
  2007. ['go-home', _findBlocks, _('Home'), null, null, null, null],
  2008. ['help', _showHelp, _('Help'), null, null, null, null]
  2009. ];
  2010. } else {
  2011. var buttonNames = [
  2012. ['run', _doFastButton, _('Run fast / long press to run slowly'), _doSlowButton, null, 'slow-button', null],
  2013. ['step', _doStepButton, _('Run step by step'), null, null, null, null],
  2014. ['stop-turtle', doStopButton, _('Stop'), null, null, null, null],
  2015. ['clear', _allClear, _('Clean'), null, null, null, null],
  2016. ['hide-blocks', _changeBlockVisibility, _('Show/hide blocks'), null, null, null, null],
  2017. ['collapse-blocks', _toggleCollapsibleStacks, _('Expand/collapse collapsable blocks'), null, null, null, null],
  2018. ['go-home', _findBlocks, _('Home'), null, null, null, null],
  2019. ['help', _showHelp, _('Help'), null, null, null, null]
  2020. ];
  2021. }
  2022. if (sugarizerCompatibility.isInsideSugarizer()) {
  2023. buttonNames.push(['sugarizer-stop', function () {
  2024. sugarizerCompatibility.data.blocks = prepareExport();
  2025. sugarizerCompatibility.saveLocally(function () {
  2026. sugarizerCompatibility.sugarizerStop();
  2027. });
  2028. }, "Stop", null, null, null, null])
  2029. }
  2030. if (showPalettesPopover) {
  2031. buttonNames.unshift(['popdown-palette', doPopdownPalette])
  2032. }
  2033. var btnSize = cellSize;
  2034. var x = Math.floor(btnSize / 2);
  2035. var y = x;
  2036. var dx = btnSize;
  2037. var dy = 0;
  2038. for (var i = 0; i < buttonNames.length; i++) {
  2039. if (!getMainToolbarButtonNames(buttonNames[i][0])) {
  2040. console.log('continue');
  2041. continue;
  2042. }
  2043. var container = _makeButton(buttonNames[i][0] + '-button', buttonNames[i][2], x, y, btnSize, 0);
  2044. _loadButtonDragHandler(container, x, y, buttonNames[i][1], buttonNames[i][3], buttonNames[i][4], buttonNames[i][5], buttonNames[i][6]);
  2045. onscreenButtons.push(container);
  2046. if (buttonNames[i][0] === 'stop-turtle') {
  2047. stopTurtleContainer = container;
  2048. stopTurtleContainerX = x;
  2049. stopTurtleContainerY = y;
  2050. } else if (buttonNames[i][0] === 'go-home') {
  2051. homeButtonContainers = [];
  2052. homeButtonContainers.push(container);
  2053. homeButtonContainersX = x;
  2054. homeButtonContainersY = y;
  2055. var container2 = _makeButton('go-home-faded-button', _('Home'), x, y, btnSize, 0);
  2056. _loadButtonDragHandler(container2, x, y, buttonNames[i][1], null, null, null, null);
  2057. homeButtonContainers.push(container2);
  2058. onscreenButtons.push(container2);
  2059. homeButtonContainers[0].visible = false;
  2060. homeButtonContainers[1].visible = true;
  2061. boundary.hide();
  2062. blocks.setHomeContainers(homeButtonContainers, boundary);
  2063. }
  2064. x += dx;
  2065. y += dy;
  2066. }
  2067. _setupRightMenu(turtleBlocksScale);
  2068. };
  2069. function _setupRightMenu(turtleBlocksScale) {
  2070. if (menuContainer !== undefined) {
  2071. stage.removeChild(menuContainer);
  2072. for (var i in onscreenMenu) {
  2073. stage.removeChild(onscreenMenu[i]);
  2074. }
  2075. }
  2076. // Misc. other buttons
  2077. if (_THIS_IS_MUSIC_BLOCKS_) {
  2078. var menuNames = [
  2079. ['planet', _doOpenSamples, _('Load samples from server')],
  2080. ['open', doLoad, _('Load project from files')],
  2081. ['save', doSave, _('Save project')],
  2082. ['lilypond', _doLilypond, _('Save sheet music')],
  2083. ['paste-disabled', pasteStack, _('Paste')],
  2084. ['Cartesian', _doCartesian, _('Cartesian')],
  2085. ['polar', _doPolar, _('Polar')],
  2086. ['utility', _doUtilityBox, _('Settings')],
  2087. ['empty-trash', _deleteBlocksBox, _('Delete all')],
  2088. ['restore-trash', _restoreTrash, _('Undo')]
  2089. ];
  2090. } else {
  2091. var menuNames = [
  2092. ['planet', _doOpenSamples, _('Load samples from server')],
  2093. ['open', doLoad, _('Load project from files')],
  2094. ['save', doSave, _('Save project')],
  2095. ['paste-disabled', pasteStack, _('Paste')],
  2096. ['Cartesian', _doCartesian, _('Cartesian')],
  2097. ['polar', _doPolar, _('Polar')],
  2098. ['utility', _doUtilityBox, _('Settings')],
  2099. ['empty-trash', _deleteBlocksBox, _('Delete all')],
  2100. ['restore-trash', _restoreTrash, _('Undo')]
  2101. ];
  2102. }
  2103. document.querySelector('#myOpenFile')
  2104. .addEventListener('change', function (event) {
  2105. thumbnails.model.controller.hide();
  2106. });
  2107. var btnSize = cellSize;
  2108. var x = Math.floor(canvas.width / turtleBlocksScale) - btnSize / 2;
  2109. var y = Math.floor(btnSize / 2);
  2110. var dx = 0;
  2111. var dy = btnSize;
  2112. menuContainer = _makeButton('menu-button', '', x, y, btnSize, menuButtonsVisible ? 90 : undefined);
  2113. _loadButtonDragHandler(menuContainer, x, y, _doMenuButton, null, null, null, null);
  2114. for (var i = 0; i < menuNames.length; i++) {
  2115. if (!getAuxToolbarButtonNames(menuNames[i][0])) {
  2116. continue;
  2117. }
  2118. x += dx;
  2119. y += dy;
  2120. var container = _makeButton(menuNames[i][0] + '-button', menuNames[i][2], x, y, btnSize, 0);
  2121. _loadButtonDragHandler(container, x, y, menuNames[i][1], null, null, null, null);
  2122. onscreenMenu.push(container);
  2123. if (menuNames[i][0] === 'utility') {
  2124. utilityButton = container;
  2125. } else if (menuNames[i][0] === 'save') {
  2126. saveButton = container;
  2127. }
  2128. container.visible = false;
  2129. }
  2130. if (menuButtonsVisible) {
  2131. for (var button in onscreenMenu) {
  2132. onscreenMenu[button].visible = true;
  2133. }
  2134. }
  2135. };
  2136. function doPopdownPalette() {
  2137. console.log('doPopdownPalette');
  2138. var p = new PopdownPalette(palettes);
  2139. p.popdown();
  2140. };
  2141. function _showHelp(firstTime) {
  2142. helpIdx = 0;
  2143. if (firstTime) {
  2144. if (helpContainer == null) {
  2145. helpContainer = new createjs.Container();
  2146. stage.addChild(helpContainer);
  2147. helpContainer.x = 65;
  2148. helpContainer.y = 65;
  2149. helpContainer.on('click', function (event) {
  2150. var bounds = helpContainer.getBounds();
  2151. if (event.stageY < helpContainer.y + bounds.height / 2) {
  2152. helpContainer.visible = false;
  2153. docById('helpElem').style.visibility = 'hidden';
  2154. } else {
  2155. helpIdx += 1;
  2156. if (helpIdx >= HELPCONTENT.length) {
  2157. helpIdx = 0;
  2158. }
  2159. var imageScale = 55 * turtleBlocksScale;
  2160. helpElem.innerHTML = '<img src ="' + HELPCONTENT[helpIdx][2] + '" style="height:' + imageScale + 'px; width: auto"></img> <h2>' + HELPCONTENT[helpIdx][0] + '</h2><p>' + HELPCONTENT[helpIdx][1] + '</p>';
  2161. }
  2162. update = true;
  2163. });
  2164. var img = new Image();
  2165. img.onload = function () {
  2166. console.log(turtleBlocksScale);
  2167. var bitmap = new createjs.Bitmap(img);
  2168. /*
  2169. if (turtleBlocksScale > 1) {
  2170. bitmap.scaleX = bitmap.scaleY = bitmap.scale = turtleBlocksScale;
  2171. } else {
  2172. bitmap.scaleX = bitmap.scaleY = bitmap.scale = 1.125;
  2173. }
  2174. */
  2175. if (helpContainer.children.length > 0) {
  2176. console.log('delete old help container');
  2177. helpContainer.removeChild(helpContainer.children[0]);
  2178. }
  2179. helpContainer.addChild(bitmap)
  2180. var bounds = helpContainer.getBounds();
  2181. var hitArea = new createjs.Shape();
  2182. hitArea.graphics.beginFill('#FFF').drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
  2183. hitArea.x = 0;
  2184. hitArea.y = 0;
  2185. helpContainer.hitArea = hitArea;
  2186. docById('helpElem').innerHTML = '<img src ="' + HELPCONTENT[helpIdx][2] + '"</img> <h2>' + HELPCONTENT[helpIdx][0] + '</h2><p>' + HELPCONTENT[helpIdx][1] + '</p>';
  2187. if (!doneTour) {
  2188. docById('helpElem').style.visibility = 'visible';
  2189. }
  2190. update = true;
  2191. };
  2192. img.src = 'images/help-container.svg';
  2193. }
  2194. var helpElem = docById('helpElem');
  2195. helpElem.style.position = 'absolute';
  2196. helpElem.style.display = 'block';
  2197. helpElem.style.paddingLeft = 20 * turtleBlocksScale + 'px';
  2198. helpElem.style.paddingRight = 20 * turtleBlocksScale + 'px';
  2199. helpElem.style.paddingTop = '0px';
  2200. helpElem.style.paddingBottom = 20 * turtleBlocksScale + 'px';
  2201. helpElem.style.fontSize = 20 + 'px'; // * turtleBlocksScale + 'px';
  2202. helpElem.style.color = '#000000'; // '#ffffff';
  2203. helpElem.style.left = 65 * turtleBlocksScale + 'px';
  2204. helpElem.style.top = 105 * turtleBlocksScale + 'px';
  2205. var w = Math.min(300, 300); // * turtleBlocksScale);
  2206. var h = Math.min(300, 300); // * turtleBlocksScale);
  2207. helpElem.style.width = w + 'px';
  2208. helpElem.style.height = h + 'px';
  2209. if (turtleBlocksScale > 1) {
  2210. var bitmap = helpContainer.children[0];
  2211. if (bitmap != undefined) {
  2212. // bitmap.scaleX = bitmap.scaleY = bitmap.scale = turtleBlocksScale;
  2213. }
  2214. }
  2215. }
  2216. doneTour = storage.doneTour === 'true';
  2217. if (firstTime && doneTour) {
  2218. docById('helpElem').style.visibility = 'hidden';
  2219. helpContainer.visible = false;
  2220. } else {
  2221. if (sugarizerCompatibility.isInsideSugarizer()) {
  2222. sugarizerCompatibility.data.doneTour = 'true';
  2223. } else {
  2224. storage.doneTour = 'true';
  2225. }
  2226. docById('helpElem').innerHTML = '<img src ="' + HELPCONTENT[helpIdx][2] + '"</img> <h2>' + HELPCONTENT[helpIdx][0] + '</h2><p>' + HELPCONTENT[helpIdx][1] + '</p>';
  2227. docById('helpElem').style.visibility = 'visible';
  2228. helpContainer.visible = true;
  2229. update = true;
  2230. // Make sure the palettes and the secondary menus are
  2231. // visible while help is shown.
  2232. palettes.show();
  2233. if (!menuButtonsVisible) {
  2234. doMenuAnimation(1);
  2235. }
  2236. }
  2237. };
  2238. function _doMenuButton() {
  2239. _doMenuAnimation(1);
  2240. };
  2241. function _doMenuAnimation() {
  2242. var bitmap = last(menuContainer.children);
  2243. if (bitmap != null) {
  2244. var r = bitmap.rotation;
  2245. createjs.Tween.get(bitmap)
  2246. .to({
  2247. rotation: r
  2248. })
  2249. .to({
  2250. rotation: r + 90
  2251. }, 500);
  2252. } else {
  2253. // Race conditions during load
  2254. setTimeout(_doMenuAnimation, 50);
  2255. }
  2256. setTimeout(function () {
  2257. if (menuButtonsVisible) {
  2258. menuButtonsVisible = false;
  2259. for (var button in onscreenMenu) {
  2260. onscreenMenu[button].visible = false;
  2261. }
  2262. } else {
  2263. menuButtonsVisible = true;
  2264. for (var button in onscreenMenu) {
  2265. onscreenMenu[button].visible = true;
  2266. }
  2267. }
  2268. update = true;
  2269. }, 500);
  2270. };
  2271. function _toggleToolbar() {
  2272. buttonsVisible = !buttonsVisible;
  2273. menuContainer.visible = buttonsVisible;
  2274. headerContainer.visible = buttonsVisible;
  2275. for (var button in onscreenButtons) {
  2276. onscreenButtons[button].visible = buttonsVisible;
  2277. }
  2278. for (var button in onscreenMenu) {
  2279. onscreenMenu[button].visible = buttonsVisible;
  2280. }
  2281. update = true;
  2282. };
  2283. function _makeButton(name, label, x, y, size, rotation, parent) {
  2284. var container = new createjs.Container();
  2285. if (name === 'paste-disabled-button') {
  2286. pasteContainer = container;
  2287. }
  2288. if (parent == undefined) {
  2289. stage.addChild(container);
  2290. } else {
  2291. parent.addChild(container);
  2292. }
  2293. container.x = x;
  2294. container.y = y;
  2295. var text = new createjs.Text(label, '14px Sans', '#282828');
  2296. if (container.y < 55) {
  2297. if (container.x < 55) {
  2298. text.textAlign = 'left';
  2299. text.x = -14;
  2300. } else {
  2301. text.textAlign = 'center';
  2302. text.x = 0;
  2303. }
  2304. text.y = 30;
  2305. } else {
  2306. text.textAlign = 'right';
  2307. text.x = -28;
  2308. text.y = 0;
  2309. }
  2310. text.visible = false;
  2311. container.on('mouseover', function (event) {
  2312. for (var c = 0; c < container.children.length; c++) {
  2313. if (container.children[c].text != undefined) {
  2314. container.children[c].visible = true;
  2315. break;
  2316. }
  2317. }
  2318. });
  2319. container.on('mouseout', function (event) {
  2320. for (var c = 0; c < container.children.length; c++) {
  2321. if (container.children[c].text != undefined) {
  2322. container.children[c].visible = false;
  2323. break;
  2324. }
  2325. }
  2326. });
  2327. var img = new Image();
  2328. img.onload = function () {
  2329. var originalSize = 55; // this is the original svg size
  2330. var halfSize = Math.floor(size / 2);
  2331. var bitmap = new createjs.Bitmap(img);
  2332. if (size !== originalSize) {
  2333. bitmap.scaleX = size / originalSize;
  2334. bitmap.scaleY = size / originalSize;
  2335. }
  2336. bitmap.regX = halfSize / bitmap.scaleX;
  2337. bitmap.regY = halfSize / bitmap.scaleY;
  2338. if (rotation !== undefined) {
  2339. bitmap.rotation = rotation;
  2340. }
  2341. container.addChild(bitmap);
  2342. var hitArea = new createjs.Shape();
  2343. hitArea.graphics.beginFill('#FFF').drawEllipse(-halfSize, -halfSize, size, size);
  2344. hitArea.x = 0;
  2345. hitArea.y = 0;
  2346. container.hitArea = hitArea;
  2347. bitmap.cache(0, 0, size, size);
  2348. bitmap.updateCache();
  2349. update = true;
  2350. };
  2351. img.src = 'header-icons/' + name + '.svg';
  2352. container.addChild(text);
  2353. return container;
  2354. };
  2355. function _loadButtonDragHandler(container, ox, oy, action, longAction, extraLongAction, longImg, extraLongImg) {
  2356. // Prevent multiple button presses (i.e., debounce).
  2357. var locked = false;
  2358. if (longAction === null) {
  2359. longAction = action;
  2360. }
  2361. if (extraLongAction === null) {
  2362. extraLongAction = longAction;
  2363. }
  2364. // Long and extra-long press variables declaration
  2365. var pressTimer, pressTimerExtra, isLong = false, isExtraLong = false;
  2366. var formerContainer = container;
  2367. container.on('mousedown', function (event) {
  2368. var moved = true;
  2369. var offset = {
  2370. x: container.x - Math.round(event.stageX / turtleBlocksScale),
  2371. y: container.y - Math.round(event.stageY / turtleBlocksScale)
  2372. };
  2373. pressTimer = setTimeout(function () {
  2374. isLong = true;
  2375. if (longImg !== null) {
  2376. container.visible = false;
  2377. container = _makeButton(longImg, '', ox, oy, cellSize, 0);
  2378. }
  2379. }, 500);
  2380. pressTimerExtra = setTimeout(function () {
  2381. isExtraLong = true;
  2382. if (extraLongImg !== null) {
  2383. container.visible = false;
  2384. container = _makeButton(extraLongImg, '', ox, oy, cellSize, 0);
  2385. }
  2386. }, 1000);
  2387. var circles = showButtonHighlight(ox, oy, cellSize / 2, event, turtleBlocksScale, stage);
  2388. container.on('pressup', function (event) {
  2389. hideButtonHighlight(circles, stage);
  2390. container.x = ox;
  2391. container.y = oy;
  2392. if (longImg !== null || extraLongImg !== null) {
  2393. container.visible = false;
  2394. container = formerContainer;
  2395. container.visible = true;
  2396. }
  2397. if (action != null && moved && !locked) {
  2398. locked = true;
  2399. setTimeout(function () {
  2400. locked = false;
  2401. }, 500);
  2402. clearTimeout(pressTimer);
  2403. clearTimeout(pressTimerExtra);
  2404. if (!isLong) {
  2405. action();
  2406. } else if (!isExtraLong) {
  2407. longAction();
  2408. } else {
  2409. extraLongAction();
  2410. }
  2411. }
  2412. moved = false;
  2413. });
  2414. isLong = false;
  2415. isExtraLong = false;
  2416. });
  2417. };
  2418. };
  2419. });