김덕홍 2014-03-27
Merge branch 'fix-shortcutkeys-140313' of laziel/yobi
from pull request 711
@c559b3c4b298a9d641d8dc8aef72c118ec64841e
app/assets/stylesheets/less/_page.less
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
@@ -4431,9 +4431,11 @@
 .keymap-help {
     padding:20px;
     line-height:30px;
+    white-space:nowrap;
 
-    .nbtn {
+    .ybtn {
         min-width:20px;
+        margin-right:3px;
     }
     .actrow {
         text-align:center;
app/views/board/list.scala.html
--- app/views/board/list.scala.html
+++ app/views/board/list.scala.html
@@ -89,12 +89,6 @@
 
 		yobi.ShortcutKey.setKeymapLink({
 		   "N": "@routes.BoardApp.newPostForm(project.owner, project.name)"
-		   @if(page.getPageIndex > 0){
-           ,"A": "@getPageListUrl(page.getPageIndex - 1)"
-		   }
-		   @if(page.getTotalPageCount > 1 && (page.getPageIndex + 1 != page.getTotalPageCount)){
-		   ,"S": "@getPageListUrl(page.getPageIndex + 1)"
-		   }
 		});
 	});
 </script>
app/views/board/view.scala.html
--- app/views/board/view.scala.html
+++ app/views/board/view.scala.html
@@ -144,7 +144,7 @@
         // yobi.ShortcutKey
         yobi.ShortcutKey.setKeymapLink({
             "N": "@routes.BoardApp.newPostForm(project.owner, project.name)",
-            "L": "@routes.BoardApp.posts(project.owner, project.name)"
+            "L": "@urlToPostings"
             @if(isAllowed(UserApp.currentUser(), post.asResource(), Operation.UPDATE)){
            ,"E": "@routes.BoardApp.editPostForm(project.owner, project.name, post.getNumber)"
             }
app/views/help/keymap.scala.html
--- app/views/help/keymap.scala.html
+++ app/views/help/keymap.scala.html
@@ -1,5 +1,7 @@
 @(section:String, project:Project)
 
+@ctrl = @{if(request.headers.get("User-Agent")(0).contains("Macintosh")){"⌘"}else{"CTRL"}}
+
 <div class="pull-right" style="padding:10px 0;">
     <a href="#helpKeys" data-toggle="modal" class="ybtn ybtn-inverse ybtn-mini">
         <i class="yobicon-info-sign"></i> @Messages("title.keymap")
@@ -7,7 +9,7 @@
 
     <div id="helpKeys" class="modal hide fade keymap-help" tabindex="-1" role="dialog">
         <div class="row-fluid">
-            <div class="span4">
+            <div class="span3">
                 <h5>@Messages("project.projects")</h5>
 	            <span class="ybtn ybtn-small">H</span>
 	            <span class="help-inline">@Messages("menu.home")</span><br>
@@ -69,20 +71,20 @@
 	            }
 
                 @if(section == "issueList"){
-                <span class="ybtn ybtn-small">CTRL</span>+ <span class="ybtn ybtn-small">A</span>
+                <span class="ybtn ybtn-small">@ctrl</span>+ <span class="ybtn ybtn-small">A</span>
                 <span class="help-inline">@Messages("button.selectAll")</span><br>
                 }
             </div>
 
-            <div class="span4">
+            <div class="span5">
 	            <h5>@Messages("site")</h5>
 	            <span class="ybtn ybtn-small">P</span>
 	            <span class="help-inline">@Messages("userinfo.profile")</span><br>
 
-                <span class="ybtn ybtn-small">ALT</span>+ <span class="ybtn ybtn-small">S</span>
+                <span class="ybtn ybtn-small">ALT</span> + <span class="ybtn ybtn-small">S</span>
                 <span class="help-inline">@Messages("site.search")</span><br>
 
-	            <span class="ybtn ybtn-small">CTRL</span>+ <span class="ybtn ybtn-small">ENTER</span>
+	            <span class="ybtn ybtn-small">@ctrl</span> + <span class="ybtn ybtn-small">ENTER</span>
 	            <span class="help-inline">@Messages("button.submitForm")</span><br>
             </div>
         </div>
app/views/issue/partial_search.scala.html
--- app/views/issue/partial_search.scala.html
+++ app/views/issue/partial_search.scala.html
@@ -241,12 +241,6 @@
                 // ShortcutKey
                 yobi.ShortcutKey.setKeymapLink({
                     "N": "@routes.IssueApp.newIssueForm(project.owner, project.name)"
-                    @if(currentPage.getPageIndex > 0){
-                    ,"A": "@getPageListUrl(currentPage.getPageIndex - 1)"
-                    }
-                    @if(currentPage.getTotalPageCount > 1 && (currentPage.getPageIndex + 1 != currentPage.getTotalPageCount)){
-                   ,"S": "@getPageListUrl(currentPage.getPageIndex + 1)"
-                    }
                 });
             });
         </script>
