Jihan Kim 2014-07-11
Refactored pullRequest form
@3ad394e37252e74b51e0f4aed6ba15154a0539b1
app/assets/stylesheets/less/_common.less
--- app/assets/stylesheets/less/_common.less
+++ app/assets/stylesheets/less/_common.less
@@ -233,3 +233,8 @@
     -o-box-shadow: 0 1px 1px rgba(0,0,0,0.2), inset 0 1px 1px rgba(0,0,0,0.1);
     box-shadow: 0 1px 1px rgba(0,0,0,0.2), inset 0 1px 1px rgba(0,0,0,0.1);
 }
+
+.vmiddle-inline {
+    display:inline-block !important;
+    vertical-align: middle !important;
+}
app/assets/stylesheets/less/_page.less
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
@@ -4770,38 +4770,23 @@
 
 // -- pull request
 .pull-request-wrap {
+    position:relative;
+    display: block;
     margin-bottom:20px;
-    display: table;
+    min-height:55px;
 
-    .request-box {
-        border: 1px solid lightgray;
-        border-radius: 3px !important;
-        display:inline-block; width:350px;
-        .repo { color:@secondary; font-size:14px; }
-        .dropdown-toggle { width:350px; }
-        .dropdown-menu   { width:350px; }
-        padding: 5px;
-    }
-
-    label {
+    .field-title {
+        display:block;
         font-weight: bold;
-        cursor: default;
-    }
-
-    .request-from {
-        .request-box;
-    }
-
-    .request-to {
-        .request-box;
     }
 
     .arrow {
-        display:table-cell; vertical-align:middle;
-        width:50px;
+        position:absolute;
+        left:50%; top:20px;
+        margin-left:-16px;
         text-align:center;
-        font-size: xx-large;
-        color: lightgray;
+        color: @yobi-gray;
+        font-size: 32px;
     }
 }
 
app/controllers/PullRequestApp.java
--- app/controllers/PullRequestApp.java
+++ app/controllers/PullRequestApp.java
@@ -176,12 +176,6 @@
                 , StringUtils.defaultIfBlank(request().getQueryString("fromBranch"), fromBranches.get(0).getName())
                 , StringUtils.defaultIfBlank(request().getQueryString("toBranch"), project.defaultBranch()));
 
-        if (HttpUtil.isRequestedWithXHR(request())) {
-            response().setHeader("Cache-Control", "no-cache, no-store");
-            PullRequestMergeResult mergeResult = pullRequest.getPullRequestMergeResult();
-            return ok(partial_diff.render(new Form<>(PullRequest.class).fill(pullRequest), project, pullRequest, mergeResult));
-        }
-
         return ok(create.render("title.newPullRequest", new Form<>(PullRequest.class).fill(pullRequest), project, projects, fromProject, toProject, fromBranches, toBranches, pullRequest));
     }
 
@@ -199,6 +193,32 @@
         return selectedProject;
     }
 
+    @With(AnonymousCheckAction.class)
+    @IsCreatable(ResourceType.FORK)
+    public static Result mergeResult(String userName, String projectName) throws IOException, GitAPIException {
+        final Project project = Project.findByOwnerAndProjectName(userName, projectName);
+
+        ValidationResult validation = validateBeforePullRequest(project);
+        if(validation.hasError()) {
+            return validation.getResult();
+        }
+
+        final Project fromProject = getSelectedProject(project, request().getQueryString("fromProjectId"), false);
+        final Project toProject = getSelectedProject(project, request().getQueryString("toProjectId"), true);
+
+        final List<GitBranch> fromBranches = new GitRepository(fromProject).getAllBranches();
+        final List<GitBranch> toBranches = new GitRepository(toProject).getAllBranches();
+
+        final PullRequest pullRequest = PullRequest.createNewPullRequest(fromProject, toProject
+                , StringUtils.defaultIfBlank(request().getQueryString("fromBranch"), fromBranches.get(0).getName())
+                , StringUtils.defaultIfBlank(request().getQueryString("toBranch"), toBranches.get(0).getName()));
+
+        PullRequestMergeResult mergeResult = pullRequest.getPullRequestMergeResult();
+
+        response().setHeader("Cache-Control", "no-cache, no-store");
+        return ok(partial_merge_result.render(project, pullRequest, mergeResult));
+    }
+
     @Transactional
     @With(AnonymousCheckAction.class)
     @IsCreatable(ResourceType.FORK)
app/views/git/create.scala.html
--- app/views/git/create.scala.html
+++ app/views/git/create.scala.html
@@ -18,7 +18,10 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **@
-@(title:String, form: Form[PullRequest], project: Project, projects: List[Project] , fromProject:Project, toProject:Project, fromBranches: List[playRepository.GitBranch], toBranches: List[playRepository.GitBranch], pullRequest:models.PullRequest)
+@(title:String, form:Form[PullRequest], project:Project, projects:List[Project],
+  fromProject:Project, toProject:Project,
+  fromBranches:List[playRepository.GitBranch], toBranches:List[playRepository.GitBranch],
+  pullRequest:models.PullRequest)
 
 @import scala.collection.Map
 @import utils.TemplateHelper._
