[Notice] Announcing the End of Demo Server [Read me]
Jihan Kim 2013-04-29
merge with nforge:master
@e839b2a3d07e513e6945480159f658a7a84df71a
app/assets/stylesheets/less/_page.less
--- app/assets/stylesheets/less/_page.less
+++ app/assets/stylesheets/less/_page.less
@@ -447,7 +447,7 @@
         position: absolute;
         right: -27px;top: 23px;
         width : 18px !important;
-        height: 16px !important; /* 40px: for 2 button */
+        height: 40px !important; /* 40px: for 2 button */
         padding:16px 3px 16px 3px;
         
         .side-menu {
@@ -1494,12 +1494,12 @@
     position: relative;
     overflow: hidden;
     
-    /*height:40px;*/
-    display:block; clear:both;
+    height:40px; display:block; clear:both;
     
     .file {
-        position: absolute; z-index:2; cursor:pointer;
-        top:0; left:-5px; width:100px;
+        position: absolute; z-index:2;
+        cursor:pointer;
+        left: -15px;
         .opacity(0);
     }
     
@@ -2579,11 +2579,11 @@
             width: 115px; /*155px;*/
         }
     }
-    
+
     .branches {
-        .label { 
+        .label {
             margin:0; padding:0 5px; margin-right:3px;
-            
+
             &.branch { background-color:@orange; }
         }
     }
@@ -2794,6 +2794,8 @@
         /*padding:2px 4px; cursor:pointer;*/
         width:10px; cursor:e-resize;
         height: 99.9%;
+        vertical-align: middle;
+        color: gray;
         border-left: 1px solid #ccc;
         background: #e8e8e8;
         background: -webkit-gradient(linear, left top, right top, color-stop(0%,#f5f5f5), color-stop(100%,#e9e9e9));
app/controllers/BoardApp.java
--- app/controllers/BoardApp.java
+++ app/controllers/BoardApp.java
@@ -58,6 +58,9 @@
         Form<SearchCondition> postParamForm = new Form<SearchCondition>(SearchCondition.class);
         SearchCondition searchCondition = postParamForm.bindFromRequest().get();
         searchCondition.pageNum = pageNum - 1;
+        if(searchCondition.orderBy.equals("id")) {
+            searchCondition.orderBy = "createdDate";
+        }
 
         ExpressionList<Posting> el = searchCondition.asExpressionList(project);
         Page<Posting> posts = el.findPagingList(ITEMS_PER_PAGE).getPage(searchCondition.pageNum);
app/controllers/UserApp.java
--- app/controllers/UserApp.java
+++ app/controllers/UserApp.java
@@ -25,6 +25,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.persistence.Transient;
+
 import static play.data.Form.form;
 import static play.libs.Json.toJson;
 
@@ -198,6 +200,40 @@
 		return user;
 	}
 
+	public static Result resetUserPassword() {
+		Form<User> userForm = form(User.class).bindFromRequest();
+
+		if(userForm.hasErrors()) {
+			return badRequest();
+		}
+
+		User currentUser = currentUser();
+		User user = userForm.get();
+		
+		if(!isValidPassword(currentUser, user.oldPassword)) {
+			Form<User> currentUserForm = new Form<User>(User.class);
+			currentUserForm = currentUserForm.fill(currentUser);
+	    
+			flash(Constants.WARNING, "user.wrongPassword.alert");
+			return badRequest(edit.render(currentUserForm, currentUser));
+		}
+		
+		resetPassword(currentUser, user.password);
+		
+		//go to login page
+		session().clear();
+		response().discardCookie(TOKEN);
+		
+		flash(Constants.WARNING, "user.loginWithNewPassword");
+        return redirect(routes.UserApp.loginForm());
+
+	}
+	
+	public static boolean isValidPassword(User currentUser, String password) {
+		String hashedOldPassword = hashedPassword(password, currentUser.passwordSalt);
+		return currentUser.password.equals(hashedOldPassword); 
+	}
+	
     public static void resetPassword(User user, String newPassword) {
 		user.password = new Sha256Hash(newPassword,
 				ByteSource.Util.bytes(user.passwordSalt), 1024).toBase64();
app/models/User.java
--- app/models/User.java
+++ app/models/User.java
@@ -43,6 +43,9 @@
 
     @Pattern(value = "^[a-zA-Z0-9-]+([_.][a-zA-Z0-9-]+)*$", message = "user.wrongloginId.alert") @Required
     public String loginId;
+    
+    @Transient
+    public String oldPassword;
     public String password;
     public String passwordSalt;
 
@@ -175,7 +178,6 @@
                 ByteSource.Util.bytes(user.passwordSalt), 1024).toBase64();
         user.save();
     }
-
 
     public Resource asResource() {
         return new Resource() {
app/views/board/postList.scala.html
--- app/views/board/postList.scala.html
+++ app/views/board/postList.scala.html
@@ -4,6 +4,8 @@
 @import utils.AccessControl._
 @import scala.collection.immutable.Map
 
+@urlToList = {@routes.BoardApp.posts(project.owner, project.name, page.getPageIndex + 1)}
+
 @** header(label:String, key:String) = {
   <th>
     <a key="@key" href="@routes.BoardApp.posts(project.owner, project.name)">@label</a>
@@ -28,7 +30,7 @@
         <form method="get" id="option_form">
           <input type="hidden" name="orderBy"  value="@param.orderBy">
           <input type="hidden" name="orderDir" value="@param.orderDir">
-          <input name="filter" class="text" type="text" placeholder="@Messages("project.searchPlaceholder")" value="@param.filter"><!-- 
+          <input name="filter" class="text" type="text" placeholder="@Messages("project.searchPlaceholder")" value="@param.filter"><!--
            --><button type="submit" class="btn search-btn underConstruction">@Messages("post.menu.search")</button>
         </form>
       </div>
@@ -77,13 +79,25 @@
 
   <div class="filter-wrap board">
       <div class="filters" id="order">
-          <a href="#" key="date" class="filter underConstruction @if(param.orderBy == "date"){ active }">
-			<i class="ico btn-gray-arrow @if(param.orderBy == "date" && param.orderDir == "desc"){down}"></i>날짜순
-          </a>
+          @if(param.orderBy.equals("createdDate")) {
+            @if(param.orderDir.equals("asc")) {
+                <a href="#" data-orderBy="createdDate" data-orderDir="desc" class="filter active"><i class="ico btn-gray-arrow"></i>날짜순</a>
+            } else {
+                <a href="#" data-orderBy="createdDate" data-orderDir="asc" class="filter active"><i class="ico btn-gray-arrow down"></i>날짜순</a>
+            }
+          } else {
+              <a href="#" data-orderBy="createdDate" data-orderDir="asc" class="filter"><i class="ico btn-gray-arrow"></i>날짜순</a>
+          }
 
-          <a href="#" key="commentCount" class="filter underConstruction @if(param.orderBy == "commentCount"){ active }">
-            <i class="ico btn-gray-arrow @if(param.orderBy == "commentCount" && param.orderDir == "desc"){down}"></i>댓글순
-          </a>
+          @if(param.orderBy.equals("numOfComments")) {
+              @if(param.orderDir.equals("asc")) {
+                  <a href="#" data-orderBy="numOfComments" data-orderDir="desc" class="filter active"><i class="ico btn-gray-arrow"></i>댓글순</a>
+              } else {
+                  <a href="#" data-orderBy="numOfComments" data-orderDir="asc" class="filter active"><i class="ico btn-gray-arrow down"></i>댓글순</a>
+              }
+          } else {
+              <a href="#" data-orderBy="numOfComments" data-orderDir="asc" class="filter"><i class="ico btn-gray-arrow"></i>댓글순</a>
+          }
       </div>
   </div>
 
app/views/code/codeView.scala.html
--- app/views/code/codeView.scala.html
+++ app/views/code/codeView.scala.html
@@ -11,8 +11,8 @@
 @makeBranchItem(branch:String) = {
 	@defining(branch.split('/')){ names =>
 		@if(names(0).equals("refs") && names.length == 3){
-			<li data-value="@branch"><a href="#"><!-- 
-			 --><span class="label @branchItemType(names(1))">@branchItemType(names(1))</span><!-- 
+			<li data-value="@branch"><a href="#"><!--
+			 --><span class="label @branchItemType(names(1))">@branchItemType(names(1))</span><!--
 			 -->@names(2)
 			</a></li>
 		} else {
@@ -40,7 +40,7 @@
 			<span class="clone-label"><strong class="@project.vcs">@project.vcs</strong>@Messages("code.repoUrl")</span><!-- 
 			 --><input id="repositoryURL" type="text" class="text repo-url" readonly="readonly" value="@CodeApp.getURL(project.owner, project.name)"><!-- 
              --><a id="copyURL" href="#!/copy-url" class="copy-btn btn">COPY TO CLIPBOARD</a>
-             
+
             <div id="branches" class="btn-group branches" data-name="branch" data-activate="manual">
 				<button data-toggle="dropdown" class="btn dropdown-toggle bgwhite d-label"></button>
 				<button data-toggle="dropdown" class="btn dropdown-toggle bgwhite"><span class="caret"></span></button>
@@ -50,17 +50,6 @@
 					}
 				</ul>
 			</div> 
-			<!-- 
-			<div class="btn-group branches">
-				<button data-toggle="dropdown" class="btn dropdown-toggle d-label bgwhite" id="selected-branch" data-branch="HEAD"></button>
-				<button data-toggle="dropdown" class="btn dropdown-toggle bgwhite"><span class="caret"></span></button>
-				<ul class="dropdown-menu">
-					@for(name <- branches){
-					<li><a href="#" class="branch-item" data-branch="@name">@name</a></li>
-					}
-				</ul>
-			</div>
-			 -->
 		</div>
 		<hr class="double-sp" />
 		<div class="code-browse-wrap">
@@ -71,7 +60,7 @@
 				</div>
 				<div id="folderList" class="directories"></div>
 			</div>
-			<div class="btnResize cell"></div>
+			<div class="btnResize cell">|||</div>
 			<div class="sp-wrap cell"></div>
 			
 			@** 파일 목록 **@
app/views/issue/issueList.scala.html
--- app/views/issue/issueList.scala.html
+++ app/views/issue/issueList.scala.html
@@ -89,7 +89,7 @@
 							<div class="control-group">
 								<label class="control-label">@Messages("milestone")</label>
 								<div class="controls">
-									<div class="btn-group" data-name="milestone">
+									<div class="btn-group" data-name="milestoneId">
 										<button data-toggle="dropdown" class="btn dropdown-toggle bgwhite d-label input-medium">@Messages("milestone.state.all")</button>
 										<button data-toggle="dropdown" class="btn dropdown-toggle bgwhite"><span class="caret"></span></button>
 										<ul class="dropdown-menu">
app/views/user/edit.scala.html
--- app/views/user/edit.scala.html
+++ app/views/user/edit.scala.html
@@ -34,7 +34,7 @@
                     @helper.inputText(userForm("email"),'_showConstraints -> false,'_label -> null, 'class -> "span2")</p>
                 </div>
                 
-                <input class="btn" type="submit" value="저장"/>
+                <input class="btn" type="submit" value="@Messages("button.save")"/>
     }
                 <hr/>
                 <div class="user-other-info info-box">
@@ -79,11 +79,34 @@
                     </li>
                 }
                 </ul>
+                <div>
+                <form name="passwordReset" action="@routes.UserApp.resetUserPassword()" method="post">
+                <input type="hidden" name="loginId" value="@user.loginId" />
+                <hr />
+                <dl>
+                <dt>@Messages("user.password")</dt>
+                <dd><input type="password" name="oldPassword" value="" autocomplete="off" /></dd>
+                <dt>@Messages("user.newPassword")</dt>
+                <dd><input type="password" id="password" name="password" value="" autocomplete="off" /></dd>
+                <dt>@Messages("validation.retypePassword")</dt>
+                <dd><input type="password" id="retypedPassword" name="retypedPassword" value="" autocomplete="off" /></dd>
+                </dl>
+                <input class="btn" type="submit" value="@Messages("button.save")" />
+                </form>
+                <hr />
+                </div>
             </div>
         </section>
     </div>
 </div>
 
+	<script type="text/javascript" src="@getJSLink("lib/validate")"></script>
+	<script type="text/javascript">
+	$(document).ready(function(){
+		$hive.loadModule("user.ResetPassword");
+	});
+	</script>
+    
     <script src="@getJSLink("lib/jquery/jquery.form")" type="text/javascript"></script>
     <script type="text/javascript">
     (function(){
conf/messages.en
--- conf/messages.en
+++ conf/messages.en
@@ -299,6 +299,7 @@
 user.loginId.duplicate = Already existing ID
 user.login.alert = Please, login.
 user.password = Password
+user.newPassword = New Password
 user.confirmPassword = Confirm Password
 user.confirmPassword.alert = Password doesn't match the confirmation
 user.wrongPassword.alert = Wrong password!
conf/messages.ko
--- conf/messages.ko
+++ conf/messages.ko
@@ -305,6 +305,7 @@
 user.loginId.duplicate = 이미 존재하는 아이디입니다.
 user.login.alert = 로그인이 필요합니다.
 user.password = 비밀번호
+user.newPassword = 신규 비밀번호
 user.confirmPassword = 비밀번호 확인
 user.confirmPassword.alert = 입력한 두 비밀번호가 서로 일치하지 않습니다
 user.wrongPassword.alert = 잘못된 비밀번호입니다!
conf/routes
--- conf/routes
+++ conf/routes
@@ -23,6 +23,7 @@
 POST    /users/signup                                   controllers.UserApp.newUser()
 GET     /user/editform                                  controllers.UserApp.editUserInfoForm()
 POST    /user/edit                                      controllers.UserApp.editUserInfo()
+POST	/user/resetPassword								controllers.UserApp.resetUserPassword()
 GET     /user/isExist/:loginId                          controllers.UserApp.isUserExist(loginId)
 GET     /user/isEmailExist/:email                       controllers.UserApp.isEmailExist(email)
 
docs/technical/javascript-naming-convention.md (Renamed from public/javascripts/template/NamingRule.md)
--- public/javascripts/template/NamingRule.md
+++ docs/technical/javascript-naming-convention.md
@@ -5,7 +5,7 @@
 	
 * 단어를 생략하거나 약어를 사용하지 않는다. 단, HTML,URL 등과 같은 범용적인 약어는 사용할 수 있다. 약어 사용시에는 모두 대문자로 작성한다.
 * 한글 발음을 로마자로 그대로 표기하지 않는다 
-* 특수 문자는 가급적 사용하지 않는다. 단, 상수의 이름에서 단어를 구분하기 위한 용도와 Private 지시자 표시를 위하여 언더스코어(_)를 사용한다
+* 특수 문자는 가급적 사용하지 않는다. 단, 상수의 이름에서 단어를 구분하기 위한 용도와 Private 지시자 표시를 위하여 언더스코어(`_`)를 사용한다
 * 2글자 이상 대문자를 연속해서 사용하지 않는다. 단, 상수 이름이나 약어는 예외로 한다
 * 상수 이름이나 약어 이름을 대문자를 사용한다
 * 이름을 통해 역할과 목적을 할 수 있도록 간결하고 명료하게 작성한다
@@ -68,16 +68,16 @@
 >        getElement();
 >        showLayer();
 
-* Private 메소드 인 경우 메소드 이름 앞에 언더스코어(_)를 사용한다
+* Private 메소드 인 경우 메소드 이름 앞에 언더스코어(`_`)를 사용한다
 * 카멜 표기법을 준수한다. 복합어 이름은 첫 번째 단어를 소문자로 작성하고, 두 번째 이상의 단어 첫 글자를 대문자로 작성하여 단어를 구분한다.
-* 함수 이름의 첫 글자로 연속된 2개의 언더스코어(__) 기호와 달러 기호($)는 사용하지 않는다.
+* 함수 이름의 첫 글자로 연속된 2개의 언더스코어(`__`) 기호와 달러 기호($)는 사용하지 않는다.
 * Getter, Setter 메소드는 반드시 'get + 멤버변수 이름', 'set + 멤버변수 이름' 형식으로 작성한다. 단, Getter 메소드의 반환값이 Boolean 인 경우 get 대신 is 조합을 사용한다
 
 >        getElement();
 >        isChecked();
 >        setOption();
 
-* 이벤트 핸들러 메소드는 _on + 이벤트명 으로 정의한다. 브라우저에서 제공하는 이벤트가 아니라 특정 모듈이나 함수에서 비동기 콜백 함수(이벤트) 모델을 사용하는 경우에도 동일하다.
+* 이벤트 핸들러 메소드는 `_on` + 이벤트명 으로 정의한다. 브라우저에서 제공하는 이벤트가 아니라 특정 모듈이나 함수에서 비동기 콜백 함수(이벤트) 모델을 사용하는 경우에도 동일하다.
 
 >        function _onLoadImage(){ 
 >            console.log("image loaded");
@@ -94,7 +94,7 @@
 ## 변수 이름
 
 * 변수 이름은 명사를 사용하여 작성한다
-* Private 프로퍼티일 경우 변수 이름 앞에 언더스코어(_)를 사용한다
+* Private 프로퍼티일 경우 변수 이름 앞에 언더스코어(`_`)를 사용한다
 * 헝가리안 표기법을 준수한다. 변수의 데이터 타입, 용도에 따라 접두사를 사용한다. jQuery 프레임워크에 의해 래핑(Wrapping) 된 변수는 접두사 w를 다른 접두사와 함께 사용한다
 
 >g: 전역변수
@@ -169,7 +169,7 @@
 * 연산자의 앞과 뒤에는 공백을 삽입한다. 단, 단항 연산자, 전위/후위 연산자는 예외로 한다.
 * 소괄호( ( ) )와 키워드(if, for, return)를 사용할 때 공백을 삽입하지 않는다
 * 시작 중괄호({)는 앞에 공백을 삽입하지 않는다
-* 함수 선언 직후에는 빈 줄을 삽입하지 않는다 (function _method(){ 바로 다음 줄)
+* 함수 선언 직후에는 빈 줄을 삽입하지 않는다 (`function _method(){` 바로 다음 줄)
 * 함수 선언 사이에는 빈 줄을 삽입한다 (함수와 함수 사이)
 * 변수를 선언한 경우에는 다음에 빈 줄을 삽입하지 않는다.
 * 명령문 간에는 빈 줄을 사용하지 않는다. 단, 소스 코드의 길이가 길어지는 경우 구분을 위해 삽입할 수 있다.
public/javascripts/common/hive.ui.Dropdown.js
--- public/javascripts/common/hive.ui.Dropdown.js
+++ public/javascripts/common/hive.ui.Dropdown.js
@@ -116,7 +116,7 @@
 			htVar.fOnChange = fOnChange;
 			return true;
 		}
-		
+
 		/**
 		 * 현재 선택된 값을 반환
 		 * @return {String}
@@ -124,14 +124,14 @@
 		function _getValue(){
 			return htVar.sValue;
 		}
-		
+
 		/**
 		 * 기본값 지정이 있으면 선택된 상태로 만들기
 		 */
 		function _selectDefault(){
 			return _selectItem("li[data-selected=true]");
 		}
-		
+
 		/**
 		 * 지정한 값을 data-value 로 가진 항목을 선택 상태로 만듬
 		 * @param {String} sValue
@@ -139,7 +139,7 @@
 		function _selectByValue(sValue){
 			return _selectItem("li[data-value='" + sValue + "']");
 		}
-		
+
 		/**
 		 * 지정한 항목을 선택 상태로 만듬
 		 * @param {String} sQuery 항목 선택 셀렉터 구문
@@ -153,12 +153,12 @@
 			var welTarget = $(waFind[0]);
 			_setItemSelected(welTarget);
 			_setFormValue(welTarget);
-			
+
 			return true;
 		}
 		
 		_init(htOptions);
-		
+
 		return {
 			"getValue": _getValue,
 			"onChange": _setOnChange,
public/javascripts/service/hive.board.List.js
--- public/javascripts/service/hive.board.List.js
+++ public/javascripts/service/hive.board.List.js
@@ -3,15 +3,15 @@
  *
  * Copyright NHN Corporation.
  * Released under the MIT license
- * 
+ *
  * http://hive.dev.naver.com/license
  */
 
 (function(ns){
-	
+
 	var oNS = $hive.createNamespace(ns);
 	oNS.container[oNS.name] = function(htOptions){
-		
+
 		var htElement = {};
     	var htOrderMap = {"asc": "desc", "desc": "asc"};
 
@@ -22,7 +22,7 @@
 		function _init(htOptions){
 			_initElement(htOptions || {});
 			_attachEvent();
-			
+
 			_initPagination(htOptions);
 		}
 
@@ -31,12 +31,12 @@
 		 */
 		function _initElement(htOptions){
 			htElement.welForm = $(htOptions.sOptionForm || "#option_form");
-			htElement.welInputKey = htElement.welForm.find("input[name=key]");
-			htElement.welInputOrder = htElement.welForm.find("input[name=order]");
+			htElement.welInputOrderBy = htElement.welForm.find("input[name=orderBy]");
+			htElement.welInputOrderDir = htElement.welForm.find("input[name=orderDir]");
 			htElement.welInputPageNum = htElement.welForm.find("input[name=pageNum]");
-			
+
 			htElement.welFilter = $(htOptions.sQueryFilter || "#order a");
-			htElement.welPages = $(htOptions.sQueryPages || "#pagination a"); 
+			htElement.welPages = $(htOptions.sQueryPages || "#pagination a");
 			htElement.welPagination = $(htOptions.elPagination || '#pagination');
 		}
 
@@ -52,15 +52,12 @@
          * onClick filter
          */
         function _onClickFilter(){
-            var sKey = $(this).attr("key");
+            var orderBy = $(this).attr("data-orderBy");
+            var orderDir = $(this).attr("data-orderDir");
 
-        	// Key
-            if (sKey !== htElement.welInputKey.val()) {
-            	htElement.welInputKey.val(sKey)
-            } else { // Order
-            	var sCurrentVal = htElement.welInputOrder.val();
-            	htElement.welInputOrder.val(htOrderMap[sCurrentVal]);
-            }
+            htElement.welInputOrderBy.val(orderBy);
+            htElement.welInputOrderDir.val(orderDir);
+
             htElement.welForm.submit();
             return false;
         }
@@ -73,58 +70,13 @@
         	htElement.welForm.submit();
             return false;
         }
-        
-        
+
+
         function _initPagination(htOptions){
         	hive.Pagination.update(htElement.welPagination, htOptions.nTotalPages);
         }
-        
+
         _init(htOptions);
 	};
-	
-})("hive.board.List");
 
-/*
-nforge.namespace("board");
-nforge.board.list = function() {
-    var that = {
-        "init" : function() {
-            that.setUpEventListener();
-        },
-
-        "setUpEventListener" : function() {
-            var $headers = $("#order a");
-            $headers.click(that.onHeader);
-            
-            var $pagination = $("#pagination a");
-            $pagination.click(that.onPager);
-        },
-
-        "onHeader" : function() {
-            var key = $(this).attr("key");
-            var $input = $("#option_form input[name=key]");
-            if (key !== $input.val()) {
-                $input.val(key)
-            } else {
-                $input = $("#option_form input[name=order]");
-                if ($input.val() === "desc"){
-                	$input.val("asc");
-                } else if ($input.val() === "asc") {
-                	$input.val("desc");
-                }
-            }
-            $("#option_form").submit();
-            return false;
-        },
-
-        "onPager" : function() {
-            var $input = $("#option_form input[name=pageNum]");
-            $input.val($(this).attr("pageNum"));
-            $("#option_form").submit();
-            return false;
-        }
-    };
-    
-    return that;
-};
-*/
(파일 끝에 줄바꿈 문자 없음)
+})("hive.board.List");
(파일 끝에 줄바꿈 문자 없음)
public/javascripts/service/hive.code.Browser.js
--- public/javascripts/service/hive.code.Browser.js
+++ public/javascripts/service/hive.code.Browser.js
@@ -16,7 +16,7 @@
 		var oBranch = new hive.ui.Dropdown({
 			"elContainer": $("#branches")
 		});
-		
+
 		$(document).ready(function(){
 			$(window).bind('hashchange', function(e){
 //				_updateDynaTree();
@@ -201,32 +201,37 @@
 		function getBranch(){
 			return encodeURIComponent(oBranch.getValue());
 		}
-		
+
 		/** resize list **/
 		function _initResizeList(){
 			var nFolderListX = $("#folderList").offset().left;
 			var welBtnResize = $(".btnResize");
 			var welWrapDirectory = $(".directory-wrap");
 			var waWrapFile = $(".file-wrap"); // fileList, fileView
-			
-			welBtnResize.mousedown(function(){
-				$(window).bind("mousemove", _resizeList);
-				return false;
-			});
-			welBtnResize.mouseup(function(){
+            var draggable = true;
+
+            welBtnResize.mousedown(function () {
+                if(draggable) {
+                	$(window).bind("mousemove", _resizeList);
+                }
+                return false;
+            });
+            $(".directory-wrap").mouseup(function(){
 				$(window).unbind("mousemove", _resizeList);
 				return false;
 			});
 			$(window).click(function(){ // for IE
 				$(window).unbind("mousemove", _resizeList);
 			});
-			
+
 			// 더블클릭하면 디렉토리 목록 숨김
 			welBtnResize.dblclick(function(){
 				if(welWrapDirectory.css("display") == "none"){
+                    draggable = true;
 					welWrapDirectory.show();
 					waWrapFile.width(850 - welWrapDirectory.width());
 				} else {
+                    draggable = false;
 					welWrapDirectory.hide();
 					waWrapFile.width(850);
 				}
@@ -235,6 +240,7 @@
 			function _resizeList(weEvt){
 				var nWidth = weEvt.clientX - nFolderListX;
 				$(".directory-wrap").width(nWidth);
+                $(".directories").width(nWidth);
 				$(".file-wrap").width(850 - nWidth);
 			}
 		}
 
public/javascripts/service/hive.user.ResetPassword.js (added)
+++ public/javascripts/service/hive.user.ResetPassword.js
@@ -0,0 +1,134 @@
+/**
+ * @(#)hive.user.Reset.js 2013.04.25
+ *
+ * Copyright NHN Corporation.
+ * Released under the MIT license
+ * 
+ * http://hive.dev.naver.com/license
+ */
+(function(ns){
+	
+	var oNS = $hive.createNamespace(ns);
+	oNS.container[oNS.name] = function(){
+		
+		var htVar = {};
+		var htElement = {};
+		
+		/**
+		 * initialize
+		 */
+		function _init(){
+			_initElement();
+			_initFormValidator();
+			_attachEvent();
+		}
+		
+		/**
+		 * initialize elements
+		 */
+		function _initElement(){
+			htElement.welInputOldPassword  = $('#oldPassword');
+			htElement.welInputPassword  = $('#password');
+			htElement.welInputRetypedPassword = $('#retypedPassword');
+
+			htElement.welForm = $("form[name=passwordReset]");
+		}
+		
+		/**
+		 * attach event
+		 */
+		function _attachEvent(){
+			htElement.welInputOldPassword.focusout(_onBlurInputPassword);
+			htElement.welInputPassword.focusout(_onBlurInputPassword);
+            htElement.welInputRetypedPassword.focusout(_onBlurInputPassword);
+		}
+		
+
+		/**
+		 * 비밀번호 확인 입력란 벗어날 때 이벤트 핸들러
+		 * 마지막 입력란이므로 전체 폼 유효성 검사
+		 */
+		function _onBlurInputPassword(){
+			htVar.oValidator._validateForm();
+		}
+
+		/**
+		 * initialize FormValidator
+		 * @require validate.js
+		 */
+		function _initFormValidator(){
+			var aRules = [
+			    {"name": 'oldPassword',		"rules": 'required'},        
+	  			{"name": 'password',		"rules": 'required|min_length[4]'},
+	  			{"name": 'retypedPassword', "rules": 'required|matches[password]'}
+	  		];
+
+			htVar.oValidator = new FormValidator('passwordReset', aRules, _onFormValidate);
+
+            // set error message
+            htVar.oValidator.setMessage('required',		 Messages("validation.required"));
+            htVar.oValidator.setMessage('min_length',	 Messages("validation.tooShortPassword"));
+            htVar.oValidator.setMessage('matches',		 Messages("validation.passwordMismatch"));
+		}
+
+		/**
+		 * on validate form
+		 * @param {Array} aErrors
+		 */
+		function _onFormValidate(aErrors){
+            _clearTooltips();
+			// to avoid bootstrap bug
+			if (aErrors.length <= 0) {
+				return _clearTooltips();
+			}
+
+			var welTarget;
+			aErrors.forEach(function(htError){
+				welTarget = htElement.welForm.find("input[name=" + htError.name + "]");
+				if(welTarget){
+					showErrorMessage(welTarget, htError.message);
+				}
+			});
+		}
+
+		/**
+		 * 폼 영역에 있는 jquery.tooltip 모두 제거하는 함수
+		 */
+		function _clearTooltips(){
+			try {
+				htElement.welForm.find("input").each(function(i, v){
+					$(v).tooltip("destroy");
+				});
+			} catch(e){}
+		}
+		
+		/**
+		 * Bootstrap toolTip function has some limitation.
+		 * In this case, toolTip doesn't provide easy way to change title and contents.
+		 * So, unfortunately I had to change data value in directly.
+		 * @param {Wrapped Element} welInput
+		 * @param {String} sMessage
+		 */
+		function showErrorMessage(welInput, sMessage){
+	        welInput.tooltip({"trigger": "manual", "placement": "right"});
+
+	        var oToolTip = welInput.data('tooltip');
+	        oToolTip.options.placement = 'right';
+	        oToolTip.options.trigger   = 'manual';
+	        oToolTip.options.title     = sMessage;
+	        oToolTip.options.content   = sMessage;
+
+	        welInput.tooltip('show');
+		}
+			
+		function hideErrorMessage(welInput){
+            welInput.tooltip("hide");
+            
+            try{
+                welInput.tooltip("destroy");
+            } catch(e){} // to avoid bootstrap bug			
+		}
+		
+		_init();
+	};
+})("hive.user.ResetPassword");(파일 끝에 줄바꿈 문자 없음)
public/javascripts/template/ModuleGuide.md
--- public/javascripts/template/ModuleGuide.md
+++ public/javascripts/template/ModuleGuide.md
@@ -4,7 +4,7 @@
 
 ----
 ## 명명 규칙
-	
+
 * NamingGuide.md 참조
 
 ## 폴더 구조
@@ -19,7 +19,7 @@
 
 * __javascripts/service__
 
-    각 페이지에 사용되는 자바스크립트 모듈
+    각 페이지에 사용되는 자바스크립트 모듈.
     함수로 작성한다
 
 * __javascripts/deprecated__
@@ -50,24 +50,23 @@
 * 현재 페이지 내에서 해당 모듈을 찾을 수 없는 경우, 즉 명시적으로 <script> 태그를 통해 포함하지 않은 경우
     자동으로 javascripts/service 에서 동적으로 자바스크립트 파일을 로딩하려 시도한다.
     이미 페이지 내에 <script> 태그를 이용해 포함한 경우에는 동적 로딩은 시도되지 않는다.
-    동적 로딩을 시도하는 파일 경로는 javascripts/service/hive.(module.Name).js 이다. 
+    동적 로딩을 시도하는 파일 경로는 javascripts/service/hive.(module.Name).js 이다.
 * 자바스크립트 파일 로딩이 완료되어 모듈 코드를 사용할 수 있을 때 자동으로 초기화를 시도하며 내부적으로 수행되는 코드는 아래와 같다.
     이 중 htOption 변수는 $hive.loadModule() 의 두번째 인자와 동일하다
 
 >        new hive.module.Name(htOption)
 
-* $hive.loadModule()는 모듈 함수를 실행하는 역할만 한다. 별도의 인터페이스가 필요한 것은 아니기 때문에 
+* $hive.loadModule()는 모듈 함수를 실행하는 역할만 한다. 별도의 인터페이스가 필요한 것은 아니기 때문에
     모듈 내부의 함수 구조가 모듈 로딩 자체에 영향을 주지는 않는다.
 
 ## 기타
 
-* HTML 템플릿은 최대한 자바스크립트 파일 내에 포함하지 않는다
-* HTML 템플릿 데이터는 정적 페이지내에 <script type="text/template"> 형태로 위치시키고 자바스크립트는 그 내용을 활용하는 형태로 작성한다  
+* HTML 템플릿은 가능한 한 자바스크립트 파일 내에 포함하지 않는다
+* HTML 템플릿 데이터는 정적 페이지내에 <script type="text/template"> 형태로 위치시키고 자바스크립트는 그 내용을 활용하는 형태로 작성한다
 
 >    <script type="text/template" id="tplItem">
 >        ${name} ${email}
 >    </script>
 >
 >    var sTpl = document.getElementById("tplItem").text;
-
 
public/javascripts/template/hive.CodeTemplate.js
--- public/javascripts/template/hive.CodeTemplate.js
+++ public/javascripts/template/hive.CodeTemplate.js
@@ -65,7 +65,11 @@
         	// detachEvent() if available
         	
         	// free memory
+<<<<<<< HEAD
         	htVar = htElement = null;
+=======
+		htVar = htElement = null;
+>>>>>>> 3283b2ee5c73643ae8db9a489d502b3ed40d2cca
         }
         
         _init();
Add a comment
List