From bf07d98ccd8277630798cf0ceae65b72316940bc Mon Sep 17 00:00:00 2001 From: Tim Mickel Date: Thu, 21 Jan 2016 15:10:18 -0500 Subject: [PATCH] Modularize remaining objects with static methods --- src/editor/blocks/BlockSpecs.js | 577 +++++---- src/editor/blocks/Menu.js | 178 +-- src/editor/engine/Prims.js | 1186 +++++++++--------- src/editor/engine/Sprite.js | 1 - src/editor/ui/Alert.js | 128 +- src/editor/ui/Grid.js | 498 ++++---- src/editor/ui/Library.js | 1044 ++++++++-------- src/editor/ui/Palette.js | 1096 ++++++++-------- src/editor/ui/Project.js | 1066 ++++++++-------- src/editor/ui/Record.js | 664 +++++----- src/editor/ui/ScriptsPane.js | 602 ++++----- src/editor/ui/Thumbs.js | 1244 +++++++++--------- src/editor/ui/UI.js | 2074 ++++++++++++++++--------------- src/editor/ui/Undo.js | 766 ++++++------ src/painteditor/Paint.js | 1 - 15 files changed, 5662 insertions(+), 5463 deletions(-) diff --git a/src/editor/blocks/BlockSpecs.js b/src/editor/blocks/BlockSpecs.js index 4e6648f..a8a9079 100755 --- a/src/editor/blocks/BlockSpecs.js +++ b/src/editor/blocks/BlockSpecs.js @@ -1,306 +1,331 @@ -var BlockSpecs = function () {}; +import Localization from '../../utils/Localization'; +import IO from '../../iPad/IO'; -BlockSpecs.loadCount = 0; -BlockSpecs.loadassets = {}; -BlockSpecs.fontwhite = '#f2f3f2'; -BlockSpecs.fontpink = '#ff8ae9'; -BlockSpecs.fontdarkgray = '#6d6e6c'; -BlockSpecs.fontblack = '#1b2a34'; -BlockSpecs.fontyellow = '#ffdd33'; -BlockSpecs.fontdarkgreen = '#287f46'; -BlockSpecs.fontpurple = '#8f56e3'; -BlockSpecs.fontblue = '#0d50ab'; -BlockSpecs.fontred = '#c4281b'; -BlockSpecs.fontorange = '#da8540'; +let loadCount = 0; -BlockSpecs.fontcolors = [BlockSpecs.fontred, BlockSpecs.fontorange, BlockSpecs.fontyellow, - BlockSpecs.fontdarkgreen, BlockSpecs.fontblue, BlockSpecs.fontpink, BlockSpecs.fontpurple, - BlockSpecs.fontwhite, BlockSpecs.fontdarkgray, BlockSpecs.fontblack]; +let loadassets = {}; +let fontwhite = '#f2f3f2'; +let fontpink = '#ff8ae9'; +let fontdarkgray = '#6d6e6c'; +let fontblack = '#1b2a34'; +let fontyellow = '#ffdd33'; +let fontdarkgreen = '#287f46'; +let fontpurple = '#8f56e3'; +let fontblue = '#0d50ab'; +let fontred = '#c4281b'; +let fontorange = '#da8540'; -BlockSpecs.fontsizes = [16, 24, 36, 48, 56, 72]; +let fontcolors = [fontred, fontorange, fontyellow, + fontdarkgreen, fontblue, fontpink, fontpurple, + fontwhite, fontdarkgray, fontblack]; -BlockSpecs.getshapes = ['LetterGet_Orange', 'LetterGet_Red', 'LetterGet_Yellow', 'LetterGet_Green', +let fontsizes = [16, 24, 36, 48, 56, 72]; + +let getshapes = ['LetterGet_Orange', 'LetterGet_Red', 'LetterGet_Yellow', 'LetterGet_Green', 'LetterGet_Blue', 'LetterGet_Purple']; -BlockSpecs.sendshapes = ['LetterSend_Orange', 'LetterSend_Red', 'LetterSend_Yellow', 'LetterSend_Green', +let sendshapes = ['LetterSend_Orange', 'LetterSend_Red', 'LetterSend_Yellow', 'LetterSend_Green', 'LetterSend_Blue', 'LetterSend_Purple']; -BlockSpecs.speeds = ['speed0', 'speed1', 'speed2']; +let speeds = ['speed0', 'speed1', 'speed2']; -BlockSpecs.initBlocks = function () { - BlockSpecs.loadassets = new Object(); - BlockSpecs.loadGraphics(); - BlockSpecs.defs = BlockSpecs.setupBlocksSpecs(); - BlockSpecs.palettes = BlockSpecs.setupPalettesDef(); - BlockSpecs.categories = BlockSpecs.setupCategories(); - if (Settings.edition == 'PBS') { - BlockSpecs.canvasMask = BlockSpecs.getImageFrom('assets/ui/canvasmask', 'svg'); - } else { - BlockSpecs.canvasMask = BlockSpecs.getImageFrom('assets/ui/canvasmask'); +export default class BlockSpecs { + static get loadCount () { + return loadCount; } - if (Settings.edition != 'PBS') { - BlockSpecs.projectThumb = BlockSpecs.getImageFrom('assets/lobby/pmask'); + + static set loadCount (newLoadCount) { + loadCount = newLoadCount; } - IO.requestFromServer('assets/balloon.svg', BlockSpecs.setBalloon); - BlockSpecs.loadCount++; -}; -BlockSpecs.setBalloon = function (str) { - BlockSpecs.loadCount--; - BlockSpecs.balloon = str; -}; - - -BlockSpecs.loadGraphics = function () { - BlockSpecs.mic = BlockSpecs.getImageFrom('assets/ui/recordslot', 'svg'); - BlockSpecs.yellowStart = BlockSpecs.getImageFrom('assets/blocks/start', 'svg'); - BlockSpecs.yellowStartH = BlockSpecs.getImageFrom('assets/blocks/eh/startH'); - - BlockSpecs.yellowCmd = BlockSpecs.getImageFrom('assets/blocks/yellowCmd', 'svg'); - BlockSpecs.yellowCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/yellowCmdH'); - - BlockSpecs.redEnd = BlockSpecs.getImageFrom('assets/blocks/endshort', 'svg'); - BlockSpecs.redEndH = BlockSpecs.getImageFrom('assets/blocks/eh/stopH'); - - BlockSpecs.orangeCmd = BlockSpecs.getImageFrom('assets/blocks/flow', 'svg'); - BlockSpecs.orangeCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/flowH'); - - BlockSpecs.limeCmd = BlockSpecs.getImageFrom('assets/blocks/sounds', 'svg'); - BlockSpecs.limeCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/soundsH'); - - BlockSpecs.pinkCmd = BlockSpecs.getImageFrom('assets/blocks/looks', 'svg'); - BlockSpecs.pinkCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/looksH'); - - BlockSpecs.redEndLong = BlockSpecs.getImageFrom('assets/blocks/endlong', 'svg'); - BlockSpecs.redEndLongH = BlockSpecs.getImageFrom('assets/blocks/eh/stoplongH'); - - BlockSpecs.cShape = BlockSpecs.getImageFrom('assets/blocks/repeat'); - BlockSpecs.cShapeH = BlockSpecs.getImageFrom('assets/blocks/eh/repeatH'); - - BlockSpecs.blueCmd = BlockSpecs.getImageFrom('assets/blocks/blueCmd', 'svg'); - BlockSpecs.blueCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/blueCmdH'); - - BlockSpecs.textfieldimg = BlockSpecs.getImageFrom('assets/misc/Text-01'); - BlockSpecs.numfieldimg = BlockSpecs.getImageFrom('assets/misc/Number-01'); - BlockSpecs.pressbutton = BlockSpecs.getImageFrom('assets/misc/pushbutton-01', 'svg'); - BlockSpecs.pressbuttonSmall = BlockSpecs.getImageFrom('assets/misc/pushbutton', 'svg'); - BlockSpecs.caretrepeat = BlockSpecs.getImageFrom('assets/blocks/caretrepeat'); - BlockSpecs.cmdS = BlockSpecs.getImageFrom('assets/blocks/shadowCmd', 'svg'); - BlockSpecs.startS = BlockSpecs.getImageFrom('assets/blocks/shadowStart', 'svg'); - BlockSpecs.endS = BlockSpecs.getImageFrom('assets/blocks/shadowEndShort', 'svg'); - BlockSpecs.endLongS = BlockSpecs.getImageFrom('assets/blocks/shadowEndLong', 'svg'); - BlockSpecs.repeatS = BlockSpecs.getImageFrom('assets/blocks/shadowRepeat'); - -}; - -BlockSpecs.getImageFrom = function (url, ext) { - var img = document.createElement('img'); - img.src = url + (ext ? '.' + ext : '.png'); - if (!img.complete) { - BlockSpecs.loadassets[img.src] = img; - BlockSpecs.loadCount++; - img.onload = function () { - delete BlockSpecs.loadassets[img.src]; - BlockSpecs.loadCount--; - }; + static get fontcolors () { + return fontcolors; + } + + static get fontsizes () { + return fontsizes; + } + + static get speeds () { + return speeds; + } + + static initBlocks () { + loadassets = new Object(); + BlockSpecs.loadGraphics(); + BlockSpecs.defs = BlockSpecs.setupBlocksSpecs(); + BlockSpecs.palettes = BlockSpecs.setupPalettesDef(); + BlockSpecs.categories = BlockSpecs.setupCategories(); + if (Settings.edition == 'PBS') { + BlockSpecs.canvasMask = BlockSpecs.getImageFrom('assets/ui/canvasmask', 'svg'); + } else { + BlockSpecs.canvasMask = BlockSpecs.getImageFrom('assets/ui/canvasmask'); + } + if (Settings.edition != 'PBS') { + BlockSpecs.projectThumb = BlockSpecs.getImageFrom('assets/lobby/pmask'); + } + IO.requestFromServer('assets/balloon.svg', BlockSpecs.setBalloon); + loadCount++; + } + + static setBalloon (str) { + loadCount--; + BlockSpecs.balloon = str; } - return img; -}; -BlockSpecs.refreshLoading = function () { - for (var key in BlockSpecs.loadassets) { - if (BlockSpecs.loadassets[key].complete) { - BlockSpecs.loadCount--; + static loadGraphics () { + BlockSpecs.mic = BlockSpecs.getImageFrom('assets/ui/recordslot', 'svg'); + BlockSpecs.yellowStart = BlockSpecs.getImageFrom('assets/blocks/start', 'svg'); + BlockSpecs.yellowStartH = BlockSpecs.getImageFrom('assets/blocks/eh/startH'); + + BlockSpecs.yellowCmd = BlockSpecs.getImageFrom('assets/blocks/yellowCmd', 'svg'); + BlockSpecs.yellowCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/yellowCmdH'); + + BlockSpecs.redEnd = BlockSpecs.getImageFrom('assets/blocks/endshort', 'svg'); + BlockSpecs.redEndH = BlockSpecs.getImageFrom('assets/blocks/eh/stopH'); + + BlockSpecs.orangeCmd = BlockSpecs.getImageFrom('assets/blocks/flow', 'svg'); + BlockSpecs.orangeCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/flowH'); + + BlockSpecs.limeCmd = BlockSpecs.getImageFrom('assets/blocks/sounds', 'svg'); + BlockSpecs.limeCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/soundsH'); + + BlockSpecs.pinkCmd = BlockSpecs.getImageFrom('assets/blocks/looks', 'svg'); + BlockSpecs.pinkCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/looksH'); + + BlockSpecs.redEndLong = BlockSpecs.getImageFrom('assets/blocks/endlong', 'svg'); + BlockSpecs.redEndLongH = BlockSpecs.getImageFrom('assets/blocks/eh/stoplongH'); + + BlockSpecs.cShape = BlockSpecs.getImageFrom('assets/blocks/repeat'); + BlockSpecs.cShapeH = BlockSpecs.getImageFrom('assets/blocks/eh/repeatH'); + + BlockSpecs.blueCmd = BlockSpecs.getImageFrom('assets/blocks/blueCmd', 'svg'); + BlockSpecs.blueCmdH = BlockSpecs.getImageFrom('assets/blocks/eh/blueCmdH'); + + BlockSpecs.textfieldimg = BlockSpecs.getImageFrom('assets/misc/Text-01'); + BlockSpecs.numfieldimg = BlockSpecs.getImageFrom('assets/misc/Number-01'); + BlockSpecs.pressbutton = BlockSpecs.getImageFrom('assets/misc/pushbutton-01', 'svg'); + BlockSpecs.pressbuttonSmall = BlockSpecs.getImageFrom('assets/misc/pushbutton', 'svg'); + BlockSpecs.caretrepeat = BlockSpecs.getImageFrom('assets/blocks/caretrepeat'); + BlockSpecs.cmdS = BlockSpecs.getImageFrom('assets/blocks/shadowCmd', 'svg'); + BlockSpecs.startS = BlockSpecs.getImageFrom('assets/blocks/shadowStart', 'svg'); + BlockSpecs.endS = BlockSpecs.getImageFrom('assets/blocks/shadowEndShort', 'svg'); + BlockSpecs.endLongS = BlockSpecs.getImageFrom('assets/blocks/shadowEndLong', 'svg'); + BlockSpecs.repeatS = BlockSpecs.getImageFrom('assets/blocks/shadowRepeat'); + + } + + static getImageFrom (url, ext) { + var img = document.createElement('img'); + img.src = url + (ext ? '.' + ext : '.png'); + if (!img.complete) { + loadassets[img.src] = img; + loadCount++; + img.onload = function () { + delete loadassets[img.src]; + loadCount--; + }; + } + return img; + } + + + static refreshLoading () { + for (var key in loadassets) { + if (loadassets[key].complete) { + loadCount--; + } } } -}; -BlockSpecs.setupCategories = function () { - return new Array( - [ - BlockSpecs.getImageFrom('assets/categories/StartOn', 'svg'), - BlockSpecs.getImageFrom('assets/categories/StartOff', 'svg'), - Settings.categoryStartColor - ], - [ - BlockSpecs.getImageFrom('assets/categories/MotionOn', 'svg'), - BlockSpecs.getImageFrom('assets/categories/MotionOff', 'svg'), - Settings.categoryMotionColor - ], - [ - BlockSpecs.getImageFrom('assets/categories/LooksOn', 'svg'), - BlockSpecs.getImageFrom('assets/categories/LooksOff', 'svg'), - Settings.categoryLooksColor - ], - [ - BlockSpecs.getImageFrom('assets/categories/SoundOn', 'svg'), - BlockSpecs.getImageFrom('assets/categories/SoundOff', 'svg'), - Settings.categorySoundColor - ], - [ - BlockSpecs.getImageFrom('assets/categories/FlowOn', 'svg'), - BlockSpecs.getImageFrom('assets/categories/FlowOff', 'svg'), - Settings.categoryFlowColor - ], - [ - BlockSpecs.getImageFrom('assets/categories/StopOn', 'svg'), - BlockSpecs.getImageFrom('assets/categories/StopOff', 'svg'), - Settings.categoryStopColor - ] - ); -}; + static setupCategories () { + return new Array( + [ + BlockSpecs.getImageFrom('assets/categories/StartOn', 'svg'), + BlockSpecs.getImageFrom('assets/categories/StartOff', 'svg'), + Settings.categoryStartColor + ], + [ + BlockSpecs.getImageFrom('assets/categories/MotionOn', 'svg'), + BlockSpecs.getImageFrom('assets/categories/MotionOff', 'svg'), + Settings.categoryMotionColor + ], + [ + BlockSpecs.getImageFrom('assets/categories/LooksOn', 'svg'), + BlockSpecs.getImageFrom('assets/categories/LooksOff', 'svg'), + Settings.categoryLooksColor + ], + [ + BlockSpecs.getImageFrom('assets/categories/SoundOn', 'svg'), + BlockSpecs.getImageFrom('assets/categories/SoundOff', 'svg'), + Settings.categorySoundColor + ], + [ + BlockSpecs.getImageFrom('assets/categories/FlowOn', 'svg'), + BlockSpecs.getImageFrom('assets/categories/FlowOff', 'svg'), + Settings.categoryFlowColor + ], + [ + BlockSpecs.getImageFrom('assets/categories/StopOn', 'svg'), + BlockSpecs.getImageFrom('assets/categories/StopOff', 'svg'), + Settings.categoryStopColor + ] + ); + } -BlockSpecs.setupPalettesDef = function () { - return [['onflag', 'onclick', 'ontouch', 'onmessage', 'message'], - ['forward', 'back', 'up', 'down', 'right', 'left', 'hop', 'home'], - ['say', 'space', 'grow', 'shrink', 'same', 'space', 'hide', 'show'], - [], - ['wait', 'stopmine', 'setspeed', 'repeat'], - ['endstack', 'forever']]; -}; + static setupPalettesDef () { + return [['onflag', 'onclick', 'ontouch', 'onmessage', 'message'], + ['forward', 'back', 'up', 'down', 'right', 'left', 'hop', 'home'], + ['say', 'space', 'grow', 'shrink', 'same', 'space', 'hide', 'show'], + [], + ['wait', 'stopmine', 'setspeed', 'repeat'], + ['endstack', 'forever']]; + } -/////////////////////////////// -// Data Structure -// -// name - blocktype, icon or datastructure, blockshape, argtype, initial value, highlight, min, max, shadow -// -// arg types: -// null -// n -> number field; -// t -> text field -// m --> image menu with argvalue equal to name; -// d --> image menu with argvalue equal to number; -// c -- > color drop down -// s --> sound name -// p --> page icon -// -//////////////////////////////// + /////////////////////////////// + // Data Structure + // + // name - blocktype, icon or datastructure, blockshape, argtype, initial value, highlight, min, max, shadow + // + // arg types: + // null + // n -> number field; + // t -> text field + // m --> image menu with argvalue equal to name; + // d --> image menu with argvalue equal to number; + // c -- > color drop down + // s --> sound name + // p --> page icon + // + //////////////////////////////// -BlockSpecs.setupBlocksSpecs = function () { - return { - 'onflag': ['onflag', BlockSpecs.getImageFrom('assets/blockicons/greenFlag', 'svg'), - BlockSpecs.yellowStart, null, null, BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], - 'onmessage': ['onmessage', BlockSpecs.getshapes, BlockSpecs.yellowStart, 'm', 'Orange', - BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], - 'onclick': ['onclick', BlockSpecs.getImageFrom('assets/blockicons/OnTouch', 'svg'), - BlockSpecs.yellowStart, null, null, BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], - 'ontouch': ['ontouch', BlockSpecs.getImageFrom('assets/blockicons/Bump', 'svg'), - BlockSpecs.yellowStart, null, null, BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], - 'message': ['message', BlockSpecs.sendshapes, BlockSpecs.yellowCmd, 'm', 'Orange', - BlockSpecs.yellowCmdH, null, null, BlockSpecs.cmdS], + static setupBlocksSpecs () { + return { + 'onflag': ['onflag', BlockSpecs.getImageFrom('assets/blockicons/greenFlag', 'svg'), + BlockSpecs.yellowStart, null, null, BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], + 'onmessage': ['onmessage', getshapes, BlockSpecs.yellowStart, 'm', 'Orange', + BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], + 'onclick': ['onclick', BlockSpecs.getImageFrom('assets/blockicons/OnTouch', 'svg'), + BlockSpecs.yellowStart, null, null, BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], + 'ontouch': ['ontouch', BlockSpecs.getImageFrom('assets/blockicons/Bump', 'svg'), + BlockSpecs.yellowStart, null, null, BlockSpecs.yellowStartH, null, null, BlockSpecs.startS], + 'message': ['message', sendshapes, BlockSpecs.yellowCmd, 'm', 'Orange', + BlockSpecs.yellowCmdH, null, null, BlockSpecs.cmdS], - 'repeat': ['repeat', BlockSpecs.getImageFrom('assets/blockicons/Repeat', 'svg'), - BlockSpecs.cShape, 'n', 4, BlockSpecs.cShapeH, 0, 24, BlockSpecs.repeatS], + 'repeat': ['repeat', BlockSpecs.getImageFrom('assets/blockicons/Repeat', 'svg'), + BlockSpecs.cShape, 'n', 4, BlockSpecs.cShapeH, 0, 24, BlockSpecs.repeatS], - 'forward': ['forward', BlockSpecs.getImageFrom('assets/blockicons/Foward', 'svg'), - BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -20, 20, BlockSpecs.cmdS], - 'back': ['back', BlockSpecs.getImageFrom('assets/blockicons/Back', 'svg'), - BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -20, 20, BlockSpecs.cmdS], - 'up': ['up', BlockSpecs.getImageFrom('assets/blockicons/Up', 'svg'), - BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -15, 15, BlockSpecs.cmdS], - 'down': ['down', BlockSpecs.getImageFrom('assets/blockicons/Down', 'svg'), - BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -15, 15, BlockSpecs.cmdS], - 'right': ['right', BlockSpecs.getImageFrom('assets/blockicons/Right', 'svg'), - BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -12, 12, BlockSpecs.cmdS], - 'left': ['left', BlockSpecs.getImageFrom('assets/blockicons/Left', 'svg'), - BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -12, 12, BlockSpecs.cmdS], - 'home': ['home', BlockSpecs.getImageFrom('assets/blockicons/Home', 'svg'), - BlockSpecs.blueCmd, null, null, BlockSpecs.blueCmdH, null, null, BlockSpecs.cmdS], - 'hop': ['hop', BlockSpecs.getImageFrom('assets/blockicons/Hop', 'svg'), - BlockSpecs.blueCmd, 'n', 2, BlockSpecs.blueCmdH, -15, 15, BlockSpecs.cmdS], + 'forward': ['forward', BlockSpecs.getImageFrom('assets/blockicons/Foward', 'svg'), + BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -20, 20, BlockSpecs.cmdS], + 'back': ['back', BlockSpecs.getImageFrom('assets/blockicons/Back', 'svg'), + BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -20, 20, BlockSpecs.cmdS], + 'up': ['up', BlockSpecs.getImageFrom('assets/blockicons/Up', 'svg'), + BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -15, 15, BlockSpecs.cmdS], + 'down': ['down', BlockSpecs.getImageFrom('assets/blockicons/Down', 'svg'), + BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -15, 15, BlockSpecs.cmdS], + 'right': ['right', BlockSpecs.getImageFrom('assets/blockicons/Right', 'svg'), + BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -12, 12, BlockSpecs.cmdS], + 'left': ['left', BlockSpecs.getImageFrom('assets/blockicons/Left', 'svg'), + BlockSpecs.blueCmd, 'n', 1, BlockSpecs.blueCmdH, -12, 12, BlockSpecs.cmdS], + 'home': ['home', BlockSpecs.getImageFrom('assets/blockicons/Home', 'svg'), + BlockSpecs.blueCmd, null, null, BlockSpecs.blueCmdH, null, null, BlockSpecs.cmdS], + 'hop': ['hop', BlockSpecs.getImageFrom('assets/blockicons/Hop', 'svg'), + BlockSpecs.blueCmd, 'n', 2, BlockSpecs.blueCmdH, -15, 15, BlockSpecs.cmdS], - 'wait': ['wait', BlockSpecs.getImageFrom('assets/blockicons/Wait', 'svg'), - BlockSpecs.orangeCmd, 'n', 10, BlockSpecs.orangeCmdH, 0, 50, BlockSpecs.cmdS], - 'setspeed': ['setspeed', BlockSpecs.speeds, BlockSpecs.orangeCmd, 'd', 1, - BlockSpecs.orangeCmdH, null, null, BlockSpecs.cmdS], - 'stopmine': ['stopmine', BlockSpecs.getImageFrom('assets/blockicons/Stop', 'svg'), - BlockSpecs.orangeCmd, null, null, BlockSpecs.orangeCmdH, null, null, BlockSpecs.cmdS], + 'wait': ['wait', BlockSpecs.getImageFrom('assets/blockicons/Wait', 'svg'), + BlockSpecs.orangeCmd, 'n', 10, BlockSpecs.orangeCmdH, 0, 50, BlockSpecs.cmdS], + 'setspeed': ['setspeed', speeds, BlockSpecs.orangeCmd, 'd', 1, + BlockSpecs.orangeCmdH, null, null, BlockSpecs.cmdS], + 'stopmine': ['stopmine', BlockSpecs.getImageFrom('assets/blockicons/Stop', 'svg'), + BlockSpecs.orangeCmd, null, null, BlockSpecs.orangeCmdH, null, null, BlockSpecs.cmdS], - 'say': ['say', BlockSpecs.getImageFrom('assets/blockicons/Say', 'svg'), - BlockSpecs.pinkCmd, 't', - Localization.localize('SAY_BLOCK_DEFAULT_ARGUMENT'), BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], - 'show': ['show', BlockSpecs.getImageFrom('assets/blockicons/Appear', 'svg'), - BlockSpecs.pinkCmd, null, null, BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], - 'hide': ['hide', BlockSpecs.getImageFrom('assets/blockicons/Disappear', 'svg'), - BlockSpecs.pinkCmd, null, null, BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], - 'grow': ['grow', BlockSpecs.getImageFrom('assets/blockicons/Grow', 'svg'), - BlockSpecs.pinkCmd, 'n', 2, BlockSpecs.pinkCmdH, -10, 10, BlockSpecs.cmdS], - 'shrink': ['shrink', BlockSpecs.getImageFrom('assets/blockicons/Shrink', 'svg'), - BlockSpecs.pinkCmd, 'n', 2, BlockSpecs.pinkCmdH, -10, 10, BlockSpecs.cmdS], - 'same': ['same', BlockSpecs.getImageFrom('assets/blockicons/Reset', 'svg'), - BlockSpecs.pinkCmd, null, null, BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], + 'say': ['say', BlockSpecs.getImageFrom('assets/blockicons/Say', 'svg'), + BlockSpecs.pinkCmd, 't', + Localization.localize('SAY_BLOCK_DEFAULT_ARGUMENT'), BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], + 'show': ['show', BlockSpecs.getImageFrom('assets/blockicons/Appear', 'svg'), + BlockSpecs.pinkCmd, null, null, BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], + 'hide': ['hide', BlockSpecs.getImageFrom('assets/blockicons/Disappear', 'svg'), + BlockSpecs.pinkCmd, null, null, BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], + 'grow': ['grow', BlockSpecs.getImageFrom('assets/blockicons/Grow', 'svg'), + BlockSpecs.pinkCmd, 'n', 2, BlockSpecs.pinkCmdH, -10, 10, BlockSpecs.cmdS], + 'shrink': ['shrink', BlockSpecs.getImageFrom('assets/blockicons/Shrink', 'svg'), + BlockSpecs.pinkCmd, 'n', 2, BlockSpecs.pinkCmdH, -10, 10, BlockSpecs.cmdS], + 'same': ['same', BlockSpecs.getImageFrom('assets/blockicons/Reset', 'svg'), + BlockSpecs.pinkCmd, null, null, BlockSpecs.pinkCmdH, null, null, BlockSpecs.cmdS], - 'playsnd': ['playsnd', BlockSpecs.getImageFrom('assets/blockicons/Speaker', 'svg'), - BlockSpecs.limeCmd, 's', 'pop.mp3', BlockSpecs.limeCmdH, null, null, BlockSpecs.cmdS], - 'playusersnd': ['playusersnd', BlockSpecs.getImageFrom('assets/blockicons/Microphone', 'svg'), - BlockSpecs.limeCmd, 'r', '1', BlockSpecs.limeCmdH, null, null, BlockSpecs.cmdS], - 'endstack': ['endstack', null, BlockSpecs.redEnd, null, null, BlockSpecs.redEndH, null, null, BlockSpecs.endS], - 'forever': ['forever', BlockSpecs.getImageFrom('assets/blockicons/Forever', 'svg'), - BlockSpecs.redEnd, null, null, BlockSpecs.redEndH, null, null, BlockSpecs.endS], - 'gotopage': ['gotopage', null, - BlockSpecs.redEndLong, 'p', '2', BlockSpecs.redEndLongH, null, null, BlockSpecs.endLongS], - 'caretstart': ['caretstart', null, - BlockSpecs.getImageFrom('assets/blocks/caretstart', 'svg'), null, null, null, null, null], - 'caretend': ['caretend', null, - BlockSpecs.getImageFrom('assets/blocks/caretend', 'svg'), null, null, null, null, null], - 'caretrepeat': ['caretrepeat', null, - BlockSpecs.getImageFrom('assets/blocks/caretrepeat'), null, null, null, null, null], - 'caretcmd': ['caretcmd', null, - BlockSpecs.getImageFrom('assets/blocks/caretcmd', 'svg'), null, null, null, null, null] + 'playsnd': ['playsnd', BlockSpecs.getImageFrom('assets/blockicons/Speaker', 'svg'), + BlockSpecs.limeCmd, 's', 'pop.mp3', BlockSpecs.limeCmdH, null, null, BlockSpecs.cmdS], + 'playusersnd': ['playusersnd', BlockSpecs.getImageFrom('assets/blockicons/Microphone', 'svg'), + BlockSpecs.limeCmd, 'r', '1', BlockSpecs.limeCmdH, null, null, BlockSpecs.cmdS], + 'endstack': ['endstack', null, BlockSpecs.redEnd, null, null, + BlockSpecs.redEndH, null, null, BlockSpecs.endS], + 'forever': ['forever', BlockSpecs.getImageFrom('assets/blockicons/Forever', 'svg'), + BlockSpecs.redEnd, null, null, BlockSpecs.redEndH, null, null, BlockSpecs.endS], + 'gotopage': ['gotopage', null, + BlockSpecs.redEndLong, 'p', '2', BlockSpecs.redEndLongH, null, null, BlockSpecs.endLongS], + 'caretstart': ['caretstart', null, + BlockSpecs.getImageFrom('assets/blocks/caretstart', 'svg'), null, null, null, null, null], + 'caretend': ['caretend', null, + BlockSpecs.getImageFrom('assets/blocks/caretend', 'svg'), null, null, null, null, null], + 'caretrepeat': ['caretrepeat', null, + BlockSpecs.getImageFrom('assets/blocks/caretrepeat'), null, null, null, null, null], + 'caretcmd': ['caretcmd', null, + BlockSpecs.getImageFrom('assets/blocks/caretcmd', 'svg'), null, null, null, null, null] - }; -}; + }; + } -BlockSpecs.blockDesc = function (b, spr) { - var str = b.getArgValue() ? b.getArgValue().toString() : (b.blocktype == 'playsnd') ? 'SOUND' : ''; + static blockDesc (b, spr) { + var str = b.getArgValue() ? b.getArgValue().toString() : (b.blocktype == 'playsnd') ? 'SOUND' : ''; - return { - 'onflag': Localization.localize('BLOCK_DESC_GREEN_FLAG'), - 'onclick': Localization.localize('BLOCK_DESC_ON_TAP', { - CHARACTER_NAME: spr.name - }), - 'ontouch': Localization.localize('BLOCK_DESC_ON_BUMP', { - CHARACTER_NAME: spr.name ? spr.name : '' - }), - 'onmessage': Localization.localize('BLOCK_DESC_ON_MESSAGE', { - COLOR: Localization.localize('BLOCK_DESC_MESSAGE_COLOR_ORANGE') - }), - 'repeat': Localization.localize('BLOCK_DESC_REPEAT'), - 'forward': Localization.localize('BLOCK_DESC_MOVE_RIGHT'), - 'back': Localization.localize('BLOCK_DESC_MOVE_LEFT'), - 'up': Localization.localize('BLOCK_DESC_MOVE_UP'), - 'down': Localization.localize('BLOCK_DESC_MOVE_DOWN'), - 'home': Localization.localize('BLOCK_DESC_GO_HOME'), - 'left': Localization.localize('BLOCK_DESC_TURN_LEFT'), - 'right': Localization.localize('BLOCK_DESC_TURN_RIGHT'), - 'hop': Localization.localize('BLOCK_DESC_HOP'), - 'wait': Localization.localize('BLOCK_DESC_WAIT'), - 'setspeed': Localization.localize('BLOCK_DESC_SET_SPEED'), - 'stopmine': Localization.localize('BLOCK_DESC_STOP', { - CHARACTER_NAME: spr.name ? spr.name : spr.str - }), - 'say': Localization.localize('BLOCK_DESC_SAY'), - 'show': Localization.localize('BLOCK_DESC_SHOW'), - 'hide': Localization.localize('BLOCK_DESC_HIDE'), - 'grow': Localization.localize('BLOCK_DESC_GROW'), - 'shrink': Localization.localize('BLOCK_DESC_SHRINK'), - 'same': Localization.localize('BLOCK_DESC_RESET_SIZE'), - 'playsnd': Localization.localize('BLOCK_DESC_PLAY_SOUND', { - SOUND_NAME: Localization.localize('BLOCK_DESC_PLAY_SOUND_POP') - }), - 'playusersnd': Localization.localize('BLOCK_DESC_PLAY_RECORDED_SOUND'), - 'endstack': Localization.localize('BLOCK_DESC_END'), - 'stopall': Localization.localize('BLOCK_DESC_STOP', { - CHARACTER_NAME: spr.name ? spr.name : '' - }), - 'forever': Localization.localize('BLOCK_DESC_REPEAT_FOREVER'), - 'gotopage': Localization.localize('BLOCK_DESC_GO_TO_PAGE', { - PAGE: str - }), - 'message': Localization.localize('BLOCK_DESC_SEND_MESSAGE', { - COLOR: Localization.localize('BLOCK_DESC_MESSAGE_COLOR_ORANGE') - }) - }; -}; + return { + 'onflag': Localization.localize('BLOCK_DESC_GREEN_FLAG'), + 'onclick': Localization.localize('BLOCK_DESC_ON_TAP', { + CHARACTER_NAME: spr.name + }), + 'ontouch': Localization.localize('BLOCK_DESC_ON_BUMP', { + CHARACTER_NAME: spr.name ? spr.name : '' + }), + 'onmessage': Localization.localize('BLOCK_DESC_ON_MESSAGE', { + COLOR: Localization.localize('BLOCK_DESC_MESSAGE_COLOR_ORANGE') + }), + 'repeat': Localization.localize('BLOCK_DESC_REPEAT'), + 'forward': Localization.localize('BLOCK_DESC_MOVE_RIGHT'), + 'back': Localization.localize('BLOCK_DESC_MOVE_LEFT'), + 'up': Localization.localize('BLOCK_DESC_MOVE_UP'), + 'down': Localization.localize('BLOCK_DESC_MOVE_DOWN'), + 'home': Localization.localize('BLOCK_DESC_GO_HOME'), + 'left': Localization.localize('BLOCK_DESC_TURN_LEFT'), + 'right': Localization.localize('BLOCK_DESC_TURN_RIGHT'), + 'hop': Localization.localize('BLOCK_DESC_HOP'), + 'wait': Localization.localize('BLOCK_DESC_WAIT'), + 'setspeed': Localization.localize('BLOCK_DESC_SET_SPEED'), + 'stopmine': Localization.localize('BLOCK_DESC_STOP', { + CHARACTER_NAME: spr.name ? spr.name : spr.str + }), + 'say': Localization.localize('BLOCK_DESC_SAY'), + 'show': Localization.localize('BLOCK_DESC_SHOW'), + 'hide': Localization.localize('BLOCK_DESC_HIDE'), + 'grow': Localization.localize('BLOCK_DESC_GROW'), + 'shrink': Localization.localize('BLOCK_DESC_SHRINK'), + 'same': Localization.localize('BLOCK_DESC_RESET_SIZE'), + 'playsnd': Localization.localize('BLOCK_DESC_PLAY_SOUND', { + SOUND_NAME: Localization.localize('BLOCK_DESC_PLAY_SOUND_POP') + }), + 'playusersnd': Localization.localize('BLOCK_DESC_PLAY_RECORDED_SOUND'), + 'endstack': Localization.localize('BLOCK_DESC_END'), + 'stopall': Localization.localize('BLOCK_DESC_STOP', { + CHARACTER_NAME: spr.name ? spr.name : '' + }), + 'forever': Localization.localize('BLOCK_DESC_REPEAT_FOREVER'), + 'gotopage': Localization.localize('BLOCK_DESC_GO_TO_PAGE', { + PAGE: str + }), + 'message': Localization.localize('BLOCK_DESC_SEND_MESSAGE', { + COLOR: Localization.localize('BLOCK_DESC_MESSAGE_COLOR_ORANGE') + }) + }; + } +} diff --git a/src/editor/blocks/Menu.js b/src/editor/blocks/Menu.js index aefe020..dc82c96 100644 --- a/src/editor/blocks/Menu.js +++ b/src/editor/blocks/Menu.js @@ -1,92 +1,104 @@ -var Menu = function () {}; +import BlockSpecs from './BlockSpecs'; +import {scaleMultiplier, setProps, setCanvasSize, newHTML, isTablet, + newDiv, getDocumentHeight, drawThumbnail, frame, globalx, globaly} from '../../utils/lib'; -Menu.openMenu = undefined; +let openMenu = undefined; -Menu.openDropDown = function (b, fcn) { - var size = 50; - var color = b.owner.blocktype == 'setspeed' ? 'orange' : 'yellow'; - var list = JSON.parse(b.owner.arg.list); - var num = b.owner.arg.numperrow; - var p = b.parentNode; - var dh = size * Math.round(list.length / num); - var rows = list.length / num; - var w = size * list.length / rows; - var scaledWidth = w * scaleMultiplier; - var dx = b.left + (b.offsetWidth - scaledWidth) / 2; - if ((dx + scaledWidth) > p.width) { - dx -= ((dx + scaledWidth) - p.width); +export default class Menu { + static get openMenu () { + return openMenu; } - if (dx < 5) { - dx = 5; - } - dx += globalx(p, 0); - var dy = b.top + b.offsetHeight - ((10 + 18) * scaleMultiplier) + globaly(p, 0); - if ((dy + ((10 + dh) * scaleMultiplier)) > getDocumentHeight()) { - dy = getDocumentHeight() - ((15 + dh) * scaleMultiplier); - } - var mu = newDiv(frame, dx, dy, w, dh, { - position: 'absolute', - zIndex: 100000, - webkitTransform: 'translate(' + (-w / 2) + 'px,' + (-dh / 2) + 'px) ' + - 'scale(' + scaleMultiplier + ', ' + scaleMultiplier + ') ' + - 'translate(' + (w / 2) + 'px, ' + (dh / 2) + 'px)' - }); - mu.setAttribute('class', 'menustyle ' + color); - mu.active = b; - for (var i = 0; i < list.length; i++) { - Menu.addImageToDropDown(mu, list[i], b, fcn); - } - Menu.openMenu = mu; -}; -Menu.addImageToDropDown = function (mu, c, block, fcn) { - var img = BlockSpecs.getImageFrom('assets/blockicons/' + c, 'svg'); - var cs = newHTML('div', 'ddchoice', mu); - var micon = newHTML('canvas', undefined, cs); - var iconSize = 42; - var scaledIconSize = iconSize * window.devicePixelRatio; - setCanvasSize(micon, scaledIconSize, scaledIconSize); - setProps(micon.style, { - webkitTransform: 'translate(' + (-scaledIconSize / 2) + 'px, ' + (-scaledIconSize / 2) + 'px) ' + - 'scale(' + (1 / window.devicePixelRatio) + ', ' + (1 / window.devicePixelRatio) + ') ' + - 'translate(' + (scaledIconSize / 2) + 'px, ' + (scaledIconSize / 2) + 'px)' - }); - if (!img.complete) { - img.onload = function () { + static set openMenu (newOpenMenu) { + openMenu = newOpenMenu; + } + + static openDropDown (b, fcn) { + var size = 50; + var color = b.owner.blocktype == 'setspeed' ? 'orange' : 'yellow'; + var list = JSON.parse(b.owner.arg.list); + var num = b.owner.arg.numperrow; + var p = b.parentNode; + var dh = size * Math.round(list.length / num); + var rows = list.length / num; + var w = size * list.length / rows; + var scaledWidth = w * scaleMultiplier; + var dx = b.left + (b.offsetWidth - scaledWidth) / 2; + if ((dx + scaledWidth) > p.width) { + dx -= ((dx + scaledWidth) - p.width); + } + if (dx < 5) { + dx = 5; + } + dx += globalx(p, 0); + var dy = b.top + b.offsetHeight - ((10 + 18) * scaleMultiplier) + globaly(p, 0); + if ((dy + ((10 + dh) * scaleMultiplier)) > getDocumentHeight()) { + dy = getDocumentHeight() - ((15 + dh) * scaleMultiplier); + } + var mu = newDiv(frame, dx, dy, w, dh, { + position: 'absolute', + zIndex: 100000, + webkitTransform: 'translate(' + (-w / 2) + 'px,' + (-dh / 2) + 'px) ' + + 'scale(' + scaleMultiplier + ', ' + scaleMultiplier + ') ' + + 'translate(' + (w / 2) + 'px, ' + (dh / 2) + 'px)' + }); + mu.setAttribute('class', 'menustyle ' + color); + mu.active = b; + for (var i = 0; i < list.length; i++) { + Menu.addImageToDropDown(mu, list[i], b, fcn); + } + openMenu = mu; + } + + static addImageToDropDown (mu, c, block, fcn) { + var img = BlockSpecs.getImageFrom('assets/blockicons/' + c, 'svg'); + var cs = newHTML('div', 'ddchoice', mu); + var micon = newHTML('canvas', undefined, cs); + var iconSize = 42; + var scaledIconSize = iconSize * window.devicePixelRatio; + setCanvasSize(micon, scaledIconSize, scaledIconSize); + setProps(micon.style, { + webkitTransform: 'translate(' + (-scaledIconSize / 2) + 'px, ' + (-scaledIconSize / 2) + 'px) ' + + 'scale(' + (1 / window.devicePixelRatio) + ', ' + (1 / window.devicePixelRatio) + ') ' + + 'translate(' + (scaledIconSize / 2) + 'px, ' + (scaledIconSize / 2) + 'px)' + }); + if (!img.complete) { + img.onload = function () { + drawThumbnail(img, micon); + }; + } else { drawThumbnail(img, micon); - }; - } else { - drawThumbnail(img, micon); + } + if (isTablet) { + cs.ontouchstart = function (evt) { + handleTouchStart(evt); + }; + } else { + cs.onmouseover = function (evt) { + Menu.highlightdot(evt); + }; + cs.onmouseout = function (evt) { + Menu.unhighlightdot(evt); + }; + cs.onmousedown = function (evt) { + fcn(evt, mu, block, c); + }; + } + function handleTouchStart (e) { + if (isTablet && e.touches && (e.touches.length > 1)) { + return; + } + e.preventDefault(); + e.stopPropagation(); + fcn(e, mu, block, c); + } } - if (isTablet) { - cs.ontouchstart = function (evt) { - handleTouchStart(evt); - }; - } else { - cs.onmouseover = function (evt) { - Menu.highlightdot(evt); - }; - cs.onmouseout = function (evt) { - Menu.unhighlightdot(evt); - }; - cs.onmousedown = function (evt) { - fcn(evt, mu, block, c); - }; - } - function handleTouchStart (e) { - if (isTablet && e.touches && (e.touches.length > 1)) { + + static closeMyOpenMenu () { + if (!openMenu) { return; } - e.preventDefault(); - e.stopPropagation(); - fcn(e, mu, block, c); + openMenu.parentNode.removeChild(openMenu); + openMenu = undefined; } -}; - -Menu.closeMyOpenMenu = function () { - if (!Menu.openMenu) { - return; - } - Menu.openMenu.parentNode.removeChild(Menu.openMenu); - Menu.openMenu = undefined; -}; +} diff --git a/src/editor/engine/Prims.js b/src/editor/engine/Prims.js index ef31035..c0b0a49 100755 --- a/src/editor/engine/Prims.js +++ b/src/editor/engine/Prims.js @@ -1,631 +1,639 @@ -var Prims = function () {}; +import ScratchJr from '../ScratchJr'; +import ScratchAudio from '../../utils/ScratchAudio'; +import Grid from '../ui/Grid'; +import Vector from '../../geom/Vector'; +import {gn} from '../../utils/lib'; -Prims.tinterval = 1; -Prims.hopList = [-48, -30, -22, -14, -6, 0, 6, 14, 22, 30, 48]; -Prims.soundTime = undefined; -Prims.time = 0; +let tinterval = 1; +let hopList = [-48, -30, -22, -14, -6, 0, 6, 14, 22, 30, 48]; -Prims.init = function () { - Prims.table = {}; - Prims.table.done = Prims.Done; - Prims.table.missing = Prims.Ignore; - Prims.table.onflag = Prims.Ignore; - Prims.table.onmessage = Prims.Ignore; - Prims.table.onclick = Prims.Ignore; - Prims.table.ontouch = Prims.OnTouch; - Prims.table.onchat = Prims.Ignore; - Prims.table.repeat = Prims.Repeat; - Prims.table.forward = Prims.Forward; - Prims.table.back = Prims.Back; - Prims.table.up = Prims.Up; - Prims.table.down = Prims.Down; - Prims.table.left = Prims.Left; - Prims.table.right = Prims.Right; - Prims.table.home = Prims.Home; - Prims.table.setspeed = Prims.SetSpeed; - Prims.table.message = Prims.Message; - Prims.table.setcolor = Prims.SetColor; - Prims.table.bigger = Prims.Bigger; - Prims.table.smaller = Prims.Smaller; - Prims.table.wait = Prims.Wait; - Prims.table.caretcmd = Prims.Ignore; - Prims.table.caretstart = Prims.Ignore; - Prims.table.caretend = Prims.Ignore; - Prims.table.caretrepeat = Prims.Ignore; - Prims.table.gotopage = Prims.GotoPage; - Prims.table.endstack = Prims.DoNextBlock; - Prims.table.stopall = Prims.StopAll; - Prims.table.stopmine = Prims.StopMine; - Prims.table.forever = Prims.Forever; - Prims.table.hop = Prims.Hop; - Prims.table.show = Prims.Show; - Prims.table.hide = Prims.Hide; - Prims.table.playsnd = Prims.playSound; - Prims.table.playusersnd = Prims.playSound; - Prims.table.grow = Prims.Grow; - Prims.table.shrink = Prims.Shrink; - Prims.table.same = Prims.Same; - Prims.table.say = Prims.Say; -}; - -Prims.Done = function (strip) { - if (strip.oldblock != null) { - strip.oldblock.unhighlight(); +export default class Prims { + static get hopList () { + return hopList; } - strip.oldblock = null; - strip.isRunning = false; -}; -Prims.setTime = function (strip) { - strip.time = (new Date()) - 0; -}; + static init () { + Prims.table = {}; + Prims.table.done = Prims.Done; + Prims.table.missing = Prims.Ignore; + Prims.table.onflag = Prims.Ignore; + Prims.table.onmessage = Prims.Ignore; + Prims.table.onclick = Prims.Ignore; + Prims.table.ontouch = Prims.OnTouch; + Prims.table.onchat = Prims.Ignore; + Prims.table.repeat = Prims.Repeat; + Prims.table.forward = Prims.Forward; + Prims.table.back = Prims.Back; + Prims.table.up = Prims.Up; + Prims.table.down = Prims.Down; + Prims.table.left = Prims.Left; + Prims.table.right = Prims.Right; + Prims.table.home = Prims.Home; + Prims.table.setspeed = Prims.SetSpeed; + Prims.table.message = Prims.Message; + Prims.table.setcolor = Prims.SetColor; + Prims.table.bigger = Prims.Bigger; + Prims.table.smaller = Prims.Smaller; + Prims.table.wait = Prims.Wait; + Prims.table.caretcmd = Prims.Ignore; + Prims.table.caretstart = Prims.Ignore; + Prims.table.caretend = Prims.Ignore; + Prims.table.caretrepeat = Prims.Ignore; + Prims.table.gotopage = Prims.GotoPage; + Prims.table.endstack = Prims.DoNextBlock; + Prims.table.stopall = Prims.StopAll; + Prims.table.stopmine = Prims.StopMine; + Prims.table.forever = Prims.Forever; + Prims.table.hop = Prims.Hop; + Prims.table.show = Prims.Show; + Prims.table.hide = Prims.Hide; + Prims.table.playsnd = Prims.playSound; + Prims.table.playusersnd = Prims.playSound; + Prims.table.grow = Prims.Grow; + Prims.table.shrink = Prims.Shrink; + Prims.table.same = Prims.Same; + Prims.table.say = Prims.Say; + } -Prims.showTime = function () { - //var time = ((new Date()) - strip.time) / 1000; - // ScratchJr.log (strip.thisblock.blocktype, time, "sec") ; -}; + static Done (strip) { + if (strip.oldblock != null) { + strip.oldblock.unhighlight(); + } + strip.oldblock = null; + strip.isRunning = false; + } -Prims.DoNextBlock = function (strip) { - strip.waitTimer = Prims.tinterval * 10; - strip.thisblock = strip.thisblock.next; -}; + static setTime (strip) { + strip.time = (new Date()) - 0; + } -Prims.StopAll = function () { - ScratchJr.stopStrips(); -}; + static showTime () { + //var time = ((new Date()) - strip.time) / 1000; + // ScratchJr.log (strip.thisblock.blocktype, time, "sec") ; + } -Prims.StopMine = function (strip) { - var spr = strip.spr; - for (var i = 0; i < ScratchJr.runtime.threadsRunning.length; i++) { - if ((ScratchJr.runtime.threadsRunning[i].spr == spr) - && (ScratchJr.runtime.threadsRunning[i].thisblock != strip.thisblock)) { - ScratchJr.runtime.threadsRunning[i].stop(true); + static DoNextBlock (strip) { + strip.waitTimer = tinterval * 10; + strip.thisblock = strip.thisblock.next; + } + + static StopAll () { + ScratchJr.stopStrips(); + } + + static StopMine (strip) { + var spr = strip.spr; + for (var i = 0; i < ScratchJr.runtime.threadsRunning.length; i++) { + if ((ScratchJr.runtime.threadsRunning[i].spr == spr) + && (ScratchJr.runtime.threadsRunning[i].thisblock != strip.thisblock)) { + ScratchJr.runtime.threadsRunning[i].stop(true); + } + } + strip.thisblock = strip.thisblock.next; + ScratchJr.runtime.yield = true; + } + + static playSound (strip) { + var b = strip.thisblock; + var name = b.getSoundName(strip.spr.sounds); + // console.log ('playSound', name); + if (!strip.audio) { + var snd = ScratchAudio.projectSounds[name]; + if (!snd) { + strip.thisblock = strip.thisblock.next; + return; + } + strip.audio = snd; + snd.play(); + // console.log ("playSound", snd, strip.audio, snd.source.playbackState); + } + if (strip.audio && strip.audio.done()) { + strip.audio.clear(); + strip.thisblock = strip.thisblock.next; + strip.audio = undefined; + } + strip.waitTimer = tinterval * 4; + } + + static Say (strip) { + var b = strip.thisblock; + var s = strip.spr; + var str = b.getArgValue(); + if (strip.count < 0) { + strip.count = Math.max(30, Math.round(str.length / 8) * 30); // 7 chars per seconds; + s.openBalloon(str); + Prims.setTime(strip); + } else { + var count = strip.count; + count--; + if (count < 0) { + strip.count = -1; + s.closeBalloon(); + Prims.showTime(strip); + strip.thisblock = strip.thisblock.next; + } else { + strip.waitTimer = tinterval; + strip.count = count; + } } } - strip.thisblock = strip.thisblock.next; - ScratchJr.runtime.yield = true; -}; -Prims.playSound = function (strip) { - var b = strip.thisblock; - var name = b.getSoundName(strip.spr.sounds); - // console.log ('playSound', name); - if (!strip.audio) { - var snd = ScratchAudio.projectSounds[name]; - if (!snd) { + static GotoPage (strip) { + var b = strip.thisblock; + var n = Number(b.getArgValue()); + if (strip.count < 0) { + strip.count = 2; // delay for a 10th of a second + Prims.setTime(strip); + } else { + var count = strip.count; + count--; + if (count < 0) { + strip.count = -1; + Prims.showTime(strip); + ScratchJr.stage.gotoPage(n); + } else { + strip.waitTimer = tinterval; + strip.count = count; + } + } + } + + static Forever (strip) { + strip.thisblock = strip.firstBlock.aStart ? strip.firstBlock.next : strip.firstBlock; + ScratchJr.runtime.yield = true; + } + + static Repeat (strip) { + var b = strip.thisblock; + var n = Number(b.getArgValue()); + if (n < 1) { + n = 1; + } + if (b.repeatCounter < 0) { + b.repeatCounter = n; + } + if (b.repeatCounter == 0) { + b.repeatCounter = -1; strip.thisblock = strip.thisblock.next; + strip.waitTimer = tinterval; + } else { + strip.stack.push(strip.thisblock); + b.repeatCounter--; + strip.thisblock = strip.thisblock.inside; + ScratchJr.runtime.yield = true; + } + } + + static Ignore (strip) { + strip.thisblock = strip.thisblock.next; + } + + static Wait (strip) { + var n = strip.thisblock.getArgValue(); + strip.waitTimer = Math.round(n * 3.125); // thenth of a second + Prims.setTime(strip); + strip.thisblock = strip.thisblock.next; + } + + static Home (strip) { + var spr = strip.spr; + spr.goHome(); + strip.waitTimer = tinterval; + strip.thisblock = strip.thisblock.next; + } + + static SetSpeed (strip) { + var s = strip.spr; + var num = Number(strip.thisblock.getArgValue()); // 0 - 1 - 2 + s.speed = Math.pow(2, num); + strip.waitTimer = tinterval; + strip.thisblock = strip.thisblock.next; + } + + static Hop (strip) { + if (strip.count < 0) { // setup the hop + strip.count = hopList.length; + Prims.setTime(strip); + } + Prims.hopTo(strip); + } + + static hopTo (strip) { + var s = strip.spr; + var b = strip.thisblock; + var n = Number(b.getArgValue()); + var count = strip.count; + count--; + if (count < 0) { + strip.count = -1; + strip.vector = { + x: 0, + y: 0 + }; + Prims.showTime(strip); + strip.thisblock = strip.thisblock.next; + } else { + strip.vector = { + x: 0, + y: hopList[count] + }; + var dy = s.ycoor - strip.vector.y / 5 * n; + if (dy < 0) { + dy = 0; + } + if (dy >= (360 - Grid.size)) { + dy = (360 - Grid.size); + } + s.setPos(s.xcoor + strip.vector.x, dy); + strip.waitTimer = tinterval + Math.floor(Math.pow(2, 2 - Math.floor(s.speed / 2)) / 2); + strip.count = count; + } + } + + static Down (strip) { + var num = Number(strip.thisblock.getArgValue()) * 24; + var distance = Math.abs(num); + if (num == 0) { + strip.thisblock = strip.thisblock.next; + strip.waitTimer = tinterval; + strip.distance = -1; + strip.vector = { + x: 0, + y: 0 + }; return; } - strip.audio = snd; - snd.play(); - // console.log ("playSound", snd, strip.audio, snd.source.playbackState); + if (num == 0) { + strip.distance = 0; + } else if (strip.distance < 0) { + strip.distance = distance; + strip.vector = { + x: 0, + y: 2 + }; + Prims.setTime(strip); + } + Prims.moveAtSpeed(strip); } - if (strip.audio && strip.audio.done()) { - strip.audio.clear(); - strip.thisblock = strip.thisblock.next; - strip.audio = undefined; - } - strip.waitTimer = Prims.tinterval * 4; -}; -Prims.Say = function (strip) { - var b = strip.thisblock; - var s = strip.spr; - var str = b.getArgValue(); - if (strip.count < 0) { - strip.count = Math.max(30, Math.round(str.length / 8) * 30); // 7 chars per seconds; - s.openBalloon(str); - Prims.setTime(strip); - } else { - var count = strip.count; - count--; - if (count < 0) { - strip.count = -1; - s.closeBalloon(); + static Up (strip) { + var num = Number(strip.thisblock.getArgValue()) * 24; + var distance = Math.abs(num); + if (num == 0) { + strip.thisblock = strip.thisblock.next; + strip.waitTimer = tinterval; + strip.distance = -1; + strip.vector = { + x: 0, + y: 0 + }; + return; + } else if (strip.distance < 0) { + strip.distance = distance; + strip.vector = { + x: 0, + y: -2 + }; + Prims.setTime(strip); + } + Prims.moveAtSpeed(strip); + } + + static Forward (strip) { + var s = strip.spr; + var num = Number(strip.thisblock.getArgValue()) * 24; + var distance = Math.abs(num); + if (s.flip) { + s.flip = false; + s.render(); + } + if (num == 0) { + strip.thisblock = strip.thisblock.next; + strip.waitTimer = tinterval * Math.pow(2, 2 - Math.floor(s.speed / 2)); + strip.vector = { + x: 0, + y: 0 + }; + strip.distance = -1; + return; + } else if (strip.distance < 0) { + strip.distance = distance; + strip.vector = { + x: 2, + y: 0 + }; + Prims.setTime(strip); + } + Prims.moveAtSpeed(strip); + } + + static Back (strip) { + var s = strip.spr; + var num = Number(strip.thisblock.getArgValue()) * 24; + var distance = Math.abs(num); + if (!s.flip) { + s.flip = true; + s.render(); + } + if (num == 0) { + strip.thisblock = strip.thisblock.next; + strip.vector = { + x: 0, + y: 0 + }; + strip.waitTimer = tinterval * Math.pow(2, 2 - Math.floor(s.speed / 2)); + return; + } + if (num == 0) { + strip.distance = 0; + } else if (strip.distance < 0) { + strip.distance = distance; + strip.vector = { + x: -2, + y: 0 + }; + Prims.setTime(strip); + } + Prims.moveAtSpeed(strip); + } + + static moveAtSpeed (strip) { + var s = strip.spr; + var distance = strip.distance; + var num = Number(strip.thisblock.getArgValue()) * 12; // 1/2 cell size since vector is double + var vector = Vector.scale(strip.vector, s.speed * Math.abs(num) / num); + distance -= Math.abs(Vector.len(vector)); + if (distance < 0) { + vector = Vector.scale(strip.vector, strip.distance); + s.setPos(s.xcoor + vector.x, s.ycoor + vector.y); + strip.distance = -1; + strip.vector = { + x: 0, + y: 0 + }; Prims.showTime(strip); strip.thisblock = strip.thisblock.next; } else { - strip.waitTimer = Prims.tinterval; - strip.count = count; + s.setPos(s.xcoor + vector.x, s.ycoor + vector.y); + strip.waitTimer = tinterval; + strip.distance = distance; } } -}; -Prims.GotoPage = function (strip) { - var b = strip.thisblock; - var n = Number(b.getArgValue()); - if (strip.count < 0) { - strip.count = 2; // delay for a 10th of a second - Prims.setTime(strip); - } else { + static Right (strip) { + var s = strip.spr; + var num = Number(strip.thisblock.getArgValue()) * 30; + if (strip.count < 0) { + strip.count = Math.floor(Math.abs(num) / s.speed * 0.25); + strip.angleStep = s.speed * 4 * Math.abs(num) / num; + strip.finalAngle = s.angle + num; + strip.finalAngle = strip.finalAngle % 360; + if (strip.finalAngle < 0) { + strip.finalAngle += 360; + } + if (strip.finalAngle > 360) { + strip.finalAngle -= 360; + } + Prims.setTime(strip); + } + Prims.turning(strip); + } + + static Left (strip) { + var s = strip.spr; + var num = Number(strip.thisblock.getArgValue()) * 30; + if (strip.count < 0) { + strip.count = Math.floor(Math.abs(num) / s.speed * 0.25); + strip.angleStep = -s.speed * 4 * Math.abs(num) / num; + strip.finalAngle = s.angle - num; + strip.finalAngle = strip.finalAngle % 360; + if (strip.finalAngle < 0) { + strip.finalAngle += 360; + } + if (strip.finalAngle > 360) { + strip.finalAngle -= 360; + } + Prims.setTime(strip); + } + Prims.turning(strip); + } + + static turning (strip) { + var s = strip.spr; var count = strip.count; count--; if (count < 0) { strip.count = -1; + s.setHeading(strip.finalAngle); Prims.showTime(strip); - ScratchJr.stage.gotoPage(n); + strip.thisblock = strip.thisblock.next; } else { - strip.waitTimer = Prims.tinterval; + s.setHeading(s.angle + strip.angleStep); + strip.waitTimer = tinterval; strip.count = count; } } -}; -Prims.Forever = function (strip) { - strip.thisblock = strip.firstBlock.aStart ? strip.firstBlock.next : strip.firstBlock; - ScratchJr.runtime.yield = true; -}; - -Prims.Repeat = function (strip) { - var b = strip.thisblock; - var n = Number(b.getArgValue()); - if (n < 1) { - n = 1; - } - if (b.repeatCounter < 0) { - b.repeatCounter = n; - } - if (b.repeatCounter == 0) { - b.repeatCounter = -1; - strip.thisblock = strip.thisblock.next; - strip.waitTimer = Prims.tinterval; - } else { - strip.stack.push(strip.thisblock); - b.repeatCounter--; - strip.thisblock = strip.thisblock.inside; - ScratchJr.runtime.yield = true; - } -}; - -Prims.Ignore = function (strip) { - strip.thisblock = strip.thisblock.next; -}; - -Prims.Wait = function (strip) { - var n = strip.thisblock.getArgValue(); - strip.waitTimer = Math.round(n * 3.125); // thenth of a second - Prims.setTime(strip); - strip.thisblock = strip.thisblock.next; -}; - -Prims.Home = function (strip) { - var spr = strip.spr; - spr.goHome(); - strip.waitTimer = Prims.tinterval; - strip.thisblock = strip.thisblock.next; -}; - -Prims.SetSpeed = function (strip) { - var s = strip.spr; - var num = Number(strip.thisblock.getArgValue()); // 0 - 1 - 2 - s.speed = Math.pow(2, num); - strip.waitTimer = Prims.tinterval; - strip.thisblock = strip.thisblock.next; -}; - -Prims.Hop = function (strip) { - if (strip.count < 0) { // setup the hop - strip.count = Prims.hopList.length; - Prims.setTime(strip); - } - Prims.hopTo(strip); -}; - -Prims.hopTo = function (strip) { - var s = strip.spr; - var b = strip.thisblock; - var n = Number(b.getArgValue()); - var count = strip.count; - count--; - if (count < 0) { - strip.count = -1; - strip.vector = { - x: 0, - y: 0 - }; - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - } else { - strip.vector = { - x: 0, - y: Prims.hopList[count] - }; - var dy = s.ycoor - strip.vector.y / 5 * n; - if (dy < 0) { - dy = 0; - } - if (dy >= (360 - Grid.size)) { - dy = (360 - Grid.size); - } - s.setPos(s.xcoor + strip.vector.x, dy); - strip.waitTimer = Prims.tinterval + Math.floor(Math.pow(2, 2 - Math.floor(s.speed / 2)) / 2); - strip.count = count; - } -}; - -Prims.Down = function (strip) { - var num = Number(strip.thisblock.getArgValue()) * 24; - var distance = Math.abs(num); - if (num == 0) { - strip.thisblock = strip.thisblock.next; - strip.waitTimer = Prims.tinterval; - strip.distance = -1; - strip.vector = { - x: 0, - y: 0 - }; - return; - } - if (num == 0) { - strip.distance = 0; - } else if (strip.distance < 0) { - strip.distance = distance; - strip.vector = { - x: 0, - y: 2 - }; - Prims.setTime(strip); - } - Prims.moveAtSpeed(strip); -}; - -Prims.Up = function (strip) { - var num = Number(strip.thisblock.getArgValue()) * 24; - var distance = Math.abs(num); - if (num == 0) { - strip.thisblock = strip.thisblock.next; - strip.waitTimer = Prims.tinterval; - strip.distance = -1; - strip.vector = { - x: 0, - y: 0 - }; - return; - } else if (strip.distance < 0) { - strip.distance = distance; - strip.vector = { - x: 0, - y: -2 - }; - Prims.setTime(strip); - } - Prims.moveAtSpeed(strip); -}; - -Prims.Forward = function (strip) { - var s = strip.spr; - var num = Number(strip.thisblock.getArgValue()) * 24; - var distance = Math.abs(num); - if (s.flip) { - s.flip = false; - s.render(); - } - if (num == 0) { - strip.thisblock = strip.thisblock.next; - strip.waitTimer = Prims.tinterval * Math.pow(2, 2 - Math.floor(s.speed / 2)); - strip.vector = { - x: 0, - y: 0 - }; - strip.distance = -1; - return; - } else if (strip.distance < 0) { - strip.distance = distance; - strip.vector = { - x: 2, - y: 0 - }; - Prims.setTime(strip); - } - Prims.moveAtSpeed(strip); -}; - -Prims.Back = function (strip) { - var s = strip.spr; - var num = Number(strip.thisblock.getArgValue()) * 24; - var distance = Math.abs(num); - if (!s.flip) { - s.flip = true; - s.render(); - } - if (num == 0) { - strip.thisblock = strip.thisblock.next; - strip.vector = { - x: 0, - y: 0 - }; - strip.waitTimer = Prims.tinterval * Math.pow(2, 2 - Math.floor(s.speed / 2)); - return; - } - if (num == 0) { - strip.distance = 0; - } else if (strip.distance < 0) { - strip.distance = distance; - strip.vector = { - x: -2, - y: 0 - }; - Prims.setTime(strip); - } - Prims.moveAtSpeed(strip); -}; - -Prims.moveAtSpeed = function (strip) { - var s = strip.spr; - var distance = strip.distance; - var num = Number(strip.thisblock.getArgValue()) * 12; // 1/2 cell size since vector is double - var vector = Vector.scale(strip.vector, s.speed * Math.abs(num) / num); - distance -= Math.abs(Vector.len(vector)); - if (distance < 0) { - vector = Vector.scale(strip.vector, strip.distance); - s.setPos(s.xcoor + vector.x, s.ycoor + vector.y); - strip.distance = -1; - strip.vector = { - x: 0, - y: 0 - }; - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - } else { - s.setPos(s.xcoor + vector.x, s.ycoor + vector.y); - strip.waitTimer = Prims.tinterval; - strip.distance = distance; - } -}; - -Prims.Right = function (strip) { - var s = strip.spr; - var num = Number(strip.thisblock.getArgValue()) * 30; - if (strip.count < 0) { - strip.count = Math.floor(Math.abs(num) / s.speed * 0.25); - strip.angleStep = s.speed * 4 * Math.abs(num) / num; - strip.finalAngle = s.angle + num; - strip.finalAngle = strip.finalAngle % 360; - if (strip.finalAngle < 0) { - strip.finalAngle += 360; - } - if (strip.finalAngle > 360) { - strip.finalAngle -= 360; - } - Prims.setTime(strip); - } - Prims.turning(strip); -}; - -Prims.Left = function (strip) { - var s = strip.spr; - var num = Number(strip.thisblock.getArgValue()) * 30; - if (strip.count < 0) { - strip.count = Math.floor(Math.abs(num) / s.speed * 0.25); - strip.angleStep = -s.speed * 4 * Math.abs(num) / num; - strip.finalAngle = s.angle - num; - strip.finalAngle = strip.finalAngle % 360; - if (strip.finalAngle < 0) { - strip.finalAngle += 360; - } - if (strip.finalAngle > 360) { - strip.finalAngle -= 360; - } - Prims.setTime(strip); - } - Prims.turning(strip); -}; - -Prims.turning = function (strip) { - var s = strip.spr; - var count = strip.count; - count--; - if (count < 0) { - strip.count = -1; - s.setHeading(strip.finalAngle); - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - } else { - s.setHeading(s.angle + strip.angleStep); - strip.waitTimer = Prims.tinterval; - strip.count = count; - } -}; - -Prims.Same = function (strip) { - var s = strip.spr; - var n = (s.defaultScale - s.scale) / s.defaultScale * 10; - if (n == 0) { - strip.waitTimer = Prims.tinterval; - strip.thisblock = strip.thisblock.next; - strip.count = -1; - strip.distance = -1; - if (!strip.firstBlock.aStart) { - s.homescale = s.defaultScale; - } - return; - } - if (strip.count < 0) { - strip.distance = s.defaultScale * Math.abs(n) / n * s.speed; - strip.count = Math.floor(5 * Math.floor(Math.abs(n)) / s.speed); - Prims.setTime(strip); - if (!strip.firstBlock.aStart) { - s.homescale = s.defaultScale; - } - } - if (strip.count == 0) { - strip.count = -1; - s.noScaleFor(); - strip.distance = -1; - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - } else { - s.changeSizeBy(strip.distance * 2); - strip.waitTimer = Prims.tinterval; - strip.count = strip.count - 1; - } -}; - -Prims.Grow = function (strip) { - var s = strip.spr; - var n = Number(strip.thisblock.getArgValue()); - if (strip.count < 0) { - strip.distance = Number(s.scale) + (10 * n * s.defaultScale) / 100; - strip.distance = Math.round(strip.distance * 1000) / 1000; - strip.count = Math.floor(5 * Math.abs(n) / s.speed); - Prims.setTime(strip); - } - if (strip.count == 0) { - strip.count = -1; - s.setScaleTo(strip.distance); - if (!strip.firstBlock.aStart) { - s.homescale = s.scale; - } - strip.distance = -1; - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - } else { - s.changeSizeBy(s.defaultScale * 2 * s.speed * Math.abs(n) / n); - strip.waitTimer = Prims.tinterval; - strip.count = strip.count - 1; - } -}; - -Prims.Shrink = function (strip) { - var s = strip.spr; - var n = Number(strip.thisblock.getArgValue()); - if (strip.count < 0) { - strip.distance = s.scale - (10 * n * s.defaultScale) / 100; - strip.distance = Math.round(strip.distance * 1000) / 1000; - strip.count = Math.floor(5 * Math.abs(n) / s.speed); - Prims.setTime(strip); - } - if (strip.count == 0) { - strip.count = -1; - s.setScaleTo(strip.distance); - if (!strip.firstBlock.aStart) { - s.homescale = s.scale; - } - strip.distance = -1; - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - } else { - s.changeSizeBy(-s.defaultScale * 2 * s.speed * Math.abs(n) / n); - strip.waitTimer = Prims.tinterval; - strip.count = strip.count - 1; - } -}; - -Prims.Show = function (strip) { - var s = strip.spr; - s.shown = true; - if (strip.count < 0) { - strip.count = s.speed == 4 ? 0 : Math.floor(15 / s.speed); - Prims.setTime(strip); - } - if (strip.count == 0) { - strip.count = -1; - s.div.style.opacity = 1; - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - if (!strip.firstBlock.aStart) { - s.homeshown = true; - } - } else { - s.div.style.opacity = Math.min(1, Number(s.div.style.opacity) + (s.speed / 15)); - strip.waitTimer = Prims.tinterval * 2; - strip.count = strip.count - 1; - } -}; - -Prims.Hide = function (strip) { // same - var s = strip.spr; - s.shown = false; - if (strip.count < 0) { - strip.count = s.speed == 4 ? 0 : Math.floor(15 / s.speed); - Prims.setTime(strip); - } - if (strip.count == 0) { - strip.count = -1; - s.div.style.opacity = 0; - Prims.showTime(strip); - strip.thisblock = strip.thisblock.next; - if (!strip.firstBlock.aStart) { - s.homeshown = false; - } - } else { - s.div.style.opacity = Math.max(0, Number(s.div.style.opacity) - (s.speed / 15)); - strip.waitTimer = Prims.tinterval * 2; - strip.count = strip.count - 1; - } -}; - -Prims.OnTouch = function (strip) { - var s = strip.spr; - if (s.touchingAny()) { - strip.stack.push(strip.firstBlock); - strip.thisblock = strip.thisblock.next; - } - strip.waitTimer = Prims.tinterval; -}; - -Prims.Message = function (strip) { - var b = strip.thisblock; - var pair; - if (strip.firstTime) { - var receivers = []; - var msg = b.getArgValue(); - var findReceivers = function (block, s) { - if ((block.blocktype == 'onmessage') && (block.getArgValue() == msg)) { - receivers.push([s, block]); + static Same (strip) { + var s = strip.spr; + var n = (s.defaultScale - s.scale) / s.defaultScale * 10; + if (n == 0) { + strip.waitTimer = tinterval; + strip.thisblock = strip.thisblock.next; + strip.count = -1; + strip.distance = -1; + if (!strip.firstBlock.aStart) { + s.homescale = s.defaultScale; } - }; - Prims.applyToAllStrips(['onmessage'], findReceivers); - var newthreads = []; - for (var i in receivers) { - pair = receivers[i]; - newthreads.push(ScratchJr.runtime.restartThread(pair[0], pair[1], true)); + return; } - strip.firstTime = false; - strip.called = newthreads; - } - - // after first time - var done = true; - for (var j = 0; j < strip.called.length; j++) { - if (strip.called[j].isRunning) { - done = false; + if (strip.count < 0) { + strip.distance = s.defaultScale * Math.abs(n) / n * s.speed; + strip.count = Math.floor(5 * Math.floor(Math.abs(n)) / s.speed); + Prims.setTime(strip); + if (!strip.firstBlock.aStart) { + s.homescale = s.defaultScale; + } + } + if (strip.count == 0) { + strip.count = -1; + s.noScaleFor(); + strip.distance = -1; + Prims.showTime(strip); + strip.thisblock = strip.thisblock.next; + } else { + s.changeSizeBy(strip.distance * 2); + strip.waitTimer = tinterval; + strip.count = strip.count - 1; } } - if (done) { - strip.called = null; - strip.firstTime = true; - strip.thisblock = strip.thisblock.next; - strip.waitTimer = Prims.tinterval * 2; - } else { - ScratchJr.runtime.yield = true; + static Grow (strip) { + var s = strip.spr; + var n = Number(strip.thisblock.getArgValue()); + if (strip.count < 0) { + strip.distance = Number(s.scale) + (10 * n * s.defaultScale) / 100; + strip.distance = Math.round(strip.distance * 1000) / 1000; + strip.count = Math.floor(5 * Math.abs(n) / s.speed); + Prims.setTime(strip); + } + if (strip.count == 0) { + strip.count = -1; + s.setScaleTo(strip.distance); + if (!strip.firstBlock.aStart) { + s.homescale = s.scale; + } + strip.distance = -1; + Prims.showTime(strip); + strip.thisblock = strip.thisblock.next; + } else { + s.changeSizeBy(s.defaultScale * 2 * s.speed * Math.abs(n) / n); + strip.waitTimer = tinterval; + strip.count = strip.count - 1; + } } -}; -Prims.applyToAllStrips = function (list, fcn) { - if (!ScratchJr.stage) { - return; - } - var page = ScratchJr.stage.currentPage; - if (!page) { - return; - } - if (!page.div) { - return; - } - for (var i = 0; i < page.div.childElementCount; i++) { - var spr = page.div.childNodes[i].owner; - if (!spr) { - continue; + static Shrink (strip) { + var s = strip.spr; + var n = Number(strip.thisblock.getArgValue()); + if (strip.count < 0) { + strip.distance = s.scale - (10 * n * s.defaultScale) / 100; + strip.distance = Math.round(strip.distance * 1000) / 1000; + strip.count = Math.floor(5 * Math.abs(n) / s.speed); + Prims.setTime(strip); } - var sc = gn(spr.id + '_scripts'); - if (!sc) { - continue; - } - var topblocks = sc.owner.getBlocksType(list); - for (var j = 0; j < topblocks.length; j++) { - fcn(topblocks[j], spr); + if (strip.count == 0) { + strip.count = -1; + s.setScaleTo(strip.distance); + if (!strip.firstBlock.aStart) { + s.homescale = s.scale; + } + strip.distance = -1; + Prims.showTime(strip); + strip.thisblock = strip.thisblock.next; + } else { + s.changeSizeBy(-s.defaultScale * 2 * s.speed * Math.abs(n) / n); + strip.waitTimer = tinterval; + strip.count = strip.count - 1; } } -}; + + static Show (strip) { + var s = strip.spr; + s.shown = true; + if (strip.count < 0) { + strip.count = s.speed == 4 ? 0 : Math.floor(15 / s.speed); + Prims.setTime(strip); + } + if (strip.count == 0) { + strip.count = -1; + s.div.style.opacity = 1; + Prims.showTime(strip); + strip.thisblock = strip.thisblock.next; + if (!strip.firstBlock.aStart) { + s.homeshown = true; + } + } else { + s.div.style.opacity = Math.min(1, Number(s.div.style.opacity) + (s.speed / 15)); + strip.waitTimer = tinterval * 2; + strip.count = strip.count - 1; + } + } + + static Hide (strip) { // same + var s = strip.spr; + s.shown = false; + if (strip.count < 0) { + strip.count = s.speed == 4 ? 0 : Math.floor(15 / s.speed); + Prims.setTime(strip); + } + if (strip.count == 0) { + strip.count = -1; + s.div.style.opacity = 0; + Prims.showTime(strip); + strip.thisblock = strip.thisblock.next; + if (!strip.firstBlock.aStart) { + s.homeshown = false; + } + } else { + s.div.style.opacity = Math.max(0, Number(s.div.style.opacity) - (s.speed / 15)); + strip.waitTimer = tinterval * 2; + strip.count = strip.count - 1; + } + } + + static OnTouch (strip) { + var s = strip.spr; + if (s.touchingAny()) { + strip.stack.push(strip.firstBlock); + strip.thisblock = strip.thisblock.next; + } + strip.waitTimer = tinterval; + } + + static Message (strip) { + var b = strip.thisblock; + var pair; + if (strip.firstTime) { + var receivers = []; + var msg = b.getArgValue(); + var findReceivers = function (block, s) { + if ((block.blocktype == 'onmessage') && (block.getArgValue() == msg)) { + receivers.push([s, block]); + } + }; + Prims.applyToAllStrips(['onmessage'], findReceivers); + var newthreads = []; + for (var i in receivers) { + pair = receivers[i]; + newthreads.push(ScratchJr.runtime.restartThread(pair[0], pair[1], true)); + } + strip.firstTime = false; + strip.called = newthreads; + } + + // after first time + var done = true; + for (var j = 0; j < strip.called.length; j++) { + if (strip.called[j].isRunning) { + done = false; + } + } + + if (done) { + strip.called = null; + strip.firstTime = true; + strip.thisblock = strip.thisblock.next; + strip.waitTimer = tinterval * 2; + } else { + ScratchJr.runtime.yield = true; + } + } + + static applyToAllStrips (list, fcn) { + if (!ScratchJr.stage) { + return; + } + var page = ScratchJr.stage.currentPage; + if (!page) { + return; + } + if (!page.div) { + return; + } + for (var i = 0; i < page.div.childElementCount; i++) { + var spr = page.div.childNodes[i].owner; + if (!spr) { + continue; + } + var sc = gn(spr.id + '_scripts'); + if (!sc) { + continue; + } + var topblocks = sc.owner.getBlocksType(list); + for (var j = 0; j < topblocks.length; j++) { + fcn(topblocks[j], spr); + } + } + } +} diff --git a/src/editor/engine/Sprite.js b/src/editor/engine/Sprite.js index 70d5c23..39b1bc9 100755 --- a/src/editor/engine/Sprite.js +++ b/src/editor/engine/Sprite.js @@ -863,7 +863,6 @@ Sprite.prototype.activateInput = function () { var ti = document.forms.activetextbox.typing; gn('textbox').style.visibility = 'visible'; var me = this; - Undo.aux = Project.getProject(ScratchJr.stage.currentPage.id); ti.onblur = function () { me.unfocusText(); }; diff --git a/src/editor/ui/Alert.js b/src/editor/ui/Alert.js index 99a254d..4f9a69c 100644 --- a/src/editor/ui/Alert.js +++ b/src/editor/ui/Alert.js @@ -1,66 +1,74 @@ -var Alert = function () {}; +import DrawPath from '../../utils/DrawPath'; +import {globalx, globaly, scaleMultiplier, newCanvas, + setCanvasSize, setProps, writeText, getStringSize} from '../../utils/lib'; -Alert.balloon = undefined; +let balloon = undefined; -Alert.close = function () { - if (!Alert.balloon) { - return; - } - Alert.balloon.parentNode.removeChild(Alert.balloon); - Alert.balloon = undefined; -}; - -Alert.open = function (p, obj, label, color) { - if (Alert.balloon) { - Alert.close(); - } - var scale = scaleMultiplier; - var w = 80; - var h = 24; - var dx = (globalx(obj, obj.offsetLeft) + (obj.offsetWidth / 2)) - (w + 7 * 2 + 4) * scale / 2; - var dy = globaly(obj, obj.offsetTop) - (24 * scale); - if (dy < 5 * scale) { - dy = 5 * scale; +export default class Alert { + static get balloon () { + return balloon; } - Alert.balloon = newCanvas(p, dx, dy, w, h, { - position: 'absolute', - zIndex: 2 - }); - Alert.balloon.icon = obj; - var ctx = Alert.balloon.getContext('2d'); - w = 16 + getStringSize(ctx, 'bold 14px Verdana', label).width; - if (w < 36) { - w = 36; + static close () { + if (!balloon) { + return; + } + balloon.parentNode.removeChild(balloon); + balloon = undefined; } - dx = (globalx(obj, obj.offsetLeft) + (obj.offsetWidth / 2)) - (w + 7 * 2 + 4) * scale / 2; - if (dx < 5 * scale) { - dx = 5 * scale; - } - dx = Math.floor(dx); - setCanvasSize(Alert.balloon, w, 36); - setProps(Alert.balloon.style, { - position: 'absolute', - left: dx + 'px', - zIndex: 1000, - webkitTransform: 'translate(' + (-w / 2) + 'px, ' + (-h / 2) + 'px) ' + - 'scale(' + scale + ', ' + scale + ') ' + - 'translate(' + (w / 2) + 'px, ' + (h / 2) + 'px) ' - }); - Alert.draw(Alert.balloon.getContext('2d'), 6, w, h, color); - writeText(ctx, 'bold 14px Verdana', 'white', label, 20, 8); -}; -Alert.draw = function (ctx, curve, w, h, color) { - curve = 10; - var path = new Array(['M', 0, curve], ['q', 0, -curve, curve, -curve], ['h', w - curve * 2], - ['q', curve, 0, curve, curve], ['v', h - curve * 2], ['q', 0, curve, -curve, curve], - ['h', -(w / 2) + 7 + curve], ['l', -7, 7], ['l', -7, -7], ['h', -(w / 2) + 7 + curve], - ['q', -curve, 0, -curve, -curve], ['Z'] - ); - ctx.clearRect(0, 0, Math.max(ctx.canvas.width, w), Math.max(ctx.canvas.height, h)); - ctx.fillStyle = color; - ctx.beginPath(); - DrawPath.render(ctx, path); - ctx.fill(); -}; + static open (p, obj, label, color) { + if (balloon) { + Alert.close(); + } + var scale = scaleMultiplier; + var w = 80; + var h = 24; + var dx = (globalx(obj, obj.offsetLeft) + (obj.offsetWidth / 2)) - (w + 7 * 2 + 4) * scale / 2; + var dy = globaly(obj, obj.offsetTop) - (24 * scale); + if (dy < 5 * scale) { + dy = 5 * scale; + } + + balloon = newCanvas(p, dx, dy, w, h, { + position: 'absolute', + zIndex: 2 + }); + balloon.icon = obj; + var ctx = balloon.getContext('2d'); + w = 16 + getStringSize(ctx, 'bold 14px Verdana', label).width; + if (w < 36) { + w = 36; + } + dx = (globalx(obj, obj.offsetLeft) + (obj.offsetWidth / 2)) - (w + 7 * 2 + 4) * scale / 2; + if (dx < 5 * scale) { + dx = 5 * scale; + } + dx = Math.floor(dx); + setCanvasSize(balloon, w, 36); + setProps(balloon.style, { + position: 'absolute', + left: dx + 'px', + zIndex: 1000, + webkitTransform: 'translate(' + (-w / 2) + 'px, ' + (-h / 2) + 'px) ' + + 'scale(' + scale + ', ' + scale + ') ' + + 'translate(' + (w / 2) + 'px, ' + (h / 2) + 'px) ' + }); + Alert.draw(balloon.getContext('2d'), 6, w, h, color); + writeText(ctx, 'bold 14px Verdana', 'white', label, 20, 8); + } + + static draw (ctx, curve, w, h, color) { + curve = 10; + var path = new Array(['M', 0, curve], ['q', 0, -curve, curve, -curve], ['h', w - curve * 2], + ['q', curve, 0, curve, curve], ['v', h - curve * 2], ['q', 0, curve, -curve, curve], + ['h', -(w / 2) + 7 + curve], ['l', -7, 7], ['l', -7, -7], ['h', -(w / 2) + 7 + curve], + ['q', -curve, 0, -curve, -curve], ['Z'] + ); + ctx.clearRect(0, 0, Math.max(ctx.canvas.width, w), Math.max(ctx.canvas.height, h)); + ctx.fillStyle = color; + ctx.beginPath(); + DrawPath.render(ctx, path); + ctx.fill(); + } +} diff --git a/src/editor/ui/Grid.js b/src/editor/ui/Grid.js index a990949..a37d2e3 100644 --- a/src/editor/ui/Grid.js +++ b/src/editor/ui/Grid.js @@ -2,264 +2,276 @@ // Stage grid ////////////////////////////// -var Grid = function () {}; +import ScratchJr from '../ScratchJr'; +import Events from '../../utils/Events'; +import Localization from '../../utils/Localization'; +import {gn, scaleMultiplier, isTablet, newDiv, setProps, newP, newCanvas} from '../../utils/lib'; -Grid.width = 482; -Grid.height = 362; -Grid.size = 24; +let width = 482; +let height = 362; +let size = 24; +let hidden = true; -Grid.hidden = true; - -Grid.init = function (div) { - var w = div.offsetWidth; - var h = div.offsetHeight; - var grid = newDiv(div, 0, 0, Grid.width, Grid.height, { - position: 'absolute', - zIndex: ScratchJr.layerTop - }); - Grid.setScaleAndPosition(grid, scaleMultiplier, 47, 75, Grid.width, Grid.height); - grid.setAttribute('id', 'livegrid'); - Grid.drawLines(grid, Grid.width, Grid.height); - Grid.createNumbering(w, h); - Grid.createCursor(); - Grid.createYcursor(); - Grid.createXcursor(); -}; - -Grid.setScaleAndPosition = function (grid, scale, x, y, w, h) { - setProps(grid.style, { - webkitTransform: 'translate(' + (-w / 2) + 'px, ' + (-h / 2) + 'px) ' + - 'scale(' + scale + ') ' + - 'translate(' + (w / 2 + x) + 'px, ' + (h / 2 + y) + 'px)' - }); -}; - -Grid.drawLines = function (grid, w, h) { - var cnv = newCanvas(grid, 0, 0, w, h, { - position: 'absolute' - }); - cnv.style.opacity = 0.5; - var ctx = cnv.getContext('2d'); - ctx.strokeStyle = '#B3B3B3'; - ctx.lineWidth = 1; - var dx = Grid.size; - // vertical - for (var i = 0; i < 480 / Grid.size; i++) { - ctx.moveTo(dx, 0); - ctx.lineTo(dx, 360); - ctx.stroke(); - dx += Grid.size; +export default class Grid { + static get size () { + return size; } - var dy = Grid.size; - // horizontal - for (i = 0; i < 360 / Grid.size; i++) { - ctx.moveTo(0, dy); - ctx.lineTo(480, dy); - ctx.stroke(); - dy += Grid.size; - } - if (isTablet) { - cnv.ontouchstart = function (evt) { - ScratchJr.stage.mouseDown(evt); - }; - } else { - cnv.onmousedown = function (evt) { - ScratchJr.stage.mouseDown(evt); - }; - } -}; -Grid.createNumbering = function (w, h) { - var row = newDiv(gn('stageframe'), 0, 0, w - 46 - 30, 24, { - position: 'absolute', - zIndex: ScratchJr.layerTop - }); - row.setAttribute('id', 'rownum'); - Grid.setScaleAndPosition(row, scaleMultiplier, 46 - 24, 75 + Grid.height, w - 46 - 30, 24); - var offset = Grid.size; - var dx = offset; - for (var i = 0; i < 480 / offset; i++) { - var num = newDiv(row, dx, 0, Grid.size, Grid.size, { + static get hidden () { + return hidden; + } + + static init (div) { + var w = div.offsetWidth; + var h = div.offsetHeight; + var grid = newDiv(div, 0, 0, width, height, { position: 'absolute', - zIndex: 10 + zIndex: ScratchJr.layerTop }); - var p = newP(num, Localization.localize('GRID_NUMBER', { - N: (i + 1) - }), {}); - p.setAttribute('class', 'stylelabel'); - dx += offset; + Grid.setScaleAndPosition(grid, scaleMultiplier, 47, 75, width, height); + grid.setAttribute('id', 'livegrid'); + Grid.drawLines(grid, width, height); + Grid.createNumbering(w, h); + Grid.createCursor(); + Grid.createYcursor(); + Grid.createXcursor(); } - var column = newDiv(gn('stageframe'), 0, 0, 24, h + 24, { - position: 'absolute', - zIndex: ScratchJr.layerTop - }); - column.setAttribute('id', 'colnum'); - Grid.setScaleAndPosition(column, scaleMultiplier, 46 - 24, 74 + 1, 24, h + 24); - var dy = 360 - offset; - for (var j = 0; j < 360 / offset; j++) { - var numj = newDiv(column, 0, dy, Grid.size, Grid.size, { + + static setScaleAndPosition (grid, scale, x, y, w, h) { + setProps(grid.style, { + webkitTransform: 'translate(' + (-w / 2) + 'px, ' + (-h / 2) + 'px) ' + + 'scale(' + scale + ') ' + + 'translate(' + (w / 2 + x) + 'px, ' + (h / 2 + y) + 'px)' + }); + } + + static drawLines (grid, w, h) { + var cnv = newCanvas(grid, 0, 0, w, h, { + position: 'absolute' + }); + cnv.style.opacity = 0.5; + var ctx = cnv.getContext('2d'); + ctx.strokeStyle = '#B3B3B3'; + ctx.lineWidth = 1; + var dx = size; + // vertical + for (var i = 0; i < 480 / size; i++) { + ctx.moveTo(dx, 0); + ctx.lineTo(dx, 360); + ctx.stroke(); + dx += size; + } + var dy = size; + // horizontal + for (i = 0; i < 360 / size; i++) { + ctx.moveTo(0, dy); + ctx.lineTo(480, dy); + ctx.stroke(); + dy += size; + } + if (isTablet) { + cnv.ontouchstart = function (evt) { + ScratchJr.stage.mouseDown(evt); + }; + } else { + cnv.onmousedown = function (evt) { + ScratchJr.stage.mouseDown(evt); + }; + } + } + + static createNumbering (w, h) { + var row = newDiv(gn('stageframe'), 0, 0, w - 46 - 30, 24, { position: 'absolute', - zIndex: 10 + zIndex: ScratchJr.layerTop }); - var py = newP(numj, Localization.localize('GRID_NUMBER', { - N: j + 1 - }), {}); - py.setAttribute('class', 'stylelabel'); - dy -= offset; - } -}; - -Grid.createYcursor = function () { - var num = newDiv(gn('colnum'), 0, 0, Grid.size, Grid.size, { - position: 'absolute', - zIndex: 20 - }); - num.setAttribute('class', 'circle'); - num.style.background = '#6a99c1'; - num.setAttribute('id', 'ycursor'); - var p = newP(num, 15, {}); - p.setAttribute('class', 'circlenum'); -}; - -Grid.createXcursor = function () { - var num = newDiv(gn('rownum'), Grid.size, 0, Grid.size, Grid.size, { - position: 'absolute', - zIndex: 20 - }); - num.setAttribute('class', 'circle'); - num.style.background = '#6a99c1'; - num.setAttribute('id', 'xcursor'); - var p = newP(num, 1, {}); - p.setAttribute('class', 'circlenum'); -}; - -Grid.createCursor = function () { - var gc = newDiv(gn('livegrid'), 0, 0, Grid.size + 2, Grid.size + 2, { - position: 'absolute', - zIndex: ScratchJr.layerAboveBottom - }); - gc.setAttribute('id', 'circlenum'); - var cnv = newCanvas(gc, 0, 0, Grid.size + 2, Grid.size + 2, { - position: 'absolute' - }); - if (isTablet) { - cnv.ontouchstart = function (evt) { - Grid.mouseDownOnCursor(evt); - }; - } else { - cnv.onmousedown = function (evt) { - Grid.mouseDownOnCursor(evt); - }; - } - var ctx = cnv.getContext('2d'); - ctx.globalAlpha = 0.5; - ctx.fillStyle = '#28A5DA'; - ctx.strokeStyle = '#656e73'; - ctx.lineWidth = 3; - ctx.strokeRect(3, 3, Grid.size - 6, Grid.size - 6); - ctx.fillRect(3, 3, Grid.size - 6, Grid.size - 6); - if (isTablet) { - gc.ontouchstart = Grid.mouseDownOnCursor; - } else { - gc.onmousedown = Grid.mouseDownOnCursor; - } -}; - -Grid.mouseDownOnCursor = function (e) { - e.preventDefault(); - e.stopPropagation(); - var pt = ScratchJr.stage.getStagePt(e); - var spr = ScratchJr.getSprite(); - ScratchJr.stage.initialPoint = { - x: pt.x, - y: pt.y - }; - Events.dragthumbnail = spr.div; - Events.clearEvents(); - if (!ScratchJr.inFullscreen && spr) { - Events.holdit(spr.div, ScratchJr.stage.startShaking); - } - ScratchJr.stage.setEvents(); -}; - -Grid.updateCursor = function () { - if (Grid.hidden) { - return; - } - if (ScratchJr.inFullscreen) { - return; - } - if (!ScratchJr.stage.currentPage) { - return; - } - if (!ScratchJr.getSprite()) { - gn('circlenum').style.visibility = 'hidden'; - gn('xcursor').style.visibility = 'hidden'; - gn('ycursor').style.visibility = 'hidden'; - return; - } - var spr = gn(ScratchJr.stage.currentPage.currentSpriteName); - if (!spr) { - return; - } - var obj = spr.owner; - var c = gn('circlenum'); - if (!c) { - return; - } - var dx = obj.xcoor + Grid.size / 2; - var dy = obj.ycoor - Grid.size / 2; - gn('xcursor').style.visibility = 'visible'; - gn('ycursor').style.visibility = 'visible'; - gn('circlenum').style.visibility = 'visible'; - Grid.setCursorsValues(dx, dy); -}; - -Grid.setCursorsValues = function (dx, dy) { - var c = gn('circlenum'); - var numX = Math.round(dx / Grid.size); - var numY = Math.round(dy / Grid.size); - if (c.offsetLeft != (numX * 24)) { - var xc = gn('xcursor'); - var xstate = ((numX < 1) || (numX > 20)) ? 'hidden' : 'visible'; - setProps(xc.style, { + row.setAttribute('id', 'rownum'); + Grid.setScaleAndPosition(row, scaleMultiplier, 46 - 24, 75 + height, w - 46 - 30, 24); + var offset = size; + var dx = offset; + for (var i = 0; i < 480 / offset; i++) { + var num = newDiv(row, dx, 0, size, size, { + position: 'absolute', + zIndex: 10 + }); + var p = newP(num, Localization.localize('GRID_NUMBER', { + N: (i + 1) + }), {}); + p.setAttribute('class', 'stylelabel'); + dx += offset; + } + var column = newDiv(gn('stageframe'), 0, 0, 24, h + 24, { position: 'absolute', - left: (numX * 24) + 'px', - visibility: xstate - }); - xc.childNodes[0].textContent = Localization.localize('GRID_NUMBER', { - N: numX + zIndex: ScratchJr.layerTop }); + column.setAttribute('id', 'colnum'); + Grid.setScaleAndPosition(column, scaleMultiplier, 46 - 24, 74 + 1, 24, h + 24); + var dy = 360 - offset; + for (var j = 0; j < 360 / offset; j++) { + var numj = newDiv(column, 0, dy, size, size, { + position: 'absolute', + zIndex: 10 + }); + var py = newP(numj, Localization.localize('GRID_NUMBER', { + N: j + 1 + }), {}); + py.setAttribute('class', 'stylelabel'); + dy -= offset; + } } - if (c.offsetTop != (numY * 24)) { - var yc = gn('ycursor'); - var ystate = ((numY < 0) || (numY > 14)) ? 'hidden' : 'visible'; - setProps(yc.style, { + + static createYcursor () { + var num = newDiv(gn('colnum'), 0, 0, size, size, { + position: 'absolute', + zIndex: 20 + }); + num.setAttribute('class', 'circle'); + num.style.background = '#6a99c1'; + num.setAttribute('id', 'ycursor'); + var p = newP(num, 15, {}); + p.setAttribute('class', 'circlenum'); + } + + static createXcursor () { + var num = newDiv(gn('rownum'), size, 0, size, size, { + position: 'absolute', + zIndex: 20 + }); + num.setAttribute('class', 'circle'); + num.style.background = '#6a99c1'; + num.setAttribute('id', 'xcursor'); + var p = newP(num, 1, {}); + p.setAttribute('class', 'circlenum'); + } + + static createCursor () { + var gc = newDiv(gn('livegrid'), 0, 0, size + 2, size + 2, { + position: 'absolute', + zIndex: ScratchJr.layerAboveBottom + }); + gc.setAttribute('id', 'circlenum'); + var cnv = newCanvas(gc, 0, 0, size + 2, size + 2, { + position: 'absolute' + }); + if (isTablet) { + cnv.ontouchstart = function (evt) { + Grid.mouseDownOnCursor(evt); + }; + } else { + cnv.onmousedown = function (evt) { + Grid.mouseDownOnCursor(evt); + }; + } + var ctx = cnv.getContext('2d'); + ctx.globalAlpha = 0.5; + ctx.fillStyle = '#28A5DA'; + ctx.strokeStyle = '#656e73'; + ctx.lineWidth = 3; + ctx.strokeRect(3, 3, size - 6, size - 6); + ctx.fillRect(3, 3, size - 6, size - 6); + if (isTablet) { + gc.ontouchstart = Grid.mouseDownOnCursor; + } else { + gc.onmousedown = Grid.mouseDownOnCursor; + } + } + + static mouseDownOnCursor (e) { + e.preventDefault(); + e.stopPropagation(); + var pt = ScratchJr.stage.getStagePt(e); + var spr = ScratchJr.getSprite(); + ScratchJr.stage.initialPoint = { + x: pt.x, + y: pt.y + }; + Events.dragthumbnail = spr.div; + Events.clearEvents(); + if (!ScratchJr.inFullscreen && spr) { + Events.holdit(spr.div, ScratchJr.stage.startShaking); + } + ScratchJr.stage.setEvents(); + } + + static updateCursor () { + if (hidden) { + return; + } + if (ScratchJr.inFullscreen) { + return; + } + if (!ScratchJr.stage.currentPage) { + return; + } + if (!ScratchJr.getSprite()) { + gn('circlenum').style.visibility = 'hidden'; + gn('xcursor').style.visibility = 'hidden'; + gn('ycursor').style.visibility = 'hidden'; + return; + } + var spr = gn(ScratchJr.stage.currentPage.currentSpriteName); + if (!spr) { + return; + } + var obj = spr.owner; + var c = gn('circlenum'); + if (!c) { + return; + } + var dx = obj.xcoor + size / 2; + var dy = obj.ycoor - size / 2; + gn('xcursor').style.visibility = 'visible'; + gn('ycursor').style.visibility = 'visible'; + gn('circlenum').style.visibility = 'visible'; + Grid.setCursorsValues(dx, dy); + } + + static setCursorsValues (dx, dy) { + var c = gn('circlenum'); + var numX = Math.round(dx / size); + var numY = Math.round(dy / size); + if (c.offsetLeft != (numX * 24)) { + var xc = gn('xcursor'); + var xstate = ((numX < 1) || (numX > 20)) ? 'hidden' : 'visible'; + setProps(xc.style, { + position: 'absolute', + left: (numX * 24) + 'px', + visibility: xstate + }); + xc.childNodes[0].textContent = Localization.localize('GRID_NUMBER', { + N: numX + }); + } + if (c.offsetTop != (numY * 24)) { + var yc = gn('ycursor'); + var ystate = ((numY < 0) || (numY > 14)) ? 'hidden' : 'visible'; + setProps(yc.style, { + position: 'absolute', + top: (numY * 24) + 'px', + visibility: ystate + }); + yc.childNodes[0].textContent = Localization.localize('GRID_NUMBER', { + N: 15 - numY + }); + } + setProps(c.style, { position: 'absolute', top: (numY * 24) + 'px', - visibility: ystate - }); - yc.childNodes[0].textContent = Localization.localize('GRID_NUMBER', { - N: 15 - numY + left: ((numX - 1) * 24) + 'px' }); } - setProps(c.style, { - position: 'absolute', - top: (numY * 24) + 'px', - left: ((numX - 1) * 24) + 'px' - }); -}; -Grid.hide = function (b) { - Grid.hidden = b; - var mystate = Grid.hidden ? 'hidden' : 'visible'; - gn('livegrid').style.visibility = mystate; - gn('rownum').style.visibility = mystate; - gn('colnum').style.visibility = mystate; - if (ScratchJr.stage.currentPage) { - mystate = !ScratchJr.getSprite() ? 'hidden' : mystate; + static hide (b) { + hidden = b; + var mystate = hidden ? 'hidden' : 'visible'; + gn('livegrid').style.visibility = mystate; + gn('rownum').style.visibility = mystate; + gn('colnum').style.visibility = mystate; + if (ScratchJr.stage.currentPage) { + mystate = !ScratchJr.getSprite() ? 'hidden' : mystate; + } + gn('circlenum').style.visibility = mystate; + gn('xcursor').style.visibility = mystate; + gn('ycursor').style.visibility = mystate; } - gn('circlenum').style.visibility = mystate; - gn('xcursor').style.visibility = mystate; - gn('ycursor').style.visibility = mystate; -}; +} diff --git a/src/editor/ui/Library.js b/src/editor/ui/Library.js index fef4ced..6017974 100644 --- a/src/editor/ui/Library.js +++ b/src/editor/ui/Library.js @@ -1,552 +1,550 @@ -var Library = function () {}; -Library.selectedOne; -Library.currentProject; -Library.activeFocus; -Library.nativeJr = true; -Library.clickThumb; -Library.shaking; -Library.type; -Library.timeoutEvent; -Library.spacing = 64; -Library.fw = 220; -Library.pad; -Library.thumbnailw = 136; -Library.target = undefined; -Library.scrolltop = 0; -Library.frame; -Library.ready = false; +import ScratchJr from '../ScratchJr'; +import iOS from '../../iPad/iOS'; +import IO from '../../iPad/IO'; +import Paint from '../../painteditor/Paint'; +import Events from '../../utils/Events'; +import Localization from '../../utils/Localization'; +import ScratchAudio from '../../utils/ScratchAudio'; +import {frame, gn, newHTML, scaleMultiplier, + getDocumentWidth, getDocumentHeight, setProps, newCanvas} from '../../utils/lib'; -Library.init = function () { - Library.frame = document.getElementById('libframe'); - Library.frame.style.minHeight = Math.max(getDocumentHeight(), frame.offsetHeight) + 'px'; - var topbar = newHTML('div', 'topbar', Library.frame); - topbar.setAttribute('id', 'topbar'); - var actions = newHTML('div', 'actions', topbar); - actions.setAttribute('id', 'libactions'); - var ascontainer = newHTML('div', 'assetname-container', topbar); - var as = newHTML('div', 'assetname', ascontainer); - var myname = newHTML('p', undefined, as); - myname.setAttribute('id', 'assetname'); - myname.textContent = ''; - Library.layoutHeader(); -}; +let selectedOne; +let nativeJr = true; +let clickThumb; +let shaking; +let type; +let timeoutEvent; +let frame; -Library.createScrollPanel = function () { - var inner = newHTML('div', 'innerlibrary', Library.frame); - inner.setAttribute('id', 'asssetsview'); - var div = newHTML('div', 'scrollarea', inner); - div.setAttribute('id', 'scrollarea'); -}; - -Library.open = function (type) { - Library.ready = false; - Library.type = type; - gn('assetname').textContent = ''; - Library.nativeJr = true; - Library.target = undefined; - Library.pad = undefined; - frame.style.display = 'none'; - Library.frame.className = 'libframe appear'; - Library.frame.focus(); - Library.selectedOne = undefined; - gn('okbut').ontouchstart = (Library.type == 'costumes') ? Library.closeSpriteSelection : Library.closeBkgSelection; - Library.clean(); - Library.createScrollPanel(); - Library.addThumbnails(type); - window.ontouchstart = undefined; - window.ontouchend = undefined; - document.ontouchmove = undefined; - window.onresize = undefined; - - gn('library_paintme').style.opacity = 1; - gn('library_paintme').ontouchstart = Library.editResource; - - // Set the back button callback - ScratchJr.onBackButtonCallback.push(function () { - var e = document.createEvent('TouchEvent'); - e.initTouchEvent(); - Library.cancelPick(e); - }); -}; - -Library.clean = function () { - if (gn('scrollarea')) { - var div = gn('scrollarea').parentNode; - Library.frame.removeChild(div); +export default class Library { + static init () { + frame = document.getElementById('libframe'); + frame.style.minHeight = Math.max(getDocumentHeight(), frame.offsetHeight) + 'px'; + var topbar = newHTML('div', 'topbar', frame); + topbar.setAttribute('id', 'topbar'); + var actions = newHTML('div', 'actions', topbar); + actions.setAttribute('id', 'libactions'); + var ascontainer = newHTML('div', 'assetname-container', topbar); + var as = newHTML('div', 'assetname', ascontainer); + var myname = newHTML('p', undefined, as); + myname.setAttribute('id', 'assetname'); + myname.textContent = ''; + Library.layoutHeader(); } -}; -Library.close = function (e) { - e.preventDefault(); - e.stopPropagation(); - ScratchAudio.sndFX('tap.wav'); - ScratchJr.blur(); - Library.frame.className = 'libframe disappear'; - document.body.scrollTop = 0; - frame.style.display = 'block'; - ScratchJr.editorEvents(); - ScratchJr.onBackButtonCallback.pop(); -}; - -Library.layoutHeader = function () { - var buttons = newHTML('div', 'bkgbuttons', gn('libactions')); - var paintme = newHTML('div', 'painticon', buttons); - paintme.id = 'library_paintme'; - paintme.ontouchstart = Library.editResource; - var okbut = newHTML('div', 'okicon', buttons); - okbut.setAttribute('id', 'okbut'); - var cancelbut = newHTML('div', 'cancelicon', buttons); - cancelbut.ontouchstart = Library.cancelPick; -}; - -Library.cancelPick = function (e) { - ScratchJr.onHold = true; - Library.close(e); - setTimeout(function () { - ScratchJr.onHold = false; - }, 1000); -}; - -Library.addThumbnails = function () { - var div = gn('scrollarea'); - Library.addEmptyThumb(div, (Library.type == 'costumes') ? (118 * scaleMultiplier) : (120 * scaleMultiplier), - (Library.type == 'costumes') ? (90 * scaleMultiplier) : (90 * scaleMultiplier)); - var key = (Library.type == 'costumes') ? 'usershapes' : 'userbkgs'; - // Student' assets - var json = {}; - json.cond = 'ext = ? AND version = ?'; - json.items = ((Library.type == 'costumes') ? - ['md5', 'altmd5', 'name', 'scale', 'width', 'height'] : ['altmd5', 'md5', 'width', 'height']); - json.values = ['svg', ScratchJr.version]; - json.order = 'ctime desc'; - IO.query(key, json, Library.displayAssets); -}; - -Library.skipUserAssets = function () { - var div = gn('scrollarea'); - Library.addEmptyThumb(div, (Library.type == 'costumes') ? (118 * scaleMultiplier) : (120 * scaleMultiplier), - (Library.type == 'costumes') ? (90 * scaleMultiplier) : (90 * scaleMultiplier)); - Library.addHR(div); - Library.displayLibAssets((Library.type == 'costumes') ? MediaLib.sprites : MediaLib.backgrounds); -}; - -Library.getpadding = function (div) { - var w = Math.min(getDocumentWidth(), Library.frame.offsetWidth); - var dw = div.childNodes[1].offsetLeft - div.childNodes[0].offsetLeft; - var qty = Math.floor(w / dw); - Library.thumbnailw = dw + 10; - var pad = Math.floor((w - (qty * dw)) / 2); - if (pad < 10) { - return Math.floor((w - ((qty - 1) * dw)) / 2); + static createScrollPanel () { + var inner = newHTML('div', 'innerlibrary', frame); + inner.setAttribute('id', 'asssetsview'); + var div = newHTML('div', 'scrollarea', inner); + div.setAttribute('id', 'scrollarea'); } - return pad; -}; -Library.displayAssets = function (str) { - Library.nativeJr = true; - var div = gn('scrollarea'); - var data = JSON.parse(str); - if (data.length > 0) { - for (var i = 0; i < data.length; i++) { - Library.addAssetThumbChoose(div, data[i], 120 * scaleMultiplier, 90 * scaleMultiplier, - Library.selectAsset); - } - } - Library.addHR(div); - Library.nativeJr = false; - data = (Library.type == 'costumes') ? MediaLib.sprites : MediaLib.backgrounds; - Library.displayLibAssets(data); -}; + static open (type) { + type = type; + gn('assetname').textContent = ''; + nativeJr = true; + frame.style.display = 'none'; + frame.className = 'libframe appear'; + frame.focus(); + selectedOne = undefined; + gn('okbut').ontouchstart = (type == 'costumes') ? Library.closeSpriteSelection : Library.closeBkgSelection; + Library.clean(); + Library.createScrollPanel(); + Library.addThumbnails(type); + window.ontouchstart = undefined; + window.ontouchend = undefined; + document.ontouchmove = undefined; + window.onresize = undefined; -Library.displayLibAssets = function (data) { - var div = gn('scrollarea'); - if (data.length < 1) { - return; - } - var order = data[0].order; - var key = order ? order.split(',')[1] : ''; - for (var i = 0; i < data.length; i++) { - order = data[i].order; - var key2 = order ? order.split(',')[1] : ''; - if (key2 != key) { - Library.addHR(div); - key = key2; - } - if ('separator' in data[i]) { - Library.addHR(div); - } else { - Library.addLocalThumbChoose(div, data[i], 120 * scaleMultiplier, 90 * scaleMultiplier, Library.selectAsset); - } - } - Library.ready = true; -}; + gn('library_paintme').style.opacity = 1; + gn('library_paintme').ontouchstart = Library.editResource; -Library.addAssetThumbChoose = function (parent, aa, w, h, fcn) { - var data = Library.parseAssetData(aa); - var tb = document.createElement('div'); - parent.appendChild(tb); - tb.byme = Library.nativeJr ? 1 : 0; - var md5 = data.md5; - tb.setAttribute('class', 'assetbox off'); - tb.setAttribute('id', md5); - tb.scale = (!data.scale) ? 0.5 : data.scale; - tb.fieldname = data.name; - tb.w = Number(data.width); - tb.h = Number(data.height); - var scale = Math.min(w / tb.w, h / tb.h); - var img = newHTML('img', undefined, tb); - img.style.left = (9 * scaleMultiplier) + 'px'; - img.style.top = (7 * scaleMultiplier) + 'px'; - img.style.position = 'relative'; - img.style.height = (data.height * scale) + 'px'; - if (data.altmd5) { - IO.getAsset(data.altmd5, drawMe); - } - function drawMe (dataurl) { - img.src = dataurl; - } - tb.ontouchstart = function (evt) { - fcn(evt, tb); - }; - return tb; -}; - -Library.addLocalThumbChoose = function (parent, data, w, h, fcn) { - var tb = newHTML('div', 'assetbox off', parent); - var md5 = data.md5; - tb.byme = Library.nativeJr ? 1 : 0; - tb.setAttribute('id', md5); - tb.scale = (!data.scale) ? 0.5 : data.scale; - tb.fieldname = data.name; - tb.w = Number(data.width); - tb.h = Number(data.height); - - var img = newHTML('img', undefined, tb); - var scale = Math.min(w / tb.w, h / tb.h); - img.style.height = tb.h * scale + 'px'; - img.style.width = tb.w * scale + 'px'; - - img.style.left = Math.floor(((w - (scale * tb.w)) / 2) + (9 * scaleMultiplier)) + 'px'; - img.style.top = Math.floor(((h - (scale * tb.h)) / 2) + (9 * scaleMultiplier)) + 'px'; - img.style.position = 'relative'; - - // Cached downsized-thumbnails are in pnglibrary - var pngPath = MediaLib.path.replace('svg', 'png'); - img.src = pngPath + IO.getFilename(md5) + '.png'; - - tb.ontouchstart = function (evt) { - fcn(evt, tb); - }; - return tb; -}; - -Library.userAssetThumbnail = function (img, cnv, sizew, sizeh) { - var scale = Math.min(sizew / img.width, sizeh / img.height); - var currentCtx = cnv.getContext('2d'); - var iw = Math.floor(scale * img.width); - var ih = Math.floor(scale * img.height); - var ix = Math.floor((sizew - (scale * img.width)) / 2); - var iy = Math.floor((sizeh - (scale * img.height)) / 2); - currentCtx.drawImage(img, 0, 0, img.width, img.height, ix, iy, iw, ih); -}; - -Library.addEmptyThumb = function (parent, w, h) { - var tb = document.createElement('div'); - tb.setAttribute('class', 'assetbox off'); - tb.setAttribute('id', 'none'); - tb.fieldname = ((Library.type == 'costumes') ? - Localization.localize('LIBRARY_CHARACTER') : Localization.localize('LIBRARY_BACKGROUND')); - tb.byme = 1; - var cnv = newCanvas(tb, 9 * scaleMultiplier, 7 * scaleMultiplier, w, h, { - position: 'relative' - }); - var ctx = cnv.getContext('2d'); - ctx.fillStyle = ScratchJr.stagecolor; - ctx.fillRect(0, 0, w, h); - parent.appendChild(tb); - tb.ontouchstart = function (evt) { - Library.selectAsset(evt, tb); - }; -}; - -Library.addHR = function (div) { - var hr = document.createElement('hr'); - div.appendChild(hr); - hr.setAttribute('class', 'bigdivide'); -}; - -/////////////////////////// -//selection - - -Library.selectAsset = function (e, tb) { - tb.pt = JSON.stringify(Events.getTargetPoint(e)); - if (Library.shaking && (e.target.className == 'deleteasset')) { - Library.removeFromAssetList(); - return; - } else if (Library.shaking) { - Library.stopShaking(); - } - if (tb.byme && (tb.id != 'none')) { - holdit(tb); - } - tb.ontouchend = function (evt) { - clickMe(evt, tb); - }; - window.onmouseup = function (evt) { - clickMe(evt, tb); - }; - window.onmousemove = function (evt) { - clearEvents(evt, tb); - }; - function holdit () { - var repeat = function () { - tb.ontouchend = undefined; - window.onmouseup = undefined; - window.onmousemove = undefined; - Library.timeoutEvent = undefined; - Library.stopShaking(); - Library.shaking = tb; - Library.clearAllSelections(); - Library.startShaking(tb); - }; - Library.timeoutEvent = setTimeout(repeat, 500); - } - function clearEvents (e, tb) { - var pt = Events.getTargetPoint(e); - var pt2 = JSON.parse(tb.pt); - if (Library.distance(pt, pt2) < 30) { - return; - } - e.preventDefault(); - if (Library.timeoutEvent) { - clearTimeout(Library.timeoutEvent); - } - if (Library.clickThumb) { - Library.unSelect(Library.clickThumb); - } - Library.timeoutEvent = undefined; - tb.ontouchend = undefined; - window.onmouseup = function () { - window.onmousemove = undefined; - window.onmouseup = undefined; - }; - } - function clickMe (e, tb) { - if (Library.timeoutEvent) { - clearTimeout(Library.timeoutEvent); - } - Library.selectThisAsset(e, tb); - Library.timeoutEvent = undefined; - tb.ontouchend = undefined; - tb.onmouseup = undefined; - window.onmousemove = undefined; - window.onmouseup = undefined; - } -}; - -Library.startShaking = function (b) { - b.className = b.className + ' shakeme'; - newHTML('div', 'deleteasset', b); - Library.shaking = b; -}; - -Library.stopShaking = function () { - if (!Library.shaking) { - return; - } - var b = Library.shaking; - b.setAttribute('class', 'assetbox off'); - var ic = b.childNodes[b.childElementCount - 1]; - if (ic.getAttribute('class') == 'deleteasset') { - b.removeChild(ic); - } - Library.shaking = undefined; -}; - -Library.removeFromAssetList = function () { - ScratchAudio.sndFX('cut.wav'); - var b = Library.shaking; - b.parentNode.removeChild(b); - var key = (Library.type == 'costumes') ? 'usershapes' : 'userbkgs'; - var json = {}; - json.cond = 'md5 = ?'; - json.items = ['*']; - json.values = [b.id]; - IO.query(key, json, Library.removeAssetFromLib); - Library.clickThumb = undefined; - Library.selectedOne = undefined; - return true; -}; - -// Determine if an asset thumbnail is unique -// md5: thumbnail md5 to determine uniqueness -// type: "costumes" or "backgrounds" -// callback: called with true if unique, false if duplicate exists -Library.assetThumbnailUnique = function (md5, type, callback) { - var key = (type == 'costumes') ? 'usershapes' : 'userbkgs'; - var json = {}; - json.cond = 'ext = ? AND altmd5 = ?'; - json.items = ['md5', 'altmd5']; - json.values = ['svg', md5]; - json.order = 'ctime desc'; - IO.query(key, json, function (results) { - results = JSON.parse(results); - callback(results.length <= 1); - }); -}; - -Library.removeAssetFromLib = function (str) { - var key = (Library.type == 'costumes') ? 'usershapes' : 'userbkgs'; - var aa = JSON.parse(str)[0]; - var data = Library.parseAssetData(aa); - - if (data.altmd5) { - // Removes the thumbnail for the asset. - // First ensure that there aren't other characters/bgs using the same thumb - // (this is possible if we receive a duplicate project, for example) - Library.assetThumbnailUnique(data.altmd5, Library.type, function (isUnique) { - if (isUnique) { - iOS.remove(data.altmd5, iOS.trace); - } + // Set the back button callback + ScratchJr.onBackButtonCallback.push(function () { + var e = document.createEvent('TouchEvent'); + e.initTouchEvent(); + Library.cancelPick(e); }); } - IO.deleteobject(key, data.id, iOS.trace); -}; - -Library.parseAssetData = function (data) { - var res = new Object(); - for (var key in data) { - res[key.toLowerCase()] = data[key]; + static clean () { + if (gn('scrollarea')) { + var div = gn('scrollarea').parentNode; + frame.removeChild(div); + } } - return res; -}; -Library.selectThisAsset = function (e, tb) { - if (tb.id == Library.selectedOne) { - if (Library.type == 'costumes') { - Library.closeSpriteSelection(e); + static close (e) { + e.preventDefault(); + e.stopPropagation(); + ScratchAudio.sndFX('tap.wav'); + ScratchJr.blur(); + frame.className = 'libframe disappear'; + document.body.scrollTop = 0; + frame.style.display = 'block'; + ScratchJr.editorEvents(); + ScratchJr.onBackButtonCallback.pop(); + } + + static layoutHeader () { + var buttons = newHTML('div', 'bkgbuttons', gn('libactions')); + var paintme = newHTML('div', 'painticon', buttons); + paintme.id = 'library_paintme'; + paintme.ontouchstart = Library.editResource; + var okbut = newHTML('div', 'okicon', buttons); + okbut.setAttribute('id', 'okbut'); + var cancelbut = newHTML('div', 'cancelicon', buttons); + cancelbut.ontouchstart = Library.cancelPick; + } + + static cancelPick (e) { + ScratchJr.onHold = true; + Library.close(e); + setTimeout(function () { + ScratchJr.onHold = false; + }, 1000); + } + + static addThumbnails () { + var div = gn('scrollarea'); + Library.addEmptyThumb(div, (type == 'costumes') ? (118 * scaleMultiplier) : (120 * scaleMultiplier), + (type == 'costumes') ? (90 * scaleMultiplier) : (90 * scaleMultiplier)); + var key = (type == 'costumes') ? 'usershapes' : 'userbkgs'; + // Student' assets + var json = {}; + json.cond = 'ext = ? AND version = ?'; + json.items = ((type == 'costumes') ? + ['md5', 'altmd5', 'name', 'scale', 'width', 'height'] : ['altmd5', 'md5', 'width', 'height']); + json.values = ['svg', ScratchJr.version]; + json.order = 'ctime desc'; + IO.query(key, json, Library.displayAssets); + } + + static skipUserAssets () { + var div = gn('scrollarea'); + Library.addEmptyThumb(div, (type == 'costumes') ? (118 * scaleMultiplier) : (120 * scaleMultiplier), + (type == 'costumes') ? (90 * scaleMultiplier) : (90 * scaleMultiplier)); + Library.addHR(div); + Library.displayLibAssets((type == 'costumes') ? MediaLib.sprites : MediaLib.backgrounds); + } + + static getpadding (div) { + var w = Math.min(getDocumentWidth(), frame.offsetWidth); + var dw = div.childNodes[1].offsetLeft - div.childNodes[0].offsetLeft; + var qty = Math.floor(w / dw); + var pad = Math.floor((w - (qty * dw)) / 2); + if (pad < 10) { + return Math.floor((w - ((qty - 1) * dw)) / 2); + } + return pad; + } + + static displayAssets (str) { + nativeJr = true; + var div = gn('scrollarea'); + var data = JSON.parse(str); + if (data.length > 0) { + for (var i = 0; i < data.length; i++) { + Library.addAssetThumbChoose(div, data[i], 120 * scaleMultiplier, 90 * scaleMultiplier, + Library.selectAsset); + } + } + Library.addHR(div); + nativeJr = false; + data = (type == 'costumes') ? MediaLib.sprites : MediaLib.backgrounds; + Library.displayLibAssets(data); + } + + static displayLibAssets (data) { + var div = gn('scrollarea'); + if (data.length < 1) { + return; + } + var order = data[0].order; + var key = order ? order.split(',')[1] : ''; + for (var i = 0; i < data.length; i++) { + order = data[i].order; + var key2 = order ? order.split(',')[1] : ''; + if (key2 != key) { + Library.addHR(div); + key = key2; + } + if ('separator' in data[i]) { + Library.addHR(div); + } else { + Library.addLocalThumbChoose(div, data[i], 120 * scaleMultiplier, + 90 * scaleMultiplier, Library.selectAsset); + } + } + } + + static addAssetThumbChoose (parent, aa, w, h, fcn) { + var data = Library.parseAssetData(aa); + var tb = document.createElement('div'); + parent.appendChild(tb); + tb.byme = nativeJr ? 1 : 0; + var md5 = data.md5; + tb.setAttribute('class', 'assetbox off'); + tb.setAttribute('id', md5); + tb.scale = (!data.scale) ? 0.5 : data.scale; + tb.fieldname = data.name; + tb.w = Number(data.width); + tb.h = Number(data.height); + var scale = Math.min(w / tb.w, h / tb.h); + var img = newHTML('img', undefined, tb); + img.style.left = (9 * scaleMultiplier) + 'px'; + img.style.top = (7 * scaleMultiplier) + 'px'; + img.style.position = 'relative'; + img.style.height = (data.height * scale) + 'px'; + if (data.altmd5) { + IO.getAsset(data.altmd5, drawMe); + } + function drawMe (dataurl) { + img.src = dataurl; + } + tb.ontouchstart = function (evt) { + fcn(evt, tb); + }; + return tb; + } + + static addLocalThumbChoose (parent, data, w, h, fcn) { + var tb = newHTML('div', 'assetbox off', parent); + var md5 = data.md5; + tb.byme = nativeJr ? 1 : 0; + tb.setAttribute('id', md5); + tb.scale = (!data.scale) ? 0.5 : data.scale; + tb.fieldname = data.name; + tb.w = Number(data.width); + tb.h = Number(data.height); + + var img = newHTML('img', undefined, tb); + var scale = Math.min(w / tb.w, h / tb.h); + img.style.height = tb.h * scale + 'px'; + img.style.width = tb.w * scale + 'px'; + + img.style.left = Math.floor(((w - (scale * tb.w)) / 2) + (9 * scaleMultiplier)) + 'px'; + img.style.top = Math.floor(((h - (scale * tb.h)) / 2) + (9 * scaleMultiplier)) + 'px'; + img.style.position = 'relative'; + + // Cached downsized-thumbnails are in pnglibrary + var pngPath = MediaLib.path.replace('svg', 'png'); + img.src = pngPath + IO.getFilename(md5) + '.png'; + + tb.ontouchstart = function (evt) { + fcn(evt, tb); + }; + return tb; + } + + static userAssetThumbnail (img, cnv, sizew, sizeh) { + var scale = Math.min(sizew / img.width, sizeh / img.height); + var currentCtx = cnv.getContext('2d'); + var iw = Math.floor(scale * img.width); + var ih = Math.floor(scale * img.height); + var ix = Math.floor((sizew - (scale * img.width)) / 2); + var iy = Math.floor((sizeh - (scale * img.height)) / 2); + currentCtx.drawImage(img, 0, 0, img.width, img.height, ix, iy, iw, ih); + } + + static addEmptyThumb (parent, w, h) { + var tb = document.createElement('div'); + tb.setAttribute('class', 'assetbox off'); + tb.setAttribute('id', 'none'); + tb.fieldname = ((type == 'costumes') ? + Localization.localize('LIBRARY_CHARACTER') : Localization.localize('LIBRARY_BACKGROUND')); + tb.byme = 1; + var cnv = newCanvas(tb, 9 * scaleMultiplier, 7 * scaleMultiplier, w, h, { + position: 'relative' + }); + var ctx = cnv.getContext('2d'); + ctx.fillStyle = ScratchJr.stagecolor; + ctx.fillRect(0, 0, w, h); + parent.appendChild(tb); + tb.ontouchstart = function (evt) { + Library.selectAsset(evt, tb); + }; + } + + static addHR (div) { + var hr = document.createElement('hr'); + div.appendChild(hr); + hr.setAttribute('class', 'bigdivide'); + } + + /////////////////////////// + //selection + + + static selectAsset (e, tb) { + tb.pt = JSON.stringify(Events.getTargetPoint(e)); + if (shaking && (e.target.className == 'deleteasset')) { + Library.removeFromAssetList(); + return; + } else if (shaking) { + Library.stopShaking(); + } + if (tb.byme && (tb.id != 'none')) { + holdit(tb); + } + tb.ontouchend = function (evt) { + clickMe(evt, tb); + }; + window.onmouseup = function (evt) { + clickMe(evt, tb); + }; + window.onmousemove = function (evt) { + clearEvents(evt, tb); + }; + function holdit () { + var repeat = function () { + tb.ontouchend = undefined; + window.onmouseup = undefined; + window.onmousemove = undefined; + timeoutEvent = undefined; + Library.stopShaking(); + shaking = tb; + Library.clearAllSelections(); + Library.startShaking(tb); + }; + timeoutEvent = setTimeout(repeat, 500); + } + function clearEvents (e, tb) { + var pt = Events.getTargetPoint(e); + var pt2 = JSON.parse(tb.pt); + if (Library.distance(pt, pt2) < 30) { + return; + } + e.preventDefault(); + if (timeoutEvent) { + clearTimeout(timeoutEvent); + } + if (clickThumb) { + Library.unSelect(clickThumb); + } + timeoutEvent = undefined; + tb.ontouchend = undefined; + window.onmouseup = function () { + window.onmousemove = undefined; + window.onmouseup = undefined; + }; + } + function clickMe (e, tb) { + if (timeoutEvent) { + clearTimeout(timeoutEvent); + } + Library.selectThisAsset(e, tb); + timeoutEvent = undefined; + tb.ontouchend = undefined; + tb.onmouseup = undefined; + window.onmousemove = undefined; + window.onmouseup = undefined; + } + } + + static startShaking (b) { + b.className = b.className + ' shakeme'; + newHTML('div', 'deleteasset', b); + shaking = b; + } + + static stopShaking () { + if (!shaking) { + return; + } + var b = shaking; + b.setAttribute('class', 'assetbox off'); + var ic = b.childNodes[b.childElementCount - 1]; + if (ic.getAttribute('class') == 'deleteasset') { + b.removeChild(ic); + } + shaking = undefined; + } + + static removeFromAssetList () { + ScratchAudio.sndFX('cut.wav'); + var b = shaking; + b.parentNode.removeChild(b); + var key = (type == 'costumes') ? 'usershapes' : 'userbkgs'; + var json = {}; + json.cond = 'md5 = ?'; + json.items = ['*']; + json.values = [b.id]; + IO.query(key, json, Library.removeAssetFromLib); + clickThumb = undefined; + selectedOne = undefined; + return true; + } + + // Determine if an asset thumbnail is unique + // md5: thumbnail md5 to determine uniqueness + // type: "costumes" or "backgrounds" + // callback: called with true if unique, false if duplicate exists + static assetThumbnailUnique (md5, type, callback) { + var key = (type == 'costumes') ? 'usershapes' : 'userbkgs'; + var json = {}; + json.cond = 'ext = ? AND altmd5 = ?'; + json.items = ['md5', 'altmd5']; + json.values = ['svg', md5]; + json.order = 'ctime desc'; + IO.query(key, json, function (results) { + results = JSON.parse(results); + callback(results.length <= 1); + }); + } + + static removeAssetFromLib (str) { + var key = (type == 'costumes') ? 'usershapes' : 'userbkgs'; + var aa = JSON.parse(str)[0]; + var data = Library.parseAssetData(aa); + + if (data.altmd5) { + // Removes the thumbnail for the asset. + // First ensure that there aren't other characters/bgs using the same thumb + // (this is possible if we receive a duplicate project, for example) + Library.assetThumbnailUnique(data.altmd5, type, function (isUnique) { + if (isUnique) { + iOS.remove(data.altmd5, iOS.trace); + } + }); + } + + IO.deleteobject(key, data.id, iOS.trace); + } + + static parseAssetData (data) { + var res = new Object(); + for (var key in data) { + res[key.toLowerCase()] = data[key]; + } + return res; + } + + static selectThisAsset (e, tb) { + if (tb.id == selectedOne) { + if (type == 'costumes') { + Library.closeSpriteSelection(e); + } else { + Library.closeBkgSelection(e); + } } else { - Library.closeBkgSelection(e); - } - } else { - Library.clearAllSelections(); + Library.clearAllSelections(); - // Disable paint editor for PNG sprites - var thumbID = tb.id; - var thumbType = thumbID.substr(thumbID.length - 3); - if (thumbType == 'png') { - gn('library_paintme').style.opacity = 0; - gn('library_paintme').ontouchstart = null; + // Disable paint editor for PNG sprites + var thumbID = tb.id; + var thumbType = thumbID.substr(thumbID.length - 3); + if (thumbType == 'png') { + gn('library_paintme').style.opacity = 0; + gn('library_paintme').ontouchstart = null; + } else { + gn('library_paintme').style.opacity = 1; + gn('library_paintme').ontouchstart = Library.editResource; + } + + tb.className = 'assetbox on'; + selectedOne = tb.id; + clickThumb = tb; + if (tb.fieldname) { + gn('assetname').textContent = tb.fieldname; + } + } + } + + static clearAllSelections () { + var div = gn('scrollarea'); + for (var i = 0; i < div.childElementCount; i++) { + if (div.childNodes[i].nodeName == 'DIV') { + div.childNodes[i].className = 'assetbox off'; + } + } + } + + static unSelect (tb) { + gn('assetname').textContent = ''; + tb.className = 'assetbox off'; + selectedOne = undefined; + if (clickThumb) { + if (tb.byme && (clickThumb.childElementCount > 1)) { + clickThumb.childNodes[clickThumb.childElementCount - 1].style.visibility = 'hidden'; + } + clickThumb = undefined; + } + } + + static resizeScroll () { + var w = Math.min(getDocumentWidth(), frame.offsetWidth); + var h = Math.max(getDocumentHeight(), frame.offsetHeight); + var dx = w - 20 * scaleMultiplier; + setProps(gn('scrollarea').style, { + width: dx + 'px', + height: (h - 120 * scaleMultiplier) + 'px' + }); + } + + /////////////////////////////////////////// + // Object actions + ////////////////////////////////////////// + + static editResource (e) { + Library.close(e); + if (type != 'costumes') { + Library.editBackground(e); } else { - gn('library_paintme').style.opacity = 1; - gn('library_paintme').ontouchstart = Library.editResource; - } - - tb.className = 'assetbox on'; - Library.selectedOne = tb.id; - Library.clickThumb = tb; - if (tb.fieldname) { - gn('assetname').textContent = tb.fieldname; + Library.editCostume(e); } } -}; -Library.clearAllSelections = function () { - var div = gn('scrollarea'); - for (var i = 0; i < div.childElementCount; i++) { - if (div.childNodes[i].nodeName == 'DIV') { - div.childNodes[i].className = 'assetbox off'; + static editBackground () { + var md5 = selectedOne && (selectedOne != 'none') ? selectedOne : undefined; + Paint.open(true, md5); + } + + static editCostume () { + var sname = undefined; + var cname = selectedOne ? clickThumb.fieldname : Localization.localize('LIBRARY_CHARACTER'); + var scale = selectedOne && (selectedOne != 'none') ? clickThumb.scale : 0.5; + var md5 = selectedOne && (selectedOne != 'none') ? selectedOne : undefined; + var w = selectedOne && (selectedOne != 'none') ? Math.round(clickThumb.w) : undefined; + var h = selectedOne && (selectedOne != 'none') ? Math.round(clickThumb.h) : undefined; + Paint.open(false, md5, sname, cname, scale, w, h); + } + + static closeSpriteSelection (e) { + e.preventDefault(); + e.stopPropagation(); + var id = selectedOne ? clickThumb.fieldname : Localization.localize('LIBRARY_CHARACTER'); + if (selectedOne && (selectedOne != 'none')) { + ScratchJr.stage.currentPage.addSprite(clickThumb.scale, selectedOne, id); } - } -}; -Library.unSelect = function (tb) { - gn('assetname').textContent = ''; - tb.className = 'assetbox off'; - Library.selectedOne = undefined; - if (Library.clickThumb) { - if (tb.byme && (Library.clickThumb.childElementCount > 1)) { - Library.clickThumb.childNodes[Library.clickThumb.childElementCount - 1].style.visibility = 'hidden'; + // Prevent reporting user asset names + if (clickThumb) { + var analyticsName = clickThumb.fieldname; + if (!(selectedOne in MediaLib.keys)) { + analyticsName = 'user_asset'; + } + iOS.analyticsEvent('editor', 'new_character', analyticsName); } - Library.clickThumb = undefined; - } -}; - -Library.resizeScroll = function () { - var w = Math.min(getDocumentWidth(), frame.offsetWidth); - var h = Math.max(getDocumentHeight(), frame.offsetHeight); - var dx = w - 20 * scaleMultiplier; - setProps(gn('scrollarea').style, { - width: dx + 'px', - height: (h - 120 * scaleMultiplier) + 'px' - }); -}; - -/////////////////////////////////////////// -// Object actions -////////////////////////////////////////// - -Library.editResource = function (e) { - Library.close(e); - if (Library.type != 'costumes') { - Library.editBackground(e); - } else { - Library.editCostume(e); - } -}; - -Library.editBackground = function () { - var md5 = Library.selectedOne && (Library.selectedOne != 'none') ? Library.selectedOne : undefined; - Paint.open(true, md5); -}; - -Library.editCostume = function () { - var sname = undefined; - var cname = Library.selectedOne ? Library.clickThumb.fieldname : Localization.localize('LIBRARY_CHARACTER'); - var scale = Library.selectedOne && (Library.selectedOne != 'none') ? Library.clickThumb.scale : 0.5; - var md5 = Library.selectedOne && (Library.selectedOne != 'none') ? Library.selectedOne : undefined; - var w = Library.selectedOne && (Library.selectedOne != 'none') ? Math.round(Library.clickThumb.w) : undefined; - var h = Library.selectedOne && (Library.selectedOne != 'none') ? Math.round(Library.clickThumb.h) : undefined; - Paint.open(false, md5, sname, cname, scale, w, h); -}; - -Library.closeSpriteSelection = function (e) { - e.preventDefault(); - e.stopPropagation(); - var id = Library.selectedOne ? Library.clickThumb.fieldname : Localization.localize('LIBRARY_CHARACTER'); - if (Library.selectedOne && (Library.selectedOne != 'none')) { - ScratchJr.stage.currentPage.addSprite(Library.clickThumb.scale, Library.selectedOne, id); + Library.close(e); } - // Prevent reporting user asset names - if (Library.clickThumb) { - var analyticsName = Library.clickThumb.fieldname; - if (!(Library.selectedOne in MediaLib.keys)) { - analyticsName = 'user_asset'; + static closeBkgSelection (e) { + e.preventDefault(); + e.stopPropagation(); + if (selectedOne) { + ScratchJr.stage.currentPage.setBackground(selectedOne, ScratchJr.stage.currentPage.updateBkg); } - iOS.analyticsEvent('editor', 'new_character', analyticsName); + Library.close(e); } - Library.close(e); -}; -Library.closeBkgSelection = function (e) { - e.preventDefault(); - e.stopPropagation(); - if (Library.selectedOne) { - ScratchJr.stage.currentPage.setBackground(Library.selectedOne, ScratchJr.stage.currentPage.updateBkg); + ///////////////////////////////////////// + //Key Handeling Top Level prevention + ///////////////////////////////////////// + + static distance (pt1, pt2) { + var dx = pt1.x - pt2.x; + var dy = pt1.y - pt2.y; + return Math.round(Math.sqrt((dx * dx) + (dy * dy))); } - Library.close(e); -}; - -///////////////////////////////////////// -//Key Handeling Top Level prevention -///////////////////////////////////////// - -Library.distance = function (pt1, pt2) { - var dx = pt1.x - pt2.x; - var dy = pt1.y - pt2.y; - return Math.round(Math.sqrt((dx * dx) + (dy * dy))); -}; +} diff --git a/src/editor/ui/Palette.js b/src/editor/ui/Palette.js index bbfdd53..14f21c2 100644 --- a/src/editor/ui/Palette.js +++ b/src/editor/ui/Palette.js @@ -2,577 +2,603 @@ // Blocks Categories Palettes /////////////////////////////////// -Palette = function () {}; +import ScratchJr from '../ScratchJr'; +import Block from '../blocks/Block'; +import BlockSpecs from '../blocks/BlockSpecs'; +import ScriptsPane from './ScriptsPane'; +import Undo from './Undo'; +import iOS from '../../iPad/iOS'; +import Events from '../../utils/Events'; +import Rectangle from '../../geom/Rectangle'; +import DrawPath from '../../utils/DrawPath'; +import ScratchAudio from '../../utils/ScratchAudio'; +import Record from './Record'; +import {frame, gn, localx, newHTML, scaleMultiplier, isTablet, newDiv, + setProps, globalx, localy, globaly, drawScaled, newCanvas, + setCanvasSize, hitRect, writeText, getStringSize} from '../../utils/lib'; -Palette.blockscale = 0.75; -Palette.numcat = 0; -Palette.betweenblocks = undefined; // Set in setup() -Palette.blockdy = 5; -Palette.timeoutid = undefined; -Palette.helpballoon = undefined; -Palette.dxblocks = 10; -Palette.currentCategory; -Palette.setup = function (parent) { - Palette.blockscale *= scaleMultiplier; - Palette.blockdy *= scaleMultiplier; - Palette.blockdx *= scaleMultiplier; - Palette.betweenblocks = 90 * Palette.blockscale; - Palette.createCategorySelectors(parent); - var div = newHTML('div', 'palette', parent); - div.setAttribute('id', 'palette'); - div.ontouchstart = function (evt) { - Palette.paletteMouseDown(evt); - }; - var pc = newHTML('div', 'papercut', parent); - newHTML('div', 'withstyle', pc); -}; +let blockscale = 0.75; +let numcat = 0; // getter +let betweenblocks = undefined; // Set in setup() +let blockdy = 5; +let timeoutid = undefined; +let helpballoon = undefined; +let dxblocks = 10; -Palette.createCategorySelectors = function (parent) { - var sel = newHTML('div', 'categoryselector', parent); - sel.setAttribute('id', 'selectors'); - var bkg = newHTML('div', 'catbkg', sel); - newHTML('div', 'catimage', bkg); - var leftPx = 15 * scaleMultiplier; - var widthPx = 54 * scaleMultiplier; - for (var i = 0; i < BlockSpecs.categories.length; i++) { - Palette.createSelector(sel, i, leftPx + i * widthPx, 0, BlockSpecs.categories[i]); +export default class Palette { + static get numcat () { + return numcat; } -}; -Palette.paletteMouseDown = function (e) { - if (isTablet && e.touches && (e.touches.length > 1)) { - return; + static get helpballoon () { + return helpballoon; } - if (ScratchJr.onHold) { - return; + + static set helpballoon (newHelpballoon) { + helpballoon = newHelpballoon; } - e.preventDefault(); - ScratchJr.blur(); - var pal = gn('palette'); - var spt = Events.getTargetPoint(e); - var pt = { - x: localx(pal, spt.x), - y: localy(pal, spt.y) - }; - for (var i = 0; i < pal.childElementCount; i++) { - var ths = pal.childNodes[i]; - if (!hitRect(ths, pt)) { - continue; - } - if (ScratchJr.shaking && (ScratchJr.shaking == ths)) { - Palette.removeSound(ths); - } else { - Events.startDrag(e, ths, Palette.prepareForDrag, - Palette.dropBlockFromPalette, ScriptsPane.draggingBlock, Palette.showHelp, Palette.startShaking); + + static setup (parent) { + blockscale *= scaleMultiplier; + blockdy *= scaleMultiplier; + Palette.blockdx *= scaleMultiplier; // XXX + betweenblocks = 90 * blockscale; + Palette.createCategorySelectors(parent); + var div = newHTML('div', 'palette', parent); + div.setAttribute('id', 'palette'); + div.ontouchstart = function (evt) { + Palette.paletteMouseDown(evt); + }; + var pc = newHTML('div', 'papercut', parent); + newHTML('div', 'withstyle', pc); + } + + static createCategorySelectors (parent) { + var sel = newHTML('div', 'categoryselector', parent); + sel.setAttribute('id', 'selectors'); + var bkg = newHTML('div', 'catbkg', sel); + newHTML('div', 'catimage', bkg); + var leftPx = 15 * scaleMultiplier; + var widthPx = 54 * scaleMultiplier; + for (var i = 0; i < BlockSpecs.categories.length; i++) { + Palette.createSelector(sel, i, leftPx + i * widthPx, 0, BlockSpecs.categories[i]); } } - ScratchJr.clearSelection(); -}; -Palette.isRecorded = function (ths) { - var val = ths.owner.getArgValue(); - var list = ScratchJr.getActiveScript().owner.spr.sounds; - return list.indexOf(val) > 0; -}; - -Palette.removeSound = function (ths) { - ScratchAudio.sndFX('cut.wav'); - var indx = ths.owner.getArgValue(); - var spr = ScratchJr.getSprite(); - if (!spr) { - return; - } - var page = spr.div.parentNode.owner; - var sounds = spr.sounds.concat(); - if (indx >= sounds.length) { - return; - } - sounds.splice(indx, 1); - spr.sounds = sounds; - // recreate the sprite scripts to make sure deleted sound is properly treated - var sprdata = spr.getData(); - var div = gn(spr.id + '_scripts'); - while (div.childElementCount > 0) { - div.removeChild(div.childNodes[0]); - } - var sc = div.owner; - var list = sprdata.scripts; - for (var j = 0; j < list.length; j++) { - sc.recreateStrip(list[j]); - } - Undo.record({ - action: 'deletesound', - who: spr.id, - where: page.id, - sound: name - }); - ScratchJr.storyStart('Palette.removeSound'); // Record a change for sample projects in story-starter mode - - Palette.selectCategory(3); -}; - -Palette.showHelp = function (e, b) { - var block = b.owner; - var help = BlockSpecs.blockDesc(block, ScratchJr.getSprite()); - var str = help[block.blocktype]; - if (!str) { - return; - } - Palette.openPaletteBalloon(b, str); - Palette.timeoutid = setTimeout(Palette.closeHelpBalloon, 2000); -}; - -Palette.startShaking = function (b) { - if (!b.owner) { - return; - } - if (b.owner.blocktype != 'playusersnd') { - Palette.showHelp(null, b); return; - } - ScratchJr.shaking = b; - ScratchJr.stopShaking = Palette.stopShaking; - b.setAttribute('class', 'shakeme'); - newHTML('div', 'deletesound', b); -}; - -Palette.clickBlock = function (e, b) { - if (ScratchJr.shaking && (b == ScratchJr.shaking)) { - Palette.removeSound(b); - } else { - ScratchJr.clearSelection(); - Palette.showHelp(e, b); - } -}; - -Palette.stopShaking = function (b) { - if (!b.owner) { - return; - } - ScratchJr.shaking = undefined; - ScratchJr.stopShaking = undefined; - b.setAttribute('class', ''); - var ic = b.childNodes[b.childElementCount - 1]; - if (ic.getAttribute('class') == 'deletesound') { - b.removeChild(ic); - } -}; - -Palette.openPaletteBalloon = function (obj, label) { - if (Palette.helpballoon) { - Palette.closeHelpBalloon(); - } - var fontSize = Math.floor(14 * window.devicePixelRatio * scaleMultiplier); - var w = window.devicePixelRatio * 80 * scaleMultiplier; - var h = window.devicePixelRatio * 36 * scaleMultiplier; - var dy = globaly(obj) - 36 * scaleMultiplier; - Palette.helpballoon = newCanvas(frame, 0, dy, w, h, { - position: 'absolute', - zIndex: 1000 - }); - Palette.helpballoon.icon = obj; - var ctx = Palette.helpballoon.getContext('2d'); - w = 16 * window.devicePixelRatio * scaleMultiplier + - getStringSize(ctx, 'bold ' + fontSize + 'px ' + Settings.paletteBalloonFont, label).width; - if (w < 36 * scaleMultiplier) { - w = 36 * scaleMultiplier; - } - var dx = (globalx(obj) + (obj.offsetWidth / 2)) * window.devicePixelRatio - (w / 2); - setCanvasSize(Palette.helpballoon, w, h); - setProps(Palette.helpballoon.style, { - position: 'absolute', - webkitTransform: 'translate(' + (-w / 2) + 'px, ' + (-h / 2) + 'px) ' + - 'scale(' + (1 / window.devicePixelRatio) + ') translate(' + (dx + (w / 2)) + 'px, ' + (h / 2) + 'px)' - }); - Palette.drawBalloon(Palette.helpballoon.getContext('2d'), w, h); - writeText(ctx, 'bold ' + fontSize + 'px ' + Settings.paletteBalloonFont, 'white', label, - 21 * window.devicePixelRatio * scaleMultiplier, 8 * window.devicePixelRatio * scaleMultiplier); -}; - -Palette.hide = function () { - gn('blockspalette').childNodes[0].style.display = 'none'; - gn('blockspalette').childNodes[1].style.display = 'none'; -}; - -Palette.show = function () { - gn('blockspalette').childNodes[0].style.display = 'inline-block'; - gn('blockspalette').childNodes[1].style.display = 'inline-block'; -}; - - -Palette.closeHelpBalloon = function () { - if (Palette.timeoutid) { - clearTimeout(Palette.timeoutid); - } - if (Palette.helpballoon) { - Palette.helpballoon.parentNode.removeChild(Palette.helpballoon); - } - Palette.helpballoon = undefined; - Palette.timeoutid = undefined; -}; - -Palette.drawBalloon = function (ctx, w, h) { - var curve = 4; - var path = new Array(['M', 0, curve], ['q', 0, -curve, curve, -curve], ['h', w - curve * 2], - ['q', curve, 0, curve, curve], ['v', h - 11 - curve * 2], ['q', 0, curve, -curve, curve], - ['h', -(w / 2) + curve + 11], ['l', -11, 11], ['l', -11, -11], ['h', -(w / 2) + curve + 11], - ['q', -curve, 0, -curve, -curve], ['z'] - ); - ctx.clearRect(0, 0, Math.max(ctx.canvas.width, w), Math.max(ctx.canvas.height, h)); - ctx.fillStyle = '#4682B5'; - ctx.lineWidth = 2; - //ctx.strokeStyle = 'rgba(242,243,242,0.4)'; - ctx.beginPath(); - DrawPath.render(ctx, path); - ctx.fill(); -// ctx.stroke(); -}; - -Palette.prepareForDrag = function (e) { - e.preventDefault(); - ScratchAudio.sndFX('grab.wav'); - if (!ScratchJr.runtime.inactive()) { - ScratchJr.stopStrips(); - } - var sc = ScratchJr.getActiveScript().owner; - sc.flowCaret = null; - var pt = Events.getTargetPoint(e); - Events.dragmousex = pt.x; - Events.dragmousey = pt.y; - if (!Events.dragthumbnail.parentNode) { // palette has been removed programatically - Events.dragthumbnail = Palette.getBlockNamed(Events.dragthumbnail.owner.blocktype); - if (!Events.dragthumbnail) { - Events.cancelAll(); + static paletteMouseDown (e) { + if (isTablet && e.touches && (e.touches.length > 1)) { return; } - } - var mx = Events.dragmousex - frame.offsetLeft - localx(Events.dragthumbnail, Events.dragmousex); - var my = Events.dragmousey - frame.offsetTop - localy(Events.dragthumbnail, Events.dragmousey); - Events.dragcanvas = Events.dragthumbnail.owner.duplicateBlock(mx, my, sc.spr).div; - Events.dragcanvas.style.zIndex = ScratchJr.dragginLayer; - Events.dragDiv.appendChild(Events.dragcanvas); - // Events.dragcanvas.owner.lift(); - sc.dragList = [Events.dragcanvas.owner]; - sc.prepareCaret(Events.dragcanvas.owner); -}; - -Palette.getBlockNamed = function (str) { - var pal = gn('palette'); - for (var i = 0; i < pal.childElementCount; i++) { - if (pal.childNodes[i].owner.blocktype == str) { - return pal.childNodes[i]; + if (ScratchJr.onHold) { + return; } - } - return null; -}; - -Palette.createSelector = function (parent, n, dx, dy, spec) { - var pxWidth = 51 * scaleMultiplier; - var pxHeight = 57 * scaleMultiplier; - var div = newDiv(parent, dx, dy, pxWidth, pxHeight, { - position: 'absolute' - }); - div.index = n; - var officon = spec[1].cloneNode(true); - officon.width = pxWidth; - officon.height = pxHeight; - div.appendChild(officon); - setProps(officon.style, { - position: 'absolute', - zIndex: 6, - visibility: 'visible' - }); - var onicon = spec[0].cloneNode(true); - onicon.width = pxWidth; - onicon.height = pxHeight; - div.appendChild(onicon); - div.bkg = spec[2]; - setProps(onicon.style, { - position: 'absolute', - zIndex: 8, - visibility: 'hidden' - }); - div.ontouchstart = function (evt) { - Palette.clickOnCategory(evt); - }; -}; - -Palette.getPaletteSize = function () { - var first = gn('palette').childNodes[0]; - var last = gn('palette').childNodes[gn('palette').childElementCount - 1]; - return last.offsetLeft + last.offsetWidth - first.offsetLeft; -}; - -Palette.clickOnCategory = function (e) { - if (!e) { - return; - } - e.preventDefault(); - ScratchJr.unfocus(e); - var t = e.target; - ScratchAudio.sndFX('keydown.wav'); - var index = t.parentNode ? t.parentNode.index : 2; - Palette.selectCategory(index); -}; - -Palette.selectCategory = function (n) { - Palette.currentCategory = n; - var div = gn('selectors'); - // set the icons for text or sprite - Palette.numcat = n; - var currentSel = div.childNodes[n + 1]; - for (var i = 1; i < div.childElementCount; i++) { - var sel = div.childNodes[i]; - sel.childNodes[0].style.visibility = (sel.index != n) ? 'visible' : 'hidden'; - sel.childNodes[1].style.visibility = (sel.index == n) ? 'visible' : 'hidden'; - } - var pal = gn('palette'); - gn('blockspalette').style.background = currentSel.bkg; - while (pal.childElementCount > 0) { - pal.removeChild(pal.childNodes[0]); - } - if (!ScratchJr.getSprite()) { - return; - } - var list = (BlockSpecs.palettes[n]).concat(); - var dx = Palette.dxblocks; - for (var k = 0; k < list.length; k++) { - if (list[k] == 'space') { - dx += 30 * Palette.blockscale; - } else { - var newb = Palette.newScaledBlock(pal, list[k], - ((list[k] == 'repeat') ? 0.65 * scaleMultiplier : Palette.blockscale), dx, Palette.blockdy); - newb.lift(); - dx += Palette.betweenblocks; + e.preventDefault(); + ScratchJr.blur(); + var pal = gn('palette'); + var spt = Events.getTargetPoint(e); + var pt = { + x: localx(pal, spt.x), + y: localy(pal, spt.y) + }; + for (var i = 0; i < pal.childElementCount; i++) { + var ths = pal.childNodes[i]; + if (!hitRect(ths, pt)) { + continue; + } + if (ScratchJr.shaking && (ScratchJr.shaking == ths)) { + Palette.removeSound(ths); + } else { + Events.startDrag(e, ths, Palette.prepareForDrag, + Palette.dropBlockFromPalette, ScriptsPane.draggingBlock, Palette.showHelp, Palette.startShaking); + } } + ScratchJr.clearSelection(); } - dx += 30; - if ((n == (BlockSpecs.categories.length - 1)) && (ScratchJr.stage.pages.length > 1)) { - Palette.addPagesBlocks(dx); - } - if ((n == 3) && (ScratchJr.getSprite().sounds.length > 0)) { - Palette.addSoundsBlocks(Palette.dxblocks); - } -}; -Palette.reset = function () { - if (Palette.numcat == (BlockSpecs.categories.length - 1)) { - Palette.selectCategory(BlockSpecs.categories.length - 1); + static isRecorded (ths) { + var val = ths.owner.getArgValue(); + var list = ScratchJr.getActiveScript().owner.spr.sounds; + return list.indexOf(val) > 0; } - if (Palette.numcat == 3) { + + static removeSound (ths) { + ScratchAudio.sndFX('cut.wav'); + var indx = ths.owner.getArgValue(); + var spr = ScratchJr.getSprite(); + if (!spr) { + return; + } + var page = spr.div.parentNode.owner; + var sounds = spr.sounds.concat(); + if (indx >= sounds.length) { + return; + } + sounds.splice(indx, 1); + spr.sounds = sounds; + // recreate the sprite scripts to make sure deleted sound is properly treated + var sprdata = spr.getData(); + var div = gn(spr.id + '_scripts'); + while (div.childElementCount > 0) { + div.removeChild(div.childNodes[0]); + } + var sc = div.owner; + var list = sprdata.scripts; + for (var j = 0; j < list.length; j++) { + sc.recreateStrip(list[j]); + } + Undo.record({ + action: 'deletesound', + who: spr.id, + where: page.id, + sound: name + }); + ScratchJr.storyStart('Palette.removeSound'); // Record a change for sample projects in story-starter mode + Palette.selectCategory(3); } -}; -Palette.showSelectors = function (b) { - var n = Palette.numcat; - var div = gn('selectors'); - for (var i = 0; i < div.childElementCount; i++) { - var sel = div.childNodes[i]; - sel.childNodes[0].style.visibility = (sel.index != n) && b ? 'visible' : 'hidden'; - sel.childNodes[1].style.visibility = (sel.index == n) && b ? 'visible' : 'hidden'; - sel.childNodes[2].style.visibility = (sel.index != n) && b ? 'visible' : 'hidden'; - sel.childNodes[3].style.visibility = (sel.index == n) && b ? 'visible' : 'hidden'; - } -}; - -Palette.addPagesBlocks = function (dx) { - var pal = gn('palette'); - var spec = BlockSpecs.defs.gotopage; - for (var i = 0; i < ScratchJr.stage.pages.length; i++) { - if (ScratchJr.stage.pages[i].id == ScratchJr.stage.currentPage.id) { - continue; + static showHelp (e, b) { + var block = b.owner; + var help = BlockSpecs.blockDesc(block, ScratchJr.getSprite()); + var str = help[block.blocktype]; + if (!str) { + return; } - spec[4] = i + 1; - var newb = Palette.newScaledBlock(pal, 'gotopage', Palette.blockscale, dx, Palette.blockdy); - newb.lift(); - dx += Palette.betweenblocks + 5; + Palette.openPaletteBalloon(b, str); + timeoutid = setTimeout(Palette.closeHelpBalloon, 2000); } -}; -Palette.addSoundsBlocks = function (dx) { - var pal = gn('palette'); - var spr = ScratchJr.getSprite(); - var list = spr ? spr.sounds : []; - for (var i = 0; i < list.length; i++) { - var op = (MediaLib.sounds.indexOf(list[i]) < 0) ? 'playusersnd' : 'playsnd'; - var val = (MediaLib.sounds.indexOf(list[i]) < 0) ? i : list[i]; - var newb = Palette.addBlockSound(pal, op, val, dx, Palette.blockdy); - newb.lift(); - dx += Palette.betweenblocks; - } - if ((list.length < 6) && Record.available) { - Palette.drawRecordSound(newb.div.offsetWidth, newb.div.offsetHeight, dx); - } -}; - -Palette.addBlockSound = function (parent, op, val, dx, dy) { - var spec = BlockSpecs.defs[op]; - var old = spec[4]; - spec[4] = val; - var newb = Palette.newScaledBlock(parent, op, Palette.blockscale, dx, dy); - spec[4] = old; - return newb; -}; - -Palette.drawRecordSound = function (w, h, dx) { - var pal = gn('palette'); - var div = newDiv(pal, dx, 0, w, h, { - top: (6 * scaleMultiplier) + 'px' - }); - var cnv = newCanvas(div, 0, 0, - div.offsetWidth * window.devicePixelRatio, - div.offsetHeight * window.devicePixelRatio, - { - webkitTransform: 'translate(' + - (-div.offsetWidth * window.devicePixelRatio / 2) + 'px, ' + - (-div.offsetHeight * window.devicePixelRatio / 2) + 'px) ' + - 'scale(' + (1 / window.devicePixelRatio) + ') translate(' + - (div.offsetWidth * window.devicePixelRatio / 2) + 'px, ' + - (div.offsetHeight * window.devicePixelRatio / 2) + 'px)' + static startShaking (b) { + if (!b.owner) { + return; } - ); - if (BlockSpecs.mic.complete) { - drawScaled(BlockSpecs.mic, cnv); - } else { - BlockSpecs.mic.onload = function () { - drawScaled(BlockSpecs.mic, cnv); + if (b.owner.blocktype != 'playusersnd') { + Palette.showHelp(null, b); return; + } + ScratchJr.shaking = b; + ScratchJr.stopShaking = Palette.stopShaking; + b.setAttribute('class', 'shakeme'); + newHTML('div', 'deletesound', b); + } + + static clickBlock (e, b) { + if (ScratchJr.shaking && (b == ScratchJr.shaking)) { + Palette.removeSound(b); + } else { + ScratchJr.clearSelection(); + Palette.showHelp(e, b); + } + } + + static stopShaking (b) { + if (!b.owner) { + return; + } + ScratchJr.shaking = undefined; + ScratchJr.stopShaking = undefined; + b.setAttribute('class', ''); + var ic = b.childNodes[b.childElementCount - 1]; + if (ic.getAttribute('class') == 'deletesound') { + b.removeChild(ic); + } + } + + static openPaletteBalloon (obj, label) { + if (helpballoon) { + Palette.closeHelpBalloon(); + } + var fontSize = Math.floor(14 * window.devicePixelRatio * scaleMultiplier); + var w = window.devicePixelRatio * 80 * scaleMultiplier; + var h = window.devicePixelRatio * 36 * scaleMultiplier; + var dy = globaly(obj) - 36 * scaleMultiplier; + helpballoon = newCanvas(frame, 0, dy, w, h, { + position: 'absolute', + zIndex: 1000 + }); + helpballoon.icon = obj; + var ctx = helpballoon.getContext('2d'); + w = 16 * window.devicePixelRatio * scaleMultiplier + + getStringSize(ctx, 'bold ' + fontSize + 'px ' + Settings.paletteBalloonFont, label).width; + if (w < 36 * scaleMultiplier) { + w = 36 * scaleMultiplier; + } + var dx = (globalx(obj) + (obj.offsetWidth / 2)) * window.devicePixelRatio - (w / 2); + setCanvasSize(helpballoon, w, h); + setProps(helpballoon.style, { + position: 'absolute', + webkitTransform: 'translate(' + (-w / 2) + 'px, ' + (-h / 2) + 'px) ' + + 'scale(' + (1 / window.devicePixelRatio) + ') translate(' + (dx + (w / 2)) + 'px, ' + (h / 2) + 'px)' + }); + Palette.drawBalloon(helpballoon.getContext('2d'), w, h); + writeText(ctx, 'bold ' + fontSize + 'px ' + Settings.paletteBalloonFont, 'white', label, + 21 * window.devicePixelRatio * scaleMultiplier, 8 * window.devicePixelRatio * scaleMultiplier); + } + + static hide () { + gn('blockspalette').childNodes[0].style.display = 'none'; + gn('blockspalette').childNodes[1].style.display = 'none'; + } + + static show () { + gn('blockspalette').childNodes[0].style.display = 'inline-block'; + gn('blockspalette').childNodes[1].style.display = 'inline-block'; + } + + + static closeHelpBalloon () { + if (timeoutid) { + clearTimeout(timeoutid); + } + if (helpballoon) { + helpballoon.parentNode.removeChild(helpballoon); + } + helpballoon = undefined; + timeoutid = undefined; + } + + static drawBalloon (ctx, w, h) { + var curve = 4; + var path = new Array(['M', 0, curve], ['q', 0, -curve, curve, -curve], ['h', w - curve * 2], + ['q', curve, 0, curve, curve], ['v', h - 11 - curve * 2], ['q', 0, curve, -curve, curve], + ['h', -(w / 2) + curve + 11], ['l', -11, 11], ['l', -11, -11], ['h', -(w / 2) + curve + 11], + ['q', -curve, 0, -curve, -curve], ['z'] + ); + ctx.clearRect(0, 0, Math.max(ctx.canvas.width, w), Math.max(ctx.canvas.height, h)); + ctx.fillStyle = '#4682B5'; + ctx.lineWidth = 2; + //ctx.strokeStyle = 'rgba(242,243,242,0.4)'; + ctx.beginPath(); + DrawPath.render(ctx, path); + ctx.fill(); + // ctx.stroke(); + } + + static prepareForDrag (e) { + e.preventDefault(); + ScratchAudio.sndFX('grab.wav'); + if (!ScratchJr.runtime.inactive()) { + ScratchJr.stopStrips(); + } + var sc = ScratchJr.getActiveScript().owner; + sc.flowCaret = null; + var pt = Events.getTargetPoint(e); + Events.dragmousex = pt.x; + Events.dragmousey = pt.y; + if (!Events.dragthumbnail.parentNode) { // palette has been removed programatically + Events.dragthumbnail = Palette.getBlockNamed(Events.dragthumbnail.owner.blocktype); + if (!Events.dragthumbnail) { + Events.cancelAll(); + return; + } + } + var mx = Events.dragmousex - frame.offsetLeft - localx(Events.dragthumbnail, Events.dragmousex); + var my = Events.dragmousey - frame.offsetTop - localy(Events.dragthumbnail, Events.dragmousey); + Events.dragcanvas = Events.dragthumbnail.owner.duplicateBlock(mx, my, sc.spr).div; + Events.dragcanvas.style.zIndex = ScratchJr.dragginLayer; + Events.dragDiv.appendChild(Events.dragcanvas); + // Events.dragcanvas.owner.lift(); + sc.dragList = [Events.dragcanvas.owner]; + sc.prepareCaret(Events.dragcanvas.owner); + } + + static getBlockNamed (str) { + var pal = gn('palette'); + for (var i = 0; i < pal.childElementCount; i++) { + if (pal.childNodes[i].owner.blocktype == str) { + return pal.childNodes[i]; + } + } + return null; + } + + static createSelector (parent, n, dx, dy, spec) { + var pxWidth = 51 * scaleMultiplier; + var pxHeight = 57 * scaleMultiplier; + var div = newDiv(parent, dx, dy, pxWidth, pxHeight, { + position: 'absolute' + }); + div.index = n; + var officon = spec[1].cloneNode(true); + officon.width = pxWidth; + officon.height = pxHeight; + div.appendChild(officon); + setProps(officon.style, { + position: 'absolute', + zIndex: 6, + visibility: 'visible' + }); + var onicon = spec[0].cloneNode(true); + onicon.width = pxWidth; + onicon.height = pxHeight; + div.appendChild(onicon); + div.bkg = spec[2]; + setProps(onicon.style, { + position: 'absolute', + zIndex: 8, + visibility: 'hidden' + }); + div.ontouchstart = function (evt) { + Palette.clickOnCategory(evt); }; } - if (isTablet) { - div.ontouchstart = Palette.recordSound; - } -}; -Palette.recordSound = function (e) { - e.preventDefault(); - e.stopPropagation(); - ScratchJr.clearSelection(); - Record.appear(); -}; + static getPaletteSize () { + var first = gn('palette').childNodes[0]; + var last = gn('palette').childNodes[gn('palette').childElementCount - 1]; + return last.offsetLeft + last.offsetWidth - first.offsetLeft; + } -Palette.inStatesPalette = function () { - var div = gn('selectors'); - var sel = div.childNodes[div.childElementCount - 1]; - return sel.childNodes[0].style.visibility == 'hidden'; -}; - -// move to scratch jr app -Palette.getLandingPlace = function (el, e, scale) { - scale = typeof scale !== 'undefined' ? scale : 1; - var sc = ScratchJr.getActiveScript().owner; - var pt = e ? Events.getTargetPoint(e) : null; - if (pt && !pt.x) { - pt = null; - } - var box = new Rectangle(el.left / scale, el.top / scale, el.offsetWidth / scale, el.offsetHeight / scale); - var box2 = new Rectangle(globalx(gn('palette')), globaly(gn('palette')), - gn('palette').offsetWidth, gn('palette').offsetHeight); - if ((sc.flowCaret != null) && ((sc.flowCaret.prev != null) || - (sc.flowCaret.next != null) || (sc.flowCaret.inside != null))) { - return 'scripts'; - } - if (box2.overlapElemBy(box, 0.66) && box2.hitRect({x: el.left / scale, y: el.top / scale})) { - return 'palette'; - } - if (pt && box2.hitRect(pt)) { - return 'palette'; - } - if (Palette.overlapsWith(gn('scripts'), box)) { - return 'scripts'; - } - if (Palette.overlapsWith(gn('palette'), box)) { - return 'palette'; - } - if (Palette.overlapsWith(gn('library'), box)) { - return 'library'; - } - if (Palette.overlapsWith(gn('pages'), box)) { - return 'pages'; - } - return null; -}; - -Palette.overlapsWith = function (el, box) { - var box2 = new Rectangle(globalx(el), globaly(el), el.offsetWidth, el.offsetHeight); - return box.intersects(box2); -}; - -Palette.overlapsWith2 = function (el, box) { - var box2 = new Rectangle(el.offsetLeft, el.offsetTop, el.offsetWidth, el.offsetHeight); - return box.intersects(box2); -}; - - -Palette.getBlockfromChild = function (div) { - while (div != null) { - if (div.owner) { - return div; + static clickOnCategory (e) { + if (!e) { + return; } - div = div.parentNode; + e.preventDefault(); + ScratchJr.unfocus(e); + var t = e.target; + ScratchAudio.sndFX('keydown.wav'); + var index = t.parentNode ? t.parentNode.index : 2; + Palette.selectCategory(index); } - return null; -}; -Palette.getHittedThumb = function (el, div, scale) { - scale = typeof scale !== 'undefined' ? scale : 1; - var box1 = new Rectangle(el.left / scale, el.top / scale, el.offsetWidth / scale, el.offsetHeight / scale); - var area = 0; - var res = null; - var dh = div.parentNode.scrollTop; - for (var i = 0; i < div.childElementCount; i++) { - var node = div.childNodes[i]; - if (node.nodeName == 'FORM') { - continue; + static selectCategory (n) { + var div = gn('selectors'); + // set the icons for text or sprite + numcat = n; + var currentSel = div.childNodes[n + 1]; + for (var i = 1; i < div.childElementCount; i++) { + var sel = div.childNodes[i]; + sel.childNodes[0].style.visibility = (sel.index != n) ? 'visible' : 'hidden'; + sel.childNodes[1].style.visibility = (sel.index == n) ? 'visible' : 'hidden'; } - var box2 = new Rectangle(globalx(node, node.offsetLeft), globaly(node, node.offsetTop) - dh, - node.offsetWidth, node.offsetHeight); - var boxi = box1.intersection(box2); - var a = boxi.width * boxi.height; - if (a > area) { - area = a; - res = node; + var pal = gn('palette'); + gn('blockspalette').style.background = currentSel.bkg; + while (pal.childElementCount > 0) { + pal.removeChild(pal.childNodes[0]); + } + if (!ScratchJr.getSprite()) { + return; + } + var list = (BlockSpecs.palettes[n]).concat(); + var dx = dxblocks; + for (var k = 0; k < list.length; k++) { + if (list[k] == 'space') { + dx += 30 * blockscale; + } else { + var newb = Palette.newScaledBlock(pal, list[k], + ((list[k] == 'repeat') ? 0.65 * scaleMultiplier : blockscale), dx, blockdy); + newb.lift(); + dx += betweenblocks; + } + } + dx += 30; + if ((n == (BlockSpecs.categories.length - 1)) && (ScratchJr.stage.pages.length > 1)) { + Palette.addPagesBlocks(dx); + } + if ((n == 3) && (ScratchJr.getSprite().sounds.length > 0)) { + Palette.addSoundsBlocks(dxblocks); } } - return res; -}; -////////////////////////////////////// -// Palette Block -///////////////////////////////////// + static reset () { + if (numcat == (BlockSpecs.categories.length - 1)) { + Palette.selectCategory(BlockSpecs.categories.length - 1); + } + if (numcat == 3) { + Palette.selectCategory(3); + } + } -Palette.newScaledBlock = function (parent, op, scale, dx, dy) { - var bbx = new Block(BlockSpecs.defs[op], true, scale); - setProps(bbx.div.style, { - position: 'absolute', - left: dx + 'px', - top: dy + 'px' - }); - parent.appendChild(bbx.div); - return bbx; -}; + static showSelectors (b) { + var n = numcat; + var div = gn('selectors'); + for (var i = 0; i < div.childElementCount; i++) { + var sel = div.childNodes[i]; + sel.childNodes[0].style.visibility = (sel.index != n) && b ? 'visible' : 'hidden'; + sel.childNodes[1].style.visibility = (sel.index == n) && b ? 'visible' : 'hidden'; + sel.childNodes[2].style.visibility = (sel.index != n) && b ? 'visible' : 'hidden'; + sel.childNodes[3].style.visibility = (sel.index == n) && b ? 'visible' : 'hidden'; + } + } -Palette.dropBlockFromPalette = function (e, element) { - e.preventDefault(); - switch (Palette.getLandingPlace(element, e)) { - case 'scripts': - iOS.analyticsEvent('editor', 'new_block', element.owner.blocktype); - var sc = ScratchJr.getActiveScript(); - var dx = localx(sc, element.left); - var dy = localy(sc, element.top); - ScriptsPane.blockDropped(sc, dx, dy); - var spr = ScratchJr.getActiveScript().owner.spr; - Undo.record({ - action: 'scripts', - where: spr.div.parentNode.owner.id, - who: spr.id + static addPagesBlocks (dx) { + var pal = gn('palette'); + var spec = BlockSpecs.defs.gotopage; + for (var i = 0; i < ScratchJr.stage.pages.length; i++) { + if (ScratchJr.stage.pages[i].id == ScratchJr.stage.currentPage.id) { + continue; + } + spec[4] = i + 1; + var newb = Palette.newScaledBlock(pal, 'gotopage', blockscale, dx, blockdy); + newb.lift(); + dx += betweenblocks + 5; + } + } + + static addSoundsBlocks (dx) { + var pal = gn('palette'); + var spr = ScratchJr.getSprite(); + var list = spr ? spr.sounds : []; + for (var i = 0; i < list.length; i++) { + var op = (MediaLib.sounds.indexOf(list[i]) < 0) ? 'playusersnd' : 'playsnd'; + var val = (MediaLib.sounds.indexOf(list[i]) < 0) ? i : list[i]; + var newb = Palette.addBlockSound(pal, op, val, dx, blockdy); + newb.lift(); + dx += betweenblocks; + } + if ((list.length < 6) && Record.available) { + Palette.drawRecordSound(newb.div.offsetWidth, newb.div.offsetHeight, dx); + } + } + + static addBlockSound (parent, op, val, dx, dy) { + var spec = BlockSpecs.defs[op]; + var old = spec[4]; + spec[4] = val; + var newb = Palette.newScaledBlock(parent, op, blockscale, dx, dy); + spec[4] = old; + return newb; + } + + static drawRecordSound (w, h, dx) { + var pal = gn('palette'); + var div = newDiv(pal, dx, 0, w, h, { + top: (6 * scaleMultiplier) + 'px' }); - // Record a change for sample projects in story-starter mode - ScratchJr.storyStart('Palette.dropBlockFromPalette'); - break; - default: - ScratchJr.getActiveScript().owner.deleteBlocks(); - break; + var cnv = newCanvas(div, 0, 0, + div.offsetWidth * window.devicePixelRatio, + div.offsetHeight * window.devicePixelRatio, + { + webkitTransform: 'translate(' + + (-div.offsetWidth * window.devicePixelRatio / 2) + 'px, ' + + (-div.offsetHeight * window.devicePixelRatio / 2) + 'px) ' + + 'scale(' + (1 / window.devicePixelRatio) + ') translate(' + + (div.offsetWidth * window.devicePixelRatio / 2) + 'px, ' + + (div.offsetHeight * window.devicePixelRatio / 2) + 'px)' + } + ); + if (BlockSpecs.mic.complete) { + drawScaled(BlockSpecs.mic, cnv); + } else { + BlockSpecs.mic.onload = function () { + drawScaled(BlockSpecs.mic, cnv); + }; + } + if (isTablet) { + div.ontouchstart = Palette.recordSound; + } } - ScratchJr.getActiveScript().owner.dragList = []; -}; + + static recordSound (e) { + e.preventDefault(); + e.stopPropagation(); + ScratchJr.clearSelection(); + Record.appear(); + } + + static inStatesPalette () { + var div = gn('selectors'); + var sel = div.childNodes[div.childElementCount - 1]; + return sel.childNodes[0].style.visibility == 'hidden'; + } + + // move to scratch jr app + static getLandingPlace (el, e, scale) { + scale = typeof scale !== 'undefined' ? scale : 1; + var sc = ScratchJr.getActiveScript().owner; + var pt = e ? Events.getTargetPoint(e) : null; + if (pt && !pt.x) { + pt = null; + } + var box = new Rectangle(el.left / scale, el.top / scale, el.offsetWidth / scale, el.offsetHeight / scale); + var box2 = new Rectangle(globalx(gn('palette')), globaly(gn('palette')), + gn('palette').offsetWidth, gn('palette').offsetHeight); + if ((sc.flowCaret != null) && ((sc.flowCaret.prev != null) || + (sc.flowCaret.next != null) || (sc.flowCaret.inside != null))) { + return 'scripts'; + } + if (box2.overlapElemBy(box, 0.66) && box2.hitRect({x: el.left / scale, y: el.top / scale})) { + return 'palette'; + } + if (pt && box2.hitRect(pt)) { + return 'palette'; + } + if (Palette.overlapsWith(gn('scripts'), box)) { + return 'scripts'; + } + if (Palette.overlapsWith(gn('palette'), box)) { + return 'palette'; + } + if (Palette.overlapsWith(gn('library'), box)) { + return 'library'; + } + if (Palette.overlapsWith(gn('pages'), box)) { + return 'pages'; + } + return null; + } + + static overlapsWith (el, box) { + var box2 = new Rectangle(globalx(el), globaly(el), el.offsetWidth, el.offsetHeight); + return box.intersects(box2); + } + + static overlapsWith2 (el, box) { + var box2 = new Rectangle(el.offsetLeft, el.offsetTop, el.offsetWidth, el.offsetHeight); + return box.intersects(box2); + } + + + static getBlockfromChild (div) { + while (div != null) { + if (div.owner) { + return div; + } + div = div.parentNode; + } + return null; + } + + static getHittedThumb (el, div, scale) { + scale = typeof scale !== 'undefined' ? scale : 1; + var box1 = new Rectangle(el.left / scale, el.top / scale, el.offsetWidth / scale, el.offsetHeight / scale); + var area = 0; + var res = null; + var dh = div.parentNode.scrollTop; + for (var i = 0; i < div.childElementCount; i++) { + var node = div.childNodes[i]; + if (node.nodeName == 'FORM') { + continue; + } + var box2 = new Rectangle(globalx(node, node.offsetLeft), globaly(node, node.offsetTop) - dh, + node.offsetWidth, node.offsetHeight); + var boxi = box1.intersection(box2); + var a = boxi.width * boxi.height; + if (a > area) { + area = a; + res = node; + } + } + return res; + } + + ////////////////////////////////////// + // Palette Block + ///////////////////////////////////// + + static newScaledBlock (parent, op, scale, dx, dy) { + var bbx = new Block(BlockSpecs.defs[op], true, scale); + setProps(bbx.div.style, { + position: 'absolute', + left: dx + 'px', + top: dy + 'px' + }); + parent.appendChild(bbx.div); + return bbx; + } + + static dropBlockFromPalette (e, element) { + e.preventDefault(); + switch (Palette.getLandingPlace(element, e)) { + case 'scripts': + iOS.analyticsEvent('editor', 'new_block', element.owner.blocktype); + var sc = ScratchJr.getActiveScript(); + var dx = localx(sc, element.left); + var dy = localy(sc, element.top); + ScriptsPane.blockDropped(sc, dx, dy); + var spr = ScratchJr.getActiveScript().owner.spr; + Undo.record({ + action: 'scripts', + where: spr.div.parentNode.owner.id, + who: spr.id + }); + // Record a change for sample projects in story-starter mode + ScratchJr.storyStart('Palette.dropBlockFromPalette'); + break; + default: + ScratchJr.getActiveScript().owner.deleteBlocks(); + break; + } + ScratchJr.getActiveScript().owner.dragList = []; + } +} diff --git a/src/editor/ui/Project.js b/src/editor/ui/Project.js index dbbb168..0ff9d5a 100644 --- a/src/editor/ui/Project.js +++ b/src/editor/ui/Project.js @@ -1,573 +1,609 @@ -var Project = function () {}; +import ScratchJr from '../ScratchJr'; +import BlockSpecs from '../blocks/BlockSpecs'; +import Alert from './Alert'; +import Palette from './Palette'; +import UI from './UI'; +import Page from './Page'; +import Sprite from '../engine/Sprite'; +import iOS from '../../iPad/iOS'; +import IO from '../../iPad/IO'; +import Paint from '../../painteditor/Paint'; +import SVG2Canvas from '../../utils/SVG2Canvas'; +import {frame, gn, newHTML, scaleMultiplier, getIdFor, + isAndroid, setProps, setCanvasSize} from '../../utils/lib'; -Project.metadata = undefined; -Project.mediaCount = -1; -Project.saving = false; -Project.angle = 0; -Project.interval = undefined; -Project.pageid; -Project.loadIcon = undefined; -Project.whenDone = undefined; -Project.requestsCount = 0; -Project.error = false; -Project.projectbarsize = 66; -Project.mediaCountBase = 1; +let metadata = undefined; +let mediaCount = -1; +let saving = false; +let interval = undefined; +let pageid; +let loadIcon = undefined; +let error = false; +let projectbarsize = 66; +let mediaCountBase = 1; -Project.clear = function () { - ScratchJr.stage.clear(); - UI.clear(); -}; - -Project.load = function () { - Project.mediaCountBase = 1; - ScratchJr.log('Project load status', ScratchJr.getTime(), 'sec', BlockSpecs.loadCount); - if (BlockSpecs.loadCount > 0) { - setTimeout(function () { - Project.delayLoad(); - }, 32); - } else { - Project.startLoad(); - } -}; - -Project.delayLoad = function () { - if (BlockSpecs.loadCount < 1) { - Project.startLoad(); - } else { - setTimeout(function () { - Project.delayLoad(); - }, 32); - } -}; - -Project.startLoad = function () { - ScratchJr.log('all UI assets recieved - procced to call server', ScratchJr.getTime(), 'sec'); - Project.setProgress(20); - UI.layout(); - IO.getObject(ScratchJr.currentProject, Project.dataRecieved); -}; - -Project.dataRecieved = function (str) { - ScratchJr.log('got project metadata', ScratchJr.getTime(), 'sec'); - var data = JSON.parse(str)[0]; - Project.metadata = IO.parseProjectData(data); - Project.mediaCount = -1; - if (Project.metadata.json) { - Project.loadData(Project.metadata.json, doneProjectLoad); - } else { - Project.mediaCount = 0; - new Page(getIdFor('page')); - Palette.selectCategory(1); - // On Android 4.2, this comes up blank the first time, so try again in 100ms. - setTimeout(function () { - Palette.selectCategory(1); - }, 100); - Project.loadwait(doneProjectLoad); - } - function doneProjectLoad () { - // Clear gift flag - if ('id' in Project.metadata) { - Project.metadata.isgift = '0'; - IO.setProjectIsGift(Project.metadata); - } - Palette.selectCategory(1); - // On Android 4.2, this comes up blank the first time, so try again in 100ms. - setTimeout(function () { - Palette.selectCategory(1); - }, 100); - Paint.layout(); - Project.setProgress(100); - Project.liftCurtain(); - ScratchJr.stage.currentPage.update(); - ScratchJr.changed = false; - ScratchJr.storyStarted = false; - UI.needsScroll(); - ScratchJr.log('all thumbnails updated', ScratchJr.getTime(), 'sec'); - if (isAndroid) { - AndroidInterface.notifyEditorDoneLoading(); - } - } -}; - - -Project.init = function () { - ScratchJr.log('Project init', ScratchJr.getTime(), 'sec'); - var bd = newHTML('div', 'modal-backdrop fade', frame.parentNode); - bd.setAttribute('id', 'backdrop'); - setProps(gn('backdrop').style, { - display: 'none' - }); - var modalOuter = newHTML('div', 'modal-outer', frame.parentNode); - var modalMiddle = newHTML('div', 'modal-middle', modalOuter); - var modal = newHTML('div', 'modal hide fade', modalMiddle); - modal.setAttribute('id', 'modaldialog'); - setProps(gn('modaldialog').style, {}); - var body = newHTML('div', 'modal-body', modal); - body.setAttribute('id', 'modalbody'); - setProps(body.style, { - zoom: scaleMultiplier - }); - if (Project.loadIcon.complete) { - Project.addFeedback(); - } else { - Project.loadIcon.onload = function () { - Project.addFeedback(); - }; - } - Project.drawBlind(); -}; - -Project.addFeedback = function () { - var body = gn('modalbody'); - newHTML('div', 'loadscreenfill', body); - newHTML('div', 'topfill', body); - var cover = newHTML('div', 'loadscreencover', body); - cover.setAttribute('id', 'progressbar'); - var topcover = newHTML('div', 'topcover', body); - topcover.setAttribute('id', 'topcover'); - var cover2 = newHTML('div', 'progressbar2', body); - cover2.setAttribute('id', 'progressbar2'); - var li = newHTML('div', 'loadicon', body); - li.appendChild(Project.loadIcon); -}; - -Project.setProgress = function (perc) { - if (!gn('progressbar')) { - return; - } - var h = Project.projectbarsize - Math.round(Project.projectbarsize * perc / 100); - ScratchJr.log('setProgress', perc, h, Project.mediaCount, Project.mediaCountBase); - gn('progressbar').style.height = h + 'px'; - if (h == 0) { - gn('progressbar2').style.height = '0px'; - gn('topcover').style.background = '#F9A737'; +export default class Project { + static get metadata () { + return metadata; } -}; - -Project.drawBlind = function () { - gn('backdrop').setAttribute('class', 'modal-backdrop fade in'); - setProps(gn('backdrop').style, { - display: 'block' - }); - setProps(gn('modaldialog').style, { - display: 'block' - }); - gn('modaldialog').setAttribute('class', 'modal fade in'); -}; - -Project.loadwait = function (whenDone) { - if (Project.interval != null) { - window.clearInterval(Project.interval); + static set metadata (newMetadata) { + metadata = newMetadata; } - Project.mediaCountBase = Project.mediaCount; - if (Project.mediaCount <= 0) { - Project.getStarted(whenDone); - } else { - Project.interval = window.setInterval(function () { - Project.loadTask(whenDone); - }, 32); - } -}; -Project.loadTask = function (whenDone) { - if (Project.mediaCount <= 0) { - Project.getStarted(whenDone); - } else { - Project.setProgress(Project.getMediaLoadRatio(70)); + static get mediaCount () { + return mediaCount; } -}; -Project.getMediaLoadRatio = function (f) { - if (Project.mediaCount > Project.mediaCountBase) { - Project.mediaCountBase = Project.mediaCount; + static set mediaCount (newMediaCount) { + mediaCount = newMediaCount; } - return 20 + f - (Project.mediaCount / Project.mediaCountBase) * f; -}; -Project.getStarted = function (whenDone) { - Project.setProgress(90); - if (Project.interval) { - window.clearInterval(Project.interval); + static set loadIcon (newLoadIcon) { + loadIcon = newLoadIcon; } - Project.interval = null; - ScratchJr.log('Project images retrieved from server', ScratchJr.getTime(), 'sec'); - Project.setLoadPage(Project.pageid, whenDone); - ScratchJr.log('load done', ScratchJr.getTime(), 'sec', '-- media missing = ', Project.mediaCount); - ScratchJr.stage.resetPages(); - ScratchJr.runtime.beginTimer(); -}; -Project.liftCurtain = function () { - gn('backdrop').setAttribute('class', 'modal-backdrop fade'); - setProps(gn('backdrop').style, { - display: 'none' - }); - gn('modaldialog').setAttribute('class', 'modal fade'); - setProps(gn('modaldialog').style, { - display: 'none' - }); -}; - -Project.setLoadPage = function (pageid, whenDone) { - ScratchJr.log('setLoadPage', ScratchJr.getTime(), 'sec'); - var pages = ScratchJr.stage.getPagesID(); - if (pages.indexOf(pageid) < 0) { - ScratchJr.stage.currentPage = ScratchJr.stage.pages[0]; - } else { - ScratchJr.stage.currentPage = ScratchJr.stage.getPage(pageid); + static get error () { + return error; } - ScratchJr.stage.currentPage.div.style.visibility = 'visible'; - var list = ScratchJr.stage.pages; - for (var i = 0; i < list.length; i++) { - if (ScratchJr.stage.currentPage == list[i]) { - ScratchJr.stage.currentPage.setPageSprites('visible'); + + + static clear () { + ScratchJr.stage.clear(); + UI.clear(); + } + + static load () { + mediaCountBase = 1; + ScratchJr.log('Project load status', ScratchJr.getTime(), 'sec', BlockSpecs.loadCount); + if (BlockSpecs.loadCount > 0) { + setTimeout(function () { + Project.delayLoad(); + }, 32); } else { - list[i].setPageSprites('hidden'); + Project.startLoad(); } } - if (whenDone) { - whenDone(); + + static delayLoad () { + if (BlockSpecs.loadCount < 1) { + Project.startLoad(); + } else { + setTimeout(function () { + Project.delayLoad(); + }, 32); + } } -}; -Project.loadData = function (data, fcn) { - try { - data = (typeof data === 'string') ? JSON.parse(data) : data; - Project.mediaCount = 0; - Project.loadme(data, fcn); - Project.error = false; - } catch (e) { - console.log(e); //eslint-disable-line no-console - var errorMessage = 'Error -- project data corrupted.'; + static startLoad () { + ScratchJr.log('all UI assets recieved - procced to call server', ScratchJr.getTime(), 'sec'); + Project.setProgress(20); + UI.layout(); + IO.getObject(ScratchJr.currentProject, Project.dataRecieved); + } - if (window.reloadDebug) { - document.write(e.message + '\n' + Project.metadata['json']); + static dataRecieved (str) { + ScratchJr.log('got project metadata', ScratchJr.getTime(), 'sec'); + var data = JSON.parse(str)[0]; + metadata = IO.parseProjectData(data); + mediaCount = -1; + if (metadata.json) { + Project.loadData(metadata.json, doneProjectLoad); + } else { + mediaCount = 0; + new Page(getIdFor('page')); + Palette.selectCategory(1); + // On Android 4.2, this comes up blank the first time, so try again in 100ms. + setTimeout(function () { + Palette.selectCategory(1); + }, 100); + Project.loadwait(doneProjectLoad); + } + function doneProjectLoad () { + // Clear gift flag + if ('id' in metadata) { + metadata.isgift = '0'; + IO.setProjectIsGift(metadata); + } + Palette.selectCategory(1); + // On Android 4.2, this comes up blank the first time, so try again in 100ms. + setTimeout(function () { + Palette.selectCategory(1); + }, 100); + Paint.layout(); + Project.setProgress(100); + Project.liftCurtain(); + ScratchJr.stage.currentPage.update(); + ScratchJr.changed = false; + ScratchJr.storyStarted = false; + UI.needsScroll(); + ScratchJr.log('all thumbnails updated', ScratchJr.getTime(), 'sec'); + if (isAndroid) { + AndroidInterface.notifyEditorDoneLoading(); + } + } + } + + + static init () { + ScratchJr.log('Project init', ScratchJr.getTime(), 'sec'); + var bd = newHTML('div', 'modal-backdrop fade', frame.parentNode); + bd.setAttribute('id', 'backdrop'); + setProps(gn('backdrop').style, { + display: 'none' + }); + var modalOuter = newHTML('div', 'modal-outer', frame.parentNode); + var modalMiddle = newHTML('div', 'modal-middle', modalOuter); + var modal = newHTML('div', 'modal hide fade', modalMiddle); + modal.setAttribute('id', 'modaldialog'); + setProps(gn('modaldialog').style, {}); + var body = newHTML('div', 'modal-body', modal); + body.setAttribute('id', 'modalbody'); + setProps(body.style, { + zoom: scaleMultiplier + }); + if (loadIcon.complete) { + Project.addFeedback(); + } else { + loadIcon.onload = function () { + Project.addFeedback(); + }; + } + Project.drawBlind(); + } + + static addFeedback () { + var body = gn('modalbody'); + newHTML('div', 'loadscreenfill', body); + newHTML('div', 'topfill', body); + var cover = newHTML('div', 'loadscreencover', body); + cover.setAttribute('id', 'progressbar'); + var topcover = newHTML('div', 'topcover', body); + topcover.setAttribute('id', 'topcover'); + var cover2 = newHTML('div', 'progressbar2', body); + cover2.setAttribute('id', 'progressbar2'); + var li = newHTML('div', 'loadicon', body); + li.appendChild(loadIcon); + } + + static setProgress (perc) { + if (!gn('progressbar')) { return; } - - Alert.open(frame, gn('flip'), errorMessage, '#ff0000'); - if (Project.interval) { - window.clearInterval(Project.interval); + var h = projectbarsize - Math.round(projectbarsize * perc / 100); + ScratchJr.log('setProgress', perc, h, mediaCount, mediaCountBase); + gn('progressbar').style.height = h + 'px'; + if (h == 0) { + gn('progressbar2').style.height = '0px'; + gn('topcover').style.background = '#F9A737'; } - Project.interval = null; - Palette.selectCategory(1); - // On Android 4.2, this comes up blank the first time, so try again in 100ms. - setTimeout(function () { - Palette.selectCategory(1); - }, 100); - Project.liftCurtain(); - Project.error = true; + } -}; -Project.loadme = function (data, fcn) { - Project.recreate(data); - Project.loadwait(fcn); -}; - -Project.getLoadType = function (bkgid, sid, cid) { - if (bkgid != null) { - return 'bkg'; + static drawBlind () { + gn('backdrop').setAttribute('class', 'modal-backdrop fade in'); + setProps(gn('backdrop').style, { + display: 'block' + }); + setProps(gn('modaldialog').style, { + display: 'block' + }); + gn('modaldialog').setAttribute('class', 'modal fade in'); } - if (!cid) { - return 'none'; - } - if (sid && cid) { - return 'modify'; - } - return 'add'; -}; -////////////////////////////////////////////////// -// load project data -////////////////////////////////////////////////// - -Project.recreate = function (data) { - ScratchJr.log('Project data structures start loading', ScratchJr.getTime(), 'sec'); - Project.mediaCount = 0; - ScratchJr.stage.pages = []; - var pages = data.pages; - Project.pageid = data.currentPage; - for (var i = 0; i < pages.length; i++) { - Project.recreatePage(pages[i], data[pages[i]]); - } - Project.mediaCountBase = Project.mediaCount; -}; - -Project.recreatePage = function (name, data, fcn) { - var page = new Page(name, data, fcn); - page.div.style.visibility = 'hidden'; -}; - -Project.substractCount = function () { - Project.mediaCount--; - if ((gn('backdrop').className != 'modal-backdrop fade in') || (Project.mediaCountBase == 0)) { - return; - } - Project.setProgress(Project.getMediaLoadRatio(70)); -}; - -Project.recreateObject = function (page, name, data, callBack, active) { - var list = data.scripts; - //delete data.scripts; - var spr; - data.page = page; - if (data.type == 'sprite') { - Project.mediaCount++; - var fcn = function (spr) { - spr.setPos(data.xcoor, data.ycoor); - Project.mediaCount--; - if (gn('backdrop').className == 'modal-backdrop fade in') { - Project.setProgress(Project.getMediaLoadRatio(70)); - } - ScratchJr.log(spr.name, ScratchJr.getTime(), 'sec'); - if (callBack) { - callBack(spr); - } - }; - if (!data.defaultScale) { - data.defaultScale = 0.5; + static loadwait (whenDone) { + if (interval != null) { + window.clearInterval(interval); } - spr = new Sprite(data, fcn); - // load scripts - var sc = gn(name + '_scripts').owner; - for (var j = 0; j < list.length; j++) { - sc.recreateStrip(list[j]); - } - if (active) { - sc.activate(); + mediaCountBase = mediaCount; + if (mediaCount <= 0) { + Project.getStarted(whenDone); } else { - sc.deactivate(); - } - } else { - spr = new Sprite(data, callBack); - } - spr.div.style.opacity = spr.shown ? 1 : 0; - return spr; -}; - -////////////////////////////////////////////////// -// Save project data -////////////////////////////////////////////////// - -Project.prepareToSave = function (id, whenDone) { - if (Project.saving) { - Alert.open(frame, gn('flip'), 'Waiting', '#28A5DA'); - Project.waitUntilSaved(id, whenDone); - } else { - Alert.open(frame, gn('flip'), 'Saving', '#28A5DA'); - Project.save(id, whenDone); - } -}; - -Project.waitUntilSaved = function (id, fcn) { - if (Project.saving) { - setTimeout(function () { - Project.waitUntilSaved(id, fcn); - }, 500); - } else { - Project.save(id, fcn); - } -}; - -// Determine if thumbnailMD5 is unique to projectID -// callback(true/false) -Project.thumbnailUnique = function (thumbnailMD5, projectID, callback) { - var json = {}; - json.cond = 'deleted = ? AND id != ? AND gallery IS NULL'; - json.items = ['name', 'thumbnail', 'id']; - json.values = ['NO', projectID]; - IO.query(iOS.database, json, function (result) { - var pdata = JSON.parse(result); - var isUnique = true; - for (var p = 0; p < pdata.length; p++) { - var thispdata = IO.parseProjectData(pdata[p]); - var th = thispdata.thumbnail; - if (th) { - var thumb = (typeof th == 'string') ? JSON.parse(th) : th; - if (thumb && thumb.md5) { - if (thumb.md5 == thumbnailMD5) { - isUnique = false; - } - } - } - } - callback(isUnique); - }); -}; - -Project.save = function (id, whenDone) { - Project.saving = true; - var th = Project.metadata.thumbnail; - if (th && ScratchJr.editmode != 'storyStarter') { // Don't try to delete the thumbnail in a sample project - var thumb = (typeof th === 'string') ? JSON.parse(th) : th; - if (thumb.md5.indexOf('samples/') < 0) { // In case we've exited story-starter mode - Project.thumbnailUnique(thumb.md5, id, function (isUnique) { - if (isUnique) { - iOS.remove(thumb.md5, iOS.trace); // remove thumb; - } - }); + interval = window.setInterval(function () { + Project.loadTask(whenDone); + }, 32); } } - Project.metadata.id = id; - Project.metadata.json = Project.getProject(ScratchJr.stage.pages[0].id); - Project.getThumbnailPNG(ScratchJr.stage.pages[0], 192, 144, getMD5); - function getMD5 (dataurl) { - var pngBase64 = dataurl.split(',')[1]; - iOS.getmd5(pngBase64, function (str) { - savePNG(str, pngBase64); + + static loadTask (whenDone) { + if (mediaCount <= 0) { + Project.getStarted(whenDone); + } else { + Project.setProgress(Project.getMediaLoadRatio(70)); + } + } + + static getMediaLoadRatio (f) { + if (mediaCount > mediaCountBase) { + mediaCountBase = mediaCount; + } + return 20 + f - (mediaCount / mediaCountBase) * f; + } + + static getStarted (whenDone) { + Project.setProgress(90); + if (interval) { + window.clearInterval(interval); + } + interval = null; + ScratchJr.log('Project images retrieved from server', ScratchJr.getTime(), 'sec'); + Project.setLoadPage(pageid, whenDone); + ScratchJr.log('load done', ScratchJr.getTime(), 'sec', '-- media missing = ', mediaCount); + ScratchJr.stage.resetPages(); + ScratchJr.runtime.beginTimer(); + } + + static liftCurtain () { + gn('backdrop').setAttribute('class', 'modal-backdrop fade'); + setProps(gn('backdrop').style, { + display: 'none' + }); + gn('modaldialog').setAttribute('class', 'modal fade'); + setProps(gn('modaldialog').style, { + display: 'none' }); } - function savePNG (md5, pngBase64) { - var filename = ScratchJr.currentProject + '_' + md5; - iOS.setmedianame(pngBase64, filename, 'png', doNext); - } - - function doNext (md5) { - Project.metadata.thumbnail = { - 'pagecount': ScratchJr.stage.pages.length, - 'md5': md5 - }; - Project.metadata.mtime = (new Date()).getTime().toString(); - IO.saveProject(Project.metadata, saveDone); - } - - function saveDone () { - Project.saving = false; + static setLoadPage (pageid, whenDone) { + ScratchJr.log('setLoadPage', ScratchJr.getTime(), 'sec'); + var pages = ScratchJr.stage.getPagesID(); + if (pages.indexOf(pageid) < 0) { + ScratchJr.stage.currentPage = ScratchJr.stage.pages[0]; + } else { + ScratchJr.stage.currentPage = ScratchJr.stage.getPage(pageid); + } + ScratchJr.stage.currentPage.div.style.visibility = 'visible'; + var list = ScratchJr.stage.pages; + for (var i = 0; i < list.length; i++) { + if (ScratchJr.stage.currentPage == list[i]) { + ScratchJr.stage.currentPage.setPageSprites('visible'); + } else { + list[i].setPageSprites('hidden'); + } + } if (whenDone) { whenDone(); } } -}; -Project.getProject = function (pageid) { - var obj = {}; - obj.pages = ScratchJr.stage.getPagesID(); - obj.currentPage = pageid; - for (var i = 0; i < ScratchJr.stage.pages.length; i++) { - obj[ScratchJr.stage.pages[i].id] = ScratchJr.stage.pages[i].encodePage(); - } - return obj; -}; + static loadData (data, fcn) { + try { + data = (typeof data === 'string') ? JSON.parse(data) : data; + mediaCount = 0; + Project.loadme(data, fcn); + error = false; + } catch (e) { + console.log(e); //eslint-disable-line no-console + var errorMessage = 'Error -- project data corrupted.'; -Project.getUndo = function () { - return Project.getProject(ScratchJr.stage.currentPage.id); -}; + if (window.reloadDebug) { + document.write(e.message + '\n' + metadata['json']); + return; + } -Project.encodeSprite = function (name) { - return gn(name).owner.getData(); -}; - -Project.encodeStrip = function (b) { - var res = []; - var hasargs = ['playsnd', 'gotopage', 'playusersnd', 'setcolor', 'onmessage', 'message', 'setspeed']; - var loops = ['repeat']; - var carets = ['caretcmd', 'caretend', 'caretstart']; - while (b != null) { - var bt = b.blocktype; - // Don't encode carets in a strip - if (carets.indexOf(bt) > -1) { - b = b.next; - continue; + Alert.open(frame, gn('flip'), errorMessage, '#ff0000'); + if (interval) { + window.clearInterval(interval); + } + interval = null; + Palette.selectCategory(1); + // On Android 4.2, this comes up blank the first time, so try again in 100ms. + setTimeout(function () { + Palette.selectCategory(1); + }, 100); + Project.liftCurtain(); + error = true; } - if (bt == 'caretrepeat') { - // Convert repeat carets to actual repeats for the encoding - bt = 'repeat'; - } - var arg = (b.arg != null) || (hasargs.indexOf(bt) > -1) ? b.getArgValue() : null; - if (!arg && (arg != 0)) { - arg = 'null'; - } - var dx = b.div.left / b.scale; - var dy = b.div.top / b.scale; - var data = [bt, arg, dx, dy]; - if (loops.indexOf(bt) > -1) { - var inside = Project.encodeStrip(b.inside); - data.push(inside); - } - res.push(data); - b = b.next; } - return res; -}; -///////////////////////////// -// Project PNG Thumbnail -///////////////////////////// + static loadme (data, fcn) { + Project.recreate(data); + Project.loadwait(fcn); + } -Project.getThumbnailPNG = function (page, w, h, fcn) { - var scale = w / 480; - var data = {}; - data.pagecount = ScratchJr.stage.pages.length; - var c = document.createElement('canvas'); - setCanvasSize(c, w, h); - var ctx = c.getContext('2d'); - var md5 = page.md5; - ctx.fillStyle = Settings.stageColor; - ctx.fillRect(0, 0, w, h); - if (!md5) { - Project.drawSprites(page, scale, c, w, h, fcn); - } else { - var pcnv; - if (md5.substr(md5.length - 3) == 'png') { - var bgimg = page.div.firstElementChild.firstElementChild; - pcnv = Project.drawPNGInCanvas(bgimg, 480, 360); - } else { - pcnv = Project.drawSVGinCanvas(page.svg, 480, 360); + static getLoadType (bkgid, sid, cid) { + if (bkgid != null) { + return 'bkg'; } - ctx.drawImage(pcnv, 0, 0, 480, 360, 0, 0, w, h); - Project.drawSprites(page, scale, c, w, h, fcn); + if (!cid) { + return 'none'; + } + if (sid && cid) { + return 'modify'; + } + return 'add'; } -}; -Project.drawPNGInCanvas = function (png, w, h) { - var srccnv = document.createElement('canvas'); - setCanvasSize(srccnv, w, h); - var ctx = srccnv.getContext('2d'); - ctx.drawImage(png, 0, 0, w, h); - return srccnv; -}; -Project.drawSVGinCanvas = function (extxml, w, h) { - var srccnv = document.createElement('canvas'); - setCanvasSize(srccnv, w, h); - var ctx = srccnv.getContext('2d'); - for (var i = 0; i < extxml.childElementCount; i++) { - SVG2Canvas.drawLayer(extxml.childNodes[i], ctx, SVG2Canvas.drawLayer); + ////////////////////////////////////////////////// + // load project data + ////////////////////////////////////////////////// + + static recreate (data) { + ScratchJr.log('Project data structures start loading', ScratchJr.getTime(), 'sec'); + mediaCount = 0; + ScratchJr.stage.pages = []; + var pages = data.pages; + pageid = data.currentPage; + for (var i = 0; i < pages.length; i++) { + Project.recreatePage(pages[i], data[pages[i]]); + } + mediaCountBase = mediaCount; } - return srccnv; -}; -Project.maskBorders = function (ctx, w, h) { - ctx.save(); - ctx.globalCompositeOperation = 'destination-in'; - if (Settings.edition != 'PBS') { - ctx.drawImage(BlockSpecs.projectThumb, 0, 0, w, h); + static recreatePage (name, data, fcn) { + var page = new Page(name, data, fcn); + page.div.style.visibility = 'hidden'; } - ctx.restore(); -}; -Project.drawSprites = function (page, scale, c, w, h, fcn) { - var ctx = c.getContext('2d'); - doNext(1); - function doNext (n) { - if (!(n < page.div.childElementCount)) { - Project.maskBorders(c.getContext('2d'), w, h); - fcn(c.toDataURL('image/png')); - } else { - var spr = page.div.childNodes[n].owner; - if (!spr || !spr.shown) { - doNext(n + 1); + static substractCount () { + mediaCount--; + if ((gn('backdrop').className != 'modal-backdrop fade in') || (mediaCountBase == 0)) { + return; + } + Project.setProgress(Project.getMediaLoadRatio(70)); + } + + static recreateObject (page, name, data, callBack, active) { + var list = data.scripts; + //delete data.scripts; + var spr; + data.page = page; + if (data.type == 'sprite') { + mediaCount++; + var fcn = function (spr) { + spr.setPos(data.xcoor, data.ycoor); + mediaCount--; + if (gn('backdrop').className == 'modal-backdrop fade in') { + Project.setProgress(Project.getMediaLoadRatio(70)); + } + ScratchJr.log(spr.name, ScratchJr.getTime(), 'sec'); + if (callBack) { + callBack(spr); + } + }; + if (!data.defaultScale) { + data.defaultScale = 0.5; + } + spr = new Sprite(data, fcn); + // load scripts + var sc = gn(name + '_scripts').owner; + for (var j = 0; j < list.length; j++) { + sc.recreateStrip(list[j]); + } + if (active) { + sc.activate(); } else { - drawLoadedImage(page, ctx, spr.outline, spr, scale, n); + sc.deactivate(); + } + } else { + spr = new Sprite(data, callBack); + } + spr.div.style.opacity = spr.shown ? 1 : 0; + return spr; + } + + ////////////////////////////////////////////////// + // Save project data + ////////////////////////////////////////////////// + + static prepareToSave (id, whenDone) { + if (saving) { + Alert.open(frame, gn('flip'), 'Waiting', '#28A5DA'); + Project.waitUntilSaved(id, whenDone); + } else { + Alert.open(frame, gn('flip'), 'Saving', '#28A5DA'); + Project.save(id, whenDone); + } + } + + static waitUntilSaved (id, fcn) { + if (saving) { + setTimeout(function () { + Project.waitUntilSaved(id, fcn); + }, 500); + } else { + Project.save(id, fcn); + } + } + + // Determine if thumbnailMD5 is unique to projectID + // callback(true/false) + static thumbnailUnique (thumbnailMD5, projectID, callback) { + var json = {}; + json.cond = 'deleted = ? AND id != ? AND gallery IS NULL'; + json.items = ['name', 'thumbnail', 'id']; + json.values = ['NO', projectID]; + IO.query(iOS.database, json, function (result) { + var pdata = JSON.parse(result); + var isUnique = true; + for (var p = 0; p < pdata.length; p++) { + var thispdata = IO.parseProjectData(pdata[p]); + var th = thispdata.thumbnail; + if (th) { + var thumb = (typeof th == 'string') ? JSON.parse(th) : th; + if (thumb && thumb.md5) { + if (thumb.md5 == thumbnailMD5) { + isUnique = false; + } + } + } + } + callback(isUnique); + }); + } + + static save (id, whenDone) { + saving = true; + var th = metadata.thumbnail; + if (th && ScratchJr.editmode != 'storyStarter') { // Don't try to delete the thumbnail in a sample project + var thumb = (typeof th === 'string') ? JSON.parse(th) : th; + if (thumb.md5.indexOf('samples/') < 0) { // In case we've exited story-starter mode + Project.thumbnailUnique(thumb.md5, id, function (isUnique) { + if (isUnique) { + iOS.remove(thumb.md5, iOS.trace); // remove thumb; + } + }); + } + } + metadata.id = id; + metadata.json = Project.getProject(ScratchJr.stage.pages[0].id); + Project.getThumbnailPNG(ScratchJr.stage.pages[0], 192, 144, getMD5); + function getMD5 (dataurl) { + var pngBase64 = dataurl.split(',')[1]; + iOS.getmd5(pngBase64, function (str) { + savePNG(str, pngBase64); + }); + } + + function savePNG (md5, pngBase64) { + var filename = ScratchJr.currentProject + '_' + md5; + iOS.setmedianame(pngBase64, filename, 'png', doNext); + } + + function doNext (md5) { + metadata.thumbnail = { + 'pagecount': ScratchJr.stage.pages.length, + 'md5': md5 + }; + metadata.mtime = (new Date()).getTime().toString(); + IO.saveProject(metadata, saveDone); + } + + function saveDone () { + saving = false; + if (whenDone) { + whenDone(); } } } - function drawLoadedImage (page, ctx, img, spr, scale, n) { - page.drawSpriteImage(ctx, img, spr, scale); - doNext(n + 1); + static getProject (pageid) { + var obj = {}; + obj.pages = ScratchJr.stage.getPagesID(); + obj.currentPage = pageid; + for (var i = 0; i < ScratchJr.stage.pages.length; i++) { + obj[ScratchJr.stage.pages[i].id] = ScratchJr.stage.pages[i].encodePage(); + } + return obj; } -}; + static getUndo () { + return Project.getProject(ScratchJr.stage.currentPage.id); + } + + static encodeSprite (name) { + return gn(name).owner.getData(); + } + + static encodeStrip (b) { + var res = []; + var hasargs = ['playsnd', 'gotopage', 'playusersnd', 'setcolor', 'onmessage', 'message', 'setspeed']; + var loops = ['repeat']; + var carets = ['caretcmd', 'caretend', 'caretstart']; + while (b != null) { + var bt = b.blocktype; + // Don't encode carets in a strip + if (carets.indexOf(bt) > -1) { + b = b.next; + continue; + } + if (bt == 'caretrepeat') { + // Convert repeat carets to actual repeats for the encoding + bt = 'repeat'; + } + var arg = (b.arg != null) || (hasargs.indexOf(bt) > -1) ? b.getArgValue() : null; + if (!arg && (arg != 0)) { + arg = 'null'; + } + var dx = b.div.left / b.scale; + var dy = b.div.top / b.scale; + var data = [bt, arg, dx, dy]; + if (loops.indexOf(bt) > -1) { + var inside = Project.encodeStrip(b.inside); + data.push(inside); + } + res.push(data); + b = b.next; + } + return res; + } + + ///////////////////////////// + // Project PNG Thumbnail + ///////////////////////////// + + static getThumbnailPNG (page, w, h, fcn) { + var scale = w / 480; + var data = {}; + data.pagecount = ScratchJr.stage.pages.length; + var c = document.createElement('canvas'); + setCanvasSize(c, w, h); + var ctx = c.getContext('2d'); + var md5 = page.md5; + ctx.fillStyle = Settings.stageColor; + ctx.fillRect(0, 0, w, h); + if (!md5) { + Project.drawSprites(page, scale, c, w, h, fcn); + } else { + var pcnv; + if (md5.substr(md5.length - 3) == 'png') { + var bgimg = page.div.firstElementChild.firstElementChild; + pcnv = Project.drawPNGInCanvas(bgimg, 480, 360); + } else { + pcnv = Project.drawSVGinCanvas(page.svg, 480, 360); + } + ctx.drawImage(pcnv, 0, 0, 480, 360, 0, 0, w, h); + Project.drawSprites(page, scale, c, w, h, fcn); + } + } + static drawPNGInCanvas (png, w, h) { + var srccnv = document.createElement('canvas'); + setCanvasSize(srccnv, w, h); + var ctx = srccnv.getContext('2d'); + ctx.drawImage(png, 0, 0, w, h); + return srccnv; + } + + static drawSVGinCanvas (extxml, w, h) { + var srccnv = document.createElement('canvas'); + setCanvasSize(srccnv, w, h); + var ctx = srccnv.getContext('2d'); + for (var i = 0; i < extxml.childElementCount; i++) { + SVG2Canvas.drawLayer(extxml.childNodes[i], ctx, SVG2Canvas.drawLayer); + } + return srccnv; + } + + static maskBorders (ctx, w, h) { + ctx.save(); + ctx.globalCompositeOperation = 'destination-in'; + if (Settings.edition != 'PBS') { + ctx.drawImage(BlockSpecs.projectThumb, 0, 0, w, h); + } + ctx.restore(); + } + + static drawSprites (page, scale, c, w, h, fcn) { + var ctx = c.getContext('2d'); + doNext(1); + function doNext (n) { + if (!(n < page.div.childElementCount)) { + Project.maskBorders(c.getContext('2d'), w, h); + fcn(c.toDataURL('image/png')); + } else { + var spr = page.div.childNodes[n].owner; + if (!spr || !spr.shown) { + doNext(n + 1); + } else { + drawLoadedImage(page, ctx, spr.outline, spr, scale, n); + } + } + } + + function drawLoadedImage (page, ctx, img, spr, scale, n) { + page.drawSpriteImage(ctx, img, spr, scale); + doNext(n + 1); + } + + } +} diff --git a/src/editor/ui/Record.js b/src/editor/ui/Record.js index 14b685e..5c10c91 100644 --- a/src/editor/ui/Record.js +++ b/src/editor/ui/Record.js @@ -1,349 +1,367 @@ -var Record = function () {}; +import ScratchJr from '../ScratchJr'; +import Palette from './Palette'; +import Undo from './Undo'; +import iOS from '../../iPad/iOS'; +import ScratchAudio from '../../utils/ScratchAudio'; +import {frame, gn, newHTML, isTablet, isAndroid, setProps} from '../../utils/lib'; -Record.isAndroid = isAndroid; -Record.interval = null; -Record.recordedSound = null; -Record.isRecording = false; -Record.isPlaying = false; -Record.available = true; -Record.error = false; -Record.dialogOpen = false; -Record.timeLimit = null; -Record.playTimeLimit = null; +let interval = null; +let recordedSound = null; +let isRecording = false; +let isPlaying = false; +let available = true; +let error = false; +let dialogOpen = false; +let timeLimit = null; +let playTimeLimit = null; -// Create the recording window, including buttons and volume indicators -Record.init = function () { - var modal = newHTML('div', 'record fade', frame); - modal.setAttribute('id', 'recorddialog'); - var topbar = newHTML('div', 'toolbar', modal); - var actions = newHTML('div', 'actions', topbar); - newHTML('div', 'microphone', actions); - var buttons = newHTML('div', 'recordbuttons', actions); - var okbut = newHTML('div', 'recorddone', buttons); - if (isTablet) { - okbut.ontouchstart = Record.saveSoundAndClose; - } else { - okbut.onmousedown = Record.saveSoundAndClose; +export default class Record { + static get available () { + return available; } - var sc = newHTML('div', 'soundbox', modal); - sc.setAttribute('id', 'soundbox'); - var sv = newHTML('div', 'soundvolume', sc); - sv.setAttribute('id', 'soundvolume'); - for (var i = 0; i < 13; i++) { - var si = newHTML('div', 'indicator', sv); - newHTML('div', 'soundlevel', si); + + static set available (newAvailable) { + available = newAvailable; } - var ctrol = newHTML('div', 'soundcontrols', sc); - ctrol.setAttribute('id', 'soundcontrols'); - var lib = [['record', Record.record], ['stop', Record.stopSnd], ['play', Record.playSnd]]; - for (var j = 0; j < lib.length; j++) { - Record.newToggleClicky(ctrol, 'id_', lib[j][0], lib[j][1]); + + static get dialogOpen () { + return dialogOpen; } -}; -// Dialog box hide/show -Record.appear = function () { - gn('backdrop').setAttribute('class', 'modal-backdrop fade in'); - setProps(gn('backdrop').style, { - display: 'block' - }); - gn('recorddialog').setAttribute('class', 'record fade in'); - ScratchJr.stopStrips(); - Record.dialogOpen = true; - ScratchJr.onBackButtonCallback.push(Record.saveSoundandClose); -}; - -Record.disappear = function () { - setTimeout(function () { - gn('backdrop').setAttribute('class', 'modal-backdrop fade'); - setProps(gn('backdrop').style, { - display: 'none' - }); - gn('recorddialog').setAttribute('class', 'record fade'); - }, 333); - Record.dialogOpen = false; - ScratchJr.onBackButtonCallback.pop(); -}; - -// Register toggle buttons and handlers -Record.newToggleClicky = function (p, prefix, key, fcn) { - var button = newHTML('div', 'controlwrap', p); - newHTML('div', key + 'snd off', button); - button.setAttribute('type', 'toggleclicky'); - button.setAttribute('id', prefix + key); - if (fcn) { + // Create the recording window, including buttons and volume indicators + static init () { + var modal = newHTML('div', 'record fade', frame); + modal.setAttribute('id', 'recorddialog'); + var topbar = newHTML('div', 'toolbar', modal); + var actions = newHTML('div', 'actions', topbar); + newHTML('div', 'microphone', actions); + var buttons = newHTML('div', 'recordbuttons', actions); + var okbut = newHTML('div', 'recorddone', buttons); if (isTablet) { - button.ontouchstart = function (evt) { - fcn(evt); - }; + okbut.ontouchstart = Record.saveSoundAndClose; } else { - button.onmousedown = function (evt) { - fcn(evt); - }; + okbut.onmousedown = Record.saveSoundAndClose; + } + var sc = newHTML('div', 'soundbox', modal); + sc.setAttribute('id', 'soundbox'); + var sv = newHTML('div', 'soundvolume', sc); + sv.setAttribute('id', 'soundvolume'); + for (var i = 0; i < 13; i++) { + var si = newHTML('div', 'indicator', sv); + newHTML('div', 'soundlevel', si); + } + var ctrol = newHTML('div', 'soundcontrols', sc); + ctrol.setAttribute('id', 'soundcontrols'); + var lib = [['record', Record.record], ['stop', Record.stopSnd], ['play', Record.playSnd]]; + for (var j = 0; j < lib.length; j++) { + Record.newToggleClicky(ctrol, 'id_', lib[j][0], lib[j][1]); } } - return button; -}; -// Toggle button appearance on/off -Record.toggleButtonUI = function (button, newState) { - var element = 'id_' + button; - var newStateStr = (newState) ? 'on' : 'off'; - var attrclass = button + 'snd'; - gn(element).childNodes[0].setAttribute('class', attrclass + ' ' + newStateStr); -}; + // Dialog box hide/show + static appear () { + gn('backdrop').setAttribute('class', 'modal-backdrop fade in'); + setProps(gn('backdrop').style, { + display: 'block' + }); + gn('recorddialog').setAttribute('class', 'record fade in'); + ScratchJr.stopStrips(); + dialogOpen = true; + ScratchJr.onBackButtonCallback.push(Record.saveSoundandClose); + } -// Volume UI updater -Record.updateVolume = function (f) { - var num = Math.round(f * 13); - var div = gn('soundvolume'); - if (!Record.isRecording) { - num = 0; - } - for (var i = 0; i < 13; i++) { - div.childNodes[i].childNodes[0].setAttribute('class', ((i > num) ? 'soundlevel off' : 'soundlevel on')); - } -}; - -// Stop recording UI and turn off volume levels -Record.recordUIoff = function () { - Record.toggleButtonUI('record', false); - var div = gn('soundvolume'); - for (var i = 0; i < gn('soundvolume').childElementCount; i++) { - div.childNodes[i].childNodes[0].setAttribute('class', 'soundlevel off'); - } -}; - -// On press record button -Record.record = function (e) { - if (Record.error) { - Record.killRecorder(e); - return; - } - if (Record.isPlaying) { - Record.stopPlayingSound(doRecord); - } else { - doRecord(); - } - function doRecord () { - if (Record.isRecording) { - Record.stopRecording(); // Stop if we're already recording - } else { - iOS.sndrecord(Record.startRecording); // Start a recording - } - } -}; - -Record.startRecording = function (filename) { - if (parseInt(filename) < 0) { - // Error in getting record filename - go back to editor - Record.recordedSound = undefined; - Record.isRecording = false; - Record.killRecorder(); - Palette.selectCategory(3); - } else { - // Save recording's filename for later - Record.recordedSound = filename; - Record.isRecording = true; - Record.error = false; - Record.soundname = filename; - Record.toggleButtonUI('record', true); - var poll = function () { - iOS.volume(Record.updateVolume, Record.recordError); - }; - Record.interval = setInterval(poll, 33); - Record.timeLimit = setTimeout(function () { - if (Record.isRecording) { - Record.stopRecording(); - } - }, 60000); - } -}; - -// Press the play button -Record.playSnd = function (e) { - if (Record.error) { - Record.killRecorder(e); - return; - } - if (!Record.recordedSound) { - return; - } - if (Record.isPlaying) { - Record.stopPlayingSound(); - } else { - if (Record.isRecording) { - Record.stopRecording(Record.startPlaying); - } else { - Record.startPlaying(); - } - } -}; - -// Start playing the sound and switch UI appropriately -Record.startPlaying = function () { - iOS.startplay(Record.timeOutPlay); - Record.toggleButtonUI('play', true); - Record.isPlaying = true; -}; - -// Gets the sound duration from iOS and changes play UI state after time -Record.timeOutPlay = function (timeout) { - if (parseInt(timeout) < 0) { - timeout = 0.1; // Error - stop playing immediately - } - Record.playTimeLimit = setTimeout(function () { - Record.toggleButtonUI('play', false); - Record.isPlaying = false; - }, timeout * 1000); -}; - -// Press on stop -Record.stopSnd = function (e) { - if (Record.error) { - Record.killRecorder(e); - return; - } - if (!Record.recordedSound) { - return; - } - Record.flashStopButton(); - if (Record.isRecording) { - Record.stopRecording(); - } else if (Record.isPlaying) { - Record.stopPlayingSound(); - } -}; - -Record.flashStopButton = function () { - Record.toggleButtonUI('stop', true); - setTimeout(function () { - Record.toggleButtonUI('stop', false); - }, 200); -}; - -// Stop playing the sound and switch UI appropriately -Record.stopPlayingSound = function (fcn) { - iOS.stopplay(fcn); - Record.toggleButtonUI('play', false); - Record.isPlaying = false; - window.clearTimeout(Record.playTimeLimit); - Record.playTimeLimit = null; -}; - -// Stop the volume monitor and recording -Record.stopRecording = function (fcn) { - if (Record.timeLimit != null) { - clearTimeout(Record.timeLimit); - Record.timeLimit = null; - } - if (Record.interval != null) { - window.clearInterval(Record.interval); - Record.interval = null; + static disappear () { setTimeout(function () { - Record.volumeCheckStopped(fcn); - }, 33); - } else { - Record.volumeCheckStopped(fcn); + gn('backdrop').setAttribute('class', 'modal-backdrop fade'); + setProps(gn('backdrop').style, { + display: 'none' + }); + gn('recorddialog').setAttribute('class', 'record fade'); + }, 333); + dialogOpen = false; + ScratchJr.onBackButtonCallback.pop(); } -}; -Record.volumeCheckStopped = function (fcn) { - Record.isRecording = false; - Record.recordUIoff(); - iOS.recordstop(fcn); -}; - -// Press OK (check) -Record.saveSoundAndClose = function () { - if (Record.error || !Record.recordedSound) { - Record.killRecorder(); - } else { - if (Record.isPlaying) { - Record.stopPlayingSound(Record.closeContinueSave); - } else { - if (Record.isRecording) { - Record.stopRecording(Record.closeContinueSave); + // Register toggle buttons and handlers + static newToggleClicky (p, prefix, key, fcn) { + var button = newHTML('div', 'controlwrap', p); + newHTML('div', key + 'snd off', button); + button.setAttribute('type', 'toggleclicky'); + button.setAttribute('id', prefix + key); + if (fcn) { + if (isTablet) { + button.ontouchstart = function (evt) { + fcn(evt); + }; } else { - Record.closeContinueSave(); + button.onmousedown = function (evt) { + fcn(evt); + }; + } + } + return button; + } + + // Toggle button appearance on/off + static toggleButtonUI (button, newState) { + var element = 'id_' + button; + var newStateStr = (newState) ? 'on' : 'off'; + var attrclass = button + 'snd'; + gn(element).childNodes[0].setAttribute('class', attrclass + ' ' + newStateStr); + } + + // Volume UI updater + static updateVolume (f) { + var num = Math.round(f * 13); + var div = gn('soundvolume'); + if (!isRecording) { + num = 0; + } + for (var i = 0; i < 13; i++) { + div.childNodes[i].childNodes[0].setAttribute('class', ((i > num) ? 'soundlevel off' : 'soundlevel on')); + } + } + + // Stop recording UI and turn off volume levels + static recordUIoff () { + Record.toggleButtonUI('record', false); + var div = gn('soundvolume'); + for (var i = 0; i < gn('soundvolume').childElementCount; i++) { + div.childNodes[i].childNodes[0].setAttribute('class', 'soundlevel off'); + } + } + + // On press record button + static record (e) { + if (error) { + Record.killRecorder(e); + return; + } + if (isPlaying) { + Record.stopPlayingSound(doRecord); + } else { + doRecord(); + } + function doRecord () { + if (isRecording) { + Record.stopRecording(); // Stop if we're already recording + } else { + iOS.sndrecord(Record.startRecording); // Start a recording } } } -}; -Record.closeContinueSave = function () { - iOS.recorddisappear('YES', Record.getUserSound); -}; - -Record.closeContinueRemove = function () { - // don't get the sound - proceed right to tearDown - iOS.recorddisappear('NO', Record.tearDownRecorder); -}; - -Record.getUserSound = function () { - Record.isRecording = false; - if (!Record.isAndroid) { - iOS.getmedia(Record.recordedSound, Record.registerProjectSound); - } else { - // On Android, just pass URL - Record.registerProjectSound(null); - } -}; - -Record.registerProjectSound = function (data) { - function loadingDone (snd) { - if (snd != 'error') { - var spr = ScratchJr.getSprite(); - var page = spr.div.parentNode.owner; - spr.sounds.push(Record.recordedSound); - Undo.record({ - action: 'recordsound', - who: spr.id, - where: page.id, - sound: Record.recordedSound - }); - ScratchJr.storyStart('Record.registerProjectSound'); - } - Record.tearDownRecorder(); - Palette.selectCategory(3); - } - if (!Record.isAndroid) { - ScratchAudio.loadFromData(Record.recordedSound, data, loadingDone); - } else { - // On Android, just pass URL - ScratchAudio.loadFromLocal(Record.recordedSound, loadingDone); - } -}; - -// Called on error - remove everything and hide the recorder -Record.killRecorder = function () { - // Inform iOS and then tear-down - if (Record.isPlaying) { - Record.stopPlayingSound(Record.closeContinueRemove); // stop playing and tear-down - } else { - if (Record.isRecording) { - Record.stopRecording(Record.closeContinueRemove); // stop recording and tear-down + static startRecording (filename) { + if (parseInt(filename) < 0) { + // Error in getting record filename - go back to editor + recordedSound = undefined; + isRecording = false; + Record.killRecorder(); + Palette.selectCategory(3); } else { - Record.closeContinueRemove(); + // Save recording's filename for later + recordedSound = filename; + isRecording = true; + error = false; + Record.soundname = filename; + Record.toggleButtonUI('record', true); + var poll = function () { + iOS.volume(Record.updateVolume, Record.recordError); + }; + interval = setInterval(poll, 33); + timeLimit = setTimeout(function () { + if (isRecording) { + Record.stopRecording(); + } + }, 60000); } } -}; -Record.tearDownRecorder = function () { - // Clear errors - if (Record.error) { - Record.error = false; + // Press the play button + static playSnd (e) { + if (error) { + Record.killRecorder(e); + return; + } + if (!recordedSound) { + return; + } + if (isPlaying) { + Record.stopPlayingSound(); + } else { + if (isRecording) { + Record.stopRecording(Record.startPlaying); + } else { + Record.startPlaying(); + } + } } - // Refresh audio context - ScratchAudio.firstTime = true; - Record.isRecording = false; - Record.recordedSound = null; - // Hide the dialog - Record.disappear(); -}; -// Called when the app is put into the background -Record.recordError = function () { - Record.error = true; - Record.killRecorder(); -}; + // Start playing the sound and switch UI appropriately + static startPlaying () { + iOS.startplay(Record.timeOutPlay); + Record.toggleButtonUI('play', true); + isPlaying = true; + } + + // Gets the sound duration from iOS and changes play UI state after time + static timeOutPlay (timeout) { + if (parseInt(timeout) < 0) { + timeout = 0.1; // Error - stop playing immediately + } + playTimeLimit = setTimeout(function () { + Record.toggleButtonUI('play', false); + isPlaying = false; + }, timeout * 1000); + } + + // Press on stop + static stopSnd (e) { + if (error) { + Record.killRecorder(e); + return; + } + if (!recordedSound) { + return; + } + Record.flashStopButton(); + if (isRecording) { + Record.stopRecording(); + } else if (isPlaying) { + Record.stopPlayingSound(); + } + } + + static flashStopButton () { + Record.toggleButtonUI('stop', true); + setTimeout(function () { + Record.toggleButtonUI('stop', false); + }, 200); + } + + // Stop playing the sound and switch UI appropriately + static stopPlayingSound (fcn) { + iOS.stopplay(fcn); + Record.toggleButtonUI('play', false); + isPlaying = false; + window.clearTimeout(playTimeLimit); + playTimeLimit = null; + } + + // Stop the volume monitor and recording + static stopRecording (fcn) { + if (timeLimit != null) { + clearTimeout(timeLimit); + timeLimit = null; + } + if (interval != null) { + window.clearInterval(interval); + interval = null; + setTimeout(function () { + Record.volumeCheckStopped(fcn); + }, 33); + } else { + Record.volumeCheckStopped(fcn); + } + } + + static volumeCheckStopped (fcn) { + isRecording = false; + Record.recordUIoff(); + iOS.recordstop(fcn); + } + + // Press OK (check) + static saveSoundAndClose () { + if (error || !recordedSound) { + Record.killRecorder(); + } else { + if (isPlaying) { + Record.stopPlayingSound(Record.closeContinueSave); + } else { + if (isRecording) { + Record.stopRecording(Record.closeContinueSave); + } else { + Record.closeContinueSave(); + } + } + } + } + + static closeContinueSave () { + iOS.recorddisappear('YES', Record.getUserSound); + } + + static closeContinueRemove () { + // don't get the sound - proceed right to tearDown + iOS.recorddisappear('NO', Record.tearDownRecorder); + } + + static getUserSound () { + isRecording = false; + if (!isAndroid) { + iOS.getmedia(recordedSound, Record.registerProjectSound); + } else { + // On Android, just pass URL + Record.registerProjectSound(null); + } + } + + static registerProjectSound (data) { + function loadingDone (snd) { + if (snd != 'error') { + var spr = ScratchJr.getSprite(); + var page = spr.div.parentNode.owner; + spr.sounds.push(recordedSound); + Undo.record({ + action: 'recordsound', + who: spr.id, + where: page.id, + sound: recordedSound + }); + ScratchJr.storyStart('Record.registerProjectSound'); + } + Record.tearDownRecorder(); + Palette.selectCategory(3); + } + if (!isAndroid) { + ScratchAudio.loadFromData(recordedSound, data, loadingDone); + } else { + // On Android, just pass URL + ScratchAudio.loadFromLocal(recordedSound, loadingDone); + } + } + + // Called on error - remove everything and hide the recorder + static killRecorder () { + // Inform iOS and then tear-down + if (isPlaying) { + Record.stopPlayingSound(Record.closeContinueRemove); // stop playing and tear-down + } else { + if (isRecording) { + Record.stopRecording(Record.closeContinueRemove); // stop recording and tear-down + } else { + Record.closeContinueRemove(); + } + } + } + + static tearDownRecorder () { + // Clear errors + if (error) { + error = false; + } + // Refresh audio context + ScratchAudio.firstTime = true; + isRecording = false; + recordedSound = null; + // Hide the dialog + Record.disappear(); + } + + // Called when the app is put into the background + static recordError () { + error = true; + Record.killRecorder(); + } +} diff --git a/src/editor/ui/ScriptsPane.js b/src/editor/ui/ScriptsPane.js index 77a38ee..8b00f1f 100644 --- a/src/editor/ui/ScriptsPane.js +++ b/src/editor/ui/ScriptsPane.js @@ -1,308 +1,328 @@ -var ScriptsPane = function () {}; -ScriptsPane.scroll = undefined; +import ScratchJr from '../ScratchJr'; +import Project from './Project'; +import Thumbs from './Thumbs'; +import Palette from './Palette'; +import Undo from './Undo'; +import Events from '../../utils/Events'; +import Scroll from './Scroll'; +import Menu from './Menu'; +import ScratchAudio from '../../utils/ScratchAudio'; +import {frame, gn, localx, localy, newHTML, isTablet, + globalx, globaly, setCanvasSize, getDocumentHeight, frame} from '../../utils/lib'; -ScriptsPane.watermark; +let scroll = undefined; +let watermark; -ScriptsPane.createScripts = function (parent) { - var div = newHTML('div', 'scripts', parent); - div.setAttribute('id', 'scripts'); - ScriptsPane.watermark = newHTML('div', 'watermark', div); - var h = Math.max(getDocumentHeight(), frame.offsetHeight); - setCanvasSize(div, div.offsetWidth, h - div.offsetTop); - ScriptsPane.scroll = new Scroll(div, 'scriptscontainer', div.offsetWidth, - h - div.offsetTop, ScratchJr.getActiveScript, ScratchJr.getBlocks); -}; - -ScriptsPane.setActiveScript = function (sprname) { - var currentsc = gn(sprname + '_scripts'); - if (!currentsc) { - // Sprite not found - return; +export default class ScriptsPane { + static get scroll () { + return scroll; } - ScratchJr.stage.currentPage.setCurrentSprite(gn(sprname).owner); - currentsc.owner.activate(); - currentsc.parentNode.ontouchstart = function (evt) { - currentsc.owner.scriptsMouseDown(evt); - }; - ScriptsPane.scroll.update(); -}; -ScriptsPane.runBlock = function (e, div) { - e.preventDefault(); - e.stopPropagation(); - var b = div.owner.findFirst(); - // if (b.aStart) b = b.next; - if (!b) { - return; + static get watermark () { + return watermark; } - ScratchJr.runtime.addRunScript(ScratchJr.getSprite(), b); - ScratchJr.startCurrentPageStrips(['ontouch']); - ScratchJr.userStart = true; -}; -ScriptsPane.prepareToDrag = function (e) { - e.preventDefault(); - var pt = Events.getTargetPoint(e); - ScriptsPane.pickBlock(pt.x, pt.y, e); -}; + static createScripts (parent) { + var div = newHTML('div', 'scripts', parent); + div.setAttribute('id', 'scripts'); + watermark = newHTML('div', 'watermark', div); + var h = Math.max(getDocumentHeight(), frame.offsetHeight); + setCanvasSize(div, div.offsetWidth, h - div.offsetTop); + scroll = new Scroll(div, 'scriptscontainer', div.offsetWidth, + h - div.offsetTop, ScratchJr.getActiveScript, ScratchJr.getBlocks); + } -ScriptsPane.pickBlock = function (x, y, e) { - if (!ScratchJr.runtime.inactive()) { - ScratchJr.stopStrips(); - } - ScriptsPane.cleanCarets(); - ScratchJr.unfocus(e); - var sc = ScratchJr.getActiveScript().owner; - sc.dragList = sc.findGroup(Events.dragthumbnail.owner); - sc.flowCaret = null; - var sy = Events.dragthumbnail.parentNode.scrollTop; - var sx = Events.dragthumbnail.parentNode.scrollLeft; - Events.dragmousex = x; - Events.dragmousey = y; - var lpt = { - x: localx(Events.dragthumbnail.parentNode, x), - y: localy(Events.dragthumbnail.parentNode, y) - }; - var mx = Events.dragmousex - globalx(Events.dragDiv) - lpt.x + Events.dragthumbnail.left; - var my = Events.dragmousey - globaly(Events.dragDiv) - lpt.y + Events.dragthumbnail.top; - var mtx = new WebKitCSSMatrix(window.getComputedStyle(Events.dragthumbnail).webkitTransform); - my -= sy; - mx -= sx; - Events.dragcanvas = Events.dragthumbnail; - Events.dragcanvas.origin = 'scripts'; - Events.dragcanvas.startx = mtx.m41; - Events.dragcanvas.starty = mtx.m42; - if (!Events.dragcanvas.isReporter && Events.dragcanvas.parentNode) { - Events.dragcanvas.parentNode.removeChild(Events.dragcanvas); - } - Events.move3D(Events.dragcanvas, mx, my); - Events.dragcanvas.style.zIndex = ScratchJr.dragginLayer; - Events.dragDiv.appendChild(Events.dragcanvas); - var b = Events.dragcanvas.owner; - b.detachBlock(); - // b.lift(); - if (Events.dragcanvas.isReporter) { - return; - } - ScratchJr.getActiveScript().owner.prepareCaret(b); - for (var i = 1; i < sc.dragList.length; i++) { - b = sc.dragList[i]; - var pos = new WebKitCSSMatrix(window.getComputedStyle(b.div).webkitTransform); - var dx = pos.m41 - mtx.m41; - var dy = pos.m42 - mtx.m42; - b.moveBlock(dx, dy); - // b.lift(); - Events.dragcanvas.appendChild(b.div); - } -}; - -//////////////////////////////////////////////// -// Events MouseMove -//////////////////////////////////////////////// - -ScriptsPane.draggingBlock = function (e) { - e.preventDefault(); - var pt = Events.getTargetPoint(e); - var dx = pt.x - Events.dragmousex; - var dy = pt.y - Events.dragmousey; - Events.move3D(Events.dragcanvas, dx, dy); - ScriptsPane.blockFeedback(Events.dragcanvas.left, Events.dragcanvas.top, e); -}; - -ScriptsPane.blockFeedback = function (dx, dy, e) { - var script = ScratchJr.getActiveScript().owner; - var limit = gn('palette').parentNode.offsetTop + gn('palette').parentNode.offsetHeight; - var ycor = dy + Events.dragcanvas.offsetHeight; - if (ycor < limit) { - script.removeCaret(); - } else { - script.removeCaret(); - script.insertCaret(dx, dy); - } - var thumb; - switch (Palette.getLandingPlace(script.dragList[0].div, e)) { - case 'library': - thumb = Palette.getHittedThumb(script.dragList[0].div, gn('spritecc')); - if (thumb && (gn(thumb.owner).owner.type == ScratchJr.getSprite().type)) { - Thumbs.quickHighlight(thumb); - } else { - thumb = undefined; + static setActiveScript (sprname) { + var currentsc = gn(sprname + '_scripts'); + if (!currentsc) { + // Sprite not found + return; } + ScratchJr.stage.currentPage.setCurrentSprite(gn(sprname).owner); + currentsc.owner.activate(); + currentsc.parentNode.ontouchstart = function (evt) { + currentsc.owner.scriptsMouseDown(evt); + }; + scroll.update(); + } + + static runBlock (e, div) { + e.preventDefault(); + e.stopPropagation(); + var b = div.owner.findFirst(); + // if (b.aStart) b = b.next; + if (!b) { + return; + } + ScratchJr.runtime.addRunScript(ScratchJr.getSprite(), b); + ScratchJr.startCurrentPageStrips(['ontouch']); + ScratchJr.userStart = true; + } + + static prepareToDrag (e) { + e.preventDefault(); + var pt = Events.getTargetPoint(e); + ScriptsPane.pickBlock(pt.x, pt.y, e); + } + + static pickBlock (x, y, e) { + if (!ScratchJr.runtime.inactive()) { + ScratchJr.stopStrips(); + } + ScriptsPane.cleanCarets(); + ScratchJr.unfocus(e); + var sc = ScratchJr.getActiveScript().owner; + sc.dragList = sc.findGroup(Events.dragthumbnail.owner); + sc.flowCaret = null; + var sy = Events.dragthumbnail.parentNode.scrollTop; + var sx = Events.dragthumbnail.parentNode.scrollLeft; + Events.dragmousex = x; + Events.dragmousey = y; + var lpt = { + x: localx(Events.dragthumbnail.parentNode, x), + y: localy(Events.dragthumbnail.parentNode, y) + }; + var mx = Events.dragmousex - globalx(Events.dragDiv) - lpt.x + Events.dragthumbnail.left; + var my = Events.dragmousey - globaly(Events.dragDiv) - lpt.y + Events.dragthumbnail.top; + var mtx = new WebKitCSSMatrix(window.getComputedStyle(Events.dragthumbnail).webkitTransform); + my -= sy; + mx -= sx; + Events.dragcanvas = Events.dragthumbnail; + Events.dragcanvas.origin = 'scripts'; + Events.dragcanvas.startx = mtx.m41; + Events.dragcanvas.starty = mtx.m42; + if (!Events.dragcanvas.isReporter && Events.dragcanvas.parentNode) { + Events.dragcanvas.parentNode.removeChild(Events.dragcanvas); + } + Events.move3D(Events.dragcanvas, mx, my); + Events.dragcanvas.style.zIndex = ScratchJr.dragginLayer; + Events.dragDiv.appendChild(Events.dragcanvas); + var b = Events.dragcanvas.owner; + b.detachBlock(); + // b.lift(); + if (Events.dragcanvas.isReporter) { + return; + } + ScratchJr.getActiveScript().owner.prepareCaret(b); + for (var i = 1; i < sc.dragList.length; i++) { + b = sc.dragList[i]; + var pos = new WebKitCSSMatrix(window.getComputedStyle(b.div).webkitTransform); + var dx = pos.m41 - mtx.m41; + var dy = pos.m42 - mtx.m42; + b.moveBlock(dx, dy); + // b.lift(); + Events.dragcanvas.appendChild(b.div); + } + } + + //////////////////////////////////////////////// + // Events MouseMove + //////////////////////////////////////////////// + + static draggingBlock (e) { + e.preventDefault(); + var pt = Events.getTargetPoint(e); + var dx = pt.x - Events.dragmousex; + var dy = pt.y - Events.dragmousey; + Events.move3D(Events.dragcanvas, dx, dy); + ScriptsPane.blockFeedback(Events.dragcanvas.left, Events.dragcanvas.top, e); + } + + static blockFeedback (dx, dy, e) { + var script = ScratchJr.getActiveScript().owner; + var limit = gn('palette').parentNode.offsetTop + gn('palette').parentNode.offsetHeight; + var ycor = dy + Events.dragcanvas.offsetHeight; + if (ycor < limit) { + script.removeCaret(); + } else { + script.removeCaret(); + script.insertCaret(dx, dy); + } + var thumb; + switch (Palette.getLandingPlace(script.dragList[0].div, e)) { + case 'library': + thumb = Palette.getHittedThumb(script.dragList[0].div, gn('spritecc')); + if (thumb && (gn(thumb.owner).owner.type == ScratchJr.getSprite().type)) { + Thumbs.quickHighlight(thumb); + } else { + thumb = undefined; + } + for (var i = 0; i < gn('spritecc').childElementCount; i++) { + var spr = gn('spritecc').childNodes[i]; + if (spr.nodeName == 'FORM') { + continue; + } + if (thumb && (thumb.id != spr.id)) { + Thumbs.quickRestore(spr); + } + } + break; + default: + ScriptsPane.removeLibCaret(); + break; + } + } + + + //////////////////////////////////////////////// + // Events MouseUP + //////////////////////////////////////////////// + + static dropBlock (e, el) { + e.preventDefault(); + var sc = ScratchJr.getActiveScript(); + var spr = sc.owner.spr.id; + var page = ScratchJr.stage.currentPage; + switch (Palette.getLandingPlace(el, e)) { + case 'scripts': + var dx = localx(sc, el.left); + var dy = localy(sc, el.top); + ScriptsPane.blockDropped(sc, dx, dy); + break; + case 'library': + var thumb = Palette.getHittedThumb(el, gn('spritecc')); + ScriptsPane.blockDropped(ScratchJr.getActiveScript(), el.startx, el.starty); + if (thumb && (gn(thumb.owner).owner.type == gn(page.currentSpriteName).owner.type)) { + ScratchJr.storyStart('ScriptsPane.dropBlock:library'); + ScratchAudio.sndFX('copy.wav'); + Thumbs.quickHighlight(thumb); + setTimeout(function () { + Thumbs.quickRestore(thumb); + }, 300); + sc = gn(thumb.owner + '_scripts').owner; + var strip = Project.encodeStrip(el.owner); + var firstblock = strip[0]; + var delta = sc.gettopblocks().length * 3; + firstblock[2] = firstblock[2] + delta; + firstblock[3] = firstblock[3] + delta; + sc.recreateStrip(strip); + spr = thumb.owner; + } + break; + default: + ScratchJr.getActiveScript().owner.deleteBlocks(); + scroll.adjustCanvas(); + scroll.refresh(); + scroll.fitToScreen(); + break; + } + Undo.record({ + action: 'scripts', + where: page.id, + who: spr + }); + ScratchJr.getActiveScript().owner.dragList = []; + } + + static blockDropped (sc, dx, dy) { + Events.dragcanvas.style.zIndex = ''; + var script = ScratchJr.getActiveScript().owner; + ScriptsPane.cleanCarets(); + script.addBlockToScripts(Events.dragcanvas, dx, dy); + script.layout(Events.dragcanvas.owner); + if (sc.id == ScratchJr.getActiveScript().id) { + scroll.adjustCanvas(); + scroll.refresh(); + scroll.bounceBack(); + } + } + + static cleanCarets () { + ScratchJr.getActiveScript().owner.removeCaret(); + ScriptsPane.removeLibCaret(); + } + + static removeLibCaret () { for (var i = 0; i < gn('spritecc').childElementCount; i++) { var spr = gn('spritecc').childNodes[i]; if (spr.nodeName == 'FORM') { continue; } - if (thumb && (thumb.id != spr.id)) { - Thumbs.quickRestore(spr); + Thumbs.quickRestore(spr); + } + } + + //---------------------------------- + // Drag Script Background + //---------------------------------- + + static dragBackground (e) { + if (Menu.openMenu) { + return; + } + if (isTablet && e.touches && (e.touches.length > 1)) { + return; + } + e.preventDefault(); + e.stopPropagation(); + var sc = ScratchJr.getActiveScript(); + sc.top = sc.offsetTop; + sc.left = sc.offsetLeft; + var pt = Events.getTargetPoint(e); + Events.dragmousex = pt.x; + Events.dragmousey = pt.y; + Events.dragged = false; + ScriptsPane.setDragBackgroundEvents(ScriptsPane.dragMove, ScriptsPane.dragEnd); + } + + static setDragBackgroundEvents (fcnmove, fcnup) { + if (isTablet) { // setDragBackgroundEvents + window.ontouchmove = function (evt) { + fcnmove(evt); + }; + window.ontouchend = function (evt) { + fcnup(evt); + }; + } else { + window.onmousemove = function (evt) { + fcnmove(evt); + }; + window.onmouseup = function (evt) { + fcnup(evt); + }; + } + } + + static dragMove (e) { + var pt = Events.getTargetPoint(e); + if (!Events.dragged && (Events.distance(Events.dragmousex - pt.x, Events.dragmousey - pt.y) < 5)) { + return; + } + Events.dragged = true; + var dx = pt.x - Events.dragmousex; + var dy = pt.y - Events.dragmousey; + Events.dragmousex = pt.x; + Events.dragmousey = pt.y; + Events.move3D(ScratchJr.getActiveScript(), dx, dy); + scroll.refresh(); + e.preventDefault(); + } + + static dragEnd (e) { + Events.dragged = false; + e.preventDefault(); + Events.clearEvents(); + scroll.bounceBack(); + } + + ////////////////////// + // + ////////////////////// + + static updateScriptsPageBlocks (list) { + for (var j = 0; j < list.length; j++) { + if (!gn(list[j] + '_scripts')) { + continue; + } + var sc = gn(list[j] + '_scripts').owner; + if (!sc) { + continue; + } + var allblocks = sc.getBlocks(); + for (var i = 0; i < allblocks.length; i++) { + allblocks[i].updateBlock(); } } - break; - default: - ScriptsPane.removeLibCaret(); - break; } -}; - - -//////////////////////////////////////////////// -// Events MouseUP -//////////////////////////////////////////////// - -ScriptsPane.dropBlock = function (e, el) { - e.preventDefault(); - var sc = ScratchJr.getActiveScript(); - var spr = sc.owner.spr.id; - var page = ScratchJr.stage.currentPage; - switch (Palette.getLandingPlace(el, e)) { - case 'scripts': - var dx = localx(sc, el.left); - var dy = localy(sc, el.top); - ScriptsPane.blockDropped(sc, dx, dy); - break; - case 'library': - var thumb = Palette.getHittedThumb(el, gn('spritecc')); - ScriptsPane.blockDropped(ScratchJr.getActiveScript(), el.startx, el.starty); - if (thumb && (gn(thumb.owner).owner.type == gn(page.currentSpriteName).owner.type)) { - ScratchJr.storyStart('ScriptsPane.dropBlock:library'); - ScratchAudio.sndFX('copy.wav'); - Thumbs.quickHighlight(thumb); - setTimeout(function () { - Thumbs.quickRestore(thumb); - }, 300); - sc = gn(thumb.owner + '_scripts').owner; - var strip = Project.encodeStrip(el.owner); - var firstblock = strip[0]; - var delta = sc.gettopblocks().length * 3; - firstblock[2] = firstblock[2] + delta; - firstblock[3] = firstblock[3] + delta; - sc.recreateStrip(strip); - spr = thumb.owner; - } - break; - default: - ScratchJr.getActiveScript().owner.deleteBlocks(); - ScriptsPane.scroll.adjustCanvas(); - ScriptsPane.scroll.refresh(); - ScriptsPane.scroll.fitToScreen(); - break; - } - Undo.record({ - action: 'scripts', - where: page.id, - who: spr - }); - ScratchJr.getActiveScript().owner.dragList = []; -}; - -ScriptsPane.blockDropped = function (sc, dx, dy) { - Events.dragcanvas.style.zIndex = ''; - var script = ScratchJr.getActiveScript().owner; - ScriptsPane.cleanCarets(); - script.addBlockToScripts(Events.dragcanvas, dx, dy); - script.layout(Events.dragcanvas.owner); - if (sc.id == ScratchJr.getActiveScript().id) { - ScriptsPane.scroll.adjustCanvas(); - ScriptsPane.scroll.refresh(); - ScriptsPane.scroll.bounceBack(); - } -}; - -ScriptsPane.cleanCarets = function () { - ScratchJr.getActiveScript().owner.removeCaret(); - ScriptsPane.removeLibCaret(); -}; - -ScriptsPane.removeLibCaret = function () { - for (var i = 0; i < gn('spritecc').childElementCount; i++) { - var spr = gn('spritecc').childNodes[i]; - if (spr.nodeName == 'FORM') { - continue; - } - Thumbs.quickRestore(spr); - } -}; - -//---------------------------------- -// Drag Script Background -//---------------------------------- - -ScriptsPane.dragBackground = function (e) { - if (Menu.openMenu) { - return; - } - if (isTablet && e.touches && (e.touches.length > 1)) { - return; - } - e.preventDefault(); - e.stopPropagation(); - var sc = ScratchJr.getActiveScript(); - sc.top = sc.offsetTop; - sc.left = sc.offsetLeft; - var pt = Events.getTargetPoint(e); - Events.dragmousex = pt.x; - Events.dragmousey = pt.y; - Events.dragged = false; - ScriptsPane.setDragBackgroundEvents(ScriptsPane.dragMove, ScriptsPane.dragEnd); -}; - -ScriptsPane.setDragBackgroundEvents = function (fcnmove, fcnup) { - if (isTablet) { // setDragBackgroundEvents - window.ontouchmove = function (evt) { - fcnmove(evt); - }; - window.ontouchend = function (evt) { - fcnup(evt); - }; - } else { - window.onmousemove = function (evt) { - fcnmove(evt); - }; - window.onmouseup = function (evt) { - fcnup(evt); - }; - } -}; - -ScriptsPane.dragMove = function (e) { - var pt = Events.getTargetPoint(e); - if (!Events.dragged && (Events.distance(Events.dragmousex - pt.x, Events.dragmousey - pt.y) < 5)) { - return; - } - Events.dragged = true; - var dx = pt.x - Events.dragmousex; - var dy = pt.y - Events.dragmousey; - Events.dragmousex = pt.x; - Events.dragmousey = pt.y; - Events.move3D(ScratchJr.getActiveScript(), dx, dy); - ScriptsPane.scroll.refresh(); - e.preventDefault(); -}; - -ScriptsPane.dragEnd = function (e) { - Events.dragged = false; - e.preventDefault(); - Events.clearEvents(); - ScriptsPane.scroll.bounceBack(); -}; - -////////////////////// -// -////////////////////// - -ScriptsPane.updateScriptsPageBlocks = function (list) { - for (var j = 0; j < list.length; j++) { - if (!gn(list[j] + '_scripts')) { - continue; - } - var sc = gn(list[j] + '_scripts').owner; - if (!sc) { - continue; - } - var allblocks = sc.getBlocks(); - for (var i = 0; i < allblocks.length; i++) { - allblocks[i].updateBlock(); - } - } -}; +} diff --git a/src/editor/ui/Thumbs.js b/src/editor/ui/Thumbs.js index f73cdaa..bdaf9ad 100644 --- a/src/editor/ui/Thumbs.js +++ b/src/editor/ui/Thumbs.js @@ -2,567 +2,594 @@ // Pages ///////////////////////////////////// -Thumbs = function () {}; -Thumbs.caret = undefined; +import ScratchJr from '../ScratchJr'; +import Palette from './Palette'; +import Page from './Page'; +import ScriptsPane from './ScriptsPane'; +import Undo from './Undo'; +import UI from './UI'; +import Events from '../../utils/Events'; +import ScratchAudio from '../../utils/ScratchAudio'; +import {frame, gn, localx, newHTML, scaleMultiplier, getIdFor, + isTablet, newImage, localy, setProps} from '../../utils/lib'; -Thumbs.updatePages = function () { - var pthumbs = gn('pagecc'); - while (pthumbs.childElementCount > 0) { - pthumbs.removeChild(pthumbs.childNodes[0]); - } - var prev = undefined; - for (var i = 0; i < ScratchJr.stage.pages.length; i++) { - var page = ScratchJr.stage.pages[i]; - page.num = i + 1; - var th = page.pageThumbnail(pthumbs); - th.prev = prev; - if (prev) { - prev.next = th; +let caret = undefined; + +export default class Thumbs { + static updatePages () { + var pthumbs = gn('pagecc'); + while (pthumbs.childElementCount > 0) { + pthumbs.removeChild(pthumbs.childNodes[0]); } - if (page.id == ScratchJr.stage.currentPage.id) { - Thumbs.highlighPage(th); - } else { - Thumbs.unhighlighPage(th); + var prev = undefined; + for (var i = 0; i < ScratchJr.stage.pages.length; i++) { + var page = ScratchJr.stage.pages[i]; + page.num = i + 1; + var th = page.pageThumbnail(pthumbs); + th.prev = prev; + if (prev) { + prev.next = th; + } + if (page.id == ScratchJr.stage.currentPage.id) { + Thumbs.highlighPage(th); + } else { + Thumbs.unhighlighPage(th); + } + ScriptsPane.updateScriptsPageBlocks(JSON.parse(page.sprites)); + prev = th; } - ScriptsPane.updateScriptsPageBlocks(JSON.parse(page.sprites)); - prev = th; - } - if ((ScratchJr.stage.pages.length > 3) || !ScratchJr.isEditable()) { - return; - } - var ep = Thumbs.emptyPage(pthumbs); - ep.prev = prev; - th.next = ep; -}; - -Thumbs.getObjectFor = function (div, id) { - for (var i = 0; i < div.childElementCount; i++) { - if (div.childNodes[i].owner == id) { - return div.childNodes[i]; - } - } - return div.childNodes[0]; -}; - -Thumbs.getType = function (div, str) { - while (div != null) { - if (div.type == str) { - return div; - } - div = div.parentNode; - } - return null; -}; - -Thumbs.pageMouseDown = function (e) { - if (isTablet && e.touches && (e.touches.length > 1)) { - return; - } - if (ScratchJr.onHold) { - return; - } - e.preventDefault(); - e.stopPropagation(); - if (window.event) { - Thumbs.t = window.event.srcElement; - } else { - Thumbs.t = e.target; - } - var tb = Thumbs.getType(Thumbs.t, 'pagethumb'); - if (ScratchJr.shaking && (e.target.className == 'deletethumb')) { - ScratchJr.clearSelection(); - ScratchJr.stage.deletePage(tb.owner); - return; - } - if (ScratchJr.shaking) { - ScratchJr.clearSelection(); - return; - } - if (!tb) { - return; - } - if (!ScratchJr.isEditable() || (gn('pagecc').childElementCount < 3)) { - Thumbs.clickOnPage(e, tb.owner); - } else { - Events.startDrag(e, tb, Thumbs.prepareToDragPage, Thumbs.dropPage, Thumbs.draggingPage, - Thumbs.clickPage, Thumbs.startPageShaking); - } -}; - -Thumbs.prepareToDragPage = function (e) { - e.preventDefault(); - e.stopPropagation(); - ScratchAudio.sndFX('grab.wav'); - var pt = Events.getTargetPoint(e); - Events.dragmousex = pt.x; - Events.dragmousey = pt.y; - var mx = Events.dragmousex - frame.offsetLeft - localx(Events.dragthumbnail, Events.dragmousex); - var my = Events.dragmousey - frame.offsetTop - localy(Events.dragthumbnail, Events.dragmousey); - var mstyle = { - position: 'absolute', - left: '0px', - top: '0px', - zIndex: ScratchJr.dragginLayer - }; - Events.dragcanvas = Events.dragthumbnail; - setProps(Events.dragcanvas.style, mstyle); - Events.move3D(Events.dragcanvas, mx, my); - frame.appendChild(Events.dragcanvas); - Thumbs.caret = newHTML('div', 'pagethumb caret', gn('pagecc')); - Thumbs.caret.prev = Events.dragthumbnail.prev; - Thumbs.caret.next = Events.dragthumbnail.next; - if (Events.dragthumbnail.prev) { - (Events.dragthumbnail.prev).next = Thumbs.caret; - } - if (Events.dragthumbnail.next) { - (Events.dragthumbnail.next).prev = Thumbs.caret; - } - Thumbs.layoutPages(); - Events.dragthumbnail.pos = Thumbs.getPagePos(Events.dragcanvas.top); -}; - -Thumbs.layoutPages = function () { - var thispage = Thumbs.findFirst(); - var p = gn('pagecc'); - while (thispage) { - p.appendChild(thispage); - thispage = thispage.next; - } -}; - -Thumbs.findFirst = function () { - var kid = gn('pagecc').childNodes[0]; - while (kid.prev) { - kid = kid.prev; - } - return kid; -}; - -Thumbs.findLast = function () { - var kid = gn('pagecc').childNodes[0]; - while (kid.next) { - kid = kid.next; - } - return kid; -}; - -Thumbs.getPageOrder = function () { - var page = Thumbs.findFirst(); - var res = []; - while (page) { - var pagename = page.owner; - if (pagename) { - res.push(gn(pagename).owner); - } - page = page.next; - } - return res; -}; - -Thumbs.draggingPage = function (e, el) { - e.preventDefault(); - var pt = Events.getTargetPoint(e); - var dx = pt.x - Events.dragmousex; - var dy = pt.y - Events.dragmousey; - Events.move3D(el, dx, dy); - if (!Thumbs.caret) { - return; - } - Thumbs.removeCaret(); - Thumbs.insertCaret(el); - Thumbs.layoutPages(); -}; - -Thumbs.removeCaret = function () { - var myprev = Thumbs.caret.prev; - var mynext = Thumbs.caret.next; - if (myprev) { - myprev.next = mynext; - } - if (mynext) { - mynext.prev = myprev; - } - Thumbs.caret.prev = undefined; - Thumbs.caret.next = undefined; - var p = Thumbs.caret.parentNode; - if (p) { - p.removeChild(Thumbs.caret); - } -}; - -Thumbs.insertCaret = function (el) { - var pos = Thumbs.getPagePos(el.top); - Thumbs.positionMe(pos, Thumbs.caret); - gn('pagecc').appendChild(Thumbs.caret); -}; - -Thumbs.getPagePos = function (dy) { - var delta = gn('pagecc').childNodes[1].offsetTop - gn('pagecc').childNodes[0].offsetTop; - var pos = Math.floor(localy(gn('pagecc'), dy + (delta / 2)) / delta); - pos = Math.max(0, pos); - var max = Thumbs.getPageOrder().length; - return Math.min(max, pos); -}; - -Thumbs.positionMe = function (pos, elem) { - var beforewho = pos >= gn('pagecc').childElementCount ? undefined : gn('pagecc').childNodes[pos]; - if (!beforewho) { - var last = Thumbs.findLast(); - last.next = elem; - elem.prev = last; - elem.next = undefined; - } else { - var prev = beforewho.prev; - beforewho.prev = elem; - elem.next = beforewho; - if (prev) { - prev.next = elem; - elem.prev = prev; - } - } -}; - -Thumbs.repositionThumb = function (thumb, dy) { - var pos = Thumbs.getPagePos(dy); - if (pos != thumb.pos) { - ScratchAudio.sndFX('snap.wav'); - } - var myprev = thumb.prev; - var mynext = thumb.next; - if (myprev) { - myprev.next = mynext; - } - if (mynext) { - mynext.prev = myprev; - } - Thumbs.positionMe(pos, thumb); -}; - -Thumbs.dropPage = function (e) { - ScratchJr.storyStart('Thumbs.dropPage'); - e.preventDefault(); - if (!Thumbs.caret) { - return; - } - Events.dragthumbnail.prev = Thumbs.caret.prev; - Events.dragthumbnail.next = Thumbs.caret.next; - if (Events.dragthumbnail.prev) { - (Events.dragthumbnail.prev).next = Events.dragthumbnail; - } - if (Events.dragthumbnail.next) { - (Events.dragthumbnail.next).prev = Events.dragthumbnail; - } - if (Thumbs.caret.parentNode) { - Thumbs.caret.parentNode.removeChild(Thumbs.caret); - } - Thumbs.caret = undefined; - Events.dragthumbnail.style.position = ''; - Events.dragthumbnail.style.left = ''; - Events.dragthumbnail.style.top = ''; - Events.dragthumbnail.style.webkitTransform = ''; - var oldpos = Number(Events.dragthumbnail.childNodes[1].childNodes[0].textContent) - 1; - var oldpage = Events.dragthumbnail.owner; - Thumbs.repositionThumb(Events.dragthumbnail, Events.dragthumbnail.top); - var oldlist = ScratchJr.stage.getPagesID(); - ScratchJr.stage.pages = Thumbs.getPageOrder(); - Thumbs.layoutPages(); - Thumbs.updatePages(); - ScratchJr.stage.renumberPageBlocks(oldlist); - if (Palette.numcat == 5) { - Palette.selectCategory(5); - } - if (Thumbs.getPageOrder()[oldpos].id != oldpage) { - Undo.record({ - action: 'pageorder', - who: oldpage, - where: oldpage - }); - } -}; - -Thumbs.clickPage = function (e) { - ScratchJr.clearSelection(); - Thumbs.clickOnPage(e, Events.dragthumbnail.owner); - Events.clearEvents(); - Events.dragthumbnail = undefined; -}; - -Thumbs.clickOnPage = function (e, pagename) { - ScratchJr.unfocus(e); - var pthumbs = gn('pagecc'); - for (var i = 0; i < pthumbs.childElementCount; i++) { - var thumb = pthumbs.childNodes[i]; - if (thumb.id == 'emptypage') { - continue; - } - } - if (ScratchJr.stage.currentPage.id == pagename) { - return; - } - var page = gn(pagename).owner; - ScratchJr.stage.setPage(page, false); - Undo.record({ - action: 'changepage', - who: pagename, - where: pagename - }); -}; - - -Thumbs.startPageShaking = function (tb) { - ScratchJr.shaking = tb; - ScratchJr.stopShaking = Thumbs.stopPageShaking; - var cc = tb.getAttribute('class'); - tb.setAttribute('class', cc + ' shakeme'); - tb.childNodes[tb.childElementCount - 1].style.visibility = 'visible'; -}; - - -Thumbs.stopPageShaking = function (b) { - ScratchJr.shaking = undefined; - ScratchJr.stopShaking = undefined; - var cc = b.getAttribute('class'); - cc = cc.substr(0, cc.length - 8); - b.setAttribute('class', cc); - b.childNodes[b.childElementCount - 1].style.visibility = 'hidden'; -}; - -Thumbs.emptyPage = function (p) { - var tb = newHTML('div', 'pagethumb', p); - var c = newHTML('div', 'empty', tb); - var img; - if (Settings.edition == 'PBS') { - img = newImage(c, 'assets/ui/newpage.svg'); - } else { - img = newImage(c, 'assets/ui/newpage.png', { - position: 'absolute' - }); - } - img.setAttribute('class', 'unselectable'); - tb.setAttribute('id', 'emptypage'); - if (isTablet) { - tb.ontouchstart = function (evt) { - Thumbs.clickOnEmptyPage(evt); - }; - } else { - tb.onmousedown = function (evt) { - Thumbs.clickOnEmptyPage(evt); - }; - } - return tb; -}; - -Thumbs.clickOnEmptyPage = function (e) { - if (isTablet && e.touches && (e.touches.length > 1)) { - return; - } - ScratchAudio.sndFX('tap.wav'); - e.preventDefault(); - ScratchJr.stage.currentPage.div.style.visibility = 'hidden'; - ScratchJr.stage.currentPage.setPageSprites('hidden'); - var sc = gn(ScratchJr.stage.currentPage.currentSpriteName + '_scripts'); - if (sc) { - sc.owner.deactivate(); - } - ScratchJr.unfocus(e); - new Page(getIdFor('page')); -}; - -Thumbs.highlighPage = function (page) { - page.setAttribute('class', 'pagethumb on'); -}; - -Thumbs.unhighlighPage = function (page) { - page.setAttribute('class', 'pagethumb off'); -}; - -Thumbs.overpage = function (page) { - page.setAttribute('class', 'pagethumb drop'); -}; - -////////////////////////////////////// -// Library -///////////////////////////////////// - -Thumbs.updateSprites = function () { - var costumes = gn('spritecc'); - costumes.style.top = '0px'; - while (costumes.childElementCount > 0) { - costumes.removeChild(costumes.childNodes[0]); - } - var sprites = JSON.parse(ScratchJr.stage.currentPage.sprites); - for (var i = 0; i < sprites.length; i++) { - var s = gn(sprites[i]); - if (!s) { - continue; - } - var spr = s.owner; - if (spr.type != 'sprite') { - continue; - } - var th = spr.spriteThumbnail(costumes); - if (spr.id == ScratchJr.stage.currentPage.currentSpriteName) { - Thumbs.highlighSprite(th); - } else { - Thumbs.unhighlighSprite(th); - } - } - if (!ScratchJr.getSprite()) { - ScratchJr.stage.currentPage.setCurrentSprite(undefined); - } - UI.resetSpriteLibrary(); -}; - -Thumbs.updateSprite = function (spr) { - if (!spr) { - return; - } - if (spr.thumbnail) { - spr.updateSpriteThumb(); - } else { - var costumes = gn('spritecc'); - if (spr.type != 'sprite') { + if ((ScratchJr.stage.pages.length > 3) || !ScratchJr.isEditable()) { return; } - spr.spriteThumbnail(costumes); - Thumbs.selectThisSprite(spr); + var ep = Thumbs.emptyPage(pthumbs); + ep.prev = prev; + th.next = ep; + } + + static getObjectFor (div, id) { + for (var i = 0; i < div.childElementCount; i++) { + if (div.childNodes[i].owner == id) { + return div.childNodes[i]; + } + } + return div.childNodes[0]; + } + + static getType (div, str) { + while (div != null) { + if (div.type == str) { + return div; + } + div = div.parentNode; + } + return null; + } + + static pageMouseDown (e) { + if (isTablet && e.touches && (e.touches.length > 1)) { + return; + } + if (ScratchJr.onHold) { + return; + } + e.preventDefault(); + e.stopPropagation(); + if (window.event) { + Thumbs.t = window.event.srcElement; + } else { + Thumbs.t = e.target; + } + var tb = Thumbs.getType(Thumbs.t, 'pagethumb'); + if (ScratchJr.shaking && (e.target.className == 'deletethumb')) { + ScratchJr.clearSelection(); + ScratchJr.stage.deletePage(tb.owner); + return; + } + if (ScratchJr.shaking) { + ScratchJr.clearSelection(); + return; + } + if (!tb) { + return; + } + if (!ScratchJr.isEditable() || (gn('pagecc').childElementCount < 3)) { + Thumbs.clickOnPage(e, tb.owner); + } else { + Events.startDrag(e, tb, Thumbs.prepareToDragPage, Thumbs.dropPage, Thumbs.draggingPage, + Thumbs.clickPage, Thumbs.startPageShaking); + } + } + + static prepareToDragPage (e) { + e.preventDefault(); + e.stopPropagation(); + ScratchAudio.sndFX('grab.wav'); + var pt = Events.getTargetPoint(e); + Events.dragmousex = pt.x; + Events.dragmousey = pt.y; + var mx = Events.dragmousex - frame.offsetLeft - localx(Events.dragthumbnail, Events.dragmousex); + var my = Events.dragmousey - frame.offsetTop - localy(Events.dragthumbnail, Events.dragmousey); + var mstyle = { + position: 'absolute', + left: '0px', + top: '0px', + zIndex: ScratchJr.dragginLayer + }; + Events.dragcanvas = Events.dragthumbnail; + setProps(Events.dragcanvas.style, mstyle); + Events.move3D(Events.dragcanvas, mx, my); + frame.appendChild(Events.dragcanvas); + caret = newHTML('div', 'pagethumb caret', gn('pagecc')); + caret.prev = Events.dragthumbnail.prev; + caret.next = Events.dragthumbnail.next; + if (Events.dragthumbnail.prev) { + (Events.dragthumbnail.prev).next = caret; + } + if (Events.dragthumbnail.next) { + (Events.dragthumbnail.next).prev = caret; + } + Thumbs.layoutPages(); + Events.dragthumbnail.pos = Thumbs.getPagePos(Events.dragcanvas.top); + } + + static layoutPages () { + var thispage = Thumbs.findFirst(); + var p = gn('pagecc'); + while (thispage) { + p.appendChild(thispage); + thispage = thispage.next; + } + } + + static findFirst () { + var kid = gn('pagecc').childNodes[0]; + while (kid.prev) { + kid = kid.prev; + } + return kid; + } + + static findLast () { + var kid = gn('pagecc').childNodes[0]; + while (kid.next) { + kid = kid.next; + } + return kid; + } + + static getPageOrder () { + var page = Thumbs.findFirst(); + var res = []; + while (page) { + var pagename = page.owner; + if (pagename) { + res.push(gn(pagename).owner); + } + page = page.next; + } + return res; + } + + static draggingPage (e, el) { + e.preventDefault(); + var pt = Events.getTargetPoint(e); + var dx = pt.x - Events.dragmousex; + var dy = pt.y - Events.dragmousey; + Events.move3D(el, dx, dy); + if (!caret) { + return; + } + Thumbs.removeCaret(); + Thumbs.insertCaret(el); + Thumbs.layoutPages(); + } + + static removeCaret () { + var myprev = caret.prev; + var mynext = caret.next; + if (myprev) { + myprev.next = mynext; + } + if (mynext) { + mynext.prev = myprev; + } + caret.prev = undefined; + caret.next = undefined; + var p = caret.parentNode; + if (p) { + p.removeChild(caret); + } + } + + static insertCaret (el) { + var pos = Thumbs.getPagePos(el.top); + Thumbs.positionMe(pos, caret); + gn('pagecc').appendChild(caret); + } + + static getPagePos (dy) { + var delta = gn('pagecc').childNodes[1].offsetTop - gn('pagecc').childNodes[0].offsetTop; + var pos = Math.floor(localy(gn('pagecc'), dy + (delta / 2)) / delta); + pos = Math.max(0, pos); + var max = Thumbs.getPageOrder().length; + return Math.min(max, pos); + } + + static positionMe (pos, elem) { + var beforewho = pos >= gn('pagecc').childElementCount ? undefined : gn('pagecc').childNodes[pos]; + if (!beforewho) { + var last = Thumbs.findLast(); + last.next = elem; + elem.prev = last; + elem.next = undefined; + } else { + var prev = beforewho.prev; + beforewho.prev = elem; + elem.next = beforewho; + if (prev) { + prev.next = elem; + elem.prev = prev; + } + } + } + + static repositionThumb (thumb, dy) { + var pos = Thumbs.getPagePos(dy); + if (pos != thumb.pos) { + ScratchAudio.sndFX('snap.wav'); + } + var myprev = thumb.prev; + var mynext = thumb.next; + if (myprev) { + myprev.next = mynext; + } + if (mynext) { + mynext.prev = myprev; + } + Thumbs.positionMe(pos, thumb); + } + + static dropPage (e) { + ScratchJr.storyStart('Thumbs.dropPage'); + e.preventDefault(); + if (!caret) { + return; + } + Events.dragthumbnail.prev = caret.prev; + Events.dragthumbnail.next = caret.next; + if (Events.dragthumbnail.prev) { + (Events.dragthumbnail.prev).next = Events.dragthumbnail; + } + if (Events.dragthumbnail.next) { + (Events.dragthumbnail.next).prev = Events.dragthumbnail; + } + if (caret.parentNode) { + caret.parentNode.removeChild(caret); + } + caret = undefined; + Events.dragthumbnail.style.position = ''; + Events.dragthumbnail.style.left = ''; + Events.dragthumbnail.style.top = ''; + Events.dragthumbnail.style.webkitTransform = ''; + var oldpos = Number(Events.dragthumbnail.childNodes[1].childNodes[0].textContent) - 1; + var oldpage = Events.dragthumbnail.owner; + Thumbs.repositionThumb(Events.dragthumbnail, Events.dragthumbnail.top); + var oldlist = ScratchJr.stage.getPagesID(); + ScratchJr.stage.pages = Thumbs.getPageOrder(); + Thumbs.layoutPages(); + Thumbs.updatePages(); + ScratchJr.stage.renumberPageBlocks(oldlist); + if (Palette.numcat == 5) { + Palette.selectCategory(5); + } + if (Thumbs.getPageOrder()[oldpos].id != oldpage) { + Undo.record({ + action: 'pageorder', + who: oldpage, + where: oldpage + }); + } + } + + static clickPage (e) { + ScratchJr.clearSelection(); + Thumbs.clickOnPage(e, Events.dragthumbnail.owner); + Events.clearEvents(); + Events.dragthumbnail = undefined; + } + + static clickOnPage (e, pagename) { + ScratchJr.unfocus(e); + var pthumbs = gn('pagecc'); + for (var i = 0; i < pthumbs.childElementCount; i++) { + var thumb = pthumbs.childNodes[i]; + if (thumb.id == 'emptypage') { + continue; + } + } + if (ScratchJr.stage.currentPage.id == pagename) { + return; + } + var page = gn(pagename).owner; + ScratchJr.stage.setPage(page, false); + Undo.record({ + action: 'changepage', + who: pagename, + where: pagename + }); + } + + + static startPageShaking (tb) { + ScratchJr.shaking = tb; + ScratchJr.stopShaking = Thumbs.stopPageShaking; + var cc = tb.getAttribute('class'); + tb.setAttribute('class', cc + ' shakeme'); + tb.childNodes[tb.childElementCount - 1].style.visibility = 'visible'; + } + + + static stopPageShaking (b) { + ScratchJr.shaking = undefined; + ScratchJr.stopShaking = undefined; + var cc = b.getAttribute('class'); + cc = cc.substr(0, cc.length - 8); + b.setAttribute('class', cc); + b.childNodes[b.childElementCount - 1].style.visibility = 'hidden'; + } + + static emptyPage (p) { + var tb = newHTML('div', 'pagethumb', p); + var c = newHTML('div', 'empty', tb); + var img; + if (Settings.edition == 'PBS') { + img = newImage(c, 'assets/ui/newpage.svg'); + } else { + img = newImage(c, 'assets/ui/newpage.png', { + position: 'absolute' + }); + } + img.setAttribute('class', 'unselectable'); + tb.setAttribute('id', 'emptypage'); + if (isTablet) { + tb.ontouchstart = function (evt) { + Thumbs.clickOnEmptyPage(evt); + }; + } else { + tb.onmousedown = function (evt) { + Thumbs.clickOnEmptyPage(evt); + }; + } + return tb; + } + + static clickOnEmptyPage (e) { + if (isTablet && e.touches && (e.touches.length > 1)) { + return; + } + ScratchAudio.sndFX('tap.wav'); + e.preventDefault(); + ScratchJr.stage.currentPage.div.style.visibility = 'hidden'; + ScratchJr.stage.currentPage.setPageSprites('hidden'); + var sc = gn(ScratchJr.stage.currentPage.currentSpriteName + '_scripts'); + if (sc) { + sc.owner.deactivate(); + } + ScratchJr.unfocus(e); + new Page(getIdFor('page')); + } + + static highlighPage (page) { + page.setAttribute('class', 'pagethumb on'); + } + + static unhighlighPage (page) { + page.setAttribute('class', 'pagethumb off'); + } + + static overpage (page) { + page.setAttribute('class', 'pagethumb drop'); + } + + ////////////////////////////////////// + // Library + ///////////////////////////////////// + + static updateSprites () { + var costumes = gn('spritecc'); + costumes.style.top = '0px'; + while (costumes.childElementCount > 0) { + costumes.removeChild(costumes.childNodes[0]); + } + var sprites = JSON.parse(ScratchJr.stage.currentPage.sprites); + for (var i = 0; i < sprites.length; i++) { + var s = gn(sprites[i]); + if (!s) { + continue; + } + var spr = s.owner; + if (spr.type != 'sprite') { + continue; + } + var th = spr.spriteThumbnail(costumes); + if (spr.id == ScratchJr.stage.currentPage.currentSpriteName) { + Thumbs.highlighSprite(th); + } else { + Thumbs.unhighlighSprite(th); + } + } + if (!ScratchJr.getSprite()) { + ScratchJr.stage.currentPage.setCurrentSprite(undefined); + } UI.resetSpriteLibrary(); } -}; -///////////////////////////////////////////// -// Sprite Thumbnails -//////////////////////////////////////////// - -Thumbs.startDragThumb = function (e, tb) { - if (ScratchJr.shaking && (e.target.id == 'deletespritethumb')) { - ScratchJr.clearSelection(); - ScratchJr.stage.removeSprite(gn(tb.owner).owner); - } - if (ScratchJr.shaking) { - ScratchJr.clearSelection(); - } - if (!ScratchJr.isEditable()) { - Thumbs.clickOnSprite(e, tb); - } else { - Events.startDrag(e, tb, Thumbs.prepareToDrag, Thumbs.drop, - Thumbs.dragging, Thumbs.click, Thumbs.startCharShaking); - } -}; - -Thumbs.startCharShaking = function (tb) { - if (!tb) { - return; - } - ScratchJr.shaking = tb; - ScratchJr.stopShaking = Thumbs.stopCharShaking; - var cc = tb.getAttribute('class'); - tb.setAttribute('class', cc + ' shakethumb'); - var close = newHTML('div', 'deletespritethumb', tb); - close.id = 'deletespritethumb'; -}; - -Thumbs.stopCharShaking = function (b) { - ScratchJr.shaking = undefined; - ScratchJr.stopShaking = undefined; - var cc = b.getAttribute('class'); - cc = cc.substr(0, cc.length - 8); - b.setAttribute('class', cc); - var ic = b.childNodes[b.childElementCount - 1]; - if (ic.getAttribute('class') == 'deletespritethumb') { - b.removeChild(ic); - } -}; - -Thumbs.selectThisSprite = function (spr) { - var costumes = gn('spritecc'); - var el = spr.thumbnail; - for (var i = 0; i < costumes.childElementCount; i++) { - var th = costumes.childNodes[i]; - if (th == el) { - Thumbs.highlighSprite(el); + static updateSprite (spr) { + if (!spr) { + return; + } + if (spr.thumbnail) { + spr.updateSpriteThumb(); } else { - Thumbs.unhighlighSprite(th); + var costumes = gn('spritecc'); + if (spr.type != 'sprite') { + return; + } + spr.spriteThumbnail(costumes); + Thumbs.selectThisSprite(spr); + UI.resetSpriteLibrary(); } } -}; -Thumbs.clickOnSprite = function (e, el) { - if (ScratchJr.shaking && (ScratchJr.shaking == el)) { - ScratchJr.clearSelection(); - ScratchJr.stage.removeSprite(gn(el.owner).owner); - return; - } - var spritename = el.owner; - if (!gn(spritename)) { - return; - } - ScratchJr.unfocus(e); - var spr = gn(spritename).owner; - var page = spr.div.parentNode.owner; - page.setCurrentSprite(spr); - Thumbs.selectThisSprite(spr); -}; + ///////////////////////////////////////////// + // Sprite Thumbnails + //////////////////////////////////////////// -Thumbs.prepareToDrag = function (e) { - e.preventDefault(); - e.stopPropagation(); - ScratchAudio.sndFX('grab.wav'); - var pt = Events.getTargetPoint(e); - Events.dragmousex = pt.x; - Events.dragmousey = pt.y; - Events.dragthumbnail = Thumbs.getObjectFor(gn('spritecc'), Events.dragthumbnail.owner); - var mx = Events.dragmousex - frame.offsetLeft - - localx(Events.dragthumbnail, Events.dragmousex) - gn('topsection').offsetLeft; - var my = Events.dragmousey - frame.offsetTop - - localy(Events.dragthumbnail, Events.dragmousey) - gn('topsection').offsetTop; - var sy = Events.dragthumbnail.parentNode.parentNode.scrollTop; - var sx = Events.dragthumbnail.parentNode.parentNode.scrollLeft; - my -= sy; - mx -= sx; - var mstyle = { - position: 'absolute', - left: '0px', - top: '0px', - zIndex: ScratchJr.dragginLayer, - zoom: (100 / window.devicePixelRatio) + '%' - }; - var spr = gn(Events.dragthumbnail.owner).owner; - Events.dragcanvas = document.createElement('canvas'); - spr.drawMyImage(Events.dragcanvas, - 76 * scaleMultiplier * window.devicePixelRatio, - (76 - 12) * scaleMultiplier * window.devicePixelRatio - ); - setProps(Events.dragcanvas.style, mstyle); - Events.move3D(Events.dragcanvas, mx * window.devicePixelRatio, my * window.devicePixelRatio); - Events.dragcanvas.owner = Events.dragthumbnail.owner; - frame.appendChild(Events.dragcanvas); -}; - -Thumbs.dragging = function (e, el) { - e.preventDefault(); - var pt = Events.getTargetPoint(e); - var dx = pt.x - Events.dragmousex; - var dy = pt.y - Events.dragmousey; - Events.move3D(el, dx * window.devicePixelRatio, dy * window.devicePixelRatio); - if (Palette.getLandingPlace(el, e, window.devicePixelRatio) != 'pages') { - Thumbs.removePagesCaret(); - return; - } - var thumb = Palette.getHittedThumb(el, gn('pagecc'), window.devicePixelRatio); - if (thumb && !thumb.owner) { - thumb = undefined; - } - if (thumb) { - Thumbs.overpage(thumb); - } - for (var i = 0; i < gn('pagecc').childElementCount; i++) { - var spr = gn('pagecc').childNodes[i]; - if (!spr.owner) { - continue; + static startDragThumb (e, tb) { + if (ScratchJr.shaking && (e.target.id == 'deletespritethumb')) { + ScratchJr.clearSelection(); + ScratchJr.stage.removeSprite(gn(tb.owner).owner); } - var page = gn(spr.owner); - if (thumb && (thumb.id != spr.id)) { + if (ScratchJr.shaking) { + ScratchJr.clearSelection(); + } + if (!ScratchJr.isEditable()) { + Thumbs.clickOnSprite(e, tb); + } else { + Events.startDrag(e, tb, Thumbs.prepareToDrag, Thumbs.drop, + Thumbs.dragging, Thumbs.click, Thumbs.startCharShaking); + } + } + + static startCharShaking (tb) { + if (!tb) { + return; + } + ScratchJr.shaking = tb; + ScratchJr.stopShaking = Thumbs.stopCharShaking; + var cc = tb.getAttribute('class'); + tb.setAttribute('class', cc + ' shakethumb'); + var close = newHTML('div', 'deletespritethumb', tb); + close.id = 'deletespritethumb'; + } + + static stopCharShaking (b) { + ScratchJr.shaking = undefined; + ScratchJr.stopShaking = undefined; + var cc = b.getAttribute('class'); + cc = cc.substr(0, cc.length - 8); + b.setAttribute('class', cc); + var ic = b.childNodes[b.childElementCount - 1]; + if (ic.getAttribute('class') == 'deletespritethumb') { + b.removeChild(ic); + } + } + + static selectThisSprite (spr) { + var costumes = gn('spritecc'); + var el = spr.thumbnail; + for (var i = 0; i < costumes.childElementCount; i++) { + var th = costumes.childNodes[i]; + if (th == el) { + Thumbs.highlighSprite(el); + } else { + Thumbs.unhighlighSprite(th); + } + } + } + + static clickOnSprite (e, el) { + if (ScratchJr.shaking && (ScratchJr.shaking == el)) { + ScratchJr.clearSelection(); + ScratchJr.stage.removeSprite(gn(el.owner).owner); + return; + } + var spritename = el.owner; + if (!gn(spritename)) { + return; + } + ScratchJr.unfocus(e); + var spr = gn(spritename).owner; + var page = spr.div.parentNode.owner; + page.setCurrentSprite(spr); + Thumbs.selectThisSprite(spr); + } + + static prepareToDrag (e) { + e.preventDefault(); + e.stopPropagation(); + ScratchAudio.sndFX('grab.wav'); + var pt = Events.getTargetPoint(e); + Events.dragmousex = pt.x; + Events.dragmousey = pt.y; + Events.dragthumbnail = Thumbs.getObjectFor(gn('spritecc'), Events.dragthumbnail.owner); + var mx = Events.dragmousex - frame.offsetLeft - + localx(Events.dragthumbnail, Events.dragmousex) - gn('topsection').offsetLeft; + var my = Events.dragmousey - frame.offsetTop - + localy(Events.dragthumbnail, Events.dragmousey) - gn('topsection').offsetTop; + var sy = Events.dragthumbnail.parentNode.parentNode.scrollTop; + var sx = Events.dragthumbnail.parentNode.parentNode.scrollLeft; + my -= sy; + mx -= sx; + var mstyle = { + position: 'absolute', + left: '0px', + top: '0px', + zIndex: ScratchJr.dragginLayer, + zoom: (100 / window.devicePixelRatio) + '%' + }; + var spr = gn(Events.dragthumbnail.owner).owner; + Events.dragcanvas = document.createElement('canvas'); + spr.drawMyImage(Events.dragcanvas, + 76 * scaleMultiplier * window.devicePixelRatio, + (76 - 12) * scaleMultiplier * window.devicePixelRatio + ); + setProps(Events.dragcanvas.style, mstyle); + Events.move3D(Events.dragcanvas, mx * window.devicePixelRatio, my * window.devicePixelRatio); + Events.dragcanvas.owner = Events.dragthumbnail.owner; + frame.appendChild(Events.dragcanvas); + } + + static dragging (e, el) { + e.preventDefault(); + var pt = Events.getTargetPoint(e); + var dx = pt.x - Events.dragmousex; + var dy = pt.y - Events.dragmousey; + Events.move3D(el, dx * window.devicePixelRatio, dy * window.devicePixelRatio); + if (Palette.getLandingPlace(el, e, window.devicePixelRatio) != 'pages') { + Thumbs.removePagesCaret(); + return; + } + var thumb = Palette.getHittedThumb(el, gn('pagecc'), window.devicePixelRatio); + if (thumb && !thumb.owner) { + thumb = undefined; + } + if (thumb) { + Thumbs.overpage(thumb); + } + for (var i = 0; i < gn('pagecc').childElementCount; i++) { + var spr = gn('pagecc').childNodes[i]; + if (!spr.owner) { + continue; + } + var page = gn(spr.owner); + if (thumb && (thumb.id != spr.id)) { + if (page.owner.id == ScratchJr.stage.currentPage.id) { + Thumbs.highlighPage(spr); + } else { + Thumbs.unhighlighPage(spr); + } + } + } + } + + static removePagesCaret () { + for (var i = 0; i < gn('pagecc').childElementCount; i++) { + var spr = gn('pagecc').childNodes[i]; + if (!spr.owner) { + continue; + } + var page = gn(spr.owner); if (page.owner.id == ScratchJr.stage.currentPage.id) { Thumbs.highlighPage(spr); } else { @@ -570,82 +597,67 @@ Thumbs.dragging = function (e, el) { } } } -}; -Thumbs.removePagesCaret = function () { - for (var i = 0; i < gn('pagecc').childElementCount; i++) { - var spr = gn('pagecc').childNodes[i]; - if (!spr.owner) { - continue; + static drop (e, el) { + e.preventDefault(); + switch (Palette.getLandingPlace(el, e, window.devicePixelRatio)) { + case 'pages': + var thumb = Palette.getHittedThumb(el, gn('pagecc'), window.devicePixelRatio); + if (thumb && thumb.id != 'emptypage') { + ScratchJr.stage.copySprite(el, thumb); + } + break; + default: + break; } - var page = gn(spr.owner); - if (page.owner.id == ScratchJr.stage.currentPage.id) { - Thumbs.highlighPage(spr); + if (Events.dragcanvas) { + Events.dragcanvas.parentNode.removeChild(Events.dragcanvas); + } + Events.dragcanvas = undefined; + } + + static click (e, el) { + e.preventDefault(); + e.stopPropagation(); + if (window.event) { + Thumbs.t = window.event.srcElement; } else { - Thumbs.unhighlighPage(spr); + Thumbs.t = e.target; + } + el.setAttribute('class', ScratchJr.isEditable() ? 'spritethumb on' : 'spritethumb noneditable'); + Thumbs.clickOnSprite(e, el); + } + + static highlighSprite (spr) { + spr.setAttribute('class', ScratchJr.isEditable() ? 'spritethumb on' : 'spritethumb noneditable'); + ScriptsPane.setActiveScript(spr.owner); + Palette.reset(); + } + + static unhighlighSprite (spr) { + spr.setAttribute('class', 'spritethumb off'); + var currentsc = gn(spr.owner + '_scripts'); + currentsc.owner.deactivate(); + for (var i = 0; i < currentsc.childElementCount; i++) { + if (currentsc.childNodes[i].owner) { + currentsc.childNodes[i].owner.unhighlight(); + } } } -}; -Thumbs.drop = function (e, el) { - e.preventDefault(); - switch (Palette.getLandingPlace(el, e, window.devicePixelRatio)) { - case 'pages': - var thumb = Palette.getHittedThumb(el, gn('pagecc'), window.devicePixelRatio); - if (thumb && thumb.id != 'emptypage') { - ScratchJr.stage.copySprite(el, thumb); - } - break; - default: - break; - } - if (Events.dragcanvas) { - Events.dragcanvas.parentNode.removeChild(Events.dragcanvas); - } - Events.dragcanvas = undefined; -}; - -Thumbs.click = function (e, el) { - e.preventDefault(); - e.stopPropagation(); - if (window.event) { - Thumbs.t = window.event.srcElement; - } else { - Thumbs.t = e.target; - } - el.setAttribute('class', ScratchJr.isEditable() ? 'spritethumb on' : 'spritethumb noneditable'); - Thumbs.clickOnSprite(e, el); -}; - -Thumbs.highlighSprite = function (spr) { - spr.setAttribute('class', ScratchJr.isEditable() ? 'spritethumb on' : 'spritethumb noneditable'); - ScriptsPane.setActiveScript(spr.owner); - Palette.reset(); -}; - -Thumbs.unhighlighSprite = function (spr) { - spr.setAttribute('class', 'spritethumb off'); - var currentsc = gn(spr.owner + '_scripts'); - currentsc.owner.deactivate(); - for (var i = 0; i < currentsc.childElementCount; i++) { - if (currentsc.childNodes[i].owner) { - currentsc.childNodes[i].owner.unhighlight(); + static quickHighlight (spr) { + if (spr.owner == ScratchJr.stage.currentPage.currentSpriteName) { + spr.className = 'spritethumb on target'; + } else { + spr.className = 'spritethumb off target'; } } -}; -Thumbs.quickHighlight = function (spr) { - if (spr.owner == ScratchJr.stage.currentPage.currentSpriteName) { - spr.className = 'spritethumb on target'; - } else { - spr.className = 'spritethumb off target'; + static quickRestore (spr) { + if (spr.owner == ScratchJr.stage.currentPage.currentSpriteName) { + spr.className = ScratchJr.isEditable() ? 'spritethumb on' : 'spritethumb noneditable'; + } else { + spr.className = 'spritethumb off'; + } } -}; - -Thumbs.quickRestore = function (spr) { - if (spr.owner == ScratchJr.stage.currentPage.currentSpriteName) { - spr.className = ScratchJr.isEditable() ? 'spritethumb on' : 'spritethumb noneditable'; - } else { - spr.className = 'spritethumb off'; - } -}; +} diff --git a/src/editor/ui/UI.js b/src/editor/ui/UI.js index 7199e7a..0bd0de2 100644 --- a/src/editor/ui/UI.js +++ b/src/editor/ui/UI.js @@ -2,1114 +2,1132 @@ // General UI Layout ///////////////////////////////////// -UI = function () {}; +import ScratchJr from '../ScratchJr'; +import BlockSpecs from '../blocks/BlockSpecs'; +import Alert from './Alert'; +import Project from './Project'; +import Thumbs from './Thumbs'; +import Palette from './Palette'; +import Grid from './Grid'; +import Stage from './Stage'; +import ScriptsPane from './ScriptsPane'; +import Undo from './Undo'; +import Library from './Library'; +import iOS from '../../iPad/iOS'; +import IO from '../../iPad/IO'; +import Paint from '../../painteditor/Paint'; +import Events from '../../utils/Events'; +import Localization from '../../utils/Localization'; +import ScratchAudio from '../../utils/ScratchAudio'; +import {frame, gn, CSSTransition, localx, newHTML, scaleMultiplier, getIdFor, isTablet, newDiv, + newTextInput, isAndroid, getDocumentWidth, getDocumentHeight, setProps, globalx} from '../../utils/lib'; -UI.projectNameTextInput = null; -UI.info = null; -UI.okclicky = null; -UI.isAndroid = isAndroid; +let projectNameTextInput = null; +let info = null; +let okclicky = null; -UI.layout = function () { - UI.topSection(); - UI.middleSection(); - UI.BottomSection(); - UI.fullscreenControls(); - UI.createFormForText(frame); - ScratchJr.setupKeypad(); - ScratchJr.setupEditableField(); - UI.aspectRatioAdjustment(); -}; - -// Helps debug on Android 4.2 by enabling the user to type in a -// JavaScript expression and evaluate the output and render to console.log. -/*UI.addDebugControl = function () { - var div = newHTML('div', 'debug', document.body); - setProps(div.style, { - position: 'absolute', - left: '0px', - top: '0px', - width: '64px', - height: '64px', - background: 'red', - zIndex: 30000 - }); - div.ontouchstart = function (e) { - console.log(eval(prompt('Enter Debug JavaScript'))); - }; -};*/ - -/** Tweak some elements depending on aspect ratio */ -UI.aspectRatioAdjustment = function () { - var aspect = window.getDocumentWidth() / window.getDocumentHeight(); - if (aspect > 1.45) { - // Nudge sprite list right a bit and the pages list left a bit - gn('library').style.left = '3vw'; - gn('pages').style.right = '1vw'; +export default class UI { + static layout () { + UI.topSection(); + UI.middleSection(); + UI.BottomSection(); + UI.fullscreenControls(); + UI.createFormForText(frame); + ScratchJr.setupKeypad(); + ScratchJr.setupEditableField(); + UI.aspectRatioAdjustment(); } -}; -UI.topSection = function () { - var div = newHTML('div', 'topsection', frame); - div.setAttribute('id', 'topsection'); - if (ScratchJr.isEditable()) { - UI.addProjectInfo(); - } - UI.leftPanel(div); - UI.stageArea(div); - UI.rightPanel(div); -}; + // Helps debug on Android 4.2 by enabling the user to type in a + // JavaScript expression and evaluate the output and render to console.log. + /*static addDebugControl () { + var div = newHTML('div', 'debug', document.body); + setProps(div.style, { + position: 'absolute', + left: '0px', + top: '0px', + width: '64px', + height: '64px', + background: 'red', + zIndex: 30000 + }); + div.ontouchstart = function (e) { + console.log(eval(prompt('Enter Debug JavaScript'))); + }; + }*/ -UI.leftPanel = function (div) { - // sprite library - var sl = newHTML('div', 'leftpanel', div); - var flip = newHTML('div', 'flipme', sl); - flip.setAttribute('id', 'flip'); - flip.ontouchstart = function (evt) { - ScratchJr.saveAndFlip(evt); - }; // move to project - UI.layoutLibrary(sl); -}; - -UI.middleSection = function () { - var bp = newHTML('div', 'blockspalette', frame); - bp.setAttribute('id', 'blockspalette'); - Palette.setup(bp); - Undo.setup(bp); -}; - -UI.BottomSection = function () { - ScriptsPane.createScripts(frame); -}; - -UI.addProjectInfo = function () { - UI.info = newHTML('div', 'info', frame); - UI.info.setAttribute('id', 'projectinfo'); - var infobox = newHTML('div', 'infobox fade', frame); - infobox.setAttribute('id', 'infobox'); - UI.okclicky = newHTML('div', 'paintdone', infobox); - newHTML('div', 'infoboxlogo', infobox); - var nameField = UI.addEditableName(infobox); - var staticinfo = newHTML('div', 'fixedinfo', infobox); - var author = newHTML('div', 'infolabel', staticinfo); - author.setAttribute('id', 'deviceName'); - - if (Settings.shareEnabled) { - // Sharing - var shareButtons = newHTML('div', 'infoboxShareButtons', infobox); - - var shareEmail = newHTML('div', 'infoboxShareButton', shareButtons); - shareEmail.id = 'infoboxShareButtonEmail'; - shareEmail.textContent = Localization.localize('SHARING_BY_EMAIL'); - - if (UI.isAndroid) { - shareEmail.style.margin = 'auto'; - } else { - shareEmail.style.float = 'left'; + /** Tweak some elements depending on aspect ratio */ + static aspectRatioAdjustment () { + var aspect = window.getDocumentWidth() / window.getDocumentHeight(); + if (aspect > 1.45) { + // Nudge sprite list right a bit and the pages list left a bit + gn('library').style.left = '3vw'; + gn('pages').style.right = '1vw'; } + } - if (!UI.isAndroid) { - var shareAirdrop = newHTML('div', 'infoboxShareButton', shareButtons); - shareAirdrop.id = 'infoboxShareButtonAirdrop'; - shareAirdrop.textContent = Localization.localize('SHARING_BY_AIRDROP'); - shareAirdrop.style.float = 'right'; - shareAirdrop.ontouchstart = function (e) { + static topSection () { + var div = newHTML('div', 'topsection', frame); + div.setAttribute('id', 'topsection'); + if (ScratchJr.isEditable()) { + UI.addProjectInfo(); + } + UI.leftPanel(div); + UI.stageArea(div); + UI.rightPanel(div); + } + + static leftPanel (div) { + // sprite library + var sl = newHTML('div', 'leftpanel', div); + var flip = newHTML('div', 'flipme', sl); + flip.setAttribute('id', 'flip'); + flip.ontouchstart = function (evt) { + ScratchJr.saveAndFlip(evt); + }; // move to project + UI.layoutLibrary(sl); + } + + static middleSection () { + var bp = newHTML('div', 'blockspalette', frame); + bp.setAttribute('id', 'blockspalette'); + Palette.setup(bp); + Undo.setup(bp); + } + + static BottomSection () { + ScriptsPane.createScripts(frame); + } + + static addProjectInfo () { + info = newHTML('div', 'info', frame); + info.setAttribute('id', 'projectinfo'); + var infobox = newHTML('div', 'infobox fade', frame); + infobox.setAttribute('id', 'infobox'); + okclicky = newHTML('div', 'paintdone', infobox); + newHTML('div', 'infoboxlogo', infobox); + var nameField = UI.addEditableName(infobox); + var staticinfo = newHTML('div', 'fixedinfo', infobox); + var author = newHTML('div', 'infolabel', staticinfo); + author.setAttribute('id', 'deviceName'); + + if (Settings.shareEnabled) { + // Sharing + var shareButtons = newHTML('div', 'infoboxShareButtons', infobox); + + var shareEmail = newHTML('div', 'infoboxShareButton', shareButtons); + shareEmail.id = 'infoboxShareButtonEmail'; + shareEmail.textContent = Localization.localize('SHARING_BY_EMAIL'); + + if (isAndroid) { + shareEmail.style.margin = 'auto'; + } else { + shareEmail.style.float = 'left'; + } + + if (!isAndroid) { + var shareAirdrop = newHTML('div', 'infoboxShareButton', shareButtons); + shareAirdrop.id = 'infoboxShareButtonAirdrop'; + shareAirdrop.textContent = Localization.localize('SHARING_BY_AIRDROP'); + shareAirdrop.style.float = 'right'; + shareAirdrop.ontouchstart = function (e) { + UI.parentalGate(e, function (e) { + UI.infoDoShare(e, nameField, shareLoadingGif, 1); + }); + }; + } + + iOS.deviceName(function (name) { + gn('deviceName').textContent = name; + }); + + var shareLoadingGif = newHTML('img', 'infoboxShareLoading', shareButtons); + shareLoadingGif.src = './assets/ui/loader.png'; + + shareEmail.ontouchstart = function (e) { UI.parentalGate(e, function (e) { - UI.infoDoShare(e, nameField, shareLoadingGif, 1); + UI.infoDoShare(e, nameField, shareLoadingGif, 0); }); }; } - iOS.deviceName(function (name) { - gn('deviceName').textContent = name; - }); - - var shareLoadingGif = newHTML('img', 'infoboxShareLoading', shareButtons); - shareLoadingGif.src = './assets/ui/loader.png'; - - shareEmail.ontouchstart = function (e) { - UI.parentalGate(e, function (e) { - UI.infoDoShare(e, nameField, shareLoadingGif, 0); - }); + info.ontouchend = UI.showInfoBox; + okclicky.ontouchstart = UI.hideInfoBox; + okclicky.ontouchstart = function (evt) { + UI.hideInfoBox(evt, nameField); }; } - UI.info.ontouchend = UI.showInfoBox; - UI.okclicky.ontouchstart = UI.hideInfoBox; - UI.okclicky.ontouchstart = function (evt) { - UI.hideInfoBox(evt, nameField); - }; -}; + static parentalGate (evt, callback) { + ScratchAudio.sndFX('tap.wav'); + var pgFrame = newHTML('div', 'parentalgate', gn('frame')); -UI.parentalGate = function (evt, callback) { - ScratchAudio.sndFX('tap.wav'); - var pgFrame = newHTML('div', 'parentalgate', gn('frame')); + var pgCloseButton = newHTML('div', 'paintdone', pgFrame); + pgCloseButton.ontouchstart = function () { + parentalGateClose(false); + }; - var pgCloseButton = newHTML('div', 'paintdone', pgFrame); - pgCloseButton.ontouchstart = function () { - parentalGateClose(false); - }; + var pgProblem = newHTML('div', 'parentalgateproblem', pgFrame); + var pgChoiceA = newHTML('div', 'parentalgatechoice', pgFrame); + var pgChoiceB = newHTML('div', 'parentalgatechoice', pgFrame); + var pgChoiceC = newHTML('div', 'parentalgatechoice', pgFrame); - var pgProblem = newHTML('div', 'parentalgateproblem', pgFrame); - var pgChoiceA = newHTML('div', 'parentalgatechoice', pgFrame); - var pgChoiceB = newHTML('div', 'parentalgatechoice', pgFrame); - var pgChoiceC = newHTML('div', 'parentalgatechoice', pgFrame); + var problems = [ + // Problem, Choice A, Choice B, Choice C, Correct choice # + ['30 + 7', '37', '9', '28', 0], + ['22 + 3', '18', '25', '3', 1], + ['91 + 1', '32', '74', '92', 2], + ['30 + 4', '34', '59', '12', 0], + ['48 + 1', '9', '49', '20', 1], + ['32 + 6', '23', '99', '38', 2], + ['53 + 4', '57', '12', '90', 0], + ['26 + 3', '17', '29', '8', 1], + ['71 + 1', '58', '14', '72', 2], + ['11 + 8', '19', '23', '67', 0] + ]; - var problems = [ - // Problem, Choice A, Choice B, Choice C, Correct choice # - ['30 + 7', '37', '9', '28', 0], - ['22 + 3', '18', '25', '3', 1], - ['91 + 1', '32', '74', '92', 2], - ['30 + 4', '34', '59', '12', 0], - ['48 + 1', '9', '49', '20', 1], - ['32 + 6', '23', '99', '38', 2], - ['53 + 4', '57', '12', '90', 0], - ['26 + 3', '17', '29', '8', 1], - ['71 + 1', '58', '14', '72', 2], - ['11 + 8', '19', '23', '67', 0] - ]; + var problemChoice = Math.floor(Math.random() * problems.length); + var theProblem = problems[problemChoice]; - var problemChoice = Math.floor(Math.random() * problems.length); - var theProblem = problems[problemChoice]; + pgProblem.textContent = theProblem[0]; + pgChoiceA.textContent = theProblem[1]; + pgChoiceB.textContent = theProblem[2]; + pgChoiceC.textContent = theProblem[3]; - pgProblem.textContent = theProblem[0]; - pgChoiceA.textContent = theProblem[1]; - pgChoiceB.textContent = theProblem[2]; - pgChoiceC.textContent = theProblem[3]; - - pgChoiceA.ontouchstart = function () { - parentalGateClose(theProblem[4] == 0); - }; - pgChoiceB.ontouchstart = function () { - parentalGateClose(theProblem[4] == 1); - }; - pgChoiceC.ontouchstart = function () { - parentalGateClose(theProblem[4] == 2); - }; + pgChoiceA.ontouchstart = function () { + parentalGateClose(theProblem[4] == 0); + }; + pgChoiceB.ontouchstart = function () { + parentalGateClose(theProblem[4] == 1); + }; + pgChoiceC.ontouchstart = function () { + parentalGateClose(theProblem[4] == 2); + }; - var pgExplain = newHTML('div', 'parentalgateexplain', pgFrame); - pgExplain.textContent = Localization.localize('PARENTAL_GATE_EXPLANATION'); + var pgExplain = newHTML('div', 'parentalgateexplain', pgFrame); + pgExplain.textContent = Localization.localize('PARENTAL_GATE_EXPLANATION'); - function parentalGateClose (success) { - ScratchAudio.sndFX('exittap.wav'); - gn('frame').removeChild(pgFrame); - if (success) { - callback(evt); + function parentalGateClose (success) { + ScratchAudio.sndFX('exittap.wav'); + gn('frame').removeChild(pgFrame); + if (success) { + callback(evt); + } } } -}; -/* -+ Save the project, including the new name, then package the project and send native-side for sharing -+ -+ evt: reference to touch event triggering share -+ nameField: reference to the project rename field -+ shareLoadingGif: reference to HTML element to show during packaging/loading and hide for completion -+ shareType: which dialog to show - 0 for email; 1 for airdrop -+ */ + /* + + Save the project, including the new name, then package the project and send native-side for sharing + + + + evt: reference to touch event triggering share + + nameField: reference to the project rename field + + shareLoadingGif: reference to HTML element to show during packaging/loading and hide for completion + + shareType: which dialog to show - 0 for email; 1 for airdrop + + */ -UI.shareBody = Localization.localize('SHARING_EMAIL_TEXT'); + static infoDoShare (evt, nameField, shareLoadingGif, shareType) { + ScratchAudio.sndFX('tap.wav'); + shareLoadingGif.style.visibility = 'visible'; + nameField.blur(); // Hide the keyboard for name changes -UI.infoDoShare = function (evt, nameField, shareLoadingGif, shareType) { - ScratchAudio.sndFX('tap.wav'); - shareLoadingGif.style.visibility = 'visible'; - nameField.blur(); // Hide the keyboard for name changes + setTimeout(saveAndShare, 500); // 500ms delay to wait for loading GIF to show and keyboard to hide - setTimeout(saveAndShare, 500); // 500ms delay to wait for loading GIF to show and keyboard to hide + iOS.analyticsEvent('editor', 'share_button', (shareType == 0) ? 'email' : 'airdrop'); - iOS.analyticsEvent('editor', 'share_button', (shareType == 0) ? 'email' : 'airdrop'); + function saveAndShare () { + // Save the project's new name + UI.handleTextFieldSave(true); - function saveAndShare () { - // Save the project's new name - UI.handleTextFieldSave(true); + // Save any changes made to the project + ScratchJr.onHold = true; // Freeze the editing UI + ScratchJr.stopStripsFromTop(evt); - // Save any changes made to the project - ScratchJr.onHold = true; // Freeze the editing UI - ScratchJr.stopStripsFromTop(evt); + Project.prepareToSave(ScratchJr.currentProject, function () { + Alert.close(); - Project.prepareToSave(ScratchJr.currentProject, function () { - Alert.close(); + // Package the project as a .sjr file + IO.zipProject(ScratchJr.currentProject, function (contents) { + ScratchJr.onHold = false; // Unfreeze the editing UI + var emailSubject = Localization.localize('SHARING_EMAIL_SUBJECT', { + PROJECT_NAME: IO.shareName + }); + iOS.sendSjrToShareDialog(IO.zipFileName, emailSubject, Localization.localize('SHARING_EMAIL_TEXT'), + shareType, contents); - // Package the project as a .sjr file - IO.zipProject(ScratchJr.currentProject, function (contents) { - ScratchJr.onHold = false; // Unfreeze the editing UI - var emailSubject = Localization.localize('SHARING_EMAIL_SUBJECT', { - PROJECT_NAME: IO.shareName + shareLoadingGif.style.visibility = 'hidden'; }); - iOS.sendSjrToShareDialog(IO.zipFileName, emailSubject, UI.shareBody, shareType, contents); - - shareLoadingGif.style.visibility = 'hidden'; }); - }); - } -}; - - -UI.addEditableName = function (p) { - var pname = newHTML('form', 'projectname', p); - pname.name = 'projectname'; - pname.id = 'title'; - pname.onsubmit = function (evt) { - submitChange(evt); - }; - var ti = newHTML('input', 'pnamefield', pname); - UI.projectNameTextInput = ti; - ti.name = 'myproject'; - ti.maxLength = 30; - ti.onkeypress = undefined; - ti.autocomplete = 'off'; - ti.autocorrect = false; - ti.onblur = undefined; - ti.onfocus = function (e) { - e.preventDefault(); - ti.oldvalue = ti.value; - if (UI.isAndroid) { - AndroidInterface.scratchjr_setsoftkeyboardscrolllocation( - ti.getBoundingClientRect().top * window.devicePixelRatio, - ti.getBoundingClientRect().bottom * window.devicePixelRatio - ); - AndroidInterface.scratchjr_forceShowKeyboard(); - } - }; - ti.onkeypress = function (evt) { - handleNamePress(evt); - }; - function handleNamePress (e) { - var key = e.keyCode || e.which; - if (key == 13) { - submitChange(e); } } - function submitChange (e) { - e.preventDefault(); - var input = e.target; - input.blur(); - } - return ti; -}; -UI.handleTextFieldSave = function (dontHide) { - // Handle story-starter mode project - if (ScratchJr.isEditable() && ScratchJr.editmode == 'storyStarter' && !Project.error) { - iOS.analyticsEvent('samples', 'story_starter_edited', Project.metadata.name); - // Get the new project name - IO.uniqueProjectName({ - name: Project.metadata.name - }, function (jsonData) { - var newName = jsonData.name; - Project.metadata.name = newName; - // Create the new project - IO.createProject({ - name: newName, - version: ScratchJr.version, - mtime: (new Date()).getTime().toString() - }, function (md5) { - Project.metadata.id = md5; - ScratchJr.currentProject = md5; - ScratchJr.editmode = 'edit'; - Project.metadata.gallery = ''; - UI.finishTextFieldSave(dontHide); - }); - }); - } else { - UI.finishTextFieldSave(dontHide); - } -}; -UI.finishTextFieldSave = function (dontHide) { - var ti = UI.projectNameTextInput; - var pname = (ti.value.length == 0) ? ti.oldvalue : ti.value.substring(0, ti.maxLength); - if (Project.metadata.name != pname) { - ScratchJr.storyStart('UI.handleTextFieldSave'); - } - Project.metadata.name = pname; - ScratchJr.changed = true; - iOS.setfield(iOS.database, Project.metadata.id, 'name', pname); - if (!dontHide) { - ScratchAudio.sndFX('exittap.wav'); - gn('infobox').className = 'infobox fade'; - } -}; - -UI.showInfoBox = function (e) { - e.preventDefault(); - e.stopPropagation(); - if (Paint.saving) { - return; - } - if (ScratchJr.onHold) { - return; - } - - // Prevent button from thrashing - setTimeout(function () { - UI.okclicky.ontouchend = UI.hideInfoBox; - UI.projectNameTextInput.onblur = function () { - if (UI.isAndroid) { - AndroidInterface.scratchjr_forceHideKeyboard(); + static addEditableName (p) { + var pname = newHTML('form', 'projectname', p); + pname.name = 'projectname'; + pname.id = 'title'; + pname.onsubmit = function (evt) { + submitChange(evt); + }; + var ti = newHTML('input', 'pnamefield', pname); + projectNameTextInput = ti; + ti.name = 'myproject'; + ti.maxLength = 30; + ti.onkeypress = undefined; + ti.autocomplete = 'off'; + ti.autocorrect = false; + ti.onblur = undefined; + ti.onfocus = function (e) { + e.preventDefault(); + ti.oldvalue = ti.value; + if (isAndroid) { + AndroidInterface.scratchjr_setsoftkeyboardscrolllocation( + ti.getBoundingClientRect().top * window.devicePixelRatio, + ti.getBoundingClientRect().bottom * window.devicePixelRatio + ); + AndroidInterface.scratchjr_forceShowKeyboard(); } }; - }, 500); - UI.projectNameTextInput.onblur = function () { - if (ScratchJr.isEditable()) { - (document.forms.projectname.myproject).focus(); + ti.onkeypress = function (evt) { + handleNamePress(evt); + }; + function handleNamePress (e) { + var key = e.keyCode || e.which; + if (key == 13) { + submitChange(e); + } } - }; - UI.info.ontouchend = null; - - ScratchJr.onBackButtonCallback.push(function () { - var e2 = document.createEvent('TouchEvent'); - e2.initTouchEvent(); - e2.preventDefault(); - e2.stopPropagation(); - UI.hideInfoBox(e2); - }); - - ScratchAudio.sndFX('entertap.wav'); - ScratchJr.stopStrips(); - if (!Project.metadata.ctime) { - Project.metadata.mtime = (new Date()).getTime(); - Project.metadata.ctime = UI.formatTime((new Date()).getTime()); + function submitChange (e) { + e.preventDefault(); + var input = e.target; + input.blur(); + } + return ti; } - if (ScratchJr.isEditable()) { - (document.forms.projectname.myproject).value = Project.metadata.name; - } else { - gn('pname').textContent = Project.metadata.name; + static handleTextFieldSave (dontHide) { + // Handle story-starter mode project + if (ScratchJr.isEditable() && ScratchJr.editmode == 'storyStarter' && !Project.error) { + iOS.analyticsEvent('samples', 'story_starter_edited', Project.metadata.name); + // Get the new project name + IO.uniqueProjectName({ + name: Project.metadata.name + }, function (jsonData) { + var newName = jsonData.name; + Project.metadata.name = newName; + // Create the new project + IO.createProject({ + name: newName, + version: ScratchJr.version, + mtime: (new Date()).getTime().toString() + }, function (md5) { + Project.metadata.id = md5; + ScratchJr.currentProject = md5; + ScratchJr.editmode = 'edit'; + Project.metadata.gallery = ''; + UI.finishTextFieldSave(dontHide); + }); + }); + } else { + UI.finishTextFieldSave(dontHide); + } } - gn('infobox').className = 'infobox fade in'; - if (ScratchJr.isEditable()) { + + static finishTextFieldSave (dontHide) { + var ti = projectNameTextInput; + var pname = (ti.value.length == 0) ? ti.oldvalue : ti.value.substring(0, ti.maxLength); + if (Project.metadata.name != pname) { + ScratchJr.storyStart('UI.handleTextFieldSave'); + } + Project.metadata.name = pname; + ScratchJr.changed = true; + iOS.setfield(iOS.database, Project.metadata.id, 'name', pname); + if (!dontHide) { + ScratchAudio.sndFX('exittap.wav'); + gn('infobox').className = 'infobox fade'; + } + } + + static showInfoBox (e) { + e.preventDefault(); + e.stopPropagation(); + if (Paint.saving) { + return; + } + if (ScratchJr.onHold) { + return; + } + + // Prevent button from thrashing setTimeout(function () { - //(document.forms["projectname"]["myproject"]).focus(); + okclicky.ontouchend = UI.hideInfoBox; + projectNameTextInput.onblur = function () { + if (isAndroid) { + AndroidInterface.scratchjr_forceHideKeyboard(); + } + }; }, 500); - } -}; + projectNameTextInput.onblur = function () { + if (ScratchJr.isEditable()) { + (document.forms.projectname.myproject).focus(); + } + }; + info.ontouchend = null; -UI.formatTime = function (unixtime) { - var date = new Date(unixtime); - var year = date.getFullYear(); - var month = date.getMonth() + 1; - var day = date.getDate(); - var hour = date.getHours(); - var min = date.getMinutes(); - var sec = date.getSeconds(); - return year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + sec; -}; + ScratchJr.onBackButtonCallback.push(function () { + var e2 = document.createEvent('TouchEvent'); + e2.initTouchEvent(); + e2.preventDefault(); + e2.stopPropagation(); + UI.hideInfoBox(e2); + }); -UI.hideInfoBox = function (e) { - e.preventDefault(); - e.stopPropagation(); - ScratchJr.onBackButtonCallback.pop(); - - // Prevent button thrashing - UI.okclicky.ontouchend = null; - setTimeout(function () { - UI.info.ontouchend = UI.showInfoBox; - }, 500); - - if (ScratchJr.isEditable()) { - (document.forms.projectname.myproject).blur(); - UI.handleTextFieldSave(); - } else { - ScratchAudio.sndFX('exittap.wav'); - gn('infobox').className = 'infobox fade'; - } -}; - -////////////////////////////////////// -// Library -///////////////////////////////////// - -UI.layoutLibrary = function (sl) { - var sprites = newHTML('div', 'thumbpanel', sl); - sprites.setAttribute('id', 'library'); - //scrolling area - var p = newHTML('div', 'spritethumbs', sprites); - var div = newHTML('div', 'spritecc', p); - div.setAttribute('id', 'spritecc'); - div.ontouchstart = UI.spriteThumbsActions; - - // scrollbar - var sb = newHTML('div', 'scrollbar', sprites); - sb.setAttribute('id', 'scrollbar'); - var sbthumb = newHTML('div', 'sbthumb', sb); - sbthumb.setAttribute('id', 'sbthumb'); - - // new sprite - if (ScratchJr.isEditable()) { - var ns = newHTML('div', 'addsprite', sprites); - ns.ontouchstart = UI.addSprite; - } -}; - -UI.mascotData = function (page) { - var sprAttr = { - flip: false, - angle: 0, - shown: true, - type: 'sprite', - scale: 0.5, - defaultScale: 0.5, - speed: 2, - dirx: 1, - diry: 1, - sounds: ['pop.mp3'], - homex: 240, - homey: 180, - xcoor: 240, - ycoor: 180, - homeshown: true, - homeflip: false, - homescale: 0.5, - scripts: [] - }; - sprAttr.page = page; - sprAttr.md5 = ScratchJr.defaultSprite; - var catkey = MediaLib.keys[sprAttr.md5].name; - sprAttr.id = getIdFor(catkey); - sprAttr.name = catkey; - return sprAttr; -}; - -////////////////////////////////////// -// Scrolling -////////////////////////////////////// - -UI.needsScroll = function () { - var sc = gn('spritecc'); - var p = sc.parentNode; - if (((sc.scrollHeight / p.offsetHeight) == 1) || (gn('spritecc').childElementCount == 0)) { - gn('scrollbar').setAttribute('class', 'scrollbar off'); - } else { - gn('scrollbar').setAttribute('class', 'scrollbar on'); - UI.updateSpriteScroll(); - } -}; - -UI.updateSpriteScroll = function () { - var sc = gn('spritecc'); - var dy = sc.offsetTop; - var p = sc.parentNode; - var top = -dy / (sc.scrollHeight / p.offsetHeight); - var size = (p.offsetHeight / sc.scrollHeight) * p.offsetHeight; - var thumb = gn('sbthumb'); - thumb.style.height = size + 'px'; - thumb.style.top = top + 'px'; -}; - -UI.scrollContents = function (dy) { - var sc = gn('spritecc'); - var valy = sc.offsetTop - dy; - if (valy > 0) { - valy = 0; - } - var transition = { - duration: 0.5, - transition: 'ease-out', - style: { - top: valy + 'px' + ScratchAudio.sndFX('entertap.wav'); + ScratchJr.stopStrips(); + if (!Project.metadata.ctime) { + Project.metadata.mtime = (new Date()).getTime(); + Project.metadata.ctime = UI.formatTime((new Date()).getTime()); } - }; - CSSTransition(sc, transition); -}; -UI.spriteInView = function (spr) { - var sc = gn('spritecc'); - var achild = spr.thumbnail; - if (!achild) { - return; + if (ScratchJr.isEditable()) { + (document.forms.projectname.myproject).value = Project.metadata.name; + } else { + gn('pname').textContent = Project.metadata.name; + } + gn('infobox').className = 'infobox fade in'; + if (ScratchJr.isEditable()) { + setTimeout(function () { + //(document.forms["projectname"]["myproject"]).focus(); + }, 500); + } } - var h = gn('spritecc').parentNode.offsetHeight; - var scroll = -gn('spritecc').offsetTop; - var dy = -gn('spritecc').offsetTop; - if ((achild.offsetTop + achild.offsetHeight + scroll) > h) { - dy = h - (achild.offsetTop + achild.offsetHeight); - } - if (achild.offsetTop <= scroll) { - dy = achild.offsetTop + scroll; - } - if (dy > 0) { - dy = 0; - } - sc.style.top = dy + 'px'; - UI.needsScroll(); -}; -UI.resetSpriteLibrary = function () { - if (!ScratchJr.getSprite()) { - return; + static formatTime (unixtime) { + var date = new Date(unixtime); + var year = date.getFullYear(); + var month = date.getMonth() + 1; + var day = date.getDate(); + var hour = date.getHours(); + var min = date.getMinutes(); + var sec = date.getSeconds(); + return year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + sec; } - UI.spriteInView(ScratchJr.getSprite()); -}; -/////////////////////////////////// -// Sprite Thumbs Events -////////////////////////////////// + static hideInfoBox (e) { + e.preventDefault(); + e.stopPropagation(); + ScratchJr.onBackButtonCallback.pop(); -UI.spriteThumbsActions = function (e) { - if (isTablet && e.touches && (e.touches.length > 1)) { - return; + // Prevent button thrashing + okclicky.ontouchend = null; + setTimeout(function () { + info.ontouchend = UI.showInfoBox; + }, 500); + + if (ScratchJr.isEditable()) { + (document.forms.projectname.myproject).blur(); + UI.handleTextFieldSave(); + } else { + ScratchAudio.sndFX('exittap.wav'); + gn('infobox').className = 'infobox fade'; + } } - if (ScratchJr.onHold) { - return; + + ////////////////////////////////////// + // Library + ///////////////////////////////////// + + static layoutLibrary (sl) { + var sprites = newHTML('div', 'thumbpanel', sl); + sprites.setAttribute('id', 'library'); + //scrolling area + var p = newHTML('div', 'spritethumbs', sprites); + var div = newHTML('div', 'spritecc', p); + div.setAttribute('id', 'spritecc'); + div.ontouchstart = UI.spriteThumbsActions; + + // scrollbar + var sb = newHTML('div', 'scrollbar', sprites); + sb.setAttribute('id', 'scrollbar'); + var sbthumb = newHTML('div', 'sbthumb', sb); + sbthumb.setAttribute('id', 'sbthumb'); + + // new sprite + if (ScratchJr.isEditable()) { + var ns = newHTML('div', 'addsprite', sprites); + ns.ontouchstart = UI.addSprite; + } } - var t; - var pt = Events.getTargetPoint(e); - if (window.event) { - t = window.event.srcElement; - } else { - t = e.target; + + static mascotData (page) { + var sprAttr = { + flip: false, + angle: 0, + shown: true, + type: 'sprite', + scale: 0.5, + defaultScale: 0.5, + speed: 2, + dirx: 1, + diry: 1, + sounds: ['pop.mp3'], + homex: 240, + homey: 180, + xcoor: 240, + ycoor: 180, + homeshown: true, + homeflip: false, + homescale: 0.5, + scripts: [] + }; + sprAttr.page = page; + sprAttr.md5 = ScratchJr.defaultSprite; + var catkey = MediaLib.keys[sprAttr.md5].name; + sprAttr.id = getIdFor(catkey); + sprAttr.name = catkey; + return sprAttr; } - // if ((t.nodeName == "INPUT") || (t.nodeName == "FORM")) return; - e.preventDefault(); - e.stopPropagation(); - ScratchJr.blur(); - t.focus(); - if (t.className == 'brush') { - UI.putInPaintEditor(e); return; + + ////////////////////////////////////// + // Scrolling + ////////////////////////////////////// + + static needsScroll () { + var sc = gn('spritecc'); + var p = sc.parentNode; + if (((sc.scrollHeight / p.offsetHeight) == 1) || (gn('spritecc').childElementCount == 0)) { + gn('scrollbar').setAttribute('class', 'scrollbar off'); + } else { + gn('scrollbar').setAttribute('class', 'scrollbar on'); + UI.updateSpriteScroll(); + } } - var tb = Thumbs.getType(t, 'spritethumb'); - if (!tb) { + + static updateSpriteScroll () { + var sc = gn('spritecc'); + var dy = sc.offsetTop; + var p = sc.parentNode; + var top = -dy / (sc.scrollHeight / p.offsetHeight); + var size = (p.offsetHeight / sc.scrollHeight) * p.offsetHeight; + var thumb = gn('sbthumb'); + thumb.style.height = size + 'px'; + thumb.style.top = top + 'px'; + } + + static scrollContents (dy) { + var sc = gn('spritecc'); + var valy = sc.offsetTop - dy; + if (valy > 0) { + valy = 0; + } + var transition = { + duration: 0.5, + transition: 'ease-out', + style: { + top: valy + 'px' + } + }; + CSSTransition(sc, transition); + } + + static spriteInView (spr) { + var sc = gn('spritecc'); + var achild = spr.thumbnail; + if (!achild) { + return; + } + var h = gn('spritecc').parentNode.offsetHeight; + var scroll = -gn('spritecc').offsetTop; + var dy = -gn('spritecc').offsetTop; + if ((achild.offsetTop + achild.offsetHeight + scroll) > h) { + dy = h - (achild.offsetTop + achild.offsetHeight); + } + if (achild.offsetTop <= scroll) { + dy = achild.offsetTop + scroll; + } + if (dy > 0) { + dy = 0; + } + sc.style.top = dy + 'px'; + UI.needsScroll(); + } + + static resetSpriteLibrary () { + if (!ScratchJr.getSprite()) { + return; + } + UI.spriteInView(ScratchJr.getSprite()); + } + + /////////////////////////////////// + // Sprite Thumbs Events + ////////////////////////////////// + + static spriteThumbsActions (e) { + if (isTablet && e.touches && (e.touches.length > 1)) { + return; + } + if (ScratchJr.onHold) { + return; + } + var t; + var pt = Events.getTargetPoint(e); + if (window.event) { + t = window.event.srcElement; + } else { + t = e.target; + } + // if ((t.nodeName == "INPUT") || (t.nodeName == "FORM")) return; + e.preventDefault(); + e.stopPropagation(); + ScratchJr.blur(); + t.focus(); + if (t.className == 'brush') { + UI.putInPaintEditor(e); return; + } + var tb = Thumbs.getType(t, 'spritethumb'); + if (!tb) { + if (ScratchJr.shaking) { + ScratchJr.clearSelection(); + } + return; + } + var x = localx(t, pt.x); + if (tb && (x < 70) && ScratchJr.isEditable()) { + Thumbs.startDragThumb(e, tb); + } else { + UI.startSpriteScroll(e, tb); + } + } + + static startSpriteScroll (e, tb) { if (ScratchJr.shaking) { ScratchJr.clearSelection(); } - return; - } - var x = localx(t, pt.x); - if (tb && (x < 70) && ScratchJr.isEditable()) { - Thumbs.startDragThumb(e, tb); - } else { - UI.startSpriteScroll(e, tb); - } -}; - -UI.startSpriteScroll = function (e, tb) { - if (ScratchJr.shaking) { - ScratchJr.clearSelection(); - } - if (!tb) { - return; - } - if (gn('scrollbar').className == 'scrollbar off') { - Events.startDrag(e, tb, UI.ignoreEvent, UI.ignoreEvent, UI.ignoreEvent, UI.spriteClicked, - ScratchJr.isEditable() ? Thumbs.startCharShaking : undefined); - } else { - Events.startDrag(e, tb, UI.prepareToScroll, UI.stopScroll, UI.spriteScolling, UI.spriteClicked, - ScratchJr.isEditable() ? Thumbs.startCharShaking : undefined); - } -}; - -UI.ignoreEvent = function (e) { - e.preventDefault(); - e.stopPropagation(); -}; - -UI.prepareToScroll = function (e) { - e.preventDefault(); - e.stopPropagation(); - UI.spriteScolling(e, Events.dragthumbnail); -}; - -UI.stopScroll = function (e) { - e.preventDefault(); - e.stopPropagation(); - UI.spriteScolling(e, Events.dragthumbnail); -}; - -UI.spriteScolling = function (e) { - var pt = Events.getTargetPoint(e); - var deltay = Events.dragmousey - pt.y; - Events.dragmousey = pt.y; - var sc = gn('spritecc'); - var dy = sc.offsetTop; - dy -= deltay; - var p = sc.parentNode; - if (dy > 0) { - dy = 0; - } - if ((dy + sc.offsetHeight) < p.offsetHeight) { - dy = p.offsetHeight - sc.offsetHeight; - } - sc.style.top = dy + 'px'; - UI.updateSpriteScroll(); -}; - -UI.spriteClicked = function (e, el) { - e.preventDefault(); - e.stopPropagation(); - var t; - if (window.event) { - t = window.event.srcElement; - } else { - t = e.target; - } - if (ScratchJr.isEditable() && ScratchJr.getSprite() && - (((t.className == 'sname') && (el.owner == ScratchJr.getSprite().id)) - || (t.className == 'brush'))) { - UI.putInPaintEditor(e); - return; - } - if (el.className.indexOf('shakeme') < 0) { - el.setAttribute('class', 'spritethumb on'); - } - Thumbs.clickOnSprite(e, el); -}; - -UI.putInPaintEditor = function (e) { - ScratchJr.unfocus(e); - var s = ScratchJr.getSprite(); - if (!s) { - return; - } - ScratchJr.stopStrips(); - Paint.open(false, s.md5, s.id, s.name, s.defaultScale, Math.round(s.w), Math.round(s.h)); -}; - -/////////////////////////////// -// Setup Stage Variables -////////////////////////////// - -UI.stageArea = function (inner) { - var outerDiv = newHTML('div', 'centerpanel', inner); - var div = newHTML('div', 'stageframe', outerDiv); - div.setAttribute('id', 'stageframe'); - ScratchJr.stage = new Stage(div); - Grid.init(div); - if (ScratchJr.isEditable()) { - UI.creatTopBarClicky(div, 'addtext', 'addText', UI.addText); - UI.creatTopBarClicky(div, 'setbkg', 'changeBkg', UI.addBackground); - } - UI.creatTopBarClicky(div, 'grid', 'gridToggle off', UI.switchGrid); - UI.creatTopBarClicky(div, 'go', 'go on', UI.toggleRun); - UI.creatTopBarClicky(div, 'resetall', 'resetall', UI.resetAllSprites); - UI.creatTopBarClicky(div, 'full', 'fullscreen', ScratchJr.fullScreen); - UI.toggleGrid(true); -}; - -UI.resetAllSprites = function (e) { - e.preventDefault(); - e.stopPropagation(); - if (ScratchJr.onHold) { - return; - } - ScratchAudio.sndFX('tap.wav'); - if (!ScratchJr.runtime.inactive()) { - ScratchJr.stopStripsFromTop(e); - } - ScratchJr.resetSprites(); -}; - -UI.toggleRun = function (e) { - var isOff = ScratchJr.runtime.inactive(); - if (isOff) { - ScratchJr.runStrips(e); - } else { - ScratchJr.stopStripsFromTop(e); - } -}; - -UI.switchGrid = function () { - ScratchAudio.sndFX('tap.wav'); - UI.toggleGrid(!Grid.hidden); -}; - -UI.toggleGrid = function (b) { - Grid.hide(b); - gn('grid').className = Grid.hidden ? 'gridToggle off' : 'gridToggle on'; -}; - -UI.creatTopBarClicky = function (p, str, mstyle, fcn) { - var toggle = newHTML('div', mstyle, p); - toggle.ontouchstart = fcn; - toggle.setAttribute('id', str); -}; - -UI.fullscreenControls = function () { - UI.nextpage = newHTML('div', 'nextpage off', frame); - UI.prevpage = newHTML('div', 'nextpage off', frame); - if (isTablet) { - UI.nextpage.ontouchstart = UI.nextPage; - } else { - UI.nextpage.onmousedown = UI.nextPage; - } - if (isTablet) { - UI.prevpage.ontouchstart = UI.prevPage; - } else { - UI.prevpage.onmousedown = UI.prevPage; - } -}; - -UI.updatePageControls = function () { - var n = ScratchJr.stage.pages.indexOf(ScratchJr.stage.currentPage); - if (n == 0) { - UI.prevpage.setAttribute('class', 'prevpage off'); - } else { - UI.prevpage.setAttribute('class', 'prevpage on'); - } - if (n == (ScratchJr.stage.pages.length - 1)) { - UI.nextpage.setAttribute('class', 'nextpage off'); - } else { - UI.nextpage.setAttribute('class', 'nextpage on'); - } -}; - -UI.nextPage = function (e) { - e.preventDefault(); - e.stopPropagation(); - var n = ScratchJr.stage.pages.indexOf(ScratchJr.stage.currentPage); - n++; - if (n >= ScratchJr.stage.pages.length) { - return; - } - ScratchJr.stage.setPage(ScratchJr.stage.pages[n], false); -}; - -UI.prevPage = function (e) { - e.preventDefault(); - e.stopPropagation(); - var n = ScratchJr.stage.pages.indexOf(ScratchJr.stage.currentPage); - if (n < 1) { - return; - } - ScratchJr.stage.setPage(ScratchJr.stage.pages[n - 1], false); -}; - -UI.enterFullScreen = function () { - var w = Math.min(getDocumentWidth(), frame.offsetWidth); - var h = Math.max(getDocumentHeight(), frame.offsetHeight); - frame.appendChild(gn('stage')); - var list = ['go', 'full']; - for (var i = 0; i < list.length; i++) { - gn(list[i]).className = gn(list[i]).className + ' presentationmode'; - frame.appendChild(gn(list[i])); - } - var scale = Math.min((w - (136 * scaleMultiplier)) / gn('stage').owner.width, h / gn('stage').owner.height); - var dx = Math.floor((w - (gn('stage').owner.width * scale)) / 2); - var dy = Math.floor((h - (gn('stage').owner.height * scale)) / 2); - - ScratchJr.stage.setStageScaleAndPosition(scale, dx / scale, dy / scale); - - gn('stage').owner.currentZoom = Math.floor(scale * 100) / 100; - gn('stage').style.webkitTextSizeAdjust = Math.floor(gn('stage').owner.currentZoom * 100) + '%'; - document.body.parentNode.style.background = 'black'; - gn('stage').setAttribute('class', 'stage fullscreen'); - UI.nextpage.setAttribute('class', 'nextpage on'); -}; - -UI.quitFullScreen = function () { - var div = gn('stageframe'); - div.appendChild(gn('stage')); - ScratchJr.stage.setStageScaleAndPosition(scaleMultiplier, 46, 74); - gn('go').className = 'go off nopresent'; - div.appendChild(gn('go')); - gn('full').className = 'fullscreen'; - div.appendChild(gn('full')); - gn('stage').owner.currentZoom = 1; - gn('stage').style.webkitTextSizeAdjust = '100%'; - document.body.parentNode.style.background = 'none'; - gn('stage').setAttribute('class', 'stage normal'); - UI.nextpage.setAttribute('class', 'nextpage off'); - UI.prevpage.setAttribute('class', 'nextpage off'); - ScratchJr.stage.setViewPage(ScratchJr.stage.currentPage); - Thumbs.updateSprites(); - Thumbs.updatePages(); -}; - -////////////////////////////////////// -// Right panel -///////////////////////////////////// - -UI.rightPanel = function (div) { - var rp = newHTML('div', 'rightpanel', div); - var tb = newHTML('div', 'pages', rp); - tb.setAttribute('id', 'pages'); - var ndiv = newHTML('div', 'pagescc', tb); - ndiv.setAttribute('id', 'pagecc'); -}; - -////////////////////////////////////// -// Tools -///////////////////////////////////// - -UI.layoutToolbar = function (div) { - var h = 56; - var w = 66 * 2; - var tb = newDiv(div, 220, 0, w, h, { - position: 'absolute' - }); - tb.setAttribute('id', 'toolbar'); - var addt = newHTML('div', 'addText', tb); - addt.ontouchstart = UI.addText; - var changebkg = newHTML('div', 'changeBkg', tb); - changebkg.ontouchstart = UI.addBackground; -}; - -UI.addSprite = function (e) { - if (ScratchJr.onHold) { - return; - } - e.preventDefault(); - e.stopPropagation(); - var pt = Events.getTargetPoint(e); - if (pt.x > (globalx(e.target) + 167)) { - return; - } - ScratchAudio.sndFX('tap.wav'); - ScratchJr.stopStrips(); - ScratchJr.unfocus(e); - if (Events.dragthumbnail) { - Events.mouseUp(e); - } - Library.open('costumes'); -}; - -UI.addBackground = function (e) { - if (ScratchJr.onHold) { - return; - } - e.preventDefault(); - e.stopPropagation(); - ScratchJr.stopStrips(); - ScratchJr.unfocus(e); - if (Events.dragthumbnail) { - Events.mouseUp(e); - } - Library.open('backgrounds'); -}; - -UI.addText = function (e) { - if (ScratchJr.onHold) { - return; - } - e.preventDefault(); - e.stopPropagation(); - if (UI.isAndroid) { - if (gn('textbox').style.visibility === 'visible') { + if (!tb) { return; } - } - ScratchJr.unfocus(e); - ScratchJr.stage.currentPage.createText(); -}; - -////////////////////////////////// -// Key Handling in TextBox -////////////////////////////////// - -UI.createFormForText = function (p) { - var tf = newHTML('div', 'pagetext off', p); - tf.setAttribute('id', 'textbox'); - if (UI.isAndroid) { - tf.onmousedown = function (e) { - e.preventDefault(); - }; - } - var activetb = newHTML('form', 'pageform', tf); - activetb.name = 'activetextbox'; - activetb.id = 'myform'; - activetb.textsprite = null; - var field = newTextInput(activetb, 'text'); - field.name = 'typing'; - field.setAttribute('class', 'edittext'); - field.maxLength = 50; - field.onkeypress = undefined; - field.autocomplete = 'off'; - field.autocorrect = false; - field.onblur = undefined; - activetb.onsubmit = undefined; - var ta = newHTML('div', 'pagetextactions', tf); - var clicky = newHTML('div', 'fontsizeText off', ta); - clicky.setAttribute('id', 'fontsizebutton'); - clicky.ontouchstart = UI.openFontSizeMenu; - var col = newHTML('div', 'changecolorText off', ta); - col.setAttribute('id', 'fontcolorbutton'); - - col.ontouchstart = UI.topLevelColor; - UI.createColorMenu(tf); - UI.createTextSizeMenu(tf); -}; - -UI.createColorMenu = function (div) { - var swatchlist = BlockSpecs.fontcolors; - var spal = newHTML('div', 'textuicolormenu off', div); - spal.setAttribute('id', 'textcolormenu'); - for (var i = 0; i < swatchlist.length; i++) { - var colour = newHTML('div', 'textcolorbucket', spal); - // bucket - var sf = newHTML('div', 'swatchframe', colour); - var sc = newHTML('div', 'swatchcolor', sf); - sc.style.background = swatchlist[i]; - // - sf = newHTML('div', 'splasharea off', colour); - Paint.setSplashColor(sf, Paint.splash, swatchlist[i]); - Paint.addImageUrl(sf, Paint.splashshade); - colour.ontouchstart = UI.setTextColor; - } - UI.setMenuTextColor(gn('textcolormenu').childNodes[9]); -}; - -UI.createTextSizeMenu = function (div) { - var sizes = BlockSpecs.fontsizes; - var spal = newHTML('div', 'textuifont off', div); - spal.setAttribute('id', 'textfontsizes'); - for (var i = 0; i < sizes.length; i++) { - var textuisize = newHTML('div', 'textuisize t' + (i + 1), spal); - textuisize.fs = sizes[i]; - var sf = newHTML('span', undefined, textuisize); - sf.textContent = 'A'; - textuisize.ontouchstart = UI.setTextSize; - } - UI.setMenuTextSize(gn('textfontsizes').childNodes[5]); -}; - -UI.setMenuTextColor = function (t) { - var c = t.childNodes[0].childNodes[0].style.backgroundColor; - for (var i = 0; i < gn('textcolormenu').childElementCount; i++) { - var mycolor = gn('textcolormenu').childNodes[i].childNodes[0].childNodes[0].style.backgroundColor; - if (c == mycolor) { - gn('textcolormenu').childNodes[i].childNodes[1].setAttribute('class', 'splasharea on'); + if (gn('scrollbar').className == 'scrollbar off') { + Events.startDrag(e, tb, UI.ignoreEvent, UI.ignoreEvent, UI.ignoreEvent, UI.spriteClicked, + ScratchJr.isEditable() ? Thumbs.startCharShaking : undefined); } else { - gn('textcolormenu').childNodes[i].childNodes[1].setAttribute('class', 'splasharea off'); + Events.startDrag(e, tb, UI.prepareToScroll, UI.stopScroll, UI.spriteScolling, UI.spriteClicked, + ScratchJr.isEditable() ? Thumbs.startCharShaking : undefined); } } -}; -UI.setMenuTextSize = function (t) { - var c = t.fs; - for (var i = 0; i < gn('textfontsizes').childElementCount; i++) { - var kid = gn('textfontsizes').childNodes[i]; - var fs = kid.fs; - var ckid = kid.className.split(' ')[1]; - if (c == fs) { - gn('textfontsizes').childNodes[i].className = 'textuisize ' + ckid + ' on'; + static ignoreEvent (e) { + e.preventDefault(); + e.stopPropagation(); + } + + static prepareToScroll (e) { + e.preventDefault(); + e.stopPropagation(); + UI.spriteScolling(e, Events.dragthumbnail); + } + + static stopScroll (e) { + e.preventDefault(); + e.stopPropagation(); + UI.spriteScolling(e, Events.dragthumbnail); + } + + static spriteScolling (e) { + var pt = Events.getTargetPoint(e); + var deltay = Events.dragmousey - pt.y; + Events.dragmousey = pt.y; + var sc = gn('spritecc'); + var dy = sc.offsetTop; + dy -= deltay; + var p = sc.parentNode; + if (dy > 0) { + dy = 0; + } + if ((dy + sc.offsetHeight) < p.offsetHeight) { + dy = p.offsetHeight - sc.offsetHeight; + } + sc.style.top = dy + 'px'; + UI.updateSpriteScroll(); + } + + static spriteClicked (e, el) { + e.preventDefault(); + e.stopPropagation(); + var t; + if (window.event) { + t = window.event.srcElement; } else { - gn('textfontsizes').childNodes[i].className = 'textuisize ' + ckid + ' off'; + t = e.target; + } + if (ScratchJr.isEditable() && ScratchJr.getSprite() && + (((t.className == 'sname') && (el.owner == ScratchJr.getSprite().id)) + || (t.className == 'brush'))) { + UI.putInPaintEditor(e); + return; + } + if (el.className.indexOf('shakeme') < 0) { + el.setAttribute('class', 'spritethumb on'); + } + Thumbs.clickOnSprite(e, el); + } + + static putInPaintEditor (e) { + ScratchJr.unfocus(e); + var s = ScratchJr.getSprite(); + if (!s) { + return; + } + ScratchJr.stopStrips(); + Paint.open(false, s.md5, s.id, s.name, s.defaultScale, Math.round(s.w), Math.round(s.h)); + } + + /////////////////////////////// + // Setup Stage Variables + ////////////////////////////// + + static stageArea (inner) { + var outerDiv = newHTML('div', 'centerpanel', inner); + var div = newHTML('div', 'stageframe', outerDiv); + div.setAttribute('id', 'stageframe'); + ScratchJr.stage = new Stage(div); + Grid.init(div); + if (ScratchJr.isEditable()) { + UI.creatTopBarClicky(div, 'addtext', 'addText', UI.addText); + UI.creatTopBarClicky(div, 'setbkg', 'changeBkg', UI.addBackground); + } + UI.creatTopBarClicky(div, 'grid', 'gridToggle off', UI.switchGrid); + UI.creatTopBarClicky(div, 'go', 'go on', UI.toggleRun); + UI.creatTopBarClicky(div, 'resetall', 'resetall', UI.resetAllSprites); + UI.creatTopBarClicky(div, 'full', 'fullscreen', ScratchJr.fullScreen); + UI.toggleGrid(true); + } + + static resetAllSprites (e) { + e.preventDefault(); + e.stopPropagation(); + if (ScratchJr.onHold) { + return; + } + ScratchAudio.sndFX('tap.wav'); + if (!ScratchJr.runtime.inactive()) { + ScratchJr.stopStripsFromTop(e); + } + ScratchJr.resetSprites(); + } + + static toggleRun (e) { + var isOff = ScratchJr.runtime.inactive(); + if (isOff) { + ScratchJr.runStrips(e); + } else { + ScratchJr.stopStripsFromTop(e); } } -}; -///////////////////////////////////////////////////////// -// Text color and size -///////////////////////////////////////////////////////// + static switchGrid () { + ScratchAudio.sndFX('tap.wav'); + UI.toggleGrid(!Grid.hidden); + } -UI.topLevelColor = function (e) { - e.preventDefault(); - e.stopPropagation(); - if (gn('fontcolorbutton').className == 'changecolorText on') { - gn('fontcolorbutton').className = 'changecolorText off'; - gn('textcolormenu').className = 'textuicolormenu off'; - } else { - gn('fontsizebutton').className = 'fontsizeText off'; - gn('textfontsizes').className = 'textuifont off'; + static toggleGrid (b) { + Grid.hide(b); + gn('grid').className = Grid.hidden ? 'gridToggle off' : 'gridToggle on'; + } + + static creatTopBarClicky (p, str, mstyle, fcn) { + var toggle = newHTML('div', mstyle, p); + toggle.ontouchstart = fcn; + toggle.setAttribute('id', str); + } + + static fullscreenControls () { + UI.nextpage = newHTML('div', 'nextpage off', frame); + UI.prevpage = newHTML('div', 'nextpage off', frame); + if (isTablet) { + UI.nextpage.ontouchstart = UI.nextPage; + } else { + UI.nextpage.onmousedown = UI.nextPage; + } + if (isTablet) { + UI.prevpage.ontouchstart = UI.prevPage; + } else { + UI.prevpage.onmousedown = UI.prevPage; + } + } + + static updatePageControls () { + var n = ScratchJr.stage.pages.indexOf(ScratchJr.stage.currentPage); + if (n == 0) { + UI.prevpage.setAttribute('class', 'prevpage off'); + } else { + UI.prevpage.setAttribute('class', 'prevpage on'); + } + if (n == (ScratchJr.stage.pages.length - 1)) { + UI.nextpage.setAttribute('class', 'nextpage off'); + } else { + UI.nextpage.setAttribute('class', 'nextpage on'); + } + } + + static nextPage (e) { + e.preventDefault(); + e.stopPropagation(); + var n = ScratchJr.stage.pages.indexOf(ScratchJr.stage.currentPage); + n++; + if (n >= ScratchJr.stage.pages.length) { + return; + } + ScratchJr.stage.setPage(ScratchJr.stage.pages[n], false); + } + + static prevPage (e) { + e.preventDefault(); + e.stopPropagation(); + var n = ScratchJr.stage.pages.indexOf(ScratchJr.stage.currentPage); + if (n < 1) { + return; + } + ScratchJr.stage.setPage(ScratchJr.stage.pages[n - 1], false); + } + + static enterFullScreen () { + var w = Math.min(getDocumentWidth(), frame.offsetWidth); + var h = Math.max(getDocumentHeight(), frame.offsetHeight); + frame.appendChild(gn('stage')); + var list = ['go', 'full']; + for (var i = 0; i < list.length; i++) { + gn(list[i]).className = gn(list[i]).className + ' presentationmode'; + frame.appendChild(gn(list[i])); + } + var scale = Math.min((w - (136 * scaleMultiplier)) / gn('stage').owner.width, h / gn('stage').owner.height); + var dx = Math.floor((w - (gn('stage').owner.width * scale)) / 2); + var dy = Math.floor((h - (gn('stage').owner.height * scale)) / 2); + + ScratchJr.stage.setStageScaleAndPosition(scale, dx / scale, dy / scale); + + gn('stage').owner.currentZoom = Math.floor(scale * 100) / 100; + gn('stage').style.webkitTextSizeAdjust = Math.floor(gn('stage').owner.currentZoom * 100) + '%'; + document.body.parentNode.style.background = 'black'; + gn('stage').setAttribute('class', 'stage fullscreen'); + UI.nextpage.setAttribute('class', 'nextpage on'); + } + + static quitFullScreen () { + var div = gn('stageframe'); + div.appendChild(gn('stage')); + ScratchJr.stage.setStageScaleAndPosition(scaleMultiplier, 46, 74); + gn('go').className = 'go off nopresent'; + div.appendChild(gn('go')); + gn('full').className = 'fullscreen'; + div.appendChild(gn('full')); + gn('stage').owner.currentZoom = 1; + gn('stage').style.webkitTextSizeAdjust = '100%'; + document.body.parentNode.style.background = 'none'; + gn('stage').setAttribute('class', 'stage normal'); + UI.nextpage.setAttribute('class', 'nextpage off'); + UI.prevpage.setAttribute('class', 'nextpage off'); + ScratchJr.stage.setViewPage(ScratchJr.stage.currentPage); + Thumbs.updateSprites(); + Thumbs.updatePages(); + } + + ////////////////////////////////////// + // Right panel + ///////////////////////////////////// + + static rightPanel (div) { + var rp = newHTML('div', 'rightpanel', div); + var tb = newHTML('div', 'pages', rp); + tb.setAttribute('id', 'pages'); + var ndiv = newHTML('div', 'pagescc', tb); + ndiv.setAttribute('id', 'pagecc'); + } + + ////////////////////////////////////// + // Tools + ///////////////////////////////////// + + static layoutToolbar (div) { + var h = 56; + var w = 66 * 2; + var tb = newDiv(div, 220, 0, w, h, { + position: 'absolute' + }); + tb.setAttribute('id', 'toolbar'); + var addt = newHTML('div', 'addText', tb); + addt.ontouchstart = UI.addText; + var changebkg = newHTML('div', 'changeBkg', tb); + changebkg.ontouchstart = UI.addBackground; + } + + static addSprite (e) { + if (ScratchJr.onHold) { + return; + } + e.preventDefault(); + e.stopPropagation(); + var pt = Events.getTargetPoint(e); + if (pt.x > (globalx(e.target) + 167)) { + return; + } + ScratchAudio.sndFX('tap.wav'); + ScratchJr.stopStrips(); + ScratchJr.unfocus(e); + if (Events.dragthumbnail) { + Events.mouseUp(e); + } + Library.open('costumes'); + } + + static addBackground (e) { + if (ScratchJr.onHold) { + return; + } + e.preventDefault(); + e.stopPropagation(); + ScratchJr.stopStrips(); + ScratchJr.unfocus(e); + if (Events.dragthumbnail) { + Events.mouseUp(e); + } + Library.open('backgrounds'); + } + + static addText (e) { + if (ScratchJr.onHold) { + return; + } + e.preventDefault(); + e.stopPropagation(); + if (isAndroid) { + if (gn('textbox').style.visibility === 'visible') { + return; + } + } + ScratchJr.unfocus(e); + ScratchJr.stage.currentPage.createText(); + } + + ////////////////////////////////// + // Key Handling in TextBox + ////////////////////////////////// + + static createFormForText (p) { + var tf = newHTML('div', 'pagetext off', p); + tf.setAttribute('id', 'textbox'); + if (isAndroid) { + tf.onmousedown = function (e) { + e.preventDefault(); + }; + } + var activetb = newHTML('form', 'pageform', tf); + activetb.name = 'activetextbox'; + activetb.id = 'myform'; + activetb.textsprite = null; + var field = newTextInput(activetb, 'text'); + field.name = 'typing'; + field.setAttribute('class', 'edittext'); + field.maxLength = 50; + field.onkeypress = undefined; + field.autocomplete = 'off'; + field.autocorrect = false; + field.onblur = undefined; + activetb.onsubmit = undefined; + var ta = newHTML('div', 'pagetextactions', tf); + var clicky = newHTML('div', 'fontsizeText off', ta); + clicky.setAttribute('id', 'fontsizebutton'); + clicky.ontouchstart = UI.openFontSizeMenu; + var col = newHTML('div', 'changecolorText off', ta); + col.setAttribute('id', 'fontcolorbutton'); + + col.ontouchstart = UI.topLevelColor; + UI.createColorMenu(tf); + UI.createTextSizeMenu(tf); + } + + static createColorMenu (div) { + var swatchlist = BlockSpecs.fontcolors; + var spal = newHTML('div', 'textuicolormenu off', div); + spal.setAttribute('id', 'textcolormenu'); + for (var i = 0; i < swatchlist.length; i++) { + var colour = newHTML('div', 'textcolorbucket', spal); + // bucket + var sf = newHTML('div', 'swatchframe', colour); + var sc = newHTML('div', 'swatchcolor', sf); + sc.style.background = swatchlist[i]; + // + sf = newHTML('div', 'splasharea off', colour); + Paint.setSplashColor(sf, Paint.splash, swatchlist[i]); + Paint.addImageUrl(sf, Paint.splashshade); + colour.ontouchstart = UI.setTextColor; + } + UI.setMenuTextColor(gn('textcolormenu').childNodes[9]); + } + + static createTextSizeMenu (div) { + var sizes = BlockSpecs.fontsizes; + var spal = newHTML('div', 'textuifont off', div); + spal.setAttribute('id', 'textfontsizes'); + for (var i = 0; i < sizes.length; i++) { + var textuisize = newHTML('div', 'textuisize t' + (i + 1), spal); + textuisize.fs = sizes[i]; + var sf = newHTML('span', undefined, textuisize); + sf.textContent = 'A'; + textuisize.ontouchstart = UI.setTextSize; + } + UI.setMenuTextSize(gn('textfontsizes').childNodes[5]); + } + + static setMenuTextColor (t) { + var c = t.childNodes[0].childNodes[0].style.backgroundColor; + for (var i = 0; i < gn('textcolormenu').childElementCount; i++) { + var mycolor = gn('textcolormenu').childNodes[i].childNodes[0].childNodes[0].style.backgroundColor; + if (c == mycolor) { + gn('textcolormenu').childNodes[i].childNodes[1].setAttribute('class', 'splasharea on'); + } else { + gn('textcolormenu').childNodes[i].childNodes[1].setAttribute('class', 'splasharea off'); + } + } + } + + static setMenuTextSize (t) { + var c = t.fs; + for (var i = 0; i < gn('textfontsizes').childElementCount; i++) { + var kid = gn('textfontsizes').childNodes[i]; + var fs = kid.fs; + var ckid = kid.className.split(' ')[1]; + if (c == fs) { + gn('textfontsizes').childNodes[i].className = 'textuisize ' + ckid + ' on'; + } else { + gn('textfontsizes').childNodes[i].className = 'textuisize ' + ckid + ' off'; + } + } + } + + ///////////////////////////////////////////////////////// + // Text color and size + ///////////////////////////////////////////////////////// + + static topLevelColor (e) { + e.preventDefault(); + e.stopPropagation(); + if (gn('fontcolorbutton').className == 'changecolorText on') { + gn('fontcolorbutton').className = 'changecolorText off'; + gn('textcolormenu').className = 'textuicolormenu off'; + } else { + gn('fontsizebutton').className = 'fontsizeText off'; + gn('textfontsizes').className = 'textuifont off'; + var text = document.forms.activetextbox.textsprite; + var indx = BlockSpecs.fontcolors.indexOf(text); + if (indx > -1) { + UI.setMenuTextColor(gn('textcolormenu').childNodes[indx]); + } + gn('textcolormenu').className = 'textuicolormenu on'; + gn('fontcolorbutton').className = 'changecolorText on'; + } + } + + static setTextColor (e) { + if (e.touches && (e.touches.length > 1)) { + return; + } + e.preventDefault(); + e.stopPropagation(); + if (window.event) { + t = window.event.srcElement; + } else { + t = e.target; + } + var b = 'textcolorbucket' != t.className; + while (b) { + var t = t.parentNode; + b = t && ('textcolorbucket' != t.className); + } + if (!t) { + return; + } + ScratchAudio.sndFX('splash.wav'); + UI.setMenuTextColor(t); var text = document.forms.activetextbox.textsprite; - var indx = BlockSpecs.fontcolors.indexOf(text); - if (indx > -1) { - UI.setMenuTextColor(gn('textcolormenu').childNodes[indx]); + var c = t.childNodes[0].childNodes[0].style.background; + text.setColor(c); + Undo.record({ + action: 'edittext', + where: text.div.parentNode.owner.id, + who: text.id + }); + ScratchJr.storyStart('UI.setTextColor'); // Record a change for sample projects in story-starter mode + var ti = document.forms.activetextbox.typing; + ti.style.color = c; + } + + static openFontSizeMenu (e) { + e.preventDefault(); + e.stopPropagation(); + if (gn('fontsizebutton').className == 'fontsizeText on') { + gn('fontsizebutton').className = 'fontsizeText off'; + gn('textfontsizes').className = 'textuifont off'; + } else { + gn('fontcolorbutton').className = 'changecolorText off'; + gn('textcolormenu').className = 'textuicolormenu off'; + var text = document.forms.activetextbox.textsprite; + var indx = BlockSpecs.fontsizes.indexOf(text.fontsize); + if (indx > -1) { + UI.setMenuTextSize(gn('textfontsizes').childNodes[indx]); + } + gn('textfontsizes').className = 'textuifont on'; + gn('fontsizebutton').className = 'fontsizeText on'; } - gn('textcolormenu').className = 'textuicolormenu on'; - gn('fontcolorbutton').className = 'changecolorText on'; } -}; -UI.setTextColor = function (e) { - if (e.touches && (e.touches.length > 1)) { - return; - } - e.preventDefault(); - e.stopPropagation(); - if (window.event) { - t = window.event.srcElement; - } else { - t = e.target; - } - var b = 'textcolorbucket' != t.className; - while (b) { - var t = t.parentNode; - b = t && ('textcolorbucket' != t.className); - } - if (!t) { - return; - } - ScratchAudio.sndFX('splash.wav'); - UI.setMenuTextColor(t); - var text = document.forms.activetextbox.textsprite; - var c = t.childNodes[0].childNodes[0].style.background; - text.setColor(c); - Undo.record({ - action: 'edittext', - where: text.div.parentNode.owner.id, - who: text.id - }); - ScratchJr.storyStart('UI.setTextColor'); // Record a change for sample projects in story-starter mode - var ti = document.forms.activetextbox.typing; - ti.style.color = c; -}; - -UI.openFontSizeMenu = function (e) { - e.preventDefault(); - e.stopPropagation(); - if (gn('fontsizebutton').className == 'fontsizeText on') { - gn('fontsizebutton').className = 'fontsizeText off'; - gn('textfontsizes').className = 'textuifont off'; - } else { - gn('fontcolorbutton').className = 'changecolorText off'; - gn('textcolormenu').className = 'textuicolormenu off'; + static setTextSize (e) { + e.preventDefault(); + e.stopPropagation(); + var t; + if (window.event) { + t = window.event.srcElement; + } else { + t = e.target; + } + if (t.nodeName == 'SPAN') { + t = t.parentNode; + } + if (!t) { + return; + } + var ckid = t.className.split(' ')[0]; + if (ckid != 'textuisize') { + return; + } + UI.setMenuTextSize(t); var text = document.forms.activetextbox.textsprite; - var indx = BlockSpecs.fontsizes.indexOf(text.fontsize); - if (indx > -1) { - UI.setMenuTextSize(gn('textfontsizes').childNodes[indx]); + text.setFontSize(t.fs); + Undo.record({ + action: 'edittext', + where: text.div.parentNode.owner.id, + who: text.id + }); + ScratchJr.storyStart('UI.setTextSize'); // Record a change for sample projects in story-starter mode + var ti = document.forms.activetextbox.typing; + ti.style.fontSize = (t.fs * scaleMultiplier) + 'px'; + setProps(document.forms.activetextbox.style, { + height: ((t.fs + 10) * scaleMultiplier) + 'px' + }); + } + + /////////////////////////////////////////// + // UI clear + ///////////////////////////////////////// + + static clear () { + var costumes = gn('spritecc'); + while (costumes.childElementCount > 0) { + costumes.removeChild(costumes.childNodes[0]); + } + var pthumbs = gn('pagecc'); + while (pthumbs.childElementCount > 0) { + pthumbs.removeChild(pthumbs.childNodes[0]); } - gn('textfontsizes').className = 'textuifont on'; - gn('fontsizebutton').className = 'fontsizeText on'; } -}; - -UI.setTextSize = function (e) { - e.preventDefault(); - e.stopPropagation(); - var t; - if (window.event) { - t = window.event.srcElement; - } else { - t = e.target; - } - if (t.nodeName == 'SPAN') { - t = t.parentNode; - } - if (!t) { - return; - } - var ckid = t.className.split(' ')[0]; - if (ckid != 'textuisize') { - return; - } - UI.setMenuTextSize(t); - var text = document.forms.activetextbox.textsprite; - text.setFontSize(t.fs); - Undo.record({ - action: 'edittext', - where: text.div.parentNode.owner.id, - who: text.id - }); - ScratchJr.storyStart('UI.setTextSize'); // Record a change for sample projects in story-starter mode - var ti = document.forms.activetextbox.typing; - ti.style.fontSize = (t.fs * scaleMultiplier) + 'px'; - setProps(document.forms.activetextbox.style, { - height: ((t.fs + 10) * scaleMultiplier) + 'px' - }); -}; - -/////////////////////////////////////////// -// UI clear -///////////////////////////////////////// - -UI.clear = function () { - var costumes = gn('spritecc'); - while (costumes.childElementCount > 0) { - costumes.removeChild(costumes.childNodes[0]); - } - var pthumbs = gn('pagecc'); - while (pthumbs.childElementCount > 0) { - pthumbs.removeChild(pthumbs.childNodes[0]); - } -}; +} diff --git a/src/editor/ui/Undo.js b/src/editor/ui/Undo.js index 76ef062..173fb55 100755 --- a/src/editor/ui/Undo.js +++ b/src/editor/ui/Undo.js @@ -1,423 +1,431 @@ ////////////////////////////////// // Undo / Redo Functions ////////////////////////////////// -var Undo = function () {}; -Undo.buffer = []; -Undo.index = 0; -Undo.aux = ''; -Undo.tryCounter; +import ScratchJr from '../ScratchJr'; +import Thumbs from './Thumbs'; +import Project from './Project'; +import Palette from './Palette'; +import UI from './UI'; +import ScratchAudio from '../../utils/ScratchAudio'; +import {newHTML, isTablet, gn} from '../../utils/lib'; -Undo.init = function () { - Undo.index = Undo.buffer.length; - Undo.update(); -}; +let buffer = []; +let index = 0; +let tryCounter; -Undo.setup = function (p) { - var div = newHTML('div', 'controlundo', p); - div.setAttribute('id', 'undocontrols'); - var lib = [['undo', Undo.prevStep], ['redo', Undo.nextStep]]; - for (var i = 0; i < lib.length; i++) { - Undo.newToggleClicky(div, 'id_', lib[i][0], lib[i][1]); +export default class Undo { + static init () { + index = buffer.length; + Undo.update(); } - Undo.update(); -}; -Undo.newToggleClicky = function (p, prefix, key, fcn) { - var div = newHTML('div', key + 'button', p); - div.setAttribute('type', 'toggleclicky'); - div.setAttribute('id', prefix + key); - if (fcn) { - if (isTablet) { - div.ontouchstart = function (evt) { - fcn(evt); - }; - } else { - div.onmousedown = function (evt) { - fcn(evt); - }; + static setup (p) { + var div = newHTML('div', 'controlundo', p); + div.setAttribute('id', 'undocontrols'); + var lib = [['undo', Undo.prevStep], ['redo', Undo.nextStep]]; + for (var i = 0; i < lib.length; i++) { + Undo.newToggleClicky(div, 'id_', lib[i][0], lib[i][1]); } + Undo.update(); } - return div; -}; -Undo.record = function (obj) { - //console.log ("record", Undo.index, JSON.stringify(obj)); - if (ScratchJr.getActiveScript()) { - ScratchJr.getActiveScript().owner.removeCaret(); - } - if ((Undo.index + 1) <= Undo.buffer.length) { - Undo.buffer.splice(Undo.index + 1, Undo.buffer.length); - } - var data = Project.getUndo(); - for (var key in obj) { - data[key] = obj[key]; - } - Undo.buffer.push(data); - Undo.index++; - Undo.update(); - - // Project change state is usually tracked by looking if a particular action would record an undo - // We need slightly more specific behavior for story starters, so storyStarted is unaffected here. - ScratchJr.changed = true; -}; - -////////////////////////////////// -// Control buttons callbacks -// -//////////////////////////////// - -Undo.prevStep = function (e) { - if (isTablet && e.touches && (e.touches.length > 1)) { - return; - } - e.preventDefault(); - e.stopPropagation(); - ScratchJr.unfocus(); - ScratchJr.time = e.timeStamp; - while (Undo.index >= Undo.buffer.length) { - Undo.index--; - } - Undo.index--; - var snd = (Undo.index < 0) ? 'boing.wav' : 'tap.wav'; - ScratchAudio.sndFX(snd); - if (Undo.index < 0) { - Undo.index = 0; - } else { - Undo.smartRecreate('prev', Undo.buffer[Undo.index + 1], Undo.buffer[Undo.index]); - } -}; - -Undo.nextStep = function (e) { - if (isTablet && e.touches && (e.touches.length > 1)) { - return; - } - e.preventDefault(); - e.stopPropagation(); - ScratchJr.unfocus(); - ScratchJr.time = e.timeStamp; - Undo.index++; - var snd = (Undo.index > Undo.buffer.length - 1) ? 'boing.wav' : 'tap.wav'; - ScratchAudio.sndFX(snd); - if (Undo.index > Undo.buffer.length - 1) { - Undo.index = Undo.buffer.length - 1; - } else { - Undo.smartRecreate('next', Undo.buffer[Undo.index], Undo.buffer[Undo.index]); - } -}; - -Undo.smartRecreate = function (cmd, elem, data) { - ScratchJr.stopStrips(); - var action = elem.action; - var page = elem.where; - var spr = elem.who; - // console.log (action, page, spr); - switch (action) { - case 'pageorder': - ScratchJr.stage.pages = Undo.getPageOrder(data); - Undo.recreateAllScripts(data); - ScratchJr.stage.setPage(gn(data.currentPage).owner, false); - if (Palette.numcat == 5) { - Palette.selectCategory(5); - } - break; - case 'changepage': - ScratchJr.stage.setPage(gn(data.currentPage).owner, false); - break; - case 'changebkg': - gn(page).owner.redoChangeBkg(data); - break; - case 'scripts': - Undo.redoScripts(data, page, spr); - if (spr && gn(spr)) { - gn(page).owner.setCurrentSprite(gn(spr).owner); // sets the variables - Thumbs.selectThisSprite(gn(spr).owner); // sets the UI - UI.resetSpriteLibrary(); - } - break; - case 'deletepage': - case 'addpage': - if (data[page]) { - Undo.copyPage(data, page); - } else { - Undo.removePage(data, page); - } - break; - case 'deletesprite': - case 'copy': - if (data[page][spr]) { - Undo.copySprite(data, page, spr); - } else { - Undo.removeSprite(data, page, spr); - } - break; - case 'deletesound': - var sounds = data[page][spr].sounds.concat(); - gn(spr).owner.sounds = sounds; - Undo.redoScripts(data, page, spr); - if (Palette.numcat == 3) { - Palette.selectCategory(3); - } - break; - case 'recordsound': - spr = gn((data[page][spr]).id).owner; - if (elem.sound && (spr.sounds.indexOf(elem.sound) > -1)) { - var indx = spr.sounds.indexOf(elem.sound); - if (indx > -1) { - spr.sounds.splice(indx, 1); + static newToggleClicky (p, prefix, key, fcn) { + var div = newHTML('div', key + 'button', p); + div.setAttribute('type', 'toggleclicky'); + div.setAttribute('id', prefix + key); + if (fcn) { + if (isTablet) { + div.ontouchstart = function (evt) { + fcn(evt); + }; + } else { + div.onmousedown = function (evt) { + fcn(evt); + }; } - } else { - spr.sounds.push(elem.sound); } - if (Palette.numcat == 3) { - Palette.selectCategory(3); - } - break; - case 'edittext': // sprite delete or add - case 'modify': - Undo.removeSprite(data, page, spr); - if (data[page][spr]) { - Undo.copySprite(data, page, spr); - } - break; - default: - Project.clear(); - Undo.recreate(Undo.buffer[Undo.index]); - break; + return div; } - Undo.update(); -}; -Undo.copyPage = function (obj, page) { - var sc = ScratchJr.getSprite() ? gn(ScratchJr.stage.currentPage.currentSpriteName + '_scripts') : undefined; - if (sc) { - sc.owner.deactivate(); + static record (obj) { + //console.log ("record", index, JSON.stringify(obj)); + if (ScratchJr.getActiveScript()) { + ScratchJr.getActiveScript().owner.removeCaret(); + } + if ((index + 1) <= buffer.length) { + buffer.splice(index + 1, buffer.length); + } + var data = Project.getUndo(); + for (var key in obj) { + data[key] = obj[key]; + } + buffer.push(data); + index++; + Undo.update(); + + // Project change state is usually tracked by looking if a particular action would record an undo + // We need slightly more specific behavior for story starters, so storyStarted is unaffected here. + ScratchJr.changed = true; } - Project.recreatePage(page, obj[page], nextStep2); - function nextStep2 () { - ScratchJr.stage.pages = Undo.getPageOrder(obj); - ScratchJr.stage.setPage(gn(obj.currentPage).owner, false); - Undo.recreateAllScripts(obj); - var spritename = obj[obj.currentPage].lastSprite; - if (spritename && gn(spritename)) { - var spr = gn(spritename).owner; - var page = spr.div.parentNode.owner; - page.setCurrentSprite(spr); - Thumbs.selectThisSprite(spr); + + ////////////////////////////////// + // Control buttons callbacks + // + //////////////////////////////// + + static prevStep (e) { + if (isTablet && e.touches && (e.touches.length > 1)) { + return; + } + e.preventDefault(); + e.stopPropagation(); + ScratchJr.unfocus(); + ScratchJr.time = e.timeStamp; + while (index >= buffer.length) { + index--; + } + index--; + var snd = (index < 0) ? 'boing.wav' : 'tap.wav'; + ScratchAudio.sndFX(snd); + if (index < 0) { + index = 0; + } else { + Undo.smartRecreate('prev', buffer[index + 1], buffer[index]); + } + } + + static nextStep (e) { + if (isTablet && e.touches && (e.touches.length > 1)) { + return; + } + e.preventDefault(); + e.stopPropagation(); + ScratchJr.unfocus(); + ScratchJr.time = e.timeStamp; + index++; + var snd = (index > buffer.length - 1) ? 'boing.wav' : 'tap.wav'; + ScratchAudio.sndFX(snd); + if (index > buffer.length - 1) { + index = buffer.length - 1; + } else { + Undo.smartRecreate('next', buffer[index], buffer[index]); + } + } + + static smartRecreate (cmd, elem, data) { + ScratchJr.stopStrips(); + var action = elem.action; + var page = elem.where; + var spr = elem.who; + // console.log (action, page, spr); + switch (action) { + case 'pageorder': + ScratchJr.stage.pages = Undo.getPageOrder(data); + Undo.recreateAllScripts(data); + ScratchJr.stage.setPage(gn(data.currentPage).owner, false); if (Palette.numcat == 5) { Palette.selectCategory(5); } + break; + case 'changepage': + ScratchJr.stage.setPage(gn(data.currentPage).owner, false); + break; + case 'changebkg': + gn(page).owner.redoChangeBkg(data); + break; + case 'scripts': + Undo.redoScripts(data, page, spr); + if (spr && gn(spr)) { + gn(page).owner.setCurrentSprite(gn(spr).owner); // sets the variables + Thumbs.selectThisSprite(gn(spr).owner); // sets the UI + UI.resetSpriteLibrary(); + } + break; + case 'deletepage': + case 'addpage': + if (data[page]) { + Undo.copyPage(data, page); + } else { + Undo.removePage(data, page); + } + break; + case 'deletesprite': + case 'copy': + if (data[page][spr]) { + Undo.copySprite(data, page, spr); + } else { + Undo.removeSprite(data, page, spr); + } + break; + case 'deletesound': + var sounds = data[page][spr].sounds.concat(); + gn(spr).owner.sounds = sounds; + Undo.redoScripts(data, page, spr); + if (Palette.numcat == 3) { + Palette.selectCategory(3); + } + break; + case 'recordsound': + spr = gn((data[page][spr]).id).owner; + if (elem.sound && (spr.sounds.indexOf(elem.sound) > -1)) { + var indx = spr.sounds.indexOf(elem.sound); + if (indx > -1) { + spr.sounds.splice(indx, 1); + } + } else { + spr.sounds.push(elem.sound); + } + if (Palette.numcat == 3) { + Palette.selectCategory(3); + } + break; + case 'edittext': // sprite delete or add + case 'modify': + Undo.removeSprite(data, page, spr); + if (data[page][spr]) { + Undo.copySprite(data, page, spr); + } + break; + default: + Project.clear(); + Undo.recreate(buffer[index]); + break; + } + Undo.update(); + } + + static copyPage (obj, page) { + var sc = ScratchJr.getSprite() ? gn(ScratchJr.stage.currentPage.currentSpriteName + '_scripts') : undefined; + if (sc) { + sc.owner.deactivate(); + } + Project.recreatePage(page, obj[page], nextStep2); + function nextStep2 () { + ScratchJr.stage.pages = Undo.getPageOrder(obj); + ScratchJr.stage.setPage(gn(obj.currentPage).owner, false); + Undo.recreateAllScripts(obj); + var spritename = obj[obj.currentPage].lastSprite; + if (spritename && gn(spritename)) { + var spr = gn(spritename).owner; + var page = spr.div.parentNode.owner; + page.setCurrentSprite(spr); + Thumbs.selectThisSprite(spr); + if (Palette.numcat == 5) { + Palette.selectCategory(5); + } + } } } -}; -Undo.getPageOrder = function (data) { - var pages = data.pages; - var res = []; - for (var i = 0; i < pages.length; i++) { - res.push(gn(pages[i]).owner); + static getPageOrder (data) { + var pages = data.pages; + var res = []; + for (var i = 0; i < pages.length; i++) { + res.push(gn(pages[i]).owner); + } + return res; } - return res; -}; -Undo.recreateAllScripts = function (data) { - for (var n = 0; n < data.pages.length; n++) { - var page = data[data.pages[n]]; - var sprnames = page.sprites; - for (var i = 0; i < sprnames.length; i++) { - var spr = page[sprnames[i]]; - if (!spr) { - continue; + static recreateAllScripts (data) { + for (var n = 0; n < data.pages.length; n++) { + var page = data[data.pages[n]]; + var sprnames = page.sprites; + for (var i = 0; i < sprnames.length; i++) { + var spr = page[sprnames[i]]; + if (!spr) { + continue; + } + if (spr.type != 'sprite') { + continue; + } + var sc = gn(spr.id + '_scripts'); + if (!sc) { + continue; + } + Undo.redoScripts(data, data.pages[n], sprnames[i]); } - if (spr.type != 'sprite') { - continue; - } - var sc = gn(spr.id + '_scripts'); - if (!sc) { - continue; - } - Undo.redoScripts(data, data.pages[n], sprnames[i]); } } -}; -Undo.removePage = function (data, str) { - if (!gn(str)) { - return; - } - var page = gn(str).owner; - if (!page) { - return; - } - ScratchJr.stage.removePageBlocks(str); - ScratchJr.stage.removePage(page); - ScratchJr.stage.pages = Undo.getPageOrder(data); - if (ScratchJr.stage.pages.length == 0) { - Undo.copyPage(data, data.currentPage); - } else { - ScratchJr.stage.setViewPage(gn(data.currentPage).owner); - Thumbs.updateSprites(); - Thumbs.updatePages(); - } -}; - -Undo.redoScripts = function (data, page, spr) { - var div = gn(spr + '_scripts'); - while (div.childElementCount > 0) { - div.removeChild(div.childNodes[0]); - } - var sc = div.owner; - var list = data[page][spr].scripts; - for (var j = 0; j < list.length; j++) { - sc.recreateStrip(list[j]); - } -}; - -Undo.copySprite = function (data, page, spr) { - var obj = data[page][spr]; - var fcn = function (spr) { - if (spr.type == 'sprite') { - if (page == ScratchJr.stage.currentPage.id) { - spr.div.style.visibility = 'visible'; - } - Undo.setSprite(page, data); + static removePage (data, str) { + if (!gn(str)) { + return; + } + var page = gn(str).owner; + if (!page) { + return; + } + ScratchJr.stage.removePageBlocks(str); + ScratchJr.stage.removePage(page); + ScratchJr.stage.pages = Undo.getPageOrder(data); + if (ScratchJr.stage.pages.length == 0) { + Undo.copyPage(data, data.currentPage); } else { - var delta = spr.fontsize * 1.35; - if (spr.homey == spr.page.textstartat) { - spr.page.textstartat += delta; - } + ScratchJr.stage.setViewPage(gn(data.currentPage).owner); + Thumbs.updateSprites(); Thumbs.updatePages(); } - }; - Project.recreateObject(gn(page).owner, spr, obj, fcn, (data[page].lastSprite == spr)); -}; + } -Undo.setSprite = function (page, data) { - Thumbs.updatePages(); - if (page != ScratchJr.stage.currentPage.id) { - return; + static redoScripts (data, page, spr) { + var div = gn(spr + '_scripts'); + while (div.childElementCount > 0) { + div.removeChild(div.childNodes[0]); + } + var sc = div.owner; + var list = data[page][spr].scripts; + for (var j = 0; j < list.length; j++) { + sc.recreateStrip(list[j]); + } } - var pageobj = gn(page).owner; - var lastspritename = data[page].lastSprite; - var lastsprite = lastspritename ? gn(lastspritename) : undefined; - if (!lastsprite) { - pageobj.setCurrentSprite(undefined); - } else { - var cs = lastsprite.owner; - pageobj.setCurrentSprite(cs); - UI.needsScroll(); - Thumbs.updateSprites(); - } -}; -Undo.removeSprite = function (data, page, spr) { - if (!gn(spr)) { - return; + static copySprite (data, page, spr) { + var obj = data[page][spr]; + var fcn = function (spr) { + if (spr.type == 'sprite') { + if (page == ScratchJr.stage.currentPage.id) { + spr.div.style.visibility = 'visible'; + } + Undo.setSprite(page, data); + } else { + var delta = spr.fontsize * 1.35; + if (spr.homey == spr.page.textstartat) { + spr.page.textstartat += delta; + } + Thumbs.updatePages(); + } + }; + Project.recreateObject(gn(page).owner, spr, obj, fcn, (data[page].lastSprite == spr)); } - var sprite = gn(spr).owner; - var th = sprite.thumbnail; - ScratchJr.runtime.stopThreadSprite(sprite); - var pageobj = gn(page).owner; - var list = JSON.parse(pageobj.sprites); - var n = list.indexOf(spr); - list.splice(n, 1); - pageobj.sprites = JSON.stringify(list); - gn(spr).parentNode.removeChild(gn(spr)); - if (!gn(spr + '_scripts')) { + + static setSprite (page, data) { Thumbs.updatePages(); - return; - } - var sc = gn(spr + '_scripts'); - if (sc) { - sc.parentNode.removeChild(sc); - } - if (th && th.parentNode) { - th.parentNode.removeChild(th); - } - Undo.setSprite(page, data); -}; - -Undo.recreate = function (data) { - Project.mediaCount = 0; - ScratchJr.stage.pages = []; - var pages = data.pages; - if (data.projectsounds) { - ScratchAudio.projectsounds = data.projectsounds; - } - for (var i = 0; i < pages.length; i++) { - Project.recreatePage(pages[i], data[pages[i]]); - } - Undo.loadPage(data.currentPage); -}; - -Undo.loadPage = function (pageid) { - var pages = ScratchJr.stage.getPagesID(); - if (pages.indexOf(pageid) < 0) { - ScratchJr.stage.currentPage = ScratchJr.stage.pages[0]; - } else { - ScratchJr.stage.currentPage = ScratchJr.stage.getPage(pageid); - } - ScratchJr.stage.currentPage.div.style.visibility = 'visible'; - ScratchJr.stage.currentPage.setPageSprites('visible'); - Undo.tryCounter = 100; - if (Project.mediaCount > 0) { - setTimeout(function () { - Undo.updateImages(); - }, 20); - } else { - Undo.doneLoading(); - } -}; - -Undo.updateImages = function () { - Undo.tryCounter--; - var done = (Project.mediaCount < 1) || (Undo.tryCounter < 1); - if (done) { - Undo.doneLoading(); - } else { - setTimeout(function () { - Undo.updateImages(); - }, 20); - } -}; - -Undo.flashIcon = function (div, press) { - div.setAttribute('class', press); - setTimeout(function () { - Undo.update(); - }, 1000); -}; - -Undo.doneLoading = function () { - Thumbs.updateSprites(); - Thumbs.updatePages(); -}; - -Undo.update = function () { - if (gn('id_undo')) { - if (Undo.buffer.length == 1) { - Undo.tunOffButton(gn('id_undo')); + if (page != ScratchJr.stage.currentPage.id) { + return; + } + var pageobj = gn(page).owner; + var lastspritename = data[page].lastSprite; + var lastsprite = lastspritename ? gn(lastspritename) : undefined; + if (!lastsprite) { + pageobj.setCurrentSprite(undefined); } else { - if (Undo.index < 1) { + var cs = lastsprite.owner; + pageobj.setCurrentSprite(cs); + UI.needsScroll(); + Thumbs.updateSprites(); + } + } + + static removeSprite (data, page, spr) { + if (!gn(spr)) { + return; + } + var sprite = gn(spr).owner; + var th = sprite.thumbnail; + ScratchJr.runtime.stopThreadSprite(sprite); + var pageobj = gn(page).owner; + var list = JSON.parse(pageobj.sprites); + var n = list.indexOf(spr); + list.splice(n, 1); + pageobj.sprites = JSON.stringify(list); + gn(spr).parentNode.removeChild(gn(spr)); + if (!gn(spr + '_scripts')) { + Thumbs.updatePages(); + return; + } + var sc = gn(spr + '_scripts'); + if (sc) { + sc.parentNode.removeChild(sc); + } + if (th && th.parentNode) { + th.parentNode.removeChild(th); + } + Undo.setSprite(page, data); + } + + static recreate (data) { + Project.mediaCount = 0; + ScratchJr.stage.pages = []; + var pages = data.pages; + if (data.projectsounds) { + ScratchAudio.projectsounds = data.projectsounds; + } + for (var i = 0; i < pages.length; i++) { + Project.recreatePage(pages[i], data[pages[i]]); + } + Undo.loadPage(data.currentPage); + } + + static loadPage (pageid) { + var pages = ScratchJr.stage.getPagesID(); + if (pages.indexOf(pageid) < 0) { + ScratchJr.stage.currentPage = ScratchJr.stage.pages[0]; + } else { + ScratchJr.stage.currentPage = ScratchJr.stage.getPage(pageid); + } + ScratchJr.stage.currentPage.div.style.visibility = 'visible'; + ScratchJr.stage.currentPage.setPageSprites('visible'); + tryCounter = 100; + if (Project.mediaCount > 0) { + setTimeout(function () { + Undo.updateImages(); + }, 20); + } else { + Undo.doneLoading(); + } + } + + static updateImages () { + tryCounter--; + var done = (Project.mediaCount < 1) || (tryCounter < 1); + if (done) { + Undo.doneLoading(); + } else { + setTimeout(function () { + Undo.updateImages(); + }, 20); + } + } + + static flashIcon (div, press) { + div.setAttribute('class', press); + setTimeout(function () { + Undo.update(); + }, 1000); + } + + static doneLoading () { + Thumbs.updateSprites(); + Thumbs.updatePages(); + } + + static update () { + if (gn('id_undo')) { + if (buffer.length == 1) { Undo.tunOffButton(gn('id_undo')); } else { - Undo.tunOnButton(gn('id_undo')); + if (index < 1) { + Undo.tunOffButton(gn('id_undo')); + } else { + Undo.tunOnButton(gn('id_undo')); + } + } + if (index >= buffer.length - 1) { + Undo.tunOffButton(gn('id_redo')); + } else { + Undo.tunOnButton(gn('id_redo')); } } - if (Undo.index >= Undo.buffer.length - 1) { - Undo.tunOffButton(gn('id_redo')); - } else { - Undo.tunOnButton(gn('id_redo')); - } } -}; -Undo.tunOnButton = function (kid) { - var kclass = kid.getAttribute('class').split(' ')[0]; - kid.setAttribute('class', kclass + ' enable'); -}; + static tunOnButton (kid) { + var kclass = kid.getAttribute('class').split(' ')[0]; + kid.setAttribute('class', kclass + ' enable'); + } -Undo.tunOffButton = function (kid) { - var kclass = kid.getAttribute('class').split(' ')[0]; - kid.setAttribute('class', kclass + ' disable'); -}; + static tunOffButton (kid) { + var kclass = kid.getAttribute('class').split(' ')[0]; + kid.setAttribute('class', kclass + ' disable'); + } +} diff --git a/src/painteditor/Paint.js b/src/painteditor/Paint.js index 32d825a..951da41 100644 --- a/src/painteditor/Paint.js +++ b/src/painteditor/Paint.js @@ -626,7 +626,6 @@ export default class Paint { ti.getBoundingClientRect().bottom * window.devicePixelRatio ); } - Undo.aux = Project.getProject(ScratchJr.stage.currentPage.id); setTimeout(function () { ti.setSelectionRange(ti.value.length, ti.value.length); }, 1);