@@ -31,121 +34,104 @@
     <div class="project-page-wrap">
         <div class="content-wrap frm-wrap">
             @helper.form(action=routes.PullRequestApp.newPullRequest(project.owner, project.name), 'enctype -> "multipart/form-data", 'class->"nm"){
-                <div class="row-fluid">
-                    <div class="span10">
-                        <div class="pull-request-wrap">
-                            <div class="option request-from">
-                                <div class="option-label">
-                                    <label>@Messages("pullRequest.from")</label>
-                                    <div class="btn-group branches" data-name="fromProjectId">
-                                        <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                            <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                            <span class="d-caret"><span class="caret"></span></span>
-                                        </button>
-                                        <ul class="dropdown-menu">
-                                            @for(repo <- projects) {
-                                            @if(ProjectUser.isMember(UserApp.currentUser().id, repo.id)) {
-                                            <li data-value="@repo.id" @if(repo.id == fromProject.id) {data-selected="true"}>
-                                            <a href="#"><span class="label">repo</span>@repo.owner/@repo.name</a>
-                                            </li>
-                                            }
-                                            }
-                                        </ul>
-                                    </div>
-                                </div>
-                                <div class="option-desc mt5">
-                                    <div class="btn-group branches" data-name="fromBranch">
-                                        <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                            <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                            <span class="d-caret"><span class="caret"></span></span>
-                                        </button>
-                                        <ul class="dropdown-menu">
-                                            @for(branch <- fromBranches) {
-                                            @common.branchItem("pullRequest", null, branch.getName, null,
-                                            utils.TemplateHelper.equals(pullRequest.fromBranch, branch.getName))
-                                            }
-                                        </ul>
-                                    </div>
-                                </div>
-                            </div>
-
-                            <div class="arrow">
-                                <i class="yobicon-right-2"></i>
-                            </div>
-
-                            <div class="option request-to">
-                                <div class="option-label">
-                                    <label>@Messages("pullRequest.to")</label>
-                                    <div class="btn-group branches" data-name="toProjectId">
-                                        <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                            <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                            <span class="d-caret"><span class="caret"></span></span>
-                                        </button>
-                                        <ul class="dropdown-menu">
-                                            @for(repo <- projects) {
-                                            <li data-value="@repo.id" @if(repo.id == toProject.id) {data-selected="true"}>
-                                            <a href="#"><span class="label">repo</span>@repo.owner/@repo.name</a>
-                                            </li>
-                                            }
-                                        </ul>
-                                    </div>
-                                </div>
-                                <div class="option-desc mt5">
-                                    <div class="btn-group branches" data-name="toBranch">
-                                        <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                            <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                            <span class="d-caret"><span class="caret"></span></span>
-                                        </button>
-                                        <ul class="dropdown-menu">
-                                            @for(branch <- toBranches) {
-                                            @common.branchItem("pullRequest", null, branch.getName, null,
-                                            utils.TemplateHelper.equals(pullRequest.toBranch, branch.getName))
-                                            }
-                                        </ul>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
+                <div class="pull-request-wrap">
+                    <!-- from -->
+                    <div class="pull-left">
+                        <label for="fromProjectId" class="field-title">@Messages("pullRequest.from")</label>
+                        <select id="fromProjectId" name="fromProjectId" data-toggle="select2" class="mr5">
+                            <option></option>
+                            @for(repo <- projects if ProjectUser.isMember(UserApp.currentUser().id, repo.id)) {
+                                <option value="@repo.id" @if(repo.id == fromProject.id){ selected }>
+                                    @repo.owner / @repo.name
+                                </option>
+                            }
+                        </select>
+                        <select id="fromBranch" name="fromBranch" data-toggle="select2" data-format="branch"
+                                data-dropdown-css-class="branches" data-placeholder="@Messages("pullRequest.select.branch")">
+                            <option></option>
+                            @for(branch <- fromBranches) {
+                                <option value="@branch.getName" @if(branch.getName == pullRequest.fromBranch){ selected }>@branch.getName</option>
+                            }
+                        </select>
                     </div>
-                    <div class="span2">
-                        <table class="request-summary">
-                            <tr>
-                                <th>@Messages("pullRequest.is.safe.title")</th>
-                                <td id="ableToMerge">
-                                    @Messages("pullRequest.is.checking")
-                                </td>
-                            </tr>
-                            <tr>
-                                <th>@Messages("pullRequest.commmit.count")</th>
-                                <td id="commitCount">
-                                    @Messages("pullRequest.is.checking")
-                                </td>
-                            </tr>
-                        </table>
+                    <!-- // -->
+
+                    <div class="arrow">
+                        <i class="yobicon-right-2"></i>
+                    </div>
+
+                    <!-- to -->
+                    <div class="pull-right">
+                        <label for="toProjectId" class="field-title">@Messages("pullRequest.to")</label>
+                        <select id="toProjectId" name="toProjectId" data-toggle="select2" class="mr5">
+                            <option></option>
+                            @for(repo <- projects){
+                                <option value="@repo.id" @if(repo.id == toProject.id){ selected }>
+                                    @repo.owner / @repo.name
+                                </option>
+                            }
+                        </select>
+                        <select id="toBranch" name="toBranch" data-toggle="select2" data-format="branch"
+                                data-dropdown-css-class="branches" data-placeholder="@Messages("pullRequest.select.branch")">
+                            <option></option>
+                            @for(branch <- toBranches) {
+                                <option value="@branch.getName" @if(branch.getName == pullRequest.toBranch){ selected }>@branch.getName</option>
+                            }
+                        </select>
+                    </div>
+                    <!-- // -->
+                </div>
+
+                <div id="status" class="alert mt20 mb20">
+                    @Messages("pullRequest.is.merging")
+                </div>
+
+                <div>
+                    <input type="text" id="title" name="title" maxlength="255" class="text" placeholder="@Messages("title")">
+                    <div style="position: relative;">
+                        @common.editor("body", pullRequest.body, "", "content-body")
+                    </div>
+                    @common.fileUploader(ResourceType.PULL_REQUEST, null)
+                    @common.markdown(project)
+
+                    <div class="actions">
+                        <button type="submit" class="ybtn ybtn-success">@Messages("pullRequest.send")</button>
+                        <a href="javascript:history.back();" class="ybtn">@Messages("button.cancel")</a>
                     </div>
                 </div>
 
-                <div id="frmWrap">
-                    @* this area will be replaced with asynchronously by yobi.git.Write.js after load this page. *@
-                    <input type="hidden" id="title">
-                    <textarea id="body" style="display: none;" data-editor-mode="content-body">@pullRequest.body</textarea>
-                    <div id="spin" style="position: absolute; top:50%; left:50%"></div>
+                <ul class="nav nav-tabs mt20">
+                    <li class="active">
+                        <a href="#__commits" data-toggle="tab">
+                            <span class="vmiddle-inline">@Messages("pullRequest.menu.commit")</span>
+                            <span id="numOfCommits" class="num-badge vmiddle-inline"></span>
+                        </a>
+                    </li>
+                </ul>
+
+                <div class="tab-content">
+                    <div id="__commits" class="code-browse-wrap tab-pane active">
+                    @** this area will be replaced with asynchronously by yobi.git.Write.js after load this page. **@
+                    </div>
                 </div>
             }
         </div>
     </div>
 </div>
+
+@common.select2()
+
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.css")">
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.caret.min.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.js")"></script>
 <script type="text/javascript">
