// Copyright (c) 2014-17 Walter Bender // Copyright (c) Yash Khandelwal, GSoC'15 // Copyright (c) 2016 Tymon Radzik // // This program is free software; you can redistribute it and/or // modify it under the terms of the The GNU Affero General Public // License as published by the Free Software Foundation; either // version 3 of the License, or (at your option) any later version. // // You should have received a copy of the GNU Affero General Public // License along with this library; if not, write to the Free Software // Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA // // Note: This code is inspired by the Python Turtle Blocks project // (https://github.com/walterbender/turtleart), but implemented from // scratch. -- Walter Bender, October 2014. const _THIS_IS_MUSIC_BLOCKS_ = false; const _THIS_IS_TURTLE_BLOCKS_ = !_THIS_IS_MUSIC_BLOCKS_; function facebookInit() { window.fbAsyncInit = function () { FB.init({ appId: '1496189893985945', xfbml: true, version: 'v2.1' }); // ADD ADDITIONAL FACEBOOK CODE HERE }; }; try { (function (d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) { return; } js = d.createElement(s); js.id = id; js.src = "https://connect.facebook.net/en_US/sdk.js"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk')); } catch (e) { }; var lang = document.webL10n.getLanguage(); if (lang.indexOf('-') !== -1) { lang = lang.slice(0, lang.indexOf("-")); document.webL10n.setLanguage(lang); } if (_THIS_IS_MUSIC_BLOCKS_) { 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']; } else { 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']; } define(MYDEFINES, function (compatibility) { // Manipulate the DOM only when it is ready. requirejs(['domReady!','activity/sugarizer-compatibility'], function (doc) { if (sugarizerCompatibility.isInsideSugarizer()) { window.addEventListener('localized', function() { sugarizerCompatibility.loadData(function () { domReady(doc); }); }); document.webL10n.setLanguage(sugarizerCompatibility.getLanguage()); } else { domReady(doc); } }); function domReady(doc) { createDefaultStack(); createHelpContent(); // facebookInit(); window.scroll(0, 0); try { meSpeak.loadConfig('lib/mespeak_config.json'); var lang = document.webL10n.getLanguage(); if (sugarizerCompatibility.isInsideSugarizer()) { lang = sugarizerCompatibility.getLanguage(); } if (['es', 'ca', 'de', 'el', 'eo', 'fi', 'fr', 'hu', 'it', 'kn', 'la', 'lv', 'nl', 'pl', 'pt', 'ro', 'sk', 'sv', 'tr', 'zh'].indexOf(lang) !== -1) { meSpeak.loadVoice('lib/voices/' + lang + '.json'); } else { meSpeak.loadVoice('lib/voices/en/en.json'); } } catch (e) { console.log(e); } var canvas = docById('myCanvas'); var queue = new createjs.LoadQueue(false); // Check for the various File API support. if (window.File && window.FileReader && window.FileList && window.Blob) { var files = true; } else { alert('The File APIs are not fully supported in this browser.'); var files = false; } // Set up a file chooser for the doOpen function. var fileChooser = docById('myOpenFile'); // Set up a file chooser for the doOpenPlugin function. var pluginChooser = docById('myOpenPlugin'); // The file chooser for all files. var allFilesChooser = docById('myOpenAll'); // Are we running off of a server? var server = true; var turtleBlocksScale = 1; var stage; var turtles; var palettes; var blocks; var logo; var clearBox; var utilityBox; var thumbnails; var buttonsVisible = true; var headerContainer = null; var toolbarButtonsVisible = true; var menuButtonsVisible = true; var menuContainer = null; var scrollBlockContainer = false; var currentKey = ''; var currentKeyCode = 0; var lastKeyCode = 0; var pasteContainer = null; var pasteImage = null; var chartBitmap = null; if (_THIS_IS_TURTLE_BLOCKS_) { var saveBox; } // Calculate the palette colors. for (var p in PALETTECOLORS) { PALETTEFILLCOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1], PALETTECOLORS[p][2]); PALETTESTROKECOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1] - 30, PALETTECOLORS[p][2]); PALETTEHIGHLIGHTCOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1] + 10, PALETTECOLORS[p][2]); HIGHLIGHTSTROKECOLORS[p] = getMunsellColor(PALETTECOLORS[p][0], PALETTECOLORS[p][1] - 50, PALETTECOLORS[p][2]); } pluginObjs = { 'PALETTEPLUGINS': {}, 'PALETTEFILLCOLORS': {}, 'PALETTESTROKECOLORS': {}, 'PALETTEHIGHLIGHTCOLORS': {}, 'FLOWPLUGINS': {}, 'ARGPLUGINS': {}, 'BLOCKPLUGINS': {}, 'ONLOAD': {}, 'ONSTART': {}, 'ONSTOP': {} }; // Stacks of blocks saved in local storage var macroDict = {}; var stopTurtleContainer = null; var stopTurtleContainerX = 0; var stopTurtleContainerY = 0; var homeButtonContainers = []; var homeButtonContainersX = 0; var homeButtonContainersY = 0; var cameraID = null; var toLang = null; var fromLang = null; // initial scroll position var scrollX = 0; var scrollY = 0; // default values const DEFAULTDELAY = 500; // milleseconds const TURTLESTEP = -1; // Run in step-by-step mode const BLOCKSCALES = [1, 1.5, 2, 3, 4]; var blockscale = BLOCKSCALES.indexOf(DEFAULTBLOCKSCALE); if (blockscale === -1) { blockscale = 1; } // Time when we hit run var time = 0; // Used by pause block var waitTime = {}; // Used to track mouse state for mouse button block var stageMouseDown = false; var stageX = 0; var stageY = 0; var onXO = (screen.width === 1200 && screen.height === 900) || (screen.width === 900 && screen.height === 1200); var cellSize = 55; if (onXO) { cellSize = 75; } var onscreenButtons = []; var onscreenMenu = []; var utilityButton = null; var saveButton = null; var helpContainer = null; var helpIdx = 0; var firstRun = true; pluginsImages = {}; // Sometimes (race condition?) Firefox does not properly // initialize strings in musicutils. These methods ensure that // the names are never null. console.log('initing i18n for music terms'); initDrumI18N(); initModeI18N(); initVoiceI18N(); window.onblur = function () { logo.doStopTurtle(); }; function _findBlocks() { logo.showBlocks(); blocksContainer.x = 0; blocksContainer.y = 0; palettes.initial_x = 55; palettes.initial_y = 55; palettes.updatePalettes(); var x = 100 * turtleBlocksScale; var y = 100 * turtleBlocksScale; for (var blk in blocks.blockList) { if (!blocks.blockList[blk].trash) { var myBlock = blocks.blockList[blk]; if (myBlock.connections[0] == null) { var dx = x - myBlock.container.x; var dy = y - myBlock.container.y; blocks.moveBlockRelative(blk, dx, dy); blocks.findDragGroup(blk); if (blocks.dragGroup.length > 0) { for (var b = 0; b < blocks.dragGroup.length; b++) { var bblk = blocks.dragGroup[b]; if (b !== 0) { blocks.moveBlockRelative(bblk, dx, dy); } } } x += 200 * turtleBlocksScale; if (x > (canvas.width - 100) / (turtleBlocksScale)) { x = 100 * turtleBlocksScale; y += 100 * turtleBlocksScale; } } } } // Blocks are all home, so reset go-home-button. homeButtonContainers[0].visible = false; homeButtonContainers[1].visible = true; boundary.hide(); }; function _allClear() { if (chartBitmap != null) { stage.removeChild(chartBitmap); chartBitmap = null; } logo.boxes = {}; logo.time = 0; hideMsgs(); logo.setBackgroundColor(-1); logo.lilypondOutput = LILYPONDHEADER; for (var turtle = 0; turtle < turtles.turtleList.length; turtle++) { logo.turtleHeaps[turtle] = []; logo.lilypondStaging[turtle] = []; turtles.turtleList[turtle].doClear(true, true); } blocksContainer.x = 0; blocksContainer.y = 0; // Code specific to cleaning up music blocks Element.prototype.remove = function () { this.parentElement.removeChild(this); }; NodeList.prototype.remove = HTMLCollection.prototype.remove = function () { for (var i = 0, len = this.length; i < len; i++) { if(this[i] && this[i].parentElement) { this[i].parentElement.removeChild(this[i]); } } }; var table = document.getElementById("myTable"); if(table != null) { table.remove(); } /* var canvas = document.getElementById("music"); var context = canvas.getContext("2d"); context.clearRect(0, 0, canvas.width, canvas.height); */ }; function _doFastButton(env) { var currentDelay = logo.turtleDelay; var playingWidget = false; logo.setTurtleDelay(0); if (_THIS_IS_MUSIC_BLOCKS_) { if (docById('ptmDiv').style.visibility === 'visible') { playingWidget = true; logo.pitchTimeMatrix.playAll(); } if (docById('pscDiv').style.visibility === 'visible') { playingWidget = true; pitchstaircase.playUpAndDown(); } if (docById('rulerDiv').style.visibility === 'visible') { // If the tempo widget is open, sync it up with the // rhythm ruler. if (docById('tempoDiv').style.visibility === 'visible') { if (tempo.isMoving) { tempo.pause(); } tempo.resume(); } playingWidget = true; rhythmruler.playAll(); } // We were using the run button to play a widget, not // the turtles. if (playingWidget) { return; } // Restart tempo widget and run blocks. if (docById('tempoDiv').style.visibility === 'visible') { if (tempo.isMoving) { tempo.pause(); } tempo.resume(); } } if (!turtles.running()) { console.log('running'); logo.runLogoCommands(null, env); } else { if (currentDelay !== 0) { // keep playing at full speep console.log('running from step'); logo.step(); } else { // stop and restart console.log('stopping...'); logo.doStopTurtle(); setTimeout(function () { console.log('and running'); logo.runLogoCommands(null, env); }, 500); } } }; function _doSlowButton() { logo.setTurtleDelay(DEFAULTDELAY); if (_THIS_IS_MUSIC_BLOCKS_ && docById('ptmDiv').style.visibility === 'visible') { logo.pitchTimeMatrix.playAll(); } else if (!turtles.running()) { logo.runLogoCommands(); } else { logo.step(); } }; function _doStepButton() { var turtleCount = 0; for (var turtle in logo.stepQueue) { turtleCount += 1; } if (turtleCount === 0 || logo.turtleDelay !== TURTLESTEP) { // Either we haven't set up a queue or we are // switching modes. logo.setTurtleDelay(TURTLESTEP); // Queue and take first step. if (!turtles.running()) { logo.runLogoCommands(); } logo.step(); } else { logo.setTurtleDelay(TURTLESTEP); logo.step(); } }; function _doSlowMusicButton() { logo.setNoteDelay(DEFAULTDELAY); if (docById('ptmDiv').style.visibility === 'visible') { logo.pitchTimeMatrix.playAll(); } else if (!turtles.running()) { logo.runLogoCommands(); } else { logo.stepNote(); } }; function _doStepMusicButton() { var turtleCount = 0; for (var turtle in logo.stepQueue) { turtleCount += 1; } if (turtleCount === 0 || logo.TurtleDelay !== TURTLESTEP) { // Either we haven't set up a queue or we are // switching modes. logo.setTurtleDelay(TURTLESTEP); // Queue and take first step. if (!turtles.running()) { logo.runLogoCommands(); } logo.stepNote(); } else { logo.setTurtleDelay(TURTLESTEP); logo.stepNote(); } }; var stopTurtle = false; function doStopButton() { logo.doStopTurtle(); }; var cartesianVisible = false; function _doCartesian() { if (cartesianVisible) { _hideCartesian(); cartesianVisible = false; } else { _showCartesian(); cartesianVisible = true; } }; var polarVisible = false; function _doPolar() { if (polarVisible) { _hidePolar(); polarVisible = false; } else { _showPolar(); polarVisible = true; } }; function toggleScroller() { scrollBlockContainer = !scrollBlockContainer; }; function closeAnalytics(chartBitmap, ctx) { var button = this; button.x = (canvas.width / (2 * turtleBlocksScale)) + (300 / Math.sqrt(2)); button.y = 300.00 - (300.00 / Math.sqrt(2)); this.closeButton = _makeButton('cancel-button', _('Close'), button.x, button.y, 55, 0); this.closeButton.on('click', function (event) { console.log('Deleting Chart'); button.closeButton.visible = false; stage.removeChild(chartBitmap); logo.showBlocks(); update = true; ctx.clearRect(0, 0, 600, 600); }); }; function _isCanvasBlank(canvas) { var blank = document.createElement('canvas'); blank.width = canvas.width; blank.height = canvas.height; return canvas.toDataURL() == blank.toDataURL(); }; function doAnalytics() { var myChart = docById('myChart'); if(_isCanvasBlank(myChart) == false) { return ; } var ctx = myChart.getContext('2d'); document.body.style.cursor = 'wait'; var myRadarChart = null; var scores = analyzeProject(blocks); console.log(scores); var data = scoreToChartData(scores); var Analytics = this; Analytics.close = closeAnalytics; var __callback = function () { var imageData = myRadarChart.toBase64Image(); var img = new Image(); img.onload = function () { var chartBitmap = new createjs.Bitmap(img); stage.addChild(chartBitmap); chartBitmap.x = (canvas.width / (2 * turtleBlocksScale)) - (300); chartBitmap.y = 0; chartBitmap.scaleX = chartBitmap.scaleY = chartBitmap.scale = 600 / chartBitmap.image.width; logo.hideBlocks(); update = true; document.body.style.cursor = 'default'; Analytics.close(chartBitmap, ctx); }; img.src = imageData; }; var options = getChartOptions(__callback); console.log('creating new chart'); myRadarChart = new Chart(ctx).Radar(data, options); }; function doBiggerFont() { if (blockscale < BLOCKSCALES.length - 1) { blockscale += 1; blocks.setBlockScale(BLOCKSCALES[blockscale]); } }; function doSmallerFont() { if (blockscale > 0) { blockscale -= 1; blocks.setBlockScale(BLOCKSCALES[blockscale]); } }; // Do we need to update the stage? var update = true; // The dictionary of action name: block var actions = {}; // The dictionary of box name: value var boxes = {}; // Coordinate grid var cartesianBitmap = null; // Polar grid var polarBitmap = null; // Msg block var msgText = null; // ErrorMsg block var errorMsgText = null; var errorMsgArrow = null; var errorArtwork = {}; const ERRORARTWORK = ['emptybox', 'emptyheap', 'negroot', 'noinput', 'zerodivide', 'notanumber', 'nostack', 'notastring', 'nomicrophone']; // Get things started init(); function init() { docById('loader').className = 'loader'; stage = new createjs.Stage(canvas); createjs.Touch.enable(stage); createjs.Ticker.timingMode = createjs.Ticker.RAF_SYNCHED; createjs.Ticker.setFPS(30); createjs.Ticker.addEventListener('tick', stage); createjs.Ticker.addEventListener('tick', __tick); _createMsgContainer('#ffffff', '#7a7a7a', function (text) { msgText = text; }, 55); _createMsgContainer('#ffcbc4', '#ff0031', function (text) { errorMsgText = text; }, 110); _createErrorContainers(); /* Z-Order (top to bottom): * menus * palettes * blocks * trash * turtles * logo (drawing) */ palettesContainer = new createjs.Container(); blocksContainer = new createjs.Container(); trashContainer = new createjs.Container(); turtleContainer = new createjs.Container(); stage.addChild(turtleContainer, trashContainer, blocksContainer, palettesContainer); _setupBlocksContainerEvents(); trashcan = new Trashcan(); trashcan .setCanvas(canvas) .setStage(trashContainer) .setSize(cellSize) .setRefreshCanvas(refreshCanvas) .init(); turtles = new Turtles(); turtles .setCanvas(canvas) .setStage(turtleContainer) .setRefreshCanvas(refreshCanvas); // Put the boundary in the blocks container so it scrolls // with the blocks. boundary = new Boundary(); boundary .setStage(blocksContainer) .init(); blocks = new Blocks(); blocks .setCanvas(canvas) .setStage(blocksContainer) .setRefreshCanvas(refreshCanvas) .setTrashcan(trashcan) .setUpdateStage(stage.update) .setGetStageScale(getStageScale) .setTurtles(turtles) .setErrorMsg(errorMsg); blocks.makeCopyPasteButtons(_makeButton, updatePasteButton); turtles.setBlocks(blocks); palettes = new Palettes(); palettes .setCanvas(canvas) .setStage(palettesContainer) .setRefreshCanvas(refreshCanvas) .setSize(cellSize) .setTrashcan(trashcan) .setBlocks(blocks) .init(); initPalettes(palettes); logo = new Logo(); logo .setCanvas(canvas) .setBlocks(blocks) .setTurtles(turtles) .setStage(turtleContainer) .setRefreshCanvas(refreshCanvas) .setTextMsg(textMsg) .setErrorMsg(errorMsg) .setHideMsgs(hideMsgs) .setOnStopTurtle(onStopTurtle) .setOnRunTurtle(onRunTurtle) .setGetStageX(getStageX) .setGetStageY(getStageY) .setGetStageMouseDown(getStageMouseDown) .setGetCurrentKeyCode(getCurrentKeyCode) .setClearCurrentKeyCode(clearCurrentKeyCode) .setMeSpeak(meSpeak) .setSaveLocally(saveLocally); blocks.setLogo(logo); // Set the default background color... logo.setBackgroundColor(-1); clearBox = new ClearBox(); clearBox .setCanvas(canvas) .setStage(stage) .setRefreshCanvas(refreshCanvas) .setClear(sendAllToTrash); if (_THIS_IS_TURTLE_BLOCKS_) { saveBox = new SaveBox(); saveBox .setCanvas(canvas) .setStage(stage) .setRefreshCanvas(refreshCanvas) .setSaveTB(doSaveTB) .setSaveSVG(doSaveSVG) .setSavePNG(doSavePNG) .setSavePlanet(doUploadToPlanet) .setSaveFB(doShareOnFacebook); } utilityBox = new UtilityBox(); utilityBox .setStage(stage) .setRefreshCanvas(refreshCanvas) .setBigger(doBiggerFont) .setSmaller(doSmallerFont) .setPlugins(doOpenPlugin) .setStats(doAnalytics) .setScroller(toggleScroller); thumbnails = new SamplesViewer(); thumbnails .setStage(stage) .setRefreshCanvas(refreshCanvas) .setClear(sendAllToTrash) .setLoad(loadProject) .setLoadRaw(loadRawProject) .init(); initBasicProtoBlocks(palettes, blocks); // Load any macros saved in local storage. macroData = storage.macros; if (macroData != null) { processMacroData(macroData, palettes, blocks, macroDict); } // Blocks and palettes need access to the macros dictionary. blocks.setMacroDictionary(macroDict); palettes.setMacroDictionary(macroDict); // Load any plugins saved in local storage. pluginData = storage.plugins; if (pluginData != null) { var obj = processPluginData(pluginData, palettes, blocks, logo.evalFlowDict, logo.evalArgDict, logo.evalParameterDict, logo.evalSetterDict, logo.evalOnStartList, logo.evalOnStopList); updatePluginObj(obj); } // Load custom mode saved in local storage. var custommodeData = storage.custommode; if (custommodeData != undefined) { customMode = JSON.parse(custommodeData); console.log('restoring custom mode: ' + customMode); } fileChooser.addEventListener('click', function (event) { this.value = null; }); fileChooser.addEventListener('change', function (event) { // Read file here. var reader = new FileReader(); reader.onload = (function (theFile) { // Show busy cursor. document.body.style.cursor = 'wait'; setTimeout(function () { var rawData = reader.result; var cleanData = rawData.replace('\n', ' '); var obj = JSON.parse(cleanData); // First, hide the palettes as they will need updating. for (var name in blocks.palettes.dict) { blocks.palettes.dict[name].hideMenu(true); } refreshCanvas(); blocks.loadNewBlocks(obj); // Restore default cursor. document.body.style.cursor = 'default'; }, 200); }); reader.readAsText(fileChooser.files[0]); }, false); allFilesChooser.addEventListener('click', function (event) { this.value = null; }); pluginChooser.addEventListener('click', function (event) { window.scroll(0, 0); this.value = null; }); pluginChooser.addEventListener('change', function (event) { window.scroll(0, 0); // Read file here. var reader = new FileReader(); reader.onload = (function (theFile) { // Show busy cursor. document.body.style.cursor = 'wait'; setTimeout(function () { obj = processRawPluginData(reader.result, palettes, blocks, errorMsg, logo.evalFlowDict, logo.evalArgDict, logo.evalParameterDict, logo.evalSetterDict, logo.evalOnStartList, logo.evalOnStopList); // Save plugins to local storage. if (obj != null) { var pluginObj = preparePluginExports(obj); console.log(pluginObj); storage.plugins = pluginObj; // preparePluginExports(obj)); } // Refresh the palettes. setTimeout(function () { if (palettes.visible) { palettes.hide(); } palettes.show(); palettes.bringToTop(); }, 1000); // Restore default cursor. document.body.style.cursor = 'default'; }, 200); }); reader.readAsText(pluginChooser.files[0]); }, false); // Workaround to chrome security issues // createjs.LoadQueue(true, null, true); // Enable touch interactions if supported on the current device. // FIXME: voodoo // createjs.Touch.enable(stage, false, true); // Keep tracking the mouse even when it leaves the canvas. stage.mouseMoveOutside = true; // Enabled mouse over and mouse out events. stage.enableMouseOver(10); // default is 20 cartesianBitmap = _createGrid('images/Cartesian.svg'); polarBitmap = _createGrid('images/polar.svg'); var URL = window.location.href; var projectName = null; var runProjectOnLoad = false; _setupAndroidToolbar(); // Scale the canvas relative to the screen size. _onResize(); var urlParts; var env = []; if (!sugarizerCompatibility.isInsideSugarizer() && URL.indexOf('?') > 0) { var urlParts = URL.split('?'); if (urlParts[1].indexOf('&') > 0) { var newUrlParts = urlParts[1].split('&'); for (var i = 0; i < newUrlParts.length; i++) { if (newUrlParts[i].indexOf('=') > 0) { var args = newUrlParts[i].split('='); switch (args[0].toLowerCase()) { case 'file': projectName = args[1]; break; case 'run': if (args[1].toLowerCase() === 'true') runProjectOnLoad = true; break; case 'inurl': var url = args[1]; var getJSON = function (url) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open('get', url, true); xhr.responseType = 'json'; xhr.onload = function () { var status = xhr.status; if (status === 200) { resolve(xhr.response); } else { reject(status); } }; xhr.send(); }); }; getJSON(url).then(function (data) { console.log('Your Json result is: ' + data.arg); //you can comment this, i used it to debug n = data.arg; env.push(parseInt(n)); }, function (status) { //error detection.... alert('Something went wrong.'); }); break; case 'outurl': var url = args[1]; break; default: errorMsg("Invalid parameters"); } } } } else { if (urlParts[1].indexOf('=') > 0) var args = urlParts[1].split('='); //File is the only arg that can stand alone if (args[0].toLowerCase() === 'file') { projectName = args[1]; } } } if (projectName != null) { setTimeout(function () { console.log('loading ' + projectName); loadStartWrapper(loadProject, projectName, runProjectOnLoad, env); }, 2000); } else { setTimeout(function () { loadStartWrapper(_loadStart); }, 2000); } document.addEventListener('mousewheel', scrollEvent, false); document.addEventListener('DOMMouseScroll', scrollEvent, false); this.document.onkeydown = __keyPressed; _hideStopButton(); }; function _setupBlocksContainerEvents() { var moving = false; stage.on('stagemousemove', function (event) { stageX = event.stageX; stageY = event.stageY; }); stage.on('stagemousedown', function (event) { stageMouseDown = true; if (stage.getObjectUnderPoint() != null | turtles.running()) { stage.on('stagemouseup', function (event) { stageMouseDown = false; }); return; } moving = true; lastCords = { x: event.stageX, y: event.stageY }; stage.on('stagemousemove', function (event) { if (!moving) { return; } if (blocks.inLongPress) { blocks.saveStackButton.visible = false; blocks.dismissButton.visible = false; blocks.inLongPress = false; } if (scrollBlockContainer) { blocksContainer.x += event.stageX - lastCords.x; blocksContainer.y += event.stageY - lastCords.y; lastCords = { x: event.stageX, y: event.stageY }; refreshCanvas(); } }); stage.on('stagemouseup', function (event) { stageMouseDown = false; moving = false; }, null, true); // once = true }); }; function scrollEvent(event) { var data = event.wheelDelta || -event.detail; var delta = Math.max(-1, Math.min(1, (data))); var scrollSpeed = 30; if (event.clientX < cellSize) { palettes.menuScrollEvent(delta, scrollSpeed); palettes.hidePaletteIconCircles(); } else { palette = palettes.findPalette(event.clientX / turtleBlocksScale, event.clientY / turtleBlocksScale); if (palette) { palette.scrollEvent(delta, scrollSpeed); } } }; function getStageScale() { return turtleBlocksScale; }; function getStageX() { return turtles.screenX2turtleX(stageX / turtleBlocksScale); }; function getStageY() { return turtles.screenY2turtleY(stageY / turtleBlocksScale); }; function getStageMouseDown() { return stageMouseDown; }; function setCameraID(id) { cameraID = id; }; function _createGrid(imagePath) { var img = new Image(); img.src = imagePath; var container = new createjs.Container(); stage.addChild(container); var bitmap = new createjs.Bitmap(img); container.addChild(bitmap); bitmap.cache(0, 0, 1200, 900); bitmap.x = (canvas.width - 1200) / 2; bitmap.y = (canvas.height - 900) / 2; bitmap.scaleX = bitmap.scaleY = bitmap.scale = 1; bitmap.visible = false; bitmap.updateCache(); return bitmap; }; function _createMsgContainer(fillColor, strokeColor, callback, y) { var container = new createjs.Container(); stage.addChild(container); container.x = (canvas.width - 1000) / 2; container.y = y; container.visible = false; var img = new Image(); var svgData = MSGBLOCK.replace('fill_color', fillColor).replace( 'stroke_color', strokeColor); img.onload = function () { var msgBlock = new createjs.Bitmap(img); container.addChild(msgBlock); var text = new createjs.Text('your message here', '20px Arial', '#000000'); container.addChild(text); text.textAlign = 'center'; text.textBaseline = 'alphabetic'; text.x = 500; text.y = 30; var bounds = container.getBounds(); container.cache(bounds.x, bounds.y, bounds.width, bounds.height); var hitArea = new createjs.Shape(); hitArea.graphics.beginFill('#FFF').drawRect(0, 0, 1000, 42); hitArea.x = 0; hitArea.y = 0; container.hitArea = hitArea; container.on('click', function (event) { container.visible = false; // On the possibility that there was an error // arrow associated with this container if (errorMsgArrow != null) { errorMsgArrow.removeAllChildren(); // Hide the error arrow. } update = true; }); callback(text); blocks.setMsgText(text); }; img.src = 'data:image/svg+xml;base64,' + window.btoa( unescape(encodeURIComponent(svgData))); }; function _createErrorContainers() { // Some error messages have special artwork. for (var i = 0; i < ERRORARTWORK.length; i++) { var name = ERRORARTWORK[i]; _makeErrorArtwork(name); } }; function _makeErrorArtwork(name) { var container = new createjs.Container(); stage.addChild(container); container.x = (canvas.width - 1000) / 2; container.y = 110; errorArtwork[name] = container; errorArtwork[name].name = name; errorArtwork[name].visible = false; var img = new Image(); img.onload = function () { // console.log('creating error message artwork for ' + img.src); var artwork = new createjs.Bitmap(img); container.addChild(artwork); var text = new createjs.Text('', '20px Sans', '#000000'); container.addChild(text); text.x = 70; text.y = 10; var bounds = container.getBounds(); container.cache(bounds.x, bounds.y, bounds.width, bounds.height); var hitArea = new createjs.Shape(); hitArea.graphics.beginFill('#FFF').drawRect(0, 0, bounds.width, bounds.height); hitArea.x = 0; hitArea.y = 0; container.hitArea = hitArea; container.on('click', function (event) { container.visible = false; // On the possibility that there was an error // arrow associated with this container if (errorMsgArrow != null) { errorMsgArrow.removeAllChildren(); // Hide the error arrow. } update = true; }); }; img.src = 'images/' + name + '.svg'; }; function __keyPressed(event) { if (docById('labelDiv').classList.contains('hasKeyboard')) { return; } if (_THIS_IS_MUSIC_BLOCKS_) { if (docById('BPMInput').classList.contains('hasKeyboard')) { return; } if (docById('musicratio1').classList.contains('hasKeyboard')) { return; } if (docById('musicratio2').classList.contains('hasKeyboard')) { return; } if (docById('dissectNumber').classList.contains('hasKeyboard')) { return; } } const BACKSPACE = 8; const TAB = 9; if (event.keyCode === TAB || event.keyCode === BACKSPACE) { // Prevent browser from grabbing TAB key event.preventDefault(); } const ESC = 27; const ALT = 18; const CTRL = 17; const SHIFT = 16; const RETURN = 13; const SPACE = 32; const HOME = 36; const PAGE_UP = 33; const PAGE_DOWN = 34; const KEYCODE_LEFT = 37; const KEYCODE_RIGHT = 39; const KEYCODE_UP = 38; const KEYCODE_DOWN = 40; if (event.altKey) { switch (event.keyCode) { case 69: // 'E' _allClear(); break; case 82: // 'R' _doFastButton(); break; case 83: // 'S' logo.doStopTurtle(); break; } } else if (event.ctrlKey) { } else { switch (event.keyCode) { case KEYCODE_UP: if (blocks.activeBlock != null) { blocks.moveStackRelative(blocks.activeBlock, 0, -STANDARDBLOCKHEIGHT / 2); blocks.blockMoved(blocks.activeBlock); blocks.adjustDocks(blocks.activeBlock, true); } else if (palettes.mouseOver) { palettes.menuScrollEvent(1, 10); palettes.hidePaletteIconCircles(); } else if (palettes.activePalette != null) { palettes.activePalette.scrollEvent(STANDARDBLOCKHEIGHT, 1); } else if (scrollBlockContainer) { blocksContainer.y -= 21; } break; case KEYCODE_DOWN: if (blocks.activeBlock != null) { blocks.moveStackRelative(blocks.activeBlock, 0, STANDARDBLOCKHEIGHT / 2); blocks.blockMoved(blocks.activeBlock); blocks.adjustDocks(blocks.activeBlock, true); } else if (palettes.mouseOver) { palettes.menuScrollEvent(-1, 10); palettes.hidePaletteIconCircles(); } else if (palettes.activePalette != null) { palettes.activePalette.scrollEvent(-STANDARDBLOCKHEIGHT, 1); } else if (scrollBlockContainer) { blocksContainer.y += 21; } break; case KEYCODE_LEFT: if (blocks.activeBlock != null) { blocks.moveStackRelative(blocks.activeBlock, -STANDARDBLOCKHEIGHT / 2, 0); blocks.blockMoved(blocks.activeBlock); blocks.adjustDocks(blocks.activeBlock, true); } else if (scrollBlockContainer) { blocksContainer.x -= 21; } break; case KEYCODE_RIGHT: if (blocks.activeBlock != null) { blocks.moveStackRelative(blocks.activeBlock, STANDARDBLOCKHEIGHT / 2, 0); blocks.blockMoved(blocks.activeBlock); blocks.adjustDocks(blocks.activeBlock, true); } else if (scrollBlockContainer) { blocksContainer.x += 21; } break; case HOME: if (palettes.mouseOver) { var dy = Math.max(55 - palettes.buttons['rhythm'].y, 0); palettes.menuScrollEvent(1, dy); palettes.hidePaletteIconCircles(); } else if (palettes.activePalette != null) { palettes.activePalette.scrollEvent(-palettes.activePalette.scrollDiff, 1); } else { _findBlocks(); } break; case TAB: break; case ESC: // toggle full screen _toggleToolbar(); break; case RETURN: // toggle run logo.runLogoCommands(); break; default: // currentKey = String.fromCharCode(event.keyCode); // currentKeyCode = event.keyCode; break; } // Always store current key so as not to mask it from // the keyboard block. currentKey = String.fromCharCode(event.keyCode); currentKeyCode = event.keyCode; } }; function getCurrentKeyCode() { return currentKeyCode; }; function clearCurrentKeyCode() { currentKey = ''; currentKeyCode = 0; }; function _onResize() { if (docById('labelDiv').classList.contains('hasKeyboard')) { return; } if (!platform.androidWebkit) { var w = window.innerWidth; var h = window.innerHeight; } else { var w = window.outerWidth; var h = window.outerHeight; } var smallSide = Math.min(w, h); if (smallSide < cellSize * 11) { var mobileSize = true; if (w < cellSize * 10) { turtleBlocksScale = smallSide / (cellSize * 11); } else { turtleBlocksScale = Math.max(smallSide / (cellSize * 11), 0.75); } } else { var mobileSize = false; if (w / 1200 > h / 900) { turtleBlocksScale = w / 1200; } else { turtleBlocksScale = h / 900; } } stage.scaleX = turtleBlocksScale; stage.scaleY = turtleBlocksScale; stage.canvas.width = w; stage.canvas.height = h; /* console.log('Resize: scale ' + turtleBlocksScale + ', windowW ' + w + ', windowH ' + h + ', canvasW ' + canvas.width + ', canvasH ' + canvas.height + ', screenW ' + screen.width + ', screenH ' + screen.height); */ turtles.setScale(turtleBlocksScale); blocks.setScale(turtleBlocksScale); boundary.setScale(w, h, turtleBlocksScale); palettes.setScale(turtleBlocksScale); trashcan.resizeEvent(turtleBlocksScale); _setupAndroidToolbar(mobileSize); // Reposition coordinate grids. cartesianBitmap.x = (canvas.width / (2 * turtleBlocksScale)) - (600); cartesianBitmap.y = (canvas.height / (2 * turtleBlocksScale)) - (450); polarBitmap.x = (canvas.width / (2 * turtleBlocksScale)) - (600); polarBitmap.y = (canvas.height / (2 * turtleBlocksScale)) - (450); update = true; // Setup help now that we have calculated turtleBlocksScale. _showHelp(true); // Hide palette icons on mobile if (mobileSize) { palettes.setMobile(true); palettes.hide(); } else { palettes.setMobile(false); palettes.show(); palettes.bringToTop(); } for (var turtle = 0; turtle < turtles.turtleList.length; turtle++) { turtles.turtleList[turtle].doClear(false, false); } var artcanvas = document.getElementById("overlayCanvas"); artcanvas.width = w; artcanvas.height = h; }; window.onresize = function () { _onResize(); }; function _restoreTrash() { // Restore last stack pushed to trashStack. // First, hide the palettes as they will need updating. for (var name in blocks.palettes.dict) { blocks.palettes.dict[name].hideMenu(true); } refreshCanvas(); var dx = 0; var dy = -cellSize * 3; // Reposition blocks about trash area. if (blocks.trashStacks.length === 0) { console.log('Trash is empty--nothing to do'); return; } var thisBlock = blocks.trashStacks.pop(); // Restore drag group in trash blocks.findDragGroup(thisBlock); for (var b = 0; b < blocks.dragGroup.length; b++) { var blk = blocks.dragGroup[b]; // console.log('Restoring ' + blocks.blockList[blk].name + ' from the trash.'); blocks.blockList[blk].trash = false; blocks.moveBlockRelative(blk, dx, dy); blocks.blockList[blk].show(); } blocks.raiseStackToTop(thisBlock); if (blocks.blockList[thisBlock].name === 'start' || blocks.blockList[thisBlock].name === 'drum') { var turtle = blocks.blockList[thisBlock].value; turtles.turtleList[turtle].trash = false; turtles.turtleList[turtle].container.visible = true; } else if (blocks.blockList[thisBlock].name === 'action') { // We need to add a palette entry for this action. // But first we need to ensure we have a unqiue name, // as the name could have been taken in the interim. var actionArg = blocks.blockList[blocks.blockList[thisBlock].connections[1]]; if (actionArg != null) { var oldName = actionArg.value; // Mark the action block as still being in the // trash so that its name won't be considered when // looking for a unique name. blocks.blockList[thisBlock].trash = true; var uniqueName = blocks.findUniqueActionName(oldName); blocks.blockList[thisBlock].trash = false; if (uniqueName !== actionArg) { console.log('renaming action when restoring from trash. old name: ' + oldName + ' unique name: ' + uniqueName); actionArg.value = uniqueName; var label = actionArg.value.toString(); if (label.length > 8) { label = label.substr(0, 7) + '...'; } actionArg.text.text = label; if (actionArg.label != null) { actionArg.label.value = uniqueName; } actionArg.container.updateCache(); // Check the drag group to ensure any do // blocks are updated (in case of recursion). for (var b = 0; b < blocks.dragGroup.length; b++) { var me = blocks.blockList[blocks.dragGroup[b]]; if (['nameddo', 'nameddoArg', 'namedcalc', 'namedcalcArg'].indexOf(me.name) !== -1 && me.privateData === oldName) { console.log('reassigning nameddo to ' + uniqueName); me.privateData = uniqueName; me.value = uniqueName; var label = me.value.toString(); if (label.length > 8) { label = label.substr(0, 7) + '...'; } me.text.text = label; me.overrideName = label; me.regenerateArtwork(); me.container.updateCache(); } } } var actionName = actionArg.value; if (actionName !== _('action')) { // blocks.checkPaletteEntries('action'); console.log('FIXME: Check for unique action name here'); } } } blocks.refreshCanvas(); }; function _deleteBlocksBox() { clearBox.show(turtleBlocksScale); }; function _doUtilityBox() { utilityBox.init(turtleBlocksScale, utilityButton.x - 27, utilityButton.y, _makeButton); }; function sendAllToTrash(addStartBlock, doNotSave) { // First, hide the palettes as they will need updating. for (var name in blocks.palettes.dict) { blocks.palettes.dict[name].hideMenu(true); } refreshCanvas(); var dx = 0; var dy = cellSize * 3; for (var blk in blocks.blockList) { // If this block is at the top of a stack, push it // onto the trashStacks list. if (blocks.blockList[blk].connections[0] == null) { blocks.trashStacks.push(blk); } if (blocks.blockList[blk].name === 'start' || blocks.blockList[blk].name === 'drum') { console.log('start blk ' + blk + ' value is ' + blocks.blockList[blk].value) var turtle = blocks.blockList[blk].value; if (!blocks.blockList[blk].trash && turtle != null) { console.log('sending turtle ' + turtle + ' to trash'); turtles.turtleList[turtle].trash = true; turtles.turtleList[turtle].container.visible = false; } } else if (blocks.blockList[blk].name === 'action') { if (!blocks.blockList[blk].trash) { blocks.deleteActionBlock(blocks.blockList[blk]); } } blocks.blockList[blk].trash = true; blocks.moveBlockRelative(blk, dx, dy); blocks.blockList[blk].hide(); } if (addStartBlock) { blocks.loadNewBlocks(DATAOBJS); } else if (!doNotSave) { // Overwrite session data too. saveLocally(); } update = true; }; function _changePaletteVisibility() { if (palettes.visible) { palettes.hide(); } else { palettes.show(); palettes.bringToTop(); } }; function _changeBlockVisibility() { if (blocks.visible) { logo.hideBlocks(); palettes.hide(); } else { if (chartBitmap != null) { stage.removeChild(chartBitmap); chartBitmap = null; } logo.showBlocks(); palettes.show(); palettes.bringToTop(); } // Combine block and palette visibility into one button. // _changePaletteVisibility(); }; function _toggleCollapsibleStacks() { if (blocks.visible) { console.log('calling toggleCollapsibles'); blocks.toggleCollapsibles(); } }; function onStopTurtle() { // TODO: plugin support if (stopTurtleContainer.visible) { _hideStopButton(); } }; function onRunTurtle() { // TODO: plugin support // If the stop button is hidden, show it. if (!stopTurtleContainer.visible) { _showStopButton(); } }; function refreshCanvas() { update = true; }; function __tick(event) { // This set makes it so the stage only re-renders when an // event handler indicates a change has happened. if (update) { update = false; // Only update once stage.update(event); } }; function _doOpenSamples() { if (_THIS_IS_MUSIC_BLOCKS_) { localStorage.setItem('isMatrixHidden', docById('ptmDiv').style.visibility); localStorage.setItem('isStaircaseHidden', docById('pscDiv').style.visibility); localStorage.setItem('isPitchDrumMatrixHidden', docById('pdmDiv').style.visibility); localStorage.setItem('isRhythmRulerHidden', docById('rulerDiv').style.visibility); localStorage.setItem('isModeWidgetHidden', docById('modeDiv').style.visibility); localStorage.setItem('isSliderHidden', docById('sliderDiv').style.visibility); localStorage.setItem('isTempoHidden', docById('tempoDiv').style.visibility); if (docById('ptmDiv').style.visibility !== 'hidden') { docById('ptmDiv').style.visibility = 'hidden'; docById('ptmTableDiv').style.visibility = 'hidden'; docById('ptmButtonsDiv').style.visibility = 'hidden'; } if (docById('pdmDiv').style.visibility !== 'hidden') { docById('pdmDiv').style.visibility = 'hidden'; docById('pdmButtonsDiv').style.visibility = 'hidden'; docById('pdmTableDiv').style.visibility = 'hidden'; } if (docById('rulerDiv').style.visibility !== 'hidden') { docById('rulerDiv').style.visibility = 'hidden'; docById('rulerTableDiv').style.visibility = 'hidden'; docById('rulerButtonsDiv').style.visibility = 'hidden'; } if (docById('pscDiv').style.visibility !== 'hidden') { docById('pscDiv').style.visibility = 'hidden'; docById('pscTableDiv').style.visibility = 'hidden'; docById('pscButtonsDiv').style.visibility = 'hidden'; } if (docById('statusDiv').style.visibility !== 'hidden') { docById('statusDiv').style.visibility = 'hidden'; docById('statusButtonsDiv').style.visibility = 'hidden'; docById('statusTableDiv').style.visibility = 'hidden'; } if (docById('sliderDiv').style.visibility !== 'hidden') { docById('sliderDiv').style.visibility = 'hidden'; docById('sliderButtonsDiv').style.visibility = 'hidden'; docById('sliderTableDiv').style.visibility = 'hidden'; } if (docById('modeDiv').style.visibility !== 'hidden') { docById('modeDiv').style.visibility = 'hidden'; docById('modeButtonsDiv').style.visibility = 'hidden'; docById('modeTableDiv').style.visibility = 'hidden'; } if (docById('tempoDiv').style.visibility !== 'hidden') { if (logo.tempo != null) { logo.tempo.hide(); } } } localStorage.setItem('isStatusHidden', docById('statusDiv').style.visibility); logo.doStopTurtle(); helpContainer.visible = false; docById('helpElem').style.visibility = 'hidden'; console.log('save locally'); saveLocally(); thumbnails.show() }; function doSave() { if (_THIS_IS_MUSIC_BLOCKS_) { console.log('Saving .tb file'); var name = 'My Project'; download(name + '.tb', 'data:text/plain;charset=utf-8,' + prepareExport()); } else { saveBox.init(turtleBlocksScale, saveButton.x - 27, saveButton.y - 97, _makeButton); } }; function doSaveTB() { var filename = prompt('Filename:', 'untitled.tb'); // default filename = untitled if (filename != null) { if (fileExt(filename) !== 'tb') { filename += '.tb'; } download(filename, 'data:text/plain;charset=utf-8,' + encodeURIComponent(prepareExport())); } }; function doSaveSVG() { var filename = prompt('Filename:', 'untitled.svg'); if (filename != null) { if (fileExt(filename) !== 'svg') { filename += '.svg'; } var svg = doSVG(logo.canvas, logo, logo.turtles, logo.canvas.width, logo.canvas.height, 1.0); download(filename, 'data:image/svg+xml;utf8,' + svg, filename, '"width=' + logo.canvas.width + ', height=' + logo.canvas.height + '"'); } }; function doSavePNG() { alert("Unavailable at the moment"); //var filename = prompt('Filename:', 'untitled.png'); //if (fileExt(filename) !== 'png') { // filename += '.png'; //} //download(filename, 'data:text/plain;charset=utf-8,' + encodeURIComponent(prepareExport())); }; function doUploadToPlanet() { saveLocally(); thumbnails.show() }; function doShareOnFacebook() { alert("Facebook Sharing : disabled"); // remove when add fb share link // add code for facebook share link }; function doLoad() { console.log('Loading .tb file'); document.querySelector('#myOpenFile').focus(); document.querySelector('#myOpenFile').click(); window.scroll(0, 0); }; function _doLilypond() { // Show busy cursor. document.body.style.cursor = 'wait'; console.log('Saving .ly file'); // Suppress music and turtle output when generating // Lilypond output. logo.runningLilypond = true; logo.lilypondOutput = LILYPONDHEADER; logo.lilypondNotes = {}; for (var turtle = 0; turtle < turtles.turtleList.length; turtle++) { logo.lilypondStaging[turtle] = []; turtles.turtleList[turtle].doClear(true, true); } logo.runLogoCommands(); }; window.prepareExport = prepareExport; window.saveLocally = saveLocally; function saveLocally() { if (sugarizerCompatibility.isInsideSugarizer()) { //sugarizerCompatibility.data.blocks = prepareExport(); storage = sugarizerCompatibility.data; } else { storage = localStorage; } console.log('overwriting session data'); if (storage.currentProject === undefined) { try { storage.currentProject = 'My Project'; storage.allProjects = JSON.stringify(['My Project']) } catch (e) { // Edge case, eg. Firefox localSorage DB corrupted console.log(e); } } try { var p = storage.currentProject; storage['SESSION' + p] = prepareExport(); } catch (e) { console.log(e); } // if (isSVGEmpty(turtles)) { // We will use the music icon in these cases. // return; // } var img = new Image(); var svgData = doSVG(canvas, logo, turtles, 320, 240, 320 / canvas.width); img.onload = function () { var bitmap = new createjs.Bitmap(img); var bounds = bitmap.getBounds(); bitmap.cache(bounds.x, bounds.y, bounds.width, bounds.height); try { storage['SESSIONIMAGE' + p] = bitmap.getCacheDataURL(); } catch (e) { console.log(e); } }; img.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgData))); // console.log(img.src); if (sugarizerCompatibility.isInsideSugarizer()) { sugarizerCompatibility.saveLocally(); } }; function runProject(env){ console.log("Running Project from Event"); document.removeEventListener("finishedLoading", runProject); setTimeout(function () { console.log("Run"); _changeBlockVisibility(); _doFastButton(env); }, 5000); } function loadProject(projectName, run, env) { //set default value of run run = typeof run !== 'undefined' ? run : false; // Show busy cursor. document.body.style.cursor = 'wait'; // palettes.updatePalettes(); setTimeout(function () { if (fileExt(projectName) !== 'tb') { projectName += '.tb'; } try { try { httpGet(null); console.log('running from server or the user can access to examples.'); server = true; } catch (e) { console.log('running from filesystem or the connection isnt secure'); server = false; } if (server) { var rawData = httpGet(projectName); var cleanData = rawData.replace('\n', ''); } // First, hide the palettes as they will need updating. for (var name in blocks.palettes.dict) { blocks.palettes.dict[name].hideMenu(true); } var obj = JSON.parse(cleanData); blocks.loadNewBlocks(obj); saveLocally(); } catch (e) { console.log(e); loadStartWrapper(_loadStart); } // Restore default cursor document.body.style.cursor = 'default'; update = true; }, 200); if (run && firstRun) { if (document.addEventListener) { document.addEventListener('finishedLoading', function (){runProject(env);}, false); } else { document.attachEvent('finishedLoading', function (){runProject(env);}); } } firstRun = false; }; function loadRawProject(data) { console.log('loadRawProject ' + data); document.body.style.cursor = 'wait'; _allClear(); // First, hide the palettes as they will need updating. for (var name in blocks.palettes.dict) { blocks.palettes.dict[name].hideMenu(true); } var obj = JSON.parse(data); blocks.loadNewBlocks(obj); document.body.style.cursor = 'default'; }; function saveProject(projectName) { // palettes.updatePalettes(); // Show busy cursor. document.body.style.cursor = 'wait'; setTimeout(function () { var punctuationless = projectName.replace(/['!"#$%&\\'()\*+,\-\.\/:;<=>?@\[\\\]\^`{|}~']/g, ''); projectName = punctuationless.replace(/ /g, '_'); if (fileExt(projectName) !== 'tb') { projectName += '.tb'; } try { // Post the project var returnValue = httpPost('MusicBlocks_'+projectName, prepareExport()); errorMsg('Saved ' + projectName + ' to ' + window.location.host); var img = new Image(); var svgData = doSVG(canvas, logo, turtles, 320, 240, 320 / canvas.width); img.onload = function () { var bitmap = new createjs.Bitmap(img); var bounds = bitmap.getBounds(); bitmap.cache(bounds.x, bounds.y, bounds.width, bounds.height); // and base64-encoded png httpPost(('MusicBlocks_'+projectName).replace('.tb', '.b64'), bitmap.getCacheDataURL()); }; img.src = 'data:image/svg+xml;base64,' + window.btoa( unescape(encodeURIComponent(svgData))); // Restore default cursor document.body.style.cursor = 'default'; return returnValue; } catch (e) { console.log(e); // Restore default cursor document.body.style.cursor = 'default'; return; } }, 200); }; // Calculate time such that no matter how long it takes to // load the program, the loading animation will cycle at least // once. function loadStartWrapper(func, arg1, arg2, arg3) { var time1 = new Date(); func(arg1, arg2, arg3); var time2 = new Date(); var elapsedTime = time2.getTime() - time1.getTime(); var timeLeft = Math.max(6000 - elapsedTime); setTimeout(showContents, timeLeft); }; // Hides the loading animation and unhides the background. function showContents(){ docById('loading-image-container').style.display = 'none'; // docById('canvas').style.display = 'none'; docById('hideContents').style.display = 'block'; }; function _loadStart() { // where to put this? // palettes.updatePalettes(); console.log('LOAD START') justLoadStart = function () { console.log('loading start and a matrix'); blocks.loadNewBlocks(DATAOBJS); }; if (sugarizerCompatibility.isInsideSugarizer()) { storage = sugarizerCompatibility.data; } else { storage = localStorage; } sessionData = null; // Try restarting where we were when we hit save. var currentProject = storage.currentProject; sessionData = storage['SESSION' + currentProject]; if (sessionData) { try { if (sessionData === 'undefined' || sessionData === '[]') { console.log('empty session found: loading start'); justLoadStart(); } else { console.log('restoring session: ' + sessionData); // First, hide the palettes as they will need updating. for (var name in blocks.palettes.dict) { blocks.palettes.dict[name].hideMenu(true); } blocks.loadNewBlocks(JSON.parse(sessionData)); } } catch (e) { console.log(e); } } else { justLoadStart(); } update = true; }; function hideMsgs() { errorMsgText.parent.visible = false; if (errorMsgArrow != null) { errorMsgArrow.removeAllChildren(); refreshCanvas(); } msgText.parent.visible = false; for (var i in errorArtwork) { errorArtwork[i].visible = false; } }; function textMsg(msg) { if (msgText == null) { // The container may not be ready yet... so do nothing return; } var msgContainer = msgText.parent; msgContainer.visible = true; msgText.text = msg; msgContainer.updateCache(); stage.setChildIndex(msgContainer, stage.getNumChildren() - 1); }; function errorMsg(msg, blk, text) { _hideStopButton(); //Hide the button, as the program is going to be terminated if (errorMsgText == null) { // The container may not be ready yet... so do nothing return; } if (blk !== undefined && blk != null && !blocks.blockList[blk].collapsed) { var fromX = (canvas.width - 1000) / 2; var fromY = 128; var toX = blocks.blockList[blk].container.x + blocksContainer.x; var toY = blocks.blockList[blk].container.y + blocksContainer.y; if (errorMsgArrow == null) { errorMsgArrow = new createjs.Container(); stage.addChild(errorMsgArrow); } var line = new createjs.Shape(); errorMsgArrow.addChild(line); line.graphics.setStrokeStyle(4).beginStroke('#ff0031').moveTo(fromX, fromY).lineTo(toX, toY); stage.setChildIndex(errorMsgArrow, stage.getNumChildren() - 1); var angle = Math.atan2(toX - fromX, fromY - toY) / Math.PI * 180; var head = new createjs.Shape(); errorMsgArrow.addChild(head); head.graphics.setStrokeStyle(4).beginStroke('#ff0031').moveTo(-10, 18).lineTo(0, 0).lineTo(10, 18); head.x = toX; head.y = toY; head.rotation = angle; } switch (msg) { case NOMICERRORMSG: errorArtwork['nomicrophone'].visible = true; stage.setChildIndex(errorArtwork['nomicrophone'], stage.getNumChildren() - 1); break; case NOSTRINGERRORMSG: errorArtwork['notastring'].visible = true; stage.setChildIndex(errorArtwork['notastring'], stage.getNumChildren() - 1); break; case EMPTYHEAPERRORMSG: errorArtwork['emptyheap'].visible = true; stage.setChildIndex(errorArtwork['emptyheap'], stage.getNumChildren() - 1); break; case NOSQRTERRORMSG: errorArtwork['negroot'].visible = true; stage.setChildIndex(errorArtwork['negroot'], stage.getNumChildren() - 1); break; case NOACTIONERRORMSG: if (text == null) { text = 'foo'; } errorArtwork['nostack'].children[1].text = text; errorArtwork['nostack'].visible = true; errorArtwork['nostack'].updateCache(); stage.setChildIndex(errorArtwork['nostack'], stage.getNumChildren() - 1); break; case NOBOXERRORMSG: if (text == null) { text = 'foo'; } errorArtwork['emptybox'].children[1].text = text; errorArtwork['emptybox'].visible = true; errorArtwork['emptybox'].updateCache(); stage.setChildIndex(errorArtwork['emptybox'], stage.getNumChildren() - 1); break; case ZERODIVIDEERRORMSG: errorArtwork['zerodivide'].visible = true; stage.setChildIndex(errorArtwork['zerodivide'], stage.getNumChildren() - 1); break; case NANERRORMSG: errorArtwork['notanumber'].visible = true; stage.setChildIndex(errorArtwork['notanumber'], stage.getNumChildren() - 1); break; case NOINPUTERRORMSG: errorArtwork['noinput'].visible = true; stage.setChildIndex(errorArtwork['noinput'], stage.getNumChildren() - 1); break; default: var errorMsgContainer = errorMsgText.parent; errorMsgContainer.visible = true; errorMsgText.text = msg; stage.setChildIndex(errorMsgContainer, stage.getNumChildren() - 1); errorMsgContainer.updateCache(); break; } update = true; }; function _hideCartesian() { cartesianBitmap.visible = false; cartesianBitmap.updateCache(); update = true; }; function _showCartesian() { cartesianBitmap.visible = true; cartesianBitmap.updateCache(); update = true; }; function _hidePolar() { polarBitmap.visible = false; polarBitmap.updateCache(); update = true; }; function _showPolar() { polarBitmap.visible = true; polarBitmap.updateCache(); update = true; }; function pasteStack() { blocks.pasteStack(); }; function prepareExport() { // We don't save blocks in the trash, so we need to // consolidate the block list and remap the connections. var blockMap = []; var hasMatrixDataBlock = false; for (var blk = 0; blk < blocks.blockList.length; blk++) { var myBlock = blocks.blockList[blk]; if (myBlock.trash) { // Don't save blocks in the trash. continue; } blockMap.push(blk); } var data = []; for (var blk = 0; blk < blocks.blockList.length; blk++) { var myBlock = blocks.blockList[blk]; if (myBlock.trash) { // Don't save blocks in the trash. continue; } if (myBlock.isValueBlock() || myBlock.name === 'loadFile') { // FIX ME: scale image if it exceeds a maximum size. var args = { 'value': myBlock.value }; } else if (myBlock.name === 'start' || myBlock.name === 'drum') { // Find the turtle associated with this block. var turtle = turtles.turtleList[myBlock.value]; if (turtle == null) { var args = { 'collapsed': false, 'xcor': 0, 'ycor': 0, 'heading': 0, 'color': 0, 'shade': 50, 'pensize': 5, 'grey': 100 }; } else { var args = { 'collapsed': myBlock.collapsed, 'xcor': turtle.x, 'ycor': turtle.y, 'heading': turtle.orientation, 'color': turtle.color, 'shade': turtle.value, 'pensize': turtle.stroke, 'grey': turtle.chroma }; } } else if (myBlock.name === 'action') { var args = { 'collapsed': myBlock.collapsed } } else if(myBlock.name === 'matrix') { var args = { 'collapsed' : myBlock.collapsed } } else if(myBlock.name === 'pitchdrummatrix') { var args = { 'collapsed' : myBlock.collapsed } } else if(myBlock.name === 'status') { var args = { 'collapsed' : myBlock.collapsed } } else if (myBlock.name === 'namedbox') { var args = { 'value': myBlock.privateData } } else if (myBlock.name === 'nameddo') { var args = { 'value': myBlock.privateData } } else if (myBlock.name === 'nameddoArg') { var args = { 'value': myBlock.privateData } } else if (myBlock.name === 'namedcalc') { var args = { 'value': myBlock.privateData } } else if (myBlock.name === 'namedcalcArg') { var args = { 'value': myBlock.privateData } } else if (myBlock.name === 'namedarg') { var args = { 'value': myBlock.privateData } } else if (myBlock.name === 'matrixData') { var args = { 'notes': window.savedMatricesNotes, 'count': window.savedMatricesCount } hasMatrixDataBlock = true; } else { var args = {} } connections = []; for (var c = 0; c < myBlock.connections.length; c++) { var mapConnection = blockMap.indexOf(myBlock.connections[c]); if (myBlock.connections[c] == null || mapConnection === -1) { connections.push(null); } else { connections.push(mapConnection); } } data.push([blockMap.indexOf(blk), [myBlock.name, args], myBlock.container.x, myBlock.container.y, connections]); } return JSON.stringify(data); }; function doOpenPlugin() { // Click on the plugin open chooser in the DOM (.json). pluginChooser.focus(); pluginChooser.click(); }; function saveToFile() { var filename = prompt('Filename:'); if (fileExt(filename) !== 'tb') { filename += '.tb'; } download(filename, 'data:text/plain;charset=utf-8,' + encodeURIComponent(prepareExport())); }; function _hideStopButton() { // stopTurtleContainer.x = stopTurtleContainerX; // stopTurtleContainer.y = stopTurtleContainerY; stopTurtleContainer.visible = false; }; function _showStopButton() { // stopTurtleContainer.x = onscreenButtons[0].x; // stopTurtleContainer.y = onscreenButtons[0].y; stopTurtleContainer.visible = true; }; function blinkPasteButton(bitmap) { function handleComplete() { createjs.Tween.get(bitmap).to({alpha:1, visible:true}, 500); }; createjs.Tween.get(bitmap).to({alpha:0, visible:false}, 1000).call( handleComplete); }; function updatePasteButton() { if (pasteImage === null) { var img = new Image(); img.onload = function () { var originalSize = 55; // this is the original svg size var halfSize = Math.floor(cellSize / 2); var bitmap = new createjs.Bitmap(img); if (cellSize !== originalSize) { bitmap.scaleX = cellSize / originalSize; bitmap.scaleY = cellSize / originalSize; } bitmap.regX = halfSize / bitmap.scaleX; bitmap.regY = halfSize / bitmap.scaleY; pasteContainer.addChild(bitmap); pasteImage = bitmap; update = true; }; img.src = 'header-icons/paste-button.svg'; } else { blinkPasteButton(pasteImage); } }; function _setupAndroidToolbar(showPalettesPopover) { if (headerContainer !== undefined) { stage.removeChild(headerContainer); for (var i in onscreenButtons) { stage.removeChild(onscreenButtons[i]); } } headerContainer = new createjs.Shape(); headerContainer.graphics.f(platformColor.header).r(0, 0, screen.width / turtleBlocksScale, cellSize); if (platformColor.doHeaderShadow) { headerContainer.shadow = new createjs.Shadow('#777', 0, 2, 2); } stage.addChild(headerContainer); // Buttons used when running turtle programs // name / onpress function / label / onlongpress function / onextralongpress function / onlongpress icon / onextralongpress icon if (_THIS_IS_MUSIC_BLOCKS_) { var buttonNames = [ ['run', _doFastButton, _('Run fast / long press to run slowly / extra-long press to run music slowly'), _doSlowButton, _doSlowMusicButton, 'slow-button', 'slow-music-button'], ['step', _doStepButton, _('Run step by step'), null, null, null, null], ['step-music', _doStepMusicButton, _('Run note by note'), null, null, null, null], ['stop-turtle', doStopButton, _('Stop'), null, null, null, null], ['clear', _allClear, _('Clean'), null, null, null, null], // ['palette', _changePaletteVisibility, _('Show/hide palettes'), null, null, null, null], ['hide-blocks', _changeBlockVisibility, _('Show/hide blocks'), null, null, null, null], ['collapse-blocks', _toggleCollapsibleStacks, _('Expand/collapse collapsable blocks'), null, null, null, null], ['go-home', _findBlocks, _('Home'), null, null, null, null], ['help', _showHelp, _('Help'), null, null, null, null] ]; } else { var buttonNames = [ ['run', _doFastButton, _('Run fast / long press to run slowly'), _doSlowButton, null, 'slow-button', null], ['step', _doStepButton, _('Run step by step'), null, null, null, null], ['stop-turtle', doStopButton, _('Stop'), null, null, null, null], ['clear', _allClear, _('Clean'), null, null, null, null], ['hide-blocks', _changeBlockVisibility, _('Show/hide blocks'), null, null, null, null], ['collapse-blocks', _toggleCollapsibleStacks, _('Expand/collapse collapsable blocks'), null, null, null, null], ['go-home', _findBlocks, _('Home'), null, null, null, null], ['help', _showHelp, _('Help'), null, null, null, null] ]; } if (sugarizerCompatibility.isInsideSugarizer()) { buttonNames.push(['sugarizer-stop', function () { sugarizerCompatibility.data.blocks = prepareExport(); sugarizerCompatibility.saveLocally(function () { sugarizerCompatibility.sugarizerStop(); }); }, "Stop", null, null, null, null]) } if (showPalettesPopover) { buttonNames.unshift(['popdown-palette', doPopdownPalette]) } var btnSize = cellSize; var x = Math.floor(btnSize / 2); var y = x; var dx = btnSize; var dy = 0; for (var i = 0; i < buttonNames.length; i++) { if (!getMainToolbarButtonNames(buttonNames[i][0])) { console.log('continue'); continue; } var container = _makeButton(buttonNames[i][0] + '-button', buttonNames[i][2], x, y, btnSize, 0); _loadButtonDragHandler(container, x, y, buttonNames[i][1], buttonNames[i][3], buttonNames[i][4], buttonNames[i][5], buttonNames[i][6]); onscreenButtons.push(container); if (buttonNames[i][0] === 'stop-turtle') { stopTurtleContainer = container; stopTurtleContainerX = x; stopTurtleContainerY = y; } else if (buttonNames[i][0] === 'go-home') { homeButtonContainers = []; homeButtonContainers.push(container); homeButtonContainersX = x; homeButtonContainersY = y; var container2 = _makeButton('go-home-faded-button', _('Home'), x, y, btnSize, 0); _loadButtonDragHandler(container2, x, y, buttonNames[i][1], null, null, null, null); homeButtonContainers.push(container2); onscreenButtons.push(container2); homeButtonContainers[0].visible = false; homeButtonContainers[1].visible = true; boundary.hide(); blocks.setHomeContainers(homeButtonContainers, boundary); } x += dx; y += dy; } _setupRightMenu(turtleBlocksScale); }; function _setupRightMenu(turtleBlocksScale) { if (menuContainer !== undefined) { stage.removeChild(menuContainer); for (var i in onscreenMenu) { stage.removeChild(onscreenMenu[i]); } } // Misc. other buttons if (_THIS_IS_MUSIC_BLOCKS_) { var menuNames = [ ['planet', _doOpenSamples, _('Load samples from server')], ['open', doLoad, _('Load project from files')], ['save', doSave, _('Save project')], ['lilypond', _doLilypond, _('Save sheet music')], ['paste-disabled', pasteStack, _('Paste')], ['Cartesian', _doCartesian, _('Cartesian')], ['polar', _doPolar, _('Polar')], ['utility', _doUtilityBox, _('Settings')], ['empty-trash', _deleteBlocksBox, _('Delete all')], ['restore-trash', _restoreTrash, _('Undo')] ]; } else { var menuNames = [ ['planet', _doOpenSamples, _('Load samples from server')], ['open', doLoad, _('Load project from files')], ['save', doSave, _('Save project')], ['paste-disabled', pasteStack, _('Paste')], ['Cartesian', _doCartesian, _('Cartesian')], ['polar', _doPolar, _('Polar')], ['utility', _doUtilityBox, _('Settings')], ['empty-trash', _deleteBlocksBox, _('Delete all')], ['restore-trash', _restoreTrash, _('Undo')] ]; } document.querySelector('#myOpenFile') .addEventListener('change', function (event) { thumbnails.model.controller.hide(); }); var btnSize = cellSize; var x = Math.floor(canvas.width / turtleBlocksScale) - btnSize / 2; var y = Math.floor(btnSize / 2); var dx = 0; var dy = btnSize; menuContainer = _makeButton('menu-button', '', x, y, btnSize, menuButtonsVisible ? 90 : undefined); _loadButtonDragHandler(menuContainer, x, y, _doMenuButton, null, null, null, null); for (var i = 0; i < menuNames.length; i++) { if (!getAuxToolbarButtonNames(menuNames[i][0])) { continue; } x += dx; y += dy; var container = _makeButton(menuNames[i][0] + '-button', menuNames[i][2], x, y, btnSize, 0); _loadButtonDragHandler(container, x, y, menuNames[i][1], null, null, null, null); onscreenMenu.push(container); if (menuNames[i][0] === 'utility') { utilityButton = container; } else if (menuNames[i][0] === 'save') { saveButton = container; } container.visible = false; } if (menuButtonsVisible) { for (var button in onscreenMenu) { onscreenMenu[button].visible = true; } } }; function doPopdownPalette() { console.log('doPopdownPalette'); var p = new PopdownPalette(palettes); p.popdown(); }; function _showHelp(firstTime) { helpIdx = 0; if (firstTime) { if (helpContainer == null) { helpContainer = new createjs.Container(); stage.addChild(helpContainer); helpContainer.x = 65; helpContainer.y = 65; helpContainer.on('click', function (event) { var bounds = helpContainer.getBounds(); if (event.stageY < helpContainer.y + bounds.height / 2) { helpContainer.visible = false; docById('helpElem').style.visibility = 'hidden'; } else { helpIdx += 1; if (helpIdx >= HELPCONTENT.length) { helpIdx = 0; } var imageScale = 55 * turtleBlocksScale; helpElem.innerHTML = '

