채수원 2014-04-15
Merge branch 'improve/comment-form-140409' of laziel/yobi
from pull request 843
@3456851d6cab12ae4eeb15b28803f420bf928067
app/assets/stylesheets/less/_page.less
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
@@ -2622,7 +2622,11 @@
             padding-right: 2px;
             margin-bottom: 10px;
           }
+          .write-comment-box {
+            padding: 10px;
+          }
         }
+
         .comment {
             padding: 10px 0px;
             position: relative;
@@ -2895,8 +2899,8 @@
     overflow-x:auto;
     margin:0px;
     vertical-align : middle;
-    border:3px solid #ddd;
-    padding:10px;
+    border:2px solid #ccc;
+    padding:4px 6px;
     .border-radius(0 0 3px 3px);
 
 }
@@ -3776,7 +3780,6 @@
     .write-comment-box {
         font-family:@base-font-family;
         padding:5px;
-        background-color: #f5f5f5;
     }
 
     /** view **/
@@ -5034,7 +5037,6 @@
                                 form { margin:0;}
 
                                 .write-comment-box {
-                                    background-color: #f5f5f5;
                                     padding:15px 0 15px 50px;
                                     margin:0;
 
@@ -5180,7 +5182,7 @@
 
 .review-form {
     position:absolute; display: none;
-    z-index:99; top:0px; left:0px; right:0px;
+    z-index:1000; top:0px; left:0px; right:0px;
     padding: 15px 15px 10px;
     background: #fff;
     border:1px solid #ccc;
app/assets/stylesheets/less/_yobiUI.less
--- app/assets/stylesheets/less/_yobiUI.less
+++ app/assets/stylesheets/less/_yobiUI.less
@@ -461,6 +461,11 @@
             font-weight:bold;
         }
     }
