사용자:BANIP/자동문서작성
언사이라이터(가칭)은 비슷비슷한 내용의 백괴게임 문서를 작성할때 도움을 줄 수 있는 특수문서 아닌 특수문서입니다. 기차나 지하철이나 고속도로게임같이 템플릿이 비슷비슷한 문서 작성에 도움이 됩니다.
사용법[편집 | 원본 편집]
아래 인터페이스 보시면 왼쪽에는 그림으로 되어있고 오른쪽엔 버튼이나 텍스트박스로 이루어져 있습니다. 각각 나누어서 기능을 설명하겠습니다.
캔버스[편집 | 원본 편집]
캔버스를 잘 보시면 네모와 화살표로 이루어져 있습니다. 네모(이하 노드라고 부르겠습니다)클릭하면 노란색으로 바뀝니다. 선택된 노드는 명령팔레트에서 삭제가 가능합니다. 한 노드를 마우스로 드래그해서 다른 노드에 갖다 붙이는것으로 화살표를 이을 수 있습니다. 또한 오른쪽 마우스로 갖다 붙이는 행위로 이어진 화살표를 지우는것이 가능합니다.
명령 팔레트[편집 | 원본 편집]
새로운 노드 추가 탭의 텍스트박스에 노드 네임을 입력하고 추가를 누르는 것으로 노드 이름을 추가할 수 있습니다. 캔버스에서 선택한 노드를 기존 노드 수정 탭에서 삭제가 가능합니다. 노드 작성 완료 및 생성 파트가 좀 복잡하고 제일 중요한 부분입니다.
여기서 문서를 생성할때 각 문서는 캔버스의 노드와 대응합니다. 노드를 5개 만들었으면 다섯개 문서가 생성이되고 100개 만들었음 백개가 생성이 되요. 백괴사전에서 PAGENAME이나 USERNAME같은 틀 쓸때 대괄호 두개 쓰는것처럼 여기선 대괄호와 꺽쇠괄호를 조합해 여러 변수들을 사용 가능합니다. 노드네임 페이지네임은 인터페이스에 간단하게 적혀있으니 장황하게 설명안해도 대충 이해가 가실거라 믿구요,
링크는 첫번째 파라미터 안에 있는 문법이, 문서에 해당되는 노드가 다른 노드에 링크된 갯수만큼 반복됩니다. 무슨 소리냐 하면 부산노드가 서울과 대구 총 두개가 이어져 있으면 두번씩 반복되구요 서울로는 다른노드가 이어지기만 했지 다른노드로 링크되진 않으므로 출력되지 않습니다. 또한 이 함수 내부에서 링크노드네임이나 링크페이지네임 변수를 사용 가능한데, 이 링크함수가 반복될때 해당 노드에 대응되는 링크문서의 페이지명과 노드이름을 출력 할 수 있습니다.
어떻게든 열심히 설명해봤는데 내가봐도 뭔소린지 잘 모르겠네요 대신 예시 몇개 남겨놓을테니 대충대충 끄적여보고 감 잡으시길 바랍니다 화이팅
인터페이스[편집 | 원본 편집]
/** * 시간에 따라 바뀌는 유동적인 값을 반환합니다. cos함수, 혹은 그의 절대값 형태를 띕니다. * @param start 최소값 * @param height 진폭 * @param wavelength 파장 * @param isAbs 절대값인가 아닌가 */ var getFluidValue = function (start, height, wavelength, isAbs) { if (isAbs === void 0) { isAbs = true; } var now = Date.now() / Math.PI / wavelength; var amplitude = Math.cos(now) * height; return start + (isAbs ? Math.abs(amplitude) : amplitude); }; /** * 뷰에 해당하는 클래스, 캔버스와 연동해 노드들의 관계를 보여줌 */ var GUI = /** @class */ (function () { /** * 생성자, 컨텍스트를 가져오기 위한 용도인 getcdtx를 정의 * @param canvas 그림판으로 사용할 캔버스 객체 */ function GUI(canvas) { if (canvas === void 0) { canvas = document.querySelector("#nodemap"); } var _this = this; this.canvas = canvas; this.nodeCache = []; this.selectedNode = null; this.isUnlinkNode = false; this.dragInfo = null; /** * 캔버스에서 사용할 각종 수치 */ this.elemUnit = { wrap: { marginX: 50, marginY: 20 }, node: { intervalY: 20, width: 100, height: 50, getctx: null, selectNodeColor: "yellow" }, branch: { width: 100, weight: 3, getctx: null } }; this.elemUnit.node.getctx = function () { var ctx = _this.canvas.getContext("2d"); ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.font = "30px sans-serif"; ctx.lineWidth = 5; ctx.setLineDash([]); ctx.strokeStyle = "black"; return ctx; }; this.elemUnit.branch.getctx = function () { var ctx = _this.canvas.getContext("2d"); ctx.strokeStyle = "black"; ctx.setLineDash([]); return ctx; }; } /** * 특정 노드의 위치 획득 * @param nodeList 위치를 획득할 노드가 속하는 집단 * @param node 위치를 획득할 노드 */ GUI.prototype.getPosition = function (nodeList, node) { var thisPosition; nodeList.every(function (currentNode, i) { thisPosition = i; if (currentNode === node) return false; return true; }); return thisPosition; }; /** * 수정 대상이 될 노드 지정 * @param node 수정 대상이 될 노드 */ GUI.prototype.nodeSelect = function (node, isUnlinkNode) { this.isUnlinkNode = isUnlinkNode; this.selectedNode = node; }; GUI.prototype.getNodeByAxis = function (axis) { var _a = this.elemUnit, wrap = _a.wrap, node = _a.node, branch = _a.branch; var nodeContainerSize = node.intervalY + node.height; var isSafeX = axis.x >= branch.width + wrap.marginX && axis.x <= branch.width + wrap.marginX + node.width; if (!isSafeX) return null; var p = parseInt((axis.y - wrap.marginY) / nodeContainerSize + ""); if (p === -1) return null; return this.nodeCache[p]; }; GUI.prototype.setNodeList = function (nodeList) { this.nodeCache = nodeList; }; GUI.prototype.setDragInfo = function (axis) { this.dragInfo = axis; }; /** * 캔버스에 출력 */ GUI.prototype.draw = function () { var _this = this; var nodeList = this.nodeCache; this.initCanvasSize(nodeList.length); nodeList.forEach(function (node, i) { _this.drawNode(node, i); node.getLinked().forEach(function (linkednode) { var j = _this.getPosition(nodeList, linkednode); _this.drawbranch(i, j); }); }); this.drawbranch(this.selectedNode, this.dragInfo, function () { var ctx = _this.elemUnit.branch.getctx(); ctx.setLineDash([7, 3]); ctx.lineDashOffset = Date.now() / 100 % 100; ctx.strokeStyle = _this.isUnlinkNode ? "red" : "blue"; return ctx; }); }; GUI.prototype.initCanvasSize = function (nodeCount) { var _a = this.elemUnit, wrap = _a.wrap, node = _a.node, branch = _a.branch; var width = wrap.marginX * 2 + node.width + branch.width * 2; var height = wrap.marginY * 2 + (node.height * nodeCount) + node.intervalY * (nodeCount - 1); this.canvas.width = width; this.canvas.height = height; }; GUI.prototype.getNodePosition = function (position, hori, vert) { if (hori === void 0) { hori = "left"; } if (vert === void 0) { vert = "top"; } var _a = this.elemUnit, wrap = _a.wrap, node = _a.node, branch = _a.branch; var getBonusWidth = function (hori) { if (hori == "left") return 0; if (hori == "center") return node.width / 2; if (hori == "right") return node.width; }; var getBonusHeight = function (vert) { if (vert === "top") return 0; if (vert === "middle") return node.height / 2; if (vert === "bottom") return node.height; }; return [ wrap.marginX + branch.width + getBonusWidth(hori), wrap.marginY + (node.intervalY + node.height) * position + getBonusHeight(vert) ]; }; GUI.prototype.drawNode = function (node, position) { var nodeSize = this.elemUnit.node; var ctx = nodeSize.getctx(); var axis = this.getNodePosition(position); if (node === this.selectedNode) { ctx.fillStyle = nodeSize.selectNodeColor; ctx.fillRect.apply(ctx, this.getNodePosition(position).concat([nodeSize.width, nodeSize.height])); ctx.fillStyle = "black"; } ctx.strokeRect.apply(ctx, this.getNodePosition(position).concat([nodeSize.width, nodeSize.height])); ctx.fillText.apply(ctx, [node.getName()].concat(this.getNodePosition(position, "center", "middle"))); }; GUI.prototype.getBranchAxis = function (startPosition, endPosition) { var _this = this; var getAxisY = function (axis) { if (axis instanceof DocNode) axis = _this.getPosition(_this.nodeCache, axis); if (typeof axis == "number") return _this.getNodePosition(axis, "center", "middle")[1]; return axis.y; }; var _a = [getAxisY(startPosition), getAxisY(endPosition)], startAxisY = _a[0], endAxisY = _a[1]; var direction = startAxisY > endAxisY ? "left" : "right"; var isDirectionLeft = direction === "left"; var axisX = this.getNodePosition(0, direction, "middle")[0]; var start = [axisX, startAxisY]; var end = [axisX, endAxisY]; return [start, end]; }; GUI.prototype.drawbranch = function (startPosition, endPosition, getctx) { if (getctx === void 0) { getctx = this.elemUnit.branch.getctx; } if (startPosition === null || endPosition === null) return; var _a = this.getBranchAxis(startPosition, endPosition), start = _a[0], end = _a[1]; var max = start[1] > end[1] ? end : start; var isDirectionLeft = max === end; var radius = Math.abs((end[1] - start[1]) / 2); var arrowSize = (isDirectionLeft ? -1 : 1) * 15; var ctx = getctx(); ctx.beginPath(); ctx.arc(max[0], max[1] + radius, radius, isDirectionLeft ? Math.PI * 1 / 2 : Math.PI * 3 / 2, isDirectionLeft ? Math.PI * 3 / 2 : Math.PI * 1 / 2); ctx.moveTo.apply(ctx, end); ctx.lineTo(end[0] + arrowSize, end[1] + arrowSize); ctx.moveTo.apply(ctx, end); ctx.lineTo(end[0] + arrowSize, end[1] - arrowSize); ctx.stroke(); }; return GUI; }()); var NodeInput = /** @class */ (function () { function NodeInput(gui, nodeList) { if (gui === void 0) { gui = new GUI(); } this.gui = gui; this.nodeList = nodeList; this.selectedNode = null; this.$$ = document.querySelector.bind(document); this.initEventListener(); this.initCanvasEventListener(); } NodeInput.prototype.initEventListener = function () { var _this = this; var $$ = this.$$; // addNode var addNodeListener = function (e) { var nodenameNode = $$(".inp_addnodename"); var nodeName = nodenameNode.value; nodenameNode.value = ""; _this.nodeList.addNode(nodeName); }; $$(".btn_addnode").addEventListener("click", addNodeListener); $$(".inp_addnodename").addEventListener("keydown", function (e) { if (e.key === "Enter") { addNodeListener(e); } }); $$(".btn_removenode").addEventListener("click", function (e) { if (_this.selectedNode === null) throw Error("삭제할 노드를 선택 해 주세요."); _this.nodeList.removeNode(_this.selectedNode.getName()); var lastNode = _this.nodeList.getNodeList().slice(-1)[0]; console.log(lastNode); _this.gui.nodeSelect(lastNode, false); _this.selectedNode = lastNode; }); var createDocListener = function (e) { var sendMessage = function (message) { return $$(".btn_create_message").innerHTML = message; }; $$(".btn_create_create").removeEventListener("click", createDocListener); sendMessage("문서 생성 요청을 보냈습니다. 준비할때까지 좀만 기다려주세요"); var prefix = $$(".inp_create_target").value; var template = $$(".inp_create_form").value; if (prefix.slice(-1) !== "/") prefix += "/"; DocFactory.create(_this.nodeList, template, prefix, sendMessage); }; $$(".btn_create_create").addEventListener("click", createDocListener); }; NodeInput.prototype.initCanvasEventListener = function () { var _this = this; var $$ = this.$$; var getAxis = function (e) { return ({ x: e.offsetX, y: e.offsetY }); }; var setSelectedNode = function (node, isUnlinkMode) { _this.selectedNode = node; _this.setSelectedNode(node); _this.gui.nodeSelect(_this.selectedNode, isUnlinkMode); }; var isMouseDown = false; // selectNode $$("#nodemap").addEventListener("mousedown", function (e) { var axis = getAxis(e); isMouseDown = true; setSelectedNode(_this.gui.getNodeByAxis(axis), e.button == 2); }); // searchTarget $$("#nodemap").addEventListener("mousemove", function (e) { if (isMouseDown === false) return _this.gui.setDragInfo(null); ; var axis = getAxis(e); _this.gui.getNodeByAxis(axis); _this.gui.setDragInfo(axis); }); // setTarget $$("#nodemap").addEventListener("mouseup", function (e) { var selectedNode = _this.selectedNode; isMouseDown = false; var axis = getAxis(e); var throwNode = _this.gui.getNodeByAxis(axis); if (selectedNode === null || throwNode === null) return; if (e.button == 0) { _this.nodeList.linkNode(throwNode.getName(), selectedNode.getName()); } else if (e.button == 2) { _this.nodeList.unLinkNode(throwNode.getName(), selectedNode.getName()); } }); // removePreventEvent $$("#nodemap").addEventListener("contextmenu", function (e) { e.preventDefault(); }); }; NodeInput.prototype.setSelectedNode = function (node) { var $$ = this.$$; this.selectedNode = node; $$(".spn_nodename").innerHTML = node.getName(); }; return NodeInput; }()); var DocFactory = /** @class */ (function () { function DocFactory() { } DocFactory.setTemplateVariable = function (template, templateVariable) { var _a = templateVariable.shift(), searchValue = _a[0], replaceValue = _a[1]; template = template.replace("{[" + searchValue + "]}", replaceValue); if (templateVariable.length != 0) return DocFactory.setTemplateVariable(template, templateVariable); return template; }; DocFactory.setLinkNoding = function (node, template, prefix) { var nodeName = node.getName(); return template.replace(/\{\[링크\|([^]+)\]\}/g, function (matchedStr) { matchedStr = matchedStr.replace(/\{\[링크\|\s*([^]+)\s*\]\}/g, "$1"); return node.getLinked().map(function (linkedNode) { var templateVariable = [ ["링크노드네임", linkedNode.getName()], ["링크페이지네임", prefix + linkedNode.getName()] ]; return DocFactory.setTemplateVariable(matchedStr, templateVariable.slice()); }).join("\n"); }); }; DocFactory.setOtherVariable = function (node, template, prefix) { var nodeName = node.getName(); var templateVariable = [ ["노드네임", nodeName], ["페이지네임", prefix + nodeName] ]; return DocFactory.setTemplateVariable(template, templateVariable); }; DocFactory.create = function (docNodeList, template, prefix, messageCallback) { var nodeList = docNodeList.getNodeList(); var docQueue = nodeList.map(function (node) { var docString = template; var nodeName = node.getName(); docString = DocFactory.setLinkNoding(node, docString, prefix); docString = DocFactory.setOtherVariable(node, docString, prefix); return { title: prefix + nodeName, content: docString }; }); DocFactory.makeDoc(messageCallback, docQueue); }; DocFactory.makeDoc = function (messageCallback, docQueue) { var api = MediaWikiAPI(); var makeDocIntervar = setInterval(function () { if (docQueue.length === 0) { messageCallback("\uBAA8\uB4E0 \uBB38\uC11C\uB97C \uC791\uC131\uD588\uC2B5\uB2C8\uB2E4. \uCD5C\uADFC \uBC14\uB01C\uC744 \uD655\uC778 \uD574 \uC8FC\uC2ED\uC1FC"); clearInterval(makeDocIntervar); } var nextDoc = docQueue.pop(); api.changeDocument(nextDoc.title, "템플릿 문서작성 스크립트 실행중", nextDoc.content, true); messageCallback("\uBB38\uC11C\uB97C \uBAA8\uB450 \uC791\uC131\uD558\uAE30\uAE4C\uC9C0 \uC55E\uC73C\uB85C " + docQueue.length + "\uAC1C \uB0A8\uC558\uC2B5\uB2C8\uB2E4."); }, 3000); }; return DocFactory; }()); var DocNodeList = /** @class */ (function () { function DocNodeList(gui) { if (gui === void 0) { gui = new GUI(); } this.gui = gui; this.nodeList = {}; } DocNodeList.prototype.sendDraw = function () { this.gui.setNodeList(Object.values(this.nodeList)); }; DocNodeList.prototype.hasNotNode = function (node) { return typeof node === "undefined"; }; DocNodeList.prototype.linkNode = function (forNodename, toNodename) { var _a = this, hasNotNode = _a.hasNotNode, nodeList = _a.nodeList; var forNode = nodeList[forNodename]; var toNode = nodeList[toNodename]; if (hasNotNode(forNode) || hasNotNode(toNode)) throw Error("존재하지 않는 노드입니다."); toNode.link(forNode); this.sendDraw(); }; DocNodeList.prototype.unLinkNode = function (forNodename, toNodename) { if (toNodename === void 0) { toNodename = null; } var _a = this, hasNotNode = _a.hasNotNode, nodeList = _a.nodeList; var forNode = nodeList[forNodename]; var toNode = nodeList[toNodename]; if (hasNotNode(forNode)) throw Error("존재하지 않는 노드입니다."); if (hasNotNode(toNodename)) { this.getNodeList().forEach(function (currentNode) { currentNode.unLink(forNode); forNode.unLink(currentNode); }); } else { toNode.unLink(forNode); } this.sendDraw(); }; DocNodeList.prototype.addNode = function (nodename) { var nodeList = this.nodeList; if (nodeList[nodename] instanceof Node) throw Error("이미 존재하는 노드입니다"); else { nodeList[nodename] = new DocNode(nodename); this.sendDraw(); } }; DocNodeList.prototype.removeNode = function (nodename) { var nodeList = this.nodeList; var targetNode = nodeList[nodename]; if (this.hasNotNode(targetNode)) throw Error("존재하지 않는 노드입니다."); delete nodeList[nodename]; this.getNodeList().forEach(function (node) { return node.unLink(targetNode); }); this.sendDraw(); }; DocNodeList.prototype.getNodeList = function () { return Object.values(this.nodeList); }; return DocNodeList; }()); var DocNode = /** @class */ (function () { function DocNode(name) { this.name = name; this.linkedNode = []; } DocNode.prototype.link = function (node) { if (node === this) return; //throw Error("연결하려는 노드가 자신입니다."); if (this.linkedNode.some(function (currentNode) { return currentNode === node; })) throw Error("이미 연결된 노드입니다."); this.linkedNode.push(node); }; DocNode.prototype.unLink = function (node) { var _this = this; this.linkedNode.forEach(function (currentNode, i) { if (currentNode === node) _this.linkedNode.splice(i, 1); }); }; DocNode.prototype.getLinked = function () { return this.linkedNode; }; DocNode.prototype.getName = function () { return this.name; }; DocNode.prototype.setName = function (name) { return this.name = name; }; return DocNode; }()); var initHTML = function () { $("#content").append(`<div class="wrap"><section class="section_node">\n <canvas id="nodemap"></canvas>\n</section>\n<section class="section_node">\n <h2>노드 스케치 조작법</h2>\n <ul>\n <li>노드 선택: 선택하고 싶은 노드 클릭</li>\n <li>노드 링킹: 링크하고 싶은 노드 클릭 후 링크 대상이 되는 노드에 갖다 끌기</li>\n <li>노드 링킹 해제: 해제하고 싶은 노드 클릭 후 링크 대상이 되는 노드에 갖다 끌기</li>\n </ul>\n <h2>새로운 노드 추가</h2>\n 노드 이름 <input class="inp_addnodename"/> <button class="btn_addnode" placeholder="노드의 제목을 입력해주세요..." > 추가 </button>\n <h2>기존 노드 수정</h2>\n 현재 선택된 노드: <span class="spn_nodename"> 선택되지 않음 </span>\n <button class="btn_removenode"> 노드 삭제 </button> \n\n <h2>노드 작성 완료 및 문서 생성</h2>\n <pre>\n 아래 텍스트 에리어에 각 문서에 생성될 템플릿을 입력 해 주세요. 템플릿에서 사용 가능한 특수 문법은 다음과 같습니다.\n {[노드네임]} : 노드 이름을 출력합니다.\n {[페이지네임]} : 페이지 이름을 출력합니다.\n {[링크| .... ]} : 다른 노드와 링크된 갯수만큼 파라미터 안의 내용을 반복합니다.\n {[링크노드네임]} : 링크함수 내부에 쓰이는 변수입니다. 링크된 노드의 이름을 출력합니다.\n {[링크페이지네임]} : 링크함수 내부에 쓰이는 변수입니다. 링크된 페이지의 이름을 출력합니다.\n </pre>\n 최상위 문서 제목<input class="inp_create_target" value="{{FULLPAGENAME}}/테스트" size="40" />\n\n <textarea class="inp_create_form"> \n현재역은 {[노드네임]}입니다.\n== 선택지 ==\n{[링크|\n* [[{[링크페이지네임]}|{[링크노드네임]}역으로]]\n]}\n </textarea>\n <button class="btn_create_create"> 생성 </button>\n <div class="btn_create_message"> 문서를 생성할 준비가 다 되었으면 위 버튼 눌러주세요 </div>\n</section></div>`); }; var test = (function () { initHTML(); var gui = new GUI(); var nodeList = new DocNodeList(gui); var nodeInput = new NodeInput(gui, nodeList); nodeList.addNode("서울"); nodeList.addNode("부산"); nodeList.addNode("인천"); nodeList.addNode("대구"); nodeList.linkNode("서울", "부산"); nodeList.linkNode("서울", "인천"); nodeList.linkNode("서울", "대구"); nodeList.linkNode("대구", "부산"); setInterval(function () { return gui.draw(); }, 50); //nodeList.removeNode("미남") })();