// 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 = '
' + _('back') + '
';
for (var name in this.models) {
html += '';
}
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)));
};