chore: Remove obsolete .classpath file
- Deleted the .classpath file as it is no longer needed in the project structure. - This cleanup helps maintain a tidy codebase by removing unnecessary files that may cause confusion or clutter.
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<classpath>
|
|
||||||
<classpathentry kind="output" path="WebContent/WEB-INF/classes"/>
|
|
||||||
</classpath>
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
---
|
|
||||||
description:
|
|
||||||
globs:
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
|
|
||||||
# 아키텍처 가이드
|
|
||||||
|
|
||||||
## 전체 아키텍처
|
|
||||||
|
|
||||||
이 애플리케이션은 전형적인 Spring MVC 3-tier 아키텍처를 따릅니다:
|
|
||||||
|
|
||||||
- **Presentation Layer**: JSP + jQuery (Frontend)
|
|
||||||
- **Business Layer**: Spring Controllers + Services (Backend Logic)
|
|
||||||
- **Data Access Layer**: MyBatis + PostgreSQL (Database)
|
|
||||||
|
|
||||||
## 패키지 구조
|
|
||||||
|
|
||||||
```
|
|
||||||
src/com/pms/
|
|
||||||
├── controller/ # Spring MVC Controllers (@Controller)
|
|
||||||
├── service/ # Business Logic (@Service)
|
|
||||||
├── mapper/ # MyBatis XML Mappers
|
|
||||||
├── common/ # 공통 유틸리티 및 설정
|
|
||||||
├── salesmgmt/ # 영업관리 모듈
|
|
||||||
└── ions/ # 특수 모듈
|
|
||||||
```
|
|
||||||
|
|
||||||
## 주요 컴포넌트
|
|
||||||
|
|
||||||
### Controllers
|
|
||||||
|
|
||||||
모든 컨트롤러는 [BaseService](mdc:src/com/pms/common/service/BaseService.java)를 상속받습니다.
|
|
||||||
|
|
||||||
- URL 패턴: `*.do` (예: `/admin/menuMngList.do`)
|
|
||||||
- 주요 컨트롤러: [AdminController](mdc:src/com/pms/controller/AdminController.java)
|
|
||||||
|
|
||||||
### Services
|
|
||||||
|
|
||||||
비즈니스 로직을 처리하는 서비스 계층입니다.
|
|
||||||
|
|
||||||
- 예시: [AdminService](mdc:src/com/pms/service/AdminService.java)
|
|
||||||
- MyBatis SqlSession을 직접 사용하여 데이터베이스 접근
|
|
||||||
|
|
||||||
### MyBatis Mappers
|
|
||||||
|
|
||||||
SQL 쿼리를 XML로 정의합니다.
|
|
||||||
|
|
||||||
- 위치: `src/com/pms/mapper/`
|
|
||||||
- 예시: [admin.xml](mdc:src/com/pms/mapper/admin.xml)
|
|
||||||
|
|
||||||
### JSP Views
|
|
||||||
|
|
||||||
JSP 뷰 파일들은 `WebContent/WEB-INF/view/` 디렉토리에 위치합니다.
|
|
||||||
|
|
||||||
- InternalResourceViewResolver 사용
|
|
||||||
- prefix: `/WEB-INF/view`, suffix: `.jsp`
|
|
||||||
|
|
||||||
## 데이터베이스 설정
|
|
||||||
|
|
||||||
- JNDI DataSource 사용: `plm`
|
|
||||||
- PostgreSQL 연결
|
|
||||||
- 초기 데이터: [ilshin.pgsql](mdc:db/ilshin.pgsql)
|
|
||||||
|
|
||||||
## 설정 파일 위치
|
|
||||||
|
|
||||||
- Spring 설정: [dispatcher-servlet.xml](mdc:WebContent/WEB-INF/dispatcher-servlet.xml)
|
|
||||||
- 로깅 설정: [log4j.xml](mdc:WebContent/WEB-INF/log4j.xml)
|
|
||||||
- 웹 설정: [web.xml](mdc:WebContent/WEB-INF/web.xml)
|
|
||||||
|
|
@ -1,207 +1,145 @@
|
||||||
---
|
---
|
||||||
description:
|
description:
|
||||||
globs:
|
globs:
|
||||||
alwaysApply: true
|
alwaysApply: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 데이터베이스 가이드
|
# 데이터베이스 가이드
|
||||||
|
|
||||||
## 데이터베이스 설정
|
## 데이터베이스 설정
|
||||||
|
|
||||||
### PostgreSQL 연결
|
### PostgreSQL 연결
|
||||||
- **JNDI 리소스명**: `plm`
|
- **드라이버**: `pg` (node-postgres)
|
||||||
- **드라이버**: `org.postgresql.Driver`
|
- **설정 파일**: `backend-node/src/config/database.ts`
|
||||||
- **설정 파일**: [context.xml](mdc:tomcat-conf/context.xml)
|
- **환경 변수**: `backend-node/.env` (DATABASE_URL)
|
||||||
|
|
||||||
### 초기 데이터
|
### 연결 풀 사용
|
||||||
- **스키마 파일**: [ilshin.pgsql](mdc:db/ilshin.pgsql)
|
```typescript
|
||||||
- **역할 설정**: [00-create-roles.sh](mdc:db/00-create-roles.sh)
|
import { Pool } from 'pg';
|
||||||
|
|
||||||
## MyBatis 설정
|
const pool = new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
max: 20,
|
||||||
|
idleTimeoutMillis: 30000,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### SqlSession 사용 패턴
|
## 쿼리 패턴
|
||||||
```java
|
|
||||||
public List<Map<String, Object>> getData(Map<String, Object> paramMap) {
|
### 기본 CRUD
|
||||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
```typescript
|
||||||
try {
|
// SELECT
|
||||||
return sqlSession.selectList("namespace.queryId", paramMap);
|
const result = await pool.query(
|
||||||
} finally {
|
'SELECT * FROM example_table WHERE company_code = $1 ORDER BY created_at DESC',
|
||||||
sqlSession.close(); // 반드시 리소스 해제
|
[companyCode]
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
// INSERT
|
||||||
|
const result = await pool.query(
|
||||||
|
'INSERT INTO example_table (company_code, name) VALUES ($1, $2) RETURNING *',
|
||||||
|
[companyCode, name]
|
||||||
|
);
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
const result = await pool.query(
|
||||||
|
'UPDATE example_table SET name = $1, updated_at = NOW() WHERE id = $2 AND company_code = $3 RETURNING *',
|
||||||
|
[name, id, companyCode]
|
||||||
|
);
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
const result = await pool.query(
|
||||||
|
'DELETE FROM example_table WHERE id = $1 AND company_code = $2 RETURNING id',
|
||||||
|
[id, companyCode]
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 트랜잭션 처리
|
### 트랜잭션 처리
|
||||||
```java
|
```typescript
|
||||||
public void saveData(Map<String, Object> paramMap) {
|
const client = await pool.connect();
|
||||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession(false); // autoCommit=false
|
try {
|
||||||
try {
|
await client.query('BEGIN');
|
||||||
sqlSession.insert("namespace.insertQuery", paramMap);
|
await client.query('INSERT INTO ...', [params]);
|
||||||
sqlSession.update("namespace.updateQuery", paramMap);
|
await client.query('UPDATE ...', [params]);
|
||||||
sqlSession.commit(); // 명시적 커밋
|
await client.query('COMMIT');
|
||||||
} catch (Exception e) {
|
} catch (e) {
|
||||||
sqlSession.rollback(); // 오류 시 롤백
|
await client.query('ROLLBACK');
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
sqlSession.close();
|
client.release();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 매퍼 XML 작성 가이드
|
### 동적 조건 처리
|
||||||
|
```typescript
|
||||||
|
const conditions: string[] = ['company_code = $1'];
|
||||||
|
const params: any[] = [companyCode];
|
||||||
|
let paramIndex = 2;
|
||||||
|
|
||||||
### 기본 구조
|
if (searchText) {
|
||||||
```xml
|
conditions.push(`name ILIKE $${paramIndex}`);
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
params.push(`%${searchText}%`);
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
paramIndex++;
|
||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
}
|
||||||
<mapper namespace="admin">
|
|
||||||
<!-- 쿼리 정의 -->
|
|
||||||
</mapper>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 파라미터 바인딩
|
if (status) {
|
||||||
```xml
|
conditions.push(`status = $${paramIndex}`);
|
||||||
<!-- 안전한 파라미터 바인딩 (권장) -->
|
params.push(status);
|
||||||
<select id="selectUser" parameterType="map" resultType="map">
|
paramIndex++;
|
||||||
SELECT * FROM users
|
}
|
||||||
WHERE user_id = #{userId}
|
|
||||||
AND status = #{status}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- 동적 조건 처리 -->
|
const query = `SELECT * FROM example_table WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC`;
|
||||||
<select id="selectUserList" parameterType="map" resultType="map">
|
const result = await pool.query(query, params);
|
||||||
SELECT * FROM users
|
|
||||||
WHERE 1=1
|
|
||||||
<if test="userName != null and userName != ''">
|
|
||||||
AND user_name LIKE '%' || #{userName} || '%'
|
|
||||||
</if>
|
|
||||||
<if test="deptCode != null and deptCode != ''">
|
|
||||||
AND dept_code = #{deptCode}
|
|
||||||
</if>
|
|
||||||
</select>
|
|
||||||
```
|
|
||||||
|
|
||||||
### PostgreSQL 특화 문법
|
|
||||||
```xml
|
|
||||||
<!-- 시퀀스 사용 -->
|
|
||||||
<insert id="insertData" parameterType="map">
|
|
||||||
INSERT INTO table_name (id, name, reg_date)
|
|
||||||
VALUES (nextval('seq_table'), #{name}, now())
|
|
||||||
</insert>
|
|
||||||
|
|
||||||
<!-- 숫자 타입 캐스팅 -->
|
|
||||||
<update id="updateData" parameterType="map">
|
|
||||||
UPDATE table_name
|
|
||||||
SET status = #{status}
|
|
||||||
WHERE id = #{id}::numeric
|
|
||||||
</update>
|
|
||||||
|
|
||||||
<!-- 재귀 쿼리 (메뉴 트리 구조) -->
|
|
||||||
<select id="selectMenuTree" resultType="map">
|
|
||||||
WITH RECURSIVE menu_tree AS (
|
|
||||||
SELECT * FROM menu_info WHERE parent_id = 0
|
|
||||||
UNION ALL
|
|
||||||
SELECT m.* FROM menu_info m
|
|
||||||
JOIN menu_tree mt ON m.parent_id = mt.id
|
|
||||||
)
|
|
||||||
SELECT * FROM menu_tree ORDER BY path
|
|
||||||
</select>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 주요 테이블 구조
|
## 주요 테이블 구조
|
||||||
|
|
||||||
### 메뉴 관리
|
### 메뉴 관리
|
||||||
- **MENU_INFO**: 메뉴 정보
|
- **menu_info**: 메뉴 정보
|
||||||
- **MENU_AUTH_GROUP**: 메뉴 권한 그룹
|
- **menu_auth_group**: 메뉴 권한 그룹
|
||||||
- **AUTH_GROUP**: 권한 그룹 정보
|
- **auth_group**: 권한 그룹 정보
|
||||||
|
|
||||||
### 사용자 관리
|
### 사용자 관리
|
||||||
- **USER_INFO**: 사용자 정보
|
- **user_info**: 사용자 정보
|
||||||
- **DEPT_INFO**: 부서 정보
|
- **dept_info**: 부서 정보
|
||||||
- **USER_AUTH**: 사용자 권한
|
- **user_auth**: 사용자 권한
|
||||||
|
|
||||||
### 코드 관리
|
### 코드 관리
|
||||||
- **CODE_INFO**: 공통 코드
|
- **code_info**: 공통 코드
|
||||||
- **CODE_CATEGORY**: 코드 카테고리
|
- **code_category**: 코드 카테고리
|
||||||
|
|
||||||
## 데이터베이스 개발 모범 사례
|
## 마이그레이션
|
||||||
|
|
||||||
### 1. 파라미터 검증
|
마이그레이션 파일은 `db/migrations/` 디렉토리에 순번으로 관리:
|
||||||
```xml
|
|
||||||
<select id="selectData" parameterType="map" resultType="map">
|
|
||||||
SELECT * FROM table_name
|
|
||||||
WHERE 1=1
|
|
||||||
<if test="id != null and id != ''">
|
|
||||||
AND id = #{id}::numeric
|
|
||||||
</if>
|
|
||||||
</select>
|
|
||||||
```
|
```
|
||||||
|
db/migrations/
|
||||||
### 2. 페이징 처리
|
├── 001_initial_schema.sql
|
||||||
```xml
|
├── 002_add_company_code.sql
|
||||||
<select id="selectListWithPaging" parameterType="map" resultType="map">
|
├── ...
|
||||||
SELECT * FROM (
|
└── 1021_create_numbering_audit_log.sql
|
||||||
SELECT *, ROW_NUMBER() OVER (ORDER BY reg_date DESC) as rnum
|
|
||||||
FROM table_name
|
|
||||||
WHERE 1=1
|
|
||||||
<!-- 검색 조건 -->
|
|
||||||
) t
|
|
||||||
WHERE rnum BETWEEN #{startRow}::numeric AND #{endRow}::numeric
|
|
||||||
</select>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 대소문자 처리
|
|
||||||
```xml
|
|
||||||
<!-- PostgreSQL은 대소문자를 구분하므로 주의 -->
|
|
||||||
<select id="selectData" resultType="map">
|
|
||||||
SELECT
|
|
||||||
user_id as "userId", -- 카멜케이스 변환
|
|
||||||
user_name as "userName",
|
|
||||||
UPPER(status) as "status"
|
|
||||||
FROM user_info
|
|
||||||
</select>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. NULL 처리
|
|
||||||
```xml
|
|
||||||
<select id="selectData" resultType="map">
|
|
||||||
SELECT
|
|
||||||
COALESCE(description, '') as description,
|
|
||||||
CASE WHEN status = 'Y' THEN '활성' ELSE '비활성' END as statusName
|
|
||||||
FROM table_name
|
|
||||||
</select>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 성능 최적화
|
## 성능 최적화
|
||||||
|
|
||||||
### 인덱스 활용
|
### 인덱스 활용
|
||||||
```sql
|
```sql
|
||||||
-- 자주 검색되는 컬럼에 인덱스 생성
|
CREATE INDEX idx_example_company_code ON example_table(company_code);
|
||||||
CREATE INDEX idx_user_dept ON user_info(dept_code);
|
CREATE INDEX idx_user_dept ON user_info(dept_code);
|
||||||
CREATE INDEX idx_menu_parent ON menu_info(parent_id);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 쿼리 최적화
|
### 페이징 처리
|
||||||
```xml
|
```typescript
|
||||||
<!-- EXISTS 사용으로 성능 개선 -->
|
const query = `
|
||||||
<select id="selectUserWithAuth" resultType="map">
|
SELECT * FROM example_table
|
||||||
SELECT u.* FROM user_info u
|
WHERE company_code = $1
|
||||||
WHERE EXISTS (
|
ORDER BY created_at DESC
|
||||||
SELECT 1 FROM user_auth ua
|
LIMIT $2 OFFSET $3
|
||||||
WHERE ua.user_id = u.user_id
|
`;
|
||||||
AND ua.auth_code = #{authCode}
|
const result = await pool.query(query, [companyCode, limit, offset]);
|
||||||
)
|
|
||||||
</select>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 배치 처리
|
### 슬로우 쿼리 확인
|
||||||
```xml
|
```sql
|
||||||
<!-- 대량 데이터 삽입 시 배치 사용 -->
|
SELECT query, mean_time, calls
|
||||||
<insert id="insertBatch" parameterType="list">
|
FROM pg_stat_statements
|
||||||
INSERT INTO table_name (col1, col2, col3)
|
ORDER BY mean_time DESC;
|
||||||
VALUES
|
```
|
||||||
<foreach collection="list" item="item" separator=",">
|
|
||||||
(#{item.col1}, #{item.col2}, #{item.col3})
|
|
||||||
</foreach>
|
|
||||||
</insert>
|
|
||||||
```
|
|
||||||
|
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
---
|
|
||||||
description:
|
|
||||||
globs:
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
# 개발 가이드
|
|
||||||
|
|
||||||
## 개발 환경 설정
|
|
||||||
|
|
||||||
### Docker 개발 환경
|
|
||||||
```bash
|
|
||||||
# 개발 환경 실행
|
|
||||||
docker-compose -f docker-compose.dev.yml up --build -d
|
|
||||||
|
|
||||||
# 운영 환경 실행
|
|
||||||
docker-compose -f docker-compose.prod.yml up --build -d
|
|
||||||
```
|
|
||||||
|
|
||||||
### 로컬 개발 환경
|
|
||||||
1. Java 7 JDK 설치
|
|
||||||
2. Eclipse IDE 설정
|
|
||||||
3. Tomcat 7.0 설정
|
|
||||||
4. PostgreSQL 데이터베이스 설정
|
|
||||||
|
|
||||||
## 코딩 컨벤션
|
|
||||||
|
|
||||||
### Controller 개발
|
|
||||||
```java
|
|
||||||
@Controller
|
|
||||||
public class ExampleController extends BaseService {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
ExampleService exampleService;
|
|
||||||
|
|
||||||
@RequestMapping("/example/list.do")
|
|
||||||
public String getList(HttpServletRequest request,
|
|
||||||
@RequestParam Map<String, Object> paramMap) {
|
|
||||||
// 비즈니스 로직은 Service에서 처리
|
|
||||||
List<Map<String, Object>> list = exampleService.getList(request, paramMap);
|
|
||||||
request.setAttribute("list", list);
|
|
||||||
return "/example/list";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service 개발
|
|
||||||
```java
|
|
||||||
@Service
|
|
||||||
public class ExampleService extends BaseService {
|
|
||||||
|
|
||||||
public List<Map<String, Object>> getList(HttpServletRequest request,
|
|
||||||
Map<String, Object> paramMap) {
|
|
||||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
|
||||||
try {
|
|
||||||
return sqlSession.selectList("example.selectList", paramMap);
|
|
||||||
} finally {
|
|
||||||
sqlSession.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### MyBatis Mapper 개발
|
|
||||||
```xml
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
||||||
<mapper namespace="example">
|
|
||||||
<select id="selectList" parameterType="map" resultType="map">
|
|
||||||
SELECT * FROM example_table
|
|
||||||
WHERE 1=1
|
|
||||||
<if test="searchText != null and searchText != ''">
|
|
||||||
AND name LIKE '%' || #{searchText} || '%'
|
|
||||||
</if>
|
|
||||||
</select>
|
|
||||||
</mapper>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 주요 유틸리티
|
|
||||||
|
|
||||||
### 공통 유틸리티
|
|
||||||
- [CommonUtils](mdc:src/com/pms/common/utils/CommonUtils.java) - 공통 유틸리티 메서드
|
|
||||||
- [Constants](mdc:src/com/pms/common/utils/Constants.java) - 상수 정의
|
|
||||||
- [Message](mdc:src/com/pms/common/Message.java) - 메시지 처리
|
|
||||||
|
|
||||||
### 파일 관련
|
|
||||||
- [FileRenameClass](mdc:src/com/pms/common/FileRenameClass.java) - 파일명 변경
|
|
||||||
- 파일 업로드/다운로드 처리
|
|
||||||
|
|
||||||
## 프론트엔드 개발
|
|
||||||
|
|
||||||
### JSP 개발
|
|
||||||
- 위치: `WebContent/WEB-INF/view/`
|
|
||||||
- 공통 초기화: [init_jqGrid.jsp](mdc:WebContent/init_jqGrid.jsp)
|
|
||||||
- 스타일시트: [all.css](mdc:WebContent/css/all.css)
|
|
||||||
|
|
||||||
### JavaScript 라이브러리
|
|
||||||
- jQuery 1.11.3/2.1.4
|
|
||||||
- jqGrid 4.7.1 - 데이터 그리드
|
|
||||||
- Tabulator - 테이블 컴포넌트
|
|
||||||
- rMateChart - 차트 라이브러리
|
|
||||||
|
|
||||||
## 데이터베이스 개발
|
|
||||||
|
|
||||||
### 연결 설정
|
|
||||||
- JNDI 리소스명: `plm`
|
|
||||||
- 드라이버: PostgreSQL
|
|
||||||
- 컨텍스트 설정: [context.xml](mdc:tomcat-conf/context.xml)
|
|
||||||
|
|
||||||
### 스키마 관리
|
|
||||||
- 초기 스키마: [ilshin.pgsql](mdc:db/ilshin.pgsql)
|
|
||||||
- 역할 설정: [00-create-roles.sh](mdc:db/00-create-roles.sh)
|
|
||||||
|
|
||||||
## 빌드 및 배포
|
|
||||||
- Eclipse 기반 빌드 (Maven/Gradle 미사용)
|
|
||||||
- 컴파일된 클래스: `WebContent/WEB-INF/classes/`
|
|
||||||
- 라이브러리: `WebContent/WEB-INF/lib/`
|
|
||||||
- WAR 파일로 Tomcat 배포
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
---
|
|
||||||
description:
|
|
||||||
globs:
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
# Next.js 마이그레이션 가이드
|
|
||||||
|
|
||||||
## 마이그레이션 개요
|
|
||||||
현재 JSP/jQuery 기반 프론트엔드를 Next.js로 전환하는 작업이 계획되어 있습니다.
|
|
||||||
자세한 내용은 [TODO.md](mdc:TODO.md)를 참조하세요.
|
|
||||||
|
|
||||||
## 현재 프론트엔드 분석
|
|
||||||
|
|
||||||
### JSP 뷰 구조
|
|
||||||
```
|
|
||||||
WebContent/WEB-INF/view/
|
|
||||||
├── admin/ # 관리자 화면
|
|
||||||
├── approval/ # 승인 관리
|
|
||||||
├── common/ # 공통 컴포넌트
|
|
||||||
├── dashboard/ # 대시보드
|
|
||||||
├── main/ # 메인 화면
|
|
||||||
└── ... # 기타 모듈별 화면
|
|
||||||
```
|
|
||||||
|
|
||||||
### 주요 JavaScript 라이브러리
|
|
||||||
- **jQuery**: 1.11.3/2.1.4 - DOM 조작 및 AJAX
|
|
||||||
- **jqGrid**: 4.7.1 - 데이터 그리드 (교체 필요)
|
|
||||||
- **Tabulator**: 테이블 컴포넌트
|
|
||||||
- **rMateChart**: 차트 라이브러리 (교체 필요)
|
|
||||||
- **CKEditor**: 텍스트 에디터
|
|
||||||
|
|
||||||
### CSS 프레임워크
|
|
||||||
- [all.css](mdc:WebContent/css/all.css) - 메인 스타일시트
|
|
||||||
- jQuery UI 테마 적용
|
|
||||||
- 반응형 디자인 미적용 (데스크톱 중심)
|
|
||||||
|
|
||||||
## API 설계 가이드
|
|
||||||
|
|
||||||
### RESTful API 변환
|
|
||||||
현재 Spring MVC는 JSP 뷰를 반환하는 구조입니다:
|
|
||||||
```java
|
|
||||||
@RequestMapping("/admin/menuMngList.do")
|
|
||||||
public String getMenuList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
|
||||||
// 데이터 조회
|
|
||||||
List<Map<String, Object>> menuList = adminService.getMenuList(request, paramMap);
|
|
||||||
request.setAttribute("menuList", menuList);
|
|
||||||
return "/admin/menu/menuMngList"; // JSP 뷰 반환
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Next.js 연동을 위해 JSON API로 변환 필요:
|
|
||||||
```java
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api")
|
|
||||||
public class AdminApiController {
|
|
||||||
|
|
||||||
@GetMapping("/admin/menus")
|
|
||||||
@ResponseBody
|
|
||||||
public ResponseEntity<ApiResponse<List<Map<String, Object>>>> getMenuList(
|
|
||||||
@RequestParam Map<String, Object> paramMap) {
|
|
||||||
List<Map<String, Object>> menuList = adminService.getMenuList(null, paramMap);
|
|
||||||
return ResponseEntity.ok(ApiResponse.success(menuList));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### API 응답 표준화
|
|
||||||
```java
|
|
||||||
public class ApiResponse<T> {
|
|
||||||
private boolean success;
|
|
||||||
private T data;
|
|
||||||
private String message;
|
|
||||||
private String errorCode;
|
|
||||||
|
|
||||||
public static <T> ApiResponse<T> success(T data) {
|
|
||||||
return new ApiResponse<>(true, data, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> ApiResponse<T> error(String message, String errorCode) {
|
|
||||||
return new ApiResponse<>(false, null, message, errorCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 컴포넌트 매핑 가이드
|
|
||||||
|
|
||||||
### 데이터 그리드 교체
|
|
||||||
**현재**: jqGrid 4.7.1
|
|
||||||
```javascript
|
|
||||||
$("#grid").jqGrid({
|
|
||||||
url: 'menuMngList.do',
|
|
||||||
datatype: 'json',
|
|
||||||
colModel: [
|
|
||||||
{name: 'menuName', label: '메뉴명'},
|
|
||||||
{name: 'url', label: 'URL'}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
**변환 후**: TanStack Table 또는 AG Grid
|
|
||||||
```tsx
|
|
||||||
import { useTable } from '@tanstack/react-table';
|
|
||||||
|
|
||||||
const MenuTable = () => {
|
|
||||||
const columns = [
|
|
||||||
{ accessorKey: 'menuName', header: '메뉴명' },
|
|
||||||
{ accessorKey: 'url', header: 'URL' }
|
|
||||||
];
|
|
||||||
|
|
||||||
const table = useTable({ data, columns });
|
|
||||||
// 테이블 렌더링
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### 차트 라이브러리 교체
|
|
||||||
**현재**: rMateChart
|
|
||||||
**변환 후**: Recharts 또는 Chart.js
|
|
||||||
|
|
||||||
### 폼 처리 교체
|
|
||||||
**현재**: jQuery 기반 폼 처리
|
|
||||||
**변환 후**: react-hook-form 사용
|
|
||||||
|
|
||||||
## 인증/인가 처리
|
|
||||||
|
|
||||||
### 현재 세션 기반 인증
|
|
||||||
```java
|
|
||||||
// 세션에서 사용자 정보 조회
|
|
||||||
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Next.js 연동 방안
|
|
||||||
1. **세션 유지**: 쿠키 기반 세션 ID 전달
|
|
||||||
2. **JWT 토큰**: 새로운 토큰 기반 인증 도입
|
|
||||||
3. **하이브리드**: 기존 세션 + API 토큰
|
|
||||||
|
|
||||||
## 개발 단계별 접근
|
|
||||||
|
|
||||||
### Phase 1: API 개발
|
|
||||||
1. 기존 Controller 분석
|
|
||||||
2. @RestController 신규 생성
|
|
||||||
3. 기존 Service 재사용
|
|
||||||
4. CORS 설정 추가
|
|
||||||
|
|
||||||
### Phase 2: Next.js 기본 구조
|
|
||||||
1. Next.js 프로젝트 생성
|
|
||||||
2. 기본 레이아웃 구현
|
|
||||||
3. 라우팅 구조 설계
|
|
||||||
4. 공통 컴포넌트 개발
|
|
||||||
|
|
||||||
### Phase 3: 화면별 마이그레이션
|
|
||||||
1. 관리자 화면부터 시작
|
|
||||||
2. 주요 업무 화면 순차 전환
|
|
||||||
3. 대시보드 및 리포트 화면
|
|
||||||
|
|
||||||
### Phase 4: 테스트 및 최적화
|
|
||||||
1. 기능 테스트
|
|
||||||
2. 성능 최적화
|
|
||||||
3. 사용자 테스트
|
|
||||||
4. 점진적 배포
|
|
||||||
|
|
||||||
## 주의사항
|
|
||||||
|
|
||||||
### 데이터 호환성
|
|
||||||
- 기존 데이터베이스 스키마 유지
|
|
||||||
- API 응답 형식 표준화
|
|
||||||
- 날짜/시간 형식 통일
|
|
||||||
|
|
||||||
### 사용자 경험
|
|
||||||
- 기존 업무 프로세스 유지
|
|
||||||
- 화면 전환 시 혼란 최소화
|
|
||||||
- 점진적 마이그레이션 고려
|
|
||||||
|
|
||||||
### 성능 고려사항
|
|
||||||
- API 응답 속도 최적화
|
|
||||||
- 클라이언트 사이드 캐싱
|
|
||||||
- 이미지 및 정적 자원 최적화
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
---
|
|
||||||
description:
|
|
||||||
globs:
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
# 네비게이션 가이드
|
|
||||||
|
|
||||||
## 프로젝트 구조 이해
|
|
||||||
|
|
||||||
### 루트 디렉토리
|
|
||||||
```
|
|
||||||
plm-ilshin/
|
|
||||||
├── src/ # Java 소스 코드
|
|
||||||
├── WebContent/ # 웹 리소스 (JSP, CSS, JS, 이미지)
|
|
||||||
├── db/ # 데이터베이스 스크립트
|
|
||||||
├── tomcat-conf/ # Tomcat 설정
|
|
||||||
├── docker-compose.*.yml # Docker 설정
|
|
||||||
└── 문서/ # 프로젝트 문서
|
|
||||||
```
|
|
||||||
|
|
||||||
### 주요 소스 디렉토리
|
|
||||||
```
|
|
||||||
src/com/pms/
|
|
||||||
├── controller/ # 웹 컨트롤러 (@Controller)
|
|
||||||
├── service/ # 비즈니스 로직 (@Service)
|
|
||||||
├── mapper/ # MyBatis SQL 매퍼 (XML)
|
|
||||||
├── common/ # 공통 컴포넌트
|
|
||||||
├── salesmgmt/ # 영업관리 모듈
|
|
||||||
└── ions/ # 특수 기능 모듈
|
|
||||||
```
|
|
||||||
|
|
||||||
### 웹 리소스 구조
|
|
||||||
```
|
|
||||||
WebContent/
|
|
||||||
├── WEB-INF/
|
|
||||||
│ ├── view/ # JSP 뷰 파일
|
|
||||||
│ ├── lib/ # JAR 라이브러리
|
|
||||||
│ ├── classes/ # 컴파일된 클래스
|
|
||||||
│ └── *.xml # 설정 파일
|
|
||||||
├── css/ # 스타일시트
|
|
||||||
├── js/ # JavaScript 파일
|
|
||||||
├── images/ # 이미지 리소스
|
|
||||||
└── template/ # 템플릿 파일
|
|
||||||
```
|
|
||||||
|
|
||||||
## 주요 파일 찾기
|
|
||||||
|
|
||||||
### 컨트롤러 찾기
|
|
||||||
특정 URL에 대한 컨트롤러를 찾을 때:
|
|
||||||
1. URL 패턴 확인 (예: `/admin/menuMngList.do`)
|
|
||||||
2. `src/com/pms/controller/` 에서 해당 `@RequestMapping` 검색
|
|
||||||
3. 주요 컨트롤러들:
|
|
||||||
- [AdminController.java](mdc:src/com/pms/controller/AdminController.java) - 관리자 기능
|
|
||||||
- [ApprovalController.java](mdc:src/com/pms/controller/ApprovalController.java) - 승인 관리
|
|
||||||
- [AsController.java](mdc:src/com/pms/controller/AsController.java) - AS 관리
|
|
||||||
|
|
||||||
### 서비스 찾기
|
|
||||||
비즈니스 로직을 찾을 때:
|
|
||||||
1. 컨트롤러에서 `@Autowired` 된 서비스 확인
|
|
||||||
2. `src/com/pms/service/` 디렉토리에서 해당 서비스 파일 찾기
|
|
||||||
3. 주요 서비스들:
|
|
||||||
- [AdminService.java](mdc:src/com/pms/service/AdminService.java) - 관리자 서비스
|
|
||||||
- [ApprovalService.java](mdc:src/com/pms/service/ApprovalService.java) - 승인 서비스
|
|
||||||
|
|
||||||
### SQL 쿼리 찾기
|
|
||||||
데이터베이스 쿼리를 찾을 때:
|
|
||||||
1. 서비스 코드에서 `sqlSession.selectList("namespace.queryId")` 확인
|
|
||||||
2. `src/com/pms/mapper/` 에서 해당 namespace XML 파일 찾기
|
|
||||||
3. XML 파일 내에서 queryId로 검색
|
|
||||||
|
|
||||||
### JSP 뷰 찾기
|
|
||||||
화면을 찾을 때:
|
|
||||||
1. 컨트롤러 메서드의 return 값 확인 (예: `"/admin/menu/menuMngList"`)
|
|
||||||
2. `WebContent/WEB-INF/view/` + return 값 + `.jsp` 경로로 파일 찾기
|
|
||||||
|
|
||||||
## 모듈별 주요 기능
|
|
||||||
|
|
||||||
### 관리자 모듈 (`/admin/*`)
|
|
||||||
- 메뉴 관리: [AdminController.java](mdc:src/com/pms/controller/AdminController.java)
|
|
||||||
- 사용자 관리, 권한 관리
|
|
||||||
- 코드 관리, 카테고리 관리
|
|
||||||
- 시스템 로그 관리
|
|
||||||
|
|
||||||
### 영업 관리 (`/salesmgmt/*`)
|
|
||||||
- 위치: `src/com/pms/salesmgmt/`
|
|
||||||
- 영업 관련 컨트롤러, 서비스, 매퍼 분리
|
|
||||||
|
|
||||||
### 공통 기능 (`/common/*`)
|
|
||||||
- 공통 유틸리티: [CommonUtils](mdc:src/com/pms/common/utils/CommonUtils.java)
|
|
||||||
- 메시지 처리: [Message](mdc:src/com/pms/common/Message.java)
|
|
||||||
- 파일 처리: [FileRenameClass](mdc:src/com/pms/common/FileRenameClass.java)
|
|
||||||
|
|
||||||
## 개발 시 주의사항
|
|
||||||
|
|
||||||
### 파일 수정 시
|
|
||||||
1. Java 파일 수정 → Eclipse에서 자동 컴파일 → `WebContent/WEB-INF/classes/`에 반영
|
|
||||||
2. JSP/CSS/JS 수정 → 바로 반영 (서버 재시작 불필요)
|
|
||||||
3. XML 설정 파일 수정 → 서버 재시작 필요
|
|
||||||
|
|
||||||
### 데이터베이스 관련
|
|
||||||
1. 스키마 변경 시 [ilshin.pgsql](mdc:db/ilshin.pgsql) 업데이트
|
|
||||||
2. 새로운 쿼리 추가 시 해당 mapper XML 파일에 추가
|
|
||||||
3. 트랜잭션 처리는 서비스 레벨에서 관리
|
|
||||||
|
|
@ -8,30 +8,69 @@ alwaysApply: true
|
||||||
|
|
||||||
## 프로젝트 정보
|
## 프로젝트 정보
|
||||||
|
|
||||||
이 프로젝트는 제품 수명 주기 관리(PLM - Product Lifecycle Management) 솔루션입니다.
|
이 프로젝트는 WACE ERP/PLM 솔루션입니다.
|
||||||
Spring Framework 기반의 Java 웹 애플리케이션으로, 제품 개발부터 폐기까지의 전체 생명주기를 관리합니다.
|
Node.js + Next.js 기반의 풀스택 웹 애플리케이션으로, 멀티테넌시를 지원합니다.
|
||||||
|
|
||||||
## 기술 스택
|
## 기술 스택
|
||||||
|
|
||||||
- **Backend**: Java 7, Spring Framework 3.2.4, MyBatis 3.2.3
|
- **Backend**: Node.js 20+, Express 4, TypeScript
|
||||||
- **Frontend**: JSP, jQuery 1.11.3/2.1.4, jqGrid 4.7.1
|
- **Frontend**: Next.js (App Router, Turbopack), React, shadcn/ui, Tailwind CSS
|
||||||
- **Database**: PostgreSQL
|
- **Database**: PostgreSQL (pg 드라이버 직접 사용)
|
||||||
- **WAS**: Apache Tomcat 7.0
|
- **인증**: JWT (jsonwebtoken)
|
||||||
- **Build**: Eclipse IDE 기반 (Maven/Gradle 미사용)
|
- **빌드**: npm, TypeScript (tsc)
|
||||||
|
- **개발도구**: nodemon (백엔드 핫리로드), Turbopack (프론트엔드)
|
||||||
|
|
||||||
|
## 프로젝트 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
ERP-node/
|
||||||
|
├── backend-node/ # Express + TypeScript 백엔드
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── app.ts # 엔트리포인트
|
||||||
|
│ │ ├── controllers/ # API 컨트롤러
|
||||||
|
│ │ ├── services/ # 비즈니스 로직
|
||||||
|
│ │ ├── middleware/ # 인증, 에러처리 미들웨어
|
||||||
|
│ │ ├── routes/ # 라우터
|
||||||
|
│ │ └── config/ # DB 연결 등 설정
|
||||||
|
│ └── package.json
|
||||||
|
├── frontend/ # Next.js 프론트엔드
|
||||||
|
│ ├── app/ # App Router 페이지
|
||||||
|
│ ├── components/ # React 컴포넌트 (shadcn/ui)
|
||||||
|
│ ├── lib/ # 유틸리티, API 클라이언트
|
||||||
|
│ ├── hooks/ # Custom React Hooks
|
||||||
|
│ └── package.json
|
||||||
|
├── db/ # 데이터베이스 마이그레이션 SQL
|
||||||
|
│ └── migrations/ # 순차 마이그레이션 파일
|
||||||
|
├── docker/ # Docker 설정 (dev/prod/deploy)
|
||||||
|
├── scripts/ # 개발/배포 스크립트
|
||||||
|
└── docs/ # 프로젝트 문서
|
||||||
|
```
|
||||||
|
|
||||||
## 주요 기능
|
## 주요 기능
|
||||||
|
|
||||||
- 제품 정보 관리
|
- 사용자 및 권한 관리 (멀티테넌시)
|
||||||
- BOM (Bill of Materials) 관리
|
- 메뉴 및 화면 관리
|
||||||
- 설계 변경 관리 (ECO/ECR)
|
- 플로우(워크플로우) 관리
|
||||||
- 문서 관리 및 버전 제어
|
- BOM 관리
|
||||||
- 프로젝트/일정 관리
|
- 문서/파일 관리
|
||||||
- 사용자 및 권한 관리
|
- 화면 디자이너 (동적 화면 생성)
|
||||||
- 워크플로우 관리
|
- 메일 연동
|
||||||
|
- 외부 DB 연결
|
||||||
|
|
||||||
|
## 개발 환경
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 백엔드 (nodemon으로 자동 재시작)
|
||||||
|
cd backend-node && npm run dev
|
||||||
|
|
||||||
|
# 프론트엔드 (Turbopack)
|
||||||
|
cd frontend && npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
## 주요 설정 파일
|
## 주요 설정 파일
|
||||||
|
|
||||||
- [web.xml](mdc:WebContent/WEB-INF/web.xml) - 웹 애플리케이션 배포 설정
|
- `backend-node/.env` - 백엔드 환경 변수 (DB, JWT 등)
|
||||||
- [dispatcher-servlet.xml](mdc:WebContent/WEB-INF/dispatcher-servlet.xml) - Spring MVC 설정
|
- `frontend/.env.local` - 프론트엔드 환경 변수
|
||||||
- [docker-compose.dev.yml](mdc:docker-compose.dev.yml) - 개발환경 Docker 설정
|
- `docker/` - Docker Compose 설정 (dev/prod)
|
||||||
- [docker-compose.prod.yml](mdc:docker-compose.prod.yml) - 운영환경 Docker 설정
|
- `Dockerfile` - 프로덕션 멀티스테이지 빌드
|
||||||
|
- `Jenkinsfile` - CI/CD 파이프라인
|
||||||
|
|
|
||||||
|
|
@ -1,248 +1,126 @@
|
||||||
---
|
---
|
||||||
description:
|
description:
|
||||||
globs:
|
globs:
|
||||||
alwaysApply: true
|
alwaysApply: true
|
||||||
---
|
---
|
||||||
|
|
||||||
# 보안 가이드
|
# 보안 가이드
|
||||||
|
|
||||||
## 인증 및 인가
|
## 인증 및 인가
|
||||||
|
|
||||||
### 세션 기반 인증
|
### JWT 기반 인증
|
||||||
현재 시스템은 세션 기반 인증을 사용합니다:
|
현재 시스템은 JWT 토큰 기반 인증을 사용합니다:
|
||||||
|
|
||||||
```java
|
```typescript
|
||||||
// 사용자 인증 정보 저장
|
// 토큰 생성 (로그인 시)
|
||||||
PersonBean person = new PersonBean();
|
const token = jwt.sign(
|
||||||
person.setUserId(userId);
|
{ userId, companyCode, role },
|
||||||
person.setUserName(userName);
|
process.env.JWT_SECRET,
|
||||||
request.getSession().setAttribute(Constants.PERSON_BEAN, person);
|
{ expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
|
||||||
|
);
|
||||||
|
|
||||||
// 인증 정보 조회
|
// 토큰 검증 (미들웨어)
|
||||||
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
if (person == null) {
|
req.user = decoded;
|
||||||
// 로그인 페이지로 리다이렉트
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 권한 관리 구조
|
### 권한 관리 구조
|
||||||
- **AUTH_GROUP**: 권한 그룹 정의
|
- **auth_group**: 권한 그룹 정의
|
||||||
- **MENU_AUTH_GROUP**: 메뉴별 권한 그룹 매핑
|
- **menu_auth_group**: 메뉴별 권한 그룹 매핑
|
||||||
- **USER_AUTH**: 사용자별 권한 할당
|
- **user_auth**: 사용자별 권한 할당
|
||||||
|
|
||||||
### 메뉴 접근 권한 체크
|
### 인증 미들웨어
|
||||||
```java
|
`backend-node/src/middleware/authMiddleware.ts` 참조
|
||||||
public HashMap<String, Object> checkUserMenuAuth(HttpServletRequest request, Map<String, Object> paramMap) {
|
|
||||||
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
|
|
||||||
String userId = person.getUserId();
|
|
||||||
|
|
||||||
paramMap.put("userId", userId);
|
|
||||||
paramMap.put("menuUrl", request.getRequestURI());
|
|
||||||
|
|
||||||
// 권한 체크 쿼리 실행
|
|
||||||
return sqlSession.selectOne("admin.checkUserMenuAuth", paramMap);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 데이터 보안
|
## 데이터 보안
|
||||||
|
|
||||||
### SQL 인젝션 방지
|
### SQL 인젝션 방지
|
||||||
**올바른 방법** - 파라미터 바인딩 사용:
|
**올바른 방법** - 파라미터 바인딩 사용:
|
||||||
```xml
|
```typescript
|
||||||
<select id="selectUser" parameterType="map" resultType="map">
|
const result = await pool.query(
|
||||||
SELECT * FROM user_info
|
'SELECT * FROM user_info WHERE user_id = $1',
|
||||||
WHERE user_id = #{userId} <!-- 안전한 파라미터 바인딩 -->
|
[userId]
|
||||||
</select>
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
**위험한 방법** - 직접 문자열 치환:
|
**위험한 방법** - 직접 문자열 삽입:
|
||||||
```xml
|
```typescript
|
||||||
<select id="selectUser" parameterType="map" resultType="map">
|
const result = await pool.query(
|
||||||
SELECT * FROM user_info
|
`SELECT * FROM user_info WHERE user_id = '${userId}'`
|
||||||
WHERE user_id = '${userId}' <!-- SQL 인젝션 위험 -->
|
);
|
||||||
</select>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 패스워드 보안
|
### 패스워드 보안
|
||||||
```java
|
```typescript
|
||||||
// 패스워드 암호화 (EncryptUtil 사용)
|
import bcrypt from 'bcryptjs';
|
||||||
String encryptedPassword = EncryptUtil.encrypt(plainPassword);
|
|
||||||
|
|
||||||
// 패스워드 검증
|
// 해싱
|
||||||
boolean isValid = EncryptUtil.matches(plainPassword, encryptedPassword);
|
const hashedPassword = await bcrypt.hash(plainPassword, 10);
|
||||||
|
|
||||||
|
// 검증
|
||||||
|
const isValid = await bcrypt.compare(plainPassword, hashedPassword);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 입력값 검증
|
### 입력값 검증
|
||||||
```java
|
```typescript
|
||||||
// CommonUtils를 사용한 입력값 정제
|
import Joi from 'joi';
|
||||||
String safeInput = CommonUtils.checkNull(request.getParameter("input"));
|
|
||||||
if (StringUtils.isEmpty(safeInput)) {
|
const schema = Joi.object({
|
||||||
throw new IllegalArgumentException("필수 입력값이 누락되었습니다.");
|
userId: Joi.string().required().max(50),
|
||||||
|
email: Joi.string().email().required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { error, value } = schema.validate(req.body);
|
||||||
|
if (error) {
|
||||||
|
return res.status(400).json({ success: false, message: error.message });
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 세션 보안
|
## 보안 미들웨어
|
||||||
|
|
||||||
### 세션 설정
|
### Helmet (보안 헤더)
|
||||||
[web.xml](mdc:WebContent/WEB-INF/web.xml)에서 세션 타임아웃 설정:
|
```typescript
|
||||||
```xml
|
import helmet from 'helmet';
|
||||||
<session-config>
|
app.use(helmet());
|
||||||
<session-timeout>1440</session-timeout> <!-- 24시간 -->
|
|
||||||
</session-config>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 세션 무효화
|
### Rate Limiting
|
||||||
```java
|
```typescript
|
||||||
// 로그아웃 시 세션 무효화
|
import rateLimit from 'express-rate-limit';
|
||||||
@RequestMapping("/logout.do")
|
const limiter = rateLimit({
|
||||||
public String logout(HttpServletRequest request) {
|
windowMs: 15 * 60 * 1000,
|
||||||
HttpSession session = request.getSession(false);
|
max: 100,
|
||||||
if (session != null) {
|
});
|
||||||
session.invalidate();
|
app.use('/api/', limiter);
|
||||||
}
|
|
||||||
return "redirect:/login.do";
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 파일 업로드 보안
|
### CORS 설정
|
||||||
|
```typescript
|
||||||
### 파일 확장자 검증
|
import cors from 'cors';
|
||||||
```java
|
app.use(cors({
|
||||||
public boolean isAllowedFileType(String fileName) {
|
origin: process.env.CORS_ORIGIN,
|
||||||
String[] allowedExtensions = {".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx", ".xls", ".xlsx"};
|
credentials: true,
|
||||||
String extension = fileName.toLowerCase().substring(fileName.lastIndexOf("."));
|
}));
|
||||||
return Arrays.asList(allowedExtensions).contains(extension);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 파일 크기 제한
|
|
||||||
```java
|
|
||||||
// web.xml에서 파일 업로드 크기 제한
|
|
||||||
<multipart-config>
|
|
||||||
<max-file-size>10485760</max-file-size> <!-- 10MB -->
|
|
||||||
<max-request-size>52428800</max-request-size> <!-- 50MB -->
|
|
||||||
</multipart-config>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 안전한 파일명 생성
|
|
||||||
```java
|
|
||||||
// FileRenameClass 사용하여 안전한 파일명 생성
|
|
||||||
String safeFileName = FileRenameClass.rename(originalFileName);
|
|
||||||
```
|
|
||||||
|
|
||||||
## XSS 방지
|
|
||||||
|
|
||||||
### 출력값 이스케이프
|
|
||||||
JSP에서 사용자 입력값 출력 시:
|
|
||||||
```jsp
|
|
||||||
<!-- 안전한 출력 -->
|
|
||||||
<c:out value="${userInput}" escapeXml="true"/>
|
|
||||||
|
|
||||||
<!-- 위험한 출력 -->
|
|
||||||
${userInput} <!-- XSS 공격 가능 -->
|
|
||||||
```
|
|
||||||
|
|
||||||
### JavaScript에서 데이터 처리
|
|
||||||
```javascript
|
|
||||||
// 안전한 방법
|
|
||||||
var safeData = $('<div>').text(userInput).html();
|
|
||||||
|
|
||||||
// 위험한 방법
|
|
||||||
var dangerousData = userInput; // XSS 공격 가능
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSRF 방지
|
|
||||||
|
|
||||||
### 토큰 기반 CSRF 방지
|
|
||||||
```jsp
|
|
||||||
<!-- 폼에 CSRF 토큰 포함 -->
|
|
||||||
<form method="post" action="save.do">
|
|
||||||
<input type="hidden" name="csrfToken" value="${csrfToken}"/>
|
|
||||||
<!-- 기타 입력 필드 -->
|
|
||||||
</form>
|
|
||||||
```
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 컨트롤러에서 CSRF 토큰 검증
|
|
||||||
@RequestMapping("/save.do")
|
|
||||||
public String save(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
|
||||||
String sessionToken = (String)request.getSession().getAttribute("csrfToken");
|
|
||||||
String requestToken = (String)paramMap.get("csrfToken");
|
|
||||||
|
|
||||||
if (!sessionToken.equals(requestToken)) {
|
|
||||||
throw new SecurityException("CSRF 토큰이 일치하지 않습니다.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 정상 처리
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 로깅 및 감사
|
|
||||||
|
|
||||||
### 보안 이벤트 로깅
|
|
||||||
```java
|
|
||||||
private static final Logger securityLogger = LoggerFactory.getLogger("SECURITY");
|
|
||||||
|
|
||||||
public void logSecurityEvent(String event, String userId, String details) {
|
|
||||||
securityLogger.info("Security Event: {} | User: {} | Details: {}", event, userId, details);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 사용 예시
|
|
||||||
logSecurityEvent("LOGIN_SUCCESS", userId, request.getRemoteAddr());
|
|
||||||
logSecurityEvent("ACCESS_DENIED", userId, request.getRequestURI());
|
|
||||||
```
|
|
||||||
|
|
||||||
### 민감 정보 마스킹
|
|
||||||
```java
|
|
||||||
public String maskSensitiveData(String data) {
|
|
||||||
if (data == null || data.length() < 4) {
|
|
||||||
return "****";
|
|
||||||
}
|
|
||||||
return data.substring(0, 2) + "****" + data.substring(data.length() - 2);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 환경별 보안 설정
|
|
||||||
|
|
||||||
### 개발 환경
|
|
||||||
- 상세한 오류 메시지 표시
|
|
||||||
- 디버그 모드 활성화
|
|
||||||
- 보안 제약 완화
|
|
||||||
|
|
||||||
### 운영 환경
|
|
||||||
- 일반적인 오류 메시지만 표시
|
|
||||||
- 디버그 모드 비활성화
|
|
||||||
- 엄격한 보안 정책 적용
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 환경별 설정 예시
|
|
||||||
if (Constants.IS_PRODUCTION) {
|
|
||||||
// 운영 환경 설정
|
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "접근이 거부되었습니다.");
|
|
||||||
} else {
|
|
||||||
// 개발 환경 설정
|
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "권한 부족: " + detailedMessage);
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 보안 체크리스트
|
## 보안 체크리스트
|
||||||
|
|
||||||
### 코드 레벨
|
### 코드 레벨
|
||||||
- [ ] SQL 인젝션 방지 (#{} 파라미터 바인딩 사용)
|
- [ ] SQL 인젝션 방지 ($1 파라미터 바인딩 사용)
|
||||||
- [ ] XSS 방지 (출력값 이스케이프)
|
- [ ] XSS 방지 (React 자동 이스케이프 활용)
|
||||||
- [ ] CSRF 방지 (토큰 검증)
|
- [ ] 입력값 검증 (Joi 스키마)
|
||||||
- [ ] 입력값 검증 및 정제
|
- [ ] 패스워드 bcrypt 해싱
|
||||||
- [ ] 패스워드 암호화 저장
|
- [ ] JWT 토큰 만료 설정
|
||||||
|
|
||||||
### 설정 레벨
|
### 설정 레벨
|
||||||
- [ ] 세션 타임아웃 설정
|
- [ ] Helmet 보안 헤더
|
||||||
- [ ] 파일 업로드 제한
|
- [ ] Rate Limiting 적용
|
||||||
- [ ] 오류 페이지 설정
|
- [ ] CORS 적절한 설정
|
||||||
- [ ] HTTPS 사용 (운영 환경)
|
- [ ] HTTPS 사용 (운영 환경)
|
||||||
- [ ] 보안 헤더 설정
|
- [ ] 환경 변수로 시크릿 관리
|
||||||
|
|
||||||
### 운영 레벨
|
### 운영 레벨
|
||||||
- [ ] 정기적인 보안 점검
|
- [ ] winston 로깅 모니터링
|
||||||
- [ ] 로그 모니터링
|
|
||||||
- [ ] 권한 정기 검토
|
- [ ] 권한 정기 검토
|
||||||
- [ ] 패스워드 정책 적용
|
- [ ] 패스워드 정책 적용
|
||||||
- [ ] 백업 데이터 암호화
|
- [ ] 백업 데이터 암호화
|
||||||
|
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
---
|
|
||||||
description:
|
|
||||||
globs:
|
|
||||||
alwaysApply: true
|
|
||||||
---
|
|
||||||
# 트러블슈팅 가이드
|
|
||||||
|
|
||||||
## 일반적인 문제 해결
|
|
||||||
|
|
||||||
### 애플리케이션 시작 오류
|
|
||||||
|
|
||||||
#### 데이터베이스 연결 실패
|
|
||||||
```
|
|
||||||
원인: JNDI DataSource 설정 문제
|
|
||||||
해결:
|
|
||||||
1. tomcat-conf/context.xml 확인
|
|
||||||
2. PostgreSQL 서비스 상태 확인
|
|
||||||
3. 데이터베이스 접속 정보 확인
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 클래스 로딩 오류
|
|
||||||
```
|
|
||||||
원인: 컴파일되지 않은 Java 파일
|
|
||||||
해결:
|
|
||||||
1. Eclipse에서 프로젝트 Clean & Build
|
|
||||||
2. WebContent/WEB-INF/classes/ 디렉토리 확인
|
|
||||||
3. 누락된 라이브러리 확인 (WebContent/WEB-INF/lib/)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 런타임 오류
|
|
||||||
|
|
||||||
#### 404 오류 (페이지를 찾을 수 없음)
|
|
||||||
```
|
|
||||||
원인: URL 매핑 문제
|
|
||||||
해결:
|
|
||||||
1. @RequestMapping 어노테이션 확인
|
|
||||||
2. JSP 파일 경로 확인 (/WEB-INF/view/)
|
|
||||||
3. web.xml의 servlet-mapping 확인 (*.do 패턴)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 500 오류 (서버 내부 오류)
|
|
||||||
```
|
|
||||||
원인: Java 코드 실행 오류
|
|
||||||
해결:
|
|
||||||
1. 로그 파일 확인 (log4j 설정)
|
|
||||||
2. MyBatis SQL 오류 확인
|
|
||||||
3. NullPointerException 체크
|
|
||||||
4. 데이터베이스 트랜잭션 오류 확인
|
|
||||||
```
|
|
||||||
|
|
||||||
### 데이터베이스 관련 문제
|
|
||||||
|
|
||||||
#### SQL 실행 오류
|
|
||||||
```
|
|
||||||
원인: MyBatis 매퍼 설정 문제
|
|
||||||
해결:
|
|
||||||
1. mapper XML 파일의 SQL 문법 확인
|
|
||||||
2. parameterType과 resultType 확인
|
|
||||||
3. 테이블/컬럼명 대소문자 확인 (PostgreSQL)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 트랜잭션 문제
|
|
||||||
```
|
|
||||||
원인: SqlSession 관리 문제
|
|
||||||
해결:
|
|
||||||
1. SqlSession.close() 호출 확인
|
|
||||||
2. try-finally 블록에서 리소스 정리
|
|
||||||
3. 트랜잭션 커밋/롤백 처리
|
|
||||||
```
|
|
||||||
|
|
||||||
### 프론트엔드 문제
|
|
||||||
|
|
||||||
#### JavaScript 오류
|
|
||||||
```
|
|
||||||
원인: jQuery/jqGrid 라이브러리 문제
|
|
||||||
해결:
|
|
||||||
1. 브라우저 개발자 도구 콘솔 확인
|
|
||||||
2. JavaScript 파일 로딩 순서 확인
|
|
||||||
3. jQuery 버전 호환성 확인
|
|
||||||
```
|
|
||||||
|
|
||||||
#### CSS 스타일 문제
|
|
||||||
```
|
|
||||||
원인: CSS 파일 로딩 또는 경로 문제
|
|
||||||
해결:
|
|
||||||
1. CSS 파일 경로 확인
|
|
||||||
2. 브라우저 캐시 클리어
|
|
||||||
3. all.css 파일 확인
|
|
||||||
```
|
|
||||||
|
|
||||||
## 개발 환경 문제
|
|
||||||
|
|
||||||
### Docker 환경
|
|
||||||
```bash
|
|
||||||
# 컨테이너 로그 확인
|
|
||||||
docker-compose logs app
|
|
||||||
|
|
||||||
# 컨테이너 내부 접속
|
|
||||||
docker-compose exec app bash
|
|
||||||
|
|
||||||
# 데이터베이스 접속 확인
|
|
||||||
docker-compose exec db psql -U postgres -d ilshin
|
|
||||||
```
|
|
||||||
|
|
||||||
### Eclipse 환경
|
|
||||||
```
|
|
||||||
문제: 프로젝트 인식 오류
|
|
||||||
해결:
|
|
||||||
1. .project 파일 확인
|
|
||||||
2. .classpath 파일 확인
|
|
||||||
3. Project Properties > Java Build Path 확인
|
|
||||||
4. Server Runtime 설정 확인 (Tomcat 7.0)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 로그 분석
|
|
||||||
|
|
||||||
### 주요 로그 위치
|
|
||||||
- 애플리케이션 로그: log4j 설정에 따름
|
|
||||||
- Tomcat 로그: `$CATALINA_HOME/logs/`
|
|
||||||
- Docker 로그: `docker-compose logs`
|
|
||||||
|
|
||||||
### 로그 설정 파일
|
|
||||||
- [log4j.xml](mdc:WebContent/WEB-INF/log4j.xml) - 로깅 설정
|
|
||||||
- 로그 레벨 조정으로 상세 정보 확인 가능
|
|
||||||
|
|
||||||
## 성능 문제
|
|
||||||
|
|
||||||
### 데이터베이스 성능
|
|
||||||
```sql
|
|
||||||
-- 슬로우 쿼리 확인
|
|
||||||
SELECT query, mean_time, calls
|
|
||||||
FROM pg_stat_statements
|
|
||||||
ORDER BY mean_time DESC;
|
|
||||||
|
|
||||||
-- 인덱스 사용률 확인
|
|
||||||
SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch
|
|
||||||
FROM pg_stat_user_indexes;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 메모리 문제
|
|
||||||
```
|
|
||||||
원인: 메모리 누수 또는 부족
|
|
||||||
해결:
|
|
||||||
1. JVM 힙 메모리 설정 확인
|
|
||||||
2. SqlSession 리소스 정리 확인
|
|
||||||
3. 대량 데이터 처리 시 페이징 적용
|
|
||||||
```
|
|
||||||
|
|
||||||
## 보안 관련
|
|
||||||
|
|
||||||
### 권한 문제
|
|
||||||
```
|
|
||||||
원인: 사용자 권한 설정 오류
|
|
||||||
해결:
|
|
||||||
1. MENU_AUTH_GROUP 테이블 확인
|
|
||||||
2. 사용자 세션 정보 확인
|
|
||||||
3. Spring Security 설정 확인 (있는 경우)
|
|
||||||
```
|
|
||||||
|
|
||||||
### SQL 인젝션 방지
|
|
||||||
```
|
|
||||||
주의사항:
|
|
||||||
1. MyBatis #{} 파라미터 바인딩 사용
|
|
||||||
2. ${} 직접 문자열 치환 지양
|
|
||||||
3. 사용자 입력값 검증
|
|
||||||
```
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
# PLM ILSHIN 개발환경 설정
|
|
||||||
|
|
||||||
# 애플리케이션 환경
|
|
||||||
NODE_ENV=development
|
|
||||||
|
|
||||||
# 데이터베이스 설정
|
|
||||||
DB_URL=jdbc:postgresql://39.117.244.52:11132/plm
|
|
||||||
DB_USERNAME=postgres
|
|
||||||
DB_PASSWORD=ph0909!!
|
|
||||||
|
|
||||||
# PostgreSQL 환경 변수 (내부 DB 사용 시)
|
|
||||||
POSTGRES_DB=plm
|
|
||||||
POSTGRES_USER=postgres
|
|
||||||
POSTGRES_PASSWORD=ph0909!!
|
|
||||||
|
|
||||||
# 애플리케이션 포트
|
|
||||||
APP_PORT=8090
|
|
||||||
|
|
||||||
# JVM 옵션
|
|
||||||
JAVA_OPTS="-Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m"
|
|
||||||
|
|
||||||
# 로그 레벨
|
|
||||||
LOG_LEVEL=DEBUG
|
|
||||||
|
|
||||||
# 개발 모드 플래그
|
|
||||||
DEBUG=true
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
# Claude Code (로컬 전용 - Git 제외)
|
# Claude Code
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# Package manager logs
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
@ -13,138 +17,125 @@ pids
|
||||||
*.seed
|
*.seed
|
||||||
*.pid.lock
|
*.pid.lock
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
# Coverage
|
||||||
coverage/
|
coverage/
|
||||||
*.lcov
|
*.lcov
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
lib-cov
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# TypeScript cache
|
# TypeScript cache
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
# Optional npm cache directory
|
# Build outputs
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Cache
|
||||||
.npm
|
.npm
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
.cache/
|
||||||
# Microbundle cache
|
.parcel-cache
|
||||||
.rpt2_cache/
|
.rpt2_cache/
|
||||||
.rts2_cache_cjs/
|
.rts2_cache_cjs/
|
||||||
.rts2_cache_es/
|
.rts2_cache_es/
|
||||||
.rts2_cache_umd/
|
.rts2_cache_umd/
|
||||||
|
*.cache
|
||||||
|
cache/
|
||||||
|
|
||||||
# Optional REPL history
|
# Next.js
|
||||||
|
.next
|
||||||
|
|
||||||
|
# Storybook
|
||||||
|
.out
|
||||||
|
.storybook-out
|
||||||
|
|
||||||
|
# REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
# Output of 'npm pack'
|
# Package archives
|
||||||
*.tgz
|
*.tgz
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
.yarn-integrity
|
||||||
|
|
||||||
# dotenv environment variables file
|
# Temporary
|
||||||
|
tmp/
|
||||||
|
temp/
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
log/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# ===== 환경 변수 및 민감 정보 =====
|
||||||
|
|
||||||
|
# 환경 변수
|
||||||
.env
|
.env
|
||||||
.env.test
|
.env.test
|
||||||
.env.local
|
.env.local
|
||||||
.env.production
|
.env.production
|
||||||
|
.env.docker
|
||||||
|
backend-node/.env
|
||||||
|
backend-node/.env.local
|
||||||
|
backend-node/.env.development
|
||||||
|
backend-node/.env.production
|
||||||
|
backend-node/.env.test
|
||||||
|
frontend/.env
|
||||||
|
frontend/.env.local
|
||||||
|
frontend/.env.development
|
||||||
|
frontend/.env.production
|
||||||
|
frontend/.env.test
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
# Docker
|
||||||
.cache
|
docker-compose.override.yml
|
||||||
.parcel-cache
|
docker-compose.prod.yml
|
||||||
|
|
||||||
# Next.js build output
|
# 설정 파일
|
||||||
.next
|
configs/
|
||||||
|
settings/
|
||||||
|
|
||||||
# Nuxt.js build / generate output
|
# 키/인증서
|
||||||
.nuxt
|
*.key
|
||||||
dist
|
*.pem
|
||||||
|
*.p12
|
||||||
|
*.pfx
|
||||||
|
*.crt
|
||||||
|
*.cert
|
||||||
|
secrets/
|
||||||
|
secrets.json
|
||||||
|
secrets.yaml
|
||||||
|
secrets.yml
|
||||||
|
api-keys.json
|
||||||
|
tokens.json
|
||||||
|
|
||||||
# Build cache
|
# 데이터베이스 덤프
|
||||||
.cache/
|
*.sql
|
||||||
|
*.dump
|
||||||
|
db/dump/
|
||||||
|
db/backup/
|
||||||
|
|
||||||
# Storybook build outputs
|
# 백업
|
||||||
.out
|
*.bak
|
||||||
.storybook-out
|
*.backup
|
||||||
|
*.old
|
||||||
|
backup/
|
||||||
|
|
||||||
# Temporary folders
|
# ===== IDE =====
|
||||||
tmp/
|
|
||||||
temp/
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# Bower dependency directory (https://bower.io/)
|
|
||||||
bower_components
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
|
|
||||||
# IDE files
|
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
|
*.user
|
||||||
|
|
||||||
# OS generated files
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.DS_Store?
|
.DS_Store?
|
||||||
._*
|
._*
|
||||||
|
|
@ -153,127 +144,7 @@ jspm_packages/
|
||||||
ehthumbs.db
|
ehthumbs.db
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# Build outputs
|
# ===== 업로드/미디어 =====
|
||||||
dist/
|
|
||||||
build/
|
|
||||||
|
|
||||||
# Test coverage
|
|
||||||
coverage/
|
|
||||||
|
|
||||||
# ===== 민감한 정보 보호 =====
|
|
||||||
|
|
||||||
# 데이터베이스 연결 정보
|
|
||||||
backend-node/.env
|
|
||||||
backend-node/.env.local
|
|
||||||
backend-node/.env.development
|
|
||||||
backend-node/.env.production
|
|
||||||
backend-node/.env.test
|
|
||||||
|
|
||||||
# 백엔드 환경 변수
|
|
||||||
backend/.env
|
|
||||||
backend/.env.local
|
|
||||||
backend/.env.development
|
|
||||||
backend/.env.production
|
|
||||||
backend/.env.test
|
|
||||||
|
|
||||||
# 프론트엔드 환경 변수
|
|
||||||
frontend/.env
|
|
||||||
frontend/.env.local
|
|
||||||
frontend/.env.development
|
|
||||||
frontend/.env.production
|
|
||||||
frontend/.env.test
|
|
||||||
|
|
||||||
# Docker 관련 민감 정보
|
|
||||||
docker-compose.override.yml
|
|
||||||
docker-compose.prod.yml
|
|
||||||
.env.docker
|
|
||||||
|
|
||||||
# 설정 파일들
|
|
||||||
configs/
|
|
||||||
settings/
|
|
||||||
*.config.js
|
|
||||||
*.config.ts
|
|
||||||
*.config.json
|
|
||||||
|
|
||||||
# 로그 파일들
|
|
||||||
*.log
|
|
||||||
logs/
|
|
||||||
log/
|
|
||||||
|
|
||||||
# 임시 파일들
|
|
||||||
*.tmp
|
|
||||||
*.temp
|
|
||||||
temp/
|
|
||||||
tmp/
|
|
||||||
|
|
||||||
# 백업 파일들
|
|
||||||
*.bak
|
|
||||||
*.backup
|
|
||||||
*.old
|
|
||||||
backup/
|
|
||||||
|
|
||||||
# 키 파일들
|
|
||||||
*.key
|
|
||||||
*.pem
|
|
||||||
*.p12
|
|
||||||
*.pfx
|
|
||||||
*.crt
|
|
||||||
*.cert
|
|
||||||
|
|
||||||
# API 키 및 토큰
|
|
||||||
secrets/
|
|
||||||
secrets.json
|
|
||||||
secrets.yaml
|
|
||||||
secrets.yml
|
|
||||||
api-keys.json
|
|
||||||
tokens.json
|
|
||||||
|
|
||||||
# 데이터베이스 덤프 파일
|
|
||||||
*.sql
|
|
||||||
*.dump
|
|
||||||
*.backup
|
|
||||||
db/dump/
|
|
||||||
db/backup/
|
|
||||||
|
|
||||||
# 캐시 파일들
|
|
||||||
.cache/
|
|
||||||
cache/
|
|
||||||
*.cache
|
|
||||||
|
|
||||||
# 사용자별 설정
|
|
||||||
.vscode/settings.json
|
|
||||||
.idea/workspace.xml
|
|
||||||
*.user
|
|
||||||
|
|
||||||
# ===== Gradle 관련 파일들 (레거시 Java 프로젝트) =====
|
|
||||||
# Gradle 캐시 및 빌드 파일들
|
|
||||||
.gradle/
|
|
||||||
*/.gradle/
|
|
||||||
gradle/
|
|
||||||
gradlew
|
|
||||||
gradlew.bat
|
|
||||||
gradle.properties
|
|
||||||
build/
|
|
||||||
*/build/
|
|
||||||
|
|
||||||
# Gradle Wrapper
|
|
||||||
gradle-wrapper.jar
|
|
||||||
gradle-wrapper.properties
|
|
||||||
|
|
||||||
# IntelliJ IDEA 관련 (Gradle 프로젝트)
|
|
||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
*.ipr
|
|
||||||
*.iws
|
|
||||||
out/
|
|
||||||
|
|
||||||
# Eclipse 관련 (Gradle 프로젝트)
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
.settings/
|
|
||||||
bin/
|
|
||||||
|
|
||||||
# 업로드된 파일들 제외
|
|
||||||
backend-node/uploads/
|
backend-node/uploads/
|
||||||
uploads/
|
uploads/
|
||||||
*.jpg
|
*.jpg
|
||||||
|
|
@ -289,6 +160,7 @@ uploads/
|
||||||
*.hwp
|
*.hwp
|
||||||
*.hwpx
|
*.hwpx
|
||||||
|
|
||||||
|
# ===== 기타 =====
|
||||||
claude.md
|
claude.md
|
||||||
|
|
||||||
# AI 에이전트 테스트 산출물
|
# AI 에이전트 테스트 산출물
|
||||||
|
|
@ -296,6 +168,6 @@ claude.md
|
||||||
*-screenshots/
|
*-screenshots/
|
||||||
*-test.mjs
|
*-test.mjs
|
||||||
|
|
||||||
# 개인 작업 문서 (popdocs)
|
# 개인 작업 문서
|
||||||
popdocs/
|
popdocs/
|
||||||
.cursor/rules/popdocs-safety.mdc
|
.cursor/rules/popdocs-safety.mdc
|
||||||
|
|
|
||||||
47
.project
|
|
@ -1,47 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<projectDescription>
|
|
||||||
<name>ilshin</name>
|
|
||||||
<comment></comment>
|
|
||||||
<projects>
|
|
||||||
</projects>
|
|
||||||
<buildSpec>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
<buildCommand>
|
|
||||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
|
||||||
<arguments>
|
|
||||||
</arguments>
|
|
||||||
</buildCommand>
|
|
||||||
</buildSpec>
|
|
||||||
<natures>
|
|
||||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
|
||||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
|
||||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
|
||||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
|
||||||
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
|
||||||
</natures>
|
|
||||||
<filteredResources>
|
|
||||||
<filter>
|
|
||||||
<id>1746619144814</id>
|
|
||||||
<name></name>
|
|
||||||
<type>30</type>
|
|
||||||
<matcher>
|
|
||||||
<id>org.eclipse.core.resources.regexFilterMatcher</id>
|
|
||||||
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
|
|
||||||
</matcher>
|
|
||||||
</filter>
|
|
||||||
</filteredResources>
|
|
||||||
</projectDescription>
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<classpath>
|
|
||||||
<classpathentry excluding="**/*.min.js|**/node_modules/*|**/bower_components/*" kind="src" path="WebContent"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
|
|
||||||
<attributes>
|
|
||||||
<attribute name="hide" value="true"/>
|
|
||||||
</attributes>
|
|
||||||
</classpathentry>
|
|
||||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
|
|
||||||
<classpathentry kind="output" path=""/>
|
|
||||||
</classpath>
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
encoding//WebContent/WEB-INF/view/materMgmt/materOrderDown1.jsp=UTF-8
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
eclipse.preferences.version=1
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
|
||||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
|
||||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
|
||||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
|
||||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
|
||||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
|
||||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
|
||||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
|
||||||
org.eclipse.jdt.core.compiler.source=1.7
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
|
||||||
<wb-module deploy-name="plm">
|
|
||||||
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
|
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
|
|
||||||
<property name="context-root" value="plm"/>
|
|
||||||
<property name="java-output-path" value="/plm/build/classes"/>
|
|
||||||
</wb-module>
|
|
||||||
</project-modules>
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<faceted-project>
|
|
||||||
<runtime name="ilshin"/>
|
|
||||||
<fixed facet="wst.jsdt.web"/>
|
|
||||||
<fixed facet="jst.web"/>
|
|
||||||
<fixed facet="java"/>
|
|
||||||
<installed facet="java" version="1.7"/>
|
|
||||||
<installed facet="jst.web" version="3.0"/>
|
|
||||||
<installed facet="wst.jsdt.web" version="1.0"/>
|
|
||||||
</faceted-project>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Window
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
DELEGATES_PREFERENCE=delegateValidatorList
|
|
||||||
USER_BUILD_PREFERENCE=enabledBuildValidatorListorg.eclipse.jst.j2ee.internal.classpathdep.ClasspathDependencyValidator;
|
|
||||||
USER_MANUAL_PREFERENCE=enabledManualValidatorListorg.eclipse.jst.j2ee.internal.classpathdep.ClasspathDependencyValidator;
|
|
||||||
USER_PREFERENCE=overrideGlobalPreferencestruedisableAllValidationtrueversion1.2.700.v201508251749
|
|
||||||
eclipse.preferences.version=1
|
|
||||||
override=true
|
|
||||||
suspend=true
|
|
||||||
vf.version=3
|
|
||||||
519
README.md
|
|
@ -1,472 +1,175 @@
|
||||||
# WACE 솔루션 (PLM)
|
# WACE 솔루션 (ERP/PLM)
|
||||||
|
|
||||||
## 프로젝트 개요
|
## 프로젝트 개요
|
||||||
|
|
||||||
본 프로젝트는 제품 수명 주기 관리(PLM - Product Lifecycle Management) 솔루션입니다.
|
본 프로젝트는 WACE ERP/PLM(Product Lifecycle Management) 솔루션입니다.
|
||||||
**기존 JSP 기반 프론트엔드를 Next.js 14로 완전 전환**하여 현대적이고 사용자 친화적인 웹 애플리케이션을 제공합니다.
|
Node.js + Next.js 기반 풀스택 웹 애플리케이션으로, 멀티테넌시를 지원합니다.
|
||||||
|
|
||||||
## 🚀 주요 특징
|
## 주요 특징
|
||||||
|
|
||||||
- **모던 프론트엔드**: Next.js 14 + TypeScript + shadcn/ui
|
- **모던 프론트엔드**: Next.js (App Router) + TypeScript + shadcn/ui
|
||||||
|
- **Node.js 백엔드**: Express + TypeScript + PostgreSQL
|
||||||
- **반응형 디자인**: 데스크톱, 태블릿, 모바일 모든 기기 지원
|
- **반응형 디자인**: 데스크톱, 태블릿, 모바일 모든 기기 지원
|
||||||
- **안정적인 백엔드**: 검증된 Java Spring + MyBatis 기반 API
|
- **멀티테넌시**: 회사별 데이터 격리 (company_code 기반)
|
||||||
- **Docker 기반 배포**: 개발/운영 환경 일관성 보장
|
- **Docker 기반 배포**: 개발/운영 환경 일관성 보장
|
||||||
- **타입 안전성**: TypeScript로 런타임 에러 방지
|
- **타입 안전성**: TypeScript로 런타임 에러 방지
|
||||||
|
|
||||||
## 🛠️ 기술 스택
|
## 기술 스택
|
||||||
|
|
||||||
### Frontend (Next.js 14)
|
### Frontend
|
||||||
|
|
||||||
- **프레임워크**: Next.js 14 (App Router)
|
- **프레임워크**: Next.js (App Router, Turbopack)
|
||||||
- **언어**: TypeScript
|
- **언어**: TypeScript
|
||||||
- **UI 라이브러리**: shadcn/ui + Radix UI
|
- **UI 라이브러리**: shadcn/ui + Radix UI
|
||||||
- **스타일링**: Tailwind CSS
|
- **스타일링**: Tailwind CSS
|
||||||
- **폼 처리**: React Hook Form + Zod
|
- **상태 관리**: TanStack Query + React Context
|
||||||
- **상태 관리**: React Context + Hooks
|
|
||||||
- **아이콘**: Lucide React
|
- **아이콘**: Lucide React
|
||||||
|
|
||||||
### Backend (기존 유지)
|
### Backend
|
||||||
|
|
||||||
- **언어**: Java 7
|
- **런타임**: Node.js 20+
|
||||||
- **프레임워크**: Spring Framework 3.2.4
|
- **프레임워크**: Express 4
|
||||||
- **ORM**: MyBatis 3.2.3
|
- **언어**: TypeScript
|
||||||
- **데이터베이스**: PostgreSQL
|
- **데이터베이스**: PostgreSQL (pg 드라이버)
|
||||||
- **WAS**: Apache Tomcat 7.0
|
- **인증**: JWT (jsonwebtoken) + bcryptjs
|
||||||
|
- **로깅**: Winston
|
||||||
|
|
||||||
### 개발 도구
|
### 개발 도구
|
||||||
|
|
||||||
- **컨테이너화**: Docker + Docker Compose
|
- **컨테이너화**: Docker + Docker Compose
|
||||||
- **코드 품질**: ESLint + TypeScript
|
- **코드 품질**: ESLint + Prettier
|
||||||
- **패키지 관리**: npm
|
- **테스트**: Jest + Supertest
|
||||||
|
- **백엔드 핫리로드**: nodemon
|
||||||
|
- **CI/CD**: Jenkins
|
||||||
|
|
||||||
## 📁 프로젝트 구조
|
## 프로젝트 구조
|
||||||
|
|
||||||
```
|
```
|
||||||
new_ph/
|
ERP-node/
|
||||||
├── frontend/ # Next.js 프론트엔드
|
├── backend-node/ # Express + TypeScript 백엔드
|
||||||
│ ├── app/ # Next.js App Router
|
│ ├── src/
|
||||||
│ │ ├── (auth)/ # 인증 관련 페이지
|
│ │ ├── app.ts # 엔트리포인트
|
||||||
│ │ │ └── login/ # 로그인 페이지
|
│ │ ├── controllers/ # API 컨트롤러
|
||||||
│ │ ├── dashboard/ # 대시보드
|
│ │ ├── services/ # 비즈니스 로직
|
||||||
│ │ └── layout.tsx # 루트 레이아웃
|
│ │ ├── middleware/ # 인증, 에러처리 미들웨어
|
||||||
│ ├── components/ # 재사용 컴포넌트
|
│ │ ├── routes/ # 라우터
|
||||||
|
│ │ └── config/ # DB 연결 등 설정
|
||||||
|
│ └── package.json
|
||||||
|
├── frontend/ # Next.js 프론트엔드
|
||||||
|
│ ├── app/ # App Router 페이지
|
||||||
|
│ ├── components/ # React 컴포넌트
|
||||||
│ │ ├── ui/ # shadcn/ui 기본 컴포넌트
|
│ │ ├── ui/ # shadcn/ui 기본 컴포넌트
|
||||||
│ │ └── layout/ # 레이아웃 컴포넌트
|
│ │ ├── admin/ # 관리자 컴포넌트
|
||||||
│ ├── lib/ # 유틸리티 함수
|
│ │ ├── screen/ # 화면 디자이너
|
||||||
│ └── types/ # TypeScript 타입 정의
|
│ │ └── v2/ # V2 컴포넌트
|
||||||
├── src/ # Java 백엔드 소스
|
│ ├── lib/ # 유틸리티, API 클라이언트
|
||||||
│ └── com/pms/ # 패키지 구조
|
│ ├── hooks/ # Custom React Hooks
|
||||||
├── WebContent/ # 레거시 JSP (사용 중단)
|
│ └── package.json
|
||||||
├── db/ # 데이터베이스 스크립트
|
├── db/ # 데이터베이스
|
||||||
└── docker-compose.win.yml # Windows 환경 설정
|
│ └── migrations/ # 순차 마이그레이션 SQL
|
||||||
|
├── docker/ # Docker 설정 (dev/prod/deploy)
|
||||||
|
├── scripts/ # 개발/배포 스크립트
|
||||||
|
├── docs/ # 프로젝트 문서
|
||||||
|
├── Dockerfile # 프로덕션 멀티스테이지 빌드
|
||||||
|
├── Jenkinsfile # CI/CD 파이프라인
|
||||||
|
└── .cursorrules # AI 개발 가이드
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 빠른 시작
|
## 빠른 시작
|
||||||
|
|
||||||
### 1. 필수 요구사항
|
### 1. 필수 요구사항
|
||||||
|
|
||||||
- **Docker Desktop** (Windows/Mac) 또는 **Docker + Docker Compose** (Linux)
|
- **Node.js**: 20.10+
|
||||||
- **Git** (소스 코드 관리)
|
- **PostgreSQL**: 데이터베이스 서버
|
||||||
|
- **npm**: 10.0+
|
||||||
|
|
||||||
### 2. Windows 환경에서 실행
|
### 2. 개발 환경 실행
|
||||||
|
|
||||||
#### 자동 실행 (권장)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 프로젝트 시작
|
# 백엔드 (nodemon으로 자동 재시작)
|
||||||
./run-windows.bat
|
cd backend-node && npm install && npm run dev
|
||||||
|
|
||||||
# 서비스 상태 확인
|
# 프론트엔드 (Turbopack)
|
||||||
./status-windows.bat
|
cd frontend && npm install && npm run dev
|
||||||
|
|
||||||
# 서비스 중지
|
|
||||||
./stop-windows.bat
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 수동 실행
|
### 3. Docker 환경 실행
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Docker 컨테이너 실행
|
# 백엔드 + 프론트엔드 (개발)
|
||||||
docker-compose -f docker-compose.win.yml up --build -d
|
docker-compose -f docker-compose.backend.win.yml up -d
|
||||||
|
docker-compose -f docker-compose.frontend.win.yml up -d
|
||||||
|
|
||||||
# 로그 확인
|
# 프로덕션 배포
|
||||||
docker-compose -f docker-compose.win.yml logs -f
|
docker-compose -f docker/deploy/docker-compose.yml up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 서비스 접속
|
### 4. 서비스 접속
|
||||||
|
|
||||||
| 서비스 | URL | 설명 |
|
| 서비스 | URL | 설명 |
|
||||||
| -------------- | --------------------- | ------------------------------ |
|
| -------------- | --------------------- | ------------------------------ |
|
||||||
| **프론트엔드** | http://localhost:9771 | Next.js 기반 사용자 인터페이스 |
|
| **프론트엔드** | http://localhost:9771 | Next.js 사용자 인터페이스 |
|
||||||
| **백엔드 API** | http://localhost:9090 | Spring 기반 REST API |
|
| **백엔드 API** | http://localhost:8080 | Express REST API |
|
||||||
|
|
||||||
> **주의**: 기존 JSP 화면(`localhost:9090`)은 더 이상 사용하지 않습니다.
|
## 주요 기능
|
||||||
> 모든 사용자는 **Next.js 프론트엔드(`localhost:9771`)**를 사용해주세요.
|
|
||||||
|
|
||||||
## 🎨 UI/UX 디자인 시스템
|
### 1. 사용자 및 권한 관리
|
||||||
|
- 사용자 계정 관리 (CRUD)
|
||||||
|
- 역할 기반 접근 제어 (RBAC)
|
||||||
|
- 부서/조직 관리
|
||||||
|
- 멀티테넌시 (회사별 데이터 격리)
|
||||||
|
|
||||||
### shadcn/ui 컴포넌트 라이브러리
|
### 2. 메뉴 및 화면 관리
|
||||||
|
- 동적 메뉴 구성
|
||||||
|
- 화면 디자이너 (드래그앤드롭)
|
||||||
|
- V2 컴포넌트 시스템
|
||||||
|
|
||||||
- **일관된 디자인**: 전체 애플리케이션에서 통일된 UI 컴포넌트
|
### 3. 플로우(워크플로우) 관리
|
||||||
- **접근성**: WCAG 가이드라인 준수
|
- 비즈니스 프로세스 정의
|
||||||
- **커스터마이징**: Tailwind CSS로 쉬운 스타일 변경
|
- 데이터 흐름 관리
|
||||||
- **다크모드**: 자동 테마 전환 지원
|
- 감사 로그
|
||||||
|
|
||||||
### 공통 스타일 가이드
|
### 4. 제품/BOM 관리
|
||||||
|
- BOM 구성 및 버전 관리
|
||||||
|
- 제품 정보 관리
|
||||||
|
|
||||||
```typescript
|
### 5. 기타
|
||||||
// 색상 팔레트
|
- 파일/문서 관리
|
||||||
const colors = {
|
- 메일 연동
|
||||||
primary: "hsl(222.2 47.4% 11.2%)", // 네이비 블루
|
- 외부 DB 연결
|
||||||
secondary: "hsl(210 40% 96%)", // 연한 그레이
|
- 번호 채번 규칙
|
||||||
accent: "hsl(210 40% 98%)", // 거의 화이트
|
|
||||||
destructive: "hsl(0 62.8% 30.6%)", // 레드
|
|
||||||
muted: "hsl(210 40% 96%)", // 음소거된 그레이
|
|
||||||
};
|
|
||||||
|
|
||||||
// 타이포그래피
|
## 환경 변수
|
||||||
const typography = {
|
|
||||||
fontFamily: "Inter, system-ui, sans-serif",
|
|
||||||
fontSize: {
|
|
||||||
xs: "0.75rem", // 12px
|
|
||||||
sm: "0.875rem", // 14px
|
|
||||||
base: "1rem", // 16px
|
|
||||||
lg: "1.125rem", // 18px
|
|
||||||
xl: "1.25rem", // 20px
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 개발 가이드
|
|
||||||
|
|
||||||
### 컴포넌트 개발 원칙
|
|
||||||
|
|
||||||
#### 1. 재사용 가능한 컴포넌트
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// components/ui/button.tsx
|
|
||||||
interface ButtonProps {
|
|
||||||
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost";
|
|
||||||
size?: "default" | "sm" | "lg" | "icon";
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Button({
|
|
||||||
variant = "default",
|
|
||||||
size = "default",
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: ButtonProps) {
|
|
||||||
return (
|
|
||||||
<button className={cn(buttonVariants({ variant, size }))} {...props}>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. 폼 컴포넌트
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// React Hook Form + Zod 사용
|
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import * as z from "zod";
|
|
||||||
|
|
||||||
const loginSchema = z.object({
|
|
||||||
userId: z.string().min(1, "사용자 ID를 입력해주세요"),
|
|
||||||
password: z.string().min(1, "비밀번호를 입력해주세요"),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function LoginForm() {
|
|
||||||
const form = useForm<z.infer<typeof loginSchema>>({
|
|
||||||
resolver: zodResolver(loginSchema),
|
|
||||||
});
|
|
||||||
|
|
||||||
// 폼 처리 로직
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. API 연동
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// lib/api.ts
|
|
||||||
class ApiClient {
|
|
||||||
private baseURL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:9090";
|
|
||||||
|
|
||||||
async login(credentials: LoginCredentials): Promise<LoginResponse> {
|
|
||||||
const response = await fetch(`${this.baseURL}/api/auth/login`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(credentials),
|
|
||||||
credentials: "include", // 세션 쿠키 포함
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) throw new Error("로그인 실패");
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 스타일링 가이드
|
|
||||||
|
|
||||||
#### 1. Tailwind CSS 클래스
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// 일반적인 레이아웃
|
|
||||||
<div className="flex items-center justify-center min-h-screen bg-slate-50">
|
|
||||||
<div className="w-full max-w-md space-y-6">
|
|
||||||
{/* 컨텐츠 */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
// 카드 컴포넌트
|
|
||||||
<div className="bg-white rounded-lg shadow-lg border border-slate-200 p-6">
|
|
||||||
{/* 카드 내용 */}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
// 반응형 그리드
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
{/* 그리드 아이템 */}
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. CSS 변수 활용
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* globals.css */
|
|
||||||
:root {
|
|
||||||
--background: 0 0% 100%;
|
|
||||||
--foreground: 222.2 84% 4.9%;
|
|
||||||
--primary: 222.2 47.4% 11.2%;
|
|
||||||
--primary-foreground: 210 40% 98%;
|
|
||||||
--secondary: 210 40% 96%;
|
|
||||||
--secondary-foreground: 222.2 84% 4.9%;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔐 인증 시스템
|
|
||||||
|
|
||||||
### 세션 기반 인증 (기존 백엔드 호환)
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// 로그인 프로세스
|
|
||||||
1. 사용자가 로그인 폼 제출
|
|
||||||
2. Next.js에서 백엔드 API 호출
|
|
||||||
3. 백엔드에서 세션 생성 (기존 로직 사용)
|
|
||||||
4. 프론트엔드에서 인증 상태 관리
|
|
||||||
5. 보호된 라우트 접근 제어
|
|
||||||
```
|
|
||||||
|
|
||||||
### 라우트 보호
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// middleware.ts
|
|
||||||
export function middleware(request: NextRequest) {
|
|
||||||
const { pathname } = request.nextUrl;
|
|
||||||
|
|
||||||
// 공개 페이지
|
|
||||||
const publicPaths = ["/login", "/"];
|
|
||||||
if (publicPaths.includes(pathname)) return NextResponse.next();
|
|
||||||
|
|
||||||
// 인증 확인
|
|
||||||
const sessionCookie = request.cookies.get("JSESSIONID");
|
|
||||||
if (!sessionCookie) {
|
|
||||||
return NextResponse.redirect(new URL("/login", request.url));
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.next();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 주요 기능
|
|
||||||
|
|
||||||
### 1. 대시보드
|
|
||||||
|
|
||||||
- **프로젝트 현황**: 진행 중인 프로젝트 상태 모니터링
|
|
||||||
- **작업 요약**: 개인별 할당된 작업 목록
|
|
||||||
- **알림 센터**: 중요한 업데이트 및 알림
|
|
||||||
- **차트 및 그래프**: 프로젝트 진척도 시각화
|
|
||||||
|
|
||||||
### 2. 프로젝트 관리
|
|
||||||
|
|
||||||
- **프로젝트 생성/수정**: 새 프로젝트 등록 및 정보 관리
|
|
||||||
- **팀 구성**: 프로젝트 멤버 할당 및 역할 관리
|
|
||||||
- **마일스톤**: 주요 일정 및 목표 설정
|
|
||||||
- **진행 상황 추적**: 실시간 프로젝트 진척도 모니터링
|
|
||||||
|
|
||||||
### 3. 제품 관리
|
|
||||||
|
|
||||||
- **제품 카탈로그**: 제품 정보 및 사양 관리
|
|
||||||
- **BOM 관리**: Bill of Materials 구성 및 버전 관리
|
|
||||||
- **설계 변경**: ECO/ECR 프로세스 관리
|
|
||||||
- **문서 관리**: 기술 문서 및 도면 버전 제어
|
|
||||||
|
|
||||||
### 4. 사용자 관리
|
|
||||||
|
|
||||||
- **사용자 계정**: 계정 생성/수정/비활성화
|
|
||||||
- **권한 관리**: 역할 기반 접근 제어 (RBAC)
|
|
||||||
- **부서 관리**: 조직 구조 및 부서별 권한 설정
|
|
||||||
- **감사 로그**: 사용자 활동 추적 및 보안 모니터링
|
|
||||||
|
|
||||||
## 🚢 배포 가이드
|
|
||||||
|
|
||||||
### 개발 환경
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 프론트엔드 개발 서버
|
# backend-node/.env
|
||||||
cd frontend && npm run dev
|
DATABASE_URL=postgresql://postgres:password@localhost:5432/dbname
|
||||||
|
JWT_SECRET=your-jwt-secret
|
||||||
|
JWT_EXPIRES_IN=24h
|
||||||
|
PORT=8080
|
||||||
|
CORS_ORIGIN=http://localhost:9771
|
||||||
|
|
||||||
# 백엔드 (Docker)
|
# frontend/.env.local
|
||||||
docker-compose -f docker-compose.win.yml up -d plm-app
|
NEXT_PUBLIC_API_URL=http://localhost:8080/api
|
||||||
```
|
```
|
||||||
|
|
||||||
### 운영 환경
|
## 배포
|
||||||
|
|
||||||
|
### 프로덕션 빌드
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 전체 서비스 배포
|
# 멀티스테이지 Docker 빌드 (백엔드 + 프론트엔드)
|
||||||
docker-compose -f docker-compose.prod.yml up -d
|
docker build -t wace-solution .
|
||||||
|
|
||||||
# 무중단 배포 (blue-green)
|
|
||||||
./deploy-production.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 환경 변수 설정
|
### CI/CD
|
||||||
|
|
||||||
```bash
|
Jenkins 파이프라인 (`Jenkinsfile`)으로 자동 빌드 및 배포가 설정되어 있습니다.
|
||||||
# .env.local (Next.js)
|
|
||||||
NEXT_PUBLIC_API_URL=http://localhost:9090
|
|
||||||
NEXT_PUBLIC_APP_ENV=development
|
|
||||||
|
|
||||||
# docker-compose.win.yml (백엔드)
|
## 코드 컨벤션
|
||||||
DB_URL=jdbc:postgresql://db:5432/plm
|
|
||||||
DB_USERNAME=postgres
|
|
||||||
DB_PASSWORD=secure_password
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 문제 해결
|
|
||||||
|
|
||||||
### 자주 발생하는 문제
|
|
||||||
|
|
||||||
#### 1. 로그인 화면이 업데이트되지 않는 경우
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 브라우저 캐시 클리어 후 다음 확인:
|
|
||||||
# - Next.js 서버 재시작
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# - 올바른 URL 접속 확인
|
|
||||||
# 올바름: http://localhost:9771/login
|
|
||||||
# 잘못됨: http://localhost:9090/login.jsp
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Docker 컨테이너 실행 오류
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 포트 충돌 확인
|
|
||||||
netstat -ano | findstr :9771
|
|
||||||
netstat -ano | findstr :9090
|
|
||||||
|
|
||||||
# Docker 시스템 정리
|
|
||||||
docker system prune -a
|
|
||||||
docker-compose -f docker-compose.win.yml down --volumes
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. API 연결 오류
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 백엔드 컨테이너 로그 확인
|
|
||||||
docker-compose -f docker-compose.win.yml logs plm-app
|
|
||||||
|
|
||||||
# 네트워크 연결 확인
|
|
||||||
curl http://localhost:9090/api/health
|
|
||||||
```
|
|
||||||
|
|
||||||
### 개발자 도구
|
|
||||||
|
|
||||||
#### 브라우저 개발자 도구
|
|
||||||
|
|
||||||
- **Console**: JavaScript 오류 확인
|
|
||||||
- **Network**: API 요청/응답 모니터링
|
|
||||||
- **Application**: 세션 쿠키 확인
|
|
||||||
|
|
||||||
#### 로그 확인
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Next.js 개발 서버 로그
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# 백엔드 애플리케이션 로그
|
|
||||||
docker-compose -f docker-compose.win.yml logs -f plm-app
|
|
||||||
|
|
||||||
# 데이터베이스 로그
|
|
||||||
docker-compose -f docker-compose.win.yml logs -f db
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📈 성능 최적화
|
|
||||||
|
|
||||||
### Next.js 최적화
|
|
||||||
|
|
||||||
- **이미지 최적화**: Next.js Image 컴포넌트 사용
|
|
||||||
- **코드 분할**: 동적 임포트로 번들 크기 최소화
|
|
||||||
- **서버 사이드 렌더링**: 초기 로딩 속도 개선
|
|
||||||
- **정적 생성**: 변경되지 않는 페이지 사전 생성
|
|
||||||
|
|
||||||
### 백엔드 최적화
|
|
||||||
|
|
||||||
- **데이터베이스 인덱스**: 자주 조회되는 필드 인덱싱
|
|
||||||
- **쿼리 최적화**: N+1 문제 해결
|
|
||||||
- **캐싱**: Redis를 통한 세션 및 데이터 캐싱
|
|
||||||
- **리소스 최적화**: JVM 메모리 튜닝
|
|
||||||
|
|
||||||
## 🤝 기여 가이드
|
|
||||||
|
|
||||||
### 코드 컨벤션
|
|
||||||
|
|
||||||
- **TypeScript**: 엄격한 타입 정의 사용
|
- **TypeScript**: 엄격한 타입 정의 사용
|
||||||
- **ESLint**: 코드 품질 유지.
|
- **ESLint + Prettier**: 일관된 코드 스타일
|
||||||
- **Prettier**: 일관된 코드 포맷팅
|
- **shadcn/ui**: UI 컴포넌트 표준
|
||||||
- **커밋 메시지**: Conventional Commits 규칙 준수
|
- **API 클라이언트**: `frontend/lib/api/` 전용 클라이언트 사용 (fetch 직접 사용 금지)
|
||||||
|
- **멀티테넌시**: 모든 쿼리에 company_code 필터링 필수
|
||||||
### 브랜치 전략
|
|
||||||
|
|
||||||
```bash
|
|
||||||
main # 운영 환경 배포 브랜치
|
|
||||||
develop # 개발 환경 통합 브랜치
|
|
||||||
feature/* # 기능 개발 브랜치
|
|
||||||
hotfix/* # 긴급 수정 브랜치
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pull Request 프로세스
|
|
||||||
|
|
||||||
1. 기능 브랜치에서 개발
|
|
||||||
2. 테스트 코드 작성
|
|
||||||
3. PR 생성 및 코드 리뷰
|
|
||||||
4. 승인 후 develop 브랜치에 병합
|
|
||||||
|
|
||||||
## 📞 지원 및 문의
|
|
||||||
|
|
||||||
- **개발팀 문의**: 내부 Slack 채널 `#plm-support`
|
|
||||||
- **버그 리포트**: GitHub Issues
|
|
||||||
- **기능 요청**: Product Owner와 협의
|
|
||||||
- **긴급 상황**: 개발팀 직접 연락
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 변경 이력
|
|
||||||
|
|
||||||
### v2.0.0 (2025년 1월)
|
|
||||||
|
|
||||||
- ✅ JSP → Next.js 14 완전 전환
|
|
||||||
- ✅ shadcn/ui 디자인 시스템 도입
|
|
||||||
- ✅ TypeScript 타입 안전성 강화
|
|
||||||
- ✅ 반응형 디자인 적용
|
|
||||||
- ✅ WACE 브랜딩 적용
|
|
||||||
|
|
||||||
### v1.x (레거시)
|
|
||||||
|
|
||||||
- ❌ JSP + jQuery 기반 (사용 중단)
|
|
||||||
- ❌ 데스크톱 전용 UI
|
|
||||||
- ❌ 제한적인 확장성
|
|
||||||
|
|
||||||
**🎉 현재 버전 2.0.0에서는 완전히 새로운 사용자 경험을 제공합니다!**
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<Context docBase="ilshin" path="/" reloadable="true" source="org.eclipse.jst.jee.server:ilshin">
|
|
||||||
<Resource auth="Container" driverClassName="org.postgresql.Driver" maxActive="100" maxIdle="10" maxWait="-1" name="plm" password="admin0909!!" type="javax.sql.DataSource" url="jdbc:postgresql://211.224.136.4:5432/ilshin" username="postgres"/>
|
|
||||||
</Context>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
Manifest-Version: 1.0
|
|
||||||
Class-Path:
|
|
||||||
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<title>네이버 :: Smart Editor 2 ™</title>
|
|
||||||
<script type="text/javascript" src="./js/HuskyEZCreator.js" charset="utf-8"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<form action="sample.php" method="post">
|
|
||||||
<textarea name="ir1" id="ir1" rows="10" cols="100" style="width:766px; height:412px; display:none;"></textarea>
|
|
||||||
<!--textarea name="ir1" id="ir1" rows="10" cols="100" style="width:100%; height:412px; min-width:610px; display:none;"></textarea-->
|
|
||||||
<p>
|
|
||||||
<input type="button" onclick="pasteHTML();" value="본문에 내용 넣기" />
|
|
||||||
<input type="button" onclick="showHTML();" value="본문 내용 가져오기" />
|
|
||||||
<input type="button" onclick="submitContents(this);" value="서버로 내용 전송" />
|
|
||||||
<input type="button" onclick="setDefaultFont();" value="기본 폰트 지정하기 (궁서_24)" />
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
var oEditors = [];
|
|
||||||
|
|
||||||
// 추가 글꼴 목록
|
|
||||||
//var aAdditionalFontSet = [["MS UI Gothic", "MS UI Gothic"], ["Comic Sans MS", "Comic Sans MS"],["TEST","TEST"]];
|
|
||||||
|
|
||||||
nhn.husky.EZCreator.createInIFrame({
|
|
||||||
oAppRef: oEditors,
|
|
||||||
elPlaceHolder: "ir1",
|
|
||||||
sSkinURI: "SmartEditor2Skin.html",
|
|
||||||
htParams : {
|
|
||||||
bUseToolbar : true, // 툴바 사용 여부 (true:사용/ false:사용하지 않음)
|
|
||||||
bUseVerticalResizer : true, // 입력창 크기 조절바 사용 여부 (true:사용/ false:사용하지 않음)
|
|
||||||
bUseModeChanger : true, // 모드 탭(Editor | HTML | TEXT) 사용 여부 (true:사용/ false:사용하지 않음)
|
|
||||||
//aAdditionalFontList : aAdditionalFontSet, // 추가 글꼴 목록
|
|
||||||
fOnBeforeUnload : function(){
|
|
||||||
//alert("완료!");
|
|
||||||
}
|
|
||||||
}, //boolean
|
|
||||||
fOnAppLoad : function(){
|
|
||||||
//예제 코드
|
|
||||||
//oEditors.getById["ir1"].exec("PASTE_HTML", ["로딩이 완료된 후에 본문에 삽입되는 text입니다."]);
|
|
||||||
},
|
|
||||||
fCreator: "createSEditor2"
|
|
||||||
});
|
|
||||||
|
|
||||||
function pasteHTML() {
|
|
||||||
var sHTML = "<span style='color:#FF0000;'>이미지도 같은 방식으로 삽입합니다.<\/span>";
|
|
||||||
oEditors.getById["ir1"].exec("PASTE_HTML", [sHTML]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showHTML() {
|
|
||||||
var sHTML = oEditors.getById["ir1"].getIR();
|
|
||||||
alert(sHTML);
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitContents(elClickedObj) {
|
|
||||||
oEditors.getById["ir1"].exec("UPDATE_CONTENTS_FIELD", []); // 에디터의 내용이 textarea에 적용됩니다.
|
|
||||||
|
|
||||||
// 에디터의 내용에 대한 값 검증은 이곳에서 document.getElementById("ir1").value를 이용해서 처리하면 됩니다.
|
|
||||||
|
|
||||||
try {
|
|
||||||
elClickedObj.form.submit();
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setDefaultFont() {
|
|
||||||
var sDefaultFont = '궁서';
|
|
||||||
var nFontSize = 24;
|
|
||||||
oEditors.getById["ir1"].setDefaultFont(sDefaultFont, nFontSize);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
|
||||||
/* COMMON */
|
|
||||||
body,#smart_editor2,#smart_editor2 p,#smart_editor2 h1,#smart_editor2 h2,#smart_editor2 h3,#smart_editor2 h4,#smart_editor2 h5,#smart_editor2 h6,#smart_editor2 ul,#smart_editor2 ol,#smart_editor2 li,#smart_editor2 dl,#smart_editor2 dt,#smart_editor2 dd,#smart_editor2 table,#smart_editor2 th,#smart_editor2 td,#smart_editor2 form,#smart_editor2 fieldset,#smart_editor2 legend,#smart_editor2 input,#smart_editor2 textarea,#smart_editor2 button,#smart_editor2 select{margin:0;padding:0}
|
|
||||||
#smart_editor2,#smart_editor2 h1,#smart_editor2 h2,#smart_editor2 h3,#smart_editor2 h4,#smart_editor2 h5,#smart_editor2 h6,#smart_editor2 input,#smart_editor2 textarea,#smart_editor2 select,#smart_editor2 table,#smart_editor2 button{font-family:'돋움',Dotum,Helvetica,sans-serif;font-size:12px;color:#666}
|
|
||||||
#smart_editor2 span,#smart_editor2 em{font-size:12px}
|
|
||||||
#smart_editor2 em,#smart_editor2 address{font-style:normal}
|
|
||||||
#smart_editor2 img,#smart_editor2 fieldset{border:0}
|
|
||||||
#smart_editor2 hr{display:none}
|
|
||||||
#smart_editor2 ol,#smart_editor2 ul{list-style:none}
|
|
||||||
#smart_editor2 button{border:0;background:none;font-size:11px;vertical-align:top;cursor:pointer}
|
|
||||||
#smart_editor2 button span,#smart_editor2 button em{visibility:hidden;overflow:hidden;position:absolute;top:0;font-size:0;line-height:0}
|
|
||||||
#smart_editor2 legend,#smart_editor2 .blind{visibility:hidden;overflow:hidden;position:absolute;width:0;height:0;font-size:0;line-height:0}
|
|
||||||
#smart_editor2 .input_ty1{height:14px;margin:0;padding:4px 2px 0 4px;border:1px solid #c7c7c7;font-size:11px;color:#666}
|
|
||||||
#smart_editor2 a:link,#smart_editor2 a:visited,#smart_editor2 a:active,#smart_editor2 a:focus{color:#666;text-decoration:none}
|
|
||||||
#smart_editor2 a:hover{color:#666;text-decoration:underline}
|
|
||||||
/* LAYOUT */
|
|
||||||
#smart_editor2 .se2_header{margin:10px 0 29px 0}
|
|
||||||
#smart_editor2 .se2_bi{float:left;width:93px;height:20px;margin:0;padding:0;background:url("../img/ko_KR/btn_set.png?130306") -343px -358px no-repeat;font-size:0;line-height:0;text-indent:-10000px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_allhelp{display:inline-block;width:18px;height:18px;padding:0;background:url("../img/ko_KR/btn_set.png?130306") -437px -358px no-repeat;font-size:0;line-height:0;text-indent:-10000px;vertical-align:middle}
|
|
||||||
#smart_editor2 #smart_editor2_content{border:1px solid #b5b5b5}
|
|
||||||
#smart_editor2 .se2_tool{overflow:visible;position:relative;z-index:25}
|
|
||||||
/* EDITINGAREA */
|
|
||||||
#smart_editor2 .se2_input_area{position:relative;z-index:22;height:400px;margin:0;padding:0;*zoom:1}
|
|
||||||
#smart_editor2 .se2_input_wysiwyg,#smart_editor2 .se2_input_syntax{display:block;overflow:auto;width:100%;height:100%;margin:0;*margin:-1px 0 0 0;border:0}
|
|
||||||
/* EDITINGMODE */
|
|
||||||
#smart_editor2 .se2_conversion_mode{position:relative;height:15px;padding-top:1px;border-top:1px solid #b5b5b5;background:url("../img/icon_set.gif") 0 -896px repeat-x}
|
|
||||||
#smart_editor2 .se2_inputarea_controller{display:block;clear:both;position:relative;width:100%;height:15px;text-align:center;cursor:n-resize}
|
|
||||||
#smart_editor2 .se2_inputarea_controller span,#smart_editor2 .controller_on span{background:url("../img/ico_extend.png") no-repeat}
|
|
||||||
#smart_editor2 .se2_inputarea_controller span{position:static;display:inline-block;visibility:visible;overflow:hidden;height:15px;padding-left:11px;background-position:0 2px;color:#888;font-size:11px;letter-spacing:-1px;line-height:16px;white-space:nowrap}
|
|
||||||
* + html #smart_editor2 .se2_inputarea_controller span{line-height:14px}
|
|
||||||
#smart_editor2 .controller_on span{background-position:0 -21px;color:#249c04}
|
|
||||||
#smart_editor2 .ly_controller{display:block;position:absolute;bottom:2px;left:50%;width:287px;margin-left:-148px;padding:8px 0 7px 9px;border:1px solid #827f7c;background:#fffdef}
|
|
||||||
#smart_editor2 .ly_controller p{color:#666;font-size:11px;letter-spacing:-1px;line-height:11px}
|
|
||||||
#smart_editor2 .ly_controller .bt_clse,#smart_editor2 .ly_controller .ic_arr{position:absolute;background:url("../img/ico_extend.png") no-repeat}
|
|
||||||
#smart_editor2 .ly_controller .bt_clse{top:5px;right:4px;width:14px;height:15px;background-position:1px -43px}
|
|
||||||
#smart_editor2 .ly_controller .ic_arr{top:25px;left:50%;width:10px;height:6px;margin-left:-5px;background-position:0 -65px}
|
|
||||||
#smart_editor2 .se2_converter{float:left;position:absolute;top:-1px;right:3px;z-index:20}
|
|
||||||
#smart_editor2 .se2_converter li{float:left}
|
|
||||||
#smart_editor2 .se2_converter .se2_to_editor{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") 0 -85px no-repeat;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_converter .se2_to_html{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -59px -70px no-repeat;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_converter .se2_to_text{width:60px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -417px -466px no-repeat;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_converter .active .se2_to_editor{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") 0 -70px no-repeat;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_converter .active .se2_to_html{width:59px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -59px -85px no-repeat;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_converter .active .se2_to_text{width:60px;height:15px;background:url("../img/ko_KR/btn_set.png?130306") -417px -481px no-repeat;vertical-align:top}
|
|
||||||
/* EDITINGAREA_HTMLSRC */
|
|
||||||
#smart_editor2 .off .ico_btn,#smart_editor2 .off .se2_more,#smart_editor2 .off .se2_more2,#smart_editor2 .off .se2_font_family,#smart_editor2 .off .se2_font_size,#smart_editor2 .off .se2_bold,#smart_editor2 .off .se2_underline,#smart_editor2 .off .se2_italic,#smart_editor2 .off .se2_tdel,#smart_editor2 .off .se2_fcolor,#smart_editor2 .off .se2_fcolor_more,#smart_editor2 .off .se2_bgcolor,#smart_editor2 .off .se2_bgcolor_more,#smart_editor2 .off .se2_left,#smart_editor2 .off .se2_center,#smart_editor2 .off .se2_right,#smart_editor2 .off .se2_justify,#smart_editor2 .off .se2_ol,#smart_editor2 .off .se2_ul,#smart_editor2 .off .se2_indent,#smart_editor2 .off .se2_outdent,#smart_editor2 .off .se2_lineheight,#smart_editor2 .off .se2_del_style,#smart_editor2 .off .se2_blockquote,#smart_editor2 .off .se2_summary,#smart_editor2 .off .se2_footnote,#smart_editor2 .off .se2_url,#smart_editor2 .off .se2_emoticon,#smart_editor2 .off .se2_character,#smart_editor2 .off .se2_table,#smart_editor2 .off .se2_find,#smart_editor2 .off .se2_spelling,#smart_editor2 .off .se2_sup,#smart_editor2 .off .se2_sub,#smart_editor2 .off .se2_text_tool_more,#smart_editor2 .off .se2_new,#smart_editor2 .off .selected_color,#smart_editor2 .off .se2_lineSticker{-ms-filter:alpha(opacity=50);opacity:.5;cursor:default;filter:alpha(opacity=50)}
|
|
||||||
/* LAYER */
|
|
||||||
#smart_editor2 .se2_text_tool .se2_layer{display:none;float:left;position:absolute;top:20px;left:0;z-index:50;margin:0;padding:0;border:1px solid #bcbbbb;background:#fafafa}
|
|
||||||
#smart_editor2 .se2_text_tool li.active{z-index:50}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_layer{display:block}
|
|
||||||
#smart_editor2 .se2_text_tool .active li .se2_layer{display:none}
|
|
||||||
#smart_editor2 .se2_text_tool .active .active .se2_layer{display:block}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_layer .se2_in_layer{float:left;margin:0;padding:0;border:1px solid #fff;background:#fafafa}
|
|
||||||
/* TEXT_TOOLBAR */
|
|
||||||
#smart_editor2 .se2_text_tool{position:relative;clear:both;z-index:30;padding:4px 0 4px 3px;background:#f4f4f4 url("../img/bg_text_tool.gif") 0 0 repeat-x;border-bottom:1px solid #b5b5b5;*zoom:1}
|
|
||||||
#smart_editor2 .se2_text_tool:after{content:"";display:block;clear:both}
|
|
||||||
#smart_editor2 .se2_text_tool ul{float:left;display:inline;margin-right:3px;padding-left:1px;white-space:nowrap}
|
|
||||||
#smart_editor2 .se2_text_tool li{_display:inline;float:left;position:relative;z-index:30}
|
|
||||||
#smart_editor2 .se2_text_tool button,#smart_editor2 .se2_multy .se2_icon{width:21px;height:21px;background:url("../img/ko_KR/text_tool_set.png?140317") no-repeat;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_font_type{position:relative}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_font_type li{margin-left:3px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_font_type button{text-align:left}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_font_type button.se2_font_family span,#smart_editor2 .se2_text_tool .se2_font_type button.se2_font_size span{display:inline-block;visibility:visible;position:static;width:52px;height:20px;padding:0 0 0 6px;font-size:12px;line-height:20px;*line-height:22px;color:#333;*zoom:1}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy{position:absolute;top:0;right:0;padding-left:0;margin-right:0;white-space:nowrap;border-left:1px solid #e0dedf}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy .se2_mn{float:left;white-space:nowrap}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy button{background-image:none;width:47px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy .se2_icon{display:inline-block;visibility:visible;overflow:visible;position:static;width:16px;height:29px;margin:-1px 2px 0 -1px;background-position:0 -132px;line-height:30px;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy button,#smart_editor2 .se2_text_tool .se2_multy button span{height:29px;line-height:29px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_map .se2_icon{background-position:-29px -132px}
|
|
||||||
#smart_editor2 .se2_text_tool button span.se2_mntxt{display:inline-block;visibility:visible;overflow:visible;_overflow-y:hidden;position:relative;*margin-right:-1px;width:auto;height:29px;font-weight:normal;font-size:11px;line-height:30px;*line-height:29px;_line-height:30px;color:#444;letter-spacing:-1px;vertical-align:top}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy .se2_photo{margin-right:1px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy .hover .ico_btn{background:#e8e8e8}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_multy .se2_mn.hover{background:#e0dedf}
|
|
||||||
/* TEXT_TOOLBAR : ROUNDING */
|
|
||||||
#smart_editor2 ul li.first_child button span.tool_bg,#smart_editor2 ul li.last_child button span.tool_bg,#smart_editor2 ul li.single_child button span.tool_bg{visibility:visible;height:21px}
|
|
||||||
#smart_editor2 ul li.first_child button span.tool_bg{left:-1px;width:3px;background:url("../img/bg_button_left.gif?20121228") no-repeat}
|
|
||||||
#smart_editor2 ul li.last_child button span.tool_bg{right:0px;_right:-1px;width:2px;background:url("../img/bg_button_right.gif") no-repeat}
|
|
||||||
#smart_editor2 ul li.single_child{padding-right:1px}
|
|
||||||
#smart_editor2 ul li.single_child button span.tool_bg{left:0;background:url("../img/bg_button.gif?20121228") no-repeat;width:22px}
|
|
||||||
#smart_editor2 div.se2_text_tool ul li.hover button span.tool_bg{background-position:0 -21px}
|
|
||||||
#smart_editor2 div.se2_text_tool ul li.active button span.tool_bg,#smart_editor2 div.se2_text_tool ul li.active li.active button span.tool_bg{background-position:0 -42px}
|
|
||||||
#smart_editor2 div.se2_text_tool ul li.active li button span.tool_bg{background-position:0 0}
|
|
||||||
/* TEXT_TOOLBAR : SUB_MENU */
|
|
||||||
#smart_editor2 .se2_sub_text_tool{display:none;position:absolute;top:20px;left:0;z-index:40;width:auto;height:29px;padding:0 4px 0 0;border:1px solid #b5b5b5;border-top:1px solid #9a9a9a;background:#f4f4f4}
|
|
||||||
#smart_editor2 .active .se2_sub_text_tool{display:block}
|
|
||||||
#smart_editor2 .se2_sub_text_tool ul{float:left;height:25px;margin:0;padding:4px 0 0 4px}
|
|
||||||
/* TEXT_TOOLBAR : SUB_MENU_SIZE */
|
|
||||||
#smart_editor2 .se2_sub_step1{width:88px}
|
|
||||||
#smart_editor2 .se2_sub_step2{width:199px}
|
|
||||||
#smart_editor2 .se2_sub_step2_1{width:178px}
|
|
||||||
/* TEXT_TOOLBAR : BUTTON */
|
|
||||||
#smart_editor2 .se2_text_tool .se2_font_family{width:70px;height:21px;background-position:0 -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_font_family{background-position:0 -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_font_family{background-position:0 -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_font_size{width:45px;height:21px;background-position:-70px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_font_size{background-position:-70px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_font_size{background-position:-70px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_bold{background-position:-115px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_bold{background-position:-115px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_bold{background-position:-115px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_underline{background-position:-136px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_underline{background-position:-136px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_underline{background-position:-136px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_italic{background-position:-157px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_italic{background-position:-157px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_italic{background-position:-157px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_tdel{background-position:-178px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_tdel{background-position:-178px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_tdel{background-position:-178px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_fcolor{position:relative;background-position:-199px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_fcolor{background-position:-199px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_fcolor{background-position:-199px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_fcolor_more{background-position:-220px -10px;width:10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_fcolor_more{background-position:-220px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_fcolor_more{background-position:-220px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .selected_color{position:absolute;top:14px;left:5px;width:11px;height:3px;font-size:0}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_ol,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_ol{background-position:-345px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_ul,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_ul{background-position:-366px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_ol,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_ol{background-position:-345px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_ul,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_ul{background-position:-366px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_ol,#smart_editor2 .se2_text_tool .active .active .se2_ol{background-position:-345px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_ul,#smart_editor2 .se2_text_tool .active .active .se2_ul{background-position:-366px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_indent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_indent{background-position:-408px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_outdent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .se2_outdent{background-position:-387px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_indent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_indent{background-position:-408px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_outdent,#smart_editor2 .se2_text_tool .active .se2_sub_text_tool .hover .se2_outdent{background-position:-387px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_indent,#smart_editor2 .se2_text_tool .active .active .se2_indent{background-position:-408px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_outdent,#smart_editor2 .se2_text_tool .active .active .se2_outdent{background-position:-387px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_lineheight{background-position:-429px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_lineheight{background-position:-429px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_lineheight{background-position:-429px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_url{background-position:-513px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_url{background-position:-513px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_url{background-position:-513px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_bgcolor{position:relative;background-position:-230px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_bgcolor{background-position:-230px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_bgcolor{background-position:-230px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_bgcolor_more{background-position:-251px -10px;width:10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_bgcolor_more{background-position:-251px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_bgcolor_more{background-position:-251px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_left{background-position:-261px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_left{background-position:-261px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_left{background-position:-261px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_center{background-position:-282px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_center{background-position:-282px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_center{background-position:-282px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_right{background-position:-303px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_right{background-position:-303px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_right{background-position:-303px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_justify{background-position:-324px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_justify{background-position:-324px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_justify{background-position:-324px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_blockquote{background-position:-471px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_blockquote{background-position:-471px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_blockquote{background-position:-471px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_character{background-position:-555px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_character{background-position:-555px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_character{background-position:-555px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_table{background-position:-576px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_table{background-position:-576px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_table{background-position:-576px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_find{background-position:-597px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_find{background-position:-597px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_find{background-position:-597px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_sup{background-position:-660px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_sup{background-position:-660px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_sup{background-position:-660px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_sub{background-position:-681px -10px}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_sub{background-position:-681px -72px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_sub{background-position:-681px -103px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_text_tool_more{background-position:0 -41px;width:13px}
|
|
||||||
#smart_editor2 .se2_text_tool .se2_text_tool_more span.tool_bg{background:none}
|
|
||||||
#smart_editor2 .se2_text_tool .hover .se2_text_tool_more{background-position:-13px -41px}
|
|
||||||
#smart_editor2 .se2_text_tool .active .se2_text_tool_more{background-position:-26px -41px}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
|
||||||
/* COMMON */
|
|
||||||
body,.se2_inputarea{margin:0;padding:0;font-family:'돋움',Dotum,Helvetica,Sans-serif;font-size:12px;line-height:1.5}
|
|
||||||
/* body,.se2_inputarea,.se2_inputarea th,.se2_inputarea td{margin:0;padding:0;font-family:'돋움',Dotum,Helvetica,Sans-serif;font-size:12px;line-height:1.5;color:#666} */
|
|
||||||
.se2_inputarea p,.se2_inputarea br{margin:0;padding:0}
|
|
||||||
.se2_inputarea{margin:15px;word-wrap:break-word;*word-wrap:normal;*word-break:break-all}
|
|
||||||
.se2_inputarea td{word-break:break-all}
|
|
||||||
.se2_inputarea_890{width:741px;margin:20px 0 10px 64px}
|
|
||||||
.se2_inputarea_698{width:548px;margin:20px 0 10px 64px}
|
|
||||||
/* TEXT_TOOLBAR : QUOTE */
|
|
||||||
.se2_quote1{margin:0 0 30px 20px;padding:0 8px;border-left:2px solid #ccc;color:#888}
|
|
||||||
.se2_quote2{margin:0 0 30px 13px;padding:0 8px 0 16px;background:url("../img/bg_quote2.gif") 0 3px no-repeat;color:#888}
|
|
||||||
.se2_quote3{margin:0 0 30px;padding:12px 10px 11px;border:1px dashed #ccc;color:#888}
|
|
||||||
.se2_quote4{margin:0 0 30px;padding:12px 10px 11px;border:1px dashed #66b246;color:#888}
|
|
||||||
.se2_quote5{margin:0 0 30px;padding:12px 10px 11px;border:1px dashed #ccc;background:#fafafa;color:#888}
|
|
||||||
.se2_quote6{margin:0 0 30px;padding:12px 10px 11px;border:1px solid #e5e5e5;color:#888}
|
|
||||||
.se2_quote7{margin:0 0 30px;padding:12px 10px 11px;border:1px solid #66b246;color:#888}
|
|
||||||
.se2_quote8{margin:0 0 30px;padding:12px 10px 11px;border:1px solid #e5e5e5;background:#fafafa;color:#888}
|
|
||||||
.se2_quote9{margin:0 0 30px;padding:12px 10px 11px;border:2px solid #e5e5e5;color:#888}
|
|
||||||
.se2_quote10{margin:0 0 30px;padding:12px 10px 11px;border:2px solid #e5e5e5;background:#fafafa;color:#888}
|
|
||||||
|
|
@ -1,462 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
|
||||||
/* TEXT_TOOLBAR : FONTNAME */
|
|
||||||
#smart_editor2 .se2_tool .se2_l_font_fam{width:202px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_tool .se2_l_font_fam li{display:block;width:202px;height:21px;margin:0;padding:0;color:#333;cursor:pointer}
|
|
||||||
#smart_editor2 .se2_l_font_fam .hover,#smart_editor2 .se2_l_font_fam .active{background:#ebebeb}
|
|
||||||
#smart_editor2 .se2_l_font_fam button{width:200px;height:21px;margin:0;padding:2px 0 2px 0px;background:none;text-align:left}
|
|
||||||
#smart_editor2 .se2_l_font_fam button span{display:block;visibility:visible;overflow:visible;position:relative;top:auto;left:auto;width:auto;height:auto;margin:0 0 0 4px;padding:0;font-size:12px;line-height:normal;color:#333}
|
|
||||||
#smart_editor2 .se2_l_font_fam button span span{display:inline;visibility:visible;overflow:visible;width:auto;height:auto;margin:0 0 0 4px;font-family:Verdana;font-size:12px;line-height:14px;color:#888}
|
|
||||||
#smart_editor2 .se2_l_font_fam button span em{visibility:visible;overflow:auto;position:static;width:auto;height:auto;margin-right:-4px;font-size:12px;color:#888}
|
|
||||||
#smart_editor2 .se2_l_font_fam .se2_division{width:162px;height:2px !important;margin:1px 0 1px 0px;border:0;background:url("../img/bg_line1.gif") 0 0 repeat-x;font-size:0;cursor:default}
|
|
||||||
/* TEXT_TOOLBAR : FONTSIZE */
|
|
||||||
#smart_editor2 .se2_tool .se2_l_font_size{width:302px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_tool .se2_l_font_size li{width:302px;margin:0;padding:0;color:#333;cursor:pointer}
|
|
||||||
#smart_editor2 .se2_l_font_size .hover,#smart_editor2 .se2_l_font_size .active{background:#ebebeb}
|
|
||||||
#smart_editor2 .se2_l_font_size button{width:300px;height:auto;margin:0;padding:2px 0 1px 0px;*padding:4px 0 1px 0px;background:none;text-align:left}
|
|
||||||
#smart_editor2 .se2_l_font_size button span{display:block;visibility:visible;overflow:visible;position:relative;top:auto;left:auto;width:auto;height:auto;margin:0 0 0 4px;padding:0;line-height:normal;color:#373737;letter-spacing:0px}
|
|
||||||
#smart_editor2 .se2_l_font_size button span span{display:inline;margin:0 0 0 5px;padding:0}
|
|
||||||
#smart_editor2 .se2_l_font_size span em{visibility:visible;overflow:auto;position:static;width:auto;height:auto;color:#888}
|
|
||||||
/* TEXT_TOOLBAR : FONTCOLOR */
|
|
||||||
#smart_editor2 .se2_palette{float:left;position:relative;width:225px;margin:0;padding:11px 0 10px 0}
|
|
||||||
#smart_editor2 .se2_palette .se2_pick_color{_display:inline;float:left;clear:both;width:205px;margin:0 0 0 11px;padding:0}
|
|
||||||
#smart_editor2 .se2_palette .se2_pick_color li{float:left;width:12px;height:12px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_palette .se2_pick_color li button{width:11px;height:11px;border:0}
|
|
||||||
#smart_editor2 .se2_palette .se2_pick_color li button span{display:block;visibility:visible;overflow:visible;position:absolute;top:1px;left:1px;width:11px;height:11px}
|
|
||||||
#smart_editor2 .se2_palette .se2_pick_color li button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0}
|
|
||||||
#smart_editor2 .se2_palette .se2_pick_color .hover button,#smart_editor2 .se2_palette .se2_pick_color .active button{width:11px;height:11px;border:1px solid #666}
|
|
||||||
#smart_editor2 .se2_palette .se2_pick_color .hover span,#smart_editor2 .se2_palette .se2_pick_color .active span{width:7px;height:7px;border:1px solid #fff}
|
|
||||||
#smart_editor2 .se2_palette .se2_view_more{_display:inline;float:left;width:46px;height:23px;margin:1px 0 0 1px;background:url("../img/ko_KR/btn_set.png?130306") 0 -47px no-repeat}
|
|
||||||
#smart_editor2 .se2_palette .se2_view_more2{_display:inline;float:left;width:46px;height:23px;margin:1px 0 0 1px;background:url("../img/ko_KR/btn_set.png?130306") 0 -24px no-repeat}
|
|
||||||
#smart_editor2 .se2_palette h4{_display:inline;float:left;width:203px;margin:9px 0 0 11px;padding:10px 0 4px 0;background:url("../img/bg_line1.gif") repeat-x;font-weight:normal;font-size:12px;line-height:14px;color:#333;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_palette2{float:left;_float:none;width:214px;margin:9px 0 0 0;padding:11px 0 0 11px;background:url("../img/bg_line1.gif") repeat-x}
|
|
||||||
#smart_editor2 .se2_palette2 .se2_color_set{float:left}
|
|
||||||
#smart_editor2 .se2_palette2 .se2_selected_color{_display:inline;float:left;width:83px;height:18px;margin:0;border:1px solid #c7c7c7;background:#fff}
|
|
||||||
#smart_editor2 .se2_palette2 .se2_selected_color span{_display:inline;float:left;width:79px;height:14px;margin:2px}
|
|
||||||
#smart_editor2 .se2_palette2 .input_ty1{_display:inline;float:left;width:67px;height:16px;margin:0 3px 0 3px;padding:2px 2px 0 4px;font-family:tahoma;font-size:11px}
|
|
||||||
#smart_editor2 .se2_palette2 button.se2_btn_insert{float:left;width:35px;height:21px;margin-left:2px;padding:0;background:url("../img/ko_KR/btn_set.png?130306") -80px 0 no-repeat}
|
|
||||||
#smart_editor2 .se2_gradation1{float:left;_float:none;width:201px;height:128px;margin:4px 0 0 0;border:1px solid #c7c7c7;cursor:crosshair}
|
|
||||||
#smart_editor2 .se2_gradation2{float:left;_float:none;width:201px;height:10px;margin:4px 0 1px 0;border:1px solid #c7c7c7;cursor:crosshair}
|
|
||||||
/* TEXT_TOOLBAR : BGCOLOR */
|
|
||||||
#smart_editor2 .se2_palette_bgcolor{width:225px;margin:11px 0 0;padding:0}
|
|
||||||
#smart_editor2 .se2_palette_bgcolor .se2_background{width:205px;margin:0 11px 0 11px}
|
|
||||||
#smart_editor2 .se2_palette_bgcolor .se2_background li{width:68px;height:20px}
|
|
||||||
#smart_editor2 .se2_palette_bgcolor .se2_background button{width:67px;height:19px;border:0}
|
|
||||||
#smart_editor2 .se2_palette_bgcolor .se2_background span{left:0;display:block;visibility:visible;overflow:visible;width:65px;height:17px;padding:0}
|
|
||||||
#smart_editor2 .se2_palette_bgcolor .se2_background span span{display:block;visibility:visible;overflow:visible;width:64px;height:16px;padding:3px 0 0 3px;font-size:11px;line-height:14px;text-align:left}
|
|
||||||
#smart_editor2 .se2_palette_bgcolor .se2_background .hover span{width:65px;height:17px;border:1px solid #666}
|
|
||||||
#smart_editor2 .se2_palette_bgcolor .se2_background .hover span span{width:62px;height:14px;padding:1px 0 0 1px;border:1px solid #fff}
|
|
||||||
/* TEXT_TOOLBAR : LINEHEIGHT */
|
|
||||||
#smart_editor2 .se2_l_line_height{width:107px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_l_line_height li{width:107px;margin:0;padding:0;border-top:0;border-bottom:0;color:#333;cursor:pointer}
|
|
||||||
#smart_editor2 .se2_l_line_height .hover{background:#ebebeb}
|
|
||||||
#smart_editor2 .se2_l_line_height button{width:105px;height:19px;margin:0;padding:3px 0 2px 0px;background:none;text-align:left}
|
|
||||||
#smart_editor2 .se2_l_line_height button span{visibility:visible;overflow:visible;position:relative;width:auto;height:auto;margin:0;padding:0 0 0 15px;font-size:12px;line-height:normal;color:#373737}
|
|
||||||
#smart_editor2 .se2_l_line_height li button.active span{background:url("../img/icon_set.gif") 5px -30px no-repeat}
|
|
||||||
#smart_editor2 .se2_l_line_height_user{clear:both;width:83px;margin:5px 0 0 12px;padding:10px 0 0 0;_padding:11px 0 0 0;background:url("../img/bg_line1.gif") repeat-x}
|
|
||||||
#smart_editor2 .se2_l_line_height_user h3{margin:0 0 4px 0;_margin:0 0 2px -1px;padding:0;line-height:14px;color:#000;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_l_line_height_user .bx_input{display:block;position:relative;width:83px}
|
|
||||||
#smart_editor2 .se2_l_line_height_user .btn_up{position:absolute;top:2px;*top:3px;left:68px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -54px no-repeat}
|
|
||||||
#smart_editor2 .se2_l_line_height_user .btn_down{position:absolute;top:10px;*top:11px;left:68px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -62px no-repeat}
|
|
||||||
#smart_editor2 .se2_l_line_height_user .btn_area{margin:5px 0 10px 0}
|
|
||||||
#smart_editor2 .se2_tool .btn_area .se2_btn_apply3{width:41px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat}
|
|
||||||
#smart_editor2 .se2_tool .btn_area .se2_btn_cancel3{width:39px;height:24px;margin-left:3px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
|
||||||
/* TEXT_TOOLBAR : QUOTE */
|
|
||||||
#smart_editor2 .se2_quote{width:425px;height:56px}
|
|
||||||
#smart_editor2 .se2_quote ul{_display:inline;float:left;margin:11px 0 0 9px;padding:0}
|
|
||||||
#smart_editor2 .se2_quote li{_display:inline;float:left;margin:0 0 0 2px;padding:0}
|
|
||||||
#smart_editor2 .se2_quote button{width:34px;height:34px;margin:0;padding:0;background:url("../img/ko_KR/btn_set.png?130306") no-repeat;cursor:pointer}
|
|
||||||
#smart_editor2 .se2_quote button span{left:0;display:block;visibility:visible;overflow:visible;width:32px;height:32px;margin:0;padding:0;border:1px solid #c7c7c7}
|
|
||||||
#smart_editor2 .se2_quote button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote1{background-position:1px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote2{background-position:-32px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote3{background-position:-65px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote4{background-position:-98px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote5{background-position:-131px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote6{background-position:-164px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote7{background-position:-197px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote8{background-position:-230px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote9{background-position:-263px -375px}
|
|
||||||
#smart_editor2 .se2_quote .se2_quote10{background-position:-296px -375px}
|
|
||||||
#smart_editor2 .se2_quote .hover button span,#smart_editor2 .se2_quote .active button span{width:30px;height:30px;margin:0;padding:0;border:2px solid #44b525}
|
|
||||||
#smart_editor2 .se2_quote .hover button span span,#smart_editor2 .se2_quote .active button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_quote .se2_cancel2{float:left;width:40px;height:35px;margin:11px 0 0 5px;background:url("../img/ko_KR/btn_set.png?130306") -46px -24px no-repeat}
|
|
||||||
#smart_editor2 .se2_quote .se2_cancel2 span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0;margin:0;padding:0}
|
|
||||||
/* TEXT_TOOLBAR : HYPERLINK */
|
|
||||||
#smart_editor2 .se2_url2{width:281px;padding:11px 11px 6px 11px;color:#666}
|
|
||||||
#smart_editor2 .se2_url2 .input_ty1{display:block;width:185px;height:16px;margin:0 5px 5px 0;*margin:-1px 5px 5px 0;padding:5px 2px 0 4px}
|
|
||||||
#smart_editor2 .se2_url2 .se2_url_new{width:15px;height:15px;margin:-1px 3px 1px -1px;*margin:-2px 3px 2px -1px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_url2 label{font-size:11px;line-height:14px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_url2 .se2_apply{position:absolute;top:13px;right:51px;width:41px;height:24px;margin:-1px 3px 1px 0;background:url("../img/ko_KR/btn_set.png?130306") no-repeat}
|
|
||||||
#smart_editor2 .se2_url2 .se2_cancel{position:absolute;top:13px;right:9px;width:39px;height:24px;margin:-1px 3px 1px 0;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
|
||||||
/* TEXT_TOOLBAR : SCHARACTER */
|
|
||||||
#smart_editor2 .se2_bx_character{width:469px;height:272px;margin:0;padding:0;background:url("../img/ko_KR/bx_set_110302.gif") 9px -1230px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char_tab{_display:inline;float:left;position:relative;width:443px;margin:11px 10px 200px 11px;padding:0 0 0 1px}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char_tab li{position:static;margin:0 0 0 -1px;padding:0}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char1{width:76px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -204px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char2{width:86px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -75px -204px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char3{width:68px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -160px -204px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char4{width:55px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -227px -204px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char5{width:97px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -281px -204px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_char6{width:66px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -377px -204px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .active .se2_char1{width:76px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -230px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .active .se2_char2{width:86px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -75px -230px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .active .se2_char3{width:68px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -160px -230px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .active .se2_char4{width:55px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -227px -230px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .active .se2_char5{width:97px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -281px -230px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .active .se2_char6{width:66px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -377px -230px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_s_character{display:none;position:absolute;top:26px;left:0;width:448px;height:194px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_bx_character .active .se2_s_character{display:block}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_s_character ul{float:left;width:422px;height:172px;margin:0;padding:9px 0 0 11px}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_s_character li{_display:inline;float:left;position:relative;width:20px;height:18px;margin:0 0 1px 1px;background:#fff}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_s_character button{width:20px;height:18px;margin:0;padding:2px;background:none}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_s_character .hover,#smart_editor2 .se2_bx_character .se2_s_character .active{background:url("../img/ko_KR/btn_set.png?130306") -446px -274px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_character .se2_s_character button span{left:0;display:block;visibility:visible;overflow:visible;width:14px;height:16px;margin:3px 0 0 3px;border:0;background:none;font-size:12px;line-height:normal}
|
|
||||||
#smart_editor2 .se2_apply_character{clear:both;position:relative;padding:0 0 0 11px}
|
|
||||||
#smart_editor2 .se2_apply_character label{margin:0 3px 0 0;font-size:12px;color:#666;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_apply_character .input_ty1{width:283px;height:17px;margin:-1px 5px 1px 0;padding:4px 0 0 5px;font-size:12px;color:#666;letter-spacing:0;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_apply_character .se2_confirm{width:41px;height:24px;margin-right:3px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_apply_character .se2_cancel{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat;vertical-align:middle}
|
|
||||||
/* TEXT_TOOLBAR : TABLECREATOR */
|
|
||||||
#smart_editor2 .se2_table_set{position:relative;width:166px;margin:3px 11px 0 11px;padding:8px 0 0 0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_cell_num{float:left;width:73px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_cell_num dt{float:left;clear:both;width:17px;height:23px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_cell_num dt label{display:block;margin:5px 0 0 0;font-size:11px;color:#666}
|
|
||||||
#smart_editor2 .se2_table_set .se2_cell_num dd{float:left;position:relative;width:54px;height:23px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_cell_num .input_ty2{display:block;width:32px;height:16px;*margin:-1px 0 0 0;padding:2px 19px 0 0px;border:1px solid #c7c7c7;font-family:tahoma,verdana,times New Roman;font-size:11px;color:#666;text-align:right;*direction:rtl}
|
|
||||||
#smart_editor2 .se2_table_set .se2_cell_num .input_ty2::-ms-clear{display:none}
|
|
||||||
#smart_editor2 .se2_table_set .se2_pre_table{float:right;width:91px;height:43px;background:#c7c7c7;border-spacing:1px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_pre_table tr{background:#fff}
|
|
||||||
#smart_editor2 .se2_table_set .se2_pre_table td{font-size:0;line-height:0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_add{position:absolute;top:2px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -54px no-repeat}
|
|
||||||
#smart_editor2 .se2_table_set .se2_del{position:absolute;top:10px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -62px no-repeat}
|
|
||||||
/* TEXT_TOOLBAR : TABLEEDITOR */
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1{float:left;width:166px;margin:7px 0 0 0;padding:10px 0 5px;background:url("../img/bg_line1.gif") repeat-x}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1 dt{width:166px;margin:0 0 6px 0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1 dd{width:166px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1 dt input{width:15px;height:15px;margin:-1px 3px 1px 0;_margin:-2px 3px 2px 0;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1 dt label{font-weight:bold;font-size:11px;color:#666;letter-spacing:-1px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1{float:left;position:relative;z-index:59;width:166px;margin:1px 0 0 0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_2{z-index:54;margin:0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_3{z-index:53;margin:0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_4{z-index:52;margin:0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 dt{_display:inline;float:left;clear:both;width:66px;height:22px;margin:1px 0 0 18px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 dt label{display:block;margin:4px 0 0 0;font-weight:normal;font-size:11px;color:#666;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 dd{float:left;position:relative;width:82px;height:23px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .input_ty1{width:72px;height:16px;*margin:-1px 0 0 0;padding:2px 2px 0 6px;font-family:tahoma,verdana,times New Roman;font-size:11px;color:#666}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .input_ty3{float:left;width:49px;height:16px;margin:0 3px 0 0;padding:2px 4px 0 4px;border:1px solid #c7c7c7;font-family:tahoma,verdana,times New Roman;font-size:11px;color:#666}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .se2_add{top:2px;right:2px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .se2_del{top:10px;right:2px}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper1_1 .se2_color_set .input_ty1{_display:inline;float:left;width:67px;height:16px;margin:0 3px 0 3px;padding:2px 2px 0 4px;font-family:tahoma,verdana,times New Roman;font-size:11px}
|
|
||||||
#smart_editor2 .se2_select_ty1{position:relative;width:80px;height:18px;border:1px solid #c7c7c7;background:#fff;font-size:11px;line-height:14px;text-align:left}
|
|
||||||
#smart_editor2 .se2_select_ty1 span{float:left;width:54px;height:18px;margin:0 0 0 5px;font-size:11px;line-height:14px;color:#666}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style0{position:relative;top:3px;left:-3px;white-space:nowrap}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style1{height:15px;margin:3px 0 0 4px;font-size:11px;line-height:14px;color:#666;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style2{background:url("../img/bg_set.gif") 0 -50px repeat-x}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style3{background:url("../img/bg_set.gif") 0 -68px repeat-x}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style4{background:url("../img/bg_set.gif") 0 -85px repeat-x}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style5{background:url("../img/bg_set.gif") 0 -103px repeat-x}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style6{background:url("../img/bg_set.gif") 0 -121px repeat-x}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_b_style7{background:url("../img/bg_set.gif") 0 -139px repeat-x}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_view_more{position:absolute;top:1px;right:1px;width:13px;height:16px;background:url("../img/ko_KR/btn_set.png?130306") -112px -54px no-repeat}
|
|
||||||
#smart_editor2 .se2_select_ty1 .se2_view_more2{position:absolute;top:1px;right:1px;width:13px;height:16px;background:url("../img/ko_KR/btn_set.png?130306") -99px -54px no-repeat}
|
|
||||||
/* TEXT_TOOLBAR : TABLEEDITOR > BORDER */
|
|
||||||
#smart_editor2 .se2_table_set .se2_b_t_b1{border-top:1px solid #b1b1b1}
|
|
||||||
#smart_editor2 .se2_layer_b_style{position:absolute;top:20px;right:0px;width:80px;padding-bottom:1px;border:1px solid #c7c7c7;border-top:1px solid #a8a8a8;background:#fff}
|
|
||||||
#smart_editor2 .se2_layer_b_style ul{width:80px;margin:0;padding:1px 0 0 0}
|
|
||||||
#smart_editor2 .se2_layer_b_style li{width:80px;height:18px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_layer_b_style .hover,#smart_editor2 .se2_layer_b_style .active{background:#ebebeb}
|
|
||||||
#smart_editor2 .se2_layer_b_style button{width:80px;height:18px;background:none}
|
|
||||||
#smart_editor2 .se2_layer_b_style button span{left:0;display:block;visibility:visible;overflow:visible;width:71px;height:18px;margin:0 0 0 5px;font-size:11px;line-height:15px;text-align:left}
|
|
||||||
#smart_editor2 .se2_layer_b_style button span span{visibility:hidden;overflow:hidden;position:absolute;top:0;left:0;width:0;height:0}
|
|
||||||
#smart_editor2 .se2_layer_b_style .se2_b_style1 span{margin:3px 0 0 4px;font-size:11px;line-height:14px;color:#666;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_layer_b_style .se2_b_style2 span{background:url("../img/bg_set.gif") 0 -50px repeat-x}
|
|
||||||
#smart_editor2 .se2_layer_b_style .se2_b_style3 span{background:url("../img/bg_set.gif") 0 -68px repeat-x}
|
|
||||||
#smart_editor2 .se2_layer_b_style .se2_b_style4 span{background:url("../img/bg_set.gif") 0 -86px repeat-x}
|
|
||||||
#smart_editor2 .se2_layer_b_style .se2_b_style5 span{background:url("../img/bg_set.gif") 0 -103px repeat-x}
|
|
||||||
#smart_editor2 .se2_layer_b_style .se2_b_style6 span{background:url("../img/bg_set.gif") 0 -121px repeat-x}
|
|
||||||
#smart_editor2 .se2_layer_b_style .se2_b_style7 span{background:url("../img/bg_set.gif") 0 -139px repeat-x}
|
|
||||||
/* TEXT_TOOLBAR : TABLEEDITOR > COLOR */
|
|
||||||
#smart_editor2 .se2_pre_color{float:left;width:18px;height:18px;border:1px solid #c7c7c7}
|
|
||||||
#smart_editor2 .se2_pre_color button{float:left;width:14px;height:14px;margin:2px 0 0 2px;padding:0}
|
|
||||||
#smart_editor2 .se2_pre_color button span{overflow:hidden;position:absolute;top:-10000px;left:-10000px;z-index:-100;width:0;height:0}
|
|
||||||
/* TEXT_TOOLBAR : TABLEEDITOR > DIMMED */
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_dim1{clear:both;position:absolute;top:71px;left:16px;z-index:60;width:157px;height:118px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_dim2{position:absolute;top:116px;left:16px;z-index:55;width:157px;height:45px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_dim3{clear:both;position:absolute;top:192px;left:16px;z-index:51;width:157px;height:39px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
/* TEXT_TOOLBAR : TABLEEDITOR > STYLE PREVIEW */
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper2{float:left;position:relative;z-index:50;width:166px;margin:2px 0 0 0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper2 dt{float:left;width:84px;height:33px;margin:4px 0 0 0}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper2 dt input{width:15px;height:15px;margin:-1px 3px 1px 0;_margin:-2px 3px 2px 0;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper2 dt label{font-weight:bold;font-size:11px;color:#666;letter-spacing:-1px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_table_set .se2_t_proper2 dd{float:left;width:66px;height:33px}
|
|
||||||
#smart_editor2 .se2_select_ty2{position:relative;width:65px;height:31px;border:1px solid #c7c7c7;background:#fff;font-size:11px;line-height:14px;text-align:left}
|
|
||||||
#smart_editor2 .se2_select_ty2 span{float:left;width:45px;height:25px;margin:3px 0 0 3px;background:url("../img/ko_KR/btn_set.png?130306") repeat-x}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style1{background-position:0 -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style2{background-position:-46px -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style3{background-position:-92px -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style4{background-position:-138px -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style5{background-position:-184px -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style6{background-position:-230px -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style7{background-position:-276px -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style8{background-position:-322px -410px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style9{background-position:0 -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style10{background-position:-46px -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style11{background-position:-92px -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style12{background-position:-138px -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style13{background-position:-184px -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style14{background-position:-230px -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style15{background-position:-276px -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_t_style16{background-position:-322px -436px}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_view_more{position:absolute;top:1px;right:1px;_right:0px;width:13px !important;height:29px !important;background:url("../img/ko_KR/btn_set.png?130306") -353px -48px no-repeat !important}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_view_more2{position:absolute;top:1px;right:1px;_right:0px;width:13px !important;height:29px !important;background:url("../img/ko_KR/btn_set.png?130306") -340px -48px no-repeat !important}
|
|
||||||
#smart_editor2 .se2_select_ty2 .se2_view_more span{display:none}
|
|
||||||
/* TEXT_TOOLBAR : TABLEEDITOR > STYLE */
|
|
||||||
#smart_editor2 .se2_layer_t_style{position:absolute;top:33px;right:15px;width:208px;border:1px solid #c7c7c7;border-top:1px solid #a8a8a8;background:#fff}
|
|
||||||
#smart_editor2 .se2_layer_t_style ul{width:204px;height:126px;margin:1px 2px;padding:1px 0 0 0;background:#fff}
|
|
||||||
#smart_editor2 .se2_layer_t_style li{_display:inline;float:left;width:45px;height:25px;margin:1px;padding:1px;border:1px solid #fff}
|
|
||||||
#smart_editor2 .se2_layer_t_style .hover,#smart_editor2 .se2_layer_t_style .active{border:1px solid #666;background:#fff}
|
|
||||||
#smart_editor2 .se2_layer_t_style button{width:45px;height:25px;background:url("../img/ko_KR/btn_set.png?130306") repeat-x !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style1{background-position:0 -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style2{background-position:-46px -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style3{background-position:-92px -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style4{background-position:-138px -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style5{background-position:-184px -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style6{background-position:-230px -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style7{background-position:-276px -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style8{background-position:-322px -410px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style9{background-position:0 -436px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style10{background-position:-46px -436px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style11{background-position:-92px -436px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style12{background-position:-138px -436px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style13{background-position:-184px -436px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style14{background-position:-230px -436px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style15{background-position:-276px -436px !important}
|
|
||||||
#smart_editor2 .se2_layer_t_style .se2_t_style16{background-position:-322px -436px !important}
|
|
||||||
#smart_editor2 .se2_table_set .se2_btn_area{float:left;width:166px;margin:6px 0 0 0;padding:12px 0 8px 0;background:url("../img/bg_line1.gif") repeat-x;text-align:center}
|
|
||||||
#smart_editor2 .se2_table_set button.se2_apply{width:41px;height:24px;margin-right:3px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat}
|
|
||||||
#smart_editor2 .se2_table_set button.se2_cancel{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
|
||||||
#smart_editor2 .se2_table_set .se2_rd{width:14px;height:14px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_table_set .se2_celltit{font-size:11px;font-size:11px;color:#666;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_table_set dt label.se2_celltit{display:inline}
|
|
||||||
/* TEXT_TOOLBAR : FINDREPLACE */
|
|
||||||
#smart_editor2 .se2_bx_find_revise{position:relative;width:255px;margin:0;padding:0}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_close{position:absolute;top:5px;right:8px;width:20px;height:20px;background:url("../img/ko_KR/btn_set.png?130306") -151px -1px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise h3{margin:0;padding:10px 0 13px 10px;background:url("../img/bg_find_h3.gif") 0 -1px repeat-x;font-size:12px;line-height:14px;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_bx_find_revise ul{position:relative;margin:8px 0 0 0;padding:0 0 0 12px}
|
|
||||||
#smart_editor2 .se2_bx_find_revise ul li{_display:inline;float:left;position:static;margin:0 0 0 -1px;padding:0}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_tabfind{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -100px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_tabrevise{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -116px -100px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .active .se2_tabfind{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") 0 -126px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .active .se2_tabrevise{width:117px;height:26px;background:url("../img/ko_KR/btn_set.png?130306") -116px -126px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_in_bx_find dl{_display:inline;float:left;width:223px;margin:0 0 0 9px;padding:7px 0 13px 14px;background:url("../img/ko_KR/bx_set_110302.gif") -289px -1518px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_in_bx_revise dl{_display:inline;float:left;width:223px;margin:0 0 0 9px;padding:7px 0 13px 14px;background:url("../img/ko_KR/bx_set_110302.gif") -289px -1619px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise dt{_display:inline;float:left;clear:both;width:47px;margin:1px 0 2px 0}
|
|
||||||
#smart_editor2 .se2_bx_find_revise dd{float:left;margin:0 0 2px 0}
|
|
||||||
#smart_editor2 .se2_bx_find_revise label{float:left;padding:5px 0 0 0;font-size:11px;color:#666;letter-spacing:-2px}
|
|
||||||
#smart_editor2 .se2_bx_find_revise input{float:left;width:155px;height:12px;margin:1px 0 0 0;padding:3px 2px 3px 4px;font-size:12px;color:#666}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_find_btns{float:left;clear:both;width:255px;padding:8px 0 10px 0;text-align:center}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_find_next{width:65px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -180px -48px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_find_next2{width:61px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -180px -24px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_revise1{width:54px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -245px -48px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_revise2{width:70px;height:24px;margin:0 3px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -245px -24px no-repeat}
|
|
||||||
#smart_editor2 .se2_bx_find_revise .se2_cancel{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -41px 0 no-repeat}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE */
|
|
||||||
#smart_editor2 .se2_qmax{position:absolute;width:18px;height:18px;background:url("../img/ko_KR/btn_set.png?130306") -339px -169px no-repeat}
|
|
||||||
#smart_editor2 .se2_qeditor{position:absolute;top:0;left:0;width:183px;margin:0;padding:0;border:1px solid #c7c7c7;border-right:1px solid #ababab;border-bottom:1px solid #ababab;background:#fafafa}
|
|
||||||
#smart_editor2 .se2_qeditor label,#smart_editor2 .se2_qeditor span,#smart_editor2 .se2_qeditor dt{font-size:11px;color:#666;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_qbar{position:relative;width:183px;height:11px;background:url("../img/ko_KR/bx_set_110302.gif") 0 -731px no-repeat}
|
|
||||||
#smart_editor2 .se2_qbar .se2_qmini{position:absolute;top:-1px;right:0;*right:-1px;_right:-3px;width:18px;height:14px;background:url("../img/ko_KR/btn_set.png?130306") -315px -170px no-repeat}
|
|
||||||
#smart_editor2 .se2_qbar .se2_qmini button{width:20px;height:14px;margin-top:-1px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qbody0{float:left;border:1px solid #fefefe}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qbody{position:relative;z-index:90;width:174px;padding:4px 0 0 7px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe1{overflow:hidden;width:174px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe1 dt{float:left;width:22px;height:18px;padding:4px 0 0 0}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe1 dd{float:left;width:65px;height:22px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_addrow{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -49px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_addcol{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -49px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_seprow{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -68px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_sepcol{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -68px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_delrow{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -106px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_delcol{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -106px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_merrow{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -125px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_mercol{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -125px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_seprow_off{width:28px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -87px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_sepcol_off{width:29px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -87px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_merrow_off{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -385px -144px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_mercol_off{width:57px;height:19px;background:url("../img/ko_KR/btn_set.png?130306") no-repeat -413px -144px}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND */
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2{_display:inline;float:left;position:relative;z-index:100;width:165px;margin:2px 0 0 1px;padding:7px 0 0 0;background:url("../img/bg_line1.gif") repeat-x;zoom:1}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_1 dt{float:left;width:62px;padding:3px 0 0 0}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_1 dt input{width:15px;height:15px;margin:-1px 1px 1px -1px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_1 dd{float:left;position:relative;zoom:1}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_3{padding:7px 0 6px 0}
|
|
||||||
/* My글양식 없을때 */
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2{position:relative;_position:absolute}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 dt{float:left;width:50px;padding:3px 0 0 13px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 dt input{width:15px;height:15px;margin:-1px 2px 1px -1px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 dd{float:left}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > STYLE */
|
|
||||||
#smart_editor2 .se2_table_set .se2_qbody .se2_t_proper2{float:left;*float:none;position:static;width:166px;margin:5px 0 0 1px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe3 dt{float:left;width:62px;padding:0}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe3 dt label{font-weight:normal}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe3 dt input{width:15px;height:15px;margin:-1px 1px 1px -1px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe3 dd .se2_qe3_table{position:relative}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND PREWVIEW */
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_color{float:left;width:18px;height:18px;border:1px solid #c7c7c7}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_color button{float:left;width:14px;height:14px;margin:2px 0 0 2px;padding:0}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_color button span{overflow:hidden;position:absolute;top:-10000px;left:-10000px;z-index:-100;width:0;height:0}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND LAYER */
|
|
||||||
#smart_editor2 .se2_qeditor .se2_layer{float:left;clear:both;position:absolute;top:20px;left:0;margin:0;padding:0;border:1px solid #c7c7c7;border-top:1px solid #9a9a9a;background:#fafafa}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_layer .se2_in_layer{float:left;margin:0;padding:0;border:1px solid #fff;background:#fafafa}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_layer button{vertical-align:top}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_layer .se2_pick_color li{position:relative}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND IMAGE */
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg{float:left;width:14px;height:14px;padding:2px;border:1px solid #c7c7c7}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 button{width:16px;height:16px;background:url("../img/ko_KR/btn_set.png?130306") 0 -261px no-repeat}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > CELL_BACKGROUND IMAGE LAYER */
|
|
||||||
#smart_editor2 .se2_cellimg_set{_display:inline;float:left;width:136px;margin:4px 3px 0 4px;padding-bottom:4px}
|
|
||||||
#smart_editor2 .se2_cellimg_set li{_display:inline;float:left;width:16px;height:16px;margin:0 1px 1px 0}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg0{background-position:-255px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg1{background-position:0 -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg2{background-position:-17px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg3{background-position:-34px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg4{background-position:-51px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg5{background-position:-68px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg6{background-position:-85px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg7{background-position:-102px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg8{background-position:-119px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg9{background-position:-136px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg10{background-position:-153px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg11{background-position:-170px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg12{background-position:-187px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg13{background-position:-204px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg14{background-position:-221px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg15{background-position:-238px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg16{background-position:-255px -261px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg17{background-position:0 -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg18{background-position:-17px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg19{background-position:-34px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg20{background-position:-51px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg21{background-position:-68px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg22{background-position:-85px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg23{background-position:-102px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg24{background-position:-119px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg25{background-position:-136px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg26{background-position:-153px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg27{background-position:-170px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg28{background-position:-187px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg29{background-position:-204px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg30{background-position:-221px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qe2_2 .se2_cellimg31{background-position:-238px -278px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg button{width:14px;height:14px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg1{background-position:-1px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg2{background-position:-18px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg3{background-position:-35px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg4{background-position:-52px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg5{background-position:-69px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg6{background-position:-86px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg7{background-position:-103px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg8{background-position:-120px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg9{background-position:-137px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg10{background-position:-154px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg11{background-position:-171px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg12{background-position:-188px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg13{background-position:-205px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg14{background-position:-222px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg15{background-position:-239px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg16{background-position:-256px -262px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg17{background-position:-1px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg18{background-position:-18px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg19{background-position:-35px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg20{background-position:-52px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg21{background-position:-69px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg22{background-position:-86px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg23{background-position:-103px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg24{background-position:-120px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg25{background-position:-137px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg26{background-position:-154px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg27{background-position:-171px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg28{background-position:-188px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg29{background-position:-205px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg30{background-position:-222px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg31{background-position:-239px -279px}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_pre_bgimg .se2_cellimg32{background-position:-256px -279px}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_TABLE > MY REVIEW */
|
|
||||||
#smart_editor2 .se2_btn_area{_display:inline;float:left;clear:both;width:166px;margin:5px 0 0 1px;padding:7px 0 6px 0;background:url("../img/bg_line1.gif") repeat-x;text-align:center}
|
|
||||||
#smart_editor2 .se2_btn_area .se2_btn_save{width:97px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -369px -163px no-repeat}
|
|
||||||
/* TEXT_TOOLBAR : QUICKEDITOR_IMAGE */
|
|
||||||
#smart_editor2 .se2_qe10{width:166px;margin:0;*margin:-2px 0 0 0}
|
|
||||||
#smart_editor2 .se2_qe10 label{margin:0 1px 0 0;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_qe10 .se2_sheight{margin-left:4px}
|
|
||||||
#smart_editor2 .se2_qe10 .input_ty1{width:30px;height:13px;margin:0 0 1px 1px;padding:3px 4px 0 1px;font-size:11px;letter-spacing:0;text-align:right;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_qe10 .se2_sreset{width:41px;height:19px;margin-left:3px;background:url("../img/ko_KR/btn_set.png?130306") -401px -184px no-repeat;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_qe10_1{margin-top:4px;padding:10px 0 3px;background:url("../img/bg_line1.gif") repeat-x}
|
|
||||||
#smart_editor2 .se2_qe10_1 input{width:15px;height:15px;margin:-1px 3px 1px -1px;vertical-align:middle}
|
|
||||||
#smart_editor2 .se2_qe11{float:left;width:166px;margin:4px 0 0 0;padding:7px 0 2px 0;background:url("../img/bg_line1.gif") repeat-x}
|
|
||||||
#smart_editor2 .se2_qe11_1{float:left;width:99px}
|
|
||||||
#smart_editor2 .se2_qe11_1 dt{float:left;width:56px;height:15px;padding:5px 0 0 0}
|
|
||||||
#smart_editor2 .se2_qe11_1 dd{float:left;position:relative;width:38px;height:20px}
|
|
||||||
#smart_editor2 .se2_qe11_1 .input_ty1{display:block;width:29px;height:15px;margin:0;*margin:-1px 0 1px 0;padding:3px 1px 0 5px;font-size:11px;letter-spacing:0;text-align:left}
|
|
||||||
#smart_editor2 .se2_qe11_1 .se2_add{position:absolute;top:2px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -54px no-repeat}
|
|
||||||
#smart_editor2 .se2_qe11_1 .se2_del{position:absolute;top:10px;right:3px;width:13px;height:8px;background:url("../img/ko_KR/btn_set.png?130306") -86px -62px no-repeat}
|
|
||||||
#smart_editor2 .se2_qe11_2{float:left;width:67px}
|
|
||||||
#smart_editor2 .se2_qe11_2 dt{float:left;width:47px;margin:5px 0 0 0}
|
|
||||||
#smart_editor2 .se2_qe11_2 dd{float:left;position:relative;width:20px}
|
|
||||||
#smart_editor2 .se2_qe12{float:left;width:166px;margin:3px 0 0 0;padding:7px 0 0 0;background:url("../img/bg_line1.gif") repeat-x}
|
|
||||||
#smart_editor2 .se2_qe12 dt{float:left;margin:5px 4px 0 0}
|
|
||||||
#smart_editor2 .se2_qe12 dd{float:left;padding:0 0 6px 0}
|
|
||||||
#smart_editor2 .se2_qe12 .se2_align0{float:left;width:19px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -276px -121px no-repeat}
|
|
||||||
#smart_editor2 .se2_qe12 .se2_align1{float:left;width:19px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -295px -121px no-repeat}
|
|
||||||
#smart_editor2 .se2_qe12 .se2_align2{float:left;width:20px;height:21px;background:url("../img/ko_KR/btn_set.png?130306") -314px -121px no-repeat}
|
|
||||||
#smart_editor2 .se2_qe13{position:relative;z-index:10;zoom:1}
|
|
||||||
#smart_editor2 .se2_qe13 dt{float:left;width:62px;padding:3px 0 0}
|
|
||||||
#smart_editor2 .se2_qe13 dt input{width:15px;height:15px;margin:-1px 1px 1px -1px;vertical-align:middle;zoom:1}
|
|
||||||
#smart_editor2 .se2_qe13 dt .se2_qdim2{width:32px}
|
|
||||||
#smart_editor2 .se2_qe13 dd .se2_select_ty1{width:38px}
|
|
||||||
#smart_editor2 .se2_qe13 dd .se2_select_ty1 span{width:15px}
|
|
||||||
#smart_editor2 .se2_qe13 dd .input_ty1{width:20px}
|
|
||||||
#smart_editor2 .se2_qe13 dd .se2_palette2 .input_ty1{width:67px}
|
|
||||||
#smart_editor2 .se2_qe13 .se2_add{*top:3px}
|
|
||||||
#smart_editor2 .se2_qe13 .se2_del{*top:11px}
|
|
||||||
#smart_editor2 .se2_qe13 .se2_layer_b_style{right:-2px;_right:0}
|
|
||||||
#smart_editor2 .se2_qe13 .se2_layer_b_style li span{width:auto;margin:0 4px 0 5px;padding-top:2px}
|
|
||||||
#smart_editor2 .se2_qe13 dd{_display:inline;float:left;position:relative;width:29px;margin-right:5px;_margin-right:3px;zoom:1}
|
|
||||||
#smart_editor2 .se2_qe13 dd .se2_palette h4{margin-top:9px;font-family:dotum;font-size:12px}
|
|
||||||
#smart_editor2 .se2_qe13 dd.dd_type{width:38px}
|
|
||||||
#smart_editor2 .se2_qe13 dd.dd_type2{width:37px;margin-right:3px}
|
|
||||||
#smart_editor2 .se2_qe13 dd.dd_type2 .input_ty1{width:29px}
|
|
||||||
#smart_editor2 .se2_qe13 dd.dd_type2 button{right:2px;_right:1px}
|
|
||||||
#smart_editor2 .se2_qe13 dd.dd_type3{width:20px;margin:0}
|
|
||||||
#smart_editor2 .se2_qe13_v1{_display:inline;float:left;margin:2px 0 1px}
|
|
||||||
#smart_editor2 .se2_qe13_v1 dt{padding:4px 0 0 1px}
|
|
||||||
#smart_editor2 .se2_qe13_v2{_display:inline;float:left;position:relative;z-index:100;width:165px;margin:4px 0 0 1px;zoom:1}
|
|
||||||
#smart_editor2 .se2_qe13_v2 dd{width:18px;margin:0}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim1{clear:both;position:absolute;top:25px;left:115px;width:60px;height:23px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim2{clear:both;position:absolute;top:55px;left:24px;z-index:110;width:70px;height:22px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim3{clear:both;position:absolute;top:55px;left:118px;z-index:110;width:56px;height:22px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim4{clear:both;position:absolute;top:81px;left:23px;z-index:35;width:116px;height:35px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim5{clear:both;position:absolute;top:31px;left:106px;width:68px;height:26px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim6c{clear:both;position:absolute;top:25px;left:28px;width:29px;height:23px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim6r{clear:both;position:absolute;top:25px;left:57px;width:29px;height:23px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_highedit{float:right;width:56px;height:21px;margin:-27px 8px 0 0;background:url("../img/ko_KR/btn_set.png?130306") -329px -142px no-repeat}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim7{clear:both;position:absolute;top:55px;left:24px;z-index:110;width:150px;height:48px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim8{clear:both;position:absolute;top:105px;left:24px;z-index:110;width:150px;height:37px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim9{clear:both;position:absolute;top:55px;left:111px;z-index:110;width:65px;height:24px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim10{clear:both;position:absolute;top:55px;left:100px;z-index:110;width:77px;height:24px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
#smart_editor2 .se2_qeditor .se2_qdim11{clear:both;position:absolute;top:55px;left:65px;z-index:110;width:115px;height:24px;background:#fafafa;opacity:0.5;filter:alpha(opacity=50)}
|
|
||||||
/* HELP : ACCESSIBILITY */
|
|
||||||
#smart_editor2 .se2_accessibility{z-index:90}
|
|
||||||
#smart_editor2 .se2_accessibility .se2_in_layer{width:568px;padding:0 10px;background:#fafafa;border:1px solid #bcbbbb}
|
|
||||||
#smart_editor2 .se2_accessibility h3{margin:0 -10px;padding:6px 0 12px 0;background:url("../img/bg_find_h3.gif") repeat-x;font-size:12px;line-height:14px;letter-spacing:-1px}
|
|
||||||
#smart_editor2 .se2_accessibility h3 strong{display:inline-block;padding:4px 0 3px 11px;color:#333;letter-spacing:0}
|
|
||||||
#smart_editor2 .se2_accessibility .se2_close{position:absolute;top:10px;right:12px;width:13px;height:12px;background:url("../img/ko_KR/btn_set.png?130306") -155px -5px no-repeat}
|
|
||||||
#smart_editor2 .se2_accessibility .box_help{padding:0 2px;margin-top:8px;background:url("../img/bg_help.gif") 0 100% no-repeat}
|
|
||||||
#smart_editor2 .se2_accessibility .box_help div{overflow:hidden;padding:20px 21px 24px;border-top:1px solid #d0d0d0;color:#333}
|
|
||||||
#smart_editor2 .se2_accessibility .box_help strong{display:block;margin-bottom:2px}
|
|
||||||
#smart_editor2 .se2_accessibility .box_help p{margin-bottom:28px;line-height:1.5}
|
|
||||||
#smart_editor2 .se2_accessibility .box_help ul{width:150%;margin-top:10px}
|
|
||||||
#smart_editor2 .se2_accessibility .box_help li{position:relative;float:left;width:252px;padding:5px 0 5px 9px;margin-right:40px;background:url("../img/ko_KR/btn_set.png?130306") -475px -51px no-repeat;border-right:1px solid #f0f0f0;*zoom:1;line-height:1}
|
|
||||||
#smart_editor2 .se2_accessibility .box_help li span{position:absolute;top:4px;left:138px;line-height:1.2}
|
|
||||||
#smart_editor2 .se2_accessibility .se2_btns{padding:9px 0 10px;text-align:center}
|
|
||||||
#smart_editor2 .se2_accessibility .se2_btns .se2_close2{width:39px;height:24px;background:url("../img/ko_KR/btn_set.png?130306") -235px -120px no-repeat}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
/* NHN Web Standardization Team (http://html.nhndesign.com/) HHJ 090226 */
|
|
||||||
/* COMMON */
|
|
||||||
.se2_outputarea,.se2_outputarea th,.se2_outputarea td{margin:0;padding:0;color:#666;font-size:12px;font-family:'돋움',Dotum,'굴림',Gulim,Helvetica,Sans-serif;line-height:1.5}
|
|
||||||
.se2_outputarea p{margin:0;padding:0}
|
|
||||||
.se2_outputarea a:hover{text-decoration:underline}
|
|
||||||
.se2_outputarea a:link{color:#0000ff}
|
|
||||||
.se2_outputarea ul{margin:0 0 0 40px;padding:0}
|
|
||||||
.se2_outputarea ul li{margin:0;list-style-type:disc;padding:0}
|
|
||||||
.se2_outputarea ul ul li{list-style-type:circle}
|
|
||||||
.se2_outputarea ul ul ul li{list-style-type:square}
|
|
||||||
.se2_outputarea img,.se2_outputarea fieldset{border:0}
|
|
||||||
|
Before Width: | Height: | Size: 115 B |
|
Before Width: | Height: | Size: 526 B |
|
Before Width: | Height: | Size: 331 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 159 B |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 103 B |
|
Before Width: | Height: | Size: 43 B |
|
Before Width: | Height: | Size: 56 B |
|
Before Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 104 B |
|
Before Width: | Height: | Size: 139 B |
|
Before Width: | Height: | Size: 155 B |
|
Before Width: | Height: | Size: 270 B |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
|
@ -1,134 +0,0 @@
|
||||||
if(typeof window.nhn=='undefined') window.nhn = {};
|
|
||||||
if (!nhn.husky) nhn.husky = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileOverview This file contains application creation helper function, which would load up an HTML(Skin) file and then execute a specified create function.
|
|
||||||
* @name HuskyEZCreator.js
|
|
||||||
*/
|
|
||||||
nhn.husky.EZCreator = new (function(){
|
|
||||||
this.nBlockerCount = 0;
|
|
||||||
|
|
||||||
this.createInIFrame = function(htOptions){
|
|
||||||
if(arguments.length == 1){
|
|
||||||
var oAppRef = htOptions.oAppRef;
|
|
||||||
var elPlaceHolder = htOptions.elPlaceHolder;
|
|
||||||
var sSkinURI = htOptions.sSkinURI;
|
|
||||||
var fCreator = htOptions.fCreator;
|
|
||||||
var fOnAppLoad = htOptions.fOnAppLoad;
|
|
||||||
var bUseBlocker = htOptions.bUseBlocker;
|
|
||||||
var htParams = htOptions.htParams || null;
|
|
||||||
}else{
|
|
||||||
// for backward compatibility only
|
|
||||||
var oAppRef = arguments[0];
|
|
||||||
var elPlaceHolder = arguments[1];
|
|
||||||
var sSkinURI = arguments[2];
|
|
||||||
var fCreator = arguments[3];
|
|
||||||
var fOnAppLoad = arguments[4];
|
|
||||||
var bUseBlocker = arguments[5];
|
|
||||||
var htParams = arguments[6];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(bUseBlocker) nhn.husky.EZCreator.showBlocker();
|
|
||||||
|
|
||||||
var attachEvent = function(elNode, sEvent, fHandler){
|
|
||||||
if(elNode.addEventListener){
|
|
||||||
elNode.addEventListener(sEvent, fHandler, false);
|
|
||||||
}else{
|
|
||||||
elNode.attachEvent("on"+sEvent, fHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!elPlaceHolder){
|
|
||||||
alert("Placeholder is required!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof(elPlaceHolder) != "object")
|
|
||||||
elPlaceHolder = document.getElementById(elPlaceHolder);
|
|
||||||
|
|
||||||
var elIFrame, nEditorWidth, nEditorHeight;
|
|
||||||
|
|
||||||
|
|
||||||
try{
|
|
||||||
elIFrame = document.createElement("<IFRAME frameborder=0 scrolling=no>");
|
|
||||||
}catch(e){
|
|
||||||
elIFrame = document.createElement("IFRAME");
|
|
||||||
elIFrame.setAttribute("frameborder", "0");
|
|
||||||
elIFrame.setAttribute("scrolling", "no");
|
|
||||||
}
|
|
||||||
|
|
||||||
elIFrame.style.width = "1px";
|
|
||||||
elIFrame.style.height = "1px";
|
|
||||||
elPlaceHolder.parentNode.insertBefore(elIFrame, elPlaceHolder.nextSibling);
|
|
||||||
|
|
||||||
attachEvent(elIFrame, "load", function(){
|
|
||||||
fCreator = elIFrame.contentWindow[fCreator] || elIFrame.contentWindow.createSEditor2;
|
|
||||||
|
|
||||||
// top.document.title = ((new Date())-window.STime);
|
|
||||||
// window.STime = new Date();
|
|
||||||
|
|
||||||
try{
|
|
||||||
|
|
||||||
nEditorWidth = elIFrame.contentWindow.document.body.scrollWidth || "500px";
|
|
||||||
nEditorHeight = elIFrame.contentWindow.document.body.scrollHeight + 12;
|
|
||||||
elIFrame.style.width = "100%";
|
|
||||||
elIFrame.style.height = nEditorHeight+ "px";
|
|
||||||
elIFrame.contentWindow.document.body.style.margin = "0";
|
|
||||||
}catch(e){
|
|
||||||
nhn.husky.EZCreator.hideBlocker(true);
|
|
||||||
elIFrame.style.border = "5px solid red";
|
|
||||||
elIFrame.style.width = "500px";
|
|
||||||
elIFrame.style.height = "500px";
|
|
||||||
alert("Failed to access "+sSkinURI);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oApp = fCreator(elPlaceHolder, htParams); // oEditor
|
|
||||||
|
|
||||||
|
|
||||||
oApp.elPlaceHolder = elPlaceHolder;
|
|
||||||
|
|
||||||
oAppRef[oAppRef.length] = oApp;
|
|
||||||
if(!oAppRef.getById) oAppRef.getById = {};
|
|
||||||
|
|
||||||
if(elPlaceHolder.id) oAppRef.getById[elPlaceHolder.id] = oApp;
|
|
||||||
|
|
||||||
oApp.run({fnOnAppReady:fOnAppLoad});
|
|
||||||
|
|
||||||
// top.document.title += ", "+((new Date())-window.STime);
|
|
||||||
nhn.husky.EZCreator.hideBlocker();
|
|
||||||
});
|
|
||||||
// window.STime = new Date();
|
|
||||||
elIFrame.src = sSkinURI;
|
|
||||||
this.elIFrame = elIFrame;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.showBlocker = function(){
|
|
||||||
if(this.nBlockerCount<1){
|
|
||||||
var elBlocker = document.createElement("DIV");
|
|
||||||
elBlocker.style.position = "absolute";
|
|
||||||
elBlocker.style.top = 0;
|
|
||||||
elBlocker.style.left = 0;
|
|
||||||
elBlocker.style.backgroundColor = "#FFFFFF";
|
|
||||||
elBlocker.style.width = "100%";
|
|
||||||
|
|
||||||
document.body.appendChild(elBlocker);
|
|
||||||
|
|
||||||
nhn.husky.EZCreator.elBlocker = elBlocker;
|
|
||||||
}
|
|
||||||
|
|
||||||
nhn.husky.EZCreator.elBlocker.style.height = Math.max(document.body.scrollHeight, document.body.clientHeight)+"px";
|
|
||||||
|
|
||||||
this.nBlockerCount++;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.hideBlocker = function(bForce){
|
|
||||||
if(!bForce){
|
|
||||||
if(--this.nBlockerCount > 0) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nBlockerCount = 0;
|
|
||||||
|
|
||||||
if(nhn.husky.EZCreator.elBlocker) nhn.husky.EZCreator.elBlocker.style.display = "none";
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
function createSEditor2(elIRField, htParams, elSeAppContainer){
|
|
||||||
if(!window.$Jindo){
|
|
||||||
parent.document.body.innerHTML="진도 프레임웍이 필요합니다.<br>\n<a href='http://dev.naver.com/projects/jindo/download'>http://dev.naver.com/projects/jindo/download</a>에서 Jindo 1.5.3 버전의 jindo.min.js를 다운로드 받아 /js 폴더에 복사 해 주세요.\n(아직 Jindo 2 는 지원하지 않습니다.)";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var elAppContainer = (elSeAppContainer || jindo.$("smart_editor2"));
|
|
||||||
var elEditingArea = jindo.$$.getSingle("DIV.husky_seditor_editing_area_container", elAppContainer);
|
|
||||||
var oWYSIWYGIFrame = jindo.$$.getSingle("IFRAME.se2_input_wysiwyg", elEditingArea);
|
|
||||||
var oIRTextarea = elIRField?elIRField:jindo.$$.getSingle("TEXTAREA.blind", elEditingArea);
|
|
||||||
var oHTMLSrc = jindo.$$.getSingle("TEXTAREA.se2_input_htmlsrc", elEditingArea);
|
|
||||||
var oTextArea = jindo.$$.getSingle("TEXTAREA.se2_input_text", elEditingArea);
|
|
||||||
|
|
||||||
if(!htParams){
|
|
||||||
htParams = {};
|
|
||||||
htParams.fOnBeforeUnload = null;
|
|
||||||
}
|
|
||||||
htParams.elAppContainer = elAppContainer; // 에디터 UI 최상위 element 셋팅
|
|
||||||
htParams.oNavigator = jindo.$Agent().navigator(); // navigator 객체 셋팅
|
|
||||||
|
|
||||||
var oEditor = new nhn.husky.HuskyCore(htParams);
|
|
||||||
oEditor.registerPlugin(new nhn.husky.CorePlugin(htParams?htParams.fOnAppLoad:null));
|
|
||||||
oEditor.registerPlugin(new nhn.husky.StringConverterManager());
|
|
||||||
|
|
||||||
var htDimension = {
|
|
||||||
nMinHeight:205,
|
|
||||||
nMinWidth:parseInt(elIRField.style.minWidth, 10)||570,
|
|
||||||
nHeight:elIRField.style.height||elIRField.offsetHeight,
|
|
||||||
nWidth:elIRField.style.width||elIRField.offsetWidth
|
|
||||||
};
|
|
||||||
|
|
||||||
var htConversionMode = {
|
|
||||||
bUseVerticalResizer : htParams.bUseVerticalResizer,
|
|
||||||
bUseModeChanger : htParams.bUseModeChanger
|
|
||||||
};
|
|
||||||
|
|
||||||
var aAdditionalFontList = htParams.aAdditionalFontList;
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_EditingAreaManager("WYSIWYG", oIRTextarea, htDimension, htParams.fOnBeforeUnload, elAppContainer));
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_EditingArea_WYSIWYG(oWYSIWYGIFrame)); // Tab Editor 모드
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_EditingArea_HTMLSrc(oHTMLSrc)); // Tab HTML 모드
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_EditingArea_TEXT(oTextArea)); // Tab Text 모드
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_EditingModeChanger(elAppContainer, htConversionMode)); // 모드간 변경(Editor, HTML, Text)
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_PasteHandler()); // WYSIWYG Paste Handler
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.HuskyRangeManager(oWYSIWYGIFrame));
|
|
||||||
oEditor.registerPlugin(new nhn.husky.Utils());
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_UtilPlugin());
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_WYSIWYGStyler());
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_Toolbar(elAppContainer));
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.Hotkey()); // 단축키
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_EditingAreaVerticalResizer(elAppContainer, htConversionMode)); // 편집영역 리사이즈
|
|
||||||
oEditor.registerPlugin(new nhn.husky.DialogLayerManager());
|
|
||||||
oEditor.registerPlugin(new nhn.husky.ActiveLayerManager());
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_WYSIWYGStyleGetter()); // 커서 위치 스타일 정보 가져오기
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_WYSIWYGEnterKey("P")); // 엔터 시 처리, 현재는 P로 처리
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_ColorPalette(elAppContainer)); // 색상 팔레트
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_FontColor(elAppContainer)); // 글자색
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_BGColor(elAppContainer)); // 글자배경색
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_FontNameWithLayerUI(elAppContainer, aAdditionalFontList)); // 글꼴종류
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_FontSizeWithLayerUI(elAppContainer)); // 글꼴크기
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_LineStyler());
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_ExecCommand(oWYSIWYGIFrame));
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_LineHeightWithLayerUI(elAppContainer)); // 줄간격
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_Quote(elAppContainer)); // 인용구
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_Hyperlink(elAppContainer)); // 링크
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_SCharacter(elAppContainer)); // 특수문자
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_FindReplacePlugin(elAppContainer)); // 찾기/바꾸기
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_TableCreator(elAppContainer)); // 테이블 생성
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_TableEditor(elAppContainer)); // 테이블 편집
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_TableBlockStyler(elAppContainer)); // 테이블 스타일
|
|
||||||
if(nhn.husky.SE2M_AttachQuickPhoto){
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_AttachQuickPhoto(elAppContainer)); // 사진
|
|
||||||
}
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.MessageManager(oMessageMap));
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_QuickEditor_Common(elAppContainer)); // 퀵에디터 공통(표, 이미지)
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2B_CSSLoader()); // CSS lazy load
|
|
||||||
if(window.frameElement){
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_OuterIFrameControl(elAppContainer, 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE_ToolbarToggler(elAppContainer, htParams.bUseToolbar));
|
|
||||||
oEditor.registerPlugin(new nhn.husky.SE2M_Accessibility(elAppContainer)); // 에디터내의 웹접근성 관련 기능모음 플러그인
|
|
||||||
|
|
||||||
return oEditor;
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* Smart Editor 2 Configuration : This setting must be changed by service
|
|
||||||
*/
|
|
||||||
window.nhn = window.nhn || {};
|
|
||||||
nhn.husky = nhn.husky || {};
|
|
||||||
nhn.husky.SE2M_Configuration = nhn.husky.SE2M_Configuration || {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CSS LazyLoad를 위한 경로
|
|
||||||
*/
|
|
||||||
nhn.husky.SE2M_Configuration.SE2B_CSSLoader = {
|
|
||||||
sCSSBaseURI : "css"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 편집영역 설정
|
|
||||||
*/
|
|
||||||
nhn.husky.SE2M_Configuration.SE_EditingAreaManager = {
|
|
||||||
sCSSBaseURI : "css", // smart_editor2_inputarea.html 파일의 상대경로
|
|
||||||
sBlankPageURL : "smart_editor2_inputarea.html",
|
|
||||||
sBlankPageURL_EmulateIE7 : "smart_editor2_inputarea_ie8.html",
|
|
||||||
aAddtionalEmulateIE7 : [] // IE8 default 사용, IE9 ~ 선택적 사용
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [웹접근성]
|
|
||||||
* 단축키 ALT+, ALT+. 을 이용하여 스마트에디터 영역의 이전/이후 요소로 이동할 수 있다.
|
|
||||||
* sBeforeElementId : 스마트에디터 영역 이전 요소의 id
|
|
||||||
* sNextElementId : 스마트에디터 영역 이후 요소의 id
|
|
||||||
*
|
|
||||||
* 스마트에디터 영역 이외의 제목 영역 (예:스마트에디터가 적용된 블로그 쓰기 페이지에서의 제목 영역) 에 해당하는 엘리먼트에서 Tab키를 누르면 에디팅 영역으로 포커스를 이동시킬 수 있다.
|
|
||||||
* sTitleElementId : 제목에 해당하는 input 요소의 id.
|
|
||||||
*/
|
|
||||||
nhn.husky.SE2M_Configuration.SE2M_Accessibility = {
|
|
||||||
sBeforeElementId : '',
|
|
||||||
sNextElementId : '',
|
|
||||||
sTitleElementId : ''
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 링크 기능 옵션
|
|
||||||
*/
|
|
||||||
nhn.husky.SE2M_Configuration.SE2M_Hyperlink = {
|
|
||||||
bAutolink : true // 자동링크기능 사용여부(기본값:true)
|
|
||||||
};
|
|
||||||
|
|
||||||
nhn.husky.SE2M_Configuration.Quote = {
|
|
||||||
sImageBaseURL : 'http://static.se2.naver.com/static/img'
|
|
||||||
};
|
|
||||||
nhn.husky.SE2M_Configuration.SE2M_ColorPalette = {
|
|
||||||
bAddRecentColorFromDefault : false
|
|
||||||
};
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
SmartEditor Basic 2.0 릴리즈 패키지
|
|
||||||
|
|
||||||
SmartEdtitor™는 Javascript로 구현된 웹 기반의 WYSIWYG 에디터입니다. SmartEdtitor™는 WYSIWYG 모드 및 HTML 편집 모드와 TEXT 모드를 제공하고, 자유로운 폰트 크기 설정 기능, 줄 간격 설정 기능, 단어 찾기/바꾸기 기능 등 편집에 필요한 다양한 기능을 제공하므로 사용자들은 SmartEdtitor™를 사용하여 쉽고 편리하게 원하는 형태의 글을 작성할 수 있습니다.
|
|
||||||
또한, SmartEdtitor™의 구조는 기능을 쉽게 추가할 수 있는 플러그인 구조로 되어 있어 정해진 규칙에 따라 플러그인을 만들기만 하면 됩니다.
|
|
||||||
|
|
||||||
현재 SmartEdtitor™는 네이버, 한게임 등 NHN의 주요 서비스에 적용되어 있습니다.
|
|
||||||
지원하는 브라우저 환경은 아래와 같으며 지속적으로 지원 대상 브라우저를 확장할 예정입니다.
|
|
||||||
|
|
||||||
* 지원하는 브라우저
|
|
||||||
Internet Explorer 7.0+ / 10.0-
|
|
||||||
FireFox 3.5+
|
|
||||||
Safari 4.0+
|
|
||||||
Chrome 4.0+
|
|
||||||
|
|
||||||
또한 지속적인 기능 추가를 통해 편리하고 강력한 에디터로 거듭날 것입니다.
|
|
||||||
|
|
||||||
라이센스 : LGPL v2
|
|
||||||
홈페이지 : http://dev.naver.com/projects/smarteditor
|
|
||||||
|
|
||||||
===================================================================================
|
|
||||||
|
|
||||||
릴리즈 패키지에 포함된 파일은 아래와 같습니다.
|
|
||||||
/css : 에디터에서 사용하는 css 파일
|
|
||||||
/img : 에디터에서 사용하는 이미지 파일
|
|
||||||
/js : 에디터를 적용할 때 사용하는 JS 파일
|
|
||||||
/photo_uploader : 사진 퀵 업로더 팝업 UI를 구성하는 파일
|
|
||||||
readme.txt : 간략한 설명
|
|
||||||
release_notes.txt : 릴리즈 노트
|
|
||||||
sample.php : SmartEditor2.html을 이용해 편집한 내용을 서버에서 받는 php 예제
|
|
||||||
smart_editor2_inputarea.html : 에디터의 편집 영역을 나타내는 HTML로 에디터를 적용할 때 반드시 필요
|
|
||||||
smart_editor2_inputarea_ie8.html : smart_editor2_inputarea.html와 동일한 기능이나 사용자의 브라우저 Internet Explorer 8.x 이상인 경우에 사용
|
|
||||||
SmartEditor2.html : 에디터 데모 페이지. 에디터 적용 시에도 참고 할 수 있다.
|
|
||||||
SmartEditor2Skin.html : 에디터를 적용한 페이지에서 로드하는 에디터의 스킨 HTML 파일로 에디터에서 사용하는 JS 파일과 css 파일을 링크하며 에디터의 마크업을 가지고 있다. SmartEditor2.html 에서도 확인할 수 있다.
|
|
||||||
src_include.txt : 자바스크립트 플러그인 소스를 직접 수정하고자 할 경우 참고할 수 있는 파일
|
|
||||||
|
|
||||||
===================================================================================
|
|
||||||
|
|
||||||
사용 중 불편한 점이 있거나 버그를 발견하는 경우 SmartEdtitor™ 프로젝트의 이슈에 올려 주세요~~~
|
|
||||||
http://dev.naver.com/projects/smarteditor/issue
|
|
||||||
여기입니다! :)
|
|
||||||
|
|
@ -1,255 +0,0 @@
|
||||||
==============================================================================================
|
|
||||||
2.3.10_임시
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- 크롬 > 밑줄 선택 글작성하다 취소선 선택하고 밑줄 선택을 취소한 경우 툴바에 반영되지 않는 문제
|
|
||||||
- 굵게/밑줄/기울림/취소선이 있는 상태에서 엔터치고 폰트크기 수정하면 이전 폰트크기로 줄간격이 유지되는 문제
|
|
||||||
- 외부프로그램 테이블 복사 붙여넣기 관련 오류 수정
|
|
||||||
- IE8이하 > 글자크기 지정 후 엔터를 치면 커서위치가 위로 올라감
|
|
||||||
- IE9이상 > 글꼴 효과를 미리 지정 한 후에 텍스트 입력 시, 색상 변경은 적용되나 굵게 기울임 밑줄 취소선 등의 효과는 적용안됨
|
|
||||||
- [FF]밑줄 선택> 내용입력 후 엔터>밑줄 취소 후 내용 입력>마우스로 커서 클릭 후 내용 계속 입력 시 밑줄이 있는 글로 노출됨
|
|
||||||
- [FF] 메모장에서 작성한 내용을 붙여넣기 후 엔터 > 내용입력 > 엔터 했을 때 줄바꿈이 되지 않는 현상
|
|
||||||
- HTML5 > 글자를 선택하여 폰트크기 지정시 굵게/밑줄/기울림/취소선이 있으면 이전에 적용한 폰트크기 기준으로 줄간격이 유지되는 문제
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- IE에서 자동으로 공백이 삽입되는 문제
|
|
||||||
- MacOS > 사파리 > 외부프로그램 테이블 붙여넣기 개선
|
|
||||||
|
|
||||||
3. 보안 패치
|
|
||||||
- 사진첨부 샘플의 null byte injection 취약점 보완
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.10
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- 크롬 > 브라우저 확대축소시 폰트크기가 잘못 나오는 이슈
|
|
||||||
- IE > 표삽입>임의로 두개 칸 선택하여 셀 병합>행삽입 클릭 시 JS 오류 발생
|
|
||||||
- IE11 > 호환성 보기를 설정하지 않을 경우 글꼴목록이 선택되지 않는 문제 수정
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- 외부프로그램 테이블 복사 붙여넣기 개선
|
|
||||||
- 입력창 조절 안내 레이어를 주석처리하면 스크립트 오류 발생
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.9
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- 파이어폭스에서 에디팅시 스타일깨짐 등 오작동
|
|
||||||
- Chrome > 찾기/바꾸기 > 모두바꾸기 버튼 클릭시 찾을단어가 지워지지 않고 남아있음
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- 링크 > 자동링크 설정/해제 옵션 추가
|
|
||||||
- [IE11] WYSIWYG 모드와 HTML 모드를 오갈 때마다 문서의 마지막에 비정상적인 <BR>이 첨가됩니다.
|
|
||||||
- [웹접근성] 빠져나가기 단축키 기능 개선
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.8
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- 테이블 내부 영역을 Shift + 클릭으로 선택 후 정렬하고 HTML 로 전환하면 더미 P 태그가 생성되는 문제 수정
|
|
||||||
- 테이블 내부 영역 선택 혹은 에디터 내용 전체 선택 후 정렬 시 동작안함
|
|
||||||
- [IE10, IE11] 표의 셀을 드래그했을 때 블럭 지정이 되지 않는 현상
|
|
||||||
- HTML 모드 변환시 태그 자동 정렬에 의한 버그
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- [MacOS 대응] 폰트변경이슈
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.7
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- 에디터에 표 생성 후 일부 셀 선택하여 배경색 설정> 배경색 설정된 셀 선택 후 셀 삽입 시 색상이 삽입되지 않습니다.
|
|
||||||
- [IE9특정] 글 작성 중 번호매기기 또는 글머리 적용 후 정렬방식을 변경하면 엔터키 누를 시 커서가 한줄 떨어져서 노출됩니다.
|
|
||||||
- [IE10] 표 생성 후 표 드래그 시 셀의 너비/높이가 늘어나는 현상
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- IE11 대응
|
|
||||||
- 특수기호 삽입시 커서 위치가 뒤쪽으로 나오도록 개선
|
|
||||||
- 커서에 활성화된 글꼴 확인 로직 개선
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.6
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- 글 작성 후 번호매기기 적용하고 엔터키 수행하는 경우 JS 오류가 발생하는 현상 수정
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.5
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 기능 개선
|
|
||||||
- 줄간격 설정 시 값을 직접 입력하는 경우 줄간격의 최소값 적용
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.4
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- [IE9/10] pre 태그의 바로 다음에 \n이 존재하는 경우 개행이 되지 않는 이슈 해결
|
|
||||||
- 입력창 크기 조절바 사용 여부 오류 해결
|
|
||||||
- 사진 퀵 업로더 모듈 오타 수정 ($newPath -> $new_path)
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- 글꼴 목록에 글꼴 종류 추가하기 기능 (SmartEditor2.html 참조)
|
|
||||||
- 사진 퀵 업로더 모듈에 이미지 파일 확장자 체크 추가
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.3
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- IE9 에서 템플릿을 적용한 표 생성 후 일부의 셀을 드래그하는 경우 셀의 높이가 늘어나는 현상 수정
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- MAC OS의 CMD 키로 Ctrl 단축키 기능 적용 확장
|
|
||||||
- 기본 글꼴 종류 추가 (Courier New, 나눔고딕 코딩)
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.1
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 기능 개선
|
|
||||||
- [웹접근성] 글쓰기 영역의 iframe의 title속성에 단축키 설명 제공
|
|
||||||
- [웹접근성] 제목 input영역에서 제목 입력 후 TAB하면 스마트에디터 편집 영역으로 포커스 이동하는 기능 추가
|
|
||||||
- [웹접근성] 툴바 영역의 이전/다음 아이템 이동을 TAB, SHIFT+TAB으로 이동할 수 있도록 추가
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.3.0
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 기능 개선
|
|
||||||
- [웹접근성] 키보드로만 메뉴를 이동할 수 있도록 단축키 적용
|
|
||||||
- [웹접근성] 웹접근성 도움말 제공
|
|
||||||
- 편집모드와 사이즈 조절바 사용 옵션 추가
|
|
||||||
- 사진 첨부 팝업 데모 파일 구조 개선
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.2.1
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- 사진 퀵 업로더 추가 시, 가이드 대로 수행했을 때 사진 첨부가 2번 실행되는 문제 해결
|
|
||||||
: loader-min.js 파일 내에 사진 퀵 업로더 소스가 포함되어 있던 부분 제거하여 소스 분리
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- 툴바의 기능 제거/순서 변경이 쉽도록 마크업 구조 개선
|
|
||||||
※ 툴바의 기능 제거/순서 변경은 가이드 문서를 참고하세요.
|
|
||||||
|
|
||||||
|
|
||||||
3. 폴더/파일 변경
|
|
||||||
- /js_src 폴더 제거
|
|
||||||
- /js/smarteditor2.js 추가
|
|
||||||
: /js_src 폴더를 /js/smarteditor2.js 로 대체했습니다.
|
|
||||||
: /js_src 폴더 구조에서 사용자가 소스를 검색하여 수정하기 어렵던 부분을 보완하기 위하여
|
|
||||||
: /js_src 폴더 내의 플러그인 소스를 통합한 /js/smarteditor2.js 를 추가했습니다.
|
|
||||||
- /js/loader-min.js 제거
|
|
||||||
- /js/smarteditor2.min.js 추가
|
|
||||||
: /js/loader-min.js 파일을 /js/smarteditor2.min.js로 대체했습니다.
|
|
||||||
- /quick_photo_uploader 폴더 추가
|
|
||||||
- /popup 폴더 이동
|
|
||||||
: /popup 폴더 - 사진 퀵 업로더의 팝업과 관련된 소스
|
|
||||||
: /plugin 폴더 - 사진 퀵 업로더의 사진첨부를 처리하는 플러그인 js 소스
|
|
||||||
- /img/ko_KR 폴더 추가
|
|
||||||
: 이후의 다국어 버전 지원을 위하여 이미지 폴더 내 디렉토리가 추가되었습니다.
|
|
||||||
: 언어 별 구분이 필요없는 이미지는 /img 바로 하위에 두었고,
|
|
||||||
: 언어 별로 구분되어야 하는 이미지는 /img/ko_KR 과 같이 언어 별 디렉토리로 구분했습니다.
|
|
||||||
: 버전 업그레이드를 하는 경우 이미지 경로가 변경된 점에 주의하시기 바랍니다.
|
|
||||||
- /js/SE2B_Configuration.js 제거
|
|
||||||
- /js/SE2B_Configuration_Service.js 추가
|
|
||||||
- /js/SE2B_Configuration_General.js 추가
|
|
||||||
: /js/SE2B_Configuration_Service.js 와 /js/SE2B_Configuration_General.js로 파일 분리했습니다.
|
|
||||||
: /js/SE2B_Configuration_Service.js 는 적용을 할 때 사용자가 변경할 가능성이 높은 플러그인 설정을 갖고,
|
|
||||||
: /js/SE2B_Configuration_General.js 는 서비스에 적용할 때 변경할 가능성이 거의 없는 설정입니다.
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.1.3
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- [Chrome] 보기 페이지에 글자색이 설정되어 있는 경우 글 작성 시 내용에 적용한 글자색으로 노출되지 않는 문제 해결
|
|
||||||
- 엔터 처리가 <BR>로 설정된 경우에도 텍스트 모드에서 모드변경 혹은 글 저장할 때 개행이 <P>로 표시되는 문제 해결
|
|
||||||
- [IE9] 각주 삽입 시, 하단으로 떨어지는 이슈 해결
|
|
||||||
- [Chrome] 인용구 밖에 글머리기호/번호매기기가 있을 때 인용구 안에서 글머리기호/번호매기기 시 내용이 인용구 밖으로 나가는 문제 해결
|
|
||||||
- [IE] IE에서 특정 블로그 글을 복사하여 붙여넣기 했을 때 개행이 제거되는 문제 해결
|
|
||||||
- 사진을 드래그해서 사이즈를 변경한 후 저장 혹은 HTML모드로 변경하면, 사진 사이즈가 원복되는 현상 해결
|
|
||||||
- [Chrome/FF/Safari] 스크롤바가 생성되도록 문자입력 후 엔터 클릭하지 않은 상태에서 이미지 하나 삽입 시 이미지에 포커싱이 놓이지 않는 문제 해결
|
|
||||||
- [IE9 표준] 사진을 스크롤로 일부 가린 상태에서 재편집하여 적용했을 때 계속 가려진 상태인 문제 해결
|
|
||||||
- FF에서 사진을 여러장 첨부 시 스크롤이 가장 마지막 추가한 사진으로 내려가지 않음 해결
|
|
||||||
- 호환 모드를 제거하고 사진 첨부 시 에디팅 영역의 커서 주위에 <sub><sup> 태그가 붙어서 글자가 매우 작게 되는 현상 해결
|
|
||||||
- [IE9] 에디터에 각주 연속으로 입력 시 커서가 각주사이로 이동되는 현상 해결
|
|
||||||
- 글꼴색/글꼴배경색 더보기에서 글꼴색 선택>다시 다른 색상 선택 후 처음 선택되었던 색상 선택 시 처음 선택색상이 원래 자리에서 삭제되지 않는 현상 해결
|
|
||||||
- 제공하지 않는 기능인 이모티콘 플러그인 소스 제거
|
|
||||||
- 플러그인 태그 코드 추가 시 <li> 태그와 <button> 태그 사이에 개행이 있으면 이벤트가 등록되지 않는 현상 해결
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- 표 삽입 시 본문 작성 영역 안에 너비 100%로 생성되도록 개선
|
|
||||||
- 호환모드 설정이 설정 파일 정보에 따라 처리되도록 변경
|
|
||||||
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.1.2
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 버그 수정
|
|
||||||
- [IE9]Shift+Enter를 여러번 하고 글의 중간의 마지막 글자 다음에서 엔터를 쳤을 때 엔터 위치가 달라지는 현상 수정
|
|
||||||
- [IE9]메모장에서 붙여 넣기 후 내용 중간의 마지막 글자 다음에서 엔터를 쳤을 때 엔터 위치가 달라지는 현상 수정
|
|
||||||
- 한 줄 입력 후 색상을 적용하고 내용 중간에서 엔터를 쳤을 때 적용되었던 색상이 풀리던 현상 수정
|
|
||||||
- 글꼴 레이어를 열었을 때, 샘플 텍스트가 잘못 나오던 현상 수정
|
|
||||||
- 인용구를 14개까지 중첩하고, 15개부터 경고 창이 나오도록 수정
|
|
||||||
|
|
||||||
2. 기능 개선
|
|
||||||
- 찾기/바꾸기 레이어를 닫았다가 다시 열 때, [바꿀 단어] 입력란이 초기화 되도록 개선
|
|
||||||
- 찾기/바꾸기 레이어 오픈 시 툴바 버튼 inactive 처리
|
|
||||||
- 표 추가 레이어의 테이블 색상, 배경 색상의 기본 값을 SmartEditor2Skin.html에서 변경할 수 있도록 함
|
|
||||||
※주의 : 기존의 html파일에 덮어 씌우게 되면 기본 배경 색상이 다르게 표시됨
|
|
||||||
따라서 반드시 새로 업데이트 된 html 파일을 사용하기를 권장
|
|
||||||
임의로 수정하려면 위 파일의 아래 부분의 value를 아래와 같이 변경해야 함
|
|
||||||
<input id="se2_b_color" name="" type="text" maxlength="7" value="#cccccc" class="input_ty3">
|
|
||||||
<input id="se2_cellbg" name="" type="text" maxlength="7" value="#ffffff" class="input_ty3">
|
|
||||||
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.1.1
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 기능 추가
|
|
||||||
- 에디터 로딩 완료 시점에 실행되는 함수 (fOnAppLoad) 정의
|
|
||||||
|
|
||||||
2. 버그 수정
|
|
||||||
- 에디터 초기 Width에 100%가 설정될 수 있도록 수정, minWidth 설정 추가
|
|
||||||
- 마크업에서 나눔 글꼴을 제외하면 JS 에러가 나는 문제 수정
|
|
||||||
- [IE9] 글자 색상 적용 후 내용 중간에서 계속 Enter할 때 Enter가 되지 않는 오류 수정
|
|
||||||
- [Chrome/Safari] 표 간단편집기 위에서 text를 drag하면 JS 에러가 발생하는 문제 수정
|
|
||||||
|
|
||||||
3. 기능 개선
|
|
||||||
- 사진 퀵 업로더 : 쉽게 사용할 수 있도록 소스 수정 및 예제 보강
|
|
||||||
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.1.0
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 기능 추가
|
|
||||||
- 사진 퀵 업로더 : 사진 첨부 팝업 UI 제공 (HTML5 지원)
|
|
||||||
- 에디터 본문에 글 작성 후 창을 닫을 때 발생하는 alert 메세지를 사용자가 설정할 수 있도록 옵션을 추가함
|
|
||||||
- Jindo 모듈을 패키지에 포함하도록 빌드를 수정함
|
|
||||||
- document.domain을 제거함
|
|
||||||
- 에디터 초기 Width를 설정할 수 있도록 수정함
|
|
||||||
- 툴바의 접힘/펼침 기능을 제공하는 SE_ToolbarToggler 플러그인 추가함
|
|
||||||
|
|
||||||
2. 버그 수정
|
|
||||||
- 에디터 리사이즈 시 북마크 태그가 본문에 추가되는 이슈 확인 및 수정함
|
|
||||||
|
|
||||||
|
|
||||||
==============================================================================================
|
|
||||||
2.0.0
|
|
||||||
----------------------------------------------------------------------------------------------
|
|
||||||
1. 기능 강화
|
|
||||||
- 글꼴과 글자 크기
|
|
||||||
: 기존의 Selectbox 형태의 글꼴 목록을 깔끔한 디자인의 레이어로 제공한다.
|
|
||||||
- 글자색과 글자 배경색
|
|
||||||
: 기존의 기본 색상표 이외에 다양한 색상을 선택할 수 있는 컬러 팔레트를 확장 지원한다.
|
|
||||||
- 줄간격
|
|
||||||
: 기존의 Selectbox 형태의 줄간격 목록을 깔끔한 디자인의 레이어로 제공한다.
|
|
||||||
또한, 줄간격을 직접 설정할 수 있도록 직접 입력 기능도 확장 지원한다.
|
|
||||||
- 인용구
|
|
||||||
: 기존의 7가지에서 10가지로 인용구 디자인을 확장 지원한다.
|
|
||||||
- 표
|
|
||||||
: 표 생성 시 기존의 테두리 색상과 두께를 설정할 수 있는 기능 이외에 테두리 스타일을 설정할 수 있는 기능을 확장 지원한다.
|
|
||||||
또한, 표 템플릿을 제공하여 보다 쉽게 표 스타일을 생성할 수 있도록 하였다.
|
|
||||||
|
|
||||||
2. 기능 추가
|
|
||||||
- 표 간단편집기
|
|
||||||
: 표 생성 후 스타일을 편집할 수 있도록 표 편집 기능을 추가 제공한다.
|
|
||||||
- TEXT 모드
|
|
||||||
: WYSIWYG와 HTML 모드 이외에 TEXT 모드를 제공하여 텍스트만으로 본문의 내용을 작성할 수 있도록 편집 모드를 추가 제공한다.
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="ko" xml:lang="ko">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<title>Smart Editor™ WYSIWYG Mode</title>
|
|
||||||
<link href="css/smart_editor2_in.css" rel="stylesheet" type="text/css">
|
|
||||||
</head>
|
|
||||||
<body class="smartOutput se2_inputarea">
|
|
||||||
<p>
|
|
||||||
<b><u>에디터 내용:</u></b>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div style="width:736px;">
|
|
||||||
<?php
|
|
||||||
$postMessage = $_POST["ir1"];
|
|
||||||
echo $postMessage;
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
<p>
|
|
||||||
<b><span style="color:#FF0000">주의: </span>sample.php는 샘플 파일로 정상 동작하지 않을 수 있습니다. 이 점 주의바랍니다.</b>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<?php echo(htmlspecialchars_decode('<img id="test" width="0" height="0">'))?>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
if(!document.getElementById("test")) {
|
|
||||||
alert("PHP가 실행되지 않았습니다. 내용을 로컬 파일로 전송한 것이 아니라 서버로 전송했는지 확인 해 주십시오.");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
/**
|
|
||||||
* @use 간단 포토 업로드용으로 제작되었습니다.
|
|
||||||
* @author cielo
|
|
||||||
* @See nhn.husky.SE2M_Configuration
|
|
||||||
* @ 팝업 마크업은 SimplePhotoUpload.html과 SimplePhotoUpload_html5.html이 있습니다.
|
|
||||||
*/
|
|
||||||
|
|
||||||
nhn.husky.SE2M_AttachQuickPhoto = jindo.$Class({
|
|
||||||
name : "SE2M_AttachQuickPhoto",
|
|
||||||
|
|
||||||
$init : function(){},
|
|
||||||
|
|
||||||
$ON_MSG_APP_READY : function(){
|
|
||||||
this.oApp.exec("REGISTER_UI_EVENT", ["photo_attach", "click", "ATTACHPHOTO_OPEN_WINDOW"]);
|
|
||||||
},
|
|
||||||
|
|
||||||
$LOCAL_BEFORE_FIRST : function(sMsg){
|
|
||||||
if(!!this.oPopupMgr){ return; }
|
|
||||||
// Popup Manager에서 사용할 param
|
|
||||||
this.htPopupOption = {
|
|
||||||
oApp : this.oApp,
|
|
||||||
sName : this.name,
|
|
||||||
bScroll : false,
|
|
||||||
sProperties : "",
|
|
||||||
sUrl : ""
|
|
||||||
};
|
|
||||||
this.oPopupMgr = nhn.husky.PopUpManager.getInstance(this.oApp);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 포토 웹탑 오픈
|
|
||||||
*/
|
|
||||||
$ON_ATTACHPHOTO_OPEN_WINDOW : function(){
|
|
||||||
this.htPopupOption.sUrl = this.makePopupURL();
|
|
||||||
this.htPopupOption.sProperties = "left=0,top=0,width=403,height=359,scrollbars=yes,location=no,status=0,resizable=no";
|
|
||||||
|
|
||||||
this.oPopupWindow = this.oPopupMgr.openWindow(this.htPopupOption);
|
|
||||||
|
|
||||||
// 처음 로딩하고 IE에서 커서가 전혀 없는 경우
|
|
||||||
// 복수 업로드시에 순서가 바뀜
|
|
||||||
// [SMARTEDITORSUS-1698]
|
|
||||||
this.oApp.exec('FOCUS', [true]);
|
|
||||||
// --[SMARTEDITORSUS-1698]
|
|
||||||
return (!!this.oPopupWindow ? true : false);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 서비스별로 팝업에 parameter를 추가하여 URL을 생성하는 함수
|
|
||||||
* nhn.husky.SE2M_AttachQuickPhoto.prototype.makePopupURL로 덮어써서 사용하시면 됨.
|
|
||||||
*/
|
|
||||||
makePopupURL : function(){
|
|
||||||
var sPopupUrl = "./sample/photo_uploader/photo_uploader.html";
|
|
||||||
|
|
||||||
return sPopupUrl;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 팝업에서 호출되는 메세지.
|
|
||||||
*/
|
|
||||||
$ON_SET_PHOTO : function(aPhotoData){
|
|
||||||
var sContents,
|
|
||||||
aPhotoInfo,
|
|
||||||
htData;
|
|
||||||
|
|
||||||
if( !aPhotoData ){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
|
||||||
sContents = "";
|
|
||||||
for(var i = 0; i <aPhotoData.length; i++){
|
|
||||||
htData = aPhotoData[i];
|
|
||||||
|
|
||||||
if(!htData.sAlign){
|
|
||||||
htData.sAlign = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
aPhotoInfo = {
|
|
||||||
sName : htData.sFileName || "",
|
|
||||||
sOriginalImageURL : htData.sFileURL,
|
|
||||||
bNewLine : htData.bNewLine || false
|
|
||||||
};
|
|
||||||
|
|
||||||
sContents += this._getPhotoTag(aPhotoInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.oApp.exec("PASTE_HTML", [sContents]); // 위즐 첨부 파일 부분 확인
|
|
||||||
}catch(e){
|
|
||||||
// upload시 error발생에 대해서 skip함
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @use 일반 포토 tag 생성
|
|
||||||
*/
|
|
||||||
_getPhotoTag : function(htPhotoInfo){
|
|
||||||
// id와 class는 썸네일과 연관이 많습니다. 수정시 썸네일 영역도 Test
|
|
||||||
var sTag = '<img src="{=sOriginalImageURL}" title="{=sName}" >';
|
|
||||||
if(htPhotoInfo.bNewLine){
|
|
||||||
sTag += '<br style="clear:both;">';
|
|
||||||
}
|
|
||||||
sTag = jindo.$Template(sTag).process(htPhotoInfo);
|
|
||||||
|
|
||||||
return sTag;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,684 +0,0 @@
|
||||||
//변수 선언 및 초기화
|
|
||||||
var nImageInfoCnt = 0;
|
|
||||||
var htImageInfo = []; //image file정보 저장
|
|
||||||
var aResult = [];
|
|
||||||
|
|
||||||
var rFilter = /^(image\/bmp|image\/gif|image\/jpg|image\/jpeg|image\/png)$/i;
|
|
||||||
var rFilter2 = /^(bmp|gif|jpg|jpeg|png)$/i;
|
|
||||||
var nTotalSize = 0;
|
|
||||||
var nMaxImageSize = 10*1024*1024;
|
|
||||||
var nMaxTotalImageSize = 50*1024*1024;
|
|
||||||
var nMaxImageCount = 10;
|
|
||||||
var nImageFileCount = 0;
|
|
||||||
var bSupportDragAndDropAPI = false;
|
|
||||||
var oFileUploader;
|
|
||||||
var bAttachEvent = false;
|
|
||||||
|
|
||||||
//마크업에 따른 할당
|
|
||||||
var elContent= $("pop_content");
|
|
||||||
var elDropArea = jindo.$$.getSingle(".drag_area",elContent);
|
|
||||||
var elDropAreaUL = jindo.$$.getSingle(".lst_type",elContent);
|
|
||||||
var elCountTxtTxt = jindo.$$.getSingle("#imageCountTxt",elContent);
|
|
||||||
var elTotalSizeTxt = jindo.$$.getSingle("#totalSizeTxt",elContent);
|
|
||||||
var elTextGuide = $("guide_text");
|
|
||||||
var welUploadInputBox = $Element("uploadInputBox");
|
|
||||||
var oNavigator = jindo.$Agent().navigator();
|
|
||||||
|
|
||||||
//마크업-공통
|
|
||||||
var welBtnConfirm = $Element("btn_confirm"); //확인 버튼
|
|
||||||
var welBtnCancel= $Element("btn_cancel"); //취소 버튼
|
|
||||||
|
|
||||||
//진도로 랩핑된 element
|
|
||||||
var welTextGuide = $Element(elTextGuide);
|
|
||||||
var welDropArea = $Element(elDropArea);
|
|
||||||
var welDropAreaUL = $Element(elDropAreaUL);
|
|
||||||
var fnUploadImage = null;
|
|
||||||
|
|
||||||
//File API 지원 여부로 결정
|
|
||||||
function checkDragAndDropAPI(){
|
|
||||||
try{
|
|
||||||
if( !oNavigator.ie ){
|
|
||||||
if(!!oNavigator.safari && oNavigator.version <= 5){
|
|
||||||
bSupportDragAndDropAPI = false;
|
|
||||||
}else{
|
|
||||||
bSupportDragAndDropAPI = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bSupportDragAndDropAPI = false;
|
|
||||||
}
|
|
||||||
}catch(e){
|
|
||||||
bSupportDragAndDropAPI = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------- html5 미지원 브라우저에서 (IE9 이하) ---------------
|
|
||||||
/**
|
|
||||||
* 이미지를 첨부 후 활성화된 버튼 상태
|
|
||||||
*/
|
|
||||||
function goStartMode(){
|
|
||||||
var sSrc = welBtnConfirm.attr("src")|| "";
|
|
||||||
if(sSrc.indexOf("btn_confirm2.png") < 0 ){
|
|
||||||
welBtnConfirm.attr("src","./img/btn_confirm2.png");
|
|
||||||
fnUploadImage.attach(welBtnConfirm.$value(), "click");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* 이미지를 첨부 전 비활성화된 버튼 상태
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
function goReadyMode(){
|
|
||||||
var sSrc = welBtnConfirm.attr("src")|| "";
|
|
||||||
if(sSrc.indexOf("btn_confirm2.png") >= 0 ){
|
|
||||||
fnUploadImage.detach(welBtnConfirm.$value(), "click");
|
|
||||||
welBtnConfirm.attr("src","./img/btn_confirm.png");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 일반 업로드
|
|
||||||
* @desc oFileUploader의 upload함수를 호출함.
|
|
||||||
*/
|
|
||||||
function generalUpload(){
|
|
||||||
oFileUploader.upload();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 이미지 첨부 전 안내 텍스트가 나오는 배경으로 '설정'하는 함수.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
function readyModeBG (){
|
|
||||||
var sClass = welTextGuide.className();
|
|
||||||
if(sClass.indexOf('nobg') >= 0){
|
|
||||||
welTextGuide.removeClass('nobg');
|
|
||||||
welTextGuide.className('bg');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 이미지 첨부 전 안내 텍스트가 나오는 배경을 '제거'하는 함수.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
function startModeBG (){
|
|
||||||
var sClass = welTextGuide.className();
|
|
||||||
if(sClass.indexOf('nobg') < 0){
|
|
||||||
welTextGuide.removeClass('bg');
|
|
||||||
welTextGuide.className('nobg');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------- html5 지원되는 브라우저에서 사용하는 함수 --------------------------
|
|
||||||
/**
|
|
||||||
* 팝업에 노출될 업로드 예정 사진의 수.
|
|
||||||
* @param {Object} nCount 현재 업로드 예정인 사진 장수
|
|
||||||
* @param {Object} nVariable 삭제되는 수
|
|
||||||
*/
|
|
||||||
function updateViewCount (nCount, nVariable){
|
|
||||||
var nCnt = nCount + nVariable;
|
|
||||||
elCountTxtTxt.innerHTML = nCnt +"장";
|
|
||||||
nImageFileCount = nCnt;
|
|
||||||
return nCnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 팝업에 노출될 업로드될 사진 총 용량
|
|
||||||
*/
|
|
||||||
function updateViewTotalSize(){
|
|
||||||
var nViewTotalSize = Number(parseInt((nTotalSize || 0), 10) / (1024*1024));
|
|
||||||
elTotalSizeTxt.innerHTML = nViewTotalSize.toFixed(2) +"MB";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 이미지 전체 용량 재계산.
|
|
||||||
* @param {Object} sParentId
|
|
||||||
*/
|
|
||||||
function refreshTotalImageSize(sParentId){
|
|
||||||
var nDelImgSize = htImageInfo[sParentId].size;
|
|
||||||
if(nTotalSize - nDelImgSize > -1 ){
|
|
||||||
nTotalSize = nTotalSize - nDelImgSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hash table에서 이미지 정보 초기화.
|
|
||||||
* @param {Object} sParentId
|
|
||||||
*/
|
|
||||||
function removeImageInfo (sParentId){
|
|
||||||
//삭제된 이미지의 공간을 초기화 한다.
|
|
||||||
htImageInfo[sParentId] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* byte로 받은 이미지 용량을 화면에 표시를 위해 포맷팅
|
|
||||||
* @param {Object} nByte
|
|
||||||
*/
|
|
||||||
function setUnitString (nByte) {
|
|
||||||
var nImageSize;
|
|
||||||
var sUnit;
|
|
||||||
|
|
||||||
if(nByte < 0 ){
|
|
||||||
nByte = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( nByte < 1024) {
|
|
||||||
nImageSize = Number(nByte);
|
|
||||||
sUnit = 'B';
|
|
||||||
return nImageSize + sUnit;
|
|
||||||
} else if( nByte > (1024*1024)) {
|
|
||||||
nImageSize = Number(parseInt((nByte || 0), 10) / (1024*1024));
|
|
||||||
sUnit = 'MB';
|
|
||||||
return nImageSize.toFixed(2) + sUnit;
|
|
||||||
} else {
|
|
||||||
nImageSize = Number(parseInt((nByte || 0), 10) / 1024);
|
|
||||||
sUnit = 'KB';
|
|
||||||
return nImageSize.toFixed(0) + sUnit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 화면 목록에 적당하게 이름을 잘라서 표시.
|
|
||||||
* @param {Object} sName 파일명
|
|
||||||
* @param {Object} nMaxLng 최대 길이
|
|
||||||
*/
|
|
||||||
function cuttingNameByLength (sName, nMaxLng) {
|
|
||||||
var sTemp, nIndex;
|
|
||||||
if(sName.length > nMaxLng){
|
|
||||||
nIndex = sName.indexOf(".");
|
|
||||||
sTemp = sName.substring(0,nMaxLng) + "..." + sName.substring(nIndex,sName.length) ;
|
|
||||||
} else {
|
|
||||||
sTemp = sName;
|
|
||||||
}
|
|
||||||
return sTemp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total Image Size를 체크해서 추가로 이미지를 넣을지 말지를 결정함.
|
|
||||||
* @param {Object} nByte
|
|
||||||
*/
|
|
||||||
function checkTotalImageSize(nByte){
|
|
||||||
if( nTotalSize + nByte < nMaxTotalImageSize){
|
|
||||||
nTotalSize = nTotalSize + nByte;
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이벤트 핸들러 할당
|
|
||||||
function dragEnter(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragExit(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function dragOver(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 드랍 영역에 사진을 떨구는 순간 발생하는 이벤트
|
|
||||||
* @param {Object} ev
|
|
||||||
*/
|
|
||||||
function drop(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
ev.preventDefault();
|
|
||||||
|
|
||||||
if (nImageFileCount >= 10){
|
|
||||||
alert("최대 10장까지만 등록할 수 있습니다.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(typeof ev.dataTransfer.files == 'undefined'){
|
|
||||||
alert("HTML5를 지원하지 않는 브라우저입니다.");
|
|
||||||
}else{
|
|
||||||
//변수 선언
|
|
||||||
var wel,
|
|
||||||
files,
|
|
||||||
nCount,
|
|
||||||
sListTag = '';
|
|
||||||
|
|
||||||
//초기화
|
|
||||||
files = ev.dataTransfer.files;
|
|
||||||
nCount = files.length;
|
|
||||||
|
|
||||||
if (!!files && nCount === 0){
|
|
||||||
//파일이 아닌, 웹페이지에서 이미지를 드래서 놓는 경우.
|
|
||||||
alert("정상적인 첨부방식이 아닙니다.");
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0, j = nImageFileCount ; i < nCount ; i++){
|
|
||||||
if (!rFilter.test(files[i].type)) {
|
|
||||||
alert("이미지파일 (jpg,gif,png,bmp)만 업로드 가능합니다.");
|
|
||||||
} else if(files[i].size > nMaxImageSize){
|
|
||||||
alert("이미지 용량이 10MB를 초과하여 등록할 수 없습니다.");
|
|
||||||
} else {
|
|
||||||
//제한된 수만 업로드 가능.
|
|
||||||
if ( j < nMaxImageCount ){
|
|
||||||
sListTag += addImage(files[i]);
|
|
||||||
|
|
||||||
//다음 사진을위한 셋팅
|
|
||||||
j = j+1;
|
|
||||||
nImageInfoCnt = nImageInfoCnt+1;
|
|
||||||
} else {
|
|
||||||
alert("최대 10장까지만 등록할 수 있습니다.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(j > 0){
|
|
||||||
//배경 이미지 변경
|
|
||||||
startModeBG();
|
|
||||||
if ( sListTag.length > 1){
|
|
||||||
welDropAreaUL.prependHTML(sListTag);
|
|
||||||
}
|
|
||||||
//이미지 총사이즈 view update
|
|
||||||
updateViewTotalSize();
|
|
||||||
//이미치 총 수 view update
|
|
||||||
nImageFileCount = j;
|
|
||||||
updateViewCount(nImageFileCount, 0);
|
|
||||||
// 저장 버튼 활성화
|
|
||||||
goStartMode();
|
|
||||||
}else{
|
|
||||||
readyModeBG();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 이미지를 추가하기 위해서 file을 저장하고, 목록에 보여주기 위해서 string을 만드는 함수.
|
|
||||||
* @param ofile 한개의 이미지 파일
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
function addImage(ofile){
|
|
||||||
//파일 사이즈
|
|
||||||
var ofile = ofile,
|
|
||||||
sFileSize = 0,
|
|
||||||
sFileName = "",
|
|
||||||
sLiTag = "",
|
|
||||||
bExceedLimitTotalSize = false,
|
|
||||||
aFileList = [];
|
|
||||||
|
|
||||||
sFileSize = setUnitString(ofile.size);
|
|
||||||
sFileName = cuttingNameByLength(ofile.name, 15);
|
|
||||||
bExceedLimitTotalSize = checkTotalImageSize(ofile.size);
|
|
||||||
|
|
||||||
if( !!bExceedLimitTotalSize ){
|
|
||||||
alert("전체 이미지 용량이 50MB를 초과하여 등록할 수 없습니다. \n\n (파일명 : "+sFileName+", 사이즈 : "+sFileSize+")");
|
|
||||||
} else {
|
|
||||||
//이미지 정보 저장
|
|
||||||
htImageInfo['img'+nImageInfoCnt] = ofile;
|
|
||||||
|
|
||||||
//List 마크업 생성하기
|
|
||||||
aFileList.push(' <li id="img'+nImageInfoCnt+'" class="imgLi"><span>'+ sFileName +'</span>');
|
|
||||||
aFileList.push(' <em>'+ sFileSize +'</em>');
|
|
||||||
aFileList.push(' <a onclick="delImage(\'img'+nImageInfoCnt+'\')"><img class="del_button" src="./img/btn_del.png" width="14" height="13" alt="첨부 사진 삭제"></a>');
|
|
||||||
aFileList.push(' </li> ');
|
|
||||||
|
|
||||||
sLiTag = aFileList.join(" ");
|
|
||||||
aFileList = [];
|
|
||||||
}
|
|
||||||
return sLiTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HTML5 DragAndDrop으로 사진을 추가하고, 확인버튼을 누른 경우에 동작한다.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
function html5Upload() {
|
|
||||||
var tempFile,
|
|
||||||
sUploadURL;
|
|
||||||
|
|
||||||
//sUploadURL= 'file_uploader_html5.php'; //upload URL
|
|
||||||
sUploadURL= 'file_uploader_html5.jsp'; //upload URL
|
|
||||||
|
|
||||||
//파일을 하나씩 보내고, 결과를 받음.
|
|
||||||
for(var j=0, k=0; j < nImageInfoCnt; j++) {
|
|
||||||
tempFile = htImageInfo['img'+j];
|
|
||||||
try{
|
|
||||||
if(!!tempFile){
|
|
||||||
//Ajax통신하는 부분. 파일과 업로더할 url을 전달한다.
|
|
||||||
callAjaxForHTML5(tempFile,sUploadURL);
|
|
||||||
k += 1;
|
|
||||||
}
|
|
||||||
}catch(e){}
|
|
||||||
tempFile = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function callAjaxForHTML5 (tempFile, sUploadURL){
|
|
||||||
var oAjax = jindo.$Ajax(sUploadURL, {
|
|
||||||
type: 'xhr',
|
|
||||||
method : "post",
|
|
||||||
onload : function(res){ // 요청이 완료되면 실행될 콜백 함수
|
|
||||||
var sResString = res._response.responseText;
|
|
||||||
if (res.readyState() == 4) {
|
|
||||||
if(sResString.indexOf("NOTALLOW_") > -1){
|
|
||||||
var sFileName = sResString.replace("NOTALLOW_", "");
|
|
||||||
alert("이미지 파일(jpg,gif,png,bmp)만 업로드 하실 수 있습니다. ("+sFileName+")");
|
|
||||||
}else{
|
|
||||||
//성공 시에 responseText를 가지고 array로 만드는 부분.
|
|
||||||
makeArrayFromString(res._response.responseText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
timeout : 3,
|
|
||||||
onerror : jindo.$Fn(onAjaxError, this).bind()
|
|
||||||
});
|
|
||||||
oAjax.header("contentType","multipart/form-data");
|
|
||||||
oAjax.header("file-name",encodeURIComponent(tempFile.name));
|
|
||||||
oAjax.header("file-size",tempFile.size);
|
|
||||||
oAjax.header("file-Type",tempFile.type);
|
|
||||||
oAjax.request(tempFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeArrayFromString(sResString){
|
|
||||||
var aTemp = [],
|
|
||||||
aSubTemp = [],
|
|
||||||
htTemp = {}
|
|
||||||
aResultleng = 0;
|
|
||||||
|
|
||||||
try{
|
|
||||||
if(!sResString || sResString.indexOf("sFileURL") < 0){
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
aTemp = sResString.split("&");
|
|
||||||
for (var i = 0; i < aTemp.length ; i++){
|
|
||||||
if( !!aTemp[i] && aTemp[i] != "" && aTemp[i].indexOf("=") > 0){
|
|
||||||
aSubTemp = aTemp[i].split("=");
|
|
||||||
htTemp[aSubTemp[0]] = aSubTemp[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(e){}
|
|
||||||
|
|
||||||
aResultleng = aResult.length;
|
|
||||||
aResult[aResultleng] = htTemp;
|
|
||||||
|
|
||||||
if(aResult.length == nImageFileCount){
|
|
||||||
setPhotoToEditor(aResult);
|
|
||||||
aResult = null;
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 사진 삭제 시에 호출되는 함수
|
|
||||||
* @param {Object} sParentId
|
|
||||||
*/
|
|
||||||
function delImage (sParentId){
|
|
||||||
var elLi = jindo.$$.getSingle("#"+sParentId);
|
|
||||||
|
|
||||||
refreshTotalImageSize(sParentId);
|
|
||||||
|
|
||||||
updateViewTotalSize();
|
|
||||||
updateViewCount(nImageFileCount,-1);
|
|
||||||
//사진 file array에서 정보 삭제.
|
|
||||||
removeImageInfo(sParentId);
|
|
||||||
//해당 li삭제
|
|
||||||
$Element(elLi).leave();
|
|
||||||
|
|
||||||
//마지막 이미지인경우.
|
|
||||||
if(nImageFileCount === 0){
|
|
||||||
readyModeBG();
|
|
||||||
//사진 추가 버튼 비활성화
|
|
||||||
goReadyMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop 영역 이벤트 다시 활성화.
|
|
||||||
if(!bAttachEvent){
|
|
||||||
addEvent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 이벤트 할당
|
|
||||||
*/
|
|
||||||
function addEvent() {
|
|
||||||
bAttachEvent = true;
|
|
||||||
elDropArea.addEventListener("dragenter", dragEnter, false);
|
|
||||||
elDropArea.addEventListener("dragexit", dragExit, false);
|
|
||||||
elDropArea.addEventListener("dragover", dragOver, false);
|
|
||||||
elDropArea.addEventListener("drop", drop, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeEvent(){
|
|
||||||
bAttachEvent = false;
|
|
||||||
elDropArea.removeEventListener("dragenter", dragEnter, false);
|
|
||||||
elDropArea.removeEventListener("dragexit", dragExit, false);
|
|
||||||
elDropArea.removeEventListener("dragover", dragOver, false);
|
|
||||||
elDropArea.removeEventListener("drop", drop, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ajax 통신 시 error가 발생할 때 처리하는 함수입니다.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
function onAjaxError (){
|
|
||||||
alert("[가이드]사진 업로더할 서버URL셋팅이 필요합니다.-onAjaxError");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 이미지 업로드 시작
|
|
||||||
* 확인 버튼 클릭하면 호출되는 msg
|
|
||||||
*/
|
|
||||||
function uploadImage (e){
|
|
||||||
if(!bSupportDragAndDropAPI){
|
|
||||||
generalUpload();
|
|
||||||
}else{
|
|
||||||
html5Upload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* jindo에 파일 업로드 사용.(iframe에 Form을 Submit하여 리프레시없이 파일을 업로드하는 컴포넌트)
|
|
||||||
*/
|
|
||||||
function callFileUploader (){
|
|
||||||
oFileUploader = new jindo.FileUploader(jindo.$("uploadInputBox"),{
|
|
||||||
// sUrl : location.href.replace(/\/[^\/]*$/, '') + '/file_uploader.php', //샘플 URL입니다.
|
|
||||||
// sCallback : location.href.replace(/\/[^\/]*$/, '') + '/callback.html', //업로드 이후에 iframe이 redirect될 콜백페이지의 주소
|
|
||||||
sUrl : '/imageUpload.do', //파일업로드를 처리하는 페이지
|
|
||||||
sCallback : '/SE2/sample/photo_uploader/callback.html', //업로드 이후에 iframe이 redirect될 콜백페이지의 주소
|
|
||||||
sFiletype : "*.jpg;*.png;*.bmp;*.gif", //허용할 파일의 형식. ex) "*", "*.*", "*.jpg", 구분자(;)
|
|
||||||
sMsgNotAllowedExt : 'JPG, GIF, PNG, BMP 확장자만 가능합니다', //허용할 파일의 형식이 아닌경우에 띄워주는 경고창의 문구
|
|
||||||
bAutoUpload : false, //파일이 선택됨과 동시에 자동으로 업로드를 수행할지 여부 (upload 메소드 수행)
|
|
||||||
bAutoReset : true // 업로드한 직후에 파일폼을 리셋 시킬지 여부 (reset 메소드 수행)
|
|
||||||
}).attach({
|
|
||||||
select : function(oCustomEvent) {
|
|
||||||
//파일 선택이 완료되었을 때 발생
|
|
||||||
// oCustomEvent (이벤트 객체) = {
|
|
||||||
// sValue (String) 선택된 File Input의 값
|
|
||||||
// bAllowed (Boolean) 선택된 파일의 형식이 허용되는 형식인지 여부
|
|
||||||
// sMsgNotAllowedExt (String) 허용되지 않는 파일 형식인 경우 띄워줄 경고메세지
|
|
||||||
// }
|
|
||||||
// 선택된 파일의 형식이 허용되는 경우만 처리
|
|
||||||
if(oCustomEvent.bAllowed === true){
|
|
||||||
goStartMode();
|
|
||||||
}else{
|
|
||||||
goReadyMode();
|
|
||||||
oFileUploader.reset();
|
|
||||||
}
|
|
||||||
// bAllowed 값이 false인 경우 경고문구와 함께 alert 수행
|
|
||||||
// oCustomEvent.stop(); 수행시 bAllowed 가 false이더라도 alert이 수행되지 않음
|
|
||||||
},
|
|
||||||
success : function(oCustomEvent) {
|
|
||||||
// alert("success");
|
|
||||||
// 업로드가 성공적으로 완료되었을 때 발생
|
|
||||||
// oCustomEvent(이벤트 객체) = {
|
|
||||||
// htResult (Object) 서버에서 전달해주는 결과 객체 (서버 설정에 따라 유동적으로 선택가능)
|
|
||||||
// }
|
|
||||||
var aResult = [];
|
|
||||||
aResult[0] = oCustomEvent.htResult;
|
|
||||||
setPhotoToEditor(aResult);
|
|
||||||
//버튼 비활성화
|
|
||||||
goReadyMode();
|
|
||||||
oFileUploader.reset();
|
|
||||||
window.close();
|
|
||||||
},
|
|
||||||
error : function(oCustomEvent) {
|
|
||||||
//업로드가 실패했을 때 발생
|
|
||||||
//oCustomEvent(이벤트 객체) = {
|
|
||||||
// htResult : { (Object) 서버에서 전달해주는 결과 객체. 에러발생시 errstr 프로퍼티를 반드시 포함하도록 서버 응답을 설정하여야한다.
|
|
||||||
// errstr : (String) 에러메시지
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//var wel = jindo.$Element("info");
|
|
||||||
//wel.html(oCustomEvent.htResult.errstr);
|
|
||||||
alert(oCustomEvent.htResult.errstr);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 페이지 닫기 버튼 클릭
|
|
||||||
*/
|
|
||||||
function closeWindow(){
|
|
||||||
if(bSupportDragAndDropAPI){
|
|
||||||
removeEvent();
|
|
||||||
}
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onload = function(){
|
|
||||||
checkDragAndDropAPI();
|
|
||||||
|
|
||||||
if(bSupportDragAndDropAPI){
|
|
||||||
$Element("pop_container2").hide();
|
|
||||||
$Element("pop_container").show();
|
|
||||||
|
|
||||||
welTextGuide.removeClass("nobg");
|
|
||||||
welTextGuide.className("bg");
|
|
||||||
|
|
||||||
addEvent();
|
|
||||||
} else {
|
|
||||||
$Element("pop_container").hide();
|
|
||||||
$Element("pop_container2").show();
|
|
||||||
callFileUploader();
|
|
||||||
}
|
|
||||||
fnUploadImage = $Fn(uploadImage,this);
|
|
||||||
$Fn(closeWindow,this).attach(welBtnCancel.$value(), "click");
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 서버로부터 받은 데이타를 에디터에 전달하고 창을 닫음.
|
|
||||||
* @parameter aFileInfo [{},{},...]
|
|
||||||
* @ex aFileInfo = [
|
|
||||||
* {
|
|
||||||
sFileName : "nmms_215646753.gif",
|
|
||||||
sFileURL :"http://static.naver.net/www/u/2010/0611/nmms_215646753.gif",
|
|
||||||
bNewLine : true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sFileName : "btn_sch_over.gif",
|
|
||||||
sFileURL :"http://static1.naver.net/w9/btn_sch_over.gif",
|
|
||||||
bNewLine : true
|
|
||||||
}
|
|
||||||
* ]
|
|
||||||
*/
|
|
||||||
function setPhotoToEditor(oFileInfo){
|
|
||||||
if (!!opener && !!opener.nhn && !!opener.nhn.husky && !!opener.nhn.husky.PopUpManager) {
|
|
||||||
//스마트 에디터 플러그인을 통해서 넣는 방법 (oFileInfo는 Array)
|
|
||||||
opener.nhn.husky.PopUpManager.setCallback(window, 'SET_PHOTO', [oFileInfo]);
|
|
||||||
//본문에 바로 tag를 넣는 방법 (oFileInfo는 String으로 <img src=....> )
|
|
||||||
//opener.nhn.husky.PopUpManager.setCallback(window, 'PASTE_HTML', [oFileInfo]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2012.05 현재] jindo.$Ajax.prototype.request에서 file과 form을 지원하지 안함.
|
|
||||||
jindo.$Ajax.prototype.request = function(oData) {
|
|
||||||
this._status++;
|
|
||||||
var t = this;
|
|
||||||
var req = this._request;
|
|
||||||
var opt = this._options;
|
|
||||||
var data, v,a = [], data = "";
|
|
||||||
var _timer = null;
|
|
||||||
var url = this._url;
|
|
||||||
this._is_abort = false;
|
|
||||||
|
|
||||||
if( opt.postBody && opt.type.toUpperCase()=="XHR" && opt.method.toUpperCase()!="GET"){
|
|
||||||
if(typeof oData == 'string'){
|
|
||||||
data = oData;
|
|
||||||
}else{
|
|
||||||
data = jindo.$Json(oData).toString();
|
|
||||||
}
|
|
||||||
}else if (typeof oData == "undefined" || !oData) {
|
|
||||||
data = null;
|
|
||||||
} else {
|
|
||||||
data = oData;
|
|
||||||
}
|
|
||||||
|
|
||||||
req.open(opt.method.toUpperCase(), url, opt.async);
|
|
||||||
if (opt.sendheader) {
|
|
||||||
if(!this._headers["Content-Type"]){
|
|
||||||
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
|
|
||||||
}
|
|
||||||
req.setRequestHeader("charset", "utf-8");
|
|
||||||
for (var x in this._headers) {
|
|
||||||
if(this._headers.hasOwnProperty(x)){
|
|
||||||
if (typeof this._headers[x] == "function")
|
|
||||||
continue;
|
|
||||||
req.setRequestHeader(x, String(this._headers[x]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var navi = navigator.userAgent;
|
|
||||||
if(req.addEventListener&&!(navi.indexOf("Opera") > -1)&&!(navi.indexOf("MSIE") > -1)){
|
|
||||||
/*
|
|
||||||
* opera 10.60에서 XMLHttpRequest에 addEventListener기 추가되었지만 정상적으로 동작하지 않아 opera는 무조건 dom1방식으로 지원함.
|
|
||||||
* IE9에서도 opera와 같은 문제가 있음.
|
|
||||||
*/
|
|
||||||
if(this._loadFunc){ req.removeEventListener("load", this._loadFunc, false); }
|
|
||||||
this._loadFunc = function(rq){
|
|
||||||
clearTimeout(_timer);
|
|
||||||
_timer = undefined;
|
|
||||||
t._onload(rq);
|
|
||||||
}
|
|
||||||
req.addEventListener("load", this._loadFunc, false);
|
|
||||||
}else{
|
|
||||||
if (typeof req.onload != "undefined") {
|
|
||||||
req.onload = function(rq){
|
|
||||||
if(req.readyState == 4 && !t._is_abort){
|
|
||||||
clearTimeout(_timer);
|
|
||||||
_timer = undefined;
|
|
||||||
t._onload(rq);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* IE6에서는 onreadystatechange가 동기적으로 실행되어 timeout이벤트가 발생안됨.
|
|
||||||
* 그래서 interval로 체크하여 timeout이벤트가 정상적으로 발생되도록 수정. 비동기 방식일때만
|
|
||||||
|
|
||||||
*/
|
|
||||||
if(window.navigator.userAgent.match(/(?:MSIE) ([0-9.]+)/)[1]==6&&opt.async){
|
|
||||||
var onreadystatechange = function(rq){
|
|
||||||
if(req.readyState == 4 && !t._is_abort){
|
|
||||||
if(_timer){
|
|
||||||
clearTimeout(_timer);
|
|
||||||
_timer = undefined;
|
|
||||||
}
|
|
||||||
t._onload(rq);
|
|
||||||
clearInterval(t._interval);
|
|
||||||
t._interval = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this._interval = setInterval(onreadystatechange,300);
|
|
||||||
|
|
||||||
}else{
|
|
||||||
req.onreadystatechange = function(rq){
|
|
||||||
if(req.readyState == 4){
|
|
||||||
clearTimeout(_timer);
|
|
||||||
_timer = undefined;
|
|
||||||
t._onload(rq);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req.send(data);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
|
||||||
<html lang="ko">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<title>FileUploader Callback</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script type="text/javascript">
|
|
||||||
// alert("callback");
|
|
||||||
// document.domain 설정
|
|
||||||
try { document.domain = "http://localhost"; } catch(e) {}
|
|
||||||
|
|
||||||
// execute callback script
|
|
||||||
var sUrl = document.location.search.substr(1);
|
|
||||||
if (sUrl != "blank") {
|
|
||||||
var oParameter = {}; // query array
|
|
||||||
|
|
||||||
sUrl.replace(/([^=]+)=([^&]*)(&|$)/g, function(){
|
|
||||||
oParameter[arguments[1]] = arguments[2];
|
|
||||||
return "";
|
|
||||||
});
|
|
||||||
|
|
||||||
if ((oParameter.errstr || '').length) { // on error
|
|
||||||
(parent.jindo.FileUploader._oCallback[oParameter.callback_func+'_error'])(oParameter);
|
|
||||||
} else {
|
|
||||||
(parent.jindo.FileUploader._oCallback[oParameter.callback_func+'_success'])(oParameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
<%--------------------------------------------------------------------------------
|
|
||||||
* 화면명 : Smart Editor 2.8 에디터 - 싱글 파일 업로드 처리
|
|
||||||
* 파일명 : /SE2/sample/photo_uploader/file_uploader.jsp
|
|
||||||
--------------------------------------------------------------------------------%>
|
|
||||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
|
||||||
<%@ page import="java.util.List"%>
|
|
||||||
<%@ page import="java.util.UUID"%>
|
|
||||||
<%@ page import="java.io.File"%>
|
|
||||||
<%@ page import="java.io.FileOutputStream"%>
|
|
||||||
<%@ page import="java.io.InputStream"%>
|
|
||||||
<%@ page import="java.io.OutputStream"%>
|
|
||||||
<%@ page import="org.apache.commons.fileupload.FileItem"%>
|
|
||||||
<%@ page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%>
|
|
||||||
<%@ page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%>
|
|
||||||
<%
|
|
||||||
// 로컬경로에 파일 저장하기 ============================================
|
|
||||||
String return1 = "";
|
|
||||||
String return2 = "";
|
|
||||||
String return3 = "";
|
|
||||||
String name = "";
|
|
||||||
|
|
||||||
// multipart로 전송되었는가 체크
|
|
||||||
if(ServletFileUpload.isMultipartContent(request)) {
|
|
||||||
ServletFileUpload uploadHandler = new ServletFileUpload(new DiskFileItemFactory());
|
|
||||||
|
|
||||||
// UTF-8 인코딩 설정
|
|
||||||
uploadHandler.setHeaderEncoding("UTF-8");
|
|
||||||
|
|
||||||
List<FileItem> items = uploadHandler.parseRequest(request);
|
|
||||||
|
|
||||||
// 각 필드태그들을 FOR문을 이용하여 비교를 합니다.
|
|
||||||
for(FileItem item : items) {
|
|
||||||
if(item.getFieldName().equals("callback")) {
|
|
||||||
return1 = item.getString("UTF-8");
|
|
||||||
} else if(item.getFieldName().equals("callback_func")) {
|
|
||||||
return2 = "?callback_func="+item.getString("UTF-8");
|
|
||||||
} else if(item.getFieldName().equals("Filedata")) {
|
|
||||||
// FILE 태그가 1개이상일 경우
|
|
||||||
if(item.getSize() > 0) {
|
|
||||||
// 확장자
|
|
||||||
String ext = item.getName().substring(item.getName().lastIndexOf(".")+1);
|
|
||||||
|
|
||||||
// 파일 기본경로
|
|
||||||
String defaultPath = request.getServletContext().getRealPath("/");
|
|
||||||
|
|
||||||
// 파일 기본경로 _ 상세경로
|
|
||||||
String path = defaultPath + "upload" + File.separator;
|
|
||||||
|
|
||||||
File file = new File(path);
|
|
||||||
|
|
||||||
// 디렉토리 존재하지 않을경우 디렉토리 생성
|
|
||||||
if(!file.exists()) {
|
|
||||||
file.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 서버에 업로드 할 파일명(한글문제로 인해 원본파일은 올리지 않는것이 좋음)
|
|
||||||
String realname = UUID.randomUUID().toString() + "." + ext;
|
|
||||||
|
|
||||||
///////////////// 서버에 파일쓰기 /////////////////
|
|
||||||
InputStream is = item.getInputStream();
|
|
||||||
OutputStream os=new FileOutputStream(path + realname);
|
|
||||||
int numRead;
|
|
||||||
byte b[] = new byte[(int)item.getSize()];
|
|
||||||
|
|
||||||
while((numRead = is.read(b,0,b.length)) != -1) {
|
|
||||||
os.write(b,0,numRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is != null) is.close();
|
|
||||||
|
|
||||||
os.flush();
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
System.out.println("path : "+path);
|
|
||||||
System.out.println("realname : "+realname);
|
|
||||||
|
|
||||||
// 파일 삭제
|
|
||||||
// File f1 = new File(path, realname);
|
|
||||||
// if (!f1.isDirectory()) {
|
|
||||||
// if(!f1.delete()) {
|
|
||||||
// System.out.println("File 삭제 오류!");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
///////////////// 서버에 파일쓰기 /////////////////
|
|
||||||
return3 += "&bNewLine=true&sFileName="+name+"&sFileURL=/upload/"+realname;
|
|
||||||
} else {
|
|
||||||
return3 += "&errstr=error";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response.sendRedirect(return1+return2+return3);
|
|
||||||
// ./로컬경로에 파일 저장하기 ============================================
|
|
||||||
%>
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
<?php
|
|
||||||
// default redirection
|
|
||||||
$url = $_REQUEST["callback"].'?callback_func='.$_REQUEST["callback_func"];
|
|
||||||
$bSuccessUpload = is_uploaded_file($_FILES['Filedata']['tmp_name']);
|
|
||||||
|
|
||||||
// SUCCESSFUL
|
|
||||||
if(bSuccessUpload) {
|
|
||||||
$tmp_name = $_FILES['Filedata']['tmp_name'];
|
|
||||||
$name = $_FILES['Filedata']['name'];
|
|
||||||
|
|
||||||
$filename_ext = strtolower(array_pop(explode('.',$name)));
|
|
||||||
$allow_file = array("jpg", "png", "bmp", "gif");
|
|
||||||
|
|
||||||
if(!in_array($filename_ext, $allow_file)) {
|
|
||||||
$url .= '&errstr='.$name;
|
|
||||||
} else {
|
|
||||||
$uploadDir = '../../upload/';
|
|
||||||
if(!is_dir($uploadDir)){
|
|
||||||
mkdir($uploadDir, 0777);
|
|
||||||
}
|
|
||||||
|
|
||||||
$newPath = $uploadDir.urlencode($_FILES['Filedata']['name']);
|
|
||||||
|
|
||||||
@move_uploaded_file($tmp_name, $newPath);
|
|
||||||
|
|
||||||
$url .= "&bNewLine=true";
|
|
||||||
$url .= "&sFileName=".urlencode(urlencode($name));
|
|
||||||
$url .= "&sFileURL=upload/".urlencode(urlencode($name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// FAILED
|
|
||||||
else {
|
|
||||||
$url .= '&errstr=error';
|
|
||||||
}
|
|
||||||
|
|
||||||
header('Location: '. $url);
|
|
||||||
?>
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
<%--------------------------------------------------------------------------------
|
|
||||||
* 화면명 : Smart Editor 2.8 에디터 - 다중 파일 업로드 처리
|
|
||||||
* 파일명 : /SE2/sample/photo_uploader/file_uploader_html5.jsp
|
|
||||||
--------------------------------------------------------------------------------%>
|
|
||||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
|
||||||
<%@ page import="java.util.List"%>
|
|
||||||
<%@ page import="java.util.UUID"%>
|
|
||||||
<%@ page import="java.io.File"%>
|
|
||||||
<%@ page import="java.io.FileOutputStream"%>
|
|
||||||
<%@ page import="java.io.InputStream"%>
|
|
||||||
<%@ page import="java.io.OutputStream"%>
|
|
||||||
<%@ page import="org.apache.commons.fileupload.FileItem"%>
|
|
||||||
<%@ page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%>
|
|
||||||
<%@ page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%>
|
|
||||||
<%
|
|
||||||
// 로컬경로에 파일 저장하기 ============================================
|
|
||||||
String sFileInfo = "";
|
|
||||||
|
|
||||||
// 파일명 - 싱글파일업로드와 다르게 멀티파일업로드는 HEADER로 넘어옴
|
|
||||||
String name = request.getHeader("file-name");
|
|
||||||
|
|
||||||
// 확장자
|
|
||||||
String ext = name.substring(name.lastIndexOf(".")+1);
|
|
||||||
|
|
||||||
// 파일 기본경로
|
|
||||||
String defaultPath = request.getServletContext().getRealPath("/");
|
|
||||||
|
|
||||||
// 파일 기본경로 _ 상세경로
|
|
||||||
String path = defaultPath + "upload" + File.separator;
|
|
||||||
|
|
||||||
File file = new File(path);
|
|
||||||
if(!file.exists()) {
|
|
||||||
file.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
String realname = UUID.randomUUID().toString() + "." + ext;
|
|
||||||
InputStream is = request.getInputStream();
|
|
||||||
OutputStream os = new FileOutputStream(path + realname);
|
|
||||||
int numRead;
|
|
||||||
|
|
||||||
// 파일쓰기
|
|
||||||
byte b[] = new byte[Integer.parseInt(request.getHeader("file-size"))];
|
|
||||||
while((numRead = is.read(b,0,b.length)) != -1) {
|
|
||||||
os.write(b,0,numRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(is != null) {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
os.flush();
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
System.out.println("path : "+path);
|
|
||||||
System.out.println("realname : "+realname);
|
|
||||||
|
|
||||||
// 파일 삭제
|
|
||||||
// File f1 = new File(path, realname);
|
|
||||||
// if (!f1.isDirectory()) {
|
|
||||||
// if(!f1.delete()) {
|
|
||||||
// System.out.println("File 삭제 오류!");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
sFileInfo += "&bNewLine=true&sFileName="+ name+"&sFileURL="+"/upload/"+realname;
|
|
||||||
out.println(sFileInfo);
|
|
||||||
|
|
||||||
// ./로컬경로에 파일 저장하기 ============================================
|
|
||||||
%>
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
$sFileInfo = '';
|
|
||||||
$headers = array();
|
|
||||||
|
|
||||||
foreach($_SERVER as $k => $v) {
|
|
||||||
if(substr($k, 0, 9) == "HTTP_FILE") {
|
|
||||||
$k = substr(strtolower($k), 5);
|
|
||||||
$headers[$k] = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = new stdClass;
|
|
||||||
$file->name = str_replace("\0", "", rawurldecode($headers['file_name']));
|
|
||||||
$file->size = $headers['file_size'];
|
|
||||||
$file->content = file_get_contents("php://input");
|
|
||||||
|
|
||||||
$filename_ext = strtolower(array_pop(explode('.',$file->name)));
|
|
||||||
$allow_file = array("jpg", "png", "bmp", "gif");
|
|
||||||
|
|
||||||
if(!in_array($filename_ext, $allow_file)) {
|
|
||||||
echo "NOTALLOW_".$file->name;
|
|
||||||
} else {
|
|
||||||
$uploadDir = '../../upload/';
|
|
||||||
if(!is_dir($uploadDir)){
|
|
||||||
mkdir($uploadDir, 0777);
|
|
||||||
}
|
|
||||||
|
|
||||||
$newPath = $uploadDir.iconv("utf-8", "cp949", $file->name);
|
|
||||||
|
|
||||||
if(file_put_contents($newPath, $file->content)) {
|
|
||||||
$sFileInfo .= "&bNewLine=true";
|
|
||||||
$sFileInfo .= "&sFileName=".$file->name;
|
|
||||||
$sFileInfo .= "&sFileURL=upload/".$file->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $sFileInfo;
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 718 B |
|
Before Width: | Height: | Size: 553 B |
|
Before Width: | Height: | Size: 553 B |
|
Before Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 506 B |
|
|
@ -1,390 +0,0 @@
|
||||||
/**
|
|
||||||
* Jindo Component
|
|
||||||
* @version 1.0.3
|
|
||||||
* NHN_Library:Jindo_Component-1.0.3;JavaScript Components for Jindo;
|
|
||||||
* @include Component, UIComponent, FileUploader
|
|
||||||
*/
|
|
||||||
jindo.Component = jindo.$Class({
|
|
||||||
_htEventHandler: null,
|
|
||||||
_htOption: null,
|
|
||||||
$init: function () {
|
|
||||||
var aInstance = this.constructor.getInstance();
|
|
||||||
aInstance.push(this);
|
|
||||||
this._htEventHandler = {};
|
|
||||||
this._htOption = {};
|
|
||||||
this._htOption._htSetter = {};
|
|
||||||
},
|
|
||||||
option: function (sName, vValue) {
|
|
||||||
switch (typeof sName) {
|
|
||||||
case "undefined":
|
|
||||||
return this._htOption;
|
|
||||||
case "string":
|
|
||||||
if (typeof vValue != "undefined") {
|
|
||||||
if (sName == "htCustomEventHandler") {
|
|
||||||
if (typeof this._htOption[sName] == "undefined") {
|
|
||||||
this.attach(vValue);
|
|
||||||
} else {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._htOption[sName] = vValue;
|
|
||||||
if (typeof this._htOption._htSetter[sName] == "function") {
|
|
||||||
this._htOption._htSetter[sName](vValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return this._htOption[sName];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "object":
|
|
||||||
for (var sKey in sName) {
|
|
||||||
if (sKey == "htCustomEventHandler") {
|
|
||||||
if (typeof this._htOption[sKey] == "undefined") {
|
|
||||||
this.attach(sName[sKey]);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._htOption[sKey] = sName[sKey];
|
|
||||||
if (typeof this._htOption._htSetter[sKey] == "function") {
|
|
||||||
this._htOption._htSetter[sKey](sName[sKey]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
optionSetter: function (sName, fSetter) {
|
|
||||||
switch (typeof sName) {
|
|
||||||
case "undefined":
|
|
||||||
return this._htOption._htSetter;
|
|
||||||
case "string":
|
|
||||||
if (typeof fSetter != "undefined") {
|
|
||||||
this._htOption._htSetter[sName] = jindo.$Fn(fSetter, this).bind();
|
|
||||||
} else {
|
|
||||||
return this._htOption._htSetter[sName];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "object":
|
|
||||||
for (var sKey in sName) {
|
|
||||||
this._htOption._htSetter[sKey] = jindo.$Fn(sName[sKey], this).bind();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
fireEvent: function (sEvent, oEvent) {
|
|
||||||
oEvent = oEvent || {};
|
|
||||||
var fInlineHandler = this['on' + sEvent],
|
|
||||||
aHandlerList = this._htEventHandler[sEvent] || [],
|
|
||||||
bHasInlineHandler = typeof fInlineHandler == "function",
|
|
||||||
bHasHandlerList = aHandlerList.length > 0;
|
|
||||||
if (!bHasInlineHandler && !bHasHandlerList) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
aHandlerList = aHandlerList.concat();
|
|
||||||
oEvent.sType = sEvent;
|
|
||||||
if (typeof oEvent._aExtend == 'undefined') {
|
|
||||||
oEvent._aExtend = [];
|
|
||||||
oEvent.stop = function () {
|
|
||||||
if (oEvent._aExtend.length > 0) {
|
|
||||||
oEvent._aExtend[oEvent._aExtend.length - 1].bCanceled = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
oEvent._aExtend.push({
|
|
||||||
sType: sEvent,
|
|
||||||
bCanceled: false
|
|
||||||
});
|
|
||||||
var aArg = [oEvent],
|
|
||||||
i, nLen;
|
|
||||||
for (i = 2, nLen = arguments.length; i < nLen; i++) {
|
|
||||||
aArg.push(arguments[i]);
|
|
||||||
}
|
|
||||||
if (bHasInlineHandler) {
|
|
||||||
fInlineHandler.apply(this, aArg);
|
|
||||||
}
|
|
||||||
if (bHasHandlerList) {
|
|
||||||
var fHandler;
|
|
||||||
for (i = 0, fHandler;
|
|
||||||
(fHandler = aHandlerList[i]); i++) {
|
|
||||||
fHandler.apply(this, aArg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !oEvent._aExtend.pop().bCanceled;
|
|
||||||
},
|
|
||||||
attach: function (sEvent, fHandlerToAttach) {
|
|
||||||
if (arguments.length == 1) {
|
|
||||||
jindo.$H(arguments[0]).forEach(jindo.$Fn(function (fHandler, sEvent) {
|
|
||||||
this.attach(sEvent, fHandler);
|
|
||||||
}, this).bind());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
var aHandler = this._htEventHandler[sEvent];
|
|
||||||
if (typeof aHandler == 'undefined') {
|
|
||||||
aHandler = this._htEventHandler[sEvent] = [];
|
|
||||||
}
|
|
||||||
aHandler.push(fHandlerToAttach);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
detach: function (sEvent, fHandlerToDetach) {
|
|
||||||
if (arguments.length == 1) {
|
|
||||||
jindo.$H(arguments[0]).forEach(jindo.$Fn(function (fHandler, sEvent) {
|
|
||||||
this.detach(sEvent, fHandler);
|
|
||||||
}, this).bind());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
var aHandler = this._htEventHandler[sEvent];
|
|
||||||
if (aHandler) {
|
|
||||||
for (var i = 0, fHandler;
|
|
||||||
(fHandler = aHandler[i]); i++) {
|
|
||||||
if (fHandler === fHandlerToDetach) {
|
|
||||||
aHandler = aHandler.splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
detachAll: function (sEvent) {
|
|
||||||
var aHandler = this._htEventHandler;
|
|
||||||
if (arguments.length) {
|
|
||||||
if (typeof aHandler[sEvent] == 'undefined') {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
delete aHandler[sEvent];
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
for (var o in aHandler) {
|
|
||||||
delete aHandler[o];
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
jindo.Component.factory = function (aObject, htOption) {
|
|
||||||
var aReturn = [],
|
|
||||||
oInstance;
|
|
||||||
if (typeof htOption == "undefined") {
|
|
||||||
htOption = {};
|
|
||||||
}
|
|
||||||
for (var i = 0, el;
|
|
||||||
(el = aObject[i]); i++) {
|
|
||||||
oInstance = new this(el, htOption);
|
|
||||||
aReturn[aReturn.length] = oInstance;
|
|
||||||
}
|
|
||||||
return aReturn;
|
|
||||||
};
|
|
||||||
jindo.Component.getInstance = function () {
|
|
||||||
if (typeof this._aInstance == "undefined") {
|
|
||||||
this._aInstance = [];
|
|
||||||
}
|
|
||||||
return this._aInstance;
|
|
||||||
};
|
|
||||||
jindo.UIComponent = jindo.$Class({
|
|
||||||
$init: function () {
|
|
||||||
this._bIsActivating = false;
|
|
||||||
},
|
|
||||||
isActivating: function () {
|
|
||||||
return this._bIsActivating;
|
|
||||||
},
|
|
||||||
activate: function () {
|
|
||||||
if (this.isActivating()) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
this._bIsActivating = true;
|
|
||||||
if (arguments.length > 0) {
|
|
||||||
this._onActivate.apply(this, arguments);
|
|
||||||
} else {
|
|
||||||
this._onActivate();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
deactivate: function () {
|
|
||||||
if (!this.isActivating()) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
this._bIsActivating = false;
|
|
||||||
if (arguments.length > 0) {
|
|
||||||
this._onDeactivate.apply(this, arguments);
|
|
||||||
} else {
|
|
||||||
this._onDeactivate();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}).extend(jindo.Component);
|
|
||||||
jindo.FileUploader = jindo.$Class({
|
|
||||||
_bIsActivating: false,
|
|
||||||
_aHiddenInput: [],
|
|
||||||
$init: function (elFileSelect, htOption) {
|
|
||||||
var htDefaultOption = {
|
|
||||||
sUrl: '',
|
|
||||||
sCallback: '',
|
|
||||||
htData: {},
|
|
||||||
sFiletype: '*',
|
|
||||||
sMsgNotAllowedExt: "업로드가 허용되지 않는 파일형식입니다",
|
|
||||||
bAutoUpload: false,
|
|
||||||
bAutoReset: true,
|
|
||||||
bActivateOnload: true
|
|
||||||
};
|
|
||||||
this.option(htDefaultOption);
|
|
||||||
this.option(htOption || {});
|
|
||||||
this._el = jindo.$(elFileSelect);
|
|
||||||
this._wel = jindo.$Element(this._el);
|
|
||||||
this._elForm = this._el.form;
|
|
||||||
this._aHiddenInput = [];
|
|
||||||
this.constructor._oCallback = {};
|
|
||||||
this._wfChange = jindo.$Fn(this._onFileSelectChange, this);
|
|
||||||
if (this.option("bActivateOnload")) {
|
|
||||||
this.activate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_appendIframe: function () {
|
|
||||||
var sIframeName = 'tmpFrame_' + this._makeUniqueId();
|
|
||||||
this._welIframe = jindo.$Element(jindo.$('<iframe name="' + sIframeName + '" src="' + this.option("sCallback") + '?blank">')).css({
|
|
||||||
width: '10px',
|
|
||||||
border: '2px',
|
|
||||||
height: '10px',
|
|
||||||
left: '10px',
|
|
||||||
top: '10px'
|
|
||||||
});
|
|
||||||
document.body.appendChild(this._welIframe.$value());
|
|
||||||
},
|
|
||||||
_removeIframe: function () {
|
|
||||||
this._welIframe.leave();
|
|
||||||
},
|
|
||||||
getBaseElement: function () {
|
|
||||||
return this.getFileSelect();
|
|
||||||
},
|
|
||||||
getFileSelect: function () {
|
|
||||||
return this._el;
|
|
||||||
},
|
|
||||||
getFormElement: function () {
|
|
||||||
return this._elForm;
|
|
||||||
},
|
|
||||||
upload: function () {
|
|
||||||
this._appendIframe();
|
|
||||||
var elForm = this.getFormElement(),
|
|
||||||
welForm = jindo.$Element(elForm),
|
|
||||||
sIframeName = this._welIframe.attr("name"),
|
|
||||||
sFunctionName = sIframeName + '_func',
|
|
||||||
sAction = this.option("sUrl");
|
|
||||||
welForm.attr({
|
|
||||||
target: sIframeName,
|
|
||||||
action: sAction
|
|
||||||
});
|
|
||||||
this._aHiddenInput.push(this._createElement('input', {
|
|
||||||
'type': 'hidden',
|
|
||||||
'name': 'callback',
|
|
||||||
'value': this.option("sCallback")
|
|
||||||
}));
|
|
||||||
this._aHiddenInput.push(this._createElement('input', {
|
|
||||||
'type': 'hidden',
|
|
||||||
'name': 'callback_func',
|
|
||||||
'value': sFunctionName
|
|
||||||
}));
|
|
||||||
for (var k in this.option("htData")) {
|
|
||||||
this._aHiddenInput.push(this._createElement('input', {
|
|
||||||
'type': 'hidden',
|
|
||||||
'name': k,
|
|
||||||
'value': this.option("htData")[k]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
for (var i = 0; i < this._aHiddenInput.length; i++) {
|
|
||||||
elForm.appendChild(this._aHiddenInput[i]);
|
|
||||||
}
|
|
||||||
this.constructor._oCallback[sFunctionName + '_success'] = jindo.$Fn(function (oParameter) {
|
|
||||||
this.fireEvent("success", {
|
|
||||||
htResult: oParameter
|
|
||||||
});
|
|
||||||
delete this.constructor._oCallback[oParameter.callback_func + '_success'];
|
|
||||||
delete this.constructor._oCallback[oParameter.callback_func + '_error'];
|
|
||||||
for (var i = 0; i < this._aHiddenInput.length; i++) {
|
|
||||||
jindo.$Element(this._aHiddenInput[i]).leave();
|
|
||||||
}
|
|
||||||
this._aHiddenInput.length = 0;
|
|
||||||
this._removeIframe();
|
|
||||||
}, this).bind();
|
|
||||||
this.constructor._oCallback[sFunctionName + '_error'] = jindo.$Fn(function (oParameter) {
|
|
||||||
this.fireEvent("error", {
|
|
||||||
htResult: oParameter
|
|
||||||
});
|
|
||||||
delete this.constructor._oCallback[oParameter.callback_func + '_success'];
|
|
||||||
delete this.constructor._oCallback[oParameter.callback_func + '_error'];
|
|
||||||
for (var i = 0; i < this._aHiddenInput.length; i++) {
|
|
||||||
jindo.$Element(this._aHiddenInput[i]).leave();
|
|
||||||
}
|
|
||||||
this._aHiddenInput.length = 0;
|
|
||||||
this._removeIframe();
|
|
||||||
}, this).bind();
|
|
||||||
|
|
||||||
elForm.submit();
|
|
||||||
if (this.option("bAutoReset")) {
|
|
||||||
this.reset();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
reset: function () {
|
|
||||||
var elWrapForm = jindo.$("<form>");
|
|
||||||
this._wel.wrap(elWrapForm);
|
|
||||||
elWrapForm.reset();
|
|
||||||
jindo.$Element(elWrapForm).replace(this._el);
|
|
||||||
var elForm = this.getFormElement(),
|
|
||||||
welForm = jindo.$Element(elForm);
|
|
||||||
welForm.attr({
|
|
||||||
target: this._sPrevTarget,
|
|
||||||
action: this._sAction
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
_onActivate: function () {
|
|
||||||
var elForm = this.getFormElement(),
|
|
||||||
welForm = jindo.$Element(elForm);
|
|
||||||
this._sPrevTarget = welForm.attr("target");
|
|
||||||
this._sAction = welForm.attr("action");
|
|
||||||
this._el.value = "";
|
|
||||||
this._wfChange.attach(this._el, "change");
|
|
||||||
},
|
|
||||||
_onDeactivate: function () {
|
|
||||||
this._wfChange.detach(this._el, "change");
|
|
||||||
},
|
|
||||||
_makeUniqueId: function () {
|
|
||||||
return new Date().getMilliseconds() + Math.floor(Math.random() * 100000);
|
|
||||||
},
|
|
||||||
_createElement: function (name, attributes) {
|
|
||||||
var el = jindo.$("<" + name + ">");
|
|
||||||
var wel = jindo.$Element(el);
|
|
||||||
for (var k in attributes) {
|
|
||||||
wel.attr(k, attributes[k]);
|
|
||||||
}
|
|
||||||
return el;
|
|
||||||
},
|
|
||||||
_checkExtension: function (sFile) {
|
|
||||||
var aType = this.option("sFiletype").split(';');
|
|
||||||
for (var i = 0, sType; i < aType.length; i++) {
|
|
||||||
sType = (aType[i] == "*.*") ? "*" : aType[i];
|
|
||||||
sType = sType.replace(/^\s+|\s+$/, '');
|
|
||||||
sType = sType.replace(/\./g, '\\.');
|
|
||||||
sType = sType.replace(/\*/g, '[^\\\/]+');
|
|
||||||
if ((new RegExp(sType + '$', 'gi')).test(sFile)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
_onFileSelectChange: function (we) {
|
|
||||||
var sValue = we.element.value,
|
|
||||||
bAllowed = this._checkExtension(sValue),
|
|
||||||
htParam = {
|
|
||||||
sValue: sValue,
|
|
||||||
bAllowed: bAllowed,
|
|
||||||
sMsgNotAllowedExt: this.option("sMsgNotAllowedExt")
|
|
||||||
};
|
|
||||||
if (sValue.length && this.fireEvent("select", htParam)) {
|
|
||||||
if (bAllowed) {
|
|
||||||
if (this.option("bAutoUpload")) {
|
|
||||||
this.upload();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert(htParam.sMsgNotAllowedExt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).extend(jindo.UIComponent);
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
|
||||||
<html lang="ko">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<meta http-equiv="Content-Script-Type" content="text/javascript">
|
|
||||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
|
||||||
<title>사진 첨부하기 :: SmartEditor2</title>
|
|
||||||
<style type="text/css">
|
|
||||||
/* NHN Web Standard 1Team JJS 120106 */
|
|
||||||
/* Common */
|
|
||||||
body,p,h1,h2,h3,h4,h5,h6,ul,ol,li,dl,dt,dd,table,th,td,form,fieldset,legend,input,textarea,button,select{margin:0;padding:0}
|
|
||||||
body,input,textarea,select,button,table{font-family:'돋움',Dotum,Helvetica,sans-serif;font-size:12px}
|
|
||||||
img,fieldset{border:0}
|
|
||||||
ul,ol{list-style:none}
|
|
||||||
em,address{font-style:normal}
|
|
||||||
a{text-decoration:none}
|
|
||||||
a:hover,a:active,a:focus{text-decoration:underline}
|
|
||||||
|
|
||||||
/* Contents */
|
|
||||||
.blind{visibility:hidden;position:absolute;line-height:0}
|
|
||||||
#pop_wrap{width:383px}
|
|
||||||
#pop_header{height:26px;padding:14px 0 0 20px;border-bottom:1px solid #ededeb;background:#f4f4f3}
|
|
||||||
.pop_container{padding:11px 20px 0}
|
|
||||||
#pop_footer{margin:21px 20px 0;padding:10px 0 16px;border-top:1px solid #e5e5e5;text-align:center}
|
|
||||||
h1{color:#333;font-size:14px;letter-spacing:-1px}
|
|
||||||
.btn_area{word-spacing:2px}
|
|
||||||
.pop_container .drag_area{overflow:hidden;overflow-y:auto;position:relative;width:341px;height:129px;margin-top:4px;border:1px solid #eceff2}
|
|
||||||
.pop_container .drag_area .bg{display:block;position:absolute;top:0;left:0;width:341px;height:129px;background:#fdfdfd url(./img/bg_drag_image.png) 0 0 no-repeat}
|
|
||||||
.pop_container .nobg{background:none}
|
|
||||||
.pop_container .bar{color:#e0e0e0}
|
|
||||||
.pop_container .lst_type li{overflow:hidden;position:relative;padding:7px 0 6px 8px;border-bottom:1px solid #f4f4f4;vertical-align:top}
|
|
||||||
.pop_container :root .lst_type li{padding:6px 0 5px 8px}
|
|
||||||
.pop_container .lst_type li span{float:left;color:#222}
|
|
||||||
.pop_container .lst_type li em{float:right;margin-top:1px;padding-right:22px;color:#a1a1a1;font-size:11px}
|
|
||||||
.pop_container .lst_type li a{position:absolute;top:6px;right:5px}
|
|
||||||
.pop_container .dsc{margin-top:6px;color:#666;line-height:18px}
|
|
||||||
.pop_container .dsc_v1{margin-top:12px}
|
|
||||||
.pop_container .dsc em{color:#13b72a}
|
|
||||||
.pop_container2{padding:46px 60px 20px}
|
|
||||||
.pop_container2 .dsc{margin-top:6px;color:#666;line-height:18px}
|
|
||||||
.pop_container2 .dsc strong{color:#13b72a}
|
|
||||||
.upload{margin:0 4px 0 0;_margin:0;padding:6px 0 4px 6px;border:solid 1px #d5d5d5;color:#a1a1a1;font-size:12px;border-right-color:#efefef;border-bottom-color:#efefef;length:300px;}
|
|
||||||
:root .upload{padding:6px 0 2px 6px;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="pop_wrap">
|
|
||||||
<!-- header -->
|
|
||||||
<div id="pop_header">
|
|
||||||
<h1>사진 첨부하기</h1>
|
|
||||||
</div>
|
|
||||||
<!-- //header -->
|
|
||||||
<!-- container -->
|
|
||||||
|
|
||||||
<!-- [D] HTML5인 경우 pop_container 클래스와 하위 HTML 적용
|
|
||||||
그밖의 경우 pop_container2 클래스와 하위 HTML 적용 -->
|
|
||||||
<div id="pop_container2" class="pop_container2">
|
|
||||||
<!-- content -->
|
|
||||||
<!-- <form id="editor_upimage" name="editor_upimage" action="FileUploader.php" method="post" enctype="multipart/form-data" onSubmit="return false;"> -->
|
|
||||||
<form id="editor_upimage" name="editor_upimage" method="post" enctype="multipart/form-data" onSubmit="return false;">
|
|
||||||
<div id="pop_content2">
|
|
||||||
<input type="file" class="upload" id="uploadInputBox" name="Filedata">
|
|
||||||
<p class="dsc" id="info"><strong>10MB</strong>이하의 이미지 파일만 등록할 수 있습니다.<br>(JPG, GIF, PNG, BMP)</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!-- //content -->
|
|
||||||
</div>
|
|
||||||
<div id="pop_container" class="pop_container" style="display:none;">
|
|
||||||
<!-- content -->
|
|
||||||
<div id="pop_content">
|
|
||||||
<p class="dsc"><em id="imageCountTxt">0장</em>/10장 <span class="bar">|</span> <em id="totalSizeTxt">0MB</em>/50MB</p>
|
|
||||||
<!-- [D] 첨부 이미지 여부에 따른 Class 변화
|
|
||||||
첨부 이미지가 있는 경우 : em에 "bg" 클래스 적용 //첨부 이미지가 없는 경우 : em에 "nobg" 클래스 적용 -->
|
|
||||||
|
|
||||||
<div class="drag_area" id="drag_area">
|
|
||||||
<ul class="lst_type" >
|
|
||||||
</ul>
|
|
||||||
<em class="blind">마우스로 드래그해서 이미지를 추가해주세요.</em><span id="guide_text" class="bg"></span>
|
|
||||||
</div>
|
|
||||||
<div style="display:none;" id="divImageList"></div>
|
|
||||||
<p class="dsc dsc_v1"><em>한 장당 10MB, 1회에 50MB까지, 10개</em>의 이미지 파일을<br>등록할 수 있습니다. (JPG, GIF, PNG, BMP)</p>
|
|
||||||
</div>
|
|
||||||
<!-- //content -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- //container -->
|
|
||||||
<!-- footer -->
|
|
||||||
<div id="pop_footer">
|
|
||||||
<div class="btn_area">
|
|
||||||
<a href="#"><img src="./img/btn_confirm.png" width="49" height="28" alt="확인" id="btn_confirm"></a>
|
|
||||||
<a href="#"><img src="./img/btn_cancel.png" width="48" height="28" alt="취소" id="btn_cancel"></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- //footer -->
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript" src="jindo.min.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="jindo.fileuploader.js" charset="utf-8"></script>
|
|
||||||
<script type="text/javascript" src="attach_photo.js" charset="utf-8"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ko">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Smart Editor™ WYSIWYG Mode</title>
|
|
||||||
</head>
|
|
||||||
<body class="se2_inputarea" style="height:0;-webkit-nbsp-mode:normal"></body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
|
||||||
<html lang="ko">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
|
|
||||||
<title>Smart Editor™ WYSIWYG Mode</title>
|
|
||||||
</head>
|
|
||||||
<body class="se2_inputarea" style="height:0"></body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
||||||
xmlns:p="http://www.springframework.org/schema/p"
|
|
||||||
xmlns:context="http://www.springframework.org/schema/context"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns:task="http://www.springframework.org/schema/task"
|
|
||||||
xmlns:util="http://www.springframework.org/schema/util"
|
|
||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
|
||||||
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
|
|
||||||
http://www.springframework.org/schema/util
|
|
||||||
http://www.springframework.org/schema/util/spring-util-4.0.xsd
|
|
||||||
http://www.springframework.org/schema/context
|
|
||||||
http://www.springframework.org/schema/context/spring-context-4.0.xsd
|
|
||||||
http://www.springframework.org/schema/task
|
|
||||||
http://www.springframework.org/schema/task/spring-task-3.0.xsd
|
|
||||||
">
|
|
||||||
|
|
||||||
<context:component-scan base-package="com.pms"></context:component-scan>
|
|
||||||
|
|
||||||
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
|
|
||||||
<property name="basenames">
|
|
||||||
<list>
|
|
||||||
<value>classpath:com/pms/message/message</value>
|
|
||||||
<value>classpath:com/pms/message/common</value>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
<property name="cacheSeconds">
|
|
||||||
<value>60</value>
|
|
||||||
</property>
|
|
||||||
<property name="defaultEncoding" value="UTF-8"/>
|
|
||||||
</bean>
|
|
||||||
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
|
|
||||||
<property name="defaultLocale" value="ko"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
|
|
||||||
<property name="paramName" value="lang" />
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
|
||||||
<property name="prefix" value="/WEB-INF/view"/>
|
|
||||||
<property name="suffix" value=".jsp"/>
|
|
||||||
<property name="order" value="1"/>
|
|
||||||
</bean>
|
|
||||||
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
|
|
||||||
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
|
|
||||||
<property name="messageConverters">
|
|
||||||
<list>
|
|
||||||
<ref bean="jacksonMessageConverter"/>
|
|
||||||
</list>
|
|
||||||
</property>
|
|
||||||
</bean>
|
|
||||||
<task:scheduler id="gsScheduler" pool-size="10" />
|
|
||||||
<task:annotation-driven scheduler="gsScheduler" />
|
|
||||||
</beans>
|
|
||||||
|
|
||||||