define([ 'jquery' , 'nbextensions/visualpython/src/common/vpCommon' , 'nbextensions/visualpython/src/common/constant' , 'nbextensions/visualpython/src/common/StringBuilder' , 'nbextensions/visualpython/src/common/vpXMLHandler' , 'nbextensions/visualpython/src/common/component/vpAccordionBox' , 'nbextensions/visualpython/src/common/component/vpLineNumberTextArea' , 'nbextensions/visualpython/src/common/component/vpIconInputText' , 'nbextensions/visualpython/src/common/vpFuncJS' , './constData.js' ], function ( $, vpCommon, vpConst, sb, xmlHandler, vpAccordionBox, vpLineNumberTextArea,vpIconInputText, vpFuncJS , constData ) { const { BLOCK_CODELINE_TYPE , VP_ID_APIBLOCK_LEFT_TAB_API , VP_CLASS_APIBLOCK_OPTION_TAB_SELECTOR } = constData; let xmlLibraries; let loadedFuncJS; let generatedMetaData; let loadedFuncID; var events; let librarySearchComplete = new Array(); let searchBoxUUID; let block_closure; try { // events 에 대한 예외 발생 가능할 것으로 예상. // console.log('requirejs',requirejs); events = requirejs('base/js/events'); } catch (err) { if (window.events === undefined) { var Events = function () { }; window.events = $([new Events()]); } events = window.events; } // mathjaxutils 경로 문제 처리 - notebook 패키지 버전 6.1.6부터는 base/js로 이동됨 try { // 6.1.6 version path for mathjaxutils mathjaxutils = requirejs('base/js/mathjaxutils'); } catch (err) { // console.log('behind 6.1.6... reload mathjaxutils.'); mathjaxutils = requirejs('notebook/js/mathjaxutils') } var getOptionPageURL = function(funcID) { // console.log('funcID', funcID); var sbURL = new sb.StringBuilder(); sbURL.append(Jupyter.notebook.base_url); sbURL.append(vpConst.BASE_PATH); sbURL.append(vpConst.SOURCE_PATH); // 함수 경로 바인딩 var optionData = $(xmlLibraries.getXML()).find(vpConst.LIBRARY_ITEM_TAG + "[" + vpConst.LIBRARY_ITEM_ID_ATTR + "=" + funcID + "]"); var filePath = $(optionData).find(vpConst.LIBRARY_ITEM_FILE_URL_NODE).text(); sbURL.append(filePath); return sbURL.toString(); } var addAutoCompleteItem = function(item) { // 이미 등록된 항목은 제외한다. if (!librarySearchComplete.includes(item)) { librarySearchComplete.push(item); } } var setLibraryLoadComplete = function() { libraryLoadComplete = true; events.trigger('library_load_complete.vp'); } var librariesBind = function(node, container) { $(node).children(vpConst.LIBRARY_ITEM_TAG + "[" + vpConst.LIBRARY_ITEM_TYPE_ATTR + "=" + vpConst.LIBRARY_ITEM_TYPE_PACKAGE + "]").each(function() { var thisNode = $(this); var accboxTopGrp; if ($(thisNode).attr(vpConst.LIBRARY_ITEM_DEPTH_ATTR) == 0) { accboxTopGrp = makeLibraryTopGroupBox($(thisNode)); $(container).append(accboxTopGrp.toTagString()); } }); } var bindSearchAutoComplete = function() { $(xmlLibraries.getXML()).find(vpConst.LIBRARY_ITEM_TAG + "[" + vpConst.LIBRARY_ITEM_TYPE_ATTR + "=" + vpConst.LIBRARY_ITEM_TYPE_FUNCTION + "]").each(function() { addAutoCompleteItem($(this).attr(vpConst.LIBRARY_ITEM_NAME_ATTR)); $(this).attr(vpConst.LIBRARY_ITEM_TAG_ATTR).split(",").forEach(function(tag) { addAutoCompleteItem(tag.trim()); }); }); $(vpCommon.wrapSelector(vpCommon.formatString(".{0} input", searchBoxUUID))).autocomplete({ source: librarySearchComplete , classes: { "ui-autocomplete": "vp-search-autocomplete" } }); } var libraryLoadCallback = function(container) { setLibraryLoadComplete(); librariesBind($(xmlLibraries.getXML()).children(vpConst.LIBRARY_ITEM_WRAP_NODE), container); bindSearchAutoComplete(); }; var loadLibraries = function(container) { var libraryURL = window.location.origin + vpConst.PATH_SEPARATOR + vpConst.BASE_PATH + vpConst.DATA_PATH + vpConst.VP_LIBRARIES_XML_URL; xmlLibraries = new xmlHandler.VpXMLHandler(libraryURL); xmlLibraries.loadFile(libraryLoadCallback, container); /** 추가 */ return xmlLibraries; } var loadLibrariesToJson = function(callback, param) { var libraryURL = window.location.origin + vpConst.PATH_SEPARATOR + vpConst.BASE_PATH + vpConst.DATA_PATH + vpConst.VP_LIBRARIES_XML_URL; xmlLibraries = new xmlHandler.VpXMLHandler(libraryURL); param = xmlLibraries; xmlLibraries.loadFile(callback, param); } /** * 최상위 패키지는 아코디언 박스로 생성한다. * @param {xmlNode} topGrpNode 최상위 페키지 */ var makeLibraryTopGroupBox = function(topGrpNode) { var accBox = new vpAccordionBox.vpAccordionBox($(topGrpNode).attr(vpConst.LIBRARY_ITEM_NAME_ATTR), false, true); // 추가 클래스 설정 // accBox.addClass(vpConst.ACCORDION_GRAY_BGCOLOR); accBox.addClass(vpConst.ACCORDION_SMALL_ARROW); // 속성 부여 accBox.addAttribute(vpConst.LIBRARY_ITEM_DATA_ID, $(topGrpNode).attr(vpConst.LIBRARY_ITEM_ID_ATTR)); // 자식 그룹 노드 생성 accBox.appendContent(makeLibraryListsGroupNode(topGrpNode)); return accBox; } /** * api list 그룹 하위 표시 토글 * @param {object} trigger 이벤트 트리거 객체 */ var toggleApiListSubGroupShow = function(trigger) { $(trigger).parent().children(vpCommon.formatString("li.{0}", vpConst.LIST_ITEM_LIBRARY_GROUP)).not($(trigger)).find(vpCommon.formatString(".{0}", vpConst.LIST_ITEM_LIBRARY)).hide(); $(trigger).children(vpCommon.formatString(".{0}", vpConst.LIST_ITEM_LIBRARY)).toggle(); // 하이라이트 처리 $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", VP_ID_APIBLOCK_LEFT_TAB_API))) .find(vpCommon.formatString(".{0}", vpConst.COLOR_FONT_ORANGE)) .not($(trigger)).removeClass(vpConst.COLOR_FONT_ORANGE); $(trigger).addClass(vpConst.COLOR_FONT_ORANGE); } /** * 옵션 페이지 로드 완료 callback. * @param {funcJS} funcJS 옵션 js 객체 */ var optionPageLoadCallback_block = function(funcJS) { /** --------------- 기존의 optionPageLoadCallback코드 에서 변경한 코드 --------------------- */ /** 블럭이 package를 지역변수로 잡음 * package(== funcJS)는 API List(common, numpy, pandas... 등등) 혹은 Markdown 을 의미 */ block_closure.setImportPakage(funcJS); const importPakage = block_closure.getImportPakage(); /** Text 블럭일 경우 */ if (block_closure.getBlockType() == BLOCK_CODELINE_TYPE.TEXT) { /** Markdown package와 Text 블럭이 데이터를 통신하기 위해 서로의 this 포인트를 지역변수로 참조 함*/ importPakage.setBlock(block_closure); } /** ---------------- 이하 기존의 optionPageLoadCallback코드 -----------------------------*/ $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", vpConst.OPTION_LOAD_AREA))).empty(); $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", vpConst.OPTION_NAVIGATOR_INFO_PANEL), vpCommon.formatString(".{0}", vpConst.OPTION_NAVIGATOR_INFO_NODE))).remove(); $(vpCommon.wrapSelector(vpConst.OPTION_CONTAINER)).children(vpConst.OPTION_PAGE).remove(); // load 옵션 변경시 기존 옵션 이벤트 언바인드 호출. if (loadedFuncJS != undefined) { // loadedFuncJS.unbindOptionEvent(); } loadedFuncJS = funcJS; var naviInfoTag = makeOptionPageNaviInfo($(xmlLibraries.getXML()).find(vpConst.LIBRARY_ITEM_TAG + "[" + vpConst.LIBRARY_ITEM_ID_ATTR + "=" + loadedFuncID + "]")); $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", vpConst.OPTION_NAVIGATOR_INFO_PANEL))).append(naviInfoTag); // metadata 존재하면 load if (loadedFuncJS.metadata !== undefined && loadedFuncJS.metadata != "") { loadedFuncJS.loadMeta(loadedFuncJS, generatedMetaData); } var blockOptionPageDom = makeUpGreenRoomHTML(); /** --------------- 다시 기존의 optionPageLoadCallback코드 에서 변경한 코드 --------------------- */ block_closure.setBlockOptionPageDom(blockOptionPageDom); block_closure.renderOptionPage(); loadedFuncJS.bindOptionEvent(); } /** * 그룹 노드 리스트 아이템으로 생성 * @param {xmlNode} grpNode 그룹 노드 */ var makeLibraryListsGroupNode = function(grpNode) { var sbGrpNode = new sb.StringBuilder(); sbGrpNode.appendLine(makeLibraryListsFunctionNode(grpNode)); sbGrpNode.appendFormatLine(""); return sbGrpNode.toString(); } /** * 함수 노드 리스트 아이템으로 생성 * @param {xmlNode} grpNode 그룹 노드 */ var makeLibraryListsFunctionNode = function(grpNode) { var sbFuncNode = new sb.StringBuilder(); sbFuncNode.appendFormatLine(""); return sbFuncNode.toString(); } /** * api list library 검색 * @param {String} keyword 검색어 */ var searchAPIList = function(keyword) { alert(vpCommon.formatString("search keyword > {0}", keyword)); } /** * 옵션 페이지 로드 * @param {String} funcID xml 함수 id * @param {function} callback 로드 완료시 실행할 함수 * @param {JSON} metadata 메타데이터 */ var loadOption_block = function(funcID, callback, metadata) { var loadUrl = ''; if (funcID == 'com_markdown') { loadUrl = 'markdown/markdown.js'; } else { loadUrl = getOptionPageURL(funcID); } // var loadUrl = getOptionPageURL(funcID); // 옵션 페이지 url 로딩이 정상처리 된 경우 js 파일 로드 if (loadUrl !== "") { // 옵션 로드 loadedFuncID = funcID; generatedCode = undefined; generatedMetaData = metadata; requirejs([loadUrl], function (loaded) { loaded.initOption(callback, metadata); }); } } var loadOption_textBlock = function(funcID, callback, metadata) { var loadUrl = '/nbextensions/visualpython/src/markdown/markdown.js'; // 옵션 페이지 url 로딩이 정상처리 된 경우 js 파일 로드 if (loadUrl !== "") { // 옵션 로드 loadedFuncID = funcID; generatedCode = undefined; generatedMetaData = metadata; requirejs([loadUrl], function (loaded) { loaded.initOption(callback, metadata); }); } } /** * 로드된 함수 경로 바인딩 * @param {xmlNode} node node for binding */ var makeOptionPageNaviInfo = function(node) { var sbNaviInfo = new sb.StringBuilder(); if ($(node).parent().attr(vpConst.LIBRARY_ITEM_DEPTH_ATTR) !== undefined) { sbNaviInfo.appendLine(makeOptionPageNaviInfo($(node).parent())); } sbNaviInfo.appendFormatLine("{3}" , vpConst.OPTION_NAVIGATOR_INFO_NODE, vpConst.LIBRARY_ITEM_DATA_ID, $(node).attr(vpConst.LIBRARY_ITEM_ID_ATTR), $(node).attr(vpConst.LIBRARY_ITEM_NAME_ATTR)); return sbNaviInfo.toString(); } /** * 로딩된 옵션 페이지 html 처리 */ var makeUpGreenRoomHTML = function() { var that; $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", vpConst.OPTION_GREEN_ROOM), vpCommon.formatString(".{0}", vpConst.API_OPTION_PAGE))).each(function() { $(this).find("h4:eq(0)").hide(); $(this).find("hr:eq(0)").hide(); $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", vpConst.OPTION_LOAD_AREA))).html($(this)); $(vpCommon.wrapSelector(vpCommon.formatString(".{0}", VP_CLASS_APIBLOCK_OPTION_TAB_SELECTOR))).append($(this)); that = $(this); }); // that = $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", vpConst.OPTION_GREEN_ROOM), vpCommon.formatString(".{0}", vpConst.API_OPTION_PAGE)))[0]; // $(that).find("h4:eq(0)").hide(); // $(that).find("hr:eq(0)").hide(); // $(vpCommon.wrapSelector(vpCommon.formatString("#{0}", vpConst.OPTION_LOAD_AREA))).append($(that)); // $(vpCommon.wrapSelector(vpCommon.formatString(".{0}", VP_CLASS_APIBLOCK_OPTION_TAB_SELECTOR))).append($(that)); return that; } /** 2021 01 19 이진용 추가 * funcID를 인자로 받고 * funcID에 매핑되는 navigation Info를 가져오는 함수 * navigation Info는 API List 블록 preview에 표시하기 위해 사용됨 */ var getNavigationInfo = function(funcID) { const naviInfoTag = makeOptionPageNaviInfo($(xmlLibraries.getXML()).find(vpConst.LIBRARY_ITEM_TAG + "[" + vpConst.LIBRARY_ITEM_ID_ATTR + "=" + funcID + "]")); let naviInfo = ''; let index = 0; $(naviInfoTag).each(function() { index++; if ($(this).html()) { naviInfo += $(this).html(); naviInfo += ' '; if (index != $(naviInfoTag).length) { naviInfo += '>'; naviInfo += ' '; } } }); return naviInfo; } /** 2021 01 19 이진용 추가 * TODO: 이 함수는 새로 개선 하고 싶은 함수 * * -함수 설명- * 생성한 API List블럭이나 Text 블럭을 클로저 변수인 block_closure에 담아놓고 * optionPageLoadCallback_block의 콜백 인자로 * 전달하기 위해서 생성한 함수 * 콜백인자로 전달하는 이유는 * optionPageLoadCallback_block의 콜백 인자로 API List블럭이나 Text 블럭을 넣어야 * python common, numpy, pandas 같은 package를 API List블럭이 setImportPackage함수를 통해 지역변수 데이터로 가져올 수 있고, * Markdown package를 Text 블럭이 마찬가지로 setImportPackage함수를 통해 지역변수 데이터로 가져올 수 있기 때문 * * -민주님께 전달- * 현재 loadoption과 optionPageLoadCallback 콜백 함수를 거쳐야만 * API Block블럭에서 API List package에 접근할 수 있어서 이런 방식을 택했습니다. (다른 방법이 있을 수도 있는데 일단은 이렇게 했습니다.) * 개인적으로는 * 모든 API List package 클래스의 첫번째에 똑같은 optionPageLoadCallback 코드가 있는게 비효율적이라 생각합니다. * 그렇다고 현재 이걸 바꾼다는건 무리인거 같습니다. * * API Block이나 혹은 또 다른 무언가를 새로 만들었을 때, * API List의 데이터를 가져오기 위해 통신한다던가 할 때 필요한 프로토콜을 정할 필요가 있는 것 같습니다. * */ /** * @param {Block} block_closure_param API List 블럭 or Text 블럭 만 올 수 있음 */ const setClosureBlock = function(block_closure_param) { block_closure = block_closure_param; } return { loadLibraries , loadLibrariesToJson , loadOption_block , loadOption_textBlock , makeUpGreenRoomHTML , optionPageLoadCallback_block , setClosureBlock , getNavigationInfo , libraryLoadCallback , toggleApiListSubGroupShow , makeOptionPageNaviInfo }; });