-$(document).ready(function() {
+$(function(){
     $yobi.loadModule("git.Write", {
-        "sFormURL"      : "@routes.PullRequestApp.newPullRequestForm(project.owner, project.name)",
-        "welFromProject": $("div[data-name='fromProjectId']"),
-        "welToProject"  : $("div[data-name='toProjectId']"),
-        "welFromBranch" : $("div[data-name='fromBranch']"),
-        "welToBranch"   : $("div[data-name='toBranch']")
+        "mergeResultURL": "@routes.PullRequestApp.mergeResult(project.owner, project.name)",
+        "fromProject" : $("#fromProjectId"),
+        "toProject"   : $("#toProjectId"),
+        "fromBranch"  : $("#fromBranch"),
+        "toBranch"    : $("#toBranch")
     });
 
     // yobi.Mention
app/views/git/edit.scala.html
--- app/views/git/edit.scala.html
+++ app/views/git/edit.scala.html
@@ -18,112 +18,127 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **@
-@(title:String, form: Form[PullRequest], project: Project, fromBranches: List[playRepository.GitBranch], toBranches: List[playRepository.GitBranch], pull: PullRequest)
+@(title:String, form:Form[PullRequest], project:Project,
+  fromBranches: List[playRepository.GitBranch], toBranches:List[playRepository.GitBranch],
+  pullRequest:models.PullRequest)
 
 @import scala.collection.Map
 @import utils.TemplateHelper._
 @import utils.TemplateHelper.Branches._
 @implicitField = @{ helper.FieldConstructor(simpleForm) }
 
-@projectLayout(title, pull.toProject, utils.MenuType.PULL_REQUEST) {
-@projectMenu(pull.toProject, utils.MenuType.PULL_REQUEST, "main-menu-only")
+@projectLayout(title, pullRequest.toProject, utils.MenuType.PULL_REQUEST) {
+@projectMenu(pullRequest.toProject, utils.MenuType.PULL_REQUEST, "main-menu-only")
 <div class="page-wrap-outer">
     <div class="project-page-wrap">
         <div class="content-wrap frm-wrap">
-            @helper.form(action=routes.PullRequestApp.editPullRequest(pull.toProject.owner, pull.toProject.name, pull.number), 'enctype -> "multipart/form-data", 'class->"nm"){
+            @helper.form(action=routes.PullRequestApp.editPullRequest(pullRequest.toProject.owner, pullRequest.toProject.name, pullRequest.number), 'enctype -> "multipart/form-data", 'class->"nm"){
                 <div class="pull-request-wrap">
-                    <div class="option request-from">
-                        <div class="option-label">@Messages("pullRequest.from"):
-                            <div class="btn-group branches" data-name="fromProjectId">
-                                <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                    <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                    <span class="d-caret"><span class="caret"></span></span>
-                                </button>
-                                <ul class="dropdown-menu">
-                                    <li data-value="@pull.fromProject.id" data-selected="true">
-                                        <a href="#"><span class="label">repo</span>@pull.fromProject.owner/@pull.fromProject.name</a>
-                                    </li>
-                                </ul>
-                            </div>
-                        </div>
-                        <div class="option-desc mt5">
-                            <div class="btn-group branches" data-name="fromBranch">
-                                <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                    <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                    <span class="d-caret"><span class="caret"></span></span>
-                                </button>
-                                <ul class="dropdown-menu">
-                                    @defining(form("fromBranch").value()) { fromBranch =>
-                                        @common.branchItem("pullRequest", null, fromBranch, null, true)
-                                    }
-                                </ul>
-                            </div>
-                        </div>
+                    <!-- from -->
+                    <div class="pull-left">
+                        <label for="fromProjectId" class="field-title">@Messages("pullRequest.from")</label>
+                        <select id="fromProjectId" name="fromProjectId" data-toggle="select2" class="mr5" disabled>
+                            <option value="@pullRequest.fromProject.id" selected>
+                                @pullRequest.fromProject.owner / @pullRequest.fromProject.name
+                            </option>
+                        </select>
+                        <select id="fromBranch" name="fromBranch" data-toggle="select2" data-format="branch" disabled
+                                data-dropdown-css-class="branches" data-placeholder="@Messages("pullRequest.select.branch")">
+                            <option></option>
+                            @for(branch <- fromBranches) {
+                                <option value="@branch.getName" @if(branch.getName == pullRequest.fromBranch){ selected }>@branch.getName</option>
+                            }
+                        </select>
+
+                        <input type="hidden" name="fromProjectId" value="@pullRequest.fromProject.id">
+                        <input type="hidden" name="fromBranch" value="@pullRequest.fromBranch">
                     </div>
+                    <!-- // -->
 
                     <div class="arrow">
                         <i class="yobicon-right-2"></i>
                     </div>
 
-                    <div class="option request-to">
-                        <div class="option-label">@Messages("pullRequest.to"):
-                            <div class="btn-group branches" data-name="toProjectId">
-                                <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                    <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                    <span class="d-caret"><span class="caret"></span></span>
-                                </button>
-                                <ul class="dropdown-menu">
-                                    <li data-value="@pull.toProject.id" data-selected="true">
-                                        <a href="#"><span class="label">repo</span>@pull.toProject.owner/@pull.toProject.name</a>
-                                    </li>
-                                </ul>
-                            </div>
-                        </div>
-                        <div class="option-desc mt5">
-                            <div class="btn-group branches" data-name="toBranch">
-                                <button class="btn dropdown-toggle auto" data-toggle="dropdown">
-                                    <span class="d-label">@Messages("pullRequest.select.branch")</span>
-                                    <span class="d-caret"><span class="caret"></span></span>
-                                </button>
-                                <ul class="dropdown-menu">
-                                @defining(form("toBranch").value()) { toBranch =>
-                                    @common.branchItem("pullRequest", null, toBranch, null, true)
-                                }
-                                </ul>
-                            </div>
-                        </div>
+                    <!-- to -->
+                    <div class="pull-right">
+                        <label for="toProjectId" class="field-title">@Messages("pullRequest.to")</label>
+                        <select id="toProjectId" name="toProjectId" data-toggle="select2" class="mr5" disabled>
+                            <option value="@pullRequest.toProject.id" selected>
+                                @pullRequest.toProject.owner / @pullRequest.toProject.name
+                            </option>
+                        </select>
+                        <select id="toBranch" name="toBranch" data-toggle="select2" data-format="branch" disabled
+                                data-dropdown-css-class="branches" data-placeholder="@Messages("pullRequest.select.branch")">
+                            <option></option>
+                            @for(branch <- toBranches) {
+                                <option value="@branch.getName" @if(branch.getName == pullRequest.toBranch){ selected }>@branch.getName</option>
+                            }
+                        </select>
+
+                        <input type="hidden" name="toProjectId" value="@pullRequest.toProject.id">
+                        <input type="hidden" name="toBranch" value="@pullRequest.toBranch">
+                    </div>
+                    <!-- // -->
+                </div>
+
+                <div id="status" class="alert mt20 mb20">
+                    @Messages("pullRequest.is.merging")
+                </div>
+
+                <div>
+                    <input type="text" id="title" name="title" maxlength="255" class="text"
+                           value="@pullRequest.title" placeholder="@Messages("title")" data-is-user-has-typed="true">
+                    <div style="position: relative;">
+                        @common.editor("body", pullRequest.body, "data-is-user-has-typed=true", "content-body")
+                    </div>
+                    @common.fileUploader(ResourceType.PULL_REQUEST, pullRequest.id)
+                    @common.markdown(project)
+
+                    <div class="actions">
+                        <button type="submit" class="ybtn ybtn-success">@Messages("button.save")</button>
+                        <a href="javascript:history.back();" class="ybtn">@Messages("button.cancel")</a>
                     </div>
                 </div>
 
-                <input type="text" name="title" id="title" placeholder="" maxlength="maxlength" tabindex="1" class="text" value="@pull.title">
-                <div style="position: relative;">
-                    @common.editor("body", pull.body, "tabindex=2", "content-body")
-                </div>
+                <ul class="nav nav-tabs mt20">
+                    <li class="active">
+                        <a href="#__commits" data-toggle="tab">
+                            <span class="vmiddle-inline">@Messages("pullRequest.menu.commit")</span>
+                            <span id="numOfCommits" class="num-badge vmiddle-inline"></span>
+                        </a>
+                    </li>
+                </ul>
 
-                @common.fileUploader(ResourceType.PULL_REQUEST, pull.id)
-
-                <div class="actions">
-                    <button type="submit" class="ybtn ybtn-info">@Messages("button.save")</button>
-                    <a href="javascript:history.back();" class="ybtn">@Messages("button.cancel")</a>
+                <div class="tab-content">
+                    <div id="__commits" class="code-browse-wrap tab-pane active">
+                    @** this area will be replaced with asynchronously by yobi.git.Write.js after load this page. **@
+                    </div>
                 </div>
             }
         </div>
     </div>
 </div>
-@common.markdown(project)
+
+@common.select2()
 
 <link rel="stylesheet" type="text/css" media="screen" href="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.css")">
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.caret.min.js")"></script>
 <script type="text/javascript" src="@routes.Assets.at("javascripts/lib/atjs/jquery.atwho.js")"></script>
 <script type="text/javascript">
-	$(document).ready(function(){
-        $yobi.loadModule("git.Write");
+$(function(){
+    $yobi.loadModule("git.Write", {
+        "sMergeResultURL": "@routes.PullRequestApp.mergeResult(project.owner, project.name)",
+        "welFromProject" : $("#fromProjectId"),
+        "welToProject"   : $("#toProjectId"),
+        "welFromBranch"  : $("#fromBranch"),
+        "welToBranch"    : $("#toBranch")
+    });
 
-        // yobi.Mention
-        yobi.Mention({
-            "target": "textarea[id^=editor-]",
-            "url"   : "@routes.ProjectApp.mentionList(project.owner, project.name)"
-        });
-	});
+    // yobi.Mention
+    yobi.Mention({
+        "target": "textarea[id^=editor-]",
+        "url"   : "@routes.ProjectApp.mentionList(project.owner, project.name)"
+    });
+});
 </script>
 }
 
app/views/git/partial_diff.scala.html (deleted)
--- app/views/git/partial_diff.scala.html
@@ -1,138 +0,0 @@
-@**
-* Yobi, Project Hosting SW
-*
-* Copyright 2013 NAVER Corp.
-* http://yobi.io
-*
-* @Author Wansoon Park
-*
-* 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.
-**@
-@(form: Form[PullRequest], project: Project, pullRequest:models.PullRequest, result:models.PullRequestMergeResult)
-
-@import utils.JodaDateUtil
-@import utils.TemplateHelper._
-@import utils.AccessControl._
-@import models.enumeration
-@import playRepository.GitCommit
-@import scala.collection.Map
-
-@implicitField = @{ helper.FieldConstructor(simpleForm) }
-
-@getCodeURL(project: Project) = @{
-    if(session == null){
-        CodeApp.getURL(project.owner, project.name)
-    } else {
-        defining(ProjectUser.roleOf(session.get("loginId"), project)) { role =>
-            if(role == "manager" || role == "member"){
-                CodeApp.getURL(project.owner, project.name).replace("://", "://" + session.get("loginId") + "@")
-            } else {
-                CodeApp.getURL(project.owner, project.name)
-            }
-        }
-    }
-}
-
-<input type="text" name="title" id="title" placeholder="@Messages("title")" maxlength="255" tabindex="1" class="text" value="@pullRequest.title">
-<div style="position: relative;">
-    @common.editor("body", pullRequest.body, "tabindex=2", "content-body")
-</div>
-@common.fileUploader(ResourceType.PULL_REQUEST, null)
-@common.markdown(project)
-
-<div class="actions">
-    <button type="submit" class="ybtn ybtn-success">@Messages("button.save")</button>
-    <a href="javascript:history.back();" class="ybtn">@Messages("button.cancel")</a>
-</div>
-
-<div id="spin" style="position: absolute; top:50%; left:50%"></div>
-
-<div id="diff">
-@if(result != null) {
-    @if(result.getGitCommits().isEmpty) {
-        <h4>@Messages("pullRequest.diff.noChanges")</h4>
-    } else {
-        <div style="margin-top:20px;">
-            @if(result.getGitConflicts == null){
-            <div class="alert alert-success">
-                <h5>@Messages("pullRequest.is.safe")</h5>
-            </div>
-            } else {
-            <div class="alert alert-error">
-                <h5>@Messages("pullRequest.is.not.safe")</h5>
-            </div>
-            }
-        </div>
-
-        <ul class="nav nav-tabs nm">
-            <li class="active"><a href="#__commits" data-toggle="tab">@Messages("pullRequest.menu.commit")</a></li>
-        </ul>
-        <div class="tab-content">
-            <div id="__commits" class="code-browse-wrap tab-pane active">
-                <div id="history" class="commit-wrap mt20">
-                    <table class="code-table commits">
-                        <thead class="thead">
-                        <tr>
-                            <td class="commit-id"><strong>@{"@"}</strong></td>
-                            <td class="messages"><strong>@Messages("code.commitMsg")</strong></td>
-                            <td class="date"><strong>@Messages("code.commitDate")</strong></td>
-                            <td class="author"><strong>@Messages("code.author")</strong></td>
-                        </tr>
-                        </thead>
-                        <tbody class="tbody">
-                        @for(commit <- result.getGitCommits) {
-                        <tr>
-                            <td class="commit-id">
-                                <a href="@routes.CodeHistoryApp.show(pullRequest.fromProject.owner, pullRequest.fromProject.name, commit.getId)">
-                                    @commit.getShortId
-                                </a>
-                            </td>
-                            <td class="messages">
-                                @defining(CommitComment.count(pullRequest.fromProject, commit.getId, null)){ numOfComment =>
-                                    @if(numOfComment > 0) {
-                                    <span class="number-of-comments"><i class="yobicon-comments"></i> @numOfComment</span>
-                                    }
-                                }
-                                @defining(commit.getMessage){ commitMsg =>
-                                    @common.commitMsg(commitMsg.split("\n")(0),
-                                        commitMsg,
-                                        routes.CodeHistoryApp.show(pullRequest.fromProject.owner, pullRequest.fromProject.name, commit.getId).toString())
-                                }
-                            </td>
-                            <td class="date" title="@JodaDateUtil.getDateString(commit.getAuthorDate)">
-                                @agoOrDateString(commit.getAuthorDate)
-                            </td>
-                            <td class="author @commit.getAuthorEmail">
-                                @defining(User.find.where.eq("email", commit.getAuthorEmail).findUnique) { user =>
-                                @if(user != null) {
-                                <a href="@routes.UserApp.userInfo(user.loginId)" class="avatar-wrap">
-                                    <img src="@user.avatarUrl" alt="@user.name" width="32" height="32"/>
-                                </a>
-                                } else {
-                                <div class="avatar-wrap">
-                                    <img src="@urlToPicture(commit.getAuthorEmail, 32)" width="32" height="32"/>
-                                </div>
-                                }
-                                }
-                            </td>
-                        </tr>
-                        }
-                        </tbody>
-                    </table>
-                </div>
-            </div>
-        </div>
-    }
-}
-</div>
-<input type="hidden" id="commitChanged" value="@if(result != null){ @result.hasDiffCommits } else {false}" />
 
app/views/git/partial_merge_result.scala.html (added)
+++ app/views/git/partial_merge_result.scala.html
@@ -0,0 +1,99 @@
+@**
+* 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.
+**@
+@(project: Project, pullRequest:models.PullRequest, result:models.PullRequestMergeResult)
+
+@import utils.JodaDateUtil
+@import utils.TemplateHelper._
+@import utils.AccessControl._
+@import models.enumeration
+@import playRepository.GitCommit
+@import scala.collection.Map
+
+@if(result != null) {
+    @defining(result.getGitCommits){ commits =>
+        <div id="mergeResult" class="code-browser-wrap"
+             data-commits="@commits.size"
+             data-pullrequest-title="@pullRequest.title"
+             data-pullrequest-body="@pullRequest.body"
+             @if(!commits.isEmpty){
+                data-conflict="@result.conflicts"
+             }
+        >
+            @if(commits.isEmpty) {
+                <div>
+                    <h5>@Messages("pullRequest.diff.noChanges")</h5>
+                </div>
+            } else {
+                <div class="commit-wrap">
+                    <table class="code-table commits">
+                        <thead class="thead">
+                        <tr>
+                            <td class="commit-id"><strong>@{"@"}</strong></td>
+                            <td class="messages"><strong>@Messages("code.commitMsg")</strong></td>
+                            <td class="date"><strong>@Messages("code.commitDate")</strong></td>
+                            <td class="author"><strong>@Messages("code.author")</strong></td>
+                        </tr>
+                        </thead>
+                        <tbody class="tbody">
+                        @for(commit <- commits){
+                        <tr>
+                            <td class="commit-id">
+                                <a href="@routes.CodeHistoryApp.show(pullRequest.fromProject.owner, pullRequest.fromProject.name, commit.getId)">
+                                    @commit.getShortId
+                                </a>
+                            </td>
+                            <td class="messages">
+                                @defining(CommitComment.count(pullRequest.fromProject, commit.getId, null)){ numOfComment =>
+                                    @if(numOfComment > 0) {
+                                    <span class="number-of-comments"><i class="yobicon-comments"></i> @numOfComment</span>
+                                    }
+                                }
+                                @defining(commit.getMessage){ commitMsg =>
+                                    @common.commitMsg(commitMsg.split("\n")(0),
+                                        commitMsg,
+                                        routes.CodeHistoryApp.show(pullRequest.fromProject.owner, pullRequest.fromProject.name, commit.getId).toString())
+                                }
+                            </td>
+                            <td class="date" title="@JodaDateUtil.getDateString(commit.getAuthorDate)">
+                                @agoOrDateString(commit.getAuthorDate)
+                            </td>
+                            <td class="author @commit.getAuthorEmail">
+                                @defining(User.find.where.eq("email", commit.getAuthorEmail).findUnique) { user =>
+                                  @if(user != null) {
+                                      <a href="@routes.UserApp.userInfo(user.loginId)" class="avatar-wrap">
+                                          <img src="@user.avatarUrl" alt="@user.name" width="32" height="32"/>
+                                      </a>
+                                  } else {
+                                      <div class="avatar-wrap">
+                                          <img src="@urlToPicture(commit.getAuthorEmail, 32)" width="32" height="32"/>
+                                      </div>
+                                  }
+                                }
+                            </td>
+                        </tr>
+                        }
+                        </tbody>
+                    </table>
+                </div>
+            }
+        </div>
+    }
+}
conf/messages
--- conf/messages
+++ conf/messages
@@ -580,6 +580,7 @@
 pullRequest.help.message.1 = This is a simple way to contribute your code to original project.
 pullRequest.help.message.2 = Please select your branch containing your code to be sent and that of original project to receive your code, and describe your code briefly.
 pullRequest.help.message.3 = Member of the original project can accept or postpone your code.
+pullRequest.ignore.conflict = This code seems to have conflicts when merging. Do you really want to continue?
 pullRequest.is.checking = Checking
 pullRequest.is.empty = No pull requests have been received
 pullRequest.is.merging = We are checking if the code is safe. Please wait for a while to complete this process.
@@ -626,6 +627,7 @@
 pullRequest.review.participants = <strong>{0}</strong> participants
 pullRequest.review.total = Total review
 pullRequest.select.branch = Select branch
+pullRequest.send = Send pull request
 pullRequest.sender = Sender
 pullRequest.sent = Sent code
 pullRequest.sentByMe = Sent by me
conf/messages.ja
--- conf/messages.ja
+++ conf/messages.ja
@@ -437,6 +437,7 @@
 pullRequest.help.message.1 = 原本プロジェクトにコードを寄与する方法です
 pullRequest.help.message.2 = 送ろうとするコードが入っているブランチとそれを受けてくれる原本プロジェクトのブランチを指定しどんなコードか説明してください
 pullRequest.help.message.3 = 原本プロジェクトのメンバーがプルリクエストを確認し、受諾するか保留するかを決めます
+pullRequest.ignore.conflict = このコードはコンフリクトが発生するように見えます。それでもコードを送りますか?
 pullRequest.is.empty = プルリクエストがありません
 pullRequest.is.merging = Checking whether code is safety. Please wait for a while to complete.
 pullRequest.is.not.safe = コードは自動的にマージすると衝突が発生します。自動でマージできません。
@@ -469,6 +470,7 @@
 pullRequest.resolver.step9 = rebaseが終わったらoriginにコードを強制的にpushします
 pullRequest.restore.branch = ブランチ復旧
 pullRequest.restore.frombranch.message = ブランチを復旧できます
+pullRequest.send = プルリクエストを送る
 pullRequest.sender = 送り主
 pullRequest.sent = 送ったリクエスト
 pullRequest.sentByMe = 私が送ったリクエスト
conf/messages.ko
--- conf/messages.ko
+++ conf/messages.ko
@@ -580,6 +580,7 @@
 pullRequest.help.message.1 = 원본 프로젝트에 코드를 기여하는 방법입니다.
 pullRequest.help.message.2 = 본인이 보낼 코드가 들어있는 브랜치와 보낸 코드를 받아줄 원본 프로젝트의 브랜치를 지정하고 어떤 코드인지 설명해 주세요.
 pullRequest.help.message.3 = 원본 프로젝트의 멤버가 해당 코드를 확인하고 코드를 받거나 보류할 수 있습니다.
+pullRequest.ignore.conflict = 충돌이 발생하여 코드를 자동으로 병합할 수 없습니다. 그래도 코드를 보내시겠습니까?
 pullRequest.is.checking = 확인중
 pullRequest.is.empty = 등록된 코드 주고 받기가 없습니다.
 pullRequest.is.merging = 코드가 안전한지 확인하고 있습니다. 완료될때까지 잠시만 기다려주십시오.
@@ -626,6 +627,7 @@
 pullRequest.review.participants = 참여자 <strong>{0}</strong>명
 pullRequest.review.total = 전체 리뷰
 pullRequest.select.branch = 브랜치를 선택하세요.
+pullRequest.send = 코드 보내기
 pullRequest.sender = 보낸 사람
 pullRequest.sent = 보낸 코드
 pullRequest.sentByMe = 내가 보낸 코드
conf/routes
--- conf/routes
+++ conf/routes
@@ -187,6 +187,7 @@
 GET            /:ownerName/:project/pullRequest/:id/changes/:commitId                 controllers.PullRequestApp.specificChange(ownerName, project, id: Long, commitId: String)
 GET            /:ownerName/:project/pullRequest/:id/state                             controllers.PullRequestApp.pullRequestState(ownerName, project, id: Long)
 GET            /:ownerName/:project/newPullRequestForm                                controllers.PullRequestApp.newPullRequestForm(ownerName:String, project:String)
+GET            /:ownerName/:project/newPullRequest/mergeResult                        controllers.PullRequestApp.mergeResult(ownerName, project)
 POST           /:ownerName/:project/pullRequests                                      controllers.PullRequestApp.newPullRequest(ownerName, project)
 POST           /:ownerName/:project/pullRequest/:id/accept                            controllers.PullRequestApp.accept(ownerName, project, id: Long)
 POST           /:ownerName/:project/pullRequest/:id/close                             controllers.PullRequestApp.close(ownerName, project, id: Long)
public/javascripts/service/yobi.git.Write.js
--- public/javascripts/service/yobi.git.Write.js
+++ public/javascripts/service/yobi.git.Write.js
@@ -20,243 +20,252 @@
  */
 (function(ns){
     var oNS = $yobi.createNamespace(ns);
-    oNS.container[oNS.name] = function(htOptions){
+    oNS.container[oNS.name] = function(options){
 
-        var htVar = {};
-        var htElement = {};
+        var vars = {};
+        var elements = {};
 
         /**
          * initialize
          */
-        function _init(htOptions){
-            _initVar(htOptions || {});
-            _initElement(htOptions || {});
+        function _init(options){
+            _initVar(options);
+            _initElement(options);
             _attachEvent();
 
             _initFileUploader();
+            _onChangeBranch();
         }
 
         /**
          * initialize variables
          */
-        function _initVar() {
-            htVar.sFormURL = htOptions.sFormURL;
-            htVar.oFromProject = new yobi.ui.Dropdown({"elContainer": htOptions.welFromProject});
-            htVar.oToProject = new yobi.ui.Dropdown({"elContainer": htOptions.welToProject});
-            htVar.oFromBranch  = new yobi.ui.Dropdown({"elContainer": htOptions.welFromBranch});
-            htVar.oToBranch  = new yobi.ui.Dropdown({"elContainer": htOptions.welToBranch});
-            htVar.sUploaderId = null;
-            htVar.oSpinner = null;
-
-            htVar.htUserInput = {};
-            htVar.sTplFileItem = $('#tplAttachedFile').text();
+        function _initVar(options) {
+            vars.mergeResult = {};
+            vars.mergeResultURL = options.mergeResultURL;
+            vars.tplFileItem = $('#tplAttachedFile').text();
+            vars.uploaderId = null;
         }
 
         /**
          * initialize element variables
          */
-        function _initElement(htOptions){
-            htElement.welForm = $("form.nm");
-            htElement.welInputTitle = $('#title');
-            htElement.welInputBody  = $('textarea[data-editor-mode="content-body"]');
+        function _initElement(options){
+            elements.form  = $("form.nm");
+            elements.title = $('#title');
+            elements.body  = $('textarea[data-editor-mode="content-body"]');
 
-            htElement.welInputFromProject = $('input[name="fromProjectId"]');
-            htElement.welInputToProject = $('input[name="toProjectId"]');
-            htElement.welInputFromBranch = $('input[name="fromBranch"]');
-            htElement.welInputToBranch = $('input[name="toBranch"]');
+            elements.fromProject = options.fromProject;
+            elements.fromBranch  = options.fromBranch;
+            elements.toProject   = options.toProject;
+            elements.toBranch    = options.toBranch;
 
-            htElement.welUploader = $("#upload");
-            htElement.welContainer = $("#frmWrap");
-
-            htElement.welAbleToMerge = $("#ableToMerge");
-            htElement.welCommitCount = $("#commitCount");
+            elements.uploader = $("#upload");
+            elements.numOfCommits = $("#numOfCommits");
+            elements.commits = $("#__commits");
+            elements.status = $("#status");
         }
 
         /**
          * attach event handlers
          */
         function _attachEvent(){
-            htElement.welForm.submit(_onSubmitForm);
-            htElement.welInputTitle.on("keyup", _onKeyupInput);
-            htElement.welInputBody.on("keyup", _onKeyupInput);
+            elements.form.on("submit", _onSubmitForm);
+            elements.title.on("keyup", _onKeyUpInput);
+            elements.body.on("keyup", _onKeyUpInput);
 
-            htVar.oFromProject.onChange(_refreshNewPullRequestForm);
-            htVar.oToProject.onChange(_refreshNewPullRequestForm);
-            htVar.oFromBranch.onChange(_reloadNewPullRequestForm);
-            htVar.oToBranch.onChange(_reloadNewPullRequestForm);
+            // onChangeProject
+            elements.fromProject.on("change", _onChangeProject);
+            elements.toProject.on("change", _onChangeProject);
+
+            // onChangeBranch
+            elements.fromBranch.on("change", _onChangeBranch);
+            elements.toBranch.on("change", _onChangeBranch);
 
             $(document.body).on("click", "button.moreBtn", function(){
                 $(this).next("pre.commitMsg.desc").toggleClass("hidden");
             });
-
-            $('body').on('click','button.more',function(){
-               $(this).next('pre').toggleClass("hidden");
-            });
-
-            _reloadNewPullRequestForm();
         }
 
         /**
-         * @param {Wrapped Event} weEvt
+         * "keyup" event handler of inputTitle, inputBody.
+         * Mark as user has typed on this input, and detach this event handler.
+         * Ignore if the pressed key is ENTER.
+         *
+         * @param {Wrapped Event} evt
          */
-        function _onKeyupInput(weEvt){
-            var welTarget = $(weEvt.target);
-            var sInputId = welTarget.attr("id");
-            htVar.htUserInput = htVar.htUserInput || {};
-            htVar.htUserInput[sInputId] = true;
+        function _onKeyUpInput(evt){
+            var keyCode = (evt.keyCode || evt.which);
+
+            if(keyCode !== 13){
+                $(evt.target).data("isUserHasTyped", true)
+                             .off("keyup", _onKeyUpInput);
+            }
         }
 
         /**
+         * Reload page with changed fromProjectId, toProjectId query string.
+         *
          * @private
          */
-        function _refreshNewPullRequestForm(){
-            var htData = {};
+        function _onChangeProject(){
+            var data = _getFormValue();
 
-            htData.fromProjectId = htVar.oFromProject.getValue();
-            htData.toProjectId = htVar.oToProject.getValue();
-
-            document.location.href = htVar.sFormURL + "?fromProjectId=" + htData.fromProjectId + "&toProjectId=" + htData.toProjectId;
+            location.search = "?fromProjectId=" + data.fromProject + "&toProjectId=" + data.toProject;
         }
 
         /**
-         * request to reload pullRequestForm
+         * Request merge result with changed branch.
+         *
+         * @private
          */
-        function _reloadNewPullRequestForm(){
-            var htData = {};
-            htData.fromBranch = htVar.oFromBranch.getValue();
-            htData.toBranch = htVar.oToBranch.getValue();
-            htData.fromProjectId = htVar.oFromProject.getValue();
-            htData.toProjectId = htVar.oToProject.getValue();
+        function _onChangeBranch(){
+            var data = _getFormValue();
 
-            if(!(htData.fromBranch && htData.toBranch)) {
+            if(!data.fromBranch && !data.toBranch){
                 return;
             }
 
-            _startSpinner();
+            _showMergeResult({"message" : Messages("pullRequest.is.merging")});
 
-            $.ajax(htVar.sFormURL, {
-                "method" : "get",
-                "data"   : htData,
-                "success": _onSuccessReloadForm,
-                "error"  : _onErrorReloadForm
+            yobi.ui.Spinner.show();
+
+            $.ajax(vars.mergeResultURL, {
+                "data": data
+            })
+            .done(_onSuccessMergeResult)
+            .fail(_onErrorMergeResult)
+            .always(function(){
+                yobi.ui.Spinner.hide();
             });
         }
 
         /**
-         * onSuccess to reloadForm
+         * On success to load mergeResult
+         * Fill element.commits and form field title/body.
+         * and show result with parsing responded HTML.
+         *
+         * @param resultHTML
+         * @private
          */
-        function _onSuccessReloadForm(sRes){
-            var sTitle = htElement.welInputTitle.val();
-            var sBody = htElement.welInputBody.val();
-
-            htElement.welContainer.html(sRes);
-            _reloadElement();
-
-            // 만약 사용자가 입력한 제목이나 본문이 있으면 내용을 유지한다
-            if(sTitle.length > 0 && htVar.htUserInput.title){
-                htElement.welInputTitle.val(sTitle);
-            }
-            if(sBody.length > 0 && htVar.htUserInput.body){
-                htElement.welInputBody.val(sBody);
-            }
-
-            _initFileUploader();
-            _stopSpinner();
-            _updateSummary();
-        }
-
-        function _updateSummary() {
-            var success = $(".alert-success");
-            var error = $(".alert-error");
-            if(success.length > 0) {
-                htElement.welAbleToMerge.text(Messages("pullRequest.is.safe.to.merge"));
-            } else if(error.length > 0){
-                htElement.welAbleToMerge.text(Messages("pullRequest.is.not.safe.to.merge"));
-            } else {
-                htElement.welAbleToMerge.text(Messages("pullRequest.diff.noChanges"));
-            }
-
-            var commits = $(".commits tr");
-            if(commits.length > 0) {
-                htElement.welCommitCount.text(commits.length - 1);
-            } else {
-                htElement.welCommitCount.text(Messages("pullRequest.commit.is.empty"));
-            }
-
-        }
-
-        function _reloadElement(){
-            htElement.welInputTitle = $('#title');
-            htElement.welInputBody  = $('textarea[data-editor-mode="content-body"]');
-            htElement.welUploader = $("#upload");
-
-            htElement.welInputTitle.on("keyup", _onKeyupInput);
-            htElement.welInputBody.on("keyup", _onKeyupInput);
+        function _onSuccessMergeResult(resultHTML){
+            elements.commits.html(resultHTML);
+            vars.mergeResult = _getMergeResultData();
+            _showMergeResult(vars.mergeResult);
+            _fillFormTitleBody(vars.mergeResult);
         }
 
         /**
-         * onFailed to reloadForm
+         * Returns merge result data to show.
+         * {@code container} relative data relies on views/git/partial_merge_result.scala.html
+         *
+         * @returns {{cssClass: string, message: *, isConflict: boolean, numOfCommits: Number, title: *, body: *}}
+         * @private
          */
-        function _onErrorReloadForm(oRes){
-            _stopSpinner();
-            $yobi.alert(Messages("pullRequest.error.newPullRequestForm", oRes.status, oRes.statusText));
-        }
+        function _getMergeResultData(){
+            var container = $("#mergeResult");
+            var data = {
+                "cssClass"    : "alert-info",
+                "message"     : Messages("pullRequest.diff.noChanges"),
+                "isConflict"  : (container.data("conflict") === "true"),
+                "numOfCommits": parseInt(container.data("commits"), 10),
+                "title"       : container.data("pullrequestTitle"),
+                "body"        : container.data("pullrequestBody")
+            };
 
-        function _startSpinner(){
-            htVar.oSpinner = htVar.oSpinner || new Spinner();
-            htVar.oSpinner.spin(document.getElementById('spin'));
-        }
-
-        function _stopSpinner(){
-            if(htVar.oSpinner){
-                htVar.oSpinner.stop();
+            if(data.numOfCommits > 0){
+                data.message  = data.isConflict ? Messages("pullRequest.is.not.safe") : Messages("pullRequest.is.safe");
+                data.cssClass = data.isConflict ? "alert-error" : "alert-success";
             }
-            htVar.oSpinner = null;
+
+            return data;
+        }
+
+        function _showMergeResult(mergeResult){
+            elements.status.removeClass("alert-success alert-error alert-info")
+                               .addClass(mergeResult.cssClass)
+                               .html(mergeResult.message);
+
+            elements.numOfCommits.html(mergeResult.numOfCommits || "");
         }
 
         /**
-         * Event handler on submit form
+         * Fill form input title and body with merge result data
+         * if user doesn't have typed.
+         *
+         * @param data
+         * @private
          */
-        function _onSubmitForm(weEvt){
+        function _fillFormTitleBody(data){
+            var isUserHasTyped = elements.title.data("isUserHasTyped") ||
+                                 elements.body.data("isUserHasTyped");
+
+            if(!isUserHasTyped){
+                elements.title.val(data.title);
+                elements.body.val(data.body);
+            }
+        }
+
+        /**
+         * On error occurs to get merge result.
+         * Show error message and response status.
+         *
+         * @param res
+         * @private
+         */
+        function _onErrorMergeResult(res){
+            _showMergeResult({
+                "message" : Messages("pullRequest.error.newPullRequestForm", res.status, res.statusText),
+                "cssClass": "alert-error"
+            });
+            _fillFormTitleBody({"title":"", "body":""});
+        }
+
+        /**
+         * "submit" event handler of the form.
+         * Returns false if validate fails.
+         *
+         * @returns {Boolean}
+         * @private
+         */
+        function _onSubmitForm(){
             return _validateForm();
         }
 
         /**
          * Validate form before submit
+         *
+         * @returns {boolean}
+         * @private
          */
         function _validateForm(){
-            // these two fields should be loaded dynamically.
-            htElement.welInputFromBranch = $('input[name="fromBranch"]');
-            htElement.welInputToBranch = $('input[name="toBranch"]');
-            htElement.welInputFromProject = $('input[name="fromProjectId"]');
-            htElement.welInputToProject = $('input[name="toProjectId"]');
+            // Check whether is commit exists to send
+            if(!vars.mergeResult.numOfCommits){
+                $yobi.alert(Messages("pullRequest.diff.noChanges"));
+                return false;
+            }
 
-            // check whether required field is empty
-            var htRequired = {
-                "title"     : $.trim(htElement.welInputTitle.val()),
-                "fromProject": $.trim(htElement.welInputFromProject.val()),
-                "toProject" : $.trim(htElement.welInputToProject.val()),
-                "fromBranch": $.trim(htElement.welInputFromBranch.val()),
-                "toBranch"  : $.trim(htElement.welInputToBranch.val())
-            };
+            // Show confirm dialog in case of conflict
+            if(vars.mergeResult.isConflict && !vars.mergeResult.forceSubmit){
+                $yobi.confirm(Messages("pullRequest.ignore.conflict"), function(data){
+                    if(data.nButtonIndex === 1){
+                        vars.mergeResult.forceSubmit = true;
+                        elements.form.submit();
+                    }
+                });
+                return false;
+            }
 
-            for(var sMessageKey in htRequired){
-                if(htRequired[sMessageKey].length === 0){
-                    $yobi.alert(Messages("pullRequest." + sMessageKey + ".required"));
+            // Check whether required field is empty
+            var requiredField = _getFormValue();
+
+            for(var fieldName in requiredField){
+                if(requiredField[fieldName].length === 0){
+                    $yobi.alert(Messages("pullRequest." + fieldName + ".required"));
                     return false;
                 }
             }
 
-            if(!htVar.sFormURL) {
-                return true;
-            }
-
-            var bCommitNotChanged = $.trim($("#commitChanged").val()) != "true";
-
-            if(bCommitNotChanged) {
-                $yobi.alert(Messages("pullRequest.diff.noChanges"));
-                return false;
-            }
             return true;
         }
 
@@ -264,24 +273,35 @@
          * initialize fileUploader
          */
         function _initFileUploader(){
-            if(htVar.sUploaderId){
-                htVar.oAttachments.destroy();
-                yobi.Files.destroyUploader(htVar.sUploaderId);
-                htVar.sUploaderId = null;
+            if(vars.uploaderId){
+                vars.attachments.destroy();
+                yobi.Files.destroyUploader(vars.uploaderId);
+                vars.uploaderId = null;
             }
 
-            var oUploader = yobi.Files.getUploader(htElement.welUploader, htElement.welInputBody);
+            var oUploader = yobi.Files.getUploader(elements.uploader, elements.body);
+
             if(oUploader){
-                htVar.sUploaderId = oUploader.attr("data-namespace");
-                htVar.oAttachments = new yobi.Attachments({
-                    "elContainer"  : htElement.welUploader,
-                    "elTextarea"   : htElement.welInputBody,
-                    "sTplFileItem" : htVar.sTplFileItem,
-                    "sUploaderId"  : htVar.sUploaderId
+                vars.uploaderId = oUploader.attr("data-namespace");
+                vars.attachments = new yobi.Attachments({
+                    "elContainer"  : elements.uploader,
+                    "elTextarea"   : elements.body,
+                    "sTplFileItem" : vars.tplFileItem,
+                    "sUploaderId"  : vars.uploaderId
                 });
             }
         }
 
-        _init(htOptions || {});
+        function _getFormValue(){
+            return {
+                "title"      : $.trim(elements.title.val()),
+                "fromProject": $.trim(elements.fromProject.val()),
+                "toProject"  : $.trim(elements.toProject.val()),
+                "fromBranch" : $.trim(elements.fromBranch.val()),
+                "toBranch"   : $.trim(elements.toBranch.val())
+            };
+        }
+
+        _init(options || {});
     };
 })("yobi.git.Write");
Add a comment
List