티스토리에는 많은 치환자가 사용된다. 각 치환자의 특징과 주의할 점을 정리해보려고 한다.
s_t3
s_t3
은 티스토리 가이드에 다음과 같이 정의되어 있다.
자바스크립트를 지정해줄 영역을 선언하기 위해서
<body>
안에<s_t3></s_t3>
를 넣어줘야 합니다.
여기서 말하는 자바스크립트는 티스토리 기능이 동작하도록 하는 스크립트이다. 실제로 s_t3
을 넣은 영역 제일 상위에 다음의 스크립트가 추가된다. 티스토리 주인이냐 아니냐에 따라 조금 다른데 주인일 경우 스크립트가 더 길다. 아래는 손님에게 추가되는 내용이다. 이 스크립트가 없을경우 댓글 작성이나 단축키 등이 동작하지 않는다. 그 기능을 사용하지 않을 것이라면 없애도 된다.
<script type="text/javascript">
function secondaryDomainLogin(authData) {
if (!authData) {
return;
}
var request = new HTTPRequest("POST", '/api/secondaryDomain/');
request.async = false;
request.onSuccess = function() {
window.location.reload();
};
request.onVerify = function() {
try {
var result = eval("(" + this.getText() + ")");
return (result.error == false)
}
catch (e) {
return false;
}
};
request.send('authData=' + encodeURIComponent(authData));
}
</script>
<script type="text/javascript" src="http://www.tistory.com/api/secondaryDomain/?callback=secondaryDomainLogin&dummy=1727273845"></script>
<script type="text/javascript">
//<![CDATA[
var TOP_SSL_URL = 'https://www.tistory.com';
function processShortcut(event) {
if (isIE)
{
event = window.event;
event.target = event.srcElement;
}
if (event.altKey || event.ctrlKey || event.metaKey)
return;
switch (event.target.nodeName) {
case "INPUT":
case "SELECT":
case "TEXTAREA":
return;
}
switch (event.keyCode) {
case 81: //Q
window.location = "/admin";
break;
case 83: //S
window.location = "/378";
break;
case 90: //Z
window.location = "#recentEntries";
break;
case 88: //X
window.location = "#recentComments";
break;
case 67: //C
window.location = "#recentTrackback";
break;
}
}
document.onkeydown = processShortcut;
function addComment(caller, entryId) {
var oForm = findFormObject(caller);
if (!oForm)
return false;
var request = new HTTPRequest("POST", oForm.action);
request.onSuccess = function () {
if(entryId == 0)
window.location = blogURL + "/guestbook";
else {
document.getElementById("entry" + entryId + "Comment").innerHTML = this.getText("/response/commentBlock");
if(document.getElementById("recentComments"))
document.getElementById("recentComments").innerHTML = this.getText("/response/recentCommentBlock");
if(document.getElementById("commentCount" + entryId))
document.getElementById("commentCount" + entryId).innerHTML = this.getText("/response/commentView");
if(document.getElementById("commentCountOnRecentEntries" + entryId))
document.getElementById("commentCountOnRecentEntries" + entryId).innerHTML = "(" + this.getText("/response/commentCount") + ")";
}
if(typeof window.needCommentCaptcha !== "undefined"){
captchaPlugin.init('complete');
}
}
request.onError = function() {
var description = this.getText("/response/description");
if (description) { alert(description); }
}
var queryString = "key=tistory";
var captchaInput = document.getElementById('inputCaptcha');
if (oForm["name"])
queryString += "&name=" + encodeURIComponent(oForm["name"].value);
if (oForm["password"])
queryString += "&password=" + encodeURIComponent(oForm["password"].value);
if (oForm["homepage"])
queryString += "&homepage=" + encodeURIComponent(oForm["homepage"].value);
if (oForm["secret"] && oForm["secret"].checked)
queryString += "&secret=1";
if (oForm["comment"])
queryString += "&comment=" + encodeURIComponent(oForm["comment"].value);
if (captchaInput) {
if (!captchaInput.value) {
alert('그림문자가 입력되지 않았습니다.');
return false;
}
queryString += "&captcha=" + encodeURIComponent(captchaInput.value);
}
request.send(queryString);
}
function commentRequireLogin() {
if(confirm("'JooStory Blog' 블로그에 댓글을 남기시려면 티스토리에 로그인하셔야 합니다. \n 지금 로그인하시겠습니까?")) {
window.location = "https://www.tistory.com/login/?requestURI=http%3A%2F%2Fblog.joostory.net%2F379";
} else {
window.focus();
}
}
function commentObserverForAuth(evetObj) {
var reex = /name|password|homepage|secret|comment/;
if (isIE)
var name = evetObj.srcElement.name
else
var name = evetObj.target.name
if(reex.test(name) && !(new RegExp("^entry\\d+password$").test(name))) {
commentRequireLogin();
}
}
STD.addEventListener(document);
document.addEventListener("click", commentObserverForAuth, false);
var openWindow='';
function alignCenter(win,width,height) {
if(navigator.userAgent.indexOf("Chrome") == -1)
win.moveTo(screen.width/2-width/2,screen.height/2-height/2);
}
function deleteComment(id) {
var width = 450;
var height = 360;
try { openWindow.close(); } catch(e) { }
openWindow = window.open("/comment/delete/" + id, "tatter", "width="+width+",height="+height+",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function commentComment(parent, guestbookPage) {
var width = 450;
var height = 360;
try { openWindow.close(); } catch(e) { }
openWindow = window.open("/comment/comment/" + parent + (guestbookPage ? "?guestbookPage=" + guestbookPage: ""), "tatter", "width="+width+",height="+height+",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function editEntry(parent,child) {
var width = 1169;
var height = 600;
if(openWindow != '') openWindow.close();
openWindow = window.open("/admin/entry/edit/" + parent + "?popupEditor&returnURL=CLOSEME","tatter", "width="+1169+",height="+600+",location=0,menubar=0,resizable=1,scrollbars=1,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function guestbookComment(parent) {
var width = 450;
var height = 360;
try { openWindow.close(); } catch(e) { }
openWindow = window.open("/comment/comment/" + parent, "tatter", "width="+width+",height="+height+",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function sendTrackback(id) {
var width = 700;
var height = 500;
try { openWindow.close(); } catch(e) { }
openWindow = window.open("/trackback/send/" + id, "tatter", "width=580,height=400,location=0,menubar=0,resizable=1,scrollbars=1,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function deleteTrackback(id,entryId) {
if (confirm("트랙백을 삭제하기 위해서는 로그인이 필요합니다.\n로그인 하시겠습니까?\t")) {
window.location = "https://www.tistory.com/login/";
return false;
} else {
return false;
}
}
function reloadEntry(id) {
var password = document.getElementById("entry" + id + "password");
if (!password)
return;
document.cookie = "GUEST_PASSWORD=" + encodeURIComponent(password.value) + ";path=";
window.location.href = window.location.href;
}
loadedComments = new Array();
loadedTrackbacks = new Array();
function viewTrigger(id, category, categoryId) {
var request = new HTTPRequest("GET", "/"+category+"/view/" + id);
var target = document.getElementById('entry'+id+(category == 'comment' ? 'Comment' : 'Trackback'));
if(target == null)
return false;
request.onSuccess = function() {
target.innerHTML = this.getText("/response/result");
target.style.display = 'block';
highlight();
category == 'comment' ? loadedComments[id] = true : loadedTrackbacks[id] = true;
if(categoryId > -1)
location = location.toString();
if(category == 'trackback') {
var holder = document.getElementById('TrackbackCopyHolder' + id);
if(holder) {
holder.innerHTML = AC_FL_RunContentNotWriteGetString(
'src','http://s1.daumcdn.net/cfs.tistory/resource/620/blog/script/copyTrackback',
'Flashvars','url=http://blog.joostory.net/trackback/' + id + '&mode=whiteButton',
'width','29',
'height','18',
'id', 'TrackbackCopyButton' + id,
'wmode','transparent',
'allowScriptAccess','always',
'type','application/x-shockwave-flash',
'pluginspage','http://www.macromedia.com/go/getflashplayer'
);
}
}
if(typeof window.needCommentCaptcha !== "undefined"){
captchaPlugin.init();
}
}
request.onError = function() {
alert('실패 했습니다');
}
request.send();
}
function highlight() {
var hash = new RegExp("^#(comment\\d+)").exec(window.location.hash);
if(hash && (el = document.getElementById(hash[1])))
highlightElement(hash[1], 0, el.style.backgroundColor);
}
function highlightElement(id, amount, origColor) {
var el = document.getElementById(id);
if (!el) {
return;
}
el.style.backgroundColor = amount % 2 ? "#FFFF44" : origColor;
if (++amount < 7) {
setTimeout("highlightElement('" + id + "', " + amount + ", '" + origColor + "')", 200);
}
}
function toggleLayerForEntry(id, category, categoryId, mode) {
if((category == 'comment' ? loadedComments[id] : loadedTrackbacks[id])) {
try {
var obj = document.getElementById('entry'+id+(category == 'comment' ? 'Comment' : 'Trackback'));
if(mode == undefined)
obj.style.display = (obj.style.display == "none") ? "block" : "none";
else
obj.style.display = (mode == 'show') ? "block" : "none";
} catch (e) {
}
return true;
} else {
if(categoryId) {
viewTrigger(id,category, categoryId);
} else {
viewTrigger(id,category, -1);
}
}
}
function ObserverForAnchor(evetObj) {
var lo = location.toString();
var queryString = lo.substr(lo.lastIndexOf('/')+1);
if(queryString.indexOf('#')>-1) {
var qsElements = queryString.split('#');
if(qsElements[1].indexOf('comment')>-1) {
var category = 'comment';
} else if(qsElements[1].indexOf('trackback')>-1) {
var category = 'trackback';
}
if(category) {
entryid = qsElements[0];
categoryId = qsElements[1].substr(category.length);
toggleLayerForEntry(entryid,category,categoryId, 'show');
}
}
}
STD.addEventListener(window);
window.addEventListener("load", ObserverForAnchor, false);
//]]>
</script>
<div style="margin:0px; padding:0px; border:none; background:none; float:none; clear:none; z-index:0"></div>
header
blogmenu_
[#_blog_menu_#]
는 다음으로 치환된다.
<ul> <li class="t_menu_home first">
<a href="/">홈</a>
</li>
<li class="t_menu_tag">
<a href="/tag">태그</a>
</li>
<li class="t_menu_medialog">
<a href="/media">미디어로그</a>
</li>
<li class="t_menu_location">
<a href="/location">위치로그</a>
</li>
<li class="t_menu_guestbook last">
<a href="/guestbook">방명록</a>
</li>
</ul>
content
검색
티스토리의 검색은 parameter가 아닌 /search/[text] 같은 url로 하기 때문에 일반적인 폼으로는 검색을 구현할 수 없다. 따라서 반드시 가이드에 나온 치환자를 사용해야 한다. 가이드에는 onclick에 넣으라고 했지만 false를 return하기때문에 submit 이벤트에 넣어줘도 잘 동작한다.
태그
[#_tag_label_rep_#]
는 다음과 같이 치환된다. 콤마까지 넣어준다. 따라서 다양한 스타일링을 하기에는 무리가 있다.
<a href="/tag/angularJs" rel="tag">angularJs</a>,<a href="/tag/handlebars" rel="tag">handlebars</a>
댓글
[#_rp_rep_id_#]
는 댓글의 고유id인데 hash로 접근하기 위해서 사용한다. 따라서 댓글에 직접 접근을 할 수 있도록 하려면 넣어줘야한다.
[#_rp_rep_class_#]
는 글의 성격에 따라 관리자의 댓글은 rpadmin, 일반 사용자가 작성한 글은 rpgeneral, 비밀글은 rp_secret 으로 치환된다.
댓글쓰기
<s_rp_input_form>
는 form 태그를 만든다. class를 따로 지정할 수는 없다.
댓글이 없는 경우 [#_article_rep_rp_link_#]
는 댓글을 작성할 수 없다는 메시지를 출력하는 alert으로 치환된다.
<s_rp_count>
는 댓글이 2개 이상일 경우에만 원하는 내용을 넣을 수 있다. 2개 미만(0개, 1개)인 경우 "Comment 0" 과 같은 형태로 치환된다.
paging
_prev_page_
, _next_page_
<s_paging></s_paging>
내부에 <s_paging_rep></s_paging_rep>
로 페이지 리스트를 뿌릴 수 있다. 이전 페이지, 다음 페이지로의 이동은 [#_prev_page_#]
등으로 따로 명시해주면 되는데 통째로 href="[prevPageUrl]"
로 변환되니 href="[#_prev_page_#]"
처럼 사용하면 안된다.
s_paging_rep
<s_paging_rep></s_paging_rep>
는 루프를 돌면서 [#_paging_rep_link_#]
와 [#_paging_rep_link_num_#]
를 사용할 수 있도록 해주는데 1~ 100까지의 숫자가 있다고 한다면 "1, 2, 3, 4, ..., 100" 로 출력한다. 근데 문제는 "..." 에 있다. 무조건 <span class="interword">...</span>
가 중간에 추가되며 이를 컨트롤 할 수가 없다. li 로 페이징을 구성했다면 다음과 같은 결과를 얻게 된다.
<li><a class="num"><span class="selected">1</span></a></li>
<li><a href="/378" class="num"><span>2</span></a></li>
<li><a href="/377" class="num"><span>3</span></a></li>
<li><a href="/376" class="num"><span>4</span></a></li>
<li><a href="/375" class="num"><span>5</span></a></li>
<span class="interword">...</span>
<li><a href="/1" class="num"><span>378</span></a></li>
paging에 li를 사용해선 안된다. 이전/다음만 사용하려면 li를 사용해도 된다.
_no_more_prev_
, _no_more_next_
이전 글이 없을때 [#_no_more_prev_#]
는 no-more-prev
로 [#_no_more_next_#]
는 no-more-prev
로 변환된다. 이 class에 대한 css를 지정해두어야 한다.