' + HELPCONTENT[helpIdx][0] + '

' + HELPCONTENT[helpIdx][1] + '

'; } update = true; }); var img = new Image(); img.onload = function () { console.log(turtleBlocksScale); var bitmap = new createjs.Bitmap(img); /* if (turtleBlocksScale > 1) { bitmap.scaleX = bitmap.scaleY = bitmap.scale = turtleBlocksScale; } else { bitmap.scaleX = bitmap.scaleY = bitmap.scale = 1.125; } */ if (helpContainer.children.length > 0) { console.log('delete old help container'); helpContainer.removeChild(helpContainer.children[0]); } helpContainer.addChild(bitmap) var bounds = helpContainer.getBounds(); var hitArea = new createjs.Shape(); hitArea.graphics.beginFill('#FFF').drawRect(bounds.x, bounds.y, bounds.width, bounds.height); hitArea.x = 0; hitArea.y = 0; helpContainer.hitArea = hitArea; docById('helpElem').innerHTML = '

' + HELPCONTENT[helpIdx][0] + '

' + HELPCONTENT[helpIdx][1] + '

'; if (!doneTour) { docById('helpElem').style.visibility = 'visible'; } update = true; }; img.src = 'images/help-container.svg'; } var helpElem = docById('helpElem'); helpElem.style.position = 'absolute'; helpElem.style.display = 'block'; helpElem.style.paddingLeft = 20 * turtleBlocksScale + 'px'; helpElem.style.paddingRight = 20 * turtleBlocksScale + 'px'; helpElem.style.paddingTop = '0px'; helpElem.style.paddingBottom = 20 * turtleBlocksScale + 'px'; helpElem.style.fontSize = 20 + 'px'; // * turtleBlocksScale + 'px'; helpElem.style.color = '#000000'; // '#ffffff'; helpElem.style.left = 65 * turtleBlocksScale + 'px'; helpElem.style.top = 105 * turtleBlocksScale + 'px'; var w = Math.min(300, 300); // * turtleBlocksScale); var h = Math.min(300, 300); // * turtleBlocksScale); helpElem.style.width = w + 'px'; helpElem.style.height = h + 'px'; if (turtleBlocksScale > 1) { var bitmap = helpContainer.children[0]; if (bitmap != undefined) { // bitmap.scaleX = bitmap.scaleY = bitmap.scale = turtleBlocksScale; } } } doneTour = storage.doneTour === 'true'; if (firstTime && doneTour) { docById('helpElem').style.visibility = 'hidden'; helpContainer.visible = false; } else { if (sugarizerCompatibility.isInsideSugarizer()) { sugarizerCompatibility.data.doneTour = 'true'; } else { storage.doneTour = 'true'; } docById('helpElem').innerHTML = '

