");
$(sbEditorOptionLine.toString()).prependTo(target);
}
/**
* 코드 생성
* @param {boolean} addCell 셀에 추가
* @param {boolean} exec 실행여부
*/
Markdown.prototype.generateCode = function(addCell = false, exec = false) {
// var gCode = $(this.wrapSelector(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "markdownEditor"))).val();
var that = this;
const textBlock = that.getBlock();
const textarea = textBlock.getBlockOptionPageDom().find(`#vp_markdownEditor`).get(0);
var gCode = textarea.value;
$(this.wrapSelector(vpCommon.formatString(".{0} #{1}{2} input[type=hidden]", this.uuid, vpConst.VP_ID_PREFIX, "attachEncdoedDataArea"))).each(function() {
var indicator = $(this).data('indicator');
var data = $(this).val();
that.attachments.push({
indicator: indicator
, data: data
});
gCode = gCode.replace(indicator, data);
});
// metadata save for attachments
$('#vp_imgAttachment').val(encodeURIComponent(JSON.stringify(this.attachments)));
this.generatedCode = gCode;
if (addCell) {
this.cellExecute(this.generatedCode, exec, "markdown");
}
return this.generatedCode;
}
/**
* 옵션내 이벤트 바인딩.
*/
Markdown.prototype.bindOptionEvent = function() {
var that = this;
$(document).on(vpCommon.formatString("propertychange change keyup paste input.{0}", that.uuid), that.wrapSelector(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "markdownEditor")), function(evt) {
// previewRender(that.uuid, $(this).val());
evt.stopPropagation();
that.previewRender($(this).val());
});
$(document).on(vpCommon.formatString("click.{0}", that.uuid), that.wrapSelector(vpCommon.formatString(".{0} div[class^={1}]", vpConst.VP_MARKDOWN_EDITOR_TOOLBAR + that.uuid, vpConst.VP_MARKDOWN_TOOBAR_BTN)), function(evt) {
// jquery object convert to javascript object for get selection properies
// console.log($(vpCommon.formatString(".{0} #{1}{2}", that.uuid, vpConst.VP_ID_PREFIX, "markdownEditor")));
const textBlock = that.getBlock();
const textarea = textBlock.getBlockOptionPageDom().find(`#vp_markdownEditor`).get(0);
if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_TITLE)) {
adjustTitle(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_BOLD)) {
adjustBold(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_ITALIC)) {
adjustItalic(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_CODE)) {
adjustCode(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_LINK)) {
adjustLink(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_IMAGE)) {
// 클릭시 hidden file control 오픈처리. 선택시 처리는 hidden file control onChange에서 처리
// $(this).parent().find(vpCommon.formatString(".{0}", vpConst.VP_MARKDOWN_TOOBAR_INPUT_FILE_IMAGE)).click();
openImageFile(that.uuid);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_INDENT)) {
addIndentStyle(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_ORDER_LIST)) {
addOrderdList(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_UNORDER_LIST)) {
addUnorderdList(textarea);
} else if ($(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_HORIZONTAL_LINE)) {
addHorizontalLine(textarea);
}
// 이미지 로드의 경우 처리단계에서 랜더링 호출 하므로 이곳에서 호출하면 중복
if (!$(this).hasClass(vpConst.VP_MARKDOWN_TOOBAR_BTN_IMAGE)) {
// 마크다운 렌더링 시행
// previewRender(that.uuid, textarea.value);
that.previewRender(textarea.value);
// change 트리거 발생 - 입력값 저장 및 좌측 번호칸 동기화
$(textarea).trigger('change');
}
});
// $(document).on(vpCommon.formatString("change.{0}", that.uuid), that.wrapSelector(vpCommon.formatString(".{0}", vpConst.VP_MARKDOWN_TOOBAR_INPUT_FILE_IMAGE)), function(evt) {
// // jquery object convert to javascript object for get selection properies
// var textarea = $(vpCommon.formatString(".{0} #{1}{2}", that.uuid, vpConst.VP_ID_PREFIX, "markdownEditor")).get(0);
// // importImage(that.uuid, textarea, evt.target.files[0]);
// that.importImage(that.uuid, textarea, evt.target.files[0]);
// });
$(document).off(vpCommon.formatString("imgFileOpenForMarkdown.fileNavigation.{0}", that.uuid));
$(document).on(vpCommon.formatString("imgFileOpenForMarkdown.fileNavigation.{0}", that.uuid), function(e) {
// jquery object convert to javascript object for get selection properies
// var textarea = $(vpCommon.formatString(".{0} #{1}{2}", that.uuid, vpConst.VP_ID_PREFIX, "markdownEditor")).get(0);
const textBlock = that.getBlock();
const textarea = textBlock.getBlockOptionPageDom().find(`#vp_markdownEditor`).get(0);
that.importImageByPath(textarea, $(that.wrapSelector("#vp_imgFilePath")).val());
});
}
Markdown.prototype.setBlock = function(block) {
this.block = block;
}
Markdown.prototype.getBlock = function() {
return this.block;
}
/**
* 마크다운 렌더링
* @param {String} uuid 포함된 옵션 uuid
* @param {String} text 렌더링할 텍스트
*/
var previewRender = function(uuid, text, block) {
$(vpCommon.wrapSelector(vpCommon.formatString(".{0} #{1}{2} input[type=hidden]", uuid, vpConst.VP_ID_PREFIX, "attachEncdoedDataArea"))).each(function() {
text = text.replace($(this).data("indicator"), $(this).val());
});
var math = null;
var text_and_math = mathjaxutils.remove_math(text);
text = text_and_math[0];
math = text_and_math[1];
var renderer = new marked.Renderer();
// console.log('text',text);
marked(text, { renderer: renderer }, function (err, html) {
html = mathjaxutils.replace_math(html, math);
document.getElementById(vpCommon.formatString("{0}{1}", vpConst.VP_ID_PREFIX, "markdownPreview")).innerHTML = html;
MathJax.Hub.Queue(["Typeset", MathJax.Hub, vpCommon.formatString("{0}{1}", vpConst.VP_ID_PREFIX, "markdownPreview")]);
// console.log('html',html);
});
}
Markdown.prototype.previewRender = function(text) {
var that = this;
const textBlock = this.getBlock();
const textarea = textBlock.getBlockOptionPageDom().find(`#vp_markdownEditor`).get(0);
const blockUUID = textBlock.getUUID();
// 이미지 실제 데이터 가져오기
$(vpCommon.wrapSelector(vpCommon.formatString(".{0} #{1}{2} input[type=hidden]", this.uuid, vpConst.VP_ID_PREFIX, "attachEncdoedDataArea"))).each(function() {
text = text.replace($(this).data("indicator"), $(this).val());
});
var math = null;
var text_and_math = mathjaxutils.remove_math(text);
text = text_and_math[0];
math = text_and_math[1];
var renderer = new marked.Renderer();
marked(text, { renderer: renderer }, function (err, html) {
html = mathjaxutils.replace_math(html, math);
// document.getElementById(vpCommon.formatString("{0}{1}", vpConst.VP_ID_PREFIX, "markdownPreview")).innerHTML = html;
MathJax.Hub.Queue(["Typeset", MathJax.Hub, that.wrapSelector(vpCommon.formatString("{0}{1}", vpConst.VP_ID_PREFIX, "markdownPreview"))]);
// 변환안 된 이미지 있으면 경로로 설정하기 FIXME:
html = html.replaceAll('vpImportImage:', '');
textBlock.writeCode(html);
});
}
Markdown.prototype.importImageByPath = async function(textarea, file) {
var that = this;
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
var code = vpCommon.formatString("_vp_get_image_by_path('{0}')", file);
vpCommon.kernelExecute(code)
.then(data => {
var imgResult = data["image/png"];
textarea.value = vpCommon.formatString("{0}{4}", preText, file, vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file, postText);
// 파일 ATTACH ID 생성
var pathSplit = file.split('.').join('/').split('/');
var fileId = pathSplit[pathSplit.length - 2];
var attachID = vpCommon.formatString("{0}__{1}", vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, fileId);
if ($(that.wrapSelector(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID))).length > 0) {
$(that.wrapSelector(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID))).val('(data:image/png;base64,' + imgResult + ')')
} else {
$(that.wrapSelector(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea")))
.append(vpCommon.formatString("", attachID, vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file, imgResult));
}
// previewRender(motherUUID, textarea.value);
that.previewRender(textarea.value);
// change 트리거 발생 - 입력값 저장 및 좌측 번호칸 동기화
$(textarea).trigger('change');
});
}
Markdown.prototype.importImage = async function(motherUUID, textarea, file) {
var that = this;
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
var fileReader = new FileReader();
fileReader.onload = function() {
textarea.value = vpCommon.formatString("{0}{4}", preText, file.name, vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file.name, postText);
that.getBlock().setState({
[STATE_codeLine]: textarea.value
});
var attachID = vpCommon.formatString("{0}__{1}", vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file.name.substring(0, file.name.indexOf(".")));
// if ($(textarea).parent().parent().find(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID)).length > 0) {
// $(textarea).parent().parent().find(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID)).val(fileReader.result)
// } else {
// $(textarea).parent().parent().find(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea"))
// .append(vpCommon.formatString("", attachID, vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file.name, fileReader.result));
// }
if ($(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID)).length > 0) {
$(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID)).val(fileReader.result)
} else {
$(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea"))
.append(vpCommon.formatString("", attachID, vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file.name, fileReader.result));
}
// previewRender(motherUUID, textarea.value);
that.previewRender(motherUUID, textarea.value);
}
fileReader.readAsDataURL(file);
}
// Jupyter Insert Image할 때 확장자 기준
var allowExtensionList = ['jpg', 'png', 'gif', 'jpeg', 'xbm', 'tif', 'pjp', 'jfif', 'ico', 'tiff', 'svg', 'webp', 'svgz', 'bmp', 'pjpeg', 'avif'];
var fileState = function() {
// file navigation : state 데이터 목록
this.state = {
paramData:{
encoding: "utf-8" // 인코딩
, delimiter: "," // 구분자
},
returnVariable:"", // 반환값
isReturnVariable: false,
fileExtension: allowExtensionList // 확장자
};
this.fileResultState = {
pathInputId : vpCommon.wrapSelector(vpCommon.formatString("#{0}", 'vp_imgFilePath'))
};
this.triggerName = '';
}
// Image File State for fileNavigation
var imgFileState = new fileState();
/**
* Open file navigator for selecting image file
*/
var openImageFile = async function(uuid) {
var loadURLstyle = Jupyter.notebook.base_url + vpConst.BASE_PATH + vpConst.STYLE_PATH;
var loadURLhtml = Jupyter.notebook.base_url + vpConst.BASE_PATH + vpConst.SOURCE_PATH + "component/fileNavigation/index.html";
vpCommon.loadCss( loadURLstyle + "component/fileNavigation.css");
await $(``).load(loadURLhtml, () => {
$('#vp_fileNavigation').removeClass("hide");
$('#vp_fileNavigation').addClass("show");
var {vp_init, vp_bindEventFunctions } = fileNavigation;
imgFileState.triggerName = vpCommon.formatString("imgFileOpenForMarkdown.fileNavigation.{0}", uuid);
fileNavigation.vp_init(imgFileState, FILE_NAVIGATION_TYPE.READ_IMG_FOR_MARKDOWN);
fileNavigation.vp_bindEventFunctions();
}).appendTo("#site");
};
/**
* 타이틀 설정
* @param {object} textarea 마크다운 에디터 입력창
*/
var adjustTitle = function(textarea) {
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
// 커서 기준 현재 라인
var lineText = preText.substring(preText.lastIndexOf('\n') + 1) + postText.substring(0, postText.indexOf('\n'));
// 빈 라인이면 default text로 변경해준다.
if (lineText.trim() == "") {
lineText = vpConst.VP_MARKDOWN_DEFAULT_NEW_TITLE_TEXT;
}
var adjustText;
// 현재 라인이 타이틀 마크다운으로 되어 있는지 확인
if (/^[#]{6}\s{1,}/i.test(lineText)) { // #이 6개면 1개로 되돌린다.
adjustText = preText.substring(0, preText.lastIndexOf('\n') + 1) + "#" + lineText.replace("######", "") + postText.substring(postText.indexOf('\n'));
} else if (/^[#]{1,5}\s{1,}/i.test(lineText)) { // #이 1개에서 5개사이면 #한개 추가한다
adjustText = preText.substring(0, preText.lastIndexOf('\n') + 1) + "#" + lineText + postText.substring(postText.indexOf('\n'));
} else { // #이 6개를 초과했거나 없으면 #과 1공백문자를 추가한다.
adjustText = preText.substring(0, preText.lastIndexOf('\n') + 1) + "# " + lineText + postText.substring(postText.indexOf('\n'));
}
// 변경 텍스트 반영
textarea.value = adjustText;
that.getBlock().setState({
[STATE_codeLine]: textarea.value
});
}
/**
* 볼드체 설정
* @param {object} textarea 마크다운 에디터 입력창
*/
var adjustBold = function(textarea) {
// 정규식
let regExp = /^[*]{2}(.*?)[*]{2}$/;
// 선택된 텍스트
var selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
var targetText = ""; // 비교대상 텍스트
var adjustText; // 변형 결과 텍스트
var preCheck = true; // 기준 앞 비교 필요 여부
var postCheck = true; // 기준 뒤 비교 필요 여부
var nIdx = 0; // 반복문에서 사용할 카운터
var preShiftCount = 0; // 대상 앞 시프트 카운트
var postShiftCount = 0; // 대상 뒤 시프트 카운트
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, textarea.selectionStart);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(textarea.selectionEnd);
// 선택 영역이 없는경우 디폴트 텍스트로 볼드 입력
if (textarea.selectionStart == textarea.selectionEnd) {
var checkChar;
var preCheckChar;
// 커서 앞으로 체크
while (true) {
nIdx++;
preCheckChar = checkChar;
checkChar = textarea.value.charAt(textarea.selectionStart - nIdx);
// *가 끝나면 중지
if (preCheckChar == "*" && checkChar != "*") break;
// 문자가 없거나 공백이거나 개행이면 중지
if (checkChar == "" || checkChar == " " || checkChar == '\n') {
break;
} else {
targetText = checkChar + targetText;
preShiftCount++;
}
}
// 커서 뒤로 체크
nIdx = 0;
while (true) {
preCheckChar = checkChar;
checkChar = textarea.value.charAt(textarea.selectionStart + nIdx);
// *가 끝나면 중지
if (preCheckChar == "*" && checkChar != "*") break;
// 문자가 없거나 공백이거나 개행이면 중지
if (checkChar == "" || checkChar == " " || checkChar == '\n') {
break;
} else {
targetText = targetText + checkChar;
postShiftCount++;
}
nIdx++;
}
// targetText로 포함시킨 만큼 preText, postText 잘라내기
// 잘라낸게 존재하면 slice
if (preShiftCount != 0) {
preText = preText.slice(0, -preShiftCount);
}
postText = postText.substring(postShiftCount);
// 볼드체를 제거할지 추가할지 판단
if (regExp.test(targetText.trim())) { // ** ** 로 감싸져 있어 볼드 제거
adjustText = preText + targetText.slice(2, -2) + postText;
} else {
// 대상 텍스트가 없거나 *를 포함하는 경우는 커서 단어(공백) 뒤에 디폴트 텍스트 추가
if (targetText == "" || targetText.indexOf("*") > -1) {
adjustText = preText + targetText + (targetText == "" ? "**" : " **") + vpConst.VP_MARKDOWN_DEFAULT_BOLD_TEXT + "**" + postText;
} else { // 대상 텍스트에 대하여 볼드체 추가
adjustText = preText + "**" + targetText + "**" + postText;
}
}
} else { // 선택영역이 있는경우 볼드체를 제거할지 추가할지 판단
targetText = selectedText;
// selectedText 앞뒤로 * 가 아닌것 만날때까지 targetText에 추가.
for (nIdx = 0; nIdx < 3; nIdx++) {
// 선택영역 앞으로 * 가 붙어있는경우 확장해준다.
if (preCheck && textarea.value.charAt(textarea.selectionStart - (nIdx + 1)) === "*") {
targetText = "*" + targetText;
preShiftCount++;
} else { // 선택영역 앞이 * 가 아닌경우 범위 확장 중지
preCheck = false;
}
// 선택영역 뒤로 * 가 붙어있는경우 확장해준다.
if (postCheck && textarea.value.charAt(textarea.selectionEnd + nIdx) === "*") {
targetText = targetText + "*";
postShiftCount++;
} else { // 선택영역 뒤가 * 가 아닌경우 범위 확장 중지
postCheck = false;
}
}
// 볼드체를 제거할지 추가할지 판단
if (regExp.test(targetText.trim())) { // ** ** 로 감싸져 있어 볼드 제거
// targetText로 포함시킨 만큼 preText, postText 잘라내기
// 잘라낸게 존재하면 slice
if (preShiftCount != 0) {
preText = preText.slice(0, -preShiftCount);
}
postText = postText.substring(postShiftCount);
adjustText = preText + targetText.slice(2, -2) + postText;
} else {
adjustText = preText + "**" + targetText + "**" + postText;
}
}
// 변경 텍스트 반영
textarea.value = adjustText;
}
/**
* 이탈릭체 설정
* @param {object} textarea 마크다운 에디터 입력창
*/
var adjustItalic = function(textarea) {
// 정규식
let regExp = /^[*]{1}(.*?)[*]{1}$/;
// 선택된 텍스트
var selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
var targetText = ""; // 비교대상 텍스트
var adjustText; // 변형 결과 텍스트
var preCheck = true; // 기준 앞 비교 필요 여부
var postCheck = true; // 기준 뒤 비교 필요 여부
var nIdx = 0; // 반복문에서 사용할 카운터
var preShiftCount = 0; // 대상 앞 시프트 카운트
var postShiftCount = 0; // 대상 뒤 시프트 카운트
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, textarea.selectionStart);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(textarea.selectionEnd);
// 선택 영역이 없는경우 디폴트 텍스트로 이탈릭 입력
if (textarea.selectionStart == textarea.selectionEnd) {
var checkChar;
var preCheckChar;
// 커서 앞으로 체크
while (true) {
nIdx++;
preCheckChar = checkChar;
checkChar = textarea.value.charAt(textarea.selectionStart - nIdx);
if (preCheckChar == "*" && checkChar != "*") break;
// 문자가 없거나 공백이거나 개행이면 중지
if (checkChar == "" || checkChar == " " || checkChar == '\n') {
break;
} else {
targetText = checkChar + targetText;
preShiftCount++;
}
}
// 커서 뒤로 체크
nIdx = 0;
while (true) {
preCheckChar = checkChar;
checkChar = textarea.value.charAt(textarea.selectionStart + nIdx);
if (preCheckChar == "*" && checkChar != "*") break;
// 문자가 없거나 공백이거나 개행이면 중지
if (checkChar == "" || checkChar == " " || checkChar == '\n') {
break;
} else {
targetText = targetText + checkChar;
postShiftCount++;
}
nIdx++;
}
// targetText로 포함시킨 만큼 preText, postText 잘라내기
// 잘라낸게 존재하면 slice
if (preShiftCount != 0) {
preText = preText.slice(0, -preShiftCount);
}
postText = postText.substring(postShiftCount);
// 이탈릭체를 제거할지 추가할지 판단
if (regExp.test(targetText.trim())) { // * * 로 감싸져 있어 이탈릭 제거
adjustText = preText + targetText.slice(1, -1) + postText;
} else {
// 대상 텍스트가 없거나 *를 포함하는 경우는 커서 단어(공백) 뒤에 디폴트 텍스트 추가
if (targetText == "" || targetText.indexOf("*") > -1) {
adjustText = preText + targetText + (targetText == "" ? "*" : " *") + vpConst.VP_MARKDOWN_DEFAULT_ITALIC_TEXT + "*" + postText;
} else { // 대상 텍스트에 대하여 이탈릭체 추가
adjustText = preText + "*" + targetText + "*" + postText;
}
}
} else { // 선택영역이 있는경우 이탈릭체를 제거할지 추가할지 판단
targetText = selectedText;
// selectedText 앞뒤로 * 가 아닌것 만날때까지 targetText에 추가.
for (nIdx = 0; nIdx < 3; nIdx++) {
// 선택영역 앞으로 * 가 붙어있는경우 확장해준다.
if (preCheck && textarea.value.charAt(textarea.selectionStart - (nIdx + 1)) === "*") {
targetText = "*" + targetText;
preShiftCount++;
} else { // 선택영역 앞이 * 가 아닌경우 범위 확장 중지
preCheck = false;
}
// 선택영역 뒤로 * 가 붙어있는경우 확장해준다.
if (postCheck && textarea.value.charAt(textarea.selectionEnd + nIdx) === "*") {
targetText = targetText + "*";
postShiftCount++;
} else { // 선택영역 뒤가 * 가 아닌경우 범위 확장 중지
postCheck = false;
}
}
// 이탈릭체를 제거할지 추가할지 판단
if (regExp.test(targetText.trim())) { // * * 로 감싸져 있어 이탈릭 제거
// targetText로 포함시킨 만큼 preText, postText 잘라내기
// 잘라낸게 존재하면 slice
if (preShiftCount != 0) {
preText = preText.slice(0, -preShiftCount);
}
postText = postText.substring(postShiftCount);
adjustText = preText + targetText.slice(1, -1) + postText;
} else {
adjustText = preText + "*" + targetText + "*" + postText;
}
}
// 변경 텍스트 반영
textarea.value = adjustText;
}
/**
* 코드체 설정
* @param {object} textarea 마크다운 에디터 입력창
*/
var adjustCode = function(textarea) {
// 정규식
let regExp = /^[`]{1}(.*?)[`]{1}$/;
// 선택된 텍스트
var selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
var targetText = ""; // 비교대상 텍스트
var adjustText; // 변형 결과 텍스트
var preCheck = true; // 기준 앞 비교 필요 여부
var postCheck = true; // 기준 뒤 비교 필요 여부
var nIdx = 0; // 반복문에서 사용할 카운터
var preShiftCount = 0; // 대상 앞 시프트 카운트
var postShiftCount = 0; // 대상 뒤 시프트 카운트
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, textarea.selectionStart);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(textarea.selectionEnd);
// 선택 영역이 없는경우 디폴트 텍스트로 코드 입력
if (textarea.selectionStart == textarea.selectionEnd) {
var checkChar;
var preCheckChar;
// 커서 앞으로 체크
while (true) {
nIdx++;
preCheckChar = checkChar;
checkChar = textarea.value.charAt(textarea.selectionStart - nIdx);
if (preCheckChar == "`" && checkChar != "`") break;
// 문자가 없거나 공백이거나 개행이면 중지
if (checkChar == "" || checkChar == " " || checkChar == '\n') {
break;
} else {
targetText = checkChar + targetText;
preShiftCount++;
}
}
// 커서 뒤로 체크
nIdx = 0;
while (true) {
preCheckChar = checkChar;
checkChar = textarea.value.charAt(textarea.selectionStart + nIdx);
if (preCheckChar == "`" && checkChar != "`") break;
// 문자가 없거나 공백이거나 개행이면 중지
if (checkChar == "" || checkChar == " " || checkChar == '\n') {
break;
} else {
targetText = targetText + checkChar;
postShiftCount++;
}
nIdx++;
}
// targetText로 포함시킨 만큼 preText, postText 잘라내기
// 잘라낸게 존재하면 slice
if (preShiftCount != 0) {
preText = preText.slice(0, -preShiftCount);
}
postText = postText.substring(postShiftCount);
// 코드체를 제거할지 추가할지 판단
if (regExp.test(targetText.trim())) { // ` ` 로 감싸져 있어 코드 제거
adjustText = preText + targetText.slice(1, -1) + postText;
} else {
// 대상 텍스트가 없거나 `를 포함하는 경우는 커서 단어(공백) 뒤에 디폴트 텍스트 추가
if (targetText == "" || targetText.indexOf("`") > -1) {
adjustText = preText + targetText + "\n\n```\n# " + vpConst.VP_MARKDOWN_DEFAULT_CODE_TEXT + "\n```\n\n\n" + postText;
} else { // 대상 텍스트에 대하여 코드체 추가
adjustText = preText + "`" + targetText + "`" + postText;
}
}
} else { // 선택영역이 있는경우 코드체를 제거할지 추가할지 판단
targetText = selectedText;
// selectedText 앞뒤로 ` 가 아닌것 만날때까지 targetText에 추가.
for (nIdx = 0; nIdx < 3; nIdx++) {
// 선택영역 앞으로 ` 가 붙어있는경우 확장해준다.
if (preCheck && textarea.value.charAt(textarea.selectionStart - (nIdx + 1)) === "`") {
targetText = "`" + targetText;
preShiftCount++;
} else { // 선택영역 앞이 ` 가 아닌경우 범위 확장 중지
preCheck = false;
}
// 선택영역 뒤로 ` 가 붙어있는경우 확장해준다.
if (postCheck && textarea.value.charAt(textarea.selectionEnd + nIdx) === "`") {
targetText = targetText + "`";
postShiftCount++;
} else { // 선택영역 뒤가 ` 가 아닌경우 범위 확장 중지
postCheck = false;
}
}
// 코드체를 제거할지 추가할지 판단
if (regExp.test(targetText.trim())) { // ` ` 로 감싸져 있어 코드 제거
// targetText로 포함시킨 만큼 preText, postText 잘라내기
// 잘라낸게 존재하면 slice
if (preShiftCount != 0) {
preText = preText.slice(0, -preShiftCount);
}
postText = postText.substring(postShiftCount);
adjustText = preText + targetText.slice(1, -1) + postText;
} else {
adjustText = preText + "`" + targetText + "`" + postText;
}
}
// 변경 텍스트 반영
textarea.value = adjustText;
}
/**
* 링크 설정
* @param {object} textarea 마크다운 에디터 입력창
*/
var adjustLink = function(textarea) {
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, textarea.selectionStart);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(textarea.selectionEnd);
// 선택된 텍스트
var selectedText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
var linkText;
if (selectedText.startsWith("http://") || selectedText.startsWith("https://")) {
linkText = vpCommon.formatString("[{0}]({1})", vpConst.VP_MARKDOWN_DEFAULT_LINK_TEXT, selectedText);
} else {
linkText = vpCommon.formatString("[{0}](https://)", (selectedText == undefined || selectedText == "" ? vpConst.VP_MARKDOWN_DEFAULT_LINK_TEXT : selectedText));
}
textarea.value = preText + linkText + postText;
}
/**
* 선택된 이미지 파일 인코딩하여 첨부
* @param {String} motherUUID 포함된 옵션 uuid
* @param {object} textarea 마크다운 에디터 입력창
* @param {object} file 선택된 파일 정보
*/
var importImage = function(motherUUID, textarea, file) {
var that = this;
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
var fileReader = new FileReader();
fileReader.onload = function() {
textarea.value = vpCommon.formatString("{0}{4}", preText, file.name, vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file.name, postText);
var attachID = vpCommon.formatString("{0}__{1}", vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file.name.substring(0, file.name.indexOf(".")));
if ($(textarea).parent().parent().find(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID)).length > 0) {
$(textarea).parent().parent().find(vpCommon.formatString("#{0}{1} #{2}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea", attachID)).val(fileReader.result)
} else {
$(textarea).parent().parent().find(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "attachEncdoedDataArea"))
.append(vpCommon.formatString("", attachID, vpConst.VP_MARKDOWN_TOOLBAR_IMAGE_INDICATOR, file.name, fileReader.result));
}
// previewRender(motherUUID, textarea.value);
that.previewRender(textarea.value);
}
fileReader.readAsDataURL(file);
}
/**
* 들여쓰기 스타일 추가
* @param {object} textarea 마크다운 에디터 입력창
*/
var addIndentStyle = function(textarea) {
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
// 커서 기준 뒤 텍스트중 현재 커서 라인 텍스트는 preText 로 이전 한다.
preText = preText + postText.substring(0, postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
// 이전해준 현재 커서 라인 텍스트만큼 postText 에서 제거한다.
postText = postText.substring(postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
// 변경 텍스트 반영
textarea.value = preText + vpConst.VP_MARKDOWN_DEFAULT_INDENT_TEXT + postText;
}
/**
* 번호 매기기 목록 추가
* @param {object} textarea 마크다운 에디터 입력창
*/
var addOrderdList = function(textarea) {
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
// 커서 기준 뒤 텍스트중 현재 커서 라인 텍스트는 preText 로 이전 한다.
preText = preText + postText.substring(0, postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
// 이전해준 현재 커서 라인 텍스트만큼 postText 에서 제거한다.
postText = postText.substring(postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
var addText = vpCommon.formatString("\n\n1. {0}\n2. {1}\n\n", vpConst.VP_MARKDOWN_DEFAULT_LIST_TEXT, vpConst.VP_MARKDOWN_DEFAULT_LIST_TEXT);
// 변경 텍스트 반영
textarea.value = preText + addText + postText;
}
/**
* 글머리기호 목록 추가
* @param {object} textarea 마크다운 에디터 입력창
*/
var addUnorderdList = function(textarea) {
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
// 커서 기준 뒤 텍스트중 현재 커서 라인 텍스트는 preText 로 이전 한다.
preText = preText + postText.substring(0, postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
// 이전해준 현재 커서 라인 텍스트만큼 postText 에서 제거한다.
postText = postText.substring(postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
var addText = vpCommon.formatString("\n\n* {0}\n* {1}\n\n", vpConst.VP_MARKDOWN_DEFAULT_LIST_TEXT, vpConst.VP_MARKDOWN_DEFAULT_LIST_TEXT);
// 변경 텍스트 반영
textarea.value = preText + addText + postText;
}
/**
* 가로줄 추가
* @param {object} textarea 마크다운 에디터 입력창
*/
var addHorizontalLine = function(textarea) {
// 커서 위치
var cursorPosition = 0;
// 선택영역 시작과 끝이 같으면 선택 영역이 없는 현재 커서 위치
if (textarea.selectionStart == textarea.selectionEnd) {
cursorPosition = textarea.selectionStart;
} else { // 선택영역 시작과 끝이 다르면 선택 방향에 따라서 잡아준다.
cursorPosition = textarea.selectionDirection == "forward" ? textarea.selectionEnd : textarea.selectionStart;
}
// 커서 기준 앞 전체 텍스트
var preText = textarea.value.substring(0, cursorPosition);
// 커서 기준 뒤 전체 텍스트
var postText = textarea.value.substring(cursorPosition);
// 커서 기준 뒤 텍스트중 현재 커서 라인 텍스트는 preText 로 이전 한다.
preText = preText + postText.substring(0, postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
// 이전해준 현재 커서 라인 텍스트만큼 postText 에서 제거한다.
postText = postText.substring(postText.indexOf('\n') == -1 ? postText.length : postText.indexOf('\n'));
// 변경 텍스트 반영
textarea.value = preText + "\n\n---\n\n" + postText;
}
/**
* 메타데이터 로드 후 액션
* @param {funcJS} option
* @param {JSON} meta
*/
Markdown.prototype.loadMetaExpend = function(funcJS, meta) {
console.log('loadMetaExpend');
this.previewRender($(this.wrapSelector(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "markdownEditor"))).val());
// previewRender(this.uuid, $(vpCommon.formatString("#{0}{1}", vpConst.VP_ID_PREFIX, "markdownEditor")).val());
}
return {
initOption: initOption
};
});