// Copyright (c) 2014-17 Walter Bender
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the The GNU Affero General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
|
|
|
|
// All things related to palettes
|
|
requirejs(['activity/utils']);
|
|
|
|
const PROTOBLOCKSCALE = 1.0;
|
|
const PALETTELEFTMARGIN = 10;
|
|
|
|
|
|
function maxPaletteHeight(menuSize, scale) {
|
|
// Palettes don't start at the top of the screen and the last
|
|
// block in a palette cannot start at the bottom of the screen,
|
|
// hence - 2 * menuSize.
|
|
|
|
var h = (windowHeight() * canvasPixelRatio()) / scale - (2 * menuSize);
|
|
return h - (h % STANDARDBLOCKHEIGHT) + (STANDARDBLOCKHEIGHT / 2);
|
|
};
|
|
|
|
|
|
function paletteBlockButtonPush(blocks, name, arg) {
|
|
var blk = blocks.makeBlock(name, arg);
|
|
return blk;
|
|
};
|
|
|
|
|
|
// There are several components to the palette system:
|
|
//
|
|
// (1) A palette button (in the Palettes.buttons dictionary) is a
|
|
// button that envokes a palette; The buttons have artwork associated
|
|
// with them: a bitmap and a highlighted bitmap that is shown when the
|
|
// mouse is over the button. (The artwork is found in artwork.js.)
|
|
//
|
|
// loadPaletteButtonHandler is the event handler for palette buttons.
|
|
//
|
|
// (2) A menu (in the Palettes.dict dictionary) is the palette
|
|
// itself. It consists of a title bar (with an icon, label, and close
|
|
// button), and individual containers for each protoblock on the
|
|
// menu. There is a background behind each protoblock that is part of
|
|
// the palette container.
|
|
//
|
|
// loadPaletteMenuItemHandler is the event handler for the palette menu.
|
|
|
|
|
|
function Palettes () {
|
|
this.canvas = null;
|
|
this.blocks = null;
|
|
this.refreshCanvas = null;
|
|
this.stage = null;
|
|
this.cellSize = null;
|
|
this.scrollDiff = 0;
|
|
this.originalSize = 55; // this is the original svg size
|
|
this.trashcan = null;
|
|
this.initial_x = 55;
|
|
this.initial_y = 55;
|
|
this.firstTime = true;
|
|
this.background = null;
|
|
this.upIndicator = null;
|
|
this.upIndicatorStatus = false;
|
|
this.downIndicator = null;
|
|
this.downIndicatorStatus = true;
|
|
this.circles = {};
|
|
this.palette_text = new createjs.Text('', '20px Arial', '#ff7700');
|
|
this.mouseOver = false;
|
|
this.activePalette = null;
|
|
this.visible = true;
|
|
this.scale = 1.0;
|
|
this.mobile = false;
|
|
this.current = DEFAULTPALETTE;
|
|
this.x = null;
|
|
this.y = null;
|
|
this.container = null;
|
|
|
|
if (sugarizerCompatibility.isInsideSugarizer()) {
|
|
storage = sugarizerCompatibility.data;
|
|
} else {
|
|
storage = localStorage;
|
|
}
|
|
|
|
// The collection of palettes.
|
|
this.dict = {};
|
|
this.buttons = {}; // The toolbar button for each palette.
|
|
|
|
this.init = function () {
|
|
this.halfCellSize = Math.floor(this.cellSize / 2);
|
|
this.x = 0;
|
|
this.y = this.cellSize;
|
|
|
|
this.container = new createjs.Container();
|
|
this.container.snapToPixelEnabled = true;
|
|
this.stage.addChild(this.container);
|
|
};
|
|
|
|
this.setCanvas = function (canvas) {
|
|
this.canvas = canvas;
|
|
return this;
|
|
};
|
|
|
|
this.setStage = function (stage) {
|
|
this.stage = stage;
|
|
return this;
|
|
};
|
|
|
|
this.setRefreshCanvas = function (refreshCanvas) {
|
|
this.refreshCanvas = refreshCanvas;
|
|
return this;
|
|
};
|
|
|
|
this.setTrashcan = function (trashcan) {
|
|
this.trashcan = trashcan;
|
|
return this;
|
|
};
|
|
|
|
this.setSize = function (size) {
|
|
this.cellSize = size;
|
|
return this;
|
|
};
|
|
|
|
this.setMobile = function (mobile) {
|
|
this.mobile = mobile;
|
|
if (mobile) {
|
|
this._hideMenus();
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
this.setScale = function (scale) {
|
|
this.scale = scale;
|
|
|
|
this._updateButtonMasks();
|
|
|
|
for (var i in this.dict) {
|
|
this.dict[i]._resizeEvent();
|
|
}
|
|
|
|
if (this.downIndicator != null) {
|
|
this.downIndicator.y = (windowHeight() / scale) - 27;
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
// We need access to the macro dictionary because we load them.
|
|
this.setMacroDictionary = function (obj) {
|
|
this.macroDict = obj;
|
|
|
|
return this;
|
|
};
|
|
|
|
this.menuScrollEvent = function (direction, scrollSpeed) {
|
|
var keys = Object.keys(this.buttons);
|
|
|
|
var diff = direction * scrollSpeed;
|
|
if (this.buttons[keys[0]].y + diff > this.cellSize && direction > 0) {
|
|
this.upIndicator.visible = false;
|
|
this.upIndicatorStatus = this.upIndicator.visible;
|
|
this.refreshCanvas();
|
|
return;
|
|
} else {
|
|
this.upIndicatorStatus = this.upIndicator.visible;
|
|
this.upIndicator.visible = true;
|
|
}
|
|
|
|
if (this.buttons[last(keys)].y + diff < windowHeight() / this.scale - this.cellSize && direction < 0) {
|
|
this.downIndicator.visible = false;
|
|
this.downIndicatorStatus = this.downIndicator.visible;
|
|
this.refreshCanvas();
|
|
return;
|
|
} else {
|
|
this.downIndicator.visible = true;
|
|
this.downIndicatorStatus = this.downIndicator.visible;
|
|
}
|
|
|
|
this.scrollDiff += diff;
|
|
|
|
for (var name in this.buttons) {
|
|
this.buttons[name].y += diff;
|
|
this.buttons[name].visible = true;
|
|
}
|
|
|
|
this._updateButtonMasks();
|
|
this.refreshCanvas();
|
|
};
|
|
|
|
this._updateButtonMasks = function () {
|
|
for (var name in this.buttons) {
|
|
var s = new createjs.Shape();
|
|
s.graphics.r(0, 0, this.cellSize, windowHeight() / this.scale);
|
|
s.x = 0;
|
|
s.y = this.cellSize / 2;
|
|
this.buttons[name].mask = s;
|
|
}
|
|
};
|
|
|
|
this.hidePaletteIconCircles = function () {
|
|
if (!sugarizerCompatibility.isInsideSugarizer()) {
|
|
hidePaletteNameDisplay(palette_text, this.stage);
|
|
}
|
|
hideButtonHighlight(this.circles, this.stage);
|
|
};
|
|
|
|
this.makePalettes = function (hide) {
|
|
if (this.firstTime) {
|
|
var shape = new createjs.Shape();
|
|
shape.graphics.f('#a2c5d8').r(0, 0, 55, windowHeight()).ef();
|
|
shape.width = 55;
|
|
shape.height = windowHeight();
|
|
this.stage.addChild(shape);
|
|
this.background = shape;
|
|
}
|
|
|
|
function __processUpIcon(palettes, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.4;
|
|
palettes.stage.addChild(bitmap);
|
|
bitmap.x = 55;
|
|
bitmap.y = 55;
|
|
bitmap.visible = false;
|
|
palettes.upIndicator = bitmap;
|
|
|
|
palettes.upIndicator.on('click', function (event) {
|
|
palettes.menuScrollEvent(1, 40);
|
|
palettes.hidePaletteIconCircles();
|
|
});
|
|
};
|
|
|
|
function __processDownIcon(palettes, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.4;
|
|
palettes.stage.addChild(bitmap);
|
|
bitmap.x = 55;
|
|
bitmap.y = (windowHeight() / palettes.scale) - 27;
|
|
|
|
bitmap.visible = true;
|
|
palettes.downIndicator = bitmap;
|
|
|
|
palettes.downIndicator.on('click', function (event) {
|
|
palettes.menuScrollEvent(-1, 40);
|
|
palettes.hidePaletteIconCircles();
|
|
});
|
|
};
|
|
|
|
if (this.upIndicator == null && this.firstTime) {
|
|
makePaletteBitmap(this, UPICON.replace('#000000', '#FFFFFF'), 'up', __processUpIcon, null);
|
|
}
|
|
|
|
if (this.downbIndicator == null && this.firstTime) {
|
|
makePaletteBitmap(this, DOWNICON.replace('#000000', '#FFFFFF'), 'down', __processDownIcon, null);
|
|
}
|
|
|
|
this.firstTime = false;
|
|
|
|
// Make an icon/button for each palette
|
|
|
|
var that = this;
|
|
|
|
function __processButtonIcon(palettes, name, bitmap, args) {
|
|
that.buttons[name].addChild(bitmap);
|
|
if (that.cellSize != that.originalSize) {
|
|
bitmap.scaleX = that.cellSize / that.originalSize;
|
|
bitmap.scaleY = that.cellSize / that.originalSize;
|
|
}
|
|
|
|
var hitArea = new createjs.Shape();
|
|
hitArea.graphics.beginFill('#FFF').drawEllipse(-that.halfCellSize, -that.halfCellSize, that.cellSize, that.cellSize);
|
|
hitArea.x = that.halfCellSize;
|
|
hitArea.y = that.halfCellSize;
|
|
that.buttons[name].hitArea = hitArea;
|
|
that.buttons[name].visible = false;
|
|
|
|
that.dict[name].makeMenu(true);
|
|
that.dict[name]._moveMenu(that.cellSize, that.cellSize);
|
|
that.dict[name]._updateMenu(false);
|
|
that._loadPaletteButtonHandler(name);
|
|
};
|
|
|
|
for (var name in this.dict) {
|
|
if (name in this.buttons) {
|
|
this.dict[name]._updateMenu(hide);
|
|
} else {
|
|
this.buttons[name] = new createjs.Container();
|
|
this.buttons[name].snapToPixelEnabled = true;
|
|
this.stage.addChild(this.buttons[name]);
|
|
this.buttons[name].x = this.x;
|
|
this.buttons[name].y = this.y + this.scrollDiff;
|
|
this.y += this.cellSize;
|
|
|
|
makePaletteBitmap(this, PALETTEICONS[name], name, __processButtonIcon, null);
|
|
}
|
|
}
|
|
};
|
|
|
|
this.showPalette = function (name) {
|
|
if (this.mobile) {
|
|
return;
|
|
}
|
|
|
|
for (var i in this.dict) {
|
|
if (this.dict[i] === this.dict[name]) {
|
|
this.dict[name]._resetLayout();
|
|
this.dict[name].showMenu(true);
|
|
this.dict[name]._showMenuItems(true);
|
|
} else {
|
|
if (this.dict[i].visible) {
|
|
this.dict[i].hideMenu(true);
|
|
this.dict[i]._hideMenuItems(false);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
this._showMenus = function () {
|
|
// Show the menu buttons, but not the palettes.
|
|
if (this.mobile) {
|
|
return;
|
|
}
|
|
|
|
for (var name in this.buttons) {
|
|
this.buttons[name].visible = true;
|
|
}
|
|
|
|
if (this.background != null) {
|
|
this.background.visible = true;
|
|
}
|
|
|
|
// If the palette indicators were visible, restore them.
|
|
if (this.upIndicatorStatus) {
|
|
this.upIndicator.visible = true;
|
|
}
|
|
|
|
if (this.downIndicatorStatus && this.downIndicator != null) {
|
|
this.downIndicator.visible = true;
|
|
}
|
|
|
|
this.refreshCanvas();
|
|
};
|
|
|
|
this._hideMenus = function () {
|
|
// Hide the menu buttons and the palettes themselves.
|
|
for (var name in this.buttons) {
|
|
this.buttons[name].visible = false;
|
|
}
|
|
|
|
for (var name in this.dict) {
|
|
this.dict[name].hideMenu(true);
|
|
}
|
|
|
|
if (this.upIndicator != null) {
|
|
this.upIndicator.visible = false;
|
|
this.downIndicator.visible = false;
|
|
this.background.visible = false;
|
|
}
|
|
|
|
this.refreshCanvas();
|
|
};
|
|
|
|
this.getInfo = function () {
|
|
for (var key in this.dict) {
|
|
console.log(this.dict[key].getInfo());
|
|
}
|
|
};
|
|
|
|
this.updatePalettes = function (showPalette) {
|
|
if (showPalette != null) {
|
|
this.makePalettes(false);
|
|
var myPalettes = this;
|
|
setTimeout(function () {
|
|
myPalettes.dict[showPalette]._resetLayout();
|
|
// Show the action palette after adding/deleting new nameddo blocks.
|
|
myPalettes.dict[showPalette].showMenu();
|
|
myPalettes.dict[showPalette]._showMenuItems();
|
|
myPalettes.refreshCanvas();
|
|
}, 100);
|
|
} else {
|
|
this.makePalettes(true);
|
|
this.refreshCanvas();
|
|
}
|
|
|
|
if (this.mobile) {
|
|
var that = this;
|
|
setTimeout(function () {
|
|
that.hide();
|
|
|
|
for (var i in that.dict) {
|
|
if (that.dict[i].visible) {
|
|
that.dict[i].hideMenu(true);
|
|
that.dict[i]._hideMenuItems(true);
|
|
}
|
|
}
|
|
}, 500);
|
|
}
|
|
};
|
|
|
|
this.hide = function () {
|
|
this._hideMenus();
|
|
this.visible = false;
|
|
};
|
|
|
|
this.show = function () {
|
|
if (this.mobile) {
|
|
this._hideMenus();
|
|
this.visible = false;
|
|
} else {
|
|
this._showMenus();
|
|
this.visible = true;
|
|
}
|
|
};
|
|
|
|
this.setBlocks = function (blocks) {
|
|
this.blocks = blocks;
|
|
return this;
|
|
};
|
|
|
|
this.add = function (name) {
|
|
this.dict[name] = new Palette(this, name);
|
|
return this;
|
|
};
|
|
|
|
this.remove = function (name) {
|
|
if (!(name in this.buttons)) {
|
|
console.log('Palette.remove: Cannot find palette ' + name);
|
|
return;
|
|
}
|
|
|
|
this.buttons[name].removeAllChildren();
|
|
var btnKeys = Object.keys(this.dict);
|
|
for (var btnKey = btnKeys.indexOf(name) + 1; btnKey < btnKeys.length; btnKey++) {
|
|
this.buttons[btnKeys[btnKey]].y -= this.cellSize;
|
|
}
|
|
delete this.buttons[name];
|
|
delete this.dict[name];
|
|
this.y -= this.cellSize;
|
|
this.makePalettes(true);
|
|
};
|
|
|
|
this.bringToTop = function () {
|
|
// Move all the palettes to the top layer of the stage
|
|
for (var name in this.dict) {
|
|
this.stage.removeChild(this.dict[name].menuContainer);
|
|
this.stage.addChild(this.dict[name].menuContainer);
|
|
for (var item in this.dict[name].protoContainers) {
|
|
this.stage.removeChild(this.dict[name].protoContainers[item]);
|
|
this.stage.addChild(this.dict[name].protoContainers[item]);
|
|
}
|
|
// console.log('in bring to top');
|
|
// this.dict[name]._resetLayout();
|
|
}
|
|
this.refreshCanvas();
|
|
};
|
|
|
|
this.findPalette = function (x, y) {
|
|
for (var name in this.dict) {
|
|
var px = this.dict[name].menuContainer.x;
|
|
var py = this.dict[name].menuContainer.y;
|
|
var height = Math.min(maxPaletteHeight(this.cellSize, this.scale), this.dict[name].y);
|
|
if (this.dict[name].menuContainer.visible && px < x &&
|
|
x < px + MENUWIDTH && py < y && y < py + height) {
|
|
return this.dict[name];
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Palette Button event handlers
|
|
this._loadPaletteButtonHandler = function (name) {
|
|
var palettes = this;
|
|
var locked = false;
|
|
var scrolling = false;
|
|
|
|
var that = this;
|
|
|
|
this.buttons[name].on('mousedown', function (event) {
|
|
scrolling = true;
|
|
var lastY = event.stageY;
|
|
|
|
palettes.buttons[name].on('pressmove', function (event) {
|
|
if (!scrolling) {
|
|
return;
|
|
}
|
|
|
|
var diff = event.stageY - lastY;
|
|
palettes.menuScrollEvent(diff, 10);
|
|
lastY = event.stageY;
|
|
});
|
|
|
|
palettes.buttons[name].on('pressup', function (event) {
|
|
scrolling = false;
|
|
}, null, true); // once = true
|
|
});
|
|
|
|
// A palette button opens or closes a palette.
|
|
this.buttons[name].on('mouseover', function (event) {
|
|
palettes.mouseOver = true;
|
|
var r = palettes.cellSize / 2;
|
|
that.circles = showButtonHighlight(palettes.buttons[name].x + r, palettes.buttons[name].y + r, r, event, palettes.scale, palettes.stage);
|
|
|
|
/*add tooltip for palette buttons*/
|
|
if (!sugarizerCompatibility.isInsideSugarizer()) {
|
|
palette_text = new createjs.Text(_(name), '20px Arial', 'black');
|
|
palette_text.x = palettes.buttons[name].x + 2.2 * r;
|
|
palette_text.y = palettes.buttons[name].y + 5 * r / 8;
|
|
palettes.stage.addChild(palette_text);
|
|
}
|
|
});
|
|
|
|
this.buttons[name].on('pressup', function (event) {
|
|
palettes.mouseOver = false;
|
|
if (!sugarizerCompatibility.isInsideSugarizer()) {
|
|
hidePaletteNameDisplay(palette_text, palettes.stage);
|
|
}
|
|
hideButtonHighlight(that.circles, palettes.stage);
|
|
});
|
|
|
|
this.buttons[name].on('mouseout', function (event) {
|
|
palettes.mouseOver = false;
|
|
if (!sugarizerCompatibility.isInsideSugarizer()) {
|
|
hidePaletteNameDisplay(palette_text, palettes.stage);
|
|
}
|
|
hideButtonHighlight(that.circles, palettes.stage);
|
|
});
|
|
|
|
this.buttons[name].on('click', function (event) {
|
|
if (locked) {
|
|
return;
|
|
}
|
|
locked = true;
|
|
|
|
setTimeout(function () {
|
|
locked = false;
|
|
}, 500);
|
|
|
|
palettes.dict[name]._moveMenu(palettes.initial_x, palettes.initial_y);
|
|
palettes.showPalette(name);
|
|
palettes.refreshCanvas();
|
|
});
|
|
};
|
|
|
|
this.removeActionPrototype = function (actionName) {
|
|
var blockRemoved = false;
|
|
for (var blk = 0; blk < this.dict['action'].protoList.length; blk++) {
|
|
var actionBlock = this.dict['action'].protoList[blk];
|
|
if (['nameddo', 'namedcalc', 'nameddoArg', 'namedcalcArg'].indexOf(actionBlock.name) !== -1 && (actionBlock.defaults[0] === actionName)) {
|
|
// Remove the palette protoList entry for this block.
|
|
this.dict['action'].remove(actionBlock, actionName);
|
|
|
|
// And remove it from the protoBlock dictionary.
|
|
if (this.blocks.protoBlockDict['myDo_' + actionName]) {
|
|
// console.log('DELETING PROTOBLOCKS FOR ACTION ' + actionName);
|
|
delete this.blocks.protoBlockDict['myDo_' + actionName];
|
|
} else if (this.blocks.protoBlockDict['myCalc_' + actionName]) {
|
|
// console.log('deleting protoblocks for action ' + actionName);
|
|
delete this.blocks.protoBlockDict['myCalc_' + actionName];
|
|
} else if (this.blocks.protoBlockDict['myDoArg_' + actionName]) {
|
|
// console.log('deleting protoblocks for action ' + actionName);
|
|
delete this.blocks.protoBlockDict['myDoArg_' + actionName];
|
|
} else if (this.blocks.protoBlockDict['myCalcArg_' + actionName]) {
|
|
// console.log('deleting protoblocks for action ' + actionName);
|
|
delete this.blocks.protoBlockDict['myCalcArg_' + actionName];
|
|
}
|
|
this.dict['action'].y = 0;
|
|
blockRemoved = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Force an update if a block was removed.
|
|
if (blockRemoved) {
|
|
this.hide();
|
|
this.updatePalettes('action');
|
|
if (this.mobile) {
|
|
this.hide();
|
|
} else {
|
|
this.show();
|
|
}
|
|
}
|
|
};
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
// Kind of a model, but it only keeps a list of SVGs
|
|
function PaletteModel(palette, palettes, name) {
|
|
this.palette = palette;
|
|
this.palettes = palettes;
|
|
this.name = name;
|
|
this.blocks = [];
|
|
|
|
this.update = function () {
|
|
this.blocks = [];
|
|
for (var blk in this.palette.protoList) {
|
|
var block = this.palette.protoList[blk];
|
|
// Don't show hidden blocks on the menus
|
|
if (block.hidden) {
|
|
continue;
|
|
}
|
|
|
|
// Create a proto block for each palette entry.
|
|
var blkname = block.name;
|
|
var modname = blkname;
|
|
|
|
switch (blkname) {
|
|
// Use the name of the action in the label
|
|
case 'storein':
|
|
modname = 'store in ' + block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
break;
|
|
case 'box':
|
|
modname = block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
break;
|
|
case 'namedbox':
|
|
if (block.defaults[0] === undefined) {
|
|
modname = 'namedbox';
|
|
var arg = _('box');
|
|
} else {
|
|
modname = block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
}
|
|
break;
|
|
case 'namedarg':
|
|
if (block.defaults[0] === undefined) {
|
|
modname = 'namedarg';
|
|
var arg = '1';
|
|
} else {
|
|
modname = block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
}
|
|
break;
|
|
case 'nameddo':
|
|
if (block.defaults[0] === undefined) {
|
|
modname = 'nameddo';
|
|
var arg = _('action');
|
|
} else {
|
|
modname = block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
}
|
|
break;
|
|
case 'nameddoArg':
|
|
if (block.defaults[0] === undefined) {
|
|
modname = 'nameddoArg';
|
|
var arg = _('action');
|
|
} else {
|
|
modname = block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
}
|
|
break;
|
|
case 'namedcalc':
|
|
if (block.defaults[0] === undefined) {
|
|
modname = 'namedcalc';
|
|
var arg = _('action');
|
|
} else {
|
|
modname = block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
}
|
|
break;
|
|
case 'namedcalcArg':
|
|
if (block.defaults[0] === undefined) {
|
|
modname = 'namedcalcArg';
|
|
var arg = _('action');
|
|
} else {
|
|
modname = block.defaults[0];
|
|
var arg = block.defaults[0];
|
|
}
|
|
break;
|
|
}
|
|
|
|
var protoBlock = this.palettes.blocks.protoBlockDict[blkname];
|
|
if (protoBlock == null) {
|
|
console.log('Could not find block ' + blkname);
|
|
continue;
|
|
}
|
|
|
|
var label = '';
|
|
// console.log(protoBlock.name);
|
|
switch (protoBlock.name) {
|
|
case 'text':
|
|
label = _('text');
|
|
break;
|
|
case 'solfege':
|
|
label = i18nSolfege('sol');
|
|
break;
|
|
case 'eastindiansolfege':
|
|
label = 'sargam';
|
|
break;
|
|
case 'notename':
|
|
label = 'G';
|
|
break;
|
|
case 'number':
|
|
label = NUMBERBLOCKDEFAULT.toString();
|
|
break;
|
|
case 'less':
|
|
case 'greater':
|
|
case 'equal':
|
|
// Label should be inside _() when defined.
|
|
label = protoBlock.staticLabels[0];
|
|
break;
|
|
case 'namedarg':
|
|
label = 'arg ' + arg;
|
|
break;
|
|
default:
|
|
if (blkname != modname) {
|
|
// Override label for do, storein, box, and namedarg
|
|
if (blkname === 'storein' && block.defaults[0] === _('box')) {
|
|
label = _('store in');
|
|
} else {
|
|
label = block.defaults[0];
|
|
}
|
|
} else if (protoBlock.staticLabels.length > 0) {
|
|
label = protoBlock.staticLabels[0];
|
|
if (label === '') {
|
|
if (blkname === 'loadFile') {
|
|
label = _('open file')
|
|
} else {
|
|
label = blkname;
|
|
}
|
|
}
|
|
} else {
|
|
label = blkname;
|
|
}
|
|
}
|
|
|
|
if (['do', 'nameddo', 'namedbox', 'namedcalc', 'doArg', 'calcArg', 'nameddoArg', 'namedcalcArg'].indexOf(protoBlock.name) != -1 && label != null && label.length > 8) {
|
|
label = label.substr(0, 7) + '...';
|
|
}
|
|
|
|
// Don't display the label on image blocks.
|
|
if (protoBlock.image) {
|
|
label = '';
|
|
}
|
|
|
|
// Finally, the SVGs!
|
|
switch (protoBlock.name) {
|
|
case 'namedbox':
|
|
case 'namedarg':
|
|
// so the label will fit
|
|
var svg = new SVG();
|
|
svg.init();
|
|
svg.setScale(protoBlock.scale);
|
|
svg.setExpand(60, 0, 0, 0);
|
|
svg.setOutie(true);
|
|
var artwork = svg.basicBox();
|
|
var docks = svg.docks;
|
|
break;
|
|
case 'nameddo':
|
|
// so the label will fit
|
|
var svg = new SVG();
|
|
svg.init();
|
|
svg.setScale(protoBlock.scale);
|
|
svg.setExpand(30, 0, 0, 0);
|
|
var artwork = svg.basicBlock();
|
|
var docks = svg.docks;
|
|
break;
|
|
default:
|
|
var obj = protoBlock.generator();
|
|
var artwork = obj[0];
|
|
var docks = obj[1];
|
|
break;
|
|
}
|
|
|
|
if (protoBlock.disabled) {
|
|
artwork = artwork
|
|
.replace(/fill_color/g, DISABLEDFILLCOLOR)
|
|
.replace(/stroke_color/g, DISABLEDSTROKECOLOR)
|
|
.replace('block_label', label);
|
|
} else {
|
|
artwork = artwork
|
|
.replace(/fill_color/g,
|
|
PALETTEFILLCOLORS[protoBlock.palette.name])
|
|
.replace(/stroke_color/g,
|
|
PALETTESTROKECOLORS[protoBlock.palette.name])
|
|
.replace('block_label', label);
|
|
}
|
|
|
|
for (var i = 0; i <= protoBlock.args; i++) {
|
|
artwork = artwork.replace('arg_label_' + i, protoBlock.staticLabels[i] || '');
|
|
}
|
|
|
|
this.blocks.push({
|
|
blk: blk,
|
|
name: blkname,
|
|
modname: modname,
|
|
height: STANDARDBLOCKHEIGHT,
|
|
label: label,
|
|
artwork: artwork,
|
|
artwork64: 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(artwork))),
|
|
docks: docks,
|
|
image: block.image,
|
|
scale: block.scale,
|
|
palettename: this.palette.name
|
|
});
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
function PopdownPalette(palettes) {
|
|
this.palettes = palettes;
|
|
this.models = {};
|
|
|
|
for (var name in this.palettes.dict) {
|
|
this.models[name] = new PaletteModel(this.palettes.dict[name],
|
|
this.palettes, name);
|
|
};
|
|
|
|
this.update = function () {
|
|
var html = '<div class="back"><h2>' + _('back') + '</h2></div>';
|
|
for (var name in this.models) {
|
|
html += '<div class="palette">';
|
|
var icon = PALETTEICONS[name].replace(/#f{3,6}/gi, PALETTEFILLCOLORS[name]);
|
|
//.TRANS: popout: to detach as a separate window
|
|
html += format('<h2 data-name="{n}"> \
|
|
{i}<span>{n}</span> \
|
|
<img class="hide-button" src="header-icons/hide.svg" \
|
|
alt="{' + _('hide') + '}" \
|
|
title="{' + _('hide') + '}" /> \
|
|
<img class="show-button" src="header-icons/show.svg" \
|
|
alt="{' + _('show') + '}" \
|
|
title="{' + _('show') + '}" /> \
|
|
<img class="popout-button" src="header-icons/popout.svg" \
|
|
alt="{' + _('popout') + '}" \
|
|
title="{' + _('popout') + '}" /> \
|
|
</h2>',
|
|
{i: icon, n: toTitleCase(_(name))});
|
|
html += '<ul>';
|
|
this.models[name].update();
|
|
|
|
var blocks = this.models[name].blocks;
|
|
if (BUILTINPALETTES.indexOf(name) > -1)
|
|
blocks.reverse();
|
|
|
|
for (var blk in blocks) {
|
|
html += format('<li title="{label}" \
|
|
data-blk="{blk}" \
|
|
data-palettename="{palettename}" \
|
|
data-modname="{modname}"> \
|
|
<img src="{artwork64}" alt="{label}" /> \
|
|
</li>', blocks[blk]);
|
|
}
|
|
html += '</div>';
|
|
}
|
|
document.querySelector('#popdown-palette').innerHTML = html;
|
|
|
|
var that = this;
|
|
|
|
document.querySelector('#popdown-palette .back').addEventListener('click', function () {
|
|
that.popup();
|
|
});
|
|
|
|
var eles = document.querySelectorAll('#popdown-palette > .palette');
|
|
Array.prototype.forEach.call(eles, function (d) {
|
|
d.querySelector('h2').addEventListener('click', function () {
|
|
if (d.classList.contains('show')) {
|
|
d.classList.remove('show');
|
|
} else {
|
|
d.classList.add('show');
|
|
}
|
|
});
|
|
|
|
d.querySelector('.popout-button').addEventListener('click', function () {
|
|
that.popup();
|
|
that.palettes.showPalette(d.querySelector('h2').dataset.name);
|
|
});
|
|
});
|
|
|
|
var eles = document.querySelectorAll('#popdown-palette li');
|
|
Array.prototype.forEach.call(eles, function (e) {
|
|
e.addEventListener('click', function (event) {
|
|
that.popup();
|
|
var palette = that.palettes.dict[e.dataset.palettename];
|
|
var container = palette.protoContainers[e.dataset.modname];
|
|
|
|
// console.log(e.dataset.blk + ' ' + e.dataset.modname);
|
|
var newBlock = palette._makeBlockFromPalette(palette.protoList[e.dataset.blk], e.dataset.modname, function (newBlock) {
|
|
// Move the drag group under the cursor.
|
|
that.palettes.blocks.findDragGroup(newBlock);
|
|
for (var i in that.palettes.blocks.dragGroup) {
|
|
that.palettes.blocks.moveBlockRelative(that.palettes.blocks.dragGroup[i], Math.round(event.clientX / that.palettes.scale) - that.palettes.blocks.stage.x, Math.round(event.clientY / that.palettes.scale) - that.palettes.blocks.stage.y);
|
|
}
|
|
|
|
// Dock with other blocks if needed
|
|
that.palettes.blocks.blockMoved(newBlock);
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
this.popdown = function () {
|
|
this.update();
|
|
document.querySelector('#popdown-palette').classList.add('show');
|
|
};
|
|
|
|
this.popup = function () {
|
|
document.querySelector('#popdown-palette').classList.remove('show');
|
|
};
|
|
};
|
|
|
|
// Define objects for individual palettes.
|
|
function Palette(palettes, name) {
|
|
this.palettes = palettes;
|
|
this.name = name;
|
|
this.model = new PaletteModel(this, palettes, name);
|
|
this.visible = false;
|
|
this.menuContainer = null;
|
|
this.protoList = [];
|
|
this.protoContainers = {};
|
|
this.background = null;
|
|
this.scrollDiff = 0
|
|
this.y = 0;
|
|
this.size = 0;
|
|
this.columns = 0;
|
|
this.draggingProtoBlock = false;
|
|
this.mouseHandled = false;
|
|
this.upButton = null;
|
|
this.downButton = null;
|
|
this.fadedUpButton = null;
|
|
this.fadedDownButton = null;
|
|
this.count = 0;
|
|
|
|
this.makeMenu = function (createHeader) {
|
|
var palette = this;
|
|
|
|
function __processButtonIcon(palette, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.8;
|
|
palette.menuContainer.addChild(bitmap);
|
|
palette.palettes.container.addChild(palette.menuContainer);
|
|
};
|
|
|
|
function __processCloseIcon(palette, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.7;
|
|
palette.menuContainer.addChild(bitmap);
|
|
bitmap.x = paletteWidth - STANDARDBLOCKHEIGHT;
|
|
bitmap.y = 0;
|
|
|
|
var hitArea = new createjs.Shape();
|
|
hitArea.graphics.beginFill('#FFF').drawEllipse(-paletteWidth / 2, -STANDARDBLOCKHEIGHT / 2, paletteWidth, STANDARDBLOCKHEIGHT);
|
|
hitArea.x = paletteWidth / 2;
|
|
hitArea.y = STANDARDBLOCKHEIGHT / 2;
|
|
palette.menuContainer.hitArea = hitArea;
|
|
palette.menuContainer.visible = false;
|
|
|
|
if (!palette.mouseHandled) {
|
|
palette._loadPaletteMenuHandler();
|
|
palette.mouseHandled = true;
|
|
}
|
|
};
|
|
|
|
function __processUpIcon(palette, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.7;
|
|
palette.palettes.stage.addChild(bitmap);
|
|
bitmap.x = palette.menuContainer.x + paletteWidth;
|
|
bitmap.y = palette.menuContainer.y + STANDARDBLOCKHEIGHT;
|
|
__calculateHitArea(bitmap);
|
|
var hitArea = new createjs.Shape();
|
|
bitmap.visible = false;
|
|
palette.upButton = bitmap;
|
|
|
|
palette.upButton.on('click', function (event) {
|
|
palette.scrollEvent(STANDARDBLOCKHEIGHT, 10);
|
|
});
|
|
|
|
};
|
|
|
|
function __processDownIcon(palette, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.7;
|
|
palette.palettes.stage.addChild(bitmap);
|
|
bitmap.x = palette.menuContainer.x + paletteWidth;
|
|
bitmap.y = palette._getDownButtonY() - STANDARDBLOCKHEIGHT;
|
|
__calculateHitArea(bitmap);
|
|
palette.downButton = bitmap;
|
|
|
|
palette.downButton.on('click', function (event) {
|
|
palette.scrollEvent(-STANDARDBLOCKHEIGHT, 10);
|
|
});
|
|
};
|
|
|
|
function __makeFadedDownIcon(palette, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.7;
|
|
palette.palettes.stage.addChild(bitmap);
|
|
bitmap.x = palette.menuContainer.x + paletteWidth;
|
|
bitmap.y = palette._getDownButtonY();
|
|
__calculateHitArea(bitmap);
|
|
palette.fadedDownButton = bitmap;
|
|
};
|
|
|
|
function __makeFadedUpIcon(palette, name, bitmap, args) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = 0.7;
|
|
palette.palettes.stage.addChild(bitmap);
|
|
bitmap.x = palette.menuContainer.x + paletteWidth;
|
|
bitmap.y = palette.menuContainer.y + STANDARDBLOCKHEIGHT;
|
|
__calculateHitArea(bitmap);
|
|
palette.fadedUpButton = bitmap;
|
|
};
|
|
|
|
function __calculateHitArea(bitmap) {
|
|
var hitArea = new createjs.Shape();
|
|
hitArea.graphics.beginFill('#FFF').drawRect(0, 0, STANDARDBLOCKHEIGHT, STANDARDBLOCKHEIGHT);
|
|
hitArea.x = 0;
|
|
hitArea.y = 0;
|
|
bitmap.hitArea = hitArea;
|
|
bitmap.visible = false;
|
|
};
|
|
|
|
function __processHeader(palette, name, bitmap, args) {
|
|
palette.menuContainer.addChild(bitmap);
|
|
|
|
makePaletteBitmap(palette, DOWNICON, name, __processDownIcon, null);
|
|
makePaletteBitmap(palette, FADEDDOWNICON, name, __makeFadedDownIcon, null);
|
|
makePaletteBitmap(palette, FADEDUPICON, name, __makeFadedUpIcon, null);
|
|
makePaletteBitmap(palette, UPICON, name, __processUpIcon, null);
|
|
makePaletteBitmap(palette, CLOSEICON, name, __processCloseIcon, null);
|
|
makePaletteBitmap(palette, PALETTEICONS[name], name, __processButtonIcon, null);
|
|
};
|
|
|
|
if (this.menuContainer == null) {
|
|
this.menuContainer = new createjs.Container();
|
|
this.menuContainer.snapToPixelEnabled = true;
|
|
}
|
|
|
|
if (!createHeader) {
|
|
return;
|
|
}
|
|
|
|
var paletteWidth = MENUWIDTH + this._getOverflowWidth();
|
|
this.menuContainer.removeAllChildren();
|
|
|
|
// Create the menu button
|
|
makePaletteBitmap(this, PALETTEHEADER.replace('fill_color', '#282828').replace('palette_label', toTitleCase(_(this.name))).replace(/header_width/g, paletteWidth), this.name, __processHeader, null);
|
|
};
|
|
|
|
this._getDownButtonY = function () {
|
|
var h = maxPaletteHeight(this.palettes.cellSize, this.palettes.scale);
|
|
return h + STANDARDBLOCKHEIGHT / 2;
|
|
};
|
|
|
|
this._resizeEvent = function () {
|
|
this.hide();
|
|
this._updateBackground();
|
|
this._updateBlockMasks();
|
|
|
|
if (this.downButton !== null) {
|
|
this.downButton.y = this._getDownButtonY();
|
|
this.fadedDownButton.y = this.downButton.y;
|
|
}
|
|
};
|
|
|
|
this._updateBlockMasks = function () {
|
|
var h = Math.min(maxPaletteHeight(this.palettes.cellSize, this.palettes.scale), this.y);
|
|
var w = MENUWIDTH + this._getOverflowWidth();
|
|
for (var i in this.protoContainers) {
|
|
var s = new createjs.Shape();
|
|
s.graphics.r(0, 0, w, h);
|
|
s.x = this.background.x;
|
|
s.y = this.background.y;
|
|
this.protoContainers[i].mask = s;
|
|
}
|
|
};
|
|
|
|
this._getOverflowWidth = function() {
|
|
var maxWidth = 0;
|
|
for(var i in this.protoList) {
|
|
maxWidth = Math.max(maxWidth, this.protoList[i].textWidth);
|
|
}
|
|
return (maxWidth > 100 ? maxWidth - 30 : 0);
|
|
}
|
|
|
|
this._updateBackground = function () {
|
|
if (this.menuContainer == null) {
|
|
return;
|
|
}
|
|
|
|
if (this.background !== null) {
|
|
this.background.removeAllChildren();
|
|
} else {
|
|
this.background = new createjs.Container();
|
|
this.background.snapToPixelEnabled = true;
|
|
this.background.visible = false;
|
|
this.palettes.stage.addChild(this.background);
|
|
this._setupBackgroundEvents();
|
|
}
|
|
|
|
// Since we don't always add items at the end, the dependency
|
|
// on this.y is unrelable. Easy workaround is just to always
|
|
// extend the palette to the bottom.
|
|
|
|
// var h = Math.min(maxPaletteHeight(this.palettes.cellSize, this.palettes.scale), this.y);
|
|
var h = maxPaletteHeight(this.palettes.cellSize, this.palettes.scale);
|
|
|
|
var shape = new createjs.Shape();
|
|
shape.graphics.f('#949494').r(0, 0, MENUWIDTH + this._getOverflowWidth(), h).ef();
|
|
shape.width = MENUWIDTH + this._getOverflowWidth();
|
|
shape.height = h;
|
|
this.background.addChild(shape);
|
|
|
|
this.background.x = this.menuContainer.x;
|
|
this.background.y = this.menuContainer.y + STANDARDBLOCKHEIGHT;
|
|
};
|
|
|
|
this._resetLayout = function () {
|
|
// Account for menu toolbar
|
|
if (this.menuContainer == null) {
|
|
console.log('menuContainer is null');
|
|
return;
|
|
}
|
|
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].y -= this.scrollDiff;
|
|
}
|
|
|
|
this.y = this.menuContainer.y + STANDARDBLOCKHEIGHT;
|
|
var items = [];
|
|
// Reverse order
|
|
for (var i in this.protoContainers) {
|
|
items.push(this.protoContainers[i]);
|
|
}
|
|
var n = items.length;
|
|
for (var j = 0; j < n; j++) {
|
|
var i = items.pop();
|
|
i.x = this.menuContainer.x;
|
|
i.y = this.y;
|
|
var bounds = i.getBounds();
|
|
if (bounds != null) {
|
|
// Pack them in a bit tighter
|
|
this.y += bounds.height - (STANDARDBLOCKHEIGHT * 0.1);
|
|
} else {
|
|
// If artwork isn't ready, assume it is of standard
|
|
// size, e.g., and action block.
|
|
this.y += STANDARDBLOCKHEIGHT * 0.9;
|
|
}
|
|
}
|
|
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].y += this.scrollDiff;
|
|
}
|
|
};
|
|
|
|
this._updateMenu = function (hide) {
|
|
var palette = this;
|
|
|
|
function __calculateBounds(palette, blk, modname, protoListBlk) {
|
|
var bounds = palette.protoContainers[modname].getBounds();
|
|
palette.protoContainers[modname].cache(bounds.x, bounds.y, Math.ceil(bounds.width), Math.ceil(bounds.height));
|
|
|
|
var hitArea = new createjs.Shape();
|
|
// Trim the hitArea height slightly to make it easier to
|
|
// select single-height blocks below double-height blocks.
|
|
hitArea.graphics.beginFill('#FFF').drawRect(0, 0, Math.ceil(bounds.width), Math.ceil(bounds.height * 0.75));
|
|
palette.protoContainers[modname].hitArea = hitArea;
|
|
palette._loadPaletteMenuItemHandler(protoListBlk, modname);
|
|
palette.palettes.refreshCanvas();
|
|
};
|
|
|
|
function __processBitmap(palette, modname, bitmap, args) {
|
|
var b = args[0];
|
|
var blk = args[1];
|
|
var protoListBlk = args[2];
|
|
|
|
if (palette.protoContainers[modname] == undefined) {
|
|
console.log('no protoContainer for ' + modname);
|
|
return;
|
|
}
|
|
|
|
palette.protoContainers[modname].addChild(bitmap);
|
|
bitmap.x = PALETTELEFTMARGIN;
|
|
bitmap.y = 0;
|
|
bitmap.scaleX = PROTOBLOCKSCALE;
|
|
bitmap.scaleY = PROTOBLOCKSCALE;
|
|
bitmap.scale = PROTOBLOCKSCALE;
|
|
|
|
if (b.image) {
|
|
var image = new Image();
|
|
image.onload = function () {
|
|
var bitmap = new createjs.Bitmap(image);
|
|
if (image.width > image.height) {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = MEDIASAFEAREA[2] / image.width * (b.scale / 2);
|
|
} else {
|
|
bitmap.scaleX = bitmap.scaleY = bitmap.scale = MEDIASAFEAREA[3] / image.height * (b.scale / 2);
|
|
}
|
|
palette.protoContainers[modname].addChild(bitmap);
|
|
bitmap.x = MEDIASAFEAREA[0] * (b.scale / 2);
|
|
bitmap.y = MEDIASAFEAREA[1] * (b.scale / 2);
|
|
__calculateBounds(palette, blk, modname, protoListBlk);
|
|
};
|
|
|
|
image.src = b.image;
|
|
} else {
|
|
__calculateBounds(palette, blk, modname, protoListBlk);
|
|
}
|
|
};
|
|
|
|
function __processFiller(palette, modname, bitmap, args) {
|
|
var b = args[0];
|
|
makePaletteBitmap(palette, b.artwork, b.modname, __processBitmap, args);
|
|
};
|
|
|
|
if (this.menuContainer == null) {
|
|
this.makeMenu(true);
|
|
} else {
|
|
// Hide the menu while we update.
|
|
if (hide) {
|
|
this.hide();
|
|
} else if (this.palettes.mobile) {
|
|
this.hide();
|
|
}
|
|
}
|
|
|
|
this.y = 0;
|
|
this.model.update();
|
|
|
|
var blocks = this.model.blocks;
|
|
if (BUILTINPALETTES.indexOf(name) == -1)
|
|
blocks.reverse();
|
|
|
|
for (var blk in blocks) {
|
|
var b = blocks[blk];
|
|
if (!this.protoContainers[b.modname]) {
|
|
// create graphics for the palette entry for this block
|
|
this.protoContainers[b.modname] = new createjs.Container();
|
|
this.protoContainers[b.modname].snapToPixelEnabled = true;
|
|
|
|
this.protoContainers[b.modname].x = this.menuContainer.x;
|
|
this.protoContainers[b.modname].y = this.menuContainer.y + this.y + this.scrollDiff + STANDARDBLOCKHEIGHT;
|
|
this.palettes.stage.addChild(this.protoContainers[b.modname]);
|
|
this.protoContainers[b.modname].visible = false;
|
|
|
|
this.size += Math.ceil(b.height * PROTOBLOCKSCALE);
|
|
this.y += Math.ceil(b.height * PROTOBLOCKSCALE);
|
|
this._updateBackground();
|
|
|
|
// Since the protoList might change while this block
|
|
// is being created, we cannot rely on blk to be the
|
|
// proper index, so pass the entry itself as an
|
|
// argument.
|
|
makePaletteBitmap(this, PALETTEFILLER.replace(/filler_height/g, b.height.toString()), b.modname, __processFiller, [b, blk, this.protoList[blk]]);
|
|
} else {
|
|
this.protoContainers[b.modname].x = this.menuContainer.x;
|
|
this.protoContainers[b.modname].y = this.menuContainer.y + this.y + this.scrollDiff + STANDARDBLOCKHEIGHT;
|
|
this.y += Math.ceil(b.height * PROTOBLOCKSCALE);
|
|
}
|
|
}
|
|
|
|
this.makeMenu(false);
|
|
|
|
if (this.palettes.mobile) {
|
|
this.hide();
|
|
}
|
|
};
|
|
|
|
this._moveMenu = function (x, y) {
|
|
// :sigh: race condition on iOS 7.1.2
|
|
if (this.menuContainer == null) return;
|
|
var dx = x - this.menuContainer.x;
|
|
var dy = y - this.menuContainer.y;
|
|
this.menuContainer.x = x;
|
|
this.menuContainer.y = y;
|
|
this._moveMenuItemsRelative(dx, dy);
|
|
};
|
|
|
|
this._moveMenuRelative = function (dx, dy) {
|
|
this.menuContainer.x += dx;
|
|
this.menuContainer.y += dy;
|
|
this._moveMenuItemsRelative(dx, dy);
|
|
};
|
|
|
|
this.hide = function () {
|
|
this.hideMenu();
|
|
};
|
|
|
|
this.show = function () {
|
|
if (this.palettes.mobile) {
|
|
this.hideMenu();
|
|
} else {
|
|
this.showMenu();
|
|
}
|
|
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].visible = true;
|
|
}
|
|
this._updateBlockMasks();
|
|
if (this.background !== null) {
|
|
this.background.visible = true;
|
|
}
|
|
};
|
|
|
|
this.hideMenu = function () {
|
|
if (this.menuContainer != null) {
|
|
this.menuContainer.visible = false;
|
|
this._hideMenuItems(true);
|
|
}
|
|
|
|
this._moveMenu(this.palettes.cellSize, this.palettes.cellSize);
|
|
};
|
|
|
|
this.showMenu = function () {
|
|
if (this.palettes.mobile) {
|
|
this.menuContainer.visible = false;
|
|
} else {
|
|
this.menuContainer.visible = true;
|
|
}
|
|
};
|
|
|
|
this._hideMenuItems = function (init) {
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].visible = false;
|
|
}
|
|
|
|
if (this.background !== null) {
|
|
this.background.visible = false;
|
|
}
|
|
|
|
if (this.fadedDownButton != null) {
|
|
this.upButton.visible = false;
|
|
this.downButton.visible = false;
|
|
this.fadedUpButton.visible = false;
|
|
this.fadedDownButton.visible = false;
|
|
}
|
|
|
|
this.visible = false;
|
|
};
|
|
|
|
this._showMenuItems = function (init) {
|
|
if (this.scrollDiff === 0) {
|
|
this.count = 0;
|
|
}
|
|
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].visible = true;
|
|
}
|
|
|
|
this._updateBlockMasks();
|
|
if (this.background !== null) {
|
|
this.background.visible = true;
|
|
}
|
|
|
|
// Use scroll position to determine visibility
|
|
this.scrollEvent(0, 10);
|
|
this.visible = true;
|
|
};
|
|
|
|
this._moveMenuItems = function (x, y) {
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].x = x;
|
|
this.protoContainers[i].y = y;
|
|
}
|
|
|
|
if (this.background !== null) {
|
|
this.background.x = x;
|
|
this.background.y = y;
|
|
}
|
|
};
|
|
|
|
this._moveMenuItemsRelative = function (dx, dy) {
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].x += dx;
|
|
this.protoContainers[i].y += dy;
|
|
}
|
|
|
|
if (this.background !== null) {
|
|
this.background.x += dx;
|
|
this.background.y += dy;
|
|
}
|
|
|
|
if (this.fadedDownButton !== null) {
|
|
this.upButton.x += dx;
|
|
this.upButton.y += dy;
|
|
this.downButton.x += dx;
|
|
this.downButton.y += dy;
|
|
this.fadedUpButton.x += dx;
|
|
this.fadedUpButton.y += dy;
|
|
this.fadedDownButton.x += dx;
|
|
this.fadedDownButton.y += dy;
|
|
}
|
|
};
|
|
|
|
this.scrollEvent = function (direction, scrollSpeed) {
|
|
var diff = direction * scrollSpeed;
|
|
var h = Math.min(maxPaletteHeight(this.palettes.cellSize, this.palettes.scale), this.y);
|
|
|
|
if (this.y < maxPaletteHeight(this.palettes.cellSize, this.palettes.scale)) {
|
|
this.upButton.visible = false;
|
|
this.downButton.visible = false;
|
|
this.fadedUpButton.visible = false;
|
|
this.fadedDownButton.visible = false;
|
|
return;
|
|
}
|
|
|
|
if (this.scrollDiff + diff > 0 && direction > 0) {
|
|
var dy = -this.scrollDiff;
|
|
if (dy === 0) {
|
|
this.downButton.visible = true;
|
|
this.upButton.visible = false;
|
|
this.fadedUpButton.visible = true;
|
|
this.fadedDownButton.visible = false;
|
|
return;
|
|
}
|
|
|
|
this.scrollDiff += dy;
|
|
this.fadedDownButton.visible = false;
|
|
this.downButton.visible = true;
|
|
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].y += dy;
|
|
this.protoContainers[i].visible = true;
|
|
|
|
if (this.scrollDiff === 0) {
|
|
this.downButton.visible = true;
|
|
this.upButton.visible = false;
|
|
this.fadedUpButton.visible = true;
|
|
this.fadedDownButton.visible = false;
|
|
}
|
|
}
|
|
} else if (this.y + this.scrollDiff + diff < h && direction < 0) {
|
|
var dy = -this.y + h - this.scrollDiff;
|
|
if (dy === 0) {
|
|
this.upButton.visible = true;
|
|
this.downButton.visible = false;
|
|
this.fadedDownButton.visible = true;
|
|
this.fadedUpButton.visible = false;
|
|
return;
|
|
}
|
|
|
|
this.scrollDiff += -this.y + h - this.scrollDiff;
|
|
this.fadedUpButton.visible = false;
|
|
this.upButton.visible = true;
|
|
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].y += dy;
|
|
this.protoContainers[i].visible = true;
|
|
}
|
|
|
|
if(-this.y + h - this.scrollDiff === 0) {
|
|
this.upButton.visible = true;
|
|
this.downButton.visible = false;
|
|
this.fadedDownButton.visible = true;
|
|
this.fadedUpButton.visible = false;
|
|
}
|
|
|
|
} else if (this.count === 0) {
|
|
this.fadedUpButton.visible = true;
|
|
this.fadedDownButton.visible = false;
|
|
this.upButton.visible = false;
|
|
this.downButton.visible = true;
|
|
} else {
|
|
this.scrollDiff += diff;
|
|
this.fadedUpButton.visible = false;
|
|
this.fadedDownButton.visible = false;
|
|
this.upButton.visible = true;
|
|
this.downButton.visible = true;
|
|
|
|
for (var i in this.protoContainers) {
|
|
this.protoContainers[i].y += diff;
|
|
this.protoContainers[i].visible = true;
|
|
}
|
|
}
|
|
this._updateBlockMasks();
|
|
var stage = this.palettes.stage;
|
|
stage.setChildIndex(this.menuContainer, stage.getNumChildren() - 1);
|
|
this.palettes.refreshCanvas();
|
|
this.count += 1;
|
|
};
|
|
|
|
this.getInfo = function () {
|
|
var returnString = this.name + ' palette:';
|
|
for (var thisBlock in this.protoList) {
|
|
returnString += ' ' + this.protoList[thisBlock].name;
|
|
}
|
|
return returnString;
|
|
};
|
|
|
|
this.remove = function (protoblock, name) {
|
|
// Remove the protoblock and its associated artwork container.
|
|
// console.log('removing action ' + name);
|
|
var i = this.protoList.indexOf(protoblock);
|
|
if (i !== -1) {
|
|
this.protoList.splice(i, 1);
|
|
}
|
|
|
|
for (var i = 0; i < this.model.blocks.length; i++) {
|
|
if (['nameddo', 'nameddoArg', 'namedcalc', 'namedcalcArg'].indexOf(this.model.blocks[i].blkname) !== -1 && this.model.blocks[i].modname === name) {
|
|
this.model.blocks.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
this.palettes.stage.removeChild(this.protoContainers[name]);
|
|
delete this.protoContainers[name];
|
|
};
|
|
|
|
this.add = function (protoblock, top) {
|
|
// Add a new palette entry to the end of the list (default) or
|
|
// to the top.
|
|
if (this.protoList.indexOf(protoblock) === -1) {
|
|
if (top === undefined) {
|
|
this.protoList.push(protoblock);
|
|
} else {
|
|
this.protoList.splice(0, 0, protoblock);
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
this._setupBackgroundEvents = function () {
|
|
var palette = this;
|
|
var scrolling = false;
|
|
|
|
this.background.on('mouseover', function (event) {
|
|
palette.palettes.activePalette = palette;
|
|
});
|
|
|
|
this.background.on('mouseout', function (event) {
|
|
palette.palettes.activePalette = null;
|
|
});
|
|
|
|
this.background.on('mousedown', function (event) {
|
|
scrolling = true;
|
|
var lastY = event.stageY;
|
|
|
|
palette.background.on('pressmove', function (event) {
|
|
if (!scrolling) {
|
|
return;
|
|
}
|
|
|
|
var diff = event.stageY - lastY;
|
|
palette.scrollEvent(diff, 10);
|
|
lastY = event.stageY;
|
|
});
|
|
|
|
palette.background.on('pressup', function (event) {
|
|
palette.palettes.activePalette = null;
|
|
scrolling = false;
|
|
}, null, true); // once = true
|
|
});
|
|
};
|
|
|
|
// Palette Menu event handlers
|
|
this._loadPaletteMenuHandler =function () {
|
|
// The palette menu is the container for the protoblocks. One
|
|
// palette per palette button.
|
|
|
|
var palette = this;
|
|
var locked = false;
|
|
var trashcan = this.palettes.trashcan;
|
|
var paletteWidth = MENUWIDTH + this._getOverflowWidth();
|
|
|
|
this.menuContainer.on('click', function (event) {
|
|
if (Math.round(event.stageX / palette.palettes.scale) > palette.menuContainer.x + paletteWidth - STANDARDBLOCKHEIGHT) {
|
|
palette.hide();
|
|
palette.palettes.refreshCanvas();
|
|
return;
|
|
}
|
|
|
|
if (locked) {
|
|
return;
|
|
}
|
|
locked = true;
|
|
setTimeout(function () {
|
|
locked = false;
|
|
}, 500);
|
|
|
|
for (var p in palette.palettes.dict) {
|
|
if (palette.name != p) {
|
|
if (palette.palettes.dict[p].visible) {
|
|
palette.palettes.dict[p]._hideMenuItems(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (palette.visible) {
|
|
palette._hideMenuItems(false);
|
|
} else {
|
|
palette._showMenuItems(false);
|
|
}
|
|
palette.palettes.refreshCanvas();
|
|
});
|
|
|
|
this.menuContainer.on('mousedown', function (event) {
|
|
trashcan.show();
|
|
// Move them all?
|
|
var offset = {
|
|
x: palette.menuContainer.x - Math.round(event.stageX / palette.palettes.scale),
|
|
y: palette.menuContainer.y - Math.round(event.stageY / palette.palettes.scale)
|
|
};
|
|
|
|
palette.menuContainer.on('pressup', function (event) {
|
|
if (trashcan.overTrashcan(event.stageX / palette.palettes.scale, event.stageY / palette.palettes.scale)) {
|
|
if (trashcan.isVisible) {
|
|
palette.hide();
|
|
palette.palettes.refreshCanvas();
|
|
// Only delete plugin palettes.
|
|
if (palette.name === 'myblocks') {
|
|
palette._promptMacrosDelete();
|
|
} else if (BUILTINPALETTES.indexOf(palette.name) === -1) {
|
|
palette._promptPaletteDelete();
|
|
}
|
|
}
|
|
}
|
|
trashcan.hide();
|
|
});
|
|
|
|
palette.menuContainer.on('mouseout', function (event) {
|
|
if (trashcan.overTrashcan(event.stageX / palette.palettes.scale, event.stageY / palette.palettes.scale)) {
|
|
if (trashcan.isVisible) {
|
|
palette.hide();
|
|
palette.palettes.refreshCanvas();
|
|
}
|
|
}
|
|
trashcan.hide();
|
|
});
|
|
|
|
palette.menuContainer.on('pressmove', function (event) {
|
|
var oldX = palette.menuContainer.x;
|
|
var oldY = palette.menuContainer.y;
|
|
palette.menuContainer.x = Math.round(event.stageX / palette.palettes.scale) + offset.x;
|
|
palette.menuContainer.y = Math.round(event.stageY / palette.palettes.scale) + offset.y;
|
|
palette.palettes.refreshCanvas();
|
|
var dx = palette.menuContainer.x - oldX;
|
|
var dy = palette.menuContainer.y - oldY;
|
|
palette.palettes.initial_x = palette.menuContainer.x;
|
|
palette.palettes.initial_y = palette.menuContainer.y;
|
|
|
|
// If we are over the trash, warn the user.
|
|
if (trashcan.overTrashcan(event.stageX / palette.palettes.scale, event.stageY / palette.palettes.scale)) {
|
|
trashcan.startHighlightAnimation();
|
|
} else {
|
|
trashcan.stopHighlightAnimation();
|
|
}
|
|
|
|
// Hide the menu items while drag.
|
|
palette._hideMenuItems(false);
|
|
palette._moveMenuItemsRelative(dx, dy);
|
|
});
|
|
});
|
|
};
|
|
|
|
// Menu Item event handlers
|
|
this._loadPaletteMenuItemHandler = function (protoblk, blkname) {
|
|
// A menu item is a protoblock that is used to create a new block.
|
|
var palette = this;
|
|
var pressupLock = false;
|
|
var pressed = false;
|
|
var moved = false;
|
|
var saveX = this.protoContainers[blkname].x;
|
|
var saveY = this.protoContainers[blkname].y;
|
|
var bgScrolling = false;
|
|
|
|
this.protoContainers[blkname].on('mouseover', function (event) {
|
|
palette.palettes.activePalette = palette;
|
|
});
|
|
|
|
this.protoContainers[blkname].on('mousedown', function (event) {
|
|
var stage = palette.palettes.stage;
|
|
stage.setChildIndex(palette.protoContainers[blkname], stage.getNumChildren() - 1);
|
|
|
|
var h = Math.min(maxPaletteHeight(palette.palettes.cellSize, palette.palettes.scale), palette.palettes.y);
|
|
var clickY = event.stageY/palette.palettes.scale;
|
|
var paletteEndY = palette.menuContainer.y + h + STANDARDBLOCKHEIGHT;
|
|
|
|
// if(clickY < paletteEndY)
|
|
palette.protoContainers[blkname].mask = null;
|
|
|
|
moved = false;
|
|
pressed = true;
|
|
saveX = palette.protoContainers[blkname].x;
|
|
saveY = palette.protoContainers[blkname].y - palette.scrollDiff;
|
|
var startX = event.stageX;
|
|
var startY = event.stageY;
|
|
var lastY = event.stageY;
|
|
|
|
if (palette.draggingProtoBlock) {
|
|
return;
|
|
}
|
|
|
|
var mode = window.hasMouse ? MODEDRAG : MODEUNSURE;
|
|
|
|
palette.protoContainers[blkname].on('pressmove', function (event) {
|
|
if (mode === MODEDRAG) {
|
|
// if(clickY < paletteEndY)
|
|
moved = true;
|
|
palette.draggingProtoBlock = true;
|
|
palette.protoContainers[blkname].x = Math.round(event.stageX / palette.palettes.scale) - PALETTELEFTMARGIN;
|
|
palette.protoContainers[blkname].y = Math.round(event.stageY / palette.palettes.scale);
|
|
palette.palettes.refreshCanvas();
|
|
return;
|
|
}
|
|
|
|
if (mode === MODESCROLL) {
|
|
var diff = event.stageY - lastY;
|
|
palette.scrollEvent(diff, 10);
|
|
lastY = event.stageY;
|
|
return;
|
|
}
|
|
|
|
var xd = Math.abs(event.stageX - startX);
|
|
var yd = Math.abs(event.stageY - startY);
|
|
var diff = Math.sqrt(xd * xd + yd * yd);
|
|
if (mode === MODEUNSURE && diff > DECIDEDISTANCE) {
|
|
mode = yd > xd ? MODESCROLL : MODEDRAG;
|
|
}
|
|
});
|
|
});
|
|
|
|
this.protoContainers[blkname].on('mouseout', function (event) {
|
|
// Catch case when pressup event is missed.
|
|
// Put the protoblock back on the palette...
|
|
palette.palettes.activePalette = null;
|
|
|
|
if (pressed && moved) {
|
|
palette._restoreProtoblock(blkname, saveX, saveY + palette.scrollDiff);
|
|
pressed = false;
|
|
moved = false;
|
|
}
|
|
});
|
|
|
|
this.protoContainers[blkname].on('pressup', function (event) {
|
|
palette.palettes.activePalette = null;
|
|
|
|
if (pressupLock) {
|
|
return;
|
|
} else {
|
|
pressupLock = true;
|
|
setTimeout(function () {
|
|
pressupLock = false;
|
|
}, 1000);
|
|
}
|
|
|
|
palette._makeBlockFromProtoblock(protoblk, moved, blkname, event, saveX, saveY);
|
|
});
|
|
};
|
|
|
|
this._restoreProtoblock = function (name, x, y) {
|
|
// Return protoblock we've been dragging back to the palette.
|
|
this.protoContainers[name].x = x;
|
|
this.protoContainers[name].y = y;
|
|
// console.log('restore ' + name);
|
|
this._resetLayout();
|
|
};
|
|
|
|
this._promptPaletteDelete = function () {
|
|
var msg = 'Do you want to remove all "%s" blocks from your project?'.replace('%s', this.name)
|
|
if (!confirm(msg)) {
|
|
return;
|
|
}
|
|
|
|
this.palettes.remove(this.name);
|
|
|
|
delete pluginObjs['PALETTEHIGHLIGHTCOLORS'][this.name];
|
|
delete pluginObjs['PALETTESTROKECOLORS'][this.name];
|
|
delete pluginObjs['PALETTEFILLCOLORS'][this.name];
|
|
delete pluginObjs['PALETTEPLUGINS'][this.name];
|
|
|
|
if ('GLOBALS' in pluginObjs) {
|
|
delete pluginObjs['GLOBALS'][this.name];
|
|
}
|
|
if ('IMAGES' in pluginObjs) {
|
|
delete pluginObjs['IMAGES'][this.name];
|
|
}
|
|
if ('ONLOAD' in pluginObjs) {
|
|
delete pluginObjs['ONLOAD'][this.name];
|
|
}
|
|
if ('ONSTART' in pluginObjs) {
|
|
delete pluginObjs['ONSTART'][this.name];
|
|
}
|
|
if ('ONSTOP' in pluginObjs) {
|
|
delete pluginObjs['ONSTOP'][this.name];
|
|
}
|
|
|
|
for (var i = 0; i < this.protoList.length; i++) {
|
|
var name = this.protoList[i].name;
|
|
delete pluginObjs['FLOWPLUGINS'][name];
|
|
delete pluginObjs['ARGPLUGINS'][name];
|
|
delete pluginObjs['BLOCKPLUGINS'][name];
|
|
}
|
|
|
|
storage.plugins = preparePluginExports({});
|
|
if (sugarizerCompatibility.isInsideSugarizer()) {
|
|
sugarizerCompatibility.saveLocally();
|
|
}
|
|
};
|
|
|
|
this._promptMacrosDelete = function () {
|
|
var msg = 'Do you want to remove all the stacks from your custom palette?';
|
|
if (!confirm(msg)) {
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < this.protoList.length; i++) {
|
|
var name = this.protoList[i].name;
|
|
delete this.protoContainers[name];
|
|
this.protoList.splice(i, 1);
|
|
}
|
|
|
|
this.palettes.updatePalettes('myblocks');
|
|
storage.macros = prepareMacroExports(null, null, {});
|
|
|
|
if (sugarizerCompatibility.isInsideSugarizer()) {
|
|
sugarizerCompatibility.saveLocally();
|
|
}
|
|
};
|
|
|
|
this._makeBlockFromPalette = function (protoblk, blkname, callback) {
|
|
if (protoblk == null) {
|
|
console.log('null protoblk?');
|
|
return;
|
|
}
|
|
switch (protoblk.name) {
|
|
case 'do':
|
|
blkname = 'do ' + protoblk.defaults[0];
|
|
var newBlk = protoblk.name;
|
|
var arg = protoblk.defaults[0];
|
|
break;
|
|
case 'storein':
|
|
// Use the name of the box in the label
|
|
blkname = 'store in ' + protoblk.defaults[0];
|
|
var newBlk = protoblk.name;
|
|
var arg = protoblk.defaults[0];
|
|
break;
|
|
case 'box':
|
|
// Use the name of the box in the label
|
|
blkname = protoblk.defaults[0];
|
|
var newBlk = protoblk.name;
|
|
var arg = protoblk.defaults[0];
|
|
break;
|
|
case 'namedbox':
|
|
// Use the name of the box in the label
|
|
if (protoblk.defaults[0] === undefined) {
|
|
blkname = 'namedbox';
|
|
var arg = _('box');
|
|
} else {
|
|
blkname = protoblk.defaults[0];
|
|
var arg = protoblk.defaults[0];
|
|
}
|
|
var newBlk = protoblk.name;
|
|
break;
|
|
case 'namedarg':
|
|
// Use the name of the arg in the label
|
|
if (protoblk.defaults[0] === undefined) {
|
|
blkname = 'namedarg';
|
|
var arg = '1';
|
|
} else {
|
|
blkname = protoblk.defaults[0];
|
|
var arg = protoblk.defaults[0];
|
|
}
|
|
var newBlk = protoblk.name;
|
|
break;
|
|
case 'nameddo':
|
|
// Use the name of the action in the label
|
|
if (protoblk.defaults[0] === undefined) {
|
|
blkname = 'nameddo';
|
|
var arg = _('action');
|
|
} else {
|
|
blkname = protoblk.defaults[0];
|
|
var arg = protoblk.defaults[0];
|
|
}
|
|
var newBlk = protoblk.name;
|
|
break;
|
|
case 'nameddoArg':
|
|
// Use the name of the action in the label
|
|
if (protoblk.defaults[0] === undefined) {
|
|
blkname = 'nameddoArg';
|
|
var arg = _('action');
|
|
} else {
|
|
blkname = protoblk.defaults[0];
|
|
var arg = protoblk.defaults[0];
|
|
}
|
|
var newBlk = protoblk.name;
|
|
break;
|
|
case 'namedcalc':
|
|
// Use the name of the action in the label
|
|
if (protoblk.defaults[0] === undefined) {
|
|
blkname = 'namedcalc';
|
|
var arg = _('action');
|
|
} else {
|
|
blkname = protoblk.defaults[0];
|
|
var arg = protoblk.defaults[0];
|
|
}
|
|
var newBlk = protoblk.name;
|
|
break;
|
|
case 'namedcalcArg':
|
|
// Use the name of the action in the label
|
|
if (protoblk.defaults[0] === undefined) {
|
|
blkname = 'namedcalcArg';
|
|
var arg = _('action');
|
|
} else {
|
|
blkname = protoblk.defaults[0];
|
|
var arg = protoblk.defaults[0];
|
|
}
|
|
var newBlk = protoblk.name;
|
|
break;
|
|
default:
|
|
var newBlk = blkname;
|
|
var arg = '__NOARG__';
|
|
break;
|
|
}
|
|
|
|
if (protoblk.name !== 'namedbox' && blockIsMacro(blkname)) {
|
|
moved = true;
|
|
saveX = this.protoContainers[blkname].x;
|
|
saveY = this.protoContainers[blkname].y;
|
|
this._makeBlockFromProtoblock(protoblk, moved, blkname, null, saveX, saveY);
|
|
} else {
|
|
var newBlock = paletteBlockButtonPush(this.palettes.blocks, newBlk, arg);
|
|
callback(newBlock);
|
|
}
|
|
};
|
|
|
|
this.cleanup = function () {
|
|
this._resetLayout();
|
|
this._updateBlockMasks();
|
|
this.palettes.refreshCanvas();
|
|
};
|
|
|
|
this._makeBlockFromProtoblock = function (protoblk, moved, blkname, event, saveX, saveY) {
|
|
var that = this;
|
|
|
|
function __myCallback (newBlock) {
|
|
// Move the drag group under the cursor.
|
|
that.palettes.blocks.findDragGroup(newBlock);
|
|
for (var i in that.palettes.blocks.dragGroup) {
|
|
that.palettes.blocks.moveBlockRelative(that.palettes.blocks.dragGroup[i], Math.round(event.stageX / that.palettes.scale) - that.palettes.blocks.stage.x, Math.round(event.stageY / that.palettes.scale) - that.palettes.blocks.stage.y);
|
|
}
|
|
// Dock with other blocks if needed
|
|
that.palettes.blocks.blockMoved(newBlock);
|
|
that.palettes.blocks.checkBounds();
|
|
};
|
|
|
|
if (moved) {
|
|
moved = false;
|
|
this.draggingProtoBlock = false;
|
|
|
|
var macroExpansion = getMacroExpansion(blkname, this.protoContainers[blkname].x - this.palettes.blocks.stage.x, this.protoContainers[blkname].y - this.palettes.blocks.stage.y);
|
|
|
|
if (macroExpansion != null) {
|
|
this.palettes.blocks.loadNewBlocks(macroExpansion);
|
|
var thisBlock = this.palettes.blocks.blockList.length - 1;
|
|
var topBlk = this.palettes.blocks.findTopBlock(thisBlock);
|
|
} else if (this.name === 'myblocks') {
|
|
// If we are on the myblocks palette, it is a macro.
|
|
var macroName = blkname.replace('macro_', '');
|
|
|
|
// We need to copy the macro data so it is not overwritten.
|
|
var obj = [];
|
|
for (var b = 0; b < this.palettes.macroDict[macroName].length; b++) {
|
|
var valueEntry = this.palettes.macroDict[macroName][b][1];
|
|
var newValue = [];
|
|
if (typeof(valueEntry) === 'string') {
|
|
newValue = valueEntry;
|
|
} else if (typeof(valueEntry[1]) === 'string') {
|
|
if (valueEntry[0] === 'number') {
|
|
newValue = [valueEntry[0], Number(valueEntry[1])];
|
|
} else {
|
|
newValue = [valueEntry[0], valueEntry[1]];
|
|
}
|
|
} else if (typeof(valueEntry[1]) === 'number') {
|
|
if (valueEntry[0] === 'number') {
|
|
newValue = [valueEntry[0], valueEntry[1]];
|
|
} else {
|
|
newValue = [valueEntry[0], valueEntry[1].toString()];
|
|
}
|
|
} else {
|
|
if (valueEntry[0] === 'number') {
|
|
newValue = [valueEntry[0], Number(valueEntry[1]['value'])];
|
|
} else {
|
|
newValue = [valueEntry[0], {'value': valueEntry[1]['value']}];
|
|
}
|
|
}
|
|
|
|
var newBlock = [this.palettes.macroDict[macroName][b][0],
|
|
newValue,
|
|
this.palettes.macroDict[macroName][b][2],
|
|
this.palettes.macroDict[macroName][b][3],
|
|
this.palettes.macroDict[macroName][b][4]];
|
|
obj.push(newBlock);
|
|
}
|
|
|
|
// Set the position of the top block in the stack
|
|
// before loading.
|
|
obj[0][2] = this.protoContainers[blkname].x - this.palettes.blocks.stage.x;
|
|
obj[0][3] = this.protoContainers[blkname].y - this.palettes.blocks.stage.y;
|
|
this.palettes.blocks.loadNewBlocks(obj);
|
|
|
|
// Ensure collapse state of new stack is set properly.
|
|
var thisBlock = this.palettes.blocks.blockList.length - 1;
|
|
var topBlk = this.palettes.blocks.findTopBlock(thisBlock);
|
|
setTimeout(function () {
|
|
this.palettes.blocks.blockList[topBlk].collapseToggle();
|
|
}, 500);
|
|
} else {
|
|
var newBlock = this._makeBlockFromPalette(protoblk, blkname, __myCallback, newBlock);
|
|
}
|
|
|
|
// Put the protoblock back on the palette...
|
|
this.cleanup();
|
|
}
|
|
};
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
function initPalettes (palettes) {
|
|
// Instantiate the palettes object on first load.
|
|
|
|
for (var i = 0; i < BUILTINPALETTES.length; i++) {
|
|
palettes.add(BUILTINPALETTES[i]);
|
|
}
|
|
|
|
palettes.makePalettes(true);
|
|
|
|
// Give the palettes time to load.
|
|
// We are in no hurry since we are waiting on the splash screen.
|
|
setTimeout(function () {
|
|
palettes.show();
|
|
palettes.bringToTop();
|
|
}, 6000);
|
|
};
|
|
|
|
|
|
const MODEUNSURE = 0;
|
|
const MODEDRAG = 1;
|
|
const MODESCROLL = 2;
|
|
const DECIDEDISTANCE = 20;
|
|
|
|
|
|
function makePaletteBitmap(palette, data, name, callback, extras) {
|
|
// Async creation of bitmap from SVG data
|
|
// Works with Chrome, Safari, Firefox (untested on IE)
|
|
var img = new Image();
|
|
img.onload = function () {
|
|
var bitmap = new createjs.Bitmap(img);
|
|
callback(palette, name, bitmap, extras);
|
|
};
|
|
|
|
img.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(data)));
|
|
};
|