' + HELPCONTENT[helpIdx][0] + '

' + HELPCONTENT[helpIdx][1] + '

'; docById('helpElem').style.visibility = 'visible'; helpContainer.visible = true; update = true; // Make sure the palettes and the secondary menus are // visible while help is shown. palettes.show(); if (!menuButtonsVisible) { doMenuAnimation(1); } } }; function _doMenuButton() { _doMenuAnimation(1); }; function _doMenuAnimation() { var bitmap = last(menuContainer.children); if (bitmap != null) { var r = bitmap.rotation; createjs.Tween.get(bitmap) .to({ rotation: r }) .to({ rotation: r + 90 }, 500); } else { // Race conditions during load setTimeout(_doMenuAnimation, 50); } setTimeout(function () { if (menuButtonsVisible) { menuButtonsVisible = false; for (var button in onscreenMenu) { onscreenMenu[button].visible = false; } } else { menuButtonsVisible = true; for (var button in onscreenMenu) { onscreenMenu[button].visible = true; } } update = true; }, 500); }; function _toggleToolbar() { buttonsVisible = !buttonsVisible; menuContainer.visible = buttonsVisible; headerContainer.visible = buttonsVisible; for (var button in onscreenButtons) { onscreenButtons[button].visible = buttonsVisible; } for (var button in onscreenMenu) { onscreenMenu[button].visible = buttonsVisible; } update = true; }; function _makeButton(name, label, x, y, size, rotation, parent) { var container = new createjs.Container(); if (name === 'paste-disabled-button') { pasteContainer = container; } if (parent == undefined) { stage.addChild(container); } else { parent.addChild(container); } container.x = x; container.y = y; var text = new createjs.Text(label, '14px Sans', '#282828'); if (container.y < 55) { if (container.x < 55) { text.textAlign = 'left'; text.x = -14; } else { text.textAlign = 'center'; text.x = 0; } text.y = 30; } else { text.textAlign = 'right'; text.x = -28; text.y = 0; } text.visible = false; container.on('mouseover', function (event) { for (var c = 0; c < container.children.length; c++) { if (container.children[c].text != undefined) { container.children[c].visible = true; break; } } }); container.on('mouseout', function (event) { for (var c = 0; c < container.children.length; c++) { if (container.children[c].text != undefined) { container.children[c].visible = false; break; } } }); var img = new Image(); img.onload = function () { var originalSize = 55; // this is the original svg size var halfSize = Math.floor(size / 2); var bitmap = new createjs.Bitmap(img); if (size !== originalSize) { bitmap.scaleX = size / originalSize; bitmap.scaleY = size / originalSize; } bitmap.regX = halfSize / bitmap.scaleX; bitmap.regY = halfSize / bitmap.scaleY; if (rotation !== undefined) { bitmap.rotation = rotation; } container.addChild(bitmap); var hitArea = new createjs.Shape(); hitArea.graphics.beginFill('#FFF').drawEllipse(-halfSize, -halfSize, size, size); hitArea.x = 0; hitArea.y = 0; container.hitArea = hitArea; bitmap.cache(0, 0, size, size); bitmap.updateCache(); update = true; }; img.src = 'header-icons/' + name + '.svg'; container.addChild(text); return container; }; function _loadButtonDragHandler(container, ox, oy, action, longAction, extraLongAction, longImg, extraLongImg) { // Prevent multiple button presses (i.e., debounce). var locked = false; if (longAction === null) { longAction = action; } if (extraLongAction === null) { extraLongAction = longAction; } // Long and extra-long press variables declaration var pressTimer, pressTimerExtra, isLong = false, isExtraLong = false; var formerContainer = container; container.on('mousedown', function (event) { var moved = true; var offset = { x: container.x - Math.round(event.stageX / turtleBlocksScale), y: container.y - Math.round(event.stageY / turtleBlocksScale) }; pressTimer = setTimeout(function () { isLong = true; if (longImg !== null) { container.visible = false; container = _makeButton(longImg, '', ox, oy, cellSize, 0); } }, 500); pressTimerExtra = setTimeout(function () { isExtraLong = true; if (extraLongImg !== null) { container.visible = false; container = _makeButton(extraLongImg, '', ox, oy, cellSize, 0); } }, 1000); var circles = showButtonHighlight(ox, oy, cellSize / 2, event, turtleBlocksScale, stage); container.on('pressup', function (event) { hideButtonHighlight(circles, stage); container.x = ox; container.y = oy; if (longImg !== null || extraLongImg !== null) { container.visible = false; container = formerContainer; container.visible = true; } if (action != null && moved && !locked) { locked = true; setTimeout(function () { locked = false; }, 500); clearTimeout(pressTimer); clearTimeout(pressTimerExtra); if (!isLong) { action(); } else if (!isExtraLong) { longAction(); } else { extraLongAction(); } } moved = false; }); isLong = false; isExtraLong = false; }); }; }; });