+
+    &.small {
+        height:29px;
+        li a { padding:4px 15px; }
+    }
 }
 
 .tab-box {
@@ -606,7 +611,7 @@
 
 .yobiSpinner {
     display: none;
-    position: fixed; z-index: 999;
+    position: fixed; z-index: 99999;
     top: 0; bottom: 0; left: 0; right: 0;
 
     .spinContainer {
app/utils/TemplateHelper.scala
--- app/utils/TemplateHelper.scala
+++ app/utils/TemplateHelper.scala
@@ -41,6 +41,14 @@
     baseUrl + prefix + query.dropRight(1)
   }
 
+  def buildAttrString(attrMap: java.util.Map[String, String]): String = {
+    var attr = ""
+    attrMap.map {
+      v => attr += v._1 + "=" + v._2 + " "
+    }
+    attr.dropRight(1)
+  }
+
   def agoString(duration: org.joda.time.Duration) = {
     if (duration != null){
       val sec = duration.getMillis / DateTimeConstants.MILLIS_PER_SECOND
app/views/board/create.scala.html
--- app/views/board/create.scala.html
+++ app/views/board/create.scala.html
@@ -1,5 +1,6 @@
 @(title:String, form:Form[Posting], project:Project, isAllowedToNotice:Boolean)
 
+@import scala.collection.Map
 @import utils.TemplateHelper._
 @import models.enumeration._
 @implicitField = @{ helper.FieldConstructor(simpleForm) }
@@ -28,18 +29,7 @@
     			</dd>
 
                 <dd style="position: relative;">
-                    <div id="mode-select">
-                        <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                        <label for="edit-mode" style="margin-right:3px;">Edit</label>
-                        <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                        <label for="preview-mode">Preview</label>
-                    </div>
-                    <a href="#" class="go-zen"><i class="s s--zen" >Edit in Zen Mode</i></a>
-                    @help.markdown()
-    				 <div class="textarea-box">
-                        <textarea id="body" name="body" class="zen-mode content" markdown="true" tabindex="2" data-label="@Messages("post.error.emptyBody")"></textarea>
-                    </div>
-
+                    @common.editor("body", "", Map("tabindex"->"2"))
     			</dd>
 
     			@if(isAllowedToNotice){
@@ -77,10 +67,6 @@
 		"elTextarea"	 : $("#body"),
 		"sUploaderAction": "@routes.AttachmentApp.uploadFile()"
 	});
-
-	$("#title").focus();
-    $('.zen-mode').zenForm({theme: 'light'});
-    $('.s--zen').tooltip({delay: { show: 500, hide: 100 }, title: '@Messages("title.zenmode")', placement: 'left'});
 
     yobi.Mention({
         target:'body',
app/views/board/edit.scala.html
--- app/views/board/edit.scala.html
+++ app/views/board/edit.scala.html
@@ -1,5 +1,6 @@
 @(title:String, form:Form[Posting], posting:Posting, number:Long,  project:Project, isAllowedToNotice:Boolean)
 
+@import scala.collection.Map
 @import utils.TemplateHelper._
 @import models.enumeration.ResourceType
 @implicitField = @{ helper.FieldConstructor(simpleForm) }
@@ -28,17 +29,7 @@
     			</dd>
 
     		    <dd style="position: relative;">
-                    <div id="mode-select">
-                        <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                        <label for="edit-mode" style="margin-right:3px;">Edit</label>
-                        <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                        <label for="preview-mode">Preview</label>
-                    </div>
-                    <a href="#" class="go-zen" ><i class="s s--zen">Edit in Zen Mode</i></a>
-                    @help.markdown()
-                    <div class="textarea-box">
-                        <textarea id="body" name="body" class="zen-mode content" markdown="true" tabindex="2" data-label="@Messages("post.error.emptyBody")">@posting.body</textarea>
-                    </div>
+                    @common.editor("body", posting.body, Map("tabindex"->"2"))
     			</dd>
 
     			@if(isAllowedToNotice){
@@ -77,8 +68,6 @@
 		"elTextarea"	 : $("#body"),
 		"sUploaderAction": "@routes.AttachmentApp.uploadFile"
 	});
-    $('.zen-mode').zenForm({theme: 'light'});
-    $('.s--zen').tooltip({delay: { show: 500, hide: 100 }, title: '@Messages("title.zenmode")', placement: 'left'});
 
     yobi.Mention({
         target:'body',
 
app/views/board/partial_comments.scala.html (added)
+++ app/views/board/partial_comments.scala.html
@@ -0,0 +1,71 @@
+@**
+/**
+* Yobi, Project Hosting SW
+*
+* Copyright 2013 NAVER Corp.
+* http://yobi.io
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+**@
+
+@(project:Project, post:Posting)
+
+@import utils.TemplateHelper._
+@import utils.AccessControl._
+@import utils.JodaDateUtil
+
+@isAuthorComment(commentId: String) = @{
+    if(commentId == UserApp.currentUser().loginId) {"author"}
+}
+
+
+<div class="comment-header"><i class="yobicon-comments"></i> <strong>@Messages("common.comment")</strong> <strong class="num">@post.comments.size</strong></div>
+<hr class="nm" />
+
+<ul class="comments">
+@for(comment <-post.comments){
+    <li class="comment @isAuthorComment(comment.authorLoginId)" id="comment-@comment.id">
+        <div class="comment-avatar">
+            <a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
+                <img src="@User.findByLoginId(comment.authorLoginId).avatarUrl" width="32" height="32" alt="@comment.authorLoginId">
+            </a>
+        </div>
+        <div class="media-body">
+            <div class="meta-info">
+                <span class="comment_author pull-left">
+                    <a href="@routes.UserApp.userInfo(comment.authorLoginId)" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
+                        <strong>@comment.authorLoginId </strong>
+                    </a>
+                </span>
+                <a href="#comment-@comment.id" class="ago" title="@JodaDateUtil.getDateString(comment.createdDate)">@utils.TemplateHelper.agoString(comment.ago())</a>
+                <span class="act-row pull-right">
+                    @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.UPDATE)) {
+                        <button type="button" class="btn-transparent mr10" data-toggle="comment-edit" data-comment-id="@comment.id" title="@Messages("common.comment.edit")"><i class="yobicon-edit-2"></i></button>
+                    }
+                    @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)) {
+                        <button type="button" class="btn-transparent" data-toggle="comment-delete" data-request-uri="@routes.BoardApp.deleteComment(project.owner, project.name, post.getNumber, comment.id)" title="@Messages("common.comment.delete")"><i class="yobicon-trash"></i></button>
+                    }
+                </span>
+            </div>
+
+            @common.commentUpdateForm(comment.id, routes.BoardApp.newComment(project.owner, project.name, post.getNumber).toString(), comment.contents)
+
+            <div id="comment-body-@comment.id">
+                <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
+                <div class="attachments pull-right" data-resource-type="@ResourceType.NONISSUE_COMMENT" data-resource-id="@comment.id"></div>
+            </div>
+        </div>
+    </li>
+}
+</ul>
app/views/board/view.scala.html
--- app/views/board/view.scala.html
+++ app/views/board/view.scala.html
@@ -8,10 +8,6 @@
 
 @urlToPostings = @{ urlToList(request.getHeader("Referer"), routes.BoardApp.posts(project.owner, project.name).toString()) }
 
-@isAuthorComment(commentId: String) = @{
-    if(commentId == UserApp.currentUser().loginId) {"author"}
-}
-
 @projectLayout(post.title, project, utils.MenuType.BOARD){
 @projectMenu(project, utils.MenuType.BOARD, "main-menu-only")
 <div class="page-wrap-outer">
@@ -44,7 +40,7 @@
     			</div>
     		</div>
     		<div class="content markdown-wrap markdown-before" markdown="true">@post.body</div>
-    		<div class="attachments" id="attachments" data-resourceType="@ResourceType.BOARD_POST" data-resourceId="@post.id"></div>
+            <div class="attachments" id="attachments" data-resource-type="@ResourceType.BOARD_POST" data-resource-id="@post.id"></div>
     	</div>
     	<div class="board-footer board-actrow">
     	    <div class="pull-left">
@@ -68,43 +64,7 @@
 
     	@** Comment **@
     	<div id="comments" class="board-comment-wrap">
-    		<div class="comment-header"><i class="yobicon-comments"></i> <strong>@Messages("common.comment")</strong> <strong class="num">@post.comments.size</strong></div>
-    	    <hr class="nm" />
-
-    	    <ul class="comments">
-    	    @for(comment <-post.comments){
-    			<li class="comment @isAuthorComment(comment.authorLoginId)" id="comment-@comment.id">
-                    <div class="comment-avatar">
-                        <a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
-                            <img src="@User.findByLoginId(comment.authorLoginId).avatarUrl" width="32" height="32" alt="@comment.authorLoginId">
-                        </a>
-                    </div>
-                    @common.commentUpdateForm(project, ResourceType.NONISSUE_COMMENT, comment.id, routes.BoardApp.newComment(project.owner, project.name, post.getNumber).toString(), comment.contents)
-                    <div class="media-body" id="comment-body-@comment.id">
-                        <div class="meta-info">
-                            <span class="comment_author pull-left">
-                                <a href="@routes.UserApp.userInfo(comment.authorLoginId)" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
-                                    <strong>@comment.authorLoginId </strong>
-                                </a>
-                            </span>
-                            <a href="#comment-@comment.id" class="ago" title="@JodaDateUtil.getDateString(comment.createdDate)">@utils.TemplateHelper.agoString(comment.ago())</a>
-                            <span class="act-row pull-right">
-                                @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.UPDATE)) {
-                                    <button type="button" class="btn-transparent mr10" data-toggle="comment-edit" data-comment-editform-id="comment-editform-@comment.id" title="@Messages("common.comment.edit")"><i class="yobicon-edit-2"></i></button>
-                                }
-                                @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)) {
-                                    <button type="button" class="btn-transparent" data-toggle="comment-delete" data-request-uri="@routes.BoardApp.deleteComment(project.owner, project.name, post.getNumber, comment.id)" title="@Messages("common.comment.delete")"><i class="yobicon-trash"></i></button>
-                                }
-                            </span>
-                        </div>
-
-                        <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
-
-                        <div class="attachments pull-right" data-resourceType="@ResourceType.NONISSUE_COMMENT" data-resourceId="@comment.id"></div>
-                    </div>
-                </li>
-    	    }
-    		</ul>
+            @partial_comments(project, post)
 
             @common.commentForm(post.asResource(), ResourceType.NONISSUE_COMMENT, routes.BoardApp.newComment(project.owner, project.name, post.getNumber).toString())
     	</div>
app/views/code/partial_nonrange_codecomment_thread.scala.html
--- app/views/code/partial_nonrange_codecomment_thread.scala.html
+++ app/views/code/partial_nonrange_codecomment_thread.scala.html
@@ -53,7 +53,7 @@
                 </div>
 
                 <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.getContents</div>
-                <div class="attachments" data-resourceType="@comment.asResource.getType" data-resourceId="@comment.id"></div>
+                <div class="attachments" data-resource-type="@comment.asResource.getType" data-resource-id="@comment.id"></div>
             </div>
         </li>
         }
app/views/code/svnDiff.scala.html
--- app/views/code/svnDiff.scala.html
+++ app/views/code/svnDiff.scala.html
@@ -119,7 +119,7 @@
 
                             <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
 
-                            <div class="attachments" data-resourceType="@ResourceType.COMMIT_COMMENT" data-resourceId="@comment.id"></div>
+                            <div class="attachments" data-resource-type="@ResourceType.COMMIT_COMMENT" data-resource-id="@comment.id"></div>
                         </div>
                     </li>
                     }
app/views/common/commentForm.scala.html
--- app/views/common/commentForm.scala.html
+++ app/views/common/commentForm.scala.html
@@ -1,34 +1,22 @@
 @(container: models.resource.Resource, resourceType:ResourceType, action:String)
 
+@import scala.collection.Map
 @import models.enumeration.ResourceType
 @import utils.AccessControl._
 
 @if(isResourceCreatable(User.findByLoginId(session.get("loginId")), container, resourceType)){
 
     <form id="comment-form" action="@action" method="post" enctype="multipart/form-data">
-        <div class="write-comment-box">
+        <div class="write-comment-box clearbg">
+            @common.editor("contents", "", Map("id"->"comment-editor"))
+
+            @** fileUploader **@
+            @if(!UserApp.currentUser.isAnonymous){
+                @common.fileUploader(resourceType, null)
+            }
+            @** end of fileUploader **@
+
             <div class="write-comment-wrap">
-                <div id="mode-select" data-toggle="buttons">
-                    <ul class="nav nav-tabs">
-                        <li class="active">
-                            <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                            <a href="#" data-toggle="tab" onclick="$('#edit-mode').trigger('click')">Edit</a>
-                        </li>
-                        <li>
-                            <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                            <a href="#" onclick="$('#preview-mode').trigger('click')" data-toggle="tab">Preview</a>
-                        </li>
-                    </ul>
-                </div>
-                @help.markdown()
-                <div class="textarea-box">
-                    <textarea id="comment-editor" name="contents" class="comment" markdown="true"></textarea>
-                </div>
-                @** fileUploader **@
-                @if(!UserApp.currentUser.isAnonymous) {
-                    @common.fileUploader(resourceType, null)
-                }
-                @** end of fileUploader **@
                 <div class="right-txt">
                     <button type="button" class="ybtn hidden" id="dynamic-comment-btn"></button>
                     <button type="submit" class="ybtn ybtn-success">@Messages("button.comment.new")</button>
app/views/common/commentUpdateForm.scala.html
--- app/views/common/commentUpdateForm.scala.html
+++ app/views/common/commentUpdateForm.scala.html
@@ -1,32 +1,18 @@
-@(project:Project, resourceType:ResourceType, resourceId:Long, action:String, contents:String)
+@(commentId:Long, action:String, contents:String)
 
-@import models.enumeration.ResourceType
-    <div id="comment-editform-@resourceId" class="comment-update-form">
-    <form id="comment-update-form" action="@action" method="post">
+<div id="comment-editform-@commentId" class="comment-update-form">
+    <form action="@action" method="post">
+        <input type="hidden" name="id" value="@commentId">
+
         <div class="write-comment-box">
             <div class="write-comment-wrap">
-                <div id="mode-select" data-toggle="buttons">
-                    <ul class="nav nav-tabs">
-                    <li class="active">
-                        <input type="radio" name="edit-mode" id="edit-mode-@resourceId" value="edit" checked="checked" class="radio-btn">
-                        <a href="#" data-toggle="tab" onclick="$('#edit-mode-@resourceId').trigger('click')">Edit</a>
-                    </li>
-                    <li>
-                        <input type="radio" name="edit-mode" id="preview-mode-@resourceId" value="preview" class="radio-btn">
-                        <a href="#" onclick="$('#preview-mode-@resourceId').trigger('click')" data-toggle="tab">Preview</a>
-                    </li>
-                    </ul>
-                </div>
-                @help.markdown()
-                <input type="hidden" name="id" value="@resourceId">
-                <div class="textarea-box">
-                    <textarea id="comment-editor" name="contents" class="comment comment-editor" markdown="true">@contents</textarea>
-                </div>
+                @common.editor("contents", contents)
+
                 <div class="right-txt comment-update-button">
-                    <button type="button" class="ybtn ybtn-cancel" data-comment-body-id="comment-body-@resourceId">@Messages("button.cancel")</button>
+                    <button type="button" class="ybtn ybtn-cancel" data-comment-id="@commentId">@Messages("button.cancel")</button>
                     <button type="submit" class="ybtn ybtn-info">@Messages("button.save")</button>
                 </div>
             </div>
         </div>
     </form>
-    </div>
+</div>
 
app/views/common/editor.scala.html (added)
+++ app/views/common/editor.scala.html
@@ -0,0 +1,52 @@
+@**
+* Yobi, Project Hosting SW
+*
+* Copyright 2014 NAVER Corp.
+* http://yobi.io
+*
+* @Author JiHan Kim
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+**@
+@(editorName:String = "contents", textValue:String="", textareaAttr:Map[String,String]=null)
+
+@import org.apache.commons.lang3.RandomStringUtils._
+@import utils.TemplateHelper._
+
+@defining(randomAlphabetic(8)){ wrapId =>
+<div data-toggle="markdown-editor" class="mt10">
+    <ul class="nav nav-tabs nm small">
+        <li class="active">
+            <a href="#edit-@wrapId" data-toggle="tab" data-mode="edit">@Messages("common.editor.edit")</a>
+        </li>
+        <li>
+            <a href="#preview-@wrapId" data-toggle="tab" data-mode="preview">@Messages("common.editor.preview")</a>
+        </li>
+    </ul>
+
+    <div class="tab-content" style="position:relative;">
+        @help.markdown()
+
+        <div id="edit-@wrapId" class="tab-pane active">
+            <div class="textarea-box">
+                <textarea name="@editorName" class="content comment nm" markdown="true"
+                    @if(textareaAttr != null){ @buildAttrString(textareaAttr) }>@textValue</textarea>
+            </div>
+        </div>
+
+        <div id="preview-@wrapId" class="tab-pane">
+            <div class="markdown-preview markdown-wrap"></div>
+        </div>
+    </div>
+</div>
+}
app/views/common/fileUploader.scala.html
--- app/views/common/fileUploader.scala.html
+++ app/views/common/fileUploader.scala.html
@@ -1,23 +1,6 @@
-@(resType:ResourceType, resId:Long)
+@(resourceType:ResourceType, resourceId:Long)
 
-<div id="upload" class="upload-wrap content-footer" data-resourceType="@resType" @if(resId != null){data-resourceId="@resId"}>
-    <div class="attach-wrap">
-        <span class="help help-droppable">@Messages("common.attach.drophere")</span>
-        <div class="btn-wrap">
-             <div class="nbtn medium white fake-file-wrap">
-                 <i class="yobicon-upload"></i> @Messages("button.upload")<!--
-             --> <input type="file" class="file" name="filePath" multiple="multiple">
-             </div>
-        </div>
-        <span class="plain">@Messages("common.attach.clickbutton")</span>
-        <span class="help help-pastable">@Messages("common.attach.pastehere")</span>
-    </div>
-
-    <ul class="attached-files unstyled"></ul>
-    <p class="right-txt help">
-        <i class="yobicon-supportrequest"></i> @Messages("common.attach.attachIfYouSave")
-    </p>
-</div>
+@common.uploadForm(resourceType, resourceId, "upload")
 
 <script type="text/x-jquery-tmpl" id="tplAttachedFile">
     <li class="attached-file" data-id="${fileId}" data-name="${fileName}" data-href="${fileHref}" data-mime="${mimeType}" data-size="${fileSize}">
app/views/common/markdown.scala.html
--- app/views/common/markdown.scala.html
+++ app/views/common/markdown.scala.html
@@ -1,7 +1,5 @@
 @(project: Project = null)
 
-@import utils.TemplateHelper._
-
 <link rel="stylesheet" type="text/css" href="@routes.Assets.at("javascripts/lib/highlight/styles/default.css")" />
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/highlight/highlight.pack.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/marked.js")"></script>
@@ -9,7 +7,6 @@
 <script type="text/javascript">
 $(document).ready(function(){
     var htOptions = {
-        "sTplSwitch": $("#tplMarkdownPreview").text(),
         "bBreaks": ($('[markdown]').hasClass('readme-body')) ? false : true
     };
 
@@ -21,11 +18,4 @@
     // Reusable markdown renderer
     yobi.Markdown.init(htOptions);
 });
-</script>
-
-<script type="text/x-jquery-tmpl" id="tplMarkdownPreview">
-<input type="radio" name="edit-mode" value="edit" checked="checked" class="radio-btn" />
-<label for="edit-mode" style="margin-right:3px;">Edit</label>
-<input type="radio" name="edit-mode" value="preview" class="radio-btn" />
-<label for="preview-mode">Preview</label>
 </script>
app/views/common/reviewForm.scala.html
--- app/views/common/reviewForm.scala.html
+++ app/views/common/reviewForm.scala.html
@@ -38,31 +38,11 @@
         </div>
         <div class="write-comment-box nm">
             <div class="write-comment-wrap">
-                @help.markdown()
-                <div class="textarea-box">
-                    <textarea name="contents" class="text comment" markdown="true"></textarea>
-                </div>
+                @common.editor("contents")
 
                 @** fileUploader **@
                 @if(!UserApp.currentUser.isAnonymous){
-                <div class="upload-wrap content-footer" data-resourceType="@resourceType">
-                    <div class="attach-wrap">
-                        <span class="help help-droppable">@Messages("common.attach.drophere")</span>
-                        <div class="btn-wrap">
-                             <div class="nbtn medium white fake-file-wrap">
-                                 <i class="yobicon-upload"></i> @Messages("button.upload")<!--
-                             --> <input type="file" class="file" name="filePath" multiple="multiple">
-                             </div>
-                        </div>
-                        <span class="plain">@Messages("common.attach.clickbutton")</span>
-                        <span class="help help-pastable">@Messages("common.attach.pastehere")</span>
-                    </div>
-
-                    <ul class="attached-files unstyled"></ul>
-                    <p class="right-txt help">
-                        <i class="yobicon-supportrequest"></i> @Messages("common.attach.attachIfYouSave")
-                    </p>
-                </div>
+                    @common.uploadForm(resourceType)
                 }
                 @** end of fileUploader **@
 
 
app/views/common/uploadForm.scala.html (added)
+++ app/views/common/uploadForm.scala.html
@@ -0,0 +1,44 @@
+@**
+* Yobi, Project Hosting SW
+*
+* Copyright 2014 NAVER Corp.
+* http://yobi.io
+*
+* @Author JiHan Kim
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+**@
+@(resourceType:ResourceType, resourceId:Long=null, formId:String=null)
+<div
+    class="upload-wrap content-footer"
+    data-resource-type="@resourceType"
+    @if(resourceId != null){data-resource-id="@resourceId"}
+    @if(formId != null){id="@formId"}
+>
+    <div class="attach-wrap">
+        <span class="help help-droppable">@Messages("common.attach.drophere")</span>
+        <div class="btn-wrap">
+            <div class="nbtn medium white fake-file-wrap">
+                <i class="yobicon-upload"></i> @Messages("button.upload")
+                <input type="file" class="file" name="filePath" multiple="multiple">
+            </div>
+        </div>
+        <span class="plain">@Messages("common.attach.clickbutton")</span>
+        <span class="help help-pastable">@Messages("common.attach.pastehere")</span>
+    </div>
+
+    <ul class="attached-files unstyled"></ul>
+    <p class="right-txt help">
+        <i class="yobicon-supportrequest"></i> @Messages("common.attach.attachIfYouSave")
+    </p>
+</div>
app/views/git/edit.scala.html
--- app/views/git/edit.scala.html
+++ app/views/git/edit.scala.html
@@ -1,5 +1,6 @@
 @(title:String, form: Form[PullRequest], project: Project, fromBranches: List[playRepository.GitBranch], toBranches: List[playRepository.GitBranch], pull: PullRequest)
 
+@import scala.collection.Map
 @import utils.TemplateHelper._
 @import utils.TemplateHelper.Branches._
 @implicitField = @{ helper.FieldConstructor(simpleForm) }
@@ -96,17 +97,7 @@
 
                 <input type="text" name="title" id="title" placeholder="" maxlength="maxlength" tabindex="1" class="text" value="@pull.title">
                 <div style="position: relative;">
-                    <div id="mode-select">
-                        <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                        <label for="edit-mode" style="margin-right:3px;">Edit</label>
-                        <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                        <label for="preview-mode">Preview</label>
-                    </div>
-                    <a href="#" class="go-zen"><i class="s s--zen">Edit in Zen Mode</i></a>
-                    @help.markdown()
-                    <div class="textarea-box">
-                        <textarea id="body" name="body" markdown="true" class="zen-mode content" tabindex="2" >@pull.body</textarea>
-                    </div>
+                    @common.editor("body", pull.body, Map("tabindex"->"2"))
                 </div>
 
                 @common.fileUploader(ResourceType.PULL_REQUEST, pull.id)
app/views/git/partial_diff.scala.html
--- app/views/git/partial_diff.scala.html
+++ app/views/git/partial_diff.scala.html
@@ -5,11 +5,9 @@
 @import utils.AccessControl._
 @import models.enumeration
 @import playRepository.GitCommit
-@implicitField = @{ helper.FieldConstructor(simpleForm) }
+@import scala.collection.Map
 
-@branchName(branch:String) = @{
-    branch.replace("refs/heads/", "")
-}
+@implicitField = @{ helper.FieldConstructor(simpleForm) }
 
 @getCodeURL(project: Project) = @{
     if(session == null){
@@ -27,17 +25,7 @@
 
 <input type="text" name="title" id="title" placeholder="" maxlength="maxlength" tabindex="1" class="text" value="@pullRequest.title">
 <div style="position: relative;">
-    <div id="mode-select">
-        <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-        <label for="edit-mode" style="margin-right:3px;">Edit</label>
-        <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-        <label for="preview-mode">Preview</label>
-    </div>
-    <a href="#" class="go-zen"><i class="s s--zen">Edit in Zen Mode</i></a>
-    @help.markdown()
-    <div class="textarea-box">
-        <textarea id="body" name="body" markdown="true" class="zen-mode content" tabindex="2" >@pullRequest.body</textarea>
-    </div>
+    @common.editor("body", pullRequest.body, Map("tabindex"->"2"))
 </div>
 @common.fileUploader(ResourceType.PULL_REQUEST, null)
 @common.markdown(project)
app/views/git/view.scala.html
--- app/views/git/view.scala.html
+++ app/views/git/view.scala.html
@@ -17,7 +17,7 @@
             <div class="span9">
                 @partial_branch(pull)
                 <div class="content markdown-wrap markdown-before" markdown="true">@pull.body</div>
-                <div class="attachments" data-resourceType="@ResourceType.PULL_REQUEST" data-resourceId="@pull.id"></div>
+                <div class="attachments" data-resource-type="@ResourceType.PULL_REQUEST" data-resource-id="@pull.id"></div>
             </div>
             <div class="span3">
                 <div class="author-info">
app/views/issue/create.scala.html
--- app/views/issue/create.scala.html
+++ app/views/issue/create.scala.html
@@ -38,17 +38,7 @@
                 <div class="span9">
                     <dl>
                         <dd style="position: relative;">
-                            <div id="mode-select">
-                                <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                                <label for="edit-mode" style="margin-right:3px;">Edit</label>
-                                <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                                <label for="preview-mode">Preview</label>
-                            </div>
-                            <a href="#" class="go-zen"><i class="s s--zen">Edit in Zen Mode</i></a>
-                            @help.markdown()
-                            <div class="textarea-box">
-                                <textarea id="body" name="body" markdown="true" class="zen-mode content nm" tabindex="2" data-label="@Messages("issue.error.emptyBody")"></textarea>
-                            </div>
+                            @common.editor("body", "", Map("tabindex"->"2"))
                         </dd>
                     </dl>
 
app/views/issue/edit.scala.html
--- app/views/issue/edit.scala.html
+++ app/views/issue/edit.scala.html
@@ -39,17 +39,7 @@
                 <div class="span9">
                     <dl>
                         <dd style="position: relative;">
-                            <div id="mode-select">
-                                <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                                <label for="edit-mode" style="margin-right:3px;">Edit</label>
-                                <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                                <label for="preview-mode">Preview</label>
-                            </div>
-                            <a href="#" class="go-zen"><i class="s s--zen">Edit in Zen Mode</i></a>
-                            @help.markdown()
-                            <div class="textarea-box">
-                                <textarea id="body" name="body" markdown="true" class="zen-mode content nm" tabindex="2">@issue.body</textarea>
-                            </div>
+                            @common.editor("body", issue.body, Map("tabindex"->"2"))
                         </dd>
                     </dl>
 
app/views/issue/partial_comments.scala.html
--- app/views/issue/partial_comments.scala.html
+++ app/views/issue/partial_comments.scala.html
@@ -77,8 +77,7 @@
                 <img src="@User.findByLoginId(comment.authorLoginId).avatarUrl" width="32" height="32" alt="@comment.authorLoginId">
             </a>
         </div>
-        @common.commentUpdateForm(project, ResourceType.ISSUE_COMMENT, comment.id, routes.IssueApp.newComment(project.owner, project.name, issue.getNumber).toString(), comment.contents)
-        <div class="media-body" id="comment-body-@comment.id">
+        <div class="media-body">
             <div class="meta-info">
                 <span class="comment_author pull-left">
                     <a href="@routes.UserApp.userInfo(comment.authorLoginId)" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
@@ -88,7 +87,7 @@
                 <a href="#comment-@comment.id" class="ago" title="@JodaDateUtil.getDateString(comment.createdDate)">@utils.TemplateHelper.agoString(comment.ago())</a>
                 <span class="act-row pull-right">
                     @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.UPDATE)) {
-                        <button type="button" class="btn-transparent mr10" data-toggle="comment-edit" data-comment-editform-id="comment-editform-@comment.id" title="@Messages("common.comment.edit")"><i class="yobicon-edit-2"></i></button>
+                        <button type="button" class="btn-transparent mr10" data-toggle="comment-edit" data-comment-id="@comment.id" title="@Messages("common.comment.edit")"><i class="yobicon-edit-2"></i></button>
                     }
 
                     @if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)) {
@@ -97,8 +96,12 @@
                 </span>
             </div>
 
-            <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
-            <div class="attachments pull-right" data-resourceType="@ResourceType.ISSUE_COMMENT" data-resourceId="@comment.id"></div>
+            @common.commentUpdateForm(comment.id, routes.IssueApp.newComment(project.owner, project.name, issue.getNumber).toString(), comment.contents)
+
+            <div id="comment-body-@comment.id">
+                <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.contents</div>
+                <div class="attachments pull-right" data-resource-type="@ResourceType.ISSUE_COMMENT" data-resource-id="@comment.id"></div>
+            </div>
         </div>
     </li>
 
app/views/issue/view.scala.html
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
@@ -74,7 +74,7 @@
                 } else {
                 <div class="content markdown-wrap markdown-before" markdown="true">@issue.body</div>
                 }
-                <div class="attachments" id="attachments" data-resourceType="@ResourceType.ISSUE_POST" data-resourceId="@issue.id"></div>
+                <div class="attachments" id="attachments" data-resource-type="@ResourceType.ISSUE_POST" data-resource-id="@issue.id"></div>
             </div>
 
             <div class="span3 mb20">
app/views/layout.scala.html
--- app/views/layout.scala.html
+++ app/views/layout.scala.html
@@ -1,5 +1,4 @@
 @(title: String)(theme:String)(content: Html)
-@import utils.TemplateHelper._
 <!DOCTYPE html>
 <html>
 <head>
@@ -11,14 +10,12 @@
 <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.ico")">
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("bootstrap/css/bootstrap.css")">
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("stylesheets/yobicon/style.css")">
-<link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/zenform/zen-form.css")">
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/select2/select2.css")"/>
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("stylesheets/yobi.css")">
 
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/jquery/jquery-1.9.0.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/jquery/jquery.pjax.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.Common.js")"></script>
-<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/zenform/zen-form.js")"></script>
 </head>
 
 <body class="@theme">
app/views/milestone/create.scala.html
--- app/views/milestone/create.scala.html
+++ app/views/milestone/create.scala.html
@@ -2,6 +2,7 @@
 
 @import utils.TemplateHelper._
 @import models.enumeration._
+@import scala.collection.Map
 
 @projectLayout(title, project, utils.MenuType.MILESTONE) {
 @projectMenu(project, utils.MenuType.MILESTONE, "")
@@ -18,17 +19,7 @@
                         <div class="content-wrap">
                             <label for="contents">@Messages("milestone.form.content")</label>
                             <div style="position: relative;">
-                                <div id="mode-select">
-                                    <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                                    <label for="edit-mode" style="margin-right:3px;">Edit</label>
-                                    <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                                    <label for="preview-mode">Preview</label>
-                                </div>
-                                <a href="#" class="go-zen"><i class="s s--zen">Edit in Zen Mode</i></a>
-                                @help.markdown()
-                                <div class="textarea-box">
-                                    <textarea id="contents" class="content" name="contents" markdown="true"></textarea>
-                                </div>
+                                @common.editor("contents", "", Map("id"->"contents"))
                             </div>
                         </div>
 
app/views/milestone/edit.scala.html
--- app/views/milestone/edit.scala.html
+++ app/views/milestone/edit.scala.html
@@ -2,6 +2,8 @@
 
 @import utils.TemplateHelper._
 @import models.enumeration._
+@import scala.collection.Map
+
 @implicitField = @{ helper.FieldConstructor(simpleForm) }
 
 @projectLayout(title, project, utils.MenuType.MILESTONE) {
@@ -21,17 +23,7 @@
                     <label for="contents">@Messages("milestone.form.content")</label>
                     @helper.input(form("contents")){(id, name, value, args) =>
                     <div style="position: relative;">
-                        <div id="mode-select">
-                            <input type="radio" name="edit-mode" id="edit-mode" value="edit" checked="checked" class="radio-btn">
-                            <label for="edit-mode" style="margin-right:3px;">Edit</label>
-                            <input type="radio" name="edit-mode" id="preview-mode" value="preview" class="radio-btn">
-                            <label for="preview-mode">Preview</label>
-                        </div>
-                        <a href="#" class="go-zen"><i class="s s--zen">Edit in Zen Mode</i></a>
-                        @help.markdown()
-                        <div class="textarea-box">
-                            <textarea id="contents" class="content" id="@id" name="@name" markdown="true">@value</textarea>
-                        </div>
+                        @common.editor("contents", value.getOrElse(""), Map("id"->"contents"))
                     </div>
                     }
                 </div>
app/views/milestone/view.scala.html
--- app/views/milestone/view.scala.html
+++ app/views/milestone/view.scala.html
@@ -39,7 +39,7 @@
             </div>
             <div class="milestone-desc">
                 <div markdown="true" class="markdown-wrap">@milestone.contents</div>
-                <div class="attachments" data-resourceType="@ResourceType.MILESTONE" data-resourceId="@milestone.id"></div>
+                <div class="attachments" data-resource-type="@ResourceType.MILESTONE" data-resource-id="@milestone.id"></div>
             </div>
 
             <div id="issues">
app/views/partial_comment_thread.scala.html
--- app/views/partial_comment_thread.scala.html
+++ app/views/partial_comment_thread.scala.html
@@ -63,7 +63,7 @@
                 </div>
 
                 <div class="comment-body markdown-wrap markdown-before" markdown="true">@comment.getContents</div>
-                <div class="attachments" data-resourceType="@comment.asResource.getType" data-resourceId="@comment.id"></div>
+                <div class="attachments" data-resource-type="@comment.asResource.getType" data-resource-id="@comment.id"></div>
             </div>
         </li>
         }
app/views/welcome/restart.scala.html
--- app/views/welcome/restart.scala.html
+++ app/views/welcome/restart.scala.html
@@ -11,12 +11,10 @@
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("bootstrap/css/bootstrap.css")">
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("stylesheets/yobi.css")">
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("stylesheets/yobicon/style.css")">
-<link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/zenform/zen-form.css")">
 
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/jquery/jquery-1.9.0.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/jquery/jquery.pjax.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.Common.js")"></script>
-<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/zenform/zen-form.js")"></script>
 <style type="text/css">
     .secret-box { width:50%; margin:20px auto; }
     .secret-wrap { padding:50px 0; text-align:center; }
app/views/welcome/secret.scala.html
--- app/views/welcome/secret.scala.html
+++ app/views/welcome/secret.scala.html
@@ -12,12 +12,10 @@
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("bootstrap/css/bootstrap.css")">
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("stylesheets/yobi.css")">
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("stylesheets/yobicon/style.css")">
-<link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/zenform/zen-form.css")">
 
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/jquery/jquery-1.9.0.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/jquery/jquery.pjax.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/common/yobi.Common.js")"></script>
-<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/zenform/zen-form.js")"></script>
 <style type="text/css">
     .secret-box { width:50%; margin:20px auto; }
     .secret-wrap { text-align:center; }
conf/messages
--- conf/messages
+++ conf/messages
@@ -128,6 +128,8 @@
 common.comment.delete = Delete Comment
 common.comment.delete.confirm = If this is deleted, then it cannot be recovered. Is it okay and go through?
 common.comment.edit = comment edit
+common.editor.edit = Edit
+common.editor.preview = Preview
 common.experimental = Experimental
 common.experimental.description = It means that can be changed or interrupted at any time.<br>Appreciate for your generosity.
 common.experimental.title = Experimental: this feature is on development.
conf/messages.ko
--- conf/messages.ko
+++ conf/messages.ko
@@ -128,6 +128,8 @@
 common.comment.delete = 댓글 삭제
 common.comment.delete.confirm = 해당 댓글이 삭제되면 영원히 복구할 수 없습니다. 그래도 삭제하시겠습니까?
 common.comment.edit = 댓글 수정
+common.editor.edit = 편집
+common.editor.preview = 미리보기
 common.experimental = 실험적인 기능
 common.experimental.description = 이 기능은 아직 개발 진행 중으로 언제든지 변경되거나 개발 중단될 수 있습니다.<br>너그러운 마음으로 응원해주세요.
 common.experimental.title = 실험적인 기능: 새롭게 개발 중인 기능을 선보입니다
public/javascripts/common/yobi.CodeCommentBox.js
--- public/javascripts/common/yobi.CodeCommentBox.js
+++ public/javascripts/common/yobi.CodeCommentBox.js
@@ -85,20 +85,6 @@
         htElement.welCommentForm.on("click", '[data-toggle="close"]', function(){
             _hide();
         });
-
-        htElement.welCommentForm.on("submit", function(){
-            _removeEmptyFieldsOnForm();
-        });
-    }
-
-    /**
-     * Remove empty INPUT elements
-     * @private
-     */
-    function _removeEmptyFieldsOnForm(){
-        htElement.welCommentForm.find("input").filter(function(){
-            return ($(this).val().length === 0);
-        }).remove();
     }
 
     /**
public/javascripts/common/yobi.Comment.js
--- public/javascripts/common/yobi.Comment.js
+++ public/javascripts/common/yobi.Comment.js
@@ -44,16 +44,15 @@
      */
     function _attachEvent() {
         htElement.welContainer.on('click', '[data-toggle="comment-delete"]', _openDeleteModal);
-        htElement.welContainer.on('click', '[data-toggle="comment-edit"]', function _editFormShowToggle(){
-            var editformId = $(this).data('comment-editform-id');
-            $('#' + editformId).toggle();
-            $(this).parents('.media-body').toggle();
-        });
-        htElement.welContainer.on('click', '.ybtn-cancel', function _hideEditForm(){
-            $(this).parents('.comment-update-form').toggle();
-            var commentBodyId = $(this).data('comment-body-id');
-            $('#' + commentBodyId).toggle();
-        });
+        htElement.welContainer.on('click', '[data-toggle="comment-edit"]', _toggleEditForm);
+        htElement.welContainer.on('click', '.ybtn-cancel', _toggleEditForm);
+    }
+
+    function _toggleEditForm(){
+        var commentId = $(this).data("commentId");
+
+        $('#comment-editform-' + commentId).toggle();
+        $('#comment-body-' + commentId).toggle();
     }
 
     /**
public/javascripts/common/yobi.Markdown.js
--- public/javascripts/common/yobi.Markdown.js
+++ public/javascripts/common/yobi.Markdown.js
@@ -9,7 +9,6 @@
 yobi.Markdown = (function(htOptions){
 
     var htVar = {};
-    var htElement = {};
 
     /**
      * initialize
@@ -27,10 +26,10 @@
      * @param {Hash Table} htOptions
      */
     function _initVar(htOptions){
-        htVar.sTplSwitch = htOptions.sTplSwitch;
         htVar.sIssuesUrl = htOptions.sIssuesUrl;
         htVar.sProjectUrl = htOptions.sProjectUrl;
         htVar.bBreaks = htOptions.bBreaks;
+
         htVar.sUserRules = '[a-z0-9_\\-\\.]';
         htVar.sProjecRules = '[a-z0-9_\\-\\.]';
         htVar.sIssueRules = '\\d';
@@ -176,31 +175,33 @@
     }
 
     /**
+     * set Markdown Viewer
+     *
+     * @param {Wrapped Element} welTarget is not <textarea> or <input>
+     */
+    function _setViewer(welTarget) {
+        var sMarkdownText = welTarget.text();
+        var sContentBody  = (sMarkdownText) ? _renderMarkdown(sMarkdownText) : welTarget.html();
+        welTarget.html(sContentBody).removeClass('markdown-before');
+    }
+
+    /**
      * set Markdown Editor
      *
      * @param {Wrapped Element} welTextarea
      */
-    function _setEditor(welTextarea) {
-        // create new preview area
-        var welPreview = $('<div class="markdown-preview markdown-wrap">');
-        var welTextareaBox = welTextarea.parents('.write-comment-wrap').find('.textarea-box');
+    function _setEditor(welTextarea){
+        var elContainer = welTextarea.parents('[data-toggle="markdown-editor"]').get(0);
 
-        welPreview.css({
-            "display"   : "none",
-            "min-height": welTextarea.height() + 'px'
-        });
+        if(!elContainer){
+            return false;
+        }
 
-        var welPreviewSwitch = welTextarea.parents('.write-comment-wrap').find('input[name="edit-mode"]');
-
-        var fOnChangeSwitch = function() {
-            var bPreview = (welTextarea.parents('.write-comment-wrap').find("input:radio[name=edit-mode]:checked").val() == "preview");
+        $(elContainer).on("click", 'a[data-mode="preview"]', function(weEvt){
+            var welPreview = $(weEvt.delegateTarget).find("div.markdown-preview");
             welPreview.html(_renderMarkdown(welTextarea.val()));
-            (bPreview ? welPreview: welTextareaBox).show();
-            (bPreview ? welTextareaBox: welPreview).hide();
-        };
-
-        welPreviewSwitch.change(fOnChangeSwitch);
-        welTextareaBox.before(welPreview);
+            welPreview.css({"min-height": welTextarea.height() + 'px'});
+        });
 
         welTextarea.on("keydown.tabkey-event-handler", function(e) {
             if(e.keyCode === 9){ //tab
@@ -211,17 +212,6 @@
                 this.selectionEnd = start + 1;
             }
         });
-    }
-
-    /**
-     * set Markdown Viewer
-     *
-     * @param {Wrapped Element} welTarget is not <textarea> or <input>
-     */
-    function _setViewer(welTarget) {
-        var sMarkdownText = welTarget.text();
-        var sContentBody  = (sMarkdownText) ? _renderMarkdown(sMarkdownText) : welTarget.html();
-        welTarget.html(sContentBody).removeClass('markdown-before');
     }
 
     /**
@@ -238,7 +228,7 @@
     }
 
     /**
-     * Returns that specifieid element is editable
+     * Returns that specified element is editable
      *
      * @param {HTMLElement} elTarget
      * @return {Boolean}
 
public/javascripts/lib/zenform/zen-form-sprites.png (Binary) (deleted)
--- public/javascripts/lib/zenform/zen-form-sprites.png
Binary file is not shown
 
public/javascripts/lib/zenform/zen-form.css (deleted)
--- public/javascripts/lib/zenform/zen-form.css
@@ -1,365 +0,0 @@
-/* ================================== *\
- * Zen Form
- * ================================== */
-
-.zen-forms {
-	font: normal 18px/1.2 "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
-	position: absolute;
-	z-index: 99999;
-	top: 0;
-	left: 0;
-	right: 0;
-	min-height: 100%;
-	padding: 24px 40px 24px 24px;
-	-webkit-box-sizing: border-box;
-	   -moz-box-sizing: border-box;
-	        box-sizing: border-box;
-}
-
-.zen-forms-body-wrap {
-	width: 0;
-	height: 0;
-	overflow: hidden;
-}
-
-body {
-	max-width: 100%;
-}
-
-.zen-forms-input-wrap {
-	max-width: 640px;
-	position: relative;
-	margin: 0 auto;
-}
-
-/**
- * Buttons style
- */
-
-.zen-forms-header {
-	max-width: 640px;
-	text-align: right;
-	margin: 0 auto 1em auto;
-}
-
-.zen-forms-close-button,
-.zen-forms-theme-switch {
-	cursor: pointer;
-	font-size: .8em;
-	padding: 0 .5em;
-	line-height: 2;
-	display: inline-block;
-	vertical-align: top;
-}
-
-.zen-icon {
-	background-image: url(zen-form-sprites.png);
-	display: inline-block;
-	line-height: 1;
-	position: relative;
-	vertical-align: middle;
-	zoom: 1;
-	width: 16px;
-	height: 16px;
-	top: -1px;
-	overflow: hidden;
-   *text-indent: -9999px;
-}
-
-.zen-icon:before {
-	content: "";
-	display: block;
-	width: 0;
-	height: 100%;
-}
-
-.light-theme .zen-forms-close-button:hover .zen-icon,
-.zen-icon--close {
-	background-position: -16px 0;
-}
-
-.light-theme .zen-forms-theme-switch:hover .zen-icon,
-.zen-icon--theme {
-	background-position: 0 0;
-}
-
-.light-theme .zen-icon--close,
-.zen-forms-close-button:hover .zen-icon {
-	background-position: -16px -16px;
-}
-
-.light-theme .zen-icon--theme,
-.zen-forms-theme-switch:hover .zen-icon {
-	background-position: 0 -16px;
-}
-
-
-/**
- * Inputs basic style
- */
-
-.zen-forms-input-wrap:after {
-	clear: both;
-	content: '';
-	display: table;
-	margin-bottom: 2px;
-}
-
-.zen-forms .input {
-	box-shadow: none;
-	background: none;
-	text-shadow: none;
-	padding: 7px 0;
-	width: 100%;
-	border-radius: 0;
-	border: none;
-	margin: 0;
-	cursor: pointer;
-	font: inherit;
-}
-
-.zen-forms .input:focus {
-	cursor: text;
-}
-
-.zen-forms .select {
-	display: none;
-}
-
-.zen-forms .custom-select-wrap + label {
-	padding: 8px 3px;
-	position: static;
-	display: inline-block;
-	width: auto;
-	float: left;
-}
-
-.zen-forms .custom-select-wrap {
-	display: inline-block;
-	vertical-align: top;
-	max-width: 100%;
-}
-
-.zen-forms .custom-select {
-	border-radius: 5px;
-	top: 0;
-	right: 0;
-	left: 0;
-	overflow: auto;
-	max-height: 300px;
-}
-
-.zen-forms .custom-select a {
-	display: inline-block;
-	display: block;
-	text-decoration: none;
-	padding: 6px 10px;
-	display: none;
-	max-width: 100%;
-	overflow: hidden;
-	text-overflow: ellipsis;
-}
-
-.zen-forms .custom-select span {
-	position: relative;
-	white-space: nowrap;
-	overflow: hidden;
-	text-overflow: ellipsis;
-	max-width: 100%;
-}
-
-.zen-forms .custom-select.is-open a,
-.zen-forms .custom-select .selected {
-	position: static;
-	display: block;
-}
-
-.zen-forms textarea {
-	resize: none;
-}
-
-.zen-forms label {
-	cursor: pointer;
-	padding: 8px 8px 8px 0;
-	display: inline-block;
-	vertical-align: top;
-	font: inherit;
-	-webkit-box-sizing: border-box;
-	   -moz-box-sizing: border-box;
-	        box-sizing: border-box;
-}
-
-.zen-forms .input + label {
-	position: absolute;
-	display: none;
-	top: 0;
-	width: 100%;
-}
-
-.zen-forms .empty + label {
-	display: block;
-}
-
-.zen-forms .input:focus + label {
-	display: none;
-}
-
-@media (min-width: 1024px) {
-
-	.zen-forms .input:focus + label {
-		display: block;
-		background: #000;
-		font-size: .8em;
-		padding: 0 .5em;
-		line-height: 2;
-		border-radius: 3px;
-		margin: 5px 15px 0 0;
-		right: 100%;
-		width: auto;
-		white-space: nowrap;
-		color: #fff;
-		opacity: .5;
-	}
-
-	.zen-forms .input:focus + label:after {
-		content: '';
-		top: 50%;
-		left: 100%;
-		width: 0px;
-		height: 0px;
-		margin-top: -4px;
-		position: absolute;
-		border-style: solid;
-		border-width: 4px 0 4px 4px;
-		border-color: transparent transparent transparent #000000;
-	}
-
-}
-
-.zen-forms :focus {
-	outline-color: transparent;
-	outline-style: none;
-}
-
-/**
- * Dark theme
- */
-.zen-forms {
-	background: #151a1c;
-}
-
-.zen-forms .zen-forms-close-button,
-.zen-forms .zen-forms-theme-switch {
-	color: #768991;
-}
-
-.zen-forms .zen-forms-close-button:hover,
-.zen-forms .zen-forms-theme-switch:hover {
-	color: #707071;
-}
-
-.zen-forms label {
-	color: #415056;
-}
-
-.zen-forms .input {
-	color: #768991;
-    border-bottom: 1px solid #dadada;
-}
-
-.zen-forms .no-bottom-line {
-    border-bottom: none;
-}
-
-.zen-forms .custom-select {
-	background-color: #151a1c;
-	border: 2px solid #0f1314;
-}
-
-.zen-forms .custom-select a {
-	color: #768991;
-}
-
-.zen-forms .custom-select.is-open a:hover {
-	background-color: #181e20;
-}
-
-.zen-forms .custom-select.is-open .selected {
-	background-color: #0f1314;
-}
-
-/**
- * Light theme
- */
-.zen-forms.light-theme {
-	background: #fefefe;
-}
-
-.zen-forms.light-theme .zen-forms-close-button,
-.zen-forms.light-theme .zen-forms-theme-switch {
-	color: #707071;
-}
-
-.zen-forms.light-theme .zen-forms-close-button:hover,
-.zen-forms.light-theme .zen-forms-theme-switch:hover {
-	color: #768991;
-}
-
-.zen-forms.light-theme label {
-	color: #959697;
-}
-
-.zen-forms.light-theme .input {
-	color: #707071;
-}
-
-.zen-forms.light-theme .custom-select {
-	background-color: #fefefe;
-	border: 2px solid #e5e5e5;
-}
-
-.zen-forms.light-theme .custom-select a {
-	color: #707071;
-}
-
-.zen-forms.light-theme .custom-select.is-open a:hover {
-	background-color: #f5f5f5;
-}
-
-.zen-forms.light-theme .custom-select.is-open .selected {
-	background-color: #e5e5e5;
-}
-
-.go-zen--textarea {
-    position: absolute;
-    top: 5px;
-    right: 5px;
-}
-.s {
-    background-image: url(https://raw.github.com/Idered/zen-form/master/zen-form-sprites.png);
-    display: inline-block;
-    line-height: 1;
-    position: relative;
-    vertical-align: top;
-    zoom: 1;
-    width: 16px;
-    height: 16px;
-    top: -1px;
-    overflow: hidden;
-    *text-indent: -9999px;
-}
-
-.s:before {
-    content: "";
-    display: block;
-    width: 0;
-    height: 100%;
-}
-
-.s--zen {
-    background-position: -16px -16px;
-}
-
-.s--zen:hover {
-    background-position: -16px 0;
-}
 
public/javascripts/lib/zenform/zen-form.js (deleted)
--- public/javascripts/lib/zenform/zen-form.js
@@ -1,392 +0,0 @@
-/** Zen Forms 1.0.3 | MIT License | git.io/zen-form */
-
-(function ($) {
-
-    $.fn.zenForm = function (settings) {
-
-        settings = $.extend({
-            trigger: '.go-zen',
-            theme: 'dark'
-        }, settings);
-
-        /**
-         * Helper functions
-         */
-        var Utils = {
-
-            /**
-             * (Un)Wrap body content to hide overflow
-             */
-            bodyWrap: function () {
-
-                var $body = $('body'),
-                    $wrap = $body.children('.zen-forms-body-wrap');
-
-                if ($wrap.length) {
-                    $wrap.children().unwrap();
-                } else {
-                    $body.wrapInner('<div class="zen-forms-body-wrap"/>');
-                }
-
-            }, // bodyWrap
-
-            /**
-             * Watch inputs and add "empty" class if needed
-             */
-            watchEmpty: function () {
-
-                App.Environment.find('input, textarea, select').each(function () {
-
-                   $(this).on('change', function () {
-
-                        $(this)[$(this).val() ? 'removeClass' : 'addClass']('empty');
-
-                   }).trigger('change');
-
-                });
-
-            },
-
-            /**
-             * Custom styled selects
-             */
-            customSelect: function ($select, $customSelect) {
-
-                var $selected;
-
-                $customSelect.on('click', function (event) {
-
-                    event.stopPropagation();
-
-                    $selected = $customSelect.find('.selected');
-
-                    $customSelect.toggleClass('is-open');
-
-                    if ($customSelect.hasClass('is-open')) {
-                        $customSelect.scrollTop(
-                            $selected.position().top - $selected.outerHeight()
-                        );
-                    }
-
-
-                }).find('a').on('click', function () {
-
-                    $(this).addClass('selected').siblings().removeClass('selected');
-
-                    $select.val($(this).data('value'));
-
-                });
-
-            }, // customSelect
-
-            /**
-             * Hide any elements(mostly selects) when clicked outside them
-             */
-            manageSelects: function () {
-
-                $(document).on('click', function () {
-                    $('.is-open').removeClass('is-open');
-                });
-
-            }, // manageSelects
-
-            /**
-             * Hide any elements(mostly selects) when clicked outside them
-             */
-            focusFirst: function () {
-
-                var $first = App.Environment.find('input').first();
-
-                // we need to re-set value to remove focus selection
-                $first.focus().val($first.val());
-
-            } // focusFirst
-
-        }, // Utils
-
-        /**
-         * Core functionality
-         */
-        App = {
-
-            /**
-             * Orginal form element
-             */
-            Form: null,
-
-            /**
-             * Wrapper element
-             */
-            Environment: null,
-
-            /**
-             * Functions to create and manipulate environment
-             */
-            env: {
-
-
-                /**
-                 * Object where elements created with App.env.addObject are appended
-                 */
-                wrapper: null,
-
-                create: function () {
-
-                    // Callback: zf-initialize
-                    App.Form.trigger('zf-initialize');
-
-                    Utils.bodyWrap();
-
-                    App.Environment = $('<div>', {
-                        class: 'zen-forms' + (settings.theme == 'dark' ? '' : ' light-theme')
-                    }).hide().appendTo('body').fadeIn(200);
-
-                    // ESC to exit. Thanks @ktmud
-                    $('body').on('keydown', function (event) {
-
-                        if (event.which == 27)
-                            App.env.destroy($elements);
-
-                    });
-
-                    return App.Environment;
-
-                }, // create
-
-                /**
-                 * Update orginal inputs with new values and destroy Environment
-                 */
-                destroy: function ($elements) {
-
-                    // Callback: zf-destroy
-                    App.Form.trigger('zf-destroy', App.Environment);
-
-                    $('body').off('keydown');
-
-                    // Update orginal inputs with new values
-                    $elements.each(function (i) {
-
-                        var $el = $('#zen-forms-input' + i);
-
-                        if ($el.length) {
-                            $(this).val($el.val());
-                        }
-
-                    });
-
-                    Utils.bodyWrap();
-
-                    // Hide and remove Environment
-                    App.Environment.fadeOut(200, function () {
-
-                        App.env.wrapper = null;
-
-                        App.Environment.remove();
-
-                    });
-
-                    // Callback: zf-destroyed
-                    App.Form.trigger('zf-destroyed');
-
-                }, // destroy
-
-                /**
-                 * Append inputs, textareas to Environment
-                 */
-                add: function ($elements) {
-
-                    var $el, $label, value, id, ID, label;
-
-                    $elements.each(function (i) {
-
-                        App.env.wrapper = App.env.createObject('div', {
-                            class: 'zen-forms-input-wrap'
-                        }).appendTo(App.Environment);
-
-                        $el = $(this);
-
-                        value = $el.val();
-
-                        id = $el.attr('id');
-
-                        ID = 'zen-forms-input' + i;
-
-                        label = $el.data('label') || $("label[for=" + id + "]").text() || $el.attr('placeholder') || '';
-
-                        // Exclude specified elements
-                        if ($.inArray( $el.attr('type'), ['checkbox', 'radio', 'submit']) == -1) {
-
-                            if ($el.is('input') )
-                                App.env.addInput($el, ID, value);
-                            else if ($el.is('select') )
-                                App.env.addSelect($el, ID);
-                            else
-                                App.env.addTextarea($el, ID, value);
-
-                            $label = App.env.addObject('label', {
-                                for: ID,
-                                text: label
-                            });
-
-                            if ($el.is('select') )
-                                $label.prependTo(App.env.wrapper);
-
-                        }
-
-                    });
-
-                    // Callback: zf-initialized
-                    App.Form.trigger('zf-initialized', App.Environment);
-
-                }, // add
-
-                addInput: function ($input, ID, value) {
-
-                    return App.env.addObject('input', {
-                        id: ID,
-                        value: value,
-                        class: 'input',
-                        type: $input.attr('type')
-                    });
-
-                }, // addInput
-
-                addTextarea: function ($textarea, ID, value) {
-
-                    return App.env.addObject('textarea', {
-                        id: ID,
-                        text: value,
-                        rows: 20,
-                        class: 'input no-bottom-line'
-                    });
-
-                }, // addTextarea
-
-                addSelect: function ($orginalSelect, ID) {
-
-                    var $select = App.env.addObject('select', {
-                            id: ID,
-                            class: 'select'
-                        }),
-                        $options = $orginalSelect.find('option'),
-                        $customSelect = App.env.addObject('div', {
-                            class: 'custom-select-wrap',
-                            html: '<div class="custom-select"></div>'
-                        }).children();
-
-                    $select.append($options.clone());
-
-                    $.each($options, function (i, option) {
-
-                        App.env.createObject('a', {
-                            href: '#',
-                            html: '<span>' + $(option).text() + '</span>' ,
-                            'data-value': $(option).attr('value'),
-                            class: $(option).prop('selected') ? 'selected' : ''
-                        }).appendTo($customSelect);
-
-                    });
-
-                    $select.val($orginalSelect.val());
-
-                    Utils.customSelect($select, $customSelect);
-
-                    return $customSelect;
-
-                }, // addSelect
-
-                /**
-                 * Wrapper for creating jQuery objects
-                 */
-                createObject: function (type, params, fn, fnMethod) {
-
-                    return $('<'+type+'>', params).on(fnMethod || 'click', fn);
-
-                }, // createObject
-
-                /**
-                 * Wrapper for adding jQuery objects to wrapper
-                 */
-                addObject: function (type, params, fn, fnMethod) {
-
-                    return App.env.createObject(type, params, fn, fnMethod).appendTo(App.env.wrapper || App.Environment);
-
-                }, // addObject
-
-                switchTheme: function () {
-
-                    App.Environment.toggleClass('light-theme');
-
-                } // switchTheme
-
-            }, // env
-
-            zen: function ($elements) {
-
-                // Create environment
-                App.env.create();
-
-                // Add wrapper div for close and theme buttons
-                App.env.wrapper = App.env.createObject('div', {
-                    class: 'zen-forms-header'
-                }).appendTo(App.Environment);
-
-                // Add close button
-                App.env.addObject('a', {
-                    class: 'zen-forms-close-button',
-                    html: '<i class="zen-icon zen-icon--close"></i> Exit Zen Mode'
-                }, function () {
-                    App.env.destroy($elements);
-                });
-
-                // Add theme switch button
-                App.env.addObject('a', {
-                    class: 'zen-forms-theme-switch',
-                    html: '<i class="zen-icon zen-icon--theme"></i> Switch theme'
-                }, function () {
-                    App.env.switchTheme();
-                });
-
-                // Add inputs and textareas from form
-                App.env.add($elements);
-
-                // Additional select functionality
-                Utils.manageSelects();
-
-                // Select first input
-                Utils.focusFirst();
-
-                // Add .empty class for empty inputs
-                Utils.watchEmpty();
-
-            } // zen
-
-        }; // App
-
-        App.Form = $(this);
-
-        var $elements = App.Form.is('form') ? App.Form.find('input, textarea, select') : App.Form;
-
-        $(settings.trigger).on('click', function (event) {
-
-            event.preventDefault();
-
-            App.zen($elements);
-
-        });
-
-        // Command: destroy
-        App.Form.on('destroy', function () {
-            App.env.destroy($elements);
-        });
-
-        // Command: init
-        App.Form.on('init', function () {
-            App.zen($elements);
-        });
-
-        return this;
-
-    };
-
-})(jQuery);
public/javascripts/service/yobi.board.Write.js
--- public/javascripts/service/yobi.board.Write.js
+++ public/javascripts/service/yobi.board.Write.js
@@ -24,6 +24,7 @@
             _attachEvent();
 
             _initFileUploader();
+            $("#title").focus();
         }
 
         /**
public/javascripts/service/yobi.issue.Write.js
--- public/javascripts/service/yobi.issue.Write.js
+++ public/javascripts/service/yobi.issue.Write.js
@@ -27,9 +27,6 @@
 
             // 제목 입력란에 포커스
             htElement.welInputTitle.focus();
-
-            // zenForm
-            _initZenForm();
         }
 
         /**
@@ -136,19 +133,6 @@
 
             $(window).off("beforeunload", _onBeforeUnload);
             return true;
-        }
-
-        /**
-         * ZenForm 초기화
-         * initialize zenForm
-         */
-        function _initZenForm(){
-            $(".zen-mode").zenForm({"theme": "light"});
-            $(".s--zen").tooltip({
-                "delay": {"show": 500, "hide": 100},
-                "title": Messages("title.zenmode"),
-                "placement": "left"
-            });
         }
 
         _init(htOptions);
Add a comment
List