app/views/issue/view.scala.html
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
@@ -309,13 +309,16 @@
             "welMilestone"  : $("#milestone"),
             "welAssignee"   : $("#assignee"),
             "welIssueUpdateForm": $("#issueUpdateForm"),
-            "sIssueCheckBoxesSelector": "[type=checkbox][name=checked-issue]"
+            "sIssueCheckBoxesSelector": "[type=checkbox][name=checked-issue]",
+            "sNextState"    : "@issue.nextState().toString.toLowerCase",
+            "sNextStateUrl" : "@routes.IssueApp.nextState(project.owner, project.name, issue.getNumber)",
+            "sCommentWithStateUrl": "@routes.IssueApp.newCommentWithState(project.owner, project.name, issue.getNumber)"
         });
 
         // yobi.ShortcutKey
         yobi.ShortcutKey.setKeymapLink({
             "N": "@routes.IssueApp.newIssueForm(project.owner, project.name)",
-            "L": "@routes.IssueApp.issues(project.owner, project.name,"open")"
+            "L": "@urlToIssues"
             @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) {
            ,"E": "@routes.IssueApp.editIssueForm(project.owner, project.name, issue.getNumber)"
             }
@@ -324,39 +327,6 @@
         yobi.Mention({
             target:'comment-editor',
             url : "@Html(routes.ProjectApp.mentionList(project.owner, project.name, issue.getNumber, issue.asResource().getType.resource()).toString())"
-        });
-    });
-</script>
-
-<script>
-    //add "comment & close" like button at comment form
-    $(function(){
-        var welEditor = $("#comment-editor");
-        var welDynamicCommentBtn = $("#dynamic-comment-btn");
-        var welCommentForm = $("#comment-form" );
-        var welWithStateTransition = $("<input type='hidden' name='withStateTransition'>");
-
-        var sNextState = "@Messages("button.nextState."+issue.nextState().toString.toLowerCase)";
-        var sCommentAndNextState = "@Html(Messages("button.commentAndNextState."+issue.nextState().toString.toLowerCase))";
-        welCommentForm.prepend(welWithStateTransition);
-        welDynamicCommentBtn.removeClass('hidden');
-        welDynamicCommentBtn.html("@Messages("button.nextState."+issue.nextState().toString.toLowerCase)")
-            .on("click", function(){
-                if(welEditor.val().length > 0){
-                    welWithStateTransition.val("true");
-                    welCommentForm.attr("action", "@routes.IssueApp.newCommentWithState(project.owner, project.name, issue.getNumber)");
-                    welCommentForm.submit();
-                } else {
-                    welWithStateTransition.val("");
-                    location.href="@routes.IssueApp.nextState(project.owner, project.name, issue.getNumber)";
-                }
-            });
-        welEditor.on("keyup", function(){
-            if(welEditor.val().length > 0){
-                welDynamicCommentBtn.html(sCommentAndNextState);
-            } else {
-                welDynamicCommentBtn.html(sNextState);
-            }
         });
     });
 </script>
public/javascripts/common/yobi.Pagination.js
--- public/javascripts/common/yobi.Pagination.js
+++ public/javascripts/common/yobi.Pagination.js
@@ -16,6 +16,8 @@
 yobi.Pagination = (function(window, document) {
     var htRegEx = {};
     var rxDigit = /^.[0-9]*$/;
+    // $.isNumeric determines hex, point or negative numbers as numeric.
+    // but, rxDigit finds only positive decimal integer numbers
 
     /**
      * getQuery
@@ -88,102 +90,189 @@
     }
 
     /**
-     * window.updatePagination
+     * Update pagination
+     *
+     * @param {HTMLElement} elTarget
+     * @param {Number} nTotalPages
+     * @param {Hash Table} htOpt
      */
-    window.updatePagination = function(target, totalPages, options) {
-        if (totalPages <= 0){
+    function updatePagination(elTarget, nTotalPages, htOptions) {
+        if (nTotalPages <= 0){
             return;
         }
 
-        var target = $(target);
-        var linkToPrev, linkToNext, urlToPrevPage, urlToNextPage;
-        var options = options || {};
+        var welTarget = $(elTarget);
+        var htData = htOptions || {};
 
-        options.url = options.url || document.URL;
-        options.firstPage = options.firstPage || 1;
+        htData.url = htData.url || document.URL;
+        htData.firstPage = htData.firstPage || 1;
+        htData.totalPages = nTotalPages;
+        htData.paramNameForPage = htData.paramNameForPage || 'pageNum';
+        htData.current = !rxDigit.test(htData.current) ? _getPageNumFromUrl(htData) : htData.current;
+        htData.hasPrev = (typeof htData.hasPrev === "undefined") ? htData.current > htData.firstPage : htData.hasPrev;
+        htData.hasNext = (typeof htData.hasNext === "undefined") ? htData.current < htData.totalPages : htData.hasNext;
 
-        var pageNumFromUrl;
-        var paramNameForPage = options.paramNameForPage || 'pageNum';
+        validateOptions(htData);
 
-        if (!$.isNumeric(options.current)) {
-            query = getQuery(options.url);
-            pageNumFromUrl  = parseInt(valueFromQuery(paramNameForPage, query));
-            options.current = pageNumFromUrl || options.firstPage;
-        }
+        welTarget.html('');
+        welTarget.addClass('page-navigation-wrap');
 
-        options.hasPrev = (typeof options.hasPrev == "undefined") ? options.current > options.firstPage : options.hasPrev;
-        options.hasNext = (typeof options.hasNext == "undefined") ? options.current < totalPages : options.hasNext;
-
-        validateOptions(options);
-
-        target.html('');
-        target.addClass('page-navigation-wrap');
-
-        // previous page exists
-        var welPagePrev;
-        if (options.hasPrev) {
-            linkToPrev = $('<a pjax-page>').append($('<i class="ico btn-pg-prev">')).append($('<span>').text('PREV'));
-
-            if (typeof (options.submit) == 'function') {
-                linkToPrev.attr('href', 'javascript: void(0);').click(function(e) {
-                    options.submit(options.current - 1);
-                });
-            } else {
-                urlToPrevPage = urlWithPageNum(options.url, options.current - 1, paramNameForPage);
-                linkToPrev.attr('href', urlToPrevPage);
-            }
-
-            welPagePrev = $('<li class="page-num ikon">').append(linkToPrev);
-        } else {
-            welPagePrev = $('<li class="page-num ikon">').append($('<i class="ico btn-pg-prev off">')).append($('<span class="off">').text('PREV'));
-        }
-
-        // on submit event handler
-        if (typeof (options.submit) == 'function') {
-            var keydownOnInput = function(e) {
-                if(validateInputNum($(target), options.current)){
-                    options.submit($(target).val());
-                }
-            };
-        } else {
-            var keydownOnInput = function(e) {
-                var welTarget = $(e.target || e.srcElement);
-                if (e.which == 13 && validateInputNum(welTarget, options.current)) {
-                    document.location.href = urlWithPageNum(options.url, welTarget.val(), paramNameForPage);
-                }
-            }
-        }
+        // prev/next link
+        var welPagePrev = _getPrevPageLink(htData);
+        var welPageNext = _getNextPageLink(htData);
 
         // page input box
-        var elInput = $('<input name="pageNum" type="number" pattern="[0-9]*" min="1" max="' + totalPages + '" class="input-mini" value="' + options.current + '">').keydown(keydownOnInput);
-        var welPageInputContainer = $('<li class="page-num">').append(elInput);
+        var welPageInput = _getPageInputBox(htData);
+        var welPageInputWrap = $('<li class="page-num">').append(welPageInput);
         var welDelimiter = $('<li class="page-num delimiter">').text('/');
-        var welTotalPages = $('<li class="page-num">').text(totalPages);
-
-        // next page exists
-        var welPageNext;
-        if (options.hasNext) {
-            linkToNext = $('<a pjax-page>').append($('<span>').text('NEXT')).append($('<i class="ico btn-pg-next">'));
-
-            if (typeof (options.submit) == 'function') {
-                linkToNext.attr('href', 'javascript: void(0);').click(function(e) { options.submit(options.current + 1);});
-            } else {
-                urlToNextPage = urlWithPageNum(options.url, options.current + 1, paramNameForPage);
-                linkToNext.attr('href', urlToNextPage);
-            }
-
-            welPageNext = $('<li class="page-num ikon">').append(linkToNext);
-        } else {
-            welPageNext = $('<li class="page-num ikon">').append($('<span class="off">').text('NEXT').append('<i class="ico btn-pg-next off">'));
-        }
+        var welTotalPages = $('<li class="page-num">').text(nTotalPages);
 
         // fill #pagination
-        var welPageList = $('<ul class="page-nums">').append([welPagePrev, welPageInputContainer, welDelimiter, welTotalPages, welPageNext]);
-        target.append(welPageList);
-    };
+        var welPageList = $('<ul class="page-nums">');
+        welPageList.append([welPagePrev, welPageInputWrap, welDelimiter, welTotalPages, welPageNext]);
+        welTarget.append(welPageList);
+    }
+
+    /**
+     * Get current page number from QueryString
+     *
+     * @param htData
+     * @returns {Number}
+     * @private
+     */
+    function _getPageNumFromUrl(htData){
+        var sQuery = getQuery(htData.url);
+        var nPageNumFromUrl  = parseInt(valueFromQuery(htData.paramNameForPage, sQuery), 10);
+        return nPageNumFromUrl || htData.firstPage;
+    }
+
+    /**
+     * Get PageNum INPUT element
+     *
+     * @param htData
+     * @returns {Wrapped Element}
+     * @private
+     */
+    function _getPageInputBox(htData){
+        var welPageInput = $('<input type="number" pattern="[0-9]*" class="input-mini">');
+
+        welPageInput.prop({
+            "name" : htData.paramNameForPage,
+            "max"  : htData.totalPages,
+            "min"  : 1
+        });
+
+        welPageInput.val(htData.current);
+
+        welPageInput.on("keydown", function(weEvt){
+            if(!isValidInputNum(welPageInput, htData.current)){
+                return;
+            }
+
+            var nCurrentValue = welPageInput.val();
+
+            if(typeof htData.submit === "function"){
+                htData.submit(nCurrentValue);
+            } else if(weEvt.which === 13){
+                document.location.href = urlWithPageNum(htData.url, nCurrentValue, htData.paramNameForPage);
+            }
+        });
+
+        return welPageInput;
+    }
+
+    /**
+     * Get previous page link
+     *
+     * @param htData
+     * @returns {Wrapped Element}
+     * @private
+     */
+    function _getPrevPageLink(htData){
+        var sLinkText = Messages("button.prevPage") || 'PREV';
+        var sLinkHTMLOn = '<i class="ico btn-pg-prev"></i><span>' + sLinkText + '</span>';
+        var sLinkHTMLOff = '<i class="ico btn-pg-prev off"></i><span class="off">' + sLinkText + '</span>';
+
+        var htOptions = $.extend(htData, {
+            "bActive"  : htData.hasPrev,
+            "sLinkHref": htData.hasPrev ? urlWithPageNum(htData.url, htData.current - 1, htData.paramNameForPage) : "",
+            "sLinkHTMLOn"   : sLinkHTMLOn,
+            "sLinkHTMLOff"  : sLinkHTMLOff,
+            "sShortcutKey"  : "A",
+            "nSubmitPageNum": htData.current - 1
+        });
+
+        var welPagePrev = _buildPageLink(htOptions);
+
+        return welPagePrev;
+    }
+
+    /**
+     * Get next page link
+     *
+     * @param htData
+     * @returns {Wrapped Element}
+     * @private
+     */
+    function _getNextPageLink(htData){
+        var sLinkText = Messages("button.nextPage") || 'NEXT';
+        var sLinkHTMLOn = '<span>' + sLinkText + '</span><i class="ico btn-pg-next"></i>';
+        var sLinkHTMLOff = '<span class="off">' + sLinkText + '</span><i class="ico btn-pg-next off"></i>';
+
+        var htOptions = $.extend(htData, {
+            "bActive"  : htData.hasNext,
+            "sLinkHref": htData.hasNext ? urlWithPageNum(htData.url, htData.current + 1, htData.paramNameForPage) : "",
+            "sLinkHTMLOn"   : sLinkHTMLOn,
+            "sLinkHTMLOff"  : sLinkHTMLOff,
+            "sShortcutKey"  : "S",
+            "nSubmitPageNum": htData.current + 1
+        });
+
+        var welPageNext = _buildPageLink(htOptions);
+
+        return welPageNext;
+    }
+
+    /**
+     * Build prev/next page link
+     *
+     * @param htData
+     * @returns {Wrapped Element}
+     * @private
+     */
+    function _buildPageLink(htData){
+        var welPageLink = $('<li class="page-num ikon">');
+
+        if(htData.bActive){
+            var welLink = $('<a pjax-page>');
+            welLink.html(htData.sLinkHTMLOn);
+
+            if(typeof htData.submit === 'function'){
+                welLink.attr("href", "javascript: void(0);");
+                welLink.on("click", function(){
+                    htData.submit(htData.nSubmitPageNum);
+                });
+            } else {
+                welLink.attr("href", htData.sLinkHref);
+            }
+
+            welPageLink.append(welLink);
+        } else {
+            welPageLink.html(htData.sLinkHTMLOff);
+        }
+
+        // if yobi.ShortcutKey exists
+        if(yobi.ShortcutKey){
+            var htKeyOpt = {};
+            htKeyOpt[htData.sShortcutKey] = htData.sLinkHref;
+            yobi.ShortcutKey.setKeymapLink(htKeyOpt);
+        }
+
+        return welPageLink;
+    }
 
     // validate number range
-    function validateInputNum(welTarget, nCurrentPageNum){
+    function isValidInputNum(welTarget, nCurrentPageNum){
         if(rxDigit.test(welTarget.val()) === false){
             welTarget.val(nCurrentPageNum);
             return false;
public/javascripts/common/yobi.ShortcutKey.js
--- public/javascripts/common/yobi.ShortcutKey.js
+++ public/javascripts/common/yobi.ShortcutKey.js
@@ -24,6 +24,7 @@
      */
     function _initVar(){
         htVar.rxTrim = /\s+/g;
+        htVar.aFormTags = ["INPUT", "TEXTAREA"];
         htVar.aCombinationKeys = ["CTRL", "ALT", "SHIFT"];
         htVar.htKeycodeMap = {
             '13':'ENTER', '38':'UP', '40':'DOWN', '37':'LEFT', '39':'RIGHT', '13':'ENTER', '27':'ESC',
@@ -40,13 +41,17 @@
      * add event listener
      */
     function _attachEvent(){
-        $(window).bind("keydown", _onKeyDown);
-        $(window).bind("beforeunload", destroy); // free memory
+        $(window).on({
+            "keydown"     : _onKeyDown,
+            "beforeunload": destroy // free memory
+        });
     }
 
     function _detachEvent(){
-        $(window).unbind("keydown", _onKeyDown);
-        $(window).unbind("beforeunload", destroy);
+        $(window).off({
+            "keydown"     : _onKeyDown,
+            "beforeunload": destroy // free memory
+        });
     }
 
     /**
@@ -54,24 +59,25 @@
      */
     function _onKeyDown(weEvt){
         var sKeyInput = _getKeyString(weEvt);
-        var aHandlers = htHandlers[sKeyInput] || [];
+        var fHandler = htHandlers[sKeyInput];
 
-        _runEventHandler(aHandlers, weEvt, sKeyInput);
+        if(typeof fHandler === "function"){
+            _runEventHandler(fHandler, weEvt, sKeyInput);
+        }
     }
 
-    function _runEventHandler(aHandlers, weEvt, sKeyInput){
+    function _runEventHandler(fHandler, weEvt, sKeyInput){
+        var sTagName = weEvt.target.tagName.toUpperCase();
         var htInfo = {
             "weEvt"     : weEvt,
             "welTarget" : $(weEvt.target),
-            "sTagName"  : weEvt.target.tagName,
+            "sTagName"  : sTagName,
             "sKeyInput" : sKeyInput,
-            "bFormInput": (weEvt.target.tagName == "INPUT" || weEvt.target.tagName == "TEXTAREA")
+            "bFormInput": (htVar.aFormTags.indexOf(sTagName) > -1)
         };
 
         try {
-            aHandlers.forEach(function(fHandler){
-                fHandler(htInfo);
-            });
+            fHandler(htInfo);
         }catch(e){} finally {
             htInfo = null;
         }
@@ -85,25 +91,20 @@
      * @param {Hash Table} vKey {"keyCombination:function(){}, "key":function(){}}
      */
     function attachHandler(vKey, fHandler){
-        if(typeof vKey == "string"){
-            return _addHandler(vKey, fHandler);
+        if(typeof vKey === "string"){
+            return _setHandler(vKey, fHandler);
         }
 
-        var fHandler;
-        for(var sKey in vKey){
+        var fHandler, sKey;
+        for(sKey in vKey){
             fHandler = vKey[sKey];
-            _addHandler(sKey, fHandler);
+            _setHandler(sKey, fHandler);
         }
     }
 
-    function _addHandler(sKey, fHandler){
+    function _setHandler(sKey, fHandler){
         sKey = _normalizeKeyString(sKey);
-
-        if(!(htHandlers[sKey] instanceof Array)){
-            htHandlers[sKey] = [];
-        }
-
-        htHandlers[sKey].push(fHandler);
+        htHandlers[sKey] = fHandler;
     }
 
     /**
@@ -111,14 +112,9 @@
      * @param {String} sKey
      * @param {String} fHandler
      */
-    function detachHandler(sKeyInput, fHandler){
+    function detachHandler(sKeyInput){
         var sKey = _normalizeKeyString(sKeyInput);
-        var aHandlers = htHandlers[sKey];
-
-        if(aHandlers instanceof Array){
-            aHandlers.splice(aHandlers.indexOf(fHandler), 1);
-            htHandlers[sKey] = aHandlers;
-        }
+        delete htHandlers[sKey];
     }
 
     /**
@@ -127,7 +123,7 @@
      */
     function _getKeyString(weEvt){
         var sMainKey = htVar.htKeycodeMap[weEvt.keyCode];
-        if(typeof sMainKey == "undefined"){ // ignore event if not on keyMap
+        if(typeof sMainKey === "undefined"){ // ignore event if not on keyMap
             return;
         }
 
@@ -184,12 +180,19 @@
      * });
      */
     function setKeymapLink(htKeyMap){
-        for(var sKey in htKeyMap){
-            attachHandler(sKey, function(htInfo){
-                if(!htInfo.bFormInput){
-                    document.location.href = htKeyMap[htInfo.sKeyInput];
-                }
-            });
+        var sKey;
+        var fHandler = function(htInfo){
+            if(!htInfo.bFormInput){
+                document.location.href = htKeyMap[htInfo.sKeyInput];
+            }
+        };
+
+        for(sKey in htKeyMap){
+            if(htKeyMap[sKey]){
+                attachHandler(sKey, fHandler);
+            } else {
+                detachHandler(sKey);
+            }
         }
     }
 
public/javascripts/service/yobi.issue.View.js
--- public/javascripts/service/yobi.issue.View.js
+++ public/javascripts/service/yobi.issue.View.js
@@ -29,6 +29,8 @@
             _setLabelTextColor();
 
             _setTimelineUpdateTimer();
+
+            _setBtnCommentAndClose();
         }
 
         /**
@@ -71,6 +73,11 @@
             htVar.sTimelineHTML = htElement.welTimelineList.html();
             htVar.nTimelineItems = _countTimelineItems(); // 타임라인 항목 갯수
             htVar.bOnFocusTextarea = false; // 댓글 작성폼에 포커스가 있는지 여부
+
+            // for comment-and-close
+            htVar.sNextState = htOptions.sNextState;
+            htVar.sNextStateUrl = htOptions.sNextStateUrl;
+            htVar.sCommentWithStateUrl = htOptions.sCommentWithStateUrl;
         }
 
         /**
@@ -390,6 +397,51 @@
             return htElement.welTimelineList.find("ul.comments > li").length;
         }
 
+        /**
+         * Add "comment & close" like button at comment form
+         * @private
+         */
+        function _setBtnCommentAndClose(){
+            var welEditor = $("#comment-editor");
+            var welDynamicCommentBtn = $("#dynamic-comment-btn");
+            var welCommentForm = $("#comment-form");
+            var welWithStateTransition = $("<input type='hidden' name='withStateTransition'>");
+
+            var sNextState = Messages("button.nextState." + htVar.sNextState);
+            var sCommentAndNextState = Messages("button.commentAndNextState." + htVar.sNextState);
+
+            welCommentForm.prepend(welWithStateTransition);
+            welDynamicCommentBtn.removeClass('hidden');
+            welDynamicCommentBtn.html(Messages("button.nextState." + htVar.sNextState));
+            welDynamicCommentBtn.on("click", function(){
+                if(welEditor.val().length > 0){
+                    welWithStateTransition.val("true");
+                    welCommentForm.attr("action", htVar.sCommentWithStateUrl);
+                    welCommentForm.submit();
+                } else {
+                    welWithStateTransition.val("");
+                    location.href = htVar.sNextStateUrl;
+                }
+            });
+
+            welEditor.on("keyup", function(){
+                if(welEditor.val().length > 0){
+                    welDynamicCommentBtn.html(sCommentAndNextState);
+                } else {
+                    welDynamicCommentBtn.html(sNextState);
+                }
+            });
+
+            // if yobi.ShortcutKey exists
+            if(yobi.ShortcutKey){
+                yobi.ShortcutKey.attach("CTRL+SHIFT+ENTER", function(htInfo){
+                    if(htInfo.welTarget.is(welEditor)){
+                        welDynamicCommentBtn.click();
+                    }
+                });
+            }
+        }
+
         // initialize
         _init(htOptions || {});
     };
Add a comment
List