define([ 'nbextensions/visualpython/src/common/vpCommon' , 'nbextensions/visualpython/src/container/vpContainer' , 'nbextensions/visualpython/src/common/constant' , './api.js' , './constData.js' , './component/option/class_option.js' , './component/option/def_option.js' , './component/option/if_option.js' , './component/option/elif_option.js' , './component/option/except_option.js' , './component/option/for_option.js' , './component/option/import_option.js' , './component/option/code_option.js' , './component/option/return_option.js' , './component/option/while_option.js' , './component/option/lambda_option.js' , './component/option/api_option.js' , './component/option/text_option.js' , './component/option/node_option.js' , './component/option/none_option.js' ], function ( vpCommon, vpContainer, vpConst , api , constData , InitClassBlockOption , InitDefBlockOption , InitIfBlockOption , InitElifBlockOption , InitExceptBlockOption , InitForBlockOption , InitImportBlockOption , InitCodeBlockOption , InitReturnBlockOption , InitWhileBlockOption , InitLambdaBlockOption , ApiBlockOption , InitTextBlockOption , InitNodeBlockOption , InitNoneOption) { const { InitAPIBlockOption, LoadAPIBlockOption } = ApiBlockOption; const { SetChildBlockList_down_first_indent_last , MapTypeToName , MapNewLineStrToIndentString , IsCanHaveIndentBlock , IsElifElseExceptFinallyBlockType , IsIfForTryBlockType , GenerateApiCode , GenerateClassInParamList , GenerateDefCode , GenerateReturnCode , GenerateIfCode , GenerateForCode , GenerateLambdaCode , GenerateExceptCode , GenerateImportCode , GenerateWhileCode , IsCodeBlockType , IsDefineBlockType , IsNodeTextBlockType , ShowCodeBlockCode } = api; const { BLOCK_CODELINE_TYPE , BLOCK_DIRECTION , FOCUSED_PAGE_TYPE , FOR_BLOCK_ARG3_TYPE , IF_BLOCK_CONDITION_TYPE , NUM_BLOCK_HEIGHT_PX , NUM_INDENT_DEPTH_PX , NUM_MAX_ITERATION , NUM_NODE_OR_TEXT_BLOCK_MARGIN_TOP_PX , NUM_EXCEED_DEPTH , NUM_BLOCK_BOTTOM_HOLDER_HEIGHT , STR_EMPTY , STR_TOP , STR_LEFT , STR_BORDER , STR_BORDER_LEFT , STR_PX , STR_OPACITY , STR_MARGIN_TOP , STR_MARGIN_LEFT , STR_DISPLAY , STR_BACKGROUND_COLOR , STR_HEIGHT , STR_NONE , STR_BLOCK , STR_FLEX , STR_POSITION , STR_ABSOLUTE , STR_TRANSPARENT , STR_WIDTH , STR_TITLE , STR_BREAK , STR_CONTINUE , STR_PASS , STR_ONE_SPACE , STR_ONE_INDENT , STR_MSG_BLOCK_DEPTH_MUSH_NOT_EXCEED_6 , STR_CLICK , STR_RIGHT_CLICK , STR_KEYWORD_NEW_LINE , STR_INPUT_YOUR_CODE , STR_COLOR , VP_BLOCK , VP_BLOCK_BLOCKCODELINETYPE_CLASS_DEF , VP_CLASS_PREFIX , VP_CLASS_APIBLOCK_BOARD , VP_CLASS_BLOCK_BOTTOM_HOLDER , VP_CLASS_BLOCK_SHADOWBLOCK_CONTAINER , VP_CLASS_APIBLOCK_BLOCK_HEADER , VP_CLASS_SELECTED_SHADOWBLOCK , VP_CLASS_STYLE_FLEX_ROW_BETWEEN , VP_CLASS_STYLE_FLEX_ROW_END , VP_CLASS_BLOCK_SUB_BTN_CONTAINER , VP_CLASS_BLOCK_LEFT_HOLDER , VP_CLASS_BLOCK_DEPTH_INFO , VP_CLASS_BLOCK_NUM_INFO , VP_CLASS_APIBLOCK_NODEBLOCK_INPUT , VP_CLASS_APIBLOCK_NODEBLOCK_TEXT , VP_CLASS_APIBLOCK_NODEBLOCK , VP_CLASS_APIBLOCK_NODEBLOCK_TEXT_CONTAINER , STR_CHANGE_KEYUP_PASTE , STATE_className , STATE_defName , STATE_isIfElse , STATE_isFinally , STATE_codeLine , COLOR_CLASS_DEF , COLOR_CONTROL , COLOR_API , COLOR_CODE , COLOR_CLASS_DEF_STRONG , COLOR_CONTROL_STRONG , COLOR_API_STRONG , COLOR_CODE_STRONG , COLOR_WHITE , COLOR_BLACK , COLOR_LINENUMBER , COLOR_GRAY_input_your_code , ERROR_AB0002_INFINITE_LOOP , IMPORT_DEFAULT_DATA_LIST } = constData; var Block = function(blockContainerThis, type , blockData, isGroupBlock=false) { var codeStr = STR_EMPTY; var blockType = type; if ( blockType == BLOCK_CODELINE_TYPE.CLASS ) { var classNum = blockContainerThis.getClassNum(); blockContainerThis.addClassNum(); } else if ( blockType == BLOCK_CODELINE_TYPE.DEF) { var defNum = blockContainerThis.getDefNum(); blockContainerThis.addDefNum(); } else if (blockType == BLOCK_CODELINE_TYPE.BREAK) { codeStr = STR_BREAK } else if (blockType == BLOCK_CODELINE_TYPE.CONTINUE) { codeStr = STR_CONTINUE } else if (blockType == BLOCK_CODELINE_TYPE.PASS) { codeStr = STR_PASS } else if (blockType == BLOCK_CODELINE_TYPE.NODE) { blockContainerThis.addNodeBlock(this); } else if (blockType == BLOCK_CODELINE_TYPE.TEXT) { blockContainerThis.addTextBlock(this); } /** state 데이터는 vpnote로 저장할 때, 저장 되는 데이터 * state가 아닌 데이터는 저장 할 수 없고, 주피터에서 블럭을 표현하는 용도로만 사용된다. */ this.state = { className: 'vpClass' + classNum , parentClassName: STR_EMPTY , classInParamList: [STR_EMPTY] , defName: 'vpFunc' + defNum , defInParamList: [] , ifConditionList: [ { arg1: STR_EMPTY , arg2: STR_EMPTY , arg3: STR_EMPTY , arg4: STR_EMPTY , arg5: STR_EMPTY , arg6: STR_EMPTY , codeLine: STR_EMPTY , conditionType: IF_BLOCK_CONDITION_TYPE.ARG } ] , elifConditionList: [ { arg1: STR_EMPTY , arg2: STR_EMPTY , arg3: STR_EMPTY , arg4: STR_EMPTY , arg5: STR_EMPTY , arg6: STR_EMPTY , codeLine: STR_EMPTY , conditionType: IF_BLOCK_CONDITION_TYPE.ARG } ] , isIfElse: false , forParam: { arg1: STR_EMPTY , arg2: STR_EMPTY , arg3: FOR_BLOCK_ARG3_TYPE.INPUT_STR , arg4: STR_EMPTY , arg5: STR_EMPTY , arg6: STR_EMPTY , arg7: STR_EMPTY , arg3InputStr: STR_EMPTY , arg3Default: STR_EMPTY } , whileConditionList: [ { arg1: STR_EMPTY , arg2: STR_EMPTY , arg3: STR_EMPTY , arg4: 'and' } ] , exceptConditionList: [ { arg1: STR_EMPTY , arg2: `none` , arg3: STR_EMPTY , codeLine: STR_EMPTY , conditionType: IF_BLOCK_CONDITION_TYPE.ARG } ] , isFinally: false , baseImportList: IMPORT_DEFAULT_DATA_LIST , customImportList: [] , isBaseImportPage: true , lambdaArg1: STR_EMPTY , lambdaArg2List: [ ] , lambdaArg3: STR_EMPTY , returnOutParamList: [ STR_EMPTY ] , customCodeLine: codeStr , metadata: null , funcID: STR_EMPTY } this.state_backup = { ...this.state }; this.isGroupBlock = isGroupBlock; /** string 데이터 */ this.blockName = STR_EMPTY; this.codeLine = STR_EMPTY; /** boolean 데이터 */ this.isClicked = false; this.isCtrlPressed = false; this.isNowMoved = false; this.isIfElse = false; this.isFinally = false; /** number(숫자) 데이터 */ this.opacity = 1; this.depth = 0; this.blockLeftShadowHeight = 0; this.blockNumber = 0; this.width = blockContainerThis.getBlockMaxWidth(); /** this 블럭만이 가지는 특수 데이터 * 이 데이터들은 이동하거나 삭제 할 때 등 여러 용도로 사용 된다. */ this.type = type; // this 블럭의 타입 값 (type은 class : 1, def: 2, if: 3 ...) this.childBlockUUIDList = []; // vpnote 저장용 데이터 this.uuid = vpCommon.getUUID(); // this 블럭의 고유 값 this.direction = BLOCK_DIRECTION.NONE; // this 블럭의 위치 값. // 위치는 this 블럭의 부모로 부터 DOWN, INDENT인지 결정됨 /** this 데이터 */ this.blockContainerThis = blockContainerThis; /** -------- 블럭을 표현하는 dom 데이터 ------------------------------- */ this.blockMainDom = null; this.blockOptionPageDom = null; /** -------- this 블럭을 중심으로 위치에 따른 block 데이터 ------------*/ this.prevBlock = null; // this 블럭의 부모 블럭 this.childBlock_down = null; // this 블럭의 자식 블럭 (DOWN 위치) this.childBlock_indent = null; // this 블럭의 자식 블럭 (INDENT 위치) this.childBlockList = []; // this 블럭의 자식 블럭 list /** -------- this 블럭을 중심으로 특수 타입의 block 데이터 ---------*/ this.ifElseBlock = null; // if, for, try 블럭만 가질 수 있음 this.finallyBlock = null; // try 블럭만 가질 수 있음 this.lastElifBlock = null; // if 블럭만 가질 수 있음 this.lastChildBlock = null; // 블럭마다 클릭하면 고유 영역(this 블럭을 중심으로 자식 블럭 리스트)이 존재. // 이 고유지역 중 가장 아래의 자식 블럭을 의미 this.propertyBlockFromDef = null; // def블럭만 가질 수 있음. def 블럭에서 decoration 블럭을 생성할 때의 decoration 블럭을 의미 /** ------- api list 블럭과 text 블럭만 가질 수 있는 데이터 ------------ */ this.optionPageLoadCallback = null; this.loadOption = null; this.importPakage = null; /** -------- node 블럭만 가질 수 있는 데이터 --------------------------------------*/ this.isNodeBlockInput = false; this.isNodeBlockToggled = false; this.isNodeBlockTitleEmpty = true; var name = MapTypeToName(type); this.setBlockName(name); /** vpnote를 open하고 블럭을 생성할 때 */ if (blockData) { this.state = blockData.blockOptionState; this.uuid = blockData.UUID; /** Logic의 Define, Control, Execute을 드래그해서 블럭을 생성할 때 */ } else { this.blockContainerThis.createBlock_fromLogic(this); } // if (!temporary) { // this.blockContainerThis.addBlock(this); // this.init(); // this.renderColor_thisBlockArea(true); // } } Block.prototype.init = function() { var blockContainerThis = this.getBlockContainerThis(); /** 기본 블럭의 dom 생성 */ var blockMainDom = blockContainerThis.makeBlockDom(this, true); this.setBlockMainDom(blockMainDom); /** node 블럭과 text 블럭일 경우의 만 LineNumberInfo Dom 생성 */ var blockType = this.getBlockType(); if ( blockType == BLOCK_CODELINE_TYPE.NODE || blockType == BLOCK_CODELINE_TYPE.TEXT || this.isGroupBlock) { blockMainDom = blockContainerThis.makeBlockLineNumberInfoDom(this); } this.bindEventAll(); } Block.prototype.apply = function() { var blockContainerThis = this.blockContainerThis; blockContainerThis.addBlock(this); this.init(); this.renderColor_thisBlockArea(true); this.getChildBlockList().forEach(childBlock => { childBlock.apply(); }); } Block.prototype.saveState = function() { if (this.getBlockType() == BLOCK_CODELINE_TYPE.API || this.getBlockType() == BLOCK_CODELINE_TYPE.TEXT ) { // set metadata var importPackage = this.getImportPakage(); // get generatedCode and save as metadata if (importPackage) { var code = importPackage.generateCode(false, false); importPackage.metaGenerate(); // importPackage.metadata.code = code; importPackage['metadata'] = { ...importPackage['metadata'], code : code } this.state.metadata = importPackage.metadata; } } // save backup this.state_backup = { ...this.state }; this.isModified = false; } Block.prototype.loadState = function() { this.state = { ...this.state_backup }; // this.renderOptionPage(true); // Library 페이지는 로드가 늦게 되어서... this.isModified = false; } /** 블럭에 표시할 데이터를 dom에 write함 * @param {string} textInfo 블럭의 text에 입력할 text string */ Block.prototype.writeCode = function(textInfo) { var blockType = this.getBlockType(); var blockUUID = this.getUUID(); /** node 블럭일 경우 */ if (this.getBlockType() == BLOCK_CODELINE_TYPE.NODE) { $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_NODEBLOCK_TEXT + blockUUID).html(textInfo); $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_NODEBLOCK_INPUT + blockUUID).val(textInfo); /** CODE 블럭일 경우 */ } else if ( IsCodeBlockType(blockType) == true ) { var codeStr = ShowCodeBlockCode(this); $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BLOCK_HEADER + blockUUID).html(codeStr); $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BLOCK_HEADER + blockUUID).attr('title', codeStr); /** node 블럭이 아닐 경우, CODE 블럭이 아닐 경우 */ } else { if ( blockType == BLOCK_CODELINE_TYPE.CLASS) { $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BLOCK_HEADER + 'class-name-' + blockUUID).html(this.getState('className')); } if ( blockType == BLOCK_CODELINE_TYPE.DEF) { $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BLOCK_HEADER + 'def-name-' + blockUUID).html(this.getState('defName')); } $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BLOCK_HEADER + blockUUID).html(textInfo); $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BLOCK_HEADER + blockUUID).attr('title', textInfo); } } // ** --------------------------- Block을 삭제, 수정, 불러오기 혹은 주변 block과의 관계를 규정하는 메소드들 --------------------------- */ Block.prototype.isGroupBlock = function() { return this.isGroupBlock; } Block.prototype.setGroupBlock = function(isGroupBlock) { // if same state, do nothing if (this.isGroupBlock == isGroupBlock) { return; } // set groupblock this.isGroupBlock = isGroupBlock; // 1) remove group state if (!isGroupBlock) { this.blockContainerThis.removeNodeBlock(this); $(this.blockMainDom).find(VP_CLASS_PREFIX + VP_CLASS_BLOCK_NUM_INFO).css('display','none'); $(this.blockMainDom).css(STR_MARGIN_TOP, 0); } // 2) add group state else { this.blockContainerThis.addNodeBlock(this); if ($(this.blockMainDom).find(VP_CLASS_PREFIX + VP_CLASS_BLOCK_NUM_INFO).length <= 0) { this.blockContainerThis.makeBlockLineNumberInfoDom(this); } $(this.blockMainDom).find(VP_CLASS_PREFIX + VP_CLASS_BLOCK_NUM_INFO).css('display','block'); $(this.blockMainDom).css(STR_MARGIN_TOP, NUM_NODE_OR_TEXT_BLOCK_MARGIN_TOP_PX); } } Block.prototype.getPrevBlock = function() { return this.prevBlock } Block.prototype.setPrevBlock = function(prevBlock) { this.prevBlock = prevBlock; } Block.prototype.addChildBlockList = function(nextBlock) { this.childBlockList = [ ...this.childBlockList, nextBlock] } Block.prototype.setChildBlockList = function(childBlockList) { this.childBlockList = childBlockList; } Block.prototype.getChildBlockList = function() { return this.childBlockList; } /** this 블럭의 자식 블럭 중에 DOWN 위치에 존재하는 블럭을 가져옴 */ Block.prototype.getChildBlock_down = function() { var childBlock_down = null; var childBlockList = this.getChildBlockList(); childBlockList.some(block => { if ( block.getDirection() == BLOCK_DIRECTION.DOWN) { childBlock_down = block; return true; } }); return childBlock_down; } /** this 블럭의 자식 블럭 중에 INDENT 위치에 존재하는 블럭을 가져옴 */ Block.prototype.getChildBlock_indent = function() { var childBlock_indent = null; var childBlockList = this.getChildBlockList(); childBlockList.some(block => { if ( block.getDirection() == BLOCK_DIRECTION.INDENT) { childBlock_indent = block; return true; } }); return childBlock_indent; } /** this 블럭의 자식 블럭(DOWN or INDENT 2개 까지 존재할 수 있음) 중에 * 인자로 들어온 deletedBlock과 일치하면 삭제 */ Block.prototype.deleteChildBlock = function(deletedBlock) { var childBlockList = this.getChildBlockList(); childBlockList.some((block, index) => { if (block.getUUID() == deletedBlock.getUUID()) { childBlockList.splice(index, 1); } }); } Block.prototype.setPropertyBlockFromDef = function(propertyBlockFromDef) { this.propertyBlockFromDef = propertyBlockFromDef; } Block.prototype.getPropertyBlockFromDef = function() { return this.propertyBlockFromDef; } /** * if 블럭이 생성한 elifList 중에 가장 아래에 위치한 elif block을 set, get * @param {BLOCK} lastElifBlock */ Block.prototype.setLastElifBlock = function(lastElifBlock) { this.lastElifBlock = lastElifBlock; } Block.prototype.getLastElifBlock = function() { if (this.lastElifBlock) { return this.lastElifBlock; } else { return this; } } Block.prototype.getElseBlock = function() { return this.ifElseBlock; } Block.prototype.setElseBlock = function(ifElseBlock) { this.ifElseBlock = ifElseBlock; } /** * ---------------thisBlockArea는 블럭을 클릭했을 때 진하게 색칠되는 영역을 말함-------------- * ---------------lastBlock_from_thisBlockArea은 thisBlockArea 영역의 맨 마지막 블럭을 의미함----- */ /** * @param { Block } lastChildBlock */ Block.prototype.setLastBlock_from_thisBlockArea = function(lastChildBlock) { this.lastChildBlock = lastChildBlock; } Block.prototype.getLastBlock_from_thisBlockArea = function() { this.getBlockList_thisBlockArea(); return this.lastChildBlock; } /** 현재 root 블럭부터 모든 자식 블럭리스트 들을 board에 놓인 순서대로 전부 가져온다 */ Block.prototype.getRootToChildBlockList = function() { var blockContainerThis = this.getBlockContainerThis(); var rootBlock = blockContainerThis.getRootBlock(); return rootBlock.getThisToLastBlockList(); } /** 현재 this 블럭부터 모든 자식 블럭리스트 들을 board에 놓인 순서대로 가져온다 */ Block.prototype.getThisToLastBlockList = function() { var childBlockList = this.getChildBlockList(); var stack = []; stack.push(childBlockList); var thisToLastBlockList = [this]; thisToLastBlockList = this._getThisToLastBlockList(thisToLastBlockList, stack); return thisToLastBlockList; } /** * @private * @param {Array} childBlockList * @param {Array} stack * @param {boolean} isNodeBlock node 블럭의 자식 블럭을 가져오냐 아니냐 true/false * @param {boolean} isIfForTryBlock if, for, try 블럭의 자식 블럭을 가져오냐 아니냐 true/false * @param {Block} ifForTryBlock if, for, try 블럭 */ Block.prototype._getThisToLastBlockList = function(thisToLastBlockList, stack, isNodeBlock = false, isIfForTryBlock = false, ifForTryBlock) { // console.log('isNodeBlock',isNodeBlock); if (isIfForTryBlock == true) { var _childBlockList = this.getChildBlockList(); stack.push(_childBlockList); } var iteration = 0; var current; while (stack.length != 0) { current = stack.shift(); /** FIXME: 무한루프 체크 */ if (iteration > NUM_MAX_ITERATION) { console.log(ERROR_AB0002_INFINITE_LOOP); break; } iteration++; /** 배열 일 때 */ if (Array.isArray(current)) { var currBlockList = current; stack = SetChildBlockList_down_first_indent_last(stack, currBlockList); /** 배열 이 아닐 때 */ } else { var currBlock = current; var blockType = currBlock.getBlockType(); var blockDirection = currBlock.getDirection(); if (isNodeBlock == true) { if ( ( IsNodeTextBlockType(currBlock.getBlockType()) == true || currBlock.isGroupBlock ) && currBlock.getUUID() != this.getUUID()) { break; } this.setLastBlock_from_thisBlockArea(currBlock); thisToLastBlockList.push(currBlock); } else if (isIfForTryBlock == true) { if ( blockDirection == BLOCK_DIRECTION.DOWN ) { if (IsIfForTryBlockType(blockType) == true) { break; } else if (IsElifElseExceptFinallyBlockType(blockType) == true) { var lastChildBlock = currBlock.getChildBlock_down(); if (lastChildBlock) { thisToLastBlockList.push(lastChildBlock); ifForTryBlock.setLastBlock_from_thisBlockArea(lastChildBlock); } var elifOrElseExceptFinallyBlockList = currBlock.getBlockList_thisBlockArea(); elifOrElseExceptFinallyBlockList.forEach(elifOrElseExceptFinallyBlock => { thisToLastBlockList.push(elifOrElseExceptFinallyBlock); }); } } } else { thisToLastBlockList.push(currBlock); } var childBlockList = currBlock.getChildBlockList(); stack.unshift(childBlockList); } } return thisToLastBlockList; } /** 현재 this 블럭부터 하위 depth 자식 블럭리스트(동일 depth 블럭 제거) 들을 전부 가져온다 */ Block.prototype.getBlockList_thisBlockArea = function() { var stack = []; var blockList_thisBlockArea = []; var blockType = this.getBlockType(); if ( blockType == BLOCK_CODELINE_TYPE.NODE || this.isGroupBlock) { stack = [this]; blockList_thisBlockArea = this._getThisToLastBlockList(blockList_thisBlockArea, stack, true); return blockList_thisBlockArea; } /** this 블럭의 indent 위치에 자식 블럭이 있는지 확인 * this 블럭의 indent 위치에 자식 블럭이 있어야 하위 depth 자식 블럭들을 가져올 수 있다. */ var childBlock_indent = this.getChildBlock_indent(); if (childBlock_indent) { stack.push(childBlock_indent); } blockList_thisBlockArea = [this]; /** this 블럭의 down 위치에 자식 블럭이 있는지 확인 */ var lastChildBlock = null; if (IsCanHaveIndentBlock(blockType) == true) { var childBlock_down = this.getChildBlock_down(); if (childBlock_down){ blockList_thisBlockArea.push(childBlock_down); lastChildBlock = childBlock_down; } } else { lastChildBlock = this; } /** if for try 일 경우 */ if ( IsIfForTryBlockType(blockType) == true && lastChildBlock != null) { var ifForTryList = lastChildBlock._getThisToLastBlockList([], [], false, true, this); blockList_thisBlockArea = this._getThisToLastBlockList(blockList_thisBlockArea, stack); if (!this.lastChildBlock) { this.setLastBlock_from_thisBlockArea(lastChildBlock); } ifForTryList.forEach(block => { blockList_thisBlockArea.push(block); }); return blockList_thisBlockArea; /** if for try가 아닐 경우 */ } else { blockList_thisBlockArea = this._getThisToLastBlockList(blockList_thisBlockArea, stack); this.setLastBlock_from_thisBlockArea(lastChildBlock); return blockList_thisBlockArea; } } Block.prototype.getBlockList_thisBlockArea_noShadowBlock = function() { var childBlockList = this.getBlockList_thisBlockArea(); childBlockList = childBlockList.filter(childBlock => { if (childBlock.getBlockType() == BLOCK_CODELINE_TYPE.SHADOW) { return false; } else { return true; } }); return childBlockList; } /** 생성할 블럭이 6뎁스를 초과 할 경우 alert창을 띄워 막음 */ Block.prototype.alertExceedDepth = function() { vpCommon.renderAlertModal(STR_MSG_BLOCK_DEPTH_MUSH_NOT_EXCEED_6); var blockContainerThis = this.getBlockContainerThis(); blockContainerThis.resetOptionPage(); } /** param으로 받아온 block을 this블럭의 자식으로 append. * 블럭을 생성할 때, 혹은 이동할 때 사용되는 메소드 * @param {BLOCK} appendedBlock 자식으로 append할 블럭 * @param {BLOCK_DIRECTION} direction 자식으로 append할 블럭의 위치 설정 */ Block.prototype.appendBlock = function(appendedBlock, direction) { var blockContainerThis = this.getBlockContainerThis(); var thisBlock = this; /** * depth가 6초과할 경우 alert */ var depth = thisBlock.calculateDepthAndGet(); if ( (depth >= NUM_EXCEED_DEPTH && direction == BLOCK_DIRECTION.INDENT) || depth > NUM_EXCEED_DEPTH) { appendedBlock.alertExceedDepth(); return; } /** append할 block의 prev 블럭이 this 블럭일 때 * 이동한 block을 내 위치에 다시 놓을 경우 * */ var prevBlock = appendedBlock.getPrevBlock(); if ( prevBlock ) { if (prevBlock.getUUID() == thisBlock.getUUID()) { return; } } appendedBlock.reConnectPrevBlock(); /** appendedBlock이 존재하는 영역의 가장 아래 블럭을 가져옴 */ var lastChildBlock = appendedBlock.getLastBlock_from_thisBlockArea(); // 새로 들어온 block의 이전 블럭을 현재 this블럭으로 정함 appendedBlock.setPrevBlock(thisBlock); // 2번째 인자인 direction이 down 일 경우 if (direction == BLOCK_DIRECTION.DOWN) { // console.log('BLOCK_DIRECTION.DOWN', appendedBlock.getDirection()); var blockType = appendedBlock.getBlockType(); if (IsElifElseExceptFinallyBlockType(blockType) == false) { thisBlock.reConnectLastChildBlock(prevBlock, lastChildBlock, appendedBlock.getDirection()); } var lastBlock = blockContainerThis.getLastBottomBlock(appendedBlock); var childBlockList = thisBlock.getChildBlockList(); lastBlock.setChildBlockList(childBlockList); childBlockList.forEach(block => { block.setPrevBlock(lastBlock); }); thisBlock.setChildBlockList([appendedBlock]); // 2번째 인자인 direction이 indent일 경우 } else { // console.log('BLOCK_DIRECTION.INDENT', appendedBlock.getDirection()); thisBlock.reConnectLastChildBlock(prevBlock, lastChildBlock, appendedBlock.getDirection()); var childBlock_indent = thisBlock.getChildBlock_indent(); if (childBlock_indent) { childBlock_indent.setDirection(BLOCK_DIRECTION.DOWN); childBlock_indent.setPrevBlock(lastChildBlock); lastChildBlock.addChildBlockList(childBlock_indent); thisBlock.deleteChildBlock(childBlock_indent); } thisBlock.addChildBlockList(appendedBlock); } appendedBlock.setDirection(direction); } /** * 하위 depth block들을 지운다 */ Block.prototype.deleteBlock_childBlockList = function() { var blockContainerThis = this.getBlockContainerThis(); /** * 만약 root 블럭일 경우 */ if ( this.getPrevBlock() == null) { this.setDirection(BLOCK_DIRECTION.NONE); this.removeRootBlock(); /** * 만약 root이 아닌 일반 블럭일 경우 */ } else { /** 부모 블럭과 연결 끊음 */ this.reConnectPrevBlock(); var prevBlock = this.getPrevBlock(); var deletedBlockDirection = this.getDirection(); var lastChildBlock = this.getLastBlock_from_thisBlockArea(); /** this 블럭이 존재하는 영역의 가장 아래 블럭과의 연결을 끊음*/ this.reConnectLastChildBlock(prevBlock, lastChildBlock, deletedBlockDirection); } var blockList_thisBlockArea = this.getBlockList_thisBlockArea(); blockList_thisBlockArea.forEach(block => { block.deleteBlockDomAndData(); }); /** 현재 블럭 리스트가 다 제거되면 (blockList.length == 0) * 이전 블럭 리스트도 제거함 */ if (blockContainerThis.getBlockList().length == 0) { blockContainerThis.setPrevBlockList([]); }; /** 다시 렌더링 */ blockContainerThis.reRenderAllBlock_asc(); } /** this 블럭을 이동하거나 삭제할 때, * this 블럭과 prev 블럭과의 관계를 끊는 메소드 */ Block.prototype.reConnectPrevBlock = function() { var prevBlock = this.getPrevBlock(); var block = this; if ( prevBlock ) { prevBlock.deleteChildBlock(block); } } /** this 블럭을 이동하거나 삭제할 때, * this 블럭과 this 블럭영역의 가장 마지막 블럭(lastChildBlock)의 자식 블럭(down)과의 연결을 끊고, * this 블럭의 prev 블럭과 this 블럭영역의 가장 마지막 블럭(lastChildBlock)의 자식 블럭(down)을 연결 시킴 */ Block.prototype.reConnectLastChildBlock = function(prevBlock, lastChildBlock, direction) { /** 블럭을 이동 시킬 때와 삭제할 때 childBlock_down가 존재 * 블럭을 새로 만들 때는 childBlock_down가 존재하지 않는다 x */ if (lastChildBlock) { var childBlock_down = lastChildBlock.getChildBlock_down(); if (childBlock_down) { childBlock_down.setDirection(direction); childBlock_down.setPrevBlock(prevBlock); prevBlock.addChildBlockList(childBlock_down); lastChildBlock.deleteChildBlock(childBlock_down); } } } /** * blockContainer의 blockList에서 block 삭제 */ Block.prototype.deleteBlockDomAndData = function() { /** board에 container dom에서 * this 블럭의 dom을 삭제 제거 */ const blockMainDom = this.getBlockMainDom(); $(blockMainDom).remove(); $(blockMainDom).empty(); /** blockContainer에서 block 데이터 삭제 제거 */ const blockContainerThis = this.getBlockContainerThis(); const blockUUID = this.getUUID(); blockContainerThis.deleteBlock(blockUUID); blockContainerThis.deleteNodeBlock(blockUUID); } // ** --------------------------- Block dom 관련 메소드들 --------------------------- */ /** block dom을 가져옴 */ Block.prototype.getBlockMainDom = function() { return this.blockMainDom; } /** block dom을 set */ Block.prototype.setBlockMainDom = function(blockMainDom) { this.blockMainDom = blockMainDom; } /** Block Left Shadow dom을 가져옴 */ Block.prototype.getBlockLeftShadowDom = function() { var blockMainDom = this.getBlockMainDom(); return $(blockMainDom).find(VP_CLASS_PREFIX + VP_CLASS_BLOCK_LEFT_HOLDER); } /** depth를 표시하는 dom을 가져옴 */ Block.prototype.getBlockDepthInfoDom = function() { var blockMainDom = this.getBlockMainDom(); return $(blockMainDom).find(VP_CLASS_PREFIX + VP_CLASS_BLOCK_DEPTH_INFO); } /** LineNumber Info를 표시하는 dom을 가져옴 */ Block.prototype.getBlockLineNumberInfoDom = function() { var blockMainDom = this.getBlockMainDom(); return $(blockMainDom).find(VP_CLASS_PREFIX + VP_CLASS_BLOCK_NUM_INFO); } /** Block Option Dom을 표시하는 dom */ Block.prototype.getBlockOptionPageDom = function() { return this.blockOptionPageDom; } Block.prototype.setBlockOptionPageDom = function(blockOptionPageDom) { this.blockOptionPageDom = blockOptionPageDom; } /** 현재 root 블럭부터 하위 depth 자식 블럭리스트(동일 depth 블럭 제거) 들을 전부 가져오고, * 가져온 block들의 정보를 가지고 html dom을 만들어 return 한다. * 블럭을 이동할 때 보여지는 dom을 생성 */ Block.prototype.makeMovedBlockDom = function() { var blockContainerThis = this.getBlockContainerThis(); var childBlockDomList = []; var rootDepth = 0; var $_boardPage = blockContainerThis.getBoardPage_$(); var $_blockNewMainDom = null; var blockList_thisBlockArea = this.getBlockList_thisBlockArea(); var firstBlock = blockList_thisBlockArea[0]; /** 첫번째 블럭 dom 생성*/ if (firstBlock) { rootDepth = firstBlock.calculateDepthAndGet(); var blockMainDom = blockContainerThis.makeBlockDom(firstBlock, false); $_blockNewMainDom = $(blockMainDom); $_blockNewMainDom.css(STR_POSITION, STR_ABSOLUTE); $_boardPage.append($_blockNewMainDom); } /** 두번째 이후 블럭 dom 생성 * 첫번째 블럭이 node 블럭이고 * toggle 된 상태면 두번째 이후 블럭 생성 안함 */ if (firstBlock.getBlockType() != BLOCK_CODELINE_TYPE.NODE) { blockList_thisBlockArea.forEach((block, index) => { if (index == 0) { return; } else { var blockMainDom = blockContainerThis.makeBlockDom(block, false); var indentPxNum = block.getIndentNumber() - (rootDepth * NUM_INDENT_DEPTH_PX); $(blockMainDom).css(STR_MARGIN_LEFT, indentPxNum); childBlockDomList.push(blockMainDom); } }); } /** stack dom을 만들고 * stack dom에 자식 dom을 넣음 * blockNewMainDom은 stack dom을 자식 dom으로 넣음 */ var $fragDom = $(document.createDocumentFragment()); childBlockDomList.forEach(childDom => { $fragDom.append(childDom); }); var $_moveChildListDom = $(`
`); $_moveChildListDom.append($fragDom); $_blockNewMainDom.append($_moveChildListDom); return $_blockNewMainDom; } /** block main dom의 width, height 값을 가져온다 */ Block.prototype.getBlockMainDomPosition = function() { var blockMainDom = this.getBlockMainDom(); var clientRect = $(blockMainDom)[0].getBoundingClientRect(); return clientRect; } // ** --------------------------- Block 멤버변수의 set, get 관련 메소드들 --------------------------- */ Block.prototype.setDepth = function(depth) { this.depth = depth; } Block.prototype.getDepth = function() { return this.depth; } Block.prototype.getBlockType = function() { return this.type; } Block.prototype.getBlockName = function() { return this.blockName; } Block.prototype.setBlockName = function(blockName) { this.blockName = blockName; } Block.prototype.getUUID = function() { return this.uuid; } Block.prototype.setUUID = function(uuid) { this.uuid = uuid; } /** * @param {ENUM} direction INDENT OR DOWN */ Block.prototype.setDirection = function(direction) { this.direction = direction; } Block.prototype.getDirection = function() { return this.direction; } Block.prototype.getChildBlockUUIDList = function() { return this.childBlockUUIDList; } Block.prototype.setChildBlockUUIDList = function(childBlockUUIDList) { this.childBlockUUIDList = childBlockUUIDList; } Block.prototype.getBlockContainerThis = function() { return this.blockContainerThis; } /** board의 class, def, if, for, try, while 블럭 이동할 때 * 순간 순간 임시로 변하는 left shadow height */ Block.prototype.getBlockLeftShadowHeight = function() { return this.blockLeftShadowHeight; } Block.prototype.setBlockLeftShadowHeight = function(blockLeftShadowHeight) { this.blockLeftShadowHeight = blockLeftShadowHeight; } // Block.prototype.getIsCtrlPressed = function() { // return this.isCtrlPressed; // } // Block.prototype.setIsCtrlPressed = function(isCtrlPressed) { // this.isCtrlPressed = isCtrlPressed; // } Block.prototype.getIsNowMoved = function() { return this.isNowMoved; } Block.prototype.setIsNowMoved = function(isNowMoved) { this.isNowMoved = isNowMoved; } /** * 코드를 생성하기위해 IndentString을 가져오는 함수 * depth 1개 당 TAB 1개 * @param {number} rootDepth 기본값 0 */ Block.prototype.getIndentString = function(rootDepth = 0) { var depth = this.getDepth() - rootDepth; var indentString = STR_EMPTY; while (depth-- != 0) { indentString += STR_ONE_INDENT; } return indentString; } /** * 코드를 생성하기위해 IndentNumber을 가져오는 함수 * depth 1개 당 NUM_INDENT_DEPTH_PX 1개 증가 */ Block.prototype.getIndentNumber = function() { var depth = this.getDepth(); var indentPxNum = 0; while (depth-- != 0) { indentPxNum += NUM_INDENT_DEPTH_PX; } return indentPxNum; } /** node 블럭 toggle */ Block.prototype.getIsNodeBlockToggled = function() { return this.isNodeBlockToggled; } Block.prototype.setIsNodeBlockToggled = function(isNodeBlockToggled) { this.isNodeBlockToggled = isNodeBlockToggled; } /** node 블럭 title empty */ Block.prototype.getIsNodeBlockTitleEmpty = function() { return this.isNodeBlockTitleEmpty; } Block.prototype.setIsNodeBlockTitleEmpty = function(isNodeBlockTitleEmpty) { this.isNodeBlockTitleEmpty = isNodeBlockTitleEmpty; } Block.prototype.getNowCodeLine = function() { var blockType = this.getBlockType(); var codeLineStr = ''; if ( blockType == BLOCK_CODELINE_TYPE.CLASS ) { codeLineStr = GenerateClassInParamList(this); } else if ( blockType == BLOCK_CODELINE_TYPE.DEF ) { codeLineStr = GenerateDefCode(this); } else if (blockType == BLOCK_CODELINE_TYPE.IF) { codeLineStr = GenerateIfCode(this, BLOCK_CODELINE_TYPE.IF); } else if (blockType == BLOCK_CODELINE_TYPE.ELIF) { codeLineStr = GenerateIfCode(this, BLOCK_CODELINE_TYPE.ELIF); } else if (blockType == BLOCK_CODELINE_TYPE.FOR) { codeLineStr = GenerateForCode(this); } else if (blockType == BLOCK_CODELINE_TYPE.WHILE) { codeLineStr = GenerateWhileCode(this); } else if (blockType == BLOCK_CODELINE_TYPE.EXCEPT) { codeLineStr = GenerateExceptCode(this); } else if (blockType == BLOCK_CODELINE_TYPE.RETURN) { codeLineStr = GenerateReturnCode(this); } else if (blockType == BLOCK_CODELINE_TYPE.LAMBDA) { codeLineStr = GenerateLambdaCode(this); } else if (blockType == BLOCK_CODELINE_TYPE.IMPORT) { codeLineStr = ShowImportListAtBlock(this); } else if (blockType == BLOCK_CODELINE_TYPE.BREAK || blockType == BLOCK_CODELINE_TYPE.CONTINUE || blockType == BLOCK_CODELINE_TYPE.PASS || blockType == BLOCK_CODELINE_TYPE.CODE || blockType == BLOCK_CODELINE_TYPE.COMMENT || blockType == BLOCK_CODELINE_TYPE.PRINT || blockType == BLOCK_CODELINE_TYPE.PROPERTY || blockType == BLOCK_CODELINE_TYPE.TEXT || blockType == BLOCK_CODELINE_TYPE.API || blockType == BLOCK_CODELINE_TYPE.NODE ) { codeLineStr = this.getState(STATE_codeLine); } return codeLineStr; } /** code를 생성하는 메소드 * @param {string} indentString * @param {boolean} isRun */ Block.prototype.setCodeLineAndGet = function(indentString = STR_EMPTY, isRun = false) { var thisBlock = this; var codeLine = indentString; var blockName = this.getBlockName(); var type = thisBlock.getBlockType(); switch (type) { case BLOCK_CODELINE_TYPE.NODE: { if (thisBlock.getIsNodeBlockTitleEmpty() == true) { codeLine += `# [Visual Python] Node ${thisBlock.getBlockNumber()}`; } else { codeLine += `# [Visual Python] Node ${thisBlock.getBlockNumber()} : ${thisBlock.getState(STATE_codeLine)}`; } break; } case BLOCK_CODELINE_TYPE.TEXT: case BLOCK_CODELINE_TYPE.API: { // console.log('isRun',isRun); vpCommon.setIsAPIListRunCode(isRun); /** API List Pakage가 없을 때 */ if (!thisBlock.getImportPakage()) { // console.log('no api list package'); // return 'BREAK_RUN'; // TODO: 없으면 기존 metadata의 code로 처리 codeLine += GenerateApiCode(thisBlock); } else { var apiListPackage = thisBlock.getImportPakage(); var generatedCode = apiListPackage.generateCode(false, false); var apicode = ''; /** API List 에서 generatedCode가 없을 때 */ if (!generatedCode) { generatedCode = ''; } else { apicode = MapNewLineStrToIndentString( generatedCode, indentString); } apiListPackage.metaSave(); apiListPackage.metaGenerate(); /** API List 에서 BREAK_RUN을 리턴할 때 */ if (apicode.indexOf('BREAK_RUN') != -1) { return 'BREAK_RUN'; } else { codeLine += apicode; } } break; } //class case BLOCK_CODELINE_TYPE.CLASS: { codeLine += `${blockName.toLowerCase()}`; codeLine += STR_ONE_SPACE; codeLine += thisBlock.getState(STATE_className); codeLine += GenerateClassInParamList(thisBlock); break; } //def case BLOCK_CODELINE_TYPE.DEF: { codeLine += `${blockName.toLowerCase()}`; codeLine += STR_ONE_SPACE; codeLine += thisBlock.getState(STATE_defName); codeLine += GenerateDefCode(thisBlock); break; } //if case BLOCK_CODELINE_TYPE.IF: { codeLine += `${blockName.toLowerCase()}`; codeLine += STR_ONE_SPACE; codeLine += GenerateIfCode(thisBlock, BLOCK_CODELINE_TYPE.IF); codeLine += `:`; break; } //for case BLOCK_CODELINE_TYPE.FOR: { codeLine += `${blockName.toLowerCase()}`; codeLine += STR_ONE_SPACE; codeLine += GenerateForCode(thisBlock); codeLine += `:`; break; } //while case BLOCK_CODELINE_TYPE.WHILE: { codeLine += `${blockName.toLowerCase()}`; codeLine += STR_ONE_SPACE; codeLine += GenerateWhileCode(thisBlock); codeLine += `:`; break; } /** import */ case BLOCK_CODELINE_TYPE.IMPORT: { codeLine += GenerateImportCode(thisBlock, indentString); break; } /** try */ case BLOCK_CODELINE_TYPE.TRY: { codeLine += `${blockName.toLowerCase()}:`; break; } /** return */ case BLOCK_CODELINE_TYPE.RETURN: { codeLine += `${blockName.toLowerCase()} `; codeLine += GenerateReturnCode(thisBlock); break; } /** break */ case BLOCK_CODELINE_TYPE.BREAK: { codeLine += MapNewLineStrToIndentString(thisBlock.getState(STATE_codeLine), indentString); break; } /** continue */ case BLOCK_CODELINE_TYPE.CONTINUE: { codeLine += MapNewLineStrToIndentString(thisBlock.getState(STATE_codeLine), indentString); break; } /** pass */ case BLOCK_CODELINE_TYPE.PASS: { codeLine += MapNewLineStrToIndentString(thisBlock.getState(STATE_codeLine), indentString); break; } /** elif */ case BLOCK_CODELINE_TYPE.ELIF: { codeLine += `${blockName.toLowerCase()}`; codeLine += STR_ONE_SPACE; codeLine += GenerateIfCode(thisBlock, BLOCK_CODELINE_TYPE.ELIF); codeLine += `:`; break; } /** else */ case BLOCK_CODELINE_TYPE.ELSE: { codeLine += `${blockName.toLowerCase()}:`; break; } /** for else */ case BLOCK_CODELINE_TYPE.FOR_ELSE: { codeLine += `${blockName.toLowerCase()}:`; break; } /** except */ case BLOCK_CODELINE_TYPE.EXCEPT: { codeLine += `${blockName.toLowerCase()}`; codeLine += STR_ONE_SPACE; codeLine += GenerateExceptCode(thisBlock); codeLine += `:`; break; } /** finally */ case BLOCK_CODELINE_TYPE.FINALLY: { codeLine += `${blockName.toLowerCase()}:`; break; } /** code */ case BLOCK_CODELINE_TYPE.CODE: { codeLine += MapNewLineStrToIndentString(thisBlock.getState(STATE_codeLine), indentString); break; } case BLOCK_CODELINE_TYPE.PROPERTY: { codeLine += '@'; codeLine += MapNewLineStrToIndentString(thisBlock.getState(STATE_codeLine), indentString); break; } case BLOCK_CODELINE_TYPE.LAMBDA: { codeLine += GenerateLambdaCode(thisBlock); break; } case BLOCK_CODELINE_TYPE.COMMENT: { codeLine += '#'; codeLine += thisBlock.getState(STATE_codeLine).replace(/(\r\n\t|\n|\r\t)/gm,"\n#"); break; } case BLOCK_CODELINE_TYPE.PRINT: { codeLine += `${blockName.toLowerCase()}`; codeLine += `(`; codeLine += thisBlock.getState(STATE_codeLine).replace(/(\r\n\t|\n|\r\t)/gm,","); codeLine += `)`; break; } } this.codeLine = codeLine; return codeLine; } Block.prototype.getCodeLine = function() { return this.codeLine; } /** * block의 depth를 계산하고 depth 를 가져오는 함수 */ Block.prototype.calculateDepthAndGet = function() { var depth = 0; var direction = this.getDirection(); var prevBlock = this; while (prevBlock.getPrevBlock() != null) { prevBlock = prevBlock.getPrevBlock(); if (direction == BLOCK_DIRECTION.INDENT && prevBlock.getDirection() != BLOCK_DIRECTION.DOWN ) { depth++; } else { if (prevBlock.getDirection() == BLOCK_DIRECTION.INDENT) { depth++; } } } return depth; } /** * block의 width를 set, get */ Block.prototype.getWidth = function() { return this.width; } Block.prototype.setWidth = function(width) { this.width = width; } Block.prototype.setBlockNumber = function(blockNumber) { this.blockNumber = blockNumber; } Block.prototype.getBlockNumber = function() { return this.blockNumber; } // ** --------------------------- Block render 관련 메소드들 --------------------------- */ /** * @param {ENUM} opacityNum STR_BLOCK or STR_NONE * STR_BLOCK면 1 STR_NONE면 0 */ Block.prototype.renderMovedBlockListOpacity = function(opacityNum) { var str = STR_EMPTY; if (opacityNum == 0) { str = STR_NONE; } else { str = STR_BLOCK; } var childBlockList = this.getBlockList_thisBlockArea(); childBlockList.forEach(block => { var blockMainDom = block.getBlockMainDom(); $(blockMainDom).css(STR_OPACITY, opacityNum); $(blockMainDom).css(STR_DISPLAY, str); }); } /** * @param {boolean} isColor true면, false면 */ Block.prototype.renderColor_thisBlockArea = function(isColor) { /** block color 색칠 */ this.renderSelectedBlockBorderColor(isColor); this.renderSelectedBlockColor(isColor); var blockList = this.getBlockList_thisBlockArea(); blockList.forEach(block => { block.renderSelectedBlockColor(isColor); }); } /** * block 클릭시 block border 주황색으로 변경 * @param {boolean} isColor true면 #F37704, false면 transparent */ Block.prototype.renderSelectedBlockBorderColor = function(isColor) { var blockContainerThis = this.getBlockContainerThis(); /** 모든 블럭들의 border 컬러 초기화 */ var blockList = blockContainerThis.getBlockList(); blockList.forEach(block => { if (block.getBlockType() == BLOCK_CODELINE_TYPE.NODE) { $(block.getBlockMainDom()).css(STR_BORDER, '0.5px solid #C4C4C4'); } else { $(block.getBlockMainDom()).css(STR_BORDER, '1px solid transparent'); } }); if (isColor == true) { if (this.getBlockType() == BLOCK_CODELINE_TYPE.TEXT) { $(this.getBlockMainDom()).css(STR_BORDER_LEFT, '1px solid #F37704'); } else { $(this.getBlockMainDom()).css(STR_BORDER, '1px solid #F37704'); } } } /** * block 클릭시 block border 주황.색으로 변경 * @param {boolean} isReset true면 기본 컬러, false면 색칠된 컬러 */ Block.prototype.renderSelectedBlockColor = function(isReset) { var blockMainDom = this.getBlockMainDom(); var blockType = this.getBlockType(); if (blockType == BLOCK_CODELINE_TYPE.SHADOW ) { return; } if (isReset == true) { $(blockMainDom).css(STR_DISPLAY, STR_FLEX); } /** class & def 블럭 */ if ( IsDefineBlockType(blockType) == true) { if (isReset == true) { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_CLASS_DEF_STRONG); } else { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_CLASS_DEF); } } else if ( blockType == BLOCK_CODELINE_TYPE.IF || blockType == BLOCK_CODELINE_TYPE.FOR || blockType == BLOCK_CODELINE_TYPE.WHILE || blockType == BLOCK_CODELINE_TYPE.TRY || blockType == BLOCK_CODELINE_TYPE.ELSE || blockType == BLOCK_CODELINE_TYPE.ELIF || blockType == BLOCK_CODELINE_TYPE.FOR_ELSE || blockType == BLOCK_CODELINE_TYPE.EXCEPT || blockType == BLOCK_CODELINE_TYPE.FINALLY || blockType == BLOCK_CODELINE_TYPE.LAMBDA || blockType == BLOCK_CODELINE_TYPE.IMPORT || blockType == BLOCK_CODELINE_TYPE.PROPERTY || blockType == BLOCK_CODELINE_TYPE.RETURN ) { if (isReset == true) { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_CONTROL_STRONG); } else { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_CONTROL); } } else if (blockType == BLOCK_CODELINE_TYPE.API) { if (isReset == true) { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_API_STRONG); } else { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_API); } } else if (blockType == BLOCK_CODELINE_TYPE.TEXT ) { $(blockMainDom).css(STR_BACKGROUND_COLOR, STR_TRANSPARENT); } else if (blockType == BLOCK_CODELINE_TYPE.NODE) { } else { if (isReset == true) { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_CODE_STRONG); } else { $(blockMainDom).css(STR_BACKGROUND_COLOR, COLOR_CODE); } } } /** block shadow를 보여주거나 감추는 메소드 */ Block.prototype.renderBlockShadow = function(NONE_OR_BLOCK) { var blockType = this.getBlockType(); /** 아래 shadow */ if ( IsCanHaveIndentBlock(blockType) == true ) { var childBlock_down = this.getChildBlock_down(); var blockMainDom = childBlock_down.getBlockMainDom(); $( blockMainDom ).css(STR_DISPLAY, NONE_OR_BLOCK); } /** 왼쪽 shadow */ var blockLeftHolderDom = this.getBlockLeftShadowDom(); $(blockLeftHolderDom).css(STR_DISPLAY, NONE_OR_BLOCK); } /** * Block Left Holder dom의 height 계산 */ Block.prototype.renderBlockLeftShadowHeight = function() { var leftHolderClientRect = $(this.getBlockLeftShadowDom())[0].getBoundingClientRect(); var blockType = this.getBlockType(); if ( IsCanHaveIndentBlock(blockType) == true) { var childBlock_down = this.getChildBlock_down(); var blockMainDom = childBlock_down.getBlockMainDom(); var childBlock_downBlockClientRect = $(blockMainDom)[0].getBoundingClientRect(); var distance = childBlock_downBlockClientRect.y - leftHolderClientRect.y; $(this.getBlockLeftShadowDom()).css(STR_HEIGHT, distance); this.setBlockLeftShadowHeight(distance); } } /** * node 블럭의 input 상태를 변경하는 메소드 * block이면 input 태그 형식 -> node 블럭의 이름을 변경할 수 있는 상태 * none이면 일반 형식 * @param {string ENUM} STR_BLOCK_OR_NONE block or none */ Block.prototype.renderNodeBlockInput = function(STR_BLOCK_OR_NONE) { var blockUUID = this.getUUID(); if (STR_BLOCK_OR_NONE == STR_BLOCK) { $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_NODEBLOCK_INPUT + blockUUID).css(STR_DISPLAY, STR_BLOCK); $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_NODEBLOCK_TEXT_CONTAINER + blockUUID).css(STR_DISPLAY, STR_NONE); } else { $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_NODEBLOCK_INPUT + blockUUID).css(STR_DISPLAY, STR_NONE); $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_NODEBLOCK_TEXT_CONTAINER + blockUUID).css(STR_DISPLAY, STR_BLOCK); } } /** node 블럭을 렌더링할 때 * text 영역과 blank 영역 구분해서 width를 계산하고 렌더링 * * ex) text 영역에 Node라고 쓰여있으면, * 4글자 써있으므로 4 x 8 = 32px로 계산해 text 영역의 width를 계산하고 나머지 blank 영역을 계산 * @option * @param {number} blockMaxWidth resize할 때만 인자로 받는다 */ Block.prototype.renderNodeBlockWidth = function(blockMaxWidth) { var codeStr = this.getState(STATE_codeLine); var codeStrLength = codeStr.length; var nodeBlockTextWidth = 0; while(codeStrLength-- != 0) { nodeBlockTextWidth += 8; } var blockMainDom = this.getBlockMainDom(); var nodeBlockBlankWidth = 0; /** visual python 전체 혹은 option을 resize 할 때만 */ if (blockMaxWidth) { if (nodeBlockTextWidth > (blockMaxWidth * 0.70)) { $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text-container').css('overflow', 'hidden'); } else { $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text-container').css('overflow', 'visible'); } nodeBlockBlankWidth = blockMaxWidth - nodeBlockTextWidth - 25; /** node 블럭을 생성한 경우나 * node 블럭의 input 태그에 title 입력하고 enter를 누를 경우 */ } else { var thisBlockWidth = this.getWidth(); if (nodeBlockTextWidth > (thisBlockWidth * 0.70)) { $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text-container').css('overflow', 'hidden'); } else { $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text-container').css('overflow', 'visible'); } nodeBlockBlankWidth = thisBlockWidth - nodeBlockTextWidth; } $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text-blank').css(STR_WIDTH, nodeBlockBlankWidth); } /** * Block Type에 맵핑되는 Option을 Option tab에 렌더링하는 html 함수 */ Block.prototype.renderOptionPage = function(resetOptionPage=false) { var thisBlock = this; var blockContainerThis = this.getBlockContainerThis(); var optionPageSelector = blockContainerThis.getOptionPageSelector(); var blockUUID = this.getUUID(); var blockOptionPageDom = blockContainerThis.getOptionDom(blockUUID); var blockType = thisBlock.getBlockType(); switch(blockType) { /** class */ case BLOCK_CODELINE_TYPE.CLASS: { blockOptionPageDom = InitClassBlockOption(thisBlock, optionPageSelector); break; } /** def */ case BLOCK_CODELINE_TYPE.DEF: { blockOptionPageDom = InitDefBlockOption(thisBlock, optionPageSelector); break; } /** if */ case BLOCK_CODELINE_TYPE.IF: { blockOptionPageDom = InitIfBlockOption(thisBlock, optionPageSelector); break; } /** elif */ case BLOCK_CODELINE_TYPE.ELIF: { blockOptionPageDom = InitElifBlockOption(thisBlock, optionPageSelector); break; } /** else */ case BLOCK_CODELINE_TYPE.ELSE: { blockOptionPageDom = InitNoneOption(thisBlock, optionPageSelector); break; } /** for */ case BLOCK_CODELINE_TYPE.FOR: { blockOptionPageDom = InitForBlockOption(thisBlock, optionPageSelector); break; } /** while */ case BLOCK_CODELINE_TYPE.WHILE: { blockOptionPageDom = InitWhileBlockOption(thisBlock, optionPageSelector); break; } /** Try */ case BLOCK_CODELINE_TYPE.TRY: { blockOptionPageDom = InitNoneOption(thisBlock, optionPageSelector); break; } /** Except */ case BLOCK_CODELINE_TYPE.EXCEPT: { blockOptionPageDom = InitExceptBlockOption(thisBlock, optionPageSelector); break; } /** Try */ case BLOCK_CODELINE_TYPE.FINALLY: { blockOptionPageDom = InitNoneOption(thisBlock, optionPageSelector); break; } /** Return */ case BLOCK_CODELINE_TYPE.RETURN : { blockOptionPageDom = InitReturnBlockOption(thisBlock, optionPageSelector); break; } /** Code block */ case BLOCK_CODELINE_TYPE.CODE: { blockOptionPageDom = InitCodeBlockOption(thisBlock, optionPageSelector); break; } /** Break block */ case BLOCK_CODELINE_TYPE.BREAK: { blockOptionPageDom = InitCodeBlockOption(thisBlock, optionPageSelector); break; } /** Continue block */ case BLOCK_CODELINE_TYPE.CONTINUE: { blockOptionPageDom = InitCodeBlockOption(thisBlock, optionPageSelector); break; } /** Property block */ case BLOCK_CODELINE_TYPE.PROPERTY: { blockOptionPageDom = InitCodeBlockOption(thisBlock, optionPageSelector); break; } /** Pass block */ case BLOCK_CODELINE_TYPE.PASS: { blockOptionPageDom = InitCodeBlockOption(thisBlock, optionPageSelector); break; } /** Lambda block */ case BLOCK_CODELINE_TYPE.LAMBDA: { blockOptionPageDom = InitLambdaBlockOption(thisBlock, optionPageSelector); break; } /** Comment block */ case BLOCK_CODELINE_TYPE.COMMENT: { blockOptionPageDom = InitCodeBlockOption(thisBlock, optionPageSelector); break; } /** Print block */ case BLOCK_CODELINE_TYPE.PRINT: { blockOptionPageDom = InitCodeBlockOption(thisBlock, optionPageSelector); break; } /** Api block */ case BLOCK_CODELINE_TYPE.API: { InitAPIBlockOption(thisBlock, optionPageSelector, resetOptionPage); break; } /** Import block */ case BLOCK_CODELINE_TYPE.IMPORT: { blockOptionPageDom = InitImportBlockOption(thisBlock, optionPageSelector); break; } /** Node block */ case BLOCK_CODELINE_TYPE.NODE: { blockOptionPageDom = InitNodeBlockOption(thisBlock, optionPageSelector); break; } /** Text block */ case BLOCK_CODELINE_TYPE.TEXT: { blockOptionPageDom = InitTextBlockOption(thisBlock, optionPageSelector); break; } } if (blockType == BLOCK_CODELINE_TYPE.API) { // set navigation blockContainerThis.setNavigator(blockType, thisBlock.state.customCodeLine); return; } else if (blockType == BLOCK_CODELINE_TYPE.NODE) { // set navigation blockContainerThis.setNavigator(blockType, thisBlock.state.customCodeLine); } else { // set navigation blockContainerThis.setNavigator(blockType, thisBlock.getBlockName()); } blockContainerThis.setOptionDom(blockUUID, blockType, blockOptionPageDom); blockContainerThis.reRenderOptionDomPool(blockOptionPageDom); } Block.prototype.renderApiOption = function() { var thisBlock = this; var blockContainerThis = this.getBlockContainerThis(); var optionPageSelector = blockContainerThis.getOptionPageSelector(); var blockUUID = this.getUUID(); var blockOptionPageDom = blockContainerThis.getOptionDom(blockUUID); var blockType = thisBlock.getBlockType(); LoadAPIBlockOption(thisBlock, optionPageSelector); } Block.prototype.resetOptionPage = function() { var blockContainerThis = this.getBlockContainerThis(); var blockUUID = this.getUUID(); var blockType = this.getBlockType(); var optionPageSelector = blockContainerThis.getOptionPageSelector(); var blockOptionPageDom = InitNoneOption(this, optionPageSelector); blockContainerThis.setOptionDom(blockUUID, blockType, blockOptionPageDom); blockContainerThis.reRenderOptionDomPool(blockOptionPageDom); } // ** --------------------------- Block state 관련 메소드들 --------------------------- */ Block.prototype.setState = function(newState) { this.state = { ...this.state , ...newState } // this.setBlockTitle(); } /**특정 state Name 값을 가져오는 함수 @param {string} stateKeyName */ Block.prototype.getState = function(stateKeyName) { return this.state[stateKeyName]; } /** 변경된 codeline state를 html title로 set */ Block.prototype.setBlockTitle = function() { var codeLine = this.setCodeLineAndGet(); // console.log('codeLine',codeLine); var blockMainDom = this.getBlockMainDom(); $(blockMainDom).attr('title', codeLine); } /** * block의 left holder의 height를 계산하고 height를 set */ Block.prototype.calculateLeftHolderHeightAndSet = function() { var blockHeight = 0; // var blockType = this.getBlockType(); // if ( blockType == BLOCK_CODELINE_TYPE.CLASS // || blockType == BLOCK_CODELINE_TYPE.DEF) { // blockHeight += NUM_BLOCK_BOTTOM_HOLDER_HEIGHT; // } var childBlockList = this.getBlockList_thisBlockArea(); childBlockList.forEach(childBlock => { if (childBlock.getBlockType() == BLOCK_CODELINE_TYPE.SHADOW) { blockHeight += NUM_BLOCK_BOTTOM_HOLDER_HEIGHT; } else { blockHeight += NUM_BLOCK_HEIGHT_PX; } }); this.setBlockLeftShadowHeight(blockHeight); } /** ---------------------------이벤트 함수 바인딩--------------------------- */ Block.prototype.bindEventAll = function() { var blockType = this.getBlockType(); /** elif else except finally 블럭일 경우 Drag 이동 금지 */ if ( IsElifElseExceptFinallyBlockType(blockType) == true ) { this.bindClickEvent(); this.bindLineNumberEvent(); } else if (blockType == BLOCK_CODELINE_TYPE.TEXT) { /** text 블럭일 경우 */ this.bindDragEvent(); // 추가: Text Block Drag 가능하도록 this.bindClickEvent(); this.bindLineNumberEvent(); /** node 블럭일 경우 */ } else if (blockType == BLOCK_CODELINE_TYPE.NODE) { // this.bindClickEvent(); this.bindDragEvent(); this.bindNodeBlockClickEvent_text(); this.bindNodeBlockClickEvent_blank(); this.bindLineNumberEvent(); /** group 블럭일 경우 */ } else if (this.isGroupBlock) { this.bindDragEvent(); this.bindClickEvent(); this.bindLineNumberEvent(); /** 그 외 블럭일 경우 */ } else { this.bindClickEvent(); this.bindDragEvent(); } this.bindRightClickEvent(); } /** node 블럭의 이름을 변경하는 이벤트 함수 bind */ Block.prototype.bindNodeBlockInputEvent = function() { var thisBlock = this; var blockUUID = thisBlock.getUUID(); $(document).off(STR_CHANGE_KEYUP_PASTE, vpCommon.wrapSelector(vpCommon.formatString(".{0}",VP_CLASS_APIBLOCK_NODEBLOCK_INPUT + blockUUID))); $(document).on(STR_CHANGE_KEYUP_PASTE, vpCommon.wrapSelector(vpCommon.formatString(".{0}",VP_CLASS_APIBLOCK_NODEBLOCK_INPUT + blockUUID)), function(event) { var inputValue = $(this).val(); thisBlock.setState({ [STATE_codeLine]: inputValue }); thisBlock.writeCode(inputValue); /** node 블럭 input 태그에 글을 작성하면 IsNodeBlockTitleEmpty은 false * IsNodeBlockTitleEmpty이 false면 node 블럭 주석에 node 블럭 타이틀이 표시 됨 */ thisBlock.setIsNodeBlockTitleEmpty(false); thisBlock.renderOptionPage(); thisBlock.renderNodeBlockWidth(); }); $(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_NODEBLOCK_INPUT + blockUUID).focus(); } /** node 블럭 앞 linenumber에 마우스를 hover했을 때 발생하는 이벤트와 * 클릭 했을때의 이벤트 함수 bind */ Block.prototype.bindLineNumberEvent = function() { var thisBlock = this; var blockLineNumberInfoDom = this.getBlockLineNumberInfoDom(); /** 아래 off 로직이 기존의 바인딩한 이벤트 함수 제거 */ $(blockLineNumberInfoDom).off(); /** node 블럭 hover */ if (this.getBlockType() == BLOCK_CODELINE_TYPE.NODE || this.isGroupBlock) { // hover 시작 $(blockLineNumberInfoDom).hover(function(event) { $(blockLineNumberInfoDom).html(`
`); $(blockLineNumberInfoDom).css(STR_BACKGROUND_COLOR, STR_TRANSPARENT); event.preventDefault(); // hover 해제 }, function(event) { $(blockLineNumberInfoDom).text(thisBlock.getBlockNumber()); $(blockLineNumberInfoDom).css(STR_BACKGROUND_COLOR, COLOR_LINENUMBER); event.preventDefault(); }); /** text 블럭 hover */ } else if (this.getBlockType() == BLOCK_CODELINE_TYPE.TEXT) { // hover 시작 $(blockLineNumberInfoDom).hover(function(event) { $(blockLineNumberInfoDom).find('.vp-apiblock-circle-play').css(STR_OPACITY, 1); $(blockLineNumberInfoDom).css(STR_BACKGROUND_COLOR, 'transparent'); event.preventDefault(); // hover 해제 }, function(event) { $(blockLineNumberInfoDom).find('.vp-apiblock-circle-play').css(STR_OPACITY, 0); event.preventDefault(); }); } /** click */ $(blockLineNumberInfoDom).on(STR_CLICK, function(event) { thisBlock.runThisBlock(); event.preventDefault(); event.stopPropagation(); }); } /** * @async * block click시 발생하는 이벤트 메소드 */ Block.prototype.bindClickEvent = function() { var thisBlock = this; var blockContainerThis = thisBlock.getBlockContainerThis(); var blockMainDom = thisBlock.getBlockMainDom(); var blockType = thisBlock.getBlockType(); /** 아래 off 로직이 기존의 바인딩한 이벤트 함수 제거 */ $(blockMainDom).off(STR_CLICK); $(blockMainDom).single_double_click( function(event){ /** this블럭이 text 블럭일 경우 */ if (thisBlock.getBlockType() == BLOCK_CODELINE_TYPE.TEXT) { var blockLineNumberInfoDom = thisBlock.getBlockLineNumberInfoDom(); $('.vp-apiblock-circle-play').css(STR_OPACITY, 0); $(blockLineNumberInfoDom).find('.vp-apiblock-circle-play').css(STR_OPACITY, 1); } blockContainerThis.resetBlockListAndRenderThisBlock(thisBlock); event.stopPropagation(); }, function () { // --------------------------------더블클릭-------------------------------; if (blockType != BLOCK_CODELINE_TYPE.TEXT) { /** TEXT 블럭은 더블클릭해도 아무런 작용을 하지 않음 */ thisBlock.runThisBlock(); } }); } /** * block right click - simple menu */ Block.prototype.bindRightClickEvent = function() { var thisBlock = this; var blockMainDom = thisBlock.getBlockMainDom(); $(blockMainDom).off(STR_RIGHT_CLICK); $(blockMainDom).on(STR_RIGHT_CLICK, function(event) { event.preventDefault(); thisBlock.blockContainerThis.blockMenu.show(thisBlock, event.pageX, event.pageY); }); } /** node 블럭에서 글자가 쓰여진 text영역을 click시 발생하는 이벤트 메소드 */ Block.prototype.bindNodeBlockClickEvent_text = function() { var thisBlock = this; var blockContainerThis = thisBlock.getBlockContainerThis(); var blockMainDom = thisBlock.getBlockMainDom(); /** 아래 off 로직이 기존의 바인딩한 이벤트 함수 제거 */ $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text').off(STR_CLICK); $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text').single_double_click( function(event){ //'-----------------------------------클릭--------------------------------------' /** 블럭 클릭시 모든 node 블럭의 상태를 일반 상태로 변경 */ const nodeBlockList = blockContainerThis.getNodeBlockList(); nodeBlockList.forEach(nodeBlock => { nodeBlock.renderNodeBlockInput(STR_NONE); }); var blockList_thisBlockArea = thisBlock.getBlockList_thisBlockArea_noShadowBlock(); if (thisBlock.getIsNodeBlockToggled() == true) { blockList_thisBlockArea.forEach( (block, index) => { $(block.getBlockMainDom()).removeClass('vp-apiblock-style-display-none'); }); thisBlock.setIsNodeBlockToggled(false); /** thisBlock이 toggle이 되지 않은 node 블럭의 경우 */ } else { blockList_thisBlockArea.forEach( (block, index) => { if (index != 0) { $(block.getBlockMainDom()).addClass('vp-apiblock-style-display-none'); } }); thisBlock.setIsNodeBlockToggled(true); } /** 맨위부터 스크롤바가 얼마나 떨어져있는지 가져오기 */ var scrollHeight = $(vpCommon.wrapSelector(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BOARD)).scrollTop(); /** this블럭이 node 블럭일 경우 */ if (thisBlock.getBlockType() == BLOCK_CODELINE_TYPE.NODE) { $(vpCommon.wrapSelector(VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BOARD)).animate({ scrollTop: scrollHeight }, 100); } blockContainerThis.reRenderAllBlock(); blockContainerThis.resetBlockListAndRenderThisBlock(thisBlock); }, function () { // --------------------------------더블클릭-------------------------------; thisBlock.renderNodeBlockInput(STR_BLOCK); thisBlock.writeCode(thisBlock.getState(STATE_codeLine)); thisBlock.bindNodeBlockInputEvent(); thisBlock.renderColor_thisBlockArea(true); blockContainerThis.setSelectBlock(thisBlock); }); } /** * node 블럭에서 글자가 쓰여지지 않은 blank영역 click시 발생하는 이벤트 메소드 */ Block.prototype.bindNodeBlockClickEvent_blank = function() { var thisBlock = this; var blockContainerThis = thisBlock.getBlockContainerThis(); var blockMainDom = thisBlock.getBlockMainDom(); /** 아래 off 로직이 기존의 바인딩한 이벤트 함수 제거 */ $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text-blank').off(STR_CLICK); $(blockMainDom).find(VP_CLASS_PREFIX + 'vp-apiblock-nodeblock-text-blank').single_double_click( function(event){ blockContainerThis.resetBlockListAndRenderThisBlock(thisBlock); }, function () { // --------------------------------더블클릭-------------------------------; thisBlock.renderNodeBlockInput(STR_BLOCK); thisBlock.writeCode(thisBlock.getState(STATE_codeLine)); thisBlock.bindNodeBlockInputEvent(); thisBlock.renderColor_thisBlockArea(true); blockContainerThis.setSelectBlock(thisBlock); }); } /** * block drag시 발생하는 이벤트 메소드 */ Block.prototype.bindDragEvent = function() { var thisBlock = this; var blockContainerThis = this.getBlockContainerThis(); var blockType = this.getBlockType(); var blockOldMainDom = this.getBlockMainDom(); var currCursorX = 0; var currCursorY = 0; var newPointX = 0; var newPointY = 0; var shadowBlock = null; var selectedBlock = null; var selectedBlockDirection = null; /** 제이쿼리 변수 */ var $_blockNewMainDom = null; var $_blockOldMainDom = $(blockOldMainDom); var $_boardPage = blockContainerThis.getBoardPage_$(); $_blockOldMainDom.draggable({ revert: 'invalid', revertDuration: 200, containment: VP_CLASS_PREFIX + VP_CLASS_APIBLOCK_BOARD, cursor: 'move', distance: 10, start: (event, ui) => { /** */ blockContainerThis.setFocusedPageType(FOCUSED_PAGE_TYPE.BOARD); /** 블럭 이동할 때 새로운 dom을 copy 생성 * 이 dom이 이동할 때 보여지는 dom * 이동이 끝나면 이 dom은 삭제한다. -> stop에 코드 존재 */ $_blockNewMainDom = thisBlock.makeMovedBlockDom(); /** 블럭 이동할 때 기존의 존재하던 하위 Block 들의 dom을 opacity 0 처리해 안보이게 함 * 이동이 끝나면 이 dom은 다시 opacity 1로 변경해 되어 보여짐 -> stop에 코드 존재 */ thisBlock.renderMovedBlockListOpacity(0); /** 이동하는 하위 Block 들의 isNowMoved true 처리 */ thisBlock.getBlockList_thisBlockArea().forEach(block => { block.setIsNowMoved(true); }); /** shadow block 생성 */ shadowBlock = blockContainerThis.createShadowBlock( blockType, thisBlock); blockContainerThis.reLoadBlockListLeftHolderHeight(); }, drag: async (event, ui) => { currCursorX = event.clientX; currCursorY = event.clientY; var maxWidth = blockContainerThis.getMaxWidth(); /** 블럭 드래그시 * 왼쪽 정렬 */ newPointX = currCursorX - $_boardPage.offset().left; var nodeBlockAdd = 0; if (thisBlock.getBlockType() == BLOCK_CODELINE_TYPE.NODE || thisBlock.getBlockType() == BLOCK_CODELINE_TYPE.TEXT) { nodeBlockAdd += NUM_NODE_OR_TEXT_BLOCK_MARGIN_TOP_PX; } newPointY = currCursorY - ( $_boardPage.offset().top - $_boardPage.scrollTop() ) - nodeBlockAdd; /** 이동한 블럭들의 루트블럭 x좌표가 editor 화면의 maxWidth 이상 일때 */ if (newPointX > maxWidth - $_blockNewMainDom.width()) { newPointX = maxWidth - $_blockNewMainDom.width(); } /** 이동한 블럭들의 루트블럭 y좌표가 0 이하 일때 */ if (newPointY < 0) { newPointY = 0; } $_blockNewMainDom.css(STR_TOP, newPointY + STR_PX); $_blockNewMainDom.css(STR_LEFT, newPointX + STR_PX); ({ selectedBlock, selectedBlockDirection} = blockContainerThis.dragBlock(true, thisBlock, shadowBlock, selectedBlock,selectedBlockDirection,currCursorX, currCursorY)); }, stop: function(event, ui) { // console.log('stop start'); // 화면 밖으로 나갔을 때, 재조정 var maxWidth = blockContainerThis.getMaxWidth(); var maxHeight = blockContainerThis.getMaxHeight(); var isDisappeared = false; /** 이동한 블럭들의 루트블럭 x좌표가 0 이하 일때 */ if (newPointX < 0) { newPointX = 0; isDisappeared = true; } /** 이동한 블럭들의 루트블럭 x좌표가 editor 화면의 maxWidth 이상 일때 */ if (newPointX > maxWidth - $_blockOldMainDom.width()) { newPointX = maxWidth - $_blockOldMainDom.width(); isDisappeared = true; } /** 이동한 블럭들의 루트블럭 y좌표가 0 이하 일때 */ if (newPointY < 0) { newPointY = 0; isDisappeared = true; } var nodeBlockList_asc = blockContainerThis.getNodeBlockList_asc(); selectedBlock = shadowBlock.getSelectBlock(); /** 블록이 화면 밖으로 나갈경우, 나간 블럭 전부 삭제 */ if (isDisappeared == true && !selectedBlock) { thisBlock.deleteBlock_childBlockList(); /** 블록이 화면 밖으로 나가지 않고 연결되는 경우 */ } else { if ( thisBlock.getPrevBlock() == null ) { thisBlock.removeRootBlock(); } /** 어떤 블록의 DOWN이나 INDENT로 조립되지 않는 경우 */ if (!selectedBlock) { /** 이동한 block이 rootblock일 경우 * 아무것도 안 함 */ if ( thisBlock.getPrevBlock() == null ) { if (blockContainerThis.getNodeBlockList().length == 1) { console.log('이동한 block이 rootblock일 경우 1'); $_blockNewMainDom.remove(); thisBlock.renderMovedBlockListOpacity(1); blockContainerThis.resetBlockListAndRenderThisBlock(thisBlock); return; } else { console.log('이동한 block이 rootblock일 경우 2'); var nodeBlock_last = nodeBlockList_asc[nodeBlockList_asc.length - 1]; var lastBlock = nodeBlock_last.getLastBlock_from_thisBlockArea(); selectedBlock = lastBlock; selectedBlock.appendBlock(thisBlock, BLOCK_DIRECTION.DOWN); } } /** 이동한 block의 prevBlock이 rootblock일 경우 */ else if ( thisBlock.getPrevBlock() && thisBlock.getPrevBlock().getUUID() == blockContainerThis.getRootBlock().getUUID() ) { // console.log('이동한 block이 prevBlock이 rootblock일 경우'); selectedBlock = blockContainerThis.getRootToLastBottomBlock(); selectedBlock.appendBlock(thisBlock, BLOCK_DIRECTION.DOWN); } /** 이동한 block이 rootblock이 아닐 경우 * 이동한 block의 prevBlock이 rootblock이 아닐 경우 */ else { selectedBlock = blockContainerThis.getRootToLastBottomBlock(); /** 이동한 Block의 하위 블럭 중에, Board에 놓여 있는 맨 마지막 Block이 포함되어 있는 경우 */ thisBlock.getBlockList_thisBlockArea().some(block => { if ( block.getUUID() == selectedBlock.getUUID() ) { selectedBlock = thisBlock.getPrevBlock(); return true; } }); selectedBlock.appendBlock(thisBlock, BLOCK_DIRECTION.DOWN); // set group block (except TEXT block) thisBlock.setGroupBlock(thisBlock.getBlockType() != BLOCK_CODELINE_TYPE.TEXT); } } else if (selectedBlock.getBlockType() == BLOCK_CODELINE_TYPE.TEXT) { // selectedBlock이 TEXT 블럭일 경우 selectedBlock.appendBlock(thisBlock, BLOCK_DIRECTION.DOWN); // set group block (except TEXT block) thisBlock.setGroupBlock(thisBlock.getBlockType() != BLOCK_CODELINE_TYPE.TEXT); // } else if (selectedBlockDirection == BLOCK_DIRECTION.NONE) { // /** 특정 그룹블록의 다음 그룹블럭으로 넣을 경우 */ // selectedBlock.appendBlock(thisBlock, BLOCK_DIRECTION.DOWN); // thisBlock.setGroupBlock(true); } /** 특정 블록의 DOWN이나 INDENT로 조립된 경우 */ else { // console.log('특정 블록의 DOWN이나 INDENT로 조립된 경우'); selectedBlock.appendBlock(thisBlock, selectedBlockDirection); thisBlock.setGroupBlock(false); } } /** 이동하는 하위 depth Block 들의 opacity 1로 변경 */ thisBlock.renderMovedBlockListOpacity(1); blockContainerThis.stopDragBlock(true, thisBlock); $_blockNewMainDom.remove(); shadowBlock.deleteShadowBlock(); } }); } /** * root 블럭이 이동할 때 */ Block.prototype.removeRootBlock = function() { var lastChildBlock = this.getLastBlock_from_thisBlockArea(); var childBlockList = lastChildBlock.getChildBlockList(); lastChildBlock.setChildBlockList([]); childBlockList.some(nextBlock => { nextBlock.setPrevBlock(null); nextBlock.setDirection(BLOCK_DIRECTION.ROOT); }); } /** * if, for, try 블럭 sub 버튼 생성 메소드 */ Block.prototype.createSubButton = function() { var thisBlock = this; var blockContainerThis = thisBlock.getBlockContainerThis(); var blockMainDom = thisBlock.getBlockMainDom(); var blockType = thisBlock.getBlockType(); var elseOnOffStr = STR_EMPTY; if (thisBlock.getState(STATE_isIfElse) == true) { elseOnOffStr = 'off'; } else { elseOnOffStr = 'on'; } var finallyOnOffStr = STR_EMPTY; if (thisBlock.getState(STATE_isFinally) == true) { finallyOnOffStr = 'off'; } else { finallyOnOffStr = 'on'; } /** Sub Button dom 생성 로직 */ var containerButton = $(`
`); var toggleElseButton = $(`
else ${elseOnOffStr}
`); if ( blockType == BLOCK_CODELINE_TYPE.IF || blockType == BLOCK_CODELINE_TYPE.FOR) { if (blockType == BLOCK_CODELINE_TYPE.IF) { var plusElifButton = $(`
+ elif
'`); containerButton.append(plusElifButton); } containerButton.append(toggleElseButton); } else if (blockType == BLOCK_CODELINE_TYPE.TRY) { var plusElifButton = $(`
+ except
'`); var finallyButton = $(`
finally ${finallyOnOffStr}
`); containerButton.append(plusElifButton); containerButton.append(toggleElseButton); containerButton.append(finallyButton); } $(blockMainDom).append(containerButton); /** plus elif 버튼 클릭 */ $(plusElifButton).click(function(plusElifEvent) { var blockType = thisBlock.getBlockType(); if (blockType == BLOCK_CODELINE_TYPE.IF) { blockType = BLOCK_CODELINE_TYPE.ELIF; } else { blockType = BLOCK_CODELINE_TYPE.EXCEPT; } var lastElifBlock = thisBlock.getLastElifBlock(); var lastElifBlock_childBlock_down = lastElifBlock.getChildBlock_down(); var createdBlock = blockContainerThis.createBlock(blockType); createdBlock.apply(); var elifConditionCode = GenerateIfCode(createdBlock, blockType); createdBlock.writeCode(elifConditionCode); thisBlock.setLastElifBlock(createdBlock); lastElifBlock_childBlock_down.appendBlock(createdBlock, BLOCK_DIRECTION.DOWN); blockContainerThis.reRenderAllBlock_asc(); }); /** else 버튼 클릭 */ $(toggleElseButton).click(function() { bindElseEvent(); }); /** finally 버튼 클릭 */ $(finallyButton).click(function() { bindElseEvent(true); }); var bindElseEvent = function(isFinally) { var STATE; if (isFinally == true) { STATE = STATE_isFinally; } else { STATE = STATE_isIfElse; } /** else가 존재하는 경우 -> else 삭제*/ if (thisBlock.getState(STATE) == true) { var elseBlock = thisBlock.getElseBlock(); if (elseBlock) { elseBlock.deleteBlock_childBlockList(); } thisBlock.setElseBlock(null); thisBlock.setState({ [STATE]: false }); /** else가 존재하지 않는 경우 -> else 생성*/ } else { var lastElifBlock; var createdBlock; if (isFinally == true) { var elseBlock = thisBlock.getElseBlock(); if (elseBlock) { lastElifBlock = elseBlock; } else { lastElifBlock = thisBlock.getLastElifBlock(); } createdBlock = blockContainerThis.createBlock(BLOCK_CODELINE_TYPE.FINALLY ); createdBlock.apply(); } else { lastElifBlock = thisBlock.getLastElifBlock(); createdBlock = blockContainerThis.createBlock(BLOCK_CODELINE_TYPE.ELSE ); createdBlock.apply(); } var lastElifBlock_childBlock_down = lastElifBlock.getChildBlock_down(); lastElifBlock_childBlock_down.appendBlock(createdBlock, BLOCK_DIRECTION.DOWN); thisBlock.setElseBlock(createdBlock); thisBlock.setState({ [STATE]: true }); } blockContainerThis.reRenderAllBlock_asc(); } } /** this Block 부터 코드 실행 */ Block.prototype.runThisBlock = function() { var thisBlock = this; var blockContainerThis = thisBlock.getBlockContainerThis(); /** text 블럭일 경우 */ if (thisBlock.getBlockType() == BLOCK_CODELINE_TYPE.TEXT) { thisBlock.getImportPakage().generateCode(true, true); return; } var code = ''; /** text 블럭이 아니면서 node 블럭이 아닐 경우 */ if (thisBlock.getBlockType() == BLOCK_CODELINE_TYPE.NODE || thisBlock.isGroupBlock) { // code = blockContainerThis.findNodeBlock(thisBlock).setCodeLineAndGet(); code += '# Group ' + thisBlock.getBlockNumber(); code += STR_KEYWORD_NEW_LINE; } code += blockContainerThis.makeCode(thisBlock); /** validation 걸릴 때 */ if (code.indexOf('BREAK_RUN') != -1) { return; } blockContainerThis.setAPIBlockCode(code); blockContainerThis.generateCode(true); } /** ------------------------------------------- 이하 메소드는 api list 블럭만 사용하는 메소드 -----------------------------------------------------*/ Block.prototype.setFuncID = function(funcID) { this.state.funcID = funcID; } Block.prototype.getFuncID = function() { return this.state.funcID; } Block.prototype.setOptionPageLoadCallback = function(optionPageLoadCallback) { this.optionPageLoadCallback = optionPageLoadCallback; } Block.prototype.getOptionPageLoadCallback = function() { return this.optionPageLoadCallback; } Block.prototype.setLoadOption = function(loadOption) { this.loadOption = loadOption; } Block.prototype.getLoadOption = function() { return this.loadOption; } Block.prototype.setImportPakage = function(importPakage) { this.importPakage = importPakage; } Block.prototype.getImportPakage = function() { return this.importPakage; } /** API List Block의 metadata를 vpnote에 저장할 때 실행하는 메소드 */ Block.prototype.setMetadata = function() { var blockUUID = this.getUUID(); var blockContainerThis = this.getBlockContainerThis(); var importPakage = this.getImportPakage(); // get generatedCode and save as metadata var code = importPakage.generateCode(false, false); importPakage.metaGenerate(); importPakage.metadata.code = code; blockContainerThis.removeOptionDom(blockUUID); var generatedMetaData = importPakage.metadata; var funcID = ''; /** importPakage.metadata의 ID가 있을 경우 */ if (generatedMetaData) { funcID = generatedMetaData['funcID']; /** importPakage.metadata의 ID가 없다면 importPakage.funcID를 가져옴*/ } else if (importPakage && importPakage.funcID) { funcID = importPakage.funcID; } vpContainer.loadOption(funcID, vpContainer.optionPageLoadCallback, generatedMetaData); this.state.metadata = generatedMetaData; } Block.prototype.deleteMetadata = function() { this.state.metadata = null; } Block.prototype.getMetadata = function() { return this.state.metadata; } return